--- linux/include/scsi/sg.h.orig	Sat Apr 11 00:22:21 1998
+++ linux/include/scsi/sg.h	Wed Oct 14 13:10:04 1998
@@ -3,17 +3,82 @@
     Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user 
      process control of SCSI devices.
     Development Sponsored by Killy Corp. NY NY
+    Enhancements for portable SCSI transport library written by
+    Heiko Eißfeldt and Jörg Schilling
 */
 
 #ifndef _SCSI_GENERIC_H
 #define _SCSI_GENERIC_H
 
+/*
+ * ioctl's
+ *
+ * Note that all timeouts are multiples of of a clock tick (HZ).
+ * Hz is usually 100 but it is 1024 on DEC alpha!
+ */
+#define SG_SET_TIMEOUT 0x2201  /* set timeout *(int *)arg==timeout in clock ticks*/
+#define SG_GET_TIMEOUT 0x2202  /* get timeout return timeout in clock ticks */
+#define SG_EMULATED_HOST 0x2203 /* true for emulated host adapter (ATAPI) */
+
+/* Used to configure SCSI command transformation layer for ATAPI devices */
+#define SG_SET_TRANSFORM 0x2204
+#define SG_GET_TRANSFORM 0x2205
+#define SG_GET_BUFSIZE 0x2206  /* get buffer size return actual SG_BIG_BUFF */
+
+
+#define SG_MAX_SENSE	16	/* Size of auto-request-sense buffer */
+
 /* 
  An SG device is accessed by writing "packets" to it, the replies
  are then read using the read call. The same header is used for 
  reply, just ignore reply_len field.
 */
+#ifdef	SG_GET_BUFSIZE
+/*
+ * This is the new version of the data structure.
+ * It tries to make the best approach without breaking the old behaviour.
+ * This version is intended to make 'sg' usable for developing SCSI user
+ * applications until a better driver is included into Linux.
+ * this version of the driver is still not able to return the amount of 
+ * sense data that is required by CCS (18 bytes).
+ *
+ * The new version is granted to be present if SG_GET_BUFSIZE is defined.
+ * The fields pack_len, reply_len and sense_len are used for different
+ * purpose when sending / receiving packets.
+ *
+ * On return:
+ * pack_len is the cmd_status						->DONE
+ * reply_len is the actual DMA transfer count				->FUTURE
+ * sense_len is the number of bytes acually returned in sense_buffer	->FUTURE
+ *
+ * Only old code that does not clear the unused 31 bits of the 
+ * old data structure breaks with the new version of the driver.
+ */
+struct sg_header {
+  int pack_len;			/* length of incoming packet (including header) / */
+  int reply_len;		/* maximum length of expected reply / actual transfer count */
+  int pack_id;			/* id number of packet */
+  int result;			/* 0==ok, otherwise refer to errno codes */
+  unsigned int twelve_byte :1;	/* Force 12 byte command length (unused if want_new is set) */
+  unsigned int want_new    :1;	/* User requests new behaviour */
+  unsigned int grant_new   :1;	/* Driver grants new behaviour */
+  unsigned int cdb_len     :5;	/* Command descriptor block length 6..31 (mandatory) */
+  unsigned int sense_len   :5;	/* Set max / get actual sense length 0..31 (mandatory) */
+  unsigned int other_flags :19;	/* for future use */
+  unsigned char sense_buffer[SG_MAX_SENSE];/* used only by reads */
 
+  /* command follows then data for command */
+ };
+/*
+ * cmd_status contains:
+ * driver_byte << 24 | host_byte << 16 | msg_byte << 8 | status_byte
+ */
+#define	sg_cmd_status	pack_len
+#else
+/*
+ * This is the old version of the data structure it is included to
+ * help developers to migrate to the new interface.
+ */
 struct sg_header
  {
   int pack_len;    /* length of incoming packet <4096 (including header) */
@@ -25,16 +90,7 @@
   unsigned char sense_buffer[16]; /* used only by reads */
   /* command follows then data for command */
  };
