menu "USB HID Boot Protocol drivers"
depends on USB!=n && USB_HID!=y
config USB_KBD
tristate "USB HIDBP Keyboard (simple Boot) support"
depends on USB && INPUT
---help---
Say Y here only if you are absolutely sure that you don't want
to use the generic HID driver for your USB keyboard and prefer
to use the keyboard in its limited Boot Protocol mode instead.
This is almost certainly not what you want. This is mostly
useful for embedded applications or simple keyboards.
To compile this driver as a module, choose M here: the
module will be called usbkbd.
If even remotely unsure, say N.
config USB_MOUSE
tristate "USB HIDBP Mouse (simple Boot) support"
depends on USB && INPUT
---help---
Say Y here only if you are absolutely sure that you don't want
to use the generic HID driver for your USB mouse and prefer
to use the mouse in its limited Boot Protocol mode instead.
This is almost certainly not what you want. This is mostly
useful for embedded applications or simple mice.
To compile this driver as a module, choose M here: the
module will be called usbmouse.
If even remotely unsure, say N.
endmenu
选中以后,make zImage好了,刻录研发板,重启,能够看到检测到鼠标驱动了,原来的报出的错误没有了,但是第二天我发现USB鼠标没有在/dev目录下,所以我重新把Device Drivers -->进入 选中HID Devices ---> 进入后,将USB的选项调整成这样,
│ │ *** USB Input Devices *** │ │
│ │ USB Human Interface Device (full HID) support │ │
│ │ [ ] Enable support for iBook/PowerBook/MacBook/MacBookPro spe│ │
│ │ [ ] Force feedback support (EXPERIMENTAL) │ │
│ │
- /dev/hiddev raw HID device support
因为选中了USB Human Interface Device (full HID) support这一项, 所以USB_MOUSE的选项被关闭了,我同时选中了对 - /dev/hiddev raw HID device support 的项,编译时能够看到:
。。。。。。
CC drivers/hid/hid-core.o
LD drivers/hid/hid.o
CC drivers/hid/usbhid/hid-core.o
CC drivers/hid/usbhid/hiddev.o
LD drivers/hid/usbhid/usbhid.o
LD drivers/hid/usbhid/built-in.o
。。。。。。
刻录,重启研发板:
。。。。。。
usb 1-1: new low speed USB device using s3c2410-ohci and address 2
usb 1-1: configuration #1 chosen from 1 choice
usbcore: registered new interface driver hiddev
input: USB Mouse as /class/input/input0
input: USB HID v1.10 Mouse [USB Mouse] on usb-s3c24xx-1
usbcore: registered new interface driver usbhid
drivers/hid/usbhid/hid-core.c: v2.6:USB HID core driver
。。。。。。
手动创建节点mknod mouse c 13 32,测试鼠标cat mouse,移动鼠标有数据打印出来。
Sam需要研究一下USB Mouse device driver. 在Kernel 2.4时代,这块东西是由Input,event等配合组成的。而在kernel2.6中。Sam还不知道是如何运作的。
首先介绍HID:
HID是Human Interface Devices的缩写.翻译成中文即为人机交互设备.这里的人机交互设备是一个宏观上面的概念,任何设备,只要符合HID spec,都可以称之为HID设备
在make menuconfig中,选中USB Human Interface Device(full HID) support。则所有USB HID都会被驱动,其中包括USB Mouse。
在drivers/hid/usbhid/Kconfig看到这项对应的为:CONFIG_USB_HID
又在drivers/hid/usbhid/Makefile中看到:obj-$(CONFIG_USB_HID) += usbhid.o换句话说,如果选中built-in.则 obj-y中加入usbhid.o
如果将这一项选中为M(module),则obj-m中加入usbhid.o
也就是说:如果选中USB Human Interface Device(full HID) support为built-in.则usbhid.o会被built-in。
再在drivers/hid/usbhid/.usbhid.o.cmd中看到usbhid.o其实是由:hid-core.o,hid-quirks.o,hiddev.o组成的。
也就是说:当在Menuconfig中选中那一项后,这三个.o都会被built-in.在drivers/hid/usbhid/hid-core.c中,有如下语句:
module_init(hid_init);
表明当hid-usb.o(hid-core.o等三个组成)添加入kernel core时,会调用hid_init.
1. hid_init分析:
hid_init首先调用usbhid_quirks_init();
1.1. usbhid_quirks_init() 解析:
其实就是查找insmod 时给的pid,vid参数在quirks列表中是否有,如果有,就替换。没有就创建。
1.2. hiddev_init();
此function只有在选中CONFIG_USB_HIDDEV才会真正做事。
也就是说:只有在配置kernel时选中下面条目才有效.
config USB_HIDDEV
bool "/dev/hiddev raw HID device support"
它只是简单的注册一个USB设备。但这个设备在USB 硬件插入时什么都不作。
1.3 usb_register(&hid_driver);
注册一个USB driver.从这个driver的id_table来看:
.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS
表明匹配的是:Interface class
.bInterfaceClass = USB_INTERFACE_CLASS_HID
表明Interface Class为HID设备,则会被唤醒。
2. HID USB设备被插入时的状况:
分析hid_driver->probe
第一个参数为USB Core传过来的USB设备Interface。第二个参数为本driver的id_table.
2.1 usb_hid_configure(intf);
首先查看quirks.使用usbhid_lookup_quirk()从静态和动态quirks list中查是否此device包含在其中。
Sam看HID driver是以mouse为线索,
interface->desc.bInterfaceSubClass=USB_CLASS_HID
interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT
interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE
所以:quirks |= HID_QUIRK_NOGET;
背景知识一:模块参数:
当使用insmod或modprobe安装模块时,可以通过模块参数给模块传递一些数值。这增加了模块的灵活性。但在使用模块参数之前,必须要让这些参数对insmod可见,则可以使用如下方式,让insmod知道参数名:
module_param_named(name, value, type, perm)
name是参数的名称(insmod时使用)
value是参数在模块中对应的变量
type是参数的类型
perm是参数的权限(其实就是/sys/module/[module_name]/parameters的权限)
例如:
int disk_size = 1024;
module_param_named(size, disk_size, int, S_IRUGO);
则给模块加上名称为"size"的参数,如果在加载模块是使用
insmod thismodule size=100,
那么在模块代码中disk_size的值就是100。相反,如果加载模块时没有指定参数,那么模块代码中disk_size的值仍是默认的1024。
注意,所有模块参数,都应该给定一个默认值。MODULE_PARM_DESC(),对模块参数的描述。
背景知识二:模块宏:
MODULE_AUTHOR();标明模块拥有者
MODULE_DESCRIPTION(); module描述
MODULE_LICENSE(); module license.如果没有,insmod时会警告
USB是主机和外围设备之间的一种连接。USB最初是为了替代各种各样的不同的接口的低速总线而设计的。(例如:串口,并口,键盘连接等)。它以单一类型的总线连接各种不同类型的设备。
USB拓扑机构不是以总线方式的。而是一棵由几个点对点的连接构成的树。连接线由4根电缆组成(电源,地线,两个数据线)
USB主控制器(Host Controller)负责询问每一个USB设备是否有数据需要发送。也就是说:一个USB设备在没有主控制器要求的情况下是不能发送数据的。
USB协议规范定义了一套任何特定类型的设备都可以遵循的标准。如果一个设备遵循该设备,就不需要一个特殊的驱动程序。这些不同的特定类型称之为类(class).例如:存储设备,键盘,鼠标,游戏杆,网络设备等。对于不符合这些类的其他设备。则需要对此设备编写特定driver.
USB设备构成:
Linux Kernel提供了USB Core来处理大部分USB的复杂性。写USB驱动,Sam觉得就是把USB硬件设备和USB Core之间给沟通起来。
USB协议把一个硬件USB设备用以下各个定义勾画出来。
概念一. USB 端点(endpoint)
USB endpoint只能往一个方向传送数据。从主机到设备(输出Endpoint)或从设备到主机(输入Endpoint)。一个Endpoint可以看作一个单向的管道。
有四种类型Endpoint,他们的区别在于传送数据的方式:
控制Endpoint:
用来控制对USB设备不同部分的访问。他们通常用于配置设备,获取设备信息,发送命令到设备,或者获取设备的状态报告。每个USB设备都有一个名为:Endpoint0的控制Endpoint。USB Core使用该Endpoint0在插入时进行设备的配置。
中断Endpoint:
每当USB主控制器要求设备传输数据时,中断Endpoint就以一个固定的速率来传送少量的数据。
USB Keyboard和Mouse通常使用中断Endpoint。
请注意,中断Endpoint和中断不同,它还是无法主动向USB主控制器发送数据。二是需要等待USB主控制器轮询。
批量Endpoint:
Bulk Endpoint用来传输大批量的数据。USB规范不保证数据能在规定时间内传输完成。但保证数据完整性。通常打印机,存储设备和网络设备使用之。
等时Endpoint:
用来传输大批量数据,但数据是否能够到达,怎无法保证。
通常数据收集设备会使用之。
Sam觉得,其实一个设备有多少个以及什么类型的Endpoint。其实是硬件设备在制造阶段已经定好的。 USB Core只是去读取了这些信息,并把这些信息传送给USB driver.
Linux Kernel中使用struct usb_host_endpoint来描述USB Endpoint。但其实其中的structusb_endpoint_descriptor才是真正的描述Endpoint的。
struct usb_endpoint_descriptor
{
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress;
__u8 bmAttributes;
__le16 wMaxPacketSize;
__u8 bInterval;
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
bEndpointAddress:
//此Endpoint USB地址。它还包含了Endpoint方向信息。通过掩码USB_DIR_OUT和USB_DIR_IN判断是输出Endpoint还是输入Endpoint。
bmAttributes;
Endpoint Type,也可以通过掩码:USB_ENDPOINT_XFER_ISOC等判断此Endpoint是中断,等时,控制还是批量Endpoint。
wMaxPacketSize;
该Endpoint一次可以处理的最大字节数。虽然driver可以传送更大的数据,但实际传送时,还是会分割成这个大小。
bInterval:
如果是中断Endpoint,它就是Endpoint的间隔设置。也就是说,中断请求间隔时间。以毫秒为单位。
概念二:接口(Interface)
数个Endpoint被捆绑为一个USB Interface。
一个 USB Interface只对应一个逻辑连接,例如鼠标,键盘或者音频流。一个USB设备可以对应多个Interface。例如Sam见过的鼠标键盘一起的设备,就有2个Interface,一个键盘,一个鼠标。
另外,有些USB扬声器有2个Interface,一个键盘,一个音频流。
注意:每个USB drver只处理一个USB Interface。所以,一个设备也许会对应多个driver.
所以,USB Core在处理USB设备插入时,会针对不同的Interface唤醒它认为合适的driver。并以参数的形式把interface传递给drver.
Linux Kernel使用struct usb_interface来描述USB Interface。但Interface参数照例是其中的usb_interface_descriptor。
struct usb_interface
{
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting;
unsigned num_altsetting;
struct usb_interface_assoc_descriptor *intf_assoc;
int minor;
enum usb_interface_condition condition;
unsigned is_active:1;
unsigned needs_remote_wakeup:1;
struct device dev;
struct device *usb_dev;
int pm_usage_cnt;
};
struct usb_host_interface *altsetting;
Interface结构体数组,包含了所有可能用于该Interface的可选配置。
struct usb_host_interface *cur_altsetting;
可选配置数
unsigned num_altsetting;
指向altsetting的指针。当前的Active 设置。
usb_interface_descriptor:Interface描述符
struct usb_interface_descriptor {
__u8 bLength; //描述符的字节长度。协议里规定,每个描述符必须以一个字节打头来表明描述符的长度。接口描述符的bLength应该是9
__u8 bDescriptorType; //描述符的类型。各种描述符的类型都在ch9.h, * Descriptor types ... USB 2.0 spec table 9.5
__u8 bInterfaceNumber; //接口号。每个配置可以包含多个接口,这个值就是它们的索引值
__u8 bAlternateSetting;
__u8 bNumEndpoints; //接口拥有的端点数量。这里并不包括端点0
__u8 bInterfaceClass; //此Interface所属Class。例如:HID=0x03
__u8 bInterfaceSubClass; //此值基于bInterfaceClass之上。表明在某个Interface class中的子class。例如:HID中有:USB_INTERFACE_SUBCLASS_BOOT
__u8 bInterfaceProtocol; //同上,HID中就有USB_INTERFACE_PROTOCOL_MOUSE
__u8 iInterface; //字符串描述符,放一些描述性厂商信息。
} __attribute__ ((packed));//__attribute__,意思就是告诉编译器,这个结构的元素都是1字节对齐的,不要再添加填充位了。
概念三:配置
一个或多个USB Interface被捆绑为配置。一个USB设备可以有多个配置,并且可以在多个配置之间切换。
配置:struct usb_host_config
USB设备:usb_device.
综上所述:
1个USB设备有一个或多个配置
1个配置有一个或多个Interface
一个Interface有一个或多个设置
Interface没有或有多个Endpoint
USB URB
Linux Kernel中的USB代码通过 urb(USB 请求快)与所有的USB设备通信。
urb被用来以异步方式从特定的USB设备的特定USB Endpoint上接收数据,或往特定的USB设备的特定USB Endpoint上发送数据。
urb是由USB driver创建的。并分配给特定USB设备的特定Endpoint。并由USB driver提交给USB Core。
一:创建urb.
urb不能在driver中静态的定义。因为这样会破坏USB Core对urb的计数机制。所以必须使用:
usb_alloc_urb函数来创建。
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
第一个参数是:等时数据包的数量。
第二个参数是:memory申请类型。
二:初始化urb:
针对中断Endpoint的urb:usb_fill_int_urb()
针对批量Endpoint的urb: usb_fill_bulk_urb()
针对控制Endpoint的urb: usb_fill_control_urb()
针对等时Endpoint的urb: 需要手动初始化。
三:提交urb:
usb_smbmit_urb();
提交urb到USB Core记录:Sam在使用开发板时,发现Kernel启动时如果Bluetooth USB Dongle插入着。则很可能无法检测到此Dongle。所以Sam在
drivers/usb/core/hub.c中的hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange)加入一句printk();来验证启动时Kernel是否正常识别了此USB设备。