精华内容
下载资源
问答
  • 只有struct device_driver定义的driver才会被usb系统自动注册到总线上,它内部的kobj才会添加到bus->drivers对象集合中,driver->knode_bus被链接到bus->klist_drivers链表中,当发现新硬件或者insmod安装驱动时总线...

    只有struct device_driver定义的driver才会被usb系统自动注册到总线上,它内部的kobj才会添加到bus->drivers对象集合中,driver->knode_bus被链接到bus->klist_drivers链表中,当发现新硬件或者insmod安装驱动时总线调用drv->bus->match(dev, drv),这个drv就是struct device_driver类型driver,之后使用dev->bus->probe(dev)如果dev->bus->probe==null那么使用drv->probe(dev),这里的drv也是struct device_driver类型driver,所以被底层bus总线调用的driver驱动是struct device_driver类型的驱动,当然match为bus提供的函数,probe则是bus->probe或者device_driver->probe而绝不会是mct_u232_driver->probe,mct_u232_driver内嵌了struct device_driver结构,在其内部的定义是mct_u232_driver->drvwrap.driver,而drvwrap.driver的方法在usb_register()->usb_register_driver()函数下面摘录的代码中登记(gliethttp_20071031):
    ...
        new_driver->drvwrap.driver.bus = &usb_bus_type;
        new_driver->drvwrap.driver.probe = usb_probe_interface;
        new_driver->drvwrap.driver.remove = usb_unbind_interface;
    ...
    所以hub发现新硬件之后,会调用usb_bus_type->match()和drvwrap.driver.probe(),在drvwrap.driver.probe()代码中会调用mct_u232_driver->probe(intf, id);这个mct_u232_driver->probe才是我们用户自己写的程序了,其他的那些probe都是usb内核系统自己提供的,我们用户可以不用去理会,来看看mct_u232_driver的定义:
    static struct usb_driver mct_u232_driver = {
        .name =        "mct_u232",
        .probe =    usb_serial_probe,
        .disconnect =    usb_serial_disconnect,
        .id_table =    id_table_combined,
        .no_dynamic_id =     1,
    };
    当usb内核系统发现新硬件并且通过bus->match匹配和drvwrap.driver.probe()初步筛选之后,就会调用我们这个mct_u232_driver中定义的usb_serial_probe了,这个usb_serial_probe所做的工作就细致的很了,因为hub检测的硬件的所有详细信息都将在这里登记到内核中,注册到各个目录下,/sys/devices/目录下,ports申请创建--bulk in、bulk out、int in和int out的read_urb,bulk_in_buffer数据缓冲,write_urb,bulk_out_buffer数据缓冲区,interrupt_in_urb,interrupt_in_buffer数据缓冲,相应的申请活动中会调用usb_fill_int_urb()将申请到的urb进一步初始化,比如read_int_callback、write_int_callback、read_bulk_callback和write_bulk_callback数据处理结束后的回调函数将被填充进去,在这个函数中有一个经常用到的变量type,他的类型为usb_serial_driver,这个type就是在usb_serial_driver_list总线上注册登记的所有drivers驱动中的和dev最后匹配上的一个usb_serial_driver指针,对于我们这里谈的东西,这个type就是mct_u232_device,因为mct_u232_device在usb_serial_register(),将自己的mct_u232_device->driver_list节点添加到了usb_serial_driver_lst总线驱动链表上,当这些初始化完成之后,将注册登记hub检测到的经过以上分析出来的所有port下的dev设备们port->dev.bus = &usb_serial_bus_type;device_register(&port->dev);//将dev注册到devices_subsys这个kset集合上,这样/sys/devices/目录下也就有了这个dev设备文件了在device_register()中会调用bus_attach_device()来为这个port->dev设备寻找合适的driver驱动,对于具体到我们的分析,就是通过bus_for_each_drv()函数遍历usb_serial_bus_type总线上的klist_drivers链表,usb_serial_bus_type的原始定义如下:
    struct bus_type usb_serial_bus_type = {
        .name =        "usb-serial",
        .match =    usb_serial_device_match,
        .probe =    usb_serial_device_probe,
        .remove =    usb_serial_device_remove,
        .drv_attrs =     drv_attrs,
    };
    所以这个bus总线提供了match和probe,因此如果驱动定义了probe,那么驱动的probe是不会调用的,不过还好usb_serial_driver->driver没有做任何定义,仅仅在我们用户自己的程序中定义了它的名字.name,具体为:
    static struct usb_serial_driver mct_u232_device = {
        .driver = {
            .owner =    THIS_MODULE,
            .name =        "mct_u232",
        },
    ...
    };
    正如最开始提到的usb_serial_driver->driver也是struct device_driver类型结构,usb_serial_bus_type总线上的dev设备的bus指针指向port->dev.bus = &usb_serial_bus_type;所以bus_for_each_drv()最后将调用usb_serial_bus_type->match()和usb_serial_bus_type->probe()来为dev硬件自己寻找匹配的设备驱动.

    流程1:insmod安装驱动函数流程[driver_attach]:
    //在__driver_attach()函数中会调用下面这两个函数[gliethttp_20071031]:
    //drv->bus->match和dev->bus->probe或者drv->probe函数
    //注意:port->dev.bus = &usb_serial_bus_type;
    // mct_u232_device->driver.bus = &usb_serial_bus_type;
    1>usb_serial_bus_type->match() --- usb_serial_device_match//内核总线提供
    2>usb_serial_bus_type->probe() --- usb_serial_device_probe//内核总线提供
    流程2:hub发现新硬件寻找驱动流程[device_attach]:
    //-----usb系统总线级-----
    1>usb_bus_type->match() --- usb_device_match()//内核总线提供
    //因为usb_bus_type不提供probe()函数,所以由用户驱动drvwrap.driver提供
    2>mct_u232_driver->drvwrap.driver.probe() --- usb_probe_interface()//内核提供
    //-----usb用户驱动自定义级-----
    //usb_probe_interface()会调用driver->probe()驱动,即:mct_u232_driver->probe()
    3>mct_u232_driver->probe() --- usb_serial_probe()//用户自定义
    //probe()会从usb_serial_driver_list总线链表上找到一个和dev匹配的usb_serial_driver指针,
    //并赋值给type变量,我们这里就是serial->type=&mct_u232_device;然后尝试调用type->probe()
    //这里就是mct_u232_device->probe(),如果找不到type,那么直接退出返回,所以这里我感觉
    //可能会出现这样一种情况:必须先给usb设备安装上驱动,然后再把usb设备插到主板上,这样linux系统
    //就能正常的匹配驱动和设备,否则,如果先将设备插到主板上,自匹配执行到这里之后会直接退出,
    //那么新插入的设备因为在bus的驱动链表上找不到匹配的driver驱动而不能登记到bus的klist_devices
    //设备链表上,所以新插入的设备也就失去了机会,之后insmod的驱动在bus的klist_devices
    //也不会找到该设备,这样insmod的驱动只是简单的把自己挂载到bus的klist_drivers驱动链表上,
    //要想使刚才的设备正常运行,需要重新插拔一次usb设备,所以我觉得好像必须先安装驱动,
    //再插入设备,否则还需要重新再插拔一次,linux的这个性质和windows不同,windows有提示界面,
    //当发现新插入的设备没有驱动时,会弹出提示窗,等待用户手动安装驱动[gliethttp_20071031].
    4>type->probe() --- mct_u232_device->probe()没有定义
    //然后调用device_add()将port->dev注册登记到总线和系统中,
    //port = serial->port;
    //port->dev.parent = &interface->dev;
    //port->dev.driver = NULL;
    //port->dev.bus = &usb_serial_bus_type;
    //retval = device_register(&port->dev);//将dev注册到系统
    //在__device_attach()函数中会调用下面这两个函数[gliethttp_20071031]:
    //drv->bus->match和dev->bus->probe或者drv->probe函数
    //注意:port->dev.bus = &usb_serial_bus_type;
    // mct_u232_device->driver.bus = &usb_serial_bus_type;
    5>usb_serial_bus_type->match() --- usb_serial_device_match//内核总线提供
    6>usb_serial_bus_type->probe() --- usb_serial_device_probe//内核总线提供

    综述:
      mct_u232_device驱动位于usb_serial_bus_type总线上,mct_u232_driver驱动位于usb_bus_type总线上,当hub发现usb新硬件之后,会首先调用usb_bus_type总线上的mct_u232_driver驱动的probe(),也就是usb_serial_probe(),在usb_serial_probe(),程序会遍历usb_serial_driver_list驱动链表的所有驱动,并尝试和发现的新硬件进行匹配,在计算匹配的过程中会调用,drv->bus->match,:usb_serial_bus_type->match()和dev->bus->probe或者drv->probe即:usb_serial_device_probe(),这样设备就和分别处在两条独立总线上的mct_u232_driver驱动以及mct_u232_device驱动关联上了

    //port->dev.parent = &interface->dev;
    //port->dev.driver = NULL;
    //port->dev.bus = &usb_serial_bus_type;
    //retval = device_register(&port->dev);//将dev注册到系统
    //在__device_attach()函数中会调用下面这两个函数[gliethttp_20071031]:
    //drv->bus->match和dev->bus->probe或者drv->probe函数
    //注意:port->dev.bus = &usb_serial_bus_type;
    // mct_u232_device->driver.bus = &usb_serial_bus_type;
    5>usb_serial_bus_type->match() --- usb_serial_device_match//内核总线提供
    6>usb_serial_bus_type->probe() --- usb_serial_device_probe//内核总线提供

    综述:
      mct_u232_device驱动位于usb_serial_bus_type总线上,mct_u232_driver驱动位于usb_bus_type总线上,当hub发现usb新硬件之后,会首先调用usb_bus_type总线上的mct_u232_driver驱动的probe(),也就是usb_serial_probe(),在usb_serial_probe(),程序会遍历usb_serial_driver_list驱动链表的所有驱动,并尝试和发现的新硬件进行匹配,在计算匹配的过程中会调用,drv->bus->match,:usb_serial_bus_type->match()和dev->bus->probe或者drv->probe即:usb_serial_device_probe(),这样设备就和分别处在两条独立总线上的mct_u232_driver驱动以及mct_u232_device驱动关联上了

    展开全文
  • wd ses device usb device 西部数据移动硬盘 驱动下载

    千次下载 热门讨论 2011-03-14 21:20:38
    wd ses device usb device 西部数据 移动硬盘 驱动 wd ses device usb device 西部数据 移动硬盘 驱动 wd ses device usb device 西部数据 移动硬盘 驱动
  • wdsesdevice驱动是一款移动硬盘驱动程序,让你能够在电脑上进行移动硬盘的正确安装,从而使用硬盘实现各类数据存储与转存的效果,十分便捷,快下载吧!内容介绍wdsesdeviceusbdevice驱动,WDC#160;SCSI#160;...
  • usb device万能驱动是一款万能的usb驱动程序。该驱动可以用于解决UNKNOWN DEVICE缺少usb驱动的问题。有需要的朋友,赶快下载体验吧!软件介绍很多朋友电脑的总线控制器内的UNKNOWN DEVICE提示没有该设备的驱动,可以...
  • usb device驱动程序,usb device驱动程序软件介绍 usb device驱动程序 很多朋友电脑的总线控制器内的UNKNOWN DEVICE提示没有该设备的驱动,可以试试这款usb device驱动看能否解决问题
  • LINUX USB device驱动

    千次阅读 2017-05-23 10:29:01
    前面学习了USB驱动的一些基础概念与重要的数据结构,那么究竟如何编写一个USB 驱动程序呢?编写与一个USB设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和...

     前面学习了USB驱动的一些基础概念与重要的数据结构,那么究竟如何编写一个USB 驱动程序呢?编写与一个USB设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否安装了硬件。当然,这些制造商和设备标识需要我们编写进USB 驱动程序中。

      USB 驱动程序依然遵循设备模型 —— 总线、设备、驱动。和I2C 总线设备驱动编写一样,所有的USB驱动程序都必须创建的主要结构体是 struct usb_driver,它们向USB 核心代码描述了USB 驱动程序。但这是个外壳,只是实现设备和总线的挂接,具体的USB 设备是什么样的,如何实现的,比如一个字符设备,我们还需填写相应的文件操作接口 ,下面我们从外到里进行剖析,学习如何搭建这样的一个USB驱动外壳框架:


    一、注册USB驱动程序

      Linux的设备驱动,特别是这种hotplug的USB设备驱动,会被编译成模块,然后在需要时挂在到内核。所以USB驱动和注册与正常的模块注册、卸载是一样的,下面是USB驱动的注册与卸载:
    [cpp] view plain copy
    1. static int __init usb_skel_init(void)     
    2. {     
    3.      int result;     
    4.      /* register this driver with the USB subsystem */     
    5.      result = usb_register(&skel_driver);     
    6.      if (result)     
    7.          err("usb_register failed. Error number %d", result);     
    8.     
    9.      return result;     
    10. }     
    11.     
    12. static void __exit usb_skel_exit(void)     
    13. {     
    14.      /* deregister this driver with the USB subsystem */     
    15.      usb_deregister(&skel_driver);     
    16. }     
    17.     
    18. module_init (usb_skel_init);     
    19. module_exit (usb_skel_exit);     
    20. MODULE_LICENSE("GPL");  

          USB设备驱动的模块加载函数通用的方法是在I2C设备驱动的模块加载函数中使用usb_register(struct *usb_driver)函数添加usb_driver的工作,而在模块卸载函数中利用usb_deregister(struct *usb_driver)做相反的工作。 对比I2C设备驱动中的 i2c_add_driver(&i2c_driver)i2c_del_driver(&i2c_driver)

        struct usb_driver是USB设备驱动,我们需要实现其成员函数:

    [cpp] view plain copy
    1. static struct usb_driver skel_driver = {     
    2.      .owner = THIS_MODULE,      
    3.      .name = "skeleton",    
    4.      .id_table = skel_table,         
    5.      .probe = skel_probe,       
    6.      .disconnect = skel_disconnect,       
    7. };      
    从代码看来,usb_driver需要初始化五个字段:

    模块的所有者 THIS_MODULE
    模块的名字  skeleton
    probe函数   skel_probe
    disconnect函数skel_disconnect

    id_table

        最重要的当然是probe函数与disconnect函数,这个在后面详细介绍,先谈一下id_table:

        id_table 是struct usb_device_id 类型,包含了一列该驱动程序可以支持的所有不同类型的USB设备。如果没有设置该变量,USB驱动程序中的探测回调该函数将不会被调用。对比I2C中struct i2c_device_id *id_table,一个驱动程序可以对应多个设备,i2c 示例:

    [cpp] view plain copy
    1. static const struct i2c_device_id mpu6050_id[] = {      
    2.     { "mpu6050", 0},      
    3.     {}      
    4. };    

        usb子系统通过设备的production ID和vendor ID的组合或者设备的class、subclass跟protocol的组合来识别设备,并调用相关的驱动程序作处理。我们可以看看这个id_table到底是什么东西:

    [cpp] view plain copy
    1. static struct usb_device_id skel_table [] = {       
    2.      { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },       
    3.      { }                    /* Terminating entry */       
    4. };       
    5.       
    6. MODULE_DEVICE_TABLE (usb, skel_table);     

       MODULE_DEVICE_TABLE的第一个参数是设备的类型,如果是USB设备,那自然是usb。后面一个参数是设备表这个设备表的最后一个元素是空的,用于标识结束。代码定义了USB_SKEL_VENDOR_ID是0xfff0,USB_SKEL_PRODUCT_ID是0xfff0,也就是说,当有一个设备接到集线器时,usb子系统就会检查这个设备的vendor ID和product ID,如果它们的值是0xfff0时,那么子系统就会调用这个skeleton模块作为设备的驱动。

       当USB设备接到USB控制器接口时,usb_core就检测该设备的一些信息,例如生产厂商ID和产品的ID,或者是设备所属的class、subclass跟protocol,以便确定应该调用哪一个驱动处理该设备
      
     

         我们下面所要做的就是对probe函数与disconnect函数的填充了,但是在对probe函数与disconnect函数填充之前,有必要先学习三个重要的数据结构,这在我们后面probe函数与disconnect函数中有很大的作用:

    二、USB驱动程序中重要数据结构

    1、usb-skeleton

           usb-skeleton 是一个局部结构体,用于与端点进行通信。下面先看一下Linux内核源码中的一个usb-skeleton(就是usb驱动的骨架咯),其定义的设备结构体就叫做usb-skel:

    [cpp] view plain copy
    1. struct usb_skel {     
    2.      struct usb_device *udev;                 /* the usb device for this device */     
    3.      struct usb_interface  *interface;            /* the interface for this device */     
    4.      struct semaphore limit_sem;         /* limiting the number of writes in progress */     
    5.      unsigned char *bulk_in_buffer;     /* the buffer to receive data */     
    6.      size_t         bulk_in_size;                  /* the size of the receive buffer */     
    7.      __u8          bulk_in_endpointAddr;        /* the address of the bulk in endpoint */     
    8.      __u8          bulk_out_endpointAddr;      /* the address of the bulk out endpoint */     
    9.      struct kref   kref;     
    10. };    

    他拥有:

    描述usb设备的结构体udev
    一个接口interface
    用于并发访问控制的semaphore(信号量) limit_sem
    用于接收数据的缓冲bulk_in_buffer
    用于接收数据的缓冲尺寸bulk_in_size
    批量输入端口地址bulk_in_endpointAddr
    批量输出端口地址bulk_out_endpointAddr

    内核使用的引用计数器


         从开发人员的角度看,每一个usb设备有若干个配置(configuration)组成,每个配置又可以有多个接口(interface)(我理解就是USB设备的一项功能),每个接口又有多个设置,而接口本身可能没有端点或者多个端点(end point)

    2、USB 接口数据结构 struct usb_interface

    [cpp] view plain copy
    1. struct usb_interface    
    2. {             
    3.          struct usb_host_interface *altsetting;     
    4.          struct usb_host_interface *cur_altsetting;          
    5.          unsigned num_altsetting;             
    6.          int minor;                          
    7.          enum usb_interface_condition condition;             
    8.          unsigned is_active:1;               
    9.          unsigned needs_remote_wakeup:1;      
    10.          struct device dev;                  
    11.          struct device *usb_dev;             
    12.          int pm_usage_cnt;                   
    13. };    

           在逻辑上,一个USB设备的功能划分是通过接口来完成的。比如说一个USB扬声器,可能会包括有两个接口:一个用于键盘控制,另外一个用于音频流传输。而事实上,这种设备需要用到不同的两个驱动程序来操作,一个控制键盘,一个控制音频流。但也有例外,比如蓝牙设备,要求有两个接口,第一用于ACL跟EVENT的传输,另外一个用于SCO链路,但两者通过一个驱动控制。在Linux上,接口使用struct usb_interface来描述,以下是该结构体中比较重要的字段:

    a -- struct usb_host_interface *altsetting(注意不是usb_interface)

           其实据我理解,他应该是每个接口的设置,虽然名字上有点奇怪。该字段是一个设置的数组(一个接口可以有多个设置),每个usb_host_interface都包含一套由struct usb_host_endpoint定义的端点配置。但这些配置次序是不定的。

    b -- struct usb_host_interface *cur_altsetting

           当前活动的设置,指向altsetting数组中的一个

    struct usb_host_interface数据结构:

    [cpp] view plain copy
    1. struct usb_host_interface     
    2. {    
    3.          struct usb_interface_descriptor desc;//usb描述符,主要有四种usb描述符,设备描述符,配置描述符,接口描述符和端点描述符,协议里规定一个usb设备是必须支持这四大描述符的。    
    4.                                  //usb描述符放在usb设备的eeprom里边    
    5.          /* array of desc.bNumEndpoint endpoints associated with this  
    6.           * interface setting. these will be in no particular order.  
    7.           */    
    8.          struct usb_host_endpoint *endpoint;//这个设置所使用的端点    
    9.     
    10.          char *string;           /* iInterface string, if present */    
    11.          unsigned char *extra;   /* Extra descriptors */关于额外描述符    
    12.          int extralen;    
    13. };    

    c -- unsigned num_altstting

          可选设置的数量,即altsetting所指数组的元素个数

    d -- int minor

         当捆绑到该接口的USB驱动程序使用USB主设备号时,USB core分配的次设备号。仅在成功调用usb_register_dev之后才有效。  


    3、USB 端点 struct usb_host_endpoint

          Linux中用struct usb_host_endpoint 来描述USB端点

    [cpp] view plain copy
    1. struct usb_host_endpoint     
    2. {    
    3.          struct usb_endpoint_descriptor desc;    
    4.          struct list_head                urb_list;//端点要处理的urb队列.urb是usb通信的主角,设备中的每个端点都可以处理一个urb队列.要想和你的usb通信,就得创建一个urb,并且为它赋好值,    
    5.                                    //交给咱们的usb core,它会找到合适的host controller,从而进行具体的数据传输    
    6.          void                            *hcpriv;//这是提供给HCD(host controller driver)用的    
    7.          struct ep_device                *ep_dev;        /* For sysfs info */    
    8.     
    9.          unsigned char *extra;   /* Extra descriptors */    
    10.          int extralen;    
    11. };    

         每个usb_host_endpoint中包含一个struct usb_endpoint_descriptor结构体,当中包含该端点的信息以及设备自定义的各种信息,这些信息包括:

    a -- bEndpointAddress(b for byte)

           8位端点地址,其地址还隐藏了端点方向的信息(之前说过,端点是单向的),可以用掩码USB_DIR_OUT和USB_DIR_IN来确定。

    b -- bmAttributes

           端点的类型,结合USB_ENDPOINT_XFERTYPE_MASK可以确定端点是USB_ENDPOINT_XFER_ISOC(等时)、USB_ENDPOINT_XFER_BULK(批量)还是USB_ENDPOINT_XFER_INT(中断)。

    c -- wMaxPacketSize

           端点一次处理的最大字节数。发送的BULK包可以大于这个数值,但会被分割传送。

    d -- bInterval

           如果端点是中断类型,该值是端点的间隔设置,以毫秒为单位


    三、探测和断开函数分析

            USB驱动程序指定了两个USB核心在适当时间调用的函数。

    1、探测函数

            当一个设备被安装而USB核心认为该驱动程序应该处理时,探测函数被调用;

            探测函数应该检查传递给他的设备信息确定驱动程序是否真的适合该设备。当驱动程序因为某种原因不应控制设备时,断开函数被调用,它可以做一些清洁的工作。

          系统会传递给探测函数的信息是什么呢?一个usb_interface * 跟一个struct usb_device_id *作为参数。他们分别是该USB设备的接口描述(一般会是该设备的第0号接口,该接口的默认设置也是第0号设置)跟它的设备ID描述(包括Vendor ID、Production ID等)。

           USB驱动程序应该初始化任何可能用于控制USB设备的局部结构体,它还应该把所需的任何设备相关信息保存到局部结构体中。例如,USB驱动程序通常需要探测设备对的端点地址和缓冲区大小,因为需要他们才能和端点通信

           下面具体分析探测函数做了哪些事情:

    a -- 探测设备的端点地址、缓冲区大小,初始化任何可能用于控制USB设备的数据结构

           下面是一个实例代码,他们探测批量类型的IN和OUT端点,把相关信息保存到一个局部设备结构体中

    [cpp] view plain copy
    1. /* set up the endpoint information */     
    2.      /* use only the first bulk-in and bulk-out endpoints */     
    3.      iface_desc = interface->cur_altsetting;     
    4.      for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {     
    5.          endpoint = &iface_desc->endpoint[i].desc;   
    6.     
    7.          if ( !dev->bulk_in_endpointAddr &&     
    8.                 ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) = = USB_DIR_IN) &&     
    9.              ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) = = USB_ENDPOINT_XFER_BULK)) {     
    10.              /* we found a bulk in endpoint */     
    11.               buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);     
    12.               dev->bulk_in_size = buffer_size;     
    13.               dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;     
    14.               dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);     
    15.               if (!dev->bulk_in_buffer) {     
    16.                   err("Could not allocate bulk_in_buffer");     
    17.                    goto error;                   
    18.               }  
    19.          }     
    20.     
    21.          if (!dev->bulk_out_endpointAddr &&     
    22.             ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)= =USB_DIR_OUT) &&     
    23.                ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)= = USB_ENDPOINT_XFER_BULK)) {     
    24.               /* we found a bulk out endpoint */     
    25.               dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;     
    26.          }     
    27.      }     
    28.     
    29.      if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {     
    30.          err("Could not find both bulk-in and bulk-out endpoints");     
    31.          goto error;     
    32.      }    

    具体流程如下:   

         该代码块首先循环访问该接口中存在的每一个端点,赋予该端点结构体的局部指针以使稍后的访问更加容易

    [cpp] view plain copy
    1. for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {     
    2.          endpoint = &iface_desc->endpoint[i].desc;   

             然后,我们有了一个端点,而还没有发现批量IN类型的端点时,查看该端点的方向是否为IN。这可以通过检查位掩码 USB_DIR_IN 是否包含在bEndpointAddress 端点变量中来确定。如果是的话,我们测定该端点类型是否批量,这首先通过USB_ENDPOINT_XFERTYPE_MASK 位掩码来取bmAttributes变量的值,然后检查它是否和USB_ENDPOINT_XFER_BULK 的值匹配来完成

    [cpp] view plain copy
    1. if ( !dev->bulk_in_endpointAddr &&     
    2.                ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) = = USB_DIR_IN) &&     
    3.             ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) = = USB_ENDPOINT_XFER_BULK)) {     

           如果这些都通过了,驱动程序就知道它已经发现了正确的端点类型,可以把该端点相关的信息保存到一个局部结构体中,就是我们前面的usb_skel ,以便稍后使用它和端点进行通信

    [cpp] view plain copy
    1. /* we found a bulk in endpoint */     
    2.               buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);     
    3.               dev->bulk_in_size = buffer_size;     
    4.               dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;     
    5.               dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);     
    6.               if (!dev->bulk_in_buffer) {     
    7.                   err("Could not allocate bulk_in_buffer");     
    8.                    goto error;                 
    9.           }     


    b -- 把已经初始化数据结构的指针保存到接口设备中

          接下来的工作是向系统注册一些以后会用的的信息。首先我们来说明一下usb_set_intfdata()他向内核注册一个data,这个data的结构可以是任意的,这段程序向内核注册了一个usb_skel结构,就是我们刚刚看到的被初始化的那个,这个data可以在以后用usb_get_intfdata来得到

    usb_set_intfdata(interface, dev);


    c -- 注册USB设备

           如果USB驱动程序没有和处理设备与用户交互(例如输入、tty、视频等)的另一种类型的子系统相关联,驱动程序可以使用USB主设备号,以便在用户空间使用传统的字符驱动程序接口。如果要这样做,USB驱动程序必须在探测函数中调用 usb_resgister_dev 函数来把设备注册到USB核心。只要该函数被调用,就要确保设备和驱动陈旭都处于可以处理用户访问设备的要求的恰当状态

    retval = usb_register_dev(interface, &skel_class);

    skel_class结构。这个结构又是什么?我们就来看看这到底是个什么东西:

    [cpp] view plain copy
    1. static struct usb_class_driver skel_class = {     
    2.      .name =       "skel%d",     
    3.      .fops =       &skel_fops,     
    4.      .minor_base = USB_SKEL_MINOR_BASE,     
    5. };    

        它其实是一个系统定义的结构,里面包含了一名字、一个文件操作结构体还有一个次设备号的基准值。事实上它才是定义真正完成对设备IO操作的函数。所以他的核心内容应该是skel_fops。

       因为usb设备可以有多个interface,每个interface所定义的IO操作可能不一样,所以向系统注册的usb_class_driver要求注册到某一个interface,而不是device,因此,usb_register_dev的第一个参数才是interface,而第二个参数就是某一个usb_class_driver。

       通常情况下,linux系统用主设备号来识别某类设备的驱动程序,用次设备号管理识别具体的设备,驱动程序可以依照次设备号来区分不同的设备,所以,这里的次设备好其实是用来管理不同的interface的,但由于这个范例只有一个interface,在代码上无法求证这个猜想。

    [cpp] view plain copy
    1. static struct file_operations skel_fops = {     
    2.      .owner = THIS_MODULE,     
    3.      .read =       skel_read,     
    4.      .write =   skel_write,     
    5.      .open =       skel_open,     
    6.      .release =    skel_release,     
    7. };    


    2、断开函数

          当设备被拔出集线器时,usb子系统会自动地调用disconnect,他做的事情不多,最重要的是注销class_driver(交还次设备号)和interface的data:

    [cpp] view plain copy
    1. dev = usb_get_intfdata(interface);    
    2. usb_set_intfdata(interface, NULL);    
    3.     
    4. /* give back our minor */    
    5. usb_deregister_dev(interface, &skel_class);    


    四、USB请求块

           USB 设备驱动代码通过urb和所有的 USB 设备通讯。urb用 struct urb 结构描述(include/linux/usb.h )。

           urb 以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个 USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。

     一个 urb 的典型生命循环如下:

     (1)被创建;
     (2)被分配给一个特定 USB 设备的特定端点;
     (3)被提交给 USB 核心;
     (4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动;
     (5)被 USB 主机控制器驱动处理, 并传送到设备;
     (6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。

     
       urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。

    [cpp] view plain copy
    1. struct urb  
    2. {  
    3.     /* 私有的:只能由usb核心和主机控制器访问的字段 */  
    4.     struct kref kref; /*urb引用计数 */  
    5.     spinlock_t lock; /* urb锁 */  
    6.     void *hcpriv; /* 主机控制器私有数据 */  
    7.     int bandwidth; /* int/iso请求的带宽 */  
    8.     atomic_t use_count; /* 并发传输计数 */  
    9.     u8 reject; /* 传输将失败*/  
    10.       
    11.     /* 公共的: 可以被驱动使用的字段 */  
    12.     struct list_head urb_list; /* 链表头*/  
    13.     struct usb_device *dev; /* 关联的usb设备 */  
    14.     unsigned int pipe; /* 管道信息 */  
    15.     int status; /* urb的当前状态 */  
    16.     unsigned int transfer_flags; /* urb_short_not_ok | ...*/  
    17.     void *transfer_buffer; /* 发送数据到设备或从设备接收数据的缓冲区 */  
    18.     dma_addr_t transfer_dma; /*用来以dma方式向设备传输数据的缓冲区 */  
    19.     int transfer_buffer_length;/*transfer_buffer或transfer_dma 指向缓冲区的大小 */  
    20.                            
    21.     int actual_length; /* urb结束后,发送或接收数据的实际长度 */  
    22.     unsigned char *setup_packet; /* 指向控制urb的设置数据包的指针*/  
    23.     dma_addr_t setup_dma; /*控制urb的设置数据包的dma缓冲区*/  
    24.     int start_frame; /*等时传输中用于设置或返回初始帧*/  
    25.     int number_of_packets; /*等时传输中等时缓冲区数据 */  
    26.     int interval; /* urb被轮询到的时间间隔(对中断和等时urb有效) */  
    27.     int error_count;  /* 等时传输错误数量 */  
    28.     void *context; /* completion函数上下文 */  
    29.     usb_complete_t complete; /* 当urb被完全传输或发生错误时,被调用 */  
    30.     struct usb_iso_packet_descriptor iso_frame_desc[0];  
    31.     /*单个urb一次可定义多个等时传输时,描述各个等时传输 */  
    32. };  


    1、创建和注销 urb

          struct urb 结构不能静态创建,必须使用 usb_alloc_urb 函数创建. 函数原型:

    struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
    //int iso_packets : urb 包含等时数据包的数目。如果不使用等时urb,则为0
    //gfp_t mem_flags : 与传递给 kmalloc 函数调用来从内核分配内存的标志类型相同

    //返回值          : 如果成功分配足够内存给 urb , 返回值为指向 urb 的指针. 如果返回值是 NULL, 则在 USB 核心中发生了错误, 且驱动需要进行适当清理


    如果驱动已经对 urb 使用完毕, 必须调用 usb_free_urb 函数,释放urb。函数原型:

    void usb_free_urb(struct urb *urb);
    //struct urb *urb : 要释放的 struct urb 指针


    2、初始化 urb

    [cpp] view plain copy
    1. static inline void usb_fill_int_urb(struct urb *urb,                                                                                                         
    2.                  struct usb_device *dev,  
    3.                  unsigned int pipe,  
    4.                  void *transfer_buffer,  
    5.                  int buffer_length,  
    6.                  usb_complete_t complete_fn,  
    7.                  void *context,  
    8.                  int interval);  
    9.   
    10. static inline void usb_fill_bulk_urb(struct urb *urb,  
    11.                  struct usb_device *dev,  
    12.                  unsigned int pipe,  
    13.                  void *transfer_buffer,  
    14.                  int buffer_length,  
    15.                  usb_complete_t complete_fn,  
    16.                  void *context);  
    17.   
    18. static inline void usb_fill_control_urb(struct urb *urb,  
    19.                     struct usb_device *dev,  
    20.                     unsigned int pipe,  
    21.                     unsigned char *setup_packet,  
    22.                     void *transfer_buffer,  
    23.                     int buffer_length,  
    24.                     usb_complete_t complete_fn,  
    25.                     void *context);  
    26.   
    27.   
    28. //struct urb *urb :指向要被初始化的 urb 的指针  
    29. //struct usb_device *dev :指向 urb 要发送到的 USB 设备.  
    30. //unsigned int pipe : urb 要被发送到的 USB 设备的特定端点. 必须使用前面提过的 usb_******pipe 函数创建  
    31. //void *transfer_buffer :指向外发数据或接收数据的缓冲区的指针.注意:不能是静态缓冲,必须使用 kmalloc 来创建.  
    32. //int buffer_length :transfer_buffer 指针指向的缓冲区的大小  
    33. //usb_complete_t complete :指向 urb 结束处理例程函数指针  
    34. //void *context :指向一个小数据块的指针, 被添加到 urb 结构中,以便被结束处理例程函数获取使用.  
    35. //int interval :中断 urb 被调度的间隔.  
    36. //函数不设置 urb 中的 transfer_flags 变量, 因此对这个成员的修改必须由驱动手动完成  
    37.   
    38. /*等时 urb 没有初始化函数,必须手动初始化,以下为一个例子*/  
    39. urb->dev = dev;  
    40. urb->context = uvd;  
    41. urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1);  
    42. urb->interval = 1;  
    43. urb->transfer_flags = URB_ISO_ASAP;  
    44. urb->transfer_buffer = cam->sts_buf[i];  
    45. urb->complete = konicawc_isoc_irq;  
    46. urb->number_of_packets = FRAMES_PER_DESC;  
    47. urb->transfer_buffer_length = FRAMES_PER_DESC;  
    48. for (j=0; j < FRAMES_PER_DESC; j++) {  
    49.         urb->iso_frame_desc[j].offset = j;  
    50.         urb->iso_frame_desc[j].length = 1;  
    51. }  


    3、提交 urb

          一旦 urb 被正确地创建并初始化, 它就可以提交给 USB 核心以发送出到 USB 设备. 这通过调用函数 usb_submit_urb 实现:

    int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
    //struct urb *urb :指向被提交的 urb 的指针 
    //gfp_t mem_flags :使用传递给 kmalloc 调用同样的参数, 用来告诉 USB 核心如何及时分配内存缓冲

    /*因为函数 usb_submit_urb 可被在任何时候被调用(包括从一个中断上下文), mem_flags 变量必须正确设置. 根据 usb_submit_urb 被调用的时间,只有 3 个有效值可用:
    GFP_ATOMIC 
    只要满足以下条件,就应当使用此值:
    1.调用者处于一个 urb 结束处理例程,中断处理例程,底半部,tasklet或者一个定时器回调函数.
    2.调用者持有自旋锁或者读写锁. 注意如果正持有一个信号量, 这个值不必要.
    3.current->state 不是 TASK_RUNNING. 除非驱动已自己改变 current 状态,否则状态应该一直是 TASK_RUNNING .

    GFP_NOIO 
    驱动处于块 I/O 处理过程中. 它还应当用在所有的存储类型的错误处理过程中.

    GFP_KERNEL 
    所有不属于之前提到的其他情况
    */


    在 urb 被成功提交给 USB 核心之后, 直到结束处理例程函数被调用前,都不能访问 urb 结构的任何成员.


    4、urb结束处理例程

          如果 usb_submit_urb 被成功调用, 并把对 urb 的控制权传递给 USB 核心, 函数返回 0; 否则返回一个负的错误代码. 如果函数调用成功, 当 urb 被结束的时候结束处理例程会被调用一次.当这个函数被调用时, USB 核心就完成了这个urb, 并将它的控制权返回给设备驱动.

    只有 3 种结束urb并调用结束处理例程的情况:

    (1)urb 被成功发送给设备, 且设备返回正确的确认.如果这样, urb 中的status变量被设置为 0.
    (2)发生错误, 错误值记录在 urb 结构中的 status 变量.
    (3)urb 从 USB 核心unlink. 这发生在要么当驱动通过调用 usb_unlink_urb 或者 usb_kill_urb告知 USB 核心取消一个已提交的 urb,或者在一个 urb 已经被提交给它时设备从系统中去除.

    5、取消 urb

    使用以下函数停止一个已经提交给 USB 核心的 urb:

    void usb_kill_urb(struct urb *urb)
    int usb_unlink_urb(struct urb *urb);

    如果调用usb_kill_urb函数,则 urb 的生命周期将被终止. 这通常在设备从系统移除时,在断开回调函数(disconnect callback)中调用.

    对一些驱动, 应当调用 usb_unlink_urb 函数来使 USB 核心停止 urb. 这个函数不会等待 urb 完全停止才返回. 这对于在中断处理例程中或者持有一个自旋锁时去停止 urb 是很有用的, 因为等待一个 urb 完全停止需要 USB 核心有使调用进程休眠的能力(wait_event()函数).

    展开全文
  • samsung android usb device驱动是官方提供的一款USB驱动,本站收集提供高速下载,用于解决USB接口不能正常识别,无法正常使用的问题,本动适用于:Windows XP / Windows 7 / Windows 8 / Windows 10 32/64位操作...

    samsung android usb device驱动是官方提供的一款USB驱动,本站收集提供高速下载,用于解决USB接口不能正常识别,无法正常使用的问题,本动适用于:Windows XP / Windows 7 / Windows 8 / Windows 10 32/64位操作系统。有需要的朋友可以来本站下载安装。
    在这里插入图片描述
    samsung android usb device驱动
    http://www.equdong.net/qtrj/usbdrv/16184.html

    展开全文
  • 天语 usb device 驱动

    2012-10-17 15:55:03
    天语usb驱动,压缩文件中有说明书,按照说明安装上驱动以后,数据线连上电脑直接可以访问手机里tf卡上的文件,再也不需安装豌豆荚等软件了,超级好用,附带赠送刷机驱动。天语W619小黄蜂亲测可用。
  • USB Mass Storage Device_驱动

    热门讨论 2012-11-12 21:10:02
    USB Mass Storage Device_
  • dv3105tx usb device驱动

    2009-06-25 12:02:45
    dv3105tx usb device驱动 dv3105tx usb device驱动
  • usb device驱动下载 首先介绍HID: HID是Human Interface Devices的缩写.翻译成中文即为人机交互设备.这里的人机交互设备是一个宏观上面的概念,任何设备,只要符合HID spec,都可以称之为HID设备 在make menuconfig中...

    usb device驱动下载

    首先介绍HID:
    HID是Human Interface Devices的缩写.翻译成中文即为人机交互设备.这里的人机交互设备是一个宏观上面的概念,任何设备,只要符合HID spec,都可以称之为HID设备

    在make menuconfig中,选中USB Human Interface Device(full HID) support。则所有USB HID都会被驱动,其中包括USB Mouse。
    在drivers/hid/usbhid/Kconfig看到这项对应的为:CONFIG_USB_HID
    又在drivers/hid/usbhid/Makefile中看到:obj-$(CONFIG_USB_HID) += usbhid.o 换句话说,如果选中built-in.则 obj-y中加入usbhid.o  如果将这一项选中为M(module),则obj-m中加入usbhid.o  
    也就是说:如果选中USB Human Interface Device(full HID) support为built-in.则usbhid.o会被built-in。
    再在drivers/hid/usbhid/.usbhid.o.cmd中看到usbhid.o其实是由:hid-core.o,hid-quirks.o,hiddev.o组成的。
    也就是说:当在Menuconfig中选中那一项后,这三个.o都会被built-in.

    下一篇从driver角度学习。 在drivers/hid/usbhid/hid-core.c中,有如下语句:
    module_init(hid_init);
    表明当hid-usb.o(hid-core.o等三个组成)添加入kernel core时,会调用hid_init.


    1. hid_init分析:
    hid_init首先调用usbhid_quirks_init();
    1.1. usbhid_quirks_init() 解析:
    其实就是查找insmod 时给的pid,vid参数在quirks列表中是否有,如果有,就替换。没有就创建。

    1.2. hiddev_init();
    此function只有在选中CONFIG_USB_HIDDEV才会真正做事。
    也就是说:只有在配置kernel时选中下面条目才有效.
    config USB_HIDDEV
    bool "/dev/hiddev raw HID device support"
    它只是简单的注册一个USB设备。但这个设备在USB 硬件插入时什么都不作。

    1.3 usb_register(&hid_driver);
    注册一个USB driver. 从这个driver的id_table来看: .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS 表明匹配的是:Interface class .bInterfaceClass = USB_INTERFACE_CLASS_HID 表明Interface Class为HID设备,则会被唤醒。
     

    2. HID USB设备被插入时的状况:
    分析hid_driver->probe
    第一个参数为USB Core传过来的USB设备Interface。第二个参数为本driver的id_table.
    2.1 usb_hid_configure(intf); 首先查看quirks.使用usbhid_lookup_quirk()从静态和动态quirks list中查是否此device包含在其中。   Sam看HID driver是以mouse为线索, interface->desc.bInterfaceSubClass=USB_CLASS_HID interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE 所以:quirks |= HID_QUIRK_NOGET;    
       



    背景知识一:模块参数:
    当使用insmod或modprobe安装模块时,可以通过模块参数给模块传递一些数值。这增加了模块的灵活性。但在使用模块参数之前,必须要让这些参数对insmod可见,则可以使用如下方式,让insmod知道参数名:
    module_param_named(name, value, type, perm)
    name是参数的名称(insmod时使用)
    value是参数在模块中对应的变量
    type是参数的类型
    perm是参数的权限(其实就是/sys/module/[module_name]/parameters的权限)


    例如:
    int disk_size = 1024;
    module_param_named(size, disk_size, int, S_IRUGO);
    则给模块加上名称为"size"的参数,如果在加载模块是使用
    insmod thismodule size=100,
    那么在模块代码中disk_size的值就是100。相反,如果加载模块时没有指定参数,那么模块代码中disk_size的值仍是默认的1024。
    注意,所有模块参数,都应该给定一个默认值。   MODULE_PARM_DESC(),对模块参数的描述。         背景知识二:模块宏: MODULE_AUTHOR();标明模块拥有者 MODULE_DESCRIPTION(); module描述 MODULE_LICENSE(); module license.如果没有,insmod时会警告 1. 解读hid device probe程序: static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
    {
     struct hid_device *hid;
     char path[64];
     int i;
     char *c;  dbg_hid("HID probe called for ifnum %d\n",
       intf->altsetting->desc.bInterfaceNumber);  if (!(hid = usb_hid_configure(intf)))
      return -ENODEV;  usbhid_init_reports(hid);
     hid_dump_device(hid);
     if (hid->quirks & HID_QUIRK_RESET_LEDS)
      usbhid_set_leds(hid);  if (!hidinput_connect(hid))
      hid->claimed |= HID_CLAIMED_INPUT;
     if (!hiddev_connect(hid))
      hid->claimed |= HID_CLAIMED_HIDDEV;  usb_set_intfdata(intf, hid);  if (!hid->claimed) {
      printk ("HID device not claimed by input or hiddev\n");
      hid_disconnect(intf);
      return -ENODEV;
     }  if ((hid->claimed & HID_CLAIMED_INPUT))
      hid_ff_init(hid);  if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
      hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
       intf->cur_altsetting->desc.bInterfaceNumber);  printk(KERN_INFO);  if (hid->claimed & HID_CLAIMED_INPUT)
      printk("input");
     if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))
      printk(",");
     if (hid->claimed & HID_CLAIMED_HIDDEV)
      printk("hiddev%d", hid->minor);  c = "Device";
     for (i = 0; i < hid->maxcollection; i++) {
      if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
          (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
          (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
       c = hid_types[hid->collection[i].usage & 0xffff];
       break;
      }
     }  usb_make_path(interface_to_usbdev(intf), path, 63);  printk(": USB HID v%x.%02x %s [%s] on %s\n",
      hid->version >> 8, hid->version & 0xff, c, hid->name, path);  return 0;
    }
    1.1: usb_hid_configure(intf)解读: 从字面来看,它是指配置hid。 static struct hid_device *usb_hid_configure(struct usb_interface *intf)
    {
     struct usb_host_interface *interface = intf->cur_altsetting;
     struct usb_device *dev = interface_to_usbdev (intf);
     struct hid_descriptor *hdesc;
     struct hid_device *hid;
     u32 quirks = 0;
     unsigned rsize = 0;
     char *rdesc;
     int n, len, insize = 0;
     struct usbhid_device *usbhid;   //得到对应vid,pid的quriks.如果没有,则返回0
     quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
       le16_to_cpu(dev->descriptor.idProduct));   //如果为boot设备且为keyboard或mouse.则quirks=HID_QUIRK_NOGET  
     if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
      if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||
       interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
        quirks |= HID_QUIRK_NOGET;
     }   //如果quirks显示要忽略,则退出probe  if (quirks & HID_QUIRK_IGNORE)
      return NULL;   //HID_QUIRK_IGNORE_MOUSE表示如果为mouse,则忽略。  if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
      (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
       return NULL;   //如果interface扩展描述符中没有类型为HID_DT_HID条目,或者interface包含的endpoint数目为0,又或者interface endpoint中扩展描述符中没有类型为HID_DT_HID的条目。则退出。
     if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
         (!interface->desc.bNumEndpoints ||
          usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
      dbg_hid("class descriptor not present\n");
      return NULL;
     }   //得到描述符长度:  for (n = 0; n < hdesc->bNumDescriptors; n++)
      if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
       rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);   //如果描述符长度不对,则退出  if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
      dbg_hid("weird size of report descriptor (%u)\n", rsize);
      return NULL;
     }   //创建此长度内存空间  if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
      dbg_hid("couldn't allocate rdesc memory\n");
      return NULL;
     }   //向dev的endpoint发送HID_REQ_SET_IDLE request.  hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);   //取得report description的详细信息,放到rdesc中。  if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
      dbg_hid("reading report descriptor failed\n");
      kfree(rdesc);
      return NULL;
     }  usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),
       le16_to_cpu(dev->descriptor.idProduct), rdesc,
       rsize, rdesc_quirks_param);  dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);
     for (n = 0; n < rsize; n++)
      dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
     dbg_hid_line("\n");   //解析report。并建立hid_device.返回给hid.  if (!(hid = hid_parse_report(rdesc, n))) {
      dbg_hid("parsing report descriptor failed\n");
      kfree(rdesc);
      return NULL;
     }  kfree(rdesc);
     hid->quirks = quirks;      if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
      goto fail_no_usbhid;  hid->driver_data = usbhid;
     usbhid->hid = hid;  usbhid->bufsize = HID_MIN_BUFFER_SIZE;
     hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize);
     hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize);
     hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize);  if (usbhid->bufsize > HID_MAX_BUFFER_SIZE)
      usbhid->bufsize = HID_MAX_BUFFER_SIZE;  hid_find_max_report(hid, HID_INPUT_REPORT, &insize);  if (insize > HID_MAX_BUFFER_SIZE)
      insize = HID_MAX_BUFFER_SIZE;  if (hid_alloc_buffers(dev, hid)) {
      hid_free_buffers(dev, hid);
      goto fail;
     }  for (n = 0; n < interface->desc.bNumEndpoints; n++) {   struct usb_endpoint_descriptor *endpoint;
      int pipe;
      int interval;   endpoint = &interface->endpoint[n].desc;
      if ((endpoint->bmAttributes & 3) != 3)  
       continue;   interval = endpoint->bInterval;   
      if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
       interval = hid_mousepoll_interval;   if (usb_endpoint_dir_in(endpoint)) {
       if (usbhid->urbin)
        continue;
       if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
        goto fail;
       pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
       usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize,
          hid_irq_in, hid, interval);
       usbhid->urbin->transfer_dma = usbhid->inbuf_dma;
       usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
      } else {
       if (usbhid->urbout)
        continue;
       if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL)))
        goto fail;
       pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);
       usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0,
          hid_irq_out, hid, interval);
       usbhid->urbout->transfer_dma = usbhid->outbuf_dma;
       usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
      }
     }  if (!usbhid->urbin) {
      err_hid("couldn't find an input interrupt endpoint");
      goto fail;
     }  init_waitqueue_head(&hid->wait);  INIT_WORK(&usbhid->reset_work, hid_reset);
     setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);  spin_lock_init(&usbhid->inlock);
     spin_lock_init(&usbhid->outlock);
     spin_lock_init(&usbhid->ctrllock);  hid->version = le16_to_cpu(hdesc->bcdHID);
     hid->country = hdesc->bCountryCode;
     hid->dev = &intf->dev;
     usbhid->intf = intf;
     usbhid->ifnum = interface->desc.bInterfaceNumber;  hid->name[0] = 0;  if (dev->manufacturer)
      strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));  if (dev->product) {
      if (dev->manufacturer)
       strlcat(hid->name, " ", sizeof(hid->name));
      strlcat(hid->name, dev->product, sizeof(hid->name));
     }  if (!strlen(hid->name))
      snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
        le16_to_cpu(dev->descriptor.idVendor),
        le16_to_cpu(dev->descriptor.idProduct));  hid->bus = BUS_USB;
     hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
     hid->product = le16_to_cpu(dev->descriptor.idProduct);  usb_make_path(dev, hid->phys, sizeof(hid->phys));
     strlcat(hid->phys, "/input", sizeof(hid->phys));
     len = strlen(hid->phys);
     if (len < sizeof(hid->phys) - 1)
      snprintf(hid->phys + len, sizeof(hid->phys) - len,
        "%d", intf->altsetting[0].desc.bInterfaceNumber);  if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
      hid->uniq[0] = 0;  usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
     if (!usbhid->urbctrl)
      goto fail;  usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
            usbhid->ctrlbuf, 1, hid_ctrl, hid);
     usbhid->urbctrl->setup_dma = usbhid->cr_dma;
     usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
     usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
     hid->hidinput_input_event = usb_hidinput_input_event;
     hid->hid_open = usbhid_open;
     hid->hid_close = usbhid_close;
    #ifdef CONFIG_USB_HIDDEV
     hid->hiddev_hid_event = hiddev_hid_event;
     hid->hiddev_report_event = hiddev_report_event;
    #endif
     return hid; fail:
     usb_free_urb(usbhid->urbin);
     usb_free_urb(usbhid->urbout);
     usb_free_urb(usbhid->urbctrl);
     hid_free_buffers(dev, hid);
     kfree(usbhid);
    fail_no_usbhid:
     hid_free_device(hid);  return NULL;
    }
      1.1.1: usbhid_lookup_quirk(pid,vid)解析: 分别使用usbhid_exists_dquirk(pid,vid),usbhid_exists_squirk(pid,vid)来查看动态和静态的quirks-list.如果有,则返回此quirks.没有,则返回0。   1.1.2:usb_get_extra_descriptor(descriptor, type, &hdesc)解析: 它调用__usb_get_extra_descriptor()来解析参数一(interface或endpoint 描述符)中的扩展描述符区--extra。看是否有类型为参数二(type)的条目,然后把地址交给参数三。 Sam未经证明的猜测:HID设备中,interface描述符中包含的这个扩展描述符。其中存放的都是HID信息,以hid_descriptor结构存放。 对于HID设备来说,在interface description之后会附加一个hid description, hid description中的最后部份包含有Report description或者Physical Descriptors的长度. hid_descriptor->usb_descriptor_header->bDescriptorType为HID_DT_HID 后面描述符(包括它自己)hid_descriptor->hid_class_descriptor->bDescriptorType 为HID_DT_REPORT   1.1.3:hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle): 它简单的调用usb_control_msg() 发送或接收一个usb 控制消息。 具体到这个例子中: usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
      HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report,
      ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); usb_sndctrlpipe(dev, 0):指出将dev中endpoint 0 设为发送control管道。 HID_REQ_SET_IDLE:此控制消息的Request。 USB_TYPE_CLASS | USB_RECIP_INTERFACE:请求类型. 调用成功才会返回。   1.1.4: hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize) 也是通过调用usb_control_msg() 发送或接收一个usb 控制消息。 发送USB_REQ_GET_DESCRIPTOR,将得到的 report descriptor放到rdesc中。   1.1.5: hid_parse_report(__u8 *start, unsigned size) 参数一:通过usb_control_msg发送request(USB_REQ_GET_DESCRIPTOR) 从设备得到的report descriptor. 参数二:是此report Descriptor的长度。 1).首先创建hid_device类型设备。 2). 将参数一中数据保存在hid_device->rdesc中。长度保存在hid_device->rsize中。 3). 使用fetch_item(__u8 *start, __u8 *end, struct hid_item *item)得到report description的下一项数据。放到item中。 4). 根据不同的item.type.分别调用 hid_parser_main,
      hid_parser_global,
      hid_parser_local,
      hid_parser_reserved 来处理。 读USB HID driver时,看到quirks这部分内容。因为之前在看USB部分代码时,常看到类似的内容,但对它的语义理解并不清楚,只是笼统地认为是一个需要修正的东西。现在稍微研究一下。   一:quirks简介: quirks: 怪癖的意思,也就是说它某种特性与通常的USB设备不相同。     Sam之前是在USB部分看到quirks: 在~/drivers/usb/core/quirks.c中,有个usb_quirk_list列表,它其实就是一个黑名单,描述了某个设备有何种问题。例如:
     { USB_DEVICE(0x03f0, 0x4002), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, 表明:vid=0x03f0,pid=0x4002的设备(Hewlett-Packard PhotoSmart 720 ),不能自动suspend. 这个列表是不断扩展的,不断添加有问题的设备上来。 则判断一个设备是不是在这张黑名单上,然后如果是的,就判断它具体是属于哪种问题,     二:HID quirks: Sam在看USB HID driver时,在modules insmod时,会调用hid_init()。它又会调用usbhid_quirks_init() 如果在insmod modules时,使用modules参数添加了quirks,格式为:quirks=pid:vid:quirks 则将此项内容添加或替换在动态建立的dquirks_list中。换句话说,也就是当modules的提供商知道自己的设备会有什么异常时,可以这样使用。 注意:此处仅仅是将某种PID,VID的quirks添加进动态列表而已。   有动态黑名单,就有静态黑名单。静态黑名单在~/drivers/hid/usbhid/hid-quirks.c中--hid_blacklist。它描绘了已知所有的quirks.     三:如何使用HID quirks: 在hid 设备probe时,会调用usbhid_lookup_quirk(),它则分别调用usbhid_exists_dquirk(动态)——和usbhid_exists_squirk(静态) 在静态黑名单和动态黑名单中寻找有没有对应的vid和pid设备。如果有,则将quirks纪录在hid->quirks中。                

    转自:点击查看

    展开全文
  • s3c2416usb device驱动

    2014-05-19 22:22:31
    完整的USB device驱动,裸机和程序,对驱动分析很有帮助
  • USB_设备 裸机本机USB驱动程序实现
  • 人家给了Bulk 方式传输的驱动,改一下描述符做成interrupt 方式的 HID驱动?下周三 任务期限就到啦
  • 软件分享 usb device驱动 软件名称:usb device驱动程序 软件大小:1012KB 软件语言:简体中文 授权方式:免费版 软件类型:国产软件 - 驱动硬件-系统工具 运行环境:Win9X/2000/XP/2003/win7/vista ...
  • 支持linux 2.6.22-24内核移植的S3C2410 USB device驱动程序! linux 内核支持USB HOST但是,不支持 USB device驱动
  • STM32 USB Device驱动库和例程
  • 前面分析了usb hub 和 generic driver,顺便将枚举的前一部分看完了。...但在分析usb-skeleton之前,我们注意到最上层的驱动都是针对usb interface的,usb driver里面的函数都用usb interface做参数,usb dri
  • USB 2.0 to COM Device 串口线 驱动程序

    千次下载 热门讨论 2012-03-29 16:28:48
    USB 2.0 to COM DeviceUSB转串口线驱动程序
  • usb device联想p70手机驱动手机刷机必备
  • 更新 Apple Mobile Device USB 驱动程序 对于 Windows XP: 右键点按“设备管理器”中的 Apple Mobile Device 条目,然后从快捷菜单中选取更新驱动程序。 如果系统询问是否连接到 Windows Update 以搜索软件,...
  • 最新 wd ses device usb device

    热门讨论 2012-01-13 22:44:06
    最新 wd ses device usb device 西部数据移动硬盘 驱动下载。 支持WIN2003、XP等系统。
  • 软件名称:usbdevice驱动程序 软件大小:1012KB 软件语言:简体中文 授权方式:免费版 软件类型:国产软件-驱动硬件-系统工具 运行环境:Win9X/2000/XP/2003/win7/vista 软件介绍:很多朋友电脑的总线控制器内...
  • 这个是苹果5在recovery模式下的Apple mobile device usb驱动程序,为2014年8月份新版本,安装这个驱动后可以让电脑正常识别你的手机,如果你之前安装ITUNES后也无法识别时,可以试试这个驱动。
  • 自己编写的USB驱动,插入USB设备,出现: [root@Nision=W]#usb 1-1: new full speed USB device usings3c2410-ohci and address 2 usb 1-1: device descriptor read/64, error -62 usb 1-1: device descriptor read/...
  • 先谈谈如何写linux驱动: - 在驱动模块初始化函数中调用register_chrdev(),将驱动向系统注册为一个字符设备,伪装成一个文件,上层的应用可以通过访问这个文件(字符设备),来操作驱动模块。 - 驱动模块注册为...
  • unknown device驱动下载_手机usb2.0万能驱动程序
  • USB是目前看到最复杂的驱动,内容很多,而且网上分析USB驱动的资料很少,因此我在学习的过程中走了很多弯路。在这里记录自己的学习过程,希望可以帮到其他研究USB驱动的同事。先推荐一本书:Bootstrap Yourself With...
  • USB是目前看到最复杂的驱动,内容很多,但网上分析USB驱动的资料很少,因此我在学习的过程中走了很多弯路。在这里记录自己的学习过程,希望可以帮到其他研究USB驱动的同事。先推荐一本书:Bootstrap Yourself With.....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,212
精华内容 884
关键字:

deviceusb驱动