我是靠谱客的博主 眯眯眼鸡,最近开发中收集的这篇文章主要介绍十六、USB驱动一、USB固件和USB传输方式二、Linux USB设备驱动模型三、USB数据传输四、USB鼠标设备驱动,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、USB固件和USB传输方式

USB固件:

USB固件一般不需要我们编写,在此不做程序分析。

USB固件中包含USB设备的出厂信息,如厂商ID、产品ID、主版本号和次版本号等。这就是为什么当我们把U盘插入USB口的时候主机可以知道这是一个U盘设备。

 

除包含出厂信息外,固件中还包含处理USB协议和设备读写操作的程序,如将数据从设备发送到总线上或从总线中将数据读取到设备中。驱动程序只是将USB规范定义的请求发送给固件程序,固件程序负责将数据写入设备中。USB规范定义了USB设备间的通信方式。

 

USB结构:

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

如USB鼠标滑动一下产生了数据,但它没有能力通知PC机来读数据,只能被动地等待PC来读。

 

USB传输类型:

1. 控制传输:可靠,时间有保证。如:USB设备的识别过程

2. 批量传输: 可靠, 时间没有保证。如:U盘

3. 中断传输:可靠,实时。如:USB鼠标

4. 实时传输:不可靠,实时。如:USB摄像头

 

 

二、Linux USB设备驱动模型

USB设备驱动模型采用总线设备驱动模型,所以它具有三部分:

1. USB Bus

2. USB Device

3. USB Driver

 

usb_bus和usb_bus_type:

每一条USB总线对应一个struct usb_bus。

 1 struct usb_bus {
 2
struct device *controller;
/* host/master side hardware */
 3
int busnum;
/* Bus number (in order of reg) */
 4
const char *bus_name;
/* stable id (PCI slot_name etc) */
 5
u8 uses_dma;
/* Does the host controller use DMA? */
 6
u8 uses_pio_for_control;
/*
 7 
* Does the host controller use PIO
 8 
* for control transfers?
 9
*/
10
u8 otg_port;
/* 0, or number of OTG/HNP port */
11
unsigned is_b_host:1;
/* true during some HNP roleswitches */
12
unsigned b_hnp_enable:1;
/* OTG: did A-Host enable HNP? */
13
unsigned sg_tablesize;
/* 0 or largest number of sg list entries */
14
15
int devnum_next;
/* Next open device number in
16 
* round-robin allocation */
17
18
struct usb_devmap devmap;
/* device address allocation map */
19
struct usb_device *root_hub;
/* Root hub */
20
struct usb_bus *hs_companion;
/* Companion EHCI bus, if any */
21
struct list_head bus_list;
/* list of busses */
22
23
int bandwidth_allocated;
/* on this bus: how much of the time
24 
* reserved for periodic (intr/iso)
25 
* requests is used, on average?
26 
* Units: microseconds/frame.
27 
* Limits: Full/low speed reserve 90%,
28 
* while high speed reserves 80%.
29
*/
30
int bandwidth_int_reqs;
/* number of Interrupt requests */
31
int bandwidth_isoc_reqs;
/* number of Isoc. requests */
32
33 #ifdef CONFIG_USB_DEVICEFS
34
struct dentry *usbfs_dentry;
/* usbfs dentry entry for the bus */
35 #endif
36
37 #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
38
struct mon_bus *mon_bus;
/* non-null when associated */
39
int monitored;
/* non-zero when monitored */
40 #endif
41 };
View Code

 

usb_bus_type定义了一种usb总线类型。在软件层次中,usb_bus属于usb_bus_type。

usb_bus_type通过bus_register(&usb_bus_type)函数向内核注册usb总线。

struct bus_type usb_bus_type = {
.name
= "usb",
.match
= usb_device_match,
.uevent = usb_uevent,
.pm
= &usb_bus_pm_ops,
};

