/[gxemul]/trunk/src/disk/diskimage.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

Diff of /trunk/src/disk/diskimage.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 37 by dpavlin, Mon Oct 8 16:21:34 2007 UTC revision 38 by dpavlin, Mon Oct 8 16:21:53 2007 UTC
# Line 25  Line 25 
25   *  SUCH DAMAGE.   *  SUCH DAMAGE.
26   *   *
27   *   *
28   *  $Id: diskimage.c,v 1.1 2007/03/16 14:13:59 debug Exp $   *  $Id: diskimage.c,v 1.5 2007/03/26 03:01:09 debug Exp $
29   *   *
30   *  Disk image support.   *  Disk image support.
31   *   *
  *  TODO:  There's probably a bug in the tape support:  
  *         Let's say there are 10240 bytes left in a file, and 10240  
  *         bytes are read. Then feof() is not true yet (?), so the next  
  *         read will also return 10240 bytes (but all zeroes), and then after  
  *         that return feof (which results in a filemark).  This is probably  
  *         trivial to fix, but I don't feel like it right now.  
  *  
32   *  TODO:  diskimage_remove()? This would be useful for floppies in PC-style   *  TODO:  diskimage_remove()? This would be useful for floppies in PC-style
33   *         machines, where disks may need to be swapped during boot etc.   *         machines, where disks may need to be swapped during boot etc.
34   */   */
# Line 59  extern int single_step; Line 52  extern int single_step;
52    
53  static char *diskimage_types[] = DISKIMAGE_TYPES;  static char *diskimage_types[] = DISKIMAGE_TYPES;
54    
 static struct scsi_transfer *first_free_scsi_transfer_alloc = NULL;  
   
