Chinaunix首页 | 论坛 | 博客
  • 博客访问: 97342
  • 博文数量: 21
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 194
  • 用 户 组: 普通用户
  • 注册时间: 2014-10-26 22:04
个人简介

大家好,我叫王大锤!

文章分类

全部博文(21)

分类: LINUX

2015-05-05 02:02:30

/*******************
 * USB总线特点
 *******************/


1.物理连接
USB设备具有热插拔,即插即用的特点,可以从总线上获得+5V,小于等于500mA的电流;
根据USB系统规范,USB总线上总共可连接127个设备;
usb电缆级联的总长度不能超过30米;




2.usb地址
USB设备和USB主机间采用树形结构,主从通讯模式,通讯只能由主机发起;
在usb总线上,由usb主机为每个连接在总线上的usb设备(包含usb集线器及根集线器)分配一个地址,usb主机将通过设备地址访问设备;
每个usb设备新加入总线时都具有默认的地址0,在设备的枚举过程中,主机会为设备分配一个新的地址,当设备的枚举过程结束后,usb主机就会一直用新的设备地址和设备通讯了;
由于设备不能超过127个,因此usb采用7位地址;




3.数据传输
usb1.1和2.0为半双工传输,采用4根电缆,两根数据线D+和D-用于差分数据传输,另两根线Vbus和GND为usb设备供电。usb电缆最长不要超过5米;
usb3.0采用全双工传输,8根线缆,在usb2.0的基础上增加了2对高速传输专用的数据线;


usb1.1协议支持:
1.5Mbps的低速数据传输(low speed);
12Mbps的全速数据传输(full speed);
usb2.0协议增加:
480Mbps的高速数据传输(high speed);
usb3.0协议增加:
2.4Gbps(全双工)的超高速数据传输(super speed);




/***************************
 * usb的描述符(descriptor)
 ***************************/
描述符提供了描述设备属性和特点的信息。当一个新的usb设备插入到usb总线后,usb主机首先要获得这个设备的描述符,从而确定这是个什么样的设备,然后才能开始后续的操作。
标准的描述符共有5种:
*设备描述符(device descriptor)
*配置描述符(configuration descriptor)
*接口描述符(interface descriptor)
*端点描述符(endpoint descriptor)
*字符串描述符(string descriptor)
在设备的枚举阶段,usb设备将按照一定的顺序向主机提供这些描述符。


(2)类(class)
usb设备连接到usb总线后,要想让设备工作起来,还需要加载对应的驱动程序。对于按照寄存器访问的设备,每个设备由于寄存器的名字,访问地址等一般是不相同的,所以要分别加载不同的驱动。由于市场上常常充满了大量不同公司生产的功能类似的设备,操作系统需要被迫为每个设备都提供不同的驱动,这会占用大量的资源。


针对这一情况,usb协议放弃了对设备寄存器,地址等的规定,采用全新的方式描述设备,并根据设备功能对设备进行分类。这样,usb设备只要遵循usb类规范,在插入usb总线后,就可以直接使用usb主机端提供的usb类驱动,完全不必再自行设计驱动程序了。


usb类为最终用户提供了很大的方便,比如我们常用的U盘设备,绝大多数都是采用大容量存储类(mass-storage class)设计的,这样,只要u盘插入的系统可以提供大容量存储类的驱动就可以了,不必为每个u盘都安装驱动程序。


usb规范中明确规定的设备类包括:
*语音设备类(audio device)
-针对麦克风,音响等语音/音频设备
*通信设备类(communications device)
-针对电话,调制解调器等设备
*芯片/智能卡接口设备类(chip/smart card interface devices)
-针对符合ISO/IEC7816规范的设备
*设备固件更新类(device firmware upgrade)
-用于更新usb设备上的固件
*影像设备(imaging device)
-针对扫描仪,数码相机等设备
*人机交互设备类(human interface device)
-针对鼠标,键盘,游戏杆等设备
*IrDA设备类(IrDA bridge device)
-针对IrDA(红外)设备
*大容量存储设备类(mass storage device)
针对CD-ROM,U盘等设备
*物理接口设备类(physical interface device)
-针对具有实时物体反馈的设备
*电源设备类(power device)
针对电源控制等设备
*打印机设备类(printer device)
-针对打印机设备
*监视器设备类(monitor device)
-针对显示器等设备




