1 |
/* |
2 |
* Copyright (C) 2003-2007 Anders Gavare. All rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
6 |
* |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
* notice, this list of conditions and the following disclaimer in the |
11 |
* documentation and/or other materials provided with the distribution. |
12 |
* 3. The name of the author may not be used to endorse or promote products |
13 |
* derived from this software without specific prior written permission. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
* |
27 |
* |
28 |
* $Id: diskimage_scsicmd.c,v 1.2 2007/03/24 04:05:34 debug Exp $ |
29 |
* |
30 |
* Disk image support: SCSI command emulation. |
31 |
* |
32 |
* TODO: There are LOTS of ugly magic values in this module. These should |
33 |
* be replaced by proper defines. |
34 |
* |
35 |
* TODO: There's probably a bug in the tape support: |
36 |
* Let's say there are 10240 bytes left in a file, and 10240 |
37 |
* bytes are read. Then feof() is not true yet (?), so the next |
38 |
* read will also return 10240 bytes (but all zeroes), and then after |
39 |
* that return feof (which results in a filemark). This is probably |
40 |
* trivial to fix, but I don't feel like it right now. |
41 |
*/ |
42 |
|
43 |
#include <stdio.h> |
44 |
#include <stdlib.h> |
45 |
#include <string.h> |
46 |
#include <unistd.h> |
47 |
|
48 |
#include "cpu.h" |
49 |
#include "diskimage.h" |
50 |
#include "machine.h" |
51 |
#include "misc.h" |
52 |
|
53 |
|
54 |
static char *diskimage_types[] = DISKIMAGE_TYPES; |
55 |
static struct scsi_transfer *first_free_scsi_transfer_alloc = NULL; |
56 |
|
57 |
|
58 |
/* |
59 |
* scsi_transfer_alloc(): |
60 |
* |
61 |
* Allocates memory for a new scsi_transfer struct, and fills it with |
62 |
* sane data (NULL pointers). |
63 |
* The return value is a pointer to the new struct. If allocation |
64 |
* failed, the program exits. |
65 |
*/ |
66 |
struct scsi_transfer *scsi_transfer_alloc(void) |
67 |
{ |
68 |
struct scsi_transfer *p; |
69 |
|
70 |
if (first_free_scsi_transfer_alloc != NULL) { |
71 |
p = first_free_scsi_transfer_alloc; |
72 |
first_free_scsi_transfer_alloc = p->next_free; |
73 |
} else { |
74 |
p = malloc(sizeof(struct scsi_transfer)); |
75 |
if (p == NULL) { |
76 |
fprintf(stderr, "scsi_transfer_alloc(): out " |
77 |
"of memory\n"); |
78 |
exit(1); |
79 |
} |
80 |
} |
81 |
|
82 |
memset(p, 0, sizeof(struct scsi_transfer)); |
83 |
|
84 |
return p; |
85 |
} |
86 |
|
87 |
|
88 |
/* |
89 |
* scsi_transfer_free(): |
90 |
* |
91 |
* Frees the space used by a scsi_transfer struct. All buffers refered |
92 |
* to by the scsi_transfer struct are freed. |
93 |
*/ |
94 |
void scsi_transfer_free(struct scsi_transfer *p) |
95 |
{ |
96 |
if (p == NULL) { |
97 |
fprintf(stderr, "scsi_transfer_free(): p == NULL\n"); |
98 |
exit(1); |
99 |
} |
100 |
|
101 |
if (p->msg_out != NULL) |
102 |
free(p->msg_out); |
103 |
if (p->cmd != NULL) |
104 |
free(p->cmd); |
105 |
if (p->data_out != NULL) |
106 |
free(p->data_out); |
107 |
|
108 |
if (p->data_in != NULL) |
109 |
free(p->data_in); |
110 |
if (p->msg_in != NULL) |
111 |
free(p->msg_in); |
112 |
if (p->status != NULL) |
113 |
free(p->status); |
114 |
|
115 |
p->next_free = first_free_scsi_transfer_alloc; |
116 |
first_free_scsi_transfer_alloc = p; |
117 |
} |
118 |
|
119 |
|
120 |
/* |
121 |
* scsi_transfer_allocbuf(): |
122 |
* |
123 |
* Helper function, used by diskimage_scsicommand(), and SCSI controller |
124 |
* devices. Example of usage: |
125 |
* |
126 |
* scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1); |
127 |
*/ |
128 |
void scsi_transfer_allocbuf(size_t *lenp, unsigned char **pp, size_t want_len, |
129 |
int clearflag) |
130 |
{ |
131 |
unsigned char *p = (*pp); |
132 |
|
133 |
if (p != NULL) { |
134 |
printf("WARNING! scsi_transfer_allocbuf(): old pointer " |
135 |
"was not NULL, freeing it now\n"); |
136 |
free(p); |
137 |
} |
138 |
|
139 |
(*lenp) = want_len; |
140 |
if ((p = malloc(want_len)) == NULL) { |
141 |
fprintf(stderr, "scsi_transfer_allocbuf(): out of " |
142 |
"memory trying to allocate %li bytes\n", (long)want_len); |
143 |
exit(1); |
144 |
} |
145 |
|
146 |
if (clearflag) |
147 |
memset(p, 0, want_len); |
148 |
|
149 |
(*pp) = p; |
150 |
} |
151 |
|
152 |
|
153 |
/**************************************************************************/ |
154 |
|
155 |
|
156 |
/* |
157 |
* diskimage__return_default_status_and_message(): |
158 |
* |
159 |
* Set the status and msg_in parts of a scsi_transfer struct |
160 |
* to default values (msg_in = 0x00, status = 0x00). |
161 |
*/ |
162 |
static void diskimage__return_default_status_and_message( |
163 |
struct scsi_transfer *xferp) |
164 |
{ |
165 |
scsi_transfer_allocbuf(&xferp->status_len, &xferp->status, 1, 0); |
166 |
xferp->status[0] = 0x00; |
167 |
scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1, 0); |
168 |
xferp->msg_in[0] = 0x00; |
169 |
} |
170 |
|
171 |
|
172 |
/* |
173 |
* diskimage__switch_tape(): |
174 |
* |
175 |
* Used by the SPACE command. (d is assumed to be non-NULL.) |
176 |
*/ |
177 |
static void diskimage__switch_tape(struct diskimage *d) |
178 |
{ |
179 |
char tmpfname[1000]; |
180 |
|
181 |
snprintf(tmpfname, sizeof(tmpfname), "%s.%i", |
182 |
d->fname, d->tape_filenr); |
183 |
tmpfname[sizeof(tmpfname)-1] = '\0'; |
184 |
|
185 |
if (d->f != NULL) |
186 |
fclose(d->f); |
187 |
|
188 |
d->f = fopen(tmpfname, d->writable? "r+" : "r"); |
189 |
if (d->f == NULL) { |
190 |
fprintf(stderr, "[ diskimage__switch_tape(): could not " |
191 |
"(re)open '%s' ]\n", tmpfname); |
192 |
/* TODO: return error */ |
193 |
} |
194 |
d->tape_offset = 0; |
195 |
} |
196 |
|
197 |
|
198 |
/**************************************************************************/ |
199 |
|
200 |
|
201 |
/* |
202 |
* diskimage_scsicommand(): |
203 |
* |
204 |
* Perform a SCSI command on a disk image. |
205 |
* |
206 |
* The xferp points to a scsi_transfer struct, containing msg_out, command, |
207 |
* and data_out coming from the SCSI controller device. This function |
208 |
* interprets the command, and (if necessary) creates responses in |
209 |
* data_in, msg_in, and status. |
210 |
* |
211 |
* Returns: |
212 |
* 2 if the command expects data from the DATA_OUT phase, |
213 |
* 1 if otherwise ok, |
214 |
* 0 on error. |
215 |
*/ |
216 |
int diskimage_scsicommand(struct cpu *cpu, int id, int type, |
217 |
struct scsi_transfer *xferp) |
218 |
{ |
219 |
char namebuf[16]; |
220 |
int retlen, i, q; |
221 |
uint64_t size; |
222 |
int64_t ofs; |
223 |
int pagecode; |
224 |
struct machine *machine = cpu->machine; |
225 |
struct diskimage *d; |
226 |
|
227 |
if (machine == NULL) { |
228 |
fatal("[ diskimage_scsicommand(): machine == NULL ]\n"); |
229 |
return 0; |
230 |
} |
231 |
|
232 |
d = machine->first_diskimage; |
233 |
while (d != NULL) { |
234 |
if (d->type == type && d->id == id) |
235 |
break; |
236 |
d = d->next; |
237 |
} |
238 |
if (d == NULL) { |
239 |
fprintf(stderr, "[ diskimage_scsicommand(): %s " |
240 |
" id %i not connected? ]\n", diskimage_types[type], id); |
241 |
} |
242 |
|
243 |
if (xferp->cmd == NULL) { |
244 |
fatal("[ diskimage_scsicommand(): cmd == NULL ]\n"); |
245 |
return 0; |
246 |
} |
247 |
|
248 |
if (xferp->cmd_len < 1) { |
249 |
fatal("[ diskimage_scsicommand(): cmd_len == %i ]\n", |
250 |
xferp->cmd_len); |
251 |
return 0; |
252 |
} |
253 |
|
254 |
debug("[ diskimage_scsicommand(id=%i) cmd=0x%02x: ", |
255 |
id, xferp->cmd[0]); |
256 |
|
257 |
#if 0 |
258 |
fatal("[ diskimage_scsicommand(id=%i) cmd=0x%02x len=%i:", |
259 |
id, xferp->cmd[0], xferp->cmd_len); |
260 |
for (i=0; i<xferp->cmd_len; i++) |
261 |
fatal(" %02x", xferp->cmd[i]); |
262 |
fatal("\n"); |
263 |
if (xferp->cmd_len > 7 && xferp->cmd[5] == 0x11) |
264 |
single_step = ENTER_SINGLE_STEPPING; |
265 |
#endif |
266 |
|
267 |
#if 0 |
268 |
{ |
269 |
static FILE *f = NULL; |
270 |
if (f == NULL) |
271 |
f = fopen("scsi_log.txt", "w"); |
272 |
if (f != NULL) { |
273 |
int i; |
274 |
fprintf(f, "id=%i cmd =", id); |
275 |
for (i=0; i<xferp->cmd_len; i++) |
276 |
fprintf(f, " %02x", xferp->cmd[i]); |
277 |
fprintf(f, "\n"); |
278 |
fflush(f); |
279 |
} |
280 |
} |
281 |
#endif |
282 |
|
283 |
switch (xferp->cmd[0]) { |
284 |
|
285 |
case SCSICMD_TEST_UNIT_READY: |
286 |
debug("TEST_UNIT_READY"); |
287 |
if (xferp->cmd_len != 6) |
288 |
debug(" (weird len=%i)", xferp->cmd_len); |
289 |
|
290 |
/* TODO: bits 765 of buf[1] contains the LUN */ |
291 |
if (xferp->cmd[1] != 0x00) |
292 |
fatal("WARNING: TEST_UNIT_READY with cmd[1]=0x%02x" |
293 |
" not yet implemented\n", (int)xferp->cmd[1]); |
294 |
|
295 |
diskimage__return_default_status_and_message(xferp); |
296 |
break; |
297 |
|
298 |
case SCSICMD_INQUIRY: |
299 |
debug("INQUIRY"); |
300 |
if (xferp->cmd_len != 6) |
301 |
debug(" (weird len=%i)", xferp->cmd_len); |
302 |
if (xferp->cmd[1] != 0x00) { |
303 |
debug("WARNING: INQUIRY with cmd[1]=0x%02x not yet " |
304 |
"implemented\n", (int)xferp->cmd[1]); |
305 |
|
306 |
break; |
307 |
} |
308 |
|
309 |
/* Return values: */ |
310 |
retlen = xferp->cmd[4]; |
311 |
if (retlen < 36) { |
312 |
fatal("WARNING: SCSI inquiry len=%i, <36!\n", retlen); |
313 |
retlen = 36; |
314 |
} |
315 |
|
316 |
/* Return data: */ |
317 |
scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in, |
318 |
retlen, 1); |
319 |
xferp->data_in[0] = 0x00; /* 0x00 = Direct-access disk */ |
320 |
xferp->data_in[1] = 0x00; /* 0x00 = non-removable */ |
321 |
xferp->data_in[2] = 0x02; /* SCSI-2 */ |
322 |
#if 0 |
323 |
xferp->data_in[3] = 0x02; /* Response data format = SCSI-2 */ |
324 |
#endif |
325 |
xferp->data_in[4] = retlen - 4; /* Additional length */ |
326 |
xferp->data_in[4] = 0x2c - 4; /* Additional length */ |
327 |
xferp->data_in[6] = 0x04; /* ACKREQQ */ |
328 |
xferp->data_in[7] = 0x60; /* WBus32, WBus16 */ |
329 |
|
330 |
/* These are padded with spaces: */ |
331 |
|
332 |
memcpy(xferp->data_in+8, "GXemul ", 8); |
333 |
if (diskimage_getname(cpu->machine, id, |
334 |
type, namebuf, sizeof(namebuf))) { |
335 |
size_t i; |
336 |
for (i=0; i<sizeof(namebuf); i++) |
337 |
if (namebuf[i] == 0) { |
338 |
for (; i<sizeof(namebuf); i++) |
339 |
namebuf[i] = ' '; |
340 |
break; |
341 |
} |
342 |
memcpy(xferp->data_in+16, namebuf, 16); |
343 |
} else |
344 |
memcpy(xferp->data_in+16, "DISK ", 16); |
345 |
memcpy(xferp->data_in+32, "0 ", 4); |
346 |
|
347 |
/* |
348 |
* Some Ultrix kernels want specific responses from |
349 |
* the drives. |
350 |
*/ |
351 |
|
352 |
if (machine->machine_type == MACHINE_PMAX) { |
353 |
/* DEC, RZ25 (rev 0900) = 832527 sectors */ |
354 |
/* DEC, RZ58 (rev 2000) = 2698061 sectors */ |
355 |
memcpy(xferp->data_in+8, "DEC ", 8); |
356 |
memcpy(xferp->data_in+16, "RZ58 (C) DEC", 16); |
357 |
memcpy(xferp->data_in+32, "2000", 4); |
358 |
} |
359 |
|
360 |
/* Some data is different for CD-ROM drives: */ |
361 |
if (d->is_a_cdrom) { |
362 |
xferp->data_in[0] = 0x05; /* 0x05 = CD-ROM */ |
363 |
xferp->data_in[1] = 0x80; /* 0x80 = removable */ |
364 |
/* memcpy(xferp->data_in+16, "CD-ROM ", 16);*/ |
365 |
|
366 |
if (machine->machine_type == MACHINE_PMAX) { |
367 |
/* SONY, CD-ROM: */ |
368 |
memcpy(xferp->data_in+8, "SONY ", 8); |
369 |
memcpy(xferp->data_in+16, |
370 |
"CD-ROM ", 16); |
371 |
|
372 |
/* ... or perhaps this: */ |
373 |
memcpy(xferp->data_in+8, "DEC ", 8); |
374 |
memcpy(xferp->data_in+16, |
375 |
"RRD42 (C) DEC ", 16); |
376 |
memcpy(xferp->data_in+32, "4.5d", 4); |
377 |
} else if (machine->machine_type == MACHINE_ARC) { |
378 |
/* NEC, CD-ROM: */ |
379 |
memcpy(xferp->data_in+8, "NEC ", 8); |
380 |
memcpy(xferp->data_in+16, |
381 |
"CD-ROM CDR-210P ", 16); |
382 |
memcpy(xferp->data_in+32, "1.0 ", 4); |
383 |
} |
384 |
} |
385 |
|
386 |
/* Data for tape devices: */ |
387 |
if (d->is_a_tape) { |
388 |
xferp->data_in[0] = 0x01; /* 0x01 = tape */ |
389 |
xferp->data_in[1] = 0x80; /* 0x80 = removable */ |
390 |
memcpy(xferp->data_in+16, "TAPE ", 16); |
391 |
|
392 |
if (machine->machine_type == MACHINE_PMAX) { |
393 |
/* |
394 |
* TODO: find out if these are correct. |
395 |
* |
396 |
* The name might be TZK10, TSZ07, or TLZ04, |
397 |
* or something completely different. |
398 |
*/ |
399 |
memcpy(xferp->data_in+8, "DEC ", 8); |
400 |
memcpy(xferp->data_in+16, |
401 |
"TK50 (C) DEC", 16); |
402 |
memcpy(xferp->data_in+32, "2000", 4); |
403 |
} |
404 |
} |
405 |
|
406 |
diskimage__return_default_status_and_message(xferp); |
407 |
break; |
408 |
|
409 |
case SCSIBLOCKCMD_READ_CAPACITY: |
410 |
debug("READ_CAPACITY"); |
411 |
|
412 |
if (xferp->cmd_len != 10) |
413 |
fatal(" [ weird READ_CAPACITY len=%i, should be 10 ] ", |
414 |
xferp->cmd_len); |
415 |
else { |
416 |
if (xferp->cmd[8] & 1) { |
417 |
/* Partial Medium Indicator bit... TODO */ |
418 |
fatal("WARNING: READ_CAPACITY with PMI bit" |
419 |
" set not yet implemented\n"); |
420 |
} |
421 |
} |
422 |
|
423 |
/* Return data: */ |
424 |
scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in, |
425 |
8, 1); |
426 |
|
427 |
diskimage_recalc_size(d); |
428 |
|
429 |
size = d->total_size / d->logical_block_size; |
430 |
if (d->total_size & (d->logical_block_size-1)) |
431 |
size ++; |
432 |
|
433 |
xferp->data_in[0] = (size >> 24) & 255; |
434 |
xferp->data_in[1] = (size >> 16) & 255; |
435 |
xferp->data_in[2] = (size >> 8) & 255; |
436 |
xferp->data_in[3] = size & 255; |
437 |
|
438 |
xferp->data_in[4] = (d->logical_block_size >> 24) & 255; |
439 |
xferp->data_in[5] = (d->logical_block_size >> 16) & 255; |
440 |
xferp->data_in[6] = (d->logical_block_size >> 8) & 255; |
441 |
xferp->data_in[7] = d->logical_block_size & 255; |
442 |
|
443 |
diskimage__return_default_status_and_message(xferp); |
444 |
break; |
445 |
|
446 |
case SCSICMD_MODE_SENSE: |
447 |
case SCSICMD_MODE_SENSE10: |
448 |
debug("MODE_SENSE"); |
449 |
q = 4; retlen = xferp->cmd[4]; |
450 |
switch (xferp->cmd_len) { |
451 |
case 6: break; |
452 |
case 10:q = 8; |
453 |
retlen = xferp->cmd[7] * 256 + xferp->cmd[8]; |
454 |
break; |
455 |
default:fatal(" (unimplemented mode_sense len=%i)", |
456 |
xferp->cmd_len); |
457 |
} |
458 |
|
459 |
/* |
460 |
* NOTE/TODO: This code doesn't handle too short retlens |
461 |
* very well. A quick hack around this is that I allocate |
462 |
* a bit too much memory, so that nothing is actually |
463 |
* written outside of xferp->data_in[]. |
464 |
*/ |
465 |
|
466 |
retlen += 100; /* Should be enough. (Ugly.) */ |
467 |
|
468 |
if ((xferp->cmd[2] & 0xc0) != 0) |
469 |
fatal("WARNING: mode sense, cmd[2] = 0x%02x\n", |
470 |
xferp->cmd[2]); |
471 |
|
472 |
/* Return data: */ |
473 |
scsi_transfer_allocbuf(&xferp->data_in_len, |
474 |
&xferp->data_in, retlen, 1); |
475 |
|
476 |
xferp->data_in_len -= 100; /* Restore size. */ |
477 |
|
478 |
pagecode = xferp->cmd[2] & 0x3f; |
479 |
|
480 |
debug("[ MODE SENSE id %i, pagecode=%i ]\n", id, pagecode); |
481 |
|
482 |
/* 4 bytes of header for 6-byte command, |
483 |
8 bytes of header for 10-byte command. */ |
484 |
xferp->data_in[0] = retlen; /* 0: mode data length */ |
485 |
xferp->data_in[1] = d->is_a_cdrom? 0x05 : 0x00; |
486 |
/* 1: medium type */ |
487 |
xferp->data_in[2] = 0x00; /* device specific |
488 |
parameter */ |
489 |
xferp->data_in[3] = 8 * 1; /* block descriptor |
490 |
length: 1 page (?) */ |
491 |
|
492 |
xferp->data_in[q+0] = 0x00; /* density code */ |
493 |
xferp->data_in[q+1] = 0; /* nr of blocks, high */ |
494 |
xferp->data_in[q+2] = 0; /* nr of blocks, mid */ |
495 |
xferp->data_in[q+3] = 0; /* nr of blocks, low */ |
496 |
xferp->data_in[q+4] = 0x00; /* reserved */ |
497 |
xferp->data_in[q+5] = (d->logical_block_size >> 16) & 255; |
498 |
xferp->data_in[q+6] = (d->logical_block_size >> 8) & 255; |
499 |
xferp->data_in[q+7] = d->logical_block_size & 255; |
500 |
q += 8; |
501 |
|
502 |
diskimage__return_default_status_and_message(xferp); |
503 |
|
504 |
/* descriptors, 8 bytes (each) */ |
505 |
|
506 |
/* page, n bytes (each) */ |
507 |
switch (pagecode) { |
508 |
case 0: |
509 |
/* TODO: Nothing here? */ |
510 |
break; |
511 |
case 1: /* read-write error recovery page */ |
512 |
xferp->data_in[q + 0] = pagecode; |
513 |
xferp->data_in[q + 1] = 10; |
514 |
break; |
515 |
case 3: /* format device page */ |
516 |
xferp->data_in[q + 0] = pagecode; |
517 |
xferp->data_in[q + 1] = 22; |
518 |
|
519 |
/* 10,11 = sectors per track */ |
520 |
xferp->data_in[q + 10] = 0; |
521 |
xferp->data_in[q + 11] = d->sectors_per_track; |
522 |
|
523 |
/* 12,13 = physical sector size */ |
524 |
xferp->data_in[q + 12] = |
525 |
(d->logical_block_size >> 8) & 255; |
526 |
xferp->data_in[q + 13] = d->logical_block_size & 255; |
527 |
break; |
528 |
case 4: /* rigid disk geometry page */ |
529 |
xferp->data_in[q + 0] = pagecode; |
530 |
xferp->data_in[q + 1] = 22; |
531 |
xferp->data_in[q + 2] = (d->ncyls >> 16) & 255; |
532 |
xferp->data_in[q + 3] = (d->ncyls >> 8) & 255; |
533 |
xferp->data_in[q + 4] = d->ncyls & 255; |
534 |
xferp->data_in[q + 5] = d->heads; |
535 |
|
536 |
xferp->data_in[q + 20] = (d->rpms >> 8) & 255; |
537 |
xferp->data_in[q + 21] = d->rpms & 255; |
538 |
break; |
539 |
case 5: /* flexible disk page */ |
540 |
xferp->data_in[q + 0] = pagecode; |
541 |
xferp->data_in[q + 1] = 0x1e; |
542 |
|
543 |
/* 2,3 = transfer rate */ |
544 |
xferp->data_in[q + 2] = ((5000) >> 8) & 255; |
545 |
xferp->data_in[q + 3] = (5000) & 255; |
546 |
|
547 |
xferp->data_in[q + 4] = d->heads; |
548 |
xferp->data_in[q + 5] = d->sectors_per_track; |
549 |
|
550 |
/* 6,7 = data bytes per sector */ |
551 |
xferp->data_in[q + 6] = (d->logical_block_size >> 8) |
552 |
& 255; |
553 |
xferp->data_in[q + 7] = d->logical_block_size & 255; |
554 |
|
555 |
xferp->data_in[q + 8] = (d->ncyls >> 8) & 255; |
556 |
xferp->data_in[q + 9] = d->ncyls & 255; |
557 |
|
558 |
xferp->data_in[q + 28] = (d->rpms >> 8) & 255; |
559 |
xferp->data_in[q + 29] = d->rpms & 255; |
560 |
break; |
561 |
default: |
562 |
fatal("[ MODE_SENSE for page %i is not yet " |
563 |
"implemented! ]\n", pagecode); |
564 |
} |
565 |
|
566 |
break; |
567 |
|
568 |
case SCSICMD_READ: |
569 |
case SCSICMD_READ_10: |
570 |
debug("READ"); |
571 |
|
572 |
/* |
573 |
* For tape devices, read data at the current position. |
574 |
* For disk and CDROM devices, the command bytes contain |
575 |
* an offset telling us where to read from the device. |
576 |
*/ |
577 |
|
578 |
if (d->is_a_tape) { |
579 |
/* bits 7..5 of cmd[1] are the LUN bits... TODO */ |
580 |
|
581 |
size = (xferp->cmd[2] << 16) + |
582 |
(xferp->cmd[3] << 8) + |
583 |
xferp->cmd[4]; |
584 |
|
585 |
/* Bit 1 of cmd[1] is the SILI bit (TODO), and |
586 |
bit 0 is the "use fixed length" bit. */ |
587 |
|
588 |
if (xferp->cmd[1] & 0x01) { |
589 |
/* Fixed block length: */ |
590 |
size *= d->logical_block_size; |
591 |
} |
592 |
|
593 |
if (d->filemark) { |
594 |
/* At end of file, switch to the next |
595 |
automagically: */ |
596 |
d->tape_filenr ++; |
597 |
diskimage__switch_tape(d); |
598 |
|
599 |
d->filemark = 0; |
600 |
} |
601 |
|
602 |
ofs = d->tape_offset; |
603 |
|
604 |
fatal("[ READ tape, id=%i file=%i, cmd[1]=%02x size=%i" |
605 |
", ofs=%lli ]\n", id, d->tape_filenr, |
606 |
xferp->cmd[1], (int)size, (long long)ofs); |
607 |
} else { |
608 |
if (xferp->cmd[0] == SCSICMD_READ) { |
609 |
if (xferp->cmd_len != 6) |
610 |
debug(" (weird len=%i)", |
611 |
xferp->cmd_len); |
612 |
|
613 |
/* |
614 |
* bits 4..0 of cmd[1], and cmd[2] and cmd[3] |
615 |
* hold the logical block address. |
616 |
* |
617 |
* cmd[4] holds the number of logical blocks |
618 |
* to transfer. (Special case if the value is |
619 |
* 0, actually means 256.) |
620 |
*/ |
621 |
ofs = ((xferp->cmd[1] & 0x1f) << 16) + |
622 |
(xferp->cmd[2] << 8) + xferp->cmd[3]; |
623 |
retlen = xferp->cmd[4]; |
624 |
if (retlen == 0) |
625 |
retlen = 256; |
626 |
} else { |
627 |
if (xferp->cmd_len != 10) |
628 |
debug(" (weird len=%i)", |
629 |
xferp->cmd_len); |
630 |
|
631 |
/* |
632 |
* cmd[2..5] hold the logical block address. |
633 |
* cmd[7..8] holds the number of logical |
634 |
* blocks to transfer. (NOTE: If the value is |
635 |
* 0, this means 0, not 65536. :-) |
636 |
*/ |
637 |
ofs = ((uint64_t)xferp->cmd[2] << 24) + |
638 |
(xferp->cmd[3] << 16) + (xferp->cmd[4] << 8) |
639 |
+ xferp->cmd[5]; |
640 |
retlen = (xferp->cmd[7] << 8) + xferp->cmd[8]; |
641 |
} |
642 |
|
643 |
size = retlen * d->logical_block_size; |
644 |
ofs *= d->logical_block_size; |
645 |
} |
646 |
|
647 |
/* Return data: */ |
648 |
scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in, |
649 |
size, 0); |
650 |
|
651 |
debug(" READ ofs=%lli size=%i\n", (long long)ofs, (int)size); |
652 |
|
653 |
diskimage__return_default_status_and_message(xferp); |
654 |
|
655 |
d->filemark = 0; |
656 |
|
657 |
/* |
658 |
* Failure? Then set check condition. |
659 |
* For tapes, error should only occur at the end of a file. |
660 |
* |
661 |
* "If the logical unit encounters a filemark during |
662 |
* a READ command, CHECK CONDITION status shall be |
663 |
* returned and the filemark and valid bits shall be |
664 |
* set to one in the sense data. The sense key shall |
665 |
* be set to NO SENSE".. |
666 |
*/ |
667 |
if (d->is_a_tape && d->f != NULL && feof(d->f)) { |
668 |
debug(" feof id=%i\n", id); |
669 |
xferp->status[0] = 0x02; /* CHECK CONDITION */ |
670 |
|
671 |
d->filemark = 1; |
672 |
} else |
673 |
diskimage__internal_access(d, 0, ofs, |
674 |
xferp->data_in, size); |
675 |
|
676 |
if (d->is_a_tape && d->f != NULL) |
677 |
d->tape_offset = ftello(d->f); |
678 |
|
679 |
/* TODO: other errors? */ |
680 |
break; |
681 |
|
682 |
case SCSICMD_WRITE: |
683 |
case SCSICMD_WRITE_10: |
684 |
debug("WRITE"); |
685 |
|
686 |
/* TODO: tape */ |
687 |
|
688 |
if (xferp->cmd[0] == SCSICMD_WRITE) { |
689 |
if (xferp->cmd_len != 6) |
690 |
debug(" (weird len=%i)", xferp->cmd_len); |
691 |
|
692 |
/* |
693 |
* bits 4..0 of cmd[1], and cmd[2] and cmd[3] hold the |
694 |
* logical block address. |
695 |
* |
696 |
* cmd[4] holds the number of logical blocks to |
697 |
* transfer. (Special case if the value is 0, actually |
698 |
* means 256.) |
699 |
*/ |
700 |
ofs = ((xferp->cmd[1] & 0x1f) << 16) + |
701 |
(xferp->cmd[2] << 8) + xferp->cmd[3]; |
702 |
retlen = xferp->cmd[4]; |
703 |
if (retlen == 0) |
704 |
retlen = 256; |
705 |
} else { |
706 |
if (xferp->cmd_len != 10) |
707 |
debug(" (weird len=%i)", xferp->cmd_len); |
708 |
|
709 |
/* |
710 |
* cmd[2..5] hold the logical block address. |
711 |
* cmd[7..8] holds the number of logical blocks to |
712 |
* transfer. (NOTE: If the value is 0 this means 0, |
713 |
* not 65536.) |
714 |
*/ |
715 |
ofs = ((uint64_t)xferp->cmd[2] << 24) + |
716 |
(xferp->cmd[3] << 16) + (xferp->cmd[4] << 8) + |
717 |
xferp->cmd[5]; |
718 |
retlen = (xferp->cmd[7] << 8) + xferp->cmd[8]; |
719 |
} |
720 |
|
721 |
size = retlen * d->logical_block_size; |
722 |
ofs *= d->logical_block_size; |
723 |
|
724 |
if (xferp->data_out_offset != size) { |
725 |
debug(", data_out == NULL, wanting %i bytes, \n\n", |
726 |
(int)size); |
727 |
xferp->data_out_len = size; |
728 |
return 2; |
729 |
} |
730 |
|
731 |
debug(", data_out != NULL, OK :-)"); |
732 |
|
733 |
debug("WRITE ofs=%i size=%i offset=%i\n", (int)ofs, |
734 |
(int)size, (int)xferp->data_out_offset); |
735 |
|
736 |
diskimage__internal_access(d, 1, ofs, |
737 |
xferp->data_out, size); |
738 |
|
739 |
/* TODO: how about return code? */ |
740 |
|
741 |
/* Is this really necessary? */ |
742 |
/* fsync(fileno(d->f)); */ |
743 |
|
744 |
diskimage__return_default_status_and_message(xferp); |
745 |
break; |
746 |
|
747 |
case SCSICMD_SYNCHRONIZE_CACHE: |
748 |
debug("SYNCHRONIZE_CACHE"); |
749 |
|
750 |
if (xferp->cmd_len != 10) |
751 |
debug(" (weird len=%i)", xferp->cmd_len); |
752 |
|
753 |
/* TODO: actualy care about cmd[] */ |
754 |
fsync(fileno(d->f)); |
755 |
|
756 |
diskimage__return_default_status_and_message(xferp); |
757 |
break; |
758 |
|
759 |
case SCSICMD_START_STOP_UNIT: |
760 |
debug("START_STOP_UNIT"); |
761 |
|
762 |
if (xferp->cmd_len != 6) |
763 |
debug(" (weird len=%i)", xferp->cmd_len); |
764 |
|
765 |
for (i=0; i<(ssize_t)xferp->cmd_len; i++) |
766 |
debug(" %02x", xferp->cmd[i]); |
767 |
|
768 |
/* TODO: actualy care about cmd[] */ |
769 |
|
770 |
diskimage__return_default_status_and_message(xferp); |
771 |
break; |
772 |
|
773 |
case SCSICMD_REQUEST_SENSE: |
774 |
debug("REQUEST_SENSE"); |
775 |
|
776 |
retlen = xferp->cmd[4]; |
777 |
|
778 |
/* TODO: bits 765 of buf[1] contains the LUN */ |
779 |
if (xferp->cmd[1] != 0x00) |
780 |
fatal("WARNING: REQUEST_SENSE with cmd[1]=0x%02x not" |
781 |
" yet implemented\n", (int)xferp->cmd[1]); |
782 |
|
783 |
if (retlen < 18) { |
784 |
fatal("WARNING: SCSI request sense len=%i, <18!\n", |
785 |
(int)retlen); |
786 |
retlen = 18; |
787 |
} |
788 |
|
789 |
/* Return data: */ |
790 |
scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in, |
791 |
retlen, 1); |
792 |
|
793 |
xferp->data_in[0] = 0x80 + 0x70;/* 0x80 = valid, |
794 |
0x70 = "current errors" */ |
795 |
xferp->data_in[2] = 0x00; /* SENSE KEY! */ |
796 |
|
797 |
if (d->filemark) { |
798 |
xferp->data_in[2] = 0x80; |
799 |
} |
800 |
debug(": [2]=0x%02x ", xferp->data_in[2]); |
801 |
|
802 |
printf(" XXX(!) \n"); |
803 |
|
804 |
/* TODO */ |
805 |
xferp->data_in[7] = retlen - 7; /* additional sense length */ |
806 |
/* TODO */ |
807 |
|
808 |
diskimage__return_default_status_and_message(xferp); |
809 |
break; |
810 |
|
811 |
case SCSICMD_READ_BLOCK_LIMITS: |
812 |
debug("READ_BLOCK_LIMITS"); |
813 |
|
814 |
retlen = 6; |
815 |
|
816 |
/* TODO: bits 765 of buf[1] contains the LUN */ |
817 |
if (xferp->cmd[1] != 0x00) |
818 |
fatal("WARNING: READ_BLOCK_LIMITS with cmd[1]=" |
819 |
"0x%02x not yet implemented\n", (int)xferp->cmd[1]); |
820 |
|
821 |
/* Return data: */ |
822 |
scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in, |
823 |
retlen, 1); |
824 |
|
825 |
/* |
826 |
* data[0] is reserved, data[1..3] contain the maximum block |
827 |
* length limit, data[4..5] contain the minimum limit. |
828 |
*/ |
829 |
|
830 |
{ |
831 |
int max_limit = 32768; |
832 |
int min_limit = 128; |
833 |
|
834 |
xferp->data_in[1] = (max_limit >> 16) & 255; |
835 |
xferp->data_in[2] = (max_limit >> 8) & 255; |
836 |
xferp->data_in[3] = max_limit & 255; |
837 |
xferp->data_in[4] = (min_limit >> 8) & 255; |
838 |
xferp->data_in[5] = min_limit & 255; |
839 |
} |
840 |
|
841 |
diskimage__return_default_status_and_message(xferp); |
842 |
break; |
843 |
|
844 |
case SCSICMD_REWIND: |
845 |
debug("REWIND"); |
846 |
|
847 |
/* TODO: bits 765 of buf[1] contains the LUN */ |
848 |
if ((xferp->cmd[1] & 0xe0) != 0x00) |
849 |
fatal("WARNING: REWIND with cmd[1]=0x%02x not yet " |
850 |
"implemented\n", (int)xferp->cmd[1]); |
851 |
|
852 |
/* Close and reopen. */ |
853 |
|
854 |
if (d->f != NULL) |
855 |
fclose(d->f); |
856 |
|
857 |
d->f = fopen(d->fname, d->writable? "r+" : "r"); |
858 |
if (d->f == NULL) { |
859 |
fprintf(stderr, "[ diskimage: could not (re)open " |
860 |
"'%s' ]\n", d->fname); |
861 |
/* TODO: return error */ |
862 |
} |
863 |
|
864 |
d->tape_offset = 0; |
865 |
d->tape_filenr = 0; |
866 |
d->filemark = 0; |
867 |
|
868 |
diskimage__return_default_status_and_message(xferp); |
869 |
break; |
870 |
|
871 |
case SCSICMD_SPACE: |
872 |
debug("SPACE"); |
873 |
|
874 |
/* TODO: bits 765 of buf[1] contains the LUN */ |
875 |
if ((xferp->cmd[1] & 0xe0) != 0x00) |
876 |
fatal("WARNING: SPACE with cmd[1]=0x%02x not yet " |
877 |
"implemented\n", (int)xferp->cmd[1]); |
878 |
|
879 |
/* |
880 |
* Bits 2..0 of buf[1] contain the 'code' which describes how |
881 |
* spacing should be done, and buf[2..4] contain the number of |
882 |
* operations. |
883 |
*/ |
884 |
debug("[ SPACE: buf[] = %02x %02x %02x %02x %02x %02x ]\n", |
885 |
xferp->cmd[0], |
886 |
xferp->cmd[1], |
887 |
xferp->cmd[2], |
888 |
xferp->cmd[3], |
889 |
xferp->cmd[4], |
890 |
xferp->cmd[5]); |
891 |
|
892 |
switch (xferp->cmd[1] & 7) { |
893 |
case 1: /* Seek to a different file nr: */ |
894 |
{ |
895 |
int diff = (xferp->cmd[2] << 16) + |
896 |
(xferp->cmd[3] << 8) + xferp->cmd[4]; |
897 |
|
898 |
/* Negative seek offset: */ |
899 |
if (diff & (1 << 23)) |
900 |
diff = - (16777216 - diff); |
901 |
|
902 |
d->tape_filenr += diff; |
903 |
} |
904 |
|
905 |
/* At end of file, switch to the next tape file: */ |
906 |
if (d->filemark) { |
907 |
d->tape_filenr ++; |
908 |
d->filemark = 0; |
909 |
} |
910 |
|
911 |
debug("{ switching to tape file %i }", d->tape_filenr); |
912 |
diskimage__switch_tape(d); |
913 |
d->filemark = 0; |
914 |
break; |
915 |
default: |
916 |
fatal("[ diskimage.c: unimplemented SPACE type %i ]\n", |
917 |
xferp->cmd[1] & 7); |
918 |
} |
919 |
|
920 |
diskimage__return_default_status_and_message(xferp); |
921 |
break; |
922 |
|
923 |
case SCSICDROM_READ_SUBCHANNEL: |
924 |
/* |
925 |
* According to |
926 |
* http://mail-index.netbsd.org/port-i386/1997/03/03/0010.html: |
927 |
* |
928 |
* "The READ_CD_CAPACITY, READ_SUBCHANNEL, and MODE_SELECT |
929 |
* commands have the same opcode in SCSI or ATAPI, but don't |
930 |
* have the same command structure"... |
931 |
* |
932 |
* TODO: This still doesn't work. Hm. |
933 |
*/ |
934 |
retlen = 48; |
935 |
|
936 |
debug("CDROM_READ_SUBCHANNEL/READ_CD_CAPACITY, cmd[1]=0x%02x", |
937 |
xferp->cmd[1]); |
938 |
|
939 |
/* Return data: */ |
940 |
scsi_transfer_allocbuf(&xferp->data_in_len, |
941 |
&xferp->data_in, retlen, 1); |
942 |
|
943 |
diskimage_recalc_size(d); |
944 |
|
945 |
size = d->total_size / d->logical_block_size; |
946 |
if (d->total_size & (d->logical_block_size-1)) |
947 |
size ++; |
948 |
|
949 |
xferp->data_in[0] = (size >> 24) & 255; |
950 |
xferp->data_in[1] = (size >> 16) & 255; |
951 |
xferp->data_in[2] = (size >> 8) & 255; |
952 |
xferp->data_in[3] = size & 255; |
953 |
|
954 |
xferp->data_in[4] = (d->logical_block_size >> 24) & 255; |
955 |
xferp->data_in[5] = (d->logical_block_size >> 16) & 255; |
956 |
xferp->data_in[6] = (d->logical_block_size >> 8) & 255; |
957 |
xferp->data_in[7] = d->logical_block_size & 255; |
958 |
|
959 |
diskimage__return_default_status_and_message(xferp); |
960 |
break; |
961 |
|
962 |
case SCSICDROM_READ_TOC: |
963 |
debug("(CDROM_READ_TOC: "); |
964 |
debug("lun=%i msf=%i ", |
965 |
xferp->cmd[1] >> 5, (xferp->cmd[1] >> 1) & 1); |
966 |
debug("starting_track=%i ", xferp->cmd[6]); |
967 |
retlen = xferp->cmd[7] * 256 + xferp->cmd[8]; |
968 |
debug("allocation_len=%i)\n", retlen); |
969 |
|
970 |
/* Return data: */ |
971 |
scsi_transfer_allocbuf(&xferp->data_in_len, |
972 |
&xferp->data_in, retlen, 1); |
973 |
|
974 |
xferp->data_in[0] = 0; |
975 |
xferp->data_in[1] = 10; |
976 |
xferp->data_in[2] = 0; /* First track. */ |
977 |
xferp->data_in[3] = 0; /* Last track. */ |
978 |
|
979 |
/* Track 0 data: */ |
980 |
xferp->data_in[4] = 0x00; /* Reserved. */ |
981 |
xferp->data_in[5] = 0x04; /* ADR + CTRL: |
982 |
Data, not audio */ |
983 |
xferp->data_in[6] = 0x00; /* Track nr */ |
984 |
xferp->data_in[7] = 0x00; /* Reserved */ |
985 |
/* 8..11 = absolute CDROM address */ |
986 |
|
987 |
diskimage__return_default_status_and_message(xferp); |
988 |
break; |
989 |
|
990 |
case SCSICDROM_READ_DISCINFO: |
991 |
/* (Patch from Håvard Eidnes.) */ |
992 |
debug("CDROM_READ_DISCINFO, cmd[1]=0x%02x", xferp->cmd[1]); |
993 |
retlen = 34; |
994 |
scsi_transfer_allocbuf(&xferp->data_in_len, |
995 |
&xferp->data_in, retlen, 1); |
996 |
|
997 |
/* TODO: Make this more generic! */ |
998 |
xferp->data_in[0] = retlen-2; /* length of info, excl len */ |
999 |
xferp->data_in[1] = 0; /* length of info-(len field), msb */ |
1000 |
xferp->data_in[2] = 0xE; /* 11=complete ses., 10=fin disc */ |
1001 |
xferp->data_in[3] = 0; /* First track on disc */ |
1002 |
xferp->data_in[4] = 1; /* Number of sessions, lsb */ |
1003 |
xferp->data_in[5] = 0; /* first_track_last_session_lsb */ |
1004 |
xferp->data_in[6] = 0; /* last_track_last_session_lsb */ |
1005 |
xferp->data_in[7] = 0x20;/* various flags */ |
1006 |
xferp->data_in[8] = 0; /* CD-ROM disc */ |
1007 |
xferp->data_in[9] = 1; /* num sessions, msb */ |
1008 |
xferp->data_in[10] = 0; /* first_track_last_session_msb */ |
1009 |
xferp->data_in[11] = 0; /* last_track_last_session_msb */ |
1010 |
{ |
1011 |
int i; |
1012 |
/* Lead-in data, for completed cd-rom: */ |
1013 |
for (i=16; i<=23; i++) |
1014 |
xferp->data_in[i] = 0xff; |
1015 |
} |
1016 |
|
1017 |
diskimage__return_default_status_and_message(xferp); |
1018 |
break; |
1019 |
|
1020 |
case SCSICDROM_READ_TRACKINFO: |
1021 |
/* (Patch from Håvard Eidnes.) */ |
1022 |
debug("CDROM_READ_TRACKINFO"); |
1023 |
retlen = 36; |
1024 |
scsi_transfer_allocbuf(&xferp->data_in_len, |
1025 |
&xferp->data_in, retlen, 1); |
1026 |
|
1027 |
diskimage_recalc_size(d); |
1028 |
|
1029 |
size = d->total_size / d->logical_block_size; |
1030 |
if (d->total_size & (d->logical_block_size-1)) |
1031 |
size ++; |
1032 |
|
1033 |
/* TODO: Make more generic? */ |
1034 |
/* TODO: Don't use magic values. */ |
1035 |
xferp->data_in[0] = retlen-2; /* length of info, excl len */ |
1036 |
xferp->data_in[1] = 0; /* length of info, msb */ |
1037 |
xferp->data_in[2] = 1; /* track#, lsb */ |
1038 |
xferp->data_in[3] = 1; /* session#, lsb */ |
1039 |
xferp->data_in[4] = 0; /* reserved */ |
1040 |
xferp->data_in[5] = 0x6; /* trk mode: unintr. data, |
1041 |
copyable */ |
1042 |
xferp->data_in[6] = 0x81; /* trk info: RT + trk mode */ |
1043 |
xferp->data_in[7] = 0x2; /* last rec=valid, next w=not |
1044 |
valid */ |
1045 |
{ |
1046 |
/* |
1047 |
* track start, next writable, free blcks, |
1048 |
* blocking factor |
1049 |
*/ |
1050 |
int i; |
1051 |
for(i=8; i<=23; i++) |
1052 |
xferp->data_in[i] = 0; |
1053 |
} |
1054 |
|
1055 |
/* Track size: */ |
1056 |
xferp->data_in[24] = (size >> 24) & 0xff; |
1057 |
xferp->data_in[25] = (size >> 16) & 0xff; |
1058 |
xferp->data_in[26] = (size >> 8) & 0xff; |
1059 |
xferp->data_in[27] = size & 0xff; |
1060 |
|
1061 |
/* Last recorded address, only for dvd; zero out the rest: */ |
1062 |
{ |
1063 |
int i; |
1064 |
for (i=28; i<=35; i++) |
1065 |
xferp->data_in[i] = 0; |
1066 |
} |
1067 |
|
1068 |
diskimage__return_default_status_and_message(xferp); |
1069 |
break; |
1070 |
|
1071 |
case SCSICMD_MODE_SELECT: |
1072 |
debug("[ SCSI MODE_SELECT: "); |
1073 |
|
1074 |
/* |
1075 |
* TODO: |
1076 |
* |
1077 |
* This is super-hardcoded for NetBSD's usage of mode_select |
1078 |
* to set the size of CDROM sectors to 2048. |
1079 |
*/ |
1080 |
|
1081 |
if (xferp->data_out_offset == 0) { |
1082 |
xferp->data_out_len = 12; /* TODO */ |
1083 |
debug("data_out == NULL, wanting %i bytes ]\n", |
1084 |
(int)xferp->data_out_len); |
1085 |
return 2; |
1086 |
} |
1087 |
|
1088 |
debug("data_out!=NULL (OK), "); |
1089 |
|
1090 |
/* TODO: Care about cmd? */ |
1091 |
|
1092 |
/* Set sector size to 2048: */ |
1093 |
/* 00 05 00 08 00 03 ca 40 00 00 08 00 */ |
1094 |
if (xferp->data_out[0] == 0x00 && |
1095 |
xferp->data_out[1] == 0x05 && |
1096 |
xferp->data_out[2] == 0x00 && |
1097 |
xferp->data_out[3] == 0x08) { |
1098 |
d->logical_block_size = |
1099 |
(xferp->data_out[9] << 16) + |
1100 |
(xferp->data_out[10] << 8) + |
1101 |
xferp->data_out[11]; |
1102 |
debug("[ setting logical_block_size to %i ]\n", |
1103 |
d->logical_block_size); |
1104 |
} else { |
1105 |
int i; |
1106 |
fatal("[ unknown MODE_SELECT: cmd ="); |
1107 |
for (i=0; i<(ssize_t)xferp->cmd_len; i++) |
1108 |
fatal(" %02x", xferp->cmd[i]); |
1109 |
fatal(", data_out ="); |
1110 |
for (i=0; i<(ssize_t)xferp->data_out_len; i++) |
1111 |
fatal(" %02x", xferp->data_out[i]); |
1112 |
fatal(" ]"); |
1113 |
} |
1114 |
|
1115 |
debug(" ]\n"); |
1116 |
diskimage__return_default_status_and_message(xferp); |
1117 |
break; |
1118 |
|
1119 |
case SCSICMD_PREVENT_ALLOW_REMOVE: |
1120 |
debug("[ SCSI 0x%02x Prevent/allow medium removal: " |
1121 |
"TODO ]\n", xferp->cmd[0]); |
1122 |
|
1123 |
diskimage__return_default_status_and_message(xferp); |
1124 |
break; |
1125 |
|
1126 |
case 0xbd: |
1127 |
fatal("[ SCSI 0x%02x (len %i), TODO: ", xferp->cmd[0], |
1128 |
xferp->cmd_len); |
1129 |
for (i=0; i<(ssize_t)xferp->cmd_len; i++) |
1130 |
fatal(" %02x", xferp->cmd[i]); |
1131 |
fatal(" ]\n"); |
1132 |
|
1133 |
/* |
1134 |
* Used by Windows NT? |
1135 |
* |
1136 |
* Not documented in http://www.danbbs.dk/~dino/ |
1137 |
* SCSI/SCSI2-D.html. |
1138 |
* Google gave the answer "MECHANISM_STATUS" for ATAPI. Hm. |
1139 |
*/ |
1140 |
|
1141 |
if (xferp->cmd_len < 12) { |
1142 |
fatal("WEIRD LEN?\n"); |
1143 |
retlen = 8; |
1144 |
} else { |
1145 |
retlen = xferp->cmd[8] * 256 + xferp->cmd[9]; |
1146 |
} |
1147 |
|
1148 |
/* Return data: */ |
1149 |
scsi_transfer_allocbuf(&xferp->data_in_len, |
1150 |
&xferp->data_in, retlen, 1); |
1151 |
|
1152 |
diskimage__return_default_status_and_message(xferp); |
1153 |
|
1154 |
break; |
1155 |
|
1156 |
default: |
1157 |
fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n", |
1158 |
xferp->cmd[0], id); |
1159 |
exit(1); |
1160 |
} |
1161 |
debug(" ]\n"); |
1162 |
|
1163 |
return 1; |
1164 |
} |
1165 |
|
1166 |
|