usb總線驅動模型裏也有struct usb_driver和struct usb_device,這點與platform虛擬總線裏的struct platform_device和struct platform_driver相似。不同之處是
platform模型中,需要寫兩個驅動文件,分別是driver.ko和device.ko,然後分別insmod。
usb驅動只需要寫一個驅動文件driver,只insmod driver.ko即可,為什麼不insmod device.ko了呢?因為沒寫,恩,不必寫。因為usb是實際的總線,當有usb設備插入的時候usbcore就會自動對該設備執行枚舉,枚舉之後usbcore就獲知了該設備的相關資源,然後把資源信息返回給能匹配的驅動。
簡言之,
平台驅動的insmod device.ko是一個模仿usb設備插入的動作,device.ko中記錄了資源信息,通過insmod device,平台核心獲取device的資源信息返回給匹配的驅動。
usb驅動模型中資源信息在儲存在設備中呢,在usb設備插入時通過枚舉,usbcore把資源信息讀出來塞進返回給匹配的驅動。
怎麼匹配的呢?對於platform是通過driver的名字和device的名字匹配的。對於usb模型,us_driver中有usb_device_id table,記錄該驅動可以處理的設備 的idVerdor和idProduct
當usb設備插入時,usbcore通過枚舉過程獲知當前設備的信息,包括兩個id,然後和註冊在usb總線上的各個usb驅動裏的usb_device_id table比較,有相同的的話,就調用哪個驅動的probe函數,並把該設備的資源信息(inteface)作為參數交給probe.這樣哪個驅動就達到處理這個設備的目的了.
當然,先插入usb設備再insmod driver.ko,這種情況下去匹配兩者的實現跟上面類似。
一下是linux/drivers/hid/usbhid/usbmouse.c裏的一個usb_device_id table
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) },
{ }
};
在linux/include/hid.h
#define USB_INTERFACE_CLASS_HID 3//在usb協議的接口描述符的bInterfaceClass字段如果為3,表示該設備為hid設備
#define USB_INTERFACE_SUBCLASS_BOOT 1//在usb協議的接口描述符的bInterfaceSubClass字段如果為1,表示是hid的子類:支持bios引導的hid
#define USB_INTERFACE_PROTOCOL_MOUSE 2//在usb協議的接口描述符的bInterfaceProtocol字段如果為2,表示協議為鼠標(在支持bios引導時用的,鍵盤協議為1)
USB_INTERFACE_INFO在linux/include/usb.h中定義,如下
#define USB_INTERFACE_INFO(cl, sc, pr) \
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \
.bInterfaceClass = (cl), \
.bInterfaceSubClass = (sc), \
.bInterfaceProtocol = (pr)
所以它表示只要是hid設備,且支持bios引導,且引導時的協議為mouse,則此驅動都可以處理。
如果只聲明支持一個設備可以用如下宏
linux/include/usb.h
#define USB_DEVICE(vend,prod) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
.idVendor = (vend), \
.idProduct = (prod)
比如在usb_device_id裏 添加一項,USB_DEVICE(0606,0001)
*****************************************************************************************************************************************************************************
一下是一個最簡單的usb測試程序(比較完整的見linux-2.6.32.2/drivers/usb/usb-skeleton.c),僅測試一下usbcore對設備枚舉玩,是否能夠成功和此驅動匹配而進入probe函數
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
/*
* Version Information
*/
#define DRIVER_VERSION "v1.0"
#define DRIVER_AUTHOR "song"
#define DRIVER_DESC "usb test"
#define DRIVER_LICENSE "GPL"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
static int test_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
printk("probe\n");
return 0;
}
static void test_usb_disconnect(struct usb_interface *intf)
{
printk("disconnect\n");
}
static struct usb_device_id test_usb_id_table [] = {
{USB_DEVICE(0x0606,0x0001) },
{}
};
MODULE_DEVICE_TABLE (usb, test_usb_id_table);
static struct usb_driver test_usb_driver = {
.name = "test_song",
.probe = test_usb_probe,
.disconnect = test_usb_disconnect,
.id_table = test_usb_id_table,
};
static int __init test_usb_init(void)
{
int retval = usb_register(&test_usb_driver);
if (retval == 0)
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
return retval;
}
static void __exit test_usb_exit(void)
{
usb_deregister(&test_usb_driver);
}
module_init(test_usb_init);
module_exit(test_usb_exit);
makefile
ifneq ($(KERNELRELEASE),)
obj-m := test_usb.o
else
KDIR := /opt/FriendlyARM/mini2440/linux-2.6.32.2
#KDIR := /lib/modules/`uname -r`/build
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif
而設備側(我的是sic8051f320單片機,內含usb從接口)的設備描述符和接口描述符,報告描述符設置如下,一下是一個自定義hid設備的部分描述符的設置
code DEVICE_DESCRIPTOR DeviceDesc =
{
18, // bLength
0x01, // bDescriptorType
SWAP16(0x0200), // bcdUSB
0x00, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
EP0_PACKET_SIZE, // bMaxPacketSize0
SWAP16(USB_HID_VID), // idVendor 0x0606 和linux側驅動中的對應
SWAP16(USB_HID_PID), // idProduct 0x0001 和linux側驅動中的對應
0x0001, // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x03, // iSerialNumber
0x01 // bNumConfigurations
}; //end of DeviceDesc
{
0x09, // bLength
0x04, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x03, // bInterfaceClass 指定為hid類
0x00, // bInterfaceSubClass 非鼠標鍵盤 0
0x00, // bInterfaceProcotol 非鼠標鍵盤 0
0x00 // iInterface
}, //end of InterfaceDesc
//Report descriptor
code unsigned char ReportDesc[] =
{
// 0x06, 0x00, 0xFF,// ; Usage Page
0x05, 0x01, //桌面設備
// 0x09, 0x01,// ; Usage
0x09, 0x00, //未定義用途,結合接口描述符的bInterfaceClass=03,則為自動以hid
0xA1, 0x01, // ; Collection//用未定義用途開集合
0x09, 0x01,// ; Usage
0x95, 0x20,// ; Report Count
0x75, 0x08,// ; Report Size
0x15, 0x00,// ; Logical Minimum
0x26, 0xFF, 0x00,// ; Logical Maximum
0x81, 0x02,// ; Input
0x09, 0x02,// ; Usage
0x95, 0x20,// ; Report Count
0x75, 0x08,// ; Report Size
0x91, 0x02,// ; Output
0xC0
};
在板子上,先不要插usb設備
[root@FriendlyARM plg]# insmod test_usb.ko
usbcore: registered new interface driver test_song
test_usb: v1.0:usb test
插上設備,信息如下,出現probe信息,説明成功匹配到驅動
[root@FriendlyARM plg]# usb 1-1.1: new full speed USB device using s3c2410-ohci and address 8
usb 1-1.1: New USB device found, idVendor=0606, idProduct=0001//廠商和產品id。不要受迷惑,是0x0606和0x0001
usb 1-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-1.1: Product: nRF24LU1 ADapter
usb 1-1.1: Manufacturer: Weisdigital
usb 1-1.1: SerialNumber: Ver1.0 N000
usb 1-1.1: configuration #1 chosen from 1 choice//以上這些信息由usbcore在枚舉完設備打印出來
probe
拔出設備,觸發函數test_usb_disconnect,信息如下
usb 1-1.1: USB disconnect, address 8
disconnect
****************************************************************************************************************************************************************
另外在mini2440上已經有一個配置,
Device Drivers ---> [*] HID Devices ---> <*> USB Human Interface Device (full HID) support
這個驅動貌似通吃所有hid設備,包括mouse kbd,自定義hid等。所以如果我插入我的自定義hid設備時,就會被usbcore把我的設備優先匹配給這個驅動(即使我自己寫的驅動已經insmod),這個驅動會在其probe函數中為我的設備自動創建一個input驅動,設備文件在/dev/input/eventx(x=1 2 3 ...)。但是我的設備沒有按鼠標或鍵盤的數據格式給主機usbcore傳送數據(當然我也不想這樣傳輸,因為我的設備是自定義hid設備,當然要自定義的數據及格式,當然也不想讓這個驅動去處理我的設備發來的數據)。
目前的解決辦法是把這個選項給禁掉。這樣usbcore就會把我的自定義hid設備匹配給我自己寫的驅動了。
當然禁掉之後,usb鼠標 鍵盤都不能用了。不過可以配置一下另外一個鼠標和鍵盤的驅動,也可用的。如下
Device Drivers ---> [*] HID Devices --->USB HID Boot Protocol drivers ---> <*> USB HIDBP Keyboard (simple Boot) support
<*> USB HIDBP Mouse (simple Boot) support
不過要想出現USB HID Boot Protocol drivers項目,需要一些操作,在Kconfig文件line 47如下
menu "USB HID Boot Protocol drivers"
depends on USB!=n && USB_HID!=y && EMBEDDED
即
USB符號不要選擇為空(要選擇m或y),USB_HID符號不要選擇為y。最後那個符號EMBEDDED暫時沒找到在哪裏配的,就刪掉了。即
menu "USB HID Boot Protocol drivers"
depends on USB!=n && USB_HID!=y
按照上面設置以後就會make menuconfig裏就會出現USB HID Boot Protocol drivers項目。
再看一下/linux-2.6.32.2/drivers/hid/usbhid/Makefile,
# Multipart objects.
usbhid-objs := hid-core.o hid-quirks.o
# Optional parts of multipart objects.
ifeq ($(CONFIG_USB_HIDDEV),y)
usbhid-objs += hiddev.o
endif
ifeq ($(CONFIG_HID_PID),y)
usbhid-objs += hid-pidff.o
endif
obj-$(CONFIG_USB_HID) += usbhid.o
obj-$(CONFIG_USB_KBD) += usbkbd.o
obj-$(CONFIG_USB_MOUSE) += usbmouse.
可知
<*> USB Human Interface Device (full HID) support 對應的驅動應該是/linux-2.6.32.2/drivers/hid/usbhid/usbhid.c
<*> USB HIDBP Keyboard (simple Boot) support 對應的驅動是/linux-2.6.32.2/drivers/hid/usbhid/usbkbd.c
<*> USB HIDBP Keyboard (simple Boot) support 對應的驅動是/linux-2.6.32.2/drivers/hid/usbhid/usbmouse.c
後兩者還好説,目錄下確實有usbkbd.c和usbmouse.c,但是沒有usbhid.c文件,那麼usbhid.o哪裏來的呢?恩,沒人規定usbhid.o非得只由usbhid.c才能生成。
同目錄下有一個文件.usbhid.o.cmd ,記錄了usbhid.o由hid-quirks.o和hid-core.o生成。目前為止感覺很麻煩の。
arm-linux-ld -EL -r -o drivers/hid/usbhid/usbhid.o drivers/hid/usbhid/hid-core.o drivers/hid/usbhid/hid-quirks.o