USB大容量存储BOT
+ -

STM32F407自带USB-SDCARD协议UBOT实现

2026-08-14 本文链接为:http://www.usbzh.com/article/detail-1791.html ,欢迎转载,转载请附上本文链接。

实现的命令有:

#define SCSI_FORMAT_UNIT                            0x04
#define SCSI_INQUIRY                                0x12
#define SCSI_MODE_SELECT6                           0x15
#define SCSI_MODE_SELECT10                          0x55
#define SCSI_MODE_SENSE6                            0x1A
#define SCSI_MODE_SENSE10                           0x5A
#define SCSI_ALLOW_MEDIUM_REMOVAL                   0x1E
#define SCSI_READ6                                  0x08
#define SCSI_READ10                                 0x28
#define SCSI_READ12                                 0xA8
#define SCSI_READ16                                 0x88

其中断OUT端点

void MSC_BOT_DataOut (USB_OTG_CORE_HANDLE  *pdev, uint8_t epnum)
{
  switch (MSC_BOT_State)
  {
  case BOT_IDLE:
    MSC_BOT_CBW_Decode(pdev);
    break;

  case BOT_DATA_OUT:  
    if(SCSI_ProcessCmd(pdev,MSC_BOT_cbw.bLUN,&MSC_BOT_cbw.CB[0]) < 0)
    {
      MSC_BOT_SendCSW (pdev, CSW_CMD_FAILED);
    }

    break;

  default:
    break;
  }
}

其IDLE时

static void  MSC_BOT_CBW_Decode (USB_OTG_CORE_HANDLE  *pdev)
{
  MSC_BOT_csw.dTag = MSC_BOT_cbw.dTag;
  MSC_BOT_csw.dDataResidue = MSC_BOT_cbw.dDataLength;

    //error
  if ((USBD_GetRxCount (pdev ,MSC_OUT_EP) != BOT_CBW_LENGTH) 
        ||  (MSC_BOT_cbw.dSignature != BOT_CBW_SIGNATURE)
      ||  (MSC_BOT_cbw.bLUN > 1) 
      ||  (MSC_BOT_cbw.bCBLength < 1) 
      ||  (MSC_BOT_cbw.bCBLength > 16))
  {  
    SCSI_SenseCode(MSC_BOT_cbw.bLUN,ILLEGAL_REQUEST, INVALID_CDB);
     MSC_BOT_Status = BOT_STATE_ERROR;   
    MSC_BOT_Abort(pdev);

  }
  else
  {
        //bot cmd
    if(SCSI_ProcessCmd(pdev,MSC_BOT_cbw.bLUN,&MSC_BOT_cbw.CB[0]) < 0)
    {
      MSC_BOT_Abort(pdev);
    }
    /*Burst xfer handled internally*/
    else if ((MSC_BOT_State != BOT_DATA_IN) && 
             (MSC_BOT_State != BOT_DATA_OUT) &&
             (MSC_BOT_State != BOT_LAST_DATA_IN)) 
    {
      if (MSC_BOT_DataLen > 0)
      {
        MSC_BOT_SendData(pdev, MSC_BOT_Data, MSC_BOT_DataLen);
      }
      else if (MSC_BOT_DataLen == 0) 
      {
        MSC_BOT_SendCSW (pdev,CSW_CMD_PASSED);
      }
    }
  }
}

可见返回-1,端点STALL

static void  MSC_BOT_Abort (USB_OTG_CORE_HANDLE  *pdev)
{

  if ((MSC_BOT_cbw.bmFlags == 0) && 
      (MSC_BOT_cbw.dDataLength != 0) &&
      (MSC_BOT_Status == BOT_STATE_NORMAL) )
  {
    DCD_EP_Stall(pdev, MSC_OUT_EP );
  }
  DCD_EP_Stall(pdev, MSC_IN_EP);

  if(MSC_BOT_Status == BOT_STATE_ERROR)
  {
    DCD_EP_PrepareRx (pdev,
                      MSC_OUT_EP,
                      (uint8_t *)&MSC_BOT_cbw, 
                      BOT_CBW_LENGTH);    
  }
}

命令解析过程

