USB MS驱动总结
Author: aaron
1 概述
Usb masstorage设备即USB的存储设备如U盘等, 该种设备严格按照USB规范来设计,制造, 因此要写该类设备的驱动必须要对USB规范等相关原理熟悉.
USB总线规范定义所有USB设备都必须遵守的规则, 而USB设备又有很多种类如存储设备,网络设备,音频设备等, 每一种这样的设备类都有自己的一个规范, 所以的USB总线规范, 设备类规范都可以在USB的官方网站上下载到, 因此很明显,本文的设备属于存储设备类, 因此该设备不仅要遵守USB的总线规范,还要遵守USB 存储设备类规范,
对于所属存储设备类下的设备还有几种传输方式如: Bulk-only传输方式, Control/Bulk/Interrupt传输方式等, 每种传输方式定义了该设备对数据,命令状态的传输方式. 也即定义了该设备上端点的数量及类型.
存储设备同host之间交互的命令也有不同的命令集可以选择(如SCSI命令集等), 这些都可以在描述符里自己定义.
本文以自己在工作中的一个项目为例详细描述USB MS在设备端的驱动程序的编写方式, 本文的设备是一个存储设备, 它逻辑上包含一个CDROM和一个T卡的盘, 它属于存储设备类, 传输方式使用的是Bulk-only方式, 命令集用的是SCSI命令集. 需要熟悉的文档规范包括: USB总线协议, USB存储设备类协议, SCSI命令集规范等.
2 USB总线协议简单描述
每个设备都有很多的描述符来详细描述该设备的基本信息及功能等:
2.1 描述符
2.1.1 设备描述符
设备描述符是用来描述该设备的基本信息,这包括该设备使用的USB协议的版本号, 设备类型(因此host就是通过这个描述符知道我们的设备是什么类型的), 设备厂商ID等(可参考USB规范). 每个设备只能有一个设备描述符.
2.1.2 配置描述符
该描述符描述了这个设备的配置信息及耗电情况及它有哪些功能,各功能使用的端点类型等等.通过这个描述符host就能知道该设备所具备的功能及如何同这个设备通信. 每个设备的配置描述符可以不只一个.
2.1.3 接口描述符
该描述符被包含在配置描述符中, 它就在逻辑上为设备定义一种功能, 多功能的设备可以有多个这种描述符.
2.1.4 端点描述符
端点就是设备同host通信的物理通道. 每个接口都有自己唯一的一个或多个端点. 而端点描述符就是用来描述该端点的属性: 传输方向, 端点类型, 端点的最大包长等.
2.1.5 字符串描述符
该描述符主要用来定义对类似厂商, 设备等信息的字符描述.
对于我们的设备:
设备描述符:
dev_descriptor_type my_dev =
{
….
.bcdUSB = 0200H, //支持USB2.0
.bDeviceClass = 00 //设备类型定义在接口描述符中
….
}
conf_descriptor_type my_conf =
{
…
.bNumInterfaces = 1 //接口数量
…
//接口1
{
…
.bInterfaceClass = 08h //存储设备类
.bInterfaceSubClass = 06h //使用SCSI命令集
.bInterfaceProtocol = 50h //Bulk-only 传输方式
….
}
//接口1所使用的端点
{
…
.bEndpointAddress = 81h //端点号是1, 传输方向为输入
.bmAttributes = 02h //这是各bulk端点
…
}
//接口1所使用的端点
{
…
.bEndpointAddress = 02h //端点号是2, 传输方向为输出
.bmAttributes = 02h //这是各bulk端点
…
}
//如果接口1还有端点则继续为该端点写描述符
// 如果有其他接口则继续写描述符.
}
2.2 设备枚举
设备在插入host后在上电正确,复位成功后会进行枚举, 该过程主要就是host通过一些USB规范上定义的标准请求同设备进行交互以读取设备的各种描述符,进而知道该设备的类型,功能,通信方式等.
3 驱动程序详细描述
3.1 总括
在枚举过程中,host会发送很多命令过来,包括获取设备描述符,获取配置描述符等, 初始化阶段的请求命令都是USB总线规范或设备类规范上定义好的, 因此我们驱动程序的一个主要任务就是要根据规范来编写对这些标准请求的响应过程.
一般情况下当设备上的某个端点收到host传来的数据时会产生一个中断, 而从这个产生中断的端点上我们就能知道是哪个interface需要处理该数据(每个端点都是唯一使用在某个interface上), 进而可以用响应的处理函数来处理这些数据(不同的interface对收到的命令会有不同的处理方式,因此要有不同的处理函数). 这里对设备的请求可以统一处理, 而对于不同设备类的请求就必须分别处理了, 整个过程大致如下:
3.2 标准请求处理
接下来就挑几个MS设备类的标准请求来讲解一下, 我们使用的是SCSI命令集来交互的, 具体命令封装格式及状态返回格式等可参考SCSI的规范.
3.2.1 Get Max LUN (class-specific request)
一个物理设备可以有多个逻辑设备(unit), host就用该命令来获取设备端支持的逻辑设备数量.
对于我们这个设备返回就是1 (1代表有2个设备).
3.2.2 INQUIRY命令
该命令请求设备上特定逻辑盘的信息(一个存储设备类可以有多个逻辑盘(lun)). 在这条命令中对于CDROM的逻辑盘我们的返回值如下:
Inquiry_resp_type Response =
{
…
.peripheral_device_type = 05h; //CDROM, T卡的盘这里返回的是00h(标准U盘)
.RMB = 1; //设备是removable的
…
}
值得注意的是我们可以定义我们自己的命令来达到我们的要求, 具体的命令格式只要按CBW的格式封装, 在设备端在对该自定义的命令进行相应就好了.
4 总结
MS 驱动无非就是相应PC过来的各种命令, 并返回相应的回复,关键是要维护好设备端的各种状态,及对过来的命令的各种检测, 及确定如何返回值.