(3)功能(function)/接口(interface)
从用户的角度来看,usb设备可以提供各类功能,而从设备角度来说,用接口来描述一项单一的功能.一个物理的usb设备内部可能有多个不同的接口(比如一个既有鼠标又有游戏杆的设备)




(4)端点(endpoint)
端点是usb设备和usb主机的通讯管道。每一个端点都有自己的端点号,一个设备最多有16个端点(用4位端点号区分),端点号由设备自行分配。每个端点只支持一种传输方式,除了支持控制传输的端点外,每个端点是单向的。






3.usb的通信
usb的通讯可以分为3层:
信号层:最底层,传输单元为包(packet),其传输由usb物理器件负责
协议层:传输单元为事务处理(transaction),对应设备的endpoint
数据传输层:传输单元为传输(transfer),对应设备的interface


(1)包packet
一个数据包最多由5个部分组成:
同步字段(SYNC);PID字段;数据字段;CRC字段;包结尾字段(EOP)


pid字段为包标识符,表明包的类型,共有10种:
a.令牌包
输出(OUT); 输入(IN); 桢起始(SOF); 设置(setup)
b.数据包
数据0(DATA0); 数据1(DATA1)
C.握手包
确认(ACK); 不确认(NAK); 停止(STALL)
d.专用包
前同步(PRE)
out,in和setup包中包含了设备的地址和端点号(7位地址+4位端点号);
sof包包含11位的桢号;
data0和data1包中包含8到1023个字节的数据(在发送数据包之前,必须先由主机发送一个out/in/setup包,从而确定数据包的发送对象以及方向)
ack/nak/stall包中只有pid类型,不含数据,ack可以由主机或设备发送,而nak和stall只能由设备发送








/***************************
 * USB驱动程序
 ***************************/


USB是主机和外围总线间的一种连接方式,最高支持480Mbps的通信速率。USB在技术上采用单主方式实现,USB设备在没有主控制器要求的情况下是不能发送数据的。USB设备连接到USB集线器上,两者通过4线电缆(地线,电源线和两根差分信号线)


USB协议规定了一些特定的类型标准,如果一个USB设备遵循这些类型标准,就不需要特定的驱动程序,这些类型称为类(class)。类型包括存储设备,键盘、鼠标、游戏杆、网络设备,调制解调器等。不符合标准类型的USB设备需要特定的驱动程序。


USB驱动主要有3类:
(1)USB host driver
(2)USB device driver
(3)USB gadget driver


USB设备驱动构架在USB核心上,USB核心为驱动程序提供了一个访问设备硬件的接口,不必考虑当前采用的host控制器。架构间的关系如下:
USB设备驱动 -> USB核心(usb协议栈) ->USB host驱动(UHCI/OHCI/EHCI驱动)
 
和USB设备相关的概念包括配置,接口,设置和端点。配置描述了一个USB设备的特性,一个配置可以包括多个接口;USB设备驱动绑定到接口而不是整个设备上;一个接口通过若干端点和USB主控制器通信;一个接口可以选择多种设置,每种设置采用不同的端口特性。
* - devices have one (usually) or more configs;
* - configs have one (often) or more interfaces;
* - interfaces have one (usually) or more settings;
* - each interface setting has zero or(usually) more endpoints.
Devices may also have class-specific or vendor-specific descriptors.




1.端点(usb_host_endpoint)
---------------------------
USB通过端点(endpoint)通信,端点是单或双方向的,从主机到设备或从设备到主机,可以看作是一个管道。
USB有4种不同的端点类型:
(1)控制(control)
通常用于配置设备,获取设备信息,发送命令到设备,获取设备状态等。每个设备都有一个"端点0"控制端点。USB协议保证控制端点的带宽。控制传输常常分为三个阶段:
*设置阶段(setup)
由一个setup包和一个data包组成
*数据阶段(data)
数据阶段是可选的,既可以没有,也可以传输多个数据包。数据阶段的方向由设置阶段决定,由一个in/out包引导一个data包
*状态阶段(status)
由in/out包和一个零长度的data包组成,in/out的方向和数据阶段正相反


