/******************************************************************************* * Copyright 2019-2020 Microchip FPGA Embedded Systems Solutions. * * SPDX-License-Identifier: MIT * * PolarFire SoC MSS USB Driver Stack * USB Logical Layer (USB-LL) * USBH-MSC class driver. * * * This file implements Host side MSC class specific initialization * and request handling. * */ #include "mss_usb_host_msc.h" #include "mss_usb_host.h" #include "mss_usb_std_def.h" #include #include #include "mpfs_hal/mss_hal.h" #ifdef __cplusplus extern "C" { #endif #ifdef MSS_USB_HOST_ENABLED /***************************************************************************//** Constant values internally used by USBH-MSC driver. */ #define MSS_USBH_MSC_CLASS_ID 0x08u #define MSS_USBH_MSC_SUBCLASS_ID 0x06u #define MSS_USBH_MSC_PROTOCOL_ID 0x50u #define MSS_USBH_MSC_DRIVER_ID (uint32_t)((MSS_USBH_MSC_CLASS_ID << 16) |\ (MSS_USBH_MSC_SUBCLASS_ID << 8)|\ (MSS_USBH_MSC_PROTOCOL_ID) ) #define SCSI_COMMAND_PASSED 0x01u #define SCSI_COMMAND_FAILED 0x02u #define SCSI_COMMAND_PHASE_ERR 0x03u #define USBH_MSC_BULK_TX_PIPE MSS_USB_TX_EP_1 #define USBH_MSC_BULK_RX_PIPE MSS_USB_RX_EP_1 #define USBH_MSC_BULK_TX_PIPE_FIFOADDR 0x100u #define USBH_MSC_BULK_RX_PIPE_FIFOADDR 0x300u #define USBH_MSC_BULK_TX_PIPE_FIFOSZ 0x200u #define USBH_MSC_BULK_RX_PIPE_FIFOSZ 0x200u /***************************************************************************//** Types internally used by USBH-MSC driver. */ typedef enum { MSC_BOT_IDLE, MSC_BOT_COMMAND_PHASE, MSC_BOT_DATA_PHASE, MSC_BOT_STATUS_PHASE, MSC_BOT_STATUS_WAITCOMPLETE, MSC_BOT_ERROR }g_msc_bot_state_t; typedef struct { uint8_t num; uint16_t maxpktsz; } msd_tdev_ep_t; typedef struct { uint8_t* cbuf; /* always31bytes */ uint8_t* dbuf; uint32_t dbuf_len; uint8_t* sbuf; /* statusalways 13bytes */ uint8_t volatile st; } scsi_command_t; /***************************************************************************//** Private functions declarations for USBH-MSC driver. */ static scsi_command_t g_scsi_command = {0}; static volatile uint8_t g_usbh_msc_alloc_event = 0u; static uint8_t g_usbh_msc_release_event = 0u; static volatile uint8_t g_usbh_msc_cep_event = 0u; static volatile uint8_t g_usbh_msc_tx_event = 0u; static volatile uint8_t g_usbh_msc_rx_event = 0u; #if defined(__GNUC__) static msd_cbw_t g_bot_cbw __attribute__ ((aligned (4))) = {0}; static uint8_t g_bot_csw[13] __attribute__ ((aligned (4))) = {0}; /* Store Inquiry response */ static uint8_t g_bot_inquiry[36] __attribute__ ((aligned (4))) = {0}; /* Store Read_capacity response */ static uint8_t g_bot_readcap[8] __attribute__ ((aligned (4)))= {0}; #elif defined(__ICCARM__) #pragma data_alignment = 4 static msd_cbw_t g_bot_cbw = {0}; static uint8_t g_bot_csw[13] = {0}; static uint8_t g_bot_inquiry[36] = {0}; static uint8_t g_bot_readcap[8] = {0}; #elif defined(__CC_ARM) __align(4) static msd_cbw_t g_bot_cbw = {0}; __align(4) static uint8_t g_bot_csw[13] = {0}; __align(4) static uint8_t g_bot_inquiry[36] = {0}; __align(4) static uint8_t g_bot_readcap[8] = {0}; #endif static volatile g_msc_bot_state_t g_msc_bot_state = MSC_BOT_IDLE; static uint8_t g_msd_tdev_addr = 0u; static mss_usb_state_t msd_tdev_state = MSS_USB_NOT_ATTACHED_STATE; static uint8_t g_msd_conf_desc[32] = {0}; /* Data type changed from uint8_t since ASSERT in start_control_xfr() */ static uint32_t g_tdev_max_lun_idx = 0u; static msd_tdev_ep_t g_tdev_in_ep = {0}; static msd_tdev_ep_t g_tdev_out_ep = {0}; static volatile mss_usbh_msc_state_t g_msc_state = USBH_MSC_IDLE; static mss_usbh_msc_err_code_t g_msch_error_code = USBH_MSC_NO_ERROR; static mss_usbh_msc_user_cb_t* g_msch_user_cb; static uint8_t usbh_msc_allocate_cb(uint8_t tdev_addr); static uint8_t usbh_msc_release_cb(uint8_t tdev_addr); static uint8_t usbh_msc_cep_done_cb(uint8_t tdev_addr, uint8_t status, uint32_t count); static uint8_t usbh_msc_tx_complete_cb(uint8_t tdev_addr, uint8_t status, uint32_t count); static uint8_t usbh_msc_rx_cb(uint8_t tdev_addr, uint8_t status, uint32_t count); static mss_usbh_msc_err_code_t MSS_USBH_MSC_validate_class_desc(uint8_t* p_cd); static mss_usbh_msc_err_code_t MSS_USBH_MSC_extract_tdev_ep_desc(void); static void usbh_msc_construct_class_req(uint8_t* buf, uint8_t req, uint8_t bInterfaceNumber); /***************************************************************************//** Definition of Class call-back functions used by USBH driver. */ mss_usbh_class_cb_t msd_class = { MSS_USBH_MSC_DRIVER_ID, usbh_msc_allocate_cb, usbh_msc_release_cb, usbh_msc_cep_done_cb, usbh_msc_tx_complete_cb, usbh_msc_rx_cb, 0 }; /******************************************************************************* * EXPORTED API Functions ******************************************************************************/ /****************************************************************************** * See mss_usb_host_msc.h for details of how to use this function. */ void MSS_USBH_MSC_init ( mss_usbh_msc_user_cb_t* user_sb ) { g_msc_state = USBH_MSC_IDLE; g_msc_bot_state = MSC_BOT_IDLE; g_tdev_max_lun_idx = 0u; memset(g_msd_conf_desc, 0u, sizeof(g_msd_conf_desc)); memset(g_bot_inquiry, 0u, sizeof(g_bot_inquiry)); memset(&g_bot_csw, 0u, sizeof(g_bot_csw)); g_tdev_in_ep.maxpktsz = 0u; g_tdev_in_ep.num = 0u; g_tdev_out_ep.maxpktsz = 0u; g_tdev_out_ep.num = 0u; g_msd_tdev_addr = 0u; g_msch_user_cb = user_sb; g_scsi_command.cbuf = (uint8_t*)0; g_scsi_command.dbuf = (uint8_t*)0; g_scsi_command.sbuf = (uint8_t*)0; g_scsi_command.dbuf_len = 0u; g_scsi_command.st = 0u; memset(g_bot_readcap, 0u, sizeof(g_bot_readcap)); } /****************************************************************************** * See mss_usb_host_msc.h for details of how to use this function. */ void* MSS_USBH_MSC_get_handle ( void ) { return ((void*)&msd_class); } /****************************************************************************** * See mss_usb_host_msc.h for details of how to use this function. */ void MSS_USBH_MSC_task ( void ) { uint8_t std_req_buf[USB_SETUP_PKT_LEN] = {0}; static volatile uint32_t wait_mili = 0u; switch (g_msc_state) { case USBH_MSC_IDLE: if (g_usbh_msc_alloc_event) { g_usbh_msc_alloc_event = 0u; g_msc_state = USBH_MSC_GET_CLASS_DESCR; } break; case USBH_MSC_GET_CLASS_DESCR: msd_tdev_state = MSS_USBH_get_tdev_state(g_msd_tdev_addr); if (MSS_USB_ADDRESS_STATE == msd_tdev_state) { mss_usb_ep_state_t cep_st; cep_st = MSS_USBH_get_cep_state(); if (MSS_USB_CEP_IDLE == cep_st) { MSS_USBH_configure_control_pipe(g_msd_tdev_addr); memset(std_req_buf, 0u, 8*(sizeof(uint8_t))); MSS_USBH_construct_get_descr_command(std_req_buf, USB_STD_REQ_DATA_DIR_IN, USB_STANDARD_REQUEST, USB_STD_REQ_RECIPIENT_DEVICE, USB_STD_REQ_GET_DESCRIPTOR, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0u, /*stringID*/ 32u);/* config_Desc_length */ g_msc_state = USBH_MSC_WAIT_GET_CLASS_DESCR; MSS_USBH_start_control_xfr(std_req_buf, (uint8_t*)&g_msd_conf_desc, USB_STD_REQ_DATA_DIR_IN, 32u); /* config_Desc_length */ } } break; case USBH_MSC_WAIT_GET_CLASS_DESCR: if (g_usbh_msc_cep_event) { mss_usbh_msc_err_code_t res = USBH_MSC_NO_ERROR; g_usbh_msc_cep_event = 0u; res = MSS_USBH_MSC_validate_class_desc(0); if (res == 0u) { g_msc_state = USBH_MSC_SET_CONFIG; } else { g_msc_state = USBH_MSC_ERROR; g_msch_error_code = res; } res = MSS_USBH_MSC_extract_tdev_ep_desc(); if (res == 0u) { g_msc_state = USBH_MSC_SET_CONFIG; } else { g_msc_state = USBH_MSC_ERROR; g_msch_error_code = res; } } break; case USBH_MSC_SET_CONFIG: if (0 != g_msch_user_cb->msch_valid_config) { g_msch_user_cb->msch_valid_config(); } memset(std_req_buf, 0u, 8*(sizeof(uint8_t))); std_req_buf[1] = USB_STD_REQ_SET_CONFIG; /* bConfigurationValue*/ std_req_buf[2] = g_msd_conf_desc[5]; g_msc_state = USBH_MSC_WAIT_SET_CONFIG; MSS_USBH_start_control_xfr(std_req_buf, (uint8_t*)&g_msd_conf_desc, USB_STD_REQ_DATA_DIR_IN, 0u); break; case USBH_MSC_WAIT_SET_CONFIG: if (g_usbh_msc_cep_event) { g_usbh_msc_cep_event = 0u; wait_mili = MSS_USBH_get_milis(); g_msc_state = USBH_MSC_WAIT_DEV_SETTLE; } break; case USBH_MSC_WAIT_DEV_SETTLE: /* After SET_CONFIG command, we must give time for device to settle * down as per spec */ if ((MSS_USBH_get_milis() - wait_mili) > 60u) { g_msc_state = USBH_MSC_GET_MAX_LUN; } break; case USBH_MSC_GET_MAX_LUN: usbh_msc_construct_class_req(std_req_buf, USB_MSC_BOT_REQ_GET_MAX_LUN, g_msd_conf_desc[11]);/* bInterfaceNum */ MSS_USBH_configure_control_pipe(g_msd_tdev_addr); g_msc_state = USBH_MSC_WAIT_GET_MAX_LUN; MSS_USBH_start_control_xfr(std_req_buf, (uint8_t*)&g_tdev_max_lun_idx, USB_STD_REQ_DATA_DIR_IN, 1u); break; case USBH_MSC_WAIT_GET_MAX_LUN: if (g_usbh_msc_cep_event) { if (MSS_USB_EP_XFR_SUCCESS == g_usbh_msc_cep_event) { g_msc_state = USBH_MSC_CONFIG_BULK_ENDPOINTS; } else if (MSS_USB_EP_STALL_RCVD == g_usbh_msc_cep_event) { /* Stall for this command means single LUN support by Target */ g_tdev_max_lun_idx = 0u; g_msc_state = USBH_MSC_CLR_CEP_STALL; /* Clear Stall using CLR_FEATURE Command */ } else { ASSERT(0); /* Invalid CEP event */ } g_usbh_msc_cep_event = 0u; } break; case USBH_MSC_CLR_CEP_STALL: { /* Std clear CEP STALL command */ uint8_t temp_buf[8u] = {0x02u, 0x01u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u}; g_msc_state = USBH_MSC_WAIT_CLR_CEP_STALL; /* clear feature Endpoint halt on Tdev Out EP */ MSS_USBH_start_control_xfr(temp_buf, (uint8_t*)&g_tdev_max_lun_idx, USB_STD_REQ_DATA_DIR_IN, 0u); break; } case USBH_MSC_WAIT_CLR_CEP_STALL: if (g_usbh_msc_cep_event) { if (MSS_USB_EP_XFR_SUCCESS == g_usbh_msc_cep_event) { g_msc_state = USBH_MSC_CONFIG_BULK_ENDPOINTS; } else if (MSS_USB_EP_STALL_RCVD == g_usbh_msc_cep_event) { g_msc_state = USBH_MSC_ERROR; g_msch_error_code = USBH_MSC_CLR_CEP_STALL_ERROR; } else { ASSERT(0);/* Invalid CEP event */ } g_usbh_msc_cep_event = 0u; } break; case USBH_MSC_CONFIG_BULK_ENDPOINTS: /* Configure BULK TX/RX Endpoints */ MSS_USBH_configure_out_pipe(g_msd_tdev_addr, USBH_MSC_BULK_TX_PIPE, g_tdev_out_ep.num, /* Targeted OUTEP Num */ USBH_MSC_BULK_TX_PIPE_FIFOADDR, USBH_MSC_BULK_TX_PIPE_FIFOSZ, g_tdev_out_ep.maxpktsz, 1, DMA_DISABLE, MSS_USB_DMA_CHANNEL1, MSS_USB_XFR_BULK, NO_ZLP_TO_XFR, 32768);/* Max NAKLimit value */ MSS_USBH_configure_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, /* Targeted OutEP Num */ USBH_MSC_BULK_RX_PIPE_FIFOADDR, USBH_MSC_BULK_RX_PIPE_FIFOSZ, g_tdev_in_ep.maxpktsz, 1, DMA_DISABLE, MSS_USB_DMA_CHANNEL2, MSS_USB_XFR_BULK, NO_ZLP_TO_XFR, 32768);/* Max NAKLimit value */ g_msc_state = USBH_MSC_TEST_UNIT_READY_CPHASE; break; case USBH_MSC_TEST_UNIT_READY_CPHASE: MSS_USBH_MSC_construct_cbw_cb6byte(USB_MSC_SCSI_TEST_UNIT_READY, 0u, &g_bot_cbw); MSS_USBH_write_out_pipe(g_msd_tdev_addr, USBH_MSC_BULK_TX_PIPE, g_tdev_out_ep.num, g_tdev_out_ep.maxpktsz, (uint8_t*)&g_bot_cbw, 31u); /* std_cbw_len */ g_msc_state = USBH_MSC_TEST_UNIT_READY_SPHASE; break; case USBH_MSC_TEST_UNIT_READY_SPHASE: if (g_usbh_msc_tx_event) { g_usbh_msc_tx_event = 0u; g_msc_state = USBH_MSC_TEST_UNIT_READY_WAITCOMPLETE; MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, (uint8_t*)&g_bot_csw, 13u);/* standard status msg */ } break; case USBH_MSC_TEST_UNIT_READY_WAITCOMPLETE: if (g_usbh_msc_rx_event) { g_usbh_msc_rx_event = 0u; /* decode the received status */ if (g_bot_csw[12] == 0x00u) /* PASSED */ { g_msc_state = USBH_MSC_SCSI_INQUIRY_CPHASE; } else if (g_bot_csw[12] == 0x01u) /* FAILED */ { g_msc_state = USBH_MSC_SCSI_REQSENSE_CPHASE; } else if (g_bot_csw[12] == 0x02u) { ASSERT(0); /* phase error, reset recovery required */ } } break; case USBH_MSC_SCSI_INQUIRY_CPHASE: MSS_USBH_MSC_construct_cbw_cb6byte(USB_MSC_SCSI_INQUIRY, 36u, /*standard INQUIRY response size */ &g_bot_cbw); g_msc_state = USBH_MSC_SCSI_INQUIRY_DPHASE; MSS_USBH_write_out_pipe(g_msd_tdev_addr, USBH_MSC_BULK_TX_PIPE, g_tdev_out_ep.num, g_tdev_out_ep.maxpktsz, (uint8_t*)&g_bot_cbw, 31u); /* std_cbw_len */ break; case USBH_MSC_SCSI_INQUIRY_DPHASE: if (g_usbh_msc_tx_event) { g_usbh_msc_tx_event = 0u; g_msc_state = USBH_MSC_SCSI_INQUIRY_SPHASE; MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, g_bot_inquiry, 36u);/* standard INQUIRY response size */ } break; case USBH_MSC_SCSI_INQUIRY_SPHASE: if (g_usbh_msc_rx_event) { g_usbh_msc_rx_event = 0u; g_msc_state = USBH_MSC_SCSI_INQUIRY_WAITCOMPLETE; MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, (uint8_t*)&g_bot_csw, 13u);/* standard status msg */ } break; case USBH_MSC_SCSI_INQUIRY_WAITCOMPLETE: if (g_usbh_msc_rx_event) { g_usbh_msc_rx_event = 0x0U; g_msc_state = USBH_MSC_SCSI_READ_CAPACITY_CPHASE; } break; case USBH_MSC_SCSI_REQSENSE_CPHASE: { /* This is a standard command buffer for REQUEST SENSE command */ uint8_t cbuf[] = {0x55U, 0x53U, 0x42U, 0x43U, 0x28U, 0x2EU, 0x59U, 0xAFU, 0x12U, 0x00U, 0x00U, 0x00U, 0x80U, 0x00U, 0x0CU, 0x03U, 0x00U, 0x00U, 0x00U, 0x12U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; g_msc_state = USBH_MSC_SCSI_REQSENSE_DPHASE; MSS_USBH_write_out_pipe(g_msd_tdev_addr, USBH_MSC_BULK_TX_PIPE, g_tdev_out_ep.num, g_tdev_out_ep.maxpktsz, (uint8_t*)&cbuf, 31u); /*std_cbw_len */ } break; case USBH_MSC_SCSI_REQSENSE_DPHASE: if (g_usbh_msc_tx_event) { /* standard response from device for REQUEST sense command is 18 bytes */ static uint8_t reqsense_buf[18]; g_usbh_msc_tx_event = 0u; g_msc_state = USBH_MSC_SCSI_REQSENSE_SPHASE; MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, reqsense_buf, 18u); } break; case USBH_MSC_SCSI_REQSENSE_SPHASE: if (g_usbh_msc_rx_event) { g_usbh_msc_rx_event = 0u; g_msc_state = USBH_MSC_SCSI_REQSENSE_WAITCOMPLETE; MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, (uint8_t*)&g_bot_csw, 13u);/* standard status msg */ } break; case USBH_MSC_SCSI_REQSENSE_WAITCOMPLETE: if (g_usbh_msc_rx_event) { g_usbh_msc_rx_event = 0u; g_msc_state = USBH_MSC_TEST_UNIT_READY_CPHASE; } break; case USBH_MSC_SCSI_READ_CAPACITY_CPHASE: MSS_USBH_MSC_construct_cbw_cb10byte(USB_MSC_SCSI_READ_CAPACITY_10, 0u, 0u, 0u, 0u, &g_bot_cbw); g_msc_state = USBH_MSC_SCSI_READ_CAPACITY_DPHASE; MSS_USBH_write_out_pipe(g_msd_tdev_addr, USBH_MSC_BULK_TX_PIPE, g_tdev_out_ep.num, g_tdev_out_ep.maxpktsz, (uint8_t*)&g_bot_cbw, 31u); /* std_cbw_len */ break; case USBH_MSC_SCSI_READ_CAPACITY_DPHASE: if (g_usbh_msc_tx_event) { g_usbh_msc_rx_event = 0u; g_msc_state = USBH_MSC_SCSI_READ_CAPACITY_SPHASE; /* standard Read Capacity response size is 8bytes. * We need this info later so keep it in g_bot_readcap */ MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, g_bot_readcap, 8u); } break; case USBH_MSC_SCSI_READ_CAPACITY_SPHASE: if (g_usbh_msc_rx_event) { g_usbh_msc_rx_event = 0u; g_msc_state = USBH_MSC_SCSI_READ_CAPACITY_WAITCOMPLETE; MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, (uint8_t*)&g_bot_csw, 13u);/* standard status msg */ } break; case USBH_MSC_SCSI_READ_CAPACITY_WAITCOMPLETE: if (g_usbh_msc_rx_event) { uint32_t sector_size = 0u; g_usbh_msc_rx_event = 0u; sector_size = ((g_bot_readcap[4u] << 24u) | /*sector size */ (g_bot_readcap[5u] << 16u) | (g_bot_readcap[6u] << 8u) | g_bot_readcap[7u]); if (sector_size != 512u) { g_msc_state = USBH_MSC_ERROR; g_msch_error_code = USBH_MSC_SECTOR_SIZE_NOT_SUPPORTED; } else { g_msc_state = USBH_MSC_DEVICE_READY; if (0 != g_msch_user_cb->msch_tdev_ready) { g_msch_user_cb->msch_tdev_ready(); } } } break; case USBH_MSC_DEVICE_READY: /* * The USBH-MSC driver will stay in this state. * Being in this state, the SCSI requests will be executed as * user the user makes those requests through the APIs of this driver. */ break; case USBH_MSC_ERROR: { static uint8_t error = 0u; if (0u == error) { error = 1u; if (0 != g_msch_user_cb->msch_error) { g_msch_user_cb->msch_error(g_msch_error_code); } } } break; case USBH_MSC_BOT_RETRY: { static uint32_t first_mili = 0; static uint32_t crrent_mili = 0; if (0u == first_mili) { first_mili = MSS_USBH_get_milis(); } crrent_mili = MSS_USBH_get_milis(); /* Found that the Sandisc devices are reporting NAKTIMEOUT error. * This is mostly happening when moving from DATA phase to Status * phase of the SCSI read/write command. At this stage restarting * the status phase is able to get the device to respond properly. * The NAKLIMIT is device specific. The WIndows hosts allow up to * 5Sec before declaring NAKTIMOUT. MSS USB allows to wait up to * ~4Sec. Hence waiting for another 1 Sec here hoping that the * device is recovered by then. */ if (MSC_BOT_STATUS_WAITCOMPLETE == g_msc_bot_state) { if ((crrent_mili - first_mili) >= 1000u) { first_mili = 0u; crrent_mili = 0u; MSS_USBH_abort_in_pipe(USBH_MSC_BULK_RX_PIPE); MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, g_scsi_command.sbuf, 13u); g_msc_state = USBH_MSC_DEVICE_READY; } } } break; default: { ASSERT(0); /*Reset recovery should be tried.*/ } break; } } /******************************************************************************* * See mss_usb_host_msc.h for details of how to use this function. */ static void usbh_msc_construct_class_req ( uint8_t* buf, uint8_t req, uint8_t bInterfaceNumber ) { /* This implementation is as per MSC class spec */ buf[0] = 0x21u; /* bmRequestType */ buf[1] = req; /* bmRequest */ buf[2] = 0x00u; buf[3] = 0x00u; /* wValue */ buf[4] = bInterfaceNumber; buf[5] = 0x00u; /* wIndex */ if (req == USB_MSC_BOT_REQ_GET_MAX_LUN) { buf[6] = 0x01u; buf[0] = 0xA1u; /* bmRequestType */ } else if (req == USB_MSC_BOT_REQ_BMS_RESET) { buf[6] = 0x00u; buf[0] = 0x21u; /* bmRequestType */ } else { ASSERT(0);/* invalid MSC class class request */ } buf[7] = 0x00u; } /******************************************************************************* * See mss_usb_host_msc.h for details of how to use this function. */ void MSS_USBH_MSC_construct_cbw_cb6byte ( uint8_t command_opcode, uint32_t data_xfr_len, msd_cbw_t* buf ) { /* This implementation is as per MSC class spec*/ /* Inquiry, Request_sense, Test_unit_ready commands (cb10 byte commands) */ memset(buf, 0u, 31*(sizeof(uint8_t))); buf->dCBWSignature = USB_MSC_BOT_CBW_SIGNATURE; buf->dCBWTag = 0x20304050U; buf->dCBWDataTransferLength = data_xfr_len; if (USB_MSC_SCSI_TEST_UNIT_READY == command_opcode) { buf->bCBWFlags = 0x00u; } else if ((USB_MSC_SCSI_INQUIRY == command_opcode) || (USB_MSC_SCSI_REQUEST_SENSE == command_opcode)) { buf->bCBWFlags = 0x80u; } else { ASSERT(0);/* invalid cb6byte command */ } buf->bCBWCBLength = 0x06u; buf->CBWCB[0] = command_opcode; buf->CBWCB[4] = data_xfr_len; } /******************************************************************************* * See mss_usb_host_msc.h for details of how to use this function. */ void MSS_USBH_MSC_construct_cbw_cb10byte ( uint8_t command_opcode, uint8_t lun, uint32_t lb_addr, uint16_t num_of_lb, uint16_t lb_size, msd_cbw_t* buf ) { /* This implementation is as per MSC class spec */ /* Read_capacity, Read10, Write10 commands (cb10 byte commands) */ memset(buf, 0u, 31*(sizeof(uint8_t))); buf->dCBWSignature = USB_MSC_BOT_CBW_SIGNATURE; buf->dCBWTag = 0x20304050U; if (USB_MSC_SCSI_WRITE_10 == command_opcode) { buf->bCBWFlags = 0x00u; /* H2D */ } else if ((USB_MSC_SCSI_READ_10 == command_opcode) || (USB_MSC_SCSI_READ_CAPACITY_10 == command_opcode)) { buf->bCBWFlags = 0x80u; /* D2H */ } buf->bCBWCBLength = 0x0Au; buf->CBWCB[0] = command_opcode; if ((USB_MSC_SCSI_READ_10 == command_opcode) || (USB_MSC_SCSI_WRITE_10 == command_opcode)) { buf->dCBWDataTransferLength = (num_of_lb * lb_size); /* Transfer length */ buf->CBWCB[1] = lun; /* Lun Number */ buf->CBWCB[2] = (uint8_t)((lb_addr >> 24) & 0xFFU); /* MSB first */ buf->CBWCB[3] = (uint8_t)((lb_addr >> 16) & 0xFFU); buf->CBWCB[4] = (uint8_t)((lb_addr >> 8) & 0xFFU); buf->CBWCB[5] = (uint8_t)(lb_addr & 0xFFU); buf->CBWCB[7] = (uint8_t)((num_of_lb >> 8) & 0xFFU); /* MSB first */ buf->CBWCB[8] = (uint8_t)(num_of_lb & 0xFFU); } else if ((USB_MSC_SCSI_READ_CAPACITY_10 == command_opcode)) { buf->dCBWDataTransferLength = 0x08u; /* Read Capacity Transfer length */ } else { ASSERT(0);/* invalid cb10byte command */ } } /******************************************************************************* * See mss_usb_host_msc.h for details of how to use this function. */ mss_usbh_msc_state_t MSS_USBH_MSC_get_state ( void ) { return(g_msc_state); } uint8_t MSS_USBH_MSC_scsi_req ( uint8_t* command_buf, /* always31bytes */ uint8_t* data_buf, uint32_t data_buf_len, uint8_t* status_buf /* status always 13bytes */ ) { g_scsi_command.cbuf = command_buf; g_scsi_command.dbuf = data_buf; g_scsi_command.dbuf_len = data_buf_len; g_scsi_command.sbuf = status_buf; g_msc_bot_state = MSC_BOT_COMMAND_PHASE; MSS_USBH_write_out_pipe(g_msd_tdev_addr, USBH_MSC_BULK_TX_PIPE, g_tdev_out_ep.num, g_tdev_out_ep.maxpktsz, g_scsi_command.cbuf, 31u); /* std_cbw_len */ return(0); } /******************************************************************************* * See mss_usb_host_msc.h for details of how to use this function. */ uint8_t MSS_USBH_MSC_is_scsi_req_complete ( void ) { return (g_scsi_command.st); } /******************************************************************************* * See mss_usb_host_msc.h for details of how to use this function. */ uint32_t MSS_USBH_MSC_get_sector_count ( void ) { /* * TODO:Check this * Read_capacity_10 command returns the Last LAB i.e. Address of the * last Logical block. Hence Number of logical blocks(sectors) is (Last LBA+1) */ return ((g_bot_readcap[0u] << 24u) | (g_bot_readcap[1u] << 16u) | (g_bot_readcap[2u] << 8u) | g_bot_readcap[3u]); } /******************************************************************************* * See mss_usb_host_msc.h for details of how to use this function. */ uint32_t MSS_USBH_MSC_get_sector_size ( void ) { /* * Return the logical block(sector) size in bytes. */ return ((g_bot_readcap[4u] << 24u) | (g_bot_readcap[5u] << 16u) | (g_bot_readcap[6u] << 8u) | g_bot_readcap[7u]); } /******************************************************************************* * See mss_usb_host_msc.h for details of how to use this function. */ int8_t MSS_USBH_MSC_read ( uint8_t* buf, uint32_t sector, uint32_t count ) { if (0u == g_scsi_command.st) { g_scsi_command.st = 1u; MSS_USBH_MSC_construct_cbw_cb10byte(USB_MSC_SCSI_READ_10, 0u, sector, count, 512u, &g_bot_cbw); MSS_USBH_MSC_scsi_req((uint8_t*)&g_bot_cbw, buf, (count*512u), (uint8_t*)&g_bot_csw); return (0); } else { return (-1); /* previous command is in progress */ } } /******************************************************************************* * See mss_usb_host_msc.h for details of how to use this function. */ int8_t MSS_USBH_MSC_write ( uint8_t* buf, uint32_t sector, uint32_t count ) { if (0u == g_scsi_command.st) { g_scsi_command.st = 1u; MSS_USBH_MSC_construct_cbw_cb10byte(USB_MSC_SCSI_WRITE_10, 0u, sector, count, 512u, &g_bot_cbw); MSS_USBH_MSC_scsi_req((uint8_t*)&g_bot_cbw, buf, (count*512u), (uint8_t*)&g_bot_csw); return (0); } else { return (-1); /* Previous command is in progress */ } } /******************************************************************************* * Internal Functions ******************************************************************************/ /* * This Call-back function is executed when the USBH-MSC driver is allocated * to the attached device by USBH driver. */ uint8_t usbh_msc_allocate_cb ( uint8_t tdev_addr ) { g_msd_tdev_addr = tdev_addr; g_usbh_msc_alloc_event = 1u; return(USB_SUCCESS); } /* * This Call-back function is executed when the USBH-MSC driver is released * from the attached device by USBH driver. */ uint8_t usbh_msc_release_cb ( uint8_t tdev_addr ) { g_msc_state = USBH_MSC_IDLE; g_msc_bot_state = MSC_BOT_IDLE; g_tdev_max_lun_idx = 0u; memset(g_msd_conf_desc, 0u, sizeof(g_msd_conf_desc)); memset(g_bot_inquiry, 0u, sizeof(g_bot_inquiry)); memset(&g_bot_csw, 0u, sizeof(g_bot_csw)); g_tdev_in_ep.maxpktsz = 0u; g_tdev_in_ep.num = 0u; g_tdev_out_ep.maxpktsz = 0u; g_tdev_out_ep.num = 0u; g_msd_tdev_addr = 0u; g_scsi_command.cbuf = (uint8_t*)0; g_scsi_command.dbuf = (uint8_t*)0; g_scsi_command.sbuf = (uint8_t*)0; g_scsi_command.dbuf_len = 0u; g_scsi_command.st = 0u; memset(g_bot_readcap, 0u, sizeof(g_bot_readcap)); MSS_USB_CIF_tx_ep_disable_irq(USBH_MSC_BULK_RX_PIPE); MSS_USB_CIF_tx_ep_clr_csrreg(USBH_MSC_BULK_RX_PIPE); MSS_USB_CIF_dma_clr_ctrlreg(MSS_USB_DMA_CHANNEL2); MSS_USB_CIF_rx_ep_disable_irq(USBH_MSC_BULK_TX_PIPE); MSS_USB_CIF_rx_ep_clr_csrreg(USBH_MSC_BULK_TX_PIPE); MSS_USB_CIF_dma_clr_ctrlreg(MSS_USB_DMA_CHANNEL1); if (0 != g_msch_user_cb->msch_driver_released) { g_msch_user_cb->msch_driver_released(); } return(USB_SUCCESS); } /* * This Call-back function is executed when the control transfer initiated by this * driver is complete. */ uint8_t usbh_msc_cep_done_cb ( uint8_t tdev_addr, uint8_t status, uint32_t count ) { g_usbh_msc_cep_event = status; return (USB_SUCCESS); } /* * This Call-back function is executed when the data OUT transfer initiated by * this driver is complete. */ uint8_t usbh_msc_tx_complete_cb ( uint8_t tdev_addr, uint8_t status, uint32_t count ) { if (g_msc_state < USBH_MSC_DEVICE_READY) { g_usbh_msc_tx_event = 1u; } else { if (0u == status) { switch (g_msc_bot_state) { case MSC_BOT_COMMAND_PHASE: if (0u == g_scsi_command.dbuf_len)/* zdl request */ { g_msc_bot_state = MSC_BOT_STATUS_WAITCOMPLETE; MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, g_scsi_command.sbuf, 13u); } else { g_msc_bot_state = MSC_BOT_DATA_PHASE; if (g_scsi_command.cbuf[12] & 0x80U) /* bmCBWFLags field -- Read Command */ { MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, g_scsi_command.dbuf, g_scsi_command.dbuf_len); } else /* write command */ { MSS_USBH_write_out_pipe(g_msd_tdev_addr, USBH_MSC_BULK_TX_PIPE, g_tdev_out_ep.num, g_tdev_out_ep.maxpktsz, g_scsi_command.dbuf, g_scsi_command.dbuf_len); /* data_length */ } } break; case MSC_BOT_DATA_PHASE: if (count == g_scsi_command.dbuf_len) { g_msc_bot_state = MSC_BOT_STATUS_WAITCOMPLETE; MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, g_scsi_command.sbuf, 13u); } else { ASSERT(0); /* at this point all data must be transfered */ } break; case MSC_BOT_STATUS_WAITCOMPLETE: break; default: ASSERT(0); /* g_msc_bot_state must not be in any other state */ break; } } else if (MSS_USB_EP_NAK_TOUT & status) { /* Device responding with NAKs. Retry */ g_msc_state = USBH_MSC_BOT_RETRY; } else { ASSERT(0);/* Handling any other error. Not yet supported */ } } return (USB_SUCCESS); } /* * This Call-back function is executed when the data IN transfer initiated by * this driver is complete. */ uint8_t usbh_msc_rx_cb ( uint8_t tdev_addr, uint8_t status, uint32_t count ) { if (g_msc_state < USBH_MSC_DEVICE_READY) { g_usbh_msc_rx_event = 1u; } else { if (0x0U == status) { switch (g_msc_bot_state) { case MSC_BOT_DATA_PHASE: if (count == g_scsi_command.dbuf_len) { g_msc_bot_state = MSC_BOT_STATUS_WAITCOMPLETE; MSS_USBH_read_in_pipe(g_msd_tdev_addr, USBH_MSC_BULK_RX_PIPE, g_tdev_in_ep.num, g_tdev_in_ep.maxpktsz, g_scsi_command.sbuf, 13u); } else { ASSERT(0);/* at this point all data must be transferred */ } break; case MSC_BOT_STATUS_WAITCOMPLETE: g_usbh_msc_rx_event = 0u; g_scsi_command.st = 0u; g_msc_bot_state = MSC_BOT_IDLE; break; default: ASSERT(0);/*g_msc_bot_state must not be in any other state */ break; } } else if (MSS_USB_EP_NAK_TOUT & status) { /* Device responding with NAKs. Retry */ g_msc_state = USBH_MSC_BOT_RETRY; } else { ASSERT(0);/* Handling any other error. Not yet supported */ } } return (USB_SUCCESS); } /* * This function validates the MSC class descriptors. */ mss_usbh_msc_err_code_t MSS_USBH_MSC_validate_class_desc ( uint8_t* p_cd ) { return(USBH_MSC_NO_ERROR); } /* * This function extract the endpoint information from the Config Descriptor. */ mss_usbh_msc_err_code_t MSS_USBH_MSC_extract_tdev_ep_desc ( void ) { mss_usbh_msc_err_code_t error = USBH_MSC_NO_ERROR; if (!(g_msd_conf_desc[21u] & USB_EP_DESCR_ATTR_BULK)) /* FirstEP Attributes Not BulkEP */ { error = USBH_MSC_WRONG_DESCR; } if (!(g_msd_conf_desc[28u] & USB_EP_DESCR_ATTR_BULK)) /*SecondEP Attributes Not BulkEP */ { error = USBH_MSC_WRONG_DESCR; } if (g_msd_conf_desc[20u] & USB_STD_REQ_DATA_DIR_MASK) /* TdevEP is IN type */ { g_tdev_in_ep.num = (g_msd_conf_desc[20u] & 0x7fu); g_tdev_in_ep.maxpktsz = (uint16_t)((g_msd_conf_desc[23u] << 8u) | (g_msd_conf_desc[22u])); g_tdev_out_ep.num = (g_msd_conf_desc[27u] & 0x7fu); g_tdev_out_ep.maxpktsz = (uint16_t)((g_msd_conf_desc[30u] << 8u) | (g_msd_conf_desc[29u])); } else if (g_msd_conf_desc[27u] & USB_STD_REQ_DATA_DIR_MASK) { g_tdev_in_ep.num = (g_msd_conf_desc[27u] & 0x7fu); g_tdev_in_ep.maxpktsz = (uint16_t)((g_msd_conf_desc[30u] << 8u) | (g_msd_conf_desc[29u])); g_tdev_out_ep.num = (g_msd_conf_desc[20u] & 0x7fu); g_tdev_out_ep.maxpktsz = (uint16_t)((g_msd_conf_desc[23u] << 8u) | (g_msd_conf_desc[22u])); } else { error = USBH_MSC_WRONG_DESCR; } return(error); } #endif /* MSS_USB_HOST_ENABLED */ #ifdef __cplusplus } #endif