2011年9月27日 星期二

V-USB custom-class 範例解說

這篇將摘要說明 V-USB custom-class 範例的程式碼。要讀這篇,讀者必須對 USB 有足夠的認識。

customer-class 分成兩個部份:韌體和 PC 端的指令列工具 (commandline)

韌體

usbconfig.h

usbconfig.h 是 V-USB 的設定檔,每個 V-USB 的程式都會從 V-USB 的 usbconfig-prototype.h 複製過來並改名為 usbconfig.h 然後根據自己的硬體調整設定。

底下是 custom-class 對 usbconfig.h 所做的調整:

  • USB_CFG_IOPORTNAME, USB_CFG_DMINUS_BIT and USB_CFG_DPLUS_BIT

這些設定 USB bus D+ 和 D- 所用的接腳。預設是:

#define USB_CFG_IOPORTNAME      D
#define USB_CFG_DMINUS_BIT      4
#define USB_CFG_DPLUS_BIT       2

  • USB_CFG_VENDOR_ID and USB_CFG_DEVICE_ID

這些設定 VID 和 PID。預設是:

#define  USB_CFG_VENDOR_ID       0xc0, 0x16
#define  USB_CFG_DEVICE_ID       0xdc, 0x05

資料表示方式是 low byte first,VID 0x16c0 和 PID 0x05dc 是 obdev 提供的共享 VID/PID。

  • USB_CFG_DEVICE_NAME and USB_CFG_DEVICE_NAME_LEN

這些設定 Device Name 和資料長度,custom-class 把 Device Name 設成 "LEDControl",資料長度是 10:

#define USB_CFG_DEVICE_NAME     'L', 'E', 'D', 'C', 'o', 'n', 't', 'r', 'o', 'l'
#define USB_CFG_DEVICE_NAME_LEN 10

  • USB_CFG_DEVICE_CLASS = 0xff

這個設定 Device Class。USB 定義了一些常用的 device class,你可以在 USB Class Codes 這個頁面中找到。0xff 代表的是 "Vendor Specific",也就是廠商自行定義的 function。

request.h

這個檔案由 host software 和 device firmware 所共享,它定義 host 和 device 之間通訊的 request number。

custom-class 共定義了三個 request numbers:

  • #define CUSTOM_RQ_ECHO          0

這是對 device 要求送回 wValue 和 wIndex 的 request (Control-IN),用來測試通訊的可靠度。wValue 和 wIndex 只有在 USB Specficication 或 device class specification 有定義時才有意義。對於 Vendor request 而言,可以送任意的資料。

  • #define CUSTOM_RQ_SET_STATUS    1

設定 LED 狀態 (Control-OUT)。要求的 LED 狀態會放在 control transfer 的 wValue 欄位中,wValue 低位元組 Bit 0 用來控制 LED 的開關,1 是開,0 是關。control transfer 的 data stage 實際上沒有送出 OUT data (zero length data)。

  • #define CUSTOM_RQ_GET_STATUS    2

取得 LED 目前的狀態 (Control-IN)。這個 control transfer 在 data stage 中 device 會送 1 個 byte 的資料給 host,LED 的狀態會儲存在 data byte 的 BIT 0。

main.c

主程式分成 main() 和 usbFunctionSetup() 兩個函式。

main() 的邏輯很簡單,首先呼叫 usbInit() 初始化 V-USB driver,接著先做一個強迫 USB enumeration 的動作,然後呼叫 sei() 啟用中斷並進入 main loop。main loop 是一個無窮迴圈,在 main loop 中,必須呼叫 usbPoll() 讓 driver 處理工作。根據 V-USB 的說明,usbPoll() 至少每 50 ms 要跑一次。

usbFunctionSetup() 用來處理 endpoint 0 的 control message 中屬於 vendor 或 class 的 requests。它有一個參數: 一個指向 8 bytes 的 setup data 的指標。使用者必須檢查 request 種類並接收 (Control-OUT ) 或回傳 (Control-IN) 資料給 host。

這 8 bytes 的 setup data 的結構型別是 usbRequest_t,定義在 usbdrv.h:

最基本的 usbFunctionSetup() 是根據 wValue 的 request number 打開或關閉 LED,底下用 request number 1 示範:

底下是 custom-class的 usbFunctionSetup():

如你所見,程式邏輯只是透過 bRequest 判斷是哪一個 request number,然後再依 request number 執行對應的動作,看是要設定 LED 狀態、取得 LED 狀態或是做 ECHO Test,就這麼簡單。關於 request number,請參考前面 request.h 的說明。

指令列工具 (commandline)

指令列工具包含 opendevice.c 和 set-led.c。custom-class 把開啟 USB device 的功能集中到 opendevice.c。

要打開指定 VID/PID 的 USB device,只要呼叫 usbOpenDevice() 函式即可,如下:

custom-class 利用 libusb 的 usb_control_msg() 發送 control message,邏輯主要都寫在 set-led.c。usb_control_msg() 的原型為:

int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout);

以打開或關閉 LED 的 request 為例,呼叫方法為:

其中 USB_TYPE_VENDOR 指定 Vendor request,USB_RECIP_DEVICE 指定接收者為 Device,而 USB_ENDPOINT_OUT 則是指定傳輸型態為 Control-OUT transfer。isOn 為 1 代表開 LED,0 代表關 LED。

而查詢 LED 狀態的 request 為:

其中 USB_ENDPOINT_IN 指定傳輸型態為 Control-IN transfer。

關於 setup data 的資料結構,可參考 USB Specification 這張表格:

image 

延伸閱讀

0 意見: