zoukankan      html  css  js  c++  java
  • 利用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
    ");
    		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
    ",
    					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命令或者改写标准的命令来进行通信了.

      

  • 相关阅读:
    第20月第30日 集体智慧编程
    第20月第29天 cocoa抽象工厂 cocoapods组件化 cocoapods升级
    第20月第28天 tensorflow
    第20月第27天 游戏编程的人工智能
    第20月第22天 2016计算机大会后记——机器学习:发展与未来
    第20月第18天 小码哥swift
    第20月第17天 mvvm 多次点击push -ObjC
    第20月第14天 objc_getAssociatedObject _cmd
    第20月第9天 paddlepaddle
    第20月第4天 pycharm utf-8
  • 原文地址:https://www.cnblogs.com/fengeryi/p/3905324.html
Copyright © 2011-2022 走看看