eb615e1788
Don't try to flush buffers if the drive says it has none. More error checking and reporting. Hack: if drive hangs, it can be reset by issuing a mt -f device offline. I've been able to make several 4G backups. However there is still problems with some configurations. It is not clear if it is hardware or driver problems yet.
864 lines
25 KiB
C
864 lines
25 KiB
C
/*-
|
|
* Copyright (c) 1998 Søren Schmidt
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer
|
|
* in this position and unchanged.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* $Id: wst.c,v 1.4 1998/03/10 12:00:24 sos Exp sos $
|
|
*/
|
|
|
|
#include "wdc.h"
|
|
#include "wst.h"
|
|
#include "opt_atapi.h"
|
|
|
|
#if NWST > 0 && NWDC > 0 && defined(ATAPI)
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/mtio.h>
|
|
#ifdef DEVFS
|
|
#include <sys/devfsext.h>
|
|
#endif
|
|
#include <machine/clock.h>
|
|
#include <i386/isa/atapi.h>
|
|
|
|
static d_open_t wstopen;
|
|
static d_close_t wstclose;
|
|
static d_ioctl_t wstioctl;
|
|
static d_strategy_t wststrategy;
|
|
|
|
#define CDEV_MAJOR 90
|
|
#define BDEV_MAJOR 24
|
|
static struct cdevsw wst_cdevsw;
|
|
static struct bdevsw wst_bdevsw = {
|
|
wstopen, wstclose, wststrategy, wstioctl,
|
|
nodump, nopsize, D_TAPE, "wst", &wst_cdevsw, -1
|
|
};
|
|
|
|
static int wst_total = 0;
|
|
|
|
#define NUNIT (NWDC*2)
|
|
#define UNIT(d) ((minor(d) >> 3) & 3)
|
|
|
|
#define WST_OPEN 0x0001 /* The device is opened */
|
|
#define WST_MEDIA_CHANGED 0x0002 /* The media have changed */
|
|
#define WST_DATA_WRITTEN 0x0004 /* Data has been written */
|
|
#define WST_FM_WRITTEN 0x0008 /* Filemark has been written */
|
|
#define WST_DEBUG 0x0010 /* Print debug info */
|
|
|
|
/* ATAPI tape commands not in std ATAPI command set */
|
|
#define ATAPI_TAPE_REWIND 0x01
|
|
#define ATAPI_TAPE_REQUEST_SENSE 0x03
|
|
#define ATAPI_TAPE_READ_CMD 0x08
|
|
#define ATAPI_TAPE_WRITE_CMD 0x0a
|
|
#define ATAPI_TAPE_WEOF 0x10
|
|
#define WEOF_WRITE_MASK 0x01
|
|
#define ATAPI_TAPE_SPACE_CMD 0x11
|
|
#define SP_FM 0x01
|
|
#define SP_EOD 0x03
|
|
#define ATAPI_TAPE_ERASE 0x19
|
|
#define ATAPI_TAPE_MODE_SENSE 0x1a
|
|
#define ATAPI_TAPE_LOAD_UNLOAD 0x1b
|
|
#define LU_LOAD_MASK 0x01
|
|
#define LU_RETENSION_MASK 0x02
|
|
#define LU_EOT_MASK 0x04
|
|
|
|
#define DSC_POLL_INTERVAL 10
|
|
|
|
/*
|
|
* MODE SENSE parameter header
|
|
*/
|
|
struct wst_header {
|
|
u_char data_length; /* Total length of data */
|
|
u_char medium_type; /* Medium type (if any) */
|
|
u_char dsp; /* Device specific parameter */
|
|
u_char bdl; /* Block Descriptor Length */
|
|
};
|
|
|
|
/*
|
|
* ATAPI tape drive Capabilities and Mechanical Status Page
|
|
*/
|
|
#define ATAPI_TAPE_CAP_PAGE 0x2a
|
|
|
|
struct wst_cappage {
|
|
u_char page_code :6; /* Page code == 0x2a */
|
|
u_char reserved1_67 :2;
|
|
u_char page_length; /* Page Length == 0x12 */
|
|
u_char reserved2;
|
|
u_char reserved3;
|
|
u_char readonly :1; /* Read Only Mode */
|
|
u_char reserved4_1234 :4;
|
|
u_char reverse :1; /* Supports reverse direction */
|
|
u_char reserved4_67 :2;
|
|
u_char reserved5_012 :3;
|
|
u_char eformat :1; /* Supports ERASE formatting */
|
|
u_char reserved5_4 :1;
|
|
u_char qfa :1; /* Supports QFA formats */
|
|
u_char reserved5_67 :2;
|
|
u_char lock :1; /* Supports locking media */
|
|
u_char locked :1; /* The media is locked */
|
|
u_char prevent :1; /* Defaults to prevent state */
|
|
u_char eject :1; /* Supports eject */
|
|
u_char disconnect :1; /* Can break request > ctl */
|
|
u_char reserved6_5 :1;
|
|
u_char ecc :1; /* Supports error correction */
|
|
u_char compress :1; /* Supports data compression */
|
|
u_char reserved7_0 :1;
|
|
u_char blk512 :1; /* Supports 512b block size */
|
|
u_char blk1024 :1; /* Supports 1024b block size */
|
|
u_char reserved7_3456 :4;
|
|
u_char slowb :1; /* Restricts byte count */
|
|
u_short max_speed; /* Supported speed in KBps */
|
|
u_short max_defects; /* Max stored defect entries */
|
|
u_short ctl; /* Continuous Transfer Limit */
|
|
u_short speed; /* Current Speed, in KBps */
|
|
u_short buffer_size; /* Buffer Size, in 512 bytes */
|
|
u_char reserved18;
|
|
u_char reserved19;
|
|
};
|
|
|
|
/*
|
|
* REQUEST SENSE structure
|
|
*/
|
|
struct wst_reqsense {
|
|
u_char error_code :7; /* Current or deferred errors */
|
|
u_char valid :1; /* Follows QIC-157C */
|
|
u_char reserved1; /* Segment Number - Reserved */
|
|
u_char sense_key :4; /* Sense Key */
|
|
u_char reserved2_4 :1; /* Reserved */
|
|
u_char ili :1; /* Incorrect Length Indicator */
|
|
u_char eom :1; /* End Of Medium */
|
|
u_char filemark :1; /* Filemark */
|
|
u_int info __attribute__((packed)); /* Cmd specific info */
|
|
u_char asl; /* Additional sense length (n-7) */
|
|
u_int command_specific; /* Additional cmd specific info */
|
|
u_char asc; /* Additional Sense Code */
|
|
u_char ascq; /* Additional Sense Code Qualifier */
|
|
u_char replaceable_unit_code; /* Field Replaceable Unit Code */
|
|
u_char sk_specific1 :7; /* Sense Key Specific */
|
|
u_char sksv :1; /* Sense Key Specific info valid */
|
|
u_char sk_specific2; /* Sense Key Specific */
|
|
u_char sk_specific3; /* Sense Key Specific */
|
|
u_char pad[2]; /* Padding */
|
|
};
|
|
|
|
struct wst {
|
|
struct atapi *ata; /* Controller structure */
|
|
int unit; /* IDE bus drive unit */
|
|
int lun; /* Logical device unit */
|
|
int flags; /* Device state flags */
|
|
int blksize; /* Block size (512 | 1024) */
|
|
struct buf_queue_head buf_queue; /* Queue of i/o requests */
|
|
struct atapi_params *param; /* Drive parameters table */
|
|
struct wst_header header; /* MODE SENSE param header */
|
|
struct wst_cappage cap; /* Capabilities page info */
|
|
#ifdef DEVFS
|
|
void *cdevs;
|
|
void *bdevs;
|
|
#endif
|
|
};
|
|
|
|
static struct wst *wsttab[NUNIT]; /* Drive info by unit number */
|
|
static int wstnlun = 0; /* Number of config'd drives */
|
|
|
|
#ifndef ATAPI_STATIC
|
|
static
|
|
#endif
|
|
int wstattach(struct atapi *ata, int unit, struct atapi_params *ap, int debug);
|
|
static int wst_sense(struct wst *t);
|
|
static void wst_describe(struct wst *t);
|
|
static int wst_open(dev_t dev, int chardev);
|
|
static void wst_poll_dsc(struct wst *t);
|
|
static void wst_start(struct wst *t);
|
|
static void wst_done(struct wst *t, struct buf *bp, int resid, struct atapires result);
|
|
static int wst_error(struct wst *t, struct atapires result);
|
|
static void wst_drvinit(void *unused);
|
|
static int wst_space_cmd(struct wst *t, u_char function, u_int count);
|
|
static int wst_write_filemark(struct wst *t, u_char function);
|
|
static int wst_erase(struct wst *t);
|
|
static int wst_load_unload(struct wst *t, u_char finction);
|
|
static int wst_rewind(struct wst *t);
|
|
static void wst_reset(struct wst *t);
|
|
|
|
static void
|
|
wst_dump(int lun, char *label, void *data, int len)
|
|
{
|
|
u_char *p = data;
|
|
|
|
printf("wst%d: %s %x", lun, label, *p++);
|
|
while(--len > 0)
|
|
printf("-%x", *p++);
|
|
printf("\n");
|
|
}
|
|
|
|
#ifndef ATAPI_STATIC
|
|
static
|
|
#endif
|
|
int
|
|
wstattach(struct atapi *ata, int unit, struct atapi_params *ap, int debug)
|
|
{
|
|
struct wst *t;
|
|
struct atapires result;
|
|
int lun, i;
|
|
char buffer[255];
|
|
|
|
if (wstnlun >= NUNIT) {
|
|
printf("wst: too many units\n");
|
|
return(-1);
|
|
}
|
|
if (!atapi_request_immediate) {
|
|
printf("wst: configuration error, ATAPI core code not present!\n");
|
|
printf(
|
|
"wst: check `options ATAPI_STATIC' in your kernel config file!\n");
|
|
return(-1);
|
|
}
|
|
t = malloc(sizeof(struct wst), M_TEMP, M_NOWAIT);
|
|
if (!t) {
|
|
printf("wst: out of memory\n");
|
|
return(-1);
|
|
}
|
|
wsttab[wstnlun] = t;
|
|
bzero(t, sizeof(struct wst));
|
|
bufq_init(&t->buf_queue);
|
|
t->ata = ata;
|
|
t->unit = unit;
|
|
t->ata->use_dsc = 1;
|
|
lun = t->lun = wstnlun;
|
|
t->param = ap;
|
|
t->flags = WST_MEDIA_CHANGED | WST_DEBUG;
|
|
|
|
if (wst_sense(t))
|
|
return -1;
|
|
|
|
wst_describe(t);
|
|
wstnlun++;
|
|
|
|
#ifdef DEVFS
|
|
t->bdevs = devfs_add_devswf(&wst_bdevsw, 0, DV_BLK, UID_ROOT, GID_OPERATOR,
|
|
0640, "wst%d", t->lun);
|
|
t->cdevs = devfs_add_devswf(&wst_cdevsw, 0, DV_CHR, UID_ROOT, GID_OPERATOR,
|
|
0640, "rwst%d", t->lun);
|
|
#endif /* DEVFS */
|
|
return(1);
|
|
}
|
|
|
|
static int
|
|
wst_sense(struct wst *t)
|
|
{
|
|
struct atapires result;
|
|
int count;
|
|
char buffer[255];
|
|
|
|
/* Get drive capabilities, some drives needs this repeated */
|
|
for (count = 0 ; count < 5 ; count++) {
|
|
result = atapi_request_immediate(t->ata, t->unit,
|
|
ATAPI_TAPE_MODE_SENSE,
|
|
8, /* DBD = 1 no block descr */
|
|
ATAPI_TAPE_CAP_PAGE,
|
|
sizeof(buffer)>>8, sizeof(buffer),
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, buffer, sizeof(buffer));
|
|
if (result.code == 0 || result.code == RES_UNDERRUN)
|
|
break;
|
|
}
|
|
|
|
/* Some drives have shorter capabilities page. */
|
|
if (result.code == RES_UNDERRUN)
|
|
result.code = 0;
|
|
|
|
if (result.code == 0) {
|
|
bcopy(buffer, &t->header, sizeof(struct wst_header));
|
|
bcopy(buffer+sizeof(struct wst_header),
|
|
&t->cap, sizeof(struct wst_cappage));
|
|
if (t->cap.page_code != ATAPI_TAPE_CAP_PAGE)
|
|
return 1;
|
|
t->cap.max_speed = ntohs(t->cap.max_speed);
|
|
t->cap.max_defects = ntohs(t->cap.max_defects);
|
|
t->cap.ctl = ntohs(t->cap.ctl);
|
|
t->cap.speed = ntohs(t->cap.speed);
|
|
t->cap.buffer_size = ntohs(t->cap.buffer_size);
|
|
t->blksize = (t->cap.blk512 ? 512 : (t->cap.blk1024 ? 1024 : 0));
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
wst_describe(struct wst *t)
|
|
{
|
|
printf("wst%d: ", t->lun);
|
|
switch (t->header.medium_type) {
|
|
case 0x00: printf("Drive empty"); break;
|
|
case 0x17: printf("Travan 1 (400 Mbyte) media"); break;
|
|
case 0xb6: printf("Travan 4 (4 Gbyte) media"); break;
|
|
default: printf("Unknown media (0x%x)", t->header.medium_type);
|
|
}
|
|
if (t->cap.readonly) printf(", readonly");
|
|
if (t->cap.reverse) printf(", reverse");
|
|
if (t->cap.eformat) printf(", eformat");
|
|
if (t->cap.qfa) printf(", qfa");
|
|
if (t->cap.lock) printf(", lock");
|
|
if (t->cap.locked) printf(", locked");
|
|
if (t->cap.prevent) printf(", prevent");
|
|
if (t->cap.eject) printf(", eject");
|
|
if (t->cap.disconnect) printf(", disconnect");
|
|
if (t->cap.ecc) printf(", ecc");
|
|
if (t->cap.compress) printf(", compress");
|
|
if (t->cap.blk512) printf(", 512b");
|
|
if (t->cap.blk1024) printf(", 1024b");
|
|
if (t->cap.slowb) printf(", slowb");
|
|
printf("\nwst%d: ", t->lun);
|
|
printf("Max speed=%dKb/s, ", t->cap.max_speed);
|
|
printf("Transfer limit=%d blocks, ", t->cap.ctl);
|
|
printf("Buffer size=%d blocks", t->cap.buffer_size);
|
|
printf("\n");
|
|
}
|
|
|
|
int
|
|
wstopen(dev_t dev, int flags, int fmt, struct proc *p)
|
|
{
|
|
int lun = UNIT(dev);
|
|
char buffer[255];
|
|
struct wst *t;
|
|
struct atapires result;
|
|
|
|
/* Check that the device number and that the ATAPI driver is loaded. */
|
|
if (lun >= wstnlun || !atapi_request_immediate) {
|
|
printf("ENXIO lun=%d, wstnlun=%d, im=%d\n",
|
|
lun, wstnlun, atapi_request_immediate);
|
|
return(ENXIO);
|
|
}
|
|
t = wsttab[lun];
|
|
if (t->flags == WST_OPEN)
|
|
return EBUSY;
|
|
if (wst_sense(t))
|
|
printf("wst%d: Sense media type failed\n", t->lun);
|
|
t->flags &= ~WST_MEDIA_CHANGED;
|
|
t->flags &= ~(WST_DATA_WRITTEN | WST_FM_WRITTEN);
|
|
t->flags |= WST_OPEN;
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
wstclose(dev_t dev, int flags, int fmt, struct proc *p)
|
|
{
|
|
int lun = UNIT(dev);
|
|
struct wst *t = wsttab[lun];
|
|
|
|
/* Flush buffers, some drives fail here, but they report ctl = 0 */
|
|
if (t->cap.ctl)
|
|
wst_write_filemark(t, 0);
|
|
|
|
/* Write filemark if data written to tape */
|
|
if (t->flags & (WST_DATA_WRITTEN || WST_FM_WRITTEN) == WST_DATA_WRITTEN)
|
|
wst_write_filemark(t, WEOF_WRITE_MASK);
|
|
|
|
/* If minor is even rewind on close */
|
|
if (!(minor(dev) & 0x01))
|
|
wst_rewind(t);
|
|
|
|
t->flags &= ~WST_OPEN;
|
|
if (t->flags & WST_DEBUG)
|
|
printf("wst%d: %d total bytes transferred\n", t->lun, wst_total);
|
|
return(0);
|
|
}
|
|
|
|
void
|
|
wststrategy(struct buf *bp)
|
|
{
|
|
int lun = UNIT(bp->b_dev);
|
|
struct wst *t = wsttab[lun];
|
|
int x;
|
|
|
|
/* If it's a null transfer, return immediatly. */
|
|
if (bp->b_bcount == 0) {
|
|
bp->b_resid = 0;
|
|
biodone(bp);
|
|
return;
|
|
}
|
|
|
|
/* Check for != blocksize requests */
|
|
if (bp->b_bcount % t->blksize) {
|
|
printf("wst%d: bad request, must be multiple of %d\n", lun, t->blksize);
|
|
bp->b_error = EIO;
|
|
bp->b_flags |= B_ERROR;
|
|
biodone(bp);
|
|
return;
|
|
}
|
|
|
|
if (bp->b_bcount > t->blksize*t->cap.ctl) {
|
|
printf("wst%d: WARNING: CTL exceeded %d>%d\n",
|
|
lun, bp->b_bcount, t->blksize*t->cap.ctl);
|
|
}
|
|
|
|
x = splbio();
|
|
wst_total += bp->b_bcount;
|
|
bufq_insert_tail(&t->buf_queue, bp);
|
|
wst_start(t);
|
|
splx(x);
|
|
}
|
|
|
|
static void
|
|
wst_poll_dsc(struct wst *t)
|
|
{
|
|
/* We should use a final timeout here SOS XXX */
|
|
if (!(inb(t->ata->port + AR_STATUS) & ARS_DSC)) {
|
|
timeout((timeout_t*)wst_poll_dsc, t, DSC_POLL_INTERVAL);
|
|
return;
|
|
}
|
|
t->ata->wait_for_dsc = 0;
|
|
wakeup((caddr_t)t);
|
|
}
|
|
|
|
static void
|
|
wst_start(struct wst *t)
|
|
{
|
|
struct buf *bp = bufq_first(&t->buf_queue);
|
|
u_long blk_count;
|
|
u_char op_code;
|
|
long byte_count;
|
|
|
|
if (!bp)
|
|
return;
|
|
|
|
if (t->ata->wait_for_dsc)
|
|
printf("wst%d: ERROR! allready waiting for DSC\n", t->lun);
|
|
|
|
/* Sleep waiting for a ready drive (DSC) */
|
|
if (t->ata->use_dsc && !(inb(t->ata->port + AR_STATUS) & ARS_DSC)) {
|
|
t->ata->wait_for_dsc = 1;
|
|
timeout((timeout_t*)wst_poll_dsc, t, DSC_POLL_INTERVAL);
|
|
tsleep((caddr_t) t, 0, "wstdsc", 0);
|
|
}
|
|
|
|
bufq_remove(&t->buf_queue, bp);
|
|
blk_count = bp->b_bcount / t->blksize;
|
|
|
|
if (bp->b_flags & B_READ) {
|
|
op_code = ATAPI_TAPE_READ_CMD;
|
|
byte_count = bp->b_bcount;
|
|
} else {
|
|
op_code = ATAPI_TAPE_WRITE_CMD;
|
|
t->flags |= WST_DATA_WRITTEN;
|
|
byte_count = -bp->b_bcount;
|
|
}
|
|
|
|
atapi_request_callback(t->ata, t->unit, op_code, 1,
|
|
blk_count>>16, blk_count>>8, blk_count,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
(u_char*) bp->b_data, byte_count,
|
|
(void*)wst_done, t, bp);
|
|
}
|
|
|
|
static void
|
|
wst_done(struct wst *t, struct buf *bp, int resid,
|
|
struct atapires result)
|
|
{
|
|
if (result.code) {
|
|
printf("wst_done: ");
|
|
wst_error(t, result);
|
|
bp->b_error = EIO;
|
|
bp->b_flags |= B_ERROR;
|
|
}
|
|
else
|
|
bp->b_resid = resid;
|
|
|
|
biodone(bp);
|
|
/*wst_start(t);*/
|
|
}
|
|
|
|
static int
|
|
wst_error(struct wst *t, struct atapires result)
|
|
{
|
|
struct wst_reqsense sense;
|
|
|
|
if (result.code != RES_ERR) {
|
|
printf("wst%d: ERROR code=%d, status=%b, error=%b\n", t->lun,
|
|
result.code, result.status, ARS_BITS, result.error, AER_BITS);
|
|
return 1;
|
|
}
|
|
|
|
if ((result.error & AER_SKEY) && (result.status & ARS_CHECK)) {
|
|
atapi_request_immediate(t->ata, t->unit,
|
|
ATAPI_TAPE_REQUEST_SENSE,
|
|
0, 0, 0, sizeof(sense),
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, (char*) &sense, sizeof(struct wst_reqsense));
|
|
/*wst_dump(t->lun, "req_sense", &sense, sizeof(struct wst_reqsense));*/
|
|
}
|
|
switch (result.error & AER_SKEY) {
|
|
case AER_SK_NOT_READY:
|
|
if (result.error & ~AER_SKEY) {
|
|
if (t->flags & WST_DEBUG)
|
|
printf("wst%d: not ready\n", t->lun);
|
|
break;
|
|
}
|
|
if (!(t->flags & WST_MEDIA_CHANGED))
|
|
if (t->flags & WST_DEBUG)
|
|
printf("wst%d: no media\n", t->lun);
|
|
t->flags |= WST_MEDIA_CHANGED;
|
|
break;
|
|
|
|
case AER_SK_BLANK_CHECK:
|
|
if (t->flags & WST_DEBUG)
|
|
printf("wst%d: EOD encountered\n", t->lun);
|
|
break;
|
|
|
|
case AER_SK_MEDIUM_ERROR:
|
|
if (t->flags & WST_DEBUG)
|
|
printf("wst%d: nonrecovered data error\n", t->lun);
|
|
break;
|
|
|
|
case AER_SK_HARDWARE_ERROR:
|
|
if (t->flags & WST_DEBUG)
|
|
printf("wst%d: nonrecovered hardware error\n", t->lun);
|
|
break;
|
|
|
|
case AER_SK_ILLEGAL_REQUEST:
|
|
if (t->flags & WST_DEBUG)
|
|
printf("wst%d: invalid command\n", t->lun);
|
|
break;
|
|
|
|
case AER_SK_UNIT_ATTENTION:
|
|
if (!(t->flags & WST_MEDIA_CHANGED))
|
|
printf("wst%d: media changed\n", t->lun);
|
|
t->flags |= WST_MEDIA_CHANGED;
|
|
break;
|
|
|
|
case AER_SK_DATA_PROTECT:
|
|
if (t->flags & WST_DEBUG)
|
|
printf("wst%d: reading read protected data\n", t->lun);
|
|
break;
|
|
|
|
case AER_SK_ABORTED_COMMAND:
|
|
if (t->flags & WST_DEBUG)
|
|
printf("wst%d: command aborted\n", t->lun);
|
|
break;
|
|
|
|
case AER_SK_MISCOMPARE:
|
|
if (t->flags & WST_DEBUG)
|
|
printf("wst%d: data don't match medium\n", t->lun);
|
|
break;
|
|
|
|
default:
|
|
printf("wst%d: i/o error, status=%b, error=%b\n", t->lun,
|
|
result.status, ARS_BITS, result.error, AER_BITS);
|
|
}
|
|
printf("total=%d ERR=%x len=%d ASC=%x ASCQ=%x\n",
|
|
wst_total, sense.error_code, ntohl(sense.info),
|
|
sense.asc, sense.ascq);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
wstioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p)
|
|
{
|
|
int lun = UNIT(dev);
|
|
int error = 0;
|
|
struct wst *t = wsttab[lun];
|
|
|
|
switch (cmd) {
|
|
case MTIOCGET:
|
|
{
|
|
struct mtget *g = (struct mtget *) addr;
|
|
|
|
bzero(g, sizeof(struct mtget));
|
|
g->mt_type = 7;
|
|
g->mt_density = 1;
|
|
g->mt_blksiz = t->blksize;
|
|
g->mt_comp = t->cap.compress;
|
|
g->mt_density0 = 0; g->mt_density1 = 0;
|
|
g->mt_density2 = 0; g->mt_density3 = 0;
|
|
g->mt_blksiz0 = 0; g->mt_blksiz1 = 0;
|
|
g->mt_blksiz2 = 0; g->mt_blksiz3 = 0;
|
|
g->mt_comp0 = 0; g->mt_comp1 = 0;
|
|
g->mt_comp2 = 0; g->mt_comp3 = 0;
|
|
break;
|
|
}
|
|
case MTIOCTOP:
|
|
{
|
|
int i;
|
|
struct mtop *mt = (struct mtop *)addr;
|
|
|
|
switch ((short) (mt->mt_op)) {
|
|
case MTWEOF:
|
|
for (i=0; i < mt->mt_count && !error; i++)
|
|
error = wst_write_filemark(t, WEOF_WRITE_MASK);
|
|
break;
|
|
case MTFSF:
|
|
if (mt->mt_count)
|
|
error = wst_space_cmd(t, SP_FM, mt->mt_count);
|
|
break;
|
|
case MTBSF:
|
|
if (mt->mt_count)
|
|
error = wst_space_cmd(t, SP_FM, -(mt->mt_count));
|
|
break;
|
|
case MTFSR:
|
|
error = EINVAL; break;
|
|
case MTBSR:
|
|
error = EINVAL; break;
|
|
case MTREW:
|
|
error = wst_rewind(t);
|
|
break;
|
|
case MTOFFL:
|
|
#if 1 /* Misuse as a reset func for now */
|
|
wst_reset(t);
|
|
wst_sense(t);
|
|
wst_describe(t);
|
|
#else
|
|
if (error = wst_rewind(t))
|
|
break;
|
|
error = wst_load_unload(t, !LU_LOAD_MASK);
|
|
#endif
|
|
break;
|
|
case MTNOP:
|
|
error = wst_write_filemark(t, 0);
|
|
break;
|
|
case MTCACHE:
|
|
error = EINVAL; break;
|
|
case MTNOCACHE:
|
|
error = EINVAL; break;
|
|
case MTSETBSIZ:
|
|
error = EINVAL; break;
|
|
case MTSETDNSTY:
|
|
error = EINVAL; break;
|
|
case MTERASE:
|
|
error = wst_erase(t);
|
|
break;
|
|
case MTEOD:
|
|
error = wst_space_cmd(t, SP_EOD, 0);
|
|
break;
|
|
case MTCOMP:
|
|
error = EINVAL; break;
|
|
case MTRETENS:
|
|
error = wst_load_unload(t, LU_RETENSION_MASK|LU_LOAD_MASK);
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
}
|
|
return error;
|
|
}
|
|
default:
|
|
return(ENOTTY);
|
|
}
|
|
return(error);
|
|
}
|
|
|
|
static int
|
|
wst_space_cmd(struct wst *t, u_char function, u_int count)
|
|
{
|
|
struct atapires result;
|
|
|
|
result = atapi_request_wait(t->ata, t->unit,
|
|
ATAPI_TAPE_SPACE_CMD, function,
|
|
count>>16, count>>8, count,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0);
|
|
if (result.code) {
|
|
printf("wst_space_cmd: ");
|
|
wst_error(t, result);
|
|
return EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
wst_write_filemark(struct wst *t, u_char function)
|
|
{
|
|
struct atapires result;
|
|
|
|
if (function) {
|
|
if (t->flags & WST_FM_WRITTEN)
|
|
t->flags &= ~WST_DATA_WRITTEN;
|
|
else
|
|
t->flags |= WST_FM_WRITTEN;
|
|
}
|
|
result = atapi_request_wait(t->ata, t->unit,
|
|
ATAPI_TAPE_WEOF, 0, 0, 0, function,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0);
|
|
if (result.code) {
|
|
printf("wst_write_filemark: ");
|
|
wst_error(t, result);
|
|
return EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
wst_load_unload(struct wst *t, u_char function)
|
|
{
|
|
struct atapires result;
|
|
|
|
result = atapi_request_wait(t->ata, t->unit,
|
|
ATAPI_TAPE_LOAD_UNLOAD, 0, 0, 0, function,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0);
|
|
if (result.code) {
|
|
printf("wst_load_unload: ");
|
|
wst_error(t, result);
|
|
return EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
wst_erase(struct wst *t)
|
|
{
|
|
int error;
|
|
struct atapires result;
|
|
|
|
if (error = wst_rewind(t))
|
|
return error;
|
|
result = atapi_request_wait(t->ata, t->unit,
|
|
ATAPI_TAPE_ERASE, 3, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
NULL, 0);
|
|
if (result.code) {
|
|
printf("wst_erase: ");
|
|
wst_error(t, result);
|
|
return EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
wst_rewind(struct wst *t)
|
|
{
|
|
struct atapires result;
|
|
|
|
result = atapi_request_wait(t->ata, t->unit,
|
|
ATAPI_TAPE_REWIND, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
NULL, 0);
|
|
if (result.code) {
|
|
printf("wst_rewind: ");
|
|
wst_error(t, result);
|
|
return EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
wst_reset(struct wst *t)
|
|
{
|
|
outb(t->ata->port + AR_DRIVE, ARD_DRIVE1);
|
|
DELAY(30);
|
|
outb(t->ata->port + AR_COMMAND, 0x08);
|
|
DELAY(30);
|
|
}
|
|
|
|
#ifdef WST_MODULE
|
|
|
|
#include <sys/exec.h>
|
|
#include <sys/sysent.h>
|
|
#include <sys/lkm.h>
|
|
|
|
MOD_DEV(wst, LM_DT_BLOCK, BDEV_MAJOR, &wst_bdevsw);
|
|
MOD_DEV(rwst, LM_DT_CHAR, CDEV_MAJOR, &wst_cdevsw);
|
|
|
|
int
|
|
wst_load(struct lkm_table *lkmtp, int cmd)
|
|
{
|
|
struct atapi *ata;
|
|
int n, u;
|
|
|
|
if (!atapi_start)
|
|
/* No ATAPI driver available. */
|
|
return EPROTONOSUPPORT;
|
|
n = 0;
|
|
for (ata=atapi_tab; ata<atapi_tab+2; ++ata)
|
|
if (ata->port)
|
|
for (u=0; u<2; ++u)
|
|
/* Probing controller ata->ctrlr, unit u. */
|
|
if (ata->params[u] && !ata->attached[u] &&
|
|
wstattach(ata, u, ata->params[u],
|
|
ata->debug) >= 0) {
|
|
ata->attached[u] = 1;
|
|
++n;
|
|
}
|
|
if (!n)
|
|
return ENXIO;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
wst_unload(struct lkm_table *lkmtp, int cmd)
|
|
{
|
|
struct wst **t;
|
|
|
|
for (t=wsttab; t<wsttab+wstnlun; ++t)
|
|
if (((*t)->flags & WST_OPEN))
|
|
return EBUSY;
|
|
for (t=wsttab; t<wsttab+wstnlun; ++t) {
|
|
(*t)->ata->attached[(*t)->unit] = 0;
|
|
free(*t, M_TEMP);
|
|
}
|
|
wstnlun = 0;
|
|
bzero(wsttab, sizeof(wsttab));
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
wst_mod(struct lkm_table *lkmtp, int cmd, int ver)
|
|
{
|
|
int err = 0;
|
|
|
|
if (ver != LKM_VERSION)
|
|
return EINVAL;
|
|
|
|
if (cmd == LKM_E_LOAD)
|
|
err = wst_load(lkmtp, cmd);
|
|
else if (cmd == LKM_E_UNLOAD)
|
|
err = wst_unload(lkmtp, cmd);
|
|
if (err)
|
|
return err;
|
|
|
|
lkmtp->private.lkm_dev = & MOD_PRIVATE(rwst);
|
|
err = lkmdispatch(lkmtp, cmd);
|
|
if (err)
|
|
return err;
|
|
|
|
lkmtp->private.lkm_dev = & MOD_PRIVATE(wst);
|
|
return lkmdispatch(lkmtp, cmd);
|
|
}
|
|
#endif /* WST_MODULE */
|
|
|
|
static wst_devsw_installed = 0;
|
|
|
|
static void
|
|
wst_drvinit(void *unused)
|
|
{
|
|
if (!wst_devsw_installed) {
|
|
bdevsw_add_generic(BDEV_MAJOR, CDEV_MAJOR, &wst_bdevsw);
|
|
wst_devsw_installed = 1;
|
|
}
|
|
}
|
|
|
|
SYSINIT(wstdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,wst_drvinit,NULL)
|
|
|
|
#endif /* NWST && NWDC && ATAPI */
|