Skip to main content
 首页 » 操作系统

USB设备驱动_WDS

2022年07月19日30JustinYoung

1. usb_alloc_dev中的 bus_type 中指定了匹配函数,和uevent中的环境参数。 ====>不同的bus_type的匹配函数可能是不同的,uevent的
环境变量参数也可能是不同的。

2. 推荐《LINUX内核源代码情景分析》,里面讲USB控制器源代码讲解的非常清晰。

3.当USB设备插入的时候会调用 usb_driver 的probe(),当USB设备拔出的时候会调用 disconnect()函数,和其它非支持热插拔的有些不
不同。所以usb_driver结构体里面至少要初始化id_table,probe,disconnect.

4.一个逻辑设备使用一个接口标识,比如声卡有录音和播放音乐的功能,因此有两个接口,两个驱动,分别负责录音和播放。

5.写驱动时先参考别人的,然后搞出一个最简单的框架,然后在逐渐丰满它。

6.唯一的dev->devnum用作了构建pipe的参数,应该可以使USB知道通过这个pipe的数据是发给哪个设备的。
int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
/*1<<30|(dev->devnum << 8) | (endpoint << 15) | 0x80*/
#define usb_rcvintpipe(dev, endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)

7.中断传输是USB主机控制器不断地查询,然后有数据了USB主机控制器就中断CPU,然后回调urb完成回调函数。回调函数类似中断服务函数
一样被调用。

8.若不明白鼠标数据的含义,可以先把数据打印出来,然后移动/点击鼠标对比查看数据的含义。

10.# cat /dev/tty1 用来查看串口打印。

11.# hexdump /dev/input0 查看设备节点接收到的原始的数据,测试是OK的

12.目的:把一个USB鼠标用作键盘,参考drivers/hid/usbhid/usbmouse.c
测试:
1.make menuconfig去掉之前的mouse驱动。若不去掉内核会匹配上它。
  -> Device Drivers
    -> HID Devices
      <> USB Human Interface Device (full HID) support
2.加载自己的驱动,插拔USB鼠标测试。

实验源码:

/* 
 * drivers\hid\usbhid\usbmouse.c 
 */ 
 
#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 struct usb_device_id usbmouse_as_key_id_table [] = { 
    { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, 
        USB_INTERFACE_PROTOCOL_MOUSE) }, 
    //{USB_DEVICE(0x1234,0x5678)}, 
    { }    /* Terminating entry */ 
}; 
 
static void usbmouse_as_key_irq(struct urb *urb) 
{ 
    static unsigned char pre_val; 
#if 0     
    int i; 
    static int cnt = 0; 
    printk("data cnt %d: ", ++cnt); 
    for (i = 0; i < len; i++) 
    { 
        printk("%02x ", usb_buf[i]); 
    } 
    printk("\n"); 
#endif 
    /* USB鼠标数据含义 
     * data[0]: bit0-左键, 1-按下, 0-松开 
     *          bit1-右键, 1-按下, 0-松开 
     *          bit2-中键, 1-按下, 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]; 
 
    /* 重新提交urb */ 
    usb_submit_urb(uk_urb, GFP_KERNEL); 
} 
 
static int usbmouse_as_key_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; 
 
    /* a. 分配一个input_dev */ 
    uk_dev = input_allocate_device(); 
     
    /* b. 设置 */ 
    /* b.1 能产生哪类事件 */ 
    set_bit(EV_KEY, uk_dev->evbit); 
    set_bit(EV_REP, uk_dev->evbit); 
     
    /* b.2 能产生哪些事件 */ 
    set_bit(KEY_L, uk_dev->keybit); 
    set_bit(KEY_S, uk_dev->keybit); 
    set_bit(KEY_ENTER, uk_dev->keybit); 
     
    /* c. 注册 */ 
    input_register_device(uk_dev); 
     
    /* d. 硬件相关操作 */ 
    /* 数据传输3要素: 源,目的,长度 */ 
    /* 源: USB设备的某个端点 */ 
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); 
 
    /* 长度: */ 
    len = endpoint->wMaxPacketSize; 
 
    /* 目的: */ 
    usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys); 
 
    /* 使用"3要素" */ 
    /* 分配usb request block */ 
    uk_urb = usb_alloc_urb(0, GFP_KERNEL); 
    /* 使用"3要素设置urb" */ 
    usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval); 
    uk_urb->transfer_dma = usb_buf_phys; 
    uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 
 
    /* 使用URB */ 
    usb_submit_urb(uk_urb, GFP_KERNEL); 
     
    return 0; 
} 
 
static void usbmouse_as_key_disconnect(struct usb_interface *intf) 
{ 
    struct usb_device *dev = interface_to_usbdev(intf); 
 
    //printk("disconnect usbmouse!\n"); 
    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); 
} 
 
