分类: 嵌入式
2015-06-09 12:44:54
USB驱动程序
15年5月29日11:16:33
程序如下所示:
1
2
#include
3
#include
4
#include
5
#include
6
#include
7
#include
8
9 static struct usb_device_id usbmouse_as_key_id_table [] = {
10 { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
11 USB_INTERFACE_PROTOCOL_MOUSE) },
12 { } /* Terminating entry */
13 };
14
15 static struct input_dev *uk_dev;
16 static char *usb_buf;
17 static dma_addr_t usb_phya;
18 static int len;
19 static struct urb *uk_urb;
20
21 static void usbmouse_as_key_irq()
22 {
23 unsigned int pre_val;
24 #if 0
25 int i;
26 static int cnt = 0;
27 //printk("len = %d ", len);
28 printk("data cnt %d: ", ++cnt);
29 //printk("0x%02x ", usb_buf[1]);
30 for (i = 0; i < len; i++)
31 {
32 printk("%02x ", usb_buf[i]);
33 }
34 printk("\n");
35 #endif
36
37 if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))
38 {
39 input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0))? 1 : 0);
40 input_sync(uk_dev);
41 }
42
43 if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))
44 {
45 input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1))? 1 : 0);
46 input_sync(uk_dev);
47 }
48
49 if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))
50 {
51 input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2))? 1 : 0);
52 input_sync(uk_dev);
53 }
54
55 pre_val = usb_buf[0];
56
57
58 /* chong xin ti jiao urb */
59 usb_submit_urb(uk_urb, GFP_KERNEL);
60 }
61
62 static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
63 {
64 struct usb_device *dev = interface_to_usbdev(intf);
65 struct usb_host_interface *interface;
66 struct usb_endpoint_descriptor *endpoint;
67 int pipe;
68 #if 0
69 //printk("found usbmouse!\n");
70 //printk("bcdUSB = %x\n", dev->descriptor.bcdUSB);
71 //printk("VID = 0x%x\n", dev->descriptor.idVendor);
72 //printk("PID = 0x%x\n", dev->descriptor.idProduct);
73 #endif
74
75 interface = intf->cur_altsetting;
76 endpoint = &interface->endpoint[0].desc;
77
78 uk_dev = input_allocate_device();
79
80 set_bit(EV_KEY, uk_dev->evbit);
81 set_bit(EV_REP, uk_dev->evbit);
82
83 set_bit(KEY_L, uk_dev->keybit);
84 set_bit(KEY_S, uk_dev->keybit);
85 set_bit(KEY_ENTER, uk_dev->keybit);
86
87 input_register_device(uk_dev);
88
89 /* 三要素 :源,长度,目的 */
90 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
91 len = endpoint->wMaxPacketSize;
92 usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_phya);
93
94 /* 使用3要素*/
95 uk_urb = usb_alloc_urb(0, GFP_KERNEL);
96
97 usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
98 uk_urb->transfer_dma = usb_phya;
99 uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
100
101 usb_submit_urb(uk_urb, GFP_KERNEL);
102
103 return 0;
104 }
105
106 static void usbmouse_as_key_disconnect(struct usb_interface *intf)
107 {
108 struct usb_device *dev = interface_to_usbdev(intf);
109
110 usb_kill_urb(uk_urb);
111 usb_free_urb(uk_urb);
112
113 usb_buffer_free(dev, len, usb_buf, usb_phya);
114
115 input_unregister_device(uk_dev);
116 input_free_device(uk_dev);
117
118 printk("disconnect usbmouse!\n");
119 }
120
121 static struct usb_driver usbmouse_as_key_driver = {
122 .name = "usbmouse_as_key",
123 .probe = usbmouse_as_key_probe,
124 .disconnect = usbmouse_as_key_disconnect,
125 .id_table = usbmouse_as_key_id_table,
126 };
127
128 static int usbmouse_as_key_init()
129 {
130 usb_register(&usbmouse_as_key_driver);
131 return 0;
132 }
133
134 static void usbmouse_as_key_exit()
135 {
136 usb_deregister(&usbmouse_as_key_driver);
137 }
138
139 module_init(usbmouse_as_key_init);
140 module_exit(usbmouse_as_key_exit);
141
142 MODULE_LICENSE("GPL");
143
一.
USB设备驱动核心是usb_driver结构体,创建一个新的usb_driver结构体,只需要初始化5个字段:
static struct usb_driver usbmouse_as_key_driver = {
.owner = THIS_MODULE,
.name = "usbmouse_as_key",
.probe = usbmouse_as_key_probe,
.disconnect = usbmouse_as_key_disconnect,
.id_table = usbmouse_as_key_id_table,
};
其中的id_table 成员描述了这个驱动所支持的USB设备列表,包含USB设备的制造商ID,产品ID,产品版本,设备类,接口等信息,它的生成需要借助USB_DEVICE(...),USB_DEVICE_VER(...), USB_DEVICE_INFO(...), USB_INTERFACE_INFO(...)来生成。在本例中如程序中9~13行所示。
创建完usb_driver结构体后,就是在出入口函数中注册或者卸载这个结构体,用usb_register(&usbmouse_as_key_driver);和 usb_deregister(&usbmouse_as_key_driver); 来执行。
二.
写完出入口函数,就是该写probe函数及其它的反函数disconnect函数。
三.
写这两个函数之前,需要先清楚关于USB的几个定义:设备,配置,接口,端点。
每个设备都提供了不同级别的配置,每个配置可以有多个接口,而设备接口是很多端点的集合。主机只能通过端点与设备进行通信。下面从大范围到小范围介绍以下几个概念:
(1)设备描述符:关于设备的通用信息,如供应商ID,产品ID和修订ID, 支持的设备类,子类,和适用的协议,以及默认的端点的最大包大小。在内核中,USB设备用usb_device结构体来描述,USB设备描述符用usb_device_descriptor结构体来描述。
/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {
__u8 bLength; /* 描述符长度 */
__u8 bDescriptorType; /* 描述符类型编号 */
__le16 bcdUSB; /* USB版本号 */
__u8 bDeviceClass; /* USB分配的设备类code */
__u8 bDeviceSubClass; /* USB分配的子类code */
__u8 bDeviceProtocol; /* USB遵循的协议 */
__u8 bMaxPacketSize0; /* endpoint0最大包大小 */
__le16 idVendor; /* 厂商编号 */
__le16 idProduct; /* 产品编号 */
__le16 bcdDevice; /* 设备出厂编号 */
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber;
__u8 bNumConfigurations;
} __attribute__ ((packed));
(2)配置描述符:此配置中的接口数,支持的挂起和恢复能力以及功率要求。USB配置在内核中用usb_host_config结构体来描述, USB 配置描述符定义为结构体usb_config_descriptor:
struct usb_config_descriptor {
__u8 bLength; /* 描述符长度 */
__u8 bDescriptorType; /* 描述符类型编号 */
__le16 wTotalLength; /* 配置所返回的所有数据的大小 */
__u8 bNumInterfaces; /* 配置所支持的接口数 */
__u8 bConfigurationValue;
__u8 iConfiguration;
__u8 bmAttributes;
__u8 bMaxPower; /* 设备从总线提取的最大电流值 */
} __attribute__ ((packed));
(3)接口描述符:接口类,子类和适用的协议,接口备用配置的数目和端点数目,USB接口在内核中使用usb_interface结构体来描述,USB接口描述符定义为结构体usb_interface_descriptor:
/* USB_DT_INTERFACE: Interface descriptor */
struct usb_interface_descriptor {
__u8 bLength; /* 描述符长度 */
__u8 bDescriptorType; /* 描述符类型 */
__u8 bInterfaceNumber; /* 接口的编号 */
__u8 bAlternateSetting; /* 备用的接口描述符编号 */
__u8 bNumEndpoints; /* 该接口使用的端点数,不包括端点0 */
__u8 bInterfaceClass; /* 接口类型 */
__u8 bInterfaceSubClass; /* 接口子类型 */
__u8 bInterfaceProtocol; /* 接口所遵循的协议 */
__u8 iInterface;
} __attribute__ ((packed));
(4)端点描述符:端点地址,方向和类型,支持的最大包大小,如果是中断类型的端点则还包括轮询频率。在内核中,USB端点使用usb_host_endpoint结构体来描述,USB端点描述符定义为usb_endpoint_descriptor:
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
__u8 bLength; /* 描述符长度 */
__u8 bDescriptorType; /* 描述符类型 */
__u8 bEndpointAddress; /* 端点地址 */
__u8 bmAttributes; /* 端点属性 */
__le16 wMaxPacketSize; /* 本端点接收或发送的最大数据包的大小 */
__u8 bInterval; /* 轮询数据传送的时间间隔 */
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
USB端点有4中类型,控制,中断,批量,等时。
控制传输:可靠,时间有保证,比如USB设备的识别过程。
中断传输:可靠,实时,比如USB鼠标。
批量传输:可靠,事件没有保证,比如:U盘。
等时床数:不可靠,实时,比如:USB摄像头。
(5)某些还会有字符描述符,暂时在这先不写了。
四.
在USB驱动程序中,另外一个重要的概念是URB(USB请求块)。
在USB驱动程序中,通过usb_driver程序来连接内核与USB设备,但是传输通信数据等的载体就是URB结构体。这个请求块用struct urb结构体来描述:
struct urb
{
/* private: usb core and host controller only fields in the urb --------私有的,只能由USB核心 和主机控制器访问的字段*/
struct kref kref; /* reference count of the URB urb引用计数*/
spinlock_t lock; /* lock for the URB */
void *hcpriv; /* private data for host controller */
atomic_t use_count; /* concurrent submissions counter */
u8 reject; /* submissions will fail */
/* public: documented fields in the urb that can be used by drivers-----公共的,可供驱动使用 的字段 */
struct list_head urb_list; /* list head for use by the urb's current owner 链表头*/
struct usb_device *dev; /* (in) pointer to associated device 关联的USB设备*/
unsigned int pipe; /* (in) pipe information 管道信息*/
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer 发送或接受数据缓冲区*/
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer 物理地址*/
int transfer_buffer_length; /* (in) data buffer length 数据缓冲区的长度*/
int actual_length; /* (return) actual transfer length */
unsigned char *setup_packet; /* (in) setup packet (control only) */
dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
int start_frame; /* (modify) start frame (ISO) */
int number_of_packets; /* (in) number of ISO packets */
int interval; /* (modify) transfer interval
* (INT/ISO) */
int error_count; /* (return) number of ISO errors */
void *context; /* (in) context for completion */
usb_complete_t complete; /* (in) completion routine 完成函数,当urb被完全传输或者发 生错误时将要调用这个函数。 */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/* (in) ISO ONLY */
};
下面讲urb的处理流程:
(1)创建和销毁urb
struct urb结构体不能在驱动程序中静态地创建,因为这样会破坏USB核心对urb所使用的引用计数机制。它必须使用usb_alloc_urb函数来创建。函数原型如下:
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
第一个参数是指urb应该包含的等时数据包的数量,如果不打算创建等时urb,该值应该为0.第二个参数与kmalloc函数的标志相同。
销毁urb用void usb_free_urb(struct *urb);来实现。
(2)初始化urb
当一个urb被创建之后,它必须被初始化以后才能够被USB核心使用。对于不同的端点,有不同的初始化方式。对于中断端点的urb初始化方式如下所示:
static inline void usb_fill_int_urb (struct urb *urb, struct usb_device *dev, unsigned int pipe,
void *transfer_buffer, int buffer_length, usb_complete_t complete_fn,
void *context, int interval);
其中,第一个参数指向需初始化的urb的指针,第二个参数指该urb所发送的目标USB设备,第三个参数指用于保存外发数据或者接收数据的缓冲区的指针,并且这个pipe参数需要使用 usb_rcvintpipe()函数来创建,第四个参数指缓冲区的大小,第五个参数指当该urb结束之后调用的处理函数。后面两个参数暂时不重要。
对于批量urb,使用void usb_fill_bulk_urb(...)函数来初始化和 usb_fill_int_urb函数基本相同。
对于控制urb,使用void usb_fill_control_urb(...)函数来初始化。
对于等时urb,没有函数帮忙初始化,必须手工进行初始化。具体例子就不写了,看书上吧~
(3)urb被USB程序正确创建并且初始化以后,就可以提交到USB核心以发送到USB设备了,这时通过调用usb_submit_urb函数来完成,函数原型如下:
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
urb参数指向即将被发送到设备的urb指针,mem_flags参数以供有三个有效值可以使用,GFP_ATOMIC, GFP_NOIO, GFP_KERNEL,一般情况下都是 GFP_KERNEL。
(4)取消urb
应该调用int usb_kill_urb(struct urb *urb)或int usb_unlink_urb(struct urb *urb)函数来终止一个已经被提交到USB核心的urb。
五.
通晓以上概念以后,下面开始写probe函数:
(1)写的时候就需要从小范围往大范围写,首先写出端点描述符usb_endpoint_descriptor, 然后就是接口描述函数usb_host_interface,USB设备驱动程序通常需要把一个struct usb_interface结构体的数据转换成为一个struct usb_device结构体, 通过interface_to_usbdev函数来转换。例如本例中通过struct usb_device *dev = interface_to_usbdev(intf);这句换来转换。
(2)这个例子的目的是通过写USB驱动程序,实现用鼠标的左键,右键,中键分别实现L, S, ENTER的功能。所以,需要使用到输入子系统的内容。首先分配一个input_dev结构体,设置能够产生哪些大事件,然后继续设置能够产生这类大事件里面的哪些小事件,设置好以后,注册这个结构体。
(3)然后就该使用urb了,首先创建一个urb,然后设置urb。记住使用urb的三要素:源,目的,长度。源pipe使用usb_rcvintpipe()来创建,
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
len = endpoint->wMaxPacketSize;
usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_phya);
设置好以后初始化urb为中断urb或者其他,初始化完成以后才能够提交给核心程序。
六. Disconnect函数就是probe函数的反函数。
七. 下面就是写出来初始化urb为中断urb的时候的中断处理函数。
下面写出USB设备的其他概念:
(1)USB是主从结构的
所有的USB传输,都是从USB主机这方发起的,USB设备没有“主动”通知USB主机的能力。
例子:USB鼠标滑动一下立即产生数据,但是它没有能力通知PC机来读数据,只能被动地等PC机来读。
(2)USB传输的对象:端点(endpoint)
我们说的“读U盘”,“写U盘”可以细化为:把数据写到U盘的端点1,从U盘的端点2读出数据。
除了端点0外,每一个端点只支持一个方向的数据传输,端点0用于控制传输,既能输出也能输入。
(3)每一个端点都有传输类型,传输方向。
(4)术语里说的输入(IN),输出(OUT)都是基于USB主机的立场说的。
比如鼠标的数据是从鼠标传到PC机,对应的端点称为“输入”端点。
(5)USB总线的作用:
a:识别USB设备
b:找到并安装对应的设备驱动
c:提供USB读写函数(但是它并不理解数据含义)。