tinyusb  0.4
Click here to lend your support to tinyusb donation and make a donation at pledgie.com
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
msc_device.c
Go to the documentation of this file.
1 /**************************************************************************/
37 /**************************************************************************/
38 
39 #include "tusb_option.h"
40 
41 #if (MODE_DEVICE_SUPPORTED && TUSB_CFG_DEVICE_MSC)
42 
43 #define _TINY_USB_SOURCE_FILE_
44 //--------------------------------------------------------------------+
45 // INCLUDE
46 //--------------------------------------------------------------------+
47 #include "common/common.h"
48 #include "msc_device.h"
49 
50 //--------------------------------------------------------------------+
51 // MACRO CONSTANT TYPEDEF
52 //--------------------------------------------------------------------+
53 typedef struct {
54  uint8_t scsi_data[64]; // buffer for scsi's response other than read10 & write10. NOTE should be multiple of 64 to be compatible with lpc11/13u
55  ATTR_USB_MIN_ALIGNMENT msc_cmd_block_wrapper_t cbw;
56 
57 #if defined (__ICCARM__) && (TUSB_CFG_MCU == MCU_LPC11UXX || TUSB_CFG_MCU == MCU_LPC13UXX)
58  uint8_t padding1[64-sizeof(msc_cmd_block_wrapper_t)]; // IAR cannot align struct's member
59 #endif
60 
61  ATTR_USB_MIN_ALIGNMENT msc_cmd_status_wrapper_t csw;
62 
63  uint8_t max_lun;
64  uint8_t interface_number;
65  endpoint_handle_t edpt_in, edpt_out;
66 }mscd_interface_t;
67 
68 TUSB_CFG_ATTR_USBRAM STATIC_VAR mscd_interface_t mscd_data;
69 //--------------------------------------------------------------------+
70 // INTERNAL OBJECT & FUNCTION DECLARATION
71 //--------------------------------------------------------------------+
72 static bool read10_write10_data_xfer(mscd_interface_t* p_msc);
73 
74 //--------------------------------------------------------------------+
75 // USBD-CLASS API
76 //--------------------------------------------------------------------+
77 void mscd_init(void)
78 {
79  memclr_(&mscd_data, sizeof(mscd_interface_t));
80 }
81 
82 void mscd_close(uint8_t coreid)
83 {
84  memclr_(&mscd_data, sizeof(mscd_interface_t));
85  tusbd_msc_unmounted_cb(coreid);
86 }
87 
88 tusb_error_t mscd_open(uint8_t coreid, tusb_descriptor_interface_t const * p_interface_desc, uint16_t *p_length)
89 {
90  ASSERT( ( MSC_SUBCLASS_SCSI == p_interface_desc->bInterfaceSubClass &&
91  MSC_PROTOCOL_BOT == p_interface_desc->bInterfaceProtocol ), TUSB_ERROR_MSC_UNSUPPORTED_PROTOCOL );
92 
93  mscd_interface_t * p_msc = &mscd_data;
94 
95  //------------- Open Data Pipe -------------//
96  tusb_descriptor_endpoint_t const *p_endpoint = (tusb_descriptor_endpoint_t const *) descriptor_next( (uint8_t const*) p_interface_desc );
97  for(uint32_t i=0; i<2; i++)
98  {
99  ASSERT(TUSB_DESC_TYPE_ENDPOINT == p_endpoint->bDescriptorType &&
100  TUSB_XFER_BULK == p_endpoint->bmAttributes.xfer, TUSB_ERROR_DESCRIPTOR_CORRUPTED);
101 
102  endpoint_handle_t * p_edpt_hdl = ( p_endpoint->bEndpointAddress & TUSB_DIR_DEV_TO_HOST_MASK ) ?
103  &p_msc->edpt_in : &p_msc->edpt_out;
104 
105  (*p_edpt_hdl) = dcd_pipe_open(coreid, p_endpoint, p_interface_desc->bInterfaceClass);
106  ASSERT( endpointhandle_is_valid(*p_edpt_hdl), TUSB_ERROR_DCD_FAILED);
107 
108  p_endpoint = (tusb_descriptor_endpoint_t const *) descriptor_next( (uint8_t const*) p_endpoint );
109  }
110 
111  p_msc->interface_number = p_interface_desc->bInterfaceNumber;
112 
113  (*p_length) += sizeof(tusb_descriptor_interface_t) + 2*sizeof(tusb_descriptor_endpoint_t);
114 
115  tusbd_msc_mounted_cb(coreid);
116 
117  //------------- Queue Endpoint OUT for Command Block Wrapper -------------//
118  ASSERT_STATUS( dcd_pipe_xfer(p_msc->edpt_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cmd_block_wrapper_t), true) );
119 
120  return TUSB_ERROR_NONE;
121 }
122 
123 tusb_error_t mscd_control_request_subtask(uint8_t coreid, tusb_control_request_t const * p_request)
124 {
125  ASSERT(p_request->bmRequestType_bit.type == TUSB_REQUEST_TYPE_CLASS, TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT);
126 
127  mscd_interface_t * p_msc = &mscd_data;
128 
129  switch(p_request->bRequest)
130  {
131  case MSC_REQUEST_RESET:
132  dcd_pipe_control_xfer(coreid, TUSB_DIR_HOST_TO_DEV, NULL, 0, false);
133  break;
134 
136  p_msc->scsi_data[0] = p_msc->max_lun; // Note: lpc11/13u need xfer data's address to be aligned 64 -> make use of scsi_data instead of using max_lun directly
137  dcd_pipe_control_xfer(coreid, TUSB_DIR_DEV_TO_HOST, p_msc->scsi_data, 1, false);
138  break;
139 
140  default:
141  return TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT;
142  }
143 
144  return TUSB_ERROR_NONE;
145 }
146 
147 //--------------------------------------------------------------------+
148 // MSCD APPLICATION CALLBACK
149 //--------------------------------------------------------------------+
150 tusb_error_t mscd_xfer_cb(endpoint_handle_t edpt_hdl, tusb_event_t event, uint32_t xferred_bytes)
151 {
152  static bool is_waiting_read10_write10 = false; // indicate we are transferring data in READ10, WRITE10 command
153 
154  mscd_interface_t * const p_msc = &mscd_data;
155  msc_cmd_block_wrapper_t * const p_cbw = &p_msc->cbw;
156  msc_cmd_status_wrapper_t * const p_csw = &p_msc->csw;
157 
158  //------------- new CBW received -------------//
159  if ( !is_waiting_read10_write10 )
160  {
161  if ( endpointhandle_is_equal(p_msc->edpt_in, edpt_hdl) ) return TUSB_ERROR_NONE; // bulk in interrupt for dcd to clean up
162 
163  ASSERT( endpointhandle_is_equal(p_msc->edpt_out, edpt_hdl) &&
164  xferred_bytes == sizeof(msc_cmd_block_wrapper_t) &&
165  event == TUSB_EVENT_XFER_COMPLETE &&
166  p_cbw->signature == MSC_CBW_SIGNATURE, TUSB_ERROR_INVALID_PARA );
167 
168  p_csw->signature = MSC_CSW_SIGNATURE;
169  p_csw->tag = p_cbw->tag;
170  p_csw->data_residue = 0;
171 
172  if ( (SCSI_CMD_READ_10 != p_cbw->command[0]) && (SCSI_CMD_WRITE_10 != p_cbw->command[0]) )
173  {
174  void const *p_buffer = NULL;
175  uint16_t actual_length = (uint16_t) p_cbw->xfer_bytes;
176 
177  // TODO SCSI data out transfer is not yet supported
178  ASSERT_FALSE( p_cbw->xfer_bytes > 0 && !BIT_TEST_(p_cbw->dir, 7), TUSB_ERROR_NOT_SUPPORTED_YET);
179 
180  p_csw->status = tusbd_msc_scsi_cb(edpt_hdl.coreid, p_cbw->lun, p_cbw->command, &p_buffer, &actual_length);
181 
182  //------------- Data Phase (non READ10, WRITE10) -------------//
183  if ( p_cbw->xfer_bytes )
184  {
185  ASSERT( p_cbw->xfer_bytes >= actual_length, TUSB_ERROR_INVALID_PARA );
186  ASSERT( sizeof(p_msc->scsi_data) >= actual_length, TUSB_ERROR_NOT_ENOUGH_MEMORY); // needs to increase size for scsi_data
187 
188  endpoint_handle_t const edpt_data = BIT_TEST_(p_cbw->dir, 7) ? p_msc->edpt_in : p_msc->edpt_out;
189 
190  if ( p_buffer == NULL || actual_length == 0 )
191  { // application does not provide data to response --> possibly unsupported SCSI command
192  ASSERT_STATUS( dcd_pipe_stall(edpt_data) );
193  p_csw->status = MSC_CSW_STATUS_FAILED;
194  }else
195  {
196  memcpy(p_msc->scsi_data, p_buffer, actual_length);
197  ASSERT_STATUS( dcd_pipe_queue_xfer( edpt_data, p_msc->scsi_data, actual_length ) );
198  }
199  }
200  }
201  }
202 
203  //------------- Data Phase For READ10 & WRITE10 (can be executed several times) -------------//
204  if ( (SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0]) )
205  {
206  is_waiting_read10_write10 = !read10_write10_data_xfer(p_msc);
207  }
208 
209  //------------- Status Phase -------------//
210  // Either bulk in & out can be stalled in the data phase, dcd must make sure these queued transfer will be resumed after host clear stall
211  if (!is_waiting_read10_write10)
212  {
213  ASSERT_STATUS( dcd_pipe_xfer( p_msc->edpt_in , (uint8_t*) p_csw, sizeof(msc_cmd_status_wrapper_t), false) );
214 
215  //------------- Queue the next CBW -------------//
216  ASSERT_STATUS( dcd_pipe_xfer( p_msc->edpt_out, (uint8_t*) p_cbw, sizeof(msc_cmd_block_wrapper_t), true) );
217  }
218 
219  return TUSB_ERROR_NONE;
220 }
221 
222 // return true if data phase is complete, false if not yet complete
223 static bool read10_write10_data_xfer(mscd_interface_t* p_msc)
224 {
225  msc_cmd_block_wrapper_t * const p_cbw = &p_msc->cbw;
226  msc_cmd_status_wrapper_t * const p_csw = &p_msc->csw;
227 
228  scsi_read10_t* p_readwrite = (scsi_read10_t*) &p_cbw->command; // read10 & write10 has the same format
229 
230  endpoint_handle_t const edpt_hdl = BIT_TEST_(p_cbw->dir, 7) ? p_msc->edpt_in : p_msc->edpt_out;
231 
232  uint32_t const lba = __be2n(p_readwrite->lba);
233  uint16_t const block_count = __be2n_16(p_readwrite->block_count);
234  void *p_buffer = NULL;
235 
236  uint16_t xferred_block = (SCSI_CMD_READ_10 == p_cbw->command[0]) ? tusbd_msc_read10_cb (edpt_hdl.coreid, p_cbw->lun, &p_buffer, lba, block_count) :
237  tusbd_msc_write10_cb(edpt_hdl.coreid, p_cbw->lun, &p_buffer, lba, block_count);
238  xferred_block = min16_of(xferred_block, block_count);
239 
240  uint16_t const xferred_byte = xferred_block * (p_cbw->xfer_bytes / block_count);
241 
242  if ( 0 == xferred_block )
243  { // xferred_block is zero will cause pipe is stalled & status in CSW set to failed
244  p_csw->data_residue = p_cbw->xfer_bytes;
245  p_csw->status = MSC_CSW_STATUS_FAILED;
246 
247  (void) dcd_pipe_stall(edpt_hdl);
248 
249  return true;
250  } else if (xferred_block < block_count)
251  {
252  ASSERT_STATUS( dcd_pipe_xfer( edpt_hdl, p_buffer, xferred_byte, true) );
253 
254  // adjust lba, block_count, xfer_bytes for the next call
255  p_readwrite->lba = __n2be(lba+xferred_block);
256  p_readwrite->block_count = __n2be_16(block_count - xferred_block);
257  p_cbw->xfer_bytes -= xferred_byte;
258 
259  return false;
260  }else
261  {
262  p_csw->status = MSC_CSW_STATUS_PASSED;
263  ASSERT_STATUS( dcd_pipe_queue_xfer( edpt_hdl, p_buffer, xferred_byte) );
264  return true;
265  }
266 }
267 
268 #endif
uint32_t xfer_bytes
The number of bytes of data that the host expects to transfer on the Bulk-In or Bulk-Out endpoint (as...
Definition: msc.h:100
SCSI Read 10 Command.
Definition: msc.h:340
USB Standard Interface Descriptor (section 9.6.1 table 9-12)
uint32_t tag
The device shall set this field to the value received in the dCBWTag of the associated CBW...
Definition: msc.h:112
#define __n2be(x)
built-in function to convert 32-bit from native to Big Endian
Definition: compiler_gcc.h:135
uint32_t data_residue
For Data-Out the device shall report in the dCSWDataResiduethe difference between the amount of data ...
Definition: msc.h:113
uint16_t tusbd_msc_read10_cb(uint8_t coreid, uint8_t lun, void **pp_buffer, uint32_t lba, uint16_t block_count)
Callback that is invoked when tinyusb stack received SCSI_CMD_READ_10 command from host...
MSC_CSW_STATUS_FAILED.
Definition: msc.h:92
uint8_t bInterfaceProtocol
Protocol code (assigned by the USB). These codes are qualified by the value of the bInterfaceClass ...
Constant value of 43425355h (little endian)
Definition: msc.h:69
Constant value of 53425355h (little endian)
Definition: msc.h:70
uint8_t lun
The device Logical Unit Number (LUN) to which the command block is being sent. For devices that suppo...
Definition: msc.h:102
uint32_t signature
Signature that helps identify this data packet as a CBW. The signature field shall contain the value ...
Definition: msc.h:98
The WRITE (10) command requests thatthe device server transfer the specified logical block(s) from th...
Definition: msc.h:134
#define BIT_TEST_(x, n)
check if n-th bit of x is 1
Definition: binary.h:57
#define __be2n(x)
built-in function to convert 32-bit from Big Endian to native
Definition: compiler_gcc.h:136
Bulk-Only Transport.
Definition: msc.h:78
The READ (10) command requests that the device server read the specified logical block(s) and transfe...
Definition: msc.h:133
struct tusb_descriptor_endpoint_t::@8 bmAttributes
This field describes the endpoint's attributes when it is configured using the bConfigurationValue. Bits 1..0: Transfer Type - 00 = Control - 01 = Isochronous - 10 = Bulk - 11 = Interrupt If not an isochronous endpoint, bits 5..2 are reserved and must be set to zero. If isochronous, they are defined as follows: Bits 3..2: Synchronization Type - 00 = No Synchronization - 01 = Asynchronous - 10 = Adaptive - 11 = Synchronous Bits 5..4: Usage Type - 00 = Data endpoint - 01 = Feedback endpoint - 10 = Implicit feedback Data endpoint - 11 = Reserved Refer to Chapter 5 of USB 2.0 specification for more information. All other bits are reserved and must be reset to zero. Reserved bits must be ignored by the host.
uint16_t block_count
Number of Blocks used by this command.
Definition: msc.h:345
#define TUSB_CFG_ATTR_USBRAM
uint32_t lba
The first Logical Block Address (LBA) accessed by this command.
Definition: msc.h:343
MSC_CSW_STATUS_PASSED.
Definition: msc.h:91
void tusbd_msc_mounted_cb(uint8_t coreid)
Callback function that will be invoked when this interface is mounted (configured) by USB host...
uint8_t bDescriptorType
ENDPOINT Descriptor Type.
uint8_t bInterfaceSubClass
Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bInterfaceCla...
USB Standard Endpoint Descriptor (section 9.6.1 table 9-13)
This request is used to reset the mass storage device and its associated interface. This class-specific request shall ready the device for the next CBW from the host.
Definition: msc.h:84
uint8_t bInterfaceNumber
Number of this interface. Zero-based value identifying the index in the array of concurrent interface...
uint32_t tag
Tag sent by the host. The device shall echo the contents of this field back to the host in the dCSWTa...
Definition: msc.h:99
The Get Max LUN device request is used to determine the number of logical units supported by the devi...
Definition: msc.h:83
uint8_t dir
Bit 7 of this field define transfer direction - 0 : Data-Out from host to the device. - 1 : Data-In from the device to the host.
Definition: msc.h:101
Command Status Wrapper.
Definition: msc.h:110
tusb_error_t
Error Code returned.
Definition: tusb_errors.h:100
Command Block Wrapper.
Definition: msc.h:97
uint16_t tusbd_msc_write10_cb(uint8_t coreid, uint8_t lun, void **pp_buffer, uint32_t lba, uint16_t block_count)
Callback that is invoked when tinyusb stack received SCSI_CMD_WRITE_10 command from host...
SCSI transparent command set.
Definition: msc.h:65
uint8_t command[16]
The command block to be executed by the device. The device shall interpret the first cmd_len bytes in...
Definition: msc.h:104
uint8_t bInterfaceClass
Class code (assigned by the USB-IF).
uint32_t signature
Signature that helps identify this data packet as a CSW. The signature field shall contain the value ...
Definition: msc.h:111
uint8_t status
indicates the success or failure of the command. Values from msc_csw_status_t
Definition: msc.h:114
uint8_t bEndpointAddress
The address of the endpoint on the USB device described by this descriptor. The address is encoded as...
void tusbd_msc_unmounted_cb(uint8_t coreid)
Callback function that will be invoked when this interface is unmounted (bus reset/unplugged) ...
msc_csw_status_t tusbd_msc_scsi_cb(uint8_t coreid, uint8_t lun, uint8_t scsi_cmd[16], void const **pp_buffer, uint16_t *p_length)
Callback that is invoked when tinyusb stack received an SCSI command other than SCSI_CMD_WRITE_10 and...