利用mass storage class 作免驅動usb設備.

  當須要使用usb bulk傳輸,想讓設備像串口通信那樣和PC主機通訊, 一般須要本身作一個PC端的驅動,比較麻煩.linux

  爲避免在pc上編寫usb設備驅動的麻煩,能夠將設備作成mass storage 類的設備,使用通用的驅動.windows

  在通信以前設備端須要先作兩件事:app

  1,實現mass storage 類的描述符和類請求.函數

  2,實現必要的SCSI命令,讓PC認爲該設備已正常運做.測試

 

我利用修改linux中的gadget zero設備作了一個簡單的設備. 若是是在裸機程序下面作,應該也差很少,直接拿芯片廠商BSP中的USB樣例程序修改便可.spa

  

  

  

1實現mass storage 類的描述符和類請求.blog

mass storage接口

在linux中對應代碼:ip

1)設備描述符ci

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命令或者改寫標準的命令來進行通訊了.

相關文章
相關標籤/搜索