-
-/* ioctl's */
-#define SG_SET_TIMEOUT 0x2201  /* set timeout *(int *)arg==timeout */
-#define SG_GET_TIMEOUT 0x2202  /* get timeout return timeout */
-
-#define SG_EMULATED_HOST 0x2203 /* true for emulated host adapter (ATAPI) */
-
-/* Used to configure SCSI command transformation layer for ATAPI devices */
-#define SG_SET_TRANSFORM 0x2204
-#define SG_GET_TRANSFORM 0x2205
+#endif
 
 #define SG_DEFAULT_TIMEOUT (60*HZ) /* 1 minute timeout */
 #define SG_DEFAULT_RETRIES 1
--- linux/kernel/sysctl.c.orig	Wed Sep 30 19:15:03 1998
+++ linux/kernel/sysctl.c	Wed Oct 14 13:11:41 1998
@@ -45,9 +45,6 @@
 #ifdef CONFIG_KMOD
 extern char modprobe_path[];
 #endif
-#ifdef CONFIG_CHR_DEV_SG
-extern int sg_big_buff;
-#endif
 
 #ifdef __sparc__
 extern char reboot_command [];
@@ -188,10 +185,6 @@
 #ifdef CONFIG_KMOD
 	{KERN_MODPROBE, "modprobe", &modprobe_path, 256,
 	 0644, NULL, &proc_dostring, &sysctl_string },
-#endif
-#ifdef CONFIG_CHR_DEV_SG
-	{KERN_SG_BIG_BUFF, "sg-big-buff", &sg_big_buff, sizeof (int),
-	 0444, NULL, &proc_dointvec},
 #endif
 	{0}
 };
--- linux/drivers/scsi/sg.c.orig	Mon Aug 24 22:14:10 1998
+++ linux/drivers/scsi/sg.c	Wed Oct 14 13:11:41 1998
@@ -5,6 +5,14 @@
  *  Development Sponsored by Killy Corp. NY NY
  *
  *  Borrows code from st driver.
+ *
+ *  Heiko Eißfeldt, 08.01.1998
+ *  Changed buffer allocation to a lazy kmalloc scheme in order to allow
+ *  multiple requests in parallel on different devices. For example reading
+ *  from a cdrom and writing on a cd burner.
+ *
+ *  NOTE: SG_BIG_BUFF is kept for compatibility, but has no effect anymore.
+ *  The corresponding sysctl entry is gone.
  */
 #include <linux/module.h>
 
@@ -28,8 +36,6 @@
 #include <scsi/scsi_ioctl.h>
 #include <scsi/sg.h>
 
-int sg_big_buff = SG_BIG_BUFF;		/* for now, sg_big_buff is read-only through sysctl */
-
 static int sg_init(void);
 static int sg_attach(Scsi_Device *);
 static int sg_detect(Scsi_Device *);
@@ -41,12 +47,6 @@
 					       sg_detect, sg_init,
 					       NULL, sg_attach, sg_detach};
 
-#ifdef SG_BIG_BUFF
-static char *big_buff = NULL;
-static struct wait_queue *big_wait;   /* wait for buffer available */
-static int big_inuse=0;
-#endif
-
 struct scsi_generic
 {
     Scsi_Device *device;
@@ -64,7 +64,6 @@
 };
 
 static struct scsi_generic *scsi_generics=NULL;
-static void sg_free(char *buff,int size);
 
 static int sg_ioctl(struct inode * inode,struct file * file,
 		    unsigned int cmd_in, unsigned long arg)
@@ -97,6 +96,12 @@
 	return scsi_generics[dev].timeout;
     case SG_EMULATED_HOST:
     	return put_user(scsi_generics[dev].device->host->hostt->emulated, (int *) arg);
