Linux下的USB设备驱动

本文介绍了Linux下USB设备驱动的相关概念,包括USB接口的工作原理、USB驱动的框架以及USB设备驱动程序的编写流程。USB驱动主要由USB总线驱动和设备驱动组成,设备驱动负责解释USB传输数据的含义。通过注册并匹配usb_driver结构体,设备插入时会调用probe函数进行设备初始化。此外,还以USB鼠标驱动为例,展示了简单的驱动实现。

一、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");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值