zoukankan      html  css  js  c++  java
  • Linux USB驱动学习总结(三)---- USB鼠标的加载、初始化和通信过程

    1、usbmouse的定义:usb鼠标既包含usb设备(usb_device)的属性也包含input输入设备(input_dev)的属性

    struct usb_mouse {
    
    char name[128];///USB鼠标设备名称
    
    char phys[64];///路径
    
    struct usb_device *usbdev;///USB设备
    
    struct input_dev *dev;///Input 设备
    
    struct urb *irq; ///urb结构体
    
     
    signed char *data;///数据传输缓冲区指针
    
    dma_addr_t data_dma;///
    
    };

    2、usbmouse的加载:

    module_usb_driver(usb_mouse_driver);///系统启动时注册usb_mouse_driver

    3、usbmouse的初始化:

     1 static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
     2 {
     3     struct usb_device *dev = interface_to_usbdev(intf);///通过 USB 接口来获得 usb 设备
     4     struct usb_host_interface *interface;
     5     struct usb_endpoint_descriptor *endpoint;
     6     struct usb_mouse *mouse;
     7     struct input_dev *input_dev;
     8     int pipe, maxp;
     9     int error = -ENOMEM;
    10 
    11     interface = intf->cur_altsetting;///获取 usb_host_interface
    12 
    13     if (interface->desc.bNumEndpoints != 1)/// usb鼠标端点有且只有一个控制端点,否则返回 ENODEV 
    14         return -ENODEV;
    15 
    16     endpoint = &interface->endpoint[0].desc; ///usb鼠标只有一个端点,获取端点描述符
    17     if (!usb_endpoint_is_int_in(endpoint))///检查端点是不是中断类型的输入端点
    18         return -ENODEV;
    19 
    20     pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);////获取 pipe(),根据端点地址bEndpointAddress,中断方式,IN端点就可以得到一个pipe,然后主机就知道跟谁去通信,该如何通信
    21     maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));///获取主机和设备一次通讯的最大字节数
    22 
    23     mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);///分配一个usb_mouse
    24     input_dev = input_allocate_device();///初始化 input 设备
    25     if (!mouse || !input_dev)
    26         goto fail1;
    27 
    28     mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma);///为usbmouse的data 分配8个字节的空间
    29     if (!mouse->data)
    30         goto fail1;
    31 
    32     mouse->irq = usb_alloc_urb(0, GFP_KERNEL);///申请分配 urb ,赋值给 usb_mouse 的 urb
    33     if (!mouse->irq)
    34         goto fail2;
    35 
    36     mouse->usbdev = dev; //设置usb鼠标设备的usb设备对象
    37     mouse->dev = input_dev;//设备usb鼠标设备的input设备对象
    38 
    39     if (dev->manufacturer)///枚举时候有获取到有效的厂商名
    40         strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));///复制厂商名到 name 
    41 
    42     if (dev->product) { //枚举时候有获取到有效的产品名
    43         if (dev->manufacturer) //如果也有厂商名
    44             strlcat(mouse->name, " ", sizeof(mouse->name));     //则用空格将厂商名和产品名隔开
    45         strlcat(mouse->name, dev->product, sizeof(mouse->name));//追加产品名到name
    46     }
    47 
    48     if (!strlen(mouse->name)) //如果厂商和产品名都没有
    49         snprintf(mouse->name, sizeof(mouse->name), //则直接根据厂商id和产品id给name赋值
    50              "USB HIDBP Mouse %04x:%04x",
    51              le16_to_cpu(dev->descriptor.idVendor),
    52              le16_to_cpu(dev->descriptor.idProduct));
    53 
    54     usb_make_path(dev, mouse->phys, sizeof(mouse->phys)); //设置设备路径名
    55     strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); //追加/input0
    56 
    57     input_dev->name = mouse->name; //输入设备的名字设置成usb鼠标的名字
    58     input_dev->phys = mouse->phys; //输入设备的路径设置成usb鼠标的路径
    59     usb_to_input_id(dev, &input_dev->id); //设置输入设备的bustype,vendor,product,version
    60     input_dev->dev.parent = &intf->dev; //usb接口设备为输入设备的父设备
    61 
    62     ////evbit 关于设备支持事件类型的 bitmap 
    63     input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); ///BIT_MASK 找到参数值所在的 bit位,输入事件按键类型 + 相对位移
    64     input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | ///鼠标支持左键、右键、中键三个按键
    65         BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
    66     input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); ///REL_X REL_Y 表示鼠标的位置信息 x  Y 
    67     input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | ///在已有按键的基础上加上一个边键和一个而外的键
    68         BIT_MASK(BTN_EXTRA);
    69     input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);///给相对事件加上滚轮的事件
    70 
    71     input_set_drvdata(input_dev, mouse);///usb鼠标驱动文件作为输入设备的设备文件的驱动数据 " input_dev -> dev->driver_data = mouse "
    72 
    73     input_dev->open = usb_mouse_open; //设置输入事件的打开方法
    74     input_dev->close = usb_mouse_close; //设置输入事件的关闭方法
    75 
    76     usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data, ///初始化 urb (中断传输方式),并指定 urb 的回调函数是 usb_mouse_irq
    77              (maxp > 8 ? 8 : maxp),
    78              usb_mouse_irq, mouse, endpoint->bInterval);//// usb_mouse_irq --回调函数,上下文信息 -- mouse
    79     mouse->irq->transfer_dma = mouse->data_dma;//dma数据缓冲区指向usb鼠标设备的data_dma成员
    80     mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;///没有 DMA 映射 
    81 
    82     error = input_register_device(mouse->dev);///注册设备驱动 mouse->dev
    83     if (error)
    84         goto fail3;
    85 
    86     usb_set_intfdata(intf, mouse);///usb 鼠标驱动文件作为 usb 接口设备的设备文件的驱动数据 ;intf->dev->driver_data = mouse ;
    87     return 0;
    88 
    89 fail3:    
    90     usb_free_urb(mouse->irq);
    91 fail2:    
    92     usb_free_coherent(dev, 8, mouse->data, mouse->data_dma);
    93 fail1:    
    94     input_free_device(input_dev);
    95     kfree(mouse);
    96     return error;
    97 }

    经过probe过程,注册了输入设备则会在/dev/input/目录下会产生对应的鼠标设备节点,应用程序可以打开该节点来控制usb鼠标设备

    关键函数调用顺序:

    .open   =   evdev_open,///上层应用程序通过系统调用open 打开设备

    static int evdev_open(struct inode *inode, struct file *file);

    evdev_open_device(evdev);

    input_open_device(&evdev->handle);

    dev->open(dev);////---->调用usbmouse_open()

    input_dev->open = usb_mouse_open; //设置输入事件的打开方法

    usb_submit_urb(mouse->irq, GFP_KERNEL)

    usb_mouse_irq(struct urb *urb)

    input_report_keyinput_report_relinput_sync ///提交鼠标数据给input 子系统

    usb_submit_urb (urb, GFP_ATOMIC);///usb设备提交urb,主机再次轮询usb设备

    static int usb_mouse_open(struct input_dev *dev)
    {
        struct usb_mouse *mouse = input_get_drvdata(dev); //通过输入设备获取usb鼠标设备
    
        mouse->irq->dev = mouse->usbdev; //设置urb设备对应的usb设备
        if (usb_submit_urb(mouse->irq, GFP_KERNEL))///提交 urb ,只有打开设备的时候,才会把 urb 发送出去
            return -EIO;
    
        return 0;
    }
     1 static void usb_mouse_irq(struct urb *urb)
     2 {
     3     struct usb_mouse *mouse = urb->context; ///获取 usb 鼠标设备
     4     signed char *data = mouse->data; ///数据传输缓冲区指针
     5     struct input_dev *dev = mouse->dev;//输入设备
     6     int status;
     7 
     8     switch (urb->status) {///判断 urb 传输的状态
     9     case 0:            /* success */ ///传输成功跳出 switch
    10         break;
    11     case -ECONNRESET:    /* unlink */
    12     case -ENOENT:
    13     case -ESHUTDOWN:
    14         return;
    15     /* -EPIPE:  should clear the halt */
    16     default:        /* error */
    17         goto resubmit;
    18     }
    19 
    20     input_report_key(dev, BTN_LEFT,   data[0] & 0x01);////提交按键信息,data[0] 的第 0 位为 1,表示左键按下
    21     input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);////提交按键信息,data[0] 的第 1 位为 1,表示右键按下
    22     input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);////提交按键信息,data[0] 的第 2 位为 1,表示中键按下
    23     input_report_key(dev, BTN_SIDE,   data[0] & 0x08);////提交按键信息,data[0] 的第 3 位为 1,表示边键按下
    24     input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);////提交按键信息,data[0] 的第 4 位为 1,表示而外键按下
    25 
    26     input_report_rel(dev, REL_X,     data[1]);///提交鼠标相对坐标值,data[1] 为 X 坐标
    27     input_report_rel(dev, REL_Y,     data[2]);///提交鼠标相对坐标值,data[2] 为 Y 坐标
    28     input_report_rel(dev, REL_WHEEL, data[3]);///提交鼠标滚轮相对值,data[3] 为 滚轮相对值
    29 
    30     input_sync(dev);///同步信息,表示上面的信息作为完整一帧传递给上层系统
    31 resubmit:
    32     status = usb_submit_urb (urb, GFP_ATOMIC);///usb设备提交urb,主机再次轮询usb设备
    33     if (status)
    34         dev_err(&mouse->usbdev->dev,
    35             "can't resubmit intr, %s-%s/input0, status %d
    ",
    36             mouse->usbdev->bus->bus_name,
    37             mouse->usbdev->devpath, status);
    38 }

    (注:以上图片来自麦子学院 金鑫老师的课程,在此对其辛勤付出和无私分享表示真挚的感谢!)

  • 相关阅读:
    类的加载过程
    算法模板之基数排序
    算法模板之Dijkstra
    算法模板之SPFA
    算法模板之树状数组
    算法模板之排序
    深入JVM-自动内存管理(读书笔记)
    VMware Fault-Tolerant Virtual Machine 论文总结
    深入JVM--高效并发(读书笔记)
    欧拉素数筛
  • 原文地址:https://www.cnblogs.com/EaIE099/p/5124512.html
Copyright © 2011-2022 走看看