一 USB 设备类
SB 引入了设备类的概念,根据每一类驱动程序的功能将USB设备分为几大类,标准的几大类包括:
大容量存储类
网络类
集线器类
串行转换器
音频类
视频类
图像类
调制解调器
打印机
HID(Human Interface Device 人机接口设备)
每一大类的驱动程序对属于这类的所有设备通用,不需要另外再开发和安装驱动程序就可以使用。Linux-USB子系统支持主要的几类设备驱动程序。
每个USB设备都有类代码和子类代码。如 大容量存储设备类(0x08)就包含 光盘存储器(0x02)、磁带(0x03)、固态存储器(0x06)。设备驱动程序的 usb_device_id结构体包含类代码成员和子类代码成员。
也可以从 /proc/bus/usb/devices 输出结果的"I:"行看到设备的类代码和子类代码信息。
存储设备的接口有五大类
IDE、SCSI、USB,并行口,串口,其中并行口与串口的速度非常慢,不提也罢,最主要的就是IDE,usb, SCSI。IDE。
二 USB大容量存储设备
通常来讲USB大容量存储设备(Mass Storage) 主要是指 U盘,USB硬盘,笔驱动器,CD-ROM,软驱以及类似的存储设备。USB大容量存储设备利用 SCSI(Small Computer System Interface 小型计算机系统接口)协议和主机系统通信。
SCSI 接口
SCSI是“Small Computer System Interface”的缩写,即小型计算机系统接口。同IDE(ATA)完全不同的接口, IDE接口是普通PC的标准接口,而SCSI并不是专门为硬盘设计的接口,是一种广泛应用于小型机上的高速数据传输技术。
SCSI 协议
Scsi 协议是U盘,读卡器这些大容量存储所使用的协议。整个协议是工作在批量传输,分为IN、OUT两个方向。当插入U盘,USB主机控制器会识别到该设备是一个海量存储设备,然就就会根据SCSI协议,先发送CBW命令块包,U盘收到后根据SCSI协议解析该报,并回复命令执行状态包(CSW),就是应答,作为对前一个CBW命令块包处理结果的回应。Host 根据 CSW 来决定是否继续发 送下一个CBW 或是数据。
当一个U盘插入主体后,主机会拿到USB设备的描述符,从而识别到该USB设备是一个支持 Bulk-Only传输协议的海量存储设备。并使用Bulk-Only传输协议,即进入Bulk-Only传输方式,主机与USB设备间的所有数据都是通过Bulk input(批量输入端点)、 Bulk ouput(批量输出端点) 进行传输。不再通过控制端点传输任何数据。在这种传输方式下,有三种类型数据在主机和usb设备之间传输:命令块包(CBW)、命令执行状态包(CSW)、普通数据包。
Bulk-Only传输协议:Bulk-Only协议是USB组织针对大容量存储设备制定的一种块存储类协议,是USB大容量数据存储的基础协议,BOT协议用于主机和USB设备之间进行大容量数据传输。,U盘属于海量存储类。USB定义了海量存储存储设备类的规范。主要是指USB总线上的传输方法与存储介质的操作命令。海量存储设备只支持一个接口,即数据接口,此接口有三个端点, Bulk input(批量输入端点)、 Bulk ouput(批量输出端点)、中断端点。
大容量存储设备驱动程序把自己注册成一个虚拟的SCSI适配器。该虚拟适配器在上行方向上通过SCSI命令和上层通信,在下行方向通过URB与块存储器减缓数据。
在USB集线器枚举过后,USB系统发现插入设备是大容量设备 U盘,就会调用 storage_probe()。目前分析的主要工作就是,申请一个 SCSI适配器,并绑定USB接口 与 该适配器的关系,最后注册该SCSI适配器到 SCSI总线。SCSI总线扫描到该设备后,调用 sd_prob(),即sd驱动, sd驱动为该U盘在系统中生成 /dev/sd* 节点,从这以后,应用程序就可以用该接口访问这个U盘了。SCSI子系统把发向虚拟适配器的磁盘操作命令排队,在虚拟适配器这端,排队的命令以URB的形式传递给设备。
drivers\usb\storage\usb.c
static int storage_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct us_unusual_dev *unusual_dev;
//私有数据结构
struct us_data *us;
int result;
int size;
...
/*
//私有数据结构
struct us_data *us;
USB设备接口
struct usb_interface *intf
struct usb_device_id *id
struct us_unusual_dev *unusual_dev;
scsi协议主机信息模板 ??
struct scsi_host_template usb_stor_host_template
*/
result = usb_stor_probe1(&us, intf, id, unusual_dev,
&usb_stor_host_template);
if (result)
return result;
/* No special transport or protocol settings in the main module */
result = usb_stor_probe2(us);
return result;
}
static struct usb_driver usb_storage_driver = {
.name = DRV_NAME,
.probe = storage_probe,
.disconnect = usb_stor_disconnect,
.suspend = usb_stor_suspend,
...
};
/* First part of general USB mass-storage probing */
/*
//私有数据结构
struct us_data *us;
USB设备接口
struct usb_interface *intf
struct usb_device_id *id
struct us_unusual_dev *unusual_dev;
scsi协议主机信息模板 ??
struct scsi_host_template usb_stor_host_template
暂时分析的工作
申请 SCSI 主机控制器
绑定 SCSI 主机控制器 和 私有数据
绑定 私有数据 和 usb接口信息
*/
int usb_stor_probe1(struct us_data **pus,
struct usb_interface *intf,
const struct usb_device_id *id,
struct us_unusual_dev *unusual_dev,
struct scsi_host_template *sht)
{
//SCSI 主机控制器
struct Scsi_Host *host;
struct us_data *us;
int result;
dev_info(&intf->dev, "USB Mass Storage device detected\n");
host = scsi_host_alloc(sht, sizeof(*us));
if (!host) {
dev_warn(&intf->dev, "Unable to allocate the scsi host\n");
return -ENOMEM;
}
...
*pus = us = host_to_us(host);
...
INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork);
/* Associate the us_data structure with the USB device */
//us_data结构与USB设备关联s
result = associate_dev(us, intf);
if (result)
goto BadDevice;
...
}
/* Associate our private data with the USB device */
static int associate_dev(struct us_data *us, struct usb_interface *intf)
{
/* Fill in the device-related fields */
us->pusb_dev = interface_to_usbdev(intf);
us->pusb_intf = intf;
us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
...
/* Store our private data in the interface */
//在接口中存储我们的私有数据
usb_set_intfdata(intf, us);
...
return 0;
}
/* Second part of general USB mass-storage probing */
int usb_stor_probe2(struct us_data *us)
{
int result;
struct device *dev = &us->pusb_intf->dev;
...
/*
1 关联 USB设备 -- SCSI主机控制器
2 注册 SCSI主机控制器到 SCSI总线
*/
result = scsi_add_host(us_to_host(us), dev);
if (result) {
dev_warn(dev,
"Unable to add the scsi host\n");
goto HostAddErr;
}
...
/*
调用 usb_stor_scan_dwork() ,请求SCSI系统扫描总线来发现插入的SCSI设备
*/
queue_delayed_work(system_freezable_wq, &us->scan_dwork,
delay_use * HZ);
return 0;
...
return result;
}
EXPORT_SYMBOL_GPL(usb_stor_probe2);