利用mass storage class 做免驱动usb设备.

  当需要使用usb bulk传输,想让设备像串口通讯那样和PC主机通信, 通常需要自己做一个PC端的驱动,比较麻烦.

  为避免在pc上编写usb设备驱动的麻烦,可以将设备做成mass storage 类的设备,使用通用的驱动.

  在通讯之前设备端需要先做两件事:

  1,实现mass storage 类的描述符和类请求.

  2,实现必要的SCSI命令,让PC认为该设备已正常运作.

 

我利用修改linux中的gadget zero设备做了一个简单的设备. 如果是在裸机程序下面做,应该也差不多,直接拿芯片厂商BSP中的USB样例程序修改即可.

  

  

  

1实现mass storage 类的描述符和类请求.

mass storage

在linux中对应代码:

1)设备描述符

static struct usb_device_descriptor device_desc = {
	.bLength =		sizeof device_desc,
	.bDescriptorType =	USB_DT_DEVICE,

	.bcdUSB =		cpu_to_le16(0x0200),
//	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,
	.bDeviceClass =		USB_CLASS_PER_INTERFACE,

	.idVendor =		cpu_to_le16(DRIVER_VENDOR_NUM),
	.idProduct =		cpu_to_le16(DRIVER_PRODUCT_NUM),
	.bNumConfigurations =	1,
};

 设备描述符没什么特殊的,因为PC端usb驱动是与设备的接口对应的,与mass storage class对应的是接口描述符

2)接口描述符

/* SCSI device types */
#define TYPE_DISK	0x00
#define TYPE_CDROM	0x05

/* USB protocol value = the transport method */
#define USB_PR_CBI	0x00		/* Control/Bulk/Interrupt */
#define USB_PR_CB	0x01		/* Control/Bulk w/o interrupt */
#define USB_PR_BULK	0x50		/* Bulk-only */

/* USB subclass value = the protocol encapsulation */
#define USB_SC_RBC	0x01		/* Reduced Block Commands (flash) */
#define USB_SC_8020	0x02		/* SFF-8020i, MMC-2, ATAPI (CD-ROM) */
#define USB_SC_QIC	0x03		/* QIC-157 (tape) */
#define USB_SC_UFI	0x04		/* UFI (floppy) */
#define USB_SC_8070	0x05		/* SFF-8070i (removable) */
#define USB_SC_SCSI	0x06		/* Transparent SCSI */

/* Bulk-only class specific requests */
#define USB_BULK_RESET_REQUEST		0xff
#define USB_BULK_GET_MAX_LUN_REQUEST	0xfe

static struct usb_interface_descriptor source_sink_intf = {
	.bLength =		sizeof source_sink_intf,
	.bDescriptorType =	USB_DT_INTERFACE,

	.bNumEndpoints =	2,
//	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
	.bInterfaceClass =	USB_CLASS_MASS_STORAGE,
	.bInterfaceSubClass =	USB_SC_SCSI,	
	.bInterfaceProtocol =	USB_PR_BULK,	
	/* .iInterface = DYNAMIC */
};

 符合usb mass storage 类规范。对应下表

    

使用SCSI命令集,协议实现是Bulk-Only 传输。

3)实现一个mass storage 类的请求

	case USB_BULK_GET_MAX_LUN_REQUEST:
		printk("USB_BULK_GET_MAX_LUN_REQUEST\n");
		if (ctrl->bRequestType !=
		    (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
			break;
		*(u8 *) req->buf = 0;

		/* Respond with data/status */
		req->length = min((u16)1, w_length);
		value = usb_ep_queue(f->config->cdev->gadget->ep0, req, GFP_ATOMIC);
		if (value < 0)
			ERROR(f->config->cdev, "source/sinkc response, err %d\n",
					value);
		return(value);

 简单返回了一个0。

在linux中,linux把一些诸如获取描述符之类的请求集中在了一起放在了composite.c 中,不同设备类请求放在各自个f_xxx.c中各自的接口的xxx_setup函数中。

 

当实现了以上描述符和类请求之后,把嵌入式设备接上电脑,windows就会在设备管理器中列出usb mass storage设备。不过有一个黄色感叹号。

根据usb抓包情况来看是,电脑上面驱动发送SCSI命令数次不成功之后,会重新枚举过程,数次不正常之后就会认为该设备不正常。

 

2)必要的SCSI命令

大概要处理mass storage pc端驱动发过来的一下命令

#define SC_INQUIRY   0x12

#define SC_TEST_UNIT_READY  0x00

#define SC_READ_CAPACITY  0x25
#define SC_READ_FORMAT_CAPACITIES 0x23

前两条应该是必须的,后两条我也给加上了,去掉行不行,没有测试。

这些命令即可以放到linux gadget driver中也可以放到应用层程序中处理. 我是放到了应用层.

处理的流程基本是:

接收SCSI命令----->处理SCSI命令----->返回状态

基本是按照SCSI协议进行

CBW:Command Block Wrapper   命令块数据包

CSW:Command Status Wrapper  命令执行状态

按照CBW和CSW格式定义结构体:

struct ms_cbw_struct{
	u32 dCBWSignature;
	u32 dCBWTag;
	u32 dCBWDataTransferLength;
	u8 bmCBWFlags;
	u8 bCBWLUN;
	u8 bCBWCBLength;
	u8 CBWCB[SCSI_CMD_MAX_LEN];
};

struct ms_csw_struct{
	u32 dCSWSignature;
	u32 dCSWTag;
	u32 dCSWDataResidue;
	u8 bCSWStatus;
};

 

以SC_INQUIRY   命令为例

当我程序收到 0x12 命令,我就要按照scsi协议中该命令的规范来处理,我需要返回下面表格格式的数据给主机

 

.

第一个字节后5位决定了主机识别成一个cdrom或是可移动盘或其他类型的设备.

 RMB表示是否是一个可以移除设备.

Additional length (n-4)  需要填写.

其他的可根据需要填写.

 

之后返回状态CSW:

dCSWSignature固定为0x53425355,

dCSWTag 与CBW发过来的相同,

dCSWDataResidue等于CBW中要得长度和实际长度的差值.

bCSWStatus 表示命令成功或失败, 0表示成功,其他值表示失败.

 

另外这条命令

#define SC_TEST_UNIT_READY  0x00

是主机会在空闲时间不停发送的. 并且一开始连上主机,如果这条命令返回的CSW 成功,那么主机会使用READ_FORMAT_CAPACITIES 命令查询格式化的容量,read(10)读文件系统的信息. 如果得不到正确信息windows就会跳出对话框问你要不要格式化等等.

由于现在我并非真的需要做一个U盘之类的设备,所以0x00 命令,我CSW直接返回1. 这样当你点击该设备的盘符,就会提示说没有设备插入. 这对我没有影响,我只是用mass storage这个壳来进行通信的. 只是骗过mass storage的标准驱动而已. 

 

现在我就可以通过自定义的SCSI命令或者改写标准的命令来进行通信了.

 

 

 

 

 

 

  

posted @ 2014-08-19 15:31  风而已  阅读(3028)  评论(3编辑  收藏  举报