int8_t SCSI_ProcessCmd(USB_OTG_CORE_HANDLE  *pdev,
                           uint8_t lun, 
                           uint8_t *params)
{
  cdev = pdev;

  switch (params[0])
  {
  case SCSI_TEST_UNIT_READY:
    return SCSI_TestUnitReady(lun, params);

  case SCSI_REQUEST_SENSE:
    return SCSI_RequestSense (lun, params);
  case SCSI_INQUIRY:
    return SCSI_Inquiry(lun, params);

  case SCSI_START_STOP_UNIT:
    return SCSI_StartStopUnit(lun, params);

  case SCSI_ALLOW_MEDIUM_REMOVAL:
    return SCSI_StartStopUnit(lun, params);

  case SCSI_MODE_SENSE6:
    return SCSI_ModeSense6 (lun, params);

  case SCSI_MODE_SENSE10:
    return SCSI_ModeSense10 (lun, params);

  case SCSI_READ_FORMAT_CAPACITIES:
    return SCSI_ReadFormatCapacity(lun, params);

  case SCSI_READ_CAPACITY10:
    return SCSI_ReadCapacity10(lun, params);

  case SCSI_READ10:
    return SCSI_Read10(lun, params); 

  case SCSI_WRITE10:
    return SCSI_Write10(lun, params);

  case SCSI_VERIFY10:
    return SCSI_Verify10(lun, params);

  default:
    SCSI_SenseCode(lun,ILLEGAL_REQUEST, INVALID_CDB);    
    return -1;
  }
}

端点STALL

typedef struct _SENSE_ITEM {                
  char Skey;
  union {
    struct _ASCs {
      char ASC;
      char ASCQ;
    }b;
    unsigned int    ASC;
    char *pData;
  } w;
} SCSI_Sense_TypeDef; 
void SCSI_SenseCode(uint8_t lun, uint8_t sKey, uint8_t ASC)
{
  SCSI_Sense[SCSI_Sense_Tail].Skey  = sKey;
  SCSI_Sense[SCSI_Sense_Tail].w.ASC = ASC << 8;
  SCSI_Sense_Tail++;
  if (SCSI_Sense_Tail == SENSE_LIST_DEEPTH)
  {
    SCSI_Sense_Tail = 0;
  }
}

SCSI_TEST_UNIT_READY

static int8_t SCSI_TestUnitReady(uint8_t lun, uint8_t *params)
{
  /* case 9 : Hi > D0 */
  if (MSC_BOT_cbw.dDataLength != 0)
  {
    SCSI_SenseCode(MSC_BOT_cbw.bLUN,ILLEGAL_REQUEST,INVALID_CDB);
    return -1;
  }  

  if(USBD_STORAGE_fops->IsReady(lun) !=0 )
  {
    SCSI_SenseCode(lun,NOT_READY, MEDIUM_NOT_PRESENT);
    return -1;
  } 
  MSC_BOT_DataLen = 0;
  return 0;
}

SCSI_REQUEST_SENSE

static int8_t SCSI_RequestSense (uint8_t lun, uint8_t *params)
{
  uint8_t i;

  for(i=0 ; i < REQUEST_SENSE_DATA_LEN ; i++) 
  {
    MSC_BOT_Data[i] = 0;
  }

  MSC_BOT_Data[0]    = 0x70;        
  MSC_BOT_Data[7]    = REQUEST_SENSE_DATA_LEN - 6;    //应减8

  if((SCSI_Sense_Head != SCSI_Sense_Tail)) {

    MSC_BOT_Data[2]     = SCSI_Sense[SCSI_Sense_Head].Skey;        
    MSC_BOT_Data[12]    = SCSI_Sense[SCSI_Sense_Head].w.b.ASCQ;    
    MSC_BOT_Data[13]    = SCSI_Sense[SCSI_Sense_Head].w.b.ASC;    
    SCSI_Sense_Head++;

    if (SCSI_Sense_Head == SENSE_LIST_DEEPTH)
    {
      SCSI_Sense_Head = 0;
    }
  }
  MSC_BOT_DataLen = REQUEST_SENSE_DATA_LEN;  

  if (params[4] <= REQUEST_SENSE_DATA_LEN)
  {
    MSC_BOT_DataLen = params[4];
  }
  return 0;
}

SCSI_INQUIRY

#define USBD_STD_INQUIRY_LENGTH        36

//USB Mass storage
const int8_t  STORAGE_Inquirydata[] = { 

    /* LUN 0 */ 
    0x00,        
    0x80,        
    0x02,        
    0x02,
    (USBD_STD_INQUIRY_LENGTH - 4),//36-4
    0x00,
    0x00,    
    0x00,
    /* Vendor Identification */
    'A', 'L', 'I', 'E', 'N', 'T', 'E', 'K', ' ',//9×Ö½Ú
    /* Product Identification */
    'S', 'P', 'I', ' ', 'F', 'l', 'a', 's', 'h',//15×Ö½Ú
    ' ','D', 'i', 's', 'k', ' ',
    /* Product Revision Level */    
    '1', '.', '0', ' ',                            //4×Ö½Ú        

    /* LUN 1 */
    0x00,
    0x80,        
    0x02,        
    0x02,
    (USBD_STD_INQUIRY_LENGTH - 4),
    0x00,
    0x00,    
    0x00,
    /* Vendor Identification */
    'A', 'L', 'I', 'E', 'N', 'T', 'E', 'K',' ',    //9×Ö½Ú
    /* Product Identification */                
    'S', 'D', ' ', 'F', 'l', 'a', 's', 'h', ' ',//15×Ö½Ú
    'D', 'i', 's', 'k', ' ', ' ',  
    /* Product Revision Level */
    '1', '.', '0' ,' ',                          //4×Ö½Ú
}; 

