mass_storage_scsi.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. #include "mass_storage_scsi.h"
  2. #include <core/log.h>
  3. #define TAG "MassStorageSCSI"
  4. #define SCSI_TEST_UNIT_READY (0x00)
  5. #define SCSI_REQUEST_SENSE (0x03)
  6. #define SCSI_INQUIRY (0x12)
  7. #define SCSI_READ_FORMAT_CAPACITIES (0x23)
  8. #define SCSI_READ_CAPACITY_10 (0x25)
  9. #define SCSI_MODE_SENSE_6 (0x1A)
  10. #define SCSI_READ_10 (0x28)
  11. #define SCSI_PREVENT_MEDIUM_REMOVAL (0x1E)
  12. #define SCSI_START_STOP_UNIT (0x1B)
  13. #define SCSI_WRITE_10 (0x2A)
  14. bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len) {
  15. if(!len) {
  16. scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
  17. scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
  18. return false;
  19. }
  20. FURI_LOG_T(TAG, "START %02X", cmd[0]);
  21. scsi->cmd = cmd;
  22. scsi->cmd_len = len;
  23. scsi->rx_done = false;
  24. scsi->tx_done = false;
  25. switch(cmd[0]) {
  26. case SCSI_WRITE_10: {
  27. if(len < 10) return false;
  28. scsi->write_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
  29. scsi->write_10.count = cmd[7] << 8 | cmd[8];
  30. FURI_LOG_D(TAG, "SCSI_WRITE_10 %08lX %04X", scsi->write_10.lba, scsi->write_10.count);
  31. return true;
  32. }; break;
  33. case SCSI_READ_10: {
  34. if(len < 10) return false;
  35. scsi->read_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
  36. scsi->read_10.count = cmd[7] << 8 | cmd[8];
  37. FURI_LOG_D(TAG, "SCSI_READ_10 %08lX %04X", scsi->read_10.lba, scsi->read_10.count);
  38. return true;
  39. }; break;
  40. }
  41. return true;
  42. }
  43. bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len) {
  44. FURI_LOG_T(TAG, "RX %02X len %lu", scsi->cmd[0], len);
  45. if(scsi->rx_done) return false;
  46. switch(scsi->cmd[0]) {
  47. case SCSI_WRITE_10: {
  48. uint32_t block_size = SCSI_BLOCK_SIZE;
  49. uint16_t blocks = len / block_size;
  50. bool result =
  51. scsi->fn.write(scsi->fn.ctx, scsi->write_10.lba, blocks, data, blocks * block_size);
  52. scsi->write_10.lba += blocks;
  53. scsi->write_10.count -= blocks;
  54. if(!scsi->write_10.count) {
  55. scsi->rx_done = true;
  56. }
  57. return result;
  58. }; break;
  59. default: {
  60. FURI_LOG_W(TAG, "unexpected scsi rx data cmd=%02X", scsi->cmd[0]);
  61. scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
  62. scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
  63. return false;
  64. }; break;
  65. }
  66. }
  67. bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t cap) {
  68. FURI_LOG_T(TAG, "TX %02X cap %lu", scsi->cmd[0], cap);
  69. if(scsi->tx_done) return false;
  70. switch(scsi->cmd[0]) {
  71. case SCSI_REQUEST_SENSE: {
  72. FURI_LOG_D(TAG, "SCSI_REQUEST_SENSE");
  73. if(cap < 18) return false;
  74. memset(data, 0, cap);
  75. data[0] = 0x70; // fixed format sense data
  76. data[1] = 0; // obsolete
  77. data[2] = scsi->sk; // sense key
  78. data[3] = 0; // information
  79. data[4] = 0; // information
  80. data[5] = 0; // information
  81. data[6] = 0; // information
  82. data[7] = 10; // additional sense length (len-8)
  83. data[8] = 0; // command specific information
  84. data[9] = 0; // command specific information
  85. data[10] = 0; // command specific information
  86. data[11] = 0; // command specific information
  87. data[12] = scsi->asc; // additional sense code
  88. data[13] = 0; // additional sense code qualifier
  89. data[14] = 0; // field replaceable unit code
  90. data[15] = 0; // sense key specific information
  91. data[16] = 0; // sense key specific information
  92. data[17] = 0; // sense key specific information
  93. *len = 18;
  94. scsi->sk = 0;
  95. scsi->asc = 0;
  96. scsi->tx_done = true;
  97. return true;
  98. }; break;
  99. case SCSI_INQUIRY: {
  100. FURI_LOG_D(TAG, "SCSI_INQUIRY");
  101. if(scsi->cmd_len < 5) return false;
  102. if(cap < 36) return false;
  103. bool evpd = scsi->cmd[1] & 1;
  104. uint8_t page_code = scsi->cmd[2];
  105. if(evpd == 0) {
  106. if(page_code != 0) return false;
  107. data[0] = 0x00; // device type: direct access block device
  108. data[1] = 0x80; // removable: true
  109. data[2] = 0x04; // version
  110. data[3] = 0x02; // response data format
  111. data[4] = 31; // additional length (len - 5)
  112. data[5] = 0; // flags
  113. data[6] = 0; // flags
  114. data[7] = 0; // flags
  115. memcpy(data + 8, "Flipper ", 8); // vendor id
  116. memcpy(data + 16, "Mass Storage ", 16); // product id
  117. memcpy(data + 32, "0001", 4); // product revision level
  118. *len = 36;
  119. scsi->tx_done = true;
  120. return true;
  121. } else {
  122. if(page_code != 0x80) {
  123. FURI_LOG_W(TAG, "Unsupported VPD code %02X", page_code);
  124. return false;
  125. }
  126. data[0] = 0x00;
  127. data[1] = 0x80;
  128. data[2] = 0x00;
  129. data[3] = 0x01; // Serial len
  130. data[4] = '0';
  131. *len = 5;
  132. scsi->tx_done = true;
  133. return true;
  134. }
  135. }; break;
  136. case SCSI_READ_FORMAT_CAPACITIES: {
  137. FURI_LOG_D(TAG, "SCSI_READ_FORMAT_CAPACITIES");
  138. if(cap < 12) {
  139. return false;
  140. }
  141. uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx);
  142. uint32_t block_size = SCSI_BLOCK_SIZE;
  143. // Capacity List Header
  144. data[0] = 0;
  145. data[1] = 0;
  146. data[2] = 0;
  147. data[3] = 8;
  148. // Capacity Descriptor
  149. data[4] = (n_blocks - 1) >> 24;
  150. data[5] = (n_blocks - 1) >> 16;
  151. data[6] = (n_blocks - 1) >> 8;
  152. data[7] = (n_blocks - 1) & 0xFF;
  153. data[8] = 0x02; // Formatted media
  154. data[9] = block_size >> 16;
  155. data[10] = block_size >> 8;
  156. data[11] = block_size & 0xFF;
  157. *len = 12;
  158. scsi->tx_done = true;
  159. return true;
  160. }; break;
  161. case SCSI_READ_CAPACITY_10: {
  162. FURI_LOG_D(TAG, "SCSI_READ_CAPACITY_10");
  163. if(cap < 8) return false;
  164. uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx);
  165. uint32_t block_size = SCSI_BLOCK_SIZE;
  166. data[0] = (n_blocks - 1) >> 24;
  167. data[1] = (n_blocks - 1) >> 16;
  168. data[2] = (n_blocks - 1) >> 8;
  169. data[3] = (n_blocks - 1) & 0xFF;
  170. data[4] = block_size >> 24;
  171. data[5] = block_size >> 16;
  172. data[6] = block_size >> 8;
  173. data[7] = block_size & 0xFF;
  174. *len = 8;
  175. scsi->tx_done = true;
  176. return true;
  177. }; break;
  178. case SCSI_MODE_SENSE_6: {
  179. FURI_LOG_D(TAG, "SCSI_MODE_SENSE_6 %lu", cap);
  180. if(cap < 4) return false;
  181. data[0] = 3; // mode data length (len - 1)
  182. data[1] = 0; // medium type
  183. data[2] = 0; // device-specific parameter
  184. data[3] = 0; // block descriptor length
  185. *len = 4;
  186. scsi->tx_done = true;
  187. return true;
  188. }; break;
  189. case SCSI_READ_10: {
  190. uint32_t block_size = SCSI_BLOCK_SIZE;
  191. bool result =
  192. scsi->fn.read(scsi->fn.ctx, scsi->read_10.lba, scsi->read_10.count, data, len, cap);
  193. *len -= *len % block_size;
  194. uint16_t blocks = *len / block_size;
  195. scsi->read_10.lba += blocks;
  196. scsi->read_10.count -= blocks;
  197. if(!scsi->read_10.count) {
  198. scsi->tx_done = true;
  199. }
  200. return result;
  201. }; break;
  202. default: {
  203. FURI_LOG_W(TAG, "unexpected scsi tx data cmd=%02X", scsi->cmd[0]);
  204. scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
  205. scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
  206. return false;
  207. }; break;
  208. }
  209. }
  210. bool scsi_cmd_end(SCSISession* scsi) {
  211. FURI_LOG_T(TAG, "END %02X", scsi->cmd[0]);
  212. uint8_t* cmd = scsi->cmd;
  213. uint8_t len = scsi->cmd_len;
  214. scsi->cmd = NULL;
  215. scsi->cmd_len = 0;
  216. switch(cmd[0]) {
  217. case SCSI_WRITE_10:
  218. return scsi->rx_done;
  219. case SCSI_REQUEST_SENSE:
  220. case SCSI_INQUIRY:
  221. case SCSI_READ_FORMAT_CAPACITIES:
  222. case SCSI_READ_CAPACITY_10:
  223. case SCSI_MODE_SENSE_6:
  224. case SCSI_READ_10:
  225. return scsi->tx_done;
  226. case SCSI_TEST_UNIT_READY: {
  227. FURI_LOG_D(TAG, "SCSI_TEST_UNIT_READY");
  228. return true;
  229. }; break;
  230. case SCSI_PREVENT_MEDIUM_REMOVAL: {
  231. if(len < 6) return false;
  232. bool prevent = cmd[5];
  233. FURI_LOG_D(TAG, "SCSI_PREVENT_MEDIUM_REMOVAL prevent=%d", prevent);
  234. return !prevent;
  235. }; break;
  236. case SCSI_START_STOP_UNIT: {
  237. if(len < 6) return false;
  238. bool eject = (cmd[4] & 2) != 0;
  239. bool start = (cmd[4] & 1) != 0;
  240. FURI_LOG_D(TAG, "SCSI_START_STOP_UNIT eject=%d start=%d", eject, start);
  241. if(eject) {
  242. scsi->fn.eject(scsi->fn.ctx);
  243. }
  244. return true;
  245. }; break;
  246. default: {
  247. FURI_LOG_W(TAG, "unexpected scsi cmd=%02X", cmd[0]);
  248. scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
  249. scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
  250. return false;
  251. }; break;
  252. }
  253. }