4040#define USB_VID 0xCafe
4141#define USB_BCD 0x0200
4242
43+ // String Descriptor Index
44+ enum {
45+ STRID_LANGID = 0 ,
46+ STRID_MANUFACTURER ,
47+ STRID_PRODUCT ,
48+ STRID_SERIAL ,
49+ STRID_UVC_CONTROL ,
50+ STRID_UVC_STREAMING ,
51+ };
52+
53+ // array of pointer to string descriptors
54+ char const * string_desc_arr [] = {
55+ (const char []) {0x09 , 0x04 }, // 0: is supported language is English (0x0409)
56+ "TinyUSB" , // 1: Manufacturer
57+ "TinyUSB Device" , // 2: Product
58+ NULL , // 3: Serials will use unique ID if possible
59+ "TinyUSB UVC Control" , // 4: UVC Interface
60+ "TinyUSB UVC Streaming" , // 5: UVC Interface
61+ };
62+
4363//--------------------------------------------------------------------+
4464// Device Descriptors
4565//--------------------------------------------------------------------+
@@ -60,9 +80,9 @@ tusb_desc_device_t const desc_device = {
6080 .idProduct = USB_PID ,
6181 .bcdDevice = 0x0100 ,
6282
63- .iManufacturer = 0x01 ,
64- .iProduct = 0x02 ,
65- .iSerialNumber = 0x03 ,
83+ .iManufacturer = STRID_MANUFACTURER ,
84+ .iProduct = STRID_PRODUCT ,
85+ .iSerialNumber = STRID_SERIAL ,
6686
6787 .bNumConfigurations = 0x01
6888};
@@ -164,6 +184,193 @@ uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
164184}
165185#endif // highspeed
166186
187+ typedef struct TU_ATTR_PACKED {
188+ tusb_desc_interface_t itf ;
189+ tusb_desc_video_control_header_1itf_t header ;
190+ tusb_desc_video_control_camera_terminal_t camera_terminal ;
191+ tusb_desc_video_control_output_terminal_t output_terminal ;
192+ } uvc_control_desc_t ;
193+
194+ /* Windows support YUY2 and NV12
195+ * https://docs.microsoft.com/en-us/windows-hardware/drivers/stream/usb-video-class-driver-overview */
196+
197+ typedef struct TU_ATTR_PACKED {
198+ tusb_desc_interface_t itf ;
199+ tusb_desc_video_streaming_input_header_1byte_t header ;
200+ tusb_desc_video_format_uncompressed_t format ;
201+ tusb_desc_video_frame_uncompressed_continuous_t frame ;
202+ tusb_desc_video_streaming_color_matching_t color ;
203+ tusb_desc_endpoint_t ep ;
204+ } uvc_streaming_desc_t ;
205+
206+ typedef struct TU_ATTR_PACKED {
207+ tusb_desc_configuration_t config ;
208+ tusb_desc_interface_assoc_t iad ;
209+ uvc_control_desc_t video_control ;
210+ uvc_streaming_desc_t video_streaming ;
211+ } uvc_cfg_desc_t ;
212+
213+ const uvc_cfg_desc_t config_desc = {
214+ .config = {
215+ .bLength = sizeof (tusb_desc_configuration_t ),
216+ .bDescriptorType = TUSB_DESC_CONFIGURATION ,
217+
218+ .wTotalLength = sizeof (uvc_cfg_desc_t ),
219+ .bNumInterfaces = ITF_NUM_TOTAL ,
220+ .bConfigurationValue = 1 ,
221+ .iConfiguration = 0 ,
222+ .bmAttributes = TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP ,
223+ .bMaxPower = 100 / 2
224+ },
225+ .iad = {
226+ .bLength = sizeof (tusb_desc_interface_assoc_t ),
227+ .bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION ,
228+
229+ .bFirstInterface = ITF_NUM_VIDEO_CONTROL ,
230+ .bInterfaceCount = 2 ,
231+ .bFunctionClass = TUSB_CLASS_VIDEO ,
232+ .bFunctionSubClass = VIDEO_SUBCLASS_INTERFACE_COLLECTION ,
233+ .bFunctionProtocol = VIDEO_ITF_PROTOCOL_UNDEFINED ,
234+ .iFunction = 0
235+ },
236+
237+ .video_control = {
238+ .itf = {
239+ .bLength = sizeof (tusb_desc_interface_t ),
240+ .bDescriptorType = TUSB_DESC_INTERFACE ,
241+
242+ .bInterfaceNumber = ITF_NUM_VIDEO_CONTROL ,
243+ .bAlternateSetting = 0 ,
244+ .bNumEndpoints = 0 ,
245+ .bInterfaceClass = TUSB_CLASS_VIDEO ,
246+ .bInterfaceSubClass = VIDEO_SUBCLASS_CONTROL ,
247+ .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15 ,
248+ .iInterface = STRID_UVC_CONTROL
249+ },
250+ .header = {
251+ .bLength = sizeof (tusb_desc_video_control_header_1itf_t ),
252+ .bDescriptorType = TUSB_DESC_CS_INTERFACE ,
253+ .bDescriptorSubType = VIDEO_CS_ITF_VC_HEADER ,
254+
255+ .bcdUVC = VIDEO_BCD_1_50 ,
256+ .wTotalLength = sizeof (uvc_control_desc_t ) - sizeof (tusb_desc_interface_t ), // CS VC descriptors only
257+ .dwClockFrequency = UVC_CLOCK_FREQUENCY ,
258+ .bInCollection = 1 ,
259+ .baInterfaceNr = { ITF_NUM_VIDEO_STREAMING }
260+ },
261+ .camera_terminal = {
262+ .bLength = sizeof (tusb_desc_video_control_camera_terminal_t ),
263+ .bDescriptorType = TUSB_DESC_CS_INTERFACE ,
264+ .bDescriptorSubType = VIDEO_CS_ITF_VC_INPUT_TERMINAL ,
265+
266+ .bTerminalID = UVC_ENTITY_CAP_INPUT_TERMINAL ,
267+ .wTerminalType = VIDEO_ITT_CAMERA ,
268+ .bAssocTerminal = 0 ,
269+ .iTerminal = 0 ,
270+ .wObjectiveFocalLengthMin = 0 ,
271+ .wObjectiveFocalLengthMax = 0 ,
272+ .wOcularFocalLength = 0 ,
273+ .bControlSize = 3 ,
274+ .bmControls = { 0 , 0 , 0 }
275+ },
276+ .output_terminal = {
277+ .bLength = sizeof (tusb_desc_video_control_output_terminal_t ),
278+ .bDescriptorType = TUSB_DESC_CS_INTERFACE ,
279+ .bDescriptorSubType = VIDEO_CS_ITF_VC_OUTPUT_TERMINAL ,
280+
281+ .bTerminalID = UVC_ENTITY_CAP_OUTPUT_TERMINAL ,
282+ .wTerminalType = VIDEO_TT_STREAMING ,
283+ .bAssocTerminal = 0 ,
284+ .bSourceID = UVC_ENTITY_CAP_INPUT_TERMINAL ,
285+ .iTerminal = 0
286+ }
287+ },
288+
289+ .video_streaming = {
290+ .itf = {
291+ .bLength = sizeof (tusb_desc_interface_t ),
292+ .bDescriptorType = TUSB_DESC_INTERFACE ,
293+
294+ .bInterfaceNumber = ITF_NUM_VIDEO_STREAMING ,
295+ .bAlternateSetting = 0 ,
296+ .bNumEndpoints = 1 ,
297+ .bInterfaceClass = TUSB_CLASS_VIDEO ,
298+ .bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING ,
299+ .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15 ,
300+ .iInterface = STRID_UVC_STREAMING
301+ },
302+ .header = {
303+ .bLength = sizeof (tusb_desc_video_streaming_input_header_1byte_t ),
304+ .bDescriptorType = TUSB_DESC_CS_INTERFACE ,
305+ .bDescriptorSubType = VIDEO_CS_ITF_VS_INPUT_HEADER ,
306+
307+ .bNumFormats = 1 ,
308+ .wTotalLength = sizeof (uvc_streaming_desc_t ) - sizeof (tusb_desc_interface_t ) - sizeof (tusb_desc_endpoint_t ), // CS VS descriptors only
309+ .bEndpointAddress = EPNUM_VIDEO_IN ,
310+ .bmInfo = 0 ,
311+ .bTerminalLink = UVC_ENTITY_CAP_OUTPUT_TERMINAL ,
312+ .bStillCaptureMethod = 0 ,
313+ .bTriggerSupport = 0 ,
314+ .bTriggerUsage = 0 ,
315+ .bControlSize = 1 ,
316+ .bmaControls = { 0 }
317+ },
318+ .format = {
319+ .bLength = sizeof (tusb_desc_video_format_uncompressed_t ),
320+ .bDescriptorType = TUSB_DESC_CS_INTERFACE ,
321+ .bDescriptorSubType = VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED ,
322+
323+ .bFormatIndex = 1 , // 1-based index
324+ .bNumFrameDescriptors = 1 ,
325+ .guidFormat = { TUD_VIDEO_GUID_YUY2 },
326+ .bBitsPerPixel = 16 ,
327+ .bDefaultFrameIndex = 1 ,
328+ .bAspectRatioX = 0 ,
329+ .bAspectRatioY = 0 ,
330+ .bmInterlaceFlags = 0 ,
331+ .bCopyProtect = 0
332+ },
333+ .frame = {
334+ .bLength = sizeof (tusb_desc_video_frame_uncompressed_continuous_t ),
335+ .bDescriptorType = TUSB_DESC_CS_INTERFACE ,
336+ .bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED ,
337+
338+ .bFrameIndex = 1 , // 1-based index
339+ .bmCapabilities = 0 ,
340+ .wWidth = FRAME_WIDTH ,
341+ .wHeight = FRAME_HEIGHT ,
342+ .dwMinBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * 1 ,
343+ .dwMaxBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * FRAME_RATE ,
344+ .dwMaxVideoFrameBufferSize = FRAME_WIDTH * FRAME_HEIGHT * 16 / 8 ,
345+ .dwDefaultFrameInterval = 10000000 / FRAME_RATE ,
346+ .bFrameIntervalType = 0 , // continuous
347+ .dwFrameInterval = {
348+ 10000000 / FRAME_RATE , // min
349+ 10000000 , // max
350+ 10000000 / FRAME_RATE // step
351+ }
352+ },
353+ .color = {
354+ .bLength = sizeof (tusb_desc_video_streaming_color_matching_t ),
355+ .bDescriptorType = TUSB_DESC_CS_INTERFACE ,
356+ .bDescriptorSubType = VIDEO_CS_ITF_VS_COLORFORMAT ,
357+
358+ .bColorPrimaries = VIDEO_COLOR_PRIMARIES_BT709 ,
359+ .bTransferCharacteristics = VIDEO_COLOR_XFER_CH_BT709 ,
360+ .bMatrixCoefficients = VIDEO_COLOR_COEF_SMPTE170M
361+ },
362+ .ep = {
363+ .bLength = sizeof (tusb_desc_endpoint_t ),
364+ .bDescriptorType = TUSB_DESC_ENDPOINT ,
365+
366+ .bEndpointAddress = EPNUM_VIDEO_IN ,
367+ .bmAttributes = { .xfer = TUSB_XFER_BULK },
368+ .wMaxPacketSize = CFG_TUD_VIDEO_STREAMING_BULK ? 64 : CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE ,
369+ .bInterval = 1
370+ }
371+ }
372+ };
373+
167374// Invoked when received GET CONFIGURATION DESCRIPTOR
168375// Application return pointer to descriptor
169376// Descriptor contents must exist long enough for transfer to complete
@@ -174,31 +381,15 @@ uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
174381 // Although we are highspeed, host may be fullspeed.
175382 return (tud_speed_get () == TUSB_SPEED_HIGH ) ? desc_hs_configuration : desc_fs_configuration ;
176383#else
177- return desc_fs_configuration ;
384+ // return desc_fs_configuration;
385+ return (uint8_t const * ) & config_desc ;
178386#endif
179387}
180388
181389//--------------------------------------------------------------------+
182390// String Descriptors
183391//--------------------------------------------------------------------+
184392
185- // String Descriptor Index
186- enum {
187- STRID_LANGID = 0 ,
188- STRID_MANUFACTURER ,
189- STRID_PRODUCT ,
190- STRID_SERIAL ,
191- };
192-
193- // array of pointer to string descriptors
194- char const * string_desc_arr [] = {
195- (const char []) {0x09 , 0x04 }, // 0: is supported language is English (0x0409)
196- "TinyUSB" , // 1: Manufacturer
197- "TinyUSB Device" , // 2: Product
198- NULL , // 3: Serials will use unique ID if possible
199- "TinyUSB UVC" , // 4: UVC Interface
200- };
201-
202393static uint16_t _desc_str [32 + 1 ];
203394
204395// Invoked when received GET STRING DESCRIPTOR request
0 commit comments