blob: 97837b114a1a769fa6d3768cf42fb853d2dcae3b [file] [log] [blame]
Austin Schuh41baf202022-01-01 14:33:40 -08001/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2019 Ha Thach (tinyusb.org)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 * This file is part of the TinyUSB stack.
25 */
26
27#include "tusb_option.h"
28
29#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MSC)
30
31#include "device/usbd.h"
32#include "device/usbd_pvt.h"
33#include "device/dcd.h" // for faking dcd_event_xfer_complete
34
35#include "msc_device.h"
36
37//--------------------------------------------------------------------+
38// MACRO CONSTANT TYPEDEF
39//--------------------------------------------------------------------+
40
41// Can be selectively disabled to reduce logging when troubleshooting other driver
42#define MSC_DEBUG 2
43
44enum
45{
46 MSC_STAGE_CMD = 0,
47 MSC_STAGE_DATA,
48 MSC_STAGE_STATUS,
49 MSC_STAGE_STATUS_SENT,
50 MSC_STAGE_NEED_RESET,
51};
52
53typedef struct
54{
55 // TODO optimize alignment
56 CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
57 CFG_TUSB_MEM_ALIGN msc_csw_t csw;
58
59 uint8_t itf_num;
60 uint8_t ep_in;
61 uint8_t ep_out;
62
63 // Bulk Only Transfer (BOT) Protocol
64 uint8_t stage;
65 uint32_t total_len; // byte to be transferred, can be smaller than total_bytes in cbw
66 uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
67
68 // Sense Response Data
69 uint8_t sense_key;
70 uint8_t add_sense_code;
71 uint8_t add_sense_qualifier;
72}mscd_interface_t;
73
74CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
75CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_EP_BUFSIZE];
76
77//--------------------------------------------------------------------+
78// INTERNAL OBJECT & FUNCTION DECLARATION
79//--------------------------------------------------------------------+
80static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
81static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
82
83static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
84static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes);
85
86TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir)
87{
88 return tu_bit_test(dir, 7);
89}
90
91static inline bool send_csw(uint8_t rhport, mscd_interface_t* p_msc)
92{
93 // Data residue is always = host expect - actual transferred
94 p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
95
96 p_msc->stage = MSC_STAGE_STATUS_SENT;
97 return usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t));
98}
99
100static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc)
101{
102 p_msc->stage = MSC_STAGE_CMD;
103 return usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t));
104}
105
106static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status)
107{
108 msc_cbw_t const * p_cbw = &p_msc->cbw;
109 msc_csw_t * p_csw = &p_msc->csw;
110
111 p_csw->status = status;
112 p_csw->data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
113 p_msc->stage = MSC_STAGE_STATUS;
114
115 // failed but sense key is not set: default to Illegal Request
116 if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
117
118 // If there is data stage and not yet complete, stall it
119 if ( p_cbw->total_bytes && p_csw->data_residue )
120 {
121 if ( is_data_in(p_cbw->dir) )
122 {
123 usbd_edpt_stall(rhport, p_msc->ep_in);
124 }
125 else
126 {
127 usbd_edpt_stall(rhport, p_msc->ep_out);
128 }
129 }
130}
131
132static inline uint32_t rdwr10_get_lba(uint8_t const command[])
133{
134 // use offsetof to avoid pointer to the odd/unaligned address
135 uint32_t const lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
136
137 // lba is in Big Endian
138 return tu_ntohl(lba);
139}
140
141static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw)
142{
143 uint16_t const block_count = tu_unaligned_read16(cbw->command + offsetof(scsi_write10_t, block_count));
144 return tu_ntohs(block_count);
145}
146
147static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw)
148{
149 // first extract block count in the command
150 uint16_t const block_count = rdwr10_get_blockcount(cbw);
151
152 // invalid block count
153 if (block_count == 0) return 0;
154
155 return (uint16_t) (cbw->total_bytes / block_count);
156}
157
158uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw)
159{
160 uint8_t status = MSC_CSW_STATUS_PASSED;
161 uint16_t const block_count = rdwr10_get_blockcount(cbw);
162
163 if ( cbw->total_bytes == 0 )
164 {
165 if ( block_count )
166 {
167 TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
168 status = MSC_CSW_STATUS_PHASE_ERROR;
169 }else
170 {
171 // no data transfer, only exist in complaint test suite
172 }
173 }else
174 {
175 if ( SCSI_CMD_READ_10 == cbw->command[0] && !is_data_in(cbw->dir) )
176 {
177 TU_LOG(MSC_DEBUG, " SCSI case 10 (Ho <> Di)\r\n");
178 status = MSC_CSW_STATUS_PHASE_ERROR;
179 }
180 else if ( SCSI_CMD_WRITE_10 == cbw->command[0] && is_data_in(cbw->dir) )
181 {
182 TU_LOG(MSC_DEBUG, " SCSI case 8 (Hi <> Do)\r\n");
183 status = MSC_CSW_STATUS_PHASE_ERROR;
184 }
185 else if ( 0 == block_count )
186 {
187 TU_LOG(MSC_DEBUG, " SCSI case 4 Hi > Dn (READ10) or case 9 Ho > Dn (WRITE10) \r\n");
188 status = MSC_CSW_STATUS_FAILED;
189 }
190 else if ( cbw->total_bytes / block_count == 0 )
191 {
192 TU_LOG(MSC_DEBUG, " Computed block size = 0. SCSI case 7 Hi < Di (READ10) or case 13 Ho < Do (WRIT10)\r\n");
193 status = MSC_CSW_STATUS_PHASE_ERROR;
194 }
195 }
196
197 return status;
198}
199
200//--------------------------------------------------------------------+
201// Debug
202//--------------------------------------------------------------------+
203#if CFG_TUSB_DEBUG >= 2
204
205TU_ATTR_UNUSED static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
206{
207 { .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
208 { .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
209 { .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" },
210 { .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" },
211 { .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" },
212 { .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent Allow Medium Removal" },
213 { .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" },
214 { .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" },
215 { .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" },
216 { .key = SCSI_CMD_READ_10 , .data = "Read10" },
217 { .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
218};
219
220TU_ATTR_UNUSED static tu_lookup_table_t const _msc_scsi_cmd_table =
221{
222 .count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
223 .items = _msc_scsi_cmd_lookup
224};
225
226#endif
227
228//--------------------------------------------------------------------+
229// APPLICATION API
230//--------------------------------------------------------------------+
231bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier)
232{
233 (void) lun;
234
235 _mscd_itf.sense_key = sense_key;
236 _mscd_itf.add_sense_code = add_sense_code;
237 _mscd_itf.add_sense_qualifier = add_sense_qualifier;
238
239 return true;
240}
241
242//--------------------------------------------------------------------+
243// USBD Driver API
244//--------------------------------------------------------------------+
245void mscd_init(void)
246{
247 tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
248}
249
250void mscd_reset(uint8_t rhport)
251{
252 (void) rhport;
253 tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
254}
255
256uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
257{
258 // only support SCSI's BOT protocol
259 TU_VERIFY(TUSB_CLASS_MSC == itf_desc->bInterfaceClass &&
260 MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
261 MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol, 0);
262
263 // msc driver length is fixed
264 uint16_t const drv_len = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
265
266 // Max length must be at least 1 interface + 2 endpoints
267 TU_ASSERT(max_len >= drv_len, 0);
268
269 mscd_interface_t * p_msc = &_mscd_itf;
270 p_msc->itf_num = itf_desc->bInterfaceNumber;
271
272 // Open endpoint pair
273 TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0 );
274
275 // Prepare for Command Block Wrapper
276 TU_ASSERT( prepare_cbw(rhport, p_msc), drv_len);
277
278 return drv_len;
279}
280
281static void proc_bot_reset(mscd_interface_t* p_msc)
282{
283 p_msc->stage = MSC_STAGE_CMD;
284 p_msc->total_len = 0;
285 p_msc->xferred_len = 0;
286
287 p_msc->sense_key = 0;
288 p_msc->add_sense_code = 0;
289 p_msc->add_sense_qualifier = 0;
290}
291
292// Invoked when a control transfer occurred on an interface of this class
293// Driver response accordingly to the request and the transfer stage (setup/data/ack)
294// return false to stall control endpoint (e.g unsupported request)
295bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
296{
297 // nothing to do with DATA & ACK stage
298 if (stage != CONTROL_STAGE_SETUP) return true;
299
300 mscd_interface_t* p_msc = &_mscd_itf;
301
302 // Clear Endpoint Feature (stall) for recovery
303 if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
304 TUSB_REQ_RCPT_ENDPOINT == request->bmRequestType_bit.recipient &&
305 TUSB_REQ_CLEAR_FEATURE == request->bRequest &&
306 TUSB_REQ_FEATURE_EDPT_HALT == request->wValue )
307 {
308 uint8_t const ep_addr = tu_u16_low(request->wIndex);
309
310 if ( p_msc->stage == MSC_STAGE_NEED_RESET )
311 {
312 // reset recovery is required to recover from this stage
313 // Clear Stall request cannot resolve this -> continue to stall endpoint
314 usbd_edpt_stall(rhport, ep_addr);
315 }
316 else
317 {
318 if ( ep_addr == p_msc->ep_in )
319 {
320 if ( p_msc->stage == MSC_STAGE_STATUS )
321 {
322 // resume sending SCSI status if we are in this stage previously before stalled
323 TU_ASSERT( send_csw(rhport, p_msc) );
324 }
325 }
326 else if ( ep_addr == p_msc->ep_out )
327 {
328 if ( p_msc->stage == MSC_STAGE_CMD )
329 {
330 // part of reset recovery (probably due to invalid CBW) -> prepare for new command
331 // Note: skip if already queued previously
332 if ( usbd_edpt_ready(rhport, p_msc->ep_out) )
333 {
334 TU_ASSERT( prepare_cbw(rhport, p_msc) );
335 }
336 }
337 }
338 }
339
340 return true;
341 }
342
343 // From this point only handle class request only
344 TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
345
346 switch ( request->bRequest )
347 {
348 case MSC_REQ_RESET:
349 TU_LOG(MSC_DEBUG, " MSC BOT Reset\r\n");
350 TU_VERIFY(request->wValue == 0 && request->wLength == 0);
351
352 // driver state reset
353 proc_bot_reset(p_msc);
354
355 tud_control_status(rhport, request);
356 break;
357
358 case MSC_REQ_GET_MAX_LUN:
359 {
360 TU_LOG(MSC_DEBUG, " MSC Get Max Lun\r\n");
361 TU_VERIFY(request->wValue == 0 && request->wLength == 1);
362
363 uint8_t maxlun = 1;
364 if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb();
365 TU_VERIFY(maxlun);
366
367 // MAX LUN is minus 1 by specs
368 maxlun--;
369
370 tud_control_xfer(rhport, request, &maxlun, 1);
371 }
372 break;
373
374 default: return false; // stall unsupported request
375 }
376
377 return true;
378}
379
380bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
381{
382 (void) event;
383
384 mscd_interface_t* p_msc = &_mscd_itf;
385 msc_cbw_t const * p_cbw = &p_msc->cbw;
386 msc_csw_t * p_csw = &p_msc->csw;
387
388 switch (p_msc->stage)
389 {
390 case MSC_STAGE_CMD:
391 //------------- new CBW received -------------//
392 // Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
393 if(ep_addr != p_msc->ep_out) return true;
394
395 if ( !(xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE) )
396 {
397 TU_LOG(MSC_DEBUG, " SCSI CBW is not valid\r\n");
398
399 // BOT 6.6.1 If CBW is not valid stall both endpoints until reset recovery
400 p_msc->stage = MSC_STAGE_NEED_RESET;
401
402 // invalid CBW stall both endpoints
403 usbd_edpt_stall(rhport, p_msc->ep_in);
404 usbd_edpt_stall(rhport, p_msc->ep_out);
405
406 return false;
407 }
408
409 TU_LOG(MSC_DEBUG, " SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
410 //TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
411
412 p_csw->signature = MSC_CSW_SIGNATURE;
413 p_csw->tag = p_cbw->tag;
414 p_csw->data_residue = 0;
415 p_csw->status = MSC_CSW_STATUS_PASSED;
416
417 /*------------- Parse command and prepare DATA -------------*/
418 p_msc->stage = MSC_STAGE_DATA;
419 p_msc->total_len = p_cbw->total_bytes;
420 p_msc->xferred_len = 0;
421
422 // Read10 or Write10
423 if ( (SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0]) )
424 {
425 uint8_t const status = rdwr10_validate_cmd(p_cbw);
426
427 if ( status != MSC_CSW_STATUS_PASSED)
428 {
429 fail_scsi_op(rhport, p_msc, status);
430 }else if ( p_cbw->total_bytes )
431 {
432 if (SCSI_CMD_READ_10 == p_cbw->command[0])
433 {
434 proc_read10_cmd(rhport, p_msc);
435 }else
436 {
437 proc_write10_cmd(rhport, p_msc);
438 }
439 }else
440 {
441 // no data transfer, only exist in complaint test suite
442 p_msc->stage = MSC_STAGE_STATUS;
443 }
444 }
445 else
446 {
447 // For other SCSI commands
448 // 1. OUT : queue transfer (invoke app callback after done)
449 // 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
450 if ( (p_cbw->total_bytes > 0 ) && !is_data_in(p_cbw->dir) )
451 {
452 if (p_cbw->total_bytes > sizeof(_mscd_buf))
453 {
454 TU_LOG(MSC_DEBUG, " SCSI reject non READ10/WRITE10 with large data\r\n");
455 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
456 }else
457 {
458 // Didn't check for case 9 (Ho > Dn), which requires examining scsi command first
459 // but it is OK to just receive data then responded with failed status
460 TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
461 }
462 }else
463 {
464 // First process if it is a built-in commands
465 int32_t resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
466
467 // Invoke user callback if not built-in
468 if ( (resplen < 0) && (p_msc->sense_key == 0) )
469 {
470 resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
471 }
472
473 if ( resplen < 0 )
474 {
475 // unsupported command
476 TU_LOG(MSC_DEBUG, " SCSI unsupported command\r\n");
477 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
478 }
479 else if (resplen == 0)
480 {
481 if (p_cbw->total_bytes)
482 {
483 // 6.7 The 13 Cases: case 4 (Hi > Dn)
484 // TU_LOG(MSC_DEBUG, " SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
485 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
486 }else
487 {
488 // case 1 Hn = Dn: all good
489 p_msc->stage = MSC_STAGE_STATUS;
490 }
491 }
492 else
493 {
494 if ( p_cbw->total_bytes == 0 )
495 {
496 // 6.7 The 13 Cases: case 2 (Hn < Di)
497 // TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
498 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
499 }else
500 {
501 // cannot return more than host expect
502 p_msc->total_len = tu_min32((uint32_t) resplen, p_cbw->total_bytes);
503 TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
504 }
505 }
506 }
507 }
508 break;
509
510 case MSC_STAGE_DATA:
511 TU_LOG(MSC_DEBUG, " SCSI Data\r\n");
512 //TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2);
513
514 if (SCSI_CMD_READ_10 == p_cbw->command[0])
515 {
516 p_msc->xferred_len += xferred_bytes;
517
518 if ( p_msc->xferred_len >= p_msc->total_len )
519 {
520 // Data Stage is complete
521 p_msc->stage = MSC_STAGE_STATUS;
522 }else
523 {
524 proc_read10_cmd(rhport, p_msc);
525 }
526 }
527 else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
528 {
529 proc_write10_new_data(rhport, p_msc, xferred_bytes);
530 }
531 else
532 {
533 p_msc->xferred_len += xferred_bytes;
534
535 // OUT transfer, invoke callback if needed
536 if ( !is_data_in(p_cbw->dir) )
537 {
538 int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
539
540 if ( cb_result < 0 )
541 {
542 // unsupported command
543 TU_LOG(MSC_DEBUG, " SCSI unsupported command\r\n");
544 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
545 }else
546 {
547 // TODO haven't implement this scenario any further yet
548 }
549 }
550
551 if ( p_msc->xferred_len >= p_msc->total_len )
552 {
553 // Data Stage is complete
554 p_msc->stage = MSC_STAGE_STATUS;
555 }
556 else
557 {
558 // This scenario with command that take more than one transfer is already rejected at Command stage
559 TU_BREAKPOINT();
560 }
561 }
562 break;
563
564 case MSC_STAGE_STATUS:
565 // processed immediately after this switch, supposedly to be empty
566 break;
567
568 case MSC_STAGE_STATUS_SENT:
569 // Wait for the Status phase to complete
570 if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
571 {
572 TU_LOG(MSC_DEBUG, " SCSI Status = %u\r\n", p_csw->status);
573 // TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2);
574
575 // Invoke complete callback if defined
576 // Note: There is racing issue with samd51 + qspi flash testing with arduino
577 // if complete_cb() is invoked after queuing the status.
578 switch(p_cbw->command[0])
579 {
580 case SCSI_CMD_READ_10:
581 if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun);
582 break;
583
584 case SCSI_CMD_WRITE_10:
585 if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun);
586 break;
587
588 default:
589 if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
590 break;
591 }
592
593 TU_ASSERT( prepare_cbw(rhport, p_msc) );
594 }else
595 {
596 // Any xfer ended here is consider unknown error, ignore it
597 TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
598 }
599 break;
600
601 default : break;
602 }
603
604 if ( p_msc->stage == MSC_STAGE_STATUS )
605 {
606 // skip status if epin is currently stalled, will do it when received Clear Stall request
607 if ( !usbd_edpt_stalled(rhport, p_msc->ep_in) )
608 {
609 if ( (p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir) )
610 {
611 // 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status
612 // TU_LOG(MSC_DEBUG, " SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
613 usbd_edpt_stall(rhport, p_msc->ep_in);
614 }else
615 {
616 TU_ASSERT( send_csw(rhport, p_msc) );
617 }
618 }
619
620 #if TU_CHECK_MCU(OPT_MCU_CXD56)
621 // WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD.
622 // There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and
623 // hope everything will work
624 if ( usbd_edpt_stalled(rhport, p_msc->ep_in) )
625 {
626 usbd_edpt_clear_stall(rhport, p_msc->ep_in);
627 send_csw(rhport, p_msc);
628 }
629 #endif
630 }
631
632 return true;
633}
634
635/*------------------------------------------------------------------*/
636/* SCSI Command Process
637 *------------------------------------------------------------------*/
638
639// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
640// In case of a failed status, sense key must be set for reason of failure
641static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
642{
643 (void) bufsize; // TODO refractor later
644 int32_t resplen;
645
646 mscd_interface_t* p_msc = &_mscd_itf;
647
648 switch ( scsi_cmd[0] )
649 {
650 case SCSI_CMD_TEST_UNIT_READY:
651 resplen = 0;
652 if ( !tud_msc_test_unit_ready_cb(lun) )
653 {
654 // Failed status response
655 resplen = - 1;
656
657 // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
658 if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
659 }
660 break;
661
662 case SCSI_CMD_START_STOP_UNIT:
663 resplen = 0;
664
665 if (tud_msc_start_stop_cb)
666 {
667 scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
668 if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
669 {
670 // Failed status response
671 resplen = - 1;
672
673 // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
674 if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
675 }
676 }
677 break;
678
679 case SCSI_CMD_READ_CAPACITY_10:
680 {
681 uint32_t block_count;
682 uint32_t block_size;
683 uint16_t block_size_u16;
684
685 tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
686 block_size = (uint32_t) block_size_u16;
687
688 // Invalid block size/count from callback, possibly unit is not ready
689 // stall this request, set sense key to NOT READY
690 if (block_count == 0 || block_size == 0)
691 {
692 resplen = -1;
693
694 // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
695 if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
696 }else
697 {
698 scsi_read_capacity10_resp_t read_capa10;
699
700 read_capa10.last_lba = tu_htonl(block_count-1);
701 read_capa10.block_size = tu_htonl(block_size);
702
703 resplen = sizeof(read_capa10);
704 memcpy(buffer, &read_capa10, resplen);
705 }
706 }
707 break;
708
709 case SCSI_CMD_READ_FORMAT_CAPACITY:
710 {
711 scsi_read_format_capacity_data_t read_fmt_capa =
712 {
713 .list_length = 8,
714 .block_num = 0,
715 .descriptor_type = 2, // formatted media
716 .block_size_u16 = 0
717 };
718
719 uint32_t block_count;
720 uint16_t block_size;
721
722 tud_msc_capacity_cb(lun, &block_count, &block_size);
723
724 // Invalid block size/count from callback, possibly unit is not ready
725 // stall this request, set sense key to NOT READY
726 if (block_count == 0 || block_size == 0)
727 {
728 resplen = -1;
729
730 // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
731 if ( p_msc->sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
732 }else
733 {
734 read_fmt_capa.block_num = tu_htonl(block_count);
735 read_fmt_capa.block_size_u16 = tu_htons(block_size);
736
737 resplen = sizeof(read_fmt_capa);
738 memcpy(buffer, &read_fmt_capa, resplen);
739 }
740 }
741 break;
742
743 case SCSI_CMD_INQUIRY:
744 {
745 scsi_inquiry_resp_t inquiry_rsp =
746 {
747 .is_removable = 1,
748 .version = 2,
749 .response_data_format = 2,
750 };
751
752 // vendor_id, product_id, product_rev is space padded string
753 memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
754 memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
755 memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
756
757 tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
758
759 resplen = sizeof(inquiry_rsp);
760 memcpy(buffer, &inquiry_rsp, resplen);
761 }
762 break;
763
764 case SCSI_CMD_MODE_SENSE_6:
765 {
766 scsi_mode_sense6_resp_t mode_resp =
767 {
768 .data_len = 3,
769 .medium_type = 0,
770 .write_protected = false,
771 .reserved = 0,
772 .block_descriptor_len = 0 // no block descriptor are included
773 };
774
775 bool writable = true;
776 if ( tud_msc_is_writable_cb )
777 {
778 writable = tud_msc_is_writable_cb(lun);
779 }
780
781 mode_resp.write_protected = !writable;
782
783 resplen = sizeof(mode_resp);
784 memcpy(buffer, &mode_resp, resplen);
785 }
786 break;
787
788 case SCSI_CMD_REQUEST_SENSE:
789 {
790 scsi_sense_fixed_resp_t sense_rsp =
791 {
792 .response_code = 0x70,
793 .valid = 1
794 };
795
796 sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
797
798 sense_rsp.sense_key = p_msc->sense_key;
799 sense_rsp.add_sense_code = p_msc->add_sense_code;
800 sense_rsp.add_sense_qualifier = p_msc->add_sense_qualifier;
801
802 resplen = sizeof(sense_rsp);
803 memcpy(buffer, &sense_rsp, resplen);
804
805 // Clear sense data after copy
806 tud_msc_set_sense(lun, 0, 0, 0);
807 }
808 break;
809
810 default: resplen = -1; break;
811 }
812
813 return resplen;
814}
815
816static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
817{
818 msc_cbw_t const * p_cbw = &p_msc->cbw;
819
820 // block size already verified not zero
821 uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
822
823 // Adjust lba with transferred bytes
824 uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
825
826 // remaining bytes capped at class buffer
827 int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
828
829 // Application can consume smaller bytes
830 uint32_t const offset = p_msc->xferred_len % block_sz;
831 nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_buf, (uint32_t) nbytes);
832
833 if ( nbytes < 0 )
834 {
835 // negative means error -> endpoint is stalled & status in CSW set to failed
836 TU_LOG(MSC_DEBUG, " tud_msc_read10_cb() return -1\r\n");
837
838 // Sense = Flash not ready for access
839 tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_MEDIUM_ERROR, 0x33, 0x00);
840
841 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
842 }
843 else if ( nbytes == 0 )
844 {
845 // zero means not ready -> simulate an transfer complete so that this driver callback will fired again
846 dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false);
847 }
848 else
849 {
850 TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), );
851 }
852}
853
854static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
855{
856 msc_cbw_t const * p_cbw = &p_msc->cbw;
857 bool writable = true;
858
859 if ( tud_msc_is_writable_cb )
860 {
861 writable = tud_msc_is_writable_cb(p_cbw->lun);
862 }
863
864 if ( !writable )
865 {
866 // Not writable, complete this SCSI op with error
867 // Sense = Write protected
868 tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
869 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
870 return;
871 }
872
873 // remaining bytes capped at class buffer
874 int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
875
876 // Write10 callback will be called later when usb transfer complete
877 TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
878}
879
880// process new data arrived from WRITE10
881static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes)
882{
883 msc_cbw_t const * p_cbw = &p_msc->cbw;
884
885 // block size already verified not zero
886 uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
887
888 // Adjust lba with transferred bytes
889 uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
890
891 // Invoke callback to consume new data
892 uint32_t const offset = p_msc->xferred_len % block_sz;
893 int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_buf, xferred_bytes);
894
895 if ( nbytes < 0 )
896 {
897 // negative means error -> failed this scsi op
898 TU_LOG(MSC_DEBUG, " tud_msc_write10_cb() return -1\r\n");
899
900 // update actual byte before failed
901 p_msc->xferred_len += xferred_bytes;
902
903 // Sense = Flash not ready for access
904 tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_MEDIUM_ERROR, 0x33, 0x00);
905
906 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
907 }else
908 {
909 // Application consume less than what we got (including zero)
910 if ( (uint32_t) nbytes < xferred_bytes )
911 {
912 if ( nbytes > 0 )
913 {
914 p_msc->xferred_len += nbytes;
915 memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes);
916 }
917
918 // simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
919 dcd_event_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, XFER_RESULT_SUCCESS, false);
920 }
921 else
922 {
923 // Application consume all bytes in our buffer
924 p_msc->xferred_len += xferred_bytes;
925
926 if ( p_msc->xferred_len >= p_msc->total_len )
927 {
928 // Data Stage is complete
929 p_msc->stage = MSC_STAGE_STATUS;
930 }else
931 {
932 // prepare to receive more data from host
933 proc_write10_cmd(rhport, p_msc);
934 }
935 }
936 }
937}
938
939#endif