Open FFBoard
Open source force feedback firmware
msc_device.c
Go to the documentation of this file.
1/*
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 (CFG_TUD_ENABLED && CFG_TUD_MSC)
30
31#include "device/dcd.h" // for faking dcd_event_xfer_complete
32#include "device/usbd.h"
33#include "device/usbd_pvt.h"
34
35#include "msc_device.h"
36
37// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
38#ifndef CFG_TUD_MSC_LOG_LEVEL
39 #define CFG_TUD_MSC_LOG_LEVEL CFG_TUD_LOG_LEVEL
40#endif
41
42#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_MSC_LOG_LEVEL, __VA_ARGS__)
43
44//--------------------------------------------------------------------+
45// MACRO CONSTANT TYPEDEF
46//--------------------------------------------------------------------+
47enum
48{
54};
55
56typedef struct
57{
58 // TODO optimize alignment
59 CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
60 CFG_TUSB_MEM_ALIGN msc_csw_t csw;
61
62 uint8_t itf_num;
63 uint8_t ep_in;
64 uint8_t ep_out;
65
66 // Bulk Only Transfer (BOT) Protocol
67 uint8_t stage;
68 uint32_t total_len; // byte to be transferred, can be smaller than total_bytes in cbw
69 uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
70
71 // Sense Response Data
72 uint8_t sense_key;
76
77CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mscd_interface_t _mscd_itf;
78CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static uint8_t _mscd_buf[CFG_TUD_MSC_EP_BUFSIZE];
79
80//--------------------------------------------------------------------+
81// INTERNAL OBJECT & FUNCTION DECLARATION
82//--------------------------------------------------------------------+
83static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
84static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
85
86static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
87static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes);
88
89TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir)
90{
91 return tu_bit_test(dir, 7);
92}
93
94static inline bool send_csw(uint8_t rhport, mscd_interface_t* p_msc)
95{
96 // Data residue is always = host expect - actual transferred
97 p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
98
100 return usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t));
101}
102
103static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc)
104{
105 p_msc->stage = MSC_STAGE_CMD;
106 return usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t));
107}
108
109static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status)
110{
111 msc_cbw_t const * p_cbw = &p_msc->cbw;
112 msc_csw_t * p_csw = &p_msc->csw;
113
114 p_csw->status = status;
115 p_csw->data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
116 p_msc->stage = MSC_STAGE_STATUS;
117
118 // failed but sense key is not set: default to Illegal Request
119 if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
120
121 // If there is data stage and not yet complete, stall it
122 if ( p_cbw->total_bytes && p_csw->data_residue )
123 {
124 if ( is_data_in(p_cbw->dir) )
125 {
126 usbd_edpt_stall(rhport, p_msc->ep_in);
127 }
128 else
129 {
130 usbd_edpt_stall(rhport, p_msc->ep_out);
131 }
132 }
133}
134
135static inline uint32_t rdwr10_get_lba(uint8_t const command[])
136{
137 // use offsetof to avoid pointer to the odd/unaligned address
138 uint32_t const lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
139
140 // lba is in Big Endian
141 return tu_ntohl(lba);
142}
143
144static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw)
145{
146 uint16_t const block_count = tu_unaligned_read16(cbw->command + offsetof(scsi_write10_t, block_count));
147 return tu_ntohs(block_count);
148}
149
150static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw)
151{
152 // first extract block count in the command
153 uint16_t const block_count = rdwr10_get_blockcount(cbw);
154
155 // invalid block count
156 if (block_count == 0) return 0;
157
158 return (uint16_t) (cbw->total_bytes / block_count);
159}
160
162{
163 uint8_t status = MSC_CSW_STATUS_PASSED;
164 uint16_t const block_count = rdwr10_get_blockcount(cbw);
165
166 if ( cbw->total_bytes == 0 )
167 {
168 if ( block_count )
169 {
170 TU_LOG_DRV(" SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
172 }else
173 {
174 // no data transfer, only exist in complaint test suite
175 }
176 }else
177 {
178 if ( SCSI_CMD_READ_10 == cbw->command[0] && !is_data_in(cbw->dir) )
179 {
180 TU_LOG_DRV(" SCSI case 10 (Ho <> Di)\r\n");
182 }
183 else if ( SCSI_CMD_WRITE_10 == cbw->command[0] && is_data_in(cbw->dir) )
184 {
185 TU_LOG_DRV(" SCSI case 8 (Hi <> Do)\r\n");
187 }
188 else if ( 0 == block_count )
189 {
190 TU_LOG_DRV(" SCSI case 4 Hi > Dn (READ10) or case 9 Ho > Dn (WRITE10) \r\n");
191 status = MSC_CSW_STATUS_FAILED;
192 }
193 else if ( cbw->total_bytes / block_count == 0 )
194 {
195 TU_LOG_DRV(" Computed block size = 0. SCSI case 7 Hi < Di (READ10) or case 13 Ho < Do (WRIT10)\r\n");
197 }
198 }
199
200 return status;
201}
202
203//--------------------------------------------------------------------+
204// Debug
205//--------------------------------------------------------------------+
206#if CFG_TUSB_DEBUG >= CFG_TUD_MSC_LOG_LEVEL
207
208TU_ATTR_UNUSED tu_static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
209{
210 { .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
211 { .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
212 { .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" },
213 { .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" },
214 { .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" },
215 { .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent/Allow Medium Removal" },
216 { .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" },
217 { .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" },
218 { .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" },
219 { .key = SCSI_CMD_READ_10 , .data = "Read10" },
220 { .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
221};
222
223TU_ATTR_UNUSED tu_static tu_lookup_table_t const _msc_scsi_cmd_table =
224{
225 .count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
226 .items = _msc_scsi_cmd_lookup
227};
228
229#endif
230
231//--------------------------------------------------------------------+
232// APPLICATION API
233//--------------------------------------------------------------------+
234bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier)
235{
236 (void) lun;
237
238 _mscd_itf.sense_key = sense_key;
239 _mscd_itf.add_sense_code = add_sense_code;
240 _mscd_itf.add_sense_qualifier = add_sense_qualifier;
241
242 return true;
243}
244
245static inline void set_sense_medium_not_present(uint8_t lun)
246{
247 // default sense is NOT READY, MEDIUM NOT PRESENT
248 tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
249}
250
251//--------------------------------------------------------------------+
252// USBD Driver API
253//--------------------------------------------------------------------+
254void mscd_init(void) {
255 tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
256}
257
258bool mscd_deinit(void) {
259 // nothing to do
260 return true;
261}
262
263void mscd_reset(uint8_t rhport)
264{
265 (void) rhport;
266 tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
267}
268
269uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
270{
271 // only support SCSI's BOT protocol
272 TU_VERIFY(TUSB_CLASS_MSC == itf_desc->bInterfaceClass &&
273 MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
274 MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol, 0);
275
276 // msc driver length is fixed
277 uint16_t const drv_len = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
278
279 // Max length must be at least 1 interface + 2 endpoints
280 TU_ASSERT(max_len >= drv_len, 0);
281
282 mscd_interface_t * p_msc = &_mscd_itf;
283 p_msc->itf_num = itf_desc->bInterfaceNumber;
284
285 // Open endpoint pair
286 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 );
287
288 // Prepare for Command Block Wrapper
289 TU_ASSERT( prepare_cbw(rhport, p_msc), drv_len);
290
291 return drv_len;
292}
293
295{
296 p_msc->stage = MSC_STAGE_CMD;
297 p_msc->total_len = 0;
298 p_msc->xferred_len = 0;
299
300 p_msc->sense_key = 0;
301 p_msc->add_sense_code = 0;
302 p_msc->add_sense_qualifier = 0;
303}
304
305// Invoked when a control transfer occurred on an interface of this class
306// Driver response accordingly to the request and the transfer stage (setup/data/ack)
307// return false to stall control endpoint (e.g unsupported request)
308bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
309{
310 // nothing to do with DATA & ACK stage
311 if (stage != CONTROL_STAGE_SETUP) return true;
312
313 mscd_interface_t* p_msc = &_mscd_itf;
314
315 // Clear Endpoint Feature (stall) for recovery
320 {
321 uint8_t const ep_addr = tu_u16_low(request->wIndex);
322
323 if ( p_msc->stage == MSC_STAGE_NEED_RESET )
324 {
325 // reset recovery is required to recover from this stage
326 // Clear Stall request cannot resolve this -> continue to stall endpoint
327 usbd_edpt_stall(rhport, ep_addr);
328 }
329 else
330 {
331 if ( ep_addr == p_msc->ep_in )
332 {
333 if ( p_msc->stage == MSC_STAGE_STATUS )
334 {
335 // resume sending SCSI status if we are in this stage previously before stalled
336 TU_ASSERT( send_csw(rhport, p_msc) );
337 }
338 }
339 else if ( ep_addr == p_msc->ep_out )
340 {
341 if ( p_msc->stage == MSC_STAGE_CMD )
342 {
343 // part of reset recovery (probably due to invalid CBW) -> prepare for new command
344 // Note: skip if already queued previously
345 if ( usbd_edpt_ready(rhport, p_msc->ep_out) )
346 {
347 TU_ASSERT( prepare_cbw(rhport, p_msc) );
348 }
349 }
350 }
351 }
352
353 return true;
354 }
355
356 // From this point only handle class request only
358
359 switch ( request->bRequest )
360 {
361 case MSC_REQ_RESET:
362 TU_LOG_DRV(" MSC BOT Reset\r\n");
363 TU_VERIFY(request->wValue == 0 && request->wLength == 0);
364
365 // driver state reset
366 proc_bot_reset(p_msc);
367
369 break;
370
372 {
373 TU_LOG_DRV(" MSC Get Max Lun\r\n");
374 TU_VERIFY(request->wValue == 0 && request->wLength == 1);
375
376 uint8_t maxlun = 1;
378 TU_VERIFY(maxlun);
379
380 // MAX LUN is minus 1 by specs
381 maxlun--;
382
383 tud_control_xfer(rhport, request, &maxlun, 1);
384 }
385 break;
386
387 default: return false; // stall unsupported request
388 }
389
390 return true;
391}
392
393bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
394{
395 (void) event;
396
397 mscd_interface_t* p_msc = &_mscd_itf;
398 msc_cbw_t const * p_cbw = &p_msc->cbw;
399 msc_csw_t * p_csw = &p_msc->csw;
400
401 switch (p_msc->stage)
402 {
403 case MSC_STAGE_CMD:
404 //------------- new CBW received -------------//
405 // Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
406 if(ep_addr != p_msc->ep_out) return true;
407
408 if ( !(xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE) )
409 {
410 TU_LOG_DRV(" SCSI CBW is not valid\r\n");
411
412 // BOT 6.6.1 If CBW is not valid stall both endpoints until reset recovery
414
415 // invalid CBW stall both endpoints
416 usbd_edpt_stall(rhport, p_msc->ep_in);
417 usbd_edpt_stall(rhport, p_msc->ep_out);
418
419 return false;
420 }
421
422 TU_LOG_DRV(" SCSI Command [Lun%u]: %s\r\n", p_cbw->lun, tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
423 //TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
424
426 p_csw->tag = p_cbw->tag;
427 p_csw->data_residue = 0;
429
430 /*------------- Parse command and prepare DATA -------------*/
431 p_msc->stage = MSC_STAGE_DATA;
432 p_msc->total_len = p_cbw->total_bytes;
433 p_msc->xferred_len = 0;
434
435 // Read10 or Write10
436 if ( (SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0]) )
437 {
438 uint8_t const status = rdwr10_validate_cmd(p_cbw);
439
440 if ( status != MSC_CSW_STATUS_PASSED)
441 {
442 fail_scsi_op(rhport, p_msc, status);
443 }else if ( p_cbw->total_bytes )
444 {
445 if (SCSI_CMD_READ_10 == p_cbw->command[0])
446 {
447 proc_read10_cmd(rhport, p_msc);
448 }else
449 {
450 proc_write10_cmd(rhport, p_msc);
451 }
452 }else
453 {
454 // no data transfer, only exist in complaint test suite
455 p_msc->stage = MSC_STAGE_STATUS;
456 }
457 }
458 else
459 {
460 // For other SCSI commands
461 // 1. OUT : queue transfer (invoke app callback after done)
462 // 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
463 if ( (p_cbw->total_bytes > 0 ) && !is_data_in(p_cbw->dir) )
464 {
465 if (p_cbw->total_bytes > sizeof(_mscd_buf))
466 {
467 TU_LOG_DRV(" SCSI reject non READ10/WRITE10 with large data\r\n");
468 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
469 }else
470 {
471 // Didn't check for case 9 (Ho > Dn), which requires examining scsi command first
472 // but it is OK to just receive data then responded with failed status
473 TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, (uint16_t) p_msc->total_len) );
474 }
475 }else
476 {
477 // First process if it is a built-in commands
478 int32_t resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
479
480 // Invoke user callback if not built-in
481 if ( (resplen < 0) && (p_msc->sense_key == 0) )
482 {
483 resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, (uint16_t) p_msc->total_len);
484 }
485
486 if ( resplen < 0 )
487 {
488 // unsupported command
489 TU_LOG_DRV(" SCSI unsupported or failed command\r\n");
490 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
491 }
492 else if (resplen == 0)
493 {
494 if (p_cbw->total_bytes)
495 {
496 // 6.7 The 13 Cases: case 4 (Hi > Dn)
497 // TU_LOG(MSC_DEBUG, " SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
498 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
499 }else
500 {
501 // case 1 Hn = Dn: all good
502 p_msc->stage = MSC_STAGE_STATUS;
503 }
504 }
505 else
506 {
507 if ( p_cbw->total_bytes == 0 )
508 {
509 // 6.7 The 13 Cases: case 2 (Hn < Di)
510 // TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
511 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
512 }else
513 {
514 // cannot return more than host expect
515 p_msc->total_len = tu_min32((uint32_t) resplen, p_cbw->total_bytes);
516 TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, (uint16_t) p_msc->total_len) );
517 }
518 }
519 }
520 }
521 break;
522
523 case MSC_STAGE_DATA:
524 TU_LOG_DRV(" SCSI Data [Lun%u]\r\n", p_cbw->lun);
525 //TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2);
526
527 if (SCSI_CMD_READ_10 == p_cbw->command[0])
528 {
529 p_msc->xferred_len += xferred_bytes;
530
531 if ( p_msc->xferred_len >= p_msc->total_len )
532 {
533 // Data Stage is complete
534 p_msc->stage = MSC_STAGE_STATUS;
535 }else
536 {
537 proc_read10_cmd(rhport, p_msc);
538 }
539 }
540 else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
541 {
542 proc_write10_new_data(rhport, p_msc, xferred_bytes);
543 }
544 else
545 {
546 p_msc->xferred_len += xferred_bytes;
547
548 // OUT transfer, invoke callback if needed
549 if ( !is_data_in(p_cbw->dir) )
550 {
551 int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, (uint16_t) p_msc->total_len);
552
553 if ( cb_result < 0 )
554 {
555 // unsupported command
556 TU_LOG_DRV(" SCSI unsupported command\r\n");
557 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
558 }else
559 {
560 // TODO haven't implement this scenario any further yet
561 }
562 }
563
564 if ( p_msc->xferred_len >= p_msc->total_len )
565 {
566 // Data Stage is complete
567 p_msc->stage = MSC_STAGE_STATUS;
568 }
569 else
570 {
571 // This scenario with command that take more than one transfer is already rejected at Command stage
572 TU_BREAKPOINT();
573 }
574 }
575 break;
576
577 case MSC_STAGE_STATUS:
578 // processed immediately after this switch, supposedly to be empty
579 break;
580
582 // Wait for the Status phase to complete
583 if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
584 {
585 TU_LOG_DRV(" SCSI Status [Lun%u] = %u\r\n", p_cbw->lun, p_csw->status);
586 // TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2);
587
588 // Invoke complete callback if defined
589 // Note: There is racing issue with samd51 + qspi flash testing with arduino
590 // if complete_cb() is invoked after queuing the status.
591 switch(p_cbw->command[0])
592 {
593 case SCSI_CMD_READ_10:
595 break;
596
599 break;
600
601 default:
603 break;
604 }
605
606 TU_ASSERT( prepare_cbw(rhport, p_msc) );
607 }else
608 {
609 // Any xfer ended here is consider unknown error, ignore it
610 TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
611 }
612 break;
613
614 default : break;
615 }
616
617 if ( p_msc->stage == MSC_STAGE_STATUS )
618 {
619 // skip status if epin is currently stalled, will do it when received Clear Stall request
620 if ( !usbd_edpt_stalled(rhport, p_msc->ep_in) )
621 {
622 if ( (p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir) )
623 {
624 // 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status
625 // TU_LOG(MSC_DEBUG, " SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
626 usbd_edpt_stall(rhport, p_msc->ep_in);
627 }else
628 {
629 TU_ASSERT( send_csw(rhport, p_msc) );
630 }
631 }
632
633 #if TU_CHECK_MCU(OPT_MCU_CXD56)
634 // WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD.
635 // There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and
636 // hope everything will work
637 if ( usbd_edpt_stalled(rhport, p_msc->ep_in) )
638 {
639 usbd_edpt_clear_stall(rhport, p_msc->ep_in);
640 send_csw(rhport, p_msc);
641 }
642 #endif
643 }
644
645 return true;
646}
647
648/*------------------------------------------------------------------*/
649/* SCSI Command Process
650 *------------------------------------------------------------------*/
651
652// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
653// In case of a failed status, sense key must be set for reason of failure
654static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
655{
656 (void) bufsize; // TODO refractor later
657 int32_t resplen;
658
659 mscd_interface_t* p_msc = &_mscd_itf;
660
661 switch ( scsi_cmd[0] )
662 {
664 resplen = 0;
665 if ( !tud_msc_test_unit_ready_cb(lun) )
666 {
667 // Failed status response
668 resplen = - 1;
669
670 // set default sense if not set by callback
671 if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
672 }
673 break;
674
676 resplen = 0;
677
679 {
680 scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
681 if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
682 {
683 // Failed status response
684 resplen = - 1;
685
686 // set default sense if not set by callback
687 if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
688 }
689 }
690 break;
691
693 resplen = 0;
694
696 {
697 scsi_prevent_allow_medium_removal_t const * prevent_allow = (scsi_prevent_allow_medium_removal_t const *) scsi_cmd;
698 if ( !tud_msc_prevent_allow_medium_removal_cb(lun, prevent_allow->prohibit_removal, prevent_allow->control) )
699 {
700 // Failed status response
701 resplen = - 1;
702
703 // set default sense if not set by callback
704 if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
705 }
706 }
707 break;
708
709
711 {
712 uint32_t block_count;
713 uint32_t block_size;
714 uint16_t block_size_u16;
715
716 tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
717 block_size = (uint32_t) block_size_u16;
718
719 // Invalid block size/count from callback, possibly unit is not ready
720 // stall this request, set sense key to NOT READY
721 if (block_count == 0 || block_size == 0)
722 {
723 resplen = -1;
724
725 // set default sense if not set by callback
726 if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
727 }else
728 {
729 scsi_read_capacity10_resp_t read_capa10;
730
731 read_capa10.last_lba = tu_htonl(block_count-1);
732 read_capa10.block_size = tu_htonl(block_size);
733
734 resplen = sizeof(read_capa10);
735 TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_capa10, (size_t) resplen));
736 }
737 }
738 break;
739
741 {
743 {
744 .list_length = 8,
745 .block_num = 0,
746 .descriptor_type = 2, // formatted media
747 .block_size_u16 = 0
748 };
749
750 uint32_t block_count;
751 uint16_t block_size;
752
753 tud_msc_capacity_cb(lun, &block_count, &block_size);
754
755 // Invalid block size/count from callback, possibly unit is not ready
756 // stall this request, set sense key to NOT READY
757 if (block_count == 0 || block_size == 0)
758 {
759 resplen = -1;
760
761 // set default sense if not set by callback
762 if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
763 }else
764 {
765 read_fmt_capa.block_num = tu_htonl(block_count);
766 read_fmt_capa.block_size_u16 = tu_htons(block_size);
767
768 resplen = sizeof(read_fmt_capa);
769 TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_fmt_capa, (size_t) resplen));
770 }
771 }
772 break;
773
774 case SCSI_CMD_INQUIRY:
775 {
776 scsi_inquiry_resp_t inquiry_rsp =
777 {
778 .is_removable = 1,
779 .version = 2,
780 .response_data_format = 2,
781 .additional_length = sizeof(scsi_inquiry_resp_t) - 5,
782 };
783
784 // vendor_id, product_id, product_rev is space padded string
785 memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
786 memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
787 memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
788
789 tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
790
791 resplen = sizeof(inquiry_rsp);
792 TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &inquiry_rsp, (size_t) resplen));
793 }
794 break;
795
797 {
798 scsi_mode_sense6_resp_t mode_resp =
799 {
800 .data_len = 3,
801 .medium_type = 0,
802 .write_protected = false,
803 .reserved = 0,
804 .block_descriptor_len = 0 // no block descriptor are included
805 };
806
807 bool writable = true;
809 {
810 writable = tud_msc_is_writable_cb(lun);
811 }
812
813 mode_resp.write_protected = !writable;
814
815 resplen = sizeof(mode_resp);
816 TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &mode_resp, (size_t) resplen));
817 }
818 break;
819
821 {
822 scsi_sense_fixed_resp_t sense_rsp =
823 {
824 .response_code = 0x70, // current, fixed format
825 .valid = 1
826 };
827
828 sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
829 sense_rsp.sense_key = (uint8_t) (p_msc->sense_key & 0x0F);
830 sense_rsp.add_sense_code = p_msc->add_sense_code;
831 sense_rsp.add_sense_qualifier = p_msc->add_sense_qualifier;
832
833 resplen = sizeof(sense_rsp);
834 TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &sense_rsp, (size_t) resplen));
835
836 // request sense callback could overwrite the sense data
838 {
839 resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t) bufsize);
840 }
841
842 // Clear sense data after copy
843 tud_msc_set_sense(lun, 0, 0, 0);
844 }
845 break;
846
847 default: resplen = -1; break;
848 }
849
850 return resplen;
851}
852
853static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
854{
855 msc_cbw_t const * p_cbw = &p_msc->cbw;
856
857 // block size already verified not zero
858 uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
859
860 // Adjust lba with transferred bytes
861 uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
862
863 // remaining bytes capped at class buffer
864 int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
865
866 // Application can consume smaller bytes
867 uint32_t const offset = p_msc->xferred_len % block_sz;
868 nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_buf, (uint32_t) nbytes);
869
870 if ( nbytes < 0 )
871 {
872 // negative means error -> endpoint is stalled & status in CSW set to failed
873 TU_LOG_DRV(" tud_msc_read10_cb() return -1\r\n");
874
875 // set sense
877
878 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
879 }
880 else if ( nbytes == 0 )
881 {
882 // zero means not ready -> simulate an transfer complete so that this driver callback will fired again
883 dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false);
884 }
885 else
886 {
887 TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, (uint16_t) nbytes), );
888 }
889}
890
891static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
892{
893 msc_cbw_t const * p_cbw = &p_msc->cbw;
894 bool writable = true;
895
897 {
898 writable = tud_msc_is_writable_cb(p_cbw->lun);
899 }
900
901 if ( !writable )
902 {
903 // Not writable, complete this SCSI op with error
904 // Sense = Write protected
905 tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
906 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
907 return;
908 }
909
910 // remaining bytes capped at class buffer
911 uint16_t nbytes = (uint16_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
912
913 // Write10 callback will be called later when usb transfer complete
914 TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
915}
916
917// process new data arrived from WRITE10
918static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes)
919{
920 msc_cbw_t const * p_cbw = &p_msc->cbw;
921
922 // block size already verified not zero
923 uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
924
925 // Adjust lba with transferred bytes
926 uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
927
928 // Invoke callback to consume new data
929 uint32_t const offset = p_msc->xferred_len % block_sz;
930 int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_buf, xferred_bytes);
931
932 if ( nbytes < 0 )
933 {
934 // negative means error -> failed this scsi op
935 TU_LOG_DRV(" tud_msc_write10_cb() return -1\r\n");
936
937 // update actual byte before failed
938 p_msc->xferred_len += xferred_bytes;
939
940 // Set sense
942
943 fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
944 }else
945 {
946 // Application consume less than what we got (including zero)
947 if ( (uint32_t) nbytes < xferred_bytes )
948 {
949 uint32_t const left_over = xferred_bytes - (uint32_t) nbytes;
950 if ( nbytes > 0 )
951 {
952 p_msc->xferred_len += (uint16_t) nbytes;
953 memmove(_mscd_buf, _mscd_buf+nbytes, left_over);
954 }
955
956 // simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
957 dcd_event_xfer_complete(rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false);
958 }
959 else
960 {
961 // Application consume all bytes in our buffer
962 p_msc->xferred_len += xferred_bytes;
963
964 if ( p_msc->xferred_len >= p_msc->total_len )
965 {
966 // Data Stage is complete
967 p_msc->stage = MSC_STAGE_STATUS;
968 }else
969 {
970 // prepare to receive more data from host
971 proc_write10_cmd(rhport, p_msc);
972 }
973 }
974 }
975}
976
977#endif
static TU_ATTR_ALWAYS_INLINE void dcd_event_xfer_complete(uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
Definition: dcd.h:222
uint8_t const * buffer
Definition: midi_device.h:100
uint32_t bufsize
Definition: midi_device.h:95
@ MSC_PROTOCOL_BOT
Bulk-Only Transport.
Definition: msc.h:61
struct TU_ATTR_PACKED scsi_inquiry_resp_t
SCSI Inquiry Response Data.
@ SCSI_SENSE_ILLEGAL_REQUEST
Indicates an illegal parameter in the command descriptor block or in the additional parameters.
Definition: msc.h:134
@ SCSI_SENSE_NOT_READY
Indicates the logical unit addressed cannot be accessed.
Definition: msc.h:131
@ SCSI_SENSE_DATA_PROTECT
Indicates that a command that reads or writes the medium was attempted on a block that is protected f...
Definition: msc.h:136
@ MSC_CBW_SIGNATURE
Constant value of 43425355h (little endian)
Definition: msc.h:51
@ MSC_CSW_SIGNATURE
Constant value of 53425355h (little endian)
Definition: msc.h:52
@ MSC_CSW_STATUS_FAILED
MSC_CSW_STATUS_FAILED.
Definition: msc.h:77
@ MSC_CSW_STATUS_PHASE_ERROR
MSC_CSW_STATUS_PHASE_ERROR.
Definition: msc.h:78
@ MSC_CSW_STATUS_PASSED
MSC_CSW_STATUS_PASSED.
Definition: msc.h:76
struct TU_ATTR_PACKED scsi_sense_fixed_resp_t
@ SCSI_CMD_TEST_UNIT_READY
The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/wri...
Definition: msc.h:113
@ SCSI_CMD_READ_FORMAT_CAPACITY
The command allows the Host to request a list of the possible format capacities for an installed writ...
Definition: msc.h:121
@ SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
Definition: msc.h:118
@ SCSI_CMD_REQUEST_SENSE
The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used t...
Definition: msc.h:120
@ SCSI_CMD_WRITE_10
The WRITE (10) command requests that the device server transfer the specified logical block(s) from t...
Definition: msc.h:123
@ SCSI_CMD_MODE_SELECT_6
provides a means for the application client to specify medium, logical unit, or peripheral device par...
Definition: msc.h:115
@ SCSI_CMD_READ_CAPACITY_10
The SCSI Read Capacity command is used to obtain data capacity information from a target device.
Definition: msc.h:119
@ SCSI_CMD_START_STOP_UNIT
Definition: msc.h:117
@ SCSI_CMD_READ_10
The READ (10) command requests that the device server read the specified logical block(s) and transfe...
Definition: msc.h:122
@ SCSI_CMD_MODE_SENSE_6
provides a means for a device server to report parameters to an application client....
Definition: msc.h:116
@ SCSI_CMD_INQUIRY
The SCSI Inquiry command is used to obtain basic information from a target device.
Definition: msc.h:114
@ MSC_REQ_RESET
This request is used to reset the mass storage device and its associated interface....
Definition: msc.h:68
@ MSC_REQ_GET_MAX_LUN
The Get Max LUN device request is used to determine the number of logical units supported by the devi...
Definition: msc.h:67
static uint16_t rdwr10_get_blocksize(msc_cbw_t const *cbw)
Definition: msc_device.c:150
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t *p_msc)
Definition: msc_device.c:853
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
Definition: msc_device.c:393
static uint16_t rdwr10_get_blockcount(msc_cbw_t const *cbw)
Definition: msc_device.c:144
static TU_ATTR_ALWAYS_INLINE bool is_data_in(uint8_t dir)
Definition: msc_device.c:89
@ MSC_STAGE_STATUS
Definition: msc_device.c:51
@ MSC_STAGE_STATUS_SENT
Definition: msc_device.c:52
@ MSC_STAGE_DATA
Definition: msc_device.c:50
@ MSC_STAGE_CMD
Definition: msc_device.c:49
@ MSC_STAGE_NEED_RESET
Definition: msc_device.c:53
static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t *buffer, uint32_t bufsize)
Definition: msc_device.c:654
void mscd_reset(uint8_t rhport)
Definition: msc_device.c:263
static bool prepare_cbw(uint8_t rhport, mscd_interface_t *p_msc)
Definition: msc_device.c:103
uint8_t rdwr10_validate_cmd(msc_cbw_t const *cbw)
Definition: msc_device.c:161
bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
Definition: msc_device.c:308
bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier)
Definition: msc_device.c:234
static void proc_bot_reset(mscd_interface_t *p_msc)
Definition: msc_device.c:294
TU_ATTR_UNUSED tu_static tu_lookup_entry_t const _msc_scsi_cmd_lookup[]
Definition: msc_device.c:208
bool mscd_deinit(void)
Definition: msc_device.c:258
uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
Definition: msc_device.c:269
static void set_sense_medium_not_present(uint8_t lun)
Definition: msc_device.c:245
void mscd_init(void)
Definition: msc_device.c:254
static uint32_t rdwr10_get_lba(uint8_t const command[])
Definition: msc_device.c:135
CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mscd_interface_t _mscd_itf
Definition: msc_device.c:77
static bool send_csw(uint8_t rhport, mscd_interface_t *p_msc)
Definition: msc_device.c:94
TU_ATTR_UNUSED tu_static tu_lookup_table_t const _msc_scsi_cmd_table
Definition: msc_device.c:223
static void proc_write10_cmd(uint8_t rhport, mscd_interface_t *p_msc)
Definition: msc_device.c:891
CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static uint8_t _mscd_buf[CFG_TUD_MSC_EP_BUFSIZE]
Definition: msc_device.c:78
static void fail_scsi_op(uint8_t rhport, mscd_interface_t *p_msc, uint8_t status)
Definition: msc_device.c:109
static void proc_write10_new_data(uint8_t rhport, mscd_interface_t *p_msc, uint32_t xferred_bytes)
Definition: msc_device.c:918
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize)
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize)
TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun)
TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun)
TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void *buffer, uint16_t bufsize)
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize)
TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun)
TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void)
bool tud_msc_test_unit_ready_cb(uint8_t lun)
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size)
TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16])
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
TU_ATTR_WEAK bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control)
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
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:85
uint8_t product_rev[4]
4 bytes of ASCII data defined by the vendor.
Definition: msc.h:213
bool write_protected
Definition: msc.h:270
uint8_t add_sense_len
Definition: msc.h:233
uint8_t start
Definition: msc.h:299
uint8_t status
indicates the success or failure of the command. Values from msc_csw_status_t
Definition: msc.h:101
uint8_t data_len
Definition: msc.h:266
uint8_t prohibit_removal
Definition: msc.h:281
uint8_t list_length
Definition: msc.h:326
uint8_t lun
The device Logical Unit Number (LUN) to which the command block is being sent. For devices that suppo...
Definition: msc.h:88
uint16_t wValue
Definition: audio.h:934
uint8_t dir
Bit 7 of this field define transfer direction - 0 : Data-Out from host to the device....
Definition: msc.h:87
uint8_t add_sense_qualifier
Definition: msc.h:236
uint32_t signature
Signature that helps identify this data packet as a CBW. The signature field shall contain the value ...
Definition: msc.h:84
uint8_t product_id[16]
16 bytes of ASCII data defined by the vendor.
Definition: msc.h:212
uint32_t block_num
must be 8*n, length in bytes of formattable capacity descriptor followed it.
Definition: msc.h:328
uint16_t block_size_u16
Definition: msc.h:332
uint8_t power_condition
Definition: msc.h:303
uint8_t vendor_id[8]
8 bytes of ASCII data identifying the vendor of the product.
Definition: msc.h:211
uint16_t wLength
Definition: audio.h:840
uint8_t sense_key
Definition: msc.h:226
struct TU_ATTR_PACKED::@15::TU_ATTR_PACKED bmRequestType_bit
uint8_t control
Definition: msc.h:154
uint32_t data_residue
For Data-Out the device shall report in the dCSWDataResidue the difference between the amount of data...
Definition: msc.h:100
uint16_t wIndex
Definition: audio.h:943
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:90
uint8_t bInterfaceClass
Class code (assigned by the USB-IF).
Definition: tusb_types.h:349
uint8_t response_code
70h - current errors, Fixed Format 71h - deferred errors, Fixed Format
Definition: msc.h:221
uint8_t add_sense_code
Definition: msc.h:235
uint8_t bInterfaceSubClass
Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bInterfaceCla...
Definition: tusb_types.h:350
uint8_t bRequest
Request type audio_cs_req_t.
Definition: audio.h:831
uint32_t total_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:86
uint8_t bInterfaceProtocol
Protocol code (assigned by the USB). These codes are qualified by the value of the bInterfaceClass ...
Definition: tusb_types.h:351
uint8_t is_removable
Definition: msc.h:179
uint8_t bInterfaceNumber
Number of this interface. Zero-based value identifying the index in the array of concurrent interface...
Definition: tusb_types.h:346
uint8_t load_eject
Definition: msc.h:300
uint8_t ep_out
Definition: msc_device.c:64
CFG_TUSB_MEM_ALIGN msc_csw_t csw
Definition: msc_device.c:60
uint8_t add_sense_code
Definition: msc_device.c:73
uint8_t add_sense_qualifier
Definition: msc_device.c:74
uint8_t sense_key
Definition: msc_device.c:72
uint32_t total_len
Definition: msc_device.c:68
CFG_TUSB_MEM_ALIGN msc_cbw_t cbw
Definition: msc_device.c:59
uint8_t itf_num
Definition: msc_device.c:62
uint32_t xferred_len
Definition: msc_device.c:69
SCSI Read Capacity 10 Response Data.
Definition: msc.h:357
uint32_t last_lba
The last Logical Block Address of the device.
Definition: msc.h:358
uint32_t block_size
Block size in bytes.
Definition: msc.h:359
Definition: tusb_debug.h:100
uint32_t key
Definition: tusb_debug.h:101
static TU_ATTR_ALWAYS_INLINE uint8_t tu_u16_low(uint16_t ui16)
Definition: tusb_common.h:146
static TU_ATTR_ALWAYS_INLINE uint32_t tu_min32(uint32_t x, uint32_t y)
Definition: tusb_common.h:156
static TU_ATTR_ALWAYS_INLINE uint32_t tu_unaligned_read32(const void *mem)
Definition: tusb_common.h:207
static TU_ATTR_ALWAYS_INLINE uint16_t tu_unaligned_read16(const void *mem)
Definition: tusb_common.h:219
static TU_ATTR_ALWAYS_INLINE int tu_memcpy_s(void *dest, size_t destsz, const void *src, size_t count)
Definition: tusb_common.h:114
static TU_ATTR_ALWAYS_INLINE bool tu_bit_test(uint32_t value, uint8_t pos)
Definition: tusb_common.h:151
static const char * tu_lookup_find(tu_lookup_table_t const *p_table, uint32_t key)
Definition: tusb_debug.h:110
@ TUSB_REQ_CLEAR_FEATURE
Definition: tusb_types.h:123
@ TUSB_CLASS_MSC
Definition: tusb_types.h:167
xfer_result_t
Definition: tusb_types.h:236
@ XFER_RESULT_SUCCESS
Definition: tusb_types.h:237
@ TUSB_REQ_RCPT_ENDPOINT
Definition: tusb_types.h:153
@ TUSB_XFER_BULK
Definition: tusb_types.h:61
@ CONTROL_STAGE_SETUP
Definition: tusb_types.h:268
@ TUSB_REQ_TYPE_STANDARD
Definition: tusb_types.h:144
@ TUSB_REQ_TYPE_CLASS
Definition: tusb_types.h:145
struct TU_ATTR_PACKED tusb_desc_interface_t
USB Interface Descriptor.
static TU_ATTR_ALWAYS_INLINE uint8_t const * tu_desc_next(void const *desc)
Definition: tusb_types.h:531
@ TUSB_REQ_FEATURE_EDPT_HALT
Definition: tusb_types.h:138
void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
Definition: usbd.c:1398
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
Definition: usbd.c:1309
void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
Definition: usbd.c:1385
bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const *p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t *ep_out, uint8_t *ep_in)
Definition: usbd.c:1238
bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr)
Definition: usbd.c:1411
bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const *request, void *buffer, uint16_t len)
Definition: usbd_control.c:111
bool tud_control_status(uint8_t rhport, tusb_control_request_t const *request)
Definition: usbd_control.c:81
static TU_ATTR_ALWAYS_INLINE bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr)
Definition: usbd_pvt.h:116
CFG_TUH_MEM_ALIGN tusb_control_request_t request
Definition: usbh.c:259
volatile uint8_t stage
Definition: usbh.c:265