55    
56  /**************************************************************************/  /**************************************************************************/
57    
# Line 109  static int my_fseek(FILE *f, off_t offse Line 100  static int my_fseek(FILE *f, off_t offse
100    
101    
102  /*  /*
103   *  scsi_transfer_alloc():   *  diskimage_exist():
104   *   *
105   *  Allocates memory for a new scsi_transfer struct, and fills it with   *  Returns 1 if the specified disk id (for a specific type) exists, 0
106   *  sane data (NULL pointers).   *  otherwise.
  *  The return value is a pointer to the new struct.  If allocation  
  *  failed, the program exits.  
107   */   */
108  struct scsi_transfer *scsi_transfer_alloc(void)  int diskimage_exist(struct machine *machine, int id, int type)
109  {  {
110          struct scsi_transfer *p;          struct diskimage *d = machine->first_diskimage;
111    
112          if (first_free_scsi_transfer_alloc != NULL) {          while (d != NULL) {
113                  p = first_free_scsi_transfer_alloc;                  if (d->type == type && d->id == id)
114                  first_free_scsi_transfer_alloc = p->next_free;                          return 1;
115          } else {                  d = d->next;
                 p = malloc(sizeof(struct scsi_transfer));  
                 if (p == NULL) {  
                         fprintf(stderr, "scsi_transfer_alloc(): out "  
                             "of memory\n");  
                         exit(1);  
                 }  
116          }          }
117            return 0;
         memset(p, 0, sizeof(struct scsi_transfer));  
   
         return p;  
118  }  }
119    
120    
121  /*  /*
122   *  scsi_transfer_free():   *  diskimage_add_overlay():
123   *   *
124   *  Frees the space used by a scsi_transfer struct.  All buffers refered   *  Opens an overlay data file and its corresponding bitmap file, and adds
125   *  to by the scsi_transfer struct are freed.   *  the overlay to a disk image.
126   */   */
127  void scsi_transfer_free(struct scsi_transfer *p)  void diskimage_add_overlay(struct diskimage *d, char *overlay_basename)
128  {  {
129          if (p == NULL) {          struct diskimage_overlay overlay;
130                  fprintf(stderr, "scsi_transfer_free(): p == NULL\n");          size_t bitmap_name_len = strlen(overlay_basename) + 20;
131            char *bitmap_name = malloc(bitmap_name_len);
132    
133            if (bitmap_name == NULL) {
134                    fprintf(stderr, "out of memory\n");
135                  exit(1);                  exit(1);
136          }          }
137            snprintf(bitmap_name, bitmap_name_len, "%s.map", overlay_basename);
138    
139          if (p->msg_out != NULL)          overlay.overlay_basename = strdup(overlay_basename);
140                  free(p->msg_out);          overlay.f_data = fopen(overlay_basename, d->writable? "r+" : "r");
141          if (p->cmd != NULL)          if (overlay.f_data == NULL) {
142                  free(p->cmd);                  perror(overlay_basename);
143          if (p->data_out != NULL)                  exit(1);
                 free(p->data_out);  
   
         if (p->data_in != NULL)  
                 free(p->data_in);  
         if (p->msg_in != NULL)  
                 free(p->msg_in);  
         if (p->status != NULL)  
                 free(p->status);  
   
         p->next_free = first_free_scsi_transfer_alloc;  
         first_free_scsi_transfer_alloc = p;  
 }  
   
   
 /*  
  *  scsi_transfer_allocbuf():  
  *  
  *  Helper function, used by diskimage_scsicommand(), and SCSI controller  
  *  devices.  Example of usage:  
  *  
  *      scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1);  
  */  
 void scsi_transfer_allocbuf(size_t *lenp, unsigned char **pp, size_t want_len,  
         int clearflag)  
 {  
         unsigned char *p = (*pp);  
   
         if (p != NULL) {  
                 printf("WARNING! scsi_transfer_allocbuf(): old pointer "  
                     "was not NULL, freeing it now\n");  
                 free(p);  
144          }          }
145    
146          (*lenp) = want_len;          overlay.f_bitmap = fopen(bitmap_name, d->writable? "r+" : "r");
147          if ((p = malloc(want_len)) == NULL) {          if (overlay.f_bitmap == NULL) {
148                  fprintf(stderr, "scsi_transfer_allocbuf(): out of "                  perror(bitmap_name);
149                      "memory trying to allocate %li bytes\n", (long)want_len);                  fprintf(stderr, "Please create the map file first.\n");
150                  exit(1);                  exit(1);
151          }          }
152    
153          if (clearflag)          d->nr_of_overlays ++;
154                  memset(p, 0, want_len);          d->overlays = realloc(d->overlays, sizeof(struct diskimage_overlay)
155                * d->nr_of_overlays);
156          (*pp) = p;          if (d->overlays == NULL) {
157  }                  fprintf(stderr, "out of memory\n");
158                    exit(1);
159            }
 /**************************************************************************/  
160    
161            d->overlays[d->nr_of_overlays - 1] = overlay;
162    
163  /*          free(bitmap_name);
  *  diskimage_exist():  
  *  
  *  Returns 1 if the specified disk id (for a specific type) exists, 0  
  *  otherwise.  
  */  
 int diskimage_exist(struct machine *machine, int id, int type)  
 {  
         struct diskimage *d = machine->first_diskimage;  
   
         while (d != NULL) {  
                 if (d->type == type && d->id == id)  
                         return 1;  
                 d = d->next;  
         }  
         return 0;  
164  }  }
165    
166    
# Line 231  int diskimage_exist(struct machine *mach Line 170  int diskimage_exist(struct machine *mach
170   *  Recalculate a disk's size by stat()-ing it.   *  Recalculate a disk's size by stat()-ing it.
171   *  d is assumed to be non-NULL.   *  d is assumed to be non-NULL.
172   */   */
173  static void diskimage_recalc_size(struct diskimage *d)  void diskimage_recalc_size(struct diskimage *d)
174  {  {
175          struct stat st;          struct stat st;
176          int res;          int res;
# Line 327  void diskimage_getchs(struct machine *ma Line 266  void diskimage_getchs(struct machine *ma
266    
267    
268  /*  /*
  *  diskimage__return_default_status_and_message():  
  *  
  *  Set the status and msg_in parts of a scsi_transfer struct  
  *  to default values (msg_in = 0x00, status = 0x00).  
  */  
 static void diskimage__return_default_status_and_message(  
         struct scsi_transfer *xferp)  
 {  
         scsi_transfer_allocbuf(&xferp->status_len, &xferp->status, 1, 0);  
         xferp->status[0] = 0x00;  
         scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1, 0);  
         xferp->msg_in[0] = 0x00;  
 }  
   
   
 /*  
  *  diskimage__switch_tape():  
  *  
  *  Used by the SPACE command.  (d is assumed to be non-NULL.)  
  */  
 static void diskimage__switch_tape(struct diskimage *d)  
 {  
         char tmpfname[1000];  
   
         snprintf(tmpfname, sizeof(tmpfname), "%s.%i",  
             d->fname, d->tape_filenr);  
         tmpfname[sizeof(tmpfname)-1] = '\0';  
   
         if (d->f != NULL)  
                 fclose(d->f);  
   
         d->f = fopen(tmpfname, d->writable? "r+" : "r");  
         if (d->f == NULL) {  
                 fprintf(stderr, "[ diskimage__switch_tape(): could not "  
                     "(re)open '%s' ]\n", tmpfname);  
                 /*  TODO: return error  */  
         }  
         d->tape_offset = 0;  
 }  
   
   
 /*  
269   *  diskimage_access__cdrom():   *  diskimage_access__cdrom():
270   *   *
271   *  This is a special-case function, called from diskimage__internal_access().   *  This is a special-case function, called from diskimage__internal_access().
# Line 419  static size_t diskimage_access__cdrom(st Line 316  static size_t diskimage_access__cdrom(st
316  }  }
317    
318    
319  /*  /*  Helper function.  */
320   *  diskimage__internal_access():  static void overlay_set_block_in_use(struct diskimage *d,
321   *          int overlay_nr, off_t ofs)
  *  Read from or write to a struct diskimage.  
  *  
  *  Returns 1 if the access completed successfully, 0 otherwise.  
  */  
 static int diskimage__internal_access(struct diskimage *d, int writeflag,  
         off_t offset, unsigned char *buf, size_t len)  
322  {  {
323          ssize_t lendone;          off_t bit_nr = ofs / OVERLAY_BLOCK_SIZE;
324            off_t bitmap_file_offset = bit_nr / 8;
325          int res;          int res;
326            unsigned char data;
327    
328          if (buf == NULL) {          res = my_fseek(d->overlays[overlay_nr].f_bitmap,
329                  fprintf(stderr, "diskimage__internal_access(): buf = NULL\n");              bitmap_file_offset, SEEK_SET);
330            if (res) {
331                    perror("my_fseek");
332                    fprintf(stderr, "Could not seek in bitmap file?"
333                        " offset = %lli, read\n", (long long)bitmap_file_offset);
334                  exit(1);                  exit(1);
335          }          }
         if (len == 0)  
                 return 1;  
         if (d->f == NULL)  
                 return 0;  
   
         res = my_fseek(d->f, offset, SEEK_SET);  
         if (res != 0) {  
                 fatal("[ diskimage__internal_access(): fseek() failed on "  
                     "disk id %i \n", d->id);  
                 return 0;  
         }  
   
         if (writeflag) {  
                 if (!d->writable)  
                         return 0;  
   
                 lendone = fwrite(buf, 1, len, d->f);  
         } else {  
                 /*  
                  *  Special case for CD-ROMs. Actually, this is not needed  
                  *  for .iso images, only for physical CDROMS on some OSes,  
                  *  such as FreeBSD.  
                  */  
                 if (d->is_a_cdrom)  
                         lendone = diskimage_access__cdrom(d, offset, buf, len);  
                 else  
                         lendone = fread(buf, 1, len, d->f);  
336    
337                  if (lendone < (ssize_t)len)          /*  Read the original bitmap data, and OR in the new bit:  */
338                          memset(buf + lendone, 0, len - lendone);          res = fread(&data, 1, 1, d->overlays[overlay_nr].f_bitmap);
339            if (res != 1)
340                    data = 0x00;
341    
342            data |= (1 << (bit_nr & 7));
343    
344            /*  Write it back:  */
345            res = my_fseek(d->overlays[overlay_nr].f_bitmap,
346                bitmap_file_offset, SEEK_SET);
347            if (res) {
348                    perror("my_fseek");
349                    fprintf(stderr, "Could not seek in bitmap file?"
350                        " offset = %lli, write\n", (long long)bitmap_file_offset);
351                    exit(1);
352          }          }
353            res = fwrite(&data, 1, 1, d->overlays[overlay_nr].f_bitmap);
354          /*  Warn about non-complete data transfers:  */          if (res != 1) {
355          if (lendone != (ssize_t)len) {                  fprintf(stderr, "Could not write to bitmap file. Aborting.\n");
356  #ifdef UNSTABLE_DEVEL                  exit(1);
                 fatal("[ diskimage__internal_access(): disk_id %i, offset %lli"  
                     ", transfer not completed. len=%i, len_done=%i ]\n",  
                     d->id, (long long)offset, (int)len, (int)lendone);  
 #endif  
                 return 0;  
357          }          }
   
         return 1;  
358  }  }
359    
360    
361  /*  /*  Helper function.  */
362   *  diskimage_scsicommand():  static int overlay_has_block(struct diskimage *d, int overlay_nr, off_t ofs)
  *  
  *  Perform a SCSI command on a disk image.  
  *  
  *  The xferp points to a scsi_transfer struct, containing msg_out, command,  
  *  and data_out coming from the SCSI controller device.  This function  
  *  interprets the command, and (if necessary) creates responses in  
  *  data_in, msg_in, and status.  
  *  
  *  Returns:  
  *      2 if the command expects data from the DATA_OUT phase,  
  *      1 if otherwise ok,  
  *      0 on error.  
  */  
 int diskimage_scsicommand(struct cpu *cpu, int id, int type,  
         struct scsi_transfer *xferp)  
363  {  {
364          char namebuf[16];          off_t bit_nr = ofs / OVERLAY_BLOCK_SIZE;
365          int retlen, i, q;          off_t bitmap_file_offset = bit_nr / 8;
366          uint64_t size;          int res;
367          int64_t ofs;          unsigned char data;
         int pagecode;  
         struct machine *machine = cpu->machine;  
         struct diskimage *d;  
   
         if (machine == NULL) {  
                 fatal("[ diskimage_scsicommand(): machine == NULL ]\n");  
                 return 0;  
         }  
   
         d = machine->first_diskimage;  
         while (d != NULL) {  
                 if (d->type == type && d->id == id)  
                         break;  
                 d = d->next;  
         }  
         if (d == NULL) {  
                 fprintf(stderr, "[ diskimage_scsicommand(): %s "  
                     " id %i not connected? ]\n", diskimage_types[type], id);  
         }  
368    
369          if (xferp->cmd == NULL) {          res = my_fseek(d->overlays[overlay_nr].f_bitmap,
370                  fatal("[ diskimage_scsicommand(): cmd == NULL ]\n");              bitmap_file_offset, SEEK_SET);
371            if (res != 0)
372                  return 0;                  return 0;
         }  
373    
374          if (xferp->cmd_len < 1) {          /*  The seek succeeded, now read the bit:  */
375                  fatal("[ diskimage_scsicommand(): cmd_len == %i ]\n",          res = fread(&data, 1, 1, d->overlays[overlay_nr].f_bitmap);
376                      xferp->cmd_len);          if (res != 1)
377                  return 0;                  return 0;
         }  
   
         debug("[ diskimage_scsicommand(id=%i) cmd=0x%02x: ",  
             id, xferp->cmd[0]);  
378    
379  #if 0          if (data & (1 << (bit_nr & 7)))
380          fatal("[ diskimage_scsicommand(id=%i) cmd=0x%02x len=%i:",                  return 1;
             id, xferp->cmd[0], xferp->cmd_len);  
         for (i=0; i<xferp->cmd_len; i++)  
                 fatal(" %02x", xferp->cmd[i]);  
         fatal("\n");  
 if (xferp->cmd_len > 7 && xferp->cmd[5] == 0x11)  
         single_step = ENTER_SINGLE_STEPPING;  
 #endif  
381    
382  #if 0          return 0;
 {  
         static FILE *f = NULL;  
         if (f == NULL)  
                 f = fopen("scsi_log.txt", "w");  
         if (f != NULL) {  
                 int i;  
                 fprintf(f, "id=%i cmd =", id);  
                 for (i=0; i<xferp->cmd_len; i++)  
                         fprintf(f, " %02x", xferp->cmd[i]);  
                 fprintf(f, "\n");  
                 fflush(f);  
         }  
383  }  }
 #endif  
   
         switch (xferp->cmd[0]) {  
   
         case SCSICMD_TEST_UNIT_READY:  
                 debug("TEST_UNIT_READY");  
                 if (xferp->cmd_len != 6)  
                         debug(" (weird len=%i)", xferp->cmd_len);  
   
                 /*  TODO: bits 765 of buf[1] contains the LUN  */  
                 if (xferp->cmd[1] != 0x00)  
                         fatal("WARNING: TEST_UNIT_READY with cmd[1]=0x%02x"  
                             " not yet implemented\n", (int)xferp->cmd[1]);  
   
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
384    
         case SCSICMD_INQUIRY:  
                 debug("INQUIRY");  
                 if (xferp->cmd_len != 6)  
                         debug(" (weird len=%i)", xferp->cmd_len);  
                 if (xferp->cmd[1] != 0x00) {  
                         debug("WARNING: INQUIRY with cmd[1]=0x%02x not yet "  
                             "implemented\n", (int)xferp->cmd[1]);  
385    
386                          break;  /*
387                  }   *  fwrite_helper():
388     *
389                  /*  Return values:  */   *  Internal helper function. Writes to a disk image file, or if the
390                  retlen = xferp->cmd[4];   *  disk image has overlays, to the last overlay.
391                  if (retlen < 36) {   */
392                          fatal("WARNING: SCSI inquiry len=%i, <36!\n", retlen);  static size_t fwrite_helper(off_t offset, unsigned char *buf,
393                          retlen = 36;          size_t len, struct diskimage *d)
394    {
395            off_t curofs;
396    
397            /*  Fast return-path for the case when no overlays are used:  */
398            if (d->nr_of_overlays == 0) {
399                    int res = my_fseek(d->f, offset, SEEK_SET);
400                    if (res != 0) {
401                            fatal("[ diskimage__internal_access(): fseek() failed"
402                                " on disk id %i \n", d->id);
403                            return 0;
404                  }                  }
405    
406                  /*  Return data:  */                  return fwrite(buf, 1, len, d->f);
407                  scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,          }
                     retlen, 1);  
                 xferp->data_in[0] = 0x00;  /*  0x00 = Direct-access disk  */  
                 xferp->data_in[1] = 0x00;  /*  0x00 = non-removable  */  
                 xferp->data_in[2] = 0x02;  /*  SCSI-2  */  
 #if 0  
 xferp->data_in[3] = 0x02;       /*  Response data format = SCSI-2  */  
 #endif  
                 xferp->data_in[4] = retlen - 4; /*  Additional length  */  
 xferp->data_in[4] = 0x2c - 4;   /*  Additional length  */  
                 xferp->data_in[6] = 0x04;  /*  ACKREQQ  */  
                 xferp->data_in[7] = 0x60;  /*  WBus32, WBus16  */  
   
                 /*  These are padded with spaces:  */  
   
                 memcpy(xferp->data_in+8,  "GXemul  ", 8);  
                 if (diskimage_getname(cpu->machine, id,  
                     type, namebuf, sizeof(namebuf))) {  
                         size_t i;  
                         for (i=0; i<sizeof(namebuf); i++)  
                                 if (namebuf[i] == 0) {  
                                         for (; i<sizeof(namebuf); i++)  
                                                 namebuf[i] = ' ';  
                                         break;  
                                 }  
                         memcpy(xferp->data_in+16, namebuf, 16);  
                 } else  
                         memcpy(xferp->data_in+16, "DISK            ", 16);  
                 memcpy(xferp->data_in+32, "0   ", 4);  
   
                 /*  
                  *  Some Ultrix kernels want specific responses from  
                  *  the drives.  
                  */  
   
                 if (machine->machine_type == MACHINE_PMAX) {  
                         /*  DEC, RZ25 (rev 0900) = 832527 sectors  */  
                         /*  DEC, RZ58 (rev 2000) = 2698061 sectors  */  
                         memcpy(xferp->data_in+8,  "DEC     ", 8);  
                         memcpy(xferp->data_in+16, "RZ58     (C) DEC", 16);  
                         memcpy(xferp->data_in+32, "2000", 4);  
                 }  
   
                 /*  Some data is different for CD-ROM drives:  */  
                 if (d->is_a_cdrom) {  
                         xferp->data_in[0] = 0x05;  /*  0x05 = CD-ROM  */  
                         xferp->data_in[1] = 0x80;  /*  0x80 = removable  */  
                         /*  memcpy(xferp->data_in+16, "CD-ROM          ", 16);*/  
   
                         if (machine->machine_type == MACHINE_PMAX) {  
                                 /*  SONY, CD-ROM:  */  
                                 memcpy(xferp->data_in+8, "SONY    ", 8);  
                                 memcpy(xferp->data_in+16,  
                                     "CD-ROM          ", 16);  
   
                                 /*  ... or perhaps this:  */  
                                 memcpy(xferp->data_in+8, "DEC     ", 8);  
                                 memcpy(xferp->data_in+16,  
                                     "RRD42   (C) DEC ", 16);  
                                 memcpy(xferp->data_in+32, "4.5d", 4);  
                         } else if (machine->machine_type == MACHINE_ARC) {  
                                 /*  NEC, CD-ROM:  */  
                                 memcpy(xferp->data_in+8, "NEC     ", 8);  
                                 memcpy(xferp->data_in+16,  
                                     "CD-ROM CDR-210P ", 16);  
                                 memcpy(xferp->data_in+32, "1.0 ", 4);  
                         }  
                 }  
408    
409                  /*  Data for tape devices:  */          if ((len & (OVERLAY_BLOCK_SIZE-1)) != 0) {
410                  if (d->is_a_tape) {                  fatal("TODO: overlay access (write), len not multiple of "
411                          xferp->data_in[0] = 0x01;  /*  0x01 = tape  */                      "overlay block size. not yet implemented.\n");
412                          xferp->data_in[1] = 0x80;  /*  0x80 = removable  */                  fatal("len = %lli\n", (long long) len);
413                          memcpy(xferp->data_in+16, "TAPE            ", 16);                  abort();
414            }
415                          if (machine->machine_type == MACHINE_PMAX) {          if ((offset & (OVERLAY_BLOCK_SIZE-1)) != 0) {
416                                  /*                  fatal("TODO: unaligned overlay access\n");
417                                   *  TODO:  find out if these are correct.                  fatal("offset = %lli\n", (long long) offset);
418                                   *                  abort();
419                                   *  The name might be TZK10, TSZ07, or TLZ04,          }
420                                   *  or something completely different.  
421                                   */          /*  Split the write into OVERLAY_BLOCK_SIZE writes:  */
422                                  memcpy(xferp->data_in+8, "DEC     ", 8);          for (curofs=offset; curofs<offset+len; curofs+=OVERLAY_BLOCK_SIZE) {
423                                  memcpy(xferp->data_in+16,                  /*  Always write to the last overlay:  */
424                                      "TK50     (C) DEC", 16);                  int overlay_nr = d->nr_of_overlays-1;
425                                  memcpy(xferp->data_in+32, "2000", 4);                  off_t lenwritten;
426                          }                  int res = my_fseek(d->overlays[overlay_nr].f_data,
427                        curofs, SEEK_SET);
428                    if (res != 0) {
429                            fatal("[ diskimage__internal_access(): fseek()"
430                                " failed on disk id %i \n", d->id);
431                            return 0;
432                  }                  }
433    
434                  diskimage__return_default_status_and_message(xferp);                  lenwritten = fwrite(buf, 1, OVERLAY_BLOCK_SIZE,
435                  break;                      d->overlays[overlay_nr].f_data);
436                    buf += OVERLAY_BLOCK_SIZE;
437    
438          case SCSIBLOCKCMD_READ_CAPACITY:                  /*  Mark this block in the last overlay as in use:  */
439                  debug("READ_CAPACITY");                  overlay_set_block_in_use(d, overlay_nr, curofs);
440            }
441    
442                  if (xferp->cmd_len != 10)          return len;
443                          fatal(" [ weird READ_CAPACITY len=%i, should be 10 ] ",  }
                             xferp->cmd_len);  
                 else {  
                         if (xferp->cmd[8] & 1) {  
                                 /*  Partial Medium Indicator bit...  TODO  */  
                                 fatal("WARNING: READ_CAPACITY with PMI bit"  
                                     " set not yet implemented\n");  
                         }  
                 }  
444    
                 /*  Return data:  */  
                 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,  
                     8, 1);  
   
                 diskimage_recalc_size(d);  
   
                 size = d->total_size / d->logical_block_size;  
                 if (d->total_size & (d->logical_block_size-1))  
                         size ++;  
   
                 xferp->data_in[0] = (size >> 24) & 255;  
                 xferp->data_in[1] = (size >> 16) & 255;  
                 xferp->data_in[2] = (size >> 8) & 255;  
                 xferp->data_in[3] = size & 255;  
   
                 xferp->data_in[4] = (d->logical_block_size >> 24) & 255;  
                 xferp->data_in[5] = (d->logical_block_size >> 16) & 255;  
                 xferp->data_in[6] = (d->logical_block_size >> 8) & 255;  
                 xferp->data_in[7] = d->logical_block_size & 255;  
445    
446                  diskimage__return_default_status_and_message(xferp);  /*
447                  break;   *  fread_helper():
448     *
449     *  Internal helper function. Reads from a disk image file, or if the
450     *  disk image has overlays, from the last overlay that has the specific
451     *  data (or the disk image file itself).
452     */
453    static size_t fread_helper(off_t offset, unsigned char *buf,
454            size_t len, struct diskimage *d)
455    {
456            off_t curofs;
457            size_t totallenread = 0;
458    
459          case SCSICMD_MODE_SENSE:          /*  Fast return-path for the case when no overlays are used:  */
460          case SCSICMD_MODE_SENSE10:                if (d->nr_of_overlays == 0) {
461                  debug("MODE_SENSE");                  int res = my_fseek(d->f, offset, SEEK_SET);
462                  q = 4; retlen = xferp->cmd[4];                  if (res != 0) {
463                  switch (xferp->cmd_len) {                          fatal("[ diskimage__internal_access(): fseek() failed"
464                  case 6: break;                              " on disk id %i \n", d->id);
465                  case 10:q = 8;                          return 0;
                         retlen = xferp->cmd[7] * 256 + xferp->cmd[8];  
                         break;  
                 default:fatal(" (unimplemented mode_sense len=%i)",  
                             xferp->cmd_len);  
466                  }                  }
467    
468                  /*                  return fread(buf, 1, len, d->f);
469                   *  NOTE/TODO: This code doesn't handle too short retlens          }
                  *  very well. A quick hack around this is that I allocate  
                  *  a bit too much memory, so that nothing is actually  
                  *  written outside of xferp->data_in[].  
                  */  
   
                 retlen += 100;          /*  Should be enough. (Ugly.)  */  
   
                 if ((xferp->cmd[2] & 0xc0) != 0)  
                         fatal("WARNING: mode sense, cmd[2] = 0x%02x\n",  
                             xferp->cmd[2]);  
   
                 /*  Return data:  */  
                 scsi_transfer_allocbuf(&xferp->data_in_len,  
                     &xferp->data_in, retlen, 1);  
   
                 xferp->data_in_len -= 100;      /*  Restore size.  */  
   
                 pagecode = xferp->cmd[2] & 0x3f;  
   
                 debug("[ MODE SENSE id %i, pagecode=%i ]\n", id, pagecode);  
   
                 /*  4 bytes of header for 6-byte command,  
                     8 bytes of header for 10-byte command.  */  
                 xferp->data_in[0] = retlen;     /*  0: mode data length  */  
                 xferp->data_in[1] = d->is_a_cdrom? 0x05 : 0x00;  
                                 /*  1: medium type  */  
                 xferp->data_in[2] = 0x00;       /*  device specific  
                                                     parameter  */  
                 xferp->data_in[3] = 8 * 1;      /*  block descriptor  
                                                     length: 1 page (?)  */  
   
                 xferp->data_in[q+0] = 0x00;     /*  density code  */  
                 xferp->data_in[q+1] = 0;        /*  nr of blocks, high  */  
                 xferp->data_in[q+2] = 0;        /*  nr of blocks, mid  */  
                 xferp->data_in[q+3] = 0;        /*  nr of blocks, low */  
                 xferp->data_in[q+4] = 0x00;     /*  reserved  */  
                 xferp->data_in[q+5] = (d->logical_block_size >> 16) & 255;  
                 xferp->data_in[q+6] = (d->logical_block_size >> 8) & 255;  
                 xferp->data_in[q+7] = d->logical_block_size & 255;  
                 q += 8;  
   
                 diskimage__return_default_status_and_message(xferp);  
   
                 /*  descriptors, 8 bytes (each)  */  
   
                 /*  page, n bytes (each)  */  
                 switch (pagecode) {  
                 case 0:  
                         /*  TODO: Nothing here?  */  
                         break;  
                 case 1:         /*  read-write error recovery page  */  
                         xferp->data_in[q + 0] = pagecode;  
                         xferp->data_in[q + 1] = 10;  
                         break;  
                 case 3:         /*  format device page  */  
                         xferp->data_in[q + 0] = pagecode;  
                         xferp->data_in[q + 1] = 22;  
   
                         /*  10,11 = sectors per track  */  
                         xferp->data_in[q + 10] = 0;  
                         xferp->data_in[q + 11] = d->sectors_per_track;  
   
                         /*  12,13 = physical sector size  */  
                         xferp->data_in[q + 12] =  
                             (d->logical_block_size >> 8) & 255;  
                         xferp->data_in[q + 13] = d->logical_block_size & 255;  
                         break;  
                 case 4:         /*  rigid disk geometry page  */  
                         xferp->data_in[q + 0] = pagecode;  
                         xferp->data_in[q + 1] = 22;  
                         xferp->data_in[q + 2] = (d->ncyls >> 16) & 255;  
                         xferp->data_in[q + 3] = (d->ncyls >> 8) & 255;  
                         xferp->data_in[q + 4] = d->ncyls & 255;  
                         xferp->data_in[q + 5] = d->heads;  
   
                         xferp->data_in[q + 20] = (d->rpms >> 8) & 255;  
                         xferp->data_in[q + 21] = d->rpms & 255;  
                         break;  
                 case 5:         /*  flexible disk page  */  
                         xferp->data_in[q + 0] = pagecode;  
                         xferp->data_in[q + 1] = 0x1e;  
   
                         /*  2,3 = transfer rate  */  
                         xferp->data_in[q + 2] = ((5000) >> 8) & 255;  
                         xferp->data_in[q + 3] = (5000) & 255;  
   
                         xferp->data_in[q + 4] = d->heads;  
                         xferp->data_in[q + 5] = d->sectors_per_track;  
   
                         /*  6,7 = data bytes per sector  */  
                         xferp->data_in[q + 6] = (d->logical_block_size >> 8)  
                             & 255;  
                         xferp->data_in[q + 7] = d->logical_block_size & 255;  
   
                         xferp->data_in[q + 8] = (d->ncyls >> 8) & 255;  
                         xferp->data_in[q + 9] = d->ncyls & 255;  
470    
471                          xferp->data_in[q + 28] = (d->rpms >> 8) & 255;          /*  Split the read into OVERLAY_BLOCK_SIZE reads:  */
472                          xferp->data_in[q + 29] = d->rpms & 255;          for (curofs=offset; len != 0;
473                          break;              curofs = (curofs | (OVERLAY_BLOCK_SIZE-1)) + 1) {
474                  default:                  /*  Find the overlay, if any, that has this block:  */
475                          fatal("[ MODE_SENSE for page %i is not yet "                  off_t lenread, lentoread;
476                              "implemented! ]\n", pagecode);                  int overlay_nr;
477                    for (overlay_nr = d->nr_of_overlays-1;
478                        overlay_nr >= 0; overlay_nr --) {
479                            if (overlay_has_block(d, overlay_nr, curofs))
480                                    break;
481                  }                  }
482    
483                  break;                  lentoread = len > OVERLAY_BLOCK_SIZE? OVERLAY_BLOCK_SIZE : len;
   
         case SCSICMD_READ:  
         case SCSICMD_READ_10:  
                 debug("READ");  
   
                 /*  
                  *  For tape devices, read data at the current position.  
                  *  For disk and CDROM devices, the command bytes contain  
                  *  an offset telling us where to read from the device.  
                  */  
   
                 if (d->is_a_tape) {  
                         /*  bits 7..5 of cmd[1] are the LUN bits... TODO  */  
484    
485                          size = (xferp->cmd[2] << 16) +                  if (overlay_nr >= 0) {
486                                 (xferp->cmd[3] <<  8) +                          /*  Read from overlay:  */
487                                  xferp->cmd[4];                          int res = my_fseek(d->overlays[overlay_nr].f_data,
488                                curofs, SEEK_SET);
489                          /*  Bit 1 of cmd[1] is the SILI bit (TODO), and                          if (res != 0) {
490                              bit 0 is the "use fixed length" bit.  */                                  fatal("[ diskimage__internal_access(): fseek()"
491                                        " failed on disk id %i \n", d->id);
492                          if (xferp->cmd[1] & 0x01) {                                  return 0;
                                 /*  Fixed block length:  */  
                                 size *= d->logical_block_size;  
493                          }                          }
494                            lenread = fread(buf, 1, lentoread,
495                          if (d->filemark) {                              d->overlays[overlay_nr].f_data);
                                 /*  At end of file, switch to the next  
                                     automagically:  */  
                                 d->tape_filenr ++;  
                                 diskimage__switch_tape(d);  
   
                                 d->filemark = 0;  
                         }  
   
                         ofs = d->tape_offset;  
   
                         fatal("[ READ tape, id=%i file=%i, cmd[1]=%02x size=%i"  
                             ", ofs=%lli ]\n", id, d->tape_filenr,  
                             xferp->cmd[1], (int)size, (long long)ofs);  
496                  } else {                  } else {
497                          if (xferp->cmd[0] == SCSICMD_READ) {                          /*  Read from the base disk image:  */
498                                  if (xferp->cmd_len != 6)                          int res = my_fseek(d->f, curofs, SEEK_SET);
499                                          debug(" (weird len=%i)",                          if (res != 0) {
500                                              xferp->cmd_len);                                  fatal("[ diskimage__internal_access(): fseek()"
501                                        " failed on disk id %i \n", d->id);
502                                  /*                                  return 0;
                                  *  bits 4..0 of cmd[1], and cmd[2] and cmd[3]  
                                  *  hold the logical block address.  
                                  *  
                                  *  cmd[4] holds the number of logical blocks  
                                  *  to transfer. (Special case if the value is  
                                  *  0, actually means 256.)  
                                  */  
                                 ofs = ((xferp->cmd[1] & 0x1f) << 16) +  
                                       (xferp->cmd[2] << 8) + xferp->cmd[3];  
                                 retlen = xferp->cmd[4];  
                                 if (retlen == 0)  
                                         retlen = 256;  
                         } else {  
                                 if (xferp->cmd_len != 10)  
                                         debug(" (weird len=%i)",  
                                             xferp->cmd_len);  
   
                                 /*  
                                  *  cmd[2..5] hold the logical block address.  
                                  *  cmd[7..8] holds the number of logical  
                                  *  blocks to transfer. (NOTE: If the value is  
                                  *  0, this means 0, not 65536. :-)  
                                  */  
                                 ofs = ((uint64_t)xferp->cmd[2] << 24) +  
                                     (xferp->cmd[3] << 16) + (xferp->cmd[4] << 8)  
                                     + xferp->cmd[5];  
                                 retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];  
503                          }                          }
504                            lenread = fread(buf, 1, lentoread, d->f);
                         size = retlen * d->logical_block_size;  
                         ofs *= d->logical_block_size;  
                 }  
   
                 /*  Return data:  */  
                 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,  
                     size, 0);  
   
                 debug(" READ  ofs=%lli size=%i\n", (long long)ofs, (int)size);  
   
                 diskimage__return_default_status_and_message(xferp);  
   
                 d->filemark = 0;  
   
                 /*  
                  *  Failure? Then set check condition.  
                  *  For tapes, error should only occur at the end of a file.  
                  *  
                  *  "If the logical unit encounters a filemark during  
                  *   a READ command, CHECK CONDITION status shall be  
                  *   returned and the filemark and valid bits shall be  
                  *   set to one in the sense data. The sense key shall  
                  *   be set to NO SENSE"..  
                  */  
                 if (d->is_a_tape && d->f != NULL && feof(d->f)) {  
                         debug(" feof id=%i\n", id);  
                         xferp->status[0] = 0x02;        /*  CHECK CONDITION  */  
   
                         d->filemark = 1;  
                 } else  
                         diskimage__internal_access(d, 0, ofs,  
                             xferp->data_in, size);  
   
                 if (d->is_a_tape && d->f != NULL)  
                         d->tape_offset = ftello(d->f);  
   
                 /*  TODO: other errors?  */  
                 break;  
   
         case SCSICMD_WRITE:  
         case SCSICMD_WRITE_10:  
                 debug("WRITE");  
   
                 /*  TODO: tape  */  
   
                 if (xferp->cmd[0] == SCSICMD_WRITE) {  
                         if (xferp->cmd_len != 6)  
                                 debug(" (weird len=%i)", xferp->cmd_len);  
   
                         /*  
                          *  bits 4..0 of cmd[1], and cmd[2] and cmd[3] hold the  
                          *  logical block address.  
                          *  
                          *  cmd[4] holds the number of logical blocks to  
                          *  transfer. (Special case if the value is 0, actually  
                          *  means 256.)  
                          */  
                         ofs = ((xferp->cmd[1] & 0x1f) << 16) +  
                               (xferp->cmd[2] << 8) + xferp->cmd[3];  
                         retlen = xferp->cmd[4];  
                         if (retlen == 0)  
                                 retlen = 256;  
                 } else {  
                         if (xferp->cmd_len != 10)  
                                 debug(" (weird len=%i)", xferp->cmd_len);  
   
                         /*  
                          *  cmd[2..5] hold the logical block address.  
                          *  cmd[7..8] holds the number of logical blocks to  
                          *  transfer. (NOTE: If the value is 0 this means 0,  
                          *  not 65536.)  
                          */  
                         ofs = ((uint64_t)xferp->cmd[2] << 24) +  
                             (xferp->cmd[3] << 16) + (xferp->cmd[4] << 8) +  
                             xferp->cmd[5];  
                         retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];  
                 }  
   
                 size = retlen * d->logical_block_size;  
                 ofs *= d->logical_block_size;  
   
                 if (xferp->data_out_offset != size) {  
                         debug(", data_out == NULL, wanting %i bytes, \n\n",  
                             (int)size);  
                         xferp->data_out_len = size;  
                         return 2;  
                 }  
   
                 debug(", data_out != NULL, OK :-)");  
   
                 debug("WRITE ofs=%i size=%i offset=%i\n", (int)ofs,  
                     (int)size, (int)xferp->data_out_offset);  
   
                 diskimage__internal_access(d, 1, ofs,  
                     xferp->data_out, size);  
   
                 /*  TODO: how about return code?  */  
   
                 /*  Is this really necessary?  */  
                 /*  fsync(fileno(d->f));  */  
   
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
   
         case SCSICMD_SYNCHRONIZE_CACHE:  
                 debug("SYNCHRONIZE_CACHE");  
   
                 if (xferp->cmd_len != 10)  
                         debug(" (weird len=%i)", xferp->cmd_len);  
   
                 /*  TODO: actualy care about cmd[]  */  
                 fsync(fileno(d->f));  
   
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
   
         case SCSICMD_START_STOP_UNIT:  
                 debug("START_STOP_UNIT");  
   
                 if (xferp->cmd_len != 6)  
                         debug(" (weird len=%i)", xferp->cmd_len);  
   
                 for (i=0; i<(ssize_t)xferp->cmd_len; i++)  
                         debug(" %02x", xferp->cmd[i]);  
   
                 /*  TODO: actualy care about cmd[]  */  
   
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
   
         case SCSICMD_REQUEST_SENSE:  
                 debug("REQUEST_SENSE");  
   
                 retlen = xferp->cmd[4];  
   
                 /*  TODO: bits 765 of buf[1] contains the LUN  */  
                 if (xferp->cmd[1] != 0x00)  
                         fatal("WARNING: REQUEST_SENSE with cmd[1]=0x%02x not"  
                             " yet implemented\n", (int)xferp->cmd[1]);  
   
                 if (retlen < 18) {  
                         fatal("WARNING: SCSI request sense len=%i, <18!\n",  
                             (int)retlen);  
                         retlen = 18;  
                 }  
   
                 /*  Return data:  */  
                 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,  
                     retlen, 1);  
   
                 xferp->data_in[0] = 0x80 + 0x70;/*  0x80 = valid,  
                                                     0x70 = "current errors"  */  
                 xferp->data_in[2] = 0x00;       /*  SENSE KEY!  */  
   
                 if (d->filemark) {  
                         xferp->data_in[2] = 0x80;  
                 }  
                 debug(": [2]=0x%02x ", xferp->data_in[2]);  
   
                 printf(" XXX(!) \n");  
   
                 /*  TODO  */  
                 xferp->data_in[7] = retlen - 7; /*  additional sense length  */  
                 /*  TODO  */  
   
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
   
         case SCSICMD_READ_BLOCK_LIMITS:  
                 debug("READ_BLOCK_LIMITS");  
   
                 retlen = 6;  
   
                 /*  TODO: bits 765 of buf[1] contains the LUN  */  
                 if (xferp->cmd[1] != 0x00)  
                         fatal("WARNING: READ_BLOCK_LIMITS with cmd[1]="  
                             "0x%02x not yet implemented\n", (int)xferp->cmd[1]);  
   
                 /*  Return data:  */  
                 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,  
                     retlen, 1);  
   
                 /*  
                  *  data[0] is reserved, data[1..3] contain the maximum block  
                  *  length limit, data[4..5] contain the minimum limit.  
                  */  
   
                 {  
                         int max_limit = 32768;  
                         int min_limit = 128;  
   
                         xferp->data_in[1] = (max_limit >> 16) & 255;  
                         xferp->data_in[2] = (max_limit >>  8) & 255;  
                         xferp->data_in[3] =  max_limit        & 255;  
                         xferp->data_in[4] = (min_limit >>  8) & 255;  
                         xferp->data_in[5] =  min_limit        & 255;  
505                  }                  }
506    
507                  diskimage__return_default_status_and_message(xferp);                  if (lenread != lentoread) {
508                  break;                          fatal("[ INCOMPLETE READ from disk id %i, offset"
509                                " %lli ]\n", d->id, (long long)curofs);
         case SCSICMD_REWIND:  
                 debug("REWIND");  
   
                 /*  TODO: bits 765 of buf[1] contains the LUN  */  
                 if ((xferp->cmd[1] & 0xe0) != 0x00)  
                         fatal("WARNING: REWIND with cmd[1]=0x%02x not yet "  
                             "implemented\n", (int)xferp->cmd[1]);  
   
                 /*  Close and reopen.  */  
   
                 if (d->f != NULL)  
                         fclose(d->f);  
   
                 d->f = fopen(d->fname, d->writable? "r+" : "r");  
                 if (d->f == NULL) {  
                         fprintf(stderr, "[ diskimage: could not (re)open "  
                             "'%s' ]\n", d->fname);  
                         /*  TODO: return error  */  
510                  }                  }
511    
512                  d->tape_offset = 0;                  len -= lentoread;
513                  d->tape_filenr = 0;                  totallenread += lenread;
514                  d->filemark = 0;                  buf += OVERLAY_BLOCK_SIZE;
515            }
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
   
         case SCSICMD_SPACE:  
                 debug("SPACE");  
   
                 /*  TODO: bits 765 of buf[1] contains the LUN  */  
                 if ((xferp->cmd[1] & 0xe0) != 0x00)  
                         fatal("WARNING: SPACE with cmd[1]=0x%02x not yet "  
                             "implemented\n", (int)xferp->cmd[1]);  
   
                 /*  
                  *  Bits 2..0 of buf[1] contain the 'code' which describes how  
                  *  spacing should be done, and buf[2..4] contain the number of  
                  *  operations.  
                  */  
                 debug("[ SPACE: buf[] = %02x %02x %02x %02x %02x %02x ]\n",  
                     xferp->cmd[0],  
                     xferp->cmd[1],  
                     xferp->cmd[2],  
                     xferp->cmd[3],  
                     xferp->cmd[4],  
                     xferp->cmd[5]);  
   
                 switch (xferp->cmd[1] & 7) {  
                 case 1: /*  Seek to a different file nr:  */  
                         {  
                                 int diff = (xferp->cmd[2] << 16) +  
                                     (xferp->cmd[3] << 8) + xferp->cmd[4];  
   
                                 /*  Negative seek offset:  */  
                                 if (diff & (1 << 23))  
                                         diff = - (16777216 - diff);  
   
                                 d->tape_filenr += diff;  
                         }  
   
                         /*  At end of file, switch to the next tape file:  */  
                         if (d->filemark) {  
                                 d->tape_filenr ++;  
                                 d->filemark = 0;  
                         }  
   
                         debug("{ switching to tape file %i }", d->tape_filenr);  
                         diskimage__switch_tape(d);  
                         d->filemark = 0;  
                         break;  
                 default:  
                         fatal("[ diskimage.c: unimplemented SPACE type %i ]\n",  
                             xferp->cmd[1] & 7);  
                 }  
   
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
   
         case SCSICDROM_READ_SUBCHANNEL:  
                 /*  
                  *  According to  
                  *  http://mail-index.netbsd.org/port-i386/1997/03/03/0010.html:  
                  *  
                  *  "The READ_CD_CAPACITY, READ_SUBCHANNEL, and MODE_SELECT  
                  *   commands have the same opcode in SCSI or ATAPI, but don't  
                  *   have the same command structure"...  
                  *  
                  *  TODO: This still doesn't work. Hm.  
                  */  
                 retlen = 48;  
   
                 debug("CDROM_READ_SUBCHANNEL/READ_CD_CAPACITY, cmd[1]=0x%02x",  
                     xferp->cmd[1]);  
   
                 /*  Return data:  */  
                 scsi_transfer_allocbuf(&xferp->data_in_len,  
                     &xferp->data_in, retlen, 1);  
   
                 diskimage_recalc_size(d);  
   
                 size = d->total_size / d->logical_block_size;  
                 if (d->total_size & (d->logical_block_size-1))  
                         size ++;  
   
                 xferp->data_in[0] = (size >> 24) & 255;  
                 xferp->data_in[1] = (size >> 16) & 255;  
                 xferp->data_in[2] = (size >> 8) & 255;  
                 xferp->data_in[3] = size & 255;  
   
                 xferp->data_in[4] = (d->logical_block_size >> 24) & 255;  
                 xferp->data_in[5] = (d->logical_block_size >> 16) & 255;  
                 xferp->data_in[6] = (d->logical_block_size >> 8) & 255;  
                 xferp->data_in[7] = d->logical_block_size & 255;  
   
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
   
         case SCSICDROM_READ_TOC:  
                 debug("(CDROM_READ_TOC: ");  
                 debug("lun=%i msf=%i ",  
                     xferp->cmd[1] >> 5, (xferp->cmd[1] >> 1) & 1);  
                 debug("starting_track=%i ", xferp->cmd[6]);  
                 retlen = xferp->cmd[7] * 256 + xferp->cmd[8];  
                 debug("allocation_len=%i)\n", retlen);  
   
                 /*  Return data:  */  
                 scsi_transfer_allocbuf(&xferp->data_in_len,  
                     &xferp->data_in, retlen, 1);  
   
                 xferp->data_in[0] = 0;  
                 xferp->data_in[1] = 10;  
                 xferp->data_in[2] = 0;          /*  First track.  */  
                 xferp->data_in[3] = 0;          /*  Last track.  */  
   
                 /*  Track 0 data:  */  
                 xferp->data_in[4] = 0x00;       /*  Reserved.  */  
                 xferp->data_in[5] = 0x04;       /*  ADR + CTRL:  
                                                     Data, not audio  */  
                 xferp->data_in[6] = 0x00;       /*  Track nr  */  
                 xferp->data_in[7] = 0x00;       /*  Reserved  */  
                 /*  8..11 = absolute CDROM address  */  
   
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
   
         case SCSICDROM_READ_DISCINFO:  
                 debug("(SCSICDROM_READ_DISCINFO: ");  
                 debug("TODO");  
                 retlen = 0;  
   
                 /*  Return data:  */  
                 scsi_transfer_allocbuf(&xferp->data_in_len,  
                     &xferp->data_in, retlen, 1);  
   
                 /*  TODO  */  
   
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
   
         case SCSICDROM_READ_TRACKINFO:  
                 debug("(SCSICDROM_READ_TRACKINFO: ");  
                 debug("TODO");  
                 retlen = 0;  
   
                 /*  Return data:  */  
                 scsi_transfer_allocbuf(&xferp->data_in_len,  
                     &xferp->data_in, retlen, 1);  
   
                 /*  TODO  */  
   
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
   
         case SCSICMD_MODE_SELECT:  
                 debug("[ SCSI MODE_SELECT: ");  
   
                 /*  
                  *  TODO:  
                  *  
                  *  This is super-hardcoded for NetBSD's usage of mode_select  
                  *  to set the size of CDROM sectors to 2048.  
                  */  
