zoukankan      html  css  js  c++  java
  • USB驱动分析

    INIT函数:

     

    这是内核模块的初始化函数,其所作的工作只有注册定义好的USB驱动结构体。

    USB驱动结构体如下:

     

    Usb_driver中的probe函数是驱动和设备匹配成功后调用。

    Usb_driver中的disconnect函数是驱动和设备断开连接后后调用。

    Id_table中是驱动能够支持的设备列表,usb_device_id中记载的就是支持的设备。其中USB_interface_info是用来定义一类USB鼠标设备。

    MODULE_DEVICE_TABLE定义如下:

     

     

    MODULE_DEVICE_TABLE的第一个参数是设备的类型,如果是USB设备,就是usb,后一个参数是设备表。

    这条语句把支持设备数据添加到了/lib/module/内核名称/module.usbmap中,使模块装载程序知道什么模块对应什么硬件设备。

     

    EXIT函数

     

    唯一要做的就是注销usb_driver

    Probe函数:

     

    当设备匹配成功的时候,就需要调用prob函数:

     

    probe的主要任务1为向输入子系统注册,需要的工作如下:(功能相关)

    1.注册

    2.告诉输入子系统需要支持哪些事件

     

    probe的主要任务2为创建URB,那么他需要做好以下几点准备:(总线相关)

    1.urb指针

    2.要访问的设备

    3.管道

    4.host的数据缓冲区

    5.回调函数

     

    当然了,还有一大堆判断,比如判断是不是只有一个端点0HID规范)等等。

    static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)

    {

    //USB接口描述符被当做参数传递进来

    /* 设备描述 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;

     

         /* 根据HID规范,鼠标只有一个端点(不包含0号控制端点)*/

          //判断端点是否合法

    if (interface->desc.bNumEndpoints != 1)

    return -ENODEV;

     

    /* 获取端点0描述符 */

    endpoint = &interface->endpoint[0].desc;

    /* 根据HID规范,鼠标唯一的端点应为中断端点 */

    //还是在判断端点的类型是不是合法的,因为鼠标是中断控制的,所以端点应该是中断端点

    if (!usb_endpoint_is_int_in(endpoint))

    return -ENODEV;

     

    /* 生成中断管道 */

          //因为鼠标是中断控制,所以管道的类型应该是中断管道

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

    /* 返回该端点能够传输的最大的包长度,鼠标的返回的最大数据包为4个字节。*/  

    //初始化URB的时候会用到这个长度,缓冲区的长度要依照maxp来决定,最大不能超过8

    maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

     

    //mouse结构申请内存

        //mouse结构的主要作用是赋值给usb_interface中的一个属性

    //以便于触发其它函数的时候通过usb_interface中的这个属性就可以知道相关信息

    //usb_interface中的这个属性是专门为了储存用户需要的数据的

    mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);

    /* 创建input设备 */

    input_dev = input_allocate_device();

    if (!mouse || !input_dev)

    goto fail1;

     

      /* 申请内存空间用于数据传输,data 为指向该空间的地址*/

      //初始化URB需要的缓冲区,第四个参数是DMA相关

    mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma);

    if (!mouse->data)

    goto fail1;

     

    //////////////////////////////////

    ///////////////////////////////

    //到这准备工作做完了,开始操作urb阶段

    /* 分配URB */

    mouse->irq = usb_alloc_urb(0, GFP_KERNEL);

    if (!mouse->irq)

    goto fail2;

     

     //过程中顺便给mouse变量赋值

    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));

    }

    //usb_mousename属性赋值

    if (!strlen(mouse->name))

    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用来获取USB设备在sysfs中的路径

    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;

    //dev中获取总线类型,设备id,厂商id,版本号

    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)| BIT_MASK(BTN_SIDE) |

    BIT_MASK(BTN_EXTRA);

    //支持的相对坐标为鼠标移动坐标和滚轮坐标

    input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) | BIT_MASK(REL_WHEEL);

    //mouse传入input_dev,因为openclose函数的传入参数就只有input_dev,而我们需要mouse

    input_set_drvdata(input_dev, mouse);

    //制定打开函数和关闭函数

    input_dev->open = usb_mouse_open;

    input_dev->close = usb_mouse_close;

     

    /* 初始化中断URB */

    //没有中断函数,因为我们都知道其实是主控制器在不断地轮询

    //注意:mouse被设置到urb的上下文里,方便到时候调用。

    usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,

     (maxp > 8 ? 8 : maxp),

     usb_mouse_irq, mouse, endpoint->bInterval);

    //mouseirq就是urb,下面应该是设置DMA传输相关

    //flag使用URB_NO_TRANSFER_DMA_MAP的时候,优先使用transfer_dma,而不是transfer_buffer

    mouse->irq->transfer_dma = mouse->data_dma;

    mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    //注册输入子系统

    error = input_register_device(mouse->dev);

    if (error)

    goto fail3;

    //mouse传递给intf

    usb_set_intfdata(intf, mouse);

    return 0;

     

    fail3:

    usb_free_urb(mouse->irq);

    fail2:

    usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);

    fail1:

    input_free_device(input_dev);

    kfree(mouse);

    return error;

    }

    PS : mouse类型如下:

     

     

     

    Disconnect函数

    当断开匹配的时候就会调用这个函数:

    static void usb_mouse_disconnect(struct usb_interface *intf)

    {

    //probe函数中,我们把mouse传递进了intf

    struct usb_mouse *mouse = usb_get_intfdata (intf);

    usb_set_intfdata(intf, NULL);

    //mouse中的属性逐个击破

    if (mouse) {

    usb_kill_urb(mouse->irq);

    input_unregister_device(mouse->dev);

    usb_free_urb(mouse->irq);

    usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);

    kfree(mouse);

    }

    }

     

    Open函数

    当打开设备文件,就会调用open函数:

     

    Urb实在这里提交的,保证了只有打开设备文件才会提交urb

    Close函数:

    关闭设备文件的时候调用

    static void usb_mouse_close(struct input_dev *dev)

    {

    //probe函数中将mouse传入input_dev

    struct usb_mouse *mouse = input_get_drvdata(dev);

    usb_kill_urb(mouse->irq);

    }

    Usb_mouse_irq回调函数

    回调函数,完成URB请求的操作会调用一次这个回调函数。

    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;

    //检查urb是否传输成功

    switch (urb->status) {

    case 0: /* success */

    break;

    case -ECONNRESET: /* unlink */

    case -ENOENT:

    case -ESHUTDOWN:

    return;

    /* -EPIPE:  should clear the halt */

    default: /* error */

    goto resubmit;

    }

    //按这么看,data的第一个字节应该是代表按键,按下和抬起都会触发

    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);

    //第二个,第三个字节代表鼠标的xy坐标,第四个字节是滑轮的当前值

    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:

    status = usb_submit_urb (urb, GFP_ATOMIC);

    if (status)

    err ("can't resubmit intr, %s-%s/input0, status %d",

    mouse->usbdev->bus->bus_name,

    mouse->usbdev->devpath, status);

    }





  • 相关阅读:
    移动网络优化
    移动网络架构与数据传输
    移动网络简介与RRC
    CSS之外边距折叠
    网络协议之TLS
    Smarty 模板引擎简介
    FormData介绍
    相对路径与绝对路径
    OAuth2.0
    Redis学习手册(List数据类型)
  • 原文地址:https://www.cnblogs.com/dchipnau/p/5255358.html
Copyright © 2011-2022 走看看