EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
amt-ider-module.js
Go to the documentation of this file.
1/*
2Copyright 2020-2021 Intel Corporation
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15
16@description Intel AMT IDER module
17@author Ylian Saint-Hilaire
18@version v0.3.0
19*/
20
21/*jslint node: true */
22/*jshint node: true */
23/*jshint strict:false */
24/*jshint -W097 */
25/*jshint esversion: 6 */
26"use strict";
27
28// Construct a Intel AMT IDER object
29module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
30 const fs = require('fs');
31
32 var obj = {};
33 obj.protocol = 3; // IDER
34 obj.bytesToAmt = 0;
35 obj.bytesFromAmt = 0;
36 obj.rx_timeout = 30000; // Default 30000
37 obj.tx_timeout = 0; // Default 0
38 obj.heartbeat = 20000; // Default 20000
39 obj.version = 1;
40 obj.acc = null;
41 obj.inSequence = 0;
42 obj.outSequence = 0;
43 obj.iderinfo = null;
44 obj.enabled = false;
45 obj.iderStart = 0; // OnReboot = 0, Graceful = 1, Now = 2
46 obj.floppy = null;
47 obj.cdrom = null;
48 obj.floppyReady = false;
49 obj.cdromReady = false;
50 //obj.pingTimer = null;
51 obj.sectorStats = null;
52 obj.debug = false;
53
54 // Private method
55 function debug() { if (obj.debug) { console.log(...arguments); } }
56
57 // Mode Sense
58 var IDE_ModeSence_LS120Disk_Page_Array = Buffer.from([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]);
59 var IDE_ModeSence_3F_LS120_Array = Buffer.from([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]);
60 var IDE_ModeSence_FloppyDisk_Page_Array = Buffer.from([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]);
61 var IDE_ModeSence_3F_Floppy_Array = Buffer.from([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]);
62 var IDE_ModeSence_CD_1A_Array = Buffer.from([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
63 //var IDE_ModeSence_CD_1B_Array = Buffer.from([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
64 var IDE_ModeSence_CD_1D_Array = Buffer.from([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
65 var IDE_ModeSence_CD_2A_Array = Buffer.from([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]);
66 //var IDE_ModeSence_CD_01_Array = Buffer.from([0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00]);
67 var IDE_ModeSence_3F_CD_Array = Buffer.from([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]);
68
69 // 0x46 constant data
70 var IDE_CD_ConfigArrayHeader = Buffer.from([0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08]);
71 var IDE_CD_ConfigArrayProfileList = Buffer.from([0x00, 0x00, 0x03, 0x04, 0x00, 0x08, 0x01, 0x00]);
72 var IDE_CD_ConfigArrayCore = Buffer.from([0x00, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, 0x02]);
73 var IDE_CD_Morphing = Buffer.from([0x00, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00]);
74 var IDE_CD_ConfigArrayRemovable = Buffer.from([0x00, 0x03, 0x03, 0x04, 0x29, 0x00, 0x00, 0x02]);
75 var IDE_CD_ConfigArrayRandom = Buffer.from([0x00, 0x10, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00]);
76 var IDE_CD_Read = Buffer.from([0x00, 0x1E, 0x03, 0x00]);
77 var IDE_CD_PowerManagement = Buffer.from([0x01, 0x00, 0x03, 0x00]);
78 var IDE_CD_Timeout = Buffer.from([0x01, 0x05, 0x03, 0x00]);
79
80 // 0x01 constant data
81 var IDE_ModeSence_FloppyError_Recovery_Array = Buffer.from([0x00, 0x12, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]);
82 var IDE_ModeSence_Ls120Error_Recovery_Array = Buffer.from([0x00, 0x12, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]);
83 var IDE_ModeSence_CDError_Recovery_Array = Buffer.from([0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00]);
84
85 // CD info and performance
86 var RD_CD_DiskInfo = Buffer.from([0x00, 0x20, 0x0e, 0x01, 0x01, 0x01, 0x01, 0x20, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
87 var RD_CD_Performance = Buffer.from([0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00]);
88
89 // Private method, called by parent when it change state
90 obj.xxStateChange = function (newstate) {
91 if (obj.debug) console.log("IDER-StateChange", newstate);
92 if (newstate == 0) { obj.Stop(); }
93 if (newstate == 3) { obj.Start(); }
94 }
95
96 obj.diskSetup = function (floppyPath, cdromPath) {
97 debug(floppyPath, cdromPath);
98
99 // Setup floppy
100 if (floppyPath != null) {
101 try {
102 if (fs.existsSync(floppyPath) == false) { return 1; } // Floppy disk image does not exist
103 var stats = fs.statSync(floppyPath);
104 if ((stats.size % 512) != 0) { return 2; } // Invalid floppy disk image
105 obj.floppy = { size: stats.size, ptr: fs.openSync(floppyPath, 'r') };
106 } catch (ex) { return 3; } // Unable to open floppy disk image
107 }
108
109 // Setup CDROM
110 if (cdromPath != null) {
111 try {
112 if (fs.existsSync(cdromPath) == false) { return 4; } // CDROM disk image does not exist
113 var stats = fs.statSync(cdromPath);
114 if ((stats.size % 512) != 0) { return 5; } // Invalid CDROM disk image
115 obj.cdrom = { size: stats.size, ptr: fs.openSync(cdromPath, 'r') };
116 } catch (ex) { return 6; } // Unable to open CDROM disk image
117 }
118
119 if ((obj.cdrom == null) && (obj.floppy == null)) { return 7; } // Can't do IDER with no disk images
120
121 return 0;
122 }
123
124 obj.Start = function () {
125 if (obj.debug) { console.log('IDER-Start'); console.log(obj.floppy, obj.cdrom); }
126 obj.bytesToAmt = 0;
127 obj.bytesFromAmt = 0;
128 obj.inSequence = 0;
129 obj.outSequence = 0;
130 g_readQueue = [];
131
132 // Send first command, OPEN_SESSION
133 obj.SendCommand(0x40, Buffer.concat([ShortToStrX(obj.rx_timeout), ShortToStrX(obj.tx_timeout), ShortToStrX(obj.heartbeat), IntToStrX(obj.version)]));
134
135 // Send sector stats
136 if (obj.sectorStats) {
137 obj.sectorStats(0, 0, obj.floppy ? (obj.floppy.size >> 9) : 0);
138 obj.sectorStats(0, 1, obj.cdrom ? (obj.cdrom.size >> 11) : 0);
139 }
140
141 // Setup the ping timer
142 //obj.pingTimer = setInterval(function () { obj.SendCommand(0x44); }, 5000);
143 }
144
145 obj.Stop = function () {
146 if (obj.debug) console.log('IDER-Stop');
147 //if (obj.pingTimer) { clearInterval(obj.pingTimer); obj.pingTimer = null; }
148 obj.parent.Stop();
149 }
150
151 // Private method
152 obj.ProcessData = function (data) {
153 data = Buffer.from(data, 'binary');
154 obj.bytesFromAmt += data.length;
155 if (obj.acc == null) { obj.acc = data; } else { obj.acc = Buffer.concat([obj.acc, data]); }
156 if (obj.debug) console.log('IDER-ProcessData', obj.acc.length, obj.acc.toString('hex'));
157
158 // Process as many commands as possible
159 while (obj.acc != null) {
160 var len = obj.ProcessDataEx();
161 if (len == 0) return;
162 if (obj.inSequence != ReadIntX(obj.acc, 4)) {
163 if (obj.debug) console.log('ERROR: Out of sequence', obj.inSequence, ReadIntX(obj.acc, 4));
164 obj.Stop();
165 return;
166 }
167 obj.inSequence++;
168 if (len == obj.acc.length) { obj.acc = null; } else { obj.acc = obj.acc.slice(len); }
169 }
170 }
171
172 // Private method
173 obj.SendCommand = function (cmdid, data, completed, dma) {
174 if (data == null) { data = Buffer.alloc(0); }
175 var attributes = ((cmdid > 50) && (completed == true)) ? 2 : 0;
176 if (dma) { attributes += 1; }
177 var x = Buffer.concat([Buffer.from([cmdid, 0, 0, attributes]), IntToStrX(obj.outSequence++), data]);
178 obj.parent.xxSend(x);
179 obj.bytesToAmt += x.length;
180 //if (cmdid != 0x4B) { console.log('IDER-SendData', x.length, x.toString('hex')); }
181 }
182
183 // CommandEndResponse (SCSI_SENSE)
184 obj.SendCommandEndResponse = function (error, sense, device, asc, asq) {
185 if (error) { obj.SendCommand(0x51, Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc5, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0]), true); }
186 else { obj.SendCommand(0x51, Buffer.from([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); }
187 }
188
189 // DataToHost (SCSI_READ)
190 obj.SendDataToHost = function (device, completed, data, dma) {
191 var dmalen = (dma) ? 0 : data.length;
192 if (completed == true) {
193 obj.SendCommand(0x54, Buffer.concat([Buffer.from([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);
194 } else {
195 obj.SendCommand(0x54, Buffer.concat([Buffer.from([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);
196 }
197 }
198
199 // GetDataFromHost (SCSI_CHUNK)
200 obj.SendGetDataFromHost = function (device, chunksize) {
201 obj.SendCommand(0x52, Buffer.from([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);
202 }
203
204 // DisableEnableFeatures (STATUS_DATA)
205 // If type is REGS_TOGGLE (3), 4 bytes of data must be provided.
206 obj.SendDisableEnableFeatures = function (type, data) { if (data == null) { data = ''; } obj.SendCommand(0x48, Buffer.concat([Buffer.from([type]), data])); }
207
208 // Private method
209 obj.ProcessDataEx = function () {
210 if (obj.acc.length < 8) return 0;
211
212 // First 8 bytes are the header
213 // CommandID + 0x000000 + Sequence Number
214 //console.log('ProcessDataEx', obj.acc[0], obj.acc);
215
216 switch (obj.acc[0]) {
217 case 0x41: // OPEN_SESSION
218 if (obj.acc.length < 30) return 0;
219 var len = obj.acc[29];
220 if (obj.acc.length < (30 + len)) return 0;
221 obj.iderinfo = {};
222 obj.iderinfo.major = obj.acc[8];
223 obj.iderinfo.minor = obj.acc[9];
224 obj.iderinfo.fwmajor = obj.acc[10];
225 obj.iderinfo.fwminor = obj.acc[11];
226 obj.iderinfo.readbfr = ReadShortX(obj.acc, 16);
227 obj.iderinfo.writebfr = ReadShortX(obj.acc, 18);
228 obj.iderinfo.proto = obj.acc[21];
229 obj.iderinfo.iana = ReadIntX(obj.acc, 25);
230 if (obj.debug) console.log(obj.iderinfo);
231
232 if (obj.iderinfo.proto != 0) {
233 if (obj.debug) console.log("Unknown proto", obj.iderinfo.proto);
234 obj.Stop();
235 }
236 if (obj.iderinfo.readbfr > 8192) {
237 if (obj.debug) console.log("Illegal read buffer size", obj.iderinfo.readbfr);
238 obj.Stop();
239 }
240 if (obj.iderinfo.writebfr > 8192) {
241 if (obj.debug) console.log("Illegal write buffer size", obj.iderinfo.writebfr);
242 obj.Stop();
243 }
244
245 if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
246 else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
247 else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
248 //obj.SendDisableEnableFeatures(1); // GetSupportedFeatures
249 return 30 + len;
250 case 0x43: // CLOSE
251 if (obj.debug) console.log('CLOSE');
252 obj.Stop();
253 return 8;
254 case 0x44: // KEEPALIVEPING
255 obj.SendCommand(0x45); // Send PONG back
256 return 8;
257 case 0x45: // KEEPALIVEPONG
258 if (obj.debug) console.log('PONG');
259 return 8;
260 case 0x46: // RESETOCCURED
261 if (obj.acc.length < 9) return 0;
262 var resetMask = obj.acc[8];
263 if (g_media === null) {
264 // No operations are pending
265 obj.SendCommand(0x47); // Send ResetOccuredResponse
266 if (obj.debug) console.log('RESETOCCURED1', resetMask);
267 } else {
268 // Operations are being done, sent the reset once completed.
269 g_reset = true;
270 if (obj.debug) console.log('RESETOCCURED2', resetMask);
271 }
272 return 9;
273 case 0x49: // STATUS_DATA - DisableEnableFeaturesReply
274 if (obj.acc.length < 13) return 0;
275 var type = obj.acc[8];
276 var value = ReadIntX(obj.acc, 9);
277 if (obj.debug) console.log('STATUS_DATA', type, value);
278 switch (type) {
279 case 1: // REGS_AVAIL
280 if (value & 1) {
281 if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
282 else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
283 else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
284 }
285 break;
286 case 2: // REGS_STATUS
287 obj.enabled = (value & 2) ? true : false;
288 if (obj.debug) console.log("IDER Status: " + obj.enabled);
289 break;
290 case 3: // REGS_TOGGLE
291 if (value != 1) {
292 if (obj.debug) console.log("Register toggle failure");
293 } //else { obj.SendDisableEnableFeatures(2); }
294 break;
295 }
296 return 13;
297 case 0x4A: // ERROR OCCURED
298 if (obj.acc.length < 11) return 0;
299 if (obj.debug) console.log('IDER: ABORT', obj.acc[8]);
300 //obj.Stop();
301 return 11;
302 case 0x4B: // HEARTBEAT
303 //console.log('HEARTBEAT');
304 return 8;
305 case 0x50: // COMMAND WRITTEN
306 if (obj.acc.length < 28) return 0;
307 var device = (obj.acc[14] & 0x10) ? 0xB0 : 0xA0;
308 var deviceFlags = obj.acc[14];
309 var cdb = obj.acc.slice(16, 28);
310 var featureRegister = obj.acc[9];
311 if (obj.debug) console.log('SCSI_CMD', device, cdb.toString('hex'), featureRegister, deviceFlags);
312 handleSCSI(device, cdb, featureRegister, deviceFlags);
313 return 28;
314 case 0x53: // DATA FROM HOST
315 if (obj.acc.length < 14) return 0;
316 var len = ReadShortX(obj.acc, 9);
317 if (obj.acc.length < (14 + len)) return 0;
318 if (obj.debug) console.log('SCSI_WRITE, len = ' + (14 + len));
319 obj.SendCommand(0x51, Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x70, 0x03, 0x00, 0x00, 0x00, 0xa0, 0x51, 0x07, 0x27, 0x00]), true);
320 return 14 + len;
321 default:
322 if (obj.debug) console.log('Unknown IDER command', obj.acc[0]);
323 obj.Stop();
324 break;
325 }
326 return 0;
327 }
328
329 function handleSCSI(dev, cdb, featureRegister, deviceFlags) {
330 var lba;
331 var len;
332
333 switch (cdb[0]) {
334 case 0x00: // TEST_UNIT_READY:
335 if (obj.debug) console.log("SCSI: TEST_UNIT_READY", dev);
336 switch (dev) {
337 case 0xA0: // DEV_FLOPPY
338 if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
339 if (obj.floppyReady == false) { obj.floppyReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
340 break;
341 case 0xB0: // DEV_CDDVD
342 if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
343 if (obj.cdromReady == false) { obj.cdromReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
344 break;
345 default:
346 if (obj.debug) console.log("SCSI Internal error 3", dev);
347 return -1;
348 }
349 obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); // Indicate ready
350 break;
351 case 0x08: // READ_6
352 lba = ((cdb[1] & 0x1f) << 16) + (cdb[2] << 8) + cdb[3];
353 len = cdb[4];
354 if (len == 0) { len = 256; }
355 if (obj.debug) console.log("SCSI: READ_6", dev, lba, len);
356 sendDiskData(dev, lba, len, featureRegister);
357 break;
358 case 0x0a: // WRITE_6
359 lba = ((cdb[1] & 0x1f) << 16) + (cdb[2] << 8) + cdb[3];
360 len = cdb[4];
361 if (len == 0) { len = 256; }
362 if (obj.debug) console.log("SCSI: WRITE_6", dev, lba, len);
363 obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); // Write is not supported, remote no medium.
364 return -1;
365 /*
366 case 0x15: // MODE_SELECT_6:
367 console.log("SCSI ERROR: MODE_SELECT_6", dev);
368 obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
369 return -1;
370 */
371 case 0x1a: // MODE_SENSE_6
372 if (obj.debug) console.log("SCSI: MODE_SENSE_6", dev);
373 if ((cdb[2] == 0x3f) && (cdb[3] == 0x00)) {
374 var a = 0, b = 0;
375 switch (dev) {
376 case 0xA0: // DEV_FLOPPY
377 if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
378 a = 0x00;
379 b = 0x80; // Read only = 0x80, Read write = 0x00
380 break;
381 case 0xB0: // DEV_CDDVD
382 if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
383 a = 0x05;
384 b = 0x80;
385 break;
386 default:
387 if (obj.debug) console.log("SCSI Internal error 6", dev);
388 return -1;
389 }
390 obj.SendDataToHost(dev, true, Buffer.from([0, a, b, 0]), featureRegister & 1);
391 return;
392 }
393 obj.SendCommandEndResponse(1, 0x05, dev, 0x24, 0x00);
394 break;
395 case 0x1b: // START_STOP (Called when you eject the CDROM)
396 //var immediate = cdb[1] & 0x01;
397 //var loej = cdb[4] & 0x02;
398 //var start = cdb[4] & 0x01;
399 obj.SendCommandEndResponse(1, 0, dev);
400 break;
401 case 0x1e: // LOCK_UNLOCK - ALLOW_MEDIUM_REMOVAL
402 if (obj.debug) console.log("SCSI: ALLOW_MEDIUM_REMOVAL", dev);
403 if ((dev == 0xA0) && (obj.floppy == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
404 if ((dev == 0xB0) && (obj.cdrom == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
405 obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00);
406 break;
407 case 0x23: // READ_FORMAT_CAPACITIES (Floppy only)
408 if (obj.debug) console.log("SCSI: READ_FORMAT_CAPACITIES", dev);
409 var buflen = ReadShort(cdb, 7);
410 var mediaStatus = 0, sectors;
411 var mcSize = buflen / 8; // Capacity descriptor size is 8
412
413 switch (dev) {
414 case 0xA0: // DEV_FLOPPY
415 if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
416 sectors = (obj.floppy.size >> 9) - 1;
417 break;
418 case 0xB0: // DEV_CDDVD
419 if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
420 sectors = (obj.cdrom.size >> 11) - 1; // Number 2048 byte blocks
421 break;
422 default:
423 if (obj.debug) console.log("SCSI Internal error 4", dev);
424 return -1;
425 }
426
427 obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(8), Buffer.from([0x00, 0x00, 0x0b, 0x40, 0x02, 0x00, 0x02, 0x00])]), featureRegister & 1);
428 break;
429 case 0x25: // READ_CAPACITY
430 if (obj.debug) console.log("SCSI: READ_CAPACITY", dev);
431 var len = 0;
432 switch (dev) {
433 case 0xA0: // DEV_FLOPPY
434 if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
435 if (obj.floppy != null) { len = (obj.floppy.size >> 9) - 1; }
436 if (obj.debug) console.log('DEV_FLOPPY', len); // Number 512 byte blocks
437 break;
438 case 0xB0: // DEV_CDDVD
439 if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
440 if (obj.cdrom != null) { len = (obj.cdrom.size >> 11) - 1; } // Number 2048 byte blocks
441 if (obj.debug) console.log('DEV_CDDVD', len);
442 break;
443 default:
444 if (obj.debug) console.log("SCSI Internal error 4", dev);
445 return -1;
446 }
447 //if (dev == 0xA0) { dev = 0x00; } else { dev = 0x10; } // Weird but seems to work.
448 if (obj.debug) console.log("SCSI: READ_CAPACITY2", dev, deviceFlags);
449 obj.SendDataToHost(deviceFlags, true, Buffer.concat([IntToStr(len), Buffer.from([0, 0, ((dev == 0xB0) ? 0x08 : 0x02), 0])]), featureRegister & 1);
450 break;
451 case 0x28: // READ_10
452 lba = ReadInt(cdb, 2);
453 len = ReadShort(cdb, 7);
454 if (obj.debug) console.log("SCSI: READ_10", dev, lba, len);
455 sendDiskData(dev, lba, len, featureRegister);
456 break;
457 case 0x2a: // WRITE_10 (Floppy only)
458 case 0x2e: // WRITE_AND_VERIFY (Floppy only)
459 lba = ReadInt(cdb, 2);
460 len = ReadShort(cdb, 7);
461 if (obj.debug) console.log("SCSI: WRITE_10", dev, lba, len);
462 obj.SendGetDataFromHost(dev, 512 * len); // Floppy writes only, accept sectors of 512 bytes
463 break;
464 case 0x43: // READ_TOC (CD Audio only)
465 var buflen = ReadShort(cdb, 7);
466 var msf = cdb[1] & 0x02;
467 var format = cdb[2] & 0x07;
468 if (format == 0) { format = cdb[9] >> 6; }
469 if (obj.debug) console.log("SCSI: READ_TOC, dev=" + dev + ", buflen=" + buflen + ", msf=" + msf + ", format=" + format);
470
471 switch (dev) {
472 case 0xA0: // DEV_FLOPPY
473 obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00); // Not implemented
474 return -1;
475 case 0xB0: // DEV_CDDVD
476 // NOP
477 break;
478 default:
479 if (obj.debug) console.log("SCSI Internal error 9", dev);
480 return -1;
481 }
482
483 if (format == 1) { obj.SendDataToHost(dev, true, Buffer.from([0x00, 0x0a, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]), featureRegister & 1); }
484 else if (format == 0) {
485 if (msf) {
486 obj.SendDataToHost(dev, true, Buffer.from([0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x34, 0x13]), featureRegister & 1);
487 } else {
488 obj.SendDataToHost(dev, true, Buffer.from([0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00]), featureRegister & 1);
489 }
490 }
491 break;
492 case 0x46: // GET_CONFIGURATION
493 var sendall = (cdb[1] != 2);
494 var firstcode = ReadShort(cdb, 2);
495 var buflen = ReadShort(cdb, 7);
496
497 if (obj.debug) console.log("SCSI: GET_CONFIGURATION", dev, sendall, firstcode, buflen);
498 if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
499
500 // Set the header
501 var r = null;
502
503 // Add the data
504 if (firstcode == 0) { r = IDE_CD_ConfigArrayProfileList; }
505 if ((firstcode == 0x1) || (sendall && (firstcode < 0x1))) { r = IDE_CD_ConfigArrayCore; }
506 if ((firstcode == 0x2) || (sendall && (firstcode < 0x2))) { r = IDE_CD_Morphing; }
507 if ((firstcode == 0x3) || (sendall && (firstcode < 0x3))) { r = IDE_CD_ConfigArrayRemovable; }
508 if ((firstcode == 0x10) || (sendall && (firstcode < 0x10))) { r = IDE_CD_ConfigArrayRandom; }
509 if ((firstcode == 0x1E) || (sendall && (firstcode < 0x1E))) { r = IDE_CD_Read; }
510 if ((firstcode == 0x100) || (sendall && (firstcode < 0x100))) { r = IDE_CD_PowerManagement; }
511 if ((firstcode == 0x105) || (sendall && (firstcode < 0x105))) { r = IDE_CD_Timeout; }
512
513 if (r == null) {
514 //console.log('NOT RIGHT', sendall, firstcode, cdb[2], cdb[3]);
515 //process.exit(0);
516 r = Buffer.concat([IntToStr(0x0008), IntToStr(4)]);
517 } else {
518 r = Buffer.concat([IntToStr(0x0008), IntToStr(r.length + 4), r]);
519 }
520
521 // Cut the length to buflen if needed
522 if (r.length > buflen) { r = r.slice(0, buflen); }
523
524 obj.SendDataToHost(dev, true, r, featureRegister & 1);
525 return -1;
526 case 0x4a: // GET_EV_STATUS - GET_EVENT_STATUS_NOTIFICATION
527 //var buflen = (cdb[7] << 8) + cdb[8];
528 //if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
529 if (obj.debug) console.log("SCSI: GET_EVENT_STATUS_NOTIFICATION", dev, cdb[1], cdb[4], cdb[9]);
530 if ((cdb[1] != 0x01) && (cdb[4] != 0x10)) {
531 if (obj.debug) console.log('SCSI ERROR');
532 obj.SendCommandEndResponse(1, 0x05, dev, 0x26, 0x01);
533 break;
534 }
535 var present = 0x00;
536 if ((dev == 0xA0) && (obj.floppy != null)) { present = 0x02; }
537 else if ((dev == 0xB0) && (obj.cdrom != null)) { present = 0x02; }
538 obj.SendDataToHost(dev, true, Buffer.from([0x00, present, 0x80, 0x00]), featureRegister & 1); // This is the original version, 4 bytes long
539 break;
540 case 0x4c:
541 obj.SendCommand(0x51, Buffer.concat([IntToStrX(0), IntToStrX(0), IntToStrX(0), Buffer.from([0x87, 0x50, 0x03, 0x00, 0x00, 0x00, 0xb0, 0x51, 0x05, 0x20, 0x00])]), true);
542 break;
543 case 0x51: // READ_DISC_INFO
544 if (obj.debug) console.log("SCSI READ_DISC_INFO", dev);
545 obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // Correct
546 return -1;
547 case 0x55: // MODE_SELECT_10:
548 if (obj.debug) console.log("SCSI ERROR: MODE_SELECT_10", dev);
549 obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
550 return -1;
551 case 0x5a: // MODE_SENSE_10
552 if (obj.debug) console.log("SCSI: MODE_SENSE_10", dev, cdb[2] & 0x3f);
553 var buflen = ReadShort(cdb, 7);
554 //var pc = cdb[2] & 0xc0;
555 var r = null;
556
557 if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
558
559 // 1.44 mb floppy or LS120 (sectorCount == 0x3c300)
560 var sectorCount = 0;
561 if (dev == 0xA0) {
562 if (obj.floppy != null) { sectorCount = (obj.floppy.size >> 9); }
563 } else {
564 if (obj.cdrom != null) { sectorCount = (obj.cdrom.size >> 11); }
565 }
566
567 switch (cdb[2] & 0x3f) {
568 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;
569 case 0x05: if (dev == 0xA0) { r = (sectorCount <= 0xb40) ? IDE_ModeSence_FloppyDisk_Page_Array : IDE_ModeSence_LS120Disk_Page_Array; } break;
570 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;
571 case 0x1A: if (dev == 0xB0) { r = IDE_ModeSence_CD_1A_Array; } break;
572 case 0x1D: if (dev == 0xB0) { r = IDE_ModeSence_CD_1D_Array; } break;
573 case 0x2A: if (dev == 0xB0) { r = IDE_ModeSence_CD_2A_Array; } break;
574 }
575
576 if (r == null) {
577 obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // TODO: Send proper error!!!
578 } else {
579 // Set disk to read only (we don't support write).
580 //ms_data[3] = ms_data[3] | 0x80;
581 obj.SendDataToHost(dev, true, r, featureRegister & 1);
582 }
583 break;
584 case 0x51: // READ_DISK_INFORMATION
585 obj.SendDataToHost(dev, true, RD_CD_DiskInfo, featureRegister & 1);
586 break;
587 case 0xAC: // GET_PERFORMANCE
588 obj.SendDataToHost(dev, true, RD_CD_Performance, featureRegister & 1);
589 break;
590 default: // UNKNOWN COMMAND
591 if (obj.debug) console.log("IDER: Unknown SCSI command", cdb[0]);
592 obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00);
593 return -1;
594 }
595 return 0;
596 }
597
598 function sendDiskData(dev, lba, len, featureRegister) {
599 var media = null;
600 var mediaBlocks = 0;
601 if (dev == 0xA0) { media = obj.floppy; if (obj.floppy != null) { mediaBlocks = (obj.floppy.size >> 9); } }
602 if (dev == 0xB0) { media = obj.cdrom; if (obj.cdrom != null) { mediaBlocks = (obj.cdrom.size >> 11); } }
603 if ((len < 0) || (lba + len > mediaBlocks)) { obj.SendCommandEndResponse(1, 0x05, dev, 0x21, 0x00); return 0; }
604 if (len == 0) { obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); return 0; }
605 if (media != null) {
606 // Send sector stats
607 if (obj.sectorStats) { obj.sectorStats(1, (dev == 0xA0) ? 0 : 1, mediaBlocks, lba, len); }
608 if (dev == 0xA0) { lba <<= 9; len <<= 9; } else { lba <<= 11; len <<= 11; }
609 if (g_media !== null) {
610 // Queue read operation
611 g_readQueue.push({ media: media, dev: dev, lba: lba, len: len, fr: featureRegister });
612 } else {
613 // obj.iderinfo.readbfr // TODO: MaxRead
614 g_media = media;
615 g_dev = dev;
616 g_lba = lba;
617 g_len = len;
618 sendDiskDataEx(featureRegister);
619 }
620 }
621 }
622
623 var g_readQueue = [], g_dev, g_lba, g_len, g_media = null, g_reset = false;
624 function sendDiskDataEx(featureRegister) {
625 var len = g_len, lba = g_lba;
626 if (g_len > obj.iderinfo.readbfr) { len = obj.iderinfo.readbfr; }
627 g_len -= len;
628 g_lba += len;
629
630 var buffer = Buffer.alloc(len);
631 fs.read(g_media.ptr, buffer, 0, len, lba, function (error, bytesRead, buffer) {
632 obj.SendDataToHost(g_dev, (g_len == 0), buffer, featureRegister & 1);
633 if ((g_len > 0) && (g_reset == false)) {
634 sendDiskDataEx(featureRegister);
635 } else {
636 g_media = null;
637 if (g_reset) { obj.SendCommand(0x47); g_readQueue = []; g_reset = false; } // Send ResetOccuredResponse
638 else if (g_readQueue.length > 0) { var op = g_readQueue.shift(); g_media = op.media; g_dev = op.dev; g_lba = op.lba; g_len = op.len; sendDiskDataEx(op.fr); } // Un-queue read operation
639 }
640 });
641 }
642
643 return obj;
644}
645
646function ShortToStr(v) { return Buffer.from([(v >> 8) & 0xFF, v & 0xFF]); }
647function ShortToStrX(v) { return Buffer.from([v & 0xFF, (v >> 8) & 0xFF]); }
648function IntToStr(v) { return Buffer.from([(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF]); }
649function IntToStrX(v) { return Buffer.from([v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF]); }
650function ReadShort(v, p) { return (v[p] << 8) + v[p + 1]; }
651function ReadShortX(v, p) { return (v[p + 1] << 8) + v[p]; }
652function ReadInt(v, p) { return (v[p] * 0x1000000) + (v[p + 1] << 16) + (v[p + 2] << 8) + v[p + 3]; } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
653function ReadSInt(v, p) { return (v[p] << 24) + (v[p + 1] << 16) + (v[p + 2] << 8) + v[p + 3]; }
654function ReadIntX(v, p) { return (v[p + 3] * 0x1000000) + (v[p + 2] << 16) + (v[p + 1] << 8) + v[p]; }