/* 1. 分配/设置usb_driver */ 
static struct usb_driver usbmouse_as_key_driver = { 
    .name        = "usbmouse_as_key_", 
    .probe        = usbmouse_as_key_probe, 
    .disconnect    = usbmouse_as_key_disconnect, 
    .id_table    = usbmouse_as_key_id_table, 
}; 
 
 
static int usbmouse_as_key_init(void) 
{ 
    /* 2. 注册 */ 
    usb_register(&usbmouse_as_key_driver); 
    return 0; 
} 
 
static void usbmouse_as_key_exit(void) 
{ 
    usb_deregister(&usbmouse_as_key_driver);     
} 
 
module_init(usbmouse_as_key_init); 
module_exit(usbmouse_as_key_exit); 
 
MODULE_LICENSE("GPL");

参考内核中的usbmouse.c:

#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> 
 
/* for apple IDs */ 
#ifdef CONFIG_USB_HID_MODULE 
#include "../hid-ids.h" 
#endif 
 
/* 
 * Version Information 
 */ 
#define DRIVER_VERSION "v1.6" 
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" 
#define DRIVER_DESC "USB HID Boot Protocol mouse driver" 
 
MODULE_AUTHOR(DRIVER_AUTHOR); 
MODULE_DESCRIPTION(DRIVER_DESC); 
MODULE_LICENSE("GPL"); 
 
struct usb_mouse { 
    char name[128]; /*存储的是dev->manufacturer*/ 
    char phys[64]; 
    struct usb_device *usbdev; 
    struct input_dev *dev; 
    struct urb *irq; /*中断urb,也就是轮询鼠标*/ 
 
    signed char *data; /*存储DMA地址对应的虚拟地址*/ 
    dma_addr_t data_dma; /*存储DMA地址*/ 
}; 
 
static void usb_mouse_irq(struct urb *urb) 
{ 
    struct usb_mouse *mouse = urb->context; 
    signed char *data = mouse->data; 
    struct input_dev *dev = mouse->dev; 
    int status; 
 
    switch (urb->status) { 
    case 0:            /* success */ 
        break; 
    case -ECONNRESET:    /* unlink */ 
    case -ENOENT: 
    case -ESHUTDOWN: 
        return; 
    /* -EPIPE:  should clear the halt */ 
    default:        /* error */ 
        goto resubmit; 
    } 
 
    /*是把所有的坐标上报一遍,按下和松开App都可以监听到了。若是没有任 
    何改变的话这个完成函数应该不会被调用的*/ 
    input_report_key(dev, BTN_LEFT,   data[0] & 0x01); /*鼠标左键*/ 
    input_report_key(dev, BTN_RIGHT,  data[0] & 0x02); /*鼠标右键*/ 
    input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); /*鼠标中键*/ 
    input_report_key(dev, BTN_SIDE,   data[0] & 0x08); 
    input_report_key(dev, BTN_EXTRA,  data[0] & 0x10); 
 
    input_report_rel(dev, REL_X,     data[1]); 
    input_report_rel(dev, REL_Y,     data[2]); 
    input_report_rel(dev, REL_WHEEL, data[3]); 
 
    input_sync(dev); 
resubmit: 
    /*open 输入设备节点的时候submit一次,之后每次urb完成后都重复submit已保持 
    一致处于工作状态*/ 
    status = usb_submit_urb (urb, GFP_ATOMIC); 
    if (status) 
        dev_err(&mouse->usbdev->dev, 
            "can't resubmit intr, %s-%s/input0, status %d\n", 
            mouse->usbdev->bus->bus_name, 
            mouse->usbdev->devpath, status); 
} 
 
static int usb_mouse_open(struct input_dev *dev) 
{ 
    struct usb_mouse *mouse = input_get_drvdata(dev); /*probe中set进去的*/ 
 
    mouse->irq->dev = mouse->usbdev; 
    if (usb_submit_urb(mouse->irq, GFP_KERNEL)) 
        return -EIO; 
 
    return 0; 
} 
 