+    case SG_GET_BUFSIZE:
+#ifdef	SG_BIG_BUFF
+	return SG_BIG_BUFF;
+#else
+	return 4096;
+#endif
     case SCSI_IOCTL_SEND_COMMAND:
 	/*
 	  Allow SCSI_IOCTL_SEND_COMMAND without checking suser() since the
@@ -109,6 +114,47 @@
     }
 }
 
+static char *sg_lazy_malloc(int bsize, int *buff_len,
+	const struct scsi_generic *s_dev)
+{
+    void * retval;
+    int chunksize;
+
+    /* if we have enough space in last buffer, recycle buffer */
+    if (s_dev->buff != NULL && *buff_len >= bsize)
+	return s_dev->buff;
+
+    /* we don't have enough space. Forget the old buffer,
+       then get a bigger one */
+    if (s_dev->buff != NULL)
+	kfree(s_dev->buff);
+
+    /* quantify block size: 32K or 64K or 128K or more, if possible */
+    if (bsize <= 32 * 1024) {
+	chunksize = 32 * 1024;
+    } else if (bsize <= 64 * 1024) {
+	chunksize = 64 * 1024;
+    } else if (bsize <= 128 * 1024) {
+	chunksize = 128 * 1024;
+    } else {
+	/* for future extensions */
+	chunksize = bsize;
+    }
+
+    /* alloc a new block */
+    retval = kmalloc(chunksize, GFP_KERNEL | GFP_DMA);
+
+    /* if the kmalloc failed, let the size be zero */
+    if (retval == NULL) {
+	chunksize = 0;
+    }
+
+    /* register size of allocated block */
+    *buff_len = chunksize;
+
+    return (char *) retval;
+}
+
 static int sg_open(struct inode * inode, struct file * filp)
 {
     int dev=MINOR(inode->i_rdev);
@@ -127,6 +173,9 @@
   /*
    * If we want exclusive access, then wait until the device is not
    * busy, and then set the flag to prevent anyone else from using it.
+   *
+   * FIXME (HE): The device can be used through another driver!
+   * This goes currently unnoticed. Add a check for this.
    */
     if (flags & O_EXCL)
     {
@@ -164,7 +213,7 @@
         && scsi_generics[dev].complete)
     {
 	if (scsi_generics[dev].buff != NULL)
-	    sg_free(scsi_generics[dev].buff,scsi_generics[dev].buff_len);
+	    kfree(scsi_generics[dev].buff);
 	scsi_generics[dev].buff=NULL;
 	scsi_generics[dev].pending=0;
     }
@@ -181,6 +230,10 @@
 static int sg_close(struct inode * inode, struct file * filp)
 {
     int dev=MINOR(inode->i_rdev);
+    if (scsi_generics[dev].buff != NULL) {
+        kfree(scsi_generics[dev].buff);
+        scsi_generics[dev].buff = NULL;
+    }
     scsi_generics[dev].users--;
     if (scsi_generics[dev].device->host->hostt->module)
 	__MOD_DEC_USE_COUNT(scsi_generics[dev].device->host->hostt->module);
@@ -191,38 +244,6 @@
     return 0;
 }
 
-static char *sg_malloc(int size)
-{
-    if (size<=4096)
-	return (char *) scsi_malloc(size);
-#ifdef SG_BIG_BUFF
-    if (size<=SG_BIG_BUFF)
-    {
-	while(big_inuse)
-	{
-	    interruptible_sleep_on(&big_wait);
-	    if (signal_pending(current))
-		return NULL;
-	}
-	big_inuse=1;
-	return big_buff;
-    }
-#endif
-    return NULL;
-}
-
-static void sg_free(char *buff,int size)
-{
-#ifdef SG_BIG_BUFF
-    if (buff==big_buff)
-    {
-	big_inuse=0;
-	wake_up(&big_wait);
-	return;
-    }
-#endif
-    scsi_free(buff,size);
-}
 
 /*
  * Read back the results of a previous command.  We use the pending and
@@ -274,14 +295,16 @@
     /*
      * Now copy the result back to the user buffer.
      */
-    device->header.pack_len=device->header.reply_len;
+    if (device->header.want_new == 0) {
+	device->header.pack_len=device->header.reply_len;
+    }
 
     if (count>=sizeof(struct sg_header))
     {
 	copy_to_user(buf,&device->header,sizeof(struct sg_header));
 	buf+=sizeof(struct sg_header);
-	if (count>device->header.pack_len)
-	    count=device->header.pack_len;
+	if (count>device->header.reply_len)
+	    count=device->header.reply_len;
 	if (count > sizeof(struct sg_header)) {
 	    copy_to_user(buf,device->buff,count-sizeof(struct sg_header));
 	}
@@ -293,8 +316,6 @@
      * Clean up, and release the device so that we can send another
      * command.
      */
-    sg_free(device->buff,device->buff_len);
-    device->buff = NULL;
     device->pending=0;
     wake_up(&device->write_wait);
     return count;
@@ -323,6 +344,10 @@
      */
     memcpy(device->header.sense_buffer, SCpnt->sense_buffer,
 	   sizeof(SCpnt->sense_buffer));
+    if (device->header.want_new) {
+	device->header.grant_new = 1;
+	device->header.pack_len = SCpnt->result;
+    }
     switch (host_byte(SCpnt->result)) {
     case DID_OK:
       device->header.result = 0;
@@ -355,6 +380,17 @@
       break;
     }
 
+    if (driver_byte(SCpnt->result) != 0
+        && (SCpnt->sense_buffer[0] & 0x7f) == 0x70
+        && (SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION
+        && device->device->removable)
+      {
+        /*
+         * detected disc change.  Set the bit - this may be used if there
+         * are filesystems using this device.
+         */
+        device->device->changed = 1;
+      }
     /*
      * Now wake up the process that is waiting for the
      * result.
@@ -435,7 +471,11 @@
      */
     get_user(opcode, buf);
     size=COMMAND_SIZE(opcode);
-    if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
+    if (device->header.want_new) {
+	size = device->header.cdb_len;
+    } else {
+	if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
+    }
 
     /*
      * Determine buffer size.
@@ -466,18 +506,17 @@
 
     /*
      * Allocate a buffer that is large enough to hold the data
-     * that has been requested.  Round up to an even number of sectors,
-     * since scsi_malloc allocates in chunks of 512 bytes.
+     * that has been requested.
      */
     amt=bsize;
     if (!bsize)
 	bsize++;
-    bsize=(bsize+511) & ~511;
 
     /*
      * If we cannot allocate the buffer, report an error.
      */
-    if ((bsize<0) || !(device->buff=sg_malloc(device->buff_len=bsize)))
+    if ((bsize<0)
+        || !(device->buff=sg_lazy_malloc(bsize, &device->buff_len, device)))
     {
 	device->pending=0;
 	wake_up(&device->write_wait);
@@ -496,8 +535,6 @@
     {
 	device->pending=0;
 	wake_up(&device->write_wait);
-	sg_free(device->buff,device->buff_len);
-	device->buff = NULL;
 	return -EAGAIN;
     }
 #ifdef DEBUG
@@ -621,10 +658,6 @@
     printk("sg: Init generic device.\n");
 #endif
 
-#ifdef SG_BIG_BUFF
-    big_buff= (char *) scsi_init_malloc(SG_BIG_BUFF, GFP_ATOMIC | GFP_DMA);
-#endif
-
     scsi_generics = (struct scsi_generic *)
 	scsi_init_malloc((sg_template.dev_noticed + SG_EXTRA_DEVS)
 			 * sizeof(struct scsi_generic), GFP_ATOMIC);
@@ -704,10 +737,6 @@
 		       * sizeof(struct scsi_generic));
     }
     sg_template.dev_max = 0;
-#ifdef SG_BIG_BUFF
-    if(big_buff != NULL)
-	scsi_init_free(big_buff, SG_BIG_BUFF);
-#endif
 }
 #endif /* MODULE */
 
