/[gxemul]/trunk/src/disk/diskimage_scsicmd.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/src/disk/diskimage_scsicmd.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 38 - (show annotations)
Mon Oct 8 16:21:53 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 32000 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1515 2007/04/14 05:39:46 debug Exp $
20070324	Adding a "--debug" option to the configure script, to disable
		optimizations in unstable development builds.
		Moving out SCSI-specific stuff from diskimage.c into a new
		diskimage_scsicmd.c.
		Applying Hĺvard Eidnes' patch for SCSICDROM_READ_DISKINFO and
		SCSICDROM_READ_TRACKINFO. (Not really tested yet.)
		Implementing disk image "overlays" (to allow simple roll-back
		to previous disk state). Adding a 'V' disk flag for this, and
		updating the man page and misc.html.
20070325	Stability fix to cpu_dyntrans.c, when multiple physical pages
		share the same initial table entry. (The ppp == NULL check
		should be physpage_ofs == 0.) Bug found by analysing GXemul
		against a version patched for Godson.
		Fixing a second occurance of the same problem (also in
		cpu_dyntrans.c).
		Fixing a MAJOR physical page leak in cpu_dyntrans.c; pages
		weren't _added_ to the set of translated pages, they _replaced_
		all previous pages. It's amazing that this bug has been able
		to live for this long. (Triggered when emulating >128MB RAM.)
20070326	Removing the GDB debugging stub support; it was too hackish
		and ugly.
20070328	Moving around some native code generation skeleton code.
20070329	The -lm check in the configure script now also checks for sin()
		in addition to sqrt(). (Thanks to Nigel Horne for noticing that
		sqrt was not enough on Fedora Core 6.) (Not verified yet.)
20070330	Fixing an indexing bug in dev_sh4.c, found by using gcc version
		4.3.0 20070323.
20070331	Some more experimentation with native code generation.
20070404	Attempting to fix some more SH4 SCIF interrupt bugs; rewriting
		the SH interrupt assertion/deassertion code somewhat.
20070410	Splitting src/file.c into separate files in src/file/.
		Cleanup: Removing the dummy TS7200, Walnut, PB1000, and
		Meshcube emulation modes, and dev_epcom and dev_au1x00.
		Removing the experimental CHIP8/RCA180x code; it wasn't really
		working much lately, anyway. It was fun while it lasted.
		Also removing the experimental Transputer CPU support.
20070412	Moving the section about how the dynamic translation system
		works from intro.html to a separate translation.html file.
		Minor SH fixes; attempting to get OpenBSD/landisk to run
		without randomly bugging out, but no success yet.
20070413	SH SCI (serial bit interface) should now work together with a
		(new) RS5C313 clock device (for Landisk emulation).
20070414	Moving Redhat/MIPS down from supported to experimental, in
		guestoses.html.
		Preparing for a new release; doing some regression testing etc.

==============  RELEASE 0.4.5  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26