ZuluSCSI_tape.cpp 9.6 KB

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