(2)中断(interrupt)
以固定的速率传输少量数据。USB的鼠标和键盘主要采用中断方式传输。USB协议保证中断端点的带宽。


(3)批量(bulk)
用于传输大量数据。类似于TCP,数据可靠性有保证,传输时间无保证。常用于打印机,存储设备,网络设备等


(4)等时(isochronous)
类似于UDP,协议不保证数据可靠到达。常用于音频和视频设备


批量和控制是异步的,由驱动按需要使用;
等时和中断是周期性的,一旦启用就要分配带宽;


用于描述端点的结构体有两个,usb_host_endpoint和usb_endpoint_descriptor。根据USB设备枚举时传来的信息填充:

struct usb_host_endpoint {
  struct usb_endpoint_descriptor  desc;
  struct list_head  urb_list;
  void  *hcpriv;
  struct ep_device *ep_dev;
  /* For sysfs info */
  unsigned char *extra;   
  /* Extra descriptors */
  int extralen;
  int enabled;
};



/* 根据设备端点描述符的内容填充 */
struct usb_endpoint_descriptor {
  __u8  bLength;
  __u8  bDescriptorType;
  __u8  bEndpointAddress;
  __u8  bmAttributes;
  __le16 wMaxPacketSize;
  __u8  bInterval;
/* 下面两个只用在音频设备的端点上,所以,使用 USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
  __u8  bRefresh;
  __u8  bSynchAddress;
} __attribute__ ((packed));


#define USB_DT_ENDPOINT_SIZE       7
#define USB_DT_ENDPOINT_AUDIO_SIZE 9  
/* Audio extension */


@bEndpointAddress
特定端点的USB地址。bit7中还包含了端点的方向。可以结合宏USB_DIR_OUT(0,从主机到设备)和USB_DIR_IN(0x80,从设备到主机)使用。
一个设备最多16个端点(4位端点号),和传输方向结合后,可形成32个管道:
USB_ENDPOINT_NUMBER_MASK 0x0f
USB_ENDPOINT_DIR_MASK    0x80


@bmAttributes
端点的类型。结合USB_ENDPOINT_XFERTYPE_MASK使用
USB_ENDPOINT_XFERTYPE_MASK  0x03 
USB_ENDPOINT_XFER_CONTROL   0
USB_ENDPOINT_XFER_ISOC      1
USB_ENDPOINT_XFER_BULK      2
USB_ENDPOINT_XFER_INT       3


@wMaxPacketSize
端点一次可以处理的最大字节数。驱动可以发送大于此值的数据到端点


@bInterval
中断类型端点的时间间隔。以ms为单位




2.接口(usb_interface)
---------------------------
一个接口对应一个功能,而一个功能需要一个驱动程序,所以包含多个接口的设备需要有多个驱动程序。接口可以有不同的setting,最初状态为setting0



struct usb_host_interface {
  struct usb_interface_descriptor desc;
  /*desc.bNumEndpoint说明本接口使用几个端点*/    
  struct usb_host_endpoint *endpoint;
  /*本接口所包含端点的数组头*/
  char *string; 
  unsigned char *extra;
  int extralen;
};
/*一个接口有几个setting,就会填充几个usb_host_interface,但同一时间只有其中一个可用 */



struct usb_interface_descriptor {
  __u8  bLength; 
  __u8  bDescriptorType;
  __u8  bInterfaceNumber; 
  __u8  bAlternateSetting;
  __u8  bNumEndpoints;
  __u8  bInterfaceClass; 
  __u8  bInterfaceSubClass; 
  __u8  bInterfaceProtocol; 
  __u8  iInterface;
  }__attribute__((packed));
USB_DT_INTERFACE_SIZE  9 
上面的结构体是在设备枚举时填充的,而内核使用usb_interface来描述接口,类似于pci_dev,usb核心会在调用usb驱动时传入usb_interface.



struct usb_interface {
  struct usb_host_interface *altsetting;
  struct usb_host_interface *cur_altsetting;
  unsigned num_altsetting;
/*在usb_interface中,可以选择不同的接口setting*/
  ...
  int minor; /* 当使用usb major时有用(180) */
  struct device dev;
  struct device *usb_dev; /* usb class */
  ...
};
如果调用了usb_register_dev,则在usb_driver的probe函数中将通过usbcore分配的minor保存到usb_interface->minor中




3.配置(usb_host_config)
---------------------------
USB接口被捆绑为配置。一个设备可以有多个配置,同一时刻只能激活其中之一。


*设备通常有一个或多个配置(config)
*配置通常有一个或多个接口(interface)
*接口通常有一个或多个设置(setting)
*接口没有或者有一个以上的端点(endpoint)



struct usb_host_config {
  struct usb_config_descriptor desc;
  char *string;
  ...
  struct usb_interface *interface[USB_MAXINTERFACES];
  struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
/*xx_cache中为usb_host_interface分配了空间,而usb_interface只有指针*/
  unsigned char *extra; /*Extra descriptors*/
  int extralen;
};
#define USB_MAXINTERFACES  32



struct usb_config_descriptor {
  __u8  bLength;
  __u8  bDescriptorType;
  __le16 wTotalLength;
/*属于此配置的所有接口描述符和端点描述符的总大小*/
  __u8  bNumInterfaces;
  __u8  bConfigurationValue;
  __u8  iConfiguration;
  __u8  bmAttributes;
  __u8  bMaxPower;
} __attribute__ ((packed));
USB_DT_CONFIG_SIZE  9


/* from config descriptor bmAttributes */
USB_CONFIG_ATT_ONE (1 << 7) /*必须设置*/
USB_CONFIG_ATT_SELFPOWER (1 << 6) /*自供电*/
USB_CONFIG_ATT_WAKEUP(1 << 5) /*可唤醒 */
USB_CONFIG_ATT_BATTERY(1 << 4) /*电池供电*/






4.设备(usb_device)
---------------------------
内核用usb_device描述整个设备,一个设备可以包含多个usb_interface。所以,可能有多个usb_driver对应同一个usb_device。



struct usb_device {
  int devnum; /* Address on USB bus */
  char devpath [16];   
  /* Use in messages: /port/port/... */
  enum usb_device_state   state;
  enum usb_device_speed   speed; 
  ...
  unsigned int toggle[2];
  /* 每个端口一位:([0] = IN, [1] = OUT) */
  struct usb_device *parent;
  struct usb_bus *bus;
  struct usb_host_endpoint ep0;
  struct device dev;
  struct usb_device_descriptor descriptor;
  struct usb_host_config *config;
/*config的数量在device_des~的bNumConfigurations*/
  struct usb_host_config *actconfig;
  /*usb_host_config包含usb_interface的指针*/
  struct usb_host_endpoint *ep_in[16];
  struct usb_host_endpoint *ep_out[16];
  ...
#ifdef CONFIG_USB_DEVICE_CLASS
  struct device *usb_classdev;
#endif
#ifdef CONFIG_USB_DEVICEFS
  struct dentry *usbfs_dentry;
#endif
  struct usb_device *children[USB_MAXCHILDREN];
  ...
};



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_DT_DEVICE_SIZE  18


usb_interface和usb_device具有父子关系:
#define interface_to_usbdev(intf) \
        container_of(intf->dev.parent, struct usb_device, dev)


*设备通常有一个或多个配置(config)
*配置通常有一个或多个接口(interface)
*接口通常有一个或多个设置(setting)
*接口没有或者有一个以上的端点(endpoint)


结构体间的关系:
(1)usb_device->actconfig
指向usb_host_config(对应config);


(2)usb_host_config->interface
指向usb_interface的指针数组(对应interface)


(3)usb_interface->cur_altsetting
指向usb_host_interface(对应setting);


(4)usb_host_interface->endpoint
指向usb_host_endpoint的数组,数组长度保存在desc.bNumEndpoint(对应endpoint);


(5)usb_host_endpoint
包含和本端点相关的urb的链表头urb_list(对应urb);






5.在用户空间获得usb信息
---------------------------
如果内核支持usbfs,那么在/proc/bus/usb中会有当前系统usb设备的一些信息;如果内核支持sysfs,则在/sys下会有相应内容。


(1)usbfs
usbfs缺省mount到/proc/bus/usb目录下。实现代码在linux/drivers/usb/core/inode.c中。
/proc/bus/usb下文件的说明和操作方式,请参考Documentations/usb/proc_usb_info.txt。
其中devices文件是ascii形式的信息,而001/002形式的文件是二进制信息,还可以通过ioctl操作。支持的IOCTL命令在,参考linux/drivers/usb/core/devio.c。
ioctl命令需要在驱动usb_driver中提供ioctl函数支持,一般只有集线器需要。




(2)lsusb
使用lsusb-v命令,可以查看当前系统usb设备描述符的详细信息,前提是必须支持usbfs。
查看文件/usr/share/usb.ids,列出了目前支持的usb设备的vendor,product,interface等信息。在该文件的最后,还列出了标准usb设备类的class,subclass,protocol等信息。


(3)sysfs
在/sys下,无论是物理usb设备(usb_device)还是单独的usb接口(usb_interface),在sysfs中均表示为单独的设备。
而usb主控制器对应usb总线。
以usb鼠标为例,我机器上的目录为:
$>ls /sys/devices/pci0000:00/0000:00:04.2/usb1/1-2/1-2:1.0
目录说明:
(1)0000:00:04.2
是usb主机控制器所对应的pci设备号
(2)usb1
是该控制器的根集线器编号,同时也是usb总线的编号
(3)1-2
设备名,1是该设备所在的集线器编号,2是设备所插入的端口号
(4)1-2:1.0
接口名。1-2是设备名,1.0为配置1,接口0


总之,在sysfs中usb设备的命名为:
  根集线器-集线器端口号:配置.接口
如果有多级的usb集线器,则为:
  根集线器-集线器端口号-集线器端口号:配置.接口




(4)sysfs下的usb主控制器(PCI信息)
我机器的usb主控制器在/sys下的内容如下:
$>ls /sys/devices/pci0000:00/0000:00:04.3
...
irq         (12)
class       (0x0c0300)
vendor      (0x1106)                
device      (0x3038)
local_cpus  (1)
resource
resource4
pools
usb_host:usb_host2@ (指向class/usb_host/usb_host2/)
driver@
(指向/bus/pci/drivers/uhci_hcd/) 
usb2/  (包含了usb总线2下的设备)


$>cat pools
poolinfo - 0.1
uhci_qh            12   42   96  1
uhci_td             2   85   48  1
buffer-2048         0    0 2048  0
buffer-512          0    0  512  0
buffer-128          0    0  128  0
buffer-32           1  128   32  1


$>cat uevent
DRIVER=uhci_hcd
PHYSDEVBUS=pci
PHYSDEVDRIVER=uhci_hcd
PCI_CLASS=C0300
PCI_ID=1106:3038
PCI_SUBSYS_ID=0925:1234
PCI_SLOT_NAME=0000:00:04.3
MODALIAS=pci:v00001106d00003038sv00000925sd00001234bc0Csc03i00




(5)sysfs下的usb总线(host控制器信息)
$>ls /sys/devices/pci0000:00/0000:00:04.3/usb2
...
bcdDevice           (0206)
bConfigurationValue (1,可写入以改变配置)
bDeviceClass        (09)
bDeviceProtocol     (00)
bDeviceSubClass     (00)
bmAttributes        (e0)
bMaxPacketSize0     (64)
bMaxPower           (0mA)
bNumConfigurations  (1)
bNumInterfaces      (1)
busnum              (2)
dev                 (189:128)
devnum              (1)
idProduct           (0000)
idVendor            (0000)
manufacturer        (Linux 2.6.24 uhci_hcd)
maxchild            (2,4端口hub为4)
product             (UHCI Host Controller)
quirks              (0x0)
serial              (0000:00:04.3)
speed               (12)
urbnum              (73)
version             (1.10,高速host为2.0)
driver@             (/bus/usb/drivers/usb/)
subsystem@          (bus/usb/)
ep_00@  
(/class/usb_endpoint/usbdev2.1_ep00/)
usb_device:usbdev2.1@ 
(/class/usb_device/usbdev2.1/)
usb_endpoint:usbdev2.1_ep00@
(/class/usb_endpoint/usbdev2.1_ep00/)
2-0:1.0/            (根hub的接口)           
2-1/                (总线下的设备)




(6)根hub接口
$>ls .../usb2/2-0:1.0
bAlternateSetting   (0)
bInterfaceClass     (09)
bInterfaceNumber    (00)
bInterfaceProtocol  (00)
bInterfaceSubClass  (00)
bNumEndpoints       (01)
uevent
bus@    (/bus/usb)
driver@ (/bus/usb/drivers/hub)
ep_81@  (/class/usb_endpoint/usbdev2.1_ep81)
usb_endpoint:usbdev2.1_ep81@
(/class/usb_endpoint/usbdev2.1_ep81)


$>cat uevent
DEVTYPE=usb_interface
DRIVER=hub
PHYSDEVBUS=usb
PHYSDEVDRIVER=hub
DEVICE=/proc/bus/usb/002/001
PRODUCT=0/0/206
TYPE=9/0/0
INTERFACE=9/0/0
MODALIAS=usb:v0000p0000d0206dc09dsc00dp00ic09isc00ip00




(7)USB总线下的设备
$>ls .../0000:00:04.3/usb2/2-1
...
bcdDevice           (0400)
bConfigurationValue (1)
bDeviceClass        (00)
bDeviceProtocol     (00)
bDeviceSubClass     (00)
bmAttributes        (a0)
bMaxPacketSize0     (8)
bMaxPower           (44mA)
bNumConfigurations  (1)
bNumInterfaces      (1)
busnum              (2)
dev                 (189:129)
devnum              (2)
idProduct           (6001)
idVendor            (0403)
manufacturer        (FTDI)
maxchild            (0)
product             (USB Serial Converter)
quirks              (0x0)
serial              (FTDGF9E4)
speed               (12)
uevent
urbnum              (11)
version             (1.10)
bus@    /bus/usb/
driver@ /bus/usb/drivers/usb/
(定义在usb/generic.c中的usb_device_driver,对应usb设备的驱动)
ep_00@  /class/usb_endpoint/usbdev2.2_ep00/
usb_device:usbdev2.2@ /class/usb_device/usbdev2.10/
usb_endpoint:usbdev2.2_ep00@ /class/usb_endpoint/usbdev2.2_ep00/
2-1:1.0/  (2-1设备下的接口0)


$>cat uevent
MAJOR=189
MINOR=129
DEVTYPE=usb_device
DRIVER=usb
PHYSDEVBUS=usb
PHYSDEVDRIVER=usb
DEVICE=/proc/bus/usb/002/02
PRODUCT=403/6001/400
TYPE=0/0/0
BUSNUM=002
DEVNUM=02




(8)设备中的接口
$>ls 0000:00:04.3/usb2/2-1/2-1:1.0
...
bAlternateSetting   (0)
bInterfaceClass     (ff)
bInterfaceNumber    (00)
bInterfaceProtocol  (ff)
bInterfaceSubClass  (ff)
bNumEndpoints       (02)
interface           (USB Serial Converter) 
bus@      /bus/usb
driver@   /bus/usb/drivers/ftdi_sio
ep_02@    /class/usb_endpoint/usbdev2.2_ep02
ep_81@    /class/usb_endpoint/usbdev2.2_ep81
ttyUSB0/  (和usb接口相关的具体设备的内容)




(9)USB串口设备(ttyUSB0)
event_char    (write only)
latency_timer (16)
port_number   (0)
uevent
bus@         /bus/usb-serial/
driver@      /bus/usb-serial/drivers/ftdi_sio/
subsystem@   /bus/usb-serial/
tty:ttyUSB0@ /class/tty/ttyUSB0/


$>cat uevent
DRIVER=ftdi_sio
PHYSDEVBUS=usb-serial
PHYSDEVDRIVER=ftdi_sio






6.usb设备文件和/sys/class
-------------------------------
(1)usb主设备号

#define USB_MAJOR          180
#define USB_DEVICE_MAJOR   189


(2)usb设备文件
在/dev下,可以找到采用189作为主设备号的设备,如:
usbdev1.1   189, 0    (usb主控制器1,对应总线1)
usbdev1.2   189, 1    (鼠标,接在host1上)
usbdev2.1   189, 128  (usb主控制器2,对应总线2)
usbdev2.2   189, 129  (usb转串口设备,接在host2上)


这些设备文件由udevd创建,其设备号来源于/sys/class/usb_device。
$>tree /sys/class/usb_device
|-- usbdev1.1
|   |-- dev   (189:129)
|   |-- device -> ../../../devices/pci0000:00/0000:00:04.2/usb1
|   |-- power
|   |   `-- wakeup
|   |-- subsystem -> ../../../class/usb_device
|   `-- uevent
|-- usbdev1.2
|-- usbdev2.1
`-- usbdev2.2
...


其中uevent文件的内容是:
$>cat uevent
MAJOR=189
MINOR=129
PHYSDEVPATH=/devices/pci0000:00/0000:00:04.3/usb2/2-1
PHYSDEVBUS=usb
PHYSDEVDRIVER=usb




(3)端点文件
在/dev下,可以找到采用254作为主设备号的usb端点设备,254应该是由系统自动分配的,如:
usbdev1.1_ep00  254, 0
usbdev1.1_ep81  254, 1
usbdev1.2_ep00  254, 4
usbdev1.2_ep81  254, 5
usbdev2.1_ep00  254, 2
usbdev2.1_ep81  254, 3
usbdev2.2_ep00  254, 6
usbdev2.2_ep02  254, 8
usbdev2.2_ep81  254, 7


这些设备文件由udevd创建,其设备号来源于/sys/class/usb_endpoint,该目录下共8个子目录,对应8个端点:
$>ls /sys/class/usb_endpoint/usbdev2.2_ep02
bEndpointAddress  (02)
bInterval         (00)
bLength           (07)
bmAttributes      (02)
dev               (254:8)
direction         (out)
interval          (0ms)
subsystem@        (/class/usb_endpoint)
type              (Bulk)
uevent
wMaxPacketSize    (0040)


$>cat uevent
MAJOR=254
MINOR=8
PHYSDEVPATH=/devices/pci0000:00/0000:00:04.3/usb2/2-1/2-1:1.0
PHYSDEVBUS=usb
PHYSDEVDRIVER=ftdi_sio




  


=========================
    URB (usb请求块)
=========================
USB驱动程序通过urb(usb请求块)和usb设备通信,urb和skbuff以及kiocb很象。设备驱动可以为一个端点分配多个urb,也可以为不同的端点重用同一个urb。设备中的每个端点都可以处理一个urb队列。


urb的典型生命周期如下:
*由USB驱动程序创建
*分配给一个特定usb设备的特定端点
*由USB驱动程序递交到USB核心
*由USB核心交到对应的USB主控制器驱动程序
*由主控制器传输数据
*当urb结束后,由USB主控制器驱动通知设备驱动


1.urb定义
------------------------

struct urb {
  struct kref kref;
  void *hcpriv; 
  atomic_t use_count;
  ...
  struct usb_device *dev;
  struct usb_host_endpoint *ep;
  unsigned int pipe; /*用指定函数进行设置*/
  int status; /*(return)non-ISO status */
  
  unsigned int transfer_flags;
  void *transfer_buffer;
  dma_addr_t transfer_dma;
  int transfer_buffer_length;
  int actual_length;(return)
  
  unsigned char *setup_packet;
  /* (in) setup packet (control only) */
  dma_addr_t setup_dma;
  /* (in) dma addr for setup_packet */
  int start_frame; 
        
  int number_of_packets;
  /* (in) number of ISO packets */
  int interval;
  /* (modify)transfer interval(INT/ISO) */
  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]; /* (in) ISO ONLY */
};


参数说明:
(1)struct usb_device *dev (in)
urb要发送到的usb设备


(2)unsigned int pipe (in)
urb要发送到的pipe信息,包括设备号,端点号,传输方向,端点类型等,在驱动中采用如下宏初始化:
unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint);
上面的宏用于初始化一个控制OUT端点。dev中包含了usb设备号,endpoint从interface信息获得。驱动将宏的返回值赋给urb->pipe即可
类似的宏还包括:
usb_rcvctrlpipe(dev,endpoint)
usb_sndisocpipe()
usb_rcvisocpipe()
usb_sndbulkpipe()
usb_rcvbulkpipe()
usb_sndintpipe()
usb_rcvintpipe()


(3)int status (return)
对于非ISOC的urb,status中记录了urb的当前状态,驱动可以在urb的结束回调函数中访问该值。有效值包括:
0: urb传输成功
-ENOENT:urb被usb_kill_urb调用终止
-EPROTO, -EILSEQ, -EOVERFLOW:
设备,设备的固件或者电缆发生了硬件故障
还有其他一些值,参见第三版P336-337,这些错误值定义在asm-generic/errno.h和asm-generic/errno-base.h中


(4)unsigned int transfer_flags (in)
urb的传输类型。可以设置为URB_SHORT_NOT_OK等。可用的标记定义在usb.h


(5)void *transfer_buffer; (in)
收发缓冲区指针,必须通过kmalloc创建


(6)dma_addr_t transfer_dma; (in)
是transfer_buffer的DMA地址


(7)int transfer_buffer_length; (in)
缓冲区长度。对于out端点,该值可以大于设备端点所允许的最大值,HCD会将urb中的大数据块分割非小块传输。不必提交一系列缓冲区小的urb


(8)int actual_length; (return)
urb完成后,返回的实际数据长度


(9)unsigned char *setup_packet; (in)
   dma_addr_t setup_dma; (in)
用于控制urb的数据区。该区中的数据在transfer_buffer前面传送


(10)int start_frame; (modify)
    int number_of_packets; (in)
    int interval; (modify)
    int error_count; (return)
都是针对等时传输(ISO)和中断传输的


(11)void *context; (in)
    usb_complete_t complete;
当urb完全传输或发生错误时,usb核心调用complete函数。context类似于file->private_data,在回调函数中可以用urb->context获得设备结构体
typedef void (*usb_complete_t)(struct urb *);


(12)struct usb_iso_packet_descriptor iso_frame_desc[0];
针对等时urb。是等时数据包的数组,可以在单个urb中定义许多等时传输,还可用于收集单个传输的状态
struct usb_iso_packet_descriptor {
  unsigned int offset;
  unsigned int length;/*expected length*/
  unsigned int actual_length;
  int status;
};






2.创建和销毁urb
------------------------
urb必须采用动态创建和销毁。
(1)分配urb
struct urb *
usb_alloc_urb(int iso_packets, gfp_t mem_flags);
如果不创建等时urb,iso_packets为0;mem_flags传递给kmalloc,失败返回NULL


(2)释放urb
void usb_free_urb(struct urb *urb);




3.初始化urb
------------------------
通过usb_alloc_urb后,需要对返回的urb进行初始化
(1)中断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);
pipe必须要用前面的usb_sndintpipe等初始化;
transfer_buffer必须用kmalloc分配;


(2)批量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);


(3)控制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);
setup_packet: 设置数据包的数据


(4)等时urb
没有相应的初始化函数,需要手工初始化。例如:
  urb->dev = dev;
  urb->context = uvd;
  urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1);
  urb->interval = 1;
  urb->transfer_flags = URB_ISO_ASAP;
  urb->transfer_buffer = cam->sts_buf[i];
  urb->complete = koni_isoc_irq;
  urb->number_of_packets = FRAMES;
  urb->transfer_buffer_len = ...;
  for(j=0; j     urb->iso_frame_desc[j].offset = j;
    urb->iso_frame_desc[j].length = 1;
  }




 
4.提交urb
------------------------
urb初始化完成后,就可以提交给usb核心了:
(1)提交
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
成功返回0;
mem_flags可以取GFP_ATOMIC,GFP_NOIO,GFP_KERNEL


(2)回调函数
如果usb_submit_urb调用成功,当urb结束时正好调用一次回调函数。


(3)取消urb
int usb_kill_urb(struct urb *);
int usb_unlink_urb(struct urb *);
kill用于终止一个urb的生命周期,通常在disconnet中调用;
unlink不等urb真正完结就返回,因此调用不会导致睡眠。

阅读(3813) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~