summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/class/msc/msc_device.c188
-rw-r--r--src/class/msc/msc_device.h38
-rw-r--r--src/device/dcd.h2
-rw-r--r--src/device/usbd.c2
-rw-r--r--src/portable/microchip/samd21/dcd_samd21.c4
-rw-r--r--src/portable/microchip/samd51/dcd_samd51.c4
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c5
-rw-r--r--src/tusb_option.h9
8 files changed, 147 insertions, 105 deletions
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index f980e941e..c486d5e04 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -159,7 +159,7 @@ bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
case MSC_REQ_GET_MAX_LUN:
{
uint8_t maxlun = 1;
- if (tud_msc_maxlun_cb) maxlun = tud_msc_maxlun_cb();
+ if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb();
TU_VERIFY(maxlun);
// MAX LUN is minus 1 by specs
@@ -186,30 +186,64 @@ bool mscd_control_request_complete(uint8_t rhport, tusb_control_request_t const
return true;
}
-// return length of response (copied to buffer), -1 if it is not an built-in commands
-int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t bufsize)
+// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
+// In case of a failed status, sense key must be set for reason of failure
+int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
{
(void) bufsize; // TODO refractor later
- int32_t ret;
+ int32_t resplen;
- switch ( p_cbw->command[0] )
+ switch ( scsi_cmd[0] )
{
+ case SCSI_CMD_TEST_UNIT_READY:
+ resplen = 0;
+ if ( !tud_msc_test_unit_ready_cb(lun) )
+ {
+ // not ready response with Failed status and sense key = not ready
+ resplen = - 1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }
+ break;
+
+ case SCSI_CMD_START_STOP_UNIT:
+ resplen = 0;
+
+ if (tud_msc_start_stop_cb)
+ {
+ scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
+ tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject);
+ }
+ break;
+
case SCSI_CMD_READ_CAPACITY_10:
{
- scsi_read_capacity10_resp_t read_capa10;
-
uint32_t block_count;
uint32_t block_size;
uint16_t block_size_u16;
- tud_msc_capacity_cb(p_cbw->lun, &block_count, &block_size_u16);
+ tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
block_size = (uint32_t) block_size_u16;
- read_capa10.last_lba = ENDIAN_BE(block_count-1);
- read_capa10.block_size = ENDIAN_BE(block_size);
+ // Invalid block size/count from callback, possibly unit is not ready
+ // stall this request, set sense key to NOT READY
+ if (block_count == 0 || block_size == 0)
+ {
+ resplen = -1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }else
+ {
+ scsi_read_capacity10_resp_t read_capa10;
+
+ read_capa10.last_lba = ENDIAN_BE(block_count-1);
+ read_capa10.block_size = ENDIAN_BE(block_size);
- ret = sizeof(read_capa10);
- memcpy(buffer, &read_capa10, ret);
+ resplen = sizeof(read_capa10);
+ memcpy(buffer, &read_capa10, resplen);
+ }
}
break;
@@ -226,12 +260,24 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
uint32_t block_count;
uint16_t block_size;
- tud_msc_capacity_cb(p_cbw->lun, &block_count, &block_size);
- read_fmt_capa.block_num = ENDIAN_BE(block_count);
- read_fmt_capa.block_size_u16 = ENDIAN_BE16(block_size);
+ tud_msc_capacity_cb(lun, &block_count, &block_size);
+
+ // Invalid block size/count from callback, possibly unit is not ready
+ // stall this request, set sense key to NOT READY
+ if (block_count == 0 || block_size == 0)
+ {
+ resplen = -1;
- ret = sizeof(read_fmt_capa);
- memcpy(buffer, &read_fmt_capa, ret);
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }else
+ {
+ read_fmt_capa.block_num = ENDIAN_BE(block_count);
+ read_fmt_capa.block_size_u16 = ENDIAN_BE16(block_size);
+
+ resplen = sizeof(read_fmt_capa);
+ memcpy(buffer, &read_fmt_capa, resplen);
+ }
}
break;
@@ -242,23 +288,17 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
.is_removable = 1,
.version = 2,
.response_data_format = 2,
- // vendor_id, product_id, product_rev is space padded string
- .vendor_id = "",
- .product_id = "",
- .product_rev = "",
};
- memset(inquiry_rsp.vendor_id, ' ', sizeof(inquiry_rsp.vendor_id));
- memcpy(inquiry_rsp.vendor_id, CFG_TUD_MSC_VENDOR, tu_min32(strlen(CFG_TUD_MSC_VENDOR), sizeof(inquiry_rsp.vendor_id)));
-
- memset(inquiry_rsp.product_id, ' ', sizeof(inquiry_rsp.product_id));
- memcpy(inquiry_rsp.product_id, CFG_TUD_MSC_PRODUCT, tu_min32(strlen(CFG_TUD_MSC_PRODUCT), sizeof(inquiry_rsp.product_id)));
-
+ // vendor_id, product_id, product_rev is space padded string
+ memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
+ memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
- memcpy(inquiry_rsp.product_rev, CFG_TUD_MSC_PRODUCT_REV, tu_min32(strlen(CFG_TUD_MSC_PRODUCT_REV), sizeof(inquiry_rsp.product_rev)));
- ret = sizeof(inquiry_rsp);
- memcpy(buffer, &inquiry_rsp, ret);
+ tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
+
+ resplen = sizeof(inquiry_rsp);
+ memcpy(buffer, &inquiry_rsp, resplen);
}
break;
@@ -275,12 +315,12 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
bool writable = true;
if (tud_msc_is_writable_cb) {
- writable = tud_msc_is_writable_cb(p_cbw->lun);
+ writable = tud_msc_is_writable_cb(lun);
}
mode_resp.write_protected = !writable;
- ret = sizeof(mode_resp);
- memcpy(buffer, &mode_resp, ret);
+ resplen = sizeof(mode_resp);
+ memcpy(buffer, &mode_resp, resplen);
}
break;
@@ -298,18 +338,18 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
- ret = sizeof(sense_rsp);
- memcpy(buffer, &sense_rsp, ret);
+ resplen = sizeof(sense_rsp);
+ memcpy(buffer, &sense_rsp, resplen);
// Clear sense data after copy
- tud_msc_set_sense(p_cbw->lun, 0, 0, 0);
+ tud_msc_set_sense(lun, 0, 0, 0);
}
break;
- default: ret = -1; break;
+ default: resplen = -1; break;
}
- return ret;
+ return resplen;
}
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
@@ -348,60 +388,50 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
else
{
// For other SCSI commands
- // 1. Zero : Invoke app callback, skip DATA and move to STATUS stage
- // 2. OUT : queue transfer (invoke app callback after done)
- // 3. IN : invoke app callback to get response
- if ( p_cbw->total_bytes == 0)
+ // 1. OUT : queue transfer (invoke app callback after done)
+ // 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
+ if ( (p_cbw->total_bytes > 0 ) && !TU_BIT_TEST(p_cbw->dir, 7) )
{
- int32_t const cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, NULL, 0);
-
- p_msc->total_len = 0;
- p_msc->stage = MSC_STAGE_STATUS;
-
- if ( cb_result < 0 )
- {
- p_csw->status = MSC_CSW_STATUS_FAILED;
- tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
- }
- else
- {
- p_csw->status = MSC_CSW_STATUS_PASSED;
- }
- }
- else if ( !TU_BIT_TEST(p_cbw->dir, 7) )
- {
- // OUT transfer
+ // queue transfer
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
- }
- else
+ }else
{
- // IN Transfer
- int32_t cb_result;
+ int32_t resplen;
- // first process if it is a built-in commands
- cb_result = proc_builtin_scsi(p_cbw, _mscd_buf, sizeof(_mscd_buf));
+ // First process if it is a built-in commands
+ resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
- // Not an built-in command, invoke user callback
- if ( cb_result < 0 )
+ // Not built-in, invoke user callback
+ if ( (resplen < 0) && (p_msc->sense_key == 0) )
{
- cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
+ resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
}
- if ( cb_result > 0 )
- {
- p_msc->total_len = (uint32_t) cb_result;
- p_csw->status = MSC_CSW_STATUS_PASSED;
-
- TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect
- TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
- }else
+ if ( resplen < 0 )
{
p_msc->total_len = 0;
p_csw->status = MSC_CSW_STATUS_FAILED;
p_msc->stage = MSC_STAGE_STATUS;
- tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
- usbd_edpt_stall(rhport, p_msc->ep_in);
+ // failed but senskey is not set: default to Illegal Request
+ if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+
+ /// Stall bulk In if needed
+ if (p_cbw->total_bytes) usbd_edpt_stall(rhport, p_msc->ep_in);
+ }
+ else
+ {
+ p_msc->total_len = (uint32_t) resplen;
+ p_csw->status = MSC_CSW_STATUS_PASSED;
+
+ if (p_msc->total_len)
+ {
+ TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect
+ TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
+ }else
+ {
+ p_msc->stage = MSC_STAGE_STATUS;
+ }
}
}
}
diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h
index b1e7e8da1..1baf2d19a 100644
--- a/src/class/msc/msc_device.h
+++ b/src/class/msc/msc_device.h
@@ -44,18 +44,6 @@ TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
#error CFG_TUD_MSC_BUFSIZE must be defined, value of a block size should work well, the more the better
#endif
-#ifndef CFG_TUD_MSC_VENDOR
- #error CFG_TUD_MSC_VENDOR 8-byte name must be defined
-#endif
-
-#ifndef CFG_TUD_MSC_PRODUCT
- #error CFG_TUD_MSC_PRODUCT 16-byte name must be defined
-#endif
-
-#ifndef CFG_TUD_MSC_PRODUCT_REV
- #error CFG_TUD_MSC_PRODUCT_REV 4-byte string must be defined
-#endif
-
/** \addtogroup ClassDriver_MSC
* @{
* \defgroup MSC_Device Device
@@ -105,12 +93,23 @@ int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buf
*/
int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
+// Invoked when received SCSI_CMD_INQUIRY
+// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]);
+
+// Invoked when received Test Unit Ready command.
+// return true allowing host to read/write this LUN e.g SD card inserted
+bool tud_msc_test_unit_ready_cb(uint8_t lun);
+
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size);
/**
- * Callback invoked when received an SCSI command not in built-in list below.
+ * Invoked when received an SCSI command not in built-in list below.
+ * - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE
+ * - READ10 and WRITE10 has their own callbacks
+ *
* \param[in] lun Logical unit number
* \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly
* \param[out] buffer Buffer for SCSI Data Stage.
@@ -121,17 +120,18 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_siz
* \return Actual bytes processed, can be zero for no-data command.
* \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding
* endpoint and return failed status in command status wrapper phase.
- *
- * \note Following command is automatically handled by tinyusb stack, callback should not be worried:
- * - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
- * - READ10 and WRITE10 has their own callbacks
*/
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize);
/*------------- Optional callbacks -------------*/
-// Invoked when received GET_MAX_LUN request
-ATTR_WEAK uint8_t tud_msc_maxlun_cb(void);
+// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
+ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
+
+// Invoked when received Start Stop Unit command
+// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
+// - Start = 1 : active mode, if load_eject = 1 : load disk storage
+ATTR_WEAK void tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
// Invoked when Read10 command is complete
ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun);
diff --git a/src/device/dcd.h b/src/device/dcd.h
index 63f923faa..18678f15b 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -107,7 +107,7 @@ void dcd_remote_wakeup(uint8_t rhport);
* must be called to notify the stack
* - busy : Check if endpoint transferring is complete (TODO remove)
* - stall : stall endpoint
- * - clear_stall : clear stall
+ * - clear_stall : clear stall, data toggle is also reset to DATA0
*------------------------------------------------------------------*/
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
diff --git a/src/device/usbd.c b/src/device/usbd.c
index d811c471a..434a2c3d8 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -456,7 +456,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
case TUSB_REQ_CLEAR_FEATURE:
if ( TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue )
{
- dcd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
+ usbd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
}
usbd_control_status(rhport, p_request);
break;
diff --git a/src/portable/microchip/samd21/dcd_samd21.c b/src/portable/microchip/samd21/dcd_samd21.c
index 8ecfd3668..9a9964505 100644
--- a/src/portable/microchip/samd21/dcd_samd21.c
+++ b/src/portable/microchip/samd21/dcd_samd21.c
@@ -223,9 +223,9 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
- ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1;
+ ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1 | USB_DEVICE_EPSTATUSCLR_DTGLIN;
} else {
- ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0;
+ ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0 | USB_DEVICE_EPSTATUSCLR_DTGLOUT;
}
}
diff --git a/src/portable/microchip/samd51/dcd_samd51.c b/src/portable/microchip/samd51/dcd_samd51.c
index d2e7aaf7d..6a4bd0e63 100644
--- a/src/portable/microchip/samd51/dcd_samd51.c
+++ b/src/portable/microchip/samd51/dcd_samd51.c
@@ -227,9 +227,9 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
- ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1;
+ ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1 | USB_DEVICE_EPSTATUSCLR_DTGLIN;
} else {
- ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0;
+ ep->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0 | USB_DEVICE_EPSTATUSCLR_DTGLOUT;
}
}
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index 3e5f69f79..53f92bb19 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -322,7 +322,12 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
if ( tu_edpt_number(ep_addr) )
{
+ // clear stall
NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
+
+ // reset data toggle to DATA0
+ NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
+
__ISB(); __DSB();
}
}
diff --git a/src/tusb_option.h b/src/tusb_option.h
index cb6d573ea..4e55132b1 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -128,7 +128,6 @@
*/
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
- #warning CFG_TUSB_DEBUG is not defined, default value is 0
#endif
// place data in accessible RAM for usb controller
@@ -165,6 +164,14 @@
#define CFG_TUD_MSC 0
#endif
+#ifndef CFG_TUD_MIDI
+ #define CFG_TUD_MIDI 0
+#endif
+
+#ifndef CFG_TUD_CUSTOM_CLASS
+ #define CFG_TUD_CUSTOM_CLASS 0
+#endif
+
#endif // TUSB_OPT_DEVICE_ENABLED
//--------------------------------------------------------------------