其中,match()函数通过id_table来匹配device和driver。

 1 static int usb_device_match(struct device *dev, struct device_driver *drv)
 2 {
 3
/* devices and interfaces are handled separately */
 4
if (is_usb_device(dev)) {
 5
 6
/* interface drivers never match devices */
 7
if (!is_usb_device_driver(drv))
 8
return 0;
 9
10
/* TODO: Add real matching code */
11
return 1;
12
13
} else if (is_usb_interface(dev)) {
14
struct usb_interface *intf;
15
struct usb_driver *usb_drv;
16
const struct usb_device_id *id;
17
18
/* device drivers never match interfaces */
19
if (is_usb_device_driver(drv))
20
return 0;
21
22
intf = to_usb_interface(dev);
23
usb_drv = to_usb_driver(drv);
24
25
/* 匹配interface和driver->id_table */
26
id = usb_match_id(intf, usb_drv->id_table);
27
if (id)
28
return 1;
29
30
id = usb_match_dynamic_id(intf, usb_drv);
31
if (id)
32
return 1;
33 
}
34
35
return 0;
36 }
View Code

 

USB总线的任务有:

1. 识别USB设备

  a. 分配地址并将地址告诉USB设备

  b. 发出命令获取描述符

2. 查找并安装对应的设备驱动程序

3. 提供USB读写接口

 

我们现在来分析总线框架:

usb_init()
-> bus_register(&usb_bus_type);
/* 注册usb总线 */
-> bus_register_notifier(&usb_bus_type, &usb_bus_nb);
-> usb_major_init()
-> register_chrdev(180, "usb", &usb_fops);
-> usb_register(&usbfs_driver);
/* usbfs驱动 */
-> usb_hub_init();
/* 初始化USB集线器 */
-> usb_register(&hub_driver);
-> kthread_run(hub_thread, NULL, "khubd");
/* 执行线程 */
-> hub_events();
/* 如果有插拔USB设备操作 */
-> hub_port_connect_change(hub, i, portstatus, portchange);
-> usb_alloc_dev(hdev, hdev->bus, port1);
/* 分配usb内存 */
-> choose_address(udev);
/* 设置地址 */
-> hub_port_init(hub, udev, port1, i);
/* 获取设备描述符 */
-> usb_deregister(&hub_driver);
-> usb_register_device_driver(&usb_generic_driver, THIS_MODULE);

 

usb_device:

每一个USB设备对应一个struct usb_device。

 1 struct usb_device {
 2
int
devnum;
/* 设备号 */
 3
char
devpath[16];
/* 设备路径 */
 4 
u32
route;
 5
enum usb_device_state
state;
 6
enum usb_device_speed
speed;
 7
 8
struct usb_tt
*tt;
 9
int
ttport;
10
11
unsigned int toggle[2];
12
13
struct usb_device *parent;
14
struct usb_bus *bus;
/* 设备所属总线 */
15
struct usb_host_endpoint ep0;
/* 设备端点 */
16
17
struct device dev;
18
19
struct usb_device_descriptor descriptor;
20
struct usb_host_config *config;
/* 设备配置 */
21
22
struct usb_host_config *actconfig;
23
struct usb_host_endpoint *ep_in[16];
24
struct usb_host_endpoint *ep_out[16];
25
26
char **rawdescriptors;
27
28
unsigned short bus_mA;
29 
u8 portnum;
30 
u8 level;
31
32
unsigned can_submit:1;
33
unsigned persist_enabled:1;
34
unsigned have_langid:1;
35
unsigned authorized:1;
36
unsigned authenticated:1;
37
unsigned wusb:1;
38
int string_langid;
39
40
/* static strings from the device */
41
char *product;
42
char *manufacturer;
43
char *serial;
44
45
struct list_head filelist;
46 ...
47
int maxchild;
48
struct usb_device *children[USB_MAXCHILDREN];
49
50 
u32 quirks;
51 
atomic_t urbnum;
52 ...
53
struct wusb_dev *wusb_dev;
54
int slot_id;
55 };
View Code

其注册函数为int usb_new_device(struct usb_device *udev)。

 

在USB设备的逻辑组织中,包含设备、配置、接口和端点四个层次。在此我使用宋宝华老师书中图片作为解释:

 

在Linux内核中,这四个层次通过一组标准的描述符来描述,如下所示。

1. 设备描述符:usb_device用于定义USB设备,usb_device_descriptor用于定义产品ID等。

