精华内容
下载资源
问答
  • 英特尔(R) USB 3.0 可扩展主机控制器.zip
  • 英特尔USB3.0可扩展主机控制器驱动程序是英特尔USB3.0额驱动程序,包含Intelusb3.0可扩展控制器驱动、集线器驱动、监视器驱动等等,本驱动是Intel官方推出的芯片组主板USB3.0驱动,其他厂商采用7系列芯片组原生USB...
  • 英特尔(R) USB 3.0 可扩展主机控制器_1.0.9.254_2014-07-14 23 39 17
  • Microchip(美国微芯科技公司)宣布推出三款UCS100X系列产品,扩展了其编程USB端口电源控制器组合。这些全新的电源控制器为设计笔记本电脑、平板电脑、显示器、扩展底座和打印机等主机设备,以及墙壁适配器等专用...
  • 整合单片机、混合信号、模拟器件和闪存专利解决方案的供应商--Microchip Technology Inc.(美国微芯科技公司)宣布,推出三款UCS100X系列产品,扩展了其编程USB端口电源控制器组合。这些全新的电源控制器为设计...
  • 的整合单片机、混合信号、模拟器件和闪存解决方案的供应商——Microchip Technology Inc.(美国微芯科技公司)日前宣布扩展旗下编程USB端口电源控制器产品组合,推出新款双通道UCS2112器件。这一新款端口电源控制器...
  • USB接口的热插拔,即插即用,数据传输可靠,扩展方便,成本低等优点使其逐渐成为嵌入式系统的必备接口之一。目前多数嵌入式系统仅支持基础的USB低速或全速外设,不能满足人们对高速数据传输的要求。为此,基于AT91RM...
  • 全球领先的整合单片机、混合信号、模拟器件和闪存专利解决方案的供应商——Microchip Technology Inc.(美国微芯科技公司)日前宣布扩展旗下编程USB端口电源控制器产品组合,推出新款双通道UCS2112器件。这一新款...
  • 在嵌入式系统的发展历程中,Linux操作系统的源码公开,结构清晰,功能强大,移植性强等特点使其在嵌入式领域应用越来越...为此,基于AT91RM9200平台完成了高速USB的硬件设计和Linux操作系统下主机端驱动程序的开发。
  • USB接口的热插拔,即插即用,数据传输可靠,扩展方便,成本低等优点使其逐渐成为嵌入式系统的必备接口之一。目前多数嵌入式系统仅支持基础的USB低速或全速外设,不能满足人们对高速数据传输的要求。为此,基于AT91RM...
  • 明导资讯(Mentor Graphics)公司分公司Accelerated Technology推出Nucleus USB调制解调通信驱动扩展了该公司Nucleus USB... Nucleus USB调制解调通讯驱动器可实现ACM,与Nucleus USB主机和功能堆栈一
  • 英特尔® USB 3.0 可扩展主机控制器驱动程序适用于英特尔®服务器主板
  • USB 3.0 可扩展主机控制器驱动程序。 英特尔? USB 3.0 可扩展主机控制器驱动程序包含以下 Intel? 芯片组和 Intel? 处理器支持 ︰ ·Intel? 7 系列 Chipsets/Intel? C216 芯片组 ·第三代 Intel? Core? 处理器家族 ...
  • 软件介绍: ...此安装程序将安装以下组件:英特尔® USB 3.0 可扩展主机控制器驱动程序英特尔® USB 3.0 集线器驱动程序英特尔® USB 3.0 主机控制器切换驱动程序英特尔® USB 3.0 监视器
  • 同事要拷贝资料给我,问我电脑的USB接口是2.0还是3.0。之前不知道,在网上查了一下方法。 打开电脑的设备管理器 --》通用串行总线控制器。...出现了USB 3.0可扩展主机控制器,说明电脑有3.0的接口~ ...

     同事要拷贝资料给我,问我电脑的USB接口是2.0还是3.0。之前不知道,在网上查了一下方法。

    打开电脑的设备管理器 --》通用串行总线控制器。

    出现了USB 3.0可扩展主机控制器,说明电脑有3.0的接口~

     

    展开全文
  • Linux USB驱动-鼠标驱动

    2021-03-18 21:28:36
    主机侧的USB节点为根节点,所有子节点都连接在根节点集线器(ROOT HUB)上,根节点由USB主机控制器USB Host Controller)控制,设备侧的节点为子节点,由USB设备控制器(USB Device Controller)控制。在US

    1.概述

    通用串行总线(USB)用于连接主机和外围设备。USB总线采用拓扑结构,USB主机和USB设备的连接构成了一颗树,树的结点为USB节点或USB集线器(HUB),USB集线器(HUB)用于扩展设备接口,一个集线器(HUB)可接多个USB设备或多个集线器。主机侧的USB节点为根节点,所有子节点都连接在根节点集线器(ROOT HUB)上,根节点由USB主机控制器(USB Host Controller)控制,设备侧的节点为子节点,由USB设备控制器(USB Device Controller)控制。在USB总线中,只能有一个USB主机控制器(根节点),可以有多个USB设备控制器(子节点)。USB主机负责协调主机和设备之间的通讯,USB设备不能主动向主机发送任何数据。

    usb拓扑结构

    2.USB规范

    USB1.1协议包括OHCI(Open Host Controller Interface Specification)和UHCI(Universal Host Controller Interface Specification)规范。UHCI对硬件的要求较低,但驱动程序开发复杂,CPU处理负担较高,OHCI则使用硬件实现了较多的功能,对软件的要求降低,减轻了CPU的处理负担。USB2.0增加了EHCI(Enhanced Host Controller Interface),为USB 2.0主机高速数据传输控制器的软硬件设计提供了统一的接口标准,大大简化了USB 2.0的主机设计,提高了软件的可移植性。EHCI本身并不支持全速和低速设备,为了兼容USB 1.1,USB 2.0的主机控制器由EHCI和CHC(Companion Host Controller)两部分组成,CHC由OHCI和UHCI组成。xHCI是由Intel公司开发的可扩展主机控制器接口,主要面向USB3.0,同时也支持USB 2.0及以下设备。

    3.USB驱动基础

    USB设备包括设备、配置、接口和端点这四个层次,如下图所示。设备中包含若干个配置,配置中包含若干个接口,接口中包含若干个端点。

    usb层次结构
    USB设备逻辑层次结构.

    3.1.端点

    端点是USB最基本的通信形式,只能往一个方向传输数据,从主机到设备(输出设备)或者从设备到主机(称为输入端点),端点可以看作是单向的管道。每个端点都有唯一的地址和对应的属性,地址由设备地址和端点号给出,属性包括传输方向、总线访问频率、带宽、端点号和数据包的最大容量等。端点0通常为控制端点,用于设备的初始化。USB端点有四种不同的类型,分别具有不同的数据传递方式。
    (1)控制
    控制端点用于配置设备、获取设备信息、发送命令到设备、获取设备的状态。每个USB设备都有端点0的控制端点,当USB设备插入到USB主机拓扑网络中时,USB主机就通过端点0与USB设备通信,对USB设备进行配置,便于后续的数据传输。USB协议保证控制端点有足够的带宽。控制端点的数据传输方式为控制传输,控制传输可靠,时间有保证,但传输的数据量不大。如USB设备的识别过程就采用的时控制传输。
    (2)中断
    当USB主机请求USB设备传输数据时,中断端点以一个固定的速率传送少量的数据。中断端点的数据传输方式为中断传输,数据传输可靠,实时性高,这里的中断并不是USB设备产生中断,而是USB主机每隔一个固定的时间主动查询USB设备是否有数据要传输,以轮询的方式提高实时性。如USB鼠标采用的是中断传输。
    (3)批量
    批量端点用于传输大量数据,这些端点一次可以保存更多的数据。USB协议不保证这些数据传输可以在特定的时间内完成,但保证数据的准确性。如果总线上的空间不足以发送整个批量包,则将数据拆分为多个包传输。批量传输数据可靠,但实时性较低。如USB硬盘、打印机等设备就采用的是批量传输方式。
    (4)等时
    等时端点也可以传输大量数据,但数据的可靠性无法保证。采用等时传输的USB设备更加注重保持一个恒定的数据传输速度,对数据的可靠性要求不高。如USB摄像头就使用的是等时传输方式。
    Linux内核使用struct usb_endpoint_descriptor结构体描述端点。

        [include/uapi/linux/usb/ch9.h]
        struct usb_endpoint_descriptor {
            __u8  bLength;  // 端点描述符长度
            __u8  bDescriptorType;  // 端点描述符类型 
            // 端点地址,低四位是端点号,最高位表示数据传输方向,0为输出,1为输入
            __u8  bEndpointAddress;  
            __u8  bmAttributes;  // 端点类型,为0表示控制,1表示等时,2表示批量,3表示中断
            __le16 wMaxPacketSize;  // 本端点接收或发送数据包的最大字节数
            // 中断传输轮询周期,批量传输忽略,等时传输为1,中断传输范围为1-255
            __u8  bInterval;  
            __u8  bRefresh;
            __u8  bSynchAddress;
        // __attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,
        // 按照实际占用字节数进行对齐,是GCC特有的语法
        } __attribute__ ((packed));
    

    Linux内核使用struct usb_host_endpoint描述主机侧的端点。

        [include/linux/usb.h]
        struct usb_host_endpoint {
            struct usb_endpoint_descriptor desc;  // 端点描述符
            struct usb_ss_ep_comp_descriptor ss_ep_comp;
            struct list_head		urb_list;  // USB请求块链表节点,由USB核心层管理
            void				*hcpriv;  // 主机控制器使用,通常用于硬件DMA队列头
            struct ep_device		*ep_dev;	/* For sysfs info */
            unsigned char *extra;   /* Extra descriptors */
            int extralen;  // extra的字节数
            int enabled;  // URBs may be submitted to this endpoint
            int streams;  // number of USB-3 streams allocated on the endpoint
        };
    

    3.2.接口

    USB接口代表了一个USB设备的基本功能,USB接口由多个USB端口组成。USB接口只处理一种USB逻辑连接,例如鼠标、键盘或者音频流。一些USB设备具有多个接口,例如USB扬声器可以包括两个接口:USB按键和USB音频流。
    Linux内核使用struct usb_interface_descriptor描述端点。

        [include/uapi/linux/usb/ch9.h]
        struct usb_interface_descriptor {
            __u8  bLength;  // 描述符长度
            __u8  bDescriptorType;  // 描述符类型
            __u8  bInterfaceNumber;  // 接口的编号
            __u8  bAlternateSetting;  // 备用的接口描述符编号
            __u8  bNumEndpoints;  // 接口使用的端点数量,不包括端点0
            __u8  bInterfaceClass;  // 接口类型
            __u8  bInterfaceSubClass;  // 接口子类型
            __u8  bInterfaceProtocol;  // 接口所遵循的协议
            __u8  iInterface;  // 描述该接口的字符串索引值
        } __attribute__ ((packed));
    

    Linux内核使用struct usb_host_interface描述主机侧的接口。

        [include/linux/usb.h]
        struct usb_host_interface {
            struct usb_interface_descriptor	desc;  // 接口描述符
            int extralen;  // extra的字节数
            unsigned char *extra;   /* Extra descriptors */
            struct usb_host_endpoint *endpoint;  // 主机侧的端点
            char *string;		/* 接口字符串 */
        };
    

    3.3.配置

    USB配置由一个或多个USB接口组成,每个配置具有一个或多个基本功能。Linux使用struct usb_config_descriptor描述配置。

        [include/uapi/linux/usb/ch9.h]
        struct usb_config_descriptor {
            __u8  bLength;  // 描述符长度
            __u8  bDescriptorType;  // 描述符类型编号
            __le16 wTotalLength;  // 配置返回的数据长度
            __u8  bNumInterfaces;  // 配置所支持的接口数量
            __u8  bConfigurationValue;  // Set_Configuration命令所需的参数值
            __u8  iConfiguration;  // 描述该配置字符串的索引值
            __u8  bmAttributes;  // 供电模式选择
            __u8  bMaxPower;  // 设备从总线提取的最大电流
        } __attribute__ ((packed));
    

    Linux内核使用struct usb_host_config描述主机侧的配置。

        [include/linux/usb.h]
        #define USB_MAXINTERFACES	32
        #define USB_MAXIADS		(USB_MAXINTERFACES/2)
        struct usb_host_config {
            struct usb_config_descriptor	desc;  // 配置描述符
            char *string;		// 配置字符串
            // 配置的接口中关联的描述符
            struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
            /* 配置关联的接口 */
            struct usb_interface *interface[USB_MAXINTERFACES];
            // 接口的可用信息
            struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
            unsigned char *extra;   /* Extra descriptors */
            int extralen;  // extra字节数
        };
    

    3.4.设备

    USB设备有一个或多个配置组成。USB设备可以在这些配置之间切换,以改变设备的状态。Linux使用struct usb_device_descriptor描述设备。

        [include/uapi/linux/usb/ch9.h]
        struct usb_device_descriptor {
            __u8  bLength;  // 描述符长度
            __u8  bDescriptorType;  // 描述符类型编号
            __le16 bcdUSB;  // USB版本号
            __u8  bDeviceClass;  // USB分配的设备类code
            __u8  bDeviceSubClass;  // USB分配的子类code
            __u8  bDeviceProtocol;  // USB分配的协议code
            __u8  bMaxPacketSize0;  // 端点0最大包大小
            __le16 idVendor;  // 厂商编号
            __le16 idProduct;  // 产品编号
            __le16 bcdDevice;  // 设备出场编号
            __u8  iManufacturer;  // 描述厂商字符串的索引
            __u8  iProduct;  // 描述产品字符串的索引
            __u8  iSerialNumber;  // 描述设备序列号字符串的索引
            __u8  bNumConfigurations;  // 可能的配置数量
        } __attribute__ ((packed));
    

    4.Linux USB驱动程序

    在Linux系统中,USB驱动可以从两个角度观察,一个角度是主机侧,另一个角度是设备侧。主机测,处于USB驱动底层的是USB主机控制器硬件,在其上运行的是USB主机控制器驱动,再往上是USB核心层,最上层是USB设备驱动层(插入主机上的U盘、鼠标、键盘等设备驱动)。因此在主机侧,需要实现USB主机控制器驱动和USB设备驱动,前者用于控制USB主机控制器和插入USB总线的USB设备之间的通信,后者描述主机应该怎么和插入的USB设备通信。USB核心完成驱动管理和协议处理的主要工作,向上为主机侧USB设备驱动提供统一的编程接口,向下为USB主机控制器驱动提供统一的编程接口。设备侧驱动程序分为UDC驱动程序、Gadget Function API和Gadget Function驱动程序。UDC驱动程序直接访问硬件,控制USB设备控制器与USB主机控制器通信,向上提供与硬件交互的接口。Gadget Function API封装了UDC驱动程序向上提供的接口。Gadget Function驱动程序实现具体的USB设备功能。Linux内核支持的USB设备类包括USB打印设备、通信类设备、HID设备类、存储设备类、语音设备类等。驱动工程师一般需要实现USB设备驱动,USB主机控制器驱动通常由芯片厂家实现。

    usb_driver_framework

    4.1.USB设备驱动

    Linux USB核心层使用struct usb_driver结构体表示一个USB设备驱动,编写USB设备驱动时,主要实现probedisconnect函数,分别用于初始化和释放软硬件资源,设备和驱动匹配成功后,probe函数被调用,设备断开时disconnect函数被调用。使用宏usb_register注册USB设备驱动程序,使用函数usb_deregister注销USB设备驱动程序,还可以使用宏module_usb_driver注册USB驱动程序,其同时完成注册、注销、module_initmodule_exit功能。在注册USB驱动程序时,还需要定义MODULE_DEVICE_TABLE宏,将设备驱动匹配表导出到用户空间,常用于设备热插拔时进行设备识别。USB协议支持设备的热拔插。
    这里需要注意的时USB只是一种总线,而连接到总线上的USB设备可以是字符设备、tty设备、块设备、输入设备等。usb_driver结构体处理了设备和USB总线相关的工作,至于设备的功能,需要根据具体的设备类型来编写具体的设备驱动。因此USB设备驱动包含了其作为总线上挂接设备的驱动和所属设备类型的驱动。这和platform_driveri2c_driver等类似,usb_driver起到桥梁的作用,即在xxx_driver结构体的probe函数中注册具体的设备,如注册字符、tty等设备,在disconnect函数中注销字符、tty设备。

        [include/linux/usb.h]
        struct usb_driver {
            const char *name;  // 驱动名称
            // probe函数
            int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);
            void (*disconnect) (struct usb_interface *intf);
            int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
                    void *buf);
            int (*suspend) (struct usb_interface *intf, pm_message_t message);
            int (*resume) (struct usb_interface *intf);
            int (*reset_resume)(struct usb_interface *intf);
            int (*pre_reset)(struct usb_interface *intf);
            int (*post_reset)(struct usb_interface *intf);
            const struct usb_device_id *id_table;  // 描述了USB驱动支持的USB设备列表
            struct usb_dynids dynids;
            struct usbdrv_wrap drvwrap;
            unsigned int no_dynamic_id:1;
            unsigned int supports_autosuspend:1;
            unsigned int disable_hub_initiated_lpm:1;
            unsigned int soft_unbind:1;
        };
        // 注册USB设备驱动程序,driver为struct usb_driver结构体指针
        #define usb_register(driver) usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
        #define module_usb_driver(__usb_driver) module_driver(__usb_driver, 
                        usb_register, usb_deregister)
        // 注销USB设备驱动程序
        void usb_deregister(struct usb_driver *driver)
        [include/linux/device.h]
        // 简介的注册宏定义,同时完成初始化和卸载功能
        #define module_driver(__driver, __register, __unregister, ...) \
        static int __init __driver##_init(void) \
        { \
            return __register(&(__driver) , ##__VA_ARGS__); \
        } \
        module_init(__driver##_init); \
        static void __exit __driver##_exit(void) \
        { \
            __unregister(&(__driver) , ##__VA_ARGS__); \
        } \
        module_exit(__driver##_exit);
    

    id_table描述了USB驱动支持的USB设备列表,其指向了struct usb_device_id类型的数组。struct usb_device_id包含了USB设备的制造商ID、产品ID、产品版本、设备类、接口类等信息及其要匹配标志成员match_flags(表明要与那些成员匹配)。match_flags的取值从下面的宏定义中选取。

        [include/linux/mod_devicetable.h]
        #define USB_DEVICE_ID_MATCH_VENDOR		0x0001  // 按供应商ID匹配
        #define USB_DEVICE_ID_MATCH_PRODUCT		0x0002  // 按产品ID匹配
        #define USB_DEVICE_ID_MATCH_DEV_LO		0x0004
        #define USB_DEVICE_ID_MATCH_DEV_HI		0x0008
        #define USB_DEVICE_ID_MATCH_DEV_CLASS		0x0010 // 按设备类型匹配
        #define USB_DEVICE_ID_MATCH_DEV_SUBCLASS	0x0020 // 按设备子类型匹配
        #define USB_DEVICE_ID_MATCH_DEV_PROTOCOL	0x0040 // 按设备协议匹配
        #define USB_DEVICE_ID_MATCH_INT_CLASS		0x0080 // 按接口类型匹配
        #define USB_DEVICE_ID_MATCH_INT_SUBCLASS	0x0100 // 按接口子类型匹配
        #define USB_DEVICE_ID_MATCH_INT_PROTOCOL	0x0200 // 按接口协议匹配
        #define USB_DEVICE_ID_MATCH_INT_NUMBER		0x0400
        struct usb_device_id {
            __u16		match_flags;  // 匹配方式
            __u16		idVendor;  // 供应商ID,由USB协会分配
            __u16		idProduct;  // 产品ID,供应商自己分配
            __u16		bcdDevice_lo;  // 产品版本号的最小值
            __u16		bcdDevice_hi;  // 产品版本号的最大值
            __u8		bDeviceClass;  // 设备类型
            __u8		bDeviceSubClass;  // 设备子类型
            __u8		bDeviceProtocol;  // 设备协议
            __u8		bInterfaceClass;  // 接口类
            __u8		bInterfaceSubClass;  // 接口子类
            __u8		bInterfaceProtocol;  // 接口协议
    
            __u8		bInterfaceNumber;
            /* not matched against */
            kernel_ulong_t	driver_info
                __attribute__((aligned(sizeof(kernel_ulong_t))));
        };
    

    也可使用下面的宏定义来初始化usb_device_idUSB_DEVICE宏根据制造商ID和产品ID生成一个usb_device_id结构体实列,在数组中增加该元素将意味着该驱动可支持与制造商ID、产品ID相匹配的设备。USB_DEVICE_VER宏根据制造商ID、产品ID、产品版本号范围(在最大值与最小值之间)生成一个usb_device_id结构体的实列,在数组中增加该元素将意味着该驱动可支持与制造商ID、产品ID、lo~hi产品版本号范围内的产品匹配。USB_DEVICE_INFO宏用于创建一个匹配指定设备类型的usb_device_id结构体实列。USB_INTERFACE_INFO宏创建一个匹配指定接口类型的usb_device_id结构体实列。创建完usb_device_id结构体实列后,还需要使用MODULE_DEVICE_TABLE宏将usb_device_id导出到用户空间。

        [include/linux/usb.h]
        // 匹配方式的组合,用于设置struct usb_device_id的match_flags标志
        #define USB_DEVICE_ID_MATCH_DEVICE \
                (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)
        #define USB_DEVICE_ID_MATCH_DEV_RANGE \
                (USB_DEVICE_ID_MATCH_DEV_LO | USB_DEVICE_ID_MATCH_DEV_HI)
        #define USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION \
                (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_RANGE)
        #define USB_DEVICE_ID_MATCH_DEV_INFO \
                (USB_DEVICE_ID_MATCH_DEV_CLASS | \
                USB_DEVICE_ID_MATCH_DEV_SUBCLASS | \
                USB_DEVICE_ID_MATCH_DEV_PROTOCOL)
        #define USB_DEVICE_ID_MATCH_INT_INFO \
                (USB_DEVICE_ID_MATCH_INT_CLASS | \
                USB_DEVICE_ID_MATCH_INT_SUBCLASS | \
                USB_DEVICE_ID_MATCH_INT_PROTOCOL)
    
        #define USB_DEVICE(vend, prod) \
            .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
            .idVendor = (vend), \  // 制造商ID
            .idProduct = (prod)    // 产品ID
        #define USB_DEVICE_VER(vend, prod, lo, hi) \
            .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, \
            .idVendor = (vend), \     // 制造商ID
            .idProduct = (prod), \    // 产品ID
            .bcdDevice_lo = (lo), \   // 产品版本号范围的最小值
            .bcdDevice_hi = (hi)      // 产品版本号范围的最大值
        #define USB_DEVICE_INFO(cl, sc, pr) \
            .match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, \
            .bDeviceClass = (cl), \     // 设备类型 
            .bDeviceSubClass = (sc), \  // 设备子类
            .bDeviceProtocol = (pr)     // 设备协议
        #define USB_INTERFACE_INFO(cl, sc, pr) \
            .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \
            .bInterfaceClass = (cl), \     // 接口类型
            .bInterfaceSubClass = (sc), \  // 接口子类
            .bInterfaceProtocol = (pr)     // 接口协议
        ......
    

    下面是定义USB设备属性的例子,当USB核心检测到某个插入设备的属性和某个驱动程序的属性一致时,这个驱动程序的probe()函数就被调用,当拔掉设备或者卸载驱动后,USB核心层就执行disconnect()函数,将设备和驱动断开。

        // 先定义一个usb_device_id数组,使用USB_DEVICE初始化内部的元素,然后使用
        // MODULE_DEVICE_TABLE将usb_device_id导出到用户空间,便于热插拔时进行设备识别
        static struct usb_device_id id_table[] = {
            {USB_DEVICE(VEND_TD, PRODUCT_ID)},
            { },  // 最后一个元素必须是空
        }
        MODULE_DEVICE_TABLE(usb, id_table);
    

    4.2.USB设备

    USB核心层使用struct usb_device结构来表示一个USB设备。当USB控制器检测到设备连接时,就会分配一个struct usb_device结构体,然后注册到总线设备列表中,然后匹配对应的驱动。

        [include/linux/usb.h]
        struct usb_device {
            int		devnum;  // 该设备在总线上的序号
            char		devpath[16];  // USB拓扑路径
            u32		route;
            enum usb_device_state	state;  // 状态
            enum usb_device_speed	speed;  // 速度
            struct usb_tt	*tt;  // 事务转换(如高速接口兼容低速设备)
            int		ttport;;
            struct usb_device *parent;
            struct usb_bus *bus;
            struct usb_host_endpoint ep0;  // 端点0
            struct device dev;
            struct usb_device_descriptor descriptor;  // 设备描述符
            struct usb_host_bos *bos;
            struct usb_host_config *config;
            struct usb_host_config *actconfig;
            struct usb_host_endpoint *ep_in[16];  // 输入端口
            struct usb_host_endpoint *ep_out[16];  // 输出端口
            char **rawdescriptors;  // GET_DESCRIPTOR命令返回的描述符原始字符串
            unsigned short bus_mA;  // 总线电流
            u8 portnum;  // HUB端口号
            u8 level;  // USB设备树层级
            ......
        };
    

    4.3.USB主机控制器驱动

    在Linux内核中,使用struct usb_hcd描述USB主机控制区驱动,包含了主机控制器的‘家务’信息、硬件资源、状态描述和用于控制主机控制器的hc_driver等。struct usb_hcdhc_driver成员非常重要,包含了操作主机控制器的所有方法。

        [include/linux/usb/hcd.h]
        struct usb_hcd {
            struct usb_bus		self;		/* hcd is-a bus */
            struct kref		kref;		/* reference counter */
            const char		*product_desc;	/* 厂商字符串 */
            int			speed;		/* 主机控制器根HUB的速度
            char			irq_descr[24];	/* driver + bus # */
            struct timer_list	rh_timer;	/* drives root-hub polling */
            struct urb		*status_urb;	/* the current status urb */
            // USB主机控制器操作函数集合
            const struct hc_driver	*driver;	/* hw-specific hooks */
            // OTG和某些USB控制器需要和PHY交互
            struct usb_phy		*usb_phy;
            struct phy		*phy;
            unsigned long		flags;
            unsigned int		irq;		/* irq allocated */
            void __iomem		*regs;		/* device memory/io */
            resource_size_t		rsrc_start;	/* memory/io resource start */
            resource_size_t		rsrc_len;	/* memory/io resource length */
            unsigned		power_budget;	/* in mA, 0 = no limit */
            struct giveback_urb_bh  high_prio_bh;
            struct giveback_urb_bh  low_prio_bh;
            struct mutex		*bandwidth_mutex;
            struct usb_hcd		*shared_hcd;
            struct usb_hcd		*primary_hcd;
            #define HCD_BUFFER_POOLS	4
            struct dma_pool		*pool[HCD_BUFFER_POOLS];
            int			state;
            unsigned long hcd_priv[0]  // 私有数据
                    __attribute__ ((aligned(sizeof(s64))));
        };
    

    urb_enqueue函数非常关键,上层通过usb_submit_urb提交一个USB请求后,该函数内部通过调用usb_hcd_submit_urb函数,最终调用到urb_enqueue

        [include/linux/usb/hcd.h]
        struct hc_driver {
            const char	*description;	/* "ehci-hcd" etc */
            const char	*product_desc;	/* product/vendor string */
            size_t		hcd_priv_size;	/* size of private data */
            /* irq handler */
            irqreturn_t	(*irq) (struct usb_hcd *hcd);
            int	flags;
            /* called to init HCD and root hub */
            int	(*reset) (struct usb_hcd *hcd);
            int	(*start) (struct usb_hcd *hcd);
            int	(*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup);
            int	(*pci_resume)(struct usb_hcd *hcd, bool hibernated);
            void	(*stop) (struct usb_hcd *hcd);
            /* shutdown HCD */
            void(*shutdown) (struct usb_hcd *hcd);
            /* return current frame number */
            int	(*get_frame_number) (struct usb_hcd *hcd);
            /* manage i/o requests, device state */
            int	(*urb_enqueue)(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
            int	(*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb, int status);
            int	(*map_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb,gfp_t mem_flags);
            void(*unmap_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb);
            void(*endpoint_disable)(struct usb_hcd *hcd,struct usb_host_endpoint *ep);
            void(*endpoint_reset)(struct usb_hcd *hcd,struct usb_host_endpoint *ep);
            int	(*hub_status_data) (struct usb_hcd *hcd, char *buf);
            int	(*hub_control) (struct usb_hcd *hcd,u16 typeReq, u16 wValue, u16 wIndex,
                        char *buf, u16 wLength);
            int	(*bus_suspend)(struct usb_hcd *);
            int	(*bus_resume)(struct usb_hcd *);
            ......
        };
    

    在Linux内核中,使用usb_create_hcd函数创建主机控制器,使用usb_add_hcdusb_remove_hcd函数注册、注销主机控制器。

        [include/linux/usb/hcd.h]
        // 创建主机控制器struct usb_hcd结构体并初始化。driver为struct hc_driver结构体指针,dev为主机
        // 控制器的设备结构体,保存在hcd->self.controller中。bus_name为总线名称,保存在hcd->self.bus_name。
        struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
                struct device *dev, const char *bus_name)
        // 初始化并注册主机控制器struct usb_hcd结构体。hcd为struct usb_hcd结构体指针。irqnum为中断号。
        // irqflags为中断标志
        int usb_add_hcd(struct usb_hcd *hcd,unsigned int irqnum, unsigned long irqflags)
        // 注销主机控制器struct usb_hcd结构体。hcd为struct usb_hcd结构体指针。
        void usb_remove_hcd(struct usb_hcd *hcd)
    

    4.4.USB请求块URB

    USB请求块(USB Request Block,URB)是USB设备驱动中用来描述与USB设备通信的基本载体和核心数据结构,使用urb结构体描述,类似与网络设备驱动中的sk_buff结构体。

    4.4.1.URB数据结构

    struct urb结构体由两部分组成,第一部分是私有的,只能被usb核心层或主机控制器使用,第二部分是公有的,可被usb设备驱动程序使用。

        [include/linux/usb.h]
        struct urb {
            // 以下私有:usb核心层或主机控制器用
            struct kref kref;		/* URB引用计数 */
            void *hcpriv;			/* 主机控制器私有数据 */
            atomic_t use_count;		/* 并发提交个数 */
            atomic_t reject;		/* 提交失败的数量 */
            int unlinked;			/* unlink error code */
            // 以下公用:可被驱动使用的成员
            struct list_head urb_list;	    /* urb组成的链表 */
            struct list_head anchor_list;	/* the URB may be anchored */
            struct usb_anchor *anchor;
            struct usb_device *dev;		    /* 关联的设备 */
            struct usb_host_endpoint *ep;	/* (internal) pointer to endpoint */
    
            pipe用来查找端点队列,队列的特性定义在端点描述符中。pipe的各位定义如下:   
            bit7-0:bit7数据传输方向,0 = Host-to-Device [Out]1 = Device-to-Host [In]
            bit8-14:USB设备地址(编号),bit positions known to uhci-hcd
            bit15-18:端点地址(编号),... bit positions known to uhci-hcd,可找到对应的端点
            bit30-31:pipe的类型,00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk)
            unsigned int pipe;		        /* (in) pipe information */
    
            unsigned int stream_id;		    /* (in) stream ID */
            int status;			            /* (return) non-ISO status */
            unsigned int transfer_flags;	/* (in) URB_SHORT_NOT_OK | ...*/
            void *transfer_buffer;		    /* (in) associated data buffer */
            dma_addr_t transfer_dma;	    /* (in) dma addr for transfer_buffer */
            struct scatterlist *sg;		    /* (in) scatter gather buffer list */
            int num_mapped_sgs;		        /* (internal) mapped sg entries */
            int num_sgs;			        /* (in) number of entries in the sg list */
            u32 transfer_buffer_length;	    /* (in) data buffer length */
            u32 actual_length;		        /* (return) actual transfer length */
            unsigned char *setup_packet;	/* (in) setup packet (control only) */
            dma_addr_t setup_dma;		    /* (in) dma addr for setup_packet */
            int start_frame;		        /* (modify) start frame (ISO) */
            int number_of_packets;		    /* (in) number of ISO packets */
            int interval;			        /* (modify) transfer interval
            int error_count;		        /* (return) number of ISO errors */
            void *context;			        /* (in) context for completion */
            usb_complete_t complete;	    /* (in) completion routine */
            struct usb_iso_packet_descriptor iso_frame_desc[0];  // 等时数据包
        };
    

    使用usb_sndctrlpipeusb_rcvctrlpipe宏获取控制传输的发送和接收pipe,使用usb_sndisocpipeusb_rcvisocpipe宏获取等时传输的发送和接收pipe,使用usb_sndbulkpipeusb_rcvbulkpipe宏获取批量传输的发送和接收pipe,使用usb_sndintpipeusb_rcvintpipe宏获取中断传输的发送和接收pipe。

        #define USB_DIR_OUT			0		/* 数据传输方向 to device */
        #define USB_DIR_IN			0x80		/* 数据传输方向 to host */
        #define PIPE_ISOCHRONOUS		0  // pipe类型
        #define PIPE_INTERRUPT			1
        #define PIPE_CONTROL			2
        #define PIPE_BULK			3
        static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
        {
            return (dev->devnum << 8) | (endpoint << 15);
        }
        #define usb_sndctrlpipe(dev, endpoint)	\
            ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint))
        #define usb_rcvctrlpipe(dev, endpoint)	\
            ((PIPE_CONTROL << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
        #define usb_sndisocpipe(dev, endpoint)	\
            ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint))
        #define usb_rcvisocpipe(dev, endpoint)	\
            ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
        #define usb_sndbulkpipe(dev, endpoint)	\
            ((PIPE_BULK << 30) | __create_pipe(dev, endpoint))
        #define usb_rcvbulkpipe(dev, endpoint)	\
            ((PIPE_BULK << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
        #define usb_sndintpipe(dev, endpoint)	\
            ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint))
        #define usb_rcvintpipe(dev, endpoint)	\
            ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
    

    4.4.2.urb的操作函数

    (1)分配和释放URB
    使用usb_alloc_urb分配一个urb结构体,使用usb_free_urb释放一个urb结构体。参数iso_packets表示分配等时数据包的数量,若为0则不分配,mem_flags为分配内存的标志,调用kmalloc时使用。urb不宜静态创建,因为这可能破环USB核心层使用的URB引用计数。

        [include/linux/usb.h]
        // 成功返回urb的指针,否则返回NULL
        struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
        void usb_free_urb(struct urb *urb)
    

    (2)初始化URB
    urb结构体在使用之前要和USB设备端点对应起来,需要根据数据传递方式进行初始化。
    中断传输方式(端点)使用usb_fill_int_urb初始化urb结构体,参数pipe比较重要,根据pipe可以知道对应的USB设备、端点、数据传输方向及数据传输类型。对应中断传输,可使用usb_sndintpipeusb_rcvintpipe宏来获取pipe。

        [include/linux/usb.h]
        // urb-urb结构体的指针
        // dev-urb关联的usb设备结构体指针
        // pipe-端点的管道,
        // transfer_buffer-传输缓冲区指针,和urb一样,不能是静态缓冲区,不能使用kmalloc分配,
        //     必须使用专门的函数usb_alloc_coherent分配
        // buffer_length-传输缓冲区transfer_buffer的长度
        // complete_fn-传输完成的回调函数
        // context-complete_fn函数的上下文
        // interval-urb被调度的间隔,不宜太大也不宜太小,必须在规定的范围内
        static inline void usb_fill_int_urb(struct urb *urb, struct usb_device *dev,
    				    unsigned int pipe, void *transfer_buffer, int buffer_length,
    				    usb_complete_t complete_fn, void *context, int interval)
        [include/linux/usb.h]
        // 批量传输方式(端点)使用usb_fill_bulk_urb初始化urb结构体
        // pipe-可使用usb_sndbulkpipe和usb_rcvbulkpipe宏来获取。批量传输没有interval参数
        static inline void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev,
                            unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context)
        [include/linux/usb.h]
        // 控制传输方式(端点)使用usb_fill_control_urb初始化urb结构体
        // pipe-可使用usb_sndctrlpipe和usb_rcvctrlpipe宏来获取
        // setup_packet-控制传输和批量传输相比,多了一个setup_packet参数,setup_packet指向设置数据包的缓冲区。
        static inline void usb_fill_control_urb(struct urb *urb, struct usb_device *dev,
                            unsigned int pipe, unsigned char *setup_packet, void *transfer_buffer,
                            int buffer_length, usb_complete_t complete_fn, void *context)
    

    等时传输方式没有现成的初始化函数用来初始化urb结构体。需要手动初始化并设置URB_ISO_ASSP标志。
    (3)异步提交URB
    urb结构体分配和初始化完成后,就可以使用usb_submit_urb函数将请求异步的提交到USB核心层了,提交成功返回0,否则返回小于0的错误代码,请求完成后回调函数complete将被调用。urb指向要提交的urb结构体,mem_flagskmalloc函数分配内存时的标志。在将URB提交到USB核心层后,直到完成函数被调用之前,不要操作urb结构体中的任何成员。usb_submit_urb在原子上下文和进程上下文中都可以调用,mem_flags参数需要根据调用环境进行设置。如下所示:
    GFP_ATOMIC:在原子上下文环境中使用,如中断处理函数、中断下半本部分、tasklet、定时器处理函数、USR完成函数complete、持有自旋锁或读写锁时将current->stat设置为非TASK_RUNNING
    GFP_NOIO:在存储设备的块I/O和错误处理路径中使用。
    GFP_KERNEL:没有使用GFP_ATOMICGFP_NOIO的理由,就使用此标志。
    GFP_NOFS:没有使用过。

        [include/linux/usb.h]
        // 异步提交请求,complete回调函数被调用表明请求已完成
        int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
    

    (4)同步提交URB(信息)
    下面三个接口可以同步提交URB(信息),常用于提交一些简单的信息,所以省略了创建和初始化urb结构体的步骤,在请求处理完后或者超时时间到才返回,提交完成返回0,否则返回小于0的错误码。这三个接口都不能用在硬件中断、软件中断及持有自旋锁的上下文环境(!in_interrupt ())中。usb_control_msg用于提交控制传输的请求,usb_interrupt_msg用于传输中断类型的请求,usb_bulk_msg用于传输批量类型的请求。

        [include/linux/usb.h]
        // dev:USB设备结构体指针,pipe:pipe值,用于找到发送的USB设备端点,request:USB信息请求值,
        // requesttype:USB信息请求类型,value:USB消息值,index:USB消息索引值,data:发送或接收缓冲区指针,
        // size:发送或接收缓冲区的大小,timeout:等待消息完成的时间,单位为毫秒,0为一直等待
        int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
                    __u8 requesttype, __u16 value, __u16 index, void *data, 
                    __u16 size, int timeout)
        // dev:USB设备结构体指针,pipe:pipe值,用于找到发送的USB设备端点,data:发送或接收缓冲区指针,
        // len:缓冲区中发送的字节数,actual_length:保存实际传输的字节数,
        // timeout:等待消息完成的时间,单位为毫秒,0为一直等待
        int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
    		      void *data, int len, int *actual_length, int timeout)
        // dev:USB设备结构体指针,pipe:pipe值,用于找到发送的USB设备端点,data:发送或接收缓冲区指针,
        // len:缓冲区中发送的字节数,actual_length:保存实际传输的字节数,
        // timeout:等待消息完成的时间,单位为毫秒,0为一直等待
        int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
                void *data, int len, int *actual_length, int timeout)
    

    (5)取消URB
    usb_kill_urbusb_kill_urb可以取消一个urb,前者是异步取消,不等待取消成功就返回,若取消的urb正在执行,则执行完成后取消,后者是同步的,直到取消成功才返回。

        [include/linux/usb.h]
        void usb_unlink_urb(struct urb *urb)
        void usb_kill_urb(struct urb *urb)
    

    (6)URB传输数据缓冲区的分配和释放
    初始化USB请求块URB的参数transfer_buffer不能静态定义和使用kmalloc等,必须使用usb_alloc_coherent函数分配。因为USB数据内部传输用到了DMA,为了保证数据一致性,则必须要使用和DMA相关的内存分配的函数,usb_alloc_coherent函数内部调用了dma_alloc_coherent。此函数分配的内存是uncache的。使用usb_free_coherent释放分配的内存。

        [include/linux/usb.h]
        // dev:USB设备结构体
        // size:缓冲区长度
        // mem_flags:内存分配标志
        // dma:分配的内存对应的物理地址
        void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags,
    			 dma_addr_t *dma)
        void usb_free_coherent(struct usb_device *dev, size_t size, void *addr,
    		       dma_addr_t dma)
    

    5.实验

    首先取消内核中的usb mouse驱动支持,路径如下,取消USB HID transport layer的驱动支持。

    Device Drivers > 
        HID support > 
            USB HID support > 
                <> USB HID transport layer
    

    使用insmod usb_mouse.ko,插入USB鼠标,系统会打印鼠标信息,同时在dev目录下生成/dev/input/event4设备节点。使用hexdump /dev/input/event4命令,此时移动鼠标,会看到输出的数据。

    6.USB鼠标驱动源码

    	/*===========================usb_mouse.h================================*/
    	#ifndef USB_MOUSE_H
    	#define USB_MOUSE_H
    	#include <linux/usb.h>
    	#include <uapi/linux/usb/ch9.h>
    	#include <linux/usb/input.h>
    	#include <linux/input.h>
    	struct usb_mouse
    	{
    	    struct usb_host_interface *intf;
    	    struct usb_device* dev;
    	    struct usb_endpoint_descriptor* endpoint;
    	    struct urb* irq_urb;
    	    struct input_dev* input;
    	    int maxlen;
    	    unsigned int pipe;
    	    char* data;  // 虚拟地址
    	    dma_addr_t data_dma;  // 物理地址
    	    char name[128];
    	    char phys[64];
    	};
    #endif // USB_MOUSE_H
    	/*===========================usb_mouse.c================================*/
    	#include "usb_mouse.h"
    	#include <linux/kernel.h>
    	#include <linux/slab.h>
    	#include <linux/module.h>
    	#include <linux/init.h>
    	#include <linux/hid.h>
    	#include <linux/device.h>
    	
    	// 参考usbmouse.c文件,路径:usbmouse.c	drivers/hid/usbhid/usbmouse.c	
    	
    	struct usb_mouse* usb_mouse_dev = NULL;
    	
    	static int usb_mouse_open(struct input_dev *dev)
    	{
    	    int ret = 0;
    		struct usb_mouse* usb_mouse = input_get_drvdata(dev);
    		usb_mouse->irq_urb->dev = usb_mouse->dev;
    		if ((ret = usb_submit_urb(usb_mouse->irq_urb, GFP_KERNEL)) < 0) {
    	        dev_err(&usb_mouse->dev->dev, "usb_submit_urb failed, errno %d\n", ret);
    			return -EIO;
    	    }
    		return 0;
    	}
    	
    	static void usb_mouse_close(struct input_dev *dev)
    	{
    		struct usb_mouse* usb_mouse = input_get_drvdata(dev);
    		usb_kill_urb(usb_mouse->irq_urb);
    	}
    	
    	
    	static void usb_mouse_irq(struct urb *urb)
    	{
    		struct usb_mouse* usb_mouse = urb->context;
    		char* data = usb_mouse->data;
    		struct input_dev* dev = usb_mouse->input;
    		int status, i;
    	    for (i = 0; i < usb_mouse->maxlen; i++) {
    	        printk("%02x ", data[i]);
    	    }
    	    printk("\n");
    	    // 有标准的完成函数,不需要判断usb的状态
    	    /*
    		switch (urb->status) {  // 获取urb提交结果
    		case 0:			// success 
    			break;
    		case -ECONNRESET:	// unlink 
    		case -ENOENT:
    		case -ESHUTDOWN:
    			return;
    		// -EPIPE:  should clear the halt 
    		default:		// error 
    			goto resubmit;
    		}
    	    */
    		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:
    		status = usb_submit_urb(urb, GFP_ATOMIC);
    		if (status)
    			dev_err(&usb_mouse->dev->dev,
    				"can't resubmit intr, %s-%s/input0, status %d\n",
    				usb_mouse->dev->bus->bus_name,
    				usb_mouse->dev->devpath, status);
    	}
    	
    	static int usb_mouse_probe(struct usb_interface* intf, const struct usb_device_id* id)
    	{
    		int error = -ENOMEM;
    	    struct usb_device* usbdev;
    	    usbdev = interface_to_usbdev(intf);
    	    usb_mouse_dev = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL);
    	    if (NULL == usb_mouse_dev) {
    			dev_err(&usbdev->dev, "usb_mouse_dev kmalloc failed\n");
    	        return error; 
    	    }
    	    memset(usb_mouse_dev, 0, sizeof(struct usb_mouse));
    	    usb_mouse_dev->dev = usbdev;
    	    dev_info(&usbdev->dev, "bcdUSB = 0x%x\n", usb_mouse_dev->dev->descriptor.bcdUSB);  // USB版本号
    	    dev_info(&usbdev->dev, "idVendor = 0x%x\n", usb_mouse_dev->dev->descriptor.idVendor);  // 厂商编号
    	    dev_info(&usbdev->dev, "idProduct = 0x%x\n", usb_mouse_dev->dev->descriptor.idProduct);  // 设备出场编号
    	
    	    usb_mouse_dev->intf = intf->cur_altsetting;
    	    // 检查此端点数量是否为1(除端点0)
    		if (usb_mouse_dev->intf->desc.bNumEndpoints != 1) {
    			error = -ENODEV;
    			dev_err(&usbdev->dev, "bNumEndpoints error, bNumEndpoints %u\n", 
    	                usb_mouse_dev->intf->desc.bNumEndpoints);
    	        goto free_usb_mouse;
    	    }
    	    // 获取主机侧与鼠标通信的端点
    		usb_mouse_dev->endpoint = &usb_mouse_dev->intf->endpoint[0].desc;
    	    // 判断是否是中断输入端点
    		if (!usb_endpoint_is_int_in(usb_mouse_dev->endpoint)) {
    			error = -ENODEV;
    	    	dev_err(&usbdev->dev, "endpoint isn't int in\n");
    	        goto free_usb_mouse;
    	    }
    	    // 获取pipe,根据pipe可以知道对应的USB设备、端点、数据传输方向及数据传输类型
    	    usb_mouse_dev->pipe = usb_rcvintpipe(usb_mouse_dev->dev, 
    	            usb_mouse_dev->endpoint->bEndpointAddress);
    	    // 获取本端点接收或发送数据包的最大字节数
    	    usb_mouse_dev->maxlen = usb_maxpacket(usb_mouse_dev->dev, usb_mouse_dev->pipe, 
    	            usb_pipeout(usb_mouse_dev->pipe));
    	    dev_info(&usbdev->dev, "wMaxPacketSize 0x%x\n", usb_mouse_dev->maxlen);
    	    // 分配usb数据传输的缓冲区,不能使用kmalloc分配
    	    usb_mouse_dev->data = usb_alloc_coherent(usb_mouse_dev->dev, 
    	            usb_mouse_dev->maxlen, GFP_ATOMIC, &usb_mouse_dev->data_dma);
    	    if (NULL == usb_mouse_dev->data) {    
    	        dev_err(&usbdev->dev, "usb mouse alloc usb coherent buffer error\n");  
    	        goto free_usb_mouse;
    	    }
    	    // 分配一个usb请求块
    	    usb_mouse_dev->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
    	    if (NULL == usb_mouse_dev->irq_urb) {
    	        dev_err(&usbdev->dev, "usb mouse alloc usb urb error\n");  
    	        goto free_usb_coherent;
    	    }
    	    // 填充usb请求块
    	    usb_fill_int_urb(usb_mouse_dev->irq_urb, usb_mouse_dev->dev, 
    	            usb_mouse_dev->pipe, usb_mouse_dev->data, usb_mouse_dev->maxlen,
    	            // usb_mouse_dev保存到struct urb的context变量中
    	            usb_mouse_irq, usb_mouse_dev, usb_mouse_dev->endpoint->bInterval);
    	    dev_info(&usbdev->dev, "bInterval 0x%x\n", usb_mouse_dev->endpoint->bInterval);
    	    usb_mouse_dev->irq_urb->transfer_dma = usb_mouse_dev->data_dma;
    		usb_mouse_dev->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    	
    	
    	    usb_set_intfdata(intf, usb_mouse_dev);
    	    usb_mouse_dev->input = input_allocate_device();
    	    if (NULL == usb_mouse_dev->input) {
    	        dev_err(&usbdev->dev, "usb mouse allocate input device error\n");
    	        goto free_usb_urb;
    	    }
    		if (usb_mouse_dev->dev->manufacturer)
    			strlcpy(usb_mouse_dev->name, usb_mouse_dev->dev->manufacturer, 
    	                sizeof(usb_mouse_dev->name));
    		if (usb_mouse_dev->dev->product) {
    			if (usb_mouse_dev->dev->manufacturer)
    				strlcat(usb_mouse_dev->name, " ", sizeof(usb_mouse_dev->name));
    			strlcat(usb_mouse_dev->name, usb_mouse_dev->dev->product, 
    	                sizeof(usb_mouse_dev->name));
    		}
    		if (!strlen(usb_mouse_dev->name))
    			snprintf(usb_mouse_dev->name, sizeof(usb_mouse_dev->name),
    				 "USB HIDBP Mouse %04x:%04x",
    				 le16_to_cpu(usb_mouse_dev->dev->descriptor.idVendor),
    				 le16_to_cpu(usb_mouse_dev->dev->descriptor.idProduct));
    		usb_make_path(usb_mouse_dev->dev, usb_mouse_dev->phys, sizeof(usb_mouse_dev->phys));
    		strlcat(usb_mouse_dev->phys, "/input0", sizeof(usb_mouse_dev->phys));
    		usb_mouse_dev->input->name = usb_mouse_dev->name;
    		usb_mouse_dev->input->phys = usb_mouse_dev->phys;
    	    dev_info(&usbdev->dev, "name = %s\n", usb_mouse_dev->name);
    	    dev_info(&usbdev->dev, "phys = %s\n", usb_mouse_dev->phys);
    	
    		usb_to_input_id(usb_mouse_dev->dev, &usb_mouse_dev->input->id);
    		usb_mouse_dev->input->dev.parent = &intf->dev;
    		usb_mouse_dev->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
    		usb_mouse_dev->input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
    			BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
    		usb_mouse_dev->input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
    		usb_mouse_dev->input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
    			BIT_MASK(BTN_EXTRA);
    		usb_mouse_dev->input->relbit[0] |= BIT_MASK(REL_WHEEL);
    	    input_set_drvdata(usb_mouse_dev->input, usb_mouse_dev);
    	    usb_mouse_dev->input->open = usb_mouse_open;
    		usb_mouse_dev->input->close = usb_mouse_close;
    	    error = input_register_device(usb_mouse_dev->input);
    	    if (error < 0) {
    	        dev_err(&usbdev->dev, "usb mouse register input device error, errno %d\n", error);
    	        goto free_input_device;
    	    }
    	#if 0  // 测试用
    	    // 提交urb
    	    if (error = usb_submit_urb(usb_mouse_dev->irq_urb, GFP_ATOMIC)) {
    	        dev_err(&usbdev->dev, "usb_submit_urb failed, errno %d\n", error);
    			return error;        
    	    }
    	#endif
    	
    	    return 0;
    	free_input_device:
    	    input_free_device(usb_mouse_dev->input);
    	free_usb_urb:
    	    usb_free_urb(usb_mouse_dev->irq_urb);
    	free_usb_coherent:
    	    usb_free_coherent(usb_mouse_dev->dev, usb_mouse_dev->maxlen, 
    	                usb_mouse_dev->data, usb_mouse_dev->data_dma);
    	free_usb_mouse:
    	    kfree(usb_mouse_dev);
    	    usb_mouse_dev = NULL;
    	    return error;
    	}
    	
    	static void usb_mouse_disconnect(struct usb_interface *intf)
    	{
    	    dev_info(&usb_mouse_dev->dev->dev, "usb_mouse_disconnect\n");
    	    usb_kill_urb(usb_mouse_dev->irq_urb);
    	    input_unregister_device(usb_mouse_dev->input);
    	    input_free_device(usb_mouse_dev->input);
    	    usb_free_urb(usb_mouse_dev->irq_urb);
    	    usb_free_coherent(usb_mouse_dev->dev, usb_mouse_dev->maxlen, 
    	                usb_mouse_dev->data, usb_mouse_dev->data_dma);
    	    kfree(usb_mouse_dev);
    	    usb_mouse_dev = NULL;
    	}
    	
    	static struct usb_device_id usb_mouse_id_table [] = {
    		{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
    			USB_INTERFACE_PROTOCOL_MOUSE) },
    		{ }	/* Terminating entry */
    	};
    	MODULE_DEVICE_TABLE(usb, usb_mouse_id_table);
    	
    	
    	static struct usb_driver usb_mouse_driver = {
    		.name		= "usb_mouse",
    		.probe		= usb_mouse_probe,
    		.disconnect	= usb_mouse_disconnect,
    		.id_table	= usb_mouse_id_table,
    	};
    	
    	#if 0
    	static int __init usb_mouse_init(void)
    	{
    	    int ret = usb_register(&usb_mouse_driver);
    	    if (ret < 0)
    	        pr_err("usb mouse register failed, errno: %d\n", ret);
    	    return ret;
    	}
    	static void __exit usb_mouse_exit(void)
    	{
    	    usb_deregister(&usb_mouse_driver);
    	}
    	module_init(usb_mouse_init);
    	module_exit(usb_mouse_exit);
    	#else
    	module_driver(usb_mouse_driver, usb_register, usb_deregister);
    	#endif // 1
    	
    	MODULE_LICENSE("GPL");
    	MODULE_AUTHOR("liyang.plus@foxmail.com");
    
    展开全文
  • 机型:联想P53 问题:时不时听到USB弹出的声音,但实际没有...将USB根集线器(USB3.0)或可扩展主机控制器右键属性【电源管理】,取消勾选“允许关闭此设备而节约电源” 但耗电会增加,没有插入电源时不推荐使用 ...

    机型:联想P53

    问题:时不时听到USB弹出的声音,但实际没有东西插入

    解决方法:win+q 搜索 设备管理器-->通用串行总线控制器-->将USB根集线器(USB3.0)或可扩展主机控制器右键属性【电源管理】,取消勾选“允许关闭此设备而节约电源”,重启生效

    但耗电会增加,没有插入电源时不推荐使用

    展开全文
  • FTDI 公司 (Future Technology Devices International) 扩展了它的 USB 接口芯片系列,推出了 Vinculum 系列嵌入式 USB 主机控制器。FTDI 公司设计的这些 IC 供那些没有 USB 标准相关专业知识的设计师使用。这些...
  • 玉女真身:Intel展示USB3.0接口样品 Intel今天放出来一份新闻稿,指出xHCI可扩展主机控制器草案目前最新更新是v0.9版本,已经可以支持USB3.0接口了。不过有小...
    玉女真身:Intel展示USB3.0接口样品
        Intel今天放出来一份新闻稿,指出xHCI可扩展主机控制器草案目前最新更新是v0.9版本,已经可以支持USB3.0接口了。不过有小道消息宣称Intel将在下周的IDF中展示USB3.0的接口样品。

        Intel的xHCI已经可以支持USB3.0的接口,并且还可以向下兼容USB2.0的接口。依照Intel的意思USB3.0控制器将可以与USB3.0的软件对战进行通信,并且将通过发放免费RAND-Z许可证统一所有USB3.0的软硬件规范。
    001.jpg
        相关的xHCI v0.95版本会在今年第四季度发布。Intel承诺,将在8月19日的IDF大会上演示USB3.0接口的工程样品。预计真正支持USB3.0的产品将会在2009年晚些时候上市。

    USB3.0接口的数据传输速度将是USB2.0的10倍,目前USB2.0的数据传输速率在2.0~4.8Gb/s之间,传输带宽为600MB/s。USB3.0确实是非常值得期待的规范。

    来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/15453579/viewspace-438786/,如需转载,请注明出处,否则将追究法律责任。

    转载于:http://blog.itpub.net/15453579/viewspace-438786/

    展开全文
  • 此软件包提供英特尔USB 3.0可扩展主机控制器驱动程序,并受运行Windows 7操作系统的Vostro成就/Inspiron灵越/XPS/Optiplex/Alienware/Latitude/Precision/IoT系统支持。 为确保完整下载,请验证校验和值。 MD5: 7c...
  • • 通过USB主机控制外设I2C器件 • 通过内部或外部稳压输出1.8V内核电压 • 支持最高2 TB的存储寻址能力 • 符合RoHS标准的封装 - USB2642: 48引脚(7x7 mm²) QFN • 温度范围: - 商业级范围(0 °C至+70 °C) ...
  • 1、在intel驱动下载官网上下载 英特尔® USB 3.0 可扩展主机控制器驱动程序适用于英特尔® 8/9/100 系列和英特尔® C220/C610 芯片组系列 驱动 百度搜索 intel驱动下载 进入官网后,搜索usb 建议在本机上下载后...
  • - 通过CDC/NCM设备类实现的集成主机控制器端点反 射器,适合汽车应用 • USB桥接 - USB转I2C、 SPI、 UART、 I2S和GPIO • PortSwap - 配置USB 2.0差分对信号交换 • PHYBoost - 用于恢复信号完整性的编程USB ...
  • SYS安装教程

    千次阅读 2020-06-25 17:32:01
    SYS安装教程: 第一步:右击我的电脑—>管理 第二步:找到设备管理器–>通用串行总线控制器–>...Intel ®USB 3.0可扩展主机控制器 第三步:右击Intel ®USB 3.0可扩展主机控制器—>更新驱动程序即可 ...
  • 1 引言  USB接口以其数据传输快、连接简单、易于扩展、支持热插拔等特点已成为外设与PC通信的主要方式之一。随着嵌入式系统的发展,嵌入式微处理器...AVR单片机是新一代基于哈佛结构的高速RISC微控制器,具有速度快
  • 它即可以作为一个可灵活更换存储媒体的电子语音记事本,也可以作为一个语音录入,将记录在USB存储器中的文本文件方便的转移到PC机等其他文字处理设备进行再处理,具有极高的功能可扩展性。  1系统结构与原理
  • jsr80 java 访问 usb

    2015-02-14 08:52:01
    USB 设备附加到 USB 端口上时,就将这个 惟一端口 ID 分配给这台设备,并且 USB 控制器会读取 设备描述符。设备描述符包括适用于该设备的全局信息、以及设备的 配置信息。配置定义了一台 USB 设备的功能和 I/O ...
  • XHCI:即可扩展主机控制器接口,目前主要是面向USB3.0设备的,同时它也支持USB2.0及以下的设备。其中USB2.0使用的是EHCI,USB1.1使用的是OHCI。  为了实现在DOS下检索XHCI下所有的USB设备,并显示出设备接在哪个...
  • USB2.0技术为外设与主机之间通信提供了一种高效的双向数据通道,广泛地用于数据采集和工业控制等方面.系统采用USB接口芯片CY7C68013A完成与PC机的数据传输功能.利用CY7C68013A控制器的Slave FIFO方式,用FPGA产生...
  • 由于这些设备能够通过USB连接主机并进行通信,根据枚举和配置结果选择充电。这些选择可以由应用处理器控制,或者是由负责电源管理及其它系统功能的独立微控制器控制。系统检测端口类型、枚举,并向充电器发送相应...

空空如也

空空如也

1 2 3 4
收藏数 68
精华内容 27
关键字:

usb可扩展主机控制器