/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 38 - (hide 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 dpavlin 38 /*
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