struct usb_device_descriptor {
__u8
bLength;
/* 描述符长度 */
__u8
bDescriptorType;
/* 描述符类型编号 */
__le16 bcdUSB;
/* USB版本号 */
__u8
bDeviceClass;
/* USB分配的设备类 */
__u8
bDeviceSubClass;
/* USB分配的子类 */
__u8
bDeviceProtocol;
/* USB分配的协议 */
__u8
bMaxPacketSize0;
/* 端点0的最大包大小 */
__le16 idVendor;
/* 厂商编号 */
__le16 idProduct;
/* 产品编号 */
__le16 bcdDevice;
/* 出厂编号 */
__u8
iManufacturer;
__u8
iProduct;
__u8
iSerialNumber;
__u8
bNumConfigurations;
} __attribute__ ((packed));

当一个USB设备插入时,默认的设备编号为0,在未分配新的编号前,PC使用编号0与它通信。

由于PC可能拥有多个USB设备,因此在PC需要访问某个USB设备时,发出的命令含有此USB设备对应的编号地址。

 

2. 配置描述符: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_interface用于定义USB接口,usb_interface_descriptor用于定义此接口下的端点数,协议等。

struct usb_interface_descriptor {
__u8
bLength;
/* 描述符长度 */
__u8
bDescriptorType;
/* 描述符类型 */
__u8
bInterfaceNumber;
/* 接口编号 */
__u8
bAlternateSetting;
__u8
bNumEndpoints;
/* 端点数 */
__u8
bInterfaceClass;
/* 接口类型 */
__u8
bInterfaceSubClass;
__u8
bInterfaceProtocol;
__u8
iInterface;
/* 接口索引值 */
} __attribute__ ((packed));

 

4. 端点描述符:usb_host_endpoint用于定义USB端点,usb_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));

 

我们可以使用“lsusb -v -s 总线号:设备号”查看设备的详细信息

 

usb_driver:

每一个USB设备驱动对应一个struct usb_driver。

 1 struct usb_driver {
 2
const char *name;
 3
 4
int (*probe) (struct usb_interface *intf,
 5
const struct usb_device_id *id);
 6
 7
void (*disconnect) (struct usb_interface *intf);
 8
 9
int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
10
void *buf);
11
12
int (*suspend) (struct usb_interface *intf, pm_message_t message);
13
int (*resume) (struct usb_interface *intf);
14
int (*reset_resume)(struct usb_interface *intf);
15
16
int (*pre_reset)(struct usb_interface *intf);
17
int (*post_reset)(struct usb_interface *intf);
18
19
const struct usb_device_id *id_table;
20
21
struct usb_dynids dynids;
22
struct usbdrv_wrap drvwrap;
23
unsigned int no_dynamic_id:1;
24
unsigned int supports_autosuspend:1;
25
unsigned int soft_unbind:1;
26 };
View Code

其注册函数为int usb_register(struct usb_driver *driver)。

1 #define usb_register(driver) 
2
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

 

 

三、USB数据传输

USB设备驱动和USB设备通信所有的结构体为USB请求块(USB Request Block,URB)。

struct urb {
/* private: usb core and host controller only fields in the urb */
struct kref kref;
/* reference count of the URB */
void *hcpriv;
/* private data for host controller */
atomic_t use_count;
/* 用于计数,URB若提交则加1,URB若返回驱动程序减1 */
atomic_t reject;
/* submissions will fail */
int unlinked;
/* unlink error code */
/* 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 list_head anchor_list; /* the URB may be anchored */
struct usb_anchor *anchor;
struct usb_device *dev;
/* URB需要发送到的设备 */
struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
unsigned int pipe;
/* (in) pipe information */
unsigned int stream_id;
/* (in) stream ID */
int status;
/* (return) non-ISO status */
unsigned int transfer_flags;
/* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer;
/* 缓冲区,其数据可以从设备发送到主机,也可以从主机发送到设备 */
dma_addr_t transfer_dma;
/* (in) dma addr for transfer_buffer */
struct scatterlist *sg;
/* (in) scatter gather buffer list */
int num_sgs;
/* (in) number of entries in the sg list */
u32 transfer_buffer_length;
/* 表示transfer_buffer或transfer_dma的长度 */
u32 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 */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/* (in) ISO ONLY */
};

 