static int8_t  SCSI_Inquiry(uint8_t lun, uint8_t *params)
{
  uint8_t* pPage;
  uint16_t len;

  if (params[1] & 0x01)/*Evpd is set*/
  {
    pPage = (uint8_t *)MSC_Page00_Inquiry_Data;
    len = LENGTH_INQUIRY_PAGE00;
  }
  else
  {
    pPage = (uint8_t *)&USBD_STORAGE_fops->pInquiry[lun * USBD_STD_INQUIRY_LENGTH];
    len = pPage[4] + 5;

    if (params[4] <= len)
    {
      len = params[4];
    }
  }
  MSC_BOT_DataLen = len;

  while (len) 
  {
    len--;
    MSC_BOT_Data[len] = pPage[len];
  }
  return 0;
}

SCSI_START_STOP_UNIT/SCSI_ALLOW_MEDIUM_REMOVAL

static int8_t SCSI_StartStopUnit(uint8_t lun, uint8_t *params)
{
  MSC_BOT_DataLen = 0;
  return 0;
}

SCSI_MODE_SENSE6

const uint8_t  MSC_Mode_Sense6_data[] = {
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00, 
    0x00,
    0x00
};    
static int8_t SCSI_ModeSense6 (uint8_t lun, uint8_t *params)
{

  uint16_t len = 8 ;
  MSC_BOT_DataLen = len;

  while (len) 
  {
    len--;
    MSC_BOT_Data[len] = MSC_Mode_Sense6_data[len];
  }
  return 0;
}

SCSI_MODE_SENSE10

/* USB Mass storage sense 10  Data */
const uint8_t  MSC_Mode_Sense10_data[] = {
    0x00,
    0x06, 
    0x00, 
    0x00, 
    0x00, 
    0x00, 
    0x00, 
    0x00
};
static int8_t SCSI_ModeSense10 (uint8_t lun, uint8_t *params)
{
 uint16_t len = 8;

 MSC_BOT_DataLen = len;

 while (len) 
  {
    len--;
    MSC_BOT_Data[len] = MSC_Mode_Sense10_data[len];
  }
  return 0;
}

SCSI_READ_FORMAT_CAPACITIES

int8_t STORAGE_GetCapacity (uint8_t lun, uint32_t *block_num, uint32_t *block_size)
{     
    if(lun==1)
    {
        *block_size=512;  
        *block_num=SDCardInfo.CardCapacity/512;
    }else
    {
        *block_size=512;  
        *block_num=1024*1024*12/512;    //SPI FLASHµÄÇ°Ãæ12M×Ö½Ú,ÎļþϵͳÓÃ
    }    
    return 0; 
} 
static int8_t SCSI_ReadFormatCapacity(uint8_t lun, uint8_t *params)
{

  uint32_t blk_size;
  uint32_t blk_nbr;
  uint16_t i;

  for(i=0 ; i < 12 ; i++) 
  {
    MSC_BOT_Data[i] = 0;
  }

  if(USBD_STORAGE_fops->GetCapacity(lun, &blk_nbr, &blk_size) != 0)
  {
    SCSI_SenseCode(lun, NOT_READY, MEDIUM_NOT_PRESENT);
    return -1;
  } 
  else
  {
    MSC_BOT_Data[3] = 0x08;
    MSC_BOT_Data[4] = (uint8_t)(blk_nbr - 1 >> 24);
    MSC_BOT_Data[5] = (uint8_t)(blk_nbr - 1 >> 16);
    MSC_BOT_Data[6] = (uint8_t)(blk_nbr - 1 >>  8);
    MSC_BOT_Data[7] = (uint8_t)(blk_nbr - 1);

    MSC_BOT_Data[8] = 0x02;
    MSC_BOT_Data[9] = (uint8_t)(blk_size >>  16);
    MSC_BOT_Data[10] = (uint8_t)(blk_size >>  8);
    MSC_BOT_Data[11] = (uint8_t)(blk_size);

    MSC_BOT_DataLen = 12;
    return 0;
  }
}

