精华内容
下载资源
问答
  • TmpUsb TmpUsb作为USB磁盘驱动器(大小为11 KB,可用7-8 KB)呈现给计算机。 然后可以使用该驱动器存储任何数据(前提是大小不成问题)...第一次插入TmpUsb时,它将以“未设防”模式启动,从驱动器的标签上可以看到。
  • Windows内核模式驱动程序,模拟著名的USB游戏控制。 (该项目可免费获得许可,但需要财务支持以保持其持续改进。除了维护和稳定性外,还有许多需要添加的功能。如果您的公司正在使用ViGEm的组件,请考虑出来) ...
  • USB Drive Fresher将帮助您保持您的USB驱动器的清洁,根据您所指定的规则,自动删除不需要的文件。帮你删除系统的垃圾文件,比如thumbs.db或其他临时文件等。需要的用户可以前来下载。 软件功能 清理无效的U盘 ...
  • Linux驱动开发: USB驱动开发

    千次阅读 多人点赞 2021-08-24 22:03:05
    一、USB简介 1.1 什么是USB? USB是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范,被广泛地应用于个人电脑和移动设备等信息通讯产品,USB就是简写,中文叫通用串行总线。最早出现...

    一、USB简介

    1.1  什么是USB?

          USB是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范,被广泛地应用于个人电脑和移动设备等信息通讯产品,USB就是简写,中文叫通用串行总线。最早出现在1995年,伴随着奔腾机发展而来。自微软在Windows 98中加入对USB接口的支持后,USB接口才推广开来,USB设备也日渐增多,如数码相机、摄像头、扫描仪、游戏杆、打印机、键盘、鼠标等等,其中应用最广的就是摄像头和U盘了。

         USB包括老旧的USB 1.1标准和时下正流行的USB 2.0标准。传统的USB 1.1最高传输速率为12Mbps,一般厂商将其符合USB 1.1标准的产品称为“全速USB”。而高速USB 2.0最初推出时最高传输速率仅为240Mbps,后来USB2.0推广组(USB Promoter Group)在1999年10月将该速率提高到480Mbps,比传统的USB 1.1快40倍。

          USB2.0向下兼容USB 1.1,当然USB1.1设备也“向上兼容”USB 2.0,但是无法实现USB2.0的传输能力,并自动以低速传输。USB 2.0连接线的最大长度为5米,但如果用五个USB适配器,则最大长度可达30米。

          最新一代是USB 3.1,传输速度为10Gbit/s,三段式电压5V/12V/20V,最大供电100W ,新型Type C插型不再分正反。

     

          USB采用四线电缆,其中两根是用来传送数据的串行通道,另两根为下游(Downstream)设备提供电源,对于高速且需要高带宽的外设,USB以全速12Mbps的传输数据;对于低速外设,USB则以1.5Mbps的传输速率来传输数据。USB总线会根据外设情况在两种传输模式中自动地动态转换。USB是基于令牌的总线。类似于令牌环网络或FDDI基于令牌的总线。USB主控制器广播令牌,总线上设备检测令牌中的地址是否与自身相符,通过接收或发送数据给主机来响应。USB通过支持悬挂/恢复操作来管理USB总线电源。USB系统采用级联星型拓扑,该拓扑由三个基本部分组成:主机(Host),集线器(Hub)和功能设备。

          主机,也称为根,根结或根Hub,它做在主板上或作为适配卡安装在计算机上,主机包含有主控制器和根集线器(Root Hub),控制着USB总线上的数据和控制信息的流动,每个USB系统只能有一个根集线器,它连接在主控制器上。

         集线器是USB结构中的特定成分,它提供叫做端口(Port)的点将设备连接到USB总线上,同时检测连接在总线上的设备,并为这些设备提供电源管理,负责总线的故障检测和恢复。集线可为总线提供能源,亦可为自身提供能源(从外部得到电源),自身提供能源的设备可插入总线提供能源的集线器中,但总线提供能源的设备不能插入自身提供能源的集线器或支持超过四个的下游端口中,如总线提供能源设备的需要超过100mA电源时,不能同总线提供电源的集线器连接。

    USB介绍: http://www.usb.org/home

    1.2 USB设备主要优点总结

    1. 可以热插拔

    用户在使用外接设备时,不需要关机再开机等动作,而是在电脑工作时,直接将USB插上使用。

    2. 携带方便

    USB设备大多以“小、轻、薄”见长,对用户来说,随身携带大量数据时,很方便。当然USB硬盘是首要之选了。

    3. 标准统一

    大家常见的是IDE接口的硬盘,串口的鼠标键盘,并口的打印机扫描仪,可是有了USB之后,这些应用外设统统可以用同样的标准与个人电脑连接,这时就有了USB硬盘、USB鼠标、USB打印机等等。

    4. 可以连接多个设备

    USB在个人电脑上往往具有多个接口,可以同时连接几个设备,如果接上一个有四个端口的USB HUB时,就可以再连上四个USB设备,以此类推 (注:最高可连接至127个设备,扩展到一定数量时需要外加电源)

    1.3  USB电器接口定义

    一般的排列方式是:红白绿黑从左到右

    定义:

    1. 红色-USB电源: 标有-VCC、Power、5V、5VSB字样
    2. 白色-USB数据线:(负)-DATA-、USBD-、PD-、USBDT-
    3. 绿色-USB数据线:(正)-DATA+、USBD+、PD+、USBDT+
    4. 黑色-地线: GND、Ground

     

    1. 4 USB的插入检测机制

            USB端口的D+和D-均用一个15k的电阻接地,当无设备接入时,均处于低电平;在设备端在D+(表示高速设备或者全速设备)或者D-(表示低速设备)接了一个1.5k的上拉电阻到+3.3v,一旦将设备接入,USB端口的D+或者D-其中一个被拉高为3v,系统识别到外部设备接入。

           注意:高速设备首先会被识别为全速设备,然后再通过集线器和设备二者的确认最后切换到高速模式下。

         在高速模式下,采用的是电流传输模式,这个时候上拉电阻需要从D+上断开。

         usb主机检测到USB设备插入后,就要对设备进行枚举了。枚举的作用就是从设备是那个读取一些信息,知道设备是什么样的设备,如果通信,这样主机就可以根据这些信息选择合适的驱动程序。调试USB设备,很重要的一点就是USB枚举过程,只要枚举成功了,那就成功一大半了。

          当设备没有枚举成功时(可以通过一个10K的电阻将USB的电源端和D+或者D-连接起来,电脑会发现一个无法识别的设备,这个设备的PID和VID都是0,根据每个特性可以简单的判定设备的枚举是否成功。 

    二、 USB标准描述符

            USB协议为USB设备定义了一套描述设备功能和属性的有固定结构的描述符,包括标准的描述符即设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符。USB设备通过这些描述符向USB主机汇报设备的各种各样属性,主机通过对这些描述符的访问对设备进行类型识别、配置并为其提供相应的客户端驱动程序。

            USB设备通过描述符反映自己的设备特性。USB描述符是由特定格式排列的一组数据结构组成。

         在USB设备枚举过程中,主机端的协义软件需要解析从USB设备读取的所有描述符信息。在USB主向设备发送读取描述符的请求后,USB设备将所有的描述符以连续的数据流方式传输给USB主机。主机从第一个读到的字符开始,根据双方规定好的数据格式,顺序地解析读到的数据流。

         USB描述符包含标准描述符类描述符厂商特定描述3种形式。任何一种设备必须遵循USB标准描述符(除了字符串描述符可选外)。

         在USB1.X中,规定了5种标准描述符:设备描述符(Device Descriptor)、配置描述符(Configuration Descriptor)、接口描述符(Interface Descriptor)、端点描述符(Endpoint Descriptor)和字符串描述符(String Descriptor)。

        每个USB设备只有一个设备描述符,而一个设备中可包含一个或多个配置描述符,即USB设备可以有多种配置。设备的每一个配置中又可以包含一个或多个接口描述符,即USB设备可以支持多种功能(接口),接口的特性通过描述符提供。

        在USB主机访问USB设备的描述符时,USB设备依照设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符顺序将所有描述符传给主机。一设备至少要包含设备描述符、配置描述符和接口描述符,如果USB设备没有端点描述符,则它仅仅用默认管道与主机进行数据传输。

    2. 1 设备描述符

    30.2.1 设备描述符
    /* USB_DT_DEVICE: Device descriptor */
    struct usb_device_descriptor {
        __u8  bLength;
        __u8  bDescriptorType;
    
        __le16 bcdUSB;
        __u8  bDeviceClass;
        __u8  bDeviceSubClass;
        __u8  bDeviceProtocol;
        __u8  bMaxPacketSize0;
        __le16 idVendor;
        __le16 idProduct;
        __le16 bcdDevice;
        __u8  iManufacturer;
        __u8  iProduct;
        __u8  iSerialNumber;
        __u8  bNumConfigurations;
    } __attribute__ ((packed));
    
    
    //USB设备信息与驱动端匹配成功的时候调用。
    static int test_probe(struct usb_interface *intf,const struct usb_device_id *id)  //资源探索函数
    {
        printk("识别到USB光谱仪设备,正在进行设备初始化.\n");
        printk("该设备的厂商ID=%#x,设备ID:%#x\n",id->idVendor,id->idProduct);
    
        /*通过接口获取设备信息*/	
        struct usb_device *dev = interface_to_usbdev(intf);
        printk("bcdUSB = %#x\n",dev->descriptor.bcdUSB);   //从USB设备描述符中获取USB版本
        printk("vidUSB = %#x\n",dev->descriptor.idVendor); //从USB设备描述符中获取厂商ID
        printk("pidUSB = %#x\n",dev->descriptor.idProduct);//从USB设备描述符中获取产品ID
    
        /*-------------------------------*/
    }
    

    设备描述符给出了USB设备的一般信息,包括对设备及在设备配置中起全程作用的信息,包括制造商标识号ID、产品序列号、所属设备类号、默认端点的最大包长度和配置描述符的个数等。一个USB设备必须有且仅有一个设备描述符。设备描述符是设备连接到总线上时USB主机所读取的第一个描述符,它包含了14个字段,结构如下

    USB设备描述符的结构

    偏移量

    大小

    描述

    0

    bLength

    1

    数字

    此描述表的字节数

    1

    bDecriptorType

    1

    常量

    描述符的类型(此处应为0x01,即设备描述符)

    2

    bcdUSB

    2

    BCD

    此设备与描述表兼容的USB设备说明版本号(BCD 码)

    4

    bDeviceClass

    1

    设备类码:

    如果此域的值为0则一个设置下每个接口指出它自己的类,各个接口各自独立工作。
    如果此域的值处于1~FEH之间,则设备在不同的接口上支持不同的类。并这些接口可能不能独立工作。此值指出了这些接口集体的类定义。
    如果此域设为FFH,则此设备的类由厂商定义。

    5

    bDeviceSubClass

    1

    子类

    子类码

    这些码值的具体含义根据bDeviceClass 域来看。
    bDeviceClass 域为零,此域也须为零
    bDeviceClass 域为FFH,此域的所有值保留。

    6

    bDevicePortocol

    1

    协议

    协议码

    这些码的值视bDeviceClass bDeviceSubClass 的值而定。 如果设备支持设备类相关的协议,此码标志了设备类的值。如果此域的值为零,则此设备不支持设备类相关的协议,然而,可能它的接口支持设备类相关的协议。如果此域的值为FFH,此设备使用厂商定义的协议。

    7

    bMaxPacketSize0

    1

    数字

    端点0的最大包大小(仅8,16,32,64为合法值)

    8

    idVendor

    2

    ID

    厂商标志(由USB-IF组织赋值)

    10

    idProduct

    2

    ID

    产品标志(由厂商赋值)

    12

    bcdDevice

    2

    BCD

    设备发行号(BCD 码)

    14

    iManufacturer

    1

    索引

    描述厂商信息的字符串描述符的索引值。

    15

    iProduct

    1

    索引

    描述产品信息的字串描述符的索引值。

    16

    iSerialNumber

    1

    索引

    描述设备序列号信息的字串描述符的索引值。

    17

    bNumConfigurations

    1

    数字

    可能的配置描述符数目

    其中bDescriptorType为描述符的类型,其含义可查下表(此表也适用于标准命令Get_Descriptor中wValue域高字节的取值含义):

     

    USB描述符的类型值

    类型

    描述符

    描述符值

    标准描述符

    设备描述符(Device Descriptor)

    0x01

    配置描述符(Configuration Descriptor

    0x02

    字符串描述符(String Descriptor

    0x03

    接口描述符(Interface Descriptor

    0x04

    端点描述符(EndPont Descriptor

    0x05

    类描述符

    集线器类描述符(Hub Descriptor

    0x29

    人机接口类描述符(HID

    0x21

    厂商定义的描述符

    0xFF

    设备类代码bDeviceClass可查下表:

    设备的类别(bDeviceClass)

    值(十进制)

    值(十六进制)

    说明

    0

    0x00

    接口描述符中提供类的值

    2

    0x02

    通信类

    9

    0x09

    集线器类

    220

    0xDC

    用于诊断用途的设备类

    224

    0xE0

    无线通信设备类

    255

    0xFF

    厂商定义的设备类

    下表列出了一个USB鼠标的设备描述符的例子,供大家分析一下:

    一种鼠标的设备描述符示例

    字段

    描述符值(十六制)

    bLength

    0x12

    bDecriptorType

    0x01

    bcdUSB

    0x110

    bDeviceClass

    0x00

    bDeviceSubClass

    0x00

    bDevicePortocol

    0x00

    bMaxPacketSize0

    0x08

    idVendor

    0x045E(Microsoft Corporation

    idProduct

    0x0047

    bcdDevice

    0x300

    iManufacturer

    0x01

    iProduct

    0x03

    iSerialNumber

    0x00

    bNumConfigurations

    0x01

    Linux内核中定义的设备描述符结构:

    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; //endpoint0最大包大小  
        _ _le16 idVendor; //厂商编号  
        _ _le16 idProduct; //产品编号  
        _ _le16 bcdDevice; //设备出厂编号  
        _ _u8 iManufacturer; //描述厂商字符串的索引  
        _ _u8 iProduct; //描述产品字符串的索引  
        _ _u8 iSerialNumber; //描述设备序列号字符串的索引  
        _ _u8 bNumConfigurations; //可能的配置数量  
    } _ _attribute_ _ ((packed));
    

    2.2 配置描述符

          配置描述符中包括了描述符的长度(属于此描述符的所有接口描述符和端点描述符的长度的和)、供电方式(自供电/总线供电)、最大耗电量等。主果主机发出USB标准命令Get_Descriptor要求得到设备的某个配置描述符,那么除了此配置描述符以外,此配置包含的所有接口描述符与端点描述符都将提供给USB主机。

    USB配置描述符的结构

    偏移量

    大小

    描述

    0

    bLength

    1

    数字

    此描述表的字节数长度。

    1

    bDescriptorType

    1

    常量

    配置描述表类型(此处为0x02)

    2

    wTotalLength

    2

    数字

    此配置信息的总长(包括配置,接口,端点和设备类及厂商定义的描述符)

    4

    bNumInterfaces

    1

    数字

    此配置所支持的接口个数

    5

    bCongfigurationValue

    1

    数字

    在SetConfiguration()请求中用作参数来选定此配置。

    6

    iConfiguration

    1

    索引

    描述此配置的字串描述表索引

    7

    bmAttributes

    1

    位图

    配置特性:

    D7: 保留(设为一)
    D6: 自给电源
    D5: 远程唤醒
    D4..0:保留(设为一)
    一个既用总线电源又有自给电源的设备会在MaxPower域指出需要从总线取的电量。并设置D6为一。运行时期的实际电源模式可由GetStatus(DEVICE) 请求得到。

    8

    MaxPower

    1

    mA

    在此配置下的总线电源耗费量。以 2mA 为一个单位。

    下面是一种硬盘的配置描述符示例:

    一种硬盘的配置描述符示例

    字段

    描述符值(十六进制)

    bLength

    0x09

    bDescriptorType

    0x02

    wTotalLength

    0x01F

    bNumInterfaces

    0x01

    bCongfigurationValue

    0x01

    iConfiguration

    0x00

    bmAttributes

    0x0C

    MaxPower

    0x32

     Linux内核中定义的配置描述符结构

    {  
        _ _u8 bLength; //描述符长度  
        _ _u8 bDescriptorType; //描述符类型编号  
          
        _ _le16 wTotalLength; //配置所返回的所有数据的大小  
        _ _u8 bNumInterfaces; // 配置所支持的接口数  
        _ _u8 bConfigurationValue; //Set_Configuration命令需要的参数值  
        _ _u8 iConfiguration; //描述该配置的字符串的索引值  
        _ _u8 bmAttributes; //供电模式的选择  
        _ _u8 bMaxPower; //设备从总线提取的最大电流  
    } _ _attribute_ _ ((packed));
    

    2.3 接口描述符

        配置描述符中包含了一个或多个接口描述符,这里的“接口”并不是指物理存在的接口,在这里把它称之为“功能”更易理解些,例如一个设备既有录音的功能又有扬声器的功能,则这个设备至少就有两个“接口”。

        如果一个配置描述符不止支持一个接口描述符,并且每个接口描述符都有一个或多个端点描述符,那么在响应USB主机的配置描述符命令时,USB设备的端点描述符总是紧跟着相关的接口描述符后面,作为配置描述符的一部分被返回。接口描述符不可直接用Set_DescriptorGet_Descriptor来存取。

        如果一个接口仅使用端点0,则接口描述符以后就不再返回端点描述符,并且此接口表现的是一个控制接口的特性,它使用与端点0相关联的默认管道进行数据传输。在这种情况下bNumberEndpoints域应被设置成0。接口描述符在说明端点个数并不把端点0计算在内。

    USB接口描述符的结构

    偏移量

    大小

    说明

    0

    bLength

    1

    数字

    此表的字节数

    1

    bDescriptorType

    1

    常量

    接口描述表类(此处应为0x04

    2

    bInterfaceNumber

    1

    数字

    接口号,当前配置支持的接口数组索引(从零开始)。

    3

    bAlternateSetting

    1

    数字

    可选设置的索引值。

    4

    bNumEndpoints

    1

    数字

    此接口用的端点数量,如果是零则说明此接口只用缺省控制管道。

    5

    bInterfaceClass

    1

    接口所属的类值: 零值为将来的标准保留。 如果此域的值设为FFH,则此接口类由厂商说明。 所有其它的值由USB 说明保留。

    6

    bInterfaceSubClass

    1

    子类

    子类:这些值的定义视bInterfaceClass域而定。 如果bInterfaceClass域的值为零则此域的值必须为零。 bInterfaceClass域不为FFH则所有值由USB 所保留。

    7

    bInterfaceProtocol

    1

    协议

    协议码:bInterfaceClass bInterfaceSubClass 域的值而定.如果一个接口支持设备类相关的请求此域的值指出了设备类说明中所定义的协议.

    8

    iInterface

    1

    索引

    描述此接口的字串描述表的索引值。

    对于bInterfaceClass字段,表示接口所属的类别,USB协议根据功能将不同的接口划分成不的类,其具体含义如下表所示:

    USB协议定义的接口类别(bInterfaceClass)

    值(十六进制)

    类别

    0x01

    音频类

    0x02

    CDC控制类

    0x03

    人机接口类(HID

    0x05

    物理类

    0x06

    图像类

    0x07

    打印机类

    0x08

    大数据存储类

    0x09

    集线器类

    0x0A

    CDC数据类

    0x0B

    智能卡类

    0x0D

    安全类

    0xDC

    诊断设备类

    0xE0

    无线控制器类

    0xFE

    特定应用类(包括红外的桥接器等)

    0xFF

    厂商定义的设备

     Linux内核中定义的接口描述符结构

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

    2.4 端点描述符

          端点是设备与主机之间进行数据传输的逻辑接口,除配置使用的端点0(控制端点,一般一个设备只有一个控制端点)为双向端口外,其它均为单向。端点描述符描述了数据的传输类型、传输方向、数据包大小和端点号(也可称为端点地址)等。

         除了描述符中描述的端点外,每个设备必须要有一个默认的控制型端点,地址为0,它的数据传输为双向,而且没有专门的描述符,只是在设备描述符中定义了它的最大包长度。主机通过此端点向设备发送命令,获得设备的各种描述符的信息,并通过它来配置设备。

    static int test_probe(struct usb_interface *intf,const struct usb_device_id *id)  //资源探索函数
    {
        /*获取端点描述符*/
        struct usb_endpoint_descriptor *endpoint = &interface->endpoint[0].desc;
        switch(endpoint->bmAttributes)
        {
            case 0:printk("设备支持控制传输.\n");break;
            case 1:printk("设备支持同步传输.\n");break;
            case 2:printk("设备支持批量传输.\n");break;
            case 3:printk("设备支持中断传输.\n");break;
        }
    }
    struct usb_endpoint_descriptor {
        __u8  bLength;
        __u8  bDescriptorType;
        __u8  bEndpointAddress;
        __u8  bmAttributes;
        __le16 wMaxPacketSize;
        __u8  bInterval;
        __u8  bRefresh;
        __u8  bSynchAddress;
    } __attribute__ ((packed));
    

    USB端点描述符的结构

    偏移量

    大小

    说明

    0

    bLength

    1

    数字

    此描述表的字节数长度

    1

    bDescriptorType

    1

    常量

    端点描述表类(此处应为0x05

    2

    bEndpointAddress

    1

    端点

    此描述表所描述的端点的地址、方向:
    Bit 3..0 : 端点号.
    Bit 6..4 :
    保留,为零
    Bit 7:    方向,如果控制端点则略。
    0:输出端点(主机到设备)
    1:输入端点(设备到主机)

    3

    bmAttributes

    1

    位图

    此域的值描述的是在bConfigurationValue域所指的配置下端点的特性。
    Bit 1..0 :传送类型

    00=控制传送

    01=同步传送

    10=批传送

    11=中断传送

    所有其它的位都保留。

    4

    wMaxPacketSize

    2

    数字

    当前配置下此端点能够接收或发送的最大数据包的大小。 对于实进传输,此值用于为每帧的数据净负荷预留时间。在实际运行时,管道可能不完全需要预留的带宽,实际带宽可由设备通过一种非USB定义的机制汇报给主机。对于中断传输,批量传输和控制传输,端点可能发送比之短的数据包

    6

    bInterval

    1

    数字

    周期数据传输端点的时间间隙。 此域的值对于批传送的端点及控制传送的端点无意义。对于同步传送的端点此域必需为1,表示周期为1ms。对于中断传送的端点此域值的范围为1ms255ms

    下表是一种鼠标的端点描述符的示例,该端点是一个中断端点:

    一种鼠标的端点描述符示例

    值(十六进制)

    bLength

    0x07

    bDescriptorType

    0x05

    bEndpointAddress

    0x81

    bmAttributes

    0x03

    wMaxPacketSize

    0x04

    bInterval

    0x0A

    一个 USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:

    控制CONTROL

    控制端点被用来控制对 USB 设备的不同部分访问. 通常用作配置设备、获取设备信息、发送命令到设备或获取设备状态报告。这些端点通常较小。每个 USB 设备都有一个控制端点称为"端点 0", 被 USB 核心用来在插入时配置设备。USB协议保证总有足够的带宽留给控制端点传送数据到设备.

    中断INTERRUPT

    每当 USB 主机向设备请求数据时,中断端点以固定的速率传送小量的数据。此为USB 键盘和鼠标的主要的数据传送方法。它还用以传送数据到 USB 设备来控制设备。通常不用来传送大量数据。USB协议保证总有足够的带宽留给中断端点传送数据到设备.

    批量BULK

    批量端点用以传送大量数据。这些端点常比中断端点大得多. 它们普遍用于不能有任何数据丢失的数据。USB 协议不保证传输在特定时间范围内完成。如果总线上没有足够的空间来发送整个BULK包,它被分为多个包进行传输。这些端点普遍用于打印机、USB Mass StorageUSB网络设备上

    等时ISOCHRONOUS

    等时端点也批量传送大量数据, 但是这个数据不被保证能送达。这些端点用在可以处理数据丢失的设备中,并且更多依赖于保持持续的数据流。如音频和视频设备等等。

    控制和批量端点用于异步数据传送,而中断和同步端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据,USB 核心为它们保留了相应的带宽。

    端点在linux内核中使用结构 struct usb_host_endpoint 来描述,它所包含的真实端点信息在另一个结构中:struct usb_endpoint_descriptor(端点描述符,包含所有的USB特定数据)。

    Linux内核中定义的端点描述符结构

    struct usb_endpoint_descriptor  
    {  
        _ _u8 bLength; //描述符长度  
        _ _u8 bDescriptorType; //描述符类型  
        _ _u8 bEndpointAddress; //端点地址:0~3位是端点号,第7位是方向(0-OUT,1-IN)  
        _ _u8 bmAttributes; //端点属性:bit[0:1] 的值为00表示控制,为01表示同步,为02表示批量,为03表示中断  
        _ _le16 wMaxPacketSize;  本端点接收或发送的最大信息包的大小  
        _ _u8 bInterval;//轮询数据传送端点的时间间隔  
                               //对于批量传送的端点以及控制传送的端点,此域忽略  
                               //对于同步传送的端点,此域必须为1  
        _ _u8 bRefresh;  
        _ _u8 bSynchAddress;  
    } _ _attribute_ _ ((packed));
    

    2.5 字符串描述符

         字符串描述符是一种可选的USB标准描述符,描述了如制商、设备名称或序列号等信息。如果一个设备无字符串描述符,则其它描述符中与字符串有关的索引值都必须为0。字符串使用的是Unicode编码。

        主机请示得到某个字符串描述符时一般分成两步:首先主机向设备发出USB标准命令Get_Descriptor,其中所使用的字符串的索引值为0,设备返回一个字符串描述符,此描述符的结构如下:

    USB字符串描述符(响应主机请求时返回的表示语言ID的字符串描述符)

    偏移量

    大小

    描述

    0

    bLength

    1

    N+2

    此描述表的字节数

    1

    bDescriptorType

    1

    常量

    字串描述表类型(此处应为0x03

    2

    wLANGID[0]

    2

    数字

    语言标识(LANGID 0

    N

    wLANGID[x]

    2

    数字

    语言标识(LANGID)码X

        该字符串描述符双字节的语言ID的数组,wLANGID[0]~wLANGID[x]指明了设备支持的语言,具体含义可查看USB_LANGIDs.pdf

         主机根据自己需要的语言,再次向设备发出USB标准命令Get_Descriptor,指明所要求得到的字符串的索引值和语言。这次设备所返回的是Unicode编号的字符串描述符,其结构如下:

    Unicode字符串描述符(响应主机请求时真正表示字符串编码的字符串描述符)

    偏移量

    大小

    描述

    0

    bLength

    1

    数字

    此描述表的字节数(bString域的数值N2

    1

    bDescriptorType

    1

    常量

    字串描述表类型(此处应为0x03

    2

    bString

    N

    数字

    UNICODE 编码的字串

         bString域为设备实际返回的以UNICODE编码的字符串流,我们在编写设备端硬件驱动的时候需要将字符串转换为UNICODE编码,可以通过一些UNICODE转换工具进行转换。这里推荐一款USB描述符生成工具“USB Unicode 字符串描述符生成器”,它专门为编写设备端驱动程序的需要而定制,可以非常方便将需要的字符串转换成UNICODE格式,进而导入C或汇编程序代码中。

    Linux内核中定义的字符串描述符结构

    struct usb_string_descriptor  
    {  
        _ _u8 bLength; //描述符长度  
        _ _u8 bDescriptorType; //描述符类型
        _ _le16 wData[1];  
    } _ _attribute_ _ ((packed));  
    

    三、HID描述符

    3.1 HID描述符介绍

           USB 设备中有一大类就是 HID 设备,即 Human Interface Devices,人机接口设备。这类设备包括鼠标、键盘等,主要用于人与计算机进行交互。 它是 USB 协议最早支持的一种设备类。 HID 设备可以作为低速、全速、高速设备用。由于 HID 设备要求用户输入能得到及时响应,故其传输方式通常采用中断方式。

         在 USB 协议中, HID 设备的定义放置在接口描述符中, USB 的设备描述符和配置描述符中不包含 HID 设备的信息。因此,对于某些特定的 HID 设备,可以定义多个接口,只有其中一个接口为 HID 设备类即可。

        当定义一个设备为 HID 设备时,其设备描述符应为:          

    bDeviceClass=0

    bDeviceSubClass=0

    bDeviceProtocol=0

    其接口描述符应该:

    bInterfaceClass=0x03

    另外(接口描述符)

    对无引导的 HID 设备,子类代码 bInterfaceSubClass 应置 0,此时 bInterfaceProtocol 无效,置零即可。即为:

    bInterfaceClass=0x03

    bInterfaceSubClass=0

    bInterfaceProtocol=0

          对支持引导的 USB 设备,子类代码 bInterfaceSubClass 应置 1,此时 bInterfaceProtocol 可以为 1 或 2, 1 表示键盘接口, 3 表示鼠标接口。其参考设置如下:

    bInterfaceClass=0x03

    bInterfaceSubClass=1

    bInterfaceProtocol=1 或 2

           HID 设备支持 USB 标准描述符中的五个:设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符。除此之外, HID 设备还有三种特殊的描述符: HID 描述符、报告描述符、物理描述符。一个 USB 设备只能支持一个 HID 描述符,但可以支持多个报告描述符,而物理描述符则可以有也可以没有。

    3.2 HID 描述符

    HID 描述符用于识别 HID 设备中所包含的额外描述符,例如报告描述符或物理描述符等。

    其格式如下:

    字段名

    长度/字节

    地址偏移量

    说明

    bLength

    1

    0

    报告描述符长度(字节)

    bDescriptorType

    1

    1

    描述符类型: 0x21

    bcdHID

    2

    2

    HID版本号(BCD 码)

    bCountryCode

    1

    4

    国家/地区代码

    bNumDescriptor

    1

    5

    支持的其他类型描述符数量

    bDescriptorType

    1

    6

    类别描述符的类型

    wDescriptorLength

    2

    7

    报告描述符的总长度

    bDescriptorType

    1

    9

    用于识别描述符类型的常数

    wDescriptorLength

    2

    10

    描述符的总长度

    各字段含义如下:

    1. bLength: HID 描述符长度。
    2. bDescriptorType: HID 描述符类型,值为 0x21。
    3. bcdHID: HID 设备所遵循的 HID 版本号,为 4 位 16 进制的 BCD 码数据。 1.0 即 0x0100, 1.1 即 0x01
    4. bCountryCode: HID 设备国家/地区代码。
    5. bNumDescriptor: HID 设备支持的其01, 2.0即 0x0200。他设备描述符的数量。由于 HID 设备至少需要包括一个报告描述符,故其值至小为 0x01。
    6. bDescriptorType: HID 描述符附属的类别描述符长度。
    7. bDescriptorType/wDescriptorLength:可选字段,用于表示 HID 描述符附属的类别描述符类型及长度。

    3.3 报告描述符

           HID 设备的报告描述符是一种数据报表,主要用于定义 HID 设备和 USB 主机之间的数据交换格式, HID 设备报告描述符的类型值为 0x22。 

         报告描述符使用自定义的数据结构,用于传输特定的数据包。

        例如: 对于键盘,需要在数据包中指明按键的值,报告描述符把这些数据打包发给主机,主机对发来的数据进行处理。 它有四个组成部分, 其格式如下:

    0 ~1     2~ 3     4 5 6 7      8~ 23          (共24位,占4个字节)

    bSize    bType     bTag      [data]

    各字段含义:

    1. bSize:占用两个位, 指示数据部分,即[data]字段的长度, 00b表没有数据字节, 01b表只有一个数据字节, 10b 表示有两个数据字节, 11b 表有 4 个数据字节。
    2. bType:数据项类型,用于指明数据项的类型。 00b主数据类型, 01b全局数据类型, 10b局部数据类型, 11b保留。
    3. bTag:数据项标签,用于指明数据项的功能。报告描述符需要包含的数据项标签有:输入输出数据项标签、用法数据项标签、用法页数据项标签、逻辑最小和最大值数据项标签、报告大小数据项标签以及报告计数数据项标签。
    4. [data]:数据字节,随着前面 bSize 定义的大小而变化。

    3.4 物理描述符

         HID 设备的物理描述符主要用于报告物理设备的激活信息,其类型值为 0x23,它是可选的,对大部分设备不需要使用此描述符。

    四、 linux内核下USB相关的API函数与数据结构

            前面介绍了USB相关一些基础概念与重要的数据结构,接下来就分析在linux内核中如何编写一个USB 驱动程序,编写与一个USB设备驱动程序的方法和其他总线驱动方式类似,驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否安装了硬件。当然,这些制造商和设备标识需要我们编写进USB 驱动程序中。

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

    USB构成框图:

     4.1 USB设备注册与注销

    //注册USB设备
    #define usb_register(driver) \
    	usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
    
    //注销USB设备
    void usb_deregister(struct usb_driver *driver)
    
    //需要添加的头文件
    #include <linux/usb.h>
    

    4.2 USB设备注册框架示例

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/usb.h>
    //定义USB的IDTAB
    static const struct usb_device_id usbtest[] = {
    	{//148f:760b
    		USB_DEVICE(0x148f, 0x760b),	/*360WIFI的制造商ID和产品ID */
    	},
    };
    
    //USB设备信息与驱动端匹配成功的时候调用。
    static int test_probe(struct usb_interface *intf,const struct usb_device_id *id)  //资源探索函数
    {
    		printk("USB 驱动匹配成功!\n");
    		return 0;
    }
    
    //USB断开的时候调用
    static void test_disconnect(struct usb_interface *intf)
    {
    		printk("USB 设备释放成功!\n"); 
    }
    //定义USB驱动结构体 
    static struct usb_driver usbtest_driver = {
    	.name = "linux_usb_drv",
    	.id_table = usbtest,
    	.probe = test_probe,
    	.disconnect = test_disconnect
    };
    
    static int __init usbtest_init(void)
    {
    	//注册USB设备驱动
    	usb_register(&usbtest_driver);
    	return 0;
    }
    module_init(usbtest_init);
    static void __exit usbtest_exit(void)
    {
    	//注销USB设备驱动
    	usb_deregister(&usbtest_driver);
    }
    module_exit(usbtest_exit);
    MODULE_AUTHOR("xiaolong");
    MODULE_LICENSE("GPL");
    

    4.3 struct usb_driver数据结构

    struct usb_driver {
    	const char *name;   //USB设备的名字,名称可以随便填写。
    	int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);   //资源探索函数,当usB驱动端与设备端匹配成功的时候调用。
    	void (*disconnect) (struct usb_interface *intf);  //USB设备断开时调用。
    	const struct usb_device_id *id_table;  //USB设备的匹配ID列表
    ……………..
    };
    

    列出的为必填成员。

    struct usb_device_id 设备ID结构原型如下:

    struct usb_device_id {
    	/* 确定设备信息去和结构体中哪几个字段匹配来判断驱动的适用性,比如是否是HID协议等*/
    	__u16		match_flags;
    
    	/*用于特定于产品的匹配 */
    	__u16		idVendor;  /*USB设备的制造商ID,须向www.usb.org申请*/
    	__u16		idProduct;  // USB设备的产品ID,有制造商自定
    	__u16		bcdDevice_lo; /* USB设备的产品版本号最低值*/
    	__u16		bcdDevice_hi; /* 和最高值,以BCD码来表示。*/
    
    	/* 分别定义设备的类,子类和协议,他们由 USB 论坛分配并定义在 USB 规范中. 
    这些值指定这个设备的行为, 包括设备上所有的接口 */ 
    	__u8		bDeviceClass;
    	__u8		bDeviceSubClass;
    	__u8		bDeviceProtocol;
    
    	/*  分别定义单个接口的类,子类和协议,他们由 USB 论坛分配并定义在 USB 规范中 */
    	__u8		bInterfaceClass;
    	__u8		bInterfaceSubClass;
    	__u8		bInterfaceProtocol;
    
    	/* 这个值不用来匹配驱动的, 驱动用它来在 USB 驱动的探测回调函数中区分不同的设备 
          该成员一般来保存一个结构体指针,存放该设备特殊的数据
    */
    	kernel_ulong_t	driver_info;
    

     填充struct usb_device_id结构体中的__u16  match_flags成员用到的宏定义:

    #define USB_DEVICE_ID_MATCH_VENDOR		   0x0001    /*供应商厂家ID*/
    #define USB_DEVICE_ID_MATCH_PRODUCT		0x0002    /*产品ID*/
    

     快速填充usb_device_id结构体的相关宏:

    USB_DEVICE_ID_MATCH_INT_INFO            --根据接口信息
    USB_DEVICE_ID_MATCH_DEV_INFO           --根据设备的信息
    USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION --根据设备制造信息和版本
    USB_DEVICE_ID_MATCH_DEV_RANGE         --根据设备版本
    USB_DEVICE_ID_MATCH_DEVICE             --根据设备制造信息
    

     struct usb_device_id结构体填充示例1—(摘自DM9620-USB网卡)

    static const struct usb_device_id products[] = {
    	{
    		USB_DEVICE(0x07aa, 0x9601),	/* Corega FEther USB-TXC */
    		.driver_info = (unsigned long)&dm9620_info,
    	}, {
    		USB_DEVICE(0x0a46, 0x9601),	/* Davicom USB-100 */
    		.driver_info = (unsigned long)&dm9620_info,
    	},
    }
    

     struct usb_device_id结构体填充示例2------->(摘自内核自带的鼠标驱动)

    static struct usb_device_id usb_mouse_id_table [] = {
    	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
    		USB_INTERFACE_PROTOCOL_MOUSE) },
    	{ }	/* 结束 */
    };
    

    USB鼠标USB驱动端结构体填充示例:

    static struct usb_driver usb_mouse_driver = {
    	.name		= "usbmouse",         /*鼠标的名称-不是匹配使用*/
    	.probe		= usb_mouse_probe,    /*资源探测函数-匹配成功调用*/
    	.disconnect	= usb_mouse_disconnect,/*当鼠标断开连接调用*/
    	.id_table	= usb_mouse_id_table,       /*匹配驱动使用*/
    };
    

    4.4 获取当前设备信息

    struct usb_device *interface_to_usbdev(struct usb_interface *intf)

    用法示例:

    static int usb_driver_probe(struct usb_interface *intf, const struct usb_device_id *id)
    {
    	struct usb_device *dev = interface_to_usbdev(intf);  /*设备与驱动匹配成功时,在_probe 函数里获取设备的信息*/
    }
    

    4.5 创建数据传输管道

         管道是USB设备通信的通道,内核中提供了创建管道的宏,从以下内核定义宏中我们可以分析出,管道是一个 int 型的变量,由设备号、端点地址、端点类型组合而成。

    //控制CONTROL 
    #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) //输入
    
    //中断INTERRUPT 
    #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)//输入
    
    //批量BULK
    #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)//输入
    
    //等时ISOCHRONOUS 
    #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)//输入
    
    static inline unsigned int __create_pipe(struct usb_device *dev,unsigned int endpoint)
    {
    	return (dev->devnum << 8) | (endpoint << 15);
    }
    

         端点是有四种的,对应着管道也就有四种,同时端点是有IN也有OUT,相应的管道也就有两个方向,于是二四得八,上面就出现了八个创建管道的宏。有了struct usb_device结构体,也就是说知道了设备地址,再加上端点号,就可以根据需要创建指定的管道。__create_pipe宏只是一个幕后的角色,用来将设备地址和端点号放在管道正确的位置上。

    示例:

    struct usb_device *dev = interface_to_usbdev(intf);
    struct usb_host_interface *interface;
    struct usb_endpoint_descriptor *endpoint;	/* 定义端点描述符 */
    interface = intf->cur_altsetting;
    endpoint = &interface->endpoint[0].desc;
    int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);  /*创建管道*/
    

    4.6 动态分配urb

         urb(USB Request Block)Linux内核中USB驱动实现上的一个数据结构,用于组织每一次的USB设备驱动的数据传输请求。

    struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
     @iso_packets:这个urb的iso包的数量
    @mem_flags:要分配的内存类型,请参阅kmalloc()以获取列表
    

    示例:

    static struct urb *myurb= usb_alloc_urb(0, GFP_KERNEL);

    4.7 urb数据结构初始化(控制) 

    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)
    

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

    4.9 urb数据结构初始化(批量)

    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)
    

    4.10 urb数据结构初始化(等时)

     等时urb 没有和中断、控制、批量urb 类似的初始化函数,因此它们在提交到USB核心之前,需要在驱动程序中手动的初始化。例如:

    urb->dev = dev;
    urb->context = uvd;
    urb->pipe = usb_rcvisocpipe(dev,uvd->video_endp-1);
    urb->interval = 1;
    urb->transfer_flags = URB_IOS_ASAP;
    urb->transfer_buffer = can->sts_buf[i];
    urb_complete = konicawc_isoc_irq;
    urb->number_of_packets = FRAMES_PRE_DESC;
    urb->transfer_buffer_lenth = FRAMES_PRE_DESC;
    for (j=0; j < FRAMES_PRE_DESC; j++){
        urb->ios_frame_desc[j].offset  = j;
        urb->ios_frame_desc[j].length  = 1;
    }
    

    五、编写USB鼠标驱动(中断传输方式)

    5.1 USB驱动注册框架代码

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/usb.h>
    
    /*
    [   25.845000] usb 1-2.2: new high-speed USB device number 6 using s5p-ehci
    [   25.950000] usb 1-2.2: New USB device found, idVendor=0661, idProduct=294b
    [   25.950000] usb 1-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
    [   25.950000] usb 1-2.2: Product: EZ-USB
    [   25.950000] usb 1-2.2: Manufacturer: Cypress
    
    [  726.360000] usb 1-2.2: new high-speed USB device number 7 using s5p-ehci
    [  726.475000] usb 1-2.2: config 1 interface 0 altsetting 0 has 7 endpoint descriptors, different from the interface descriptor's value: 5
    [  726.480000] usb 1-2.2: New USB device found, idVendor=148f, idProduct=5370
    [  726.480000] usb 1-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
    [  726.480000] usb 1-2.2: Product: 802.11 n WLAN
    [  726.480000] usb 1-2.2: Manufacturer: Ralink
    [  726.485000] usb 1-2.2: SerialNumber: 1.0
    */
    
    //定义USB的IDTAB
    static const struct usb_device_id tiny4412_usb_id[] = 
    {
        {USB_DEVICE(0x148f,0x5370)},
        {USB_DEVICE(0x0661,0x294b)},
        {}
    };
    
    /*
    MODULE_DEVICE_TABLE 有两个功能。
    一是:将设备加入到外设队列中,
    二是告诉程序阅读者该设备是热插拔设备或是说该设备支持热插拔功能。
    该宏定义在<linux/module.h>下
    这个宏有两个参数,第一个参数设备名,第二个参数该设备加入到模块中时对应产生的设备搜索符号,这个宏生成了一个名为__mod_pci_device_table
    局部变量,这个变量指向第二个参数
    */
    MODULE_DEVICE_TABLE (usb,tiny4412_usb_id);
    
    //USB设备信息与驱动端匹配成功的时候调用。
    static int test_probe(struct usb_interface *intf,const struct usb_device_id *id)  //资源探索函数
    {
        printk("tiny4412 probe success .\n");
        return 0;
    }
    
    //USB断开的时候调用
    static void test_disconnect(struct usb_interface *intf)
    {
        printk("tiny4412 usb drv disconnect success.\n"); 
    }
    
    //定义USB驱动结构体 
    static struct usb_driver tiny4412_usb_driver = {
        .name = "tiny4412_usb_drv",
        .id_table = tiny4412_usb_id,
        .probe = test_probe,
        .disconnect = test_disconnect
    };
    
    static int __init tiny4412_usb_init(void)
    {
        //注册USB设备驱动
        usb_register(&tiny4412_usb_driver);
        printk("tiny4412 usb drv install success.\n"); 
        return 0;
    }
    
    static void __exit tiny4412_usb_exit(void)
    {
         //注销USB设备驱动
         usb_deregister(&tiny4412_usb_driver);
         printk("tiny4412 usb drv exit success.\n"); 
    }
    
    module_init(tiny4412_usb_init);
    module_exit(tiny4412_usb_exit);
    MODULE_AUTHOR("xiaolong");
    MODULE_LICENSE("GPL");
    

    运行示例: 拔插USB WIFI 弹出的提示信息。

    [root@wbyq code]# insmod linux_usb_drv.ko 
    [   19.160000] usbcore: registered new interface driver tiny4412_usb_drv
    [   19.160000] tiny4412 usb drv install success.
    [root@wbyq code]# [   25.430000] usb 1-2.2: new high-speed USB device number 5 using s5p-ehci
    [   25.545000] usb 1-2.2: config 1 interface 0 altsetting 0 has 7 endpoint descriptors, different from the interface descriptor's value: 5
    [   25.550000] usb 1-2.2: New USB device found, idVendor=148f, idProduct=5370
    [   25.550000] usb 1-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
    [   25.550000] usb 1-2.2: Product: 802.11 n WLAN
    [   25.550000] usb 1-2.2: Manufacturer: Ralink
    [   25.555000] usb 1-2.2: SerialNumber: 1.0
    [   25.570000] tiny4412 probe success .
    

    5.2 编写USB鼠标驱动

    [root@wbyq linux-3.5]# make menuconfig

    由于内核自带了usb鼠标驱动,所以需要去除:

    Device Drivers  ---> 
    	  HID support  --->
    	  	 USB HID support  --->	
    	  	 		<> USB HID transport layer    //HID传输层
    

    去掉之后,再重新编译内核,烧写内核。

    鼠标驱动代码: 该模板适用于键盘驱动。

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/usb.h>
    #include <linux/usb/input.h>
    #include <linux/hid.h>
    
    /*
    本程序为USB鼠标驱动程序,要安装本驱动,需要先将内核自带的USB驱动程序卸载掉
    */
    
    //定义USB的IDTAB 24ae:2002
    static const struct usb_device_id tiny4412_usb_id[] = {
    	{//148f:7601
    		USB_DEVICE(0x148f,0x7601),/*360WIFI的制造商ID和产品ID */
    		USB_DEVICE(0x24ae,0x2002),/*当前鼠标的ID*/
    	},
    };
    
    //USB鼠标的ID
    static struct usb_device_id usb_mouse_id[] = {
    	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
    		USB_INTERFACE_PROTOCOL_MOUSE) },
    	{ }	/* 终止进入 */
    };
    
    int size;
    static unsigned char *buf =NULL;
    static struct urb *myurb=NULL;
    dma_addr_t buf_phy;
    
    /*USB中断处理程序*/
    static void usb_complete(struct urb *urb)
    {
    	int i;
    	for(i=0;i<size;i++)
    	{
    		printk("0x%x ",buf[i]);
    	}
    	printk("\n");
    	
    	/* 重新提交异步请求*/
    	usb_submit_urb(myurb, GFP_KERNEL);
    }
    
    //USB设备信息与驱动端匹配成功的时候调用。
    static int usb_probe(struct usb_interface *intf,const struct usb_device_id *id)
    {
    	printk("USB驱动匹配成功! ID: 0x%X,0x%X\n",id->idVendor,id->idProduct);
    
    	/*通过接口获取设备信息*/
    	struct usb_device *dev = interface_to_usbdev(intf);
    	/*获取当前接口设置*/
    	struct usb_host_interface *interface=intf->cur_altsetting;
    	/*获取端点描述符*/
    	struct usb_endpoint_descriptor *endpoint = &interface->endpoint[0].desc;
    	/*中断传输:创建输入管道*/
    	int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
    	/*从端点描述符中获取传输的数据大小 */
    	size = endpoint->wMaxPacketSize;
    	printk("设备传输数据包大小:%d\n",size);
    	/*分配数据传输缓冲区*/
    	buf = usb_alloc_coherent(dev,size,GFP_ATOMIC,&buf_phy);
    	/*分配新的urb,urb是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构*/
    	myurb = usb_alloc_urb(0,GFP_KERNEL);
    	/*中断方式初始化urb*/
    	usb_fill_int_urb(myurb,dev,pipe,buf,size,usb_complete,NULL,endpoint->bInterval);
    	myurb->transfer_dma = buf_phy;
    	myurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
    	/*为端点提交异步传输请求*/
    	usb_submit_urb(myurb, GFP_KERNEL);	
    	return 0;
    }
    
    //USB断开的时候调用
    static void usb_disconnect(struct usb_interface *intf)
    {
    	struct usb_device *dev = interface_to_usbdev(intf);
    	usb_kill_urb(myurb);
    	usb_free_urb(myurb);
    	usb_free_coherent(dev,size,buf, buf_phy);
    	printk("USB 设备释放成功!\n"); 
    }
    
    //定义USB驱动结构体 
    static struct usb_driver tiny4412_usb_driver = {
    	.name = "tiny4412_usb",
    	.id_table = usb_mouse_id,
    	.probe = usb_probe,
    	.disconnect = usb_disconnect
    };
    
    static int __init tiny4412_usb_init(void)
    {
    	//注册USB设备驱动
    	usb_register(&tiny4412_usb_driver);
    	return 0;
    }
    
    static void __exit tiny4412_usb_exit(void)
    {
    	 //注销USB设备驱动
    	 usb_deregister(&tiny4412_usb_driver);
    }
    
    module_init(tiny4412_usb_init);
    module_exit(tiny4412_usb_exit);
    MODULE_AUTHOR("xiaolong");
    MODULE_LICENSE("GPL");
    

     

    六、编写USB光谱仪驱动(批量传输方式)

          USB光谱仪的USB接口支持使用批量方式传输数据,当前程序里使用的是同步方式提交请求。

    整体驱动思路:

    (1). 在驱动层里先定义光谱仪设备的设备ID和厂商ID,当设备插入时,ID匹配成功,就会调用probe函数,在probe函数里完成设备信息探测,比如: 端点数据传输方向,数据传输大小,传输方式等等。

    探测成功后,就注册一个字符设备,创建设备节点,方便应用程序调用驱动完成设备控制。

    (2).  驱动层向应用层提供了read和write接口函数,方便根据预先定义的结构体进行数据通信。

       具体情况可以参考 –下面的应用层程序示例代码。

    6.1 头文件定义支持的命令

    #ifndef SPECTROMETER_H
    #define SPECTROMETER_H
    #define Get_Module_Information  0x01000000UL
    #define Get_Spectrometer_Information    0x02000000UL
    #define Get_Module_Property 0x03000000UL
    #define Get_Data_Position_Property  0x04000000UL
    #define Get_Data_Count_Property 0x05000000UL
    #define Get_Data_Transmit_Property  0x06000000UL
    #define Get_Data_Trigger_Offset_Property    0x07000000UL
    #define Get_Exposure_Property   0x08000000UL
    #define Get_Gain_Property   0x08000000UL
    #define Get_Ad_Offset_Property  0x0A000000UL
    #define Get_Wavelength_Property 0x0C000000UL
    #define Get_Image_Size  0x0D000000UL
    #define Get_Capture_Mode    0x0F000000UL
    #define Set_Capture_Mode    0x10000000UL
    #define Get_Data_Position   0x11000000UL
    #define Set_Data_Position   0x12000000UL
    #define Get_Data_Count  0x13000000UL
    #define Set_Data_Count  0x14000000UL
    #define Get_Data_Transmit   0x15000000UL
    #define Set_Data_Transmit   0x16000000UL
    #define Get_Data_TriggerOffset  0x17000000UL
    #define Set_Data_TriggerOffset  0x18000000UL
    #define Get_Exposure_Time   0x19000000UL
    #define Set_Exposure_Time   0x1A000000UL
    #define Get_Exposure_Cycle  0x1B000000UL
    #define Set_Exposure_Cycle  0x1C000000UL
    #define Get_Trigger_Mode    0x1D000000UL
    #define Set_Trigger_Mode    0x1E000000UL
    #define Get_Trigger_Polarity    0x1F000000UL
    #define Set_Trigger_Polarity    0x20000000UL
    #define Get_Trigger_Output  0x21000000UL
    #define Set_Trigger_Output  0x22000000UL
    #define Get_Gain    0x25000000UL
    #define Set_ain 0x26000000UL
    #define Get_Ad_Offset   0x27000000UL
    #define Set_Ad_OffSet   0x28000000UL
    #define Get_Calibration_Coefficient 0x29000000UL
    #define Set_Calibration_Coefficient 0x3B000000UL
    #define Get_Cooling_Temperature 0x2A000000UL
    #define Capture_Start   0x2F000000UL
    #define Fire_Trigger    0x30000000UL
    #define Capture_Stop    0x31000000UL
    #define Read_Eeprom_User_Area   0x33000000UL
    #define Write_Eeprom_User_Area  0x34000000UL
    #define Get_Status_Request  0x3D000000UL
    #define Get_Defective_Pixel 0x3E000000UL
    
    #pragma pack(1)
    struct DEV_CMD
    {
        unsigned char buff[500];  //读写数据缓冲区
        unsigned int write_len;  //写数据长度
        unsigned int read_len;   //读数据长度
    };
    
    #define IOCTL_CMD_RW 0x39654128
    #endif
    

    6.2 应用层程序示例

    打开光谱仪设备节点,获取设备的信息或者设置设备信息。

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/ioctl.h>
    #include <stdlib.h>
    #include "spectrometer_cmd_list.h"
    #include <string.h>
     #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    #define DEV_NAME "/dev/spectrometer_usb_drv"
    
    int main(int argc,char **argv)
    {
        /*1. 打开设备文件*/
        int fd=open(DEV_NAME,2);
        if(fd<0)
        {
            printf("%s 设备文件打开失败.\n",DEV_NAME);
            return 0;
        }
        
        /*2. 读写光谱仪设备*/
        struct DEV_CMD dev_cmd;
        memset(&dev_cmd,0,sizeof(struct DEV_CMD));
        dev_cmd.write_len=8;  //写数据长度
        dev_cmd.read_len=500; //读数据长度,如果只想写不想读. 这里长度填0即可.
        //写入的命令.赋值  01 00 00 00
        dev_cmd.buff[0]=0x01;
        dev_cmd.buff[1]=0x00;
        dev_cmd.buff[2]=0x00;
        dev_cmd.buff[3]=0x00;
        dev_cmd.buff[4]=0x00;
        dev_cmd.buff[5]=0x00;
        dev_cmd.buff[6]=0x00;
        dev_cmd.buff[7]=0x00;
        //提交数据
        if(ioctl(fd,IOCTL_CMD_RW,&dev_cmd))
        {
            printf("ioctl执行失败.\n");
        }
        else
        {
            printf("写成功: %d 字节.\n",dev_cmd.write_len);
            if(dev_cmd.read_len>0)
            {
                printf("读成功: %d 字节.\n",dev_cmd.read_len);
                printf("读取数据如下:\n");
                int i;
                for (i = 0; i < dev_cmd.read_len; i++)
                {
                    printf("%#x ",dev_cmd.buff[i]);
                }
                printf("\n");
            }
        }
        
        //关闭设备
        close(fd);
        return 0;
    }
    

    6.3 支持的命令详情

    Contents

    RequestCommand
    (4Byte)

    DataSize
    (4Byte)

    Data

    Note

    Get Module Information

    0x00000001

    0x00000000

     

     

     

     

     

     

     

     

    Get Spectrometer Information

    0x00000002

    0x00000000

     

     

     

     

     

     

     

     

    Get Module Property

    0x00000003

    0x00000000

     

     

     

     

     

     

     

     

    Get Data Position Property

    0x00000004

    0x00000000

     

     

     

     

     

     

     

     

    Get Data Count Property

    0x00000005

    0x00000000

     

     

     

     

     

     

     

     

    Get Data Transmit Property

    0x00000006

    0x00000000

     

     

     

     

     

     

     

     

    Get Data Trigger Offset Property

    0x00000007

    0x00000000

     

     

     

     

     

     

     

     

    Get Exposure Property

    0x00000008

    0x00000000

     

     

     

     

     

     

     

     

    Get Gain Property

    0x00000009

    0x00000000

     

     

     

     

     

     

     

     

    Get Ad Offset Property

    0x0000000A

    0x00000000

     

     

     

     

     

     

     

     

    Get Wavelength Property

    0x0000000C

    0x00000000

     

     

     

     

     

     

     

     

    Get Image Size

    0x0000000D

    0x00000000

     

     

     

     

     

     

     

     

    Get Capture Mode

    0x0000000F

    0x00000000

     

     

     

     

     

     

     

     

    Set Capture Mode

    0x00000010

    0x00000002

    Capture Mode
    (2Byte)

     

     

     

     

     

     

    Capture Mode
    0x0000 : Specified number of measurement mode
    0x0001 : Continuous measurement mode
    0x0002 : Trigger mode

    Get Data Position

    0x00000011

    0x00000000

     

     

     

     

     

     

     

     

    Set Data Position

    0x00000012

    0x00000004

    Data Position
    (4Byte)

     

     

     

     

     

     

     

    Get Data Count

    0x00000013

    0x00000000

     

     

     

     

     

     

     

     

    Set Data Count

    0x00000014

    0x00000004

    Data Count
    (4Byte)

     

     

     

     

     

     

     

    Get Data Transmit

    0x00000015

    0x00000000

     

     

     

     

     

     

     

     

    Set Data Transmit

    0x00000016

    0x00000004

    Data Transmit
    (4Byte)

     

     

     

     

     

     

     

    Get Data TriggerOffset

    0x00000017

    0x00000000

     

     

     

     

     

     

     

     

    Set Data TriggerOffset

    0x00000018

    0x00000004

    Trigger Offset
    (4Byte)

     

     

     

     

     

     

     

    Get Exposure Time

    0x00000019

    0x00000000

     

     

     

     

     

     

     

     

    Set Exposure Time

    0x0000001A

    0x00000008

    Exposure Time
    (8Byte)

     

     

     

     

     

     

     

    Get Exposure Cycle

    0x0000001B

    0x00000000

     

     

     

     

     

     

     

     

    Set Exposure Cycle

    0x0000001C

    0x00000008

    Exposure Cycle
    (8Byte)

     

     

     

     

     

     

     

    Get Trigger Mode

    0x0000001D

    0x00000000

     

     

     

     

     

     

     

     

    Set Trigger Mode

    0x0000001E

    0x00000004

    Trigger Mode
    (4Byte)

     

     

     

     

     

     

    Trigger Mode
    0x00000000 : Internal synchronous mode
    0x00000001 : software asynchronous mode
    0x00000002 : software synchronous mode
    0x00000003 : External asynchronous edge sense mode
    0x00000004 : External asynchronous level sense mode
    0x00000005 : External synchronous edge sense mode
    0x00000006 : External synchronous level sense mode
    0x00000007 : External synchronous pulse mode

    Get Trigger Polarity

    0x0000001F

    0x00000000

     

     

     

     

     

     

     

     

    Set Trigger Polarity

    0x00000020

    0x00000004

    Trigger Polarity
    (4Byte)

     

     

     

     

     

     

    Trigger Polarity
    0x00000001 : Falling edge or Low level
    0x00000003 : Rising edge or High level

    Get Trigger Output

    0x00000021

    0x00000000

     

     

     

     

     

     

     

     

    Set Trigger Output

    0x00000022

    0x00000004

    Trigger Output
    (4Byte)

     

     

     

     

     

     

    Trigger Output
    0x00000001 : OFF
    0x00000003 : ON

    Get Gain

    0x00000025

    0x00000004

    Gain Mode
    (4Byte)

     

     

     

     

     

     

    Gain Mode
    0x00000000 : Sensor gain mode
    0x00000001 : Circuit gain mode

    Set Gain

    0x00000026

    0x00000008

    Gain Mode
    (4Byte)

    Gain
    (4Byte)

     

     

     

     

     

    Gain Mode
    0x00000000 : Sensor gain mode
    0x00000001 : Circuit gain mode

    Gain
    0x00000000 : Low gain
    0x00000001 : High gain

    Get Ad Offset

    0x00000027

    0x00000000

     

     

     

     

     

     

     

     

    Set Ad OffSet

    0x00000028

    0x00000004

    Ad Offset
    (4Byte)

     

     

     

     

     

     

     

    Get Calibration Coefficient

    0x00000029

    0x00000004

    Index
    (4Byte)

     

     

     

     

     

     

    Index
    0x00000000 : Wavelength calibration coefficient
    0x00000001 : Sensitivity calibration coefficient

    Set Calibration Coefficient

    0x0000003B

    0x00000034

    Index
    (4Byte)

    Coefficient A0
    (8Byte)

    Coefficient B1
    (8Byte)

    Coefficient B2
    (8Byte)

    Coefficient B3
    (8Byte)

    Coefficient B4
    (8Byte)

    Coefficient B5
    (8Byte)

    Index
    0x00000000 : Wavelength calibration coefficient
    0x00000001 : Sensitivity calibration coefficient

    Get Cooling Temperature

    0x0000002A

    0x00000000

     

     

     

     

     

     

     

     

    Capture Start

    0x0000002F

    0x00000002

    Capture Mode
    (2Byte)

     

     

     

     

     

     

     

    Fire Trigger

    0x00000030

    0x00000000

     

     

     

     

     

     

     

     

    Capture Stop

    0x00000031

    0x00000000

     

     

     

     

     

     

     

     

    Read Eeprom User Area

    0x00000033

    0x00000004

    Address
    (2Byte)

    Length
    (2Byte)

     

     

     

     

     

     

    Write Eeprom User Area

    0x00000034

    0x00000008

    Address
    (2Byte)

    Length
    (2Byte)

    Data
    (2Byte)

     

     

     

     

    The address that can be specified is 0-511.
    Please specify 384 henceforth, when you use it.
    Spectroscope may stop operating correctly.

    Address will be specified in word.

    Get Status Request

    0x0000003D

    0x00000000

     

     

     

     

     

     

     

    Cooled only

    Get Defective Pixel

    0x0000003E

    0x00000000

     

     

     

     

     

     

     

     

    6.4 驱动层程序示例

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/usb.h>
    #include <linux/sched.h>
    #include <linux/slab.h>
    #include <linux/miscdevice.h>
    #include <linux/fs.h>
    #include <linux/io.h>
    #include "spectrometer_cmd_list.h"
    #include <asm/uaccess.h>
    #include <linux/bcd.h>
    #include <linux/uaccess.h>
    
    /*容纳所有设备特定内容的结构 */
    struct usb_spectrometer
    {
        struct usb_device   *udev;          /* 此设备的USB设备 */
        struct usb_interface    *interface; /*该设备的接口*/
        struct usb_anchor   submitted;      /* 万一需要撤回提交*/
        struct urb      *bulk_in_urb;       /*用urb读取数据*/
        unsigned char           *bulk_in_buffer;    /* 接收数据的缓冲区 */
        size_t          bulk_in_size;       /*接收缓冲区的大小 */
        size_t          bulk_in_filled;     /* 缓冲区中的字节数 */
        size_t          bulk_in_copied;     /* 已经复制到用户空间 */
        __u8            bulk_in_endpointAddr;   /* 端点中的批量地址 */
        __u8            bulk_out_endpointAddr;  /*批量输出端点的地址 */
        int             errors;         /* 最后一个请求被取消 */
        bool            ongoing_read;       /* 读正在进行*/
        bool            processed_urb;      /* 表示尚未处理 */
    };
    static struct usb_spectrometer *dev;
        
    /*
    [   25.845000] usb 1-2.2: new high-speed USB device number 6 using s5p-ehci
    [   25.950000] usb 1-2.2: New USB device found, idVendor=0661, idProduct=294b
    [   25.950000] usb 1-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
    [   25.950000] usb 1-2.2: Product: EZ-USB
    [   25.950000] usb 1-2.2: Manufacturer: Cypress
    
    [  726.360000] usb 1-2.2: new high-speed USB device number 7 using s5p-ehci
    [  726.475000] usb 1-2.2: config 1 interface 0 altsetting 0 has 7 endpoint descriptors, different from the interface descriptor's value: 5
    [  726.480000] usb 1-2.2: New USB device found, idVendor=148f, idProduct=5370
    [  726.480000] usb 1-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
    [  726.480000] usb 1-2.2: Product: 802.11 n WLAN
    [  726.480000] usb 1-2.2: Manufacturer: Ralink
    [  726.485000] usb 1-2.2: SerialNumber: 1.0
    */
    
    //定义USB的IDTAB
    static const struct usb_device_id tiny4412_usb_id[] = 
    {
        {USB_DEVICE(0x148f,0x5370)},
        {USB_DEVICE(0x0661,0x294b)},
        {}
    };
    
    /*
    MODULE_DEVICE_TABLE 有两个功能。
    一是:将设备加入到外设队列中,
    二是告诉程序阅读者该设备是热插拔设备或是说该设备支持热插拔功能。
    该宏定义在<linux/module.h>下
    这个宏有两个参数,第一个参数设备名,第二个参数该设备加入到模块中时对应产生的设备搜索符号,这个宏生成了一个名为__mod_pci_device_table
    局部变量,这个变量指向第二个参数
    */
    
    MODULE_DEVICE_TABLE (usb,tiny4412_usb_id);
    
    static int usb_dev_open(struct inode *inode, struct file *file)
    {
        printk("open:USB光谱仪设备.\n");
        printk("命令结构大小_drv:%lu\n",sizeof(struct DEV_CMD));
        return 0;
    }
    
    //读写命令
    static long usb_dev_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long argv)
    {
        //转换指针类型
        struct DEV_CMD *buff=(struct DEV_CMD *)argv;
        struct DEV_CMD dev_cmd;
        int ret=0;
        int actual_length=0;
        int err=0;
        printk("读写:USB光谱仪设备.\n");
    
        //命令符合要求
        if(IOCTL_CMD_RW==cmd)
        {
            //拷贝应用层的数据到本地
            if(copy_from_user(&dev_cmd,buff,sizeof(struct DEV_CMD)))
            {
                printk("write: copy_from_user error...\n");
                return -2;
            }
    
            /*同步提交写请求*/
            ret=usb_bulk_msg(dev->udev,
                        usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
                        dev_cmd.buff,dev_cmd.write_len,&actual_length,HZ*20);
            if(ret<0)
            {
                printk("同步提交写请求错误.错误值:%d\n",ret);
                return -3;
            }
            dev_cmd.write_len=actual_length; //成功写入的长度
            printk("write:len=%d\n",actual_length);
    
            //读取的长度大于0.就表示需要读取数据
            if(dev_cmd.read_len>0)
            {
                    //buff清0
                memset(dev_cmd.buff,0,sizeof(dev_cmd.buff));
                /*同步提交读请求*/
                ret = usb_bulk_msg(dev->udev, 
                                usb_rcvbulkpipe(dev->udev,dev->bulk_in_endpointAddr),
                                dev_cmd.buff,dev_cmd.read_len, &actual_length,HZ*20);
                if(ret<0)
                {
                    printk("同步提交读请求错误.错误值:%d\n",ret);
                    return -4;
                }
                //实际读取的长度
                dev_cmd.read_len=actual_length;
                printk("read:len=%d\n",actual_length);
                
                //将数据拷贝到应用层
                err=copy_to_user(buff,&dev_cmd,sizeof(struct DEV_CMD));
                if(err)
                {
                    printk("read:read cmd error!!!\n");
                    return err;
                }
            }
        }
        return 0;
    }
    
    static int usb_dev_release(struct inode *inode, struct file *file)
    {
        printk("release:USB光谱仪设备.\n");
        return 0;
    }
    
    static const struct file_operations usb_dev_fops = {
        .owner = THIS_MODULE,
        .unlocked_ioctl=usb_dev_unlocked_ioctl,
        .open  = usb_dev_open,
        .release = usb_dev_release,
    };
    
    static struct miscdevice usb_dev_miscdev = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "spectrometer_usb_drv",
        .fops = &usb_dev_fops,
    };
    
    //USB设备信息与驱动端匹配成功的时候调用。
    static int test_probe(struct usb_interface *interface,const struct usb_device_id *id)  //资源探索函数
    {
        int i=0;
        size_t buffer_size;
        struct usb_device *dev_info;
        unsigned char *bcdUSB_p;
        struct usb_host_interface *host_inter;
        struct usb_endpoint_descriptor *endpoint;
        int size;
        
        dev=kzalloc(sizeof(*dev), GFP_KERNEL);
        dev->udev = usb_get_dev(interface_to_usbdev(interface));
        dev->interface = interface;
    
        printk("识别到USB光谱仪设备,正在进行设备初始化.\n");
    
        /*通过接口获取设备信息*/
        dev_info = interface_to_usbdev(interface);
        bcdUSB_p=(unsigned char *)&dev_info->descriptor.bcdUSB;
        printk("设备与描述表兼容的USB设备说明版本号=0x%x%x\n", bcd2bin(*bcdUSB_p),bcd2bin(*(bcdUSB_p+1)));   //从USB设备描述符中获取USB版本
        printk("厂商ID = %#x\n",dev_info->descriptor.idVendor); //从USB设备描述符中获取厂商ID
        printk("设备ID = %#x\n",dev_info->descriptor.idProduct);//从USB设备描述符中获取产品ID
    
        printk("设备类 = %#x\n",interface->cur_altsetting->desc.bInterfaceClass);   //从USB设备获取设备类
        printk("设备从类 = %#x\n",interface->cur_altsetting->desc.bInterfaceSubClass);//从USB设备获取设备从类
        printk("设备协议 = %#x\n",interface->cur_altsetting->desc.bInterfaceProtocol);//从USB设备获取设备协议
        printk("驱动名称:%s\n",interface->dev.driver->name);
        printk("总线名称:%s\n",interface->dev.driver->bus->name);
        
        /*获取当前接口设置*/
        host_inter=interface->cur_altsetting;
        /*获取端点描述符*/
        for(i=0;i<host_inter->desc.bNumEndpoints;i++) 
        {
            endpoint = &host_inter->endpoint[i].desc;
            printk("端点号[%d]:%d\n",i,endpoint->bEndpointAddress&0xFF);
            if(endpoint->bEndpointAddress&1<<7)
            {
                printk("端点[%d] 输入端点(设备到主机)\n",i);
            }
            else
            {
                printk("端点[%d] 输出端点(主机到设备)\n",i);
            }
            switch(endpoint->bmAttributes)
            {
                case 0:printk("端点[%d] 设备支持控制传输.\n",i);break;
                case 1:printk("端点[%d] 设备支持同步传输.\n",i);break;
                case 2:printk("端点[%d] 设备支持批量传输.\n",i);break;
                case 3:printk("端点[%d] 设备支持中断传输.\n",i);break;
            }
            /*从端点描述符中获取传输的数据大小 */
            size = usb_endpoint_maxp(endpoint);
            printk("端点[%d] 传输的数据大小:%d\n",i,size);
    
            //输入端点
            if(!dev->bulk_in_endpointAddr &&usb_endpoint_is_bulk_in(endpoint)) 
            {
                /* 批量输入端点 */
                buffer_size = usb_endpoint_maxp(endpoint);
                dev->bulk_in_size = buffer_size;
                dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
                printk("probe:dev->bulk_in_size=%lu\n",dev->bulk_in_size);
                printk("probe:dev->bulk_in_endpointAddr=%d\n",dev->bulk_in_endpointAddr);
                dev->bulk_in_buffer = kmalloc(buffer_size,GFP_KERNEL);
                if(!dev->bulk_in_buffer)
                {
                    printk("无法分配bulk_in_buffer");
                    break;
                }
                dev->bulk_in_urb = usb_alloc_urb(0,GFP_KERNEL);
                if(!dev->bulk_in_urb)
                {
                    printk("无法分配bulk_in_urb");
                    break;
                }
            }
            
            //输出端点
            if(!dev->bulk_out_endpointAddr &&usb_endpoint_is_bulk_out(endpoint))
            {
                /*  批量输出端点 */
                dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
                printk("probe:dev->bulk_out_endpointAddr=%d\n",dev->bulk_out_endpointAddr);
            }
        }
        
        /*向内核注册一个杂项字符设备*/
        if(misc_register(&usb_dev_miscdev)==0)
        {
            printk("USB光谱仪设备节点注册成功:/dev/%s ,主设备号:10,次设备号:%d\n",
                usb_dev_miscdev.name,usb_dev_miscdev.minor);    
        }
        return 0;
    }
    
    //USB断开的时候调用
    static void test_disconnect(struct usb_interface *intf)
    {
        printk("USB光谱仪设备已断开.\n"); 
        /*从内核注销一个杂项字符设备*/
        misc_deregister(&usb_dev_miscdev);
    }
    
    //定义USB驱动结构体 
    static struct usb_driver tiny4412_usb_driver = {
        .name = "spectrometer_usb_drv",
        .id_table = tiny4412_usb_id,
        .probe = test_probe,
        .disconnect = test_disconnect
    };
    
    static int __init tiny4412_usb_init(void)
    {
        printk("正在安装USB光谱仪驱动.\n"); 
        //注册USB设备驱动
        usb_register(&tiny4412_usb_driver);
        return 0;
    }
    
    static void __exit tiny4412_usb_exit(void)
    {
         //注销USB设备驱动
         usb_deregister(&tiny4412_usb_driver);
         printk("USB光谱仪驱动卸载成功.\n"); 
    }
    
    module_init(tiny4412_usb_init);
    module_exit(tiny4412_usb_exit);
    MODULE_AUTHOR("xiaolong");
    MODULE_LICENSE("GPL");
    

    展开全文
  • 本篇是Linux USB驱动系列的开篇,先讲硬件基础,后续会深入驱动架构和代码分析。本系列专注研究适用于嵌入式设备USB OTG控制和应用广泛的USB2.0协议。

    · 作为Linux驱动的学习者,我看到USB驱动的时候,真的是有种大海捞针的感觉。USB驱动代码太大,且层次复杂,难以理解。这其实是由于USB协议仍然在不断发展,我们的内核工程师还没有找到对应的规律来提供更简洁的架构给我们使用,但是我们仍然要尽力去理解这些源码的由来,本人刚好遇到的这方面的问题,想将自己总结的经验分享出来,降低大家的学习难度。
    · 在学习USB驱动开发的过程中,我遇到了很多困难,首先是讲USB驱动的书比较少,例如我的枕边书:宋宝华老师的《linux设备驱动开发详解》中USB驱动只有47页,而使用广泛的OTG设备只有区区3页,这点我非常理解,作为一本将整个驱动方面的书,在某一个点上不能过分展开。而几位大牛写的《linux那些事儿之我是USB》非常对我的胃口,但是很可惜,没有讲到OTG协议,因此本人就自告奋勇来把这一块梳理一遍,帮助大家尽快上手。
    废话不多说,本篇先讲硬件基础,后续会深入代码分析。本系列只研究适用于嵌入式设备USB OTG控制器和应用广泛的USB2.0协议。
    · 首先介绍一下OTG协议,知道的可以跳过此段,传统的USB设备区分为USB主机和USB设备,对应的USB主机控制器和USB设备控制器也区分明确,但是这些只适用于PC这种大型设备,对于更灵活嵌入式设备来说,有时候想当主机,有时候想当设备,比如我们的手机既可以做主机接摄像头、U盘等设备,也可以作为设备角色接电脑,怎么办呢,OTG协议应运而生,它使得在不用改变接线方式,就可以实现USB主机和设备之间的角色切换。
    · 下面干货来了:

    1.OTG设备的线缆区别

    · 与传统USB2.0的4芯线缆不同,OTG设备采用micro-USB的5芯线缆,相比于USB2.0,多了一根ID线,依据ID引脚状态来判断角色是什么设备。当ID是高电平,就是B设备,要ID是低电平,就是A设备。
    而且约定了当OTG设备连接到USB主机时,只能工作在USB设备模式。
    · 比如我们手机充电线可以用来连电脑,所以这micro-USB接口中的ID引脚肯定是高电平,这样microUSB就能兼容USB2.0,因为手机连电脑必然手机是从机。而有的OTG设备想一直作为A设备,可以固定
    · 把这个引脚置为低电平,然后其他引脚组成USB2.0的4芯接口。

    2.OTG设备的协议区别

    2.1 角色
    · OTG使用了两种角色:A设备和B设备,通过ID引脚来区别,每一组线缆两端规定了一个为A设备,一个为B设备。后续通过状态转换图再更细致了解。

    2.2 协议
    · OTG协议中包含了3个兼容的协议,包括会话请求协议(SRP)、主机角色协商协议(HNP)、连接检测协议(ADP)。这3个协议不用过多理解,通过后续的状态迁移图理解用处就行。

    2.3 状态迁移
    · 将OTG的状态迁移之前,先将USB2.0的状态迁移解释一下。内容都在图中,粗体是解释状态的,其它是解释过程的。
    在这里插入图片描述
    图1 USB2.0状态迁移图(加粗表示状态解释,不加粗是过程解释)
    · 由上图可以看到,usb设备真是个爱节约的好宝宝,特别注重省电,只要上电,要是没活干,就立刻挂起。
    · 在USB2.0中,状态变迁都是由主机发起的请求所触发;
    · 下面要讲到OTG各个状态的迁移,我们要知道OTG协议是兼容USB2.0的,所以需要将上面的图和下图结合来看,进一步深入理解:
    在这里插入图片描述
    图2 USB OTG状态迁移图(图中的顿号表示或)

    · 上图准确描述的各个状态迁移的条件,由此可以知道OTG设备非常的灵活,既可以作为A设备,也可以作为B设备,AB设备都可以作为主机进行传输,只不过A设备在大部分时期起到主导作用,而B设备需要在转换为主机模式前,发送HNP请求,这就是OTG最重要的部分。而SRP协议和ADP协议只用于A设备和B设备的角色切换和初始化阶段。
    · 在基本了解了上述的图之后,对于我们驱动开发就基本足够了,如果对协议细节和硬件要求感兴趣的同学,可以查询本文的参考资料。
    · 若本文有所遗漏或各位对本文有所疑问,欢迎各位留言!

    参考资料:
    《微控制器USB的信号和协议实现》工业和信息化部人才交流中心 恩智浦(中国)管理有限公司 编著

    展开全文
  • YJ USB Serial Driver r2.00.7z YouJie USB Serial Driver r2.00 This Youjie USB Driver package - Non-Microsoft WHQL Certified, supports the following device: YJ4600, GS550, HF600, HH360, HF500, HH660 ...
  • 新大陆扫描_USB转虚拟串口的驱动 新大陆扫描_USB转虚拟串口的驱动
  • STM32移植USB驱动总结

    万次阅读 多人点赞 2019-03-12 17:58:43
    stm32自带USB接口,OTG-FS(全速)和OTG-HS(高速),因为stm32f4只带有高速PHY,想使用高速模式,就需要外扩高速PHY,在此为USB3300。 系统配置一个Device端口,一个Host端口; Device端口连接主机,在此处为POS机...
    //=========================By xiaowei
    /*
    
    */
    //=========================
    

    1、硬件介绍

    1、SGM7227,USB高速切换开关,OE是芯片使能,低电平才能使总线导通;
    S脚是切换控制;
    在这里插入图片描述


    USB协议

    https://segmentfault.com/a/1190000015995506

    2、软件移植

    移植文件分析

    stm32自带USB接口,OTG-FS(全速)和OTG-HS(高速),因为stm32f4只带有高速PHY,想使用高速模式,就需要外扩高速PHY,在此为USB3300。
    系统配置一个Device端口,一个Host端口;

    Device端口连接主机,在此处为POS机,外接USB3300作为高速PHY;
    Host端口连接打印进,使用了STM32内置的全速PHY;

    移植时,我们重点要修改的就是 USB_APP 文件夹下面的代码。其他代码(USB_OTG 和USB_DEVICE 文件夹下的代码)一般不用修改。
    需要修改的文件:

    usb_bsp.c 提供了几个 USB 库需要用到的底层初始化函数,包括: IO 设置、中断设置、VBUS配置以及延时函数等,需要我们自己实现。 USB Device(Slave)和 USB Host 共用这个.c 文件。

    usbd_desc.c 提供了 USB 设备类的描述符,直接决定了 USB 设备的类型、断点、接口、字符串、制造商等重要信息。这个里面的内容,我们一般不用修改,直接用官方的即可。注意,这里: usbd_desc.c 里面的: usbd 即 device 类,同样: usbh 即 host 类,所以通过文件名我们可以很容易区分该文件是用在 device 还是 host,而只有 usb 字样的那就是 device 和 host 可以共用的。

    usbd_usr.c 提供用户应用层接口函数,即 USB 设备类的一些回调函数,当 USB 状态机处理完不同事务的时候,会调用这些回调函数,我们通过这些回调函数,就可以知道 USB 当前状态,比如:是否枚举成功了?是否连接上了?是否断开了?等,根据这些状态,用户应用程序可以执行不同操作,完成特定功能。

    usbd_storage_msd.c 提供一些磁盘操作函数,包括支持的磁盘个数,以及每个磁盘的初始化和读写等函数。本章我们设置了 2 个磁盘: SD 卡和 SPI FLASH。

    这些文件都是以回调函数的方式被内核调用,需要用户编写具体的应用程序;

    使用时,需要定义 USB_OTG_CORE_HANDLE ,是一个全局结构体类型,用于存储 USB 通信中 USB 内核需要使用的的各种变量、状态和缓存等,任何 USB 通信(不论主机,还是从机),我们都必须定义这么一个结构体以实现 USB 通信。
    USB 初始化非常简单,只需要调用 USBD_Init 函数即可,本章的 USB 读卡器属于 USB 设备类,所以使用该函数。该函数初始化了USB 设备类处理的各种回调函数,以便 USB 驱动库调用。执行完该函数以后, USB 就启动了,所有 USB 事务,都是通过 USB 中断触发,并由 USB 驱动库自动处理。

    USB设备需要存储介质,需要用户定义存储介质驱动。
    USB驱动会自己调用处理函数进行处理,用户需要改动的是底层的驱动,即磁盘的读写;具体在usbd_storage_msd.c里面,
    磁盘回调函数结构体:

    USBD_STORAGE_cb_TypeDef USBD_MICRO_SDIO_fops =
    {
      STORAGE_Init,
      STORAGE_GetCapacity,
      STORAGE_IsReady,
      STORAGE_IsWriteProtected,
      STORAGE_Read,
      STORAGE_Write,
      STORAGE_GetMaxLun,
      (int8_t *)STORAGE_Inquirydata,
    };
    

    读操作:

    int8_t STORAGE_Read (uint8_t lun, 
                     uint8_t *buf, 
                     uint32_t blk_addr,                       
                     uint16_t blk_len)
    {
    
    	buf=&U_DISK[blk_addr*512];
      return 0;
    }
    

    USB启动阶段请求

    这个博客分析的挺好:
    https://www.cnblogs.com/Daniel-G/p/3993883.html

    USB内核处理

    USB_OTG_CORE_HANDLE 是全局变量,

    • 用于存储 USB 通信中 USB 内核需要使用的的各种变量、状态和缓存等,任何 USB 通信(不论主机,还是从机),我们都必须定义这么一个结构体以实现 USB 通信,
    typedef struct USB_OTG_handle
    {
      USB_OTG_CORE_CFGS    cfg;
      USB_OTG_CORE_REGS    regs;
    #ifdef USE_DEVICE_MODE
      DCD_DEV     dev;
    #endif
    #ifdef USE_HOST_MODE
      HCD_DEV     host;
    #endifdda
    #ifdef USE_OTG_MODE
      OTG_DEV     otg;
    #endif
    }
    USB_OTG_CORE_HANDLE , *PUSB_OTG_CORE_HANDLE;
    

    USB_OTG_CORE_HANDLE分析:

    这是个全局结构体,内部存储内核处理USB设备,主机的变量;

    当USB作为Device设备时,USB_OTG_CORE_HANDLE定义为DCD_DEV ;
    其参数如下:

    typedef struct _DCD
    {
      uint8_t        device_config;
      uint8_t        device_state;
      uint8_t        device_status;
      uint8_t        device_old_status;
      uint8_t        device_address;
      uint8_t        connection_status;  
      uint8_t        test_mode;
      uint32_t       DevRemoteWakeup;
      USB_OTG_EP     in_ep   [USB_OTG_MAX_TX_FIFOS];
      USB_OTG_EP     out_ep  [USB_OTG_MAX_TX_FIFOS];
      uint8_t        setup_packet [8*3];
      USBD_Class_cb_TypeDef         *class_cb;
      USBD_Usr_cb_TypeDef           *usr_cb;
      USBD_DEVICE                   *usr_device;  
      uint8_t        *pConfig_descriptor;
     }
    DCD_DEV , *DCD_PDEV;
    

    DCD_DEV 结构说明:

    DCD_DEV结构包含用于实时保存与设备,控制传输状态机以及端点信息和状态相关的所有信息的所有变量和结构。

    中断处理
    • 所有USB的事件都是通过中断触发,并由内核自动处理。
      在此处USB_OTG_CORE_HANDLE USB_OTG_Core;用来作为主机结构;
      __ALIGN_BEGIN USB_OTG_CORE_HANDLE USB_OTG_dev __ALIGN_END ;用来作为从机设备;

    OTG概念

    On-The-Go,即OTG技术就是实现在没有Host的情况下,实现设备间的数据传送。例如数码相机直接连接到打印机上,通过OTG技术,连接两台设备间的USB口,将拍出的相片立即打印出来;也可以将数码照相机中的数据,通过OTG发送到USB接口的移动硬盘上,野外操作就没有必要携带价格昂贵的存储卡,或者背一个便携电脑。

    3、USB作为Device设备

    usbd_req和usbd_ioreq

    文件描述:该文件处理USB事务的结果。

    usb_dcd和usb_dcd_int

    这两个文件为USB库提供设备接口层封装;
    其中usb_dcd文件包含以下函数:

    DCD_EP_PrepareRx
    DCD_EP_Tx
    DCD_EP_Stall
    DCD_EP_ClrStall
    DCD_EP_Flush

    USB OTG低电平驱动器的DCD层有一个必须通过USB中断调用的功能(高速或全速):
    DCD_Handle_ISR (USB_OTG_CORE_HANDLE *pdev)

    usb_dcd_int文件说明:


    在这里插入图片描述
    上图是USB设备库构架;
    如上图所示,USB设备库由两个主要部分组成:库核心和类驱动程序
    库核心包括三个主要部分:
    USB device core
    USB requests
    USB I/O requests
    在这里插入图片描述
    USB设备库处理流程图
    上图的分析:

    分为4个模块,
    上层应用;
    设备库和设备类
    底层驱动
    上层应用调用USBD_init()函数来调用USBD库;同时应用层还为用户提供了回调函数接口,在调用USBD库时调用;
    上层应用与设备类的交互是通过USBD_Class_cb来调用的,从上一张图中可知,USB的设备类分为很多种,HID,audio,MSC等等;
    上层应用与底层驱动的交互是通过几个中断函数联系的;

    MSC设备类驱动

    MSC设备类包含文件:

    usbd_msc_core ----- MSC类的核心处理文件
    usbd_msc_bot -------bot协议封装
    usbd_msc_scsi----------SCSI指令封装

    MSC(Mass storage class)
    USB大容量存储类是围绕仅批量传输(BOT)构建的。 它使用SCSI透明命令集。
    SCSI指令集:
    在这里插入图片描述

    4、伪装成打印机

    usbd_desc.c 提供了 USB 设备类的描述符,直接决定了 USB 设备的类型、断点、接口、字
    符串、制造商等重要信息。
    在这里,需要将单片机伪装成打印机,因此需要封装其USB信息。
    需要讲解一个概念:USB设备描述符;

    USB设备描述符

    说到USB设备,不得不提到各种描述符(descriptors), 一般来说,描述符有如下几种:

    1:设备描述符(Device Descriptors)
    2:配置描述符(Configuration Descriptors)
    2:接口描述符(Interface Descriptors)
    3:端点描述符(Endpoint Descriptors)
    

    他们之间的关系如下图所示:
    在这里插入图片描述

    图片说明:设备描述符定义了配置数多少,配置描述符又定义了端口多少,一层层向下定义。

    USB描述符信息存储在USB设备中,在枚举过程中,USB主机会向USB设备发送GetDescriptor请求,
    USB设备在收到这个请求之后,会将USB描述符信息返回给USB主机,USB主机分析返回来的数据,判断出该设备是哪一种USB设备,建立相应的数据链接通道。

    设备描述符包含信息:

      描述符(Descriptor )是一个完整的数据结构, 存储在USB 设备中, 用于描述一个USB 设备的所有属性。USB主机通过一系列命令要求设备发送这些信息。
      USB 设备的属性包括很多内容, 为了便于管理, USB2.0协议将这些信息做了分类, 定义了多种描述符,包括标准和HID类的描述符,下面是标准描述符描述。
    标准描述符

    标准描述符bDescriptorType字段
    设备描述符(Device Descriptor)0x01
    配置描述符(Configuration Descriptor)0x02
    字符串描述符(String Descriptor,可选)0x03
    接口描述符(Interface Descriptor)0x04
    端点描述符(Endpoint Descriptor)0x05
    设备限定描述符(DEVICE_QUALIFIER)0x06
    其他速率配置描述符(OTHER_SPEED_CONFIGURATION)0x07
    接口功率描述符(INTERFACE_POWER)0x08

    描述符间的关系

    这里写图片描述
    一个设备有且只能有一个设备描述符,之后的描述符都允许有多个不同的描述符。

    设备描述符(Device Descriptor)

    • 每一个设备有且只有一个设备描述符
    • 默认控制管道的数据包的长度( 端点0长度)是在设备描述符中定义, 而不像其他端点在端点描述符中定义。
    • 14 个字段,18 Byte
    偏移量字段名称长度(Byte)字段值意义
    0bLength10x12设备描述符的字节数大小
    1bDescriptorType10x01设备描述符类型编号
    2bcdUSB2协议版本2.31其值就是0x0231USB版本号
    4bDeviceClass1USB分配设备类代码(1-FE)FF为厂商自定义
    5bDeviceSubClass1子类USB分配子类代码
    6bDeviceProtocol2协议USB分配设备协议代码
    7bMa xPack et Size01控制传输端点0包大小端点0最大包大小
    8idVendor2ID编号厂商编号
    10idProduct2ID编号产品编号
    12bcdDevice2BCD码设备出厂编号
    14iManufacturer1索引描述厂商字符串索引
    15iProduct1索引描述产品字符串的索引
    16iSerialNumber1索引描述设备序列号字符串的索引
    17b NumConf igur at ions1配置描述符个数可能配置数量

    配置描述符(Configuration Descriptor)

    • 8 个字段,9Byte
    • bmAttributes字段:D7保留值, 固定为1 ; D6表示供电方式, 1表示自供电, 0表示总线供电; D5,值为1 表示支持远程唤醒, 值为0 则不支持;D4~D0没有意义, 固定为0。
    偏移量字段名称长度(字节)字段值意义
    0bLength10x09配置描述符的字节数大小
    1bDescriptorType10x02配置描述符类型编号
    2wTotalLength2数字这是一个以2 字节二进制数为内容的字段。该字段表示该配置所返回的所有描述符( 包括配置、接口和端点描述符) 的大小总和。
    4bNumInterfaces1接口描述符个数此配置所支持的接口数量
    5bConfigurationValue1数字Set_Configuration /Get_Configuration命令需要的参数值
    6iConfiguration1索引描述该配置的字符串的索引值
    7bmAttributes1位图供电模式的选择,第7位D7保留固定为1;D6值为1表示自供电,值为0表示总线供电; D5值为1表示支持远程唤醒,值为0则不支持;D4~D0没有意义固定为0
    8MaxPower1字段值*2(mA)设备从总线提取的最大电流(<=500mA)

    接口描述符(Interface Descriptor)

    • 9 个字段,共9 字节
    • 只能作为配置描述符的一部分返回,不能用GetDescriptor()或SetDescriptor()直接访问。
    偏移量字段名称长度(字节)字段值意义
    0bLength10x09接口描述符的字节数大小
    1bDescriptorType10x04接口描述符类型编号
    2bInterfaceNumber1数字该接口编号
    3bAlternateSetting1数字
    4bNumEndpoints1如果为0则说明只用了端点0该接口使用的端点数 ,不包括端点0
    5bInterfaceClass1接口类型
    6bInterfaceSubClass1子类接口子类类型
    7bInterfaceProtocol1协议接口遵循的协议
    8iInterface1索引描述该接口的字符串索引值
    • bInterfaceNumber:如果一个配置拥有N个接口, 那么这些接口都是互不相干的, 每一个接口都有惟一的编号, USB 就是通过此字段来识别不同的接口。默认值为0。
    • bAlternateSetting:USB设备配置与USB配置描述符是一一对应的, 即一个配置只能有一个配置描述符。虽然由bInterfaceNumber字段可知, 每一个接口都有一个惟一确定的接口编号, 但是一个接口却可以由不只一个接口描述符来描述它。USB 允许多个接口描述符来描述同一个接口, 且这些描述符都可通过命令切换。此字段就是每一个这类描述符惟一的编号。USB可通过调用这个字段来切换描述同一个接口的不同描述符。控制传输中的Get_Inter face 命令可以用来得到目前正在使用的描述一个确定接口的接口描述符的编号, 即此字段。而Set_Inte rface 命令则以此字段值为参数, 用来使相应的接口描述符描述某个确定的接口

    端点描述符(Endpoint Descriptor)

    用于接口的每个端点都有自己的描述符。 此描述符包含信息
    需要主机确定每个端点的带宽要求。 端点描述符为始终由GetDescriptor(获取配置描述符),作为配置信息的一部分返回。 端点描述符不能直接用GetDescriptor()或SetDescriptor()直接访问。对于端点零,不存在端点描述符。

    • 6 个字段共7 字节
    • 描述接口所使用的非0 端点的属性, 包括输入/ 输出方向、端点号和端点容量即包的大小等
    偏移量字段名称长度(字节)字段值意义
    0bLength10x07端点描述符的字节数大小
    1bDescriptorType10x05端点描述符类型编号
    2bEndpointAddress1端点端点地址及输入输出属性
    3bmAttributes1位图端点的传输类型属性
    4wMaxPacketSize2数字端点收、发的最大包的大小
    6bInterval1数字主机查询端点的时间间隔

    伪装成打印机的设备描述符

    __ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] __ALIGN_END =
      {
        0x12,                       /*bLength */
        0x01,                       /*bDescriptorType*/
        0x00,                       /*bcdUSB */																	  // usb 版本			 2
        0x02,																																			//							 
    
        0x00,                       /*bDeviceClass*/
        0x00,                       /*bDeviceSubClass*/
        0x00,                       /*bDeviceProtocol*/
    
        USB_OTG_MAX_EP0_SIZE,      /*bMaxPacketSize*/															// 传输包大小			7
    
        LOBYTE(USBD_VID),           /*idVendor*/																	// 厂商编码				 8
        HIBYTE(USBD_VID),           /*idVendor*/
        LOBYTE(USBD_PID),           /*idVendor*/																	// 产品编码				 10
        HIBYTE(USBD_PID),           /*idVendor*/
        0x00,                       /*bcdDevice rel. 2.00*/												// 设备出厂编号		 12
        0x02,
    
        USBD_IDX_MFC_STR,           /*Index of manufacturer  string*/							// 厂商字符串索引			14
        USBD_IDX_PRODUCT_STR,       /*Index of product string*/										// 产品字符串索引			15
        0x00,        /*Index of serial number string*/				  		              // 设备序列号字符串索引			16
    		
        USBD_CFG_MAX_NUM            /*bNumConfigurations*/												// 配置描述符个数					 17
      } ; /* USB_DeviceDescriptor */
    
    
    • 2、其他描述符
      并且,在usbd_msc_core.c文件中,定义了配置描述符,接口描述符和端点描述符。
      在此文件下定义了打印机的设备描述符:
    __ALIGN_BEGIN uint8_t USBD_MSC_CfgDesc[USB_MSC_CONFIG_DESC_SIZ] __ALIGN_END =
    {  
    	//---------- 配置描述符 ----------
      0x09,   /* bLength: Configuation Descriptor size */
      0x02,   /* bDescriptorType: Configuration */
      0x20,  																								  // 返回数值大小
      0x00,
      0x01,   /* bNumInterfaces: 1 interface */
    
      0x01,   /* bConfigurationValue: */
      0x05,   /* iConfiguration: */					                  // 描述配置的字符索引	USBD_MSC_CfgDesc[6]
      0xC0,   /* bmAttributes: */
      0x32,   /* MaxPower 100 mA */
    	//--------------------------------
      
      //---------- 接口描述符 ----------
      0x09,   /* bLength: Interface Descriptor size */
      0x04,   /* bDescriptorType: */
      0x00,   /* bInterfaceNumber: Number of Interface */
      0x00,   /* bAlternateSetting: Alternate setting */
      0x02,   /* bNumEndpoints*/
      
      // jz-80 打印机型号
      0x07,   /* bInterfaceClass: MSC Class */               //  接口 类型			        USBD_MSC_CfgDesc[14]
      0x01,   /* bInterfaceSubClass : SCSI transparent*/		 //  接口 子类型						USBD_MSC_CfgDesc[15]
      0x02,   /* nInterfaceProtocol */											 //  接口 协议							USBD_MSC_CfgDesc[16]
    
      0x04,          /* iInterface: */											 //  接口描述字符串索引 		USBD_MSC_CfgDesc[17]
    	//--------------------------------
      
    	//---------- 端点1描述符 ----------
      0x07,   /*Endpoint descriptor length = 7*/
      0x05,   /*Endpoint descriptor type */
      MSC_IN_EP,   /*Endpoint address (IN, address 1) */		 // USBD_MSC_CfgDesc[20]
      0x02,   /*Bulk endpoint type */
      LOBYTE(MSC_MAX_PACKET),																 // USBD_MSC_CfgDesc[22]
      HIBYTE(MSC_MAX_PACKET),																 // USBD_MSC_CfgDesc[23]
      0x00,   /*Polling interval in milliseconds */
    	//---------------------------------
    
    	//---------- 端点1描述符 ----------  
      0x07,   /*Endpoint descriptor length = 7 */
      0x05,   /*Endpoint descriptor type */
      MSC_OUT_EP,   /*Endpoint address (OUT, address 1) */	 // USBD_MSC_CfgDesc[27]
      0x02,   /*Bulk endpoint type */
      LOBYTE(MSC_MAX_PACKET),																 // USBD_MSC_CfgDesc[29]
      HIBYTE(MSC_MAX_PACKET),																 // USBD_MSC_CfgDesc[30]
      0x00     /*Polling interval in milliseconds*/
    	//---------------------------------
    };
    

    PID和VID

    概念:

    PID/VID唯一标识一个设备,HardwareID是为了给系统识别的 ,他是根据PID/VID而生成的。这个与序列号没什么关系,序列号一般都是厂家固化到芯片中的信息而已。GUID只是为了标志你安装的设备是属于一个什么类当中,这个类可以显示再设备管理器中。比如:你可以定义一个类,当然这个类有与系统中任何类都不同的GUID,然后选择一个图标和类名,就可以同网卡等其他设备一起显示在设备管理器下的根目录中了

    根据USB规范的规定,所有的USB设备都有供应商ID(VID)和产品识别码(PID),主机通过不同的VID和PID来区别不同的设备,VID和PID都是两个字节长。
    其中,供应商ID(VID)由供应商向USB执行论坛申请,每个供应商的VID是唯一的,PID由供应商自行决定。
    所以理论上一个USB存储设备的VID应该是设备生产商的VID,而不是主控生产商的VID,这两个VID应该是不同的(主控生产商自己生产的设备除外)。

    作为Host主机

    函数分析

    USB_Host
    存储主机状态结构;
    使用 USB 主机的时候,需要两个结构体: USB_OTG_CORE_HANDLE和 USB_HOST。

    然后, USB 初始化,使用的是 USBH_Init,用于 USB 主机初始化,包括对 USB 硬件和 USB驱动库的初始化。如果是: USB SLAVE 通信,在只需要调用 USBD_Init 函数即可,不过 USBHOST 则还需要调用另外一个函数
    USBH_Process,
    该函数用于实现 USB 主机通信的核心状态机处理,该函数必须在主函数里面,被循环调用,而且调用频率得比较快才行(越快越好),以便及时处理各种事务。
    注意, USBH_Process 函数仅在 U 盘识别阶段,需要频繁反复调用,但是当 U 盘被识别后,剩下的操作(U 盘读写),都可以由 USB 中断处理。

    POS发送信息处理

    拦截POS信息

    在usb_dcd_int.c文件中,修改了USBD_OTG_EP1OUT_ISR_Handler中断,可以看到里面调用函数StorePosData将数据从xfer_buff中压缩存储到PosData.frompos_buf中,也就是压缩进DataFromPosBuf中。

    			if((PosData.status == 0)||(PosData.status == 1))
    			{
    				StorePosData(pdev->dev.out_ep[1].xfer_buff,pdev->dev.out_ep[1].xfer_count);
    
    				if(PosData.status == 0)
    					PosData.status = 1;
    			}
    	 
        }
    		 
        /* Inform upper layer: data ready */
        /* RX COMPLETE */
        USBD_DCD_INT_fops->DataOutStage(pdev , 1);
    

    发送给设备的信息

    在usbh_msc_bot.c文件中,更改了USBH_MSC_HandleBOTXfer函数,其中检测到USBH_MSC_SEND_CBW状态,调用USBH_BulkSendData将数据发送给设备;

    //USBH_MSC_HandleBOTXfer函数处理:
       switch (USBH_MSC_BOTXferParam.BOTState)
        {
        	case USBH_MSC_SEND_CBW:
    
    	  
    	  		USBH_BulkSendData (pdev,
            	                   PosData.frompos_buf, 
              	              	   PosData.datalen , 
                	               MSC_Machine.hc_num_out);
    	  
          	USBH_MSC_BOTXferParam.BOTStateBkp = USBH_MSC_SEND_CBW;
          	USBH_MSC_BOTXferParam.BOTState = USBH_MSC_SENT_CBW;
          
          break;      
    //
    
    

    USBH_BulkSendData 函数定义:

    pdev ------选中的设备
    buff---------发送的数据
    length---------发送的数据长度
    hc_num------------主机发送通道

    分析此部分函数,可以了解到数据从pos到打印机的过程;
    首先pos发送的数据被stm32拦截,压缩存储在DataFromPosBuf中,然后通过中断,将指令发送给打印机。

    Host用户回调函数

    usbh_usr.c里面定义了作为主机的用户回调函数:
    这里讲解几个重要的函数:
    1、USBH_USR_Device_DescAvailable

    //检测到从机的描述符
    //DeviceDesc:设备描述符指针
    void USBH_USR_Device_DescAvailable(void *DeviceDesc)
    { 
    	USBH_DevDesc_TypeDef *hs;
    	hs=DeviceDesc;   
    	printf("VID: %04Xh\r\n" , (uint32_t)(*hs).idVendor); 
    	printf("PID: %04Xh\r\n" , (uint32_t)(*hs).idProduct); 
    }
    

    这里当连接打印机后,检测打印机的设备描述符,再将VID和PID打印出来,因此我们在设置Device设备描述符时,按照这个参数设置;
    VID: 0416h
    PID: 5011h

    2、USBH_USR_Manufacturer_StringUSBH_USR_Product_StringUSBH_USR_SerialNum_String
    这三个函数用来从打印机获得其设备描述符信息;

    diskio接口函数

    diskio里需要用户自定义接口,对存储磁盘进行读写等;
    在此处,将打印机作为一个外接磁盘,提供接口函数:

    USBH_UDISK_Status
    USBH_UDISK_Read
    USBH_UDISK_Write

    1、usbd_storage_msd.c函数分析

    usbd_storage_msd.c是设备作为Device的数据存储相关函数,在此文件中需要用户自定义存储空间,读写操作等回调函数。
    在.c文件中定义了虚拟磁盘,封装了用户读写函数;

    2、Pos数据状态

    自定义了接收pos机数据的状态结构体;

    //POS信息;
    typedef struct DATA_FROM_pos
    {
      uint8_t     status;                  // 状态   0: 空闲     1:正在获取数据    2:获取完成,等待打印    3:正在打印    4:打印完成、等待转移
    	                                      
    	//-----------------------------------------------------------
    	uint8_t     *frompos_buf;            // 收到的数据缓冲区
    	uint32_t    buflen;                  // 压缩后的长度
    
    	uint32_t    datalen;                 // 压缩前的长度
    
    	uint8_t     zerototal;               // 正在压缩的 0x00 的数量
    	//-----------------------------------------------------------  
    	uint8_t needprint;                   // 需要打印
    
    	//-----------------------------------------------------------
    	uint8_t     *upzipPointer;           // 解压指针,指向需要继续解压的位置,主缓冲区	
    
    	uint8_t     *sbuf;                   // 发送数据缓冲区
    	uint8_t     sbuflen;                 // 发送数据长度
    
    	uint8_t     *unzipbuf;               // 解压缓冲区
    	uint8_t    unziplen;                 // 解压区现存数据长度
    
    	uint8_t    cmdtype;									 // 命令类型
    	uint8_t    delayziplen;							 // 等待一些数据再开始进行压缩
    	//-----------------------------------------------------------
    }DATA_FROM_POS;
    
    
    结构体成员分析

    1、status
    status是一个标志位,用来标识接收数据的状态;

    // 状态
    0: 空闲
    1:正在获取数据
    2:获取完成,等待打印
    3:正在打印
    4:打印完成、等待转移

    其中 status改变为1时,是在USBD_OTG_EP1OUT_ISR_Handler中改变的状态;
    USBD_OTG_EP1OUT_ISR_Handler如果定义#ifdef USB_OTG_HS_DEDICATED_EP1_ENABLED才能使用;
    找到usb_conf.h文件;
    在usb_conf.h文件中,注释掉了原文件的宏定义,自己定义了宏;
    因为单片机模拟打印机时,使用的是高速PHY,因此USB OTG HS CONFIGURATION对应是Device的驱动定义。

       /****************** USB OTG HS CONFIGURATION **********************************/
    #ifdef USB_OTG_HS_CORE
     #define RX_FIFO_HS_SIZE                          512
     #define TX0_FIFO_HS_SIZE                         128
     #define TX1_FIFO_HS_SIZE                         372
     #define TX2_FIFO_HS_SIZE                          0
     #define TX3_FIFO_HS_SIZE                          0
     #define TX4_FIFO_HS_SIZE                          0
     #define TX5_FIFO_HS_SIZE                          0
     #define TXH_NP_HS_FIFOSIZ                        256
     #define TXH_P_HS_FIFOSIZ                         256
    
    //  #define USB_OTG_HS_LOW_PWR_MGMT_SUPPORT		  //  old 注释//
    //  #define USB_OTG_HS_SOF_OUTPUT_ENABLED			  //  old 注释
    
     #ifdef USE_ULPI_PHY
      #define USB_OTG_ULPI_PHY_ENABLED
     #endif
     #ifdef USE_EMBEDDED_PHY
       #define USB_OTG_EMBEDDED_PHY_ENABLED
     #endif
     #define USB_OTG_HS_INTERNAL_DMA_ENABLED
     #define USB_OTG_HS_DEDICATED_EP1_ENABLED
     #define USB_OTG_EXTERNAL_VBUS_ENABLED
    #endif
    
    展开全文
  • Windows USB驱动程序,用于Sony DualShock控制 :warning: 该项目不再维护。 它已被取代。 :warning: 概括 FireShock由自定义USB用户模式驱动程序和处理与Sony DualShock 3控制进行有线通信的。 它允许第三方...
  • (一)USB驱动程序_USB基础知识

    千次阅读 2018-10-11 10:20:05
    USB驱动程序可以粗分为两类: 一、主机(Host)系统上的驱动程序 ,这个驱动程序控制插入其中的USB设备 二、设备(Device)上的驱动程序,这个驱动程序控制USB设备如何与主机通信 为了举一个形象的例子,我得先...
    深入,并且广泛
    				-沉默犀牛
    

    USB设备驱动分类

    USB驱动程序可以粗分为两类:
    一、主机(Host)系统上的驱动程序 ,这个驱动程序控制插入其中的USB设备
    二、设备(Device)上的驱动程序,这个驱动程序控制USB设备如何与主机通信

    为了举一个形象的例子,我得先展示一张图片,更细致的介绍一下以上的两种分类:

    HostDevice
    USB设备驱动(Mass storage/CDC/HID)Gadget Function 驱动(serial…)
    USB核心Gadet Funtion API
    USB Host Controller 驱动(OHCI/EHCI/UHCI)UDC驱动(omap/pxa2xx…)
    USB Host ControllerUDC

    在这个图中,主机侧与设备侧又分别分为了两个驱动:
    Host:USB设备驱动、USB Host Controller 驱动
    Device:Gadget Function 驱动、UDC驱动

    USB设备驱动和USB Host Controller驱动之间的USB core负责了USB驱动管理和协议处理的主要工作。
    1.它通过定义一些数据结构体、宏和功能函数,向上为设备驱动提供编程接口,向下为USB Host Controller驱 动提供编程接口;
    2.维护整个系统的USB设备信息;
    3.完成设备热拔插控制、总线数据传输控制等

    Gadet Function API现在是UDC驱动程序回调函数的包装

    举一个生活中的例子:当用Android系统的手机 通过USB线 连接Linux系统的PC

    Host(PC)的动作:

    1.如果你插入了USB2.0的设备,则Host的USB Host Controller 驱动要使能EHCI,如果是USB1.1的设备,就要使能OHCI
    /UHCI,不过一般做法是全都使能。在这里我们手机插入后,Host Controller 驱动会启用EHCI

    2.这个USB设备驱动,会根据插入的设备不同,而选择不同的驱动。默认已经有的驱动有存储设备、键盘、鼠标、游戏
    杆、网络设备和调制解调器。如果是其他的设备,则需要自己写驱动了。在我们的例子里,手机通过USB连接到PC上
    后,可以选择几个不同的模式,当选择仅限充电时,Host的USB设备驱动就走充电的驱动,当选择传输文件(MTP)
    时,Host的USB设备驱动就走传输文件的驱动,当选择当做摄像头时(现在大部分手机不会设置这个功能了),Host
    的USB设备驱动就走摄像头的驱动。

    Device(手机)的动作:

    1.当我的手机通过USB连接电脑以后,手机的底层USB控制器行使UDC功能,这时运行在底层的是UDC驱动.

    2.然后我再选择手机的模式,当选择仅充电时,Device的Gadget Function驱动就走充电的驱动,当选择文件传输时,
    Device的Gadget Function驱动就走文件传输的驱动(Mass storage)

    现在我们已经理清了这四种驱动都是在哪用的(Host/Device)、都是何时用的、都是干什么的。

    USB设备基础

    USB设备是非常复杂的,它由许多不同的逻辑单元组成,这些逻辑单元之间的关系可以简单地描述如下:

    1. 设备通常具有一个或者多个配置
    2. 配置经常具有一个或者多个接口
    3. 接口通常具有一个或者多个设置
    4. 接口没有或者具有一个以上端点

    接下来就以上提到的概念详细分析:

    端点:

    USB通信的最基本形式是通过一个名为端点(endpoint)的东西,USB端点只能往一个方向传送数据,从Host到Device称为OUT端点,从Device到Host称为IN端点,端点可以看做是单项的管道它有四种不同的类型,分别具有不同的传送数据的方式:

    控制:

    控制端点用来控制对USB设备的不同部分的访问。它们通常用于配置设备、获取设备信息、发送命令到设备,或者获取设备的状态报告。这些端点一般体积较小,每个USB设备都有一个名为“端点0”的控制端点,USB核心使用该端点在插入时进行设备的配置。USB协议保证这些传输始终有足够的传输带宽以传送数据到设备,这里关于端点0的功能举一个例子:在手机插入PC的例子中,我选择charge模式后,就是通过端点0来告诉PC的,切换成MTP模式后,也是通过端点0来告诉PC的,PC知道后就会进行相应的配置,这个配置就是调用上述相应的driver

    中断

    每当USB Host要求设备传输数据时,中断端点就以一个固定的速率来传送少量的数据。这些端点是USB键盘和鼠标所使用的主要传输方式。它们通常还用于发送数据到USB设备以控制设备,不过一般不用来传输大量的数据。USB协议保证这些传输始终有足够的保留带宽以传送数据

    批量

    批量(bulk)端点传送大批量的数据。这些端点通常比中断端点大得多(它们可以一次持有更多的字符)。它们常见于需要确保没有数据丢失的传输的设备。USB协议不保证这些传输始终可以在特定的时间内完成。如果总线上的空间不足以发送整个批量包,它将被分割为多个包进行传输。这些端点通常出现在打印机、存储设备和网络设备上

    等时

    等时(isochronous)端点同样可以传送大批量的数据,但数据是否达到是没有保证的。这些端点用于可以应付数据丢失情况的设备,这类设备更注重于保持一个恒定的数据流。实时的数据手记(例如音频和视频设备)几乎毫无例外都使用这类端点。

    我们知道“端点”是逻辑上的意义,在代码中是如何实现的呢?或者说,用什么东西来代表端点呢?我之后还会再多啰嗦一下其他的类似“逻辑上的意义”这种问题。现在先来看描述端点的东西吧!其实就是结构体啦,我会结合《LDD3》和《Linux设备驱动开发详解》中的描述进行注释

    /* USB_DT_ENDPOINT: Endpoint descriptor */
    struct usb_endpoint_descriptor {
    	__u8  bLength;			//描述符长度
    	__u8  bDescriptorType;		//描述符类型
    	
    	__u8  bEndpointAddress;  	//端点地址:0-3位是端点号,第7位是方向(0为out,1为in)
    	__u8  bmAttributes;		//端点属性(类型):bit[0:1] 00表示控制 01表示等时 10表示批量 11表示中断
    	__le16 wMaxPacketSize;		//该端点一次可以处理的最大字节数。
    					  注意:driver可以发送数量大于此值的数据到端点,
    					  但是在实际传输到设备的时候,数据将被分割为wMaxPacketSize大小的块。
    	__u8  bInterval;		//轮询数据传送端点的时间间隔
    					  对于批量和控制的端点,忽略此域
    					  对于等时的端点,必须为1
    					  对于中断的端点,必须为1-255
    
    	/* NOTE:  these two are _only_ in audio endpoints. */
    	/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
    	__u8  bRefresh;
    	__u8  bSynchAddress;
    } __attribute__ ((packed));
    
    

    接口:

    USB端点被捆绑为接口。USB接口只处理一种USB逻辑连接,例如鼠标、键盘或者音频流。一些USB设备具有多个接口,例如USB扬声器可以包括两个接口:一个USB键盘用于按键和一个USB音频流。因为一个USB接口代表了一个基本功能,而每个USB驱动程序控制一个接口,因此,以扬声器为例,Linux需要两个不同的驱动程序来处理一个硬件

    接下来看看接口的结构体:

    /* USB_DT_INTERFACE: Interface descriptor */
    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));
    
    

    配置

    USB接口本身被捆绑为配置。一个USB设备可以有多个配置,而且可以在配置之间切换以改变设备的状态。比如手机可以选择charge only 、MTP、PTP、Camera这几个配置。用usb_config_descriptor结构体来描述配置。USB设备驱动程序通常不需要改变这个结构体中的任何值,因此在这里不详述了。

    这里顺便提一嘴怎么看你设备中的usb设备的这些结构体信息:
    1.lsusb 查看每一个设备的 bus号 和 device号
    在这里插入图片描述
    2.lsusb -s -v [Bus:Device] 就可以显示出你填入的Bus和Device指定的设备的信息
    在这里插入图片描述

    以上是先要准备的有关USB的基础知识,下两篇文章我们分析一下驱动的源码,这四个驱动中有两个是我们不会去改动的:Host Controller driver 和 UDC 因为这都是默认的,只需要config一下就可以了。所以我们的目标放在了另外的两个驱动上: USB设备驱动 和 Function 驱动

    这里多说一下上文提到的“逻辑上的意义”,在我学习Linux驱动这三个月以来,我感觉到Device、Driver这样的东西对于Kernel来说,都是逻辑上的,只要我把Device的信息写到DTS中,那Linux的Kernel启动时,就会展开DTS,并且把这些node最终解析为device结构体,然后注册(这时是注册platform_device,其他总线上的Device是(在对应总线注册时)注册的 ),你看,kernel并不知道这些我们硬件上到底有没有连接这些Device,我想表达的就是,在Kernel看来,这些最终解析成的device结构体,就是有意义的。但是这些node在我们看来,只有逻辑上的意义。有了它们,Kernel才能认识Device。同理的去理解本文中的结构体,和Kernel中的其他结构体,它们都是只有逻辑上的意义

    那么既然Kernel并不知道Device到底存不存在,那也不能让Kernel去控制一个不存在的Device吧,这个问题怎么解决的呢?这就是Driver的用处之一了。Driver也都是体现成了结构体。比如usb_driver、i2c_driver等等,所以Driver也是在我们的眼中看来,它们也是逻辑上的并不像是我们用来控制游戏人物的键盘、鼠标那么具体。Driver写好后,Kernel就认为这是一个Device的“控制器”了,但是它不会对应现实世界的任何实物。那么Driver是怎么解决“到底有没有真实的Device连接在硬件上?”这个问题的呢?Driver通过向真实器件读取信息来判断到底有没有真实的Device,如果没有真实的Device,那Driver就读不出来正确的数据,那我Kernel就认为没有这个真实器件,也正是这个原因,我们调试某器件的时候发现driver没有加载上,就是因为Device的配置有问题,导致Kernel误认为没有这个真实器件

    为了更好的解释这个问题,我用我所接触过的两个例子,来再次阐述:
    1.TouchPanel: 大部分TouchPanel是挂载在i2c上的,所以TP的Driver->probe函数中,会通过i2c总线向TP硬件的IC读取一些信息,这就起到了验证硬件在不在的作用,如果读出的数据不对,直接return
    2.LCD:LCD会面对一个问题,就是一套代码,要适应多个屏幕,(因为咱们手机或者其他嵌入式设备的配置不同,高配的用好一点的,低配的用差一点的)那一套代码怎么适应不同的屏幕呢?就是从LCD的硬件IC读取一些信息,比如硬件的编号,GPIO的高低等等,然后用这个编号来找对应的配置,读取编号的时候,不就可以验证硬件在不在了吗。

    这篇博文参考了《Linux设备驱动开发详解》《Linux Device Driver 3》和《Linux那些事儿-我是USB》

    展开全文
  • USB驱动框架

    千次阅读 2019-06-08 21:54:06
    一开始分析USB转串口驱动一头扎到源码,也就是追了个流程,但是还是什么都不懂,再后来看USB驱动框架也还是晕乎乎,之后再看了USB基础才慢慢理解一点。 一、USB基础知识 USB是Universal Serial Bus的缩写,中文译...
  • USB数据线连接状况下,可以将扫描设备设置成USB HID-KBW输入模式,在这种模式下,扫描将成为一个虚拟键盘,数据接收主机像接收真实键盘输入一样接受此虚拟键盘的输入,识读解码得到数据后的发送过程便是敲击虚拟...
  • USB驱动框架与驱动架构

    千次阅读 2019-07-12 13:49:59
    USB主机系统中,通过根集线与外部USB从机设备相连的处理芯片,称为USB主机控制USB主机控制包含硬件、软件和固件一部分。 1.2 USB设备系统 USB设备按功能分为两部分:集线(Hub)和功能部件。从下图...
  • USB驱动器运行Windows 10

    千次阅读 2017-03-18 21:59:12
    本篇博文就会一步步的教你如何从USB驱动器加载和运行Windows 10。 让我想象一个场景。也许你使用的电脑不是你自己的,里面的系统也许是XP或Win7 Win8,系统的设置也不太符合你工作的要求(比如他的计算机装的VS2005)...
  • 部分机型是可以支持关闭USB3.0模式的,小伙伴不要着急,听我娓娓道来。 方法一 开机按F2进入BIOS,在【Advanced】菜单下,将【USB3.0 Support】选项设定为【Disabled】,然后按F10保存退出即可。 方法二 ...
  • Linux USB驱动详解

    万次阅读 2018-11-13 10:15:29
    Linux USB驱动详解
  • 伺服驱动器安装环境,尺寸说明。配线图以及指令端子定义。脉冲序列指令模式、脉冲和方向引脚、低高速指令脉冲接口、开关量引入输出引脚。编码器信号输出引脚。USB调试口CN3定义
  • USB电缆:标准的USB电缆包括一对用于电源分配的20~28AWG规格的线对和一对28AWG规格的双绞线,并具有屏蔽和完整的保护层。 高速(480 Mb / s)和全速(12 Mb / s)要求使用带有两根电源导线和双绞线信号导线的屏蔽...
  • mac Air如何安全弹出kindle?

    千次阅读 2016-12-12 22:17:19
    如果您希望通过usb充电的同时在kindle上阅读或者是购物,请保持usb数据线连接,并将kindle从您的电脑上安全弹出。 问题来了,在mac air上如何安全弹出kindle呢? ====================...
  • 该电路上的高性能PCI总线USB主控制支持所有的高速USB传输模式:480Mbps高速模式,12Mbps全速模式和1.5Mbps低速模式。所有四个下传端口都允许同时建立USB 2.0 或USB 1.1以及不同速度的连接。 采用USB104+的电路板...
  • 最近弄一个硬件时,发现新安装的Windows 10系统没有识别到usb转串口驱动。尝试网上下载安装驱动,无果。今天我们就来看看如何弄它。 嫌弃废话多的可以直接跳转到驱动识别不了怎么解决 1、驱动 2、驱动识别不了...
  • kernel\msm-3.18\drivers\usb下目录内容   我们msm8937+android7.1平台编译out目录下usb目录下有编译到的目录如下: 我们先参考kernel\msm-3.18\drivers\usb\README对这些部分的...任何linux下驱动模块都必...
  • 魅族MX3 USB调试模式所需的驱动,仅限于64位Windows系统, 需要在“设备管理”手动安装,安装步骤: 1、打开“设备管理”窗口(在“我的电脑”右键,选择“管理”,然后在打开的“计算机管理”找到“设备管理”...
  • 驱动测试程序(需要在命令行模式运行程序,否则窗口一闪而过,看不到运行结果)HelloWDM_TestApp 驱动源码HelloWdm_Source 硬件测试程序(上位机)EasyUSB_51_Programer_Hard_Tester 硬件测试程序(下位机)...
  • 1、usb驱动的安装 以官方的google android 驱动作为蓝本,然后修改驱动文件,把自己的设备加进去就可以了。 一、找到设备ID 在设备管理里,找到设备,右键属性,在详细信息面板,属性选硬件ID,下面会出现一到两...
  • USB驱动架构浅析

    千次阅读 2017-05-15 20:35:20
    1.简介 USB,即Universal Serial Bus(通用串行总线)的缩写,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。USB接口支持设备的即插即用和热插拔...当前主要采用USB2.0和USB3.0接口,USB各版本间能很好的兼
  • ORICO奥睿科DU3系列USB3.0转显示转接器驱动For WinXP-32/WinXP-64/Win7-32/Win7-64/Win8-32/Win8-64/Win8.1-32/Win8.1-64(2014年4月8日发布)DU3系列通过USB3.0高速传输接口实现DVI接口与VGA接口外置转换,将电脑...
  • 玩mini2440开发板已经很多年了...然后,却带来了一个很严重的问题,友善之臂官方原配的FriendlyARM USB Download Driver Setup_20090421驱动文件根本不能用于64位的Win10系统中。所以,这段时间我一直在倒腾怎么在64...
  • 液晶驱动板 RTD2660H USB下载 烧录 编程 使用教程 液晶驱动板 RTD2660H USB下载 烧录 编程 使用教程

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,286
精华内容 15,714
关键字:

usb驱动器模式