Consider, before we move on, that we could have presented some different pair of callback structures (along with code to go with them). Such a change would be the way we could implement a different USB class, such as an HID or mass storage device. Being able to do this is exactly why things are done this way. Often such a scheme is called "registering a driver" and is important to know about.
CDC_IF_Prop_TypeDef VCP_fops = { VCP_Init, VCP_DeInit, VCP_Ctrl, VCP_DataTx, VCP_DataRx };The structure (and corresponding typedef) are set up in library/usbd_cdc_core.h as follows:
typedef struct _CDC_IF_PROP { uint16_t (*pIf_Init) (void *pdev); uint16_t (*pIf_DeInit) (void); uint16_t (*pIf_Ctrl) (uint32_t Cmd, uint8_t* Buf, uint32_t Len); uint32_t (*pIf_DataTx) (const uint8_t* Buf, uint32_t Len); uint16_t (*pIf_DataRx) (uint8_t* Buf, uint32_t Len); } CDC_IF_Prop_TypeDef; extern USBD_Class_cb_TypeDef USBD_CDC_cb;This thing is referenced via the following alias (which is in vcp/usbd_conf.h
#define APP_FOPS VCP_fopsThis is referenced only in library/usbd_cdc_core.c, as follows:
./library/usbd_cdc_core.c: #includeNote the include that pulls in usb_conf.h from the vcp directory. This is what makes the connection between the generic CDC code in usbd_cdc_core.c and the VCP specific code. The connection between CDC_ACM and the VCP code is "wired in" by this arrangement, yet localized in such a way that changes could be easily made../library/usbd_cdc_core.c:extern CDC_IF_Prop_TypeDef APP_FOPS; ./library/usbd_cdc_core.c: APP_FOPS.pIf_Init(pdev); ./library/usbd_cdc_core.c: APP_FOPS.pIf_DeInit(); ./library/usbd_cdc_core.c: APP_FOPS.pIf_Ctrl(req->bRequest, CmdBuff, req->wLength); ./library/usbd_cdc_core.c: APP_FOPS.pIf_Ctrl(req->bRequest, (uint8_t*)&req->wValue, sizeof(req->wValue)); ./library/usbd_cdc_core.c: APP_FOPS.pIf_Ctrl(cdcCmd, CmdBuff, cdcLen); ./library/usbd_cdc_core.c: if ( APP_FOPS.pIf_DataRx(USB_Rx_Buffer, USB_Rx_Cnt)==USBD_OK )
USBD_DCD_INT_cb_TypeDef USBD_DCD_INT_cb = { USBD_DataOutStage, USBD_DataInStage, USBD_SetupStage, USBD_SOF, USBD_Reset, USBD_Suspend, USBD_Resume, USBD_IsoINIncomplete, USBD_IsoOUTIncomplete, #ifdef VBUS_SENSING_ENABLED USBD_DevConnected, USBD_DevDisconnected, #endif }; USBD_DCD_INT_cb_TypeDef *USBD_DCD_INT_fops = &USBD_DCD_INT_cb;The above loads values (specific functions) into the structure. The structure (and typedef) are defined in driver/usb_dcd_int.h
typedef struct _USBD_DCD_INT { uint8_t (* DataOutStage) (USB_OTG_CORE_HANDLE *pdev , uint8_t epnum); uint8_t (* DataInStage) (USB_OTG_CORE_HANDLE *pdev , uint8_t epnum); uint8_t (* SetupStage) (USB_OTG_CORE_HANDLE *pdev); uint8_t (* SOF) (USB_OTG_CORE_HANDLE *pdev); uint8_t (* Reset) (USB_OTG_CORE_HANDLE *pdev); uint8_t (* Suspend) (USB_OTG_CORE_HANDLE *pdev); uint8_t (* Resume) (USB_OTG_CORE_HANDLE *pdev); uint8_t (* IsoINIncomplete) (USB_OTG_CORE_HANDLE *pdev); uint8_t (* IsoOUTIncomplete) (USB_OTG_CORE_HANDLE *pdev); uint8_t (* DevConnected) (USB_OTG_CORE_HANDLE *pdev); uint8_t (* DevDisconnected) (USB_OTG_CORE_HANDLE *pdev); }USBD_DCD_INT_cb_TypeDef; extern USBD_DCD_INT_cb_TypeDef *USBD_DCD_INT_fops;Note that the structure and typedef are defined in the driver layer. The driver needs to be able to call these to send data "up" to the general USB code in the "library" layer. Indeed when we search on USBD_DCD_INT_fops we find that all of the calls are in one file: driver/usb_dcd_int.c
./driver/usb_dcd_int.c:-- USBD_DCD_INT_fops->DataOutStage(pdev , 1); ./driver/usb_dcd_int.c:-- USBD_DCD_INT_fops->DataInStage(pdev , 1); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->DevConnected (pdev); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->DevDisconnected (pdev); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->Resume (pdev); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->Suspend (pdev); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->DataInStage(pdev , epnum); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->DataOutStage(pdev , epnum); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->SetupStage(pdev); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->SOF(pdev); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->Reset(pdev); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->IsoINIncomplete (pdev); ./driver/usb_dcd_int.c: USBD_DCD_INT_fops->IsoOUTIncomplete (pdev);All of this is in response to USB events that generate interrupts. The file usb_dcd_int.c has one entry point exposed to the outside world (for our hardware), namely:
STM32_USBF_OTG_ISR_Handler () --- Handles all USB Interrupts
Tom's Computer Info / tom@mmto.org