SCSI_READ_CAPACITY10

static int8_t SCSI_ReadCapacity10(uint8_t lun, uint8_t *params)
{

  if(USBD_STORAGE_fops->GetCapacity(lun, &SCSI_blk_nbr, &SCSI_blk_size) != 0)
  {
    SCSI_SenseCode(lun,NOT_READY, MEDIUM_NOT_PRESENT);
    return -1;
  } 
  else
  {

    MSC_BOT_Data[0] = (uint8_t)(SCSI_blk_nbr - 1 >> 24);
    MSC_BOT_Data[1] = (uint8_t)(SCSI_blk_nbr - 1 >> 16);
    MSC_BOT_Data[2] = (uint8_t)(SCSI_blk_nbr - 1 >>  8);
    MSC_BOT_Data[3] = (uint8_t)(SCSI_blk_nbr - 1);

    MSC_BOT_Data[4] = (uint8_t)(SCSI_blk_size >>  24);
    MSC_BOT_Data[5] = (uint8_t)(SCSI_blk_size >>  16);
    MSC_BOT_Data[6] = (uint8_t)(SCSI_blk_size >>  8);
    MSC_BOT_Data[7] = (uint8_t)(SCSI_blk_size);

    MSC_BOT_DataLen = 8;
    return 0;
  }
}

SCSI_VERIFY10

static int8_t SCSI_CheckAddressRange (uint8_t lun , uint32_t blk_offset , uint16_t blk_nbr)
{

  if ((blk_offset + blk_nbr) > SCSI_blk_nbr )
  {
    SCSI_SenseCode(lun, ILLEGAL_REQUEST, ADDRESS_OUT_OF_RANGE);
    return -1;
  }
  return 0;
}
static int8_t SCSI_Verify10(uint8_t lun , uint8_t *params)
{
  if ((params[1]& 0x02) == 0x02) 
  {
    SCSI_SenseCode (lun, ILLEGAL_REQUEST, INVALID_FIELED_IN_COMMAND);
    return -1; /* Error, Verify Mode Not supported*/
  }

  if(SCSI_CheckAddressRange(lun, SCSI_blk_addr, SCSI_blk_len) < 0)
  {
    return -1; /* error */      
  }
  MSC_BOT_DataLen = 0;
  return 0;
}

SCSI_READ10

uint64_t  SCSI_blk_addr;    //¶Ô´óÓÚ4GBµÄSD¿¨,ÒªÓÃ64λ±íʾ
uint32_t  SCSI_blk_len;
static int8_t SCSI_Read10(uint8_t lun , uint8_t *params)
{
  if(MSC_BOT_State == BOT_IDLE)  /* Idle */
  {  
    /* case 10 : Ho <> Di */

    if ((MSC_BOT_cbw.bmFlags & 0x80) != 0x80)
    {
      SCSI_SenseCode(MSC_BOT_cbw.bLUN, ILLEGAL_REQUEST,INVALID_CDB);
      return -1;
    }    

    if(USBD_STORAGE_fops->IsReady(lun) !=0 )
    {
      SCSI_SenseCode(lun,NOT_READY,MEDIUM_NOT_PRESENT);
      return -1;
    } 

    SCSI_blk_addr = (params[2] << 24) | params[3] << 16) | (params[4] <<  8) | params[5];
    SCSI_blk_len =  (params[7] <<  8) | params[8];  

    if( SCSI_CheckAddressRange(lun, SCSI_blk_addr, SCSI_blk_len) < 0)
    {
      return -1; /* error */
    }

    MSC_BOT_State = BOT_DATA_IN;
    SCSI_blk_addr *= SCSI_blk_size;
    SCSI_blk_len  *= SCSI_blk_size;

    /* cases 4,5 : Hi <> Dn */
    if (MSC_BOT_cbw.dDataLength != SCSI_blk_len)
    {
      SCSI_SenseCode(MSC_BOT_cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB);
      return -1;
    }
  }
  MSC_BOT_DataLen = MSC_MEDIA_PACKET;  

  return SCSI_ProcessRead(lun);
}
static int8_t SCSI_ProcessRead (uint8_t lun)
{
  uint32_t len;

  len = MIN(SCSI_blk_len , MSC_MEDIA_PACKET); 

  if( USBD_STORAGE_fops->Read(lun , MSC_BOT_Data, SCSI_blk_addr / SCSI_blk_size, len / SCSI_blk_size) < 0)
  {

    SCSI_SenseCode(lun, HARDWARE_ERROR, UNRECOVERED_READ_ERROR);
    return -1; 
  }


  DCD_EP_Tx (cdev, 
             MSC_IN_EP,
             MSC_BOT_Data,
             len);


  SCSI_blk_addr   += len; 
  SCSI_blk_len    -= len;  

  /* case 6 : Hi = Di */
  MSC_BOT_csw.dDataResidue -= len;

  if (SCSI_blk_len == 0)
  {
    MSC_BOT_State = BOT_LAST_DATA_IN;
  }
  return 0;
}

