The driver is currently maintained by Kai Mäkisara (email
 Kai.Makisara@kolumbus.fi)
 
-Last modified: Mon Mar  7 21:14:44 2005 by kai.makisara
+Last modified: Thu Feb 21 21:54:16 2008 by kai.makisara
 
 
 BASICS
             MT_ST_SYSV sets the SYSV semantics (mode)
             MT_ST_NOWAIT enables immediate mode (i.e., don't wait for
                the command to finish) for some commands (e.g., rewind)
+            MT_ST_SILI enables setting the SILI bit in SCSI commands when
+               reading in variable block mode to enhance performance when
+               reading blocks shorter than the byte count; set this only
+               if you are sure that the drive supports SILI and the HBA
+               correctly returns transfer residuals
             MT_ST_DEBUGGING debugging (global; debugging must be
                compiled into the driver)
        MT_ST_SETBOOLEANS
 
    Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
  */
 
-static const char *verstr = "20080221";
+static const char *verstr = "20080224";
 
 #include <linux/module.h>
 
 
 static struct st_buffer *new_tape_buffer(int, int, int);
 static int enlarge_buffer(struct st_buffer *, int, int);
+static void clear_buffer(struct st_buffer *);
 static void normalize_buffer(struct st_buffer *);
 static int append_to_buffer(const char __user *, struct st_buffer *, int);
 static int from_buffer(struct st_buffer *, char __user *, int);
 
        memcpy(SRpnt->sense, sense, SCSI_SENSE_BUFFERSIZE);
        (STp->buffer)->cmdstat.midlevel_result = SRpnt->result = result;
+       (STp->buffer)->cmdstat.residual = resid;
        DEB( STp->write_pending = 0; )
 
        if (SRpnt->waiting)
                goto err_out;
        }
 
+       (STp->buffer)->cleared = 0;
        (STp->buffer)->writing = 0;
        (STp->buffer)->syscall_result = 0;
 
                if (STp->block_size)
                        bufsize = STp->block_size > st_fixed_buffer_size ?
                                STp->block_size : st_fixed_buffer_size;
-               else
+               else {
                        bufsize = count;
+                       /* Make sure that data from previous user is not leaked even if
+                          HBA does not return correct residual */
+                       if (is_read && STp->sili && !STbp->cleared)
+                               clear_buffer(STbp);
+               }
+
                if (bufsize > STbp->buffer_size &&
                    !enlarge_buffer(STbp, bufsize, STp->restr_dma)) {
                        printk(KERN_WARNING "%s: Can't allocate %d byte tape buffer.\n",
        memset(cmd, 0, MAX_COMMAND_SIZE);
        cmd[0] = READ_6;
        cmd[1] = (STp->block_size != 0);
+       if (!cmd[1] && STp->sili)
+               cmd[1] |= 2;
        cmd[2] = blks >> 16;
        cmd[3] = blks >> 8;
        cmd[4] = blks;
 
        }
        /* End of error handling */ 
-       else                    /* Read successful */
+       else {                  /* Read successful */
                STbp->buffer_bytes = bytes;
+               if (STp->sili) /* In fixed block mode residual is always zero here */
+                       STbp->buffer_bytes -= STp->buffer->cmdstat.residual;
+       }
 
        if (STps->drv_block >= 0) {
                if (STp->block_size == 0)
                       name, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
                       STp->scsi2_logical);
                printk(KERN_INFO
-                      "%s:    sysv: %d nowait: %d\n", name, STm->sysv, STp->immediate);
+                      "%s:    sysv: %d nowait: %d sili: %d\n", name, STm->sysv, STp->immediate,
+                       STp->sili);
                printk(KERN_INFO "%s:    debugging: %d\n",
                       name, debugging);
        }
                STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
                STp->immediate = (options & MT_ST_NOWAIT) != 0;
                STm->sysv = (options & MT_ST_SYSV) != 0;
+               STp->sili = (options & MT_ST_SILI) != 0;
                DEB( debugging = (options & MT_ST_DEBUGGING) != 0;
                     st_log_options(STp, STm, name); )
        } else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
                        STp->immediate = value;
                if ((options & MT_ST_SYSV) != 0)
                        STm->sysv = value;
+               if ((options & MT_ST_SILI) != 0)
+                       STp->sili = value;
                 DEB(
                if ((options & MT_ST_DEBUGGING) != 0)
                        debugging = value;
                STbuffer->frp_segs += 1;
                got += b_size;
                STbuffer->buffer_size = got;
+               if (STbuffer->cleared)
+                       memset(page_address(STbuffer->frp[segs].page), 0, b_size);
                segs++;
        }
        STbuffer->b_data = page_address(STbuffer->frp[0].page);
 }
 
 
+/* Make sure that no data from previous user is in the internal buffer */
+static void clear_buffer(struct st_buffer * st_bp)
+{
+       int i;
+
+       for (i=0; i < st_bp->frp_segs; i++)
+               memset(page_address(st_bp->frp[i].page), 0, st_bp->frp[i].length);
+       st_bp->cleared = 1;
+}
+
+
 /* Release the extra buffer */
 static void normalize_buffer(struct st_buffer * STbuffer)
 {
        tpnt->two_fm = ST_TWO_FM;
        tpnt->fast_mteom = ST_FAST_MTEOM;
        tpnt->scsi2_logical = ST_SCSI2LOGICAL;
+       tpnt->sili = ST_SILI;
        tpnt->immediate = ST_NOWAIT;
        tpnt->default_drvbuffer = 0xff;         /* No forced buffering */
        tpnt->partition = 0;