static void usb_mouse_close(struct input_dev *dev) 
{ 
    struct usb_mouse *mouse = input_get_drvdata(dev); 
 
    usb_kill_urb(mouse->irq); 
} 
 
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id) 
{ 
    /*通过接口获取usb_device结构体*/ 
    struct usb_device *dev = interface_to_usbdev(intf); 
    struct usb_host_interface *interface; 
    struct usb_endpoint_descriptor *endpoint; 
    struct usb_mouse *mouse; 
    struct input_dev *input_dev; 
    int pipe, maxp; 
    int error = -ENOMEM; 
 
    /*获取当前设置的这个接口的描述*/ 
    interface = intf->cur_altsetting; /* the currently  active alternate setting */ 
 
    /*进一步判断是不是鼠标,这里认为鼠标只有1个IN端点的*/ 
    if (interface->desc.bNumEndpoints != 1) 
        return -ENODEV; 
 
    endpoint = &interface->endpoint[0].desc; /*只有1个端点,所以只能是endpoint[0]*/ 
    if (!usb_endpoint_is_int_in(endpoint)) /*进一步判断是不是中断输入端点,无论是主从设备的端点名都是以host为参考起方向名字的*/ 
        return -ENODEV; 
 
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); 
    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); /*传参(dev, pipe, 0),返回:epd->wMaxPacketSize & USB_ENDPOINT_MAXP_MASK;*/ 
 
    /*构建usb鼠标这个设备*/ 
    mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); 
    input_dev = input_allocate_device(); 
    if (!mouse || !input_dev) 
        goto fail1; 
 
    /*mouse->data中存储虚拟地址,mouse->data_dma存储DMA地址*/ 
    mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma); 
    if (!mouse->data) 
        goto fail1; 
 
    /*看来host是在轮询鼠标读取鼠标中的数据*/ 
    mouse->irq = usb_alloc_urb(0, GFP_KERNEL); 
    if (!mouse->irq) 
        goto fail2; 
 
    mouse->usbdev = dev; 
    mouse->dev = input_dev; 
 
    if (dev->manufacturer) 
        strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name)); 
 
    if (dev->product) { 
        if (dev->manufacturer) 
            strlcat(mouse->name, " ", sizeof(mouse->name)); /*加一个空格*/ 
        strlcat(mouse->name, dev->product, sizeof(mouse->name)); 
    } 
 
    if (!strlen(mouse->name))  /*如果这个usb设备没有指定manufacturer和product字符使用VID PID作为名字*/ 
        snprintf(mouse->name, sizeof(mouse->name), 
             "USB HIDBP Mouse %04x:%04x", 
             le16_to_cpu(dev->descriptor.idVendor), 
             le16_to_cpu(dev->descriptor.idProduct)); 
 
    usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); 
    strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); 
 
    input_dev->name = mouse->name; 
    input_dev->phys = mouse->phys; 
    usb_to_input_id(dev, &input_dev->id); 
    input_dev->dev.parent = &intf->dev;  /*构建设备在设备树中的层次结构*/ 
 
    input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); /*对于鼠标有按键和滚轮和相对位移,所以是这两个*/ 
    /*左键 中键 右键*/ 
    input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); 
    /*只有x和y方向,没有z的*/ 
    input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); 
    /*这两个是什么意思 ???? */ 
    input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA); 
    input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); 
 
    input_set_drvdata(input_dev, mouse); 
 
    /*所有的输入设备都必须要提供这两个函数*/ 
    input_dev->open = usb_mouse_open; /*里面仅仅提交了一个urb*/ 
    input_dev->close = usb_mouse_close; /*里面只做了kill urb的操作*/ 
 
    /*完成回调函数usb_mouse_irq,当USB主机控制器收到数据后会产生一个中断,然后usb_mouse_irq会被调用*/ 
    usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, 
             (maxp > 8 ? 8 : maxp), 
             usb_mouse_irq, mouse, endpoint->bInterval); /*endpoint->bInterval: 这里查询的频率是由设备自己决定的*/ 
    mouse->irq->transfer_dma = mouse->data_dma; /*上面usb_fill_int_urb中的赋值是传给不使用DMA的transfer_buffer的*/ 
    mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* urb->transfer_dma valid on submit */ 
 
    error = input_register_device(mouse->dev); 
    if (error) 
        goto fail3; 
 
    /*为了注销时使用mouse结构*/ 
    usb_set_intfdata(intf, mouse); 
    return 0; 
 
fail3: 
    usb_free_urb(mouse->irq); 
fail2: 
    usb_free_coherent(dev, 8, mouse->data, mouse->data_dma); 
fail1: 
    input_free_device(input_dev); 
    kfree(mouse); 
    return error; 
} 
 
static void usb_mouse_disconnect(struct usb_interface *intf) 
{ 
    struct usb_mouse *mouse = usb_get_intfdata (intf); 
 
    usb_set_intfdata(intf, NULL); 
    if (mouse) { 
        usb_kill_urb(mouse->irq); 
        input_unregister_device(mouse->dev); 
        usb_free_urb(mouse->irq); 
        usb_free_coherent(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); 
        kfree(mouse); 
    } 
} 
 
static const struct usb_device_id usb_mouse_id_table[] = { 
    /*表示只要接口的class是hid,子class是boot,协议是mouse,都可以支持*/ 
    { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) }, 
    { USB_DEVICE(0x1234, 0x5678) }, /*I add it, 表示只支持这一款设备*/ 
    { }    /* Terminating entry */ 
}; 
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); 
 
static struct usb_driver usb_mouse_driver = { 
    .name        = "usbmouse", 
    .probe        = usb_mouse_probe, 
    .disconnect    = usb_mouse_disconnect, 
    .id_table    = usb_mouse_id_table, 
}; 
 
module_usb_driver(usb_mouse_driver)

本文参考链接:https://www.cnblogs.com/hellokitty2/p/10203422.html