usb总线驱动程序的作用
1. 识别设备
1.1 分配地址
1.2 并告诉USB设备(set address)
1.3 发出命令获取描述符
设备描述符的信息在 linux-2.6.22.6\include\linux\usb\Ch9.h
2 找到并安装对应的设备驱动
3 提供USB读写函数(不了解数据含义)
USB设备接到开发板上,看输出信息:
接上:
usb 1-1: new full speed USB device using s3c2410-ohci and address 2
usb 1-1: configuration #1 chosen from 1 choice
断开:
usb 1-1: USB disconnect, address 2
再接上
usb 1-1: new full speed USB device using s3c2410-ohci and address 3
usb 1-1: configuration #1 chosen from 1 choice
在内核目录(/work/system/linux-2.6.22.6/drivers)搜索
grep “USB device using ” * -nR
结果:
usb/core/hub.c:2186: "%s %s speed %sUSB device using %s and address %d\n",
通过hub.c的2186行 ,可以一步步找到总线驱动的编程思路:
hub_irq
kick_khubd
hub_thread
hub_events
hub_port_connect_change
udev = usb_alloc_dev(hdev, hdev->bus, port1);
dev->dev.bus = &usb_bus_type; //看结构图004
choose_address(udev) //选择地址,也就是给新的设备分配编号(地址)
hub_port_init //打印usb 1-1: new full speed USB device using s3c2410-ohci and address 2
hub_set_address //把地址告诉USB设备
usb_get_device_descriptor(udev, 8 )// 获得设备描述符,8字节的含义包括usb_device_descriptor前八个描述
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);//再次获得设备描述符
usb_new_device(udev) //新建一个USB设备
err - usb_get_configuration(udev);//把所有的描述符都读出来
usb_parse_configuration //解析
device_add//把device放入 usb_bus_type的dev链表,
//从usb_bus_type的driver链表里取出usb _driver
//把usb_interface和driver的id_tabe比较
//如果能匹配,调用usb _driver的probe
整体思路:
usb_bus_type
/ -------------\
usb_new_device(udev) usb_register
/ \
usb_interface usb _driver
id_tabe
probe
说明:
给新设备分配地址
static void choose_address(struct usb_device *udev)
{
int devnum;
struct usb_bus *bus = udev->bus;
/* If khubd ever becomes multithreaded, this will need a lock */
/* Try to allocate the next devnum beginning at bus->devnum_next. */
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
bus->devnum_next); //找下一个零位,一直找到第128个,插入USB设备分配地址2,下一个分配地址3...
if (devnum >= 128)
devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);//如果大于128,从头开始
bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
}
}
新建一个usb驱动
int usb_new_device(struct usb_device *udev)
{
int err;
/* Determine quirks */
usb_detect_quirks(udev);
err = usb_get_configuration(udev);//获得它的配置
if (err < 0) {
dev_err(&udev->dev, "can't read configurations, error %d\n",
err);
goto fail;
}
接口描述符
struct usb_interface_descriptor {
__u8 bLength;//设备描述符的字节数大小,为0x12
__u8 bDescriptorType;//描述符类型编号,为0x01
__u8 bInterfaceNumber;//接口的编号
__u8 bAlternateSetting;//备用的接口描述符编号
__u8 bNumEndpoints;//该接口使用端点数,不包括端点0
__u8 bInterfaceClass;//接口类型
__u8 bInterfaceSubClass;//接口子类型
__u8 bInterfaceProtocol;//接口所遵循的协议
__u8 iInterface;//描述该接口的字符串索引值
} __attribute__ ((packed));
怎样写usb设备驱动程序?
①分配/设置 USB_driver结构体
.id_tabe //支持哪些设备
.probe
.disconnect
②注册
现在使用USB鼠标用作按键 ,写一个简单usb框架:
左 :L 右:S 中:回车
① 分配input_dev
② 设置
③ 注册
④ 硬件操作
这4步在 probe 实现。
先参考内核中的usbmouse.c(drivers\hid\usbhid\usbmouse.c)
#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 usbmouse_as_key_init(void)
{
/* 注册 */
usb_register(&usbmouse_as_key_driver);
return 0;
}
static void usbmouse_as_key_exit(void)
{
/* 注销 */
usb_deregister(&usbmouse_as_key_driver);
}
module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);
第二部:
/* 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
.name = "usbmouse_as_key_", //设备名称
.probe = usbmouse_as_key_probe,
.disconnect = usbmouse_as_key_disconnect,
.id_table = usbmouse_as_key_id_table,//支持的设备
};
然后填充:
static struct usb_device_id usbmouse_as_key_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
/*支持某一款USB_DEVICE,在usb.h中的USB_DEVICE(vend,prod)*/
//{USB_DEVICE(0x1234,0x5678)},
{ } /* Terminating entry */
};
static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
printk("found usbmouse!\n");
return 0;
}
static void usbmouse_as_key_disconnect(struct usb_interface *intf)
{
printk("disconnect usbmouse!\n");
}
分析:
①
.id_table = usbmouse_as_key_id_table,//支持的设备
②
在usbmouse.c函数中去usb_mouse_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) },
{ } /* Terminating entry */
};
去宏 USB_INTERFACE_INFO
#define USB_INTERFACE_INFO(cl,sc,pr) \
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \
.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)
/*
match_flags匹配设备描述符的哪一项,INT_INFO是接口信息,cl是类,sc是子类,pr是协议。
所以,usb_mouse_id_table中,只要接口设备描述符的类是HID,子类是BOOT,协议是MOUSE 就可以支持
*/
这样就实现了一个usb驱动的框架构建。
测试:
1.make menuconfig 去掉原来的usb鼠标驱动
(cd /work/system/linux-2.6.22.6)
①
②
③
④
2.make uImage 并使用新的内核启动
3.cp arch/arm/boot/uImage /work/nfs_root/uImage_nohid
4.编译USB驱动程序,拷贝到/work/nfs_root/first_fs
5.使用新的uImage启动开发板
nfs 30000000 192.168.1.102:/work/nfs_root/uImage_nohid
bootm 30000000
mount -t nfs -o nolock,vers=2 192.168.1.102:/work/nfs_root/first_fs /mnt
cd /mnt
开发板,安装驱动 insmod usbmouse_as_key.ko
插拔鼠标可以看到如下结果:
参考usbmouse.c的usb_mouse_probe,通过usb_interface(设备接口)找到
struct usb_device *dev = interface_to_usbdev(intf);
查看usb_device结构体的设备描述符
struct usb_device_descriptor {
__u8 bLength;//设备描述符的字节数大小,为0x12
__u8 bDescriptorType;//描述符类型编号,为0x01
__le16 bcdUSB;//USB版本号
__u8 bDeviceClass;//USB分配的设备类代码,0x01~0xfe为标准设备类,0xff为厂商自定义类型
//0x00不是在设备描述符中定义的,如HID
__u8 bDeviceSubClass;//usb分配的子类代码,同上,值由USB规定和分配的
__u8 bDeviceProtocol;//USB分配的设备协议代码,同上
__u8 bMaxPacketSize0;//端点0的最大包的大小
__le16 idVendor;//厂商编号
__le16 idProduct;//产品编号
__le16 bcdDevice;//设备出厂编号
__u8 iManufacturer;//描述厂商字符串的索引
__u8 iProduct;//描述产品字符串的索引
__u8 iSerialNumber;//描述设备序列号字符串的索引
__u8 bNumConfigurations;//可能的配置数量
} __attribute__ ((packed));
在usb驱动程序的 usbmouse_as_key_probe 添加如下
static int usbmouse_as_key_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);// dev->descriptor.bcdUSB,dev结构体里面的设备描述符
printk("VID = 0x%x\n", dev->descriptor.idVendor);
printk("PID = 0x%x\n", dev->descriptor.idProduct);
}
测试结果:
①
②