|
|
1.1 root 1: /* SCSI Bus and Disk emulation */
2: #include "main.h"
3: #include "ioMem.h"
4: #include "ioMemTables.h"
5: #include "configuration.h"
6: #include "sysdeps.h"
7: #include "m68000.h"
8: #include "statusbar.h"
9: #include "scsi.h"
10: #include "file.h"
11:
12: #define LOG_SCSI_LEVEL LOG_DEBUG /* Print debugging messages */
13:
14:
15: #define COMMAND_ReadInt16(a, i) (((unsigned) a[i] << 8) | a[i + 1])
16: #define COMMAND_ReadInt24(a, i) (((unsigned) a[i] << 16) | ((unsigned) a[i + 1] << 8) | a[i + 2])
17: #define COMMAND_ReadInt32(a, i) (((unsigned) a[i] << 24) | ((unsigned) a[i + 1] << 16) | ((unsigned) a[i + 2] << 8) | a[i + 3])
18:
19:
20: #define BLOCKSIZE 512
21:
22: #define LUN_DISK 0 // for now only LUN 0 is valid for our phys drives
23:
24: /* Status Codes */
25: #define STAT_GOOD 0x00
26: #define STAT_CHECK_COND 0x02
27: #define STAT_COND_MET 0x04
28: #define STAT_BUSY 0x08
29: #define STAT_INTERMEDIATE 0x10
30: #define STAT_INTER_COND_MET 0x14
31: #define STAT_RESERV_CONFL 0x18
32:
33: /* Messages */
34: #define MSG_COMPLETE 0x00
35: #define MSG_SAVE_PTRS 0x02
36: #define MSG_RESTORE_PTRS 0x03
37: #define MSG_DISCONNECT 0x04
38: #define MSG_INITIATOR_ERR 0x05
39: #define MSG_ABORT 0x06
40: #define MSG_MSG_REJECT 0x07
41: #define MSG_NOP 0x08
42: #define MSG_PARITY_ERR 0x09
43: #define MSG_LINK_CMD_CMPLT 0x0A
44: #define MSG_LNKCMDCMPLTFLAG 0x0B
45: #define MSG_DEVICE_RESET 0x0C
46:
47: #define MSG_IDENTIFY_MASK 0x80
48: #define MSG_ID_DISCONN 0x40
49: #define MSG_LUNMASK 0x07
50:
51: /* Sense Keys */
52: #define SK_NOSENSE 0x00
53: #define SK_RECOVERED 0x01
54: #define SK_NOTREADY 0x02
55: #define SK_MEDIA 0x03
56: #define SK_HARDWARE 0x04
57: #define SK_ILLEGAL_REQ 0x05
58: #define SK_UNIT_ATN 0x06
59: #define SK_DATAPROTECT 0x07
60: #define SK_ABORTED_CMD 0x0B
61: #define SK_VOL_OVERFLOW 0x0D
62: #define SK_MISCOMPARE 0x0E
63:
64: /* Additional Sense Codes */
65: #define SC_NO_ERROR 0x00 // 0
66: #define SC_NO_SECTOR 0x01 // 4
67: #define SC_WRITE_FAULT 0x03 // 5
68: #define SC_NOT_READY 0x04 // 2
69: #define SC_INVALID_CMD 0x20 // 5
70: #define SC_INVALID_LBA 0x21 // 5
71: #define SC_INVALID_CDB 0x24 // 5
72: #define SC_INVALID_LUN 0x25 // 5
73: #define SC_WRITE_PROTECT 0x27 // 7
74:
75:
76: /* SCSI Commands */
77:
78: /* The following are multi-sector transfers with seek implied */
79: #define CMD_VERIFY_TRACK 0x05 /* Verify track */
80: #define CMD_FORMAT_TRACK 0x06 /* Format track */
81: #define CMD_READ_SECTOR 0x08 /* Read sector */
82: #define CMD_READ_SECTOR1 0x28 /* Read sector (class 1) */
83: #define CMD_WRITE_SECTOR 0x0A /* Write sector */
84: #define CMD_WRITE_SECTOR1 0x2A /* Write sector (class 1) */
85:
86: /* Other codes */
87: #define CMD_TEST_UNIT_RDY 0x00 /* Test unit ready */
88: #define CMD_FORMAT_DRIVE 0x04 /* Format the whole drive */
89: #define CMD_SEEK 0x0B /* Seek */
90: #define CMD_CORRECTION 0x0D /* Correction */
91: #define CMD_INQUIRY 0x12 /* Inquiry */
92: #define CMD_MODESELECT 0x15 /* Mode select */
93: #define CMD_MODESENSE 0x1A /* Mode sense */
94: #define CMD_REQ_SENSE 0x03 /* Request sense */
95: #define CMD_SHIP 0x1B /* Ship drive */
96: #define CMD_READ_CAPACITY1 0x25 /* Read capacity (class 1) */
97:
98: void SCSI_Emulate_Command(Uint8 *cdb);
99:
100: void SCSI_Inquiry(Uint8 *cdb);
101: void SCSI_StartStop(Uint8 *cdb);
102: void SCSI_TestUnitReady(Uint8 *cdb);
103: void SCSI_ReadCapacity(Uint8 *cdb);
104: void SCSI_ReadSector(Uint8 *cdb);
105: void SCSI_WriteSector(Uint8 *cdb);
106: void SCSI_RequestSense(Uint8 *cdb);
107: void SCSI_ModeSense(Uint8 *cdb);
108: void SCSI_FormatDrive(Uint8 *cdb);
109:
110:
111: /* Helpers */
112: int SCSI_GetCommandLength(Uint8 opcode);
113: int SCSI_GetTransferLength(Uint8 opcode, Uint8 *cdb);
114: Uint64 SCSI_GetOffset(Uint8 opcode, Uint8 *cdb);
115: int SCSI_GetCount(Uint8 opcode, Uint8 *cdb);
116:
117: void scsi_read_sector(void);
118: void scsi_write_sector(void);
119:
120:
121: /* SCSI disk */
122: struct {
123: SCSI_DEVTYPE devtype;
124: FILE* dsk;
125: Uint64 size;
126: bool readonly;
127: Uint8 lun;
128: Uint8 status;
129: Uint8 message;
130:
131: struct {
132: Uint8 key;
133: Uint8 code;
134: bool valid;
135: Uint32 info;
136: } sense;
137:
138: Uint32 lba;
139: Uint32 blockcounter;
140: Uint32 lastlba;
141:
142: Uint8** shadow;
143: } SCSIdisk[ESP_MAX_DEVS];
144:
145:
146: /* Mode Pages */
147: #define MODEPAGE_MAX_SIZE 24
148:
149: typedef struct {
150: Uint8 current[MODEPAGE_MAX_SIZE];
151: Uint8 changeable[MODEPAGE_MAX_SIZE];
152: Uint8 modepage[MODEPAGE_MAX_SIZE]; // default values
153: Uint8 saved[MODEPAGE_MAX_SIZE];
154: Uint8 pagesize;
155: } MODEPAGE;
156:
157: MODEPAGE SCSI_GetModePage(Uint8 pagecode);
158:
159:
160: /* Initialize/Uninitialize SCSI disks */
161: void SCSI_Init(void) {
162: Log_Printf(LOG_WARN, "Loading SCSI disks:\n");
163:
164: int i;
165: for (i = 0; i < ESP_MAX_DEVS; i++) {
166: SCSIdisk[i].devtype = ConfigureParams.SCSI.target[i].nDeviceType;
167: SCSI_Insert(i);
168: }
169: }
170:
171: void SCSI_Uninit(void) {
172: int i;
173: for (i = 0; i < ESP_MAX_DEVS; i++) {
174: if (SCSIdisk[i].dsk) {
175: SCSI_Eject(i);
176: }
177: }
178: }
179:
180: void SCSI_Reset(void) {
181: SCSI_Uninit();
182: SCSI_Init();
183: }
184:
185: void SCSI_Eject(Uint8 i) {
186: File_Close(SCSIdisk[i].dsk);
187: SCSIdisk[i].dsk = NULL;
188: SCSIdisk[i].size = 0;
189: SCSIdisk[i].readonly = false;
190: SCSIdisk[i].shadow = NULL;
191: }
192:
193: static void SCSI_EjectDisk(Uint8 i) {
194: ConfigureParams.SCSI.target[i].bDiskInserted = false;
195: ConfigureParams.SCSI.target[i].szImageName[0] = '\0';
196:
197: SCSI_Eject(i);
198: }
199:
200: void SCSI_Insert(Uint8 i) {
201: SCSIdisk[i].lun = SCSIdisk[i].status = SCSIdisk[i].message = 0;
202: SCSIdisk[i].sense.code = SCSIdisk[i].sense.key = SCSIdisk[i].sense.info = 0;
203: SCSIdisk[i].sense.valid = false;
204: SCSIdisk[i].lba = SCSIdisk[i].lastlba = SCSIdisk[i].blockcounter = 0;
205:
206: SCSIdisk[i].shadow = NULL;
207:
208: Log_Printf(LOG_WARN, "SCSI Disk%i: %s\n",i,ConfigureParams.SCSI.target[i].szImageName);
209:
210: if (File_Exists(ConfigureParams.SCSI.target[i].szImageName) &&
211: ConfigureParams.SCSI.target[i].bDiskInserted) {
212: if (ConfigureParams.SCSI.target[i].bWriteProtected ||
213: ConfigureParams.SCSI.target[i].nDeviceType==DEVTYPE_CD) {
214: SCSIdisk[i].dsk = File_Open(ConfigureParams.SCSI.target[i].szImageName, "rb");
215: if (SCSIdisk[i].dsk == NULL) {
216: Log_Printf(LOG_WARN, "SCSI Disk%i: Cannot open image file %s\n",
217: i, ConfigureParams.SCSI.target[i].szImageName);
218: SCSIdisk[i].size = 0;
219: SCSIdisk[i].readonly = false;
220: if (SCSIdisk[i].devtype == DEVTYPE_HARDDISK) {
221: SCSIdisk[i].devtype = DEVTYPE_NONE;
222: }
223: } else {
224: SCSIdisk[i].size = File_Length(ConfigureParams.SCSI.target[i].szImageName);
225: SCSIdisk[i].readonly = true;
226: }
227: } else {
228: SCSIdisk[i].dsk = File_Open(ConfigureParams.SCSI.target[i].szImageName, "rb+");
229: if (SCSIdisk[i].dsk == NULL) {
230: SCSIdisk[i].dsk = File_Open(ConfigureParams.SCSI.target[i].szImageName, "rb");
231: if (SCSIdisk[i].dsk == NULL) {
232: Log_Printf(LOG_WARN, "SCSI Disk%i: Cannot open image file %s\n",
233: i, ConfigureParams.SCSI.target[i].szImageName);
234: SCSIdisk[i].size = 0;
235: SCSIdisk[i].readonly = false;
236: if (SCSIdisk[i].devtype == DEVTYPE_HARDDISK) {
237: SCSIdisk[i].devtype = DEVTYPE_NONE;
238: }
239: } else {
240: SCSIdisk[i].size = File_Length(ConfigureParams.SCSI.target[i].szImageName);
241: SCSIdisk[i].readonly = true;
242: Log_Printf(LOG_WARN, "SCSI Disk%i: Image file is not writable. Enabling write protection.\n", i);
243: }
244: } else {
245: SCSIdisk[i].size = File_Length(ConfigureParams.SCSI.target[i].szImageName);
246: SCSIdisk[i].readonly = false;
247: }
248: }
249: } else {
250: SCSIdisk[i].size = 0;
251: SCSIdisk[i].dsk = NULL;
252: SCSIdisk[i].readonly = false;
253: }
254: }
255:
256:
257:
258: /* INQUIRY response data */
259: #define DEVTYPE_DISK 0x00 /* read/write disks */
260: #define DEVTYPE_TAPE 0x01 /* tapes and other sequential devices */
261: #define DEVTYPE_PRINTER 0x02 /* printers */
262: #define DEVTYPE_PROCESSOR 0x03 /* cpus */
263: #define DEVTYPE_WORM 0x04 /* write-once optical disks */
264: #define DEVTYPE_READONLY 0x05 /* cd-roms */
265: #define DEVTYPE_NOTPRESENT 0x7f /* logical unit not present */
266:
267:
268: static unsigned char inquiry_bytes[] =
269: {
270: 0x00, /* 0: device type: see above */
271: 0x00, /* 1: &0x7F - device type qualifier 0x00 unsupported, &0x80 - rmb: 0x00 = nonremovable, 0x80 = removable */
272: 0x01, /* 2: ANSI SCSI standard (first release) compliant */
273: 0x02, /* 3: Response format (format of following data): 0x01 SCSI-1 compliant */
274: 0x31, /* 4: additional length of the following data */
275: 0x00, 0x00, /* 5,6: reserved */
276: 0x1C, /* 7: RelAdr=0, Wbus32=0, Wbus16=0, Sync=1, Linked=1, RSVD=1, CmdQue=0, SftRe=0 */
277: 'P','r','e','v','i','o','u','s', /* 8-15: Vendor ASCII */
278: 'H','D','D',' ',' ',' ',' ',' ', /* 16-23: Model ASCII */
279: ' ',' ',' ',' ',' ',' ',' ',' ', /* 24-31: Blank space ASCII */
280: '0','0','0','0','0','0','0','1', /* 32-39: Revision ASCII */
281: '0','0','0','0','0','0','0','0', /* 40-47: Serial Number ASCII */
282: ' ',' ',' ',' ',' ',' ' /* 48-53: Blank space ASCII */
283: };
284:
285:
286: Uint8 SCSIdisk_Send_Status(void) {
287: SCSIbus.phase = PHASE_MI;
288: return SCSIdisk[SCSIbus.target].status;
289: }
290:
291: Uint8 SCSIdisk_Send_Message(void) {
292: return SCSIdisk[SCSIbus.target].message;
293: }
294:
295:
296: bool SCSIdisk_Select(Uint8 target) {
297:
298: /* If there is no disk drive present, return timeout true */
299: if (SCSIdisk[target].devtype==DEVTYPE_NONE) {
300: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Selection timeout, target = %i", target);
301: SCSIbus.phase = PHASE_ST;
302: return true;
303: } else {
304: SCSIbus.target = target;
305: return false;
306: }
307: }
308:
309:
310: void SCSIdisk_Receive_Command(Uint8 *cdb, Uint8 identify) {
311: Uint8 lun = 0;
312:
313: /* Get logical unit number */
314: if (identify&MSG_IDENTIFY_MASK) { /* if identify message is valid */
315: lun = identify&MSG_LUNMASK; /* use lun from identify message */
316: } else {
317: lun = (cdb[1]&0xE0)>>5; /* use lun specified in CDB */
318: }
319:
320: SCSIdisk[SCSIbus.target].lun = lun;
321:
322: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Opcode = $%02x, target = %i, lun = %i\n", cdb[0], SCSIbus.target,lun);
323:
324: SCSI_Emulate_Command(cdb);
325: }
326:
327:
328: void SCSI_Emulate_Command(Uint8 *cdb) {
329: Uint8 opcode = cdb[0];
330: Uint8 target = SCSIbus.target;
331:
332: /* First check for lun-independent commands */
333: switch (opcode) {
334: case CMD_INQUIRY:
335: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Inquiry\n");
336: SCSI_Inquiry(cdb);
337: break;
338: case CMD_REQ_SENSE:
339: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Request sense\n");
340: SCSI_RequestSense(cdb);
341: break;
342: /* Check if the specified lun is valid for our disk */
343: default:
344: if (SCSIdisk[target].lun!=LUN_DISK) {
345: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Invalid lun! Check condition.\n");
346: SCSIbus.phase = PHASE_ST;
347: SCSIdisk[target].status = STAT_CHECK_COND; /* status: check condition */
348: SCSIdisk[target].message = MSG_COMPLETE; /* TODO: CHECK THIS! */
349: SCSIdisk[target].sense.code = SC_INVALID_LUN;
350: SCSIdisk[target].sense.valid = false;
351: return;
352: }
353:
354: /* Then check for lun-dependent commands */
355: switch(opcode) {
356: case CMD_TEST_UNIT_RDY:
357: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Test unit ready\n");
358: SCSI_TestUnitReady(cdb);
359: break;
360: case CMD_READ_CAPACITY1:
361: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Read capacity\n");
362: SCSI_ReadCapacity(cdb);
363: break;
364: case CMD_READ_SECTOR:
365: case CMD_READ_SECTOR1:
366: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Read sector\n");
367: SCSI_ReadSector(cdb);
368: break;
369: case CMD_WRITE_SECTOR:
370: case CMD_WRITE_SECTOR1:
371: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Write sector\n");
372: SCSI_WriteSector(cdb);
373: break;
374: case CMD_SEEK:
375: Log_Printf(LOG_WARN, "SCSI command: Seek\n");
376: abort();
377: break;
378: case CMD_SHIP:
379: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Ship\n");
380: SCSI_StartStop(cdb);
381: break;
382: case CMD_MODESELECT:
383: Log_Printf(LOG_WARN, "SCSI command: Mode select\n");
384: abort();
385: break;
386: case CMD_MODESENSE:
387: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Mode sense\n");
388: SCSI_ModeSense(cdb);
389: break;
390: case CMD_FORMAT_DRIVE:
391: Log_Printf(LOG_SCSI_LEVEL, "SCSI command: Format drive\n");
392: SCSI_FormatDrive(cdb);
393: break;
394: /* as of yet unsupported commands */
395: case CMD_VERIFY_TRACK:
396: case CMD_FORMAT_TRACK:
397: case CMD_CORRECTION:
398: default:
399: Log_Printf(LOG_WARN, "SCSI command: Unknown Command (%02X)\n",opcode);
400: SCSIdisk[target].status = STAT_CHECK_COND;
401: SCSIdisk[target].sense.code = SC_INVALID_CMD;
402: SCSIdisk[target].sense.valid = false;
403: SCSIbus.phase = PHASE_ST;
404: break;
405: }
406: break;
407: }
408:
409: SCSIdisk[target].message = MSG_COMPLETE;
410:
411: /* Update the led each time a command is processed */
412: Statusbar_BlinkLed(DEVICE_LED_SCSI);
413: }
414:
415:
416: /* Helpers */
417:
418: int SCSI_GetCommandLength(Uint8 opcode) {
419: Uint8 group_code = (opcode&0xE0)>>5;
420: switch (group_code) {
421: case 0: return 6;
422: case 1: return 10;
423: case 5: return 12;
424: default:
425: Log_Printf(LOG_WARN, "[SCSI] Unimplemented Group Code!");
426: return 6;
427: }
428: }
429:
430: int SCSI_GetTransferLength(Uint8 opcode, Uint8 *cdb)
431: {
432: return opcode < 0x20?
433: // class 0
434: cdb[4] :
435: // class 1
436: COMMAND_ReadInt16(cdb, 7);
437: }
438:
439: Uint64 SCSI_GetOffset(Uint8 opcode, Uint8 *cdb)
440: {
441: return opcode < 0x20?
442: // class 0
443: (COMMAND_ReadInt24(cdb, 1) & 0x1FFFFF) :
444: // class 1
445: COMMAND_ReadInt32(cdb, 2);
446: }
447:
448: // get reserved count for SCSI reply
449: int SCSI_GetCount(Uint8 opcode, Uint8 *cdb)
450: {
451: return opcode < 0x20?
452: // class 0
453: ((cdb[4]==0)?0x100:cdb[4]) :
454: // class 1
455: COMMAND_ReadInt16(cdb, 7);
456: }
457:
458: static void SCSI_GuessGeometry(Uint32 size, Uint32 *cylinders, Uint32 *heads, Uint32 *sectors)
459: {
460: Uint32 c,h,s;
461:
462: for (h=16; h>0; h--) {
463: for (s=63; s>15; s--) {
464: if ((size%(s*h))==0) {
465: c=size/(s*h);
466: *cylinders=c;
467: *heads=h;
468: *sectors=s;
469: return;
470: }
471: }
472: }
473: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Disk geometry: No valid geometry found! Using default.");
474:
475: h=16;
476: s=63;
477: c=size/(s*h);
478: if ((size%(s*h))!=0) {
479: c+=1;
480: }
481: *cylinders=c;
482: *heads=h;
483: *sectors=s;
484: }
485:
486: #define SCSI_SEEK_TIME_HD 20000 /* 20 ms max seek time */
487: #define SCSI_SECTOR_TIME_HD 350 /* 1.4 MB/sec */
488: #define SCSI_SEEK_TIME_FD 200000 /* 200 ms max seek time */
489: #define SCSI_SECTOR_TIME_FD 5500 /* 90 kB/sec */
490: #define SCSI_SEEK_TIME_CD 500000 /* 500 ms max seek time */
491: #define SCSI_SECTOR_TIME_CD 3250 /* 150 kB/sec */
492:
493: Sint64 SCSI_Seek_Time(void) {
494: Uint8 target = SCSIbus.target;
495: Sint64 seektime, seekoffset, disksize;
496:
497: if (scsi_buffer.disk) {
498: switch (SCSIdisk[target].devtype) {
499: case DEVTYPE_HARDDISK:
500: seektime = SCSI_SEEK_TIME_HD;
501: break;
502: case DEVTYPE_CD:
503: seektime = SCSI_SEEK_TIME_CD;
504: break;
505: case DEVTYPE_FLOPPY:
506: seektime = SCSI_SEEK_TIME_FD;
507: break;
508: default:
509: return 0;
510: }
511: if (SCSIdisk[target].lba < SCSIdisk[target].lastlba) {
512: seekoffset = SCSIdisk[target].lastlba - SCSIdisk[target].lba;
513: } else {
514: seekoffset = SCSIdisk[target].lba - SCSIdisk[target].lastlba;
515: }
516: disksize = SCSIdisk[target].size/BLOCKSIZE;
517:
518: if (disksize <= 0) { /* make sure no zero divide occurs */
519: return 0;
520: }
521: seektime *= seekoffset;
522: seektime /= disksize;
523:
524: if (seektime > 500000) {
525: seektime = 500000;
526: }
527:
528: return seektime;
529: } else {
530: return 0;
531: }
532: }
533:
534: Sint64 SCSI_Sector_Time(void) {
535: int target = SCSIbus.target;
536: Sint64 sectors = SCSIdisk[target].blockcounter;
537:
538: if (sectors <= 0) {
539: sectors = 1;
540: }
541:
542: if (scsi_buffer.disk) {
543: switch (SCSIdisk[target].devtype) {
544: case DEVTYPE_HARDDISK:
545: return sectors * SCSI_SECTOR_TIME_HD;
546: case DEVTYPE_CD:
547: return sectors * SCSI_SECTOR_TIME_CD;
548: case DEVTYPE_FLOPPY:
549: return sectors * SCSI_SECTOR_TIME_FD;
550: default:
551: return 1000;
552: }
553: } else {
554: return 100;
555: }
556: }
557:
558: MODEPAGE SCSI_GetModePage(Uint8 pagecode) {
559: Uint8 target = SCSIbus.target;
560:
561: MODEPAGE page;
562:
563: switch (pagecode) {
564: case 0x00: // operating page
565: page.pagesize = 4;
566: page.modepage[0] = 0x00; // &0x80: page savable? (not supported!), &0x7F: page code = 0x00
567: page.modepage[1] = 0x02; // page length = 2
568: page.modepage[2] = 0x80; // &0x80: usage bit = 1, &0x10: disable unit attention = 0
569: page.modepage[3] = 0x00; // &0x7F: device type qualifier = 0x00, see inquiry!
570: break;
571:
572: case 0x01: // error recovery page
573: page.pagesize = 8;
574: page.modepage[0] = 0x01; // &0x80: page savable? (not supported!), &0x7F: page code = 0x01
575: page.modepage[1] = 0x06; // page length = 6
576: page.modepage[2] = 0x00; // AWRE, ARRE, TB, RC, EER, PER, DTE, DCR
577: page.modepage[3] = 0x1B; // retry count
578: page.modepage[4] = 0x0B; // correction span in bits
579: page.modepage[5] = 0x00; // head offset count
580: page.modepage[6] = 0x00; // data strobe offset count
581: page.modepage[7] = 0xFF; // recovery time limit
582: break;
583:
584: case 0x03: // format device page
585: page.pagesize = 0;
586: Log_Printf(LOG_WARN, "[SCSI] Mode Sense: Page %02x not yet emulated!\n", pagecode);
587: //abort();
588: break;
589:
590: case 0x04: // rigid disc geometry page
591: {
592: Uint32 num_sectors = SCSIdisk[target].size/BLOCKSIZE;
593:
594: Uint32 cylinders, heads, sectors;
595:
596: SCSI_GuessGeometry(num_sectors, &cylinders, &heads, §ors);
597:
598: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Disk geometry: %i sectors, %i cylinders, %i heads\n", sectors, cylinders, heads);
599:
600: page.pagesize = 20;
601: page.modepage[0] = 0x04; // &0x80: page savable? (not supported!), &0x7F: page code = 0x04
602: page.modepage[1] = 0x12;
603: page.modepage[2] = (cylinders >> 16) & 0xFF;
604: page.modepage[3] = (cylinders >> 8) & 0xFF;
605: page.modepage[4] = cylinders & 0xFF;
606: page.modepage[5] = heads;
607: page.modepage[6] = 0x00; // 6,7,8: starting cylinder - write precomp (not supported)
608: page.modepage[7] = 0x00;
609: page.modepage[8] = 0x00;
610: page.modepage[9] = 0x00; // 9,10,11: starting cylinder - reduced write current (not supported)
611: page.modepage[10] = 0x00;
612: page.modepage[11] = 0x00;
613: page.modepage[12] = 0x00; // 12,13: drive step rate (not supported)
614: page.modepage[13] = 0x00;
615: page.modepage[14] = 0x00; // 14,15,16: loading zone cylinder (not supported)
616: page.modepage[15] = 0x00;
617: page.modepage[16] = 0x00;
618: page.modepage[17] = 0x00; // &0x03: rotational position locking
619: page.modepage[18] = 0x00; // rotational position lock offset
620: page.modepage[19] = 0x00; // reserved
621: }
622: break;
623:
624: case 0x02: // disconnect/reconnect page
625: case 0x08: // caching page
626: case 0x0C: // notch page
627: case 0x0D: // power condition page
628: case 0x38: // cache control page
629: case 0x3C: // soft ID page (EEPROM)
630: page.pagesize = 0;
631: Log_Printf(LOG_WARN, "[SCSI] Mode Sense: Page %02x not yet emulated!\n", pagecode);
632: //abort();
633: break;
634:
635: default:
636: page.pagesize = 0;
637: Log_Printf(LOG_WARN, "[SCSI] Mode Sense: Invalid page code: %02x!\n", pagecode);
638: break;
639: }
640: return page;
641: }
642:
643:
644:
645: /* SCSI Commands */
646:
647: void SCSI_TestUnitReady(Uint8 *cdb) {
648: Uint8 target = SCSIbus.target;
649:
650: if (SCSIdisk[target].devtype!=DEVTYPE_NONE &&
651: SCSIdisk[target].devtype!=DEVTYPE_HARDDISK &&
652: SCSIdisk[target].dsk==NULL) { /* Empty drive */
653: SCSIdisk[target].status = STAT_CHECK_COND;
654: SCSIdisk[target].sense.code = SC_NOT_READY;
655: SCSIbus.phase = PHASE_ST;
656: } else {
657: SCSIdisk[target].status = STAT_GOOD;
658: SCSIdisk[target].sense.code = SC_NO_ERROR;
659: SCSIbus.phase = PHASE_ST;
660: }
661: }
662:
663: void SCSI_ReadCapacity(Uint8 *cdb) {
664: Uint8 target = SCSIbus.target;
665:
666: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Read disk image: size = %llu byte\n", SCSIdisk[target].size);
667:
668: Uint32 sectors = (SCSIdisk[target].size / BLOCKSIZE) - 1; /* last LBA */
669:
670: static Uint8 scsi_disksize[8];
671:
672: scsi_disksize[0] = (sectors >> 24) & 0xFF;
673: scsi_disksize[1] = (sectors >> 16) & 0xFF;
674: scsi_disksize[2] = (sectors >> 8) & 0xFF;
675: scsi_disksize[3] = sectors & 0xFF;
676: scsi_disksize[4] = (BLOCKSIZE >> 24) & 0xFF;
677: scsi_disksize[5] = (BLOCKSIZE >> 16) & 0xFF;
678: scsi_disksize[6] = (BLOCKSIZE >> 8) & 0xFF;
679: scsi_disksize[7] = BLOCKSIZE & 0xFF;
680:
681: memcpy(scsi_buffer.data, scsi_disksize, 8);
682: scsi_buffer.limit=scsi_buffer.size=8;
683: scsi_buffer.disk=false;
684:
685: SCSIdisk[target].status = STAT_GOOD;
686: SCSIbus.phase = PHASE_DI;
687: SCSIdisk[target].sense.code = SC_NO_ERROR;
688: SCSIdisk[target].sense.valid = false;
689: }
690:
691: void SCSI_WriteSector(Uint8 *cdb) {
692: Uint8 target = SCSIbus.target;
693:
694: SCSIdisk[target].lastlba = SCSIdisk[target].lba;
695: SCSIdisk[target].lba = SCSI_GetOffset(cdb[0], cdb);
696: SCSIdisk[target].blockcounter = SCSI_GetCount(cdb[0], cdb);
697:
698: if (SCSIdisk[target].readonly) {
699: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Write sector: Disk is write protected! Check condition.");
700: SCSIdisk[target].status = STAT_CHECK_COND;
701: SCSIdisk[target].sense.code = SC_WRITE_PROTECT;
702: SCSIdisk[target].sense.valid = false;
703: SCSIbus.phase = PHASE_ST;
704: return;
705: }
706: scsi_buffer.disk=true;
707: scsi_buffer.size=0;
708: scsi_buffer.limit=BLOCKSIZE;
709: SCSIbus.phase = PHASE_DO;
710: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Write sector: %i block(s) at offset %i (blocksize: %i byte)",
711: SCSIdisk[target].blockcounter, SCSIdisk[target].lba, BLOCKSIZE);
712: }
713:
714: void scsi_write_sector(void) {
715: Uint8 target = SCSIbus.target;
716: Uint64 offset = 0;
717:
718: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Writing block at offset %i (%i blocks remaining).",
719: SCSIdisk[target].lba,SCSIdisk[target].blockcounter-1);
720:
721: offset = ((Uint64)SCSIdisk[target].lba)*BLOCKSIZE;
722:
723: if (offset < SCSIdisk[target].size) {
724: if (ConfigureParams.SCSI.nWriteProtection != WRITEPROT_ON) {
725: File_Write(scsi_buffer.data, BLOCKSIZE, offset, SCSIdisk[target].dsk);
726: } else {
727: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] WARNING: File write disabled!");
728: if(SCSIdisk[target].shadow) {
729: if(!(SCSIdisk[target].shadow[SCSIdisk[target].lba]))
730: SCSIdisk[target].shadow[SCSIdisk[target].lba] = malloc(BLOCKSIZE);
731: memcpy(SCSIdisk[target].shadow[SCSIdisk[target].lba], scsi_buffer.data, BLOCKSIZE);
732: } else {
733: Uint32 blocks = SCSIdisk[target].size / BLOCKSIZE;
734: SCSIdisk[target].shadow = malloc(sizeof(Uint8*) * blocks);
735: for(int i = blocks; --i >= 0;)
736: SCSIdisk[target].shadow[i] = NULL;
737: }
738: }
739: scsi_buffer.limit=BLOCKSIZE;
740: scsi_buffer.size=0;
741:
742: SCSIdisk[target].status = STAT_GOOD;
743: SCSIdisk[target].sense.code = SC_NO_ERROR;
744: SCSIdisk[target].sense.valid = false;
745: SCSIdisk[target].lba++;
746: SCSIdisk[target].blockcounter--;
747: if (SCSIdisk[target].blockcounter==0) {
748: SCSIbus.phase = PHASE_ST;
749: }
750: } else {
751: SCSIdisk[target].status = STAT_CHECK_COND;
752: SCSIdisk[target].sense.code = SC_INVALID_LBA;
753: SCSIdisk[target].sense.valid = true;
754: SCSIdisk[target].sense.info = SCSIdisk[target].lba;
755: SCSIbus.phase = PHASE_ST;
756: }
757: }
758:
759: void SCSIdisk_Receive_Data(Uint8 val) {
760: /* Receive one byte. If the transfer is complete, set status phase
761: * and write the buffer contents to the disk. */
762: scsi_buffer.data[scsi_buffer.size]=val;
763: scsi_buffer.size++;
764: if (scsi_buffer.size==scsi_buffer.limit) {
765: if (scsi_buffer.disk==true) {
766: scsi_write_sector(); /* sets status phase if done or error */
767: } else {
768: SCSIbus.phase = PHASE_ST;
769: }
770: }
771: }
772:
773:
774: void SCSI_ReadSector(Uint8 *cdb) {
775: Uint8 target = SCSIbus.target;
776:
777: SCSIdisk[target].lastlba = SCSIdisk[target].lba;
778: SCSIdisk[target].lba = SCSI_GetOffset(cdb[0], cdb);
779: SCSIdisk[target].blockcounter = SCSI_GetCount(cdb[0], cdb);
780: scsi_buffer.disk=true;
781: scsi_buffer.size=0;
782: SCSIbus.phase = PHASE_DI;
783: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Read sector: %i block(s) at offset %i (blocksize: %i byte)",
784: SCSIdisk[target].blockcounter, SCSIdisk[target].lba, BLOCKSIZE);
785: scsi_read_sector();
786: }
787:
788: void scsi_read_sector(void) {
789: Uint8 target = SCSIbus.target;
790: Uint64 offset = 0;
791:
792: if (SCSIdisk[target].blockcounter==0) {
793: SCSIbus.phase = PHASE_ST;
794: return;
795: }
796:
797: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Reading block at offset %i (%i blocks remaining).",
798: SCSIdisk[target].lba,SCSIdisk[target].blockcounter-1);
799:
800: offset = ((Uint64)SCSIdisk[target].lba)*BLOCKSIZE;
801:
802: if (offset < SCSIdisk[target].size) {
803: if (SCSIdisk[target].shadow && SCSIdisk[target].shadow[SCSIdisk[target].lba]) {
804: memcpy(scsi_buffer.data, SCSIdisk[target].shadow[SCSIdisk[target].lba], BLOCKSIZE);
805: } else {
806: File_Read(scsi_buffer.data, BLOCKSIZE, offset, SCSIdisk[target].dsk);
807: }
808: scsi_buffer.limit=scsi_buffer.size=BLOCKSIZE;
809:
810: SCSIdisk[target].status = STAT_GOOD;
811: SCSIdisk[target].sense.code = SC_NO_ERROR;
812: SCSIdisk[target].sense.valid = false;
813: SCSIdisk[target].lba++;
814: SCSIdisk[target].blockcounter--;
815: } else {
816: SCSIdisk[target].status = STAT_CHECK_COND;
817: SCSIdisk[target].sense.code = SC_INVALID_LBA;
818: SCSIdisk[target].sense.valid = true;
819: SCSIdisk[target].sense.info = SCSIdisk[target].lba;
820: SCSIbus.phase = PHASE_ST;
821: }
822: }
823:
824: Uint8 SCSIdisk_Send_Data(void) {
825: /* Send one byte. If the transfer is complete, set status phase */
826: Uint8 val=scsi_buffer.data[scsi_buffer.limit-scsi_buffer.size];
827: scsi_buffer.size--;
828: if (scsi_buffer.size==0) {
829: if (scsi_buffer.disk==true) {
830: scsi_read_sector(); /* sets status phase if done or error */
831: } else {
832: SCSIbus.phase = PHASE_ST;
833: }
834: }
835: return val;
836: }
837:
838:
839: void SCSI_Inquiry (Uint8 *cdb) {
840: Uint8 target = SCSIbus.target;
841:
842: switch (SCSIdisk[target].devtype) {
843: case DEVTYPE_HARDDISK:
844: inquiry_bytes[0] = DEVTYPE_DISK;
845: inquiry_bytes[1] &= ~0x80;
846: inquiry_bytes[16] = 'H';
847: inquiry_bytes[17] = 'D';
848: inquiry_bytes[18] = 'D';
849: inquiry_bytes[19] = ' ';
850: inquiry_bytes[20] = ' ';
851: inquiry_bytes[21] = ' ';
852: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Disk is HDD\n");
853: break;
854: case DEVTYPE_CD:
855: inquiry_bytes[0] = DEVTYPE_READONLY;
856: inquiry_bytes[1] |= 0x80;
857: inquiry_bytes[16] = 'C';
858: inquiry_bytes[17] = 'D';
859: inquiry_bytes[18] = '-';
860: inquiry_bytes[19] = 'R';
861: inquiry_bytes[20] = 'O';
862: inquiry_bytes[21] = 'M';
863: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Disk is CD-ROM\n");
864: break;
865: case DEVTYPE_FLOPPY:
866: inquiry_bytes[0] = DEVTYPE_DISK;
867: inquiry_bytes[1] |= 0x80;
868: inquiry_bytes[16] = 'F';
869: inquiry_bytes[17] = 'L';
870: inquiry_bytes[18] = 'O';
871: inquiry_bytes[19] = 'P';
872: inquiry_bytes[20] = 'P';
873: inquiry_bytes[21] = 'Y';
874: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Disk is Floppy\n");
875: break;
876:
877: default:
878: break;
879: }
880:
881: if (SCSIdisk[target].lun!=LUN_DISK) {
882: inquiry_bytes[0] = DEVTYPE_NOTPRESENT;
883: }
884:
885: scsi_buffer.disk=false;
886: scsi_buffer.limit = scsi_buffer.size = SCSI_GetTransferLength(cdb[0], cdb);
887: if (scsi_buffer.limit > (int)sizeof(inquiry_bytes)) {
888: scsi_buffer.limit = scsi_buffer.size = sizeof(inquiry_bytes);
889: }
890: memcpy(scsi_buffer.data, inquiry_bytes, scsi_buffer.limit);
891:
892: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Inquiry data length: %d", scsi_buffer.limit);
893: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Inquiry Data: %c,%c,%c,%c,%c,%c,%c,%c\n",scsi_buffer.data[8],
894: scsi_buffer.data[9],scsi_buffer.data[10],scsi_buffer.data[11],scsi_buffer.data[12],
895: scsi_buffer.data[13],scsi_buffer.data[14],scsi_buffer.data[15]);
896:
897: SCSIdisk[target].status = STAT_GOOD;
898: SCSIbus.phase = PHASE_DI;
899: SCSIdisk[target].sense.code = SC_NO_ERROR;
900: SCSIdisk[target].sense.valid = false;
901: }
902:
903:
904: void SCSI_StartStop(Uint8 *cdb) {
905: Uint8 target = SCSIbus.target;
906:
907: switch (cdb[4]&0x03) {
908: case 0:
909: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Stop disk %i", target);
910: break;
911: case 1:
912: Log_Printf(LOG_SCSI_LEVEL, "[SCSI] Start disk %i", target);
913: break;
914: case 2:
915: Log_Printf(LOG_WARN, "[SCSI] Eject disk %i", target);
916: if (SCSIdisk[target].devtype!=DEVTYPE_HARDDISK) {
917: SCSI_EjectDisk(target);
918: Statusbar_AddMessage("Ejecting SCSI media.", 0);
919: }
920: break;
921: default:
922: Log_Printf(LOG_WARN, "[SCSI] Invalid start/stop");
923: break;
924: }
925:
926: SCSIdisk[target].status = STAT_GOOD;
927: SCSIbus.phase = PHASE_ST;
928: }
929:
930:
931: void SCSI_RequestSense(Uint8 *cdb) {
932: Uint8 target = SCSIbus.target;
933:
934: int nRetLen;
935: Uint8 retbuf[22];
936:
937: nRetLen = SCSI_GetCount(cdb[0], cdb);
938:
939: if ((nRetLen<4 && nRetLen!=0) || nRetLen>22) {
940: Log_Printf(LOG_WARN, "[SCSI] *** Strange REQUEST SENSE *** len=%d!",nRetLen);
941: }
942:
943: /* Limit to sane length */
944: if (nRetLen <= 0) {
945: nRetLen = 4;
946: } else if (nRetLen > 22) {
947: nRetLen = 22;
948: }
949:
950: Log_Printf(LOG_WARN, "[SCSI] REQ SENSE size = %d %s at %d", nRetLen,__FILE__,__LINE__);
951:
952: memset(retbuf, 0, nRetLen);
953:
954: retbuf[0] = 0x70;
955: if (SCSIdisk[target].sense.valid) {
956: retbuf[0] |= 0x80;
957: retbuf[3] = SCSIdisk[target].sense.info >> 24;
958: retbuf[4] = SCSIdisk[target].sense.info >> 16;
959: retbuf[5] = SCSIdisk[target].sense.info >> 8;
960: retbuf[6] = SCSIdisk[target].sense.info;
961: }
962: switch (SCSIdisk[target].sense.code) {
963: case SC_NO_ERROR:
964: SCSIdisk[target].sense.key = SK_NOSENSE;
965: break;
966: case SC_NOT_READY:
967: SCSIdisk[target].sense.key = SK_NOTREADY;
968: break;
969: case SC_WRITE_FAULT:
970: case SC_INVALID_CMD:
971: case SC_INVALID_LBA:
972: case SC_INVALID_CDB:
973: case SC_INVALID_LUN:
974: SCSIdisk[target].sense.key = SK_ILLEGAL_REQ;
975: break;
976: case SC_WRITE_PROTECT:
977: SCSIdisk[target].sense.key = SK_DATAPROTECT;
978: break;
979: case SC_NO_SECTOR:
980: default:
981: SCSIdisk[target].sense.key = SK_HARDWARE;
982: break;
983: }
984: retbuf[2] = SCSIdisk[target].sense.key;
985: retbuf[7] = 14;
986: retbuf[12] = SCSIdisk[target].sense.code;
987:
988: scsi_buffer.size=scsi_buffer.limit=nRetLen;
989: memcpy(scsi_buffer.data, retbuf, scsi_buffer.limit);
990: scsi_buffer.disk=false;
991:
992: SCSIdisk[target].status = STAT_GOOD;
993: SCSIbus.phase = PHASE_DI;
994: }
995:
996:
997: void SCSI_ModeSense(Uint8 *cdb) {
998: Uint8 target = SCSIbus.target;
999:
1000: Uint8 retbuf[256];
1001: MODEPAGE page;
1002:
1003: Uint32 sectors = SCSIdisk[target].size / BLOCKSIZE;
1004:
1005: Uint8 pagecontrol = (cdb[2] & 0x0C) >> 6;
1006: Uint8 pagecode = cdb[2] & 0x3F;
1007: Uint8 dbd = cdb[1] & 0x08; // disable block descriptor
1008:
1009: Log_Printf(LOG_WARN, "[SCSI] Mode Sense: page = %02x, page_control = %i, %s\n", pagecode, pagecontrol, dbd == 0x08 ? "block descriptor disabled" : "block descriptor enabled");
1010:
1011: /* Header */
1012: retbuf[0] = 0x00; // length of following data
1013: retbuf[1] = 0x00; // medium type (always 0)
1014: retbuf[2] = SCSIdisk[target].readonly ? 0x80 : 0x00; // if media is read-only 0x80, else 0x00
1015: retbuf[3] = 0x08; // block descriptor length
1016:
1017: /* Block descriptor data */
1018: Uint8 header_size = 4;
1019: if (!dbd) {
1020: retbuf[4] = 0x00; // media density code
1021: retbuf[5] = sectors >> 16; // Number of blocks, high (?)
1022: retbuf[6] = sectors >> 8; // Number of blocks, med (?)
1023: retbuf[7] = sectors; // Number of blocks, low (?)
1024: retbuf[8] = 0x00; // reserved
1025: retbuf[9] = (BLOCKSIZE >> 16) & 0xFF; // Block size in bytes, high
1026: retbuf[10] = (BLOCKSIZE >> 8) & 0xFF; // Block size in bytes, med
1027: retbuf[11] = BLOCKSIZE & 0xFF; // Block size in bytes, low
1028: header_size = 12;
1029: Log_Printf(LOG_WARN, "[SCSI] Mode Sense: Block descriptor data: %s, size = %i blocks, blocksize = %i byte\n",
1030: SCSIdisk[target].readonly ? "disk is read-only" : "disk is read/write" , sectors, BLOCKSIZE);
1031: }
1032: retbuf[0] = header_size - 1;
1033:
1034: /* Mode Pages */
1035: if (pagecode == 0x3F) { // return all pages!
1036: Uint8 offset = header_size;
1037: Uint8 counter;
1038: for (pagecode = 0; pagecode < 0x3F; pagecode++) {
1039: page = SCSI_GetModePage(pagecode);
1040: switch (pagecontrol) {
1041: case 0: // current values (not supported, using default values)
1042: memcpy(page.current, page.modepage, page.pagesize);
1043: for (counter = 0; counter < page.pagesize; counter++) {
1044: retbuf[counter+offset] = page.current[counter];
1045: }
1046: break;
1047: case 1: // changeable values (not supported, all 0)
1048: memset(page.changeable, 0x00, page.pagesize);
1049: for (counter = 0; counter < page.pagesize; counter++) {
1050: retbuf[counter+offset] = page.changeable[counter];
1051: }
1052: break;
1053: case 2: // default values
1054: for (counter = 0; counter < page.pagesize; counter++) {
1055: retbuf[counter+offset] = page.modepage[counter];
1056: }
1057: break;
1058: case 3: // saved values (not supported, using default values)
1059: memcpy(page.saved, page.modepage, page.pagesize);
1060: for (counter = 0; counter < page.pagesize; counter++) {
1061: retbuf[counter+offset] = page.saved[counter];
1062: }
1063: break;
1064:
1065: default:
1066: break;
1067: }
1068: offset += page.pagesize;
1069: retbuf[0] += page.pagesize;
1070: }
1071: } else { // return only single requested page
1072: page = SCSI_GetModePage(pagecode);
1073:
1074: Uint8 counter;
1075: switch (pagecontrol) {
1076: case 0: // current values (not supported, using default values)
1077: memcpy(page.current, page.modepage, page.pagesize);
1078: for (counter = 0; counter < page.pagesize; counter++) {
1079: retbuf[counter+header_size] = page.current[counter];
1080: }
1081: break;
1082: case 1: // changeable values (not supported, all 0)
1083: memset(page.changeable, 0x00, page.pagesize);
1084: for (counter = 0; counter < page.pagesize; counter++) {
1085: retbuf[counter+header_size] = page.changeable[counter];
1086: }
1087: break;
1088: case 2: // default values
1089: for (counter = 0; counter < page.pagesize; counter++) {
1090: retbuf[counter+header_size] = page.modepage[counter];
1091: }
1092: break;
1093: case 3: // saved values (not supported, using default values)
1094: memcpy(page.saved, page.modepage, page.pagesize);
1095: for (counter = 0; counter < page.pagesize; counter++) {
1096: retbuf[counter+header_size] = page.saved[counter];
1097: }
1098: break;
1099:
1100: default:
1101: break;
1102: }
1103:
1104: retbuf[0] += page.pagesize;
1105: }
1106:
1107: scsi_buffer.disk=false;
1108: scsi_buffer.limit = scsi_buffer.size = retbuf[0]+1;
1109: if (scsi_buffer.limit > SCSI_GetTransferLength(cdb[0], cdb)) {
1110: scsi_buffer.limit = scsi_buffer.size = SCSI_GetTransferLength(cdb[0], cdb);
1111: }
1112: memcpy(scsi_buffer.data, retbuf, scsi_buffer.limit);
1113:
1114: SCSIdisk[target].status = STAT_GOOD;
1115: SCSIbus.phase = PHASE_DI;
1116: SCSIdisk[target].sense.code = SC_NO_ERROR;
1117: SCSIdisk[target].sense.valid = false;
1118: }
1119:
1120:
1121: void SCSI_FormatDrive(Uint8 *cdb) {
1122: Uint8 format_data = cdb[1]&0x10;
1123:
1124: Log_Printf(LOG_WARN, "[SCSI] Format drive command with parameters %02X\n",cdb[1]&0x1F);
1125:
1126: if (format_data) {
1127: Log_Printf(LOG_WARN, "[SCSI] Format drive with format data unsupported!\n");
1128: abort();
1129: } else {
1130: SCSIdisk[SCSIbus.target].status = STAT_GOOD;
1131: SCSIbus.phase = PHASE_ST;
1132: }
1133: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.