Files
stm32-mw-usb-device/Class/VIDEO/Src/usbd_video.c
2021-09-16 17:30:01 +01:00

1065 lines
37 KiB
C

/**
******************************************************************************
* @file usbd_video.c
* @author MCD Application Team
* @brief This file provides the Video core functions.
*
******************************************************************************
* @attention
*
* Copyright (c) 2020 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
* @verbatim
*
* ===================================================================
* VIDEO Class Description
* ===================================================================
* This driver manages the Video Class 1.1 following the "USB Device Class Definition for
* Video Devices V1.0 Mar 18, 98".
* This driver implements the following aspects of the specification:
* - Device descriptor management
* - Configuration descriptor management
* - Interface Association Descriptor
* -Standard VC Interface Descriptor = interface 0
* -Standard Vs Interface Descriptor = interface 1
* - 1 Video Streaming Interface
* - 1 Video Streaming Endpoint
* - 1 Video Terminal Input (camera)
* - Video Class-Specific AC Interfaces
* - Video Class-Specific AS Interfaces
* - VideoControl Requests
* - Video Synchronization type: Asynchronous
* The current Video class version supports the following Video features:
* - image JPEG format
* - Asynchronous Endpoints
*
* @note In HS mode and when the USB DMA is used, all variables and data structures
* dealing with the DMA during the transaction process should be 32-bit aligned.
*
*
* @endverbatim
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "usbd_video.h"
#include "usbd_ctlreq.h"
#include "usbd_core.h"
/** @addtogroup STM32_USB_DEVICE_LIBRARY
* @{
*/
/** @defgroup USBD_VIDEO
* @brief USB Device Video Class core module
* @{
*/
/** @defgroup USBD_VIDEO_Private_TypesDefinitions
* @{
*/
/**
* @}
*/
/** @defgroup USBD_VIDEO_Private_Defines
* @{
*/
/**
* @}
*/
/** @defgroup USBD_VIDEO_Private_Macros
* @{
*/
/* VIDEO Device library callbacks */
static uint8_t USBD_VIDEO_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
static uint8_t USBD_VIDEO_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
static uint8_t USBD_VIDEO_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
#ifndef USE_USBD_COMPOSITE
static uint8_t *USBD_VIDEO_GetFSCfgDesc(uint16_t *length);
static uint8_t *USBD_VIDEO_GetHSCfgDesc(uint16_t *length);
static uint8_t *USBD_VIDEO_GetOtherSpeedCfgDesc(uint16_t *length);
static uint8_t *USBD_VIDEO_GetDeviceQualifierDesc(uint16_t *length);
#endif /* USE_USBD_COMPOSITE */
static uint8_t USBD_VIDEO_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);
static uint8_t USBD_VIDEO_SOF(USBD_HandleTypeDef *pdev);
static uint8_t USBD_VIDEO_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum);
/* VIDEO Requests management functions */
static void VIDEO_REQ_GetCurrent(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
static void VIDEO_REQ_SetCurrent(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
#ifndef USE_USBD_COMPOSITE
static void *USBD_VIDEO_GetVSFrameDesc(uint8_t *pConfDesc);
#endif /* USE_USBD_COMPOSITE */
static void *USBD_VIDEO_GetVideoHeaderDesc(uint8_t *pConfDesc);
/**
* @}
*/
/** @defgroup USBD_VIDEO_Private_Variables
* @{
*/
USBD_ClassTypeDef USBD_VIDEO =
{
USBD_VIDEO_Init,
USBD_VIDEO_DeInit,
USBD_VIDEO_Setup,
NULL,
NULL,
USBD_VIDEO_DataIn,
NULL,
USBD_VIDEO_SOF,
USBD_VIDEO_IsoINIncomplete,
NULL,
#ifdef USE_USBD_COMPOSITE
NULL,
NULL,
NULL,
NULL,
#else
USBD_VIDEO_GetHSCfgDesc,
USBD_VIDEO_GetFSCfgDesc,
USBD_VIDEO_GetOtherSpeedCfgDesc,
USBD_VIDEO_GetDeviceQualifierDesc,
#endif /* USE_USBD_COMPOSITE */
};
/* USB VIDEO device Configuration Descriptor (same for all speeds thanks to user defines) */
__ALIGN_BEGIN static uint8_t USBD_VIDEO_CfgDesc[] __ALIGN_END =
{
/* Configuration 1 */
USB_CONF_DESC_SIZE, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
LOBYTE(UVC_CONFIG_DESC_SIZ), /* wTotalLength: no of returned bytes */
HIBYTE(UVC_CONFIG_DESC_SIZ),
0x02, /* bNumInterfaces: 2 interfaces */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor
describing the configuration */
#if (USBD_SELF_POWERED == 1U)
0xC0, /* bmAttributes: Bus Powered according to user configuration */
#else
0x80, /* bmAttributes: Bus Powered according to user configuration */
#endif /* USBD_SELF_POWERED */
USBD_MAX_POWER, /* bMaxPower in mA according to user configuration */
/* Interface Association Descriptor */
USB_IAD_DESC_SIZE, /* bLength: Interface Association Descriptor size */
USB_DESC_TYPE_IAD, /* bDescriptorType: interface association */
0x00, /* bFirstInterface */
0x02, /* bInterfaceCount */
UVC_CC_VIDEO, /* bFunctionClass: Video class */
SC_VIDEO_INTERFACE_COLLECTION, /* bFunctionSubClass: Video Interface Collection */
PC_PROTOCOL_UNDEFINED, /* bInterfaceProtocol: protocol undefined */
0x00, /* iFunction */
/* Standard VC (Video Control) Interface Descriptor = interface 0 */
USB_IF_DESC_SIZE, /* bLength: interface descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: interface */
UVC_VC_IF_NUM, /* bInterfaceNumber: interface number */
0x00, /* bAlternateSetting: index of this alternate setting */
0x00, /* bNumEndpoints: No endpoints used for this interface */
UVC_CC_VIDEO, /* bInterfaceClass: Video Class */
SC_VIDEOCONTROL, /* bInterfaceSubClass: Video Control */
PC_PROTOCOL_UNDEFINED, /* bInterfaceProtocol: protocol is undefined */
0x00, /* iFunction: index of string descriptor relative to this item */
/* Class-specific VC Interface Descriptor */
VIDEO_VC_IF_HEADER_DESC_SIZE, /* bLength */
CS_INTERFACE, /* bDescriptorType */
VC_HEADER, /* bDescriptorSubtype */
LOBYTE(UVC_VERSION),
HIBYTE(UVC_VERSION), /* bcdUVC: UVC1.0 or UVC1.1 revision */
VS_FRAME_DESC_SIZE, /* wTotalLength: total size of class-specific descriptors */
0x00,
0x00, /* dwClockFrequency: not used. 48 Mhz value is set, but not used */
0x6C,
0xDC,
0x02,
0x01, /* bInCollection: number of streaming interfaces */
0x01, /* baInterfaceNr(1): VideoStreaming interface 1 is part
of VC interface */
/* Input Terminal Descriptor */
VIDEO_IN_TERMINAL_DESC_SIZE, /* bLength: Input terminal descriptor size */
CS_INTERFACE, /* bDescriptorType: INTERFACE */
VC_INPUT_TERMINAL, /* bDescriptorSubtype: INPUT_TERMINAL */
0x01, /* bTerminalID: ID of this Terminal */
LOBYTE(ITT_VENDOR_SPECIFIC), /* wTerminalType: 0x0200 ITT_VENDOR_SPECIFIC */
HIBYTE(ITT_VENDOR_SPECIFIC),
0x00, /* bAssocTerminal: no Terminal is associated */
0x00, /* iTerminal: index of string descriptor relative to this item */
/* Output Terminal Descriptor */
VIDEO_OUT_TERMINAL_DESC_SIZE, /* bLength: output terminal descriptor size */
CS_INTERFACE, /* bDescriptorType */
VC_OUTPUT_TERMINAL, /* bDescriptorSubtype */
0x02, /* bTerminalID */
LOBYTE(TT_STREAMING), /* wTerminalType: USB streaming terminal */
HIBYTE(TT_STREAMING),
0x00, /* bAssocTerminal: no Terminal is associated */
0x01, /* bSourceID: input is connected to output unit ID 1 */
0x00, /* iTerminal: index of string descriptor relative to this item */
/* Standard VS (Video Streaming) Interface Descriptor = interface 1, alternate setting 0 = Zero Bandwidth
(when no data are sent from the device) */
USB_IF_DESC_SIZE, /* bLength: interface descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
UVC_VS_IF_NUM, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x00, /* bNumEndpoints: no endpoints used for alternate setting 0 */
UVC_CC_VIDEO, /* bInterfaceClass */
SC_VIDEOSTREAMING, /* bInterfaceSubClass */
PC_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
0x00, /* iInterface: index of string descriptor relative to this item */
/* Class-specific VS Header Descriptor (Input) */
VIDEO_VS_IF_IN_HEADER_DESC_SIZE, /* bLength */
CS_INTERFACE, /* bDescriptorType */
VS_INPUT_HEADER, /* bDescriptorSubtype */
0x01, /* bNumFormats: 1 format descriptor is used */
VC_HEADER_SIZE,
0x00, /* Total size of Video Control Specific Descriptors */
UVC_IN_EP, /* bEndPointAddress: In endpoint is used for the alternate setting */
0x00, /* bmInfo: dynamic format change not supported */
0x02, /* bTerminalLink: output to terminal ID 2 */
0x00, /* bStillCaptureMethod: not supported */
0x00, /* bTriggerSupport: not supported */
0x00, /* bTriggerUsage: not supported */
0x01, /* bControlSize: 1 byte field size */
0x00, /* bmaControls: No specific controls used */
/* Payload Format Descriptor */
VS_FORMAT_DESC_SIZE, /* blength */
CS_INTERFACE, /* bDescriptorType */
VS_FORMAT_SUBTYPE, /* bDescriptorSubType */
0x01, /* bFormatIndex */
0x01, /* bNumFrameDescriptor */
#ifdef USBD_UVC_FORMAT_UNCOMPRESSED
DBVAL(UVC_UNCOMPRESSED_GUID), /* Giud Format: YUY2 {32595559-0000-0010-8000-00AA00389B71} */
0x00, 0x00,
0x10, 0x00,
0x80, 0x00,
0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71,
UVC_BITS_PER_PIXEL, /* bBitsPerPixel : Number of bits per pixel */
#else
0x01, /* bmFlags: FixedSizeSamples */
#endif /* USBD_UVC_FORMAT_UNCOMPRESSED */
0x01, /* bDefaultFrameIndex: default frame used is frame 1
(only one frame used) */
0x00, /* bAspectRatioX: not required by specification */
0x00, /* bAspectRatioY: not required by specification */
0x00, /* bInterlaceFlags: non interlaced stream */
0x00, /* bCopyProtect: no protection restrictions */
/* Class-specific VS (Video Streaming) Frame Descriptor */
VS_FRAME_DESC_SIZE, /* bLength */
CS_INTERFACE, /* bDescriptorType */
VS_FRAME_SUBTYPE, /* bDescriptorSubType */
0x01, /* bFrameIndex */
#ifdef USBD_UVC_FORMAT_UNCOMPRESSED
0x00, /* bmCapabilities: no till image capture */
#else
0x02, /* bmCapabilities: fixed frame rate supported */
#endif /* USBD_UVC_FORMAT_UNCOMPRESSED */
WBVAL(UVC_WIDTH), /* wWidth: Image Frame Width */
WBVAL(UVC_HEIGHT), /* wHeight: Image Frame Height */
DBVAL(UVC_MIN_BIT_RATE(UVC_CAM_FPS_FS)), /* dwMinBitRate: Minimum supported bit rate in bits/s */
DBVAL(UVC_MAX_BIT_RATE(UVC_CAM_FPS_FS)), /* dwMaxBitRate: Maximum supported bit rate in bits/s */
DBVAL(UVC_MAX_FRAME_SIZE), /* dwMaxVideoFrameBufSize: Maximum video frame size, in bytes */
DBVAL(UVC_INTERVAL(UVC_CAM_FPS_FS)), /* dwDefaultFrameInterval: following number of FPS */
0x01, /* bFrameIntervalType: Discrete frame interval type */
DBVAL(UVC_INTERVAL(UVC_CAM_FPS_FS)), /* dwMinFrameInterval: One supported value of interval (FPS) */
#ifdef USBD_UVC_FORMAT_UNCOMPRESSED
/* Color Matching Descriptor */
VS_COLOR_MATCHING_DESC_SIZE, /* bLength */
CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */
VS_COLORFORMAT, /* bDescriptorSubType: VS_COLORFORMAT */
UVC_COLOR_PRIMARIE, /* bColorPrimarie: 1: BT.709, sRGB (default) */
UVC_TFR_CHARACTERISTICS, /* bTransferCharacteristics: 1: BT.709 (default) */
UVC_MATRIX_COEFFICIENTS, /* bMatrixCoefficients: 4: BT.601, (default) */
#endif /* USBD_UVC_FORMAT_UNCOMPRESSED */
/* Standard VS Interface Descriptor = interface 1, alternate setting 1 = data transfer mode */
USB_IF_DESC_SIZE, /* bLength */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
UVC_VS_IF_NUM, /* bInterfaceNumber */
0x01, /* bAlternateSetting */
0x01, /* bNumEndpoints: one endpoint is used */
UVC_CC_VIDEO, /* bInterfaceClass */
SC_VIDEOSTREAMING, /* bInterfaceSubClass */
PC_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
0x00, /* iInterface: index of string descriptor relative to this item */
/* Standard VS (Video Streaming) data Endpoint */
USB_EP_DESC_SIZE, /* bLength */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType */
UVC_IN_EP, /* bEndpointAddress */
0x05, /* bmAttributes: ISO transfer */
LOBYTE(UVC_ISO_FS_MPS), /* wMaxPacketSize */
LOBYTE(UVC_ISO_FS_MPS),
0x01, /* bInterval: 1 frame interval */
};
#ifndef USE_USBD_COMPOSITE
/* USB Standard Device Descriptor */
__ALIGN_BEGIN static uint8_t USBD_VIDEO_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END =
{
USB_LEN_DEV_QUALIFIER_DESC,
USB_DESC_TYPE_DEVICE_QUALIFIER,
0x00,
0x02,
0xEF,
0x02,
0x01,
0x40,
0x01,
0x00,
};
#endif /* USE_USBD_COMPOSITE */
static uint8_t VIDEOinEpAdd = UVC_IN_EP;
/* Video Commit data structure */
static USBD_VideoControlTypeDef video_Commit_Control =
{
.bmHint = 0x0000U,
.bFormatIndex = 0x01U,
.bFrameIndex = 0x01U,
.dwFrameInterval = UVC_INTERVAL(UVC_CAM_FPS_FS),
.wKeyFrameRate = 0x0000U,
.wPFrameRate = 0x0000U,
.wCompQuality = 0x0000U,
.wCompWindowSize = 0x0000U,
.wDelay = 0x0000U,
.dwMaxVideoFrameSize = 0x0000U,
.dwMaxPayloadTransferSize = 0x00000000U,
.dwClockFrequency = 0x00000000U,
.bmFramingInfo = 0x00U,
.bPreferedVersion = 0x00U,
.bMinVersion = 0x00U,
.bMaxVersion = 0x00U,
};
/* Video Probe data structure */
static USBD_VideoControlTypeDef video_Probe_Control =
{
.bmHint = 0x0000U,
.bFormatIndex = 0x01U,
.bFrameIndex = 0x01U,
.dwFrameInterval = UVC_INTERVAL(UVC_CAM_FPS_FS),
.wKeyFrameRate = 0x0000U,
.wPFrameRate = 0x0000U,
.wCompQuality = 0x0000U,
.wCompWindowSize = 0x0000U,
.wDelay = 0x0000U,
.dwMaxVideoFrameSize = 0x0000U,
.dwMaxPayloadTransferSize = 0x00000000U,
.dwClockFrequency = 0x00000000U,
.bmFramingInfo = 0x00U,
.bPreferedVersion = 0x00U,
.bMinVersion = 0x00U,
.bMaxVersion = 0x00U,
};
/**
* @}
*/
/** @defgroup USBD_VIDEO_Private_Functions
* @{
*/
/**
* @brief USBD_VIDEO_Init
* Initialize the VIDEO interface
* @param pdev: device instance
* @param cfgidx: Configuration index
* @retval status
*/
static uint8_t USBD_VIDEO_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
USBD_VIDEO_HandleTypeDef *hVIDEO;
/* Allocate memory for the video control structure */
hVIDEO = (USBD_VIDEO_HandleTypeDef *)USBD_malloc(sizeof(USBD_VIDEO_HandleTypeDef));
/* Check if allocated point is NULL, then exit with error code */
if (hVIDEO == NULL)
{
return (uint8_t)USBD_FAIL;
}
/* Assign the pClassData pointer to the allocated structure */
pdev->pClassDataCmsit[pdev->classId] = (void *)hVIDEO;
pdev->pClassData = pdev->pClassDataCmsit[pdev->classId];
#ifdef USE_USBD_COMPOSITE
/* Get the Endpoints addresses allocated for this class instance */
VIDEOinEpAdd = USBD_CoreGetEPAdd(pdev, USBD_EP_IN, USBD_EP_TYPE_ISOC);
#endif /* USE_USBD_COMPOSITE */
/* Open EP IN */
if (pdev->dev_speed == USBD_SPEED_HIGH)
{
(void)USBD_LL_OpenEP(pdev, VIDEOinEpAdd, USBD_EP_TYPE_ISOC, UVC_ISO_HS_MPS);
pdev->ep_in[VIDEOinEpAdd & 0xFU].is_used = 1U;
pdev->ep_in[VIDEOinEpAdd & 0xFU].maxpacket = UVC_ISO_HS_MPS;
}
else
{
(void)USBD_LL_OpenEP(pdev, VIDEOinEpAdd, USBD_EP_TYPE_ISOC, UVC_ISO_FS_MPS);
pdev->ep_in[VIDEOinEpAdd & 0xFU].is_used = 1U;
pdev->ep_in[VIDEOinEpAdd & 0xFU].maxpacket = UVC_ISO_FS_MPS;
}
/* Init physical Interface components */
((USBD_VIDEO_ItfTypeDef *)pdev->pUserData[pdev->classId])->Init();
/* Init Xfer states */
hVIDEO->interface = 0U;
/* Some calls to unused variables, to comply with MISRA-C 2012 rules */
UNUSED(USBD_VIDEO_CfgDesc);
UNUSED(cfgidx);
/* Exit with no error code */
return (uint8_t)USBD_OK;
}
/**
* @brief USBD_VIDEO_DeInit
* DeInitialize the VIDEO layer
* @param pdev: device instance
* @param cfgidx: Configuration index
* @retval status
*/
static uint8_t USBD_VIDEO_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
UNUSED(cfgidx);
/* Check if the video structure pointer is valid */
if (pdev->pClassDataCmsit[pdev->classId] == NULL)
{
return (uint8_t)USBD_FAIL;
}
#ifdef USE_USBD_COMPOSITE
/* Get the Endpoints addresses allocated for this class instance */
VIDEOinEpAdd = USBD_CoreGetEPAdd(pdev, USBD_EP_IN, USBD_EP_TYPE_ISOC);
#endif /* USE_USBD_COMPOSITE */
/* Close EP IN */
(void)USBD_LL_CloseEP(pdev, VIDEOinEpAdd);
pdev->ep_in[VIDEOinEpAdd & 0xFU].is_used = 0U;
/* DeInit physical Interface components */
((USBD_VIDEO_ItfTypeDef *)pdev->pUserData[pdev->classId])->DeInit();
USBD_free(pdev->pClassDataCmsit[pdev->classId]);
pdev->pClassDataCmsit[pdev->classId] = NULL;
pdev->pClassData = NULL;
/* Exit with no error code */
return (uint8_t)USBD_OK;
}
/**
* @brief USBD_VIDEO_Setup
* Handle the VIDEO specific requests
* @param pdev: instance
* @param req: usb requests
* @retval status
*/
static uint8_t USBD_VIDEO_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
USBD_VIDEO_HandleTypeDef *hVIDEO = (USBD_VIDEO_HandleTypeDef *) pdev->pClassDataCmsit[pdev->classId];
uint8_t ret = (uint8_t)USBD_OK;
uint16_t status_info = 0U;
uint16_t len;
uint8_t *pbuf;
switch (req->bmRequest & USB_REQ_TYPE_MASK)
{
/* Class Requests -------------------------------*/
case USB_REQ_TYPE_CLASS:
switch (req->bRequest)
{
case UVC_GET_CUR:
case UVC_GET_DEF:
case UVC_GET_MIN:
case UVC_GET_MAX:
VIDEO_REQ_GetCurrent(pdev, req);
break;
case UVC_GET_RES:
case UVC_GET_LEN:
case UVC_GET_INFO:
break;
case UVC_SET_CUR:
VIDEO_REQ_SetCurrent(pdev, req);
break;
default:
(void) USBD_CtlError(pdev, req);
ret = (uint8_t)USBD_FAIL;
break;
}
break;
/* Standard Requests -------------------------------*/
case USB_REQ_TYPE_STANDARD:
switch (req->bRequest)
{
case USB_REQ_GET_STATUS:
if (pdev->dev_state == USBD_STATE_CONFIGURED)
{
(void) USBD_CtlSendData(pdev, (uint8_t *)&status_info, 2U);
}
else
{
USBD_CtlError(pdev, req);
ret = (uint8_t)USBD_FAIL;
}
break;
case USB_REQ_GET_DESCRIPTOR:
if ((req->wValue >> 8) == CS_DEVICE)
{
pbuf = (uint8_t *)USBD_VIDEO_GetVideoHeaderDesc(pdev->pConfDesc);
if (pbuf != NULL)
{
len = MIN((uint16_t)USB_CONF_DESC_SIZE, (uint16_t)req->wLength);
(void)USBD_CtlSendData(pdev, pbuf, len);
}
else
{
USBD_CtlError(pdev, req);
ret = (uint8_t)USBD_FAIL;
}
}
break;
case USB_REQ_GET_INTERFACE :
if (pdev->dev_state == USBD_STATE_CONFIGURED)
{
(void)USBD_CtlSendData(pdev, (uint8_t *)&hVIDEO->interface, 1U);
}
else
{
USBD_CtlError(pdev, req);
ret = (uint8_t)USBD_FAIL;
}
break;
case USB_REQ_SET_INTERFACE :
if (pdev->dev_state == USBD_STATE_CONFIGURED)
{
if (req->wValue <= USBD_MAX_NUM_INTERFACES)
{
hVIDEO->interface = LOBYTE(req->wValue);
if (hVIDEO->interface == 1U)
{
/* Start Streaming (First endpoint writing will be done on next SOF) */
(void)USBD_LL_FlushEP(pdev, VIDEOinEpAdd);
hVIDEO->uvc_state = UVC_PLAY_STATUS_READY;
}
else
{
/* Stop Streaming */
hVIDEO->uvc_state = UVC_PLAY_STATUS_STOP;
(void)USBD_LL_FlushEP(pdev, VIDEOinEpAdd);
}
}
else
{
/* Call the error management function (command will be NAKed) */
USBD_CtlError(pdev, req);
ret = (uint8_t)USBD_FAIL;
}
}
else
{
USBD_CtlError(pdev, req);
ret = (uint8_t)USBD_FAIL;
}
break;
case USB_REQ_CLEAR_FEATURE:
break;
default:
USBD_CtlError(pdev, req);
ret = (uint8_t)USBD_FAIL;
break;
}
break;
default:
USBD_CtlError(pdev, req);
ret = (uint8_t)USBD_FAIL;
break;
}
return ret;
}
/**
* @brief USBD_VIDEO_DataIn
* handle data IN Stage
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
static uint8_t USBD_VIDEO_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
USBD_VIDEO_HandleTypeDef *hVIDEO = (USBD_VIDEO_HandleTypeDef *) pdev->pClassDataCmsit[pdev->classId];
static uint8_t packet[UVC_PACKET_SIZE + (UVC_HEADER_PACKET_CNT * 2U)] = {0x00U};
static uint8_t *Pcktdata = packet;
static uint16_t PcktIdx = 0U;
static uint16_t PcktSze = UVC_PACKET_SIZE;
static uint8_t payload_header[2] = {0x02U, 0x00U};
uint8_t i = 0U;
uint32_t RemainData, DataOffset = 0U;
/* Check if the Streaming has already been started */
if (hVIDEO->uvc_state == UVC_PLAY_STATUS_STREAMING)
{
/* Get the current packet buffer, index and size from the application layer */
((USBD_VIDEO_ItfTypeDef *)pdev->pUserData[pdev->classId])->Data(&Pcktdata, &PcktSze, &PcktIdx);
/* Check if end of current image has been reached */
if (PcktSze > 2U)
{
/* Check if this is the first packet in current image */
if (PcktIdx == 0U)
{
/* Set the packet start index */
payload_header[1] ^= 0x01U;
}
RemainData = PcktSze;
/* fill the Transmit buffer */
while (RemainData > 0U)
{
packet[((DataOffset + 0U) * i)] = payload_header[0];
packet[((DataOffset + 0U) * i) + 1U] = payload_header[1];
if (RemainData > pdev->ep_in[VIDEOinEpAdd & 0xFU].maxpacket)
{
DataOffset = pdev->ep_in[VIDEOinEpAdd & 0xFU].maxpacket;
(void)USBD_memcpy((packet + ((DataOffset + 0U) * i) + 2U),
Pcktdata + ((DataOffset - 2U) * i), (DataOffset - 2U));
RemainData -= DataOffset;
i++;
}
else
{
(void)USBD_memcpy((packet + ((DataOffset + 0U) * i) + 2U),
Pcktdata + ((DataOffset - 2U) * i), (RemainData - 2U));
RemainData = 0U;
}
}
}
else
{
/* Add the packet header */
packet[0] = payload_header[0];
packet[1] = payload_header[1];
}
/* Transmit the packet on Endpoint */
(void)USBD_LL_Transmit(pdev, (uint8_t)(epnum | 0x80U),
(uint8_t *)&packet, (uint32_t)PcktSze);
}
/* Exit with no error code */
return (uint8_t) USBD_OK;
}
/**
* @brief USBD_VIDEO_SOF
* handle SOF event
* @param pdev: device instance
* @retval status
*/
static uint8_t USBD_VIDEO_SOF(USBD_HandleTypeDef *pdev)
{
USBD_VIDEO_HandleTypeDef *hVIDEO = (USBD_VIDEO_HandleTypeDef *) pdev->pClassDataCmsit[pdev->classId];
uint8_t payload[2] = {0x02U, 0x00U};
/* Check if the Streaming has already been started by SetInterface AltSetting 1 */
if (hVIDEO->uvc_state == UVC_PLAY_STATUS_READY)
{
/* Transmit the first packet indicating that Streaming is starting */
(void)USBD_LL_Transmit(pdev, VIDEOinEpAdd, (uint8_t *)payload, 2U);
/* Enable Streaming state */
hVIDEO->uvc_state = UVC_PLAY_STATUS_STREAMING;
}
/* Exit with no error code */
return (uint8_t)USBD_OK;
}
/**
* @brief USBD_VIDEO_IsoINIncomplete
* handle data ISO IN Incomplete event
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
static uint8_t USBD_VIDEO_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
UNUSED(pdev);
UNUSED(epnum);
return (uint8_t)USBD_OK;
}
/**
* @brief VIDEO_Req_GetCurrent
* Handles the GET_CUR VIDEO control request.
* @param pdev: instance
* @param req: setup class request
* @retval status
*/
static void VIDEO_REQ_GetCurrent(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
USBD_VIDEO_HandleTypeDef *hVIDEO;
hVIDEO = (USBD_VIDEO_HandleTypeDef *)(pdev->pClassDataCmsit[pdev->classId]);
static __IO uint8_t EntityStatus[8] = {0};
/* Reset buffer to zeros */
(void) USBD_memset(hVIDEO->control.data, 0, USB_MAX_EP0_SIZE);
/* Manage Video Control interface requests */
if (LOBYTE(req->wIndex) == 0x00U)
{
if (HIBYTE(req->wValue) == 0x02U)
{
/* Get the status of the current requested Entity */
EntityStatus[0] = 0x06U;
/* Send current status */
(void) USBD_CtlSendData(pdev, (uint8_t *)&EntityStatus, 1U);
}
else
{
/* Unknown request */
USBD_CtlError(pdev, req);
}
}
/* Manage Video Streaming interface requests */
else
{
if (LOBYTE(req->wValue) == (uint8_t)VS_PROBE_CONTROL)
{
/* Update bPreferedVersion, bMinVersion and bMaxVersion which must be set only by Device */
video_Probe_Control.bPreferedVersion = 0x00U;
video_Probe_Control.bMinVersion = 0x00U;
video_Probe_Control.bMaxVersion = 0x00U;
video_Probe_Control.dwMaxVideoFrameSize = UVC_MAX_FRAME_SIZE;
video_Probe_Control.dwClockFrequency = 0x02DC6C00U;
if (pdev->dev_speed == USBD_SPEED_HIGH)
{
video_Probe_Control.dwFrameInterval = (UVC_INTERVAL(UVC_CAM_FPS_HS));
video_Probe_Control.dwMaxPayloadTransferSize = UVC_ISO_HS_MPS;
}
else
{
video_Probe_Control.dwFrameInterval = (UVC_INTERVAL(UVC_CAM_FPS_FS));
video_Probe_Control.dwMaxPayloadTransferSize = UVC_ISO_FS_MPS;
}
/* Probe Request */
(void)USBD_CtlSendData(pdev, (uint8_t *)&video_Probe_Control,
MIN(req->wLength, sizeof(USBD_VideoControlTypeDef)));
}
else if (LOBYTE(req->wValue) == (uint8_t)VS_COMMIT_CONTROL)
{
if (pdev->dev_speed == USBD_SPEED_HIGH)
{
video_Commit_Control.dwFrameInterval = (UVC_INTERVAL(UVC_CAM_FPS_HS));
video_Commit_Control.dwMaxPayloadTransferSize = UVC_ISO_HS_MPS;
}
else
{
video_Commit_Control.dwFrameInterval = (UVC_INTERVAL(UVC_CAM_FPS_FS));
video_Commit_Control.dwMaxPayloadTransferSize = UVC_ISO_FS_MPS;
}
/* Commit Request */
(void)USBD_CtlSendData(pdev, (uint8_t *)&video_Commit_Control,
MIN(req->wLength, sizeof(USBD_VideoControlTypeDef)));
}
else
{
/* Send the current state */
(void) USBD_CtlSendData(pdev, hVIDEO->control.data,
MIN(req->wLength, USB_MAX_EP0_SIZE));
}
}
}
/**
* @brief VIDEO_Req_SetCurrent
* Handles the SET_CUR VIDEO control request.
* @param pdev: instance
* @param req: setup class request
* @retval status
*/
static void VIDEO_REQ_SetCurrent(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
{
USBD_VIDEO_HandleTypeDef *hVIDEO = (USBD_VIDEO_HandleTypeDef *)(pdev->pClassDataCmsit[pdev->classId]);
/* Check that the request has control data */
if (req->wLength > 0U)
{
/* Prepare the reception of the buffer over EP0 */
if (LOBYTE(req->wValue) == (uint8_t)VS_PROBE_CONTROL)
{
/* Probe Request */
(void) USBD_CtlPrepareRx(pdev, (uint8_t *)&video_Probe_Control,
MIN(req->wLength, sizeof(USBD_VideoControlTypeDef)));
}
else if (LOBYTE(req->wValue) == (uint8_t)VS_COMMIT_CONTROL)
{
/* Commit Request */
(void) USBD_CtlPrepareRx(pdev, (uint8_t *)&video_Commit_Control,
MIN(req->wLength, sizeof(USBD_VideoControlTypeDef)));
}
else
{
/* Prepare the reception of the buffer over EP0 */
(void) USBD_CtlPrepareRx(pdev, hVIDEO->control.data,
MIN(req->wLength, USB_MAX_EP0_SIZE));
}
}
}
#ifndef USE_USBD_COMPOSITE
/**
* @brief USBD_VIDEO_GetFSCfgDesc
* return configuration descriptor
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
static uint8_t *USBD_VIDEO_GetFSCfgDesc(uint16_t *length)
{
USBD_EpDescTypeDef *pEpDesc = USBD_GetEpDesc(USBD_VIDEO_CfgDesc, VIDEOinEpAdd);
USBD_VIDEO_VSFrameDescTypeDef *pVSFrameDesc = USBD_VIDEO_GetVSFrameDesc(USBD_VIDEO_CfgDesc);
if (pEpDesc != NULL)
{
pEpDesc->wMaxPacketSize = UVC_ISO_FS_MPS;
}
if (pVSFrameDesc != NULL)
{
pVSFrameDesc->dwMinBitRate = UVC_MIN_BIT_RATE(UVC_CAM_FPS_FS);
pVSFrameDesc->dwMaxBitRate = UVC_MAX_BIT_RATE(UVC_CAM_FPS_FS);
pVSFrameDesc->dwDefaultFrameInterval = UVC_INTERVAL(UVC_CAM_FPS_FS);
pVSFrameDesc->dwMinFrameInterval = UVC_INTERVAL(UVC_CAM_FPS_FS);
}
*length = (uint16_t)(sizeof(USBD_VIDEO_CfgDesc));
return USBD_VIDEO_CfgDesc;
}
/**
* @brief USBD_VIDEO_GetHSCfgDesc
* return configuration descriptor
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
static uint8_t *USBD_VIDEO_GetHSCfgDesc(uint16_t *length)
{
USBD_EpDescTypeDef *pEpDesc = USBD_GetEpDesc(USBD_VIDEO_CfgDesc, VIDEOinEpAdd);
USBD_VIDEO_VSFrameDescTypeDef *pVSFrameDesc = USBD_VIDEO_GetVSFrameDesc(USBD_VIDEO_CfgDesc);
if (pEpDesc != NULL)
{
pEpDesc->wMaxPacketSize = UVC_ISO_HS_MPS;
}
if (pVSFrameDesc != NULL)
{
pVSFrameDesc->dwMinBitRate = UVC_MIN_BIT_RATE(UVC_CAM_FPS_HS);
pVSFrameDesc->dwMaxBitRate = UVC_MAX_BIT_RATE(UVC_CAM_FPS_HS);
pVSFrameDesc->dwDefaultFrameInterval = UVC_INTERVAL(UVC_CAM_FPS_HS);
pVSFrameDesc->dwMinFrameInterval = UVC_INTERVAL(UVC_CAM_FPS_HS);
}
*length = (uint16_t)(sizeof(USBD_VIDEO_CfgDesc));
return USBD_VIDEO_CfgDesc;
}
/**
* @brief USBD_VIDEO_GetOtherSpeedCfgDesc
* return configuration descriptor
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
static uint8_t *USBD_VIDEO_GetOtherSpeedCfgDesc(uint16_t *length)
{
USBD_EpDescTypeDef *pEpDesc = USBD_GetEpDesc(USBD_VIDEO_CfgDesc, VIDEOinEpAdd);
USBD_VIDEO_VSFrameDescTypeDef *pVSFrameDesc = USBD_VIDEO_GetVSFrameDesc(USBD_VIDEO_CfgDesc);
if (pEpDesc != NULL)
{
pEpDesc->wMaxPacketSize = UVC_ISO_FS_MPS;
}
if (pVSFrameDesc != NULL)
{
pVSFrameDesc->dwMinBitRate = UVC_MIN_BIT_RATE(UVC_CAM_FPS_FS);
pVSFrameDesc->dwMaxBitRate = UVC_MAX_BIT_RATE(UVC_CAM_FPS_FS);
pVSFrameDesc->dwDefaultFrameInterval = UVC_INTERVAL(UVC_CAM_FPS_FS);
pVSFrameDesc->dwMinFrameInterval = UVC_INTERVAL(UVC_CAM_FPS_FS);
}
*length = (uint16_t)(sizeof(USBD_VIDEO_CfgDesc));
return USBD_VIDEO_CfgDesc;
}
/**
* @brief DeviceQualifierDescriptor
* return Device Qualifier descriptor
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
static uint8_t *USBD_VIDEO_GetDeviceQualifierDesc(uint16_t *length)
{
*length = (uint16_t)(sizeof(USBD_VIDEO_DeviceQualifierDesc));
return USBD_VIDEO_DeviceQualifierDesc;
}
/**
* @brief USBD_VIDEO_GetVSFrameDesc
* This function return the Video Endpoint descriptor
* @param pdev: device instance
* @param pConfDesc: pointer to Bos descriptor
* @retval pointer to video endpoint descriptor
*/
static void *USBD_VIDEO_GetVSFrameDesc(uint8_t *pConfDesc)
{
USBD_DescHeaderTypeDef *pdesc = (USBD_DescHeaderTypeDef *)(void *)pConfDesc;
USBD_ConfigDescTypeDef *desc = (USBD_ConfigDescTypeDef *)(void *)pConfDesc;
USBD_VIDEO_VSFrameDescTypeDef *pVSFrameDesc = NULL;
uint16_t ptr;
if (desc->wTotalLength > desc->bLength)
{
ptr = desc->bLength;
while (ptr < desc->wTotalLength)
{
pdesc = USBD_GetNextDesc((uint8_t *)pdesc, &ptr);
if (((pdesc->bDescriptorSubType == VS_FRAME_MJPEG) ||
(pdesc->bDescriptorSubType == VS_FRAME_UNCOMPRESSED)) &&
(pdesc->bLength == VS_FRAME_DESC_SIZE))
{
pVSFrameDesc = (USBD_VIDEO_VSFrameDescTypeDef *)(void *)pdesc;
break;
}
}
}
return (void *)pVSFrameDesc;
}
#endif /* USE_USBD_COMPOSITE */
/**
* @brief USBD_VIDEO_GetVideoHeaderDesc
* This function return the Video Header descriptor
* @param pdev: device instance
* @param pConfDesc: pointer to Bos descriptor
* @retval pointer to the Video Header descriptor
*/
static void *USBD_VIDEO_GetVideoHeaderDesc(uint8_t *pConfDesc)
{
USBD_ConfigDescTypeDef *desc = (USBD_ConfigDescTypeDef *)(void *)pConfDesc;
USBD_DescHeaderTypeDef *pdesc = (USBD_DescHeaderTypeDef *)(void *)pConfDesc;
uint8_t *pVideoDesc = NULL;
uint16_t ptr;
if (desc->wTotalLength > desc->bLength)
{
ptr = desc->bLength;
while (ptr < desc->wTotalLength)
{
pdesc = USBD_GetNextDesc((uint8_t *)pdesc, &ptr);
if ((pdesc->bDescriptorType == CS_INTERFACE) &&
(pdesc->bDescriptorSubType == VC_HEADER))
{
pVideoDesc = (uint8_t *)pdesc;
break;
}
}
}
return pVideoDesc;
}
/**
* @brief USBD_VIDEO_RegisterInterface
* @param pdev: instance
* @param fops: VIDEO interface callback
* @retval status
*/
uint8_t USBD_VIDEO_RegisterInterface(USBD_HandleTypeDef *pdev, USBD_VIDEO_ItfTypeDef *fops)
{
/* Check if the FOPS pointer is valid */
if (fops == NULL)
{
return (uint8_t)USBD_FAIL;
}
/* Assign the FOPS pointer */
pdev->pUserData[pdev->classId] = fops;
/* Exit with no error code */
return (uint8_t)USBD_OK;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/