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 ",

   

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.6drivershidusbhidusbmouse.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(" ");
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 ");
75         //printk("bcdUSB = 0x%x ", dev->descriptor.bcdUSB);
76         //printk("VID = 0x%x ", dev->descriptor.idVendor);        //供应商id
77         //printk("PID = 0x%x ", 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 ");
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*)

操作鼠标——观察数据

原文地址:https://www.cnblogs.com/lilto/p/11878104.html