[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Bug#391296: Using cdparanoia results in a flood of warnings with Linux 2.6.19-rc1



Hi,

the RedHat sgio patch from http://people.redhat.com/pjones/cdparanoia/sgiotest/tmp/cdparanoia-III-alpha9.8.sgio.patch
works like a charm with my new kernel.

I've applied the sgio patch to the dpatched source package and resolved the rejects. The result is appended to this mail. It compiles and works fine with Linux 2.6.19-rc1. But it is possible that I screwed up the BSD part.

Can anyone with a BSD based Debian try this patch? And if it is broken either fix it or give me access to a machine so that I can fix it myself?

Gregor

PS: I think this bug could be merged with #376010 and perhaps #348088
#!/bin/sh /usr/share/dpatch/dpatch-run
## 10-linux-sgio.dpatch
##
## This patch is based on the SG_IO patch from RedHat:
##   http://people.redhat.com/pjones/cdparanoia/sgiotest/tmp/cdparanoia-III-alpha9.8.sgio.patch

@DPATCH@

diff -urN cdparanoia-3a9.8/interface/cdda_interface.h cdparanoia-3a9.8-sgio/interface/cdda_interface.h
--- cdparanoia-3a9.8/interface/cdda_interface.h	2006-10-06 11:05:16.899508004 +0200
+++ cdparanoia-3a9.8-sgio/interface/cdda_interface.h	2006-10-06 10:07:18.303847546 +0200
@@ -116,10 +116,46 @@
 
 } cdrom_drive;
 
+/* buffers for use with the scsi code.  d->sg_buffer is unused,
+   and d->sg points to this struct.  We can't really change struct
+   cdrom_drive without breaking binary compatibility, so we do this
+   instead. */
+struct sg_info {
+#ifdef SG_IO
+  struct sg_io_hdr *hdr;
+#else
+  struct sg_header *hdr;
+#endif
+
+  char *cmdp;
+  ssize_t cmd_len;
+  ssize_t cmdp_buffer_len;
+  
+  char *dxferp;
+  ssize_t dxferp_buffer_len;
+  ssize_t dxferp_max_buffer_len;
+
+  unsigned char bytefill;
+  int bytecheck;
+
+  int in_size;
+  int out_size;
+
+  int (*handle_scsi_cmd)(struct cdrom_drive *d);
+  void (*setup_scsi_cmd)(struct cdrom_drive *d,
+    char *cmdp, unsigned int cmd_len,
+    unsigned int in_size, unsigned int out_size);
+};
+
+
 #define IS_AUDIO(d,i) (!(d->disc_toc[i].bFlags & 0x04))
 
 /******** Identification/autosense functions */
 
+#ifdef SG_IO
+extern int check_fd_sgio(int fd);
+#endif
+
 extern cdrom_drive *cdda_find_a_cdrom(int messagedest, char **message);
 extern cdrom_drive *cdda_identify(const char *device, int messagedest,
 				  char **message);
diff -urN cdparanoia-3a9.8/interface/interface.c cdparanoia-3a9.8-sgio/interface/interface.c
--- cdparanoia-3a9.8/interface/interface.c	2006-10-06 11:05:16.899508004 +0200
+++ cdparanoia-3a9.8-sgio/interface/interface.c	2006-10-06 10:27:23.762553352 +0200
@@ -31,7 +31,7 @@
     _clean_messages(d);
     if(d->cdda_device_name)free(d->cdda_device_name);
 #if defined(__linux__)
-    if(d->ioctl_device_name)free(d->ioctl_device_name);
+    if(d->ioctl_device_name && d->ioctl_device_name!=d->cdda_device_name)free(d->ioctl_device_name);
     if(d->drive_model)free(d->drive_model);
     if(d->cdda_fd!=-1)close(d->cdda_fd);
     if(d->ioctl_fd!=-1 && d->ioctl_fd!=d->cdda_fd)close(d->ioctl_fd);
diff -urN cdparanoia-3a9.8/interface/scan_devices.c cdparanoia-3a9.8-sgio/interface/scan_devices.c
--- cdparanoia-3a9.8/interface/scan_devices.c	2006-10-06 11:05:16.975499064 +0200
+++ cdparanoia-3a9.8-sgio/interface/scan_devices.c	2006-10-06 10:22:24.878152261 +0200
@@ -16,8 +16,8 @@
 #include <pwd.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include "cdda_interface.h"
 #include "low_interface.h"
+#include "cdda_interface.h"
 #include "common_interface.h"
 #include "utils.h"
 
@@ -65,6 +65,8 @@
 	"/dev/mcd?c", NULL};
 #endif
 
+extern void sg2_init_sg_info(cdrom_drive *d);
+extern void sgio_init_sg_info(cdrom_drive *d);
 /* Functions here look for a cdrom drive; full init of a drive type
    happens in interface.c */
 
@@ -135,8 +137,13 @@
 #endif
 
 #if defined(__linux__)	/* is order of checks important? */
-  d=cdda_identify_cooked(device,messagedest,messages);
+#  ifdef SG_IO
+  d=cdda_identify_scsi(device,NULL,messagedest,messages);
+  if(!d)d=cdda_identify_cooked(device,messagedest,messages);
+#  else
   if(!d)d=cdda_identify_scsi(device,NULL,messagedest,messages);
+  d=cdda_identify_cooked(device,messagedest,messages);
+#endif
 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
   d = cdda_identify_scsi(device, NULL, messagedest, messages);
   if (d == NULL)
@@ -499,17 +506,19 @@
 	    "\nFound an accessible SCSI CDROM drive."
 	    "\nLooking at revision of the SG interface in use...","");
 
-  if(ioctl(d->cdda_fd,SG_GET_VERSION_NUM,&version)){
+  if((version = ioctl(d->cdda_fd,SG_GET_VERSION_NUM,&major)) < 0){
     /* Up, guess not. */
     idmessage(messagedest,messages,
 	      "\tOOPS!  Old 2.0/early 2.1/early 2.2.x (non-ac patch) style "
 	      "SG.\n\tCdparanoia no longer supports the old interface.\n","");
     return(0);
   }
-  major=version/10000;
-  version-=major*10000;
-  minor=version/100;
-  version-=minor*100;
+  if (!version)
+    version = major;
+
+  major = (version >> 16) & 0xff;
+  minor = (version >> 8) & 0xff;
+  version &= 0xff;
   
   sprintf(buffer,"\tSG interface version %d.%d.%d; OK.",
 	  major,minor,version);
@@ -518,6 +527,138 @@
   return(major);
 }
 
+#ifdef SG_IO
+int get_sgio_fd(const char *device, int messagedest, char **messages) {
+	int fd;
+
+	if (!device)
+		return -errno;
+    	/* we don't really care what type of device it is -- if it can do
+	 * SG_IO, then we'll put it through the normal mmc/atapi/etc tests
+	 * later, but it's good enough for now. */
+	fd = open(device, O_RDWR|O_EXCL|O_NONBLOCK);
+ 	if (fd < 0)
+    		return -errno;
+	return check_fd_sgio(fd);
+}
+
+/* removing generic_device breaks ABI; instead, just test both devices */
+static cdrom_drive *sgio_cdda_identify_scsi(const char *generic_device, 
+				const char *ioctl_device, int messagedest,
+				char **messages){
+  cdrom_drive *d = NULL;
+  char *device = NULL;
+  int fd = -1, g_fd = -1;
+  char *p;
+
+  /* with SG_IO in 2.6, we much prefer /dev/hdc and /dev/scd0, so
+   * test ioctl_device before testing generic_device */
+
+  /* we need to resolve any symlinks for the lookup code to work */
+  if (ioctl_device)
+    device = test_resolve_symlink(ioctl_device, messagedest, messages);
+  /* test again, in case symlink resolution failed */
+  if (device) {
+    fd = get_sgio_fd(ioctl_device, messagedest, messages);
+    if (fd < 0) {
+      /* ioctl_device didn't work, so we don't need to keep the strdup of its
+       * real path around */
+      free(device);
+      device = NULL;
+    }
+  }
+  if (fd < 0) {
+    if (!generic_device)
+      goto cdda_identify_scsi_fail_free;
+    device = test_resolve_symlink(generic_device, messagedest, messages);
+    /* test again, in case symlink resolution failed */
+    if (!device)
+      goto cdda_identify_scsi_fail_return;
+    g_fd = get_sgio_fd(device, messagedest, messages);
+    if (g_fd < 0)
+      goto cdda_identify_scsi_fail_free;
+    fd = g_fd;
+  }
+
+  d=calloc(1,sizeof(cdrom_drive));
+
+  d->drive_type=SCSI_CDROM_MAJOR;
+  d->cdda_fd=fd;
+  d->ioctl_fd=fd;
+  d->bigendianp=-1; /* We don't know yet... */
+  d->nsectors=-1;
+
+  d->interface=GENERIC_SCSI;
+
+  /* alloc our big buffer for scsi commands */
+  d->sg=calloc(1, sizeof (struct sg_info));
+  ((struct sg_info *)d->sg)->dxferp_max_buffer_len = CD_FRAMESIZE_RAW;
+  if (check_fd_sgio(d->cdda_fd)) 
+    sgio_init_sg_info(d);
+  else
+    sg2_init_sg_info(d);
+
+  /* get the lun -- this used to set 0 on failure, maybe still should */
+  d->lun = -1;
+
+  p = scsi_inquiry(d);
+  if (!p)
+    goto cdda_identify_scsi_fail_free_device;
+
+  /* It would seem some TOSHIBA CDROMs gets things wrong */
+ 
+  if (!strncmp (p + 8, "TOSHIBA", 7) &&
+      !strncmp (p + 16, "CD-ROM", 6) &&
+      p[0] == TYPE_DISK) {
+    p[0] = TYPE_ROM;
+    p[1] |= 0x80;     /* removable */
+  }
+
+  if (!p || (*p != TYPE_ROM && *p != TYPE_WORM)) {
+    idmessage(messagedest, messages,
+	      "\t\tDrive is neither a CDROM nor a WORM device\n", NULL);
+    goto cdda_identify_scsi_fail_free_device;
+  }
+
+  memcpy(d->inqbytes, p, 4);
+  d->cdda_device_name = device;
+  d->ioctl_device_name = device;
+
+  d->drive_model = calloc(1, 36);
+  strscat(d->drive_model, p+8, 8);
+  strscat(d->drive_model, p+16, 16);
+  strscat(d->drive_model, p+32, 4);
+
+  idmessage(messagedest, messages, "\nCDROM model sensed sensed: %s", d->drive_model);
+  
+  return d;
+  
+cdda_identify_scsi_fail_free_device:
+  if (d) {
+    if (d->drive_model)
+      free(d->drive_model);
+    if (d->sg) {
+      struct sg_info *sgi = (struct sg_info *)d->sg;
+      
+      if (sgi->cmdp_buffer_len)
+        free(sgi->cmdp);
+      if (sgi->dxferp_buffer_len)
+        free(sgi->dxferp);
+      if (sgi->hdr)
+        free(sgi->hdr);
+      free(d->sg);
+    }
+    free(d);
+  }
+  if (fd >= 0)
+    close(fd);
+cdda_identify_scsi_fail_free:
+  if (device)
+    free(device);
+cdda_identify_scsi_fail_return:
+  return NULL;
+}
+#endif
 cdrom_drive *cdda_identify_scsi(const char *generic_device, 
 				const char *ioctl_device, int messagedest,
 				char **messages){
@@ -531,6 +672,12 @@
   int type;
   char *p;
 
+#ifdef SG_IO
+  d = sgio_cdda_identify_scsi(generic_device, ioctl_device, messagedest, messages);
+  if (d)
+    return d;
+#endif
+
   if(generic_device)
     idmessage(messagedest,messages,"\tTesting %s for SCSI interface",
 	      generic_device);
@@ -667,7 +814,6 @@
 	      "major number",generic_device);
     goto cdda_identify_scsi_fail;
   }
