| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 | /* * Copyright (c) 2024, sakumisu * * SPDX-License-Identifier: Apache-2.0 */#include "usbd_core.h"#include "usbd_video.h"#include "cherryusb_h264.h"#define VIDEO_IN_EP  0x81#define VIDEO_INT_EP 0x83#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 WIDTH  (unsigned int)(640)#define HEIGHT (unsigned int)(480)#define CAM_FPS        (30)#define INTERVAL       (unsigned long)(10000000 / CAM_FPS)#define MIN_BIT_RATE   (unsigned long)(WIDTH * HEIGHT * 16 * CAM_FPS) //16 bit#define MAX_BIT_RATE   (unsigned long)(WIDTH * HEIGHT * 16 * CAM_FPS)#define MAX_FRAME_SIZE (unsigned long)(WIDTH * HEIGHT * 2)#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))#define USB_VIDEO_DESC_SIZ (unsigned long)(9 +                            \                                           VIDEO_VC_NOEP_DESCRIPTOR_LEN + \                                           9 +                            \                                           VS_HEADER_SIZ +                \                                           9 +                            \                                           7)#define USBD_VID           0xffff#define USBD_PID           0xffff#define USBD_MAX_POWER     100#define USBD_LANGID_STRING 1033#ifdef CONFIG_USBDEV_ADVANCE_DESCstatic const uint8_t device_descriptor[] = {    USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xef, 0x02, 0x01, USBD_VID, USBD_PID, 0x0001, 0x01)};static const uint8_t config_descriptor[] = {    USB_CONFIG_DESCRIPTOR_INIT(USB_VIDEO_DESC_SIZ, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),    //VIDEO_VC_DESCRIPTOR_INIT(0x00, VIDEO_INT_EP, 0x0100, VIDEO_VC_TERMINAL_LEN, 48000000, 0x02),    VIDEO_VC_NOEP_DESCRIPTOR_INIT(0x00, VIDEO_INT_EP, 0x0100, VIDEO_VC_TERMINAL_LEN, 48000000, 0x02),    VIDEO_VS_DESCRIPTOR_INIT(0x01, 0x00, 0x00),    VIDEO_VS_INPUT_HEADER_DESCRIPTOR_INIT(0x01, VS_HEADER_SIZ, VIDEO_IN_EP, 0x00),    VIDEO_VS_FORMAT_H264_DESCRIPTOR_INIT(0x01, 0x01),    VIDEO_VS_FRAME_H264_DESCRIPTOR_INIT(0x01, WIDTH, HEIGHT, MIN_BIT_RATE, MAX_BIT_RATE, DBVAL(INTERVAL), 0x01, DBVAL(INTERVAL)),    VIDEO_VS_DESCRIPTOR_INIT(0x01, 0x01, 0x01),    /* 1.2.2.2 Standard VideoStream Isochronous Video Data Endpoint Descriptor */    USB_ENDPOINT_DESCRIPTOR_INIT(VIDEO_IN_EP, 0x05, VIDEO_PACKET_SIZE, 0x01)};static const uint8_t device_quality_descriptor[] = {    ///////////////////////////////////////    /// device qualifier descriptor    ///////////////////////////////////////    0x0a,    USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,    0x00,    0x02,    0x00,    0x00,    0x00,    0x40,    0x00,    0x00,};static const char *string_descriptors[] = {    (const char[]){ 0x09, 0x04 }, /* Langid */    "CherryUSB",                  /* Manufacturer */    "CherryUSB UVC DEMO",         /* Product */    "2022123456",                 /* Serial Number */};static const uint8_t *device_descriptor_callback(uint8_t speed){    return device_descriptor;}static const uint8_t *config_descriptor_callback(uint8_t speed){    return config_descriptor;}static const uint8_t *device_quality_descriptor_callback(uint8_t speed){    return device_quality_descriptor;}static const char *string_descriptor_callback(uint8_t speed, uint8_t index){    if (index > 3) {        return NULL;    }    return string_descriptors[index];}const struct usb_descriptor video_descriptor = {    .device_descriptor_callback = device_descriptor_callback,    .config_descriptor_callback = config_descriptor_callback,    .device_quality_descriptor_callback = device_quality_descriptor_callback,    .string_descriptor_callback = string_descriptor_callback};#elseconst uint8_t video_descriptor[] = {    USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xef, 0x02, 0x01, USBD_VID, USBD_PID, 0x0001, 0x01),    USB_CONFIG_DESCRIPTOR_INIT(USB_VIDEO_DESC_SIZ, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),    //VIDEO_VC_DESCRIPTOR_INIT(0x00, VIDEO_INT_EP, 0x0100, VIDEO_VC_TERMINAL_LEN, 48000000, 0x02),    VIDEO_VC_NOEP_DESCRIPTOR_INIT(0x00, VIDEO_INT_EP, 0x0100, VIDEO_VC_TERMINAL_LEN, 48000000, 0x02),    VIDEO_VS_DESCRIPTOR_INIT(0x01, 0x00, 0x00),    VIDEO_VS_INPUT_HEADER_DESCRIPTOR_INIT(0x01, VS_HEADER_SIZ, VIDEO_IN_EP, 0x00),    VIDEO_VS_FORMAT_H264_DESCRIPTOR_INIT(0x01, 0x01),    VIDEO_VS_FRAME_H264_DESCRIPTOR_INIT(0x01, WIDTH, HEIGHT, MIN_BIT_RATE, MAX_BIT_RATE, DBVAL(INTERVAL), 0x01, DBVAL(INTERVAL)),    VIDEO_VS_DESCRIPTOR_INIT(0x01, 0x01, 0x01),    /* 1.2.2.2 Standard VideoStream Isochronous Video Data Endpoint Descriptor */    USB_ENDPOINT_DESCRIPTOR_INIT(VIDEO_IN_EP, 0x05, VIDEO_PACKET_SIZE, 0x01),    ///////////////////////////////////////    /// string0 descriptor    ///////////////////////////////////////    USB_LANGID_INIT(USBD_LANGID_STRING),    ///////////////////////////////////////    /// string1 descriptor    ///////////////////////////////////////    0x14,                       /* bLength */    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */    'C', 0x00,                  /* wcChar0 */    'h', 0x00,                  /* wcChar1 */    'e', 0x00,                  /* wcChar2 */    'r', 0x00,                  /* wcChar3 */    'r', 0x00,                  /* wcChar4 */    'y', 0x00,                  /* wcChar5 */    'U', 0x00,                  /* wcChar6 */    'S', 0x00,                  /* wcChar7 */    'B', 0x00,                  /* wcChar8 */    ///////////////////////////////////////    /// string2 descriptor    ///////////////////////////////////////    0x26,                       /* bLength */    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */    'C', 0x00,                  /* wcChar0 */    'h', 0x00,                  /* wcChar1 */    'e', 0x00,                  /* wcChar2 */    'r', 0x00,                  /* wcChar3 */    'r', 0x00,                  /* wcChar4 */    'y', 0x00,                  /* wcChar5 */    'U', 0x00,                  /* wcChar6 */    'S', 0x00,                  /* wcChar7 */    'B', 0x00,                  /* wcChar8 */    ' ', 0x00,                  /* wcChar9 */    'U', 0x00,                  /* wcChar10 */    'V', 0x00,                  /* wcChar11 */    'C', 0x00,                  /* wcChar12 */    ' ', 0x00,                  /* wcChar13 */    'D', 0x00,                  /* wcChar14 */    'E', 0x00,                  /* wcChar15 */    'M', 0x00,                  /* wcChar16 */    'O', 0x00,                  /* wcChar17 */    ///////////////////////////////////////    /// string3 descriptor    ///////////////////////////////////////    0x16,                       /* bLength */    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */    '2', 0x00,                  /* wcChar0 */    '0', 0x00,                  /* wcChar1 */    '2', 0x00,                  /* wcChar2 */    '1', 0x00,                  /* wcChar3 */    '0', 0x00,                  /* wcChar4 */    '3', 0x00,                  /* wcChar5 */    '1', 0x00,                  /* wcChar6 */    '0', 0x00,                  /* wcChar7 */    '0', 0x00,                  /* wcChar8 */    '0', 0x00,                  /* wcChar9 */#ifdef CONFIG_USB_HS    ///////////////////////////////////////    /// device qualifier descriptor    ///////////////////////////////////////    0x0a,    USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,    0x00,    0x02,    0x00,    0x00,    0x00,    0x40,    0x00,    0x00,#endif    0x00};#endifvolatile bool tx_flag = 0;volatile bool iso_tx_busy = false;static void usbd_event_handler(uint8_t busid, uint8_t event){    switch (event) {        case USBD_EVENT_RESET:            break;        case USBD_EVENT_CONNECTED:            break;        case USBD_EVENT_DISCONNECTED:            break;        case USBD_EVENT_RESUME:            break;        case USBD_EVENT_SUSPEND:            break;        case USBD_EVENT_CONFIGURED:            tx_flag = 0;            iso_tx_busy = false;            break;        case USBD_EVENT_SET_REMOTE_WAKEUP:            break;        case USBD_EVENT_CLR_REMOTE_WAKEUP:            break;        default:            break;    }}void usbd_video_open(uint8_t busid, uint8_t intf){    tx_flag = 1;    USB_LOG_RAW("OPEN\r\n");    iso_tx_busy = false;}void usbd_video_close(uint8_t busid, uint8_t intf){    USB_LOG_RAW("CLOSE\r\n");    tx_flag = 0;    iso_tx_busy = false;}void usbd_video_iso_callback(uint8_t busid, uint8_t ep, uint32_t nbytes){    if (nbytes == 0) {        return;    }    if (usbd_video_stream_split_transfer(busid, ep)) {        /* one frame has done */        iso_tx_busy = false;    }}static struct usbd_endpoint video_in_ep = {    .ep_cb = usbd_video_iso_callback,    .ep_addr = VIDEO_IN_EP};struct usbd_interface intf0;struct usbd_interface intf1;void video_init(uint8_t busid, uintptr_t reg_base){#ifdef CONFIG_USBDEV_ADVANCE_DESC    usbd_desc_register(busid, &video_descriptor);#else    usbd_desc_register(busid, video_descriptor);#endif    usbd_add_interface(busid, usbd_video_init_intf(busid, &intf0, INTERVAL, MAX_FRAME_SIZE, MAX_PAYLOAD_SIZE));    usbd_add_interface(busid, usbd_video_init_intf(busid, &intf1, INTERVAL, MAX_FRAME_SIZE, MAX_PAYLOAD_SIZE));    usbd_add_endpoint(busid, &video_in_ep);    usbd_initialize(busid, reg_base, usbd_event_handler);}USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t packet_buffer[MAX_PAYLOAD_SIZE];USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t frame_buffer[32 * 1024];void video_test(uint8_t busid){    uint32_t out_len;    uint32_t packets;    (void)packets;    memset(packet_buffer, 0, sizeof(packet_buffer));    while (1) {        if (tx_flag) {            iso_tx_busy = true;            memcpy(frame_buffer, cherryusb_h264, sizeof(cherryusb_h264)); // cherryusb_h264 is a static yuyv frame buffer, so we need copy it to frame_buffer            usbd_video_stream_start_write(busid, VIDEO_IN_EP, packet_buffer, (uint8_t *)frame_buffer, sizeof(cherryusb_h264), false);            while (iso_tx_busy) {                if (tx_flag == 0) {                    break;                }            }        }    }}
 |