inquiry.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
  2. // Copyright (C) 2019 Landon Rodgers <g.landon.rodgers@gmail.com>
  3. // Copyright (c) 2023 joshua stein <jcs@jcs.org>
  4. // Copyright (c) 2024 Eric Helgeson <erichelgeson@gmail.com>
  5. //
  6. // This file is part of SCSI2SD.
  7. //
  8. // SCSI2SD is free software: you can redistribute it and/or modify
  9. // it under the terms of the GNU General Public License as published by
  10. // the Free Software Foundation, either version 3 of the License, or
  11. // (at your option) any later version.
  12. //
  13. // SCSI2SD is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU General Public License
  19. // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
  20. //
  21. // This work incorporates work from the following
  22. #include "scsi.h"
  23. #include "config.h"
  24. #include "inquiry.h"
  25. #include "ZuluSCSI_config.h"
  26. #include <string.h>
  27. static uint8_t StandardResponse[] =
  28. {
  29. 0x00, // "Direct-access device". AKA standard hard disk
  30. 0x00, // device type modifier
  31. 0x02, // Complies with ANSI SCSI-2.
  32. 0x01, // Response format is compatible with the old CCS format.
  33. 0x1f, // standard length.
  34. 0, 0, // Reserved
  35. 0x18 // Enable sync and linked commands
  36. };
  37. // Vendor set by config 'c','o','d','e','s','r','c',' ',
  38. // prodId set by config'S','C','S','I','2','S','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',
  39. // Revision set by config'2','.','0','a'
  40. static const uint8_t SupportedVitalPages[] =
  41. {
  42. 0x00, // "Direct-access device". AKA standard hard disk
  43. 0x00, // Page Code
  44. 0x00, // Reserved
  45. 0x04, // Page length
  46. 0x00, // Support "Supported vital product data pages"
  47. 0x80, // Support "Unit serial number page"
  48. 0x81, // Support "Implemented operating definition page"
  49. 0x82 // Support "ASCII Implemented operating definition page"
  50. };
  51. static const uint8_t UnitSerialNumber[] =
  52. {
  53. 0x00, // "Direct-access device". AKA standard hard disk
  54. 0x80, // Page Code
  55. 0x00, // Reserved
  56. 0x10, // Page length
  57. 'c','o','d','e','s','r','c','-','1','2','3','4','5','6','7','8'
  58. };
  59. static const uint8_t ImpOperatingDefinition[] =
  60. {
  61. 0x00, // "Direct-access device". AKA standard hard disk
  62. 0x81, // Page Code
  63. 0x00, // Reserved
  64. 0x03, // Page length
  65. 0x03, // Current: SCSI-2 operating definition
  66. 0x03, // Default: SCSI-2 operating definition
  67. 0x03 // Supported (list): SCSI-2 operating definition.
  68. };
  69. static const uint8_t AscImpOperatingDefinition[] =
  70. {
  71. 0x00, // "Direct-access device". AKA standard hard disk
  72. 0x82, // Page Code
  73. 0x00, // Reserved
  74. 0x07, // Page length
  75. 0x06, // Ascii length
  76. 'S','C','S','I','-','2'
  77. };
  78. static const uint8_t IomegaVendorInquiry[] =
  79. {
  80. '0', '8', '/', '2', '0', '/', '9', '6', 0x0, 0x0, 0x0, 0x0,
  81. 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
  82. 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
  83. 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
  84. '(', 'c', ')', ' ', 'C', 'o', 'p', 'y', 'r', 'i', 'g', 'h', 't', ' ', 'I', 'O',
  85. 'M', 'E', 'G', 'A', ' ', '1', '9', '9', '5', ' '
  86. };
  87. void s2s_scsiInquiry()
  88. {
  89. uint8_t evpd = scsiDev.cdb[1] & 1; // enable vital product data.
  90. uint8_t pageCode = scsiDev.cdb[2];
  91. uint32_t allocationLength = scsiDev.cdb[4];
  92. // SASI standard, X3T9.3_185_RevE states that 0 == 256 bytes
  93. // BUT SCSI 2 standard says 0 == 0.
  94. if (scsiDev.compatMode <= COMPAT_SCSI1) // excludes COMPAT_SCSI2_DISABLED
  95. {
  96. if (allocationLength == 0) allocationLength = 256;
  97. }
  98. if (!evpd)
  99. {
  100. if (pageCode)
  101. {
  102. // error.
  103. scsiDev.status = CHECK_CONDITION;
  104. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  105. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  106. scsiDev.phase = STATUS;
  107. }
  108. else
  109. {
  110. const S2S_TargetCfg* config = scsiDev.target->cfg;
  111. scsiDev.dataLen =
  112. s2s_getStandardInquiry(
  113. config,
  114. scsiDev.data,
  115. sizeof(scsiDev.data));
  116. scsiDev.phase = DATA_IN;
  117. }
  118. }
  119. else if (pageCode == 0x00)
  120. {
  121. memcpy(scsiDev.data, SupportedVitalPages, sizeof(SupportedVitalPages));
  122. scsiDev.dataLen = sizeof(SupportedVitalPages);
  123. scsiDev.phase = DATA_IN;
  124. }
  125. else if (pageCode == 0x80)
  126. {
  127. memcpy(scsiDev.data, UnitSerialNumber, sizeof(UnitSerialNumber));
  128. scsiDev.dataLen = sizeof(UnitSerialNumber);
  129. const S2S_TargetCfg* config = scsiDev.target->cfg;
  130. memcpy(&scsiDev.data[4], config->serial, sizeof(config->serial));
  131. scsiDev.phase = DATA_IN;
  132. }
  133. else if (pageCode == 0x81)
  134. {
  135. memcpy(
  136. scsiDev.data,
  137. ImpOperatingDefinition,
  138. sizeof(ImpOperatingDefinition));
  139. scsiDev.dataLen = sizeof(ImpOperatingDefinition);
  140. scsiDev.phase = DATA_IN;
  141. }
  142. else if (pageCode == 0x82)
  143. {
  144. memcpy(
  145. scsiDev.data,
  146. AscImpOperatingDefinition,
  147. sizeof(AscImpOperatingDefinition));
  148. scsiDev.dataLen = sizeof(AscImpOperatingDefinition);
  149. scsiDev.phase = DATA_IN;
  150. }
  151. else
  152. {
  153. // error.
  154. scsiDev.status = CHECK_CONDITION;
  155. scsiDev.target->sense.code = ILLEGAL_REQUEST;
  156. scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
  157. scsiDev.phase = STATUS;
  158. }
  159. if (scsiDev.phase == DATA_IN)
  160. {
  161. // VAX workaround
  162. if (allocationLength == 255 &&
  163. (scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_VMS))
  164. {
  165. allocationLength = 254;
  166. }
  167. // "real" hard drives send back exactly allocationLenth bytes, padded
  168. // with zeroes. This only seems to happen for Inquiry responses, and not
  169. // other commands that also supply an allocation length such as Mode Sense or
  170. // Request Sense.
  171. // (See below for exception to this rule when 0 allocation length)
  172. if (scsiDev.dataLen < allocationLength)
  173. {
  174. memset(
  175. &scsiDev.data[scsiDev.dataLen],
  176. 0,
  177. allocationLength - scsiDev.dataLen);
  178. }
  179. // Spec 8.2.5 requires us to simply truncate the response if it's
  180. // too big.
  181. scsiDev.dataLen = allocationLength;
  182. // Set the device type as needed.
  183. scsiDev.data[0] = getDeviceTypeQualifier();
  184. switch (scsiDev.target->cfg->deviceType)
  185. {
  186. case S2S_CFG_OPTICAL:
  187. scsiDev.data[1] |= 0x80; // Removable bit.
  188. break;
  189. case S2S_CFG_SEQUENTIAL:
  190. scsiDev.data[1] |= 0x80; // Removable bit.
  191. break;
  192. case S2S_CFG_MO:
  193. scsiDev.data[1] |= 0x80; // Removable bit.
  194. break;
  195. case S2S_CFG_FLOPPY_14MB:
  196. case S2S_CFG_REMOVABLE:
  197. case S2S_CFG_ZIP100:
  198. scsiDev.data[1] |= 0x80; // Removable bit.
  199. break;
  200. case S2S_CFG_NETWORK:
  201. scsiDev.data[2] = 0x01; // Page code.
  202. break;
  203. default:
  204. // Accept defaults for a fixed disk.
  205. break;
  206. }
  207. }
  208. // Set the first byte to indicate LUN presence.
  209. if (scsiDev.lun) // We only support lun 0
  210. {
  211. scsiDev.data[0] = 0x7F;
  212. }
  213. }
  214. uint32_t s2s_getStandardInquiry(
  215. const S2S_TargetCfg* cfg, uint8_t* out, uint32_t maxlen
  216. )
  217. {
  218. uint32_t size = 0;
  219. uint32_t buflen = sizeof(StandardResponse);
  220. if (buflen > maxlen) buflen = maxlen;
  221. memcpy(out, StandardResponse, buflen);
  222. out[1] = cfg->deviceTypeModifier;
  223. if (!(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_SCSI2))
  224. {
  225. out[2] = 1; // Report only SCSI 1 compliance version
  226. }
  227. if (scsiDev.compatMode >= COMPAT_SCSI2)
  228. {
  229. out[3] = 2; // SCSI 2 response format.
  230. }
  231. memcpy(&out[8], cfg->vendor, sizeof(cfg->vendor));
  232. memcpy(&out[16], cfg->prodId, sizeof(cfg->prodId));
  233. memcpy(&out[32], cfg->revision, sizeof(cfg->revision));
  234. size = sizeof(StandardResponse) +
  235. sizeof(cfg->vendor) +
  236. sizeof(cfg->prodId) +
  237. sizeof(cfg->revision);
  238. if(cfg->deviceType == S2S_CFG_ZIP100)
  239. {
  240. memcpy(&out[size], IomegaVendorInquiry, sizeof(IomegaVendorInquiry));
  241. size += sizeof(IomegaVendorInquiry);
  242. out[7] = 0x00; // Disable sync and linked commands
  243. out[4] = 0x75; // 117 length
  244. }
  245. // Iomega already has a vendor inquiry
  246. if(cfg->deviceType != S2S_CFG_NETWORK && cfg->deviceType != S2S_CFG_ZIP100) {
  247. memcpy(&out[size], INQUIRY_NAME, sizeof(INQUIRY_NAME));
  248. size += sizeof(INQUIRY_NAME);
  249. out[size] = TOOLBOX_API;
  250. size += 1;
  251. }
  252. return size;
  253. }
  254. uint8_t getDeviceTypeQualifier()
  255. {
  256. // Set the device type as needed.
  257. switch (scsiDev.target->cfg->deviceType)
  258. {
  259. case S2S_CFG_OPTICAL:
  260. return 0x05;
  261. break;
  262. case S2S_CFG_SEQUENTIAL:
  263. return 0x01;
  264. break;
  265. case S2S_CFG_MO:
  266. return 0x07;
  267. break;
  268. case S2S_CFG_FLOPPY_14MB:
  269. case S2S_CFG_REMOVABLE:
  270. case S2S_CFG_ZIP100:
  271. return 0;
  272. break;
  273. case S2S_CFG_NETWORK:
  274. // processor device
  275. return 0x03;
  276. break;
  277. default:
  278. // Accept defaults for a fixed disk.
  279. return 0;
  280. }
  281. }