-  
 
   d=calloc(1,sizeof(cdrom_drive));
 
@@ -688,8 +834,9 @@
   }
 
   /* malloc our big buffer for scsi commands */
-  d->sg=malloc(MAX_BIG_BUFF_SIZE);
-  d->sg_buffer=d->sg+SG_OFF;
+  d->sg=calloc(1, sizeof (struct sg_info));
+  ((struct sg_info *)d->sg)->dxferp_max_buffer_len = CD_FRAMESIZE_RAW;
+  sg2_init_sg_info(d);
 
   {
     /* get the lun */
@@ -701,6 +848,8 @@
   }
 
   p = scsi_inquiry(d);
+  if (!p)
+    goto cdda_identify_scsi_fail;
 
   /* It would seem some TOSHIBA CDROMs gets things wrong */
  
@@ -720,12 +869,11 @@
     goto cdda_identify_scsi_fail;
   }
 
-  d->drive_model=calloc(36,1);
   memcpy(d->inqbytes,p,4);
   d->cdda_device_name=copystring(generic_device);
   d->ioctl_device_name=copystring(ioctl_device);
 
-  d->drive_model=calloc(36,1);
+  d->drive_model=calloc(1, 36);
   strscat(d->drive_model,p+8,8);
   strscat(d->drive_model,p+16,16);
   strscat(d->drive_model,p+32,4);
@@ -735,6 +883,22 @@
   return(d);
   
 cdda_identify_scsi_fail:
+  if (d) {
+    if (d->drive_model)
+      free(d->drive_model);
+    if (d->sg) {
+      struct sg_info *sgi = (struct sg_info *)d->sg;
+      
+      if (sgi->cmdp_buffer_len)
+        free(sgi->cmdp);
+      if (sgi->dxferp_buffer_len)
+        free(sgi->dxferp);
+      if (sgi->hdr)
+        free(sgi->hdr);
+      free(d->sg);
+    }
+    free(d);
+  }
   if(generic_device)free((char *)generic_device);
   if(ioctl_device)free((char *)ioctl_device);
   if(i_fd!=-1)close(i_fd);
diff -urN cdparanoia-3a9.8/interface/scsi_cmds.h cdparanoia-3a9.8-sgio/interface/scsi_cmds.h
--- cdparanoia-3a9.8/interface/scsi_cmds.h	1970-01-01 01:00:00.000000000 +0100
+++ cdparanoia-3a9.8-sgio/interface/scsi_cmds.h	2006-10-06 10:07:18.316846002 +0200
@@ -0,0 +1,197 @@
+/******************************************************************
+ * CopyPolicy: GNU General Public License version 2
+ * Copyright 2004 Peter Jones <pjones@redhat.com>
+ * 
+ * macros to generate scsi commands.
+ *
+ ******************************************************************/
+
+#ifndef _SCSI_CMDS_H
+#define _SCSI_CMDS_H 1
+#include <scsi/scsi.h>
+
+/* from the MMC3 spec, rewritten as seperate macros */
+#define LBA_to_M(lba) (lba>=-150?((lba+150)/(60*75)):((lba+450150)/(60*75)))
+#define LBA_to_S(lba) (lba>=-150?((lba+150)/75):((lba+450150)/75))
+#define LBA_to_F(lba) (lba>=-150?(lba+150):(lba+450150))
+
+/* Group 1 (10b) command */
+#define SCSI_TWELVE_BYTE(a,b,c,d,e,f,g,h,i,j,k,l) ((char []) {a,b,c,d,e,f,g,h,i,j,k,l})
+#define SCSI_READ_12(fua, a, l) SCSI_TWELVE_BYTE( \
+	READ_12,		/* READ_10 */ \
+	(fua & 1) << 3,		/* force unit access */ \
+	(a >> 24) & 0xff,	/* lba byte 3 */ \
+	(a >> 16) & 0xff,	/* lba byte 2 */ \
+	(a >> 8) & 0xff,	/* lba byte 1 */ \
+	a & 0xff,		/* lba byte 0 */ \
+	0,			/* reserved */ \
+	(l >> 24) & 0xff,	/* len byte 3 */ \
+	(l >> 16) & 0xff,	/* len byte 2 */ \
+	(l >> 8) & 0xff,	/* len byte 1 */ \
+	l & 0xff,		/* len byte 0 */ \
+	0			/* control */ \
+)
+#define D4_READ_12(a, l) SCSI_TWELVE_BYTE( \
+	0xD4,			/* 0xD4 */ \
+	0,			/* lun */ \
+	0,			/* ? */ \
+	(a >> 16) & 0xff,	/* lba byte 2 */ \
+	(a >> 8) & 0xff,	/* lba byte 1 */ \
+	a & 0xff,		/* lba byte 0 */ \
+	0,			/* reserved */ \
+	0,			/* ? */ \
+	0,			/* ? */ \
+	l & 0xff,		/* len byte 0 */ \
+	0, 0			/* ? */ \
+)
+#define D8_READ_12(a, l) SCSI_TWELVE_BYTE( \
+	0xD8,			/* 0xD4 */ \
+	0,			/* lun */ \
+	0,			/* ? */ \
+	(a >> 16) & 0xff,	/* lba byte 2 */ \
+	(a >> 8) & 0xff,	/* lba byte 1 */ \
+	a & 0xff,		/* lba byte 0 */ \
+	0,			/* reserved */ \
+	0,			/* ? */ \
+	0,			/* ? */ \
+	l & 0xff,		/* len byte 0 */ \
+	0, 0			/* ? */ \
+)
+#define READ_CD_12(a, l) SCSI_TWELVE_BYTE( \
+	0xBE,			/* 0xD4 */ \
+	0,			/* ? */ \
+	(a >> 24) & 0xff,	/* lba byte 3 */ \
+	(a >> 16) & 0xff,	/* lba byte 2 */ \
+	(a >> 8) & 0xff,	/* lba byte 1 */ \
+	a & 0xff,		/* lba byte 0 */ \
+	(l >> 16) & 0xff,	/* len byte 2 */ \
+	(l >> 8) & 0xff,	/* len byte 1 */ \
+	l & 0xff,		/* len byte 0 */ \
+	10,			/* ecc */ \
+	0, 0			/* ? */ \
+)
+#define READ_CD_MSF_12(a, l) SCSI_TWELVE_BYTE( \
+	0xB9,			/* 0xD4 */ \
+	0,			/* ? */ \
+	0,			/* ? */ \
+	LBA_to_M((a)),		/* start M */ \
+	LBA_to_S((a)),		/* start S */ \
+	LBA_to_F((a)),		/* start F */ \
+	LBA_to_M((a)+(l)),	/* start M */ \
+	LBA_to_S((a)+(l)),	/* start S */ \
+	LBA_to_F((a)+(l)),	/* start F */ \
+	10,			/* ecc */ \
+	0, 0			/* ? */ \
+)
+
+#define SCSI_TEN_BYTE(a,b,c,d,e,f,g,h,i,j) ((char []) {a,b,c,d,e,f,g,h,i,j})
+#define SCSI_MODE_SENSE_10(page, size) SCSI_TEN_BYTE( \
+	MODE_SENSE_10,	/* MODE_SENSE */ \
+	0x00,		/* reserved */ \
+	page & 0x3F,		/* page */ \
+	0,		/* reserved */ \
+	0,		/* reserved */ \
+	0,		/* reserved */ \
+	0,		/* reserved */ \
+	0,		/* MSB (0) */ \
+	size,		/* sizeof(modesense - SG_OFF) */ \
+	0		/* reserved */ \
+)
+#define SCSI_MODE_SELECT_10 SCSI_TEN_BYTE( \
+	MODE_SELECT_10,	/* MODE_SELECT */ \
+	0x10,		/* no save page */ \
+	0,		/* reserved */ \
+	0,		/* reserved */ \
+	0,		/* reserved */ \
+	0,		/* reserved */ \
+	0,		/* reserved */ \
+	0,		/* reserved */ \
+	12,		/* sizeof(mode) */ \
+	0		/* reserved */ \
+)
+#define SCSI_READ_TOC(track_number) SCSI_TEN_BYTE( \
+	READ_TOC,	/* READ_TOC */ \
+	0,		/* MSF format */ \
+	0,		\
+	0,		\
+	0,		\
+	0,		\
+	track_number,	/* start track */ \
+	0,		/* len msb */ \
+	12,		/* len lsb */ \
+	0		/* flags */ \
+)
+/* a contribution from Boris for IMS cdd 522 */
+/* check this for ACER/Creative/Foo 525,620E,622E, etc? */
+#define CDD522_READ_TOC(track_number) SCSI_TEN_BYTE( \
+	0xE5,		/* CDD522_READ_TOC */ \
+	0, 0, 0, 0,	/* res */ \
+	track_number,	/* start track */ \
+	0, 0, 0, 0	/* ? */ \
+)
+#define SCSI_READ_10(fua, a, l) SCSI_TEN_BYTE( \
+	READ_10,		/* READ_10 */ \
+	(fua?8:0),		/* force unit access */ \
+	(a >> 24) & 0xff,	/* lba byte 3 */ \
+	(a >> 16) & 0xff,	/* lba byte 2 */ \
+	(a >> 8) & 0xff,	/* lba byte 1 */ \
+	a & 0xff,		/* lba byte 0 */ \
+	0,			/* reserved */ \
+	(l >> 8) & 0xff,	/* len byte 1 */ \
+	l & 0xff,		/* len byte 0 */ \
+	0			/* control */ \
+)
+#define D4_READ_10(a, l) SCSI_TEN_BYTE( \
+	0xD4,			/* 0xD4 */ \
+	0,			/* ? */ \
+	0,			/* ? */ \
+	(a >> 16) & 0xff,	/* lba byte 2 */ \
+	(a >> 8) & 0xff,	/* lba byte 1 */ \
+	a & 0xff,		/* lba byte 0 */ \
+	0,			/* reserved */ \
+	0,			/* ? */ \
+	l & 0xff,		/* len byte 0 */ \
+	0			/* control */ \
+)
+#define D5_READ_10(a, l) SCSI_TEN_BYTE( \
+	0xD5,			/* 0xD5 */ \
+	0,			/* lun */ \
+	0,			/* ? */ \
+	(a >> 16) & 0xff,	/* lba byte 2 */ \
+	(a >> 8) & 0xff,	/* lba byte 1 */ \
+	a & 0xff,		/* lba byte 0 */ \
+	0,			/* reserved */ \
+	0,			/* ? */ \
+	l & 0xff,		/* len byte 0 */ \
+	0			/* control */ \
+)
+
+
+#define SCSI_SIX_BYTE(a,b,c,d,e,f) ((char []) {a,b,c,d,e,f})
+#define SCSI_MODE_SENSE_6(page, size) SCSI_SIX_BYTE( \
+	MODE_SENSE,	/* MODE_SENSE */ \
+	0x00,		/* return block descriptor/lun */ \
+	page & 0x3F,		/* page */ \
+	0,		/* reserved */ \
+	size,		/* sizeof(modesense - SG_OFF) */ \
+	0		/* control */  \
+)
+#define SCSI_MODE_SELECT_6 SCSI_SIX_BYTE( \
+	MODE_SELECT,	/* MODE_SELECT */ \
+	0x10,		/* no save page */ \
+	0,		/* reserved */ \
+	0,		/* reserved */ \
+	12,		/* sizeof(mode) */ \
+	0		/* reserved */ \
+)
+#define SCSI_INQUIRY_6(len) SCSI_SIX_BYTE( \
+	INQUIRY,	/* INQUIRY */ \
+	0, 0, 0,	/* ? */ \
+	len, 0		/* len, ? */ \
+)
+#define SCSI_TEST_UNIT_READY_6 SCSI_SIX_BYTE( \
+	TEST_UNIT_READY,/* TEST_UNIT_READY */ \
+	0, 0, 0, 0,	/* reserved */ \
+	0		/* control */ \
+)
+#endif
diff -urN cdparanoia-3a9.8/interface/scsi_interface.c cdparanoia-3a9.8-sgio/interface/scsi_interface.c
--- cdparanoia-3a9.8/interface/scsi_interface.c	2006-10-06 11:05:16.879510357 +0200
+++ cdparanoia-3a9.8-sgio/interface/scsi_interface.c	2006-10-06 11:03:24.426721197 +0200
@@ -3,6 +3,7 @@
  * Original interface.c Copyright (C) 1994-1997 
  *            Eissfeldt heiko@colossus.escape.de
  * Current blenderization Copyright (C) 1998-1999 Monty xiphmont@mit.edu
