USB设备驱动
架构
USB的硬件识别原理
基本概念
①如何区分不同的USB设备?
每个USB设备接入PC时,USB总线驱动程序都会给它分配一个编号(地址);
接在USB总线上的每一个USB设备都有自己的编号(地址);
PC机想访问某个USB设备时,发出的命令都含有对应的编号(地址);
新接入的USB设备的设备编号(地址)是0,在未分配新编号之前,PC机使用0编号与其通信;
②USB为主从结构
USB主机发起通信,从机处于"绝对被动"地位("通知"主机接收数据的能力都没有);
③USB传输类型
控制传输 | 数据可靠 | 实时 | USB识别过程 |
批量传输 | 数据可靠 | 非实时 | U盘 |
"中断"传输 | 数据可靠 | 实时 | 鼠标 |
实时传输 | 数据不可靠 | 实时 | USB摄像头 |
④USB传输对象:端点(endpoint)
端点0:
传输类型:控制传输; 传输方向:双向(输出和输入);
除了端点0外,其它端点都是单向传输的,只支持一种传输方向;
⑤术语里,程序的输入(IN)、输出(OUT)都是基于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
scsi0 : SCSI emulation for USB Mass Storage devices
scsi 0:0:0:0: Direct-Access HTC Android Phone 0100 PQ: 0 ANSI: 2
sd 0:0:0:0: [sda] Attached SCSI removable disk
②拔掉
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
scsi1 : SCSI emulation for USB Mass Storage devices
scsi 1:0:0:0: Direct-Access HTC Android Phone 0100 PQ: 0 ANSI: 2
sd 1:0:0:0: [sda] Attached SCSI removable disk
④在内核目录下搜:
grep "USB device using" * -nR
drivers/usb/core/hub.c:2186: "%s %s speed %sUSB device using %s and address %d\n",
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;
choose_address(udev); // 给新设备分配编号(地址)
hub_port_init // usb 1-1: new full speed USB device using s3c2410-ohci and address 3
hub_set_address // 把编号(地址)告诉USB设备
usb_get_device_descriptor(udev, 8); // 获取设备描述符
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
usb_new_device(udev)
err = usb_get_configuration(udev); // 把所有的描述符都读出来,并解析
usb_parse_configuration
device_add // 把device放入usb_bus_type的dev链表,
// 从usb_bus_type的driver链表里取出usb_driver,
// 把usb_interface和usb_driver的id_table比较
// 如果能匹配,调用usb_driver的probe
驱动程序
1 /*
2 * 参考:.\linux-2.6.22.6\drivers\hid\usbhid\usbmouse.c
3 * USB鼠标用作按键输入:
4 * 左键:L;右键:S;中键:ENTER
5 */
6 #include <linux/kernel.h>
7 #include <linux/slab.h>
8 #include <linux/module.h>
9 #include <linux/init.h>
10 #include <linux/usb/input.h>
11 #include <linux/hid.h>
12
13 static struct input_dev *uk_dev;
14
15 static int usb_bufLen;
16 static char *usb_buf;
17 static dma_addr_t usb_buf_phy;
18 static struct urb *uk_urb;
19
20 static void usbMouseAsKey_irq(struct urb *urb)
21 {
22 #if 0
23 /* test: 测试鼠标数据含义 */
24 int i;
25 static int cnt = 0;
26
27 for (i=0; i<usb_bufLen; i++)
28 {
29 printk("%02x ", usb_buf[i]);
30 }
31 printk("\n");
32 #else
33 /*
34 * usb鼠标数据含义:
35 * data[0]: bit0-左键
36 * bit1-右键
37 * bit2-中键
38 */
39 static unsigned char pre_val;
40
41 if ((pre_val&(1<<0)) != (usb_buf[0]&(1<<0)))
42 {
43 //左键发生了变化
44 input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0]&(1<<0))?1:0);
45 input_sync(uk_dev);
46 }
47
48 if ((pre_val&(1<<1)) != (usb_buf[0]&(1<<1)))
49 {
50 //右键发生了变化
51 input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0]&(1<<1))?1:0);
52 input_sync(uk_dev);
53 }
54
55 if ((pre_val&(1<<2)) != (usb_buf[0]&(1<<2)))
56 {
57 //中键发生了变化
58 input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0]&(1<<2))?1:0);
59 input_sync(uk_dev);
60 }
61
62 pre_val = usb_buf[0];
63 #endif
64 /* 重新提交urb */
65 usb_submit_urb(uk_urb, GFP_KERNEL);
66 }
67
68
69 static int usbMouseAsKey_probe(struct usb_interface *intf, const struct usb_device_id *id)
70 {
71 /* test:打印usb设备厂家相关信息(当鼠标接到windows下,设备管理器可看到这些信息) */
72 //struct usb_device *dev = interface_to_usbdev(intf);
73
74 //printk("found usb mouse\n");
75 //printk("bcdUSB = 0x%x\n", dev->descriptor.bcdUSB);
76 //printk("VID = 0x%x\n", dev->descriptor.idVendor); //供应商id
77 //printk("PID = 0x%x\n", dev->descriptor.idProduct); //产品id
78 /******** test end ********/
79
80 struct usb_device *dev = interface_to_usbdev(intf);
81 struct usb_host_interface *interface;
82 struct usb_endpoint_descriptor *endpoint;
83 int pipe, maxp;
84
85
86 interface = intf->cur_altsetting;
87 endpoint = &interface->endpoint[0].desc;
88
89 /* 3 分配一个input_dev */
90 uk_dev = input_allocate_device();
91 /******** 3 end ********/
92
93 /* 4 设置 */
94 //4.1 能产生哪类事件
95 set_bit(EV_KEY, uk_dev->evbit);
96 set_bit(EV_REP, uk_dev->evbit);
97
98 //4.2 能产生哪些事件
99 set_bit(KEY_L, uk_dev->keybit);
100 set_bit(KEY_S, uk_dev->keybit);
101 set_bit(KEY_ENTER, uk_dev->keybit);
102 /******** 4 end ********/
103
104 /* 5 注册 */
105 input_register_device(uk_dev);
106 /******** 5 end ********/
107
108 /* 6 硬件相关操作 */
109 //数据传输3要素:源、目的、长度
110 //a、源:usb设备的某个端点
111 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
112 maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
113
114 //b、长度
115 usb_bufLen = endpoint->wMaxPacketSize;
116
117 //c、目的
118 usb_buf = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &usb_buf_phy);
119
120 //d、使用3要素
121 //d.1 分配urb ―― usb请求块:usb request block
122 uk_urb = usb_alloc_urb(0, GFP_KERNEL);
123
124 //d.2 填充urb
125 usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, usb_bufLen, usbMouseAsKey_irq, NULL, endpoint->bInterval); //endpoint->bInterval:主机查询频率
126 uk_urb->transfer_dma = usb_buf_phy;
127 uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
128
129 //d.3 使用urd
130 usb_submit_urb(uk_urb, GFP_KERNEL);
131 /******** 6 end ********/
132 return 0;
133 }
134
135 static void usbMouseAsKey_disconnect(struct usb_interface *intf)
136 {
137 //printk("disconnect\n");
138 usb_kill_urb(uk_urb);
139 usb_free_urb(uk_urb);
140 usb_buffer_free(interface_to_usbdev(intf), usb_bufLen, usb_buf, usb_buf_phy);
141 input_unregister_device(uk_dev);
142 input_free_device(uk_dev);
143 return 0;
144 }
145
146
147 static struct usb_device_id usbMouseAsKey_id_table [] = {
148 { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
149 USB_INTERFACE_PROTOCOL_MOUSE) },
150 { } /* Terminating entry */
151 };
152
153 /* 2 分配设置一个usb_driver结构体 */
154 static struct usb_driver usbMouseAsKey_driver = {
155 .name = "usbMouseAsKey",
156 .probe = usbMouseAsKey_probe,
157 .disconnect = usbMouseAsKey_disconnect,
158 .id_table = usbMouseAsKey_id_table,
159 };
160 /******** 2 end ********/
161
162
163 /* 1 出、入口函数 */
164 static int usbMouse_asKey_init(void)
165 {
166 /* 3 注册usb_driver */
167 usb_register(&usbMouseAsKey_driver);
168 /******** end ********/
169 return 0;
170 }
171
172
173 static void usbMouse_asKey_exit(void)
174 {
175 /* 4 卸载usb_driver */
176 usb_deregister(&usbMouseAsKey_driver);
177 /******** end ********/
178 return;
179 }
180
181
182 module_init(usbMouse_asKey_init);
183 module_exit(usbMouse_asKey_exit);
184 MODULE_LICENSE("GPL");
185 /******** 1 end ********/
Makefile
1 KERN_DIR = /work/system/linux-2.6.22.6
2
3 all:
4 make -C $(KERN_DIR) M=`pwd` modules
5
6 clean:
7 make -C $(KERN_DIR) M=`pwd` modules clean
8 rm -rf modules.order
9
10 obj-m += usb_mouseASkey.o
调试
pc-linux:
cd /work/system/linux-2.6.22.6/
make menuconfig(屏蔽鼠标驱动)
make uImage
cp arch/arm/boot/uImage /work/nfs_root/uImage_nohid
board-uboot:
nfs 30000000 192.168.0.103:/work/nfs_root/uImage_nohid
bootm 30000000
board-linux:
insmod usb_mouseASkey.ko
接上usb鼠标
ls /dev/event*
cat /dev/tty1(hexdump /dev/event*)
操作鼠标——观察数据