Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

572 lines
19KB

  1. /* MSC Teensy36 USB Host Mass Storage library
  2. * Copyright (c) 2017-2019 Warren Watson.
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining
  5. * a copy of this software and associated documentation files (the
  6. * "Software"), to deal in the Software without restriction, including
  7. * without limitation the rights to use, copy, modify, merge, publish,
  8. * distribute, sublicense, and/or sell copies of the Software, and to
  9. * permit persons to whom the Software is furnished to do so, subject to
  10. * the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be
  13. * included in all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  19. * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  20. * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. * SOFTWARE.
  23. */
  24. //MassStorageDriver.cpp
  25. #include <Arduino.h>
  26. #include "USBHost_t36.h" // Read this header first for key info
  27. #define print USBHost::print_
  28. #define println USBHost::println_
  29. // Uncomment this to display function usage and sequencing.
  30. //#define DBGprint 1
  31. // Big Endian/Little Endian
  32. #define swap32(x) ((x >> 24) & 0xff) | \
  33. ((x << 8) & 0xff0000) | \
  34. ((x >> 8) & 0xff00) | \
  35. ((x << 24) & 0xff000000)
  36. void msController::init()
  37. {
  38. contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t));
  39. contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t));
  40. contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t));
  41. driver_ready_for_device(this);
  42. }
  43. bool msController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len)
  44. {
  45. println("msController claim this=", (uint32_t)this, HEX);
  46. // only claim at interface level
  47. if (type != 1) return false;
  48. if (len < 9+7+7) return false; // Interface descriptor + 2 endpoint decriptors
  49. print_hexbytes(descriptors, len);
  50. uint32_t numendpoint = descriptors[4];
  51. if (numendpoint < 1) return false;
  52. if (descriptors[5] != 8) return false; // bInterfaceClass, 8 = MASS Storage class
  53. if (descriptors[6] != 6) return false; // bInterfaceSubClass, 6 = SCSI transparent command set (SCSI Standards)
  54. if (descriptors[7] != 80) return false; // bInterfaceProtocol, 80 = BULK-ONLY TRANSPORT
  55. uint8_t desc_index = 9;
  56. uint8_t in_index = 0xff, out_index = 0xff;
  57. println("numendpoint=", numendpoint, HEX);
  58. while (numendpoint--) {
  59. if ((descriptors[desc_index] != 7) || (descriptors[desc_index+1] != 5)) return false; // not an end point
  60. if (descriptors[desc_index+3] == 2) { // Bulk end point
  61. if (descriptors[desc_index+2] & 0x80)
  62. in_index = desc_index;
  63. else
  64. out_index = desc_index;
  65. }
  66. desc_index += 7; // point to next one...
  67. }
  68. if ((in_index == 0xff) || (out_index == 0xff)) return false; // did not find end point
  69. endpointIn = descriptors[in_index+2]; // bulk-in descriptor 1 81h
  70. endpointOut = descriptors[out_index+2]; // bulk-out descriptor 2 02h
  71. println("endpointIn=", endpointIn, HEX);
  72. println("endpointOut=", endpointOut, HEX);
  73. uint32_t sizeIn = descriptors[in_index+4] | (descriptors[in_index+5] << 8);
  74. println("packet size in (msController) = ", sizeIn);
  75. uint32_t sizeOut = descriptors[out_index+4] | (descriptors[out_index+5] << 8);
  76. println("packet size out (msController) = ", sizeOut);
  77. packetSizeIn = sizeIn;
  78. packetSizeOut = sizeOut;
  79. uint32_t intervalIn = descriptors[in_index+6];
  80. uint32_t intervalOut = descriptors[out_index+6];
  81. println("polling intervalIn = ", intervalIn);
  82. println("polling intervalOut = ", intervalOut);
  83. datapipeIn = new_Pipe(dev, 2, endpointIn, 1, packetSizeIn, intervalIn);
  84. datapipeOut = new_Pipe(dev, 2, endpointOut, 0, packetSizeOut, intervalOut);
  85. datapipeIn->callback_function = callbackIn;
  86. datapipeOut->callback_function = callbackOut;
  87. idVendor = dev->idVendor;
  88. idProduct = dev->idProduct;
  89. hubNumber = dev->hub_address;
  90. deviceAddress = dev->address;
  91. hubPort = dev->hub_port; // Used for device ID with multiple drives.
  92. msOutCompleted = false;
  93. msInCompleted = false;
  94. msControlCompleted = false;
  95. deviceAvailable = true;
  96. msDriveInfo.initialized = false;
  97. msDriveInfo.connected = true;
  98. #ifdef DBGprint
  99. Serial.printf(" connected %d\n",msDriveInfo.connected);
  100. Serial.printf(" initialized %d\n",msDriveInfo.initialized);
  101. #endif
  102. return true;
  103. }
  104. void msController::disconnect()
  105. {
  106. deviceAvailable = false;
  107. println("Device Disconnected...");
  108. msDriveInfo.connected = false;
  109. msDriveInfo.initialized = false;
  110. memset(&msDriveInfo, 0, sizeof(msDriveInfo_t));
  111. #ifdef DBGprint
  112. Serial.printf(" connected %d\n",msDriveInfo.connected);
  113. Serial.printf(" initialized %d\n",msDriveInfo.initialized);
  114. #endif
  115. }
  116. void msController::control(const Transfer_t *transfer)
  117. {
  118. println("control CallbackIn (msController)");
  119. print_hexbytes(report, 8);
  120. msControlCompleted = true;
  121. }
  122. void msController::callbackIn(const Transfer_t *transfer)
  123. {
  124. println("msController CallbackIn (static)");
  125. if (transfer->driver) {
  126. print("transfer->qtd.token = ");
  127. println(transfer->qtd.token & 255);
  128. ((msController *)(transfer->driver))->new_dataIn(transfer);
  129. }
  130. }
  131. void msController::callbackOut(const Transfer_t *transfer)
  132. {
  133. println("msController CallbackOut (static)");
  134. if (transfer->driver) {
  135. print("transfer->qtd.token = ");
  136. println(transfer->qtd.token & 255);
  137. ((msController *)(transfer->driver))->new_dataOut(transfer);
  138. }
  139. }
  140. void msController::new_dataOut(const Transfer_t *transfer)
  141. {
  142. uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);
  143. println("msController dataOut (static)", len, DEC);
  144. print_hexbytes((uint8_t*)transfer->buffer, (len < 32)? len : 32 );
  145. msOutCompleted = true; // Last out transaction is completed.
  146. }
  147. void msController::new_dataIn(const Transfer_t *transfer)
  148. {
  149. uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);
  150. println("msController dataIn (static): ", len, DEC);
  151. print_hexbytes((uint8_t*)transfer->buffer, (len < 32)? len : 32 );
  152. msInCompleted = true; // Last in transaction is completed.
  153. }
  154. // Initialize Mass Storage Device
  155. uint8_t msController::mscInit(void) {
  156. #ifdef DBGprint
  157. Serial.printf("mscIint()\n");
  158. #endif
  159. uint8_t msResult = MS_CBW_PASS;
  160. CBWTag = 0;
  161. uint32_t start = millis();
  162. // Check if device is connected.
  163. do {
  164. if((millis() - start) >= MSC_CONNECT_TIMEOUT) {
  165. return MS_NO_MEDIA_ERR; // Not connected Error.
  166. }
  167. yield();
  168. } while(!available());
  169. msReset(0); // Assume bNumInterfaces = 1 for now.
  170. delay(500);
  171. maxLUN = msGetMaxLun(0); // Assume bNumInterfaces = 1 for now
  172. // msResult = msReportLUNs(&maxLUN);
  173. //Serial.printf("maxLUN = %d\n",maxLUN);
  174. // delay(150);
  175. //-------------------------------------------------------
  176. // msResult = msStartStopUnit(1);
  177. msResult = WaitMediaReady();
  178. if(msResult)
  179. return msResult;
  180. // Retrieve drive information.
  181. msDriveInfo.initialized = true;
  182. msDriveInfo.hubNumber = getHubNumber(); // Which HUB.
  183. msDriveInfo.hubPort = getHubPort(); // Which HUB port.
  184. msDriveInfo.deviceAddress = getDeviceAddress(); // Device addreess.
  185. msDriveInfo.idVendor = getIDVendor(); // USB Vendor ID.
  186. msDriveInfo.idProduct = getIDProduct(); // USB Product ID.
  187. msResult = msDeviceInquiry(&msInquiry); // Config Info.
  188. if(msResult)
  189. return msResult;
  190. msResult = msReadDeviceCapacity(&msCapacity); // Size Info.
  191. if(msResult)
  192. return msResult;
  193. memcpy(&msDriveInfo.inquiry, &msInquiry, sizeof(msInquiryResponse_t));
  194. memcpy(&msDriveInfo.capacity, &msCapacity, sizeof(msSCSICapacity_t));
  195. return msResult;
  196. }
  197. //---------------------------------------------------------------------------
  198. // Perform Mass Storage Reset
  199. void msController::msReset(uint32_t interfaceNumber) {
  200. #ifdef DBGprint
  201. Serial.printf("msReset()\n");
  202. #endif
  203. mk_setup(setup, 0x21, 0xff, 0, interfaceNumber, 0);
  204. queue_Control_Transfer(device, &setup, NULL, this);
  205. while (!msControlCompleted) yield();
  206. msControlCompleted = false;
  207. }
  208. //---------------------------------------------------------------------------
  209. // Get MAX LUN
  210. uint8_t msController::msGetMaxLun(uint32_t interfaceNumber) {
  211. #ifdef DBGprint
  212. Serial.printf("msGetMaxLun()\n");
  213. #endif
  214. report[0] = 0;
  215. mk_setup(setup, 0xa1, 0xfe, 0, interfaceNumber, 1);
  216. queue_Control_Transfer(device, &setup, report, this);
  217. while (!msControlCompleted) yield();
  218. msControlCompleted = false;
  219. maxLUN = report[0];
  220. return maxLUN;
  221. }
  222. uint8_t msController::WaitMediaReady() {
  223. uint8_t msResult;
  224. uint32_t start = millis();
  225. #ifdef DBGprint
  226. Serial.printf("WaitMediaReady()\n");
  227. #endif
  228. do {
  229. if((millis() - start) >= MEDIA_READY_TIMEOUT) {
  230. return MS_UNIT_NOT_READY; // Not Ready Error.
  231. }
  232. msResult = msTestReady();
  233. yield();
  234. } while(msResult == 1);
  235. return msResult;
  236. }
  237. // Check if drive is connected and Initialized.
  238. uint8_t msController::checkConnectedInitialized(void) {
  239. uint8_t msResult = MS_CBW_PASS;
  240. #ifdef DBGprint
  241. Serial.printf("checkConnectedInitialized()\n");
  242. #endif
  243. if(!msDriveInfo.connected) {
  244. return MS_NO_MEDIA_ERR;
  245. }
  246. if(!msDriveInfo.initialized) {
  247. msResult = mscInit();
  248. if(msResult != MS_CBW_PASS) return MS_UNIT_NOT_READY; // Not Initialized
  249. }
  250. return MS_CBW_PASS;
  251. }
  252. //---------------------------------------------------------------------------
  253. // Send SCSI Command
  254. // Do a complete 3 stage transfer.
  255. uint8_t msController::msDoCommand(msCommandBlockWrapper_t *CBW, void *buffer)
  256. {
  257. uint8_t CSWResult = 0;
  258. mscTransferComplete = false;
  259. #ifdef DBGprint
  260. Serial.printf("msDoCommand():\n");
  261. #endif
  262. if(CBWTag == 0xFFFFFFFF) CBWTag = 1;
  263. queue_Data_Transfer(datapipeOut, CBW, sizeof(msCommandBlockWrapper_t), this); // Command stage.
  264. while(!msOutCompleted) yield();
  265. msOutCompleted = false;
  266. if((CBW->Flags == CMD_DIR_DATA_IN)) { // Data stage from device.
  267. queue_Data_Transfer(datapipeIn, buffer, CBW->TransferLength, this);
  268. while(!msInCompleted) yield();
  269. msInCompleted = false;
  270. } else { // Data stage to device.
  271. queue_Data_Transfer(datapipeOut, buffer, CBW->TransferLength, this);
  272. while(!msOutCompleted) yield();
  273. msOutCompleted = false;
  274. }
  275. CSWResult = msGetCSW(); // Status stage.
  276. // All stages of this transfer have completed.
  277. //Check for special cases.
  278. //If test for unit ready command is given then
  279. // return the CSW status byte.
  280. //Bit 0 == 1 == not ready else
  281. //Bit 0 == 0 == ready.
  282. //And the Start/Stop Unit command as well.
  283. if((CBW->CommandData[0] == CMD_TEST_UNIT_READY) ||
  284. (CBW->CommandData[0] == CMD_START_STOP_UNIT))
  285. return CSWResult;
  286. else // Process possible SCSI errors.
  287. return msProcessError(CSWResult);
  288. }
  289. //---------------------------------------------------------------------------
  290. // Get Command Status Wrapper
  291. uint8_t msController::msGetCSW(void) {
  292. #ifdef DBGprint
  293. Serial.printf("msGetCSW()\n");
  294. #endif
  295. msCommandStatusWrapper_t StatusBlockWrapper = (msCommandStatusWrapper_t)
  296. {
  297. .Signature = CSW_SIGNATURE,
  298. .Tag = 0,
  299. .DataResidue = 0, // TODO: Proccess this if received.
  300. .Status = 0
  301. };
  302. queue_Data_Transfer(datapipeIn, &StatusBlockWrapper, sizeof(StatusBlockWrapper), this);
  303. while(!msInCompleted) yield();
  304. msInCompleted = false;
  305. mscTransferComplete = true;
  306. if(StatusBlockWrapper.Signature != CSW_SIGNATURE) return msProcessError(MS_CSW_SIG_ERROR); // Signature error
  307. if(StatusBlockWrapper.Tag != CBWTag) return msProcessError(MS_CSW_TAG_ERROR); // Tag mismatch error
  308. return StatusBlockWrapper.Status;
  309. }
  310. //---------------------------------------------------------------------------
  311. // Test Unit Ready
  312. uint8_t msController::msTestReady() {
  313. #ifdef DBGprint
  314. Serial.printf("msTestReady()\n");
  315. #endif
  316. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  317. {
  318. .Signature = CBW_SIGNATURE,
  319. .Tag = ++CBWTag,
  320. .TransferLength = 0,
  321. .Flags = CMD_DIR_DATA_IN,
  322. .LUN = currentLUN,
  323. .CommandLength = 6,
  324. .CommandData = {CMD_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00}
  325. };
  326. queue_Data_Transfer(datapipeOut, &CommandBlockWrapper, sizeof(CommandBlockWrapper), this);
  327. while(!msOutCompleted) yield();
  328. msOutCompleted = false;
  329. return msGetCSW();
  330. }
  331. //---------------------------------------------------------------------------
  332. // Start/Stop unit
  333. uint8_t msController::msStartStopUnit(uint8_t mode) {
  334. #ifdef DBGprint
  335. Serial.printf("msStartStopUnit()\n");
  336. #endif
  337. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  338. {
  339. .Signature = CBW_SIGNATURE,
  340. .Tag = ++CBWTag,
  341. .TransferLength = 0,
  342. .Flags = CMD_DIR_DATA_IN,
  343. .LUN = currentLUN,
  344. .CommandLength = 6,
  345. .CommandData = {CMD_START_STOP_UNIT, 0x01, 0x00, 0x00, mode, 0x00}
  346. };
  347. queue_Data_Transfer(datapipeOut, &CommandBlockWrapper, sizeof(CommandBlockWrapper), this);
  348. while(!msOutCompleted) yield();
  349. msOutCompleted = false;
  350. return msGetCSW();
  351. }
  352. //---------------------------------------------------------------------------
  353. // Read Mass Storage Device Capacity (Number of Blocks and Block Size)
  354. uint8_t msController::msReadDeviceCapacity(msSCSICapacity_t * const Capacity) {
  355. #ifdef DBGprint
  356. Serial.printf("msReadDeviceCapacity()\n");
  357. #endif
  358. uint8_t result = 0;
  359. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  360. {
  361. .Signature = CBW_SIGNATURE,
  362. .Tag = ++CBWTag,
  363. .TransferLength = sizeof(msSCSICapacity_t),
  364. .Flags = CMD_DIR_DATA_IN,
  365. .LUN = currentLUN,
  366. .CommandLength = 10,
  367. .CommandData = {CMD_RD_CAPACITY_10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
  368. };
  369. result = msDoCommand(&CommandBlockWrapper, Capacity);
  370. Capacity->Blocks = swap32(Capacity->Blocks);
  371. Capacity->BlockSize = swap32(Capacity->BlockSize);
  372. return result;
  373. }
  374. //---------------------------------------------------------------------------
  375. // Do Mass Storage Device Inquiry
  376. uint8_t msController::msDeviceInquiry(msInquiryResponse_t * const Inquiry)
  377. {
  378. #ifdef DBGprint
  379. Serial.printf("msDeviceInquiry()\n");
  380. #endif
  381. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  382. {
  383. .Signature = CBW_SIGNATURE,
  384. .Tag = ++CBWTag,
  385. .TransferLength = sizeof(msInquiryResponse_t),
  386. .Flags = CMD_DIR_DATA_IN,
  387. .LUN = currentLUN,
  388. .CommandLength = 6,
  389. .CommandData = {CMD_INQUIRY,0x00,0x00,0x00,sizeof(msInquiryResponse_t),0x00}
  390. };
  391. return msDoCommand(&CommandBlockWrapper, Inquiry);
  392. }
  393. //---------------------------------------------------------------------------
  394. // Request Sense Data
  395. uint8_t msController::msRequestSense(msRequestSenseResponse_t * const Sense)
  396. {
  397. #ifdef DBGprint
  398. Serial.printf("msRequestSense()\n");
  399. #endif
  400. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  401. {
  402. .Signature = CBW_SIGNATURE,
  403. .Tag = ++CBWTag,
  404. .TransferLength = sizeof(msRequestSenseResponse_t),
  405. .Flags = CMD_DIR_DATA_IN,
  406. .LUN = currentLUN,
  407. .CommandLength = 6,
  408. .CommandData = {CMD_REQUEST_SENSE, 0x00, 0x00, 0x00, sizeof(msRequestSenseResponse_t), 0x00}
  409. };
  410. return msDoCommand(&CommandBlockWrapper, Sense);
  411. }
  412. //---------------------------------------------------------------------------
  413. // Report LUNs
  414. uint8_t msController::msReportLUNs(uint8_t *Buffer)
  415. {
  416. #ifdef DBGprint
  417. Serial.printf("msReportLuns()\n");
  418. #endif
  419. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  420. {
  421. .Signature = CBW_SIGNATURE,
  422. .Tag = ++CBWTag,
  423. .TransferLength = MAXLUNS,
  424. .Flags = CMD_DIR_DATA_IN,
  425. .LUN = currentLUN,
  426. .CommandLength = 12,
  427. .CommandData = {CMD_REPORT_LUNS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, MAXLUNS, 0x00, 0x00}
  428. };
  429. return msDoCommand(&CommandBlockWrapper, Buffer);
  430. }
  431. //---------------------------------------------------------------------------
  432. // Read Sectors (Multi Sector Capable)
  433. uint8_t msController::msReadBlocks(
  434. const uint32_t BlockAddress,
  435. const uint16_t Blocks,
  436. const uint16_t BlockSize,
  437. void * sectorBuffer)
  438. {
  439. #ifdef DBGprint
  440. Serial.printf("msReadBlocks()\n");
  441. #endif
  442. uint8_t BlockHi = (Blocks >> 8) & 0xFF;
  443. uint8_t BlockLo = Blocks & 0xFF;
  444. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  445. {
  446. .Signature = CBW_SIGNATURE,
  447. .Tag = ++CBWTag,
  448. .TransferLength = (uint32_t)(Blocks * BlockSize),
  449. .Flags = CMD_DIR_DATA_IN,
  450. .LUN = currentLUN,
  451. .CommandLength = 10,
  452. .CommandData = {CMD_RD_10, 0x00,
  453. (uint8_t)(BlockAddress >> 24),
  454. (uint8_t)(BlockAddress >> 16),
  455. (uint8_t)(BlockAddress >> 8),
  456. (uint8_t)(BlockAddress & 0xFF),
  457. 0x00, BlockHi, BlockLo, 0x00}
  458. };
  459. return msDoCommand(&CommandBlockWrapper, sectorBuffer);
  460. }
  461. //---------------------------------------------------------------------------
  462. // Write Sectors (Multi Sector Capable)
  463. uint8_t msController::msWriteBlocks(
  464. const uint32_t BlockAddress,
  465. const uint16_t Blocks,
  466. const uint16_t BlockSize,
  467. const void * sectorBuffer)
  468. {
  469. #ifdef DBGprint
  470. Serial.printf("msWriteBlocks()\n");
  471. #endif
  472. uint8_t BlockHi = (Blocks >> 8) & 0xFF;
  473. uint8_t BlockLo = Blocks & 0xFF;
  474. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  475. {
  476. .Signature = CBW_SIGNATURE,
  477. .Tag = ++CBWTag,
  478. .TransferLength = (uint32_t)(Blocks * BlockSize),
  479. .Flags = CMD_DIR_DATA_OUT,
  480. .LUN = currentLUN,
  481. .CommandLength = 10,
  482. .CommandData = {CMD_WR_10, 0x00,
  483. (uint8_t)(BlockAddress >> 24),
  484. (uint8_t)(BlockAddress >> 16),
  485. (uint8_t)(BlockAddress >> 8),
  486. (uint8_t)(BlockAddress & 0xFF),
  487. 0x00, BlockHi, BlockLo, 0x00}
  488. };
  489. return msDoCommand(&CommandBlockWrapper, (void *)sectorBuffer);
  490. }
  491. // Proccess Possible SCSI errors
  492. uint8_t msController::msProcessError(uint8_t msStatus) {
  493. #ifdef DBGprint
  494. Serial.printf("msProcessError()\n");
  495. #endif
  496. uint8_t msResult = 0;
  497. switch(msStatus) {
  498. case MS_CBW_PASS:
  499. return MS_CBW_PASS;
  500. break;
  501. case MS_CBW_PHASE_ERROR:
  502. Serial.printf("SCSI Phase Error: %d\n",msStatus);
  503. return MS_SCSI_ERROR;
  504. break;
  505. case MS_CSW_TAG_ERROR:
  506. Serial.printf("CSW Tag Error: %d\n",MS_CSW_TAG_ERROR);
  507. return MS_CSW_TAG_ERROR;
  508. break;
  509. case MS_CSW_SIG_ERROR:
  510. Serial.printf("CSW Signature Error: %d\n",MS_CSW_SIG_ERROR);
  511. return MS_CSW_SIG_ERROR;
  512. break;
  513. case MS_CBW_FAIL:
  514. if(msResult = msRequestSense(&msSense))
  515. Serial.printf("Failed to get sense codes. Returned code: %d\n",msResult);
  516. return MS_CBW_FAIL;
  517. break;
  518. default:
  519. Serial.printf("SCSI Error: %d\n",msStatus);
  520. return msStatus;
  521. }
  522. }