516    
517                  if (xferp->data_out_offset == 0) {          return totallenread;
518                          xferp->data_out_len = 12;       /*  TODO  */  }
                         debug("data_out == NULL, wanting %i bytes ]\n",  
                             (int)xferp->data_out_len);  
                         return 2;  
                 }  
   
                 debug("data_out!=NULL (OK), ");  
   
                 /*  TODO:  Care about cmd?  */  
   
                 /*  Set sector size to 2048:  */  
                 /*  00 05 00 08 00 03 ca 40 00 00 08 00  */  
                 if (xferp->data_out[0] == 0x00 &&  
                     xferp->data_out[1] == 0x05 &&  
                     xferp->data_out[2] == 0x00 &&  
                     xferp->data_out[3] == 0x08) {  
                         d->logical_block_size =  
                             (xferp->data_out[9] << 16) +  
                             (xferp->data_out[10] << 8) +  
                             xferp->data_out[11];  
                         debug("[ setting logical_block_size to %i ]\n",  
                             d->logical_block_size);  
                 } else {  
                         int i;  
                         fatal("[ unknown MODE_SELECT: cmd =");  
                         for (i=0; i<(ssize_t)xferp->cmd_len; i++)  
                                 fatal(" %02x", xferp->cmd[i]);  
                         fatal(", data_out =");  
                         for (i=0; i<(ssize_t)xferp->data_out_len; i++)  
                                 fatal(" %02x", xferp->data_out[i]);  
                         fatal(" ]");  
                 }  
