Files
CherryUSB/class/msc/usbd_msc.c

844 lines
28 KiB
C
Raw Normal View History

2021-07-10 18:31:58 +08:00
/**
* @file usbd_msc.c
* @brief
*
* Copyright (c) 2021 Bouffalolab team
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
#include "usbd_core.h"
#include "usbd_scsi.h"
#include "usbd_msc.h"
/* max USB packet size */
#ifndef CONFIG_USB_HS
2021-07-10 18:31:58 +08:00
#define MASS_STORAGE_BULK_EP_MPS 64
#else
#define MASS_STORAGE_BULK_EP_MPS 512
#endif
2021-07-10 18:31:58 +08:00
#define MSD_OUT_EP_IDX 0
#define MSD_IN_EP_IDX 1
/* Describe EndPoints configuration */
static usbd_endpoint_t mass_ep_data[2];
/* MSC Bulk-only Stage */
enum Stage {
/* MSC Bulk-only Stage */
MSC_BS_CBW = 0, /* Command Block Wrapper */
MSC_BS_DATA_OUT = 1, /* Data Out Phase */
MSC_BS_DATA_IN = 2, /* Data In Phase */
MSC_BS_DATA_IN_LAST = 3, /* Data In Last Phase */
MSC_BS_DATA_IN_LAST_STALL = 4, /* Data In Last Phase with Stall */
MSC_BS_CSW = 5, /* Command Status Wrapper */
MSC_BS_ERROR = 6, /* Error */
MSC_BS_RESET = 7, /* Bulk-Only Mass Storage Reset */
};
/* Device data structure */
struct usbd_msc_cfg_private {
/* state of the bulk-only state machine */
enum Stage stage;
struct CBW cbw;
struct CSW csw;
uint8_t max_lun_count;
uint16_t scsi_blk_size;
uint32_t scsi_blk_nbr;
uint32_t scsi_blk_addr;
uint32_t scsi_blk_len;
uint8_t *block_buffer;
2021-07-10 18:31:58 +08:00
} usbd_msc_cfg;
/*memory OK (after a usbd_msc_memory_verify)*/
static bool memOK;
static void usbd_msc_reset(void)
{
usbd_msc_cfg.stage = MSC_BS_CBW;
(void)memset((void *)&usbd_msc_cfg.cbw, 0, sizeof(struct CBW));
(void)memset((void *)&usbd_msc_cfg.csw, 0, sizeof(struct CSW));
usbd_msc_cfg.scsi_blk_addr = 0U;
usbd_msc_cfg.scsi_blk_len = 0U;
usbd_msc_get_cap(0, &usbd_msc_cfg.scsi_blk_nbr, &usbd_msc_cfg.scsi_blk_size);
usbd_msc_cfg.max_lun_count = 0;
if (usbd_msc_cfg.block_buffer) {
free(usbd_msc_cfg.block_buffer);
}
usbd_msc_cfg.block_buffer = malloc(usbd_msc_cfg.scsi_blk_size * sizeof(uint8_t));
2021-07-10 18:31:58 +08:00
}
/**
* @brief Handler called for Class requests not handled by the USB stack.
*
* @param pSetup Information about the request to execute.
* @param len Size of the buffer.
* @param data Buffer containing the request result.
*
* @return 0 on success, negative errno code on fail.
*/
static int msc_storage_class_request_handler(struct usb_setup_packet *pSetup, uint8_t **data, uint32_t *len)
{
switch (pSetup->bRequest) {
case MSC_REQUEST_RESET:
USBD_LOG_DBG("MSC_REQUEST_RESET");
if (pSetup->wLength) {
USBD_LOG_WRN("Invalid length");
return -1;
}
usbd_msc_reset();
break;
case MSC_REQUEST_GET_MAX_LUN:
USBD_LOG_DBG("MSC_REQUEST_GET_MAX_LUN");
if (pSetup->wLength != 1) {
USBD_LOG_WRN("Invalid length");
return -1;
}
*data = (uint8_t *)(&usbd_msc_cfg.max_lun_count);
*len = 1;
break;
default:
USBD_LOG_WRN("Unknown request 0x%02x, value 0x%02x",
pSetup->bRequest, pSetup->wValue);
return -1;
}
return 0;
}
static void usbd_msc_send_csw(void)
{
usbd_msc_cfg.csw.Signature = MSC_CSW_Signature;
if (usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr, (uint8_t *)&usbd_msc_cfg.csw,
sizeof(struct CSW), NULL) != 0) {
USBD_LOG_ERR("usb write failure");
}
usbd_msc_cfg.stage = MSC_BS_CSW;
}
static bool usbd_msc_datain_check(void)
{
if (!usbd_msc_cfg.cbw.DataLength) {
USBD_LOG_WRN("Zero length in CBW");
usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
usbd_msc_send_csw();
return false;
}
if ((usbd_msc_cfg.cbw.Flags & 0x80) == 0) {
usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
usbd_msc_send_csw();
return false;
}
return true;
}
static bool usbd_msc_send_to_host(uint8_t *buffer, uint16_t size)
{
if (size >= usbd_msc_cfg.cbw.DataLength) {
size = usbd_msc_cfg.cbw.DataLength;
usbd_msc_cfg.stage = MSC_BS_DATA_IN_LAST;
} else {
usbd_msc_cfg.stage = MSC_BS_DATA_IN_LAST_STALL;
}
if (usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr, buffer, size, NULL)) {
USBD_LOG_ERR("USB write failed\r\n");
2021-07-10 18:31:58 +08:00
return false;
}
usbd_msc_cfg.csw.DataResidue -= size;
usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
return true;
}
static void usbd_msc_memory_verify(uint8_t *buf, uint16_t size)
{
uint32_t n;
if ((usbd_msc_cfg.scsi_blk_addr + size) > (usbd_msc_cfg.scsi_blk_nbr * usbd_msc_cfg.scsi_blk_size)) {
size = usbd_msc_cfg.scsi_blk_nbr * usbd_msc_cfg.scsi_blk_size - usbd_msc_cfg.scsi_blk_addr;
usbd_msc_cfg.stage = MSC_BS_ERROR;
usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
USBD_LOG_WRN("addr overflow,verify error\r\n");
}
/* beginning of a new block -> load a whole block in RAM */
if (!(usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size)) {
USBD_LOG_DBG("Disk READ sector %d", usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size);
// if (disk_access_read(disk_pdrv, page, addr / BLOCK_SIZE, 1))
// {
// USBD_LOG_ERR("---- Disk Read Error %d", addr / BLOCK_SIZE);
// }
}
/* info are in RAM -> no need to re-read memory */
for (n = 0U; n < size; n++) {
if (usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size + n] != buf[n]) {
USBD_LOG_DBG("Mismatch sector %d offset %d",
usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size, n);
memOK = false;
break;
}
}
usbd_msc_cfg.scsi_blk_addr += size;
usbd_msc_cfg.scsi_blk_len -= size;
usbd_msc_cfg.csw.DataResidue -= size;
if (!usbd_msc_cfg.scsi_blk_len || (usbd_msc_cfg.stage == MSC_BS_CSW)) {
usbd_msc_cfg.csw.Status = (memOK) ? CSW_STATUS_CMD_PASSED : CSW_STATUS_CMD_FAILED;
usbd_msc_send_csw();
}
}
static void usbd_msc_memory_write(uint8_t *buf, uint16_t size)
{
USBD_LOG_DBG("w:%d\r\n", usbd_msc_cfg.scsi_blk_addr);
if ((usbd_msc_cfg.scsi_blk_addr + size) > (usbd_msc_cfg.scsi_blk_nbr * usbd_msc_cfg.scsi_blk_size)) {
size = usbd_msc_cfg.scsi_blk_nbr * usbd_msc_cfg.scsi_blk_size - usbd_msc_cfg.scsi_blk_addr;
usbd_msc_cfg.stage = MSC_BS_ERROR;
usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
USBD_LOG_WRN("addr overflow,write error\r\n");
}
/* we fill an array in RAM of 1 block before writing it in memory */
for (int i = 0; i < size; i++) {
usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size + i] = buf[i];
}
/* if the array is filled, write it in memory */
if ((usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size) + size >= usbd_msc_cfg.scsi_blk_size) {
usbd_msc_sector_write((usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size), usbd_msc_cfg.block_buffer, usbd_msc_cfg.scsi_blk_size);
}
usbd_msc_cfg.scsi_blk_addr += size;
usbd_msc_cfg.scsi_blk_len -= size;
usbd_msc_cfg.csw.DataResidue -= size;
if ((!usbd_msc_cfg.scsi_blk_len) || (usbd_msc_cfg.stage == MSC_BS_CSW)) {
usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
usbd_msc_send_csw();
}
}
static void usbd_msc_memory_read(void)
{
uint32_t transfer_len;
transfer_len = MIN(usbd_msc_cfg.scsi_blk_len, MASS_STORAGE_BULK_EP_MPS);
/* we read an entire block */
if (!(usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size)) {
usbd_msc_sector_read((usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size), usbd_msc_cfg.block_buffer, usbd_msc_cfg.scsi_blk_size);
}
USBD_LOG_DBG("addr:%d\r\n", usbd_msc_cfg.scsi_blk_addr);
usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr,
&usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size], transfer_len, NULL);
usbd_msc_cfg.scsi_blk_addr += transfer_len;
usbd_msc_cfg.scsi_blk_len -= transfer_len;
usbd_msc_cfg.csw.DataResidue -= transfer_len;
if (!usbd_msc_cfg.scsi_blk_len) {
usbd_msc_cfg.stage = MSC_BS_DATA_IN_LAST;
usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
}
}
/*********************************SCSI CMD*******************************************************************/
static bool scsi_test_unit_ready(void)
{
if (usbd_msc_cfg.cbw.DataLength != 0U) {
if ((usbd_msc_cfg.cbw.Flags & 0x80) != 0U) {
USBD_LOG_WRN("Stall IN endpoint\r\n");
usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
} else {
USBD_LOG_WRN("Stall OUT endpoint\r\n");
usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
}
return false;
}
usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
usbd_msc_send_csw();
return true;
}
static bool scsi_request_sense(void)
{
if (!usbd_msc_datain_check()) {
return false;
}
scsi_sense_fixed_resp_t sense_rsp = {
.response_code = 0x70,
.valid = 1
};
sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
sense_rsp.sense_key = 0x00;
sense_rsp.add_sense_code = 0x00;
sense_rsp.add_sense_qualifier = 0x00;
/* Win host requests maximum number of bytes but as all we have is 4 bytes we have
to tell host back that it is all we have, that's why we correct residue */
if (usbd_msc_cfg.csw.DataResidue > sizeof(sense_rsp)) {
usbd_msc_cfg.cbw.DataLength = sizeof(sense_rsp);
usbd_msc_cfg.csw.DataResidue = sizeof(sense_rsp);
}
#if 0
request_sense[ 2] = 0x06; /* UNIT ATTENTION */
request_sense[12] = 0x28; /* Additional Sense Code: Not ready to ready transition */
request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
#endif
#if 0
request_sense[ 2] = 0x02; /* NOT READY */
request_sense[12] = 0x3A; /* Additional Sense Code: Medium not present */
request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
#endif
#if 0
request_sense[ 2] = 0x05; /* ILLEGAL REQUEST */
request_sense[12] = 0x20; /* Additional Sense Code: Invalid command */
request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
#endif
#if 0
request_sense[ 2] = 0x00; /* NO SENSE */
request_sense[12] = 0x00; /* Additional Sense Code: No additional code */
request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
#endif
return usbd_msc_send_to_host((uint8_t *)&sense_rsp, sizeof(sense_rsp));
}
static bool scsi_inquiry_request(void)
{
if (!usbd_msc_datain_check()) {
return false;
}
scsi_inquiry_resp_t inquiry_rsp = {
.is_removable = 1, /* RMB = 1: Removable Medium */
.version = 2,
.response_data_format = 2,
.additional_length = 31
};
// vendor_id, product_id, product_rev is space padded string
memcpy(inquiry_rsp.vendor_id, "BL702USB", sizeof(inquiry_rsp.vendor_id));
memcpy(inquiry_rsp.product_id, "FAT16 RAM DEMO ", sizeof(inquiry_rsp.product_id));
memcpy(inquiry_rsp.product_rev, "1.0 ", sizeof(inquiry_rsp.product_rev));
/* Win host requests maximum number of bytes but as all we have is 4 bytes we have
to tell host back that it is all we have, that's why we correct residue */
if (usbd_msc_cfg.csw.DataResidue > sizeof(inquiry_rsp)) {
usbd_msc_cfg.cbw.DataLength = sizeof(inquiry_rsp);
usbd_msc_cfg.csw.DataResidue = sizeof(inquiry_rsp);
}
return usbd_msc_send_to_host((uint8_t *)&inquiry_rsp, sizeof(inquiry_rsp));
}
static bool scsi_mode_sense_6(void)
{
if (!usbd_msc_datain_check()) {
return false;
}
scsi_mode_sense6_resp_t mode_resp = {
.data_len = 3,
.medium_type = 0,
.write_protected = false,
.reserved = 0,
.block_descriptor_len = 0 // no block descriptor are included
};
/* Win host requests maximum number of bytes but as all we have is 4 bytes we have
to tell host back that it is all we have, that's why we correct residue */
if (usbd_msc_cfg.csw.DataResidue > sizeof(mode_resp)) {
usbd_msc_cfg.cbw.DataLength = sizeof(mode_resp);
usbd_msc_cfg.csw.DataResidue = sizeof(mode_resp);
}
return usbd_msc_send_to_host((uint8_t *)&mode_resp, sizeof(mode_resp));
}
static bool scsi_start_stop_unit(void)
{
// if (!cbw.CB[3]) { /* If power condition modifier is 0 */
// USBD_MSC_MediaReady = cbw.CB[4] & 0x01; /* Media ready = START bit value */
// usbd_msc_start_stop(USBD_MSC_MediaReady);
// cbw.bStatus = CSW_CMD_PASSED; /* Start Stop Unit -> pass */
// USBD_MSC_SetCSW();
// return;
// }
// cbw.bStatus = CSW_CMD_FAILED; /* Start Stop Unit -> fail */
// usbd_msc_send_csw();
usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
usbd_msc_send_csw();
return true;
}
/*
* USB Device MSC SCSI Media Removal Callback
* Parameters: None
* Return Value: None
*/
static bool scsi_media_removal(void)
{
// if (USBD_MSC_CBW.CB[4] & 1) { /* If prevent */
// USBD_MSC_CSW.bStatus = CSW_CMD_FAILED; /* Prevent media removal -> fail */
// } else { /* If allow */
// USBD_MSC_CSW.bStatus = CSW_CMD_PASSED; /* Allow media removal -> pass */
// }
// USBD_MSC_SetCSW();
usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
usbd_msc_send_csw();
return true;
}
static bool scsi_read_format_capacity(void)
{
if (!usbd_msc_datain_check()) {
return false;
}
scsi_read_format_capacity_resp_t read_fmt_capa = {
.list_length = 8, /* Capacity List Length */
.block_num = 0,
.descriptor_type = 2, /* Descriptor Code: Formatted Media */
.block_size_u16 = 0
};
/* Block Count */
read_fmt_capa.block_num = BSWAP32(usbd_msc_cfg.scsi_blk_nbr);
/* Block Length */
read_fmt_capa.block_size_u16 = BSWAP16(usbd_msc_cfg.scsi_blk_size);
/* Win host requests maximum number of bytes but as all we have is 4 bytes we have
to tell host back that it is all we have, that's why we correct residue */
if (usbd_msc_cfg.csw.DataResidue > sizeof(read_fmt_capa)) {
usbd_msc_cfg.cbw.DataLength = sizeof(read_fmt_capa);
usbd_msc_cfg.csw.DataResidue = sizeof(read_fmt_capa);
}
return usbd_msc_send_to_host((uint8_t *)&read_fmt_capa, sizeof(read_fmt_capa));
}
static bool scsi_read_capacity(void)
{
if (!usbd_msc_datain_check()) {
return false;
}
scsi_read_capacity10_resp_t read_capa10;
/* Last Logical Block */
read_capa10.last_lba = BSWAP32((usbd_msc_cfg.scsi_blk_nbr - 1));
/* Block Length */
read_capa10.block_size = BSWAP32(usbd_msc_cfg.scsi_blk_size);
/* Win host requests maximum number of bytes but as all we have is 4 bytes we have
to tell host back that it is all we have, that's why we correct residue */
if (usbd_msc_cfg.csw.DataResidue > sizeof(read_capa10)) {
usbd_msc_cfg.cbw.DataLength = sizeof(read_capa10);
usbd_msc_cfg.csw.DataResidue = sizeof(read_capa10);
}
return usbd_msc_send_to_host((uint8_t *)&read_capa10, sizeof(read_capa10));
}
static bool usbd_msc_read_write_process(void)
{
/* Logical Block Address of First Block */
uint32_t lba;
uint32_t blk_num = 0;
2021-07-10 18:31:58 +08:00
if (!usbd_msc_cfg.cbw.DataLength) {
USBD_LOG_WRN("Zero length in CBW\r\n");
usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
usbd_msc_send_csw();
return false;
}
lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]);
USBD_LOG_DBG("LBA (block) : 0x%x\r\n", lba);
usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size;
/* Number of Blocks to transfer */
switch (usbd_msc_cfg.cbw.CB[0]) {
case SCSI_READ10:
case SCSI_WRITE10:
case SCSI_VERIFY10:
blk_num = GET_BE16(&usbd_msc_cfg.cbw.CB[7]);
2021-07-10 18:31:58 +08:00
break;
case SCSI_READ12:
case SCSI_WRITE12:
blk_num = GET_BE32(&usbd_msc_cfg.cbw.CB[6]);
2021-07-10 18:31:58 +08:00
break;
default:
break;
}
USBD_LOG_DBG("num (block) : 0x%x\r\n", blk_num);
usbd_msc_cfg.scsi_blk_len = blk_num * usbd_msc_cfg.scsi_blk_size;
2021-07-10 18:31:58 +08:00
if ((lba + blk_num) > usbd_msc_cfg.scsi_blk_nbr) {
2021-07-10 18:31:58 +08:00
USBD_LOG_ERR("LBA out of range\r\n");
usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_FAILED;
usbd_msc_send_csw();
return false;
}
if (usbd_msc_cfg.cbw.DataLength != usbd_msc_cfg.scsi_blk_len) {
if ((usbd_msc_cfg.cbw.Flags & 0x80) != 0U) {
USBD_LOG_WRN("read write process error\r\n");
usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
} else {
USBD_LOG_WRN("read write process error\r\n");
usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
}
usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_FAILED;
usbd_msc_send_csw();
return false;
}
return true;
}
static bool scsi_mode_sense_10(void)
{
if (!usbd_msc_datain_check()) {
return false;
}
scsi_mode_10_resp_t mode10_resp = {
.mode_data_length_low = 0x06,
.write_protect = 1,
};
/* Win host requests maximum number of bytes but as all we have is 4 bytes we have
to tell host back that it is all we have, that's why we correct residue */
if (usbd_msc_cfg.csw.DataResidue > sizeof(mode10_resp)) {
usbd_msc_cfg.cbw.DataLength = sizeof(mode10_resp);
usbd_msc_cfg.csw.DataResidue = sizeof(mode10_resp);
}
return usbd_msc_send_to_host((uint8_t *)&mode10_resp, sizeof(mode10_resp));
}
static void usbd_msc_cbw_decode(uint8_t *buf, uint16_t size)
{
if (size != sizeof(usbd_msc_cfg.cbw)) {
USBD_LOG_ERR("size != sizeof(cbw)");
return;
}
memcpy((uint8_t *)&usbd_msc_cfg.cbw, buf, size);
if (usbd_msc_cfg.cbw.Signature != MSC_CBW_Signature) {
USBD_LOG_ERR("CBW Signature Mismatch");
return;
}
usbd_msc_cfg.csw.Tag = usbd_msc_cfg.cbw.Tag;
usbd_msc_cfg.csw.DataResidue = usbd_msc_cfg.cbw.DataLength;
if ((usbd_msc_cfg.cbw.CBLength < 1) || (usbd_msc_cfg.cbw.CBLength > 16) || (usbd_msc_cfg.cbw.LUN != 0U)) {
USBD_LOG_WRN("cbw.CBLength %d", usbd_msc_cfg.cbw.CBLength);
/* Stall data stage */
usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_FAILED;
usbd_msc_send_csw();
} else {
switch (usbd_msc_cfg.cbw.CB[0]) {
case SCSI_TEST_UNIT_READY:
USBD_LOG_DBG(">> TUR");
scsi_test_unit_ready();
break;
case SCSI_REQUEST_SENSE:
USBD_LOG_DBG(">> REQ_SENSE");
scsi_request_sense();
break;
case SCSI_INQUIRY:
USBD_LOG_DBG(">> INQ");
scsi_inquiry_request();
break;
case SCSI_START_STOP_UNIT:
scsi_start_stop_unit();
break;
case SCSI_MEDIA_REMOVAL:
scsi_media_removal();
break;
case SCSI_MODE_SENSE6:
USBD_LOG_DBG(">> MODE_SENSE6");
scsi_mode_sense_6();
break;
case SCSI_MODE_SENSE10:
USBD_LOG_DBG(">> MODE_SENSE10");
scsi_mode_sense_10();
break;
case SCSI_READ_FORMAT_CAPACITIES:
USBD_LOG_DBG(">> READ_FORMAT_CAPACITY");
scsi_read_format_capacity();
break;
case SCSI_READ_CAPACITY:
USBD_LOG_DBG(">> READ_CAPACITY");
scsi_read_capacity();
break;
case SCSI_READ10:
case SCSI_READ12:
USBD_LOG_DBG(">> READ");
if (usbd_msc_read_write_process()) {
if ((usbd_msc_cfg.cbw.Flags & 0x80)) {
usbd_msc_cfg.stage = MSC_BS_DATA_IN;
usbd_msc_memory_read();
} else {
usbd_ep_set_stall(
mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
USBD_LOG_WRN("Stall OUT endpoint");
usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
usbd_msc_send_csw();
}
}
break;
case SCSI_WRITE10:
case SCSI_WRITE12:
USBD_LOG_DBG(">> WRITE");
if (usbd_msc_read_write_process()) {
if (!(usbd_msc_cfg.cbw.Flags & 0x80)) {
usbd_msc_cfg.stage = MSC_BS_DATA_OUT;
} else {
usbd_ep_set_stall(
mass_ep_data[MSD_IN_EP_IDX].ep_addr);
USBD_LOG_WRN("Stall IN endpoint");
usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
usbd_msc_send_csw();
}
}
break;
case SCSI_VERIFY10:
USBD_LOG_DBG(">> VERIFY10");
if (!(usbd_msc_cfg.cbw.CB[1] & 0x02)) {
usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
usbd_msc_send_csw();
break;
}
if (usbd_msc_read_write_process()) {
if (!(usbd_msc_cfg.cbw.Flags & 0x80)) {
usbd_msc_cfg.stage = MSC_BS_DATA_OUT;
memOK = true;
} else {
usbd_ep_set_stall(
mass_ep_data[MSD_IN_EP_IDX].ep_addr);
USBD_LOG_WRN("Stall IN endpoint");
usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
usbd_msc_send_csw();
}
}
break;
default:
USBD_LOG_WRN(">> default CB[0] %x", usbd_msc_cfg.cbw.CB[0]);
/* Stall data stage */
usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
usbd_msc_cfg.stage = MSC_BS_ERROR;
break;
}
}
}
static void mass_storage_bulk_out(uint8_t ep)
{
uint32_t bytes_read = 0U;
uint8_t bo_buf[MASS_STORAGE_BULK_EP_MPS];
usbd_ep_read(ep, bo_buf, MASS_STORAGE_BULK_EP_MPS,
&bytes_read);
switch (usbd_msc_cfg.stage) {
/*the device has to decode the CBW received*/
case MSC_BS_CBW:
USBD_LOG_DBG("> BO - MSC_BS_CBW\r\n");
usbd_msc_cbw_decode(bo_buf, bytes_read);
break;
/*the device has to receive data from the host*/
case MSC_BS_DATA_OUT:
switch (usbd_msc_cfg.cbw.CB[0]) {
case SCSI_WRITE10:
case SCSI_WRITE12:
/* USBD_LOG_DBG("> BO - PROC_CBW WR");*/
usbd_msc_memory_write(bo_buf, bytes_read);
break;
case SCSI_VERIFY10:
USBD_LOG_DBG("> BO - PROC_CBW VER\r\n");
usbd_msc_memory_verify(bo_buf, bytes_read);
break;
default:
USBD_LOG_ERR("> BO - PROC_CBW default <<ERROR!!!>>\r\n");
break;
}
break;
case MSC_BS_CSW:
break;
/*an error has occurred: stall endpoint and send CSW*/
default:
USBD_LOG_WRN("Stall OUT endpoint, stage: %d\r\n", usbd_msc_cfg.stage);
// usbd_ep_set_stall(ep);
// usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
// usbd_msc_send_csw();
break;
}
/*set ep ack to recv next data*/
usbd_ep_read(ep, NULL, 0, NULL);
}
/**
* @brief EP Bulk IN handler, used to send data to the Host
*
* @param ep Endpoint address.
* @param ep_status Endpoint status code.
*
* @return N/A.
*/
static void mass_storage_bulk_in(uint8_t ep)
{
USBD_LOG_DBG("I:%d\r\n", usbd_msc_cfg.stage);
switch (usbd_msc_cfg.stage) {
/*the device has to send data to the host*/
case MSC_BS_DATA_IN:
switch (usbd_msc_cfg.cbw.CB[0]) {
case SCSI_READ10:
case SCSI_READ12:
/* USBD_LOG_DBG("< BI - PROC_CBW READ"); */
usbd_msc_memory_read();
break;
default:
USBD_LOG_ERR("< BI-PROC_CBW default <<ERROR!!>>\r\n");
break;
}
break;
/*the device has to send a CSW*/
case MSC_BS_DATA_IN_LAST:
USBD_LOG_DBG("< BI - MSC_BS_DATA_IN_LAST\r\n");
usbd_msc_send_csw();
break;
case MSC_BS_DATA_IN_LAST_STALL:
USBD_LOG_WRN("Stall IN endpoint, stage: %d\r\n", usbd_msc_cfg.stage);
//usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
usbd_msc_send_csw();
break;
/*the host has received the CSW -> we wait a CBW*/
case MSC_BS_CSW:
USBD_LOG_DBG("< BI - MSC_BS_CSW\r\n");
usbd_msc_cfg.stage = MSC_BS_CBW;
break;
default:
break;
}
}
void msc_storage_notify_handler(uint8_t event, void *arg)
{
switch (event) {
case USB_EVENT_RESET:
usbd_msc_reset();
break;
default:
break;
}
}
static usbd_class_t msc_class;
static usbd_interface_t msc_intf = {
.class_handler = msc_storage_class_request_handler,
.vendor_handler = NULL,
.notify_handler = msc_storage_notify_handler,
};
void usbd_msc_class_init(uint8_t out_ep, uint8_t in_ep)
{
msc_class.name = "usbd_msc";
usbd_class_register(&msc_class);
usbd_class_add_interface(&msc_class, &msc_intf);
mass_ep_data[0].ep_addr = out_ep;
mass_ep_data[0].ep_cb = mass_storage_bulk_out;
mass_ep_data[1].ep_addr = in_ep;
mass_ep_data[1].ep_cb = mass_storage_bulk_in;
usbd_interface_add_endpoint(&msc_intf, &mass_ep_data[0]);
usbd_interface_add_endpoint(&msc_intf, &mass_ep_data[1]);
}