URB的处理流程:

在USB设备的逻辑层次中,interface是逻辑上的设备,它使用endpoint传输urb。

也就是说,USB设备中每个endpoint都处理一个urb队列,在队列清空之前,一个urb的传输过程如下:

当要进行数据传输时,需要分配、设置和提交一个urb给USB核心。核心对urb进行解析,将控制信息提交给主机控制器,由主机控制器完成数据到设备的传输。此时,驱动程序只需等待。当数据回传到主机控制器后,会转发给USB核心,唤醒等待的驱动程序。

 

具体函数操作如下图:

各函数声明如下:

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
inline void usb_fill_int_urb (struct urb *urb,
/* 要初始化的urb指针 */
struct usb_device *dev,
/* 所要访问的设备 */
unsigned int
pipe,
/* 要访问的端点所对应的管道,使用usb_sndintpipe()或usb_rcvintpipe()创建 */
void
*transfer_buffer,
/* 要传输的数据缓冲区 */
int
buffer_length,
/* 缓冲区长度 */
usb_complete_t
complete_fn,
/* 当完成该urb所请求的操作时,要调用的回调函数 */
void
*context,
/* complet_fn函数所需的上下文,通常取值为dev */
int
interval)
/*urb被调度的时间间隔 */
inline void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length,
usb_complete_t complete_fn, void *context)
void usb_fill_control_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, unsigned char *setup_packet, void *transfer_buffer,
int buffer_length, usb_complete_t complete_fn, void *context)
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
void usb_kill_urb(struct urb *urb);
void usb_free_urb(struct urb *urb);

其中complete_fn()就是传输完成函数

 

 

四、USB鼠标设备驱动

在熟悉各个函数后,我们在本节完成USB鼠标设备驱动,步骤如下:

1. 分配设置usb_driver

.id_table

.probe

.disconnect

2. 使用usb_register()注册usb_driver

3. 在probe()函数中完成输入子系统和urb设置,最后提交urb

4. 在传输完成函数函数中判断数据,上报数据,重新提交urb

5. 在disconnect()函数中注销urb和输入子系统

6. 使用usb_deregister()注销usb_driver

 

在编写驱动时,可参考例子drivers/hid/usbhid/usbmouse.c

 

USB鼠标驱动源代码:


1 #include <linux/module.h>

2 #include <linux/fs.h>

3 #include <linux/init.h>

4 #include <linux/cdev.h>

5 #include <linux/slab.h>

6 #include <linux/device.h>

7 #include <linux/usb/input.h>

8 #include <linux/hid.h>

9
 10 #include <asm/uaccess.h>
 11
 12 static struct usb_device_id mouse_table [] = {
 13 
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
 14 
USB_INTERFACE_PROTOCOL_MOUSE) },
 15
{ }
/* Terminating entry */
 16 };
 17
 18 static struct input_dev *usbinput;
 19 static dma_addr_t usb_buf_phys;
 20 static char *buf_mem;
 21 static struct urb *usb_urb;
 22 static int len;
 23
 24 static unsigned int cnt;
 25
 26 static void usb_mouse_irq(struct urb *purb)
 27 {
 28 #if 0
 29
/* 用于测试哪个bit是用于表示左、中、右键
 30 
* 我所使用的鼠标使用bit[0]表示左、中、右键
 31
*/
 32
int i;
 33
 34
printk("cnt: %02d, ", cnt++);
 35
for(i = 0; i < len; ++i)
 36 
{
 37
printk("%02x ", buf_mem[i]);
 38 
}
 39
printk("n");
 40 #else
 41
static unsigned char preval = 0;
/* 用于存储上一个数据 */
 42
if((preval & 1) != (buf_mem[0] & 1)) {
/* 鼠标左键数据有变化 */
 43
input_report_key(usbinput, KEY_L, (buf_mem[0] & 1) ? 1 : 0);
 44 
input_sync(usbinput);
 45 
}
 46
if((preval & (1 << 1)) != (buf_mem[0] & (1 << 1))) {
 47
input_report_key(usbinput, KEY_S,
(buf_mem[0] & (1 << 1)) ? 1 : 0);
 48 
input_sync(usbinput);
 49 
}
 50
if((preval & (1 << 2)) != (buf_mem[0] & (1 << 2))) {
 51
input_report_key(usbinput, KEY_ENTER,
(buf_mem[0] & (1 << 2)) ? 1 : 0);
 52 
input_sync(usbinput);
 53 
}
 54
preval = buf_mem[0];
 55 #endif
 56
 57 
usb_submit_urb(usb_urb, GFP_KERNEL);
 58
 59 }
 60
 61 static int mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
 62 {
 63
struct usb_device *dev = interface_to_usbdev(intf);
 64
struct usb_host_interface *interface;
 65
struct usb_endpoint_descriptor *endpoint;
 66
unsigned int pipe;
 67
int ret;
 68
 69
interface = intf->cur_altsetting;
 70
endpoint = &interface->endpoint[0].desc;
 71
 72
/* 1. 输入子系统设置 */
 73
usbinput = input_allocate_device();
 74
 75
set_bit(EV_KEY, usbinput->evbit);
 76
set_bit(EV_REP, usbinput->evbit);
 77
 78
set_bit(KEY_L, usbinput->keybit);
 79
set_bit(KEY_S, usbinput->keybit);
 80
set_bit(KEY_ENTER, usbinput->keybit);
 81
 82
ret = input_register_device(usbinput);
 83
 84
/* 2. urb设置 */
 85
usb_urb = usb_alloc_urb(0, GFP_KERNEL);
 86
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
 87
 88
len = endpoint->wMaxPacketSize;
 89
buf_mem = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);
 90
 91 