519    
                 debug(" ]\n");  
                 diskimage__return_default_status_and_message(xferp);  
                 break;  
520    
521          case SCSICMD_PREVENT_ALLOW_REMOVE:  /*
522                  debug("[ SCSI 0x%02x Prevent/allow medium removal: "   *  diskimage__internal_access():
523                      "TODO ]\n", xferp->cmd[0]);   *
524     *  Read from or write to a struct diskimage.
525     *
526     *  Returns 1 if the access completed successfully, 0 otherwise.
527     */
528    int diskimage__internal_access(struct diskimage *d, int writeflag,
529            off_t offset, unsigned char *buf, size_t len)
530    {
531            ssize_t lendone;
532    
533                  diskimage__return_default_status_and_message(xferp);          if (buf == NULL) {
534                  break;                  fprintf(stderr, "diskimage__internal_access(): buf = NULL\n");
535                    exit(1);
536            }
537            if (len == 0)
538                    return 1;
539            if (d->f == NULL)
540                    return 0;
541    
542          case 0xbd:          if (writeflag) {
543                  fatal("[ SCSI 0x%02x (len %i), TODO: ", xferp->cmd[0],                  if (!d->writable)
544                      xferp->cmd_len);                          return 0;
                 for (i=0; i<(ssize_t)xferp->cmd_len; i++)  
                         fatal(" %02x", xferp->cmd[i]);  
                 fatal(" ]\n");  
545    
546                    lendone = fwrite_helper(offset, buf, len, d);
547            } else {
548                  /*                  /*
549                   *  Used by Windows NT?                   *  Special case for CD-ROMs. Actually, this is not needed
550                   *                   *  for .iso images, only for physical CDROMS on some OSes,
551                   *  Not documented in http://www.danbbs.dk/~dino/                   *  such as FreeBSD.
                  *              SCSI/SCSI2-D.html.  
                  *  Google gave the answer "MECHANISM_STATUS" for ATAPI. Hm.  
552                   */                   */
553                    if (d->is_a_cdrom)
554                            lendone = diskimage_access__cdrom(d, offset, buf, len);
555                    else
556                            lendone = fread_helper(offset, buf, len, d);
557    
558                  if (xferp->cmd_len < 12) {                  if (lendone < (ssize_t)len)
559                          fatal("WEIRD LEN?\n");                          memset(buf + lendone, 0, len - lendone);
560                          retlen = 8;          }
                 } else {  
                         retlen = xferp->cmd[8] * 256 + xferp->cmd[9];  
                 }  
   
                 /*  Return data:  */  
                 scsi_transfer_allocbuf(&xferp->data_in_len,  
                     &xferp->data_in, retlen, 1);  
   
                 diskimage__return_default_status_and_message(xferp);  
   
                 break;  
561    
562          default:          /*  Incomplete data transfer? Then return failure:  */
563                  fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n",          if (lendone != (ssize_t)len) {
564                      xferp->cmd[0], id);  #ifdef UNSTABLE_DEVEL
565                  exit(1);                  fatal
566    #else
567                    debug
568    #endif
569                        ("[ diskimage__internal_access(): disk_id %i, offset %lli"
570                        ", transfer not completed. len=%i, len_done=%i ]\n",
571                        d->id, (long long)offset, (int)len, (int)lendone);
572                    return 0;
573          }          }
         debug(" ]\n");  