SCSI_WRITE10

static int8_t SCSI_Write10 (uint8_t lun , uint8_t *params)
{
  if (MSC_BOT_State == BOT_IDLE) /* Idle */
  {
    /* case 8 : Hi <> Do */   
    if ((MSC_BOT_cbw.bmFlags & 0x80) == 0x80)
    {
      SCSI_SenseCode(MSC_BOT_cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB);
      return -1;
    }

    /* Check whether Media is ready */
    if(USBD_STORAGE_fops->IsReady(lun) !=0 )
    {
      SCSI_SenseCode(lun, NOT_READY,MEDIUM_NOT_PRESENT);
      return -1;
    } 

    /* Check If media is write-protected */
    if(USBD_STORAGE_fops->IsWriteProtected(lun) !=0 )
    {
      SCSI_SenseCode(lun,NOT_READY,WRITE_PROTECTED);
      return -1;
    } 


    SCSI_blk_addr = (params[2] << 24) (params[3] << 16) | (params[4] <<  8) |  params[5];
    SCSI_blk_len = (params[7] <<  8) |  params[8];  

    /* check if LBA address is in the right range */
    if(SCSI_CheckAddressRange(lun, SCSI_blk_addr, SCSI_blk_len) < 0)
    {
      return -1; /* error */      
    }

    SCSI_blk_addr *= SCSI_blk_size;
    SCSI_blk_len  *= SCSI_blk_size;

    /* cases 3,11,13 : Hn,Ho <> D0 */
    if (MSC_BOT_cbw.dDataLength != SCSI_blk_len)
    {
      SCSI_SenseCode(MSC_BOT_cbw.bLUN,ILLEGAL_REQUEST, INVALID_CDB);
      return -1;
    }

    /* Prepare EP to receive first data packet */
    MSC_BOT_State = BOT_DATA_OUT;
    //准备接收数据
    DCD_EP_PrepareRx (cdev,
                      MSC_OUT_EP,
                      MSC_BOT_Data, 
                      MIN (SCSI_blk_len, MSC_MEDIA_PACKET));  
  }
  else /* Write Process ongoing */
  {
      //写数据
    return SCSI_ProcessWrite(lun);
  }
  return 0;
}
static int8_t SCSI_ProcessWrite (uint8_t lun)
{
  uint32_t len;

  len = MIN(SCSI_blk_len , MSC_MEDIA_PACKET); 

  if(USBD_STORAGE_fops->Write(lun , MSC_BOT_Data, SCSI_blk_addr / SCSI_blk_size, len / SCSI_blk_size) < 0)
  {
    SCSI_SenseCode(lun, HARDWARE_ERROR, WRITE_FAULT);     
    return -1; 
  }


  SCSI_blk_addr  += len; 
  SCSI_blk_len   -= len; 

  /* case 12 : Ho = Do */
  MSC_BOT_csw.dDataResidue -= len;

  if (SCSI_blk_len == 0)
  {
    MSC_BOT_SendCSW (cdev, CSW_CMD_PASSED);
  }
  else
  {
    /* Prapare EP to Receive next packet */
    DCD_EP_PrepareRx (cdev, MSC_OUT_EP, MSC_BOT_Data,MIN (SCSI_blk_len, MSC_MEDIA_PACKET)); 
  }

  return 0;
}
本文链接为:http://www.usbzh.com/article/detail-1791.html ,欢迎转载,转载请附上本文链接。

HID人机交互QQ群:564808376    UAC音频QQ群:218581009    UVC相机QQ群:331552032    BOT&UASP大容量存储QQ群:258159197    STC-USB单片机QQ群:315457461    USB技术交流QQ群2:580684376    USB技术交流QQ群:952873936     USB技术交流3:1031974172

0 篇笔记 写笔记

关注公众号
  • HID人机交互
  • Linux&USB
  • Linux-USB百科
  • MIDI V1.0
  • TinyUSB
  • UAC音频
  • CDC
  • TYPE-C
  • USB规范
  • USB3测试
  • USB3知识点
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • Windows系统USB
  • USB资源
  • XHCI 1.2b 规范
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

    打开支付宝扫一扫,即可进行扫码打赏哦

    您的支持,是我们前进的动力!