/* * Copyright (c) 2022, sakumisu * * SPDX-License-Identifier: Apache-2.0 */ #include #ifdef RT_USB_DEVICE_VIDEO #include "include/rttusb_device.h" #include "usbd_video.h" #include "stream_define.h" #include "video_app_h264_msi.h" #include "jpg_concat_msi.h" #define CONFIG_USB_HS //默认设置为USB HS模式 #ifdef CONFIG_USB_HS #define MAX_PAYLOAD_SIZE 1024 // for high speed with one transcations every one micro frame #define VIDEO_PACKET_SIZE (unsigned int)(((MAX_PAYLOAD_SIZE / 1)) | (0x00 << 11)) // #define MAX_PAYLOAD_SIZE 2048 // for high speed with two transcations every one micro frame // #define VIDEO_PACKET_SIZE (unsigned int)(((MAX_PAYLOAD_SIZE / 2)) | (0x01 << 11)) // #define MAX_PAYLOAD_SIZE 3072 // for high speed with three transcations every one micro frame // #define VIDEO_PACKET_SIZE (unsigned int)(((MAX_PAYLOAD_SIZE / 3)) | (0x02 << 11)) #else #define MAX_PAYLOAD_SIZE 1020 #define VIDEO_PACKET_SIZE (unsigned int)(((MAX_PAYLOAD_SIZE / 1)) | (0x00 << 11)) #endif #define VIDEO_IN_EP 0x81 /* --------- Video MJPEG Format --------- */ #define VIDEO_MJPEG_bNumFrameDescriptors (2) #if VIDEO_MJPEG_bNumFrameDescriptors >= 1 #define VIDEO_MJPEG_Frame1_WIDTH (unsigned int)(640) #define VIDEO_MJPEG_Frame1_HEIGHT (unsigned int)(360) #endif #if VIDEO_MJPEG_bNumFrameDescriptors >= 2 #define VIDEO_MJPEG_Frame2_WIDTH (unsigned int)(1280) #define VIDEO_MJPEG_Frame2_HEIGHT (unsigned int)(720) #endif /* --------- Video H26x Format --------- */ #define VIDEO_H26x__bNumFrameDescriptors (2) #if VIDEO_H26x__bNumFrameDescriptors >= 1 #define VIDEO_H26x_Frame1_WIDTH (unsigned int)(1280) #define VIDEO_H26x_Frame1_HEIGHT (unsigned int)(720) #endif #if VIDEO_H26x__bNumFrameDescriptors >= 2 #define VIDEO_H26x_Frame2_WIDTH (unsigned int)(1920) #define VIDEO_H26x_Frame2_HEIGHT (unsigned int)(1080) #endif #define CAM_FPS (30) #define INTERVAL (unsigned long)(10000000 / CAM_FPS) #define MIN_BIT_RATE(width, height) (unsigned long)(width * height * 16 * CAM_FPS) //16 bit #define MAX_BIT_RATE(width, height) (unsigned long)(width * height * 16 * CAM_FPS) #define MAX_FRAME_SIZE(width, height) (unsigned long)(width * height * 2) #define UVC_MAX_PACKET_SIZE 64 #define UVC_INTF_STR_INDEX 0x02 #define VIDEO_BUFFER_SZ MAX_PAYLOAD_SIZE //USB发送缓存大小 #define EVENT_VIDEO_START (1 << 0) #define EVENT_VIDEO_STOP (1 << 1) #define EVENT_VIDEO_DATA (1 << 2) #define EVENT_VIDEO_COMPLITE (1 << 3) #define CONFIG_USBDEV_MAX_BUS 1 static struct usbd_video_priv g_usbd_video[CONFIG_USBDEV_MAX_BUS]; static struct uvc_class_device usbd_uvc; struct uvc_vc_noep_descriptor { #ifdef RT_USB_DEVICE_COMPOSITE struct uiad_descriptor iad_desc; #endif struct uinterface_descriptor intf_desc; struct video_cs_if_vc_header_descriptor hdr_desc; struct video_cs_if_vc_input_terminal_descriptor it_desc; struct video_cs_if_vc_processing_unit_descriptor pu_desc; struct video_cs_if_vc_output_terminal_descriptor ot_desc; }; struct uvc_vs_noep_descriptor { struct uinterface_descriptor intf_desc; struct video_cs_if_vs_input_header_descriptor it_desc; #if VIDEO_MJPEG_bNumFrameDescriptors >= 1 struct video_cs_if_vs_format_mjpeg_descriptor fmt_desc; struct video_cs_if_vs_frame_mjpeg_descriptor frame_desc; #endif #if VIDEO_MJPEG_bNumFrameDescriptors >= 2 struct video_cs_if_vs_frame_mjpeg_descriptor frame2_desc; #endif #if VIDEO_H26x__bNumFrameDescriptors >= 1 struct video_cs_if_vs_format_h26x_descriptor h264_desc; struct video_cs_if_vs_frame_h26x_descriptor h264_frame_desc; #endif #if VIDEO_H26x__bNumFrameDescriptors >= 2 struct video_cs_if_vs_frame_h26x_descriptor h264_frame2_desc; #endif }; struct uvc_vs_ep_descriptor { struct uinterface_descriptor intf_desc; struct uendpoint_descriptor ep_desc; }; rt_align(4) static struct udevice_descriptor dev_desc = { USB_DESC_LENGTH_DEVICE, //bLength; USB_DESC_TYPE_DEVICE, //type; USB_BCD_VERSION, //bcdUSB; USB_CLASS_MISC, //bDeviceClass; 0x02, //bDeviceSubClass; 0x01, //bDeviceProtocol; UVC_MAX_PACKET_SIZE, //bMaxPacketSize0; _VENDOR_ID, //idVendor; _PRODUCT_ID, //idProduct; USB_BCD_DEVICE, //bcdDevice; USB_STRING_MANU_INDEX, //iManufacturer; USB_STRING_PRODUCT_INDEX, //iProduct; USB_STRING_SERIAL_INDEX, //iSerialNumber;Unused. 0x00, //bNumConfigurations; }; //FS and HS needed rt_align(4) static struct usb_qualifier_descriptor dev_qualifier = { sizeof(dev_qualifier), //bLength USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType 0x0200, //bcdUSB USB_CLASS_VIDEO, //bDeviceClass 0x00, //bDeviceSubClass 0x00, //bDeviceProtocol 64, //bMaxPacketSize0 0x01, //bNumConfigurations 0, }; rt_align(4) const static char *_ustring[] = { "Language", "USBZH", //"Cherry USB", "USB2.0 Camera", //"USB Composite Device", "20240411", }; rt_align(4) static struct uvc_vc_noep_descriptor vc_desc = { #ifdef RT_USB_DEVICE_COMPOSITE { USB_DESC_LENGTH_IAD, USB_DESC_TYPE_IAD, USB_DYNAMIC, 0x02, USB_CLASS_VIDEO, VIDEO_SC_VIDEO_INTERFACE_COLLECTION, 0x00, 0x00, }, #endif /* Interface Descriptor */ { USB_DESC_LENGTH_INTERFACE, USB_DESC_TYPE_INTERFACE, USB_DYNAMIC, 0x00, 0x00, USB_CLASS_VIDEO, VIDEO_SC_VIDEOCONTROL, VIDEO_PC_PROTOCOL_UNDEFINED, #ifdef RT_USB_DEVICE_COMPOSITE UVC_INTF_STR_INDEX, #else 0x00, #endif }, /* Header Descriptor */ { sizeof(struct video_cs_if_vc_header_descriptor), //0x0d, VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, VIDEO_VC_HEADER_DESCRIPTOR_SUBTYPE, 0x0100, VIDEO_VC_TERMINAL_LEN, 48000000, 0x01, {0x01}, }, /* Input Terminal Descriptor */ { sizeof(struct video_cs_if_vc_input_terminal_descriptor), //0x12, VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE, 0x01, VIDEO_ITT_CAMERA, 0x00, 0x00, 0x0000, 0x0000, 0x0000, 0x03, {0x00, 0x00, 0x00}, }, /* Processing Unit Descriptor */ { sizeof(struct video_cs_if_vc_processing_unit_descriptor), //0x0c, VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE, 0x02, 0x01, 0x0000, 0x02, {0xFF,0xFF}, 0x00, 0x00, }, /* Output Terminal Descriptor */ { sizeof(struct video_cs_if_vc_output_terminal_descriptor), //0x09, VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE, 0x03, VIDEO_TT_STREAMING, 0x00, 0x02, 0x00, }, }; #define VIDEO_VS_INPUT_HEADER_DESC_LEN (sizeof(struct video_cs_if_vs_input_header_descriptor)) #if (VIDEO_MJPEG_bNumFrameDescriptors >= 1) #define VIDEO_MJPEG_HEADER_LEN (sizeof(struct video_cs_if_vs_format_mjpeg_descriptor) + \ sizeof(struct video_cs_if_vs_frame_mjpeg_descriptor) * VIDEO_MJPEG_bNumFrameDescriptors) #else #define VIDEO_MJPEG_HEADER_LEN 0 #endif #if (VIDEO_H26x__bNumFrameDescriptors >= 1) #define VIDEO_H26X_HEADER_LEN (sizeof(struct video_cs_if_vs_format_h26x_descriptor) + \ sizeof(struct video_cs_if_vs_frame_h26x_descriptor) * VIDEO_H26x__bNumFrameDescriptors) #else #define VIDEO_H26X_HEADER_LEN 0 #endif #define VIDEO_VS_INPUT_HEADER_wTotalLength (VIDEO_VS_INPUT_HEADER_DESC_LEN + VIDEO_MJPEG_HEADER_LEN + VIDEO_H26X_HEADER_LEN) #define VIDEO_VS_INPUT_HEADER_bNumFormats ( ((VIDEO_MJPEG_bNumFrameDescriptors) ? 1 : 0) + \ ((VIDEO_H26x__bNumFrameDescriptors) ? 1 : 0) ) #define VIDEO_VS_INPUT_HEADER_bControlSize ((VIDEO_VS_INPUT_HEADER_bNumFormats >= 2) ? 1 : 2) rt_align(4) static struct uvc_vs_noep_descriptor vs_noep_desc = { /* Interface Descriptor */ { USB_DESC_LENGTH_INTERFACE, USB_DESC_TYPE_INTERFACE, 0x01, 0x00, 0x00, USB_CLASS_VIDEO, VIDEO_SC_VIDEOSTREAMING, 0x00, 0x00, }, /* Header Descriptor */ { sizeof(struct video_cs_if_vs_input_header_descriptor), //0x0d, VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, VIDEO_VS_INPUT_HEADER_DESCRIPTOR_SUBTYPE, VIDEO_VS_INPUT_HEADER_bNumFormats, VIDEO_VS_INPUT_HEADER_wTotalLength, VIDEO_IN_EP, 0x00, 0x03, 0x00, 0x00, 0x00, VIDEO_VS_INPUT_HEADER_bControlSize, {0x00,0x00},/* bmaControls : No VideoStreaming specific controls are supported.*/ }, #if VIDEO_MJPEG_bNumFrameDescriptors >= 1 /* VS Format Descriptor */ { sizeof(struct video_cs_if_vs_format_mjpeg_descriptor), //0x0b, VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, VIDEO_VS_FORMAT_MJPEG_DESCRIPTOR_SUBTYPE, 0x01, VIDEO_MJPEG_bNumFrameDescriptors, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, }, /* VS Frame 640x360 Descriptor */ { sizeof(struct video_cs_if_vs_frame_mjpeg_descriptor), //0x1a, VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, VIDEO_VS_FRAME_MJPEG_DESCRIPTOR_SUBTYPE, 0x01, 0x00, VIDEO_MJPEG_Frame1_WIDTH, VIDEO_MJPEG_Frame1_HEIGHT, MIN_BIT_RATE(VIDEO_MJPEG_Frame1_WIDTH,VIDEO_MJPEG_Frame1_HEIGHT), MAX_BIT_RATE(VIDEO_MJPEG_Frame1_WIDTH,VIDEO_MJPEG_Frame1_HEIGHT), MAX_FRAME_SIZE(VIDEO_MJPEG_Frame1_WIDTH,VIDEO_MJPEG_Frame1_HEIGHT), INTERVAL, 0x01, {INTERVAL}, }, #endif #if VIDEO_MJPEG_bNumFrameDescriptors >= 2 /* VS Frame 1280x720 Descriptor */ { sizeof(struct video_cs_if_vs_frame_mjpeg_descriptor), //0x1a, VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, VIDEO_VS_FRAME_MJPEG_DESCRIPTOR_SUBTYPE, 0x02, 0x00, VIDEO_MJPEG_Frame2_WIDTH, VIDEO_MJPEG_Frame2_HEIGHT, MIN_BIT_RATE(VIDEO_MJPEG_Frame2_WIDTH,VIDEO_MJPEG_Frame2_HEIGHT), MAX_BIT_RATE(VIDEO_MJPEG_Frame2_WIDTH,VIDEO_MJPEG_Frame2_HEIGHT), MAX_FRAME_SIZE(VIDEO_MJPEG_Frame2_WIDTH,VIDEO_MJPEG_Frame2_HEIGHT), INTERVAL, 0x01, {INTERVAL}, }, #endif #if VIDEO_H26x__bNumFrameDescriptors >= 1 /* VS H264 Format Descriptor */ { sizeof(struct video_cs_if_vs_format_h26x_descriptor), //bLength VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, //bDescriptorType VIDEO_VS_FORMAT_FRAME_BASED_DESCRIPTOR_SUBTYPE, //bDescriptorSubType 0x02, //bFormatIndex VIDEO_H26x__bNumFrameDescriptors, //bNumFrameDescriptors {VIDEO_GUID_H264}, //guidFormat[16] 0x00, //bBitsPerPixel 0x01, //bDefaultFrameIndex 0x00, //bAspectRatioX 0x00, //bAspectRatioY 0x00, //bmInterfaceFlags 0x00, //bCopyProtect 0x01, //bVariableSize }, /* VS Frame 1280x720 Descriptor */ { sizeof(struct video_cs_if_vs_frame_h26x_descriptor), //bLength VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, //bDescriptorType VIDEO_VS_FRAME_FRAME_BASED_DESCRIPTOR_SUBTYPE, //bDescriptorSubType 0x01, //bFrameIndex 0x00, //bmCapabilities VIDEO_H26x_Frame1_WIDTH, //wWidth VIDEO_H26x_Frame1_HEIGHT, //wHeight MIN_BIT_RATE(VIDEO_H26x_Frame1_WIDTH, VIDEO_H26x_Frame1_HEIGHT), //dwMinBitRate MAX_BIT_RATE(VIDEO_H26x_Frame1_WIDTH, VIDEO_H26x_Frame1_HEIGHT), //dwMaxBitRate INTERVAL, //dwDefaultFrameInterval 0x01, //bFrameIntervalType 0x00, //dwBytesPerLine {INTERVAL}, //dwFrameInterval }, #endif #if VIDEO_H26x__bNumFrameDescriptors >= 2 /* VS Frame 1920x1080 Descriptor */ { sizeof(struct video_cs_if_vs_frame_h26x_descriptor), //bLength VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE, //bDescriptorType VIDEO_VS_FRAME_FRAME_BASED_DESCRIPTOR_SUBTYPE, //bDescriptorSubType 0x02, //bFrameIndex 0x00, //bmCapabilities VIDEO_H26x_Frame2_WIDTH, //wWidth VIDEO_H26x_Frame2_HEIGHT, //wHeight MIN_BIT_RATE(VIDEO_H26x_Frame2_WIDTH, VIDEO_H26x_Frame2_HEIGHT), //dwMinBitRate MAX_BIT_RATE(VIDEO_H26x_Frame2_WIDTH, VIDEO_H26x_Frame2_HEIGHT), //dwMaxBitRate INTERVAL, //dwDefaultFrameInterval 0x01, //bFrameIntervalType 0x00, //dwBytesPerLine {INTERVAL}, //dwFrameInterval }, #endif }; rt_align(4) static struct uvc_vs_ep_descriptor vs_ep_desc = { /* Interface Descriptor */ { USB_DESC_LENGTH_INTERFACE, USB_DESC_TYPE_INTERFACE, 0x01, 0x01, 0x01, USB_CLASS_VIDEO, VIDEO_SC_VIDEOSTREAMING, 0x00, 0x00, }, /* Endpoint Descriptor */ { USB_DESC_LENGTH_ENDPOINT, USB_DESC_TYPE_ENDPOINT, USB_DYNAMIC | USB_DIR_IN, 0x05, VIDEO_PACKET_SIZE, 0x01, }, }; static rt_err_t rt_usbd_video_send_msg(struct usbd_video_msg *msg) { return rt_mq_send(&g_usbd_video[0].video_mq, (void*)msg, sizeof(struct usbd_video_msg)); } static rt_err_t rt_usbd_video_recv_msg(struct usbd_video_msg *msg) { return rt_mq_recv(&g_usbd_video[0].video_mq, msg, sizeof(struct usbd_video_msg), 100); } static rt_err_t _ep0_cmd_handler(udevice_t device, rt_size_t size) { struct usbd_video_msg msg; LOG_D("_ep0_cmd_handler"); if(rt_usbd_video_recv_msg(&msg) < 0) { os_printf("%s %d: Not recv msg!\n"); } else { if((msg.flush_info_addr != RT_NULL) && (msg.dma_buff != RT_NULL) && (msg.copy_len != 0)) { memcpy(msg.flush_info_addr, msg.dma_buff, msg.copy_len); } } dcd_ep0_send_status(device->dcd); return RT_EOK; } static rt_bool_t usbd_video_control_request_handler(rt_uint8_t busid, ufunction_t func, ureq_t setup) { rt_uint8_t control_selector = (rt_uint8_t)(setup->wValue >> 8); static rt_uint8_t buf[26] __attribute__((aligned(4))); os_printf("%s %d\n",__FUNCTION__,__LINE__); switch (control_selector) { case VIDEO_VC_VIDEO_POWER_MODE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: break; case VIDEO_REQUEST_GET_CUR: break; case VIDEO_REQUEST_GET_INFO: break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_VC_REQUEST_ERROR_CODE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_GET_CUR: buf[0] = 0x06; rt_usbd_ep0_write(func->device, (rt_uint8_t *)buf, 1); break; case VIDEO_REQUEST_GET_INFO: break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; default: break; } return 0; } static rt_bool_t usbd_video_control_unit_terminal_request_handler(rt_uint8_t busid, ufunction_t func, ureq_t setup) { rt_uint8_t entity_id = (rt_uint8_t)(setup->wIndex >> 8); rt_uint8_t control_selector = (rt_uint8_t)(setup->wValue >> 8); struct usbd_video_msg msg; static rt_uint8_t data[26] __attribute__((aligned(4))); for (rt_uint8_t i = 0; i < 3; i++) { struct video_entity_info *entity_info = &g_usbd_video[busid].info[i]; if (entity_info->bEntityId == entity_id) { switch (entity_info->bDescriptorSubtype) { case VIDEO_VC_HEADER_DESCRIPTOR_SUBTYPE: break; case VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE: if (entity_info->wTerminalType == VIDEO_ITT_CAMERA) { switch (control_selector) { case VIDEO_CT_AE_MODE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_GET_CUR: data[0] = 0x08; rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_GET_CUR: { rt_uint32_t dwExposureTimeAbsolute = 2500; memcpy(data, (rt_uint8_t *)&dwExposureTimeAbsolute, 4); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 4); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint32_t dwExposureTimeAbsolute = 5; //0.0005sec memcpy(data, (rt_uint8_t *)&dwExposureTimeAbsolute, 4); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 4); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint32_t dwExposureTimeAbsolute = 2500; //0.2500sec memcpy(data, (rt_uint8_t *)&dwExposureTimeAbsolute, 4); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 4); } break; case VIDEO_REQUEST_GET_RES: { rt_uint32_t dwExposureTimeAbsolute = 5; //0.0005sec memcpy(data, (rt_uint8_t *)&dwExposureTimeAbsolute, 4); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 4); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint32_t dwExposureTimeAbsolute = 2500; //0.2500sec memcpy(data, (rt_uint8_t *)&dwExposureTimeAbsolute, 4); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 4); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_CT_FOCUS_ABSOLUTE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wFocusAbsolute = 0x0080; memcpy(data, (rt_uint8_t *)&wFocusAbsolute, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wFocusAbsolute = 0; memcpy(data, (rt_uint8_t *)&wFocusAbsolute, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wFocusAbsolute = 0x00ff; memcpy(data, (rt_uint8_t *)&wFocusAbsolute, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wFocusAbsolute = 0x0001; memcpy(data, (rt_uint8_t *)&wFocusAbsolute, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wFocusAbsolute = 0x0080; memcpy(data, (rt_uint8_t *)&wFocusAbsolute, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_CT_ZOOM_ABSOLUTE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wObjectiveFocalLength = 0x0064; memcpy(data, (rt_uint8_t *)&wObjectiveFocalLength, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wObjectiveFocalLength = 0x0064; memcpy(data, (rt_uint8_t *)&wObjectiveFocalLength, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wObjectiveFocalLength = 0x00c8; memcpy(data, (rt_uint8_t *)&wObjectiveFocalLength, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wObjectiveFocalLength = 0x0001; memcpy(data, (rt_uint8_t *)&wObjectiveFocalLength, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wObjectiveFocalLength = 0x0064; memcpy(data, (rt_uint8_t *)&wObjectiveFocalLength, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_CT_ROLL_ABSOLUTE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wRollAbsolute = 0x0000; memcpy(data, (rt_uint8_t *)&wRollAbsolute, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wRollAbsolute = 0x0000; memcpy(data, (rt_uint8_t *)&wRollAbsolute, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wRollAbsolute = 0x00ff; memcpy(data, (rt_uint8_t *)&wRollAbsolute, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wRollAbsolute = 0x0001; memcpy(data, (rt_uint8_t *)&wRollAbsolute, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wRollAbsolute = 0x0000; memcpy(data, (rt_uint8_t *)&wRollAbsolute, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_CT_FOCUS_AUTO_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wFocusAuto = 0x0000; memcpy(data, (rt_uint8_t *)&wFocusAuto, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; default: LOG_D("Unhandled Video Class control selector 0x%02x\r\n", control_selector); return -1; } } else { LOG_D("Unhandled Video Class wTerminalType 0x%02x\r\n", entity_info->wTerminalType); return -2; } break; case VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE: break; case VIDEO_VC_SELECTOR_UNIT_DESCRIPTOR_SUBTYPE: break; case VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE: switch (control_selector) { case VIDEO_PU_BACKLIGHT_COMPENSATION_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wBacklightCompensation; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 2; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wBacklightCompensation = g_usbd_video[busid].process_unit_info.wBacklightCompensation; memcpy(data, (rt_uint8_t *)&wBacklightCompensation, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wBacklightCompensation = 0; memcpy(data, (rt_uint8_t *)&wBacklightCompensation, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wBacklightCompensation = 8; memcpy(data, (rt_uint8_t *)&wBacklightCompensation, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wBacklightCompensation = 1; memcpy(data, (rt_uint8_t *)&wBacklightCompensation, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wBacklightCompensation = 4; memcpy(data, (rt_uint8_t *)&wBacklightCompensation, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_BRIGHTNESS_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { //rt_uint16_t wBrightness = (rt_uint16_t)(*data)[1] << 8 | (rt_uint16_t)(*data)[0]; rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wBrightness; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 2; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wBrightness = g_usbd_video[busid].process_unit_info.wBrightness; memcpy(data, (rt_uint8_t *)&wBrightness, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wBrightness = 0x0001; memcpy(data, (rt_uint8_t *)&wBrightness, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wBrightness = 0x00ff; memcpy(data, (rt_uint8_t *)&wBrightness, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wBrightness = 0x0001; memcpy(data, (rt_uint8_t *)&wBrightness, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wBrightness = 0x0080; memcpy(data, (rt_uint8_t *)&wBrightness, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_CONTRAST_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wContrast; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 2; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wContrast = g_usbd_video[busid].process_unit_info.wContrast; memcpy(data, (rt_uint8_t *)&wContrast, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wContrast = 0x0001; memcpy(data, (rt_uint8_t *)&wContrast, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wContrast = 0x00ff; memcpy(data, (rt_uint8_t *)&wContrast, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wContrast = 0x0001; memcpy(data, (rt_uint8_t *)&wContrast, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wContrast = 0x0080; memcpy(data, (rt_uint8_t *)&wContrast, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_HUE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wHue; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 2; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wHue = g_usbd_video[busid].process_unit_info.wHue; memcpy(data, (rt_uint8_t *)&wHue, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wHue = 0x0001; memcpy(data, (rt_uint8_t *)&wHue, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wHue = 0x00ff; memcpy(data, (rt_uint8_t *)&wHue, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wHue = 0x0001; memcpy(data, (rt_uint8_t *)&wHue, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wHue = 0x0080; memcpy(data, (rt_uint8_t *)&wHue, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_HUE_AUTO_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wHue_Auto; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 1; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wHue_Auto = g_usbd_video[busid].process_unit_info.wHue_Auto; memcpy(data, (rt_uint8_t *)&wHue_Auto, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wHue_Auto = 0x0001; memcpy(data, (rt_uint8_t *)&wHue_Auto, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: { data[0] = 0x0; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wHue_Auto = 0x0; memcpy(data, (rt_uint8_t *)&wHue_Auto, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_SATURATION_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wSaturation; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 2; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wSaturation = g_usbd_video[busid].process_unit_info.wSaturation; memcpy(data, (rt_uint8_t *)&wSaturation, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wSaturation = 0x0001; memcpy(data, (rt_uint8_t *)&wSaturation, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wSaturation = 0x00ff; memcpy(data, (rt_uint8_t *)&wSaturation, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wSaturation = 0x0001; memcpy(data, (rt_uint8_t *)&wSaturation, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wSaturation = 0x0080; memcpy(data, (rt_uint8_t *)&wSaturation, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_SHARPNESS_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wSharpness; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 2; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wSharpness = g_usbd_video[busid].process_unit_info.wSharpness; memcpy(data, (rt_uint8_t *)&wSharpness, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wSharpness = 0x0001; memcpy(data, (rt_uint8_t *)&wSharpness, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wSharpness = 0x00ff; memcpy(data, (rt_uint8_t *)&wSharpness, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wSharpness = 0x0001; memcpy(data, (rt_uint8_t *)&wSharpness, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wSharpness = 0x0080; memcpy(data, (rt_uint8_t *)&wSharpness, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_GAMMA_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wGamma; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 2; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wGamma = g_usbd_video[busid].process_unit_info.wGamma; memcpy(data, (rt_uint8_t *)&wGamma, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wGamma = 0x0001; memcpy(data, (rt_uint8_t *)&wGamma, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wGamma = 0x0064; memcpy(data, (rt_uint8_t *)&wGamma, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wGamma = 0x0001; memcpy(data, (rt_uint8_t *)&wGamma, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wGamma = 0x0032; memcpy(data, (rt_uint8_t *)&wGamma, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_GAIN_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wGain; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 2; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wGain = g_usbd_video[busid].process_unit_info.wGain; memcpy(data, (rt_uint8_t *)&wGain, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wGain = 0; memcpy(data, (rt_uint8_t *)&wGain, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wGain = 255; memcpy(data, (rt_uint8_t *)&wGain, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wGain = 1; memcpy(data, (rt_uint8_t *)&wGain, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wGain = 255; memcpy(data, (rt_uint8_t *)&wGain, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_POWER_LINE_FREQUENCY_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.bPowerLineFrequency; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 1; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint8_t bPowerLineFrequency = g_usbd_video[busid].process_unit_info.bPowerLineFrequency; memcpy(data, (rt_uint8_t *)&bPowerLineFrequency, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint8_t bPowerLineFrequency = 1; memcpy(data, (rt_uint8_t *)&bPowerLineFrequency, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint8_t bPowerLineFrequency = 2; memcpy(data, (rt_uint8_t *)&bPowerLineFrequency, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_RES: { rt_uint8_t bPowerLineFrequency = 1; memcpy(data, (rt_uint8_t *)&bPowerLineFrequency, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint8_t bPowerLineFrequency = 1; memcpy(data, (rt_uint8_t *)&bPowerLineFrequency, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_WHITE_BALANCE_TEMPERATURE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wWhiteBalance_Temprature; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 2; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wWhiteBalance_Temprature = g_usbd_video[busid].process_unit_info.wWhiteBalance_Temprature; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Temprature, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wWhiteBalance_Temprature = 300; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Temprature, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wWhiteBalance_Temprature = 600; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Temprature, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wWhiteBalance_Temprature = 1; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Temprature, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: { data[0] = 0x03; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wWhiteBalance_Temprature = 417; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Temprature, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wWhiteBalance_Temprature_Auto; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 1; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wWhiteBalance_Temprature_Auto = g_usbd_video[busid].process_unit_info.wWhiteBalance_Temprature_Auto; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Temprature_Auto, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_INFO: { data[0] = 0x0; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wWhiteBalance_Temprature_Auto = 0; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Temprature_Auto, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_WHITE_BALANCE_COMPONENT_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wWhiteBalance_Component; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 4; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint32_t wWhiteBalance_Component = g_usbd_video[busid].process_unit_info.wWhiteBalance_Component; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Component, 4); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 4); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint32_t wWhiteBalance_Component = 5; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Component, 4); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 4); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint32_t wWhiteBalance_Component = 5; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Component, 4); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 4); } break; case VIDEO_REQUEST_GET_RES: { rt_uint32_t wWhiteBalance_Component = 5; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Component, 4); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 4); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x05; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint32_t wWhiteBalance_Component = 5; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Component, 4); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 4); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wWhiteBalance_Component_Auto; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 1; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wWhiteBalance_Component_Auto = g_usbd_video[busid].process_unit_info.wWhiteBalance_Component_Auto; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Component_Auto, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_INFO: { data[0] = 0x0; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wWhiteBalance_Component_Auto = 0; memcpy(data, (rt_uint8_t *)&wWhiteBalance_Component_Auto, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_DIGITAL_MULTIPLIER_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wDigital_Multiplier; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 2; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint16_t wDigital_Multiplier = g_usbd_video[busid].process_unit_info.wDigital_Multiplier; memcpy(data, (rt_uint8_t *)&wDigital_Multiplier, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wDigital_Multiplier = 5; memcpy(data, (rt_uint8_t *)&wDigital_Multiplier, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wDigital_Multiplier = 5; memcpy(data, (rt_uint8_t *)&wDigital_Multiplier, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wDigital_Multiplier = 5; memcpy(data, (rt_uint8_t *)&wDigital_Multiplier, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: data[0] = 0x05; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wDigital_Multiplier = 5; memcpy(data, (rt_uint8_t *)&wDigital_Multiplier, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: { rt_usbd_ep0_read(func->device, (rt_uint8_t *)&data, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].process_unit_info.wDigital_Multiplier_Limit; msg.dma_buff = (rt_uint8_t *)&data; msg.copy_len = 1; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); } break; case VIDEO_REQUEST_GET_CUR: { rt_uint8_t wDigital_Multiplier_Limit = g_usbd_video[busid].process_unit_info.wDigital_Multiplier_Limit; memcpy(data, (rt_uint8_t *)&wDigital_Multiplier_Limit, 1); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_MIN: { rt_uint16_t wDigital_Multiplier_Limit = 0; memcpy(data, (rt_uint8_t *)&wDigital_Multiplier_Limit, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_MAX: { rt_uint16_t wDigital_Multiplier_Limit = 5; memcpy(data, (rt_uint8_t *)&wDigital_Multiplier_Limit, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_INFO: { data[0] = 0x0; //struct video_camera_capabilities rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 1); } break; case VIDEO_REQUEST_GET_RES: { rt_uint16_t wDigital_Multiplier_Limit = 0; memcpy(data, (rt_uint8_t *)&wDigital_Multiplier_Limit, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; case VIDEO_REQUEST_GET_DEF: { rt_uint16_t wDigital_Multiplier_Limit = 0; memcpy(data, (rt_uint8_t *)&wDigital_Multiplier_Limit, 2); rt_usbd_ep0_write(func->device, (rt_uint8_t *)data, 2); } break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; default: g_usbd_video[busid].error_code = 0x06; LOG_D("Unhandled Video Class control selector 0x%02x\r\n", control_selector); return -1; } break; case VIDEO_VC_EXTENSION_UNIT_DESCRIPTOR_SUBTYPE: break; case VIDEO_VC_ENCODING_UNIT_DESCRIPTOR_SUBTYPE: break; default: break; } } } return 0; } static rt_uint32_t usbd_video_mjpeg_payload_fill(rt_uint8_t busid, rt_uint8_t *input, rt_uint32_t input_len, rt_uint8_t *output, rt_uint32_t *out_len) { rt_uint32_t packets; rt_uint32_t last_packet_size; rt_uint32_t picture_pos = 0; static rt_uint8_t uvc_header[2] = { 0x02, 0x80 }; packets = (input_len + (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2) ) / (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2); //os_printf("packets:%d input len:%d dwMaxPayloadSize:%d \n",packets,input_len,g_usbd_video[busid].probe.dwMaxPayloadTransferSize); last_packet_size = input_len - ((packets - 1) * (g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2)); for (rt_size_t i = 0; i < packets; i++) { output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i] = uvc_header[0]; output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i + 1] = uvc_header[1]; if (i == (packets - 1)) { hw_memcpy(&output[2 + g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i], &input[picture_pos], last_packet_size); output[g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i + 1] |= (1 << 1); } else { hw_memcpy(&output[2 + g_usbd_video[busid].probe.dwMaxPayloadTransferSize * i], &input[picture_pos], g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2); picture_pos += g_usbd_video[busid].probe.dwMaxPayloadTransferSize - 2; } } uvc_header[1] ^= 1; *out_len = (input_len + 2 * packets); return packets; } static rt_uint32_t usbd_video_mjpeg_payload_header_fill(rt_uint8_t *input, rt_uint32_t input_len, rt_uint8_t *output, rt_uint8_t *uvc_header, rt_uint8_t uvc_header_len) { //os_printf("uvc header len:%d\n",uvc_header_len); if(input_len < VIDEO_PACKET_SIZE - uvc_header_len) //2为header的长度 { output[0] = uvc_header[0]; output[1] = uvc_header[1]; output[1] |= (1 << 1); os_memcpy(&output[2], input, input_len); uvc_header[1] ^= 1; } else { output[0] = uvc_header[0]; output[1] = uvc_header[1]; os_memcpy(&output[2], input, input_len); } return input_len + uvc_header_len; } static rt_err_t usbd_video_open(rt_uint8_t busid, ufunction_t func) { os_printf("usbd video open\r\n"); os_printf("g_usbd_video[busid].probe.bFormatIndex:%d\n",g_usbd_video[busid].probe.bFormatIndex); switch((g_usbd_video[busid].probe.bFormatIndex)) { case 1: { switch(g_usbd_video[busid].probe.bFrameIndex){ #if VIDEO_MJPEG_bNumFrameDescriptors >= 1 case 1: os_printf("MJPEG set interface 360p\r\n"); if (g_usbd_video[busid].ops.mjpeg_set_resolution) { g_usbd_video[busid].ops.mjpeg_set_resolution(1280, 720, VIDEO_MJPEG_Frame1_WIDTH, VIDEO_MJPEG_Frame1_HEIGHT, USBD_VIDEO_OPEN); } break; #endif #if VIDEO_MJPEG_bNumFrameDescriptors >= 2 case 2: os_printf("MJPEG set interface 720p\r\n"); if (g_usbd_video[busid].ops.mjpeg_set_resolution) { g_usbd_video[busid].ops.mjpeg_set_resolution(1280, 720, VIDEO_MJPEG_Frame2_WIDTH, VIDEO_MJPEG_Frame2_HEIGHT, USBD_VIDEO_OPEN); } break; #endif default: break; } } break; case 2: { switch(g_usbd_video[busid].probe.bFrameIndex){ #if VIDEO_H26x__bNumFrameDescriptors >= 1 case 1: os_printf("H264 set interface 720p\r\n"); if (g_usbd_video[busid].ops.h264_set_resolution) { g_usbd_video[busid].ops.h264_set_resolution((uint32_t)RT_NULL, (uint32_t)RT_NULL, VIDEO_H26x_Frame1_WIDTH, VIDEO_H26x_Frame1_HEIGHT, USBD_VIDEO_OPEN); } break; #endif #if VIDEO_H26x__bNumFrameDescriptors >= 2 case 2: os_printf("H264 set interface 1080p\r\n"); if (g_usbd_video[busid].ops.h264_set_resolution) { g_usbd_video[busid].ops.h264_set_resolution((uint32_t)RT_NULL, (uint32_t)RT_NULL, VIDEO_H26x_Frame2_WIDTH, VIDEO_H26x_Frame2_HEIGHT, USBD_VIDEO_OPEN); } break; #endif default: break; } } break; } usbd_uvc.open_count = 0; usbd_uvc.open_count++; rt_event_send(usbd_uvc.event, EVENT_VIDEO_START | EVENT_VIDEO_DATA); return 0; } static rt_err_t usbd_video_close(rt_uint8_t busid, ufunction_t func) { os_printf("usbd video close\r\n"); switch((g_usbd_video[busid].probe.bFormatIndex)) { case 1: { switch(g_usbd_video[busid].probe.bFrameIndex){ #if VIDEO_MJPEG_bNumFrameDescriptors >= 1 case 1: os_printf("MJPEG set interface 360p\r\n"); if (g_usbd_video[busid].ops.mjpeg_set_resolution) { g_usbd_video[busid].ops.mjpeg_set_resolution((uint32_t)RT_NULL, (uint32_t)RT_NULL, (uint32_t)RT_NULL, (uint32_t)RT_NULL, USBD_VIDEO_CLOSE); } break; #endif #if VIDEO_MJPEG_bNumFrameDescriptors >= 2 case 2: os_printf("MJPEG set interface 720p\r\n"); if (g_usbd_video[busid].ops.mjpeg_set_resolution) { g_usbd_video[busid].ops.mjpeg_set_resolution((uint32_t)RT_NULL, (uint32_t)RT_NULL, (uint32_t)RT_NULL, (uint32_t)RT_NULL, USBD_VIDEO_CLOSE); } break; #endif default: break; } } break; case 2: { switch(g_usbd_video[busid].probe.bFrameIndex){ #if VIDEO_H26x__bNumFrameDescriptors >= 1 case 1: os_printf("H264 set interface 720p\r\n"); if (g_usbd_video[busid].ops.h264_set_resolution) { g_usbd_video[busid].ops.h264_set_resolution((uint32_t)RT_NULL, (uint32_t)RT_NULL, (uint32_t)RT_NULL, (uint32_t)RT_NULL, USBD_VIDEO_CLOSE); } break; #endif #if VIDEO_H26x__bNumFrameDescriptors >= 2 case 2: os_printf("H264 set interface 1080p\r\n"); if (g_usbd_video[busid].ops.h264_set_resolution) { g_usbd_video[busid].ops.h264_set_resolution((uint32_t)RT_NULL, (uint32_t)RT_NULL, (uint32_t)RT_NULL, (uint32_t)RT_NULL, USBD_VIDEO_CLOSE); } break; #endif default: break; } } break; } usbd_uvc.open_count--; rt_event_send(usbd_uvc.event, EVENT_VIDEO_STOP); return 0; } static void usbd_video_process_unit_info_init(rt_uint8_t busid) { g_usbd_video[busid].process_unit_info.wWhiteBalance_Component = 5; g_usbd_video[busid].process_unit_info.wBacklightCompensation = 0x0004; g_usbd_video[busid].process_unit_info.wBrightness = 0x0080; g_usbd_video[busid].process_unit_info.wContrast = 0x0080; g_usbd_video[busid].process_unit_info.wHue = 0x0080; g_usbd_video[busid].process_unit_info.wHue_Auto = 0; g_usbd_video[busid].process_unit_info.wSaturation = 0x0001; g_usbd_video[busid].process_unit_info.wSharpness = 0x0001; g_usbd_video[busid].process_unit_info.wGamma = 0x0032; g_usbd_video[busid].process_unit_info.wGain = 255; g_usbd_video[busid].process_unit_info.wWhiteBalance_Temprature = 417; g_usbd_video[busid].process_unit_info.wWhiteBalance_Temprature_Auto = 0; g_usbd_video[busid].process_unit_info.wWhiteBalance_Component_Auto = 0; g_usbd_video[busid].process_unit_info.wDigital_Multiplier = 5; g_usbd_video[busid].process_unit_info.bPowerLineFrequency = 1; g_usbd_video[busid].process_unit_info.wDigital_Multiplier_Limit = 1; } static void usbd_video_probe_and_commit_controls_init(rt_uint8_t busid, rt_uint32_t dwFrameInterval, rt_uint32_t dwMaxVideoFrameSize, rt_uint32_t dwMaxPayloadTransferSize) { g_usbd_video[busid].probe.hintUnion.bmHint = 0x01; g_usbd_video[busid].probe.hintUnion1.bmHint = 0; g_usbd_video[busid].probe.bFormatIndex = 1; g_usbd_video[busid].probe.bFrameIndex = 1; g_usbd_video[busid].probe.dwFrameInterval = dwFrameInterval; g_usbd_video[busid].probe.wKeyFrameRate = 0; g_usbd_video[busid].probe.wPFrameRate = 0; g_usbd_video[busid].probe.wCompQuality = 0; g_usbd_video[busid].probe.wCompWindowSize = 0; g_usbd_video[busid].probe.wDelay = 0; g_usbd_video[busid].probe.dwMaxVideoFrameSize = dwMaxVideoFrameSize; g_usbd_video[busid].probe.dwMaxPayloadTransferSize = dwMaxPayloadTransferSize; g_usbd_video[busid].probe.dwClockFrequency = 0; g_usbd_video[busid].probe.bmFramingInfo = 0; g_usbd_video[busid].probe.bPreferedVersion = 0; g_usbd_video[busid].probe.bMinVersion = 0; g_usbd_video[busid].probe.bMaxVersion = 0; g_usbd_video[busid].commit.hintUnion.bmHint = 0x01; g_usbd_video[busid].commit.hintUnion1.bmHint = 0; g_usbd_video[busid].commit.bFormatIndex = 1; g_usbd_video[busid].commit.bFrameIndex = 1; g_usbd_video[busid].commit.dwFrameInterval = dwFrameInterval; g_usbd_video[busid].commit.wKeyFrameRate = 0; g_usbd_video[busid].commit.wPFrameRate = 0; g_usbd_video[busid].commit.wCompQuality = 0; g_usbd_video[busid].commit.wCompWindowSize = 0; g_usbd_video[busid].commit.wDelay = 0; g_usbd_video[busid].commit.dwMaxVideoFrameSize = dwMaxVideoFrameSize; g_usbd_video[busid].commit.dwMaxPayloadTransferSize = dwMaxPayloadTransferSize; g_usbd_video[busid].commit.dwClockFrequency = 0; g_usbd_video[busid].commit.bmFramingInfo = 0; g_usbd_video[busid].commit.bPreferedVersion = 0; g_usbd_video[busid].commit.bMinVersion = 0; g_usbd_video[busid].commit.bMaxVersion = 0; } static void usbd_video_init_intf(rt_uint8_t busid, rt_uint32_t dwFrameInterval, rt_uint32_t dwMaxVideoFrameSize, rt_uint32_t dwMaxPayloadTransferSize) { g_usbd_video[busid].info[0].bDescriptorSubtype = VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE; g_usbd_video[busid].info[0].bEntityId = 0x01; g_usbd_video[busid].info[0].wTerminalType = VIDEO_ITT_CAMERA; g_usbd_video[busid].info[1].bDescriptorSubtype = VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE; g_usbd_video[busid].info[1].bEntityId = 0x03; g_usbd_video[busid].info[1].wTerminalType = 0x00; g_usbd_video[busid].info[2].bDescriptorSubtype = VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE; g_usbd_video[busid].info[2].bEntityId = 0x02; g_usbd_video[busid].info[2].wTerminalType = 0x00; usbd_video_probe_and_commit_controls_init(busid, dwFrameInterval, dwMaxVideoFrameSize, dwMaxPayloadTransferSize); usbd_video_process_unit_info_init(busid); } static rt_err_t _vc_interface_as_handler(ufunction_t func, ureq_t setup) { RT_ASSERT(func != RT_NULL); RT_ASSERT(func->device != RT_NULL); RT_ASSERT(setup != RT_NULL); os_printf("_vc_interface_as_handler\n"); rt_uint8_t entity_id = (rt_uint8_t)(setup->wIndex >> 8); rt_uint8_t busid = 0; if (entity_id == 0) { return usbd_video_control_request_handler(busid, func, setup); /* Interface Control Requests */ } else { return usbd_video_control_unit_terminal_request_handler(busid, func, setup); /* Unit and Terminal Requests */ } return RT_EOK; } static rt_err_t _vs_interface_as_handler(ufunction_t func, ureq_t setup) { RT_ASSERT(func != RT_NULL); RT_ASSERT(func->device != RT_NULL); RT_ASSERT(setup != RT_NULL); struct usbd_video_msg msg; rt_uint8_t control_selector = (rt_uint8_t)(setup->wValue >> 8); rt_uint8_t busid = 0; static rt_uint8_t buf[26] __attribute__((aligned(4))); memset(buf, 0, 26); os_printf("_vs_interface_as_handler\n"); os_printf("request_type:%x bRequest:%d control_selector:%d\n",(setup->request_type & USB_REQ_TYPE_MASK), setup->bRequest,control_selector); #if 1 if ((setup->request_type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) { switch (setup->bRequest) { case USB_REQ_GET_INTERFACE: break; case USB_REQ_SET_INTERFACE: LOG_D("set interface handler"); if (setup->wValue == 1) { usbd_video_open(busid, func); } else if (setup->wValue == 0) { usbd_video_close(busid, func); } break; default: LOG_D("unknown uac request 0x%x", setup->bRequest); return -RT_ERROR; } }else{ switch (control_selector) { case VIDEO_VS_PROBE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: rt_usbd_ep0_read(func->device, (rt_uint8_t *)&buf, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)&g_usbd_video[busid].probe; msg.dma_buff = (rt_uint8_t *)&buf; msg.copy_len = 18; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); os_printf("SET CUR FormatIndex:%x FrameIndex:%x buf[2]:%x %x\n",g_usbd_video[busid].probe.bFormatIndex,g_usbd_video[busid].probe.bFrameIndex,buf[2],buf[3]); break; case VIDEO_REQUEST_GET_CUR: rt_usbd_ep0_write(func->device, (rt_uint8_t *)&g_usbd_video[busid].probe, setup->wLength); //os_printf("GET CUR payloadsize:%d\n",g_usbd_video[busid].probe.dwMaxPayloadTransferSize); break; case VIDEO_REQUEST_GET_MIN: case VIDEO_REQUEST_GET_MAX: case VIDEO_REQUEST_GET_RES: case VIDEO_REQUEST_GET_DEF: rt_usbd_ep0_write(func->device, (rt_uint8_t *)&g_usbd_video[busid].probe, setup->wLength); //os_printf("GET = payloadsize:%d\n",g_usbd_video[busid].probe.dwMaxPayloadTransferSize); break; case VIDEO_REQUEST_GET_LEN: buf[0] = sizeof(struct video_probe_and_commit_controls); rt_usbd_ep0_write(func->device, (rt_uint8_t *)buf, 1); break; case VIDEO_REQUEST_GET_INFO:; buf[0] = 0x03; rt_usbd_ep0_write(func->device, (rt_uint8_t *)buf, 1); break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_VS_COMMIT_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_SET_CUR: rt_usbd_ep0_read(func->device, (rt_uint8_t *)&g_usbd_video[busid].commit, setup->wLength, _ep0_cmd_handler); msg.flush_info_addr = (rt_uint8_t *)RT_NULL; msg.dma_buff = (rt_uint8_t *)RT_NULL; msg.copy_len = 0; msg.control_selector = control_selector; rt_usbd_video_send_msg(&msg); break; case VIDEO_REQUEST_GET_CUR: rt_usbd_ep0_write(func->device, (rt_uint8_t *)&g_usbd_video[busid].commit, setup->wLength); break; case VIDEO_REQUEST_GET_MIN: case VIDEO_REQUEST_GET_MAX: case VIDEO_REQUEST_GET_RES: case VIDEO_REQUEST_GET_DEF: rt_usbd_ep0_write(func->device, (rt_uint8_t *)&g_usbd_video[busid].commit, setup->wLength); break; case VIDEO_REQUEST_GET_LEN: buf[0] = sizeof(struct video_probe_and_commit_controls); rt_usbd_ep0_write(func->device, (rt_uint8_t *)buf, 1); break; case VIDEO_REQUEST_GET_INFO:; buf[0] = 0x03; rt_usbd_ep0_write(func->device, (rt_uint8_t *)buf, 1); break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; case VIDEO_VS_STREAM_ERROR_CODE_CONTROL: switch (setup->bRequest) { case VIDEO_REQUEST_GET_CUR: buf[0] = g_usbd_video[busid].error_code; rt_usbd_ep0_write(func->device, (rt_uint8_t *)buf, 1); break; case VIDEO_REQUEST_GET_INFO: buf[0] = 0x01; rt_usbd_ep0_write(func->device, (rt_uint8_t *)buf, 1); break; default: LOG_D("Unhandled Video Class bRequest 0x%02x\r\n", setup->bRequest); return -1; } break; default: break; } } #endif return RT_EOK; } static rt_err_t _ep_data_handler(ufunction_t func, rt_size_t size) { RT_ASSERT(func != RT_NULL); rt_event_send(usbd_uvc.event, EVENT_VIDEO_DATA); return RT_EOK; } static rt_err_t _function_enable(ufunction_t func) { RT_ASSERT(func != RT_NULL); rt_mq_init(&g_usbd_video[0].video_mq, "video_message", RT_NULL, 32, 16, RT_IPC_FLAG_FIFO); os_printf("uvc function enable"); return RT_EOK; } static rt_err_t _function_disable(ufunction_t func) { RT_ASSERT(func != RT_NULL); rt_mq_detach(&g_usbd_video[0].video_mq); os_printf("uvc function disable"); return RT_EOK; } static struct ufunction_ops ops = { _function_enable, _function_disable, RT_NULL, }; /** * This function will configure uvc descriptor. * * @param comm the communication interface number. * @param data the data interface number. * * @return RT_EOK on successful. */ static rt_err_t _uvc_descriptor_config(struct uvc_vc_noep_descriptor *vc, rt_uint8_t cintf_nr, struct uvc_vs_noep_descriptor *vs, rt_uint8_t sintf_nr) { vc->hdr_desc.baInterfaceNr[0] = sintf_nr; #ifdef RT_USB_DEVICE_COMPOSITE vc->iad_desc.bFirstInterface = cintf_nr; #endif return RT_EOK; } static void usbd_uvc_entry(void *parameter) { rt_uint32_t e; rt_uint32_t flen = 0; rt_uint32_t node_len = 0; rt_uint32_t out_len; rt_uint32_t packets; rt_uint32_t timeout = 0; rt_uint32_t index = 0; rt_uint8_t *frame_buf_addr = NULL; struct framebuff *get_f = NULL; struct ufunction *func = (struct ufunction *)parameter; static rt_uint8_t uvc_header[2] = { 0x02, 0x80 }; usbd_uvc.buffer = os_malloc(VIDEO_BUFFER_SZ); if (usbd_uvc.buffer == RT_NULL) { LOG_E("malloc failed\r\n"); goto __exit; } struct msi *s = msi_new(R_USBD_VIDEO, 2, NULL); if(s == RT_NULL) { LOG_D("open stream failed\n"); goto __exit; } usbd_uvc.recv_msi = s; while(1) { if (rt_event_recv(usbd_uvc.event, EVENT_VIDEO_START | EVENT_VIDEO_STOP, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 1000, &e) != RT_EOK) { printf("usbd video wait evevnt\r\n"); continue; } if (e & EVENT_VIDEO_START) { }else { continue; } LOG_D("video display start\r\n"); while(1){ if (get_f == NULL) { get_f = msi_get_fb(s, 0); } flen = 0; if(get_f) { // printf("U len : %d type:%d", get_f->len, get_f->stype); flen = get_f->len; node_len = get_f->len; frame_buf_addr = get_f->data; packets = 0; out_len = 0; while(flen) { if (rt_event_recv(usbd_uvc.event, EVENT_VIDEO_DATA | EVENT_VIDEO_STOP, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 1000, &e) != RT_EOK) { //一般为上位机软件视频处于暂停状态(主机未发出IN请求,从机等待DMA中断) os_printf(KERN_DEBUG"uvc_video_thread: recv event timeout\n"); } if (e & EVENT_VIDEO_STOP) { _os_printf("P"); flen = 0; index = 0; timeout = 0; break; } if (e & EVENT_VIDEO_DATA) { //printf("D"); if(flen > (VIDEO_PACKET_SIZE-sizeof(uvc_header))) { out_len = usbd_video_mjpeg_payload_header_fill((rt_uint8_t *)(frame_buf_addr + index), (VIDEO_PACKET_SIZE-sizeof(uvc_header)), usbd_uvc.buffer, (rt_uint8_t*)&uvc_header,sizeof(uvc_header)); //os_printf("1 out len:%d buffer:%x [0]:%x [1]:%x [2]:%x [3]:%x\n",out_len,frame_buf_addr + index, usbd_uvc.buffer[0], usbd_uvc.buffer[1],usbd_uvc.buffer[2],usbd_uvc.buffer[3]); usbd_uvc.ep->request.buffer = usbd_uvc.buffer; usbd_uvc.ep->request.size = VIDEO_PACKET_SIZE; usbd_uvc.ep->request.req_type = UIO_REQUEST_WRITE; rt_usbd_io_request(func->device, usbd_uvc.ep, &usbd_uvc.ep->request); flen -= (VIDEO_PACKET_SIZE-sizeof(uvc_header)); index += (VIDEO_PACKET_SIZE-sizeof(uvc_header)); }else{ out_len = usbd_video_mjpeg_payload_header_fill((rt_uint8_t *)(frame_buf_addr + index), flen, usbd_uvc.buffer, (rt_uint8_t*)&uvc_header,sizeof(uvc_header)); // os_printf("2 out len:%d buffer:%x [0]:%x [1]:%x [2]:%x [3]:%x\n",out_len,frame_buf_addr + index, usbd_uvc.buffer[0], usbd_uvc.buffer[1],usbd_uvc.buffer[2],usbd_uvc.buffer[3]); usbd_uvc.ep->request.buffer = usbd_uvc.buffer; usbd_uvc.ep->request.size = out_len; usbd_uvc.ep->request.req_type = UIO_REQUEST_WRITE; rt_usbd_io_request(func->device, usbd_uvc.ep, &usbd_uvc.ep->request); flen = 0; index = 0; break; } } } if(get_f) { msi_delete_fb(NULL, get_f); get_f = NULL; } timeout = 0; } else { timeout++; _os_printf("n"); os_sleep_ms(5); if(timeout > 100) { //通知设置分辨率的线程,可重新打开硬件msi s->enable = 0; rt_event_send(usbd_uvc.event, EVENT_VIDEO_COMPLITE); timeout = 0; break; } } } } __exit: if (usbd_uvc.buffer) { os_free(usbd_uvc.buffer); } if (s) { usbd_uvc.recv_msi = RT_NULL; msi_destroy(s); } } /** * This function will create a uvc function instance. * * @param device the usb device object. * * @return RT_EOK on successful. */ static ufunction_t rt_usbd_function_uvc_device_create(udevice_t device) { ufunction_t func; uintf_t intf_vc, intf_vs; ualtsetting_t setting_vs_noep; ualtsetting_t setting_vc, setting_vs_ep; struct uvc_vs_ep_descriptor *vs_ep_desc_t; /* parameter check */ RT_ASSERT(device != RT_NULL); #ifdef RT_USB_DEVICE_COMPOSITE rt_usbd_device_set_interface_string(device, UVC_INTF_STR_INDEX, _ustring[2]); #else /* set usb device string description */ rt_usbd_device_set_string(device, _ustring); #endif /* create a uac function */ func = rt_usbd_function_new(device, &dev_desc, &ops); //not support HS //rt_usbd_device_set_qualifier(device, &dev_qualifier); usbd_uvc_init(func); /* create interface */ intf_vc = rt_usbd_interface_new(device, _vc_interface_as_handler); //配置接口handler intf_vs = rt_usbd_interface_new(device, _vs_interface_as_handler); //配置接口handler // os_printf("===sizeof:%d %d %d\n",sizeof(struct uvc_vc_noep_descriptor),sizeof(struct uvc_vs_noep_descriptor),sizeof(struct uvc_vs_ep_descriptor)); /* create alternate setting */ setting_vc = rt_usbd_altsetting_new(sizeof(struct uvc_vc_noep_descriptor)); setting_vs_noep = rt_usbd_altsetting_new(sizeof(struct uvc_vs_noep_descriptor)); setting_vs_ep = rt_usbd_altsetting_new(sizeof(struct uvc_vs_ep_descriptor)); /* config desc in alternate setting */ rt_usbd_altsetting_config_descriptor(setting_vc, &vc_desc, (rt_off_t) & ((struct uvc_vc_noep_descriptor *)0)->intf_desc); // os_printf("intf_desc[0]:%x\n",&(vc_desc.intf_desc)); rt_usbd_altsetting_config_descriptor(setting_vs_noep, &vs_noep_desc, (rt_off_t) & ((struct uvc_vs_noep_descriptor *)0)->intf_desc); rt_usbd_altsetting_config_descriptor(setting_vs_ep, &vs_ep_desc, (rt_off_t) & ((struct uvc_vs_ep_descriptor *)0)->intf_desc); /* configure the uac interface descriptor */ _uvc_descriptor_config(setting_vc->desc, intf_vc->intf_num, setting_vs_noep->desc, intf_vs->intf_num); //_uac_samplerate_config(setting_vs_ep->desc, AUDIO_SAMPLERATE); /* create endpoint */ vs_ep_desc_t = (struct uvc_vs_ep_descriptor *)setting_vs_ep->desc; usbd_uvc.ep = rt_usbd_endpoint_new(&vs_ep_desc_t->ep_desc, _ep_data_handler); //配置端点handler /* add the endpoint to the alternate setting */ rt_usbd_altsetting_add_endpoint(setting_vs_ep, usbd_uvc.ep); /* add the alternate setting to the interface, then set default setting of the interface */ rt_usbd_interface_add_altsetting(intf_vc, setting_vc); rt_usbd_set_altsetting(intf_vc, 0); rt_usbd_interface_add_altsetting(intf_vs, setting_vs_noep); rt_usbd_interface_add_altsetting(intf_vs, setting_vs_ep); rt_usbd_set_altsetting(intf_vs, 0); /* add the interface to the uac function */ rt_usbd_function_add_interface(func, intf_vc); rt_usbd_function_add_interface(func, intf_vs); return func; } static void usbd_video_mjpeg_set_resolution(uint32_t in_weight, uint32_t in_height, uint32_t out_weight, uint32_t out_height, uint32_t en) { rt_uint32_t e; if(en) { if (usbd_uvc.cur_msi) { usbd_uvc.recv_msi->enable = 0; msi_put(usbd_uvc.cur_msi); //确保所有fb都被释放 struct framebuff *fb = NULL; do{ fb = msi_get_fb(usbd_uvc.cur_msi, 0); if(fb) msi_delete_fb(NULL, fb); }while(fb != NULL); msi_del_output(usbd_uvc.cur_msi, NULL, R_USBD_VIDEO); usbd_uvc.cur_msi = RT_NULL; os_sleep_ms(100); //等待 auto_h264 / auto_jpg work 完全停止 } //等待UVC接收msi完全释放fb if(rt_event_recv(usbd_uvc.event, EVENT_VIDEO_COMPLITE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 5000, &e) != RT_EOK) { //此处有可能触发超时,上位机重新打开摄像头可以恢复 os_printf("%s %d timeout\n",__FUNCTION__,__LINE__); return; } if((in_weight != out_weight) || (in_height != out_height)) { usbd_uvc.cur_msi = msi_find(AUTO_JPG,1); //VPP DATA1 按比例裁剪后的分辨率(默认1/2) msi_do_cmd(usbd_uvc.cur_msi, MSI_CMD_AUTO_JPG, MSI_AUTO_JPG_SWITCH_ENCODE_SRC, VPP_DATA1); } else { usbd_uvc.cur_msi = msi_find(AUTO_JPG,1); //VPP DATA0 摄像头原分辨率 msi_do_cmd(usbd_uvc.cur_msi, MSI_CMD_AUTO_JPG, MSI_AUTO_JPG_SWITCH_ENCODE_SRC, VPP_DATA0); } usbd_uvc.recv_msi->enable = 1; msi_add_output(usbd_uvc.cur_msi, NULL, R_USBD_VIDEO); } else { usbd_uvc.recv_msi->enable = 0; if (usbd_uvc.cur_msi) { msi_put(usbd_uvc.cur_msi); //确保所有fb都被释放 struct framebuff *fb = NULL; do{ fb = msi_get_fb(usbd_uvc.cur_msi, 0); if(fb) msi_delete_fb(NULL, fb); }while(fb != NULL); msi_del_output(usbd_uvc.cur_msi, NULL, R_USBD_VIDEO); } usbd_uvc.cur_msi = RT_NULL; } } static void usbd_video_h264_set_resolution(uint32_t in_weight, uint32_t in_height, uint32_t out_weight, uint32_t out_height, uint32_t en) { rt_uint32_t e; if(en) { if (usbd_uvc.cur_msi) { usbd_uvc.recv_msi->enable = 0; msi_put(usbd_uvc.cur_msi); //确保所有fb都被释放 struct framebuff *fb = NULL; do{ fb = msi_get_fb(usbd_uvc.cur_msi, 0); if(fb) msi_delete_fb(NULL, fb); }while(fb != NULL); msi_del_output(usbd_uvc.cur_msi, NULL, R_USBD_VIDEO); usbd_uvc.cur_msi = RT_NULL; os_sleep_ms(100); //等待 auto_h264 / auto_jpg work 完全停止 } //等待UVC接收msi完全释放fb if(rt_event_recv(usbd_uvc.event, EVENT_VIDEO_COMPLITE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 5000, &e) != RT_EOK) { //此处有可能触发超时,上位机重新打开摄像头可以恢复 os_printf("%s %d timeout\n",__FUNCTION__,__LINE__); return; } /* 此处可添加 H264 分辨率切换代码 ... */ usbd_uvc.cur_msi = msi_find(AUTO_H264, 1); //VPP DATA0 摄像头原分辨率 usbd_uvc.recv_msi->enable = 1; msi_add_output(usbd_uvc.cur_msi, NULL, R_USBD_VIDEO); } else { usbd_uvc.recv_msi->enable = 0; if (usbd_uvc.cur_msi) { msi_put(usbd_uvc.cur_msi); //确保所有fb都被释放 struct framebuff *fb = NULL; do{ fb = msi_get_fb(usbd_uvc.cur_msi, 0); if(fb) msi_delete_fb(NULL, fb); }while(fb != NULL); msi_del_output(usbd_uvc.cur_msi, NULL, R_USBD_VIDEO); } usbd_uvc.cur_msi = RT_NULL; } } static rt_bool_t usbd_uvc_init(struct ufunction *func) { rt_uint8_t busid = 0; rt_thread_t usbd_uvc_thread; g_usbd_video[busid].ops.mjpeg_set_resolution = usbd_video_mjpeg_set_resolution; g_usbd_video[busid].ops.h264_set_resolution = usbd_video_h264_set_resolution; usbd_uvc.cur_msi = RT_NULL; usbd_uvc.event = rt_event_create("usbd_uvc_event", RT_IPC_FLAG_FIFO); //保证第一次能够成功打开硬件 rt_event_send(usbd_uvc.event, EVENT_VIDEO_COMPLITE); usbd_uvc_thread = rt_thread_create("usbd_uvc_thread", usbd_uvc_entry, func, 1024, OS_TASK_PRIORITY_NORMAL, 10); if(usbd_uvc_thread != RT_NULL) rt_thread_startup(usbd_uvc_thread); usbd_uvc.thread = usbd_uvc_thread; return RT_EOK; } static void rt_usbd_function_uvc_device_delete() { } /* * register uvc class */ static struct udclass uvc_device_class = { .rt_usbd_function_create = rt_usbd_function_uvc_device_create, .rt_usbd_function_delete = rt_usbd_function_uvc_device_delete, }; int rt_usbd_uvc_device_class_register(rt_uint32_t dev_id) { rt_usbd_class_register(&uvc_device_class, dev_id); usbd_video_init_intf(0, INTERVAL, MAX_FRAME_SIZE(1920, 1080), MAX_PAYLOAD_SIZE); return 0; } #endif