1 /*
2  * Copyright (c) 2024, sakumisu
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "usbd_core.h"
7 #include "usbd_video.h"
8 #include "cherryusb_h264.h"
9 
10 #define VIDEO_IN_EP  0x81
11 #define VIDEO_INT_EP 0x83
12 
13 #ifdef CONFIG_USB_HS
14 #define MAX_PAYLOAD_SIZE  1024 // for high speed with one transcations every one micro frame
15 #define VIDEO_PACKET_SIZE (unsigned int)(((MAX_PAYLOAD_SIZE / 1)) | (0x00 << 11))
16 
17 // #define MAX_PAYLOAD_SIZE  2048 // for high speed with two transcations every one micro frame
18 // #define VIDEO_PACKET_SIZE (unsigned int)(((MAX_PAYLOAD_SIZE / 2)) | (0x01 << 11))
19 
20 // #define MAX_PAYLOAD_SIZE  3072 // for high speed with three transcations every one micro frame
21 // #define VIDEO_PACKET_SIZE (unsigned int)(((MAX_PAYLOAD_SIZE / 3)) | (0x02 << 11))
22 
23 #else
24 #define MAX_PAYLOAD_SIZE  1020
25 #define VIDEO_PACKET_SIZE (unsigned int)(((MAX_PAYLOAD_SIZE / 1)) | (0x00 << 11))
26 #endif
27 
28 #define WIDTH  (unsigned int)(640)
29 #define HEIGHT (unsigned int)(480)
30 
31 #define CAM_FPS        (30)
32 #define INTERVAL       (unsigned long)(10000000 / CAM_FPS)
33 #define MIN_BIT_RATE   (unsigned long)(WIDTH * HEIGHT * 16 * CAM_FPS) //16 bit
34 #define MAX_BIT_RATE   (unsigned long)(WIDTH * HEIGHT * 16 * CAM_FPS)
35 #define MAX_FRAME_SIZE (unsigned long)(WIDTH * HEIGHT * 2)
36 
37 #define VS_HEADER_SIZ (unsigned int)(VIDEO_SIZEOF_VS_INPUT_HEADER_DESC(1,1) + VIDEO_SIZEOF_VS_FORMAT_H264_DESC + VIDEO_SIZEOF_VS_FRAME_H264_DESC(1))
38 
39 #define USB_VIDEO_DESC_SIZ (unsigned long)(9 +                            \
40                                            VIDEO_VC_NOEP_DESCRIPTOR_LEN + \
41                                            9 +                            \
42                                            VS_HEADER_SIZ +                \
43                                            9 +                            \
44                                            7)
45 
46 #define USBD_VID           0xffff
47 #define USBD_PID           0xffff
48 #define USBD_MAX_POWER     100
49 #define USBD_LANGID_STRING 1033
50 
51 #ifdef CONFIG_USBDEV_ADVANCE_DESC
52 static const uint8_t device_descriptor[] = {
53     USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xef, 0x02, 0x01, USBD_VID, USBD_PID, 0x0001, 0x01)
54 };
55 
56 static const uint8_t config_descriptor[] = {
57     USB_CONFIG_DESCRIPTOR_INIT(USB_VIDEO_DESC_SIZ, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
58     //VIDEO_VC_DESCRIPTOR_INIT(0x00, VIDEO_INT_EP, 0x0100, VIDEO_VC_TERMINAL_LEN, 48000000, 0x02),
59     VIDEO_VC_NOEP_DESCRIPTOR_INIT(0x00, VIDEO_INT_EP, 0x0100, VIDEO_VC_TERMINAL_LEN, 48000000, 0x02),
60     VIDEO_VS_DESCRIPTOR_INIT(0x01, 0x00, 0x00),
61     VIDEO_VS_INPUT_HEADER_DESCRIPTOR_INIT(0x01, VS_HEADER_SIZ, VIDEO_IN_EP, 0x00),
62     VIDEO_VS_FORMAT_H264_DESCRIPTOR_INIT(0x01, 0x01),
63     VIDEO_VS_FRAME_H264_DESCRIPTOR_INIT(0x01, WIDTH, HEIGHT, MIN_BIT_RATE, MAX_BIT_RATE, DBVAL(INTERVAL), 0x01, DBVAL(INTERVAL)),
64     VIDEO_VS_DESCRIPTOR_INIT(0x01, 0x01, 0x01),
65     /* 1.2.2.2 Standard VideoStream Isochronous Video Data Endpoint Descriptor */
66     USB_ENDPOINT_DESCRIPTOR_INIT(VIDEO_IN_EP, 0x05, VIDEO_PACKET_SIZE, 0x01)
67 };
68 
69 static const uint8_t device_quality_descriptor[] = {
70     ///////////////////////////////////////
71     /// device qualifier descriptor
72     ///////////////////////////////////////
73     0x0a,
74     USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
75     0x00,
76     0x02,
77     0x00,
78     0x00,
79     0x00,
80     0x40,
81     0x00,
82     0x00,
83 };
84 
85 static const char *string_descriptors[] = {
86     (const char[]){ 0x09, 0x04 }, /* Langid */
87     "CherryUSB",                  /* Manufacturer */
88     "CherryUSB UVC DEMO",         /* Product */
89     "2022123456",                 /* Serial Number */
90 };
91 
device_descriptor_callback(uint8_t speed)92 static const uint8_t *device_descriptor_callback(uint8_t speed)
93 {
94     return device_descriptor;
95 }
96 
config_descriptor_callback(uint8_t speed)97 static const uint8_t *config_descriptor_callback(uint8_t speed)
98 {
99     return config_descriptor;
100 }
101 
device_quality_descriptor_callback(uint8_t speed)102 static const uint8_t *device_quality_descriptor_callback(uint8_t speed)
103 {
104     return device_quality_descriptor;
105 }
106 
string_descriptor_callback(uint8_t speed,uint8_t index)107 static const char *string_descriptor_callback(uint8_t speed, uint8_t index)
108 {
109     if (index > 3) {
110         return NULL;
111     }
112     return string_descriptors[index];
113 }
114 
115 const struct usb_descriptor video_descriptor = {
116     .device_descriptor_callback = device_descriptor_callback,
117     .config_descriptor_callback = config_descriptor_callback,
118     .device_quality_descriptor_callback = device_quality_descriptor_callback,
119     .string_descriptor_callback = string_descriptor_callback
120 };
121 #else
122 const uint8_t video_descriptor[] = {
123     USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xef, 0x02, 0x01, USBD_VID, USBD_PID, 0x0001, 0x01),
124     USB_CONFIG_DESCRIPTOR_INIT(USB_VIDEO_DESC_SIZ, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
125     //VIDEO_VC_DESCRIPTOR_INIT(0x00, VIDEO_INT_EP, 0x0100, VIDEO_VC_TERMINAL_LEN, 48000000, 0x02),
126     VIDEO_VC_NOEP_DESCRIPTOR_INIT(0x00, VIDEO_INT_EP, 0x0100, VIDEO_VC_TERMINAL_LEN, 48000000, 0x02),
127     VIDEO_VS_DESCRIPTOR_INIT(0x01, 0x00, 0x00),
128     VIDEO_VS_INPUT_HEADER_DESCRIPTOR_INIT(0x01, VS_HEADER_SIZ, VIDEO_IN_EP, 0x00),
129     VIDEO_VS_FORMAT_H264_DESCRIPTOR_INIT(0x01, 0x01),
130     VIDEO_VS_FRAME_H264_DESCRIPTOR_INIT(0x01, WIDTH, HEIGHT, MIN_BIT_RATE, MAX_BIT_RATE, DBVAL(INTERVAL), 0x01, DBVAL(INTERVAL)),
131     VIDEO_VS_DESCRIPTOR_INIT(0x01, 0x01, 0x01),
132     /* 1.2.2.2 Standard VideoStream Isochronous Video Data Endpoint Descriptor */
133     USB_ENDPOINT_DESCRIPTOR_INIT(VIDEO_IN_EP, 0x05, VIDEO_PACKET_SIZE, 0x01),
134 
135     ///////////////////////////////////////
136     /// string0 descriptor
137     ///////////////////////////////////////
138     USB_LANGID_INIT(USBD_LANGID_STRING),
139     ///////////////////////////////////////
140     /// string1 descriptor
141     ///////////////////////////////////////
142     0x14,                       /* bLength */
143     USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
144     'C', 0x00,                  /* wcChar0 */
145     'h', 0x00,                  /* wcChar1 */
146     'e', 0x00,                  /* wcChar2 */
147     'r', 0x00,                  /* wcChar3 */
148     'r', 0x00,                  /* wcChar4 */
149     'y', 0x00,                  /* wcChar5 */
150     'U', 0x00,                  /* wcChar6 */
151     'S', 0x00,                  /* wcChar7 */
152     'B', 0x00,                  /* wcChar8 */
153     ///////////////////////////////////////
154     /// string2 descriptor
155     ///////////////////////////////////////
156     0x26,                       /* bLength */
157     USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
158     'C', 0x00,                  /* wcChar0 */
159     'h', 0x00,                  /* wcChar1 */
160     'e', 0x00,                  /* wcChar2 */
161     'r', 0x00,                  /* wcChar3 */
162     'r', 0x00,                  /* wcChar4 */
163     'y', 0x00,                  /* wcChar5 */
164     'U', 0x00,                  /* wcChar6 */
165     'S', 0x00,                  /* wcChar7 */
166     'B', 0x00,                  /* wcChar8 */
167     ' ', 0x00,                  /* wcChar9 */
168     'U', 0x00,                  /* wcChar10 */
169     'V', 0x00,                  /* wcChar11 */
170     'C', 0x00,                  /* wcChar12 */
171     ' ', 0x00,                  /* wcChar13 */
172     'D', 0x00,                  /* wcChar14 */
173     'E', 0x00,                  /* wcChar15 */
174     'M', 0x00,                  /* wcChar16 */
175     'O', 0x00,                  /* wcChar17 */
176     ///////////////////////////////////////
177     /// string3 descriptor
178     ///////////////////////////////////////
179     0x16,                       /* bLength */
180     USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
181     '2', 0x00,                  /* wcChar0 */
182     '0', 0x00,                  /* wcChar1 */
183     '2', 0x00,                  /* wcChar2 */
184     '1', 0x00,                  /* wcChar3 */
185     '0', 0x00,                  /* wcChar4 */
186     '3', 0x00,                  /* wcChar5 */
187     '1', 0x00,                  /* wcChar6 */
188     '0', 0x00,                  /* wcChar7 */
189     '0', 0x00,                  /* wcChar8 */
190     '0', 0x00,                  /* wcChar9 */
191 #ifdef CONFIG_USB_HS
192     ///////////////////////////////////////
193     /// device qualifier descriptor
194     ///////////////////////////////////////
195     0x0a,
196     USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
197     0x00,
198     0x02,
199     0x00,
200     0x00,
201     0x00,
202     0x40,
203     0x00,
204     0x00,
205 #endif
206     0x00
207 };
208 #endif
209 
210 volatile bool tx_flag = 0;
211 volatile bool iso_tx_busy = false;
212 
usbd_event_handler(uint8_t busid,uint8_t event)213 static void usbd_event_handler(uint8_t busid, uint8_t event)
214 {
215     switch (event) {
216         case USBD_EVENT_RESET:
217             break;
218         case USBD_EVENT_CONNECTED:
219             break;
220         case USBD_EVENT_DISCONNECTED:
221             break;
222         case USBD_EVENT_RESUME:
223             break;
224         case USBD_EVENT_SUSPEND:
225             break;
226         case USBD_EVENT_CONFIGURED:
227             tx_flag = 0;
228             iso_tx_busy = false;
229             break;
230         case USBD_EVENT_SET_REMOTE_WAKEUP:
231             break;
232         case USBD_EVENT_CLR_REMOTE_WAKEUP:
233             break;
234 
235         default:
236             break;
237     }
238 }
239 
usbd_video_open(uint8_t busid,uint8_t intf)240 void usbd_video_open(uint8_t busid, uint8_t intf)
241 {
242     tx_flag = 1;
243     USB_LOG_RAW("OPEN\r\n");
244     iso_tx_busy = false;
245 }
usbd_video_close(uint8_t busid,uint8_t intf)246 void usbd_video_close(uint8_t busid, uint8_t intf)
247 {
248     USB_LOG_RAW("CLOSE\r\n");
249     tx_flag = 0;
250     iso_tx_busy = false;
251 }
252 
usbd_video_iso_callback(uint8_t busid,uint8_t ep,uint32_t nbytes)253 void usbd_video_iso_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
254 {
255     if (nbytes == 0) {
256         return;
257     }
258 
259     if (usbd_video_stream_split_transfer(busid, ep)) {
260         /* one frame has done */
261         iso_tx_busy = false;
262     }
263 }
264 
265 static struct usbd_endpoint video_in_ep = {
266     .ep_cb = usbd_video_iso_callback,
267     .ep_addr = VIDEO_IN_EP
268 };
269 
270 struct usbd_interface intf0;
271 struct usbd_interface intf1;
272 
video_init(uint8_t busid,uintptr_t reg_base)273 void video_init(uint8_t busid, uintptr_t reg_base)
274 {
275 #ifdef CONFIG_USBDEV_ADVANCE_DESC
276     usbd_desc_register(busid, &video_descriptor);
277 #else
278     usbd_desc_register(busid, video_descriptor);
279 #endif
280     usbd_add_interface(busid, usbd_video_init_intf(busid, &intf0, INTERVAL, MAX_FRAME_SIZE, MAX_PAYLOAD_SIZE));
281     usbd_add_interface(busid, usbd_video_init_intf(busid, &intf1, INTERVAL, MAX_FRAME_SIZE, MAX_PAYLOAD_SIZE));
282     usbd_add_endpoint(busid, &video_in_ep);
283 
284     usbd_initialize(busid, reg_base, usbd_event_handler);
285 }
286 
287 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t packet_buffer[MAX_PAYLOAD_SIZE];
288 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t frame_buffer[32 * 1024];
289 
video_test(uint8_t busid)290 void video_test(uint8_t busid)
291 {
292     uint32_t out_len;
293     uint32_t packets;
294 
295     (void)packets;
296     memset(packet_buffer, 0, sizeof(packet_buffer));
297     while (1) {
298         if (tx_flag) {
299             iso_tx_busy = true;
300             memcpy(frame_buffer, cherryusb_h264, sizeof(cherryusb_h264)); // cherryusb_h264 is a static yuyv frame buffer, so we need copy it to frame_buffer
301             usbd_video_stream_start_write(busid, VIDEO_IN_EP, packet_buffer, (uint8_t *)frame_buffer, sizeof(cherryusb_h264), false);
302             while (iso_tx_busy) {
303                 if (tx_flag == 0) {
304                     break;
305                 }
306             }
307         }
308     }
309 }