diff --git a/sys/i386/isa/atapi.c b/sys/i386/isa/atapi.c index 588bb3d117c..f629aaf9d95 100644 --- a/sys/i386/isa/atapi.c +++ b/sys/i386/isa/atapi.c @@ -105,7 +105,7 @@ #ifndef ATAPI_MODULE # include "wcd.h" # include "wfd.h" -/* # include "wmt.h" -- add your driver here */ +# include "wst.h" /* # include "wmd.h" -- add your driver here */ #endif @@ -169,9 +169,12 @@ static int atapi_io (struct atapi *ata, struct atapicmd *ac); static int atapi_start_cmd (struct atapi *ata, struct atapicmd *ac); static int atapi_wait_cmd (struct atapi *ata, struct atapicmd *ac); +static void atapi_poll_dsc(struct atapi *ata); + extern int wdstart (int ctrlr); -extern int wfdattach(struct atapi*, int, struct atapi_params*, int); extern int wcdattach(struct atapi*, int, struct atapi_params*, int); +extern int wfdattach(struct atapi*, int, struct atapi_params*, int); +extern int wstattach(struct atapi*, int, struct atapi_params*, int); /* * Probe the ATAPI device at IDE controller `ctlr', drive `unit'. @@ -295,11 +298,16 @@ int atapi_attach (int ctlr, int unit, int port) break; #endif - case AT_TYPE_TAPE: /* streaming tape (QIC-121 model) */ -#if NWMT > 0 - /* Add your driver here */ + case AT_TYPE_TAPE: /* streaming tape */ +#if NWST > 0 + /* ATAPI Streaming Tape */ + if (wstattach (ata, unit, ap, ata->debug) < 0) + break; + /* Device attached successfully. */ + ata->attached[unit] = 1; + return (1); #else - printf ("wdc%d: ATAPI streaming tapes not supported yet\n", ctlr); + printf ("wdc%d: ATAPI streaming tapes not configured\n", ctlr); #endif break; diff --git a/sys/i386/isa/atapi.h b/sys/i386/isa/atapi.h index eb33e312563..a58836aa89b 100644 --- a/sys/i386/isa/atapi.h +++ b/sys/i386/isa/atapi.h @@ -73,8 +73,14 @@ #define AER_SK_ILLEGAL_REQUEST 0x50 /* invalid command parameter(s) */ #define AER_SK_UNIT_ATTENTION 0x60 /* media changed */ #define AER_SK_DATA_PROTECT 0x70 /* reading read-protected sector */ +#define AER_SK_BLANK_CHECK 0x80 /* blank check */ +#define AER_SK_VENDOR_SPECIFIC 0x90 /* vendor specific skey */ +#define AER_SK_COPY_ABORTED 0xa0 /* copy aborted */ #define AER_SK_ABORTED_COMMAND 0xb0 /* command aborted, try again */ -#define AER_SK_MISCOMPARE 0xe0 /* data did not match the medium */ +#define AER_SK_EQUAL 0xc0 /* equal */ +#define AER_SK_VOLUME_OVERFLOW 0xd0 /* volume overflow */ +#define AER_SK_MISCOMPARE 0xe0 /* data did not match the medium */ +#define AER_SK_RESERVED 0xf0 #define AER_BITS "\20\4mchg\3abort\2eom\1ili" /* @@ -132,6 +138,8 @@ #define ATAPI_PLAY_TRACK 0x48 /* play by track number */ #define ATAPI_PLAY_BIG 0xa5 /* play by logical block address */ +#define DSC_POLL_INTERVAL 10 + /* * Drive parameter information */ @@ -237,6 +245,9 @@ struct atapi { /* ATAPI controller data */ u_char cmd16 : 1; /* 16-byte command flag */ u_char intrcmd : 1; /* interrupt before cmd flag */ u_char slow : 1; /* slow reaction device */ + u_char use_dsc : 1; /* use DSC completition handeling */ + u_char wait_for_dsc : 1; + u_int dsc_timeout; u_char attached[2]; /* units are attached to subdrivers */ struct atapi_params *params[2]; /* params for units 0,1 */ struct atapicmd *queue; /* queue of commands to perform */ diff --git a/sys/i386/isa/wst.c b/sys/i386/isa/wst.c new file mode 100644 index 00000000000..3e42a38ae1b --- /dev/null +++ b/sys/i386/isa/wst.c @@ -0,0 +1,827 @@ +/*- + * 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.2 1998/02/26 20:44:16 sos Exp sos $ + */ + +#include "wdc.h" +#include "wst.h" +#include "opt_atapi.h" + +#if NWST > 0 && NWDC > 0 && defined(ATAPI) + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEVFS +#include +#endif /*DEVFS*/ +#include +#include + +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_DEBUG 0x0004 /* 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 + +/* + * 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 reserved6_45 :2; + 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 */ + char description[80]; /* Device description */ +#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_start(struct wst *t); +static void wst_done(struct wst *t, struct buf *bp, int resid, struct atapires result); +static void 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 < 3 ; 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); + /*wst_describe(t);*/ + /*if (t->flags & WST_DEBUG) */ + /* wst_dump(t->lun, "cap", &t->cap, sizeof(t->cap)); */ + t->blksize = (t->cap.blk512 ? 512 : (t->cap.blk1024 ? 1024 : 0)); + + } else + return 1; + return 0; +} + +static void +wst_describe(struct wst *t) +{ + printf("wst%d: ", t->lun); + printf("Medium: "); + switch (t->header.medium_type) { + case 0x00: printf("Drive empty"); break; + case 0x17: printf("Travan 1 (400 Mbyte)"); break; + case 0xb6: printf("Travan 4 (4 Gbyte)"); break; + default: printf("Unknown (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.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; + t->flags &= ~WST_MEDIA_CHANGED; + + if (wst_sense(t)) + printf("wst%d: Sense media type failed\n", t->lun); + + 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 */ + wst_write_filemark(t, 0); + + /* if minor is even rewind on close */ + if (!(minor(dev) & 0x01)) + wst_rewind(t); + + t->flags &= ~WST_OPEN; + 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); + } + +wst_total += bp->b_bcount; + + x = splbio(); + bufq_insert_tail(&t->buf_queue, bp); + wst_start(t); + splx(x); +} + +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; + + 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; + 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 void +wst_error(struct wst *t, struct atapires result) +{ + 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; + } + 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); + return; + } + if (!(t->flags & WST_MEDIA_CHANGED)) + if (t->flags & WST_DEBUG) + printf("wst%d: no media\n", t->lun); + t->flags |= WST_MEDIA_CHANGED; + return; + + case AER_SK_BLANK_CHECK: + if (t->flags & WST_DEBUG) + printf("wst%d: EOD encountered\n", t->lun); + return; + + 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; + return; + + 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); + } + + if ((result.error & AER_SKEY) && (result.status & ARS_CHECK)) { + struct wst_reqsense sense; + + 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)); + printf("count=%d ERR=%x len=%d ASC=%x ASCQ=%x\n", + wst_total, sense.error_code, ntohl(sense.info), + sense.asc, sense.ascq); + } +} + +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 = 1; + 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: /* write filemark */ + for (i=0; i < mt->mt_count && !error; i++) + error = wst_write_filemark(t, WEOF_WRITE_MASK); + break; + case MTFSF: /* forward count filemarks */ + if (mt->mt_count) + error = wst_space_cmd(t, SP_FM, mt->mt_count); + break; + case MTBSF: /* backward count filemarks */ + 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: /* rewind */ + error = wst_rewind(t); + break; + case MTOFFL: /* rewind and go offline */ +#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: /* flush buffers */ + 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: /* erase */ + error = wst_erase(t); + break; + case MTEOD: + error = wst_space_cmd(t, SP_EOD, 0); + break; + case MTCOMP: + error = EINVAL; break; + case MTRETENS: /* retension tape */ + 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; + + 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 +#include +#include + +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; ataport) + 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; tflags & WST_OPEN)) + return EBUSY; + for (t=wsttab; tata->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 */ diff --git a/sys/pc98/pc98/atapi.h b/sys/pc98/pc98/atapi.h index eb33e312563..a58836aa89b 100644 --- a/sys/pc98/pc98/atapi.h +++ b/sys/pc98/pc98/atapi.h @@ -73,8 +73,14 @@ #define AER_SK_ILLEGAL_REQUEST 0x50 /* invalid command parameter(s) */ #define AER_SK_UNIT_ATTENTION 0x60 /* media changed */ #define AER_SK_DATA_PROTECT 0x70 /* reading read-protected sector */ +#define AER_SK_BLANK_CHECK 0x80 /* blank check */ +#define AER_SK_VENDOR_SPECIFIC 0x90 /* vendor specific skey */ +#define AER_SK_COPY_ABORTED 0xa0 /* copy aborted */ #define AER_SK_ABORTED_COMMAND 0xb0 /* command aborted, try again */ -#define AER_SK_MISCOMPARE 0xe0 /* data did not match the medium */ +#define AER_SK_EQUAL 0xc0 /* equal */ +#define AER_SK_VOLUME_OVERFLOW 0xd0 /* volume overflow */ +#define AER_SK_MISCOMPARE 0xe0 /* data did not match the medium */ +#define AER_SK_RESERVED 0xf0 #define AER_BITS "\20\4mchg\3abort\2eom\1ili" /* @@ -132,6 +138,8 @@ #define ATAPI_PLAY_TRACK 0x48 /* play by track number */ #define ATAPI_PLAY_BIG 0xa5 /* play by logical block address */ +#define DSC_POLL_INTERVAL 10 + /* * Drive parameter information */ @@ -237,6 +245,9 @@ struct atapi { /* ATAPI controller data */ u_char cmd16 : 1; /* 16-byte command flag */ u_char intrcmd : 1; /* interrupt before cmd flag */ u_char slow : 1; /* slow reaction device */ + u_char use_dsc : 1; /* use DSC completition handeling */ + u_char wait_for_dsc : 1; + u_int dsc_timeout; u_char attached[2]; /* units are attached to subdrivers */ struct atapi_params *params[2]; /* params for units 0,1 */ struct atapicmd *queue; /* queue of commands to perform */ diff --git a/sys/pc98/pc98/wst.c b/sys/pc98/pc98/wst.c new file mode 100644 index 00000000000..3e42a38ae1b --- /dev/null +++ b/sys/pc98/pc98/wst.c @@ -0,0 +1,827 @@ +/*- + * 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.2 1998/02/26 20:44:16 sos Exp sos $ + */ + +#include "wdc.h" +#include "wst.h" +#include "opt_atapi.h" + +#if NWST > 0 && NWDC > 0 && defined(ATAPI) + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEVFS +#include +#endif /*DEVFS*/ +#include +#include + +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_DEBUG 0x0004 /* 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 + +/* + * 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 reserved6_45 :2; + 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 */ + char description[80]; /* Device description */ +#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_start(struct wst *t); +static void wst_done(struct wst *t, struct buf *bp, int resid, struct atapires result); +static void 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 < 3 ; 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); + /*wst_describe(t);*/ + /*if (t->flags & WST_DEBUG) */ + /* wst_dump(t->lun, "cap", &t->cap, sizeof(t->cap)); */ + t->blksize = (t->cap.blk512 ? 512 : (t->cap.blk1024 ? 1024 : 0)); + + } else + return 1; + return 0; +} + +static void +wst_describe(struct wst *t) +{ + printf("wst%d: ", t->lun); + printf("Medium: "); + switch (t->header.medium_type) { + case 0x00: printf("Drive empty"); break; + case 0x17: printf("Travan 1 (400 Mbyte)"); break; + case 0xb6: printf("Travan 4 (4 Gbyte)"); break; + default: printf("Unknown (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.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; + t->flags &= ~WST_MEDIA_CHANGED; + + if (wst_sense(t)) + printf("wst%d: Sense media type failed\n", t->lun); + + 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 */ + wst_write_filemark(t, 0); + + /* if minor is even rewind on close */ + if (!(minor(dev) & 0x01)) + wst_rewind(t); + + t->flags &= ~WST_OPEN; + 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); + } + +wst_total += bp->b_bcount; + + x = splbio(); + bufq_insert_tail(&t->buf_queue, bp); + wst_start(t); + splx(x); +} + +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; + + 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; + 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 void +wst_error(struct wst *t, struct atapires result) +{ + 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; + } + 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); + return; + } + if (!(t->flags & WST_MEDIA_CHANGED)) + if (t->flags & WST_DEBUG) + printf("wst%d: no media\n", t->lun); + t->flags |= WST_MEDIA_CHANGED; + return; + + case AER_SK_BLANK_CHECK: + if (t->flags & WST_DEBUG) + printf("wst%d: EOD encountered\n", t->lun); + return; + + 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; + return; + + 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); + } + + if ((result.error & AER_SKEY) && (result.status & ARS_CHECK)) { + struct wst_reqsense sense; + + 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)); + printf("count=%d ERR=%x len=%d ASC=%x ASCQ=%x\n", + wst_total, sense.error_code, ntohl(sense.info), + sense.asc, sense.ascq); + } +} + +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 = 1; + 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: /* write filemark */ + for (i=0; i < mt->mt_count && !error; i++) + error = wst_write_filemark(t, WEOF_WRITE_MASK); + break; + case MTFSF: /* forward count filemarks */ + if (mt->mt_count) + error = wst_space_cmd(t, SP_FM, mt->mt_count); + break; + case MTBSF: /* backward count filemarks */ + 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: /* rewind */ + error = wst_rewind(t); + break; + case MTOFFL: /* rewind and go offline */ +#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: /* flush buffers */ + 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: /* erase */ + error = wst_erase(t); + break; + case MTEOD: + error = wst_space_cmd(t, SP_EOD, 0); + break; + case MTCOMP: + error = EINVAL; break; + case MTRETENS: /* retension tape */ + 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; + + 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 +#include +#include + +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; ataport) + 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; tflags & WST_OPEN)) + return EBUSY; + for (t=wsttab; tata->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 */