ZuluSCSI_platform_msc.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /**
  2. * Copyright (c) 2023-2024 zigzagjoe
  3. *
  4. * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  5. *
  6. * https://www.gnu.org/licenses/gpl-3.0.html
  7. * ----
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version. 
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. * GNU General Public License for more details. 
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  20. **/
  21. /* platform specific MSC routines */
  22. #ifdef PLATFORM_MASS_STORAGE
  23. #include <SdFat.h>
  24. #include <device/usbd.h>
  25. #include <hardware/gpio.h>
  26. #include "ZuluSCSI_platform.h"
  27. #include "ZuluSCSI_log.h"
  28. #include "ZuluSCSI_msc.h"
  29. #include "ZuluSCSI_msc_initiator.h"
  30. #include "ZuluSCSI_config.h"
  31. #include "ZuluSCSI_settings.h"
  32. #include <class/msc/msc.h>
  33. #include <class/msc/msc_device.h>
  34. #if CFG_TUD_MSC_EP_BUFSIZE < SD_SECTOR_SIZE
  35. #error "CFG_TUD_MSC_EP_BUFSIZE is too small! It needs to be at least 512 (SD_SECTOR_SIZE)"
  36. #endif
  37. #define DIGITAL_PIN_CYW43_OFFSET 64
  38. // external global SD variable
  39. extern SdFs SD;
  40. static bool unitReady = false;
  41. /* return true if USB presence detected / eligble to enter CR mode */
  42. bool platform_sense_msc() {
  43. #if defined(ZULUSCSI_PICO) || defined(ZULUSCSI_PICO_2)
  44. // check if we're USB powered, if not, exit immediately
  45. // pin on the wireless module, see https://github.com/earlephilhower/arduino-pico/discussions/835
  46. // Update: from the above discussion the offset 32 has been changed to 64 to access CYW43 GPIO pins
  47. // since the addition of the RP2350 chips, now stored in the DIGITAL_PIN_CYW43_OFFSET define
  48. if (rp2040.isPicoW() && !digitalRead(DIGITAL_PIN_CYW43_OFFSET + 2))
  49. return false;
  50. if (!rp2040.isPicoW() && !digitalRead(24))
  51. return false;
  52. #endif
  53. logmsg("Waiting for USB enumeration to enter Card Reader mode.");
  54. // wait for up to a second to be enumerated
  55. uint32_t start = millis();
  56. bool timed_out = false;
  57. uint16_t usb_timeout = g_scsi_settings.getSystem()->usbMassStorageWaitPeriod;
  58. while (!tud_connected())
  59. {
  60. if ((uint32_t)(millis() - start) > usb_timeout)
  61. {
  62. logmsg("Waiting for USB enumeration timed out after ", usb_timeout, "ms.");
  63. logmsg("-- Try increasing 'USBMassStorageWaitPeriod' in the ", CONFIGFILE);
  64. timed_out = true;
  65. break;
  66. }
  67. delay(100);
  68. }
  69. if (!timed_out)
  70. dbgmsg("USB enumeration took ", (int)((uint32_t)(millis() - start)), "ms");
  71. // tud_connected returns True if just got out of Bus Reset and received the very first data from host
  72. // https://github.com/hathach/tinyusb/blob/master/src/device/usbd.h#L63
  73. return tud_connected();
  74. }
  75. /* return true if we should remain in card reader mode and perform periodic tasks */
  76. bool platform_run_msc() {
  77. return unitReady;
  78. }
  79. /* perform MSC class preinit tasks */
  80. void platform_enter_msc() {
  81. dbgmsg("USB MSC buffer size: ", CFG_TUD_MSC_EP_BUFSIZE);
  82. // MSC is ready for read/write
  83. // we don't need any prep, but the var is requried as the MSC callbacks are always active
  84. unitReady = true;
  85. }
  86. /* perform any cleanup tasks for the MSC-specific functionality */
  87. void platform_exit_msc() {
  88. unitReady = false;
  89. }
  90. /* TinyUSB mass storage callbacks follow */
  91. // usb framework checks this func exists for mass storage config. no code needed.
  92. void __USBInstallMassStorage() { }
  93. // Invoked when received SCSI_CMD_INQUIRY
  94. // fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
  95. extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8],
  96. uint8_t product_id[16], uint8_t product_rev[4]) {
  97. if (g_msc_initiator) return init_msc_inquiry_cb(lun, vendor_id, product_id, product_rev);
  98. const char vid[] = "ZuluSCSI";
  99. const char pid[] = PLATFORM_PID;
  100. const char rev[] = "1.0";
  101. memcpy(vendor_id, vid, tu_min32(strlen(vid), 8));
  102. memcpy(product_id, pid, tu_min32(strlen(pid), 16));
  103. memcpy(product_rev, rev, tu_min32(strlen(rev), 4));
  104. }
  105. // max LUN supported
  106. // we only have the one SD card
  107. extern "C" uint8_t tud_msc_get_maxlun_cb(void) {
  108. if (g_msc_initiator) return init_msc_get_maxlun_cb();
  109. return 1; // number of LUNs supported
  110. }
  111. // return writable status
  112. // on platform supporting write protect switch, could do that here.
  113. // otherwise this is not actually needed
  114. extern "C" bool tud_msc_is_writable_cb (uint8_t lun)
  115. {
  116. if (g_msc_initiator) return init_msc_is_writable_cb(lun);
  117. (void) lun;
  118. return unitReady;
  119. }
  120. // see https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf pg 221
  121. extern "C" bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
  122. {
  123. if (g_msc_initiator) return init_msc_start_stop_cb(lun, power_condition, start, load_eject);
  124. if (load_eject) {
  125. if (start) {
  126. // load disk storage
  127. // do nothing as we started "loaded"
  128. } else {
  129. unitReady = false;
  130. }
  131. }
  132. return true;
  133. }
  134. // return true if we are ready to service reads/writes
  135. extern "C" bool tud_msc_test_unit_ready_cb(uint8_t lun) {
  136. if (g_msc_initiator) return init_msc_test_unit_ready_cb(lun);
  137. return unitReady;
  138. }
  139. // return size in blocks and block size
  140. extern "C" void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count,
  141. uint16_t *block_size) {
  142. if (g_msc_initiator) return init_msc_capacity_cb(lun, block_count, block_size);
  143. *block_count = unitReady ? (SD.card()->sectorCount()) : 0;
  144. *block_size = SD_SECTOR_SIZE;
  145. }
  146. // Callback invoked when received an SCSI command not in built-in list (below) which have their own callbacks
  147. // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE, READ10 and WRITE10
  148. extern "C" int32_t tud_msc_scsi_cb(uint8_t lun, const uint8_t scsi_cmd[16], void *buffer,
  149. uint16_t bufsize) {
  150. if (g_msc_initiator) return init_msc_scsi_cb(lun, scsi_cmd, buffer, bufsize);
  151. const void *response = NULL;
  152. uint16_t resplen = 0;
  153. switch (scsi_cmd[0]) {
  154. case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
  155. // Host is about to read/write etc ... better not to disconnect disk
  156. resplen = 0;
  157. break;
  158. default:
  159. // Set Sense = Invalid Command Operation
  160. tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
  161. // negative means error -> tinyusb could stall and/or response with failed status
  162. resplen = -1;
  163. break;
  164. }
  165. // return len must not larger than bufsize
  166. if (resplen > bufsize) {
  167. resplen = bufsize;
  168. }
  169. // copy response to stack's buffer if any
  170. if (response && resplen) {
  171. memcpy(buffer, response, resplen);
  172. }
  173. return resplen;
  174. }
  175. // Callback invoked when received READ10 command.
  176. // Copy disk's data to buffer (up to bufsize) and return number of copied bytes (must be multiple of block size)
  177. extern "C" int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset,
  178. void* buffer, uint32_t bufsize)
  179. {
  180. if (g_msc_initiator) return init_msc_read10_cb(lun, lba, offset, buffer, bufsize);
  181. bool rc = SD.card()->readSectors(lba, (uint8_t*) buffer, bufsize/SD_SECTOR_SIZE);
  182. // only blink fast on reads; writes will override this
  183. if (MSC_LEDMode == LED_SOLIDON)
  184. MSC_LEDMode = LED_BLINK_FAST;
  185. return rc ? bufsize : -1;
  186. }
  187. // Callback invoked when receive WRITE10 command.
  188. // Process data in buffer to disk's storage and return number of written bytes (must be multiple of block size)
  189. extern "C" int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset,
  190. uint8_t *buffer, uint32_t bufsize) {
  191. if (g_msc_initiator) return init_msc_read10_cb(lun, lba, offset, buffer, bufsize);
  192. bool rc = SD.card()->writeSectors(lba, buffer, bufsize/SD_SECTOR_SIZE);
  193. // always slow blink
  194. MSC_LEDMode = LED_BLINK_SLOW;
  195. return rc ? bufsize : -1;
  196. }
  197. // Callback invoked when WRITE10 command is completed (status received and accepted by host).
  198. // used to flush any pending cache to storage
  199. extern "C" void tud_msc_write10_complete_cb(uint8_t lun) {
  200. if (g_msc_initiator) return init_msc_write10_complete_cb(lun);
  201. }
  202. #endif