You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

583 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 0
  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. print(" connected = ");
  100. println(msDriveInfo.connected);
  101. print(" initialized = ");
  102. println(msDriveInfo.initialized);
  103. #endif
  104. return true;
  105. }
  106. void msController::disconnect()
  107. {
  108. deviceAvailable = false;
  109. println("Device Disconnected...");
  110. msDriveInfo.connected = false;
  111. msDriveInfo.initialized = false;
  112. memset(&msDriveInfo, 0, sizeof(msDriveInfo_t));
  113. #ifdef DBGprint
  114. print(" connected ");
  115. println(msDriveInfo.connected);
  116. print(" initialized ");
  117. println(msDriveInfo.initialized);
  118. #endif
  119. }
  120. void msController::control(const Transfer_t *transfer)
  121. {
  122. println("control CallbackIn (msController)");
  123. print_hexbytes(report, 8);
  124. msControlCompleted = true;
  125. }
  126. void msController::callbackIn(const Transfer_t *transfer)
  127. {
  128. println("msController CallbackIn (static)");
  129. if (transfer->driver) {
  130. print("transfer->qtd.token = ");
  131. println(transfer->qtd.token & 255);
  132. ((msController *)(transfer->driver))->new_dataIn(transfer);
  133. }
  134. }
  135. void msController::callbackOut(const Transfer_t *transfer)
  136. {
  137. println("msController CallbackOut (static)");
  138. if (transfer->driver) {
  139. print("transfer->qtd.token = ");
  140. println(transfer->qtd.token & 255);
  141. ((msController *)(transfer->driver))->new_dataOut(transfer);
  142. }
  143. }
  144. void msController::new_dataOut(const Transfer_t *transfer)
  145. {
  146. uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);
  147. println("msController dataOut (static)", len, DEC);
  148. print_hexbytes((uint8_t*)transfer->buffer, (len < 32)? len : 32 );
  149. msOutCompleted = true; // Last out transaction is completed.
  150. }
  151. void msController::new_dataIn(const Transfer_t *transfer)
  152. {
  153. uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);
  154. println("msController dataIn (static): ", len, DEC);
  155. print_hexbytes((uint8_t*)transfer->buffer, (len < 32)? len : 32 );
  156. msInCompleted = true; // Last in transaction is completed.
  157. }
  158. // Initialize Mass Storage Device
  159. uint8_t msController::mscInit(void) {
  160. #ifdef DBGprint
  161. println("mscIint()");
  162. #endif
  163. uint8_t msResult = MS_CBW_PASS;
  164. CBWTag = 0;
  165. uint32_t start = millis();
  166. // Check if device is connected.
  167. do {
  168. if((millis() - start) >= MSC_CONNECT_TIMEOUT) {
  169. return MS_NO_MEDIA_ERR; // Not connected Error.
  170. }
  171. yield();
  172. } while(!available());
  173. msReset(0); // Assume bNumInterfaces = 1 for now.
  174. // delay(500); // Not needed any more.
  175. maxLUN = msGetMaxLun(0); // Assume bNumInterfaces = 1 for now
  176. // msResult = msReportLUNs(&maxLUN);
  177. //println("maxLUN = ");
  178. //println(maxLUN);
  179. // delay(150);
  180. //-------------------------------------------------------
  181. msResult = msStartStopUnit(1);
  182. msResult = WaitMediaReady();
  183. if(msResult)
  184. return msResult;
  185. // Retrieve drive information.
  186. msDriveInfo.initialized = true;
  187. msDriveInfo.hubNumber = getHubNumber(); // Which HUB.
  188. msDriveInfo.hubPort = getHubPort(); // Which HUB port.
  189. msDriveInfo.deviceAddress = getDeviceAddress(); // Device addreess.
  190. msDriveInfo.idVendor = getIDVendor(); // USB Vendor ID.
  191. msDriveInfo.idProduct = getIDProduct(); // USB Product ID.
  192. msResult = msDeviceInquiry(&msInquiry); // Config Info.
  193. if(msResult)
  194. return msResult;
  195. msResult = msReadDeviceCapacity(&msCapacity); // Size Info.
  196. if(msResult)
  197. return msResult;
  198. memcpy(&msDriveInfo.inquiry, &msInquiry, sizeof(msInquiryResponse_t));
  199. memcpy(&msDriveInfo.capacity, &msCapacity, sizeof(msSCSICapacity_t));
  200. return msResult;
  201. }
  202. //---------------------------------------------------------------------------
  203. // Perform Mass Storage Reset
  204. void msController::msReset(uint32_t interfaceNumber) {
  205. #ifdef DBGprint
  206. println("msReset()");
  207. #endif
  208. mk_setup(setup, 0x21, 0xff, 0, interfaceNumber, 0);
  209. queue_Control_Transfer(device, &setup, NULL, this);
  210. while (!msControlCompleted) yield();
  211. msControlCompleted = false;
  212. }
  213. //---------------------------------------------------------------------------
  214. // Get MAX LUN
  215. uint8_t msController::msGetMaxLun(uint32_t interfaceNumber) {
  216. #ifdef DBGprint
  217. println("msGetMaxLun()");
  218. #endif
  219. report[0] = 0;
  220. mk_setup(setup, 0xa1, 0xfe, 0, interfaceNumber, 1);
  221. queue_Control_Transfer(device, &setup, report, this);
  222. while (!msControlCompleted) yield();
  223. msControlCompleted = false;
  224. maxLUN = report[0];
  225. return maxLUN;
  226. }
  227. uint8_t msController::WaitMediaReady() {
  228. uint8_t msResult;
  229. uint32_t start = millis();
  230. #ifdef DBGprint
  231. println("WaitMediaReady()");
  232. #endif
  233. do {
  234. if((millis() - start) >= MEDIA_READY_TIMEOUT) {
  235. return MS_UNIT_NOT_READY; // Not Ready Error.
  236. }
  237. msResult = msTestReady();
  238. yield();
  239. } while(msResult == 1);
  240. return msResult;
  241. }
  242. // Check if drive is connected and Initialized.
  243. uint8_t msController::checkConnectedInitialized(void) {
  244. uint8_t msResult = MS_CBW_PASS;
  245. #ifdef DBGprint
  246. print("checkConnectedInitialized()");
  247. #endif
  248. if(!msDriveInfo.connected) {
  249. return MS_NO_MEDIA_ERR;
  250. }
  251. if(!msDriveInfo.initialized) {
  252. msResult = mscInit();
  253. if(msResult != MS_CBW_PASS) return MS_UNIT_NOT_READY; // Not Initialized
  254. }
  255. return MS_CBW_PASS;
  256. }
  257. //---------------------------------------------------------------------------
  258. // Send SCSI Command
  259. // Do a complete 3 stage transfer.
  260. uint8_t msController::msDoCommand(msCommandBlockWrapper_t *CBW, void *buffer)
  261. {
  262. uint8_t CSWResult = 0;
  263. mscTransferComplete = false;
  264. #ifdef DBGprint
  265. println("msDoCommand()");
  266. #endif
  267. if(CBWTag == 0xFFFFFFFF) CBWTag = 1;
  268. queue_Data_Transfer(datapipeOut, CBW, sizeof(msCommandBlockWrapper_t), this); // Command stage.
  269. while(!msOutCompleted) yield();
  270. msOutCompleted = false;
  271. if((CBW->Flags == CMD_DIR_DATA_IN)) { // Data stage from device.
  272. queue_Data_Transfer(datapipeIn, buffer, CBW->TransferLength, this);
  273. while(!msInCompleted) yield();
  274. msInCompleted = false;
  275. } else { // Data stage to device.
  276. queue_Data_Transfer(datapipeOut, buffer, CBW->TransferLength, this);
  277. while(!msOutCompleted) yield();
  278. msOutCompleted = false;
  279. }
  280. CSWResult = msGetCSW(); // Status stage.
  281. // All stages of this transfer have completed.
  282. //Check for special cases.
  283. //If test for unit ready command is given then
  284. // return the CSW status byte.
  285. //Bit 0 == 1 == not ready else
  286. //Bit 0 == 0 == ready.
  287. //And the Start/Stop Unit command as well.
  288. if((CBW->CommandData[0] == CMD_TEST_UNIT_READY) ||
  289. (CBW->CommandData[0] == CMD_START_STOP_UNIT))
  290. return CSWResult;
  291. else // Process possible SCSI errors.
  292. return msProcessError(CSWResult);
  293. }
  294. //---------------------------------------------------------------------------
  295. // Get Command Status Wrapper
  296. uint8_t msController::msGetCSW(void) {
  297. #ifdef DBGprint
  298. println("msGetCSW()");
  299. #endif
  300. msCommandStatusWrapper_t StatusBlockWrapper = (msCommandStatusWrapper_t)
  301. {
  302. .Signature = CSW_SIGNATURE,
  303. .Tag = 0,
  304. .DataResidue = 0, // TODO: Proccess this if received.
  305. .Status = 0
  306. };
  307. queue_Data_Transfer(datapipeIn, &StatusBlockWrapper, sizeof(StatusBlockWrapper), this);
  308. while(!msInCompleted) yield();
  309. msInCompleted = false;
  310. mscTransferComplete = true;
  311. if(StatusBlockWrapper.Signature != CSW_SIGNATURE) return msProcessError(MS_CSW_SIG_ERROR); // Signature error
  312. if(StatusBlockWrapper.Tag != CBWTag) return msProcessError(MS_CSW_TAG_ERROR); // Tag mismatch error
  313. return StatusBlockWrapper.Status;
  314. }
  315. //---------------------------------------------------------------------------
  316. // Test Unit Ready
  317. uint8_t msController::msTestReady() {
  318. #ifdef DBGprint
  319. println("msTestReady()");
  320. #endif
  321. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  322. {
  323. .Signature = CBW_SIGNATURE,
  324. .Tag = ++CBWTag,
  325. .TransferLength = 0,
  326. .Flags = CMD_DIR_DATA_IN,
  327. .LUN = currentLUN,
  328. .CommandLength = 6,
  329. .CommandData = {CMD_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00}
  330. };
  331. queue_Data_Transfer(datapipeOut, &CommandBlockWrapper, sizeof(CommandBlockWrapper), this);
  332. while(!msOutCompleted) yield();
  333. msOutCompleted = false;
  334. return msGetCSW();
  335. }
  336. //---------------------------------------------------------------------------
  337. // Start/Stop unit
  338. uint8_t msController::msStartStopUnit(uint8_t mode) {
  339. #ifdef DBGprint
  340. println("msStartStopUnit()");
  341. #endif
  342. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  343. {
  344. .Signature = CBW_SIGNATURE,
  345. .Tag = ++CBWTag,
  346. .TransferLength = 0,
  347. .Flags = CMD_DIR_DATA_IN,
  348. .LUN = currentLUN,
  349. .CommandLength = 6,
  350. .CommandData = {CMD_START_STOP_UNIT, 0x01, 0x00, 0x00, mode, 0x00}
  351. };
  352. queue_Data_Transfer(datapipeOut, &CommandBlockWrapper, sizeof(CommandBlockWrapper), this);
  353. while(!msOutCompleted) yield();
  354. msOutCompleted = false;
  355. return msGetCSW();
  356. }
  357. //---------------------------------------------------------------------------
  358. // Read Mass Storage Device Capacity (Number of Blocks and Block Size)
  359. uint8_t msController::msReadDeviceCapacity(msSCSICapacity_t * const Capacity) {
  360. #ifdef DBGprint
  361. println("msReadDeviceCapacity()");
  362. #endif
  363. uint8_t result = 0;
  364. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  365. {
  366. .Signature = CBW_SIGNATURE,
  367. .Tag = ++CBWTag,
  368. .TransferLength = sizeof(msSCSICapacity_t),
  369. .Flags = CMD_DIR_DATA_IN,
  370. .LUN = currentLUN,
  371. .CommandLength = 10,
  372. .CommandData = {CMD_RD_CAPACITY_10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
  373. };
  374. result = msDoCommand(&CommandBlockWrapper, Capacity);
  375. Capacity->Blocks = swap32(Capacity->Blocks);
  376. Capacity->BlockSize = swap32(Capacity->BlockSize);
  377. return result;
  378. }
  379. //---------------------------------------------------------------------------
  380. // Do Mass Storage Device Inquiry
  381. uint8_t msController::msDeviceInquiry(msInquiryResponse_t * const Inquiry)
  382. {
  383. #ifdef DBGprint
  384. println("msDeviceInquiry()");
  385. #endif
  386. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  387. {
  388. .Signature = CBW_SIGNATURE,
  389. .Tag = ++CBWTag,
  390. .TransferLength = sizeof(msInquiryResponse_t),
  391. .Flags = CMD_DIR_DATA_IN,
  392. .LUN = currentLUN,
  393. .CommandLength = 6,
  394. .CommandData = {CMD_INQUIRY,0x00,0x00,0x00,sizeof(msInquiryResponse_t),0x00}
  395. };
  396. return msDoCommand(&CommandBlockWrapper, Inquiry);
  397. }
  398. //---------------------------------------------------------------------------
  399. // Request Sense Data
  400. uint8_t msController::msRequestSense(msRequestSenseResponse_t * const Sense)
  401. {
  402. #ifdef DBGprint
  403. println("msRequestSense()");
  404. #endif
  405. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  406. {
  407. .Signature = CBW_SIGNATURE,
  408. .Tag = ++CBWTag,
  409. .TransferLength = sizeof(msRequestSenseResponse_t),
  410. .Flags = CMD_DIR_DATA_IN,
  411. .LUN = currentLUN,
  412. .CommandLength = 6,
  413. .CommandData = {CMD_REQUEST_SENSE, 0x00, 0x00, 0x00, sizeof(msRequestSenseResponse_t), 0x00}
  414. };
  415. return msDoCommand(&CommandBlockWrapper, Sense);
  416. }
  417. //---------------------------------------------------------------------------
  418. // Report LUNs
  419. uint8_t msController::msReportLUNs(uint8_t *Buffer)
  420. {
  421. #ifdef DBGprint
  422. println("msReportLuns()");
  423. #endif
  424. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  425. {
  426. .Signature = CBW_SIGNATURE,
  427. .Tag = ++CBWTag,
  428. .TransferLength = MAXLUNS,
  429. .Flags = CMD_DIR_DATA_IN,
  430. .LUN = currentLUN,
  431. .CommandLength = 12,
  432. .CommandData = {CMD_REPORT_LUNS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, MAXLUNS, 0x00, 0x00}
  433. };
  434. return msDoCommand(&CommandBlockWrapper, Buffer);
  435. }
  436. //---------------------------------------------------------------------------
  437. // Read Sectors (Multi Sector Capable)
  438. uint8_t msController::msReadBlocks(
  439. const uint32_t BlockAddress,
  440. const uint16_t Blocks,
  441. const uint16_t BlockSize,
  442. void * sectorBuffer)
  443. {
  444. #ifdef DBGprint
  445. println("msReadBlocks()");
  446. #endif
  447. uint8_t BlockHi = (Blocks >> 8) & 0xFF;
  448. uint8_t BlockLo = Blocks & 0xFF;
  449. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  450. {
  451. .Signature = CBW_SIGNATURE,
  452. .Tag = ++CBWTag,
  453. .TransferLength = (uint32_t)(Blocks * BlockSize),
  454. .Flags = CMD_DIR_DATA_IN,
  455. .LUN = currentLUN,
  456. .CommandLength = 10,
  457. .CommandData = {CMD_RD_10, 0x00,
  458. (uint8_t)(BlockAddress >> 24),
  459. (uint8_t)(BlockAddress >> 16),
  460. (uint8_t)(BlockAddress >> 8),
  461. (uint8_t)(BlockAddress & 0xFF),
  462. 0x00, BlockHi, BlockLo, 0x00}
  463. };
  464. return msDoCommand(&CommandBlockWrapper, sectorBuffer);
  465. }
  466. //---------------------------------------------------------------------------
  467. // Write Sectors (Multi Sector Capable)
  468. uint8_t msController::msWriteBlocks(
  469. const uint32_t BlockAddress,
  470. const uint16_t Blocks,
  471. const uint16_t BlockSize,
  472. const void * sectorBuffer)
  473. {
  474. #ifdef DBGprint
  475. println("msWriteBlocks()");
  476. #endif
  477. uint8_t BlockHi = (Blocks >> 8) & 0xFF;
  478. uint8_t BlockLo = Blocks & 0xFF;
  479. msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t)
  480. {
  481. .Signature = CBW_SIGNATURE,
  482. .Tag = ++CBWTag,
  483. .TransferLength = (uint32_t)(Blocks * BlockSize),
  484. .Flags = CMD_DIR_DATA_OUT,
  485. .LUN = currentLUN,
  486. .CommandLength = 10,
  487. .CommandData = {CMD_WR_10, 0x00,
  488. (uint8_t)(BlockAddress >> 24),
  489. (uint8_t)(BlockAddress >> 16),
  490. (uint8_t)(BlockAddress >> 8),
  491. (uint8_t)(BlockAddress & 0xFF),
  492. 0x00, BlockHi, BlockLo, 0x00}
  493. };
  494. return msDoCommand(&CommandBlockWrapper, (void *)sectorBuffer);
  495. }
  496. // Proccess Possible SCSI errors
  497. uint8_t msController::msProcessError(uint8_t msStatus) {
  498. #ifdef DBGprint
  499. println("msProcessError()");
  500. #endif
  501. uint8_t msResult = 0;
  502. switch(msStatus) {
  503. case MS_CBW_PASS:
  504. return MS_CBW_PASS;
  505. break;
  506. case MS_CBW_PHASE_ERROR:
  507. print("SCSI Phase Error: ");
  508. println(msStatus);
  509. return MS_SCSI_ERROR;
  510. break;
  511. case MS_CSW_TAG_ERROR:
  512. print("CSW Tag Error: ");
  513. println(MS_CSW_TAG_ERROR);
  514. return MS_CSW_TAG_ERROR;
  515. break;
  516. case MS_CSW_SIG_ERROR:
  517. print("CSW Signature Error: ");
  518. println(MS_CSW_SIG_ERROR);
  519. return MS_CSW_SIG_ERROR;
  520. break;
  521. case MS_CBW_FAIL:
  522. if(msResult = msRequestSense(&msSense)) {
  523. print("Failed to get sense codes. Returned code: ");
  524. println(msResult);
  525. }
  526. return MS_CBW_FAIL;
  527. break;
  528. default:
  529. print("SCSI Error: ");
  530. println(msStatus);
  531. return msStatus;
  532. }
  533. }