ZuluSCSI_platform_msc.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. #ifdef PLATFORM_MASS_STORAGE
  22. #include <SdFat.h>
  23. #include "ZuluSCSI_platform.h"
  24. #include "ZuluSCSI_log.h"
  25. #include "ZuluSCSI_msc.h"
  26. #include "ZuluSCSI_config.h"
  27. #include "ZuluSCSI_settings.h"
  28. #include "usb_serial.h"
  29. /* gd32 USB code is all C linked */
  30. extern "C" {
  31. #include <drv_usb_hw.h>
  32. #include <usbd_core.h>
  33. #include <drv_usbd_int.h>
  34. #include "usb_conf.h"
  35. #include "usbd_msc_core.h"
  36. #include "usbd_msc_mem.h"
  37. #include "usbd_msc_bbb.h"
  38. }
  39. /* local function prototypes ('static') */
  40. static int8_t storageInit(uint8_t Lun);
  41. static int8_t storageIsReady(uint8_t Lun);
  42. static int8_t storageIsWriteProtected(uint8_t Lun);
  43. static int8_t storageGetMaxLun(void);
  44. static int8_t storageRead(uint8_t Lun,
  45. uint8_t *buf,
  46. uint32_t BlkAddr,
  47. uint16_t BlkLen);
  48. static int8_t storageWrite(uint8_t Lun,
  49. uint8_t *buf,
  50. uint32_t BlkAddr,
  51. uint16_t BlkLen);
  52. usbd_mem_cb USBD_SD_fops = {
  53. .mem_init = storageInit,
  54. .mem_ready = storageIsReady,
  55. .mem_protected = storageIsWriteProtected,
  56. .mem_read = storageRead,
  57. .mem_write = storageWrite,
  58. .mem_maxlun = storageGetMaxLun,
  59. .mem_inquiry_data = {(uint8_t *)storageInquiryData},
  60. .mem_block_size = {SD_SECTOR_SIZE},
  61. .mem_block_len = {0}
  62. };
  63. usbd_mem_cb *usbd_mem_fops = &USBD_SD_fops;
  64. // shared with usb serial
  65. extern usb_core_driver cdc_acm;
  66. // external global SD variable
  67. extern SdFs SD;
  68. // private globals
  69. static bool unitReady = false;
  70. /* returns true if card reader mode should be entered. sd card is available. */
  71. bool platform_sense_msc() {
  72. // kill usb serial.
  73. usbd_disconnect (&cdc_acm);
  74. // set the MSC storage size
  75. usbd_mem_fops->mem_block_len[0] = SD.card()->sectorCount();
  76. unitReady = true;
  77. // init the MSC class, uses ISR and other global routines from usb_serial.cpp
  78. usbd_init(&cdc_acm, USB_CORE_ENUM_FS, &msc_desc, &msc_class);
  79. logmsg("Waiting for USB enumeration to expose SD card as a mass storage device");
  80. // wait to be begin to be enumerated
  81. uint32_t start = millis();
  82. uint16_t usb_timeout = g_scsi_settings.getSystem()->usbMassStorageWaitPeriod;
  83. while ((uint32_t)(millis() - start) < usb_timeout)
  84. {
  85. if (cdc_acm.dev.cur_status >= USBD_ADDRESSED)
  86. {
  87. dbgmsg("USB enumeration took ", (int)((uint32_t)(millis() - start)), "ms");
  88. return true;
  89. }
  90. }
  91. logmsg("Waiting for USB enumeration timed out after ", usb_timeout, "ms.");
  92. logmsg("-- Try increasing 'USBMassStorageWaitPeriod' in the ", CONFIGFILE);
  93. //if not, disconnect MSC class...
  94. usbd_disconnect (&cdc_acm);
  95. // and bring serial back for later.
  96. usb_serial_init();
  97. return false;
  98. }
  99. void platform_set_msc_image_mode(bool image_mode) {
  100. if (image_mode) logmsg("Warning: USB Image mounting not supported on this platform!");
  101. //g_MSC.SDMode = !image_mode;
  102. }
  103. /* perform MSC-specific init tasks */
  104. void platform_enter_msc() {
  105. dbgmsg("USB MSC buffer size: ", (uint32_t) MSC_MEDIA_PACKET_SIZE);
  106. // give the host a moment to finish enumerate and "load" media
  107. uint32_t start = millis();
  108. uint16_t usb_timeout = g_scsi_settings.getSystem()->usbMassStorageWaitPeriod;
  109. while ((USBD_CONFIGURED != cdc_acm.dev.cur_status) && ((uint32_t)(millis() - start) < usb_timeout ) )
  110. delay(100);
  111. }
  112. /* return true while remaining in msc mode, and perform periodic tasks */
  113. bool platform_run_msc() {
  114. usbd_msc_handler *msc = (usbd_msc_handler *)cdc_acm.dev.class_data[USBD_MSC_INTERFACE];
  115. // stupid windows doesn't send start_stop_unit events if it is ejected via safely remove devices.
  116. // it just stops talking to the device so we don't know we've been ejected....
  117. // other OSes always send the start_stop_unit, windows does too when ejected from explorer.
  118. // so we watch for the OS suspending device and assume we're done in USB mode if so.
  119. // this will also trigger if the host were to suspend usb device due to going to sleep
  120. // however, I hope no sane OS would sleep mid transfer or with a dirty filesystem.
  121. // Note: Mac OS X apparently not sane.
  122. uint8_t is_suspended = cdc_acm.dev.cur_status == (uint8_t)USBD_SUSPENDED;
  123. return (! msc->scsi_disk_pop) && !is_suspended;
  124. }
  125. void platform_exit_msc() {
  126. unitReady = false;
  127. // disconnect msc....
  128. usbd_disconnect (&cdc_acm);
  129. // catch our breath....
  130. delay(200);
  131. // ... and bring usb serial up
  132. usb_serial_init();
  133. }
  134. /*!
  135. \brief initialize the storage medium
  136. \param[in] Lun: logical unit number
  137. \param[out] none
  138. \retval status
  139. */
  140. static int8_t storageInit(uint8_t Lun)
  141. {
  142. return 0;
  143. }
  144. /*!
  145. \brief check whether the medium is ready
  146. \param[in] Lun: logical unit number
  147. \param[out] none
  148. \retval status
  149. */
  150. static int8_t storageIsReady(uint8_t Lun)
  151. {
  152. return ! unitReady; // 0 = success / unit is ready
  153. }
  154. /*!
  155. \brief check whether the medium is write-protected
  156. \param[in] Lun: logical unit number
  157. \param[out] none
  158. \retval status
  159. */
  160. static int8_t storageIsWriteProtected(uint8_t Lun)
  161. {
  162. return ! unitReady; // 0 = read/write
  163. }
  164. /*!
  165. \brief read data from the medium
  166. \param[in] Lun: logical unit number
  167. \param[in] buf: pointer to the buffer to save data
  168. \param[in] BlkAddr: address of 1st block to be read
  169. \param[in] BlkLen: number of blocks to be read
  170. \param[out] none
  171. \retval status
  172. */
  173. static int8_t storageRead(uint8_t Lun,
  174. uint8_t *buf,
  175. uint32_t BlkAddr,
  176. uint16_t BlkLen)
  177. {
  178. // divide by sector size to convert address to LBA
  179. bool rc = SD.card()->readSectors(BlkAddr/SD_SECTOR_SIZE, buf, BlkLen);
  180. // only blink fast on reads; writes will override this
  181. if (MSC_LEDMode == LED_SOLIDON)
  182. MSC_LEDMode = LED_BLINK_FAST;
  183. return !rc;
  184. }
  185. /*!
  186. \brief write data to the medium
  187. \param[in] Lun: logical unit number
  188. \param[in] buf: pointer to the buffer to write
  189. \param[in] BlkAddr: address of 1st block to be written
  190. \param[in] BlkLen: number of blocks to be write
  191. \param[out] none
  192. \retval status
  193. */
  194. static int8_t storageWrite(uint8_t Lun,
  195. uint8_t *buf,
  196. uint32_t BlkAddr,
  197. uint16_t BlkLen)
  198. {
  199. // divide by sector size to convert address to LBA
  200. bool rc = SD.card()->writeSectors(BlkAddr/SD_SECTOR_SIZE, buf, BlkLen);
  201. // always slow blink
  202. MSC_LEDMode = LED_BLINK_SLOW;
  203. return !rc;
  204. }
  205. /*!
  206. \brief get number of supported logical unit
  207. \param[in] none
  208. \param[out] none
  209. \retval number of logical unit
  210. */
  211. static int8_t storageGetMaxLun(void)
  212. {
  213. return 0; // number of LUNs supported - 1
  214. }
  215. #endif // PLATFORM_MASS_STORAGE