一、USB一些相关的概念
USB是我们常用的接口,它有四条引脚线:5V、D+、D-、GND。PC的USB口的D+、D-接有下拉电阻,平时处于低电平状态。USB设备的USB口内部,D-或D+接有1.5K的上拉电阻;它一接入PC,就会把PC的USB口的D-或D+拉高,从硬件的角度通知PC有新设备接入。所以USB能够自动监测设备。
简单来说,USB驱动分为两块,一块是USB总线驱动,这个在Linux内核里已经给我们做好了。当接入USB设备时,它能察觉到USB设备的接入,给USB设备分配编号(地址),并告诉USB设备给它分配的编号(地址)是多少,和获取USB设备的描述符。然后通过设备描述符的信息找打对应的USB设备驱动程序,并给USB设备驱动程序提供读写函数。第二块就是USB设备驱动程序,我们需要做的就是这一块。USB设备驱动程序用通过USB总线驱动程序提供的读写函数,根据不同的数据做不同的事情。
其他的一些概念:
1、USB是主从结构的,所有的USB传输都是从USB主机这方发起的,USB设备没有”主动”通知USB主机的能力。例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来读数据,只能被动地等得PC机来读。
2、 USB的传输类型:
a. 控制传输:可靠,时间有保证,比如:USB设备的识别过程
b. 批量传输: 可靠, 时间没有保证, 比如:U盘
c. 中断传输:可靠,实时,比如:USB鼠标
d. 实时传输:不可靠,实时,比如:USB摄像头
3、USB传输的对象:端点(endpoint)
我们说”读U盘”、”写U盘”,可以细化为:把数据写到U盘的端点1,从U盘的端点2里读出数据。除了端点0外,每一个端点只支持一个方向的数据传输,端点0用于控制传输,既能输出也能输入。
4、 每一个端点都有传输类型,传输方向。
5、 术语里、程序里说的输入(IN)、输出(OUT) “都是” 基于USB主机的立场说的。比如鼠标的数据是从鼠标传到PC机, 对应的端点称为”输入端点”。
6、USB总线驱动程序的作用
a. 识别USB设备
b. 查找并安装对应的设备驱动程序
c. 提供USB读写函数
二、USB设备驱动的框架
app:
————————————————————
内核——————————————————
USB总线驱动程序 // 1. 识别, 2. 找到匹配的设备驱动, 3. 提供USB读写函数 (它不知道数据含义)
————————————————————
USB主机控制器
UHCI OHCI EHCI
硬件 ——————————————————–
USB设备
UHCI: intel, 低速(1.5Mbps)/全速(12Mbps)
OHCI: microsoft 低速/全速
EHCI: 高速(480Mbps)
从上面你的图可知道当我们写一个USB 设备驱动时,我们最主要的目的是解释USB传输数据的含义(即要根据数据的内容去做事情)。在接受数据之前需要让我们的USB设备驱动程序和USB设备建立一个连接,它是怎么连接的呢?通过向上注册一个usb_driver结构体,当设备插入时,通过得到的设备描述符与这个结构体进行比较匹配,当匹配时就建立联系,详细的匹配过程很复杂,这里就不深入了。当设备和驱动建立连接时,将调用注册的结构里的probe函数。为了能完成驱动的一些事务,我们需要在这个函数内进行一些设备相关操作。比如,设置数据传输的3要素(USB的源,数据长度,目的),使用3要素(urb),提交urb。
总结USB设备的驱动的框架:
1、分配usb_driver结构体;
struct usb_driver {
const char *name;//名字
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);//连接时调用
void (*disconnect) (struct usb_interface *intf);//拔出时调用
int (*ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
void (*pre_reset) (struct usb_interface *intf);
void (*post_reset) (struct usb_interface *intf);
const struct usb_device_id *id_table;//匹配ID,将用于和设备匹配
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
};
2、设置usb_driver结构体;
3、注册usb_driver结构体;int usb_register(struct usb_driver *driver)
4、在插入时调用probe函数;在probe函数里进行设备相关的操作。比如:
a、设置数据传输3要素(USB的源,数据长度,目的)。
b、使用三要素:分配一个urburb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
填充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)
完成这些步骤,我们就能写出简单的USB设备驱动程序了。
三、USB设备驱动实例
USB鼠标设备驱动,将鼠标当做按键,左键输入l,右键输入s,中间输入回车。
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
static struct input_dev *uk_dev;
static char *usb_buf;
static dma_addr_t usb_buf_phys;
static int len;
static struct urb *uk_urb;
static void my_usb_mouse_irq(struct urb *urb)
{
static unsigned char pre_val = 0;
if((pre_val & (1<<0)) != (usb_buf[0] & (1<<0))){
input_event(uk_dev,EV_KEY,KEY_L,(usb_buf[0]&(1 << 0))?1:0);
input_sync(uk_dev);
}
if((pre_val & (1<<1)) != (usb_buf[0] & (1<<1))){
input_event(uk_dev,EV_KEY,KEY_S,(usb_buf[0]&(1 << 1))?1:0);
input_sync(uk_dev);
}
if((pre_val & (1<<2)) != (usb_buf[0] & (1<<2))){
input_event(uk_dev,EV_KEY,KEY_ENTER,(usb_buf[0]&(1 << 2))?1:0);
input_sync(uk_dev);
}
pre_val = usb_buf[0];
usb_submit_urb(uk_urb, GFP_KERNEL);
}
static int my_usb_mouse_probe (struct usb_interface *intf,const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int pipe;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
/*分配一个input_device结构*/
uk_dev = input_allocate_device();
/*设置哪一类事件*/
set_bit(EV_KEY,uk_dev->evbit);
set_bit(EV_REP,uk_dev->evbit);
/*设置哪些事件*/
set_bit(KEY_L,uk_dev->keybit);
set_bit(KEY_S,uk_dev->keybit);
set_bit(KEY_ENTER,uk_dev->keybit);
/*注册input_dev结构*/
input_register_device(uk_dev);
/*硬件相关的操作*/
/*源*/
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
/*长度*/
len = endpoint->wMaxPacketSize;
/*目的*/
usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);
/*使用三要素*/
/*1、分配USB request block*/
uk_urb = usb_alloc_urb(0, GFP_KERNEL);
/*2、使用三要素填充URB*/
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, my_usb_mouse_irq, NULL, endpoint->bInterval);
uk_urb->transfer_dma= usb_buf_phys;
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/*2、使用URB*/
usb_submit_urb(uk_urb, GFP_KERNEL);
return 0;
}
static void my_usb_mouse_disconnect (struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev(intf);
usb_kill_urb(uk_urb);
usb_free_urb(uk_urb);
usb_buffer_free(dev, len, usb_buf, usb_buf_phys);
input_unregister_device(uk_dev);
input_free_device(uk_dev);
}
static struct usb_device_id my_usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
static struct usb_driver my_usb_mouse_driver = {
.name = "my_usb_mouse",
.probe = my_usb_mouse_probe,
.disconnect = my_usb_mouse_disconnect,
.id_table = my_usb_mouse_id_table,
};
static int __init my_usb_mouse_init(void)
{
return usb_register(&my_usb_mouse_driver);
}
static void __exit my_usb_mouse_exit(void)
{
usb_deregister(&my_usb_mouse_driver);
}
module_init(my_usb_mouse_init);
module_exit(my_usb_mouse_exit);
MODULE_LICENSE("GPL");
本文介绍了Linux下USB设备驱动的相关概念,包括USB接口的工作原理、USB驱动的框架以及USB设备驱动程序的编写流程。USB驱动主要由USB总线驱动和设备驱动组成,设备驱动负责解释USB传输数据的含义。通过注册并匹配usb_driver结构体,设备插入时会调用probe函数进行设备初始化。此外,还以USB鼠标驱动为例,展示了简单的驱动实现。

1280

被折叠的 条评论
为什么被折叠?



