diff options
Diffstat (limited to 'os/hal/src/usbh/hal_usbh_msd.c')
-rw-r--r-- | os/hal/src/usbh/hal_usbh_msd.c | 849 |
1 files changed, 437 insertions, 412 deletions
diff --git a/os/hal/src/usbh/hal_usbh_msd.c b/os/hal/src/usbh/hal_usbh_msd.c index 6869a74..7233a0b 100644 --- a/os/hal/src/usbh/hal_usbh_msd.c +++ b/os/hal/src/usbh/hal_usbh_msd.c @@ -1,6 +1,6 @@ /* - ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio - Copyright (C) 2015 Diego Ismirlian, TISA, (dismirlian (at) google's mail) + ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio + Copyright (C) 2015..2017 Diego Ismirlian, (dismirlian (at) google's mail) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ */ #include "hal.h" -#include "hal_usbh.h" #if HAL_USBH_USE_MSD @@ -28,9 +27,6 @@ #include "usbh/dev/msd.h" #include "usbh/internal.h" -//#pragma GCC optimize("Og") - - #if USBHMSD_DEBUG_ENABLE_TRACE #define udbgf(f, ...) usbDbgPrintf(f, ##__VA_ARGS__) #define udbg(f, ...) usbDbgPuts(f, ##__VA_ARGS__) @@ -63,26 +59,39 @@ #define uerr(f, ...) do {} while(0) #endif +static void _lun_object_deinit(USBHMassStorageLUNDriver *lunp); +/*===========================================================================*/ +/* USB Class driver loader for MSD */ +/*===========================================================================*/ +struct USBHMassStorageDriver { + /* inherited from abstract class driver */ + _usbh_base_classdriver_data + usbh_ep_t epin; + usbh_ep_t epout; + uint8_t ifnum; + uint8_t max_lun; + uint32_t tag; -/*===========================================================================*/ -/* USB Class driver loader for MSD */ -/*===========================================================================*/ + USBHMassStorageLUNDriver *luns; +}; -USBHMassStorageDriver USBHMSD[HAL_USBHMSD_MAX_INSTANCES]; +static USBHMassStorageDriver USBHMSD[HAL_USBHMSD_MAX_INSTANCES]; +static void _msd_init(void); static usbh_baseclassdriver_t *_msd_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem); static void _msd_unload(usbh_baseclassdriver_t *drv); static const usbh_classdriver_vmt_t class_driver_vmt = { + _msd_init, _msd_load, _msd_unload }; const usbh_classdriverinfo_t usbhmsdClassDriverInfo = { - 0x08, 0x06, 0x50, "MSD", &class_driver_vmt + "MSD", &class_driver_vmt }; #define MSD_REQ_RESET 0xFF @@ -91,18 +100,17 @@ const usbh_classdriverinfo_t usbhmsdClassDriverInfo = { static usbh_baseclassdriver_t *_msd_load(usbh_device_t *dev, const uint8_t *descriptor, uint16_t rem) { int i; USBHMassStorageDriver *msdp; - uint8_t luns; // should declare it here to eliminate 'control bypass initialization' warning - usbh_urbstatus_t stat; // should declare it here to eliminate 'control bypass initialization' warning + uint8_t luns; + usbh_urbstatus_t stat; - if ((rem < descriptor[0]) || (descriptor[1] != USBH_DT_INTERFACE)) + if (_usbh_match_descriptor(descriptor, rem, USBH_DT_INTERFACE, + 0x08, 0x06, 0x50) != HAL_SUCCESS) return NULL; const usbh_interface_descriptor_t * const ifdesc = (const usbh_interface_descriptor_t *)descriptor; if ((ifdesc->bAlternateSetting != 0) - || (ifdesc->bNumEndpoints < 2) - || (ifdesc->bInterfaceSubClass != 0x06) - || (ifdesc->bInterfaceProtocol != 0x50)) { + || (ifdesc->bNumEndpoints < 2)) { return NULL; } @@ -157,10 +165,10 @@ alloc_ok: /* read the number of LUNs */ uinfo("Reading Max LUN:"); - USBH_DEFINE_BUFFER(uint8_t, buff[4]); + USBH_DEFINE_BUFFER(uint8_t buff[4]); stat = usbhControlRequest(dev, - USBH_CLASSIN(USBH_REQTYPE_INTERFACE, MSD_GET_MAX_LUN, 0, msdp->ifnum), - 1, buff); + USBH_REQTYPE_CLASSIN(USBH_REQTYPE_RECIP_INTERFACE), + MSD_GET_MAX_LUN, 0, msdp->ifnum, 1, buff); if (stat == USBH_URBSTATUS_OK) { msdp->max_lun = buff[0] + 1; uinfof("\tmax_lun = %d", msdp->max_lun); @@ -188,13 +196,7 @@ alloc_ok: MSBLKD[i].next = msdp->luns; msdp->luns = &MSBLKD[i]; MSBLKD[i].msdp = msdp; - - osalSysLock(); - MSBLKD[i].state = BLK_ACTIVE; /* transition directly to active, instead of BLK_STOP */ - osalSysUnlock(); - - /* connect the LUN (TODO: review if it's best to leave the LUN disconnected) */ - usbhmsdLUNConnect(&MSBLKD[i]); + MSBLKD[i].state = BLK_ACTIVE; luns--; } } @@ -211,36 +213,24 @@ static void _msd_unload(usbh_baseclassdriver_t *drv) { USBHMassStorageDriver *const msdp = (USBHMassStorageDriver *)drv; USBHMassStorageLUNDriver *lunp = msdp->luns; - osalMutexLock(&msdp->mtx); - osalSysLock(); - usbhEPCloseS(&msdp->epin); - usbhEPCloseS(&msdp->epout); + /* disconnect all LUNs */ while (lunp) { - lunp->state = BLK_STOP; + usbhmsdLUNDisconnect(lunp); + _lun_object_deinit(lunp); lunp = lunp->next; } - osalSysUnlock(); - osalMutexUnlock(&msdp->mtx); - /* now that the LUNs are idle, deinit them */ - lunp = msdp->luns; - osalSysLock(); - while (lunp) { - usbhmsdLUNObjectInit(lunp); - lunp = lunp->next; - } - osalSysUnlock(); + usbhEPClose(&msdp->epin); + usbhEPClose(&msdp->epout); } /*===========================================================================*/ -/* MSD Class driver operations (Bulk-Only transport) */ +/* MSD Class driver operations (Bulk-Only transport) */ /*===========================================================================*/ - - /* USB Bulk Only Transport SCSI Command block wrapper */ -PACKED_STRUCT { +typedef PACKED_STRUCT { uint32_t dCBWSignature; uint32_t dCBWTag; uint32_t dCBWDataTransferLength; @@ -253,9 +243,8 @@ PACKED_STRUCT { #define MSD_CBWFLAGS_D2H 0x80 #define MSD_CBWFLAGS_H2D 0x00 - /* USB Bulk Only Transport SCSI Command status wrapper */ -PACKED_STRUCT { +typedef PACKED_STRUCT { uint32_t dCSWSignature; uint32_t dCSWTag; uint32_t dCSWDataResidue; @@ -263,34 +252,174 @@ PACKED_STRUCT { } msd_csw_t; #define MSD_CSW_SIGNATURE 0x53425355 - -typedef union { - msd_cbw_t cbw; - msd_csw_t csw; +typedef struct { + msd_cbw_t *cbw; + uint8_t csw_status; + uint32_t data_processed; } msd_transaction_t; typedef enum { - MSD_TRANSACTIONRESULT_OK, - MSD_TRANSACTIONRESULT_DISCONNECTED, - MSD_TRANSACTIONRESULT_STALL, - MSD_TRANSACTIONRESULT_BUS_ERROR, - MSD_TRANSACTIONRESULT_SYNC_ERROR -} msd_transaction_result_t; + MSD_BOTRESULT_OK, + MSD_BOTRESULT_DISCONNECTED, + MSD_BOTRESULT_ERROR +} msd_bot_result_t; typedef enum { - MSD_COMMANDRESULT_PASSED = 0, - MSD_COMMANDRESULT_FAILED = 1, - MSD_COMMANDRESULT_PHASE_ERROR = 2 -} msd_command_result_t; - -typedef struct { - msd_transaction_result_t tres; - msd_command_result_t cres; + MSD_RESULT_OK = MSD_BOTRESULT_OK, + MSD_RESULT_DISCONNECTED = MSD_BOTRESULT_DISCONNECTED, + MSD_RESULT_TRANSPORT_ERROR = MSD_BOTRESULT_ERROR, + MSD_RESULT_FAILED } msd_result_t; +#define CSW_STATUS_PASSED 0 +#define CSW_STATUS_FAILED 1 +#define CSW_STATUS_PHASE_ERROR 2 + +static bool _msd_bot_reset(USBHMassStorageDriver *msdp) { + + usbh_urbstatus_t res; + res = usbhControlRequest(msdp->dev, + USBH_REQTYPE_CLASSOUT(USBH_REQTYPE_RECIP_INTERFACE), + 0xFF, 0, msdp->ifnum, 0, NULL); + if (res != USBH_URBSTATUS_OK) { + return FALSE; + } + + osalThreadSleepMilliseconds(100); + + return usbhEPReset(&msdp->epin) && usbhEPReset(&msdp->epout); +} + +static msd_bot_result_t _msd_bot_transaction(msd_transaction_t *tran, USBHMassStorageLUNDriver *lunp, void *data) { + + uint32_t data_actual_len, actual_len; + usbh_urbstatus_t status; + USBH_DEFINE_BUFFER(msd_csw_t csw); + + tran->cbw->bCBWLUN = (uint8_t)(lunp - &lunp->msdp->luns[0]); + tran->cbw->dCBWSignature = MSD_CBW_SIGNATURE; + tran->cbw->dCBWTag = ++lunp->msdp->tag; + tran->data_processed = 0; + + /* control phase */ + status = usbhBulkTransfer(&lunp->msdp->epout, tran->cbw, + sizeof(*tran->cbw), &actual_len, OSAL_MS2I(1000)); + + if (status == USBH_URBSTATUS_CANCELLED) { + uerr("\tMSD: Control phase: USBH_URBSTATUS_CANCELLED"); + return MSD_BOTRESULT_DISCONNECTED; + } + + if ((status != USBH_URBSTATUS_OK) || (actual_len != sizeof(*tran->cbw))) { + uerrf("\tMSD: Control phase: status = %d (!= OK), actual_len = %d (expected to send %d)", + status, actual_len, sizeof(*tran->cbw)); + _msd_bot_reset(lunp->msdp); + return MSD_BOTRESULT_ERROR; + } + + + /* data phase */ + data_actual_len = 0; + if (tran->cbw->dCBWDataTransferLength) { + usbh_ep_t *const ep = tran->cbw->bmCBWFlags & MSD_CBWFLAGS_D2H ? &lunp->msdp->epin : &lunp->msdp->epout; + status = usbhBulkTransfer( + ep, + data, + tran->cbw->dCBWDataTransferLength, + &data_actual_len, OSAL_MS2I(20000)); + + if (status == USBH_URBSTATUS_CANCELLED) { + uerr("\tMSD: Data phase: USBH_URBSTATUS_CANCELLED"); + return MSD_BOTRESULT_DISCONNECTED; + } + + if (status == USBH_URBSTATUS_STALL) { + uerrf("\tMSD: Data phase: USBH_URBSTATUS_STALL, clear halt"); + status = (usbhEPReset(ep) == HAL_SUCCESS) ? USBH_URBSTATUS_OK : USBH_URBSTATUS_ERROR; + } + + if (status != USBH_URBSTATUS_OK) { + uerrf("\tMSD: Data phase: status = %d (!= OK), resetting", status); + _msd_bot_reset(lunp->msdp); + return MSD_BOTRESULT_ERROR; + } + } + + + /* status phase */ + status = usbhBulkTransfer(&lunp->msdp->epin, &csw, + sizeof(csw), &actual_len, OSAL_MS2I(1000)); + + if (status == USBH_URBSTATUS_STALL) { + uwarn("\tMSD: Status phase: USBH_URBSTATUS_STALL, clear halt and retry"); + + status = (usbhEPReset(&lunp->msdp->epin) == HAL_SUCCESS) ? USBH_URBSTATUS_OK : USBH_URBSTATUS_ERROR; + + if (status == USBH_URBSTATUS_OK) { + status = usbhBulkTransfer(&lunp->msdp->epin, &csw, + sizeof(csw), &actual_len, OSAL_MS2I(1000)); + } + } + + if (status == USBH_URBSTATUS_CANCELLED) { + uerr("\tMSD: Status phase: USBH_URBSTATUS_CANCELLED"); + return MSD_BOTRESULT_DISCONNECTED; + } + + if (status != USBH_URBSTATUS_OK) { + uerrf("\tMSD: Status phase: status = %d (!= OK), resetting", status); + _msd_bot_reset(lunp->msdp); + return MSD_BOTRESULT_ERROR; + } + + /* validate CSW */ + if ((actual_len != sizeof(csw)) + || (csw.dCSWSignature != MSD_CSW_SIGNATURE) + || (csw.dCSWTag != lunp->msdp->tag) + || (csw.bCSWStatus >= CSW_STATUS_PHASE_ERROR)) { + /* CSW is not valid */ + uerrf("\tMSD: Status phase: Invalid CSW: len=%d, dCSWSignature=%x, dCSWTag=%x (expected %x), bCSWStatus=%d, resetting", + actual_len, + csw.dCSWSignature, + csw.dCSWTag, + lunp->msdp->tag, + csw.bCSWStatus); + _msd_bot_reset(lunp->msdp); + return MSD_BOTRESULT_ERROR; + } + + /* check if CSW is meaningful */ + if ((csw.bCSWStatus != CSW_STATUS_PHASE_ERROR) + && (csw.dCSWDataResidue > tran->cbw->dCBWDataTransferLength)) { + /* CSW is not meaningful */ + uerrf("\tMSD: Status phase: CSW not meaningful: bCSWStatus=%d, dCSWDataResidue=%u, dCBWDataTransferLength=%u, resetting", + csw.bCSWStatus, + csw.dCSWDataResidue, + tran->cbw->dCBWDataTransferLength); + _msd_bot_reset(lunp->msdp); + return MSD_BOTRESULT_ERROR; + } + + if (csw.bCSWStatus == CSW_STATUS_PHASE_ERROR) { + uerr("\tMSD: Status phase: Phase error, resetting"); + _msd_bot_reset(lunp->msdp); + return MSD_BOTRESULT_ERROR; + } + + tran->data_processed = tran->cbw->dCBWDataTransferLength - csw.dCSWDataResidue; + if (data_actual_len < tran->data_processed) { + tran->data_processed = data_actual_len; + } + + tran->csw_status = csw.bCSWStatus; + + return MSD_BOTRESULT_OK; +} + + /* ----------------------------------------------------- */ -/* SCSI Commands */ +/* SCSI Commands */ /* ----------------------------------------------------- */ /* Read 10 and Write 10 */ @@ -299,7 +428,7 @@ typedef struct { /* Request sense */ #define SCSI_CMD_REQUEST_SENSE 0x03 -PACKED_STRUCT { +typedef PACKED_STRUCT { uint8_t byte[18]; } scsi_sense_response_t; @@ -333,7 +462,7 @@ PACKED_STRUCT { /* Inquiry */ #define SCSI_CMD_INQUIRY 0x12 -PACKED_STRUCT { +typedef PACKED_STRUCT { uint8_t peripheral; uint8_t removable; uint8_t version; @@ -349,14 +478,14 @@ PACKED_STRUCT { /* Read Capacity 10 */ #define SCSI_CMD_READ_CAPACITY_10 0x25 -PACKED_STRUCT { +typedef PACKED_STRUCT { uint32_t last_block_addr; uint32_t block_size; } scsi_readcapacity10_response_t; /* Start/Stop Unit */ #define SCSI_CMD_START_STOP_UNIT 0x1B -PACKED_STRUCT { +typedef PACKED_STRUCT { uint8_t op_code; uint8_t lun_immed; uint8_t res1; @@ -368,223 +497,187 @@ PACKED_STRUCT { /* test unit ready */ #define SCSI_CMD_TEST_UNIT_READY 0x00 -/* Other commands, TODO: use or remove them -#define SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E -#define SCSI_CMD_VERIFY_10 0x2F -#define SCSI_CMD_SEND_DIAGNOSTIC 0x1D -#define SCSI_CMD_MODE_SENSE_6 0x1A -*/ +static msd_result_t scsi_requestsense(USBHMassStorageLUNDriver *lunp, scsi_sense_response_t *resp); -static inline void _prepare_cbw(msd_transaction_t *tran, USBHMassStorageLUNDriver *lunp) { - tran->cbw.bCBWLUN = (uint8_t)(lunp - &lunp->msdp->luns[0]); - memset(&tran->cbw.CBWCB, 0, sizeof(tran->cbw.CBWCB)); -} - -static msd_transaction_result_t _msd_transaction(msd_transaction_t *tran, USBHMassStorageLUNDriver *lunp, void *data) { +static msd_result_t _scsi_perform_transaction(USBHMassStorageLUNDriver *lunp, + msd_transaction_t *transaction, void *data) { - uint32_t actual_len; - usbh_urbstatus_t status; - - tran->cbw.dCBWSignature = MSD_CBW_SIGNATURE; - tran->cbw.dCBWTag = ++lunp->msdp->tag; - - /* control phase */ - status = usbhBulkTransfer(&lunp->msdp->epout, &tran->cbw, - sizeof(tran->cbw), &actual_len, MS2ST(1000)); - - if (status == USBH_URBSTATUS_CANCELLED) { - uerr("\tMSD: Control phase: USBH_URBSTATUS_CANCELLED"); - return MSD_TRANSACTIONRESULT_DISCONNECTED; - } else if (status == USBH_URBSTATUS_STALL) { - uerr("\tMSD: Control phase: USBH_URBSTATUS_STALL"); - return MSD_TRANSACTIONRESULT_STALL; - } else if (status != USBH_URBSTATUS_OK) { - uerrf("\tMSD: Control phase: status = %d, != OK", status); - return MSD_TRANSACTIONRESULT_BUS_ERROR; - } else if (actual_len != sizeof(tran->cbw)) { - uerrf("\tMSD: Control phase: wrong actual_len = %d", actual_len); - return MSD_TRANSACTIONRESULT_BUS_ERROR; + msd_bot_result_t res; + res = _msd_bot_transaction(transaction, lunp, data); + if (res != MSD_BOTRESULT_OK) { + return (msd_result_t)res; } - - /* data phase */ - if (tran->cbw.dCBWDataTransferLength) { - status = usbhBulkTransfer( - tran->cbw.bmCBWFlags & MSD_CBWFLAGS_D2H ? &lunp->msdp->epin : &lunp->msdp->epout, - data, - tran->cbw.dCBWDataTransferLength, - &actual_len, MS2ST(20000)); - - if (status == USBH_URBSTATUS_CANCELLED) { - uerr("\tMSD: Data phase: USBH_URBSTATUS_CANCELLED"); - return MSD_TRANSACTIONRESULT_DISCONNECTED; - } else if (status == USBH_URBSTATUS_STALL) { - uerr("\tMSD: Data phase: USBH_URBSTATUS_STALL"); - return MSD_TRANSACTIONRESULT_STALL; - } else if (status != USBH_URBSTATUS_OK) { - uerrf("\tMSD: Data phase: status = %d, != OK", status); - return MSD_TRANSACTIONRESULT_BUS_ERROR; - } else if (actual_len != tran->cbw.dCBWDataTransferLength) { - uerrf("\tMSD: Data phase: wrong actual_len = %d", actual_len); - return MSD_TRANSACTIONRESULT_BUS_ERROR; + if (transaction->csw_status == CSW_STATUS_FAILED) { + if (transaction->cbw->CBWCB[0] != SCSI_CMD_REQUEST_SENSE) { + /* do auto-sense (except for SCSI_CMD_REQUEST_SENSE!) */ + uwarn("\tMSD: Command failed, auto-sense"); + USBH_DEFINE_BUFFER(scsi_sense_response_t sense); + if (scsi_requestsense(lunp, &sense) == MSD_RESULT_OK) { + uwarnf("\tMSD: REQUEST SENSE: Sense key=%x, ASC=%02x, ASCQ=%02x", + sense.byte[2] & 0xf, sense.byte[12], sense.byte[13]); + } } + return MSD_RESULT_FAILED; } - - /* status phase */ - status = usbhBulkTransfer(&lunp->msdp->epin, &tran->csw, - sizeof(tran->csw), &actual_len, MS2ST(1000)); - - if (status == USBH_URBSTATUS_CANCELLED) { - uerr("\tMSD: Status phase: USBH_URBSTATUS_CANCELLED"); - return MSD_TRANSACTIONRESULT_DISCONNECTED; - } else if (status == USBH_URBSTATUS_STALL) { - uerr("\tMSD: Status phase: USBH_URBSTATUS_STALL"); - return MSD_TRANSACTIONRESULT_STALL; - } else if (status != USBH_URBSTATUS_OK) { - uerrf("\tMSD: Status phase: status = %d, != OK", status); - return MSD_TRANSACTIONRESULT_BUS_ERROR; - } else if (actual_len != sizeof(tran->csw)) { - uerrf("\tMSD: Status phase: wrong actual_len = %d", actual_len); - return MSD_TRANSACTIONRESULT_BUS_ERROR; - } else if (tran->csw.dCSWSignature != MSD_CSW_SIGNATURE) { - uerr("\tMSD: Status phase: wrong signature"); - return MSD_TRANSACTIONRESULT_BUS_ERROR; - } else if (tran->csw.dCSWTag != lunp->msdp->tag) { - uerrf("\tMSD: Status phase: wrong tag (expected %d, got %d)", - lunp->msdp->tag, tran->csw.dCSWTag); - return MSD_TRANSACTIONRESULT_SYNC_ERROR; - } - - if (tran->csw.dCSWDataResidue) { - uwarnf("\tMSD: Residue=%d", tran->csw.dCSWDataResidue); - } - - return MSD_TRANSACTIONRESULT_OK; + return MSD_RESULT_OK; } - static msd_result_t scsi_inquiry(USBHMassStorageLUNDriver *lunp, scsi_inquiry_response_t *resp) { + USBH_DEFINE_BUFFER(msd_cbw_t cbw); msd_transaction_t transaction; msd_result_t res; - _prepare_cbw(&transaction, lunp); - transaction.cbw.dCBWDataTransferLength = sizeof(scsi_inquiry_response_t); - transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_D2H; - transaction.cbw.bCBWCBLength = 6; - transaction.cbw.CBWCB[0] = SCSI_CMD_INQUIRY; - transaction.cbw.CBWCB[4] = sizeof(scsi_inquiry_response_t); - - res.tres = _msd_transaction(&transaction, lunp, resp); - if (res.tres == MSD_TRANSACTIONRESULT_OK) { - res.cres = (msd_command_result_t) transaction.csw.bCSWStatus; + memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB)); + cbw.dCBWDataTransferLength = sizeof(scsi_inquiry_response_t); + cbw.bmCBWFlags = MSD_CBWFLAGS_D2H; + cbw.bCBWCBLength = 6; + cbw.CBWCB[0] = SCSI_CMD_INQUIRY; + cbw.CBWCB[4] = sizeof(scsi_inquiry_response_t); + transaction.cbw = &cbw; + + res = _scsi_perform_transaction(lunp, &transaction, resp); + if (res == MSD_RESULT_OK) { + //transaction is OK; check length + if (transaction.data_processed < cbw.dCBWDataTransferLength) { + res = MSD_RESULT_TRANSPORT_ERROR; + } } + return res; } static msd_result_t scsi_requestsense(USBHMassStorageLUNDriver *lunp, scsi_sense_response_t *resp) { + USBH_DEFINE_BUFFER(msd_cbw_t cbw); msd_transaction_t transaction; msd_result_t res; - _prepare_cbw(&transaction, lunp); - transaction.cbw.dCBWDataTransferLength = sizeof(scsi_sense_response_t); - transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_D2H; - transaction.cbw.bCBWCBLength = 12; - transaction.cbw.CBWCB[0] = SCSI_CMD_REQUEST_SENSE; - transaction.cbw.CBWCB[4] = sizeof(scsi_sense_response_t); - - res.tres = _msd_transaction(&transaction, lunp, resp); - if (res.tres == MSD_TRANSACTIONRESULT_OK) { - res.cres = (msd_command_result_t) transaction.csw.bCSWStatus; + memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB)); + cbw.dCBWDataTransferLength = sizeof(scsi_sense_response_t); + cbw.bmCBWFlags = MSD_CBWFLAGS_D2H; + cbw.bCBWCBLength = 12; + cbw.CBWCB[0] = SCSI_CMD_REQUEST_SENSE; + cbw.CBWCB[4] = sizeof(scsi_sense_response_t); + transaction.cbw = &cbw; + + res = _scsi_perform_transaction(lunp, &transaction, resp); + if (res == MSD_RESULT_OK) { + //transaction is OK; check length + if (transaction.data_processed < cbw.dCBWDataTransferLength) { + res = MSD_RESULT_TRANSPORT_ERROR; + } } + return res; } static msd_result_t scsi_testunitready(USBHMassStorageLUNDriver *lunp) { + USBH_DEFINE_BUFFER(msd_cbw_t cbw); msd_transaction_t transaction; - msd_result_t res; - _prepare_cbw(&transaction, lunp); - transaction.cbw.dCBWDataTransferLength = 0; - transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_D2H; - transaction.cbw.bCBWCBLength = 6; - transaction.cbw.CBWCB[0] = SCSI_CMD_TEST_UNIT_READY; + memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB)); + cbw.dCBWDataTransferLength = 0; + cbw.bmCBWFlags = MSD_CBWFLAGS_D2H; + cbw.bCBWCBLength = 6; + cbw.CBWCB[0] = SCSI_CMD_TEST_UNIT_READY; + transaction.cbw = &cbw; - res.tres = _msd_transaction(&transaction, lunp, NULL); - if (res.tres == MSD_TRANSACTIONRESULT_OK) { - res.cres = (msd_command_result_t) transaction.csw.bCSWStatus; - } - return res; + return _scsi_perform_transaction(lunp, &transaction, NULL); } static msd_result_t scsi_readcapacity10(USBHMassStorageLUNDriver *lunp, scsi_readcapacity10_response_t *resp) { + USBH_DEFINE_BUFFER(msd_cbw_t cbw); msd_transaction_t transaction; msd_result_t res; - _prepare_cbw(&transaction, lunp); - transaction.cbw.dCBWDataTransferLength = sizeof(scsi_readcapacity10_response_t); - transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_D2H; - transaction.cbw.bCBWCBLength = 12; - transaction.cbw.CBWCB[0] = SCSI_CMD_READ_CAPACITY_10; - - res.tres = _msd_transaction(&transaction, lunp, resp); - if (res.tres == MSD_TRANSACTIONRESULT_OK) { - res.cres = (msd_command_result_t) transaction.csw.bCSWStatus; + memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB)); + cbw.dCBWDataTransferLength = sizeof(scsi_readcapacity10_response_t); + cbw.bmCBWFlags = MSD_CBWFLAGS_D2H; + cbw.bCBWCBLength = 12; + cbw.CBWCB[0] = SCSI_CMD_READ_CAPACITY_10; + transaction.cbw = &cbw; + + res = _scsi_perform_transaction(lunp, &transaction, resp); + if (res == MSD_RESULT_OK) { + //transaction is OK; check length + if (transaction.data_processed < cbw.dCBWDataTransferLength) { + res = MSD_RESULT_TRANSPORT_ERROR; + } } + return res; } -static msd_result_t scsi_read10(USBHMassStorageLUNDriver *lunp, uint32_t lba, uint16_t n, uint8_t *data) { +static msd_result_t scsi_read10(USBHMassStorageLUNDriver *lunp, uint32_t lba, uint16_t n, uint8_t *data, uint32_t *actual_len) { + USBH_DEFINE_BUFFER(msd_cbw_t cbw); msd_transaction_t transaction; msd_result_t res; - _prepare_cbw(&transaction, lunp); - transaction.cbw.dCBWDataTransferLength = n * lunp->info.blk_size; - transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_D2H; - transaction.cbw.bCBWCBLength = 10; - transaction.cbw.CBWCB[0] = SCSI_CMD_READ_10; - transaction.cbw.CBWCB[2] = (uint8_t)(lba >> 24); - transaction.cbw.CBWCB[3] = (uint8_t)(lba >> 16); - transaction.cbw.CBWCB[4] = (uint8_t)(lba >> 8); - transaction.cbw.CBWCB[5] = (uint8_t)(lba); - transaction.cbw.CBWCB[7] = (uint8_t)(n >> 8); - transaction.cbw.CBWCB[8] = (uint8_t)(n); - - res.tres = _msd_transaction(&transaction, lunp, data); - if (res.tres == MSD_TRANSACTIONRESULT_OK) { - res.cres = (msd_command_result_t) transaction.csw.bCSWStatus; + memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB)); + cbw.dCBWDataTransferLength = n * lunp->info.blk_size; + cbw.bmCBWFlags = MSD_CBWFLAGS_D2H; + cbw.bCBWCBLength = 10; + cbw.CBWCB[0] = SCSI_CMD_READ_10; + cbw.CBWCB[2] = (uint8_t)(lba >> 24); + cbw.CBWCB[3] = (uint8_t)(lba >> 16); + cbw.CBWCB[4] = (uint8_t)(lba >> 8); + cbw.CBWCB[5] = (uint8_t)(lba); + cbw.CBWCB[7] = (uint8_t)(n >> 8); + cbw.CBWCB[8] = (uint8_t)(n); + transaction.cbw = &cbw; + + res = _scsi_perform_transaction(lunp, &transaction, data); + if (actual_len) { + *actual_len = transaction.data_processed; } + if (res == MSD_RESULT_OK) { + //transaction is OK; check length + if (transaction.data_processed < cbw.dCBWDataTransferLength) { + res = MSD_RESULT_TRANSPORT_ERROR; + } + } + return res; } -static msd_result_t scsi_write10(USBHMassStorageLUNDriver *lunp, uint32_t lba, uint16_t n, const uint8_t *data) { +static msd_result_t scsi_write10(USBHMassStorageLUNDriver *lunp, uint32_t lba, uint16_t n, const uint8_t *data, uint32_t *actual_len) { + USBH_DEFINE_BUFFER(msd_cbw_t cbw); msd_transaction_t transaction; msd_result_t res; - _prepare_cbw(&transaction, lunp); - transaction.cbw.dCBWDataTransferLength = n * lunp->info.blk_size; - transaction.cbw.bmCBWFlags = MSD_CBWFLAGS_H2D; - transaction.cbw.bCBWCBLength = 10; - transaction.cbw.CBWCB[0] = SCSI_CMD_WRITE_10; - transaction.cbw.CBWCB[2] = (uint8_t)(lba >> 24); - transaction.cbw.CBWCB[3] = (uint8_t)(lba >> 16); - transaction.cbw.CBWCB[4] = (uint8_t)(lba >> 8); - transaction.cbw.CBWCB[5] = (uint8_t)(lba); - transaction.cbw.CBWCB[7] = (uint8_t)(n >> 8); - transaction.cbw.CBWCB[8] = (uint8_t)(n); - - res.tres = _msd_transaction(&transaction, lunp, (uint8_t *)data); - if (res.tres == MSD_TRANSACTIONRESULT_OK) { - res.cres = (msd_command_result_t) transaction.csw.bCSWStatus; + memset(cbw.CBWCB, 0, sizeof(cbw.CBWCB)); + cbw.dCBWDataTransferLength = n * lunp->info.blk_size; + cbw.bmCBWFlags = MSD_CBWFLAGS_H2D; + cbw.bCBWCBLength = 10; + cbw.CBWCB[0] = SCSI_CMD_WRITE_10; + cbw.CBWCB[2] = (uint8_t)(lba >> 24); + cbw.CBWCB[3] = (uint8_t)(lba >> 16); + cbw.CBWCB[4] = (uint8_t)(lba >> 8); + cbw.CBWCB[5] = (uint8_t)(lba); + cbw.CBWCB[7] = (uint8_t)(n >> 8); + cbw.CBWCB[8] = (uint8_t)(n); + transaction.cbw = &cbw; + + res = _scsi_perform_transaction(lunp, &transaction, (void *)data); + if (actual_len) { + *actual_len = transaction.data_processed; + } + if (res == MSD_RESULT_OK) { + //transaction is OK; check length + if (transaction.data_processed < cbw.dCBWDataTransferLength) { + res = MSD_RESULT_TRANSPORT_ERROR; + } } + return res; } /*===========================================================================*/ -/* Block driver data/functions */ +/* Block driver data/functions */ /*===========================================================================*/ USBHMassStorageLUNDriver MSBLKD[HAL_USBHMSD_MAX_LUNS]; @@ -600,39 +693,22 @@ static const struct USBHMassStorageDriverVMT blk_vmt = { (bool (*)(void *, BlockDeviceInfo *))usbhmsdLUNGetInfo }; - - -static uint32_t _requestsense(USBHMassStorageLUNDriver *lunp) { - scsi_sense_response_t sense; - msd_result_t res; - - res = scsi_requestsense(lunp, &sense); - if (res.tres != MSD_TRANSACTIONRESULT_OK) { - uerr("\tREQUEST SENSE: Transaction error"); - goto failed; - } else if (res.cres == MSD_COMMANDRESULT_FAILED) { - uerr("\tREQUEST SENSE: Command Failed"); - goto failed; - } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) { - //TODO: Do reset, etc. - uerr("\tREQUEST SENSE: Command Phase Error"); - goto failed; - } - - uerrf("\tREQUEST SENSE: Sense key=%x, ASC=%02x, ASCQ=%02x", - sense.byte[2] & 0xf, sense.byte[12], sense.byte[13]); - - return (sense.byte[2] & 0xf) | (sense.byte[12] << 8) | (sense.byte[13] << 16); - -failed: - return 0xffffffff; +static void _lun_object_deinit(USBHMassStorageLUNDriver *lunp) { + osalDbgCheck(lunp != NULL); + chSemWait(&lunp->sem); + lunp->msdp = NULL; + lunp->next = NULL; + memset(&lunp->info, 0, sizeof(lunp->info)); + lunp->state = BLK_STOP; + chSemSignal(&lunp->sem); } -void usbhmsdLUNObjectInit(USBHMassStorageLUNDriver *lunp) { +static void _lun_object_init(USBHMassStorageLUNDriver *lunp) { osalDbgCheck(lunp != NULL); memset(lunp, 0, sizeof(*lunp)); lunp->vmt = &blk_vmt; lunp->state = BLK_STOP; + chSemObjectInit(&lunp->sem, 1); /* Unnecessary because of the memset: lunp->msdp = NULL; lunp->next = NULL; @@ -640,150 +716,111 @@ void usbhmsdLUNObjectInit(USBHMassStorageLUNDriver *lunp) { */ } -void usbhmsdLUNStart(USBHMassStorageLUNDriver *lunp) { - osalDbgCheck(lunp != NULL); - osalSysLock(); - osalDbgAssert((lunp->state == BLK_STOP) || (lunp->state == BLK_ACTIVE), - "invalid state"); - //TODO: complete - //lunp->state = BLK_ACTIVE; - osalSysUnlock(); -} - -void usbhmsdLUNStop(USBHMassStorageLUNDriver *lunp) { - osalDbgCheck(lunp != NULL); - osalSysLock(); - osalDbgAssert((lunp->state == BLK_STOP) || (lunp->state == BLK_ACTIVE), - "invalid state"); - //TODO: complete - //lunp->state = BLK_STOP; - osalSysUnlock(); -} - bool usbhmsdLUNConnect(USBHMassStorageLUNDriver *lunp) { - USBHMassStorageDriver *const msdp = lunp->msdp; + osalDbgCheck(lunp != NULL); + osalDbgCheck(lunp->msdp != NULL); msd_result_t res; - osalDbgCheck(msdp != NULL); - osalSysLock(); - //osalDbgAssert((lunp->state == BLK_ACTIVE) || (lunp->state == BLK_READY), - // "invalid state"); + chSemWait(&lunp->sem); + osalDbgAssert((lunp->state == BLK_READY) || (lunp->state == BLK_ACTIVE), "invalid state"); if (lunp->state == BLK_READY) { - osalSysUnlock(); + chSemSignal(&lunp->sem); return HAL_SUCCESS; - } else if (lunp->state != BLK_ACTIVE) { - osalSysUnlock(); - return HAL_FAILED; } lunp->state = BLK_CONNECTING; - osalSysUnlock(); - osalMutexLock(&msdp->mtx); - - USBH_DEFINE_BUFFER(union { - scsi_inquiry_response_t inq; - scsi_readcapacity10_response_t cap; }, u); - - uinfo("INQUIRY..."); - res = scsi_inquiry(lunp, &u.inq); - if (res.tres != MSD_TRANSACTIONRESULT_OK) { - uerr("\tINQUIRY: Transaction error"); - goto failed; - } else if (res.cres == MSD_COMMANDRESULT_FAILED) { - uerr("\tINQUIRY: Command Failed"); - _requestsense(lunp); - goto failed; - } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) { - //TODO: Do reset, etc. - uerr("\tINQUIRY: Command Phase Error"); - goto failed; - } + { + USBH_DEFINE_BUFFER(scsi_inquiry_response_t inq); + uinfo("INQUIRY..."); + res = scsi_inquiry(lunp, &inq); + if (res == MSD_RESULT_DISCONNECTED) { + goto failed; + } else if (res == MSD_RESULT_TRANSPORT_ERROR) { + //retry? + goto failed; + } else if (res == MSD_RESULT_FAILED) { + //retry? + goto failed; + } - uinfof("\tPDT=%02x", u.inq.peripheral & 0x1f); - if (u.inq.peripheral != 0) { - uerr("\tUnsupported PDT"); - goto failed; + uinfof("\tPDT=%02x", inq.peripheral & 0x1f); + if (inq.peripheral != 0) { + uerr("\tUnsupported PDT"); + goto failed; + } } // Test if unit ready - uint8_t i; + uint8_t i; for (i = 0; i < 10; i++) { uinfo("TEST UNIT READY..."); res = scsi_testunitready(lunp); - if (res.tres != MSD_TRANSACTIONRESULT_OK) { - uerr("\tTEST UNIT READY: Transaction error"); + if (res == MSD_RESULT_DISCONNECTED) { goto failed; - } else if (res.cres == MSD_COMMANDRESULT_FAILED) { - uerr("\tTEST UNIT READY: Command Failed"); - _requestsense(lunp); - continue; - } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) { - //TODO: Do reset, etc. - uerr("\tTEST UNIT READY: Command Phase Error"); + } else if (res == MSD_RESULT_TRANSPORT_ERROR) { + //retry? goto failed; + } else if (res == MSD_RESULT_FAILED) { + uinfo("\tTEST UNIT READY: Command Failed, retry"); + osalThreadSleepMilliseconds(200); + continue; } uinfo("\tReady."); break; - // osalThreadSleepMilliseconds(200); // will raise 'code is unreachable' warning } if (i == 10) goto failed; - // Read capacity - uinfo("READ CAPACITY(10)..."); - res = scsi_readcapacity10(lunp, &u.cap); - if (res.tres != MSD_TRANSACTIONRESULT_OK) { - uerr("\tREAD CAPACITY(10): Transaction error"); - goto failed; - } else if (res.cres == MSD_COMMANDRESULT_FAILED) { - uerr("\tREAD CAPACITY(10): Command Failed"); - _requestsense(lunp); - goto failed; - } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) { - //TODO: Do reset, etc. - uerr("\tREAD CAPACITY(10): Command Phase Error"); - goto failed; - } - lunp->info.blk_size = __REV(u.cap.block_size); - lunp->info.blk_num = __REV(u.cap.last_block_addr) + 1; + { + USBH_DEFINE_BUFFER(scsi_readcapacity10_response_t cap); + // Read capacity + uinfo("READ CAPACITY(10)..."); + res = scsi_readcapacity10(lunp, &cap); + if (res == MSD_RESULT_DISCONNECTED) { + goto failed; + } else if (res == MSD_RESULT_TRANSPORT_ERROR) { + //retry? + goto failed; + } else if (res == MSD_RESULT_FAILED) { + //retry? + goto failed; + } + + lunp->info.blk_size = __REV(cap.block_size); + lunp->info.blk_num = __REV(cap.last_block_addr) + 1; + } + uinfof("\tBlock size=%dbytes, blocks=%u (~%u MB)", lunp->info.blk_size, lunp->info.blk_num, (uint32_t)(((uint64_t)lunp->info.blk_size * lunp->info.blk_num) / (1024UL * 1024UL))); uinfo("MSD Connected."); - - osalMutexUnlock(&msdp->mtx); - osalSysLock(); lunp->state = BLK_READY; - osalSysUnlock(); - + chSemSignal(&lunp->sem); return HAL_SUCCESS; /* Connection failed, state reset to BLK_ACTIVE.*/ failed: - osalMutexUnlock(&msdp->mtx); - osalSysLock(); + uinfo("MSD Connect failed."); lunp->state = BLK_ACTIVE; - osalSysUnlock(); + chSemSignal(&lunp->sem); return HAL_FAILED; } - bool usbhmsdLUNDisconnect(USBHMassStorageLUNDriver *lunp) { osalDbgCheck(lunp != NULL); - osalSysLock(); - osalDbgAssert((lunp->state == BLK_ACTIVE) || (lunp->state == BLK_READY), - "invalid state"); + + chSemWait(&lunp->sem); + osalDbgAssert((lunp->state == BLK_READY) || (lunp->state == BLK_ACTIVE), "invalid state"); if (lunp->state == BLK_ACTIVE) { - osalSysUnlock(); + chSemSignal(&lunp->sem); return HAL_SUCCESS; } lunp->state = BLK_DISCONNECTING; - osalSysUnlock(); - //TODO: complete + //TODO: complete: sync, etc. - osalSysLock(); lunp->state = BLK_ACTIVE; - osalSysUnlock(); + chSemSignal(&lunp->sem); + return HAL_SUCCESS; } @@ -794,34 +831,29 @@ bool usbhmsdLUNRead(USBHMassStorageLUNDriver *lunp, uint32_t startblk, bool ret = HAL_FAILED; uint16_t blocks; msd_result_t res; + uint32_t actual_len; - osalSysLock(); + chSemWait(&lunp->sem); if (lunp->state != BLK_READY) { - osalSysUnlock(); + chSemSignal(&lunp->sem); return ret; } lunp->state = BLK_READING; - osalSysUnlock(); - osalMutexLock(&lunp->msdp->mtx); while (n) { if (n > 0xffff) { blocks = 0xffff; } else { blocks = (uint16_t)n; } - res = scsi_read10(lunp, startblk, blocks, buffer); - if (res.tres != MSD_TRANSACTIONRESULT_OK) { - uerr("\tREAD (10): Transaction error"); + res = scsi_read10(lunp, startblk, blocks, buffer, &actual_len); + if (res == MSD_RESULT_DISCONNECTED) { goto exit; - } else if (res.cres == MSD_COMMANDRESULT_FAILED) { - //TODO: request sense, and act appropriately - uerr("\tREAD (10): Command Failed"); - _requestsense(lunp); + } else if (res == MSD_RESULT_TRANSPORT_ERROR) { + //retry? goto exit; - } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) { - //TODO: Do reset, etc. - uerr("\tREAD (10): Command Phase Error"); + } else if (res == MSD_RESULT_FAILED) { + //retry? goto exit; } n -= blocks; @@ -832,15 +864,8 @@ bool usbhmsdLUNRead(USBHMassStorageLUNDriver *lunp, uint32_t startblk, ret = HAL_SUCCESS; exit: - osalMutexUnlock(&lunp->msdp->mtx); - osalSysLock(); - if (lunp->state == BLK_READING) { - lunp->state = BLK_READY; - } else { - osalDbgCheck(lunp->state == BLK_STOP); - uwarn("MSD: State = BLK_STOP"); - } - osalSysUnlock(); + lunp->state = BLK_READY; + chSemSignal(&lunp->sem); return ret; } @@ -851,34 +876,29 @@ bool usbhmsdLUNWrite(USBHMassStorageLUNDriver *lunp, uint32_t startblk, bool ret = HAL_FAILED; uint16_t blocks; msd_result_t res; + uint32_t actual_len; - osalSysLock(); + chSemWait(&lunp->sem); if (lunp->state != BLK_READY) { - osalSysUnlock(); + chSemSignal(&lunp->sem); return ret; } lunp->state = BLK_WRITING; - osalSysUnlock(); - osalMutexLock(&lunp->msdp->mtx); while (n) { if (n > 0xffff) { blocks = 0xffff; } else { blocks = (uint16_t)n; } - res = scsi_write10(lunp, startblk, blocks, buffer); - if (res.tres != MSD_TRANSACTIONRESULT_OK) { - uerr("\tWRITE (10): Transaction error"); + res = scsi_write10(lunp, startblk, blocks, buffer, &actual_len); + if (res == MSD_RESULT_DISCONNECTED) { goto exit; - } else if (res.cres == MSD_COMMANDRESULT_FAILED) { - //TODO: request sense, and act appropriately - uerr("\tWRITE (10): Command Failed"); - _requestsense(lunp); + } else if (res == MSD_RESULT_TRANSPORT_ERROR) { + //retry? goto exit; - } else if (res.cres == MSD_COMMANDRESULT_PHASE_ERROR) { - //TODO: Do reset, etc. - uerr("\tWRITE (10): Command Phase Error"); + } else if (res == MSD_RESULT_FAILED) { + //retry? goto exit; } n -= blocks; @@ -889,15 +909,8 @@ bool usbhmsdLUNWrite(USBHMassStorageLUNDriver *lunp, uint32_t startblk, ret = HAL_SUCCESS; exit: - osalMutexUnlock(&lunp->msdp->mtx); - osalSysLock(); - if (lunp->state == BLK_WRITING) { - lunp->state = BLK_READY; - } else { - osalDbgCheck(lunp->state == BLK_STOP); - uwarn("MSD: State = BLK_STOP"); - } - osalSysUnlock(); + lunp->state = BLK_READY; + chSemSignal(&lunp->sem); return ret; } @@ -911,29 +924,41 @@ bool usbhmsdLUNSync(USBHMassStorageLUNDriver *lunp) { bool usbhmsdLUNGetInfo(USBHMassStorageLUNDriver *lunp, BlockDeviceInfo *bdip) { osalDbgCheck(lunp != NULL); osalDbgCheck(bdip != NULL); - *bdip = lunp->info; - return HAL_SUCCESS; + + osalSysLock(); + if (lunp->state >= BLK_READY) { + *bdip = lunp->info; + osalSysUnlock(); + return HAL_SUCCESS; + } + osalSysUnlock(); + return HAL_FAILED; } bool usbhmsdLUNIsInserted(USBHMassStorageLUNDriver *lunp) { osalDbgCheck(lunp != NULL); - blkstate_t state; - osalSysLock(); - state = lunp->state; - osalSysUnlock(); - return (state >= BLK_ACTIVE); + return (lunp->state >= BLK_ACTIVE); } bool usbhmsdLUNIsProtected(USBHMassStorageLUNDriver *lunp) { osalDbgCheck(lunp != NULL); + //TODO: Implement return FALSE; } -void usbhmsdObjectInit(USBHMassStorageDriver *msdp) { +static void _msd_object_init(USBHMassStorageDriver *msdp) { osalDbgCheck(msdp != NULL); memset(msdp, 0, sizeof(*msdp)); msdp->info = &usbhmsdClassDriverInfo; - osalMutexObjectInit(&msdp->mtx); } +static void _msd_init(void) { + uint8_t i; + for (i = 0; i < HAL_USBHMSD_MAX_INSTANCES; i++) { + _msd_object_init(&USBHMSD[i]); + } + for (i = 0; i < HAL_USBHMSD_MAX_LUNS; i++) { + _lun_object_init(&MSBLKD[i]); + } +} #endif |