a8445737e7
Kazu writes: The VESA support code requires vm86 support. Make sure your kernel configuration file has the following line. options "VM86" If you want to statically link the VESA support code to the kernel, add the following option to the kernel configuration file. options "VESA" The vidcontrol command now accepts the following video mode names: VESA_132x25, VESA_132x43, VESA_132x50, VESA_132x60, VESA_800x600 The VESA_800x600 mode is a raster display mode. The 80x25 text will be displayed on the 800x600 screen. Useful for some laptop computers. vidcontrol accepts the new `-i <info>' option, where <info> must be either `adapter' or `mode'. When the `-i adapter' option is given, vidcontrol will print basic information (not much) on the video adapter. When the `-i mode' option is specified, vidcontrol will list video modes which are actually supported by the video adapter. Submitted by: Kazutaka YOKOTA yokota@FreeBSD.ORG
886 lines
25 KiB
C
886 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,
|
|
* without modification, immediately at the beginning of the file.
|
|
* 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$
|
|
*/
|
|
|
|
#include "wdc.h"
|
|
#include "wst.h"
|
|
#include "opt_atapi.h"
|
|
#include "opt_devfs.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/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_read_t wstread;
|
|
static d_write_t wstwrite;
|
|
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 = {
|
|
wstopen, wstclose, wstread, wstwrite,
|
|
wstioctl, nostop, nullreset, nodevtotty,
|
|
seltrue, nommap, wststrategy, "wst",
|
|
NULL, -1 };
|
|
|
|
static unsigned 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 */
|
|
#define WST_CTL_WARN 0x0020 /* Have we warned about CTL wrong? */
|
|
|
|
/* 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_int page_code :6; /* Page code == 0x2a */
|
|
u_int reserved1_67 :2;
|
|
u_char page_length; /* Page Length == 0x12 */
|
|
u_char reserved2;
|
|
u_char reserved3;
|
|
u_int readonly :1; /* Read Only Mode */
|
|
u_int reserved4_1234 :4;
|
|
u_int reverse :1; /* Supports reverse direction */
|
|
u_int reserved4_67 :2;
|
|
u_int reserved5_012 :3;
|
|
u_int eformat :1; /* Supports ERASE formatting */
|
|
u_int reserved5_4 :1;
|
|
u_int qfa :1; /* Supports QFA formats */
|
|
u_int reserved5_67 :2;
|
|
u_int lock :1; /* Supports locking media */
|
|
u_int locked :1; /* The media is locked */
|
|
u_int prevent :1; /* Defaults to prevent state */
|
|
u_int eject :1; /* Supports eject */
|
|
u_int disconnect :1; /* Can break request > ctl */
|
|
u_int reserved6_5 :1;
|
|
u_int ecc :1; /* Supports error correction */
|
|
u_int compress :1; /* Supports data compression */
|
|
u_int reserved7_0 :1;
|
|
u_int blk512 :1; /* Supports 512b block size */
|
|
u_int blk1024 :1; /* Supports 1024b block size */
|
|
u_int reserved7_3456 :4;
|
|
u_int 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_int error_code :7; /* Current or deferred errors */
|
|
u_int valid :1; /* Follows QIC-157C */
|
|
u_char reserved1; /* Segment Number - Reserved */
|
|
u_int sense_key :4; /* Sense Key */
|
|
u_int reserved2_4 :1; /* Reserved */
|
|
u_int ili :1; /* Incorrect Length Indicator */
|
|
u_int eom :1; /* End Of Medium */
|
|
u_int 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_int sk_specific1 :7; /* Sense Key Specific */
|
|
u_int 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->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=%p\n",
|
|
lun, wstnlun, (void *)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 should report ctl = 0 */
|
|
if (t->cap.ctl && (t->flags & WST_DATA_WRITTEN))
|
|
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: %ud total bytes transferred\n", t->lun, wst_total);
|
|
t->flags &= ~WST_CTL_WARN;
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
wstread(dev_t dev, struct uio *uio, int ioflag)
|
|
{
|
|
return (physio(wststrategy, NULL, dev, 1, minphys, uio));
|
|
}
|
|
|
|
static int
|
|
wstwrite(dev_t dev, struct uio *uio, int ioflag)
|
|
{
|
|
return (physio(wststrategy, NULL, dev, 0, minphys, uio));
|
|
}
|
|
|
|
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) {
|
|
if (t->flags & WST_CTL_WARN == 0) {
|
|
printf("wst%d: WARNING: CTL exceeded %ld>%d\n",
|
|
lun, bp->b_bcount, t->blksize*t->cap.ctl);
|
|
t->flags |= WST_CTL_WARN;
|
|
}
|
|
}
|
|
|
|
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=%u ERR=%x len=%ld ASC=%x ASCQ=%x\n",
|
|
wst_total, sense.error_code, (long)ntohl(sense.info),
|
|
sense.asc, sense.ascq);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
wstioctl(dev_t dev, u_long 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(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) {
|
|
dev_t dev;
|
|
|
|
dev = makedev(CDEV_MAJOR, 0);
|
|
cdevsw_add(&dev, &wst_cdevsw, NULL);
|
|
wst_devsw_installed = 1;
|
|
}
|
|
}
|
|
|
|
SYSINIT(wstdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,wst_drvinit,NULL)
|
|
|
|
#endif /* NWST && NWDC && ATAPI */
|