1. 程式人生 > >USB裝置驅動_WDS

USB裝置驅動_WDS

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 <[email protected]>"
#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)