patch-2.4.25 linux-2.4.25/drivers/scsi/osst.c
Next file: linux-2.4.25/drivers/scsi/osst.h
Previous file: linux-2.4.25/drivers/scsi/mvme16x.c
Back to the patch index
Back to the overall index
- Lines: 1142
- Date:
2004-02-18 05:36:31.000000000 -0800
- Orig file:
linux-2.4.24/drivers/scsi/osst.c
- Orig date:
2001-12-21 09:41:55.000000000 -0800
diff -urN linux-2.4.24/drivers/scsi/osst.c linux-2.4.25/drivers/scsi/osst.c
@@ -16,15 +16,15 @@
Copyright 1992 - 2000 Kai Makisara
email Kai.Makisara@metla.fi
- $Header: /home/cvsroot/Driver/osst.c,v 1.65 2001/11/11 20:38:56 riede Exp $
+ $Header: /cvsroot/osst/Driver/osst.c,v 1.67.2.1 2003/06/28 00:21:28 riede Exp $
Microscopic alterations - Rik Ling, 2000/12/21
Last modified: Wed Feb 2 22:04:05 2000 by makisara@kai.makisara.local
Some small formal changes - aeb, 950809
*/
-static const char * cvsid = "$Id: osst.c,v 1.65 2001/11/11 20:38:56 riede Exp $";
-const char * osst_version = "0.9.10";
+static const char * cvsid = "$Id: osst.c,v 1.67.2.2 2003/12/14 14:28:46 wriede Exp $";
+const char * osst_version = "0.9.14";
/* The "failure to reconnect" firmware bug */
#define OSST_FW_NEED_POLL_MIN 10601 /*(107A)*/
@@ -36,6 +36,7 @@
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/string.h>
@@ -75,9 +76,8 @@
#include "constants.h"
-static int buffer_kbs = 0;
+static int max_dev = 0;
static int write_threshold_kbs = 0;
-static int max_buffers = 0;
static int max_sg_segs = 0;
#ifdef MODULE
@@ -85,18 +85,16 @@
MODULE_DESCRIPTION("OnStream SCSI Tape Driver");
MODULE_LICENSE("GPL");
-MODULE_PARM(buffer_kbs, "i");
+MODULE_PARM(max_dev, "i");
MODULE_PARM(write_threshold_kbs, "i");
-MODULE_PARM(max_buffers, "i");
MODULE_PARM(max_sg_segs, "i");
#else
static struct osst_dev_parm {
char *name;
int *val;
} parms[] __initdata = {
- { "buffer_kbs", &buffer_kbs },
+ { "max_dev", &max_dev },
{ "write_threshold_kbs", &write_threshold_kbs },
- { "max_buffers", &max_buffers },
{ "max_sg_segs", &max_sg_segs }
};
#endif
@@ -117,11 +115,16 @@
// #define OSST_INJECT_ERRORS 1
#endif
-#define MAX_RETRIES 0
+#define MAX_RETRIES 2
+#define MAX_READ_RETRIES 0
#define MAX_WRITE_RETRIES 0
-#define MAX_READY_RETRIES 5
+#define MAX_READY_RETRIES 0
#define NO_TAPE NOT_READY
+#define OSST_WAIT_POSITION_COMPLETE (HZ > 200 ? HZ / 200 : 1)
+#define OSST_WAIT_WRITE_COMPLETE (HZ / 12)
+#define OSST_WAIT_LONG_WRITE_COMPLETE (HZ / 2)
+
#define OSST_TIMEOUT (200 * HZ)
#define OSST_LONG_TIMEOUT (1800 * HZ)
@@ -137,6 +140,7 @@
static int osst_write_threshold = OSST_WRITE_THRESHOLD;
static int osst_max_buffers = OSST_MAX_BUFFERS;
static int osst_max_sg_segs = OSST_MAX_SG;
+static int osst_max_dev = OSST_MAX_TAPES;
static OS_Scsi_Tape **os_scsi_tapes = NULL;
static OSST_buffer **osst_buffers = NULL;
@@ -539,8 +543,6 @@
STp->first_frame_position);
goto err_out;
}
- STp->frame_in_buffer = 1;
-
if (frame_seq_number != -1 && ntohl(aux->frame_seq_num) != frame_seq_number) {
if (!quiet)
#if DEBUG
@@ -567,12 +569,14 @@
}
if (aux->frame_type == OS_FRAME_TYPE_EOD) {
STps->eof = ST_EOD_1;
+ STp->frame_in_buffer = 1;
}
if (aux->frame_type == OS_FRAME_TYPE_DATA) {
blk_cnt = ntohs(aux->dat.dat_list[0].blk_cnt);
blk_sz = ntohl(aux->dat.dat_list[0].blk_sz);
STp->buffer->buffer_bytes = blk_cnt * blk_sz;
STp->buffer->read_pointer = 0;
+ STp->frame_in_buffer = 1;
/* See what block size was used to write file */
if (STp->block_size != blk_sz && blk_sz > 0) {
@@ -599,18 +603,23 @@
/*
* Wait for the unit to become Ready
*/
-static int osst_wait_ready(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, unsigned timeout)
+static int osst_wait_ready(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, unsigned timeout, int initial_delay)
{
unsigned char cmd[MAX_COMMAND_SIZE];
Scsi_Request * SRpnt;
long startwait = jiffies;
#if DEBUG
int dbg = debugging;
- int dev = TAPE_NR(STp->devt);
+ int dev = TAPE_NR(STp->devt);
printk(OSST_DEB_MSG "osst%d:D: Reached onstream wait ready\n", dev);
#endif
+ if (initial_delay > 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(initial_delay);
+ }
+
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = TEST_UNIT_READY;
@@ -722,10 +731,10 @@
{
int retval;
- osst_wait_ready(STp, aSRpnt, 15 * 60); /* TODO - can this catch a write error? */
+ osst_wait_ready(STp, aSRpnt, 15 * 60, 0); /* TODO - can this catch a write error? */
retval = osst_set_frame_position(STp, aSRpnt, frame, 0);
if (retval) return (retval);
- osst_wait_ready(STp, aSRpnt, 15 * 60);
+ osst_wait_ready(STp, aSRpnt, 15 * 60, OSST_WAIT_POSITION_COMPLETE);
return (osst_get_frame_position(STp, aSRpnt));
}
@@ -738,6 +747,7 @@
Scsi_Request * SRpnt;
int result = 0;
+ int delay = OSST_WAIT_WRITE_COMPLETE;
#if DEBUG
int dev = TAPE_NR(STp->devt);
@@ -751,12 +761,17 @@
SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, SCSI_DATA_NONE, STp->timeout, MAX_WRITE_RETRIES, TRUE);
*aSRpnt = SRpnt;
if (!SRpnt) return (-EBUSY);
-
- if ((STp->buffer)->syscall_result)
- result = osst_write_error_recovery(STp, aSRpnt, 0);
-
- result |= osst_wait_ready(STp, aSRpnt, 5 * 60);
+ if (STp->buffer->syscall_result) {
+ if ((SRpnt->sr_sense_buffer[2] & 0x0f) == 2 && SRpnt->sr_sense_buffer[12] == 4) {
+ if (SRpnt->sr_sense_buffer[13] == 8) {
+ delay = OSST_WAIT_LONG_WRITE_COMPLETE;
+ }
+ } else
+ result = osst_write_error_recovery(STp, aSRpnt, 0);
+ }
+ result |= osst_wait_ready(STp, aSRpnt, 5 * 60, delay);
STp->ps[STp->partition].rw = OS_WRITING_COMPLETE;
+
return (result);
}
@@ -833,9 +848,6 @@
/* TODO: Error handling */
if (STp->poll)
retval = osst_wait_frame (STp, aSRpnt, STp->first_frame_position, 0, timeout);
-#if 0// DEBUG
- printk ("osst_read: wait for frame returned %i\n", retval);
-#endif
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = READ_6;
@@ -847,7 +859,7 @@
printk(OSST_DEB_MSG "osst%d:D: Reading frame from OnStream tape\n", dev);
#endif
SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, OS_FRAME_SIZE, SCSI_DATA_READ,
- STp->timeout, MAX_RETRIES, TRUE);
+ STp->timeout, MAX_READ_RETRIES, TRUE);
*aSRpnt = SRpnt;
if (!SRpnt)
return (-EBUSY);
@@ -903,7 +915,8 @@
#endif
if (STps->rw != ST_READING) { /* Initialize read operation */
- if (STps->rw == ST_WRITING) {
+ if (STps->rw == ST_WRITING || STp->dirty) {
+ STp->write_type = OS_WRITE_DATA;
osst_flush_write_buffer(STp, aSRpnt);
osst_flush_drive_buffer(STp, aSRpnt);
}
@@ -921,7 +934,7 @@
#if DEBUG
printk(OSST_DEB_MSG "osst%d:D: Start Read Ahead on OnStream tape\n", dev);
#endif
- SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, SCSI_DATA_NONE, STp->timeout, MAX_RETRIES, TRUE);
+ SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, SCSI_DATA_NONE, STp->timeout, MAX_READ_RETRIES, TRUE);
*aSRpnt = SRpnt;
retval = STp->buffer->syscall_result;
}
@@ -940,6 +953,15 @@
position;
/*
+ * If we want just any frame (-1) and there is a frame in the buffer, return it
+ */
+ if (frame_seq_number == -1 && STp->frame_in_buffer) {
+#if DEBUG
+ printk(OSST_DEB_MSG "osst%d:D: Frame %d still in buffer\n", dev, STp->frame_seq_number);
+#endif
+ return (STps->eof);
+ }
+ /*
* Search and wait for the next logical tape frame
*/
while (1) {
@@ -1092,6 +1114,7 @@
if (move < 0) move -= (OS_DATA_SIZE / STp->block_size) - 1;
move /= (OS_DATA_SIZE / STp->block_size);
}
+ if (!move) move = logical_blk_num > STp->logical_blk_num ? 1 : -1;
#if DEBUG
printk(OSST_DEB_MSG
"osst%d:D: Seek retry %d at ppos %d fsq %d (est %d) lbn %d (need %d) move %d\n",
@@ -1289,7 +1312,7 @@
cmd[8] = 32768 & 0xff;
SRpnt = osst_do_scsi(SRpnt, STp, cmd, OS_FRAME_SIZE, SCSI_DATA_READ,
- STp->timeout, MAX_RETRIES, TRUE);
+ STp->timeout, MAX_READ_RETRIES, TRUE);
if ((STp->buffer)->syscall_result || !SRpnt) {
printk(KERN_ERR "osst%d:E: Failed to read frame back from OnStream buffer\n", dev);
@@ -1329,7 +1352,7 @@
dev, new_frame+i, frame_seq_number+i);
#endif
osst_set_frame_position(STp, aSRpnt, new_frame + i, 0);
- osst_wait_ready(STp, aSRpnt, 60);
+ osst_wait_ready(STp, aSRpnt, 60, OSST_WAIT_POSITION_COMPLETE);
osst_get_frame_position(STp, aSRpnt);
SRpnt = * aSRpnt;
@@ -1397,6 +1420,7 @@
if (SRpnt->sr_sense_buffer[2] == 2 && SRpnt->sr_sense_buffer[12] == 4 &&
(SRpnt->sr_sense_buffer[13] == 1 || SRpnt->sr_sense_buffer[13] == 8)) {
/* in the process of becoming ready */
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 10);
continue;
}
@@ -1469,6 +1493,8 @@
osst_set_frame_position(STp, aSRpnt, frame + skip, 1);
flag = 0;
attempts--;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
}
if (osst_get_frame_position(STp, aSRpnt) < 0) { /* additional write error */
#if DEBUG
@@ -1529,6 +1555,7 @@
debugging = 0;
}
#endif
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 10);
}
printk(KERN_ERR "osst%d:E: Failed to find valid tape media\n", dev);
@@ -1674,12 +1701,7 @@
dev, last_mark_ppos);
return (-EIO);
}
- if (mt_op == MTBSFM) {
- STp->frame_seq_number++;
- STp->frame_in_buffer = 0;
- STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
- }
- return 0;
+ goto found;
}
#if DEBUG
printk(OSST_DEB_MSG "osst%d:D: Reverting to scan filemark backwards\n", dev);
@@ -1707,10 +1729,13 @@
return (-EIO);
}
}
+found:
if (mt_op == MTBSFM) {
STp->frame_seq_number++;
- STp->frame_in_buffer = 0;
- STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
+ STp->frame_in_buffer = 0;
+ STp->buffer->buffer_bytes = 0;
+ STp->buffer->read_pointer = 0;
+ STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
}
return 0;
}
@@ -1763,8 +1788,10 @@
}
if (mt_op == MTFSF) {
STp->frame_seq_number++;
- STp->frame_in_buffer = 0;
- STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
+ STp->frame_in_buffer = 0;
+ STp->buffer->buffer_bytes = 0;
+ STp->buffer->read_pointer = 0;
+ STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
}
return 0;
}
@@ -1910,8 +1937,10 @@
}
if (mt_op == MTFSF) {
STp->frame_seq_number++;
- STp->frame_in_buffer = 0;
- STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
+ STp->frame_in_buffer = 0;
+ STp->buffer->buffer_bytes = 0;
+ STp->buffer->read_pointer = 0;
+ STp->logical_blk_num += ntohs(STp->buffer->aux->dat.dat_list[0].blk_cnt);
}
return 0;
}
@@ -2011,7 +2040,7 @@
#if DEBUG
printk(OSST_DEB_MSG "osst%d:D: Reached onstream write filler group %d\n", dev, where);
#endif
- osst_wait_ready(STp, aSRpnt, 60 * 5);
+ osst_wait_ready(STp, aSRpnt, 60 * 5, 0);
osst_set_frame_position(STp, aSRpnt, where, 0);
STp->write_type = OS_WRITE_FILLER;
while (count--) {
@@ -2037,7 +2066,7 @@
#if DEBUG
printk(OSST_DEB_MSG "osst%d:D: Reached onstream write header group %d\n", dev, where);
#endif
- osst_wait_ready(STp, aSRpnt, 60 * 5);
+ osst_wait_ready(STp, aSRpnt, 60 * 5, 0);
osst_set_frame_position(STp, aSRpnt, where, 0);
STp->write_type = OS_WRITE_HEADER;
while (count--) {
@@ -2158,7 +2187,8 @@
if (ppos == 5 || ppos == 0xbae || STp->buffer->syscall_result) {
if (osst_set_frame_position(STp, aSRpnt, ppos, 0))
printk(KERN_WARNING "osst%i:W: Couldn't position tape\n", dev);
- if (osst_initiate_read (STp, aSRpnt)) {
+ osst_wait_ready(STp, aSRpnt, 60 * 15, 0);
+ if (osst_initiate_read(STp, aSRpnt)) {
printk(KERN_WARNING "osst%i:W: Couldn't initiate read\n", dev);
return 0;
}
@@ -2377,11 +2407,12 @@
static int osst_verify_position(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt)
{
+ int write_pass = STp->wrt_pass_cntr;
int frame_position = STp->first_frame_position;
- int frame_seq_numbr = STp->frame_seq_number;
+ int frame_seq_numbr = STp->frame_seq_number;
int logical_blk_num = STp->logical_blk_num;
- int halfway_frame = STp->frame_in_buffer;
- int read_pointer = STp->buffer->read_pointer;
+ int halfway_frame = STp->frame_in_buffer;
+ int read_pointer = STp->buffer->read_pointer;
int prev_mark_ppos = -1;
int actual_mark_ppos, i, n;
#if DEBUG
@@ -2389,12 +2420,24 @@
printk(OSST_DEB_MSG "osst%d:D: Verify that the tape is really the one we think before writing\n", dev);
#endif
- osst_set_frame_position(STp, aSRpnt, frame_position - 1, 0);
- if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+ if (frame_position <= STp->first_data_ppos) {
+ /* check header match */
+ if (!osst_analyze_headers(STp, aSRpnt) ||
+ (write_pass != STp->wrt_pass_cntr)) {
#if DEBUG
- printk(OSST_DEB_MSG "osst%d:D: Couldn't get logical blk num in verify_position\n", dev);
+ printk(OSST_DEB_MSG "osst%d:D: Couldn't match header in verify_position\n", dev);
#endif
- return (-EIO);
+ return (-EIO);
+ }
+ } else {
+ /* find preceding data frame of current write pass */
+ osst_set_frame_position(STp, aSRpnt, frame_position - 1, 0);
+ if (osst_get_logical_frame(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+ printk(OSST_DEB_MSG "osst%d:D: Couldn't get logical blk num in verify_position\n", dev);
+#endif
+ return (-EIO);
+ }
}
if (STp->linux_media_version >= 4) {
for (i=0; i<STp->filemark_cnt; i++)
@@ -2671,7 +2714,7 @@
STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
SRpnt = osst_do_scsi(*aSRpnt, STp, scmd, 20, SCSI_DATA_READ,
- STp->timeout, MAX_READY_RETRIES, TRUE);
+ STp->timeout, MAX_RETRIES, TRUE);
if (!SRpnt) {
STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
return (-EBUSY);
@@ -2692,7 +2735,7 @@
scmd[0] = READ_POSITION;
STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
SRpnt = osst_do_scsi(SRpnt, STp, scmd, 20, SCSI_DATA_READ,
- STp->timeout, MAX_READY_RETRIES, TRUE);
+ STp->timeout, MAX_RETRIES, TRUE);
if (!STp->buffer->syscall_result)
memcpy (SRpnt->sr_sense_buffer, mysense, 16);
}
@@ -2764,7 +2807,7 @@
scmd[9] = 0x80;
SRpnt = osst_do_scsi(*aSRpnt, STp, scmd, 0, SCSI_DATA_NONE, STp->long_timeout,
- MAX_READY_RETRIES, TRUE);
+ MAX_RETRIES, TRUE);
if (!SRpnt)
return (-EBUSY);
*aSRpnt = SRpnt;
@@ -2777,7 +2820,7 @@
result = (-EIO);
}
if (pp != ppos)
- osst_wait_ready(STp, aSRpnt, 5 * 60);
+ osst_wait_ready(STp, aSRpnt, 5 * 60, OSST_WAIT_POSITION_COMPLETE);
} while ((pp != ppos) && (pp = ppos));
STp->first_frame_position = STp->last_frame_position = ppos;
STps->eof = ST_NOEOF;
@@ -2787,7 +2830,29 @@
return result;
}
+static int osst_write_trailer(OS_Scsi_Tape *STp, Scsi_Request ** aSRpnt, int leave_at_EOT)
+{
+ ST_partstat * STps = &(STp->ps[STp->partition]);
+ int result = 0;
+
+ if (STp->write_type != OS_WRITE_NEW_MARK) {
+ /* true unless the user wrote the filemark for us */
+ result = osst_flush_drive_buffer(STp, aSRpnt);
+ if (result < 0) goto out;
+ result = osst_write_filemark(STp, aSRpnt);
+ if (result < 0) goto out;
+
+ if (STps->drv_file >= 0)
+ STps->drv_file++ ;
+ STps->drv_block = 0;
+ }
+ result = osst_write_eod(STp, aSRpnt);
+ osst_write_header(STp, aSRpnt, leave_at_EOT);
+ STps->eof = ST_FM;
+out:
+ return result;
+}
/* osst versions of st functions - augmented and stripped to suit OnStream only */
@@ -2905,7 +2970,7 @@
result = (-EIO);
}
}
- STps->drv_block = (-1);
+ STps->drv_block = (-1); /* FIXME - even if write recovery succeeds? */
}
else {
STp->first_frame_position++;
@@ -2941,9 +3006,10 @@
return 0;
STps = &(STp->ps[STp->partition]);
- if (STps->rw == ST_WRITING) /* Writing */
+ if (STps->rw == ST_WRITING || STp->dirty) { /* Writing */
+ STp->write_type = OS_WRITE_DATA;
return osst_flush_write_buffer(STp, aSRpnt);
-
+ }
if (STp->block_size == 0)
return 0;
@@ -3166,12 +3232,16 @@
if (STps->rw == ST_READING) {
+#if DEBUG
+ printk(OSST_DEB_MSG "osst%d:D: Switching from read to write at file %d, block %d\n", dev,
+ STps->drv_file, STps->drv_block);
+#endif
retval = osst_flush_buffer(STp, &SRpnt, 0);
if (retval)
goto out;
STps->rw = ST_IDLE;
}
- else if (STps->rw != ST_WRITING) {
+ if (STps->rw != ST_WRITING) {
/* Are we totally rewriting this tape? */
if (!STp->header_ok ||
(STp->first_frame_position == STp->first_data_ppos && STps->drv_block < 0) ||
@@ -3524,9 +3594,19 @@
printk(OSST_DEB_MSG "osst%d:D: EOF up (%d). Left %d, needed %d.\n", dev,
STps->eof, (STp->buffer)->buffer_bytes, count - total);
#endif
+ /* force multiple of block size, note block_size may have been adjusted */
transfer = (((STp->buffer)->buffer_bytes < count - total ?
(STp->buffer)->buffer_bytes : count - total)/
- STp->block_size) * STp->block_size; /* force multiple of block size */
+ STp->block_size) * STp->block_size;
+
+ if (transfer == 0) {
+ printk(KERN_WARNING
+ "osst%d:W: Nothing can be transfered, requested %d, tape block size (%d%c).\n",
+ dev, count, STp->block_size < 1024?
+ STp->block_size:STp->block_size/1024,
+ STp->block_size<1024?'b':'k');
+ break;
+ }
i = from_buffer(STp->buffer, buf, transfer);
if (i) {
retval = i;
@@ -3553,7 +3633,7 @@
/* Change the eof state if no data from tape or buffer */
if (total == 0) {
if (STps->eof == ST_FM_HIT) {
- STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD:ST_FM;
+ STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD_2:ST_FM;
STps->drv_block = 0;
if (STps->drv_file >= 0)
STps->drv_file++;
@@ -3876,9 +3956,10 @@
}
break;
case MTWEOF:
- if ( STps->rw == ST_WRITING && !(STp->device)->was_reset)
+ if ((STps->rw == ST_WRITING || STp->dirty) && !(STp->device)->was_reset) {
+ STp->write_type = OS_WRITE_DATA;
ioctl_result = osst_flush_write_buffer(STp, &SRpnt);
- else
+ } else
ioctl_result = 0;
#if DEBUG
if (debugging)
@@ -3961,8 +4042,8 @@
if (debugging)
printk(OSST_DEB_MSG "osst%d:D: Spacing to end of recorded medium.\n", dev);
#endif
- osst_set_frame_position(STp, &SRpnt, STp->eod_frame_ppos, 0);
- if (osst_get_logical_frame(STp, &SRpnt, -1, 0) < 0) {
+ if ((osst_position_tape_and_confirm(STp, &SRpnt, STp->eod_frame_ppos) < 0) ||
+ (osst_get_logical_frame(STp, &SRpnt, -1, 0) < 0)) {
ioctl_result = -EIO;
goto os_bypass;
}
@@ -4020,6 +4101,23 @@
break;
case MTSETBLK: /* Set block length */
+ if ((STps->drv_block == 0 ) &&
+ !STp->dirty &&
+ ((STp->buffer)->buffer_bytes == 0) &&
+ ((arg & MT_ST_BLKSIZE_MASK) >= 512 ) &&
+ ((arg & MT_ST_BLKSIZE_MASK) <= OS_DATA_SIZE) &&
+ !(OS_DATA_SIZE % (arg & MT_ST_BLKSIZE_MASK)) ) {
+ /*
+ * Only allowed to change the block size if you opened the
+ * device at the beginning of a file before writing anything.
+ * Note, that when reading, changing block_size is futile,
+ * as the size used when writing overrides it.
+ */
+ STp->block_size = (arg & MT_ST_BLKSIZE_MASK);
+ printk(KERN_INFO "osst%d:I: Block size set to %d bytes.\n",
+ dev, STp->block_size);
+ return 0;
+ }
case MTSETDENSITY: /* Set tape density */
case MTSETDRVBUFFER: /* Set drive buffering */
case SET_DENS_AND_BLK: /* Set density and block size */
@@ -4027,11 +4125,11 @@
if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
return (-EIO); /* Not allowed if data in buffer */
if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
- (arg & MT_ST_BLKSIZE_MASK) != 0 &&
- ((arg & MT_ST_BLKSIZE_MASK) < STp->min_block ||
- (arg & MT_ST_BLKSIZE_MASK) > STp->max_block ||
- (arg & MT_ST_BLKSIZE_MASK) > osst_buffer_size)) {
- printk(KERN_WARNING "osst%d:W: Illegal block size.\n", dev);
+ (arg & MT_ST_BLKSIZE_MASK) != 0 &&
+ (arg & MT_ST_BLKSIZE_MASK) != STp->block_size ) {
+ printk(KERN_WARNING "osst%d:W: Illegal to set block size to %d%s.\n",
+ dev, (int)(arg & MT_ST_BLKSIZE_MASK),
+ (OS_DATA_SIZE % (arg & MT_ST_BLKSIZE_MASK))?"":" now");
return (-EINVAL);
}
return 0; /* FIXME silently ignore if block size didn't change */
@@ -4083,6 +4181,14 @@
if (cmd_in == MTEOM)
STps->eof = ST_EOD;
+ else if ((cmd_in == MTFSFM || cmd_in == MTBSF) && STps->eof == ST_FM_HIT) {
+ ioctl_result = osst_seek_logical_blk(STp, &SRpnt, STp->logical_blk_num-1);
+ STps->drv_block++;
+ STp->logical_blk_num++;
+ STp->frame_seq_number++;
+ STp->frame_in_buffer = 0;
+ STp->buffer->read_pointer = 0;
+ }
else if (cmd_in == MTFSF)
STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD:ST_FM;
else if (chg_eof)
@@ -4091,7 +4197,6 @@
if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
STp->rew_at_close = 0;
else if (cmd_in == MTLOAD) {
-/* STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0; FIXME */
for (i=0; i < ST_NBR_PARTITIONS; i++) {
STp->ps[i].rw = ST_IDLE;
STp->ps[i].last_block_valid = FALSE;/* FIXME - where else is this field maintained? */
@@ -4140,10 +4245,15 @@
STp->door_locked = ST_LOCK_FAILS;
if (cmd_in == MTLOAD && osst_wait_for_medium(STp, &SRpnt, 60))
- ioctl_result = osst_wait_ready(STp, &SRpnt, 5 * 60);
+ ioctl_result = osst_wait_ready(STp, &SRpnt, 5 * 60, OSST_WAIT_POSITION_COMPLETE);
}
*aSRpnt = SRpnt;
+#if DEBUG
+ printk(OSST_DEB_MSG "osst%d:D: Ioctl %s, ppos %d fseq %d lblk %d bytes %d file %d blk %d\n", dev,
+ ioctl_result?"fail":"success", STp->first_frame_position, STp->frame_seq_number,
+ STp->logical_blk_num, STp->buffer->buffer_bytes, STps->drv_file, STps->drv_block);
+#endif
return ioctl_result;
}
@@ -4263,7 +4373,7 @@
SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE,
STp->timeout, MAX_READY_RETRIES, TRUE);
}
- osst_wait_ready(STp, &SRpnt, (SRpnt->sr_sense_buffer[13]==1?15:3) * 60);
+ osst_wait_ready(STp, &SRpnt, (SRpnt->sr_sense_buffer[13]==1?15:3) * 60, 0);
}
if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
(SRpnt->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
@@ -4410,7 +4520,7 @@
}
}
- if (osst_wait_ready(STp, &SRpnt, 15 * 60)) /* FIXME - not allowed with NOBLOCK */
+ if (osst_wait_ready(STp, &SRpnt, 15 * 60, 0)) /* FIXME - not allowed with NOBLOCK */
printk(KERN_INFO "osst%i:I: Device did not become Ready in open\n",dev);
if ((STp->buffer)->syscall_result != 0) {
@@ -4551,7 +4661,8 @@
STm = &(STp->modes[STp->current_mode]);
STps = &(STp->ps[STp->partition]);
- if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) {
+ if ((STps->rw == ST_WRITING || STp->dirty) && !(STp->device)->was_reset) {
+ STp->write_type = OS_WRITE_DATA;
result = osst_flush_write_buffer(STp, &SRpnt);
if (result != 0 && result != (-ENOSPC))
goto out;
@@ -4566,22 +4677,7 @@
dev, STp->nbr_waits, STp->nbr_finished);
}
#endif
- if (STp->write_type != OS_WRITE_NEW_MARK) {
- /* true unless the user wrote the filemark for us */
- result = osst_flush_drive_buffer(STp, &SRpnt);
- if (result < 0) goto out;
- result = osst_write_filemark(STp, &SRpnt);
- if (result < 0) goto out;
-
- if (STps->drv_file >= 0)
- STps->drv_file++ ;
- STps->drv_block = 0;
- }
- result = osst_write_eod(STp, &SRpnt);
- osst_write_header(STp, &SRpnt, !(STp->rew_at_close));
-
- STps->eof = ST_FM;
-
+ result = osst_write_trailer(STp, &SRpnt, !(STp->rew_at_close));
#if DEBUG
if (debugging)
printk(OSST_DEB_MSG "osst%d:D: Buffer flushed, %d EOF(s) written\n",
@@ -4592,7 +4688,7 @@
STps = &(STp->ps[STp->partition]);
if (!STm->sysv || STps->rw != ST_READING) {
if (STp->can_bsr)
- result = osst_flush_buffer(STp, &SRpnt, 0); /* this is the default path */
+ result = osst_flush_buffer(STp, &SRpnt, 0); /* this is the default path */
else if (STps->eof == ST_FM_HIT) {
result = cross_eof(STp, &SRpnt, FALSE);
if (result) {
@@ -4607,7 +4703,7 @@
}
else if ((STps->eof == ST_NOEOF &&
!(result = cross_eof(STp, &SRpnt, TRUE))) ||
- STps->eof == ST_FM_HIT) {
+ STps->eof == ST_FM_HIT) {
if (STps->drv_file >= 0)
STps->drv_file++;
STps->drv_block = 0;
@@ -4721,6 +4817,7 @@
#endif
if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
struct mtop mtc;
+ int auto_weof = 0;
if (_IOC_SIZE(cmd_in) != sizeof(mtc)) {
retval = (-EINVAL);
@@ -4801,10 +4898,41 @@
}
}
- if (mtc.mt_op != MTNOP && mtc.mt_op != MTWEOF && mtc.mt_op != MTWSM &&
- mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTSETBLK &&
- mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART)
- STps->rw = ST_IDLE; /* Prevent automatic WEOF and fsf */
+ if (mtc.mt_op != MTCOMPRESSION && mtc.mt_op != MTLOCK &&
+ mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+ mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTSETDRVBUFFER &&
+ mtc.mt_op != MTMKPART && mtc.mt_op != MTSETPART &&
+ mtc.mt_op != MTWEOF && mtc.mt_op != MTWSM ) {
+
+ /*
+ * The user tells us to move to another position on the tape.
+ * If we were appending to the tape content, that would leave
+ * the tape without proper end, in that case write EOD and
+ * update the header to reflect its position.
+ */
+#if DEBUG
+ printk(KERN_WARNING "osst%d:D: auto_weod %s at ffp=%d,efp=%d,fsn=%d,lbn=%d,fn=%d,bn=%d\n",dev,
+ STps->rw >= ST_WRITING ? "write" : STps->rw == ST_READING ? "read" : "idle",
+ STp->first_frame_position, STp->eod_frame_ppos, STp->frame_seq_number,
+ STp->logical_blk_num, STps->drv_file, STps->drv_block );
+#endif
+ if (STps->rw >= ST_WRITING && STp->first_frame_position >= STp->eod_frame_ppos) {
+ auto_weof = ((STp->write_type != OS_WRITE_NEW_MARK) &&
+ !(mtc.mt_op == MTREW || mtc.mt_op == MTOFFL));
+ i = osst_write_trailer(STp, &SRpnt,
+ !(mtc.mt_op == MTREW || mtc.mt_op == MTOFFL));
+#if DEBUG
+ printk(KERN_WARNING "osst%d:D: post trailer xeof=%d,ffp=%d,efp=%d,fsn=%d,lbn=%d,fn=%d,bn=%d\n",
+ dev, auto_weof, STp->first_frame_position, STp->eod_frame_ppos,
+ STp->frame_seq_number, STp->logical_blk_num, STps->drv_file, STps->drv_block );
+#endif
+ if (i < 0) {
+ retval = i;
+ goto out;
+ }
+ }
+ STps->rw = ST_IDLE;
+ }
if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
osst_int_ioctl(STp, &SRpnt, MTUNLOCK, 0); /* Ignore result! */
@@ -4816,12 +4944,6 @@
}
if (mtc.mt_op == MTSETPART) {
-/* if (!STp->can_partitions ||
- mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS)
- return (-EINVAL);
- if (mtc.mt_count >= STp->nbr_partitions &&
- (STp->nbr_partitions = nbr_partitions(inode)) < 0)
- return (-EIO);*/
if (mtc.mt_count >= STp->nbr_partitions)
retval = -EINVAL;
else {
@@ -4864,14 +4986,14 @@
goto out;
}
-/* if (STp->can_partitions && STp->ready == ST_READY &&
- (i = update_partition(inode)) < 0)
- {retval=i;goto out;}*/
+ if (auto_weof)
+ cross_eof(STp, &SRpnt, FALSE);
if (mtc.mt_op == MTCOMPRESSION)
- retval = -EINVAL /*osst_compression(STp, (mtc.mt_count & 1))*/;
+ retval = -EINVAL; /* OnStream drives don't have compression hardware */
else
-
+ /* MTBSF MTBSFM MTBSR MTBSS MTEOM MTERASE MTFSF MTFSFB MTFSR MTFSS MTLOAD
+ * MTLOCK MTOFFL MTRESET MTRETEN MTREW MTUNLOAD MTUNLOCK MTWEOF MTWSM */
retval = osst_int_ioctl(STp, &SRpnt, mtc.mt_op, mtc.mt_count);
goto out;
}
@@ -4886,10 +5008,6 @@
goto out;
}
-/* if (STp->can_partitions &&
- (i = update_partition(inode)) < 0)
- {retval=i;goto out;}*/
-
if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
struct mtget mt_status;
@@ -5119,9 +5237,10 @@
for (segs=STbuffer->sg_segs, got=STbuffer->buffer_size;
segs < max_segs && got < new_size; ) {
STbuffer->sg[segs].address =
- (unsigned char *)__get_free_pages(priority, order);
+ (unsigned char *)__get_free_pages(priority,
+ (new_size - got <= PAGE_SIZE) ? 0 : order);
if (STbuffer->sg[segs].address == NULL) {
- if (new_size - got <= (max_segs - segs) * b_size / 2) {
+ if (new_size - got <= (max_segs - segs) * b_size / 2 && order) {
b_size /= 2; /* Large enough for the rest of the buffers */
order--;
continue;
@@ -5135,9 +5254,9 @@
return FALSE;
}
STbuffer->sg[segs].page = NULL;
- STbuffer->sg[segs].length = b_size;
+ STbuffer->sg[segs].length = (new_size - got <= PAGE_SIZE / 2) ? (new_size - got) : b_size;
STbuffer->sg_segs += 1;
- got += b_size;
+ got += STbuffer->sg[segs].length;
STbuffer->buffer_size = got;
segs++;
}
@@ -5320,28 +5439,26 @@
static void validate_options (void)
{
- if (buffer_kbs > 0)
- osst_buffer_size = buffer_kbs * ST_KILOBYTE;
+ if (max_dev > 0)
+ osst_max_dev = osst_max_buffers = max_dev;
if (write_threshold_kbs > 0)
osst_write_threshold = write_threshold_kbs * ST_KILOBYTE;
if (osst_write_threshold > osst_buffer_size)
osst_write_threshold = osst_buffer_size;
- if (max_buffers > 0)
- osst_max_buffers = max_buffers;
if (max_sg_segs >= OSST_FIRST_SG)
osst_max_sg_segs = max_sg_segs;
#if DEBUG
- printk(OSST_DEB_MSG "osst :D: bufsize %d, wrt %d, max buffers %d, s/g segs %d.\n",
+ printk(OSST_DEB_MSG "osst :D: bufsize %d, wrt %d, max devices %d, s/g segs %d.\n",
osst_buffer_size, osst_write_threshold, osst_max_buffers, osst_max_sg_segs);
-//printk(OSST_DEB_MSG "osst :D: sizeof(header) = %d (%s)\n",
-// sizeof(os_header_t),sizeof(os_header_t)==OS_DATA_SIZE?"ok":"error");
#endif
}
#ifndef MODULE
-/* Set the boot options. Syntax: osst=xxx,yyy,...
- where xxx is buffer size in 1024 byte blocks and yyy is write threshold
- in 1024 byte blocks. */
+/* Set the boot options. Syntax: osst=xxx,yyy,zzz
+ * where xxx is maximum nr of devices to attach,
+ * yyy is write threshold in 1024 byte blocks
+ * and zzz the maximum nr of s/g segments to handle.
+ */
static int __init osst_setup (char *str)
{
int i, ints[5];
@@ -5417,6 +5534,72 @@
return 0;
}
+/*
+ * /proc support for accessing ADR header information
+ */
+static struct proc_dir_entry * osst_proc_dir = NULL;
+static char osst_proc_dirname[] = "osst";
+
+static int osst_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ int l = 0;
+ OS_Scsi_Tape * STp = (OS_Scsi_Tape *) data;
+
+ if (!osst_proc_dir) return 0;
+
+ if (STp->header_ok && STp->linux_media)
+ l = sprintf(page, "%d.%d LIN%d %8d %8d %8d \n",
+ STp->header_cache->major_rev,
+ STp->header_cache->minor_rev,
+ STp->linux_media_version,
+ STp->first_data_ppos,
+ STp->eod_frame_ppos,
+ STp->filemark_cnt );
+ return l;
+}
+
+static void osst_proc_init(void)
+{
+ if (!proc_scsi) return;
+
+ osst_proc_dir = proc_mkdir(osst_proc_dirname, proc_scsi);
+ osst_proc_dir->owner = THIS_MODULE;
+}
+
+static void osst_proc_create(OS_Scsi_Tape * STp, int dev)
+{
+ char s[16];
+ struct proc_dir_entry * p_entry;
+
+ if (!osst_proc_dir) return;
+
+ sprintf(s, "osst%d", dev);
+ p_entry = create_proc_read_entry(s, 0444, osst_proc_dir, osst_proc_read, (void *) STp);
+ p_entry->owner = THIS_MODULE;
+}
+
+static void osst_proc_destroy(int dev)
+{
+ char s[16];
+
+ if (!osst_proc_dir) return;
+
+ sprintf(s, "osst%d", dev);
+ remove_proc_entry(s, osst_proc_dir);
+}
+
+static void osst_proc_cleanup(void)
+{
+ if ((! proc_scsi) || (!osst_proc_dir)) return;
+
+ remove_proc_entry(osst_proc_dirname, proc_scsi);
+ osst_proc_dir = NULL;
+}
+
+/*
+ * osst startup / cleanup code
+ */
+
static int osst_attach(Scsi_Device * SDp)
{
OS_Scsi_Tape * tpnt;
@@ -5462,34 +5645,18 @@
/* Rewind entry */
sprintf (name, "mt%s", formats[mode]);
-# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
tpnt->de_r[mode] =
devfs_register (SDp->de, name, DEVFS_FL_DEFAULT,
MAJOR_NR, i + (mode << 5),
S_IFCHR | S_IRUGO | S_IWUGO,
&osst_fops, NULL);
-# else
- tpnt->de_r[mode] =
- devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT,
- MAJOR_NR, i + (mode << 5),
- S_IFCHR | S_IRUGO | S_IWUGO,
- 0, 0, &osst_fops, NULL);
-# endif
/* No-rewind entry */
sprintf (name, "mt%sn", formats[mode]);
-# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
tpnt->de_n[mode] =
devfs_register (SDp->de, name, DEVFS_FL_DEFAULT,
MAJOR_NR, i + (mode << 5) + 128,
S_IFCHR | S_IRUGO | S_IWUGO,
&osst_fops, NULL);
-# else
- tpnt->de_n[mode] =
- devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT,
- MAJOR_NR, i + (mode << 5) + 128,
- S_IFCHR | S_IRUGO | S_IWUGO,
- 0, 0, &osst_fops, NULL);
-# endif
}
devfs_register_tape (tpnt->de_r[0]);
#endif
@@ -5522,7 +5689,8 @@
tpnt->os_fw_rev = osst_parse_firmware_rev (SDp->rev);
tpnt->omit_blklims = 1;
- tpnt->poll = (strncmp(SDp->model, "DI-", 3) == 0) || OSST_FW_NEED_POLL(tpnt->os_fw_rev,SDp);
+ tpnt->poll = (strncmp(SDp->model, "DI-", 3) == 0) ||
+ (strncmp(SDp->model, "FW-", 3) == 0) || OSST_FW_NEED_POLL(tpnt->os_fw_rev,SDp);
tpnt->frame_in_buffer = 0;
tpnt->header_ok = 0;
tpnt->linux_media = 0;
@@ -5559,6 +5727,8 @@
osst_template.nr_dev++;
+ osst_proc_create(tpnt, dev);
+
printk(KERN_INFO
"osst :I: Attached OnStream %.5s tape at scsi%d, channel %d, id %d, lun %d as osst%d\n",
SDp->model, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun, dev);
@@ -5597,7 +5767,7 @@
}
if (os_scsi_tapes) return 0;
- osst_template.dev_max = OSST_MAX_TAPES;
+ osst_template.dev_max = osst_max_dev;
if (osst_template.dev_max > 128 / ST_NBR_MODES)
printk(KERN_INFO "osst :I: Only %d tapes accessible.\n", 128 / ST_NBR_MODES);
os_scsi_tapes =
@@ -5637,39 +5807,42 @@
printk(OSST_DEB_MSG "osst :D: Buffer size %d bytes, write threshold %d bytes.\n",
osst_buffer_size, osst_write_threshold);
#endif
+ osst_proc_init();
return 0;
}
static void osst_detach(Scsi_Device * SDp)
{
- OS_Scsi_Tape * tpnt;
- int i;
+ OS_Scsi_Tape * tpnt;
+ int i;
#ifdef CONFIG_DEVFS_FS
- int mode;
+ int mode;
#endif
-
- for(i=0; i<osst_template.dev_max; i++) {
- tpnt = os_scsi_tapes[i];
- if(tpnt != NULL && tpnt->device == SDp) {
- tpnt->device = NULL;
+ for(i=0; i<osst_template.dev_max; i++) {
+ tpnt = os_scsi_tapes[i];
+ if(tpnt != NULL && tpnt->device == SDp) {
+ osst_proc_destroy(i);
+ tpnt->device = NULL;
#ifdef CONFIG_DEVFS_FS
- for (mode = 0; mode < ST_NBR_MODES; ++mode) {
- devfs_unregister (tpnt->de_r[mode]);
- tpnt->de_r[mode] = NULL;
- devfs_unregister (tpnt->de_n[mode]);
- tpnt->de_n[mode] = NULL;
+ for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+ devfs_unregister (tpnt->de_r[mode]);
+ tpnt->de_r[mode] = NULL;
+ devfs_unregister (tpnt->de_n[mode]);
+ tpnt->de_n[mode] = NULL;
+ }
+#endif
+ if (tpnt->header_cache != NULL) {
+ vfree(tpnt->header_cache);
+ }
+ kfree(tpnt);
+ os_scsi_tapes[i] = NULL;
+ SDp->attached--;
+ osst_template.nr_dev--;
+ osst_template.dev_noticed--;
+ return;
}
-#endif
- kfree(tpnt);
- os_scsi_tapes[i] = NULL;
- SDp->attached--;
- osst_template.nr_dev--;
- osst_template.dev_noticed--;
- return;
}
- }
- return;
}
static int __init init_osst(void)
@@ -5681,38 +5854,32 @@
static void __exit exit_osst (void)
{
- int i;
- OS_Scsi_Tape * STp;
+ int i;
- scsi_unregister_module(MODULE_SCSI_DEV, &osst_template);
+ scsi_unregister_module(MODULE_SCSI_DEV, &osst_template);
#ifdef CONFIG_DEVFS_FS
- devfs_unregister_chrdev(MAJOR_NR, "osst");
+ devfs_unregister_chrdev(MAJOR_NR, "osst");
#else
- unregister_chrdev(MAJOR_NR, "osst");
+ unregister_chrdev(MAJOR_NR, "osst");
#endif
- osst_registered--;
- if(os_scsi_tapes != NULL) {
- for (i=0; i < osst_template.dev_max; ++i) {
- if ((STp = os_scsi_tapes[i])) {
- if (STp->header_cache != NULL) vfree(STp->header_cache);
- kfree(STp);
- }
- }
- kfree(os_scsi_tapes);
+ osst_registered--;
+ osst_proc_cleanup();
+
+ if(os_scsi_tapes != NULL) {
+ kfree(os_scsi_tapes);
+ }
if (osst_buffers != NULL) {
for (i=0; i < osst_nbr_buffers; i++)
- if (osst_buffers[i] != NULL) {
- osst_buffers[i]->orig_sg_segs = 0;
- normalize_buffer(osst_buffers[i]);
- kfree(osst_buffers[i]);
- }
-
+ if (osst_buffers[i] != NULL) {
+ osst_buffers[i]->orig_sg_segs = 0;
+ normalize_buffer(osst_buffers[i]);
+ kfree(osst_buffers[i]);
+ }
kfree(osst_buffers);
}
- }
- osst_template.dev_max = 0;
- printk(KERN_INFO "osst :I: Unloaded.\n");
+ osst_template.dev_max = 0;
+ printk(KERN_INFO "osst :I: Unloaded.\n");
}
module_init(init_osst);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)