承上篇,這篇將摘要說明 V-USB hid-data 範例程式。
hid-data 分成韌體和指令列工具 (commandline) 兩個部份。
韌體
usbconfig.h
底下是 hid-data 對 usbconfig.h 所做的調整:
- USB_CFG_IOPORTNAME, USB_CFG_DMINUS_BIT 與 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
- #define USB_CFG_HAVE_INTRIN_ENDPOINT 1
這個設定 interrupt-in endpoint 1,這是 HID device 的必要選項。
- #define USB_CFG_INTR_POLL_INTERVAL 100
這個設定 interrupt-in endpoint 1 的 poll interval,單位為 miliseconds,不能小於 10 ms。
- USB_CFG_VENDOR_ID 與 USB_CFG_DEVICE_ID
這些設定 VID 和 PID。預設是:
#define USB_CFG_VENDOR_ID 0xc0, 0x16
#define USB_CFG_DEVICE_ID 0xdf, 0x05
資料表示方式是 low byte first,VID 0x16c0 和 PID 0x05df 是 obdev 提供的共享 VID/PID。
- USB_CFG_DEVICE_NAME and USB_CFG_DEVICE_NAME_LEN
這些設定 Device Name 和資料長度,hid-data 把 Device Name 設成 "DataStore",資料長度是 9:
#define USB_CFG_DEVICE_NAME 'D', 'a', 't', 'a', 'S', 't', 'o', 'r', 'e'
#define USB_CFG_DEVICE_NAME_LEN 9
- USB_CFG_DEVICE_CLASS 與 USB_CFG_INTERFACE_CLASS
這些設定 Device Class 和 Interface Class。hid-mouse 的設定為:
#define USB_CFG_DEVICE_CLASS 0
#define USB_CFG_INTERFACE_CLASS 3
USB 有定義一些常用的 device class,可在 USB Class Codes 這個頁面中找到,0 代表由 interface 指定。而 interface class 定義 3 代表這個是 HID Class。
- #define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 22
這個設定 HID report descriptor 的資料長度。你必須在程式裏定義一個叫 usbHidReportDescriptor 的陣列,用來存放 report descriptor,而且陣列 size 要跟這個設定一致為 22 bytes 才行。
- USB_CFG_IMPLEMENT_FN_READ 與 USB_CFG_IMPLEMENT_FN_WRITE
這會設定呼叫 usbFunctionWrite() 來處理 control-out transfer,而用 usbFunctionRead() 來處理 control-in transfer:
#define USB_CFG_IMPLEMENT_FN_WRITE 1
#define USB_CFG_IMPLEMENT_FN_READ 1
main.c
主程式分成 main() 和 usbFunctionSetup() 兩個函式。關於 main() 和 usbFunctionSetup() 兩個函式的說明,請參考「V-USB custom-class 範例解說」一文。
在這個範例程式中,main loop 的工作很單純,只是不斷呼叫 usbPoll() 讓 driver 處理工作。根據 V-USB 的說明,usbPoll() 至少每 50 ms 要跑一次。
底下是 hid-data的 usbFunctionSetup():
程式的邏輯是透過 bRequestRequest 判斷是不是 class request,假如是,再依 bRequest 判斷是哪一種 class request (Get Report, Set Report, Get Idle, Set Idle, Get Protocol 或 Set Protocol)。hid-data 只處 Get Report 和 Set Report 兩個 requests。在這個範例中,Get Report 是傳回 feature report 給 Host,而 Set Report 則是接收來自 host 端的 feature report。因為只有一個 feature report,所以 Get/Set Reports 沒有進一步判斷 ReportType (wValue high byte) 和 Report ID (wValue low byte)。
當收到 Get Report 或 Set Report 的 request 時,usbFunctionSet() 並沒有馬上把資料傳給 Host,而是回傳 USB_NO_MSG 讓 V-USB Driver 呼叫 usbFunctionRead() 和 usbFunctionWrite() 來處理。
usbFunctionRead() 的實作如下:
只是單純從 eeprom 讀取一塊資料 (data chunk) 並放到 buffer 給 Driver 處理。
而 usbFunctionWrite() 的實作如下:
也只是單純把從 host 收到的一塊資料 (data chunk) 寫到 eeprom。
HID Report Descriptor
底下是 hid-data 所定義的 HID Report Descriptor:
上列定義一個 Feature Report,資料長度為 128 bytes。
指令列工具 (commandline)
指令列工具包含 hiddata.c 和 hidtool.c。hid-data 把開啟 USB device 以及 Get Report 和 Set Report 的功能集中到 hiddata.c。
主程式 hidtool.c 只是間接呼叫 hiddata.c 中的 usbhidGetReport() 和 usbhidSetReport(),假如是 usbhidGetReport() 會將收到的資料以 hex format 印出,除此之外,沒有值得一提的地方。
hid-data 利用 usb_control_msg() 發送 control message,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);
以 Get Feature Report 為例,呼叫方法為:
其中 USB_TYPE_CLASS 指定 Class request,USB_RECIP_DEVICE 指定接收者為 Device,而 USB_ENDPOINT_IN 則是指定 Control-IN transfer。USBRQ_HID_GET_REPORT 表示 Get Report 的 request,而 wValue 高位元組為 USB_HID_REPORT_TYPE_FEATURE 表示要取 Feature Report,而低元組是 report number,在此為 0。
而 Set Feature Report 的呼叫方法則為:
其中 USB_ENDPOINT_OUT 指定 Control-OUT transfer。
延伸閱讀