在紀錄 In/Out Report 收發之前先來看一下一個struct
typedef struct _Device_cb
{
uint8_t (*Init) (void *pdev , uint8_t cfgidx);
uint8_t (*DeInit) (void *pdev , uint8_t cfgidx);
/* Control Endpoints*/
uint8_t (*Setup) (void *pdev , USB_SETUP_REQ *req);
uint8_t (*EP0_TxSent) (void *pdev );
uint8_t (*EP0_RxReady) (void *pdev );
/* Class Specific Endpoints*/
uint8_t (*DataIn) (void *pdev , uint8_t epnum);
uint8_t (*DataOut) (void *pdev , uint8_t epnum);
uint8_t (*SOF) (void *pdev);
uint8_t (*IsoINIncomplete) (void *pdev);
uint8_t (*IsoOUTIncomplete) (void *pdev); uint8_t *(*GetConfigDescriptor)( uint8_t speed , uint16_t *length);
#ifdef USB_OTG_HS_CORE
uint8_t *(*GetOtherConfigDescriptor)( uint8_t speed , uint16_t *length);
#endif #ifdef USB_SUPPORT_USER_STRING_DESC
uint8_t *(*GetUsrStrDescriptor)( uint8_t speed ,uint8_t index, uint16_t *length);
#endif } USBD_Class_cb_TypeDef;
這邊有特別註解 Control Endpoint, Class Specific Endpoint,理解翻譯就是Control Endpoint 即是所謂的Endpoint 0
在USB協定中,一定是透過Endpoint 0,而且一個裝置只能有一個,而且一定要支援。
它一次最大能傳的資料大小為64 bytes,並且是雙向傳輸。基本上一個USB裝置一插上電腦,電腦就會透過Endpoint 0來問一些資料。
根據USB2.0規格,Host必須保留10%的頻寬給Control傳輸。
而Class Specific Endpoint則是我定義 In/Out Report 需要使用的Function,在USB HID Init 時我會初始化我的Endpoint
static uint8_t USBD_HID_Init (void *pdev,
uint8_t cfgidx)
{ /* Open EP IN */
DCD_EP_Open(pdev,
HID_IN_EP,
HID_IN_PACKET,
USB_OTG_EP_INT); /* Open EP OUT */
DCD_EP_Open(pdev,
HID_OUT_EP,
HID_OUT_PACKET,
USB_OTG_EP_INT); DCD_EP_PrepareRx(pdev, HID_OUT_EP, WRReport_buf, USB_HID_RECEIVE_FRAME_SIZE); return USBD_OK;
}
在Window API理頭,主要是利用 WriteFile/ReadFile 跟 HidD_GetInputReport / HidD_SetOutputReport 來收發資料。
在Firmware裡頭對應 WriteFile/ReadFile 的是 Class Specific Endpoint
而對應 HidD_GetInputReport / HidD_SetOutputReport 則是透過 Endpoint 0去對Firmware 做控制。
GetInputReport / SetOutputReport 在 Firmeware 需要在 USBD_HID_Setup 裡頭加上 Set/Get Report的Case
static uint8_t USBD_HID_Setup (void *pdev, USB_SETUP_REQ *req)
{
uint8_t USBD_HID_Report_LENGTH = ;
uint16_t len = ;
uint8_t *pbuf = NULL; switch (req->bmRequest & USB_REQ_TYPE_MASK)
{
case USB_REQ_TYPE_CLASS :
switch (req->bRequest)
{ case HID_REQ_SET_PROTOCOL:
USBD_HID_Protocol = (uint8_t)(req->wValue);
break; case HID_REQ_GET_PROTOCOL:
USBD_CtlSendData (pdev,
(uint8_t *)&USBD_HID_Protocol, );
break; case HID_REQ_SET_IDLE:
USBD_HID_IdleState = (uint8_t)(req->wValue >> );
break; case HID_REQ_GET_IDLE:
USBD_CtlSendData (pdev, (uint8_t *)&USBD_HID_IdleState, );
break; case HID_REQ_SET_REPORT:
/* HidD_SetOutputReport 1-byte = Report ID (IN) */
flag = ;
USBD_HID_Report_ID = (uint8_t)(req->wValue);
USBD_HID_Report_LENGTH = (uint8_t)(req->wLength);
USBD_CtlPrepareRx (pdev, WRReport_buf, USBD_HID_Report_LENGTH);
break; case HID_REQ_GET_REPORT:
/* HidD_GetInputReport 1-byte = Report ID (OUT) */
flag = ;
WRReport_buf[] = 0x11;
WRReport_buf[] = 0x22;
WRReport_buf[] = 0x33;
WRReport_buf[] = 0x44;
WRReport_buf[] = 0x55;
WRReport_buf[] = 0x66;
WRReport_buf[] = 0x77;
WRReport_buf[] = 0x88;
USBD_CtlSendData (pdev, (uint8_t *)&WRReport_buf, ); // to pc
break; default:
USBD_CtlError (pdev, req);
return USBD_FAIL;
}
break; case USB_REQ_TYPE_STANDARD:
switch (req->bRequest)
{
case USB_REQ_GET_DESCRIPTOR:
if( req->wValue >> == HID_REPORT_DESC)
{
len = MIN(CUSTOMHID_SIZ_REPORT_DESC , req->wLength);
pbuf = CustomHID_ReportDescriptor;
//pbuf = HID_MOUSE_ReportDesc; }
else if( req->wValue >> == HID_DESCRIPTOR_TYPE)
{
//#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
// pbuf = USBD_HID_Desc;
//#else
// pbuf = USBD_HID_CfgDesc + 0x12;
//#endif
pbuf = USBD_HID_CfgDesc + 0x12;
len = MIN(USB_HID_DESC_SIZ , req->wLength);
}
USBD_CtlSendData (pdev, pbuf, len);
break;
case USB_REQ_GET_INTERFACE :
USBD_CtlSendData (pdev, (uint8_t *)&USBD_HID_AltSet, ); break; case USB_REQ_SET_INTERFACE :
USBD_HID_AltSet = (uint8_t)(req->wValue);
break;
}
}
return USBD_OK;
}
這邊提及一下Firmware 發送 Class Specific Endpoint In/Out Report 是利用
DCD_EP_Tx (pdev, HID_IN_EP, report, len); // In Report
DCD_EP_PrepareRx(pdev, HID_OUT_EP, WRReport_buf, HID_OUT_PACKET);