BlueSCSI_mode.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /**
  2. * Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
  3. * Copyright (C) 2014 Doug Brown <doug@downtowndougbrown.com>
  4. * Copyright (C) 2019 Landon Rodgers <g.landon.rodgers@gmail.com>
  5. * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  6. *
  7. * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  8. *
  9. * https://www.gnu.org/licenses/gpl-3.0.html
  10. * ----
  11. * This program is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation, either version 3 of the License, or
  14. * (at your option) any later version. 
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. * GNU General Public License for more details. 
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  23. **/
  24. #include <stdint.h>
  25. #include <string.h>
  26. #ifdef ENABLE_AUDIO_OUTPUT
  27. #include "BlueSCSI_audio.h"
  28. #endif
  29. #include "BlueSCSI_cdrom.h"
  30. #include "BlueSCSI_log.h"
  31. extern "C" {
  32. #include "BlueSCSI_mode.h"
  33. }
  34. static const uint8_t CDROMCDParametersPage[] =
  35. {
  36. 0x0D, // page code
  37. 0x06, // page length
  38. 0x00, // reserved
  39. 0x0D, // reserved, inactivity time 8 min
  40. 0x00, 0x3C, // 60 seconds per MSF M unit
  41. 0x00, 0x4B // 75 frames per MSF S unit
  42. };
  43. #ifdef ENABLE_AUDIO_OUTPUT
  44. static const uint8_t CDROMAudioControlParametersPage[] =
  45. {
  46. 0x0E, // page code
  47. 0x0E, // page length
  48. 0x04, // 'Immed' bit set, 'SOTC' bit not set
  49. 0x00, // reserved
  50. 0x00, // reserved
  51. 0x00, // reserved was // 0x80, // 1 LBAs/sec multip
  52. 0x00, 0x00, // obsolete was // 75 LBAs/sec
  53. 0x01, 0xFF, // output port 0 active, max volume
  54. 0x02, 0xFF, // output port 1 active, max volume
  55. 0x00, 0x00, // output port 2 inactive
  56. 0x00, 0x00 // output port 3 inactive
  57. };
  58. #endif
  59. // 0x2A CD-ROM Capabilities and Mechanical Status Page
  60. // This seems to have been standardized in MMC-1 but was de-facto present in
  61. // earlier SCSI-2 drives. The below mirrors one of those earlier SCSI-2
  62. // implementations, being is slightly shorter than the spec format but
  63. // otherwise returning identical information within the same bytes.
  64. static const uint8_t CDROMCapabilitiesPage[] =
  65. {
  66. 0x2A, // page code
  67. 0x0E, // page length
  68. 0x00, // CD-R/RW reading not supported
  69. 0x00, // CD-R/RW writing not supported
  70. #ifdef ENABLE_AUDIO_OUTPUT
  71. 0x01, // byte 4: audio play supported
  72. #else
  73. 0x00, // byte 4: no features supported
  74. #endif
  75. 0x03, // byte 5: CD-DA ok with accurate streaming, no other features
  76. 0x28, // byte 6: tray loader, ejection ok, but prevent/allow not supported
  77. #ifdef ENABLE_AUDIO_OUTPUT
  78. 0x03, // byte 7: separate channel mute and volumes
  79. #else
  80. 0x00, // byte 7: no features supported
  81. #endif
  82. 0x05, 0x62, // max read speed, state (8X, ~1378KB/s)
  83. #ifdef ENABLE_AUDIO_OUTPUT
  84. 0x01, 0x00, // 256 volume levels supported
  85. #else
  86. 0x00, 0x00, // no volume levels supported
  87. #endif
  88. 0x00, 0x40, // read buffer (64KB)
  89. 0x05, 0x62, // current read speed, matching max speed
  90. };
  91. static void pageIn(int pc, int dataIdx, const uint8_t* pageData, int pageLen)
  92. {
  93. memcpy(&scsiDev.data[dataIdx], pageData, pageLen);
  94. if (pc == 0x01) // Mask out (un)changable values
  95. {
  96. memset(&scsiDev.data[dataIdx+2], 0, pageLen - 2);
  97. }
  98. }
  99. extern "C"
  100. int modeSenseCDDevicePage(int pc, int idx, int pageCode, int* pageFound)
  101. {
  102. if ((scsiDev.target->cfg->deviceType == S2S_CFG_OPTICAL)
  103. && (pageCode == 0x0D || pageCode == 0x3F))
  104. {
  105. *pageFound = 1;
  106. pageIn(
  107. pc,
  108. idx,
  109. CDROMCDParametersPage,
  110. sizeof(CDROMCDParametersPage));
  111. return sizeof(CDROMCDParametersPage);
  112. }
  113. else
  114. {
  115. return 0;
  116. }
  117. }
  118. extern "C"
  119. int modeSenseCDAudioControlPage(int pc, int idx, int pageCode, int* pageFound)
  120. {
  121. #ifdef ENABLE_AUDIO_OUTPUT
  122. if ((scsiDev.target->cfg->deviceType == S2S_CFG_OPTICAL)
  123. && (pageCode == 0x0E || pageCode == 0x3F))
  124. {
  125. *pageFound = 1;
  126. pageIn(
  127. pc,
  128. idx,
  129. CDROMAudioControlParametersPage,
  130. sizeof(CDROMAudioControlParametersPage));
  131. if (pc == 0x00)
  132. {
  133. // report current port assignments and volume level
  134. uint16_t chn = audio_get_channel(scsiDev.target->targetId);
  135. uint16_t vol = audio_get_volume(scsiDev.target->targetId);
  136. scsiDev.data[idx+8] = chn & 0xFF;
  137. scsiDev.data[idx+9] = vol & 0xFF;
  138. scsiDev.data[idx+10] = chn >> 8;
  139. scsiDev.data[idx+11] = vol >> 8;
  140. }
  141. else if (pc == 0x01)
  142. {
  143. // report bits that can be set
  144. scsiDev.data[idx+8] = 0xFF;
  145. scsiDev.data[idx+9] = 0xFF;
  146. scsiDev.data[idx+10] = 0xFF;
  147. scsiDev.data[idx+11] = 0xFF;
  148. }
  149. else
  150. {
  151. // report defaults for 0x02
  152. // also report same for 0x03, though we are actually supposed
  153. // to terminate with CHECK CONDITION and SAVING PARAMETERS NOT SUPPORTED
  154. scsiDev.data[idx+8] = AUDIO_CHANNEL_ENABLE_MASK & 0xFF;
  155. scsiDev.data[idx+9] = DEFAULT_VOLUME_LEVEL & 0xFF;
  156. scsiDev.data[idx+10] = AUDIO_CHANNEL_ENABLE_MASK >> 8;
  157. scsiDev.data[idx+11] = DEFAULT_VOLUME_LEVEL >> 8;
  158. }
  159. return sizeof(CDROMAudioControlParametersPage);
  160. }
  161. else
  162. {
  163. return 0;
  164. }
  165. #else
  166. return 0;
  167. #endif
  168. }
  169. int modeSenseCDCapabilitiesPage(int pc, int idx, int pageCode, int* pageFound)
  170. {
  171. if ((scsiDev.target->cfg->deviceType == S2S_CFG_OPTICAL)
  172. && (pageCode == 0x2A || pageCode == 0x3F))
  173. {
  174. *pageFound = 1;
  175. pageIn(
  176. pc,
  177. idx,
  178. CDROMCapabilitiesPage,
  179. sizeof(CDROMCapabilitiesPage));
  180. return sizeof(CDROMCapabilitiesPage);
  181. }
  182. else
  183. {
  184. return 0;
  185. }
  186. }
  187. extern "C"
  188. int modeSelectCDAudioControlPage(int pageLen, int idx)
  189. {
  190. #ifdef ENABLE_AUDIO_OUTPUT
  191. if (scsiDev.target->cfg->deviceType == S2S_CFG_OPTICAL)
  192. {
  193. if (pageLen != 0x0E) return 0;
  194. uint16_t chn = (scsiDev.data[idx+10] << 8) + scsiDev.data[idx+8];
  195. uint16_t vol = (scsiDev.data[idx+11] << 8) + scsiDev.data[idx+9];
  196. dbgmsg("------ CD audio control page channels (", chn, "), volume (", vol, ")");
  197. audio_set_channel(scsiDev.target->targetId, chn);
  198. audio_set_volume(scsiDev.target->targetId, vol);
  199. return 1;
  200. }
  201. else
  202. {
  203. return 0;
  204. }
  205. #else
  206. return 0;
  207. #endif
  208. }