usb_fill_int_urb(usb_urb, dev, pipe, buf_mem, len,
 92
usb_mouse_irq, NULL, endpoint->bInterval);
 93
usb_urb->transfer_dma = usb_buf_phys;
 94
usb_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 95
 96
return usb_submit_urb(usb_urb, GFP_KERNEL);
 97 }
 98
 99 static void mouse_disconnect(struct usb_interface *intf)
100 {
101
struct usb_device *dev = interface_to_usbdev(intf);
102
103 
usb_kill_urb(usb_urb);
104 
usb_free_urb(usb_urb);
105
106 
usb_free_coherent(dev, len, buf_mem, usb_buf_phys);
107 
input_unregister_device(usbinput);
108 
input_free_device(usbinput);
109 }
110
111 static struct usb_driver mouse_driver = {
112
.name
= "itop4412_mouse",
113
.probe = mouse_probe,
114
.disconnect = mouse_disconnect,
115
.id_table
= mouse_table,
116 };
117
118 static int __init usbmouse_init(void)
119 {
120
return usb_register(&mouse_driver);
121 }
122
123 static void __exit usbmouse_exit(void)
124 {
125
usb_deregister(&mouse_driver);
126 }
127
128 /* 声明段属性 */
129 module_init(usbmouse_init);
130 module_exit(usbmouse_exit);
131
132 MODULE_LICENSE("GPL");
View Code

Makefile:

 1 KERN_DIR = /work/itop4412/tools/linux-3.5
 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
+= usbmouse.o
View Code

 

测试:

在编译并在开发板上insmod后,会出现如下信息:

input: Unspecified device as /devices/virtual/input/input4

插入USB鼠标后,会出现如下信息:

sbcore: registered new interface driver itop4412_mouse

 

接下来执行:

# ls /dev/event*

最后一个/dev/event2为USB鼠标对应事件

执行:

#hexdump /dev/event2

接下来按键,效果如下图:

在此我以第一行数据为例解释一下各个数字的意义:

0000000 0393 0000 55a4 0008   0001    001f    0001 0000

 次数    秒    微秒    按键类  哪个按键    按下

 

 

下一章  十七、块设备驱动

 

转载于:https://www.cnblogs.com/Lioker/p/10933171.html

最后

以上就是眯眯眼鸡为你收集整理的十六、USB驱动一、USB固件和USB传输方式二、Linux USB设备驱动模型三、USB数据传输四、USB鼠标设备驱动的全部内容,希望文章能够帮你解决十六、USB驱动一、USB固件和USB传输方式二、Linux USB设备驱动模型三、USB数据传输四、USB鼠标设备驱动所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部