BlueSCSI_platform_msc.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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 <device/usbd.h>
  24. #include <hardware/gpio.h>
  25. #include "BlueSCSI_platform.h"
  26. #include "BlueSCSI_disk.h"
  27. #include "BlueSCSI_log.h"
  28. #include "BlueSCSI_msc.h"
  29. #include "BlueSCSI_msc_initiator.h"
  30. #include "BlueSCSI_config.h"
  31. #include "BlueSCSI_settings.h"
  32. #include <class/msc/msc.h>
  33. #include <class/msc/msc_device.h>
  34. #include <pico/mutex.h>
  35. extern mutex_t __usb_mutex;
  36. #if CFG_TUD_MSC_EP_BUFSIZE < SD_SECTOR_SIZE
  37. #error "CFG_TUD_MSC_EP_BUFSIZE is too small! It needs to be at least 512 (SD_SECTOR_SIZE)"
  38. #endif
  39. #define DIGITAL_PIN_CYW43_OFFSET 64
  40. // external global SD variable
  41. extern SdFs SD;
  42. // external images configuration
  43. extern image_config_t g_DiskImages[S2S_MAX_TARGETS];
  44. static bool g_msc_lock; // To block re-entrant calls
  45. static bool g_msc_usb_mutex_held;
  46. /* globals */
  47. static struct {
  48. uint8_t lun_unitReady[S2S_MAX_TARGETS];
  49. image_config_t * lun_config[S2S_MAX_TARGETS];
  50. uint8_t lun_count = 0;
  51. uint8_t unitReady = 0;
  52. uint8_t SDMode = 1;
  53. uint8_t lun_count_prev_response = 0;
  54. } g_MSC;
  55. void platform_msc_lock_set(bool block)
  56. {
  57. if (block)
  58. {
  59. if (g_msc_lock)
  60. {
  61. logmsg("Re-entrant MSC lock!");
  62. assert(false);
  63. }
  64. g_msc_usb_mutex_held = mutex_try_enter(&__usb_mutex, NULL); // Blocks USB IRQ if not already blocked
  65. g_msc_lock = true; // Blocks platform USB polling
  66. }
  67. else
  68. {
  69. if (!g_msc_lock)
  70. {
  71. logmsg("MSC lock released when not held!");
  72. assert(false);
  73. }
  74. g_msc_lock = false;
  75. if (g_msc_usb_mutex_held)
  76. {
  77. g_msc_usb_mutex_held = false;
  78. mutex_exit(&__usb_mutex);
  79. }
  80. }
  81. }
  82. bool platform_msc_lock_get()
  83. {
  84. return g_msc_lock;
  85. }
  86. struct MSCScopedLock {
  87. public:
  88. MSCScopedLock() { platform_msc_lock_set(true); }
  89. ~MSCScopedLock() { platform_msc_lock_set(false); }
  90. };
  91. /* return true if USB presence detected / eligible to enter CR mode */
  92. bool platform_sense_msc() {
  93. logmsg("platform_sense_msc");
  94. #if defined(BLUESCSI_PICO) || defined(BLUESCSI_PICO_2) || defined(BLUESCSI_V2)
  95. // check if we're USB powered, if not, exit immediately
  96. // pin on the wireless module, see https://github.com/earlephilhower/arduino-pico/discussions/835
  97. // Update: from the above discussion the offset 32 has been changed to 64 to access CYW43 GPIO pins
  98. // since the addition of the RP2350 chips, now stored in the DIGITAL_PIN_CYW43_OFFSET define
  99. if (rp2040.isPicoW() && !digitalRead(DIGITAL_PIN_CYW43_OFFSET + 2))
  100. return false;
  101. if (!rp2040.isPicoW() && !digitalRead(24))
  102. return false;
  103. #endif
  104. logmsg("Waiting for USB enumeration to enter Card Reader mode.");
  105. // wait for up to a second to be enumerated
  106. uint32_t start = millis();
  107. bool timed_out = false;
  108. uint16_t usb_timeout = g_scsi_settings.getSystem()->usbMassStorageWaitPeriod;
  109. while (!tud_connected())
  110. {
  111. if ((uint32_t)(millis() - start) > usb_timeout)
  112. {
  113. logmsg("Waiting for USB enumeration timed out after ", usb_timeout, "ms.");
  114. logmsg("-- Try increasing 'USBMassStorageWaitPeriod' in the ", CONFIGFILE);
  115. timed_out = true;
  116. break;
  117. }
  118. delay(100);
  119. }
  120. if (!timed_out)
  121. dbgmsg("USB enumeration took ", (int)((uint32_t)(millis() - start)), "ms");
  122. // tud_connected returns True if just got out of Bus Reset and received the very first data from host
  123. // https://github.com/hathach/tinyusb/blob/master/src/device/usbd.h#L63
  124. return tud_connected();
  125. }
  126. /* perform periodic tasks, return true if we should remain in card reader mode */
  127. bool platform_run_msc() {
  128. return g_MSC.unitReady;
  129. }
  130. /* load the setting if we present images or not */
  131. void platform_set_msc_image_mode(bool image_mode) {
  132. g_MSC.SDMode = !image_mode;
  133. }
  134. /* return true if the image type makes sense as a mass-storage device */
  135. bool msc_image_eligble(uint8_t t) {
  136. switch (t) {
  137. case S2S_CFG_FIXED:
  138. case S2S_CFG_REMOVABLE:
  139. case S2S_CFG_MO: /* not actually sure about this and zip */
  140. case S2S_CFG_ZIP100:
  141. return true;
  142. case S2S_CFG_OPTICAL: /* will not contain a MBR */
  143. case S2S_CFG_FLOPPY_14MB: /* will not contain a MBR */
  144. case S2S_CFG_SEQUENTIAL: /* tape drive */
  145. case S2S_CFG_NETWORK: /* always empty */
  146. case S2S_CFG_NOT_SET:
  147. default:
  148. return false;
  149. }
  150. }
  151. /* perform MSC class preinit tasks */
  152. void platform_enter_msc() {
  153. dbgmsg("USB MSC buffer size: ", CFG_TUD_MSC_EP_BUFSIZE);
  154. g_MSC.lun_count = 0;
  155. if (!g_MSC.SDMode) {
  156. logmsg("Presenting configured images as USB storage devices");
  157. for (int i = 0; i < S2S_MAX_TARGETS; i++) {
  158. if (msc_image_eligble(g_DiskImages[i].deviceType) && g_DiskImages[i].file.isOpen()) {
  159. logmsg("USB LUN ", (int)g_MSC.lun_count," => ",g_DiskImages[i].current_image);
  160. // anything but linux probably won't deal gracefully with nonstandard or odd sector sizes, present a warning
  161. if (g_DiskImages[i].bytesPerSector != 512 && g_DiskImages[i].bytesPerSector != 4096) {
  162. logmsg("Warning: USB LUN ",(int)g_MSC.lun_count," uses a sector size of ",g_DiskImages[i].bytesPerSector,". Not all OS can deal with this!");
  163. }
  164. g_MSC.lun_config[g_MSC.lun_count] = &g_DiskImages[i];
  165. g_MSC.lun_unitReady[g_MSC.lun_count] = 1;
  166. g_MSC.lun_count ++;
  167. }
  168. }
  169. if (g_MSC.lun_count == 0) {
  170. logmsg("No images to present, falling back to SD card!");
  171. g_MSC.SDMode = 1;
  172. } else
  173. logmsg("Total USB LUN ", (int)g_MSC.lun_count);
  174. }
  175. if (g_MSC.SDMode) {
  176. logmsg("Presenting SD card as USB storage device");
  177. g_MSC.lun_count = 1;
  178. g_MSC.lun_unitReady[0] = 1;
  179. }
  180. // MSC is ready for read/write
  181. g_MSC.unitReady = g_MSC.lun_count;
  182. if (g_MSC.lun_count_prev_response != 0 &&
  183. g_MSC.lun_count != g_MSC.lun_count_prev_response)
  184. {
  185. // Host has already queried us for the number of LUNs, but
  186. // our response has now changed. We need to re-enumerate to
  187. // update it.
  188. g_MSC.lun_count_prev_response = 0;
  189. tud_disconnect();
  190. delay(250);
  191. tud_connect();
  192. }
  193. }
  194. /* perform any cleanup tasks for the MSC-specific functionality */
  195. void platform_exit_msc() {
  196. g_MSC.unitReady = 0;
  197. }
  198. /* TinyUSB mass storage callbacks follow */
  199. // usb framework checks this func exists for mass storage config. no code needed.
  200. void __USBInstallMassStorage() { }
  201. // Invoked when received SCSI_CMD_INQUIRY
  202. // fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
  203. extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8],
  204. uint8_t product_id[16], uint8_t product_rev[4]) {
  205. MSCScopedLock lock;
  206. if (g_msc_initiator) return init_msc_inquiry_cb(lun, vendor_id, product_id, product_rev);
  207. const char vid[] = "BlueSCSI";
  208. const char pid[] = PLATFORM_PID;
  209. const char rev[] = PLATFORM_REVISION;
  210. memcpy(vendor_id, vid, tu_min32(strlen(vid), 8));
  211. memcpy(product_id, pid, tu_min32(strlen(pid), 16));
  212. memcpy(product_rev, rev, tu_min32(strlen(rev), 4));
  213. }
  214. // max LUN supported
  215. // we only have the one SD card
  216. extern "C" uint8_t tud_msc_get_maxlun_cb(void)
  217. {
  218. MSCScopedLock lock;
  219. uint8_t result;
  220. if (g_msc_initiator)
  221. {
  222. result = init_msc_get_maxlun_cb();
  223. }
  224. else if (g_MSC.lun_count != 0)
  225. {
  226. result = g_MSC.lun_count; // number of LUNs supported
  227. }
  228. else
  229. {
  230. // Returning 0 makes TU_VERIFY(maxlun); fail in tinyusb/src/class/msc/msc_device.c:378
  231. // This stalls the endpoint and causes an unnecessary enumeration delay on Windows.
  232. result = 1;
  233. }
  234. g_MSC.lun_count_prev_response = result;
  235. return result;
  236. }
  237. // return writable status
  238. // on platform supporting write protect switch, could do that here.
  239. // otherwise this is not actually needed
  240. extern "C" bool tud_msc_is_writable_cb (uint8_t lun)
  241. {
  242. MSCScopedLock lock;
  243. if (g_msc_initiator) return init_msc_is_writable_cb(lun);
  244. if (g_MSC.SDMode) return g_MSC.unitReady;
  245. (void) lun;
  246. return g_MSC.unitReady && g_MSC.lun_unitReady[lun] && g_MSC.lun_config[lun]->file.isWritable();
  247. }
  248. // see https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf pg 221
  249. extern "C" bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
  250. {
  251. MSCScopedLock lock;
  252. if (g_msc_initiator) return init_msc_start_stop_cb(lun, power_condition, start, load_eject);
  253. if (load_eject) {
  254. if (start) {
  255. // load disk storage
  256. // do nothing as we started "loaded"
  257. } else {
  258. g_MSC.lun_unitReady[lun] = false;
  259. if (g_MSC.unitReady) // no more active LUNs -> global not ready flag
  260. g_MSC.unitReady --;
  261. }
  262. }
  263. return true;
  264. }
  265. // return true if we are ready to service reads/writes
  266. extern "C" bool tud_msc_test_unit_ready_cb(uint8_t lun)
  267. {
  268. MSCScopedLock lock;
  269. if (g_msc_initiator) return init_msc_test_unit_ready_cb(lun);
  270. return g_MSC.unitReady && g_MSC.lun_unitReady[lun];
  271. }
  272. // return size in blocks and block size
  273. extern "C" void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count,
  274. uint16_t *block_size)
  275. {
  276. MSCScopedLock lock;
  277. if (g_msc_initiator) return init_msc_capacity_cb(lun, block_count, block_size);
  278. if (g_MSC.SDMode) {
  279. *block_count = g_MSC.unitReady ? (SD.card()->sectorCount()) : 0;
  280. *block_size = SD_SECTOR_SIZE;
  281. } else { // present the bytesPerSector of file, though it remains to be seen if host will like this
  282. *block_count = (g_MSC.unitReady && g_MSC.lun_unitReady[lun]) ? (g_MSC.lun_config[lun]->file.size() / g_MSC.lun_config[lun]->bytesPerSector) : 0;
  283. *block_size = g_MSC.lun_config[lun]->bytesPerSector;
  284. }
  285. }
  286. // Callback invoked when received an SCSI command not in built-in list (below) which have their own callbacks
  287. // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE, READ10 and WRITE10
  288. extern "C" int32_t tud_msc_scsi_cb(uint8_t lun, const uint8_t scsi_cmd[16], void *buffer,
  289. uint16_t bufsize)
  290. {
  291. MSCScopedLock lock;
  292. if (g_msc_initiator) return init_msc_scsi_cb(lun, scsi_cmd, buffer, bufsize);
  293. const void *response = NULL;
  294. uint16_t resplen = 0;
  295. switch (scsi_cmd[0]) {
  296. case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
  297. // Host is about to read/write etc ... better not to disconnect disk
  298. resplen = 0;
  299. break;
  300. default:
  301. // Set Sense = Invalid Command Operation
  302. tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
  303. // negative means error -> tinyusb could stall and/or response with failed status
  304. resplen = -1;
  305. break;
  306. }
  307. // return len must not larger than bufsize
  308. if (resplen > bufsize) {
  309. resplen = bufsize;
  310. }
  311. // copy response to stack's buffer if any
  312. if (response && resplen) {
  313. memcpy(buffer, response, resplen);
  314. }
  315. return resplen;
  316. }
  317. // Callback invoked when received READ10 command.
  318. // Copy disk's data to buffer (up to bufsize) and return number of copied bytes (must be multiple of block size)
  319. extern "C" int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset,
  320. void* buffer, uint32_t bufsize)
  321. {
  322. MSCScopedLock lock;
  323. if (g_msc_initiator) return init_msc_read10_cb(lun, lba, offset, buffer, bufsize);
  324. bool rc = 0;
  325. if (g_MSC.SDMode) {
  326. rc = SD.card()->readSectors(lba, (uint8_t*) buffer, bufsize/SD_SECTOR_SIZE);
  327. } else {
  328. if (g_MSC.lun_unitReady[lun]) {
  329. g_MSC.lun_config[lun]->file.seek(lba * g_MSC.lun_config[lun]->bytesPerSector);
  330. rc = g_MSC.lun_config[lun]->file.read(buffer, bufsize);
  331. } else {
  332. logmsg("Attempted read to non-ready LUN ",lun);
  333. }
  334. }
  335. // only blink fast on reads; writes will override this
  336. if (MSC_LEDMode == LED_SOLIDON)
  337. MSC_LEDMode = LED_BLINK_FAST;
  338. return rc ? bufsize : -1;
  339. }
  340. // Callback invoked when receive WRITE10 command.
  341. // Process data in buffer to disk's storage and return number of written bytes (must be multiple of block size)
  342. extern "C" int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset,
  343. uint8_t *buffer, uint32_t bufsize)
  344. {
  345. MSCScopedLock lock;
  346. if (g_msc_initiator) return init_msc_write10_cb(lun, lba, offset, buffer, bufsize);
  347. bool rc = 0;
  348. if (g_MSC.SDMode) {
  349. rc = SD.card()->writeSectors(lba, buffer, bufsize/SD_SECTOR_SIZE);
  350. } else {
  351. if (g_MSC.lun_unitReady[lun]) {
  352. g_MSC.lun_config[lun]->file.seek(lba * g_MSC.lun_config[lun]->bytesPerSector);
  353. rc = g_MSC.lun_config[lun]->file.write(buffer, bufsize);
  354. } else {
  355. logmsg("Attempted write to non-ready LUN ",lun);
  356. }
  357. }
  358. // always slow blink
  359. MSC_LEDMode = LED_BLINK_SLOW;
  360. return rc ? bufsize : -1;
  361. }
  362. // Callback invoked when WRITE10 command is completed (status received and accepted by host).
  363. // used to flush any pending cache to storage
  364. extern "C" void tud_msc_write10_complete_cb(uint8_t lun)
  365. {
  366. MSCScopedLock lock;
  367. if (g_msc_initiator) return init_msc_write10_complete_cb(lun);
  368. }
  369. #endif