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