+ *                        Copyright 2004 Peter Jones <pjones@redhat.com>
  * FreeBSD porting (c) 2003
  * 	Simon 'corecode' Schubert <corecode@corecode.ath.cx>
  * 
@@ -13,6 +14,7 @@
 #include "low_interface.h"
 #include "common_interface.h"
 #include "utils.h"
+#include "scsi_cmds.h"
 
 /* hook */
 static int Dummy (cdrom_drive *d,int s){
@@ -21,7 +23,290 @@
 
 #include "drive_exceptions.h"
 
-static void tweak_SG_buffer(cdrom_drive *d){
+static void reset_scsi(cdrom_drive *d){
+  struct sg_info *sgi = (struct sg_info *)d->sg;
+  struct sg_io_hdr *hdr = sgi->hdr;
+  unsigned char key, ASC, ASCQ;
+  int arg, ret, tries;
+  char cmd[6];
+
+  d->enable_cdda(d,0);
+
+  cdmessage(d,"sending SG SCSI reset... ");
+  if(ioctl(d->cdda_fd,SG_SCSI_RESET,&arg))
+    cdmessage(d,"FAILED: EBUSY\n");
+  else
+    cdmessage(d,"OK\n");
+
+  tries = 0;
+  while(1) {
+    memcpy(cmd, SCSI_TEST_UNIT_READY_6, 6);
+    sgi->setup_scsi_cmd(d, cmd, 6, 0, 56);
+    ret = sgi->handle_scsi_cmd(d);
+
+    key = hdr->sbp[2] & 0xf;
+    ASC = hdr->sbp[12];
+    ASCQ = hdr->sbp[13];
+
+    if(key == 2 & ASC == 4 & ASCQ == 1 & tries < 10) {
+      tries++;
+      usleep(10);
+      continue;
+    }
+    break;
+  }
+
+  d->enable_cdda(d,1);
+}
+
+static int check_sbp_error(const char *sbp) {
+  char key = sbp[2] & 0xf;
+  char ASC = sbp[12];
+  char ASCQ = sbp[13];
+
+  if (sbp[0]) {
+    switch (key){
+      case 0:
+        if (errno==0)
+	  errno = EIO;
+        return(TR_UNKNOWN);
+      case 1:
+        break;
+      case 2:
+        if (errno==0)
+	  errno = EBUSY;
+        return(TR_BUSY);
+      case 3: 
+        if ((ASC==0x0C) & (ASCQ==0x09)) {
+	  /* loss of streaming */
+	  if (errno==0)
+	    errno = EIO;
+          return(TR_STREAMING);
+	} else {
+	  if (errno==0)
+	    errno = EIO;
+	  return(TR_MEDIUM);
+        }
+      case 4:
+        if (errno==0)
+	  errno = EIO;
+        return(TR_FAULT);
+      case 5:
+        if (errno==0)
+	  errno = EINVAL;
+        return(TR_ILLEGAL);
+      default:
+        if (errno==0)
+	  errno = EIO;
+        return(TR_UNKNOWN);
+    }
+  }
+  return 0;
+}
+
+#ifdef SG_IO
+int check_fd_sgio(int fd) {
+  struct sg_io_hdr hdr;
+
+  if (fd < 0)
+    return fd;
+
+  memset(&hdr, 0, sizeof (struct sg_io_hdr));
+  /* First try with interface_id = 'A'; for all but the sg case,
+   * that'll get us a -EINVAL if it supports SG_IO, and some other
+   * error for all other cases. */
+  hdr.interface_id = 'A';
+  if (ioctl(fd, SG_IO, &hdr)) {
+    switch (errno) {
+      /* sr and ata give us EINVAL when SG_IO is supported
+       * but interface_id is bad. */
+      case EINVAL:
+      /* sg gives us ENOSYS when SG_IO is supported but
+       * interface_id is bad.  IMHO, this is wrong and 
+       * needs fixing in the kernel. */
+      case ENOSYS:
+        return fd;
+      /* everything else gives ENOTTY, I think.  I'm just
+       * going to be paranoid and reject everything else. */
+      default:
+        return -errno;
+    }
+  }
+  /* if we get here, something is dreadfuly wrong. ioctl(fd,SG_IO,&hdr)
+   * handled SG_IO, but took hdr.interface_id = 'A' as valid, and an empty
+   * command as good.  Don't trust it. */
+  return -1;
+}
+
+static void sgio_tweak_SG_buffer(cdrom_drive *d) {
+  int table, reserved, cur, err;
+  char buffer[256];
+
+  /* SG_SET_RESERVED_SIZE doesn't actually allocate or reserve anything.
+   * what it _does_ do is give you an error if you ask for a value
+   * larger than q->max_sectors (the length of the device's bio request
+   * queue).  So we walk it up from 1 sector until it fails, then get
+   * the value we set it to last.
+   */
+  /* start with 2 frames, round down to our queue's sector size */
+  cur = 1;
+  do {
+    cur <<= 1; reserved = cur * (1<<9);
+    err = ioctl(d->cdda_fd, SG_SET_RESERVED_SIZE, &reserved);
+  } while(err >= 0);
+  ioctl(d->cdda_fd, SG_GET_RESERVED_SIZE, &reserved);
+
+  cur = 0;
+  /* this doesn't currently ever work, but someday somebody might
+     implement working sg lists with SG_IO devices, so who knows... */
+  table=1;
+  if (ioctl(d->cdda_fd, SG_GET_SG_TABLESIZE, &table) < 0)
+    table=1;
+
+  sprintf(buffer,"\tDMA scatter/gather table entries: %d\n\t"
+      "table entry size: %d bytes\n\t"
+      "maximum theoretical transfer: %d sectors\n",
+      table, reserved, table*reserved/CD_FRAMESIZE_RAW);
+  cdmessage(d,buffer);
+
+  cur=table*reserved;
+
+  ((struct sg_info *)d->sg)->dxferp_max_buffer_len = cur;
+
+  /* so since we never go above q->max_sectors, we should never get -EIO.
+   * we might still get -ENOMEM, but we back off for that later.  Monty
+   * had an old comment: "not too much; new kernels have trouble with DMA
+   * "allocation, so be more conservative: 32kB max until I test more 
+   * thoroughly".  We're not currently honoring that, because we should
+   * always get -ENOMEM.
+   */
+#if 0
+  cur=(cur>1024*32?1024*32:cur);
+#endif
+  d->nsectors=cur/CD_FRAMESIZE_RAW;
+  d->bigbuff=cur;
+
+  sprintf(buffer,"\tSetting default read size to %d sectors (%d bytes).\n\n",
+      d->nsectors,d->nsectors*CD_FRAMESIZE_RAW);
+  cdmessage(d,buffer);
+}
+
+static void sgio_setup_scsi_cmd(cdrom_drive *d,
+				char *cmdp,
+				unsigned int cmd_len,
+				unsigned int in_size,
+				unsigned int out_size) {
+  struct sg_info *sgi = (struct sg_info *)d->sg;
+  struct sg_io_hdr *hdr = sgi->hdr;
+
+  memset(hdr->cmdp, 0, sgi->cmdp_buffer_len);
+  memset(hdr->dxferp, sgi->bytefill, sgi->dxferp_buffer_len);
+  memcpy(hdr->cmdp, cmdp, cmd_len);
+
+  hdr->cmd_len = cmd_len;
+
+  sgi->in_size = in_size;
+  sgi->out_size = out_size;
+}
+
+static int sgio_handle_scsi_cmd(cdrom_drive *d) {
+  int status = 0;
+  struct sg_info *sgi = (struct sg_info *)d->sg;
+  struct sg_io_hdr *hdr = sgi->hdr;
+
+  if (sgi->in_size) {
+    hdr->dxfer_len = sgi->in_size;
+    hdr->dxfer_direction = SG_DXFER_TO_DEV;
+
+    errno = 0;
+    status = ioctl(d->cdda_fd, SG_IO, hdr);
+    if (status >= 0)
+      if (hdr->status)
+        status = check_sbp_error(hdr->sbp);
+    if (status < 0) 
+      return TR_EWRITE;
+  }
+  if (!sgi->in_size | sgi->out_size) {
+    hdr->dxfer_len = sgi->out_size;
+    hdr->dxfer_direction = sgi->out_size ? SG_DXFER_FROM_DEV : SG_DXFER_NONE;
+
+    errno = 0;
+    status = ioctl(d->cdda_fd, SG_IO, hdr);
+    if (status < 0)
+      return TR_EREAD;
+    if (status >= 0)
+      if (hdr->status)
+        status = check_sbp_error(hdr->sbp);
+  }
+  if (status)
+    return status;
+
+  errno = 0;
+  return 0;
+}
+
+void sgio_init_sg_info(cdrom_drive *d) {
+  struct sg_info *sgi = (struct sg_info *)d->sg;
+  struct sg_io_hdr *hdr;
+
+  hdr = calloc(sizeof (struct sg_io_hdr), 1);
+
+  sgi->cmdp = hdr->cmdp = calloc(128, 1);
+  sgi->cmdp_buffer_len = 128;
+
+  hdr->sbp = calloc(SG_MAX_SENSE,1);
+  hdr->mx_sb_len = SG_MAX_SENSE;
+
+  sgi->bytefill = '\xff';
+  sgi->bytecheck = 0;
+
+  hdr->timeout = 5000;
+  hdr->interface_id = 'S';
+
+  sgi->hdr = hdr;
+
+  sgi->setup_scsi_cmd = sgio_setup_scsi_cmd;
+  sgi->handle_scsi_cmd = sgio_handle_scsi_cmd;
+
+  sgio_tweak_SG_buffer(d);
+
+  sgi->dxferp = hdr->dxferp = calloc(sgi->dxferp_max_buffer_len, 1);
+  sgi->dxferp_buffer_len = sgi->dxferp_max_buffer_len;
+}
+#endif
+
+static void sg2_clear_garbage(cdrom_drive *d){
+  fd_set fdset;
+  struct timeval tv;
+  struct sg_header *sg_hd = (struct sg_header *)((struct sg_info *)d->sg)->hdr;
+  int flag = 0;
+
+  /* clear out any possibly preexisting garbage */
+  FD_ZERO(&fdset);
+  FD_SET(d->cdda_fd,&fdset);
+  tv.tv_sec=0;
+  tv.tv_usec=0;
+
+  /* I like select */
+  while(select(d->cdda_fd+1,&fdset,NULL,NULL,&tv)==1){
+    
+    sg_hd->twelve_byte = 0;
+    sg_hd->result = 0;
+    sg_hd->reply_len = SG_OFF;
+    read(d->cdda_fd, sg_hd, 1);
+
+    /* reset for select */
+    FD_ZERO(&fdset);
+    FD_SET(d->cdda_fd,&fdset);
+    tv.tv_sec=0;
+    tv.tv_usec=0;
+    if(!flag && d->report_all)
+      cdmessage(d,"Clearing previously returned data from SCSI buffer\n");
+    flag=1;
+  }
+}
+
+static void sg2_tweak_SG_buffer(cdrom_drive *d){
   int table,reserved;
   char buffer[256];
 
@@ -72,21 +357,7 @@
 #endif
 }
 
-#if defined(__linux__)
-static void reset_scsi(cdrom_drive *d){
-  int arg;
-  d->enable_cdda(d,0);
-
-  cdmessage(d,"sending SG SCSI reset... ");
-  if(ioctl(d->cdda_fd,SG_SCSI_RESET,&arg))
-    cdmessage(d,"FAILED: EBUSY\n");
-  else
-    cdmessage(d,"OK\n");
-  
-  d->enable_cdda(d,1);
-}
-
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 static void reset_scsi(cdrom_drive *d) {
 	d->enable_cdda(d,0);
 
@@ -109,61 +380,45 @@
 }
 #endif
 
+/* process a complete scsi command. */
 #if defined(__linux__)
-static void clear_garbage(cdrom_drive *d){
-  fd_set fdset;
-  struct timeval tv;
-  struct sg_header *sg_hd=(struct sg_header *)d->sg;
-  int flag=0;
+static void sg2_setup_scsi_cmd(cdrom_drive *d,
+				char *cmdp,
+				unsigned int cmd_len,
+				unsigned int in_size,
+				unsigned int out_size) {
+  struct sg_info *sgi = (struct sg_info *)d->sg;
+  struct sg_header *hdr = (struct sg_header *)sgi->hdr;
 
-  /* clear out any possibly preexisting garbage */
-  FD_ZERO(&fdset);
-  FD_SET(d->cdda_fd,&fdset);
-  tv.tv_sec=0;
-  tv.tv_usec=0;
+  sgi->cmdp = (char *)hdr + sizeof (struct sg_header);
+  memcpy(sgi->cmdp, cmdp, cmd_len);
 
-  /* I like select */
-  while(select(d->cdda_fd+1,&fdset,NULL,NULL,&tv)==1){
-    
-    sg_hd->twelve_byte = 0;
-    sg_hd->result = 0;
-    sg_hd->reply_len = SG_OFF;
-    read(d->cdda_fd, sg_hd, 1);
+  sgi->dxferp = sgi->cmdp + cmd_len;
+  memset(sgi->dxferp, sgi->bytefill, sgi->dxferp_max_buffer_len - cmd_len);
 
-    /* reset for select */
-    FD_ZERO(&fdset);
-    FD_SET(d->cdda_fd,&fdset);
-    tv.tv_sec=0;
-    tv.tv_usec=0;
-    if(!flag && d->report_all)
-      cdmessage(d,"Clearing previously returned data from SCSI buffer\n");
-    flag=1;
-  }
+  sgi->in_size = in_size;
+  sgi->out_size = out_size;
 }
-#endif
 
-/* process a complete scsi command. */
-#if defined(__linux__)
-static int handle_scsi_cmd(cdrom_drive *d,
-			   unsigned int cmd_len, 
-			   unsigned int in_size, 
-			   unsigned int out_size,
-
-			   unsigned char bytefill,
-			   int bytecheck){
+static int sg2_handle_scsi_cmd(cdrom_drive *d) {
   int status = 0;
-  struct sg_header *sg_hd=(struct sg_header *)d->sg;
-  long writebytes=SG_OFF+cmd_len+in_size;
+  struct sg_info *sgi = (struct sg_info *)d->sg;
+  struct sg_header *sg_hd = (struct sg_header *)sgi->hdr;
+
+  int out_size = sgi->out_size;
+  int in_size = sgi->in_size;
+
+  long writebytes = (int)(sg_hd) + SG_OFF + sgi->cmd_len + sgi->in_size;
 
   /* generic scsi device services */
 
   /* clear out any possibly preexisting garbage */
-  clear_garbage(d);
+  sg2_clear_garbage(d);
 
-  memset(sg_hd,0,sizeof(sg_hd)); 
-  sg_hd->twelve_byte = cmd_len == 12;
+  memset(sg_hd,0, SG_OFF);
+  sg_hd->twelve_byte = sgi->cmd_len == 12;
   sg_hd->result = 0;
-  sg_hd->reply_len = SG_OFF + out_size;
+  sg_hd->reply_len = SG_OFF + sgi->out_size;
 
   /* The following is one of the scariest hacks I've ever had to use.
      The idea is this: We want to know if a command fails.  The
@@ -173,8 +428,8 @@
      length for a command that doesn't take data) such that we can
      tell if the command failed.  Scared yet? */
 
-  if(bytecheck && out_size>in_size){
-    memset(d->sg_buffer+cmd_len+in_size,bytefill,out_size-in_size); 
+  if(sgi->bytecheck && out_size>in_size){
+    /* the memset for this is done in sg2_setup_scsi_cmd() */
     /* the size does not remove cmd_len due to the way the kernel
        driver copies buffers */
     writebytes+=(out_size-in_size);
@@ -262,40 +517,10 @@
     if(errno==0)errno=EIO;
     return(TR_EREAD);
   }
-
-  if(sg_hd->sense_buffer[0]){
-    char key=sg_hd->sense_buffer[2]&0xf;
-    char ASC=sg_hd->sense_buffer[12];
-    char ASCQ=sg_hd->sense_buffer[13];
-    switch(key){
-    case 0:
-      if(errno==0)errno=EIO;
-      return(TR_UNKNOWN);
-    case 1:
-      break;
-    case 2:
-      if(errno==0)errno=EBUSY;
-      return(TR_BUSY);
-    case 3: 
-      if(ASC==0x0C && ASCQ==0x09){
-	/* loss of streaming */
-	if(errno==0)errno=EIO;
-	return(TR_STREAMING);
-      }else{
-	if(errno==0)errno=EIO;
-	return(TR_MEDIUM);
-      }
-    case 4:
-      if(errno==0)errno=EIO;
-      return(TR_FAULT);
-    case 5:
-      if(errno==0)errno=EINVAL;
-      return(TR_ILLEGAL);
-    default:
-      if(errno==0)errno=EIO;
-      return(TR_UNKNOWN);
-    }
-  }
+  
+  status = check_sbp_error(sg_hd->sense_buffer);
+  if (status)
+    return status;
 
   /* still not foolproof; the following doesn't guarantee that we got
      all the data, just that the command was not rejected. */
@@ -304,10 +529,11 @@
      commands still get through.  Perhaps no data comes back even
      though the target reports success? */
 
-  if(bytecheck && in_size+cmd_len<out_size){
+  if(sgi->bytecheck && sgi->in_size+sgi->cmd_len<sgi->out_size){
     long i,flag=0;
-    for(i=in_size;i<out_size;i++)
-      if(d->sg_buffer[i]!=bytefill){
+    for(i=sgi->in_size; i<sgi->out_size; i++)
+      /* XXX check this offset */
+      if((sgi->dxferp[i])!=(sgi->bytefill)){
 	flag=1;
 	break;
       }
@@ -400,61 +626,67 @@
 #endif
 
 
-/* Group 1 (10b) command */
+void sg2_init_sg_info(cdrom_drive *d) {
+  struct sg_info *sgi = (struct sg_info *)d->sg;
+  struct sg_header *hdr;
 
-static int mode_sense_atapi(cdrom_drive *d,int size,int page){ 
-  memcpy(d->sg_buffer,  
-	 (char [])  {0x5A,   /* MODE_SENSE */
-		       0x00, /* reserved */
-		       0x00, /* page */
-		       0,    /* reserved */
-		       0,    /* reserved */
-		       0,    /* reserved */
-		       0,    /* reserved */
-		       0,    /* MSB (0) */
-		       0,    /* sizeof(modesense - SG_OFF) */
-		       0},   /* reserved */ 
-         10);
-
-  d->sg_buffer[1]=d->lun<<5;
-  d->sg_buffer[2]=0x3F&page;
-  d->sg_buffer[8]=size+4;
+  hdr = calloc(sizeof (struct sg_header)+sgi->dxferp_max_buffer_len, 1);
 
-  if (handle_scsi_cmd (d, 10, 0, size+4,'\377',1)) return(1);
+  sgi->cmdp = (char *)hdr + sizeof (struct sg_header);
+  sgi->cmdp_buffer_len = 0;
 
-  {
-    char *b=d->sg_buffer;
-    if(b[0])return(1); /* Handles only up to 256 bytes */
-    if(b[6])return(1); /* Handles only up to 256 bytes */
-
-    b[0]=b[1]-3;
-    b[1]=b[2];
-    b[2]=b[3];
-    b[3]=b[7];
+  sgi->bytefill = '\xff';
+  sgi->bytecheck = 1;
 
-    memmove(b+4,b+8,size);
-  }
-  return(0);
+  sgi->setup_scsi_cmd = sg2_setup_scsi_cmd;
+  sgi->handle_scsi_cmd = sg2_handle_scsi_cmd;
+
+  sg2_tweak_SG_buffer(d);
+
+  // sgi->dxferp = hdr + sizeof (struct sg_header);
+  sgi->dxferp = NULL;
+  sgi->dxferp_buffer_len = 0;
+}
+
+static int mode_sense_atapi(cdrom_drive *d, int size, int page) { 
+  char *buffer;
+
+  ((struct sg_info *)d->sg)->setup_scsi_cmd(d, SCSI_MODE_SENSE_10(page,size), 10, 0, size);
+
+  buffer = ((struct sg_info *)d->sg)->dxferp;
+#if 0
+  buffer[1]=d->lun<<5;
+#endif
+
+  if (((struct sg_info *)d->sg)->handle_scsi_cmd(d))
+    return 1;
+
+  if(buffer[0])return(1); /* Handles only up to 256 bytes */
+  if(buffer[6])return(1); /* Handles only up to 256 bytes */
+
+  buffer[0]=buffer[1]-3; 
+  buffer[1]=buffer[2];
+  buffer[2]=buffer[3];
+  buffer[3]=buffer[7];
+
+  memmove(buffer+4,buffer+8,size);
+  return 0;
 }
 
 /* group 0 (6b) command */
 
 static int mode_sense_scsi(cdrom_drive *d,int size,int page){  
-  memcpy(d->sg_buffer,  
-	 (char [])  {0x1A,   /* MODE_SENSE */
-		       0x00, /* return block descriptor/lun */
-		       0x00, /* page */
-		       0,    /* reserved */
-		       0,   /* sizeof(modesense - SG_OFF) */
-		       0},   /* control */ 
-         6);
+  char *buffer;
+  ((struct sg_info *)d->sg)->setup_scsi_cmd(d, SCSI_MODE_SENSE_6(page, size), 6, 0, size);
   
-  d->sg_buffer[1]=d->lun<<5;
-  d->sg_buffer[2]=(0x3F&page);
-  d->sg_buffer[4]=size;
+  buffer = ((struct sg_info *)d->sg)->dxferp;
+#if 0
+  buffer[1]=d->lun<<5;
+#endif
 
-  if (handle_scsi_cmd (d, 6, 0, size, '\377',1)) return(1);
-  return(0);
+  if (((struct sg_info *)d->sg)->handle_scsi_cmd(d))
+    return 1;
+  return 0;
 }
 
 static int mode_sense(cdrom_drive *d,int size,int page){
@@ -463,80 +695,77 @@
   return(mode_sense_scsi(d,size,page));
 }
 
-static int mode_select(cdrom_drive *d,int density,int secsize){
-  /* short circut the way Heiko does it; less flexible, but shorter */
-  if(d->is_atapi){
-    unsigned char *mode = d->sg_buffer + 18;
+static int atapi_mode_select(cdrom_drive *d, int density, int secsize) {
+  unsigned char *mode;
 
-    memcpy(d->sg_buffer,
-	   (char []) { 0x55, /* MODE_SELECT */
-			 0x10, /* no save page */
-			 0, /* reserved */
-			 0, /* reserved */
-			 0, /* reserved */
-			 0, /* reserved */
-			 0, /* reserved */
-			 0, /* reserved */
-			 12, /* sizeof(mode) */
-			 0, /* reserved */
-
-			 /* mode parameter header */
-			 0, 0, 0, 0,  0, 0, 0, 
-			 8, /* Block Descriptor Length */
-
-			 /* descriptor block */
-			 0,       /* Density Code */
-			 0, 0, 0, /* # of Blocks */
-			 0,       /* reserved */
-			 0, 0, 0},/* Blocklen */
-	   26);
-
-    d->sg_buffer[1]|=d->lun<<5;
-
-    /* prepare to read cds in the previous mode */
-    mode [0] = density;
-    mode [6] =  secsize >> 8;   /* block length "msb" */
-    mode [7] =  secsize & 0xFF; /* block length lsb */
+  ((struct sg_info *)d->sg)->setup_scsi_cmd(d, SCSI_MODE_SELECT_10, 10, 16, 0);
+  memcpy(((struct sg_info *)d->sg)->dxferp,(char []) { 
+      /* mode parameter header */
+      0, 0, 0, 0,  0, 0, 0, 
+      8,	/* Block Descriptor Length */
+      /* descriptor block */
+      0,       /* Density Code */
+      0, 0, 0, /* # of Blocks */
+      0,       /* reserved */
+      0, 0, 0},/* Blocklen */
+    16);
+
+  mode = ((struct sg_info *)d->sg)->dxferp;
+#if 0
+  mode[1] |= d->lun << 5;
+#endif
+  /* prepare to read cds in the previous mode */
+  mode[8] = density;
+  mode[14] = secsize >> 8;   /* block length "msb" */
+  mode[15] = secsize & 0xFF; /* block length lsb */
+
+  /* do the scsi cmd */
+  return ((struct sg_info *)d->sg)->handle_scsi_cmd(d);
+}
+
+static int scsi_mode_select(cdrom_drive *d, int density, int secsize) {
+  unsigned char *mode;
+
+  ((struct sg_info *)d->sg)->setup_scsi_cmd(d, SCSI_MODE_SELECT_6, 6, 12, 0);
+  memcpy(((struct sg_info *)d->sg)->dxferp,(char []) {
+      /* mode section */
+      0, 
+      0, 0, 
+      8,       /* Block Descriptor Length */
+      0,       /* Density Code */
+      0, 0, 0, /* # of Blocks */
+      0,       /* reserved */
+      0, 0, 0},/* Blocklen */
+    12);
+    
+  /* prepare to read cds in the previous mode */
+  mode = ((struct sg_info *)d->sg)->dxferp;
+  mode [4] = density;
+  mode [10] =  secsize >> 8;   /* block length "msb" */
+  mode [11] =  secsize & 0xFF; /* block length lsb */
 
     /* do the scsi cmd */
-    return(handle_scsi_cmd (d,10, 16, 0,0,0));
-
-  }else{
-    unsigned char *mode = d->sg_buffer + 10;
-
-    memcpy(d->sg_buffer,
-	   (char []) { 0x15, /* MODE_SELECT */
-			 0x10, /* no save page */
-			 0, /* reserved */
-			 0, /* reserved */
-			 12, /* sizeof(mode) */
-			 0, /* reserved */
-			 /* mode section */
-			 0, 
-			 0, 0, 
-			 8,       /* Block Descriptor Length */
-			 0,       /* Density Code */
-			 0, 0, 0, /* # of Blocks */
-			 0,       /* reserved */
-			 0, 0, 0},/* Blocklen */
-	   18);
-
-    /* prepare to read cds in the previous mode */
-    mode [0] = density;
-    mode [6] =  secsize >> 8;   /* block length "msb" */
-    mode [7] =  secsize & 0xFF; /* block length lsb */
+  return ((struct sg_info *)d->sg)->handle_scsi_cmd(d);
+}
 
-    /* do the scsi cmd */
-    return(handle_scsi_cmd (d,6, 12, 0,0,0));
-  }
+static int mode_select(cdrom_drive *d, int density, int secsize) {
+  /* short circut the way Heiko does it; less flexible, but shorter */
+  if (d->is_atapi)
+    return atapi_mode_select(d, density, secsize);
+  return scsi_mode_select(d, density, secsize);
 }
 
 /* get current sector size from SCSI cdrom drive */
 static unsigned int get_orig_sectorsize(cdrom_drive *d){
-  if(mode_sense(d,12,0x01))return(-1);
+  if (mode_sense(d,12,0x01))
+    return -1;
 
-  d->orgdens = d->sg_buffer[4];
-  return(d->orgsize = ((int)(d->sg_buffer[10])<<8)+d->sg_buffer[11]);
+  d->orgdens = ((struct sg_info *)d->sg)->dxferp[4];
+  
+  d->orgsize = (int)(((struct sg_info *)d->sg)->dxferp[10]<<8);
+  d->orgsize += (int)((struct sg_info *)d->sg)->dxferp[11];
+
+  return d->orgsize;
 }
 
 /* switch CDROM scsi drives to given sector size  */
@@ -579,23 +808,23 @@
    fails on at least one Kodak drive. */
 
 static int scsi_read_toc (cdrom_drive *d){
+  struct sg_info *sgi = (struct sg_info *)d->sg;
   int i,first,last;
   unsigned tracks;
-
-  /* READTOC, MSF format flag, res, res, res, res, Start track, len msb,
-     len lsb, flags */
+  scsi_TOC *toc;
 
   /* read the header first */
-  memcpy(d->sg_buffer, (char []){ 0x43, 0, 0, 0, 0, 0, 1, 0, 12, 0}, 10);
-  d->sg_buffer[1]=d->lun<<5;
-
-  if (handle_scsi_cmd (d,10, 0, 12,'\377',1)){
+  sgi->setup_scsi_cmd(d, SCSI_READ_TOC(1), 10, 0, 12);
+#if 0
+  sgi->dxferp[1] = d->lun << 5;
+#endif
+  if (sgi->handle_scsi_cmd(d)) {
     cderror(d,"004: Unable to read table of contents header\n");
     return(-4);
   }
 
-  first=d->sg_buffer[2];
-  last=d->sg_buffer[3];
+  first = sgi->dxferp[2];
+  last = sgi->dxferp[3];
   tracks=last-first+1;
 
   if (last > MAXTRK || first > MAXTRK || last<0 || first<0) {
@@ -604,335 +833,208 @@
   }
 
   for (i = first; i <= last; i++){
-    memcpy(d->sg_buffer, (char []){ 0x43, 0, 0, 0, 0, 0, 0, 0, 12, 0}, 10);
-    d->sg_buffer[1]=d->lun<<5;
-    d->sg_buffer[6]=i;
+    sgi->setup_scsi_cmd(d, SCSI_READ_TOC(i), 10, 0, 12);
+#if 0    
+    sgi->dxferp[1] = d->lun << 5;
+#endif
     
-    if (handle_scsi_cmd (d,10, 0, 12,'\377',1)){
+    if (sgi->handle_scsi_cmd(d)) {
       cderror(d,"005: Unable to read table of contents entry\n");
       return(-5);
     }
-    {
-      scsi_TOC *toc=(scsi_TOC *)(d->sg_buffer+4);
-
-      d->disc_toc[i-first].bFlags=toc->bFlags;
-      d->disc_toc[i-first].bTrack=i;
-      d->disc_toc[i-first].dwStartSector= d->adjust_ssize * 
-	(((int)(toc->start_MSB)<<24) | 
-	 (toc->start_1<<16)|
-	 (toc->start_2<<8)|
-	 (toc->start_LSB));
-    }
+    toc = (scsi_TOC *)(sgi->dxferp+4);
+    d->disc_toc[i-first].bFlags = toc->bFlags;
+    d->disc_toc[i-first].bTrack = i;
+    d->disc_toc[i-first].dwStartSector = 
+      d->adjust_ssize * (
+        ((int)(toc->start_MSB)<<24) | (toc->start_1 << 16) |
+        (toc->start_2 << 8) | (toc->start_LSB)
+      );
   }
 
-  memcpy(d->sg_buffer, (char []){ 0x43, 0, 0, 0, 0, 0, 0, 0, 12, 0}, 10);
-  d->sg_buffer[1]=d->lun<<5;
-  d->sg_buffer[6]=0xAA;
-    
-  if (handle_scsi_cmd (d,10, 0, 12,'\377',1)){
+  sgi->setup_scsi_cmd(d, SCSI_READ_TOC(0xAA), 10, 0, 12);
+#if 0    
+  sgi->dxferp[1] = d->lun << 5;
+#endif
+
+  if (sgi->handle_scsi_cmd(d)) {
     cderror(d,"002: Unable to read table of contents lead-out\n");
     return(-2);
   }
-  {
-    scsi_TOC *toc=(scsi_TOC *)(d->sg_buffer+4);
-    
-    d->disc_toc[i-first].bFlags=toc->bFlags;
-    d->disc_toc[i-first].bTrack=0xAA;
-    d->disc_toc[i-first].dwStartSector= d->adjust_ssize * 
-	(((int)(toc->start_MSB)<<24) | 
-	 (toc->start_1<<16)|
-	 (toc->start_2<<8)|
-	 (toc->start_LSB));
-  }
+  
+  toc = (scsi_TOC *)(sgi->dxferp+4);
+  d->disc_toc[i-first].bFlags = toc->bFlags;
+  d->disc_toc[i-first].bTrack = 0xAA;
+  d->disc_toc[i-first].dwStartSector = 
+    d->adjust_ssize * (
+      ((int)(toc->start_MSB)<<24) | (toc->start_1 << 16) |
+      (toc->start_2 << 8) | (toc->start_LSB)
+    );
 
   d->cd_extra = FixupTOC(d,tracks+1); /* include lead-out */
-  return(tracks);
+  return tracks;
 }
 
 /* a contribution from Boris for IMS cdd 522 */
 /* check this for ACER/Creative/Foo 525,620E,622E, etc? */
 static int scsi_read_toc2 (cdrom_drive *d){
-  u_int32_t foo,bar;
+  struct sg_info *sgi = (struct sg_info *)d->sg;
+  u_int32_t msb,lsb;
 
   int i;
   unsigned tracks;
 
-  memcpy(d->sg_buffer, (char[]){ 0xe5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10);
-  d->sg_buffer[5]=1;
-  d->sg_buffer[8]=255;
-
-  if (handle_scsi_cmd (d,10, 0, 256,'\377',1)){
+  sgi->setup_scsi_cmd(d, CDD522_READ_TOC(1), 10, 0, 256);
+  if (sgi->handle_scsi_cmd(d)) {
     cderror(d,"004: Unable to read table of contents header\n");
     return(-4);
   }
 
   /* copy to our structure and convert start sector */
-  tracks = d->sg_buffer[1];
+  tracks = sgi->dxferp[1];
   if (tracks > MAXTRK) {
     cderror(d,"003: CDROM reporting illegal number of tracks\n");
     return(-3);
   }
 
   for (i = 0; i < tracks; i++){
-    memcpy(d->sg_buffer, (char[]){ 0xe5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10);
-    d->sg_buffer[5]=i+1;
-    d->sg_buffer[8]=255;
+    sgi->setup_scsi_cmd(d, CDD522_READ_TOC(i+1), 10, 0, 256);
     
-    if (handle_scsi_cmd (d,10, 0, 256,'\377',1)){
+    if (sgi->handle_scsi_cmd(d)) {
       cderror(d,"005: Unable to read table of contents entry\n");
       return(-5);
     }
     
-    d->disc_toc[i].bFlags = d->sg_buffer[10];
+    d->disc_toc[i].bFlags = sgi->dxferp[10];
     d->disc_toc[i].bTrack = i + 1;
 
-    d->disc_toc[i].dwStartSector= d->adjust_ssize * 
-	(((signed char)(d->sg_buffer[2])<<24) | 
-	 (d->sg_buffer[3]<<16)|
-	 (d->sg_buffer[4]<<8)|
-	 (d->sg_buffer[5]));
+    d->disc_toc[i].dwStartSector = d->adjust_ssize * (
+        ((signed char)(sgi->dxferp[2])<<24) | 
+	(sgi->dxferp[3]<<16) |
+	(sgi->dxferp[4]<<8) |
+	(sgi->dxferp[5])
+      );
   }
 
   d->disc_toc[i].bFlags = 0;
   d->disc_toc[i].bTrack = i + 1;
-  memcpy (&foo, d->sg_buffer+2, 4);
-  memcpy (&bar, d->sg_buffer+6, 4);
-  d->disc_toc[i].dwStartSector = d->adjust_ssize * (be32_to_cpu(foo) +
-						    be32_to_cpu(bar));
-
-  d->disc_toc[i].dwStartSector= d->adjust_ssize * 
-    ((((signed char)(d->sg_buffer[2])<<24) | 
-      (d->sg_buffer[3]<<16)|
-      (d->sg_buffer[4]<<8)|
-      (d->sg_buffer[5]))+
-     
-     ((((signed char)(d->sg_buffer[6])<<24) | 
-       (d->sg_buffer[7]<<16)|
-       (d->sg_buffer[8]<<8)|
-       (d->sg_buffer[9]))));
-
+  memcpy (&msb, sgi->dxferp+2, 4);
+  memcpy (&lsb, sgi->dxferp+6, 4);
+  d->disc_toc[i].dwStartSector = d->adjust_ssize *
+      (be32_to_cpu(msb) + be32_to_cpu(lsb));
+
+  d->disc_toc[i].dwStartSector= d->adjust_ssize * (
+    ((((signed char)(sgi->dxferp[2])<<24) | 
+        (sgi->dxferp[3]<<16) |
+        (sgi->dxferp[4]<<8) |
+        (sgi->dxferp[5])
+      ) + (
+        ((signed char)(sgi->dxferp[6])<<24) | 
+        (sgi->dxferp[7]<<16) |
+        (sgi->dxferp[8]<<8) |
+        (sgi->dxferp[9])))
+    );
 
   d->cd_extra = FixupTOC(d,tracks+1);
-  return(tracks);
+  return tracks;
 }
 
 /* These do one 'extra' copy in the name of clean code */
 
-static int i_read_28 (cdrom_drive *d, void *p, long begin, long sectors){
+static int generic_scsi_read(cdrom_drive *d, void *p, long begin, long sectors,
+    char *read_cmd, int len, int in_size, int out_size) {
+  struct sg_info *sgi = (struct sg_info *)d->sg;
   int ret;
-  memcpy(d->sg_buffer,(char []){0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0},10);
-
-  if(d->fua)
-    d->sg_buffer[1]=0x08;
 
-  d->sg_buffer[1]|=d->lun<<5;
+  sgi->setup_scsi_cmd(d, read_cmd, len, in_size, out_size);
+#if 0    
+  sgi->dxferp[1] = d->lun << 5;
+#endif
 
-  d->sg_buffer[3] = (begin >> 16) & 0xFF;
-  d->sg_buffer[4] = (begin >> 8) & 0xFF;
-  d->sg_buffer[5] = begin & 0xFF;
-  d->sg_buffer[8] = sectors;
-  if((ret=handle_scsi_cmd(d,10,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+  ret = sgi->handle_scsi_cmd(d);
+  if (ret)
+    return ret;
+  if (p)
+    memcpy(p, sgi->dxferp, sectors * CD_FRAMESIZE_RAW);
+  return 0;
 }
 
-static int i_read_A8 (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  memcpy(d->sg_buffer,(char []){0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},12);
-
-  if(d->fua)
-    d->sg_buffer[1]=0x08;
-
-  d->sg_buffer[1]|=d->lun<<5;
-
-  d->sg_buffer[3] = (begin >> 16) & 0xFF;
-  d->sg_buffer[4] = (begin >> 8) & 0xFF;
-  d->sg_buffer[5] = begin & 0xFF;
-  d->sg_buffer[9] = sectors;
-  if((ret=handle_scsi_cmd(d,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+static int i_read_28(cdrom_drive *d, void *p, long begin, long sectors){
+  return generic_scsi_read(d, p, begin, sectors,
+    SCSI_READ_10(d->fua, begin, sectors), 10, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
-static int i_read_D4_10 (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  memcpy(d->sg_buffer,(char []){0xd4, 0, 0, 0, 0, 0, 0, 0, 0, 0},10);
-
-  if(d->fua)
-    d->sg_buffer[1]=0x08;
-
-  d->sg_buffer[1]|=d->lun<<5;
-  d->sg_buffer[3] = (begin >> 16) & 0xFF;
-  d->sg_buffer[4] = (begin >> 8) & 0xFF;
-  d->sg_buffer[5] = begin & 0xFF;
-  d->sg_buffer[8] = sectors;
-  if((ret=handle_scsi_cmd(d,10,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+static int i_read_A8(cdrom_drive *d, void *p, long begin, long sectors){
+  return generic_scsi_read(d, p, begin, sectors,
+    SCSI_READ_12(d->fua, begin, sectors), 12, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
-static int i_read_D4_12 (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  memcpy(d->sg_buffer,(char []){0xd4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},12);
-
-  if(d->fua)
-    d->sg_buffer[1]=0x08;
-
-  d->sg_buffer[1]|=d->lun<<5;
-  d->sg_buffer[3] = (begin >> 16) & 0xFF;
-  d->sg_buffer[4] = (begin >> 8) & 0xFF;
-  d->sg_buffer[5] = begin & 0xFF;
-  d->sg_buffer[9] = sectors;
-  if((ret=handle_scsi_cmd(d,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+static int i_read_D4_10(cdrom_drive *d, void *p, long begin, long sectors){
+  return generic_scsi_read(d, p, begin, sectors,
+    D4_READ_10(begin, sectors), 10, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
-static int i_read_D5 (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  memcpy(d->sg_buffer,(char []){0xd5, 0, 0, 0, 0, 0, 0, 0, 0, 0},10);
-
-  if(d->fua)
-    d->sg_buffer[1]=0x08;
-
-  d->sg_buffer[1]|=d->lun<<5;
-  d->sg_buffer[3] = (begin >> 16) & 0xFF;
-  d->sg_buffer[4] = (begin >> 8) & 0xFF;
-  d->sg_buffer[5] = begin & 0xFF;
-  d->sg_buffer[8] = sectors;
-  if((ret=handle_scsi_cmd(d,10,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+static int i_read_D4_12(cdrom_drive *d, void *p, long begin, long sectors){
+  return generic_scsi_read(d, p, begin, sectors,
+    D4_READ_12(begin, sectors), 12, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
-static int i_read_D8 (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  memcpy(d->sg_buffer,(char []){0xd8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},12);
-
-  if(d->fua)
-    d->sg_buffer[1]=0x08;
-
-  d->sg_buffer[1]|=d->lun<<5;
-  d->sg_buffer[3] = (begin >> 16) & 0xFF;
-  d->sg_buffer[4] = (begin >> 8) & 0xFF;
-  d->sg_buffer[5] = begin & 0xFF;
-  d->sg_buffer[9] = sectors;
-  if((ret=handle_scsi_cmd(d,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+static int i_read_D5(cdrom_drive *d, void *p, long begin, long sectors){
+  return generic_scsi_read(d, p, begin, sectors,
+    D5_READ_10(begin, sectors), 10, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
-static int i_read_mmc (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  /*  if(begin<=12007 && begin+sectors>12000){
-    errno=EIO;
-    return(TR_ILLEGAL);
-  }*/
-
-  memcpy(d->sg_buffer,(char []){0xbe, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0, 0},12);
-
-  d->sg_buffer[3] = (begin >> 16) & 0xFF;
-  d->sg_buffer[4] = (begin >> 8) & 0xFF;
-  d->sg_buffer[5] = begin & 0xFF;
-  d->sg_buffer[8] = sectors;
-  if((ret=handle_scsi_cmd(d,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+static int i_read_D8(cdrom_drive *d, void *p, long begin, long sectors){
+  return generic_scsi_read(d, p, begin, sectors,
+    D8_READ_12(begin, sectors), 12, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
-static int i_read_mmc2 (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  memcpy(d->sg_buffer,(char []){0xbe, 0, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0},12);
-
-  d->sg_buffer[3] = (begin >> 16) & 0xFF;
-  d->sg_buffer[4] = (begin >> 8) & 0xFF;
-  d->sg_buffer[5] = begin & 0xFF;
-  d->sg_buffer[8] = sectors;
-  if((ret=handle_scsi_cmd(d,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+static int i_read_mmc(cdrom_drive *d, void *p, long begin, long sectors){
+  return generic_scsi_read(d, p, begin, sectors,
+    READ_CD_12(begin, sectors), 12, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
-static int i_read_mmc3 (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  memcpy(d->sg_buffer,(char []){0xbe, 4, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0},12);
+static int i_read_mmc2(cdrom_drive *d, void *p, long begin, long sectors){
+  char cmd[12];
 
-  d->sg_buffer[3] = (begin >> 16) & 0xFF;
-  d->sg_buffer[4] = (begin >> 8) & 0xFF;
-  d->sg_buffer[5] = begin & 0xFF;
-  d->sg_buffer[8] = sectors;
-  if((ret=handle_scsi_cmd(d,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+  memcpy(cmd, READ_CD_12(begin, sectors), 12);
+  cmd[9] = 0xf8;
+  return generic_scsi_read(d, p, begin, sectors,
+    cmd, 12, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
-/* straight from the MMC3 spec */
-static inline void LBA_to_MSF(long lba,
-			      unsigned char *M, 
-			      unsigned char *S, 
-			      unsigned char *F){
-  if(lba>=-150){
-    *M=(lba+150)/(60*75);
-    lba-=(*M)*60*75;
-    *S=(lba+150)/75;
-    lba-=(*S)*75;
-    *F=(lba+150);
-  }else{
-    *M=(lba+450150)/(60*75);
-    lba-=(*M)*60*75;
-    *S=(lba+450150)/75;
-    lba-=(*S)*75;
-    *F=(lba+450150);
-  }
-}
-
-
-static int i_read_msf (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  memcpy(d->sg_buffer,(char []){0xb9, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0, 0},12);
+static int i_read_mmc3(cdrom_drive *d, void *p, long begin, long sectors){
+  char cmd[12];
 
-  LBA_to_MSF(begin,d->sg_buffer+3,d->sg_buffer+4,d->sg_buffer+5);
-  LBA_to_MSF(begin+sectors,d->sg_buffer+6,d->sg_buffer+7,d->sg_buffer+8);
+  memcpy(cmd, READ_CD_12(begin, sectors), 12);
+  cmd[1] = 4;  
+  cmd[9] = 0xf8;
+  return generic_scsi_read(d, p, begin, sectors,
+    cmd, 12, 0, sectors * CD_FRAMESIZE_RAW);
+}
 
-  if((ret=handle_scsi_cmd(d,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+static int i_read_msf(cdrom_drive *d, void *p, long begin, long sectors){
+  return generic_scsi_read(d, p, begin, sectors,
+    READ_CD_MSF_12(begin, sectors), 12, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
 static int i_read_msf2 (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  memcpy(d->sg_buffer,(char []){0xb9, 0, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0},12);
+  char cmd[12];
 
-  LBA_to_MSF(begin,d->sg_buffer+3,d->sg_buffer+4,d->sg_buffer+5);
-  LBA_to_MSF(begin+sectors,d->sg_buffer+6,d->sg_buffer+7,d->sg_buffer+8);
-
-  if((ret=handle_scsi_cmd(d,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+  memcpy(cmd, READ_CD_MSF_12(begin, sectors), 12);
+  cmd[9] = 0xf8;
+  return generic_scsi_read(d, p, begin, sectors,
+    cmd, 12, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
 static int i_read_msf3 (cdrom_drive *d, void *p, long begin, long sectors){
-  int ret;
-  memcpy(d->sg_buffer,(char []){0xb9, 4, 0, 0, 0, 0, 0, 0, 0, 0xf8, 0, 0},12);
-
-  LBA_to_MSF(begin,d->sg_buffer+3,d->sg_buffer+4,d->sg_buffer+5);
-  LBA_to_MSF(begin+sectors,d->sg_buffer+6,d->sg_buffer+7,d->sg_buffer+8);
+  char cmd[12];
 
-  if((ret=handle_scsi_cmd(d,12,0,sectors * CD_FRAMESIZE_RAW,'\177',1)))
-    return(ret);
-  if(p)memcpy(p,d->sg_buffer,sectors*CD_FRAMESIZE_RAW);
-  return(0);
+  memcpy(cmd, READ_CD_MSF_12(begin, sectors), 12);
+  cmd[1] = 4;
+  cmd[9] = 0xf8;
+  return generic_scsi_read(d, p, begin, sectors,
+    cmd, 12, 0, sectors * CD_FRAMESIZE_RAW);
 }
 
 static long scsi_read_map (cdrom_drive *d, void *p, long begin, long sectors,
@@ -940,6 +1042,10 @@
   int retry_count,err;
   char *buffer=(char *)p;
 
+  struct sg_info *sgi = (struct sg_info *)d->sg;
+  struct sg_io_hdr *hdr = sgi->hdr;
+  unsigned char key, ASC, ASCQ;
+
   /* read d->nsectors at a time, max. */
   sectors=(sectors>d->nsectors?d->nsectors:sectors);
   sectors=(sectors<1?1:sectors);
@@ -948,21 +1054,35 @@
   
   while(1) {
     if((err=map(d,(p?buffer:NULL),begin,sectors))){
-      if(d->report_all){
 #if defined(__linux__)
-	struct sg_header *sg_hd=(struct sg_header *)d->sg;
+      /* Dunno if we even need this now that scsi_reset does it,
+       * but try to take "device is becoming ready" into account */
+      key = hdr->sbp[2] & 0xf;
+      ASC = hdr->sbp[12];
+      ASCQ = hdr->sbp[13];
+
+      if(key == 2 & ASC == 4 & ASCQ == 1) {
+        if(retry_count > MAX_RETRIES-1) {
+          char b[256];
+          sprintf(b,"010: Unable to access sector %ld\n", begin);
+          cderror(d,b);
+          return(-10);
+        } else {
+          retry_count++;
+          usleep(100);
+          continue;
+        }
+      }
 #endif
+      if(d->report_all){
 	char b[256];
-
 	sprintf(b,"scsi_read error: sector=%ld length=%ld retry=%d\n",
 		begin,sectors,retry_count);
 	fputs(b, stderr);
 	cdmessage(d,b);
 	sprintf(b,"                 Sense key: %x ASC: %x ASCQ: %x\n",
 #if defined(__linux__)
-		(int)(sg_hd->sense_buffer[2]&0xf),
-		(int)(sg_hd->sense_buffer[12]),
-		(int)(sg_hd->sense_buffer[13]));
+                key, ASC, ASCQ);
 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 		d->ccb->csio.sense_data.flags & SSD_KEY,
 		d->ccb->csio.sense_data.add_sense_code,
@@ -1133,7 +1253,7 @@
 static int count_2352_bytes(cdrom_drive *d){
   long i;
   for(i=2351;i>=0;i--)
-    if(d->sg_buffer[i]!=(unsigned char)'\177')
+    if(((struct sg_info *)d->sg)->dxferp[i]!=(unsigned char)'\177')
       return(((i+3)>>2)<<2);
 
   return(0);
@@ -1142,7 +1262,7 @@
 static int verify_nonzero(cdrom_drive *d){
   long i,flag=0;
   for(i=0;i<2352;i++)
-    if(d->sg_buffer[i]!=0){
+    if(((struct sg_info *)d->sg)->dxferp[i]!=0){
       flag=1;
       break;
     }
@@ -1427,7 +1547,19 @@
 }
 
 #if defined(__linux__)
-static int check_atapi(cdrom_drive *d){
+static int check_sgio(cdrom_drive *d) {
+  int fd = d->cdda_fd;
+
+#ifdef SG_IO
+  if (fd == check_fd_sgio(fd))
+    return 1;
+#endif
+  return 0;
+}
+
+
+
+static int check_atapi(cdrom_drive *d, int using_sgio){
   int atapiret=-1;
   int fd = d->cdda_fd; /* this is the correct fd (not ioctl_fd), as the 
 			  generic device is the device we need to check */
@@ -1441,7 +1573,7 @@
     if(atapiret==1){
       cdmessage(d,"\tDrive is ATAPI (using SCSI host adaptor emulation)\n");
       /* Disable kernel SCSI command translation layer for access through sg */
-      if (ioctl(fd,SG_SET_TRANSFORM,0))
+      if (!using_sgio && ioctl(fd,SG_SET_TRANSFORM,0))
 	cderror(d,"\tCouldn't disable kernel command translation layer\n");
       d->is_atapi=1;
     }else{
@@ -1501,7 +1633,7 @@
   d->is_mmc=0;
   if(mode_sense(d,22,0x2A)==0){
   
-    b=d->sg_buffer;
+    b=((struct sg_info *)d->sg)->dxferp;
     b+=b[3]+4;
     
     if((b[0]&0x3F)==0x2A){
@@ -1543,13 +1675,19 @@
 #if defined(__linux__)
 /* request vendor brand and model */
 unsigned char *scsi_inquiry(cdrom_drive *d){
-  memcpy(d->sg_buffer,(char[]){ 0x12,0,0,0,56,0},6);
+  static char ret[56];
+  struct sg_info *sgi = (struct sg_info *)d->sg;
 
-  if(handle_scsi_cmd(d,6, 0, 56,'\377',1)) {
+  if (sgi->hdr == NULL)
+    scsi_init_drive(d);
+
+  sgi->setup_scsi_cmd(d, SCSI_INQUIRY_6(56), 6, 0, 56);
+  if (sgi->handle_scsi_cmd(d)) {
     cderror(d,"008: Unable to identify CDROM model\n");
-    return(NULL);
+    return NULL;
   }
-  return (d->sg_buffer);
+  memcpy(ret, ((struct sg_info *)d->sg)->dxferp, 56);
+  return ret;
 }
 #endif
 
@@ -1557,7 +1695,13 @@
 int scsi_init_drive(cdrom_drive *d){
   int ret;
 
+#if defined(__linux__)
+  int is_sgio = check_sgio(d);
+  check_atapi(d, is_sgio);
+#elif defined(__FreeBSD__)
   check_atapi(d);
+#endif
+
   check_mmc(d);
 
   /* generic Sony type defaults; specialize from here */
@@ -1614,17 +1758,16 @@
   if(d->tracks<1)
     return(d->tracks);
 
+#if defined(__FreeBSD__)
   tweak_SG_buffer(d);
+#endif
   d->opened=1;
 
   if((ret=verify_read_command(d)))return(ret);
   check_fua_bit(d);
 
   d->error_retry=1;
-#if defined(__linux__)
-  d->sg=realloc(d->sg,d->nsectors*CD_FRAMESIZE_RAW + SG_OFF + 128);
-  d->sg_buffer=d->sg+SG_OFF;
-#elif defined(__FreeBSD__)
+#if defined(__FreeBSD__)
   d->sg_buffer = realloc(d->sg_buffer, d->nsectors * CD_FRAMESIZE_RAW);
 #endif
   d->report_all=1;

Reply to: