BlueSCSI_tape.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /* Tape device emulation
  2. * Will be called by scsi.c from SCSI2SD.
  3. *
  4. * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
  5. * Copyright (c) 2023 Kars de Jong
  6. *
  7. * This file is licensed under the GPL version 3 or any later version. 
  8. * It is derived from cdrom.c in SCSI2SD V6
  9. *
  10. * https://www.gnu.org/licenses/gpl-3.0.html
  11. * ----
  12. * This program is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License as published by
  14. * the Free Software Foundation, either version 3 of the License, or
  15. * (at your option) any later version. 
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20. * GNU General Public License for more details. 
  21. *
  22. * You should have received a copy of the GNU General Public License
  23. * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  24. */
  25. #include "BlueSCSI_disk.h"
  26. #include "BlueSCSI_log.h"
  27. #include "BlueSCSI_config.h"
  28. extern "C" {
  29. #include <scsi.h>
  30. }
  31. static void doSeek(uint32_t lba)
  32. {
  33. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  34. uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
  35. uint32_t capacity = img.file.size() / bytesPerSector;
  36. debuglog("------ Locate tape to LBA ", (int)lba);
  37. if (lba >= capacity)
  38. {
  39. scsiDev.status = CHECK_CONDITION;
  40. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  41. scsiDev.target->sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
  42. scsiDev.phase = STATUS;
  43. }
  44. else
  45. {
  46. delay(10);
  47. img.tape_pos = lba;
  48. scsiDev.status = GOOD;
  49. scsiDev.phase = STATUS;
  50. }
  51. }
  52. extern "C" int scsiTapeCommand()
  53. {
  54. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  55. int commandHandled = 1;
  56. uint8_t command = scsiDev.cdb[0];
  57. if (command == 0x08)
  58. {
  59. // READ6
  60. bool fixed = scsiDev.cdb[1] & 1;
  61. bool supress_invalid_length = scsiDev.cdb[1] & 2;
  62. if (img.quirks == S2S_CFG_QUIRKS_OMTI)
  63. {
  64. fixed = true;
  65. }
  66. uint32_t length =
  67. (((uint32_t) scsiDev.cdb[2]) << 16) +
  68. (((uint32_t) scsiDev.cdb[3]) << 8) +
  69. scsiDev.cdb[4];
  70. // Host can request either multiple fixed-length blocks, or a single variable length one.
  71. // If host requests variable length block, we return one blocklen sized block.
  72. uint32_t blocklen = scsiDev.target->liveCfg.bytesPerSector;
  73. uint32_t blocks_to_read = length;
  74. if (!fixed)
  75. {
  76. blocks_to_read = 1;
  77. bool underlength = (length > blocklen);
  78. bool overlength = (length < blocklen);
  79. if (overlength || (underlength && !supress_invalid_length))
  80. {
  81. debuglog("------ Host requested variable block max ", (int)length, " bytes, blocksize is ", (int)blocklen);
  82. scsiDev.status = CHECK_CONDITION;
  83. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  84. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  85. scsiDev.phase = STATUS;
  86. return 1;
  87. }
  88. }
  89. if (blocks_to_read > 0)
  90. {
  91. scsiDiskStartRead(img.tape_pos, blocks_to_read);
  92. img.tape_pos += blocks_to_read;
  93. }
  94. }
  95. else if (command == 0x0A)
  96. {
  97. // WRITE6
  98. bool fixed = scsiDev.cdb[1] & 1;
  99. if (img.quirks == S2S_CFG_QUIRKS_OMTI)
  100. {
  101. fixed = true;
  102. }
  103. uint32_t length =
  104. (((uint32_t) scsiDev.cdb[2]) << 16) +
  105. (((uint32_t) scsiDev.cdb[3]) << 8) +
  106. scsiDev.cdb[4];
  107. // Host can request either multiple fixed-length blocks, or a single variable length one.
  108. // Only single block length is supported currently.
  109. uint32_t blocklen = scsiDev.target->liveCfg.bytesPerSector;
  110. uint32_t blocks_to_write = length;
  111. if (!fixed)
  112. {
  113. blocks_to_write = 1;
  114. if (length != blocklen)
  115. {
  116. debuglog("------ Host requested variable block ", (int)length, " bytes, blocksize is ", (int)blocklen);
  117. scsiDev.status = CHECK_CONDITION;
  118. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  119. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  120. scsiDev.phase = STATUS;
  121. return 1;
  122. }
  123. }
  124. if (blocks_to_write > 0)
  125. {
  126. scsiDiskStartWrite(img.tape_pos, blocks_to_write);
  127. img.tape_pos += blocks_to_write;
  128. }
  129. }
  130. else if (command == 0x13)
  131. {
  132. // VERIFY
  133. bool fixed = scsiDev.cdb[1] & 1;
  134. if (img.quirks == S2S_CFG_QUIRKS_OMTI)
  135. {
  136. fixed = true;
  137. }
  138. bool byte_compare = scsiDev.cdb[1] & 2;
  139. uint32_t length =
  140. (((uint32_t) scsiDev.cdb[2]) << 16) +
  141. (((uint32_t) scsiDev.cdb[3]) << 8) +
  142. scsiDev.cdb[4];
  143. if (!fixed)
  144. {
  145. length = 1;
  146. }
  147. if (byte_compare)
  148. {
  149. debuglog("------ Verify with byte compare is not implemented");
  150. scsiDev.status = CHECK_CONDITION;
  151. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  152. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  153. scsiDev.phase = STATUS;
  154. }
  155. else
  156. {
  157. // Host requests ECC check, report that it passed.
  158. scsiDev.status = GOOD;
  159. scsiDev.phase = STATUS;
  160. img.tape_pos += length;
  161. }
  162. }
  163. else if (command == 0x19)
  164. {
  165. // Erase
  166. // Just a stub implementation, fake erase to end of tape
  167. img.tape_pos = img.scsiSectors;
  168. }
  169. else if (command == 0x01)
  170. {
  171. // REWIND
  172. // Set tape position back to 0.
  173. img.tape_pos = 0;
  174. }
  175. else if (command == 0x05)
  176. {
  177. // READ BLOCK LIMITS
  178. uint32_t blocklen = scsiDev.target->liveCfg.bytesPerSector;
  179. scsiDev.data[0] = 0; // Reserved
  180. scsiDev.data[1] = (blocklen >> 16) & 0xFF; // Maximum block length (MSB)
  181. scsiDev.data[2] = (blocklen >> 8) & 0xFF;
  182. scsiDev.data[3] = (blocklen >> 0) & 0xFF; // Maximum block length (LSB)
  183. scsiDev.data[4] = (blocklen >> 8) & 0xFF; // Minimum block length (MSB)
  184. scsiDev.data[5] = (blocklen >> 8) & 0xFF; // Minimum block length (MSB)
  185. scsiDev.dataLen = 6;
  186. scsiDev.phase = DATA_IN;
  187. }
  188. else if (command == 0x10)
  189. {
  190. // WRITE FILEMARKS
  191. debuglog("------ Filemarks storage not implemented, reporting ok");
  192. scsiDev.status = GOOD;
  193. scsiDev.phase = STATUS;
  194. }
  195. else if (command == 0x11)
  196. {
  197. // SPACE
  198. // Set the tape position forward to a specified offset.
  199. uint8_t code = scsiDev.cdb[1] & 7;
  200. uint32_t count =
  201. (((uint32_t) scsiDev.cdb[2]) << 24) +
  202. (((uint32_t) scsiDev.cdb[3]) << 16) +
  203. (((uint32_t) scsiDev.cdb[4]) << 8) +
  204. scsiDev.cdb[5];
  205. if (code == 0)
  206. {
  207. // Blocks.
  208. uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
  209. uint32_t capacity = img.file.size() / bytesPerSector;
  210. if (count < capacity)
  211. {
  212. img.tape_pos = count;
  213. }
  214. else
  215. {
  216. scsiDev.status = CHECK_CONDITION;
  217. scsiDev.target->sense.code = BLANK_CHECK;
  218. scsiDev.target->sense.asc = 0; // END-OF-DATA DETECTED
  219. scsiDev.phase = STATUS;
  220. }
  221. }
  222. else if (code == 1)
  223. {
  224. // Filemarks.
  225. // For now just indicate end of data
  226. scsiDev.status = CHECK_CONDITION;
  227. scsiDev.target->sense.code = BLANK_CHECK;
  228. scsiDev.target->sense.asc = 0; // END-OF-DATA DETECTED
  229. scsiDev.phase = STATUS;
  230. }
  231. else if (code == 3)
  232. {
  233. // End-of-data.
  234. scsiDev.status = CHECK_CONDITION;
  235. scsiDev.target->sense.code = BLANK_CHECK;
  236. scsiDev.target->sense.asc = 0; // END-OF-DATA DETECTED
  237. scsiDev.phase = STATUS;
  238. }
  239. }
  240. else if (command == 0x2B)
  241. {
  242. // Seek/Locate 10
  243. uint32_t lba =
  244. (((uint32_t) scsiDev.cdb[3]) << 24) +
  245. (((uint32_t) scsiDev.cdb[4]) << 16) +
  246. (((uint32_t) scsiDev.cdb[5]) << 8) +
  247. scsiDev.cdb[6];
  248. doSeek(lba);
  249. }
  250. else if (command == 0x34)
  251. {
  252. // ReadPosition
  253. uint32_t lba = img.tape_pos;
  254. scsiDev.data[0] = 0x00;
  255. if (lba == 0) scsiDev.data[0] |= 0x80;
  256. if (lba >= img.scsiSectors) scsiDev.data[0] |= 0x40;
  257. scsiDev.data[1] = 0x00;
  258. scsiDev.data[2] = 0x00;
  259. scsiDev.data[3] = 0x00;
  260. scsiDev.data[4] = (lba >> 24) & 0xFF; // Next block on tape
  261. scsiDev.data[5] = (lba >> 16) & 0xFF;
  262. scsiDev.data[6] = (lba >> 8) & 0xFF;
  263. scsiDev.data[7] = (lba >> 0) & 0xFF;
  264. scsiDev.data[8] = (lba >> 24) & 0xFF; // Last block in buffer
  265. scsiDev.data[9] = (lba >> 16) & 0xFF;
  266. scsiDev.data[10] = (lba >> 8) & 0xFF;
  267. scsiDev.data[11] = (lba >> 0) & 0xFF;
  268. scsiDev.data[12] = 0x00;
  269. scsiDev.data[13] = 0x00;
  270. scsiDev.data[14] = 0x00;
  271. scsiDev.data[15] = 0x00;
  272. scsiDev.data[16] = 0x00;
  273. scsiDev.data[17] = 0x00;
  274. scsiDev.data[18] = 0x00;
  275. scsiDev.data[19] = 0x00;
  276. scsiDev.phase = DATA_IN;
  277. scsiDev.dataLen = 20;
  278. }
  279. else
  280. {
  281. commandHandled = 0;
  282. }
  283. return commandHandled;
  284. }