574    
575          return 1;          return 1;
576  }  }
# Line 1450  int diskimage_access(struct machine *mac Line 631  int diskimage_access(struct machine *mac
631   *      r       read-only (don't allow changes to the file)   *      r       read-only (don't allow changes to the file)
632   *      s       SCSI (this is the default)   *      s       SCSI (this is the default)
633   *      t       tape   *      t       tape
634     *      V       add an overlay to a disk image
635   *      0-7     force a specific SCSI ID number   *      0-7     force a specific SCSI ID number
636   *   *
637   *  machine is assumed to be non-NULL.   *  machine is assumed to be non-NULL.
# Line 1463  int diskimage_add(struct machine *machin Line 645  int diskimage_add(struct machine *machin
645          char *cp;          char *cp;
646          int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0;          int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0;
647          int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id=-1;          int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id=-1;
648          int prefix_o=0;          int prefix_o=0, prefix_V=0;
649    
650          if (fname == NULL) {          if (fname == NULL) {
651                  fprintf(stderr, "diskimage_add(): NULL ptr\n");                  fprintf(stderr, "diskimage_add(): NULL ptr\n");
# Line 1545  int diskimage_add(struct machine *machin Line 727  int diskimage_add(struct machine *machin
727                          case 't':                          case 't':
728                                  prefix_t = 1;                                  prefix_t = 1;
729                                  break;                                  break;
730                            case 'V':
731                                    prefix_V = 1;
732                                    break;
733                          case ':':                          case ':':
734                                  break;                                  break;
735                          default:                          default:
# Line 1563  int diskimage_add(struct machine *machin Line 748  int diskimage_add(struct machine *machin
748          }          }
749          memset(d, 0, sizeof(struct diskimage));          memset(d, 0, sizeof(struct diskimage));
750    
         d2 = machine->first_diskimage;  
         if (d2 == NULL) {  
                 machine->first_diskimage = d;  
         } else {  
                 while (d2->next != NULL)  
                         d2 = d2->next;  
                 d2->next = d;  
         }  
   
