Chinaunix首页 | 论坛 | 博客
  • 博客访问: 299555
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 715
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-20 20:38
文章分类
文章存档

2016年(20)

2015年(56)

分类: 嵌入式

2015-06-09 12:44:54

P { margin-bottom: 0.21cm; }

USB驱动程序

155月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...)来生成。在本例中如程序中913行所示。


创建完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驱动程序中,另外一个重要的概念是URBUSB请求块)。

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函数的标志相同。

销毁urbvoid 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,没有函数帮忙初始化,必须手工进行初始化。具体例子就不写了,看书上吧~

3urbUSB程序正确创建并且初始化以后,就可以提交到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_interfaceUSB设备驱动程序通常需要把一个struct usb_interface结构体的数据转换成为一个struct usb_device结构体, 通过interface_to_usbdev函数来转换。例如本例中通过struct usb_device *dev = interface_to_usbdev(intf);这句换来转换。

2)这个例子的目的是通过写USB驱动程序,实现用鼠标的左键,右键,中键分别实现LSENTER的功能。所以,需要使用到输入子系统的内容。首先分配一个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设备的其他概念:

1USB是主从结构的

所有的USB传输,都是从USB主机这方发起的,USB设备没有“主动”通知USB主机的能力。

例子:USB鼠标滑动一下立即产生数据,但是它没有能力通知PC机来读数据,只能被动地等PC机来读。

2USB传输的对象:端点(endpoint

我们说的“读U盘”,“写U盘”可以细化为:把数据写到U盘的端点1,从U盘的端点2读出数据。

除了端点0外,每一个端点只支持一个方向的数据传输,端点0用于控制传输,既能输出也能输入。

3)每一个端点都有传输类型,传输方向。

4)术语里说的输入(IN),输出(OUT)都是基于USB主机的立场说的。

比如鼠标的数据是从鼠标传到PC机,对应的端点称为“输入”端点。

5USB总线的作用:

a:识别USB设备

b:找到并安装对应的设备驱动

c:提供USB读写函数(但是它并不理解数据含义)。

阅读(1151) | 评论(0) | 转发(0) |
0

上一篇:触摸屏驱动

下一篇:块设备驱动程序

给主人留下些什么吧!~~