USB 总线驱动程序,在接入 USB 设备时,会帮我们构造一个新的 usb_dev 注册到“usb_bus_type”里去。这部分是内核做好的。我们要做的是,构造一个 usb_driver (驱动)结构体,注册到“usb_bus_type”中去。
在“usb_driver”结构体中有“id_table”表示他能支持哪些设备,当 USB 设备能匹配 id_table 中某一个设备时,就会调用“usb_driver”结构体中的“.probe”(自已确定在 probe 中做的事情)等函数,如当拔掉USB 设备时,就会调用其中的“.disconnect”函数。
usb_bus_type”USB 总线驱动设备模型只是提供了这一种框架而已。在".probe"函数里面,注册“字符设备”也好,注册个“input_dev”结构体也好,再或注册一个块设备也好。再或只是加了句打印也好,都可以由自已确定。
怎么写 USB 设备驱动程序?
- 分配/设置 usb_driver 结构体
.name :usb_driver :驱动结构体名字
.id_table :表示能支持的设备
.probe :表示“USB 总线驱动程序”发现一个新设备后,就会与 usb_driver 结构体比较,若 id_table 表示能支持它,就调用.probe 函数。
.disconnect :拔掉 USB 设备时调用这个函数。 - 注册
usb_register(&usb_driver );
例:
/*12.usb设备驱动框架*/
#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>
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
printf("ni hao wo shi yi ge shubiao!!!\n");
return 0;
}
static int usb_mouse_disconnect(struct usb_interface *intf)
{
printf("bye bye123!!!\n");
}
static struct usb_device_id usb_mouse_id_table [] = {
{USB_INTERFACE_INFO( //接口描述符信息
USB_INTERFACE_CLASS_HID, //接口类:HID类
USB_INTERFACE_SUBCLASS_BOOT, //子类:启动设备类
USB_INTERFACE_PROTOCOL_MOUSE)}, //协议:鼠标协议
}; //意思是:要匹配“接口描述符”里的,接口类,子类,协议是否一致
static struct usb_driver usb_mouse = {
.name = "usb_mouse",
.probe =usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
};
static int usb_mouse_init(void)
{
usb_register(&usb_mouse);
return 0;
}
static void usb_mouse_exit(void)
{
usb_deregister(&usb_mouse);
}
module_init(usb_mouse_init);
module_exit(usb_mouse_exit);
MODULE_LICENSE("GPL");
*一:id_table
static struct usb_device_id usb_mouse_id_table [] = {
{USB_INTERFACE_INFO( //接口描述符信息
USB_INTERFACE_CLASS_HID, //接口类:HID类
USB_INTERFACE_SUBCLASS_BOOT, //子类:启动设备类
USB_INTERFACE_PROTOCOL_MOUSE)}, //协议:鼠标协议
}; //意思是:要匹配“接口描述符”里的,接口类,子类,协议是否一致
发现它是通过USB_INTERFACE_INFO()这个宏定义的.该宏如下所示:
#define USB_INTERFACE_INFO(cl,sc,pr) \
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \ //设置id_table的.match_flags成员
.bInterfaceClass = (cl), .bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)
//设置id_table的3个成员,用于与匹配USB设备的3个成员
“.match_flags”:表示要匹配“设备描述符”中的哪一项。(即接口描述符)
“USB_DEVICE_ID_MATCH_INT_INFO”:INT 是接口意思,INT_INFO 就是指接口的信息。接口的信息就在“接口描述
符”里。
“.bInterfaceClass”:只要“接口描述符”里的“类”是(cl)这个东西。
“.bInterfaceSubClass”:子类是 (sc) 这个东西。
“.bInterfaceProtocol”:协议是 (pr) 这个东西。
只要这个 USB 设备里的“接口描述符”里面的“类”是“HID”类;“子类”是“BOOT”;
“协议”是“MOUSE”,那么这个 id_table 就能支持。
若是想支持“某一款”设备,即“厂家 ID”-VID,“设备 ID”-PID 这种情况:则可以这样写:
static struct usb_device_id usb_mouse_id_table [] = {
{USB_INTERFACE_INFO( //接口描述符信息
USB_INTERFACE_CLASS_HID, //接口类:HID类
USB_INTERFACE_SUBCLASS_BOOT, //子类:启动设备类
USB_INTERFACE_PROTOCOL_MOUSE)}, //协议:鼠标协议
{USB_DEVICE(0x12d1,0x1001)}, //设备的 VID 是“0x12d1”,PID 是“0x1001”,
};
二:probe 函数
Usb_driver" 结构体是支持 某个 “usb_interface”结构体的某个接口。
一个 USB 硬件可能有多个逻辑上的设备,这些逻辑上的设备就是用“usb_interface”结构表示的。如一块“声卡”有“录音”和“播放”两个“逻辑接口”,则要两个驱动程序。
先暂时什么也不做,只printk打印输出一下。
三:disconnect函数
拔掉 USB 设备时调用这个函数,只printk打印输出一下。
四:测试
make menuconfig 去掉原来的 USB 鼠标驱动,不然一接 USB 鼠标内核会找到原来的驱动。新注册上的驱动还轮不上使用。
-> Device Drivers
-> HID Devices
<> USB Human Interface Device (full HID) support 此项不选中先不要内核中的驱动。
使用新内核启动
1.简单测试
在开发板上接入、拔出 USB 鼠标查看是否有打印输出信息。
二.在 probe 函数中打印 厂家 ID 和 设备 ID:
VID 和 PID 都是在 USB“设备描述符”中的。当接入一个 USB 设备后,USB 总线驱动程序已经把这些“设备描述符”全部读了出来。直接使用即可。
1.得到“usb_device”(USB 设备)结构体
struct usb_device *dev=interface_to_usbdev(intf);
通过usb_ interface接口获取usb_device设备,为后面设置USB数据传输用
2.获取厂家 ID 和 设备 ID
获取厂家 ID 和 设备 ID,在USB设备描述符(usb_device_descriptor )结构体内,而USB设备描述符位于USB设备结构体usb_device中的成员descriptor中。
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
printk("bcdUSB = %x\n", dev->descriptor.bcdUSB);
printk("VID = 0x%x\n", dev->descriptor.idVendor);
printk("PID = 0x%x\n", dev->descriptor.idProduct);
return 0;
}