たとえば送受信の場合に何が呼び出されるか? というと、10回目に示したUSBD_CDC_cbの定義の方が使われるようだ。だから、受信時にはusbd_cdc_DataIn()が、送信時にはusbd_cdc_DataOut()がそれぞれ呼び出される形となる。これらの関数を定義しているusbd_cdc_core.cはUSBのCDC ClassのDriverなのでここに手を入れることはできないが、冒頭のコメントに、

@note
  For the Abstract Control Model, this core allows only transmitting the
  requests to lower layer dispatcher (ie. usbd_cdc_vcp.c/.h) which should
  manage each request and perform relative actions.

といった記載がある(改行位置をちょっといじってます)。調べてみると、

extern CDC_IF_Prop_TypeDef  APP_FOPS;

という宣言があり、たとえば初期化のusbd_cdc_Init()の中には、

  APP_FOPS.pIf_Init();

といった形でこのAPP_FOPSの中の関数を呼び出している。usbd_cdc_DataIn()そのものはこのAPP_FOPS内の関数を呼び出していないが、読み込みなら全データを読み込んだあとに呼び出されるusbd_cdc_EP0_RxReady()は、

/**
  * @brief  usbd_cdc_EP0_RxReady
  *         Data received on control endpoint
  * @param  pdev: device device instance
  * @retval status
  */
static uint8_t  usbd_cdc_EP0_RxReady (void  *pdev)
{ 
  if (cdcCmd != NO_CMD)
  {
    /* Process the data */
    APP_FOPS.pIf_Ctrl(cdcCmd, CmdBuff, cdcLen);

    /* Reset the command variable to default value */
    cdcCmd = NO_CMD;
  }

  return USBD_OK;
}

といった具合に、APP_FOPS.pIF_Ctrl()という関数を呼んでいるし、usbd_cdc_DataOut()の方は、

/**
  * @brief  usbd_cdc_DataOut
  *         Data received on non-control Out endpoint
  * @param  pdev: device instance
  * @param  epnum: endpoint number
  * @retval status
  */
static uint8_t  usbd_cdc_DataOut (void *pdev, uint8_t epnum)
{      
  uint16_t USB_Rx_Cnt;

  /* Get the received data buffer and update the counter */
  USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count;

  /* USB data will be immediately processed, this allow next USB traffic being 
     NAKed till the end of the application Xfer */
  APP_FOPS.pIf_DataRx(USB_Rx_Buffer, USB_Rx_Cnt);

  /* Prepare Out endpoint to receive next packet */
  DCD_EP_PrepareRx(pdev,
                   CDC_OUT_EP,
                   (uint8_t*)(USB_Rx_Buffer),
                   CDC_DATA_OUT_PACKET_SIZE);

  return USBD_OK;
}

てな具合でAPP_FOPS.pIf_DataRx()を呼び出している。

このusbd_cdc_core.cでextern宣言で参照しているAPP_FOPSの実体は? というと、usbd_conf.hで宣言している、

#define APP_FOPS                        VCP_fops

である。このVCP_fopsは? というと、usb_cdc_vcp.cの中で、

CDC_IF_Prop_TypeDef VCP_fops = 
{
  VCP_Init,
  VCP_DeInit,
  VCP_Ctrl,
  VCP_DataTx,
  VCP_DataRx
};

という宣言がある。やっとここでVCP Driverにたどりついたわけだ。

ではこのVCP Driverは何をやっているか? たとえばVCP_DataRx()は

static uint16_t VCP_DataRx (uint8_t* Buf, uint32_t Len)
{
  uint32_t i;

  for (i = 0; i < Len; i++)
  {
    USART_SendData(EVAL_COM1, *(Buf + i) );
    while(USART_GetFlagStatus(EVAL_COM1, USART_FLAG_TXE) == RESET); 
  } 

  return USBD_OK;
}

となっていて、なにやらUSARTと絡んでいる。で、ここでマニュアルを見てみることにする。STM32F105xx, STM32F107xx, STM32F2xx and STM32F4xx USB On-The-Go host and device library User manual(UM1021)には、こんな図が掲載されている(Photo01)。要するにVCP Driverの中でUSARTとUSBの間でデータのやり取りをしているわけだ。今回の目的では別にUSARTとデータ交換する必要はないので、ここに手をいれればPC側とUSBで通信ができそうだ。

Photo01: 同マニュアルより抜粋。いくつか転送モードはあるが、基本的にはUSBとシリアルポート(USART)の間でデータを受け渡しするというサンプルになっている。

(続く)