SSブログ

macOSからPi Picoを使う - その16 [Pi Pico]

急遽読み出したTinyUSB。前回前々回とTinyUSBのディレクトリ構造をみた。Pi Picoのsrcディレクトリに比べればずっと簡単。examplesディレクトリはほぼ全部一緒。複雑なUSB classではやることも難しいけど、TinyUSBをPi Picoのc-sdkと一緒に動作させるのはexamplesを見てるとなんとなくわかるような気がした。今日はTinyUSBのumbrella headerの役目を果たしているtusb.hがどうなってるかを見る....

15.2.3  tusb.h

srcのトップにあるtusb.hには
//------------- DEVICE -------------//
#if TUSB_OPT_DEVICE_ENABLED
  #include "device/usbd.h"

  #if CFG_TUD_HID
    #include "class/hid/hid_device.h"
  #endif

  #if CFG_TUD_CDC
    #include "class/cdc/cdc_device.h"
  #endif

  #if CFG_TUD_MSC
    #include "class/msc/msc_device.h"
  #endif

などとなっていて、tusb_config.hに書いた対応すべきクラスのヘッダだけを読み込むようになっている。従ってクラスごとの関数定義は使わなければ読まれなくて、TinyUSB本体側ではクラス別関数はどこからも呼ばれない構造になっている。つまりあるクラスとしての動作はそのクラスを実装したいファイル(examplesではたいていmain.c)からクラス別の関数を呼ぶことで実現される、ということになる。

それほど親切ではない、とは言えるけど、軽量でシンプルな作りになっているようである。

15.2.4  クラス独自の関数はどうやって呼ばれるか

それぞれのクラスはmain.cから呼ばれるためのApplication APIという関数群と、TinyUSBの本体部分から呼ばれるためのInternal Class Driver APIという関数群がある。このInternal Class Driver APIを本体側が呼ぶときにそれらを直接呼べない。なぜなら使わないクラスの関数は定義されないので直接呼ぶとリンクで未解決になってしまう。

そこで、driver構造体というのがsrc/device/usbd_pvt.hに定義されていて
typedef struct
{
  #if CFG_TUSB_DEBUG >= 2
  char const* name;
  #endif

  void     (* init             ) (void);
  void     (* reset            ) (uint8_t rhport);
  uint16_t (* open             ) (uint8_t rhport,
                                  tusb_desc_interface_t const * desc_intf,
                                  uint16_t max_len);
  bool     (* control_xfer_cb  ) (uint8_t rhport,
                                  uint8_t stage,
                                  tusb_control_request_t const * request);
  bool     (* xfer_cb          ) (uint8_t rhport,
                                  uint8_t ep_addr,
                                  xfer_result_t result,
                                  uint32_t xferred_bytes);
  void     (* sof              ) (uint8_t rhport); /* optional */
} usbd_class_driver_t;
src/device/usbd.cに
static usbd_class_driver_t const _usbd_driver[] =
{
  #if CFG_TUD_CDC
  {
    DRIVER_NAME("CDC")
    .init             = cdcd_init,
    .reset            = cdcd_reset,
    .open             = cdcd_open,
    .control_xfer_cb  = cdcd_control_xfer_cb,
    .xfer_cb          = cdcd_xfer_cb,
    .sof              = NULL
  },
  #endif

  #if CFG_TUD_MSC
  {
    DRIVER_NAME("MSC")
    .init             = mscd_init,
    .reset            = mscd_reset,
    .open             = mscd_open,
    .control_xfer_cb  = mscd_control_xfer_cb,
    .xfer_cb          = mscd_xfer_cb,
    .sof              = NULL
  },
  #endif
となっている。つまりこの_usbd_driver配列はクラスごとのInternal Class Driver APIの関数へのポインタを保持していて、必要とされているものだけをこの配列に登録する、ということらしい。

このusbd_class_driver_t構造体のメンバは名前をみればわかるように初期化、リセット、オープンと、control transferとdata transferが来たときに呼ぶ関数へのポインタを並べたものである。

これで
static inline usbd_class_driver_t const * get_driver(uint8_t drvid);
で何番目かを指定してこの構造体を得るようになっている。別段びっくりするようなことをしているわけではなかった。
nice!(0)  コメント(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。