amt-ider-ws-0.0.1.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. /**
  2. * @description IDER Handling Module
  3. * @author Ylian Saint-Hilaire
  4. * @version v0.0.2
  5. */
  6. // Construct a Intel AMT IDER object
  7. var CreateAmtRemoteIder = function () {
  8. var obj = {};
  9. obj.protocol = 3; // IDER
  10. obj.bytesToAmt = 0;
  11. obj.bytesFromAmt = 0;
  12. obj.rx_timeout = 30000; // Default 30000
  13. obj.tx_timeout = 0; // Default 0
  14. obj.heartbeat = 20000; // Default 20000
  15. obj.version = 1;
  16. obj.acc = "";
  17. obj.inSequence = 0;
  18. obj.outSequence = 0;
  19. obj.iderinfo = null;
  20. obj.enabled = false;
  21. obj.iderStart = 0; // OnReboot = 0, Graceful = 1, Now = 2
  22. obj.floppy = null;
  23. obj.cdrom = null;
  24. obj.floppyReady = false;
  25. obj.cdromReady = false;
  26. obj.pingTimer = null;
  27. // Private method
  28. function debug() { if (urlvars && urlvars['idertrace']) { console.log(...arguments); } }
  29. // Mode Sense
  30. var IDE_ModeSence_LS120Disk_Page_Array = String.fromCharCode(0x00, 0x26, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00);
  31. var IDE_ModeSence_3F_LS120_Array = String.fromCharCode(0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31);
  32. var IDE_ModeSence_FloppyDisk_Page_Array = String.fromCharCode(0x00, 0x26, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x04, 0xB0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00);
  33. var IDE_ModeSence_3F_Floppy_Array = String.fromCharCode(0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1e, 0x04, 0xb0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31);
  34. var IDE_ModeSence_CD_1A_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
  35. //var IDE_ModeSence_CD_1B_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
  36. var IDE_ModeSence_CD_1D_Array = String.fromCharCode(0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
  37. var IDE_ModeSence_CD_2A_Array = String.fromCharCode(0x00, 0x20, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
  38. //var IDE_ModeSence_CD_01_Array = String.fromCharCode(0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00);
  39. var IDE_ModeSence_3F_CD_Array = String.fromCharCode(0x00, 0x28, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
  40. // 0x46 constant data
  41. var IDE_CD_ConfigArrayHeader = String.fromCharCode(0x00, 0x00,0x00, 0x28, 0x00, 0x00, 0x00, 0x08);
  42. var IDE_CD_ConfigArrayProfileList = String.fromCharCode(0x00, 0x00, 0x03, 0x04, 0x00, 0x08, 0x01, 0x00);
  43. var IDE_CD_ConfigArrayCore = String.fromCharCode(0x00, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, 0x02);
  44. var IDE_CD_Morphing = String.fromCharCode(0x00, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00);
  45. var IDE_CD_ConfigArrayRemovable = String.fromCharCode(0x00, 0x03, 0x03, 0x04, 0x29, 0x00, 0x00, 0x02);
  46. var IDE_CD_ConfigArrayRandom = String.fromCharCode(0x00, 0x10, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00);
  47. var IDE_CD_Read = String.fromCharCode(0x00, 0x1E, 0x03, 0x00);
  48. var IDE_CD_PowerManagement = String.fromCharCode(0x01, 0x00, 0x03, 0x00);
  49. var IDE_CD_Timeout = String.fromCharCode(0x01, 0x05, 0x03, 0x00);
  50. // 0x01 constant data
  51. var IDE_ModeSence_FloppyError_Recovery_Array = String.fromCharCode(0x00, 0x12, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00);
  52. var IDE_ModeSence_Ls120Error_Recovery_Array = String.fromCharCode(0x00, 0x12, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00);
  53. var IDE_ModeSence_CDError_Recovery_Array = String.fromCharCode(0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00);
  54. // Private method, called by parent when it change state
  55. obj.xxStateChange = function (newstate) {
  56. debug("IDER-StateChange", newstate);
  57. if (newstate == 0) { obj.Stop(); }
  58. if (newstate == 3) { obj.Start(); }
  59. }
  60. obj.Start = function () {
  61. debug("IDER-Start");
  62. debug(obj.floppy, obj.cdrom);
  63. obj.bytesToAmt = 0;
  64. obj.bytesFromAmt = 0;
  65. obj.inSequence = 0;
  66. obj.outSequence = 0;
  67. // Send first command, OPEN_SESSION
  68. obj.SendCommand(0x40, ShortToStrX(obj.rx_timeout) + ShortToStrX(obj.tx_timeout) + ShortToStrX(obj.heartbeat) + IntToStrX(obj.version));
  69. // Setup the ping timer
  70. //obj.pingTimer = setInterval(function () { obj.SendCommand(0x44); }, 5000);
  71. }
  72. obj.Stop = function () {
  73. debug("IDER-Stop");
  74. if (obj.pingTimer) { clearInterval(obj.pingTimer); obj.pingTimer = null; }
  75. obj.parent.Stop();
  76. }
  77. // Private method
  78. obj.ProcessData = function (data) {
  79. obj.bytesFromAmt += data.length;
  80. obj.acc += data;
  81. debug('IDER-ProcessData', obj.acc.length, rstr2hex(obj.acc));
  82. // Process as many commands as possible
  83. while (true) {
  84. var len = obj.ProcessDataEx();
  85. if (len == 0) return;
  86. if (obj.inSequence != ReadIntX(obj.acc, 4)) { debug('ERROR: Out of sequence', obj.inSequence, ReadIntX(obj.acc, 4)); obj.Stop(); return; }
  87. obj.inSequence++;
  88. obj.acc = obj.acc.substring(len);
  89. }
  90. }
  91. // Private method
  92. obj.SendCommand = function (cmdid, data, completed, dma) {
  93. if (data == null) { data = ''; }
  94. var attributes = ((cmdid > 50) && (completed == true)) ? 2 : 0;
  95. if (dma) { attributes += 1; }
  96. var x = String.fromCharCode(cmdid, 0, 0, attributes) + IntToStrX(obj.outSequence++) + data;
  97. obj.parent.xxSend(x);
  98. obj.bytesToAmt += x.length;
  99. if (cmdid != 0x4B) { debug('IDER-SendData', x.length, rstr2hex(x)); }
  100. }
  101. // CommandEndResponse (SCSI_SENSE)
  102. obj.SendCommandEndResponse = function (error, sense, device, asc, asq) {
  103. if (error) { obj.SendCommand(0x51, String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc5, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0), true); }
  104. else { obj.SendCommand(0x51, String.fromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x87, (sense << 4), 3, 0, 0, 0, device, 0x51, sense, asc, asq), true); }
  105. }
  106. //54 00 00 03 71 00 00 00 00 00 0c 00 b4 00 02 00 00 00 a0 58 85 00 03 00 00 00 a0 50 00 00
  107. //54 00 00 02 26 00 00 00 00 00 0c 00 b5 00 02 00 00 0c a0 58 85 00 03 00 00 00 a0 50 00 00
  108. // DataToHost (SCSI_READ)
  109. obj.SendDataToHost = function (device, completed, data, dma) {
  110. var dmalen = (dma) ? 0 : data.length;
  111. if (completed == true) {
  112. obj.SendCommand(0x54, String.fromCharCode(0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0x85, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0, 0, 0, 0) + data, completed, dma);
  113. } else {
  114. obj.SendCommand(0x54, String.fromCharCode(0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + data, completed, dma);
  115. }
  116. }
  117. // GetDataFromHost (SCSI_CHUNK)
  118. obj.SendGetDataFromHost = function (device, chunksize) {
  119. obj.SendCommand(0x52, String.fromCharCode(0, (chunksize & 0xff), (chunksize >> 8), 0, 0xb5, 0, 0, 0, (chunksize & 0xff), (chunksize >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), false);
  120. }
  121. // DisableEnableFeatures (STATUS_DATA)
  122. // If type is REGS_TOGGLE (3), 4 bytes of data must be provided.
  123. obj.SendDisableEnableFeatures = function (type, data) { if (data == null) { data = ''; } obj.SendCommand(0x48, String.fromCharCode(type) + data); }
  124. // Private method
  125. obj.ProcessDataEx = function () {
  126. if (obj.acc.length < 8) return 0;
  127. // First 8 bytes are the header
  128. // CommandID + 0x000000 + Sequence Number
  129. switch(obj.acc.charCodeAt(0)) {
  130. case 0x41: // OPEN_SESSION
  131. if (obj.acc.length < 30) return 0;
  132. var len = obj.acc.charCodeAt(29);
  133. if (obj.acc.length < (30 + len)) return 0;
  134. obj.iderinfo = {};
  135. obj.iderinfo.major = obj.acc.charCodeAt(8);
  136. obj.iderinfo.minor = obj.acc.charCodeAt(9);
  137. obj.iderinfo.fwmajor = obj.acc.charCodeAt(10);
  138. obj.iderinfo.fwminor = obj.acc.charCodeAt(11);
  139. obj.iderinfo.readbfr = ReadShortX(obj.acc, 16);
  140. obj.iderinfo.writebfr = ReadShortX(obj.acc, 18);
  141. obj.iderinfo.proto = obj.acc.charCodeAt(21);
  142. obj.iderinfo.iana = ReadIntX(obj.acc, 25);
  143. debug(obj.iderinfo);
  144. if (obj.iderinfo.proto != 0) { debug("Unknown proto", obj.iderinfo.proto); obj.Stop(); }
  145. if (obj.iderinfo.readbfr > 8192) { debug("Illegal read buffer size", obj.iderinfo.readbfr); obj.Stop(); }
  146. if (obj.iderinfo.writebfr > 8192) { debug("Illegal write buffer size", obj.iderinfo.writebfr); obj.Stop(); }
  147. if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
  148. else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
  149. else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
  150. //obj.SendDisableEnableFeatures(1); // GetSupportedFeatures
  151. return 30 + len;
  152. case 0x43: // CLOSE
  153. debug('CLOSE');
  154. obj.Stop();
  155. return 8;
  156. case 0x44: // KEEPALIVEPING
  157. obj.SendCommand(0x45); // Send PONG back
  158. return 8;
  159. case 0x45: // KEEPALIVEPONG
  160. debug('PONG');
  161. return 8;
  162. case 0x46: // RESETOCCURED
  163. if (obj.acc.length < 9) return 0;
  164. var resetMask = obj.acc.charCodeAt(8);
  165. if (g_media === null) {
  166. // No operations are pending
  167. obj.SendCommand(0x47); // Send ResetOccuredResponse
  168. debug('RESETOCCURED1', resetMask);
  169. } else {
  170. // Operations are being done, sent the reset once completed.
  171. g_reset = true;
  172. debug('RESETOCCURED2', resetMask);
  173. }
  174. return 9;
  175. case 0x49: // STATUS_DATA - DisableEnableFeaturesReply
  176. if (obj.acc.length < 13) return 0;
  177. var type = obj.acc.charCodeAt(8);
  178. var value = ReadIntX(obj.acc, 9);
  179. debug('STATUS_DATA', type, value);
  180. switch (type)
  181. {
  182. case 1: // REGS_AVAIL
  183. if (value & 1) {
  184. if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
  185. else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
  186. else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
  187. }
  188. break;
  189. case 2: // REGS_STATUS
  190. obj.enabled = (value & 2) ? true : false;
  191. debug("IDER Status: " + obj.enabled);
  192. break;
  193. case 3: // REGS_TOGGLE
  194. if (value != 1) { debug("Register toggle failure"); } //else { obj.SendDisableEnableFeatures(2); }
  195. break;
  196. }
  197. return 13;
  198. case 0x4A: // ERROR OCCURED
  199. if (obj.acc.length < 11) return 0;
  200. debug('IDER: ABORT', obj.acc.charCodeAt(8));
  201. //obj.Stop();
  202. return 11;
  203. case 0x4B: // HEARTBEAT
  204. //debug('HEARTBEAT');
  205. return 8;
  206. case 0x50: // COMMAND WRITTEN
  207. if (obj.acc.length < 28) return 0;
  208. var device = (obj.acc.charCodeAt(14) & 0x10) ? 0xB0 : 0xA0;
  209. var deviceFlags = obj.acc.charCodeAt(14);
  210. var cdb = obj.acc.substring(16, 28);
  211. var featureRegister = obj.acc.charCodeAt(9);
  212. debug('SCSI_CMD', device, rstr2hex(cdb), featureRegister, deviceFlags);
  213. handleSCSI(device, cdb, featureRegister, deviceFlags);
  214. return 28;
  215. case 0x53: // DATA FROM HOST
  216. if (obj.acc.length < 14) return 0;
  217. var len = ReadShortX(obj.acc, 9);
  218. if (obj.acc.length < (14 + len)) return 0;
  219. debug('SCSI_WRITE, len = ' + (14 + len));
  220. obj.SendCommand(0x51, String.fromCharCode(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x70, 0x03, 0x00, 0x00, 0x00, 0xa0, 0x51, 0x07, 0x27, 0x00), true);
  221. return 14 + len;
  222. default:
  223. debug('Unknown IDER command', obj.acc[0]);
  224. obj.Stop();
  225. break;
  226. }
  227. return 0;
  228. }
  229. function handleSCSI(dev, cdb, featureRegister, deviceFlags)
  230. {
  231. var lba;
  232. var len;
  233. switch(cdb.charCodeAt(0))
  234. {
  235. case 0x00: // TEST_UNIT_READY:
  236. debug("SCSI: TEST_UNIT_READY", dev);
  237. switch (dev) {
  238. case 0xA0: // DEV_FLOPPY
  239. if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  240. if (obj.floppyReady == false) { obj.floppyReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
  241. break;
  242. case 0xB0: // DEV_CDDVD
  243. if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  244. if (obj.cdromReady == false) { obj.cdromReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
  245. break;
  246. default:
  247. debug("SCSI Internal error 3", dev);
  248. return -1;
  249. }
  250. obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); // Indicate ready
  251. break;
  252. case 0x08: // READ_6
  253. lba = ((cdb.charCodeAt(1) & 0x1f) << 16) + (cdb.charCodeAt(2) << 8) + cdb.charCodeAt(3);
  254. len = cdb.charCodeAt(4);
  255. if (len == 0) { len = 256; }
  256. debug("SCSI: READ_6", dev, lba, len);
  257. sendDiskData(dev, lba, len, featureRegister);
  258. break;
  259. case 0x0a: // WRITE_6
  260. lba = ((cdb.charCodeAt(1) & 0x1f) << 16) + (cdb.charCodeAt(2) << 8) + cdb.charCodeAt(3);
  261. len = cdb.charCodeAt(4);
  262. if (len == 0) { len = 256; }
  263. debug("SCSI: WRITE_6", dev, lba, len);
  264. obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); // Write is not supported, remote no medium.
  265. return -1;
  266. /*
  267. case 0x15: // MODE_SELECT_6:
  268. debug("SCSI ERROR: MODE_SELECT_6", dev);
  269. obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
  270. return -1;
  271. */
  272. case 0x1a: // MODE_SENSE_6
  273. debug("SCSI: MODE_SENSE_6", dev);
  274. if ((cdb.charCodeAt(2) == 0x3f) && (cdb.charCodeAt(3) == 0x00)) {
  275. var a = 0, b = 0;
  276. switch (dev) {
  277. case 0xA0: // DEV_FLOPPY
  278. if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  279. a = 0x00;
  280. b = 0x80; // Read only = 0x80, Read write = 0x00
  281. break;
  282. case 0xB0: // DEV_CDDVD
  283. if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  284. a = 0x05;
  285. b = 0x80;
  286. break;
  287. default:
  288. debug("SCSI Internal error 6", dev);
  289. return -1;
  290. }
  291. obj.SendDataToHost(dev, true, String.fromCharCode(0, a, b, 0), featureRegister & 1);
  292. return;
  293. }
  294. obj.SendCommandEndResponse(1, 0x05, dev, 0x24, 0x00);
  295. break;
  296. case 0x1b: // START_STOP (Called when you eject the CDROM)
  297. //var immediate = cdb.charCodeAt(1) & 0x01;
  298. //var loej = cdb.charCodeAt(4) & 0x02;
  299. //var start = cdb.charCodeAt(4) & 0x01;
  300. obj.SendCommandEndResponse(1, 0, dev);
  301. break;
  302. case 0x1e: // LOCK_UNLOCK - ALLOW_MEDIUM_REMOVAL
  303. debug("SCSI: ALLOW_MEDIUM_REMOVAL", dev);
  304. if ((dev == 0xA0) && (obj.floppy == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  305. if ((dev == 0xB0) && (obj.cdrom == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  306. obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00);
  307. break;
  308. case 0x23: // READ_FORMAT_CAPACITIES (Floppy only)
  309. debug("SCSI: READ_FORMAT_CAPACITIES", dev);
  310. var buflen = ReadShort(cdb, 7);
  311. var mediaStatus = 0, sectors;
  312. var mcSize = buflen / 8; // Capacity descriptor size is 8
  313. switch (dev) {
  314. case 0xA0: // DEV_FLOPPY
  315. if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
  316. sectors = (obj.floppy.size >> 9) - 1;
  317. break;
  318. case 0xB0: // DEV_CDDVD
  319. if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
  320. sectors = (obj.cdrom.size >> 11) - 1; // Number 2048 byte blocks
  321. break;
  322. default:
  323. debug("SCSI Internal error 4", dev);
  324. return -1;
  325. }
  326. obj.SendDataToHost(dev, true, IntToStr(8) + String.fromCharCode(0x00, 0x00, 0x0b, 0x40, 0x02, 0x00, 0x02, 0x00), featureRegister & 1);
  327. break;
  328. case 0x25: // READ_CAPACITY
  329. debug("SCSI: READ_CAPACITY", dev);
  330. var len = 0;
  331. switch(dev)
  332. {
  333. case 0xA0: // DEV_FLOPPY
  334. if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
  335. if (obj.floppy != null) { len = (obj.floppy.size >> 9) - 1; }
  336. debug('DEV_FLOPPY', len); // Number 512 byte blocks
  337. break;
  338. case 0xB0: // DEV_CDDVD
  339. if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
  340. if (obj.cdrom != null) { len = (obj.cdrom.size >> 11) - 1; } // Number 2048 byte blocks
  341. debug('DEV_CDDVD', len);
  342. break;
  343. default:
  344. debug("SCSI Internal error 4", dev);
  345. return -1;
  346. }
  347. //if (dev == 0xA0) { dev = 0x00; } else { dev = 0x10; } // Weird but seems to work.
  348. debug("SCSI: READ_CAPACITY2", dev, deviceFlags);
  349. obj.SendDataToHost(deviceFlags, true, IntToStr(len) + String.fromCharCode(0, 0, ((dev == 0xB0) ? 0x08 : 0x02), 0), featureRegister & 1);
  350. break;
  351. case 0x28: // READ_10
  352. lba = ReadInt(cdb, 2);
  353. len = ReadShort(cdb, 7);
  354. debug("SCSI: READ_10", dev, lba, len);
  355. sendDiskData(dev, lba, len, featureRegister);
  356. break;
  357. case 0x2a: // WRITE_10 (Floppy only)
  358. case 0x2e: // WRITE_AND_VERIFY (Floppy only)
  359. lba = ReadInt(cdb, 2);
  360. len = ReadShort(cdb, 7);
  361. debug("SCSI: WRITE_10", dev, lba, len);
  362. obj.SendGetDataFromHost(dev, 512 * len); // Floppy writes only, accept sectors of 512 bytes
  363. break;
  364. case 0x43: // READ_TOC (CD Audio only)
  365. var buflen = ReadShort(cdb, 7);
  366. var msf = cdb.charCodeAt(1) & 0x02;
  367. var format = cdb.charCodeAt(2) & 0x07;
  368. if (format == 0) { format = cdb.charCodeAt(9) >> 6; }
  369. debug("SCSI: READ_TOC, dev=" + dev + ", buflen=" + buflen + ", msf=" + msf + ", format=" + format);
  370. switch (dev) {
  371. case 0xA0: // DEV_FLOPPY
  372. obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00); // Not implemented
  373. return -1;
  374. case 0xB0: // DEV_CDDVD
  375. // NOP
  376. break;
  377. default:
  378. debug("SCSI Internal error 9", dev);
  379. return -1;
  380. }
  381. if (format == 1) { obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x0a, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00), featureRegister & 1); }
  382. else if (format == 0) {
  383. if (msf) {
  384. obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x34, 0x13), featureRegister & 1);
  385. } else {
  386. obj.SendDataToHost(dev, true, String.fromCharCode(0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00), featureRegister & 1);
  387. }
  388. }
  389. break;
  390. case 0x46: // GET_CONFIGURATION
  391. var sendall = (cdb.charCodeAt(1) != 2);
  392. var firstcode = ReadShort(cdb, 2);
  393. var buflen = ReadShort(cdb, 7);
  394. debug("SCSI: GET_CONFIGURATION", dev, sendall, firstcode, buflen);
  395. if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
  396. // Set the header
  397. var r = IntToStr(0x0008);
  398. // Add the data
  399. if (firstcode == 0) { r += IDE_CD_ConfigArrayProfileList; }
  400. if ((firstcode == 0x1) || (sendall && (firstcode < 0x1))) { r += IDE_CD_ConfigArrayCore; }
  401. if ((firstcode == 0x2) || (sendall && (firstcode < 0x2))) { r += IDE_CD_Morphing; }
  402. if ((firstcode == 0x3) || (sendall && (firstcode < 0x3))) { r += IDE_CD_ConfigArrayRemovable; }
  403. if ((firstcode == 0x10) || (sendall && (firstcode < 0x10))) { r += IDE_CD_ConfigArrayRandom; }
  404. if ((firstcode == 0x1E) || (sendall && (firstcode < 0x1E))) { r += IDE_CD_Read; }
  405. if ((firstcode == 0x100) || (sendall && (firstcode < 0x100))) { r += IDE_CD_PowerManagement; }
  406. if ((firstcode == 0x105) || (sendall && (firstcode < 0x105))) { r += IDE_CD_Timeout; }
  407. // Set the length
  408. r = IntToStr(r.length) + r;
  409. // Cut the length to buflen if needed
  410. if (r.length > buflen) { r = r.substring(0, buflen); }
  411. obj.SendDataToHost(dev, true, r, featureRegister & 1);
  412. return -1;
  413. case 0x4a: // GET_EV_STATUS - GET_EVENT_STATUS_NOTIFICATION
  414. //var buflen = (cdb.charCodeAt(7) << 8) + cdb.charCodeAt(8);
  415. //if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
  416. debug("SCSI: GET_EVENT_STATUS_NOTIFICATION", dev, cdb.charCodeAt(1), cdb.charCodeAt(4), cdb.charCodeAt(9));
  417. if ((cdb.charCodeAt(1) != 0x01) && (cdb.charCodeAt(4) != 0x10)) { debug('SCSI ERROR'); obj.SendCommandEndResponse(1, 0x05, dev, 0x26, 0x01); break; }
  418. var present = 0x00;
  419. if ((dev == 0xA0) && (obj.floppy != null)) { present = 0x02; }
  420. else if ((dev == 0xB0) && (obj.cdrom != null)) { present = 0x02; }
  421. obj.SendDataToHost(dev, true, String.fromCharCode(0x00, present, 0x80, 0x00), featureRegister & 1); // This is the original version, 4 bytes long
  422. break;
  423. case 0x4c:
  424. obj.SendCommand(0x51, IntToStrX(0) + IntToStrX(0) + IntToStrX(0) + String.fromCharCode(0x87, 0x50, 0x03, 0x00, 0x00, 0x00, 0xb0, 0x51, 0x05, 0x20, 0x00), true);
  425. break;
  426. case 0x51: // READ_DISC_INFO
  427. debug("SCSI READ_DISC_INFO", dev);
  428. obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // Correct
  429. return -1;
  430. case 0x55: // MODE_SELECT_10:
  431. debug("SCSI ERROR: MODE_SELECT_10", dev);
  432. obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
  433. return -1;
  434. case 0x5a: // MODE_SENSE_10
  435. debug("SCSI: MODE_SENSE_10", dev, cdb.charCodeAt(2) & 0x3f);
  436. var buflen = ReadShort(cdb, 7);
  437. //var pc = cdb.charCodeAt(2) & 0xc0;
  438. var r = null;
  439. if (buflen == 0) { obj.SendDataToHost(dev, true, IntToStr(0x003c) + IntToStr(0x0008), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
  440. // 1.44 mb floppy or LS120 (sectorCount == 0x3c300)
  441. var sectorCount = 0;
  442. if (dev == 0xA0) {
  443. if (obj.floppy != null) { sectorCount = (obj.floppy.size >> 9); }
  444. } else {
  445. if (obj.cdrom != null) { sectorCount = (obj.cdrom.size >> 11); }
  446. }
  447. switch (cdb.charCodeAt(2) & 0x3f) {
  448. case 0x01: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyError_Recovery_Array:IDE_ModeSence_Ls120Error_Recovery_Array; } else { r = IDE_ModeSence_CDError_Recovery_Array; } break;
  449. case 0x05: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_FloppyDisk_Page_Array:IDE_ModeSence_LS120Disk_Page_Array; } break;
  450. case 0x3f: if (dev == 0xA0) { r = (sectorCount <= 0xb40)?IDE_ModeSence_3F_Floppy_Array:IDE_ModeSence_3F_LS120_Array; } else { r = IDE_ModeSence_3F_CD_Array; } break;
  451. case 0x1A: if (dev == 0xB0) { r = IDE_ModeSence_CD_1A_Array; } break;
  452. case 0x1D: if (dev == 0xB0) { r = IDE_ModeSence_CD_1D_Array; } break;
  453. case 0x2A: if (dev == 0xB0) { r = IDE_ModeSence_CD_2A_Array; } break;
  454. }
  455. if (r == null) {
  456. obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // TODO: Send proper error!!!
  457. } else {
  458. // Set disk to read only (we don't support write).
  459. //ms_data[3] = ms_data[3] | 0x80;
  460. obj.SendDataToHost(dev, true, r, featureRegister & 1);
  461. }
  462. break;
  463. default: // UNKNOWN COMMAND
  464. debug("IDER: Unknown SCSI command", cdb.charCodeAt(0));
  465. obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00);
  466. return -1;
  467. }
  468. return 0;
  469. }
  470. function sendDiskData(dev, lba, len, featureRegister) {
  471. var media = null;
  472. var mediaBlocks = 0;
  473. if (dev == 0xA0) { media = obj.floppy; if (obj.floppy != null) { mediaBlocks = (obj.floppy.size >> 9); } }
  474. if (dev == 0xB0) { media = obj.cdrom; if (obj.cdrom != null) { mediaBlocks = (obj.cdrom.size >> 11); } }
  475. if ((len < 0) || (lba + len > mediaBlocks)) { obj.SendCommandEndResponse(1, 0x05, dev, 0x21, 0x00); return 0; }
  476. if (len == 0) { obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); return 0; }
  477. if (media != null) {
  478. if (dev == 0xA0) { lba <<= 9; len <<= 9; } else { lba <<= 11; len <<= 11; }
  479. if (g_media !== null) {
  480. console.log('IDERERROR: Read while performing read');
  481. obj.Stop();
  482. } else {
  483. // obj.iderinfo.readbfr // TODO: MaxRead
  484. g_media = media;
  485. g_dev = dev;
  486. g_lba = lba;
  487. g_len = len;
  488. sendDiskDataEx(featureRegister);
  489. }
  490. }
  491. }
  492. var g_reset = false;
  493. var g_media = null;
  494. var g_dev;
  495. var g_lba;
  496. var g_len;
  497. function sendDiskDataEx(featureRegister) {
  498. var len = g_len, lba = g_lba;
  499. if (g_len > obj.iderinfo.readbfr) { len = obj.iderinfo.readbfr; }
  500. g_len -= len;
  501. g_lba += len;
  502. var fr = new FileReader();
  503. fr.onload = function () {
  504. obj.SendDataToHost(g_dev, (g_len == 0), this.result, featureRegister & 1);
  505. if ((g_len > 0) && (g_reset == false)) {
  506. sendDiskDataEx(featureRegister);
  507. } else {
  508. g_media = null;
  509. if (g_reset) { obj.SendCommand(0x47); g_reset = false; } // Send ResetOccuredResponse
  510. }
  511. };
  512. //console.log('Read from ' + lba + ' to ' + (lba + len) + ', total of ' + len);
  513. fr.readAsBinaryString(g_media.slice(lba, lba + len));
  514. }
  515. return obj;
  516. }