751          /*  Default to IDE disks...  */          /*  Default to IDE disks...  */
752          d->type = DISKIMAGE_IDE;          d->type = DISKIMAGE_IDE;
753    
# Line 1593  int diskimage_add(struct machine *machin Line 769  int diskimage_add(struct machine *machin
769          if (prefix_s)          if (prefix_s)
770                  d->type = DISKIMAGE_SCSI;                  d->type = DISKIMAGE_SCSI;
771    
772            /*  Special case: Add an overlay for an already added disk image:  */
773            if (prefix_V) {
774                    struct diskimage *dx = machine->first_diskimage;
775    
776                    if (prefix_id < 0) {
777                            fprintf(stderr, "The 'V' disk image prefix requires"
778                                " a disk ID to also be supplied.\n");
779                            exit(1);
780                    }
781    
782                    while (dx != NULL) {
783                            if (d->type == dx->type && prefix_id == dx->id)
784                                    break;
785                            dx = dx->next;
786                    }
787    
788                    if (dx == NULL) {
789                            fprintf(stderr, "Bad ID supplied for overlay?\n");
790                            exit(1);
791                    }
792    
793                    diskimage_add_overlay(dx, fname);
794    
795                    /*  Free the preliminary d struct:  */
796                    free(d);
797    
798                    /*  Don't add any disk image. This is an overlay!  */
799                    return -1;
800            }
801    
802            /*  Add the new disk image in the disk image chain:  */
803            d2 = machine->first_diskimage;
804            if (d2 == NULL) {
805                    machine->first_diskimage = d;
806            } else {
807                    while (d2->next != NULL)
808                            d2 = d2->next;
809                    d2->next = d;
810            }
811    
812          if (prefix_o)          if (prefix_o)
813                  d->override_base_offset = override_base_offset;                  d->override_base_offset = override_base_offset;
814    
# Line 1848  int diskimage_is_a_tape(struct machine * Line 1064  int diskimage_is_a_tape(struct machine *
1064   */   */
1065  void diskimage_dump_info(struct machine *machine)  void diskimage_dump_info(struct machine *machine)
1066  {  {
1067          int iadd = DEBUG_INDENTATION;          int i, iadd = DEBUG_INDENTATION;
1068          struct diskimage *d = machine->first_diskimage;          struct diskimage *d = machine->first_diskimage;
1069    
1070          while (d != NULL) {          while (d != NULL) {
# Line 1890  void diskimage_dump_info(struct machine Line 1106  void diskimage_dump_info(struct machine
1106                          debug(" (BOOT)");                          debug(" (BOOT)");
1107                  debug("\n");                  debug("\n");
1108    
1109                    for (i=0; i<d->nr_of_overlays; i++) {
1110                            debug("overlay %i: %s\n",
1111                                i, d->overlays[i].overlay_basename);
1112                    }
1113    
1114                  debug_indentation(-iadd);                  debug_indentation(-iadd);
1115    
1116                  d = d->next;                  d = d->next;

Legend:
Removed from v.37  
changed lines
  Added in v.38

  ViewVC Help
Powered by ViewVC 1.1.26