BlueSCSI_tape.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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. extern "C" int scsiTapeCommand()
  32. {
  33. image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
  34. int commandHandled = 1;
  35. uint8_t command = scsiDev.cdb[0];
  36. if (command == 0x08)
  37. {
  38. // READ6
  39. bool fixed = scsiDev.cdb[1] & 1;
  40. bool supress_invalid_length = scsiDev.cdb[1] & 2;
  41. if (img.quirks == S2S_CFG_QUIRKS_OMTI)
  42. {
  43. fixed = true;
  44. }
  45. uint32_t length =
  46. (((uint32_t) scsiDev.cdb[2]) << 16) +
  47. (((uint32_t) scsiDev.cdb[3]) << 8) +
  48. scsiDev.cdb[4];
  49. // Host can request either multiple fixed-length blocks, or a single variable length one.
  50. // If host requests variable length block, we return one blocklen sized block.
  51. uint32_t blocklen = scsiDev.target->liveCfg.bytesPerSector;
  52. uint32_t blocks_to_read = length;
  53. if (!fixed)
  54. {
  55. blocks_to_read = 1;
  56. bool underlength = (length > blocklen);
  57. bool overlength = (length < blocklen);
  58. if (overlength || (underlength && !supress_invalid_length))
  59. {
  60. debuglog("Host requested variable block max ", (int)length, " bytes, blocksize is ", (int)blocklen);
  61. scsiDev.status = CHECK_CONDITION;
  62. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  63. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  64. scsiDev.phase = STATUS;
  65. return 1;
  66. }
  67. }
  68. if (blocks_to_read > 0)
  69. {
  70. scsiDiskStartRead(img.tape_pos, blocks_to_read);
  71. img.tape_pos += blocks_to_read;
  72. }
  73. }
  74. else if (command == 0x01)
  75. {
  76. // REWIND
  77. // Set tape position back to 0.
  78. img.tape_pos = 0;
  79. }
  80. else if (command == 0x11)
  81. {
  82. // SPACE
  83. // Set the tape position forward to a specified offset.
  84. uint8_t code = scsiDev.cdb[1] & 7;
  85. uint32_t count =
  86. (((uint32_t) scsiDev.cdb[2]) << 24) +
  87. (((uint32_t) scsiDev.cdb[3]) << 16) +
  88. (((uint32_t) scsiDev.cdb[4]) << 8) +
  89. scsiDev.cdb[5];
  90. if (code == 0)
  91. {
  92. // Blocks.
  93. uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
  94. uint32_t capacity = img.file.size() / bytesPerSector;
  95. if (count < capacity)
  96. {
  97. img.tape_pos = count;
  98. }
  99. else
  100. {
  101. scsiDev.status = CHECK_CONDITION;
  102. scsiDev.target->sense.code = BLANK_CHECK;
  103. scsiDev.target->sense.asc = 0; // END-OF-DATA DETECTED
  104. scsiDev.phase = STATUS;
  105. }
  106. }
  107. else if (code == 1)
  108. {
  109. // Filemarks.
  110. // For now just indicate end of data
  111. scsiDev.status = CHECK_CONDITION;
  112. scsiDev.target->sense.code = BLANK_CHECK;
  113. scsiDev.target->sense.asc = 0; // END-OF-DATA DETECTED
  114. scsiDev.phase = STATUS;
  115. }
  116. else if (code == 3)
  117. {
  118. // End-of-data.
  119. scsiDev.status = CHECK_CONDITION;
  120. scsiDev.target->sense.code = BLANK_CHECK;
  121. scsiDev.target->sense.asc = 0; // END-OF-DATA DETECTED
  122. scsiDev.phase = STATUS;
  123. }
  124. }
  125. else
  126. {
  127. commandHandled = 0;
  128. }
  129. return commandHandled;
  130. }