summaryrefslogtreecommitdiffstats
path: root/sys/dev/ata/atapi-tape.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ata/atapi-tape.c')
-rw-r--r--sys/dev/ata/atapi-tape.c643
1 files changed, 643 insertions, 0 deletions
diff --git a/sys/dev/ata/atapi-tape.c b/sys/dev/ata/atapi-tape.c
new file mode 100644
index 0000000..5574da3
--- /dev/null
+++ b/sys/dev/ata/atapi-tape.c
@@ -0,0 +1,643 @@
+/*-
+ * Copyright (c) 1998,1999 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "apm.h"
+#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/bus.h>
+#include <sys/mtio.h>
+#include <sys/disklabel.h>
+#include <sys/devicestat.h>
+#if NAPM > 0
+#include <machine/apm_bios.h>
+#endif
+#include <dev/ata/ata-all.h>
+#include <dev/ata/atapi-all.h>
+#include <dev/ata/atapi-tape.h>
+
+static d_open_t astopen;
+static d_close_t astclose;
+static d_ioctl_t astioctl;
+static d_strategy_t aststrategy;
+
+static struct cdevsw ast_cdevsw = {
+ /* open */ astopen,
+ /* close */ astclose,
+ /* read */ physread,
+ /* write */ physwrite,
+ /* ioctl */ astioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ aststrategy,
+ /* name */ "ast",
+ /* maj */ 119,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ D_TAPE,
+ /* bmaj */ -1
+};
+
+/* prototypes */
+int32_t astattach(struct atapi_softc *);
+static int32_t ast_sense(struct ast_softc *);
+static void ast_describe(struct ast_softc *);
+static void ast_start(struct ast_softc *);
+static int32_t ast_done(struct atapi_request *);
+static int32_t ast_mode_sense(struct ast_softc *, u_int8_t, void *, int32_t);
+static int32_t ast_mode_select(struct ast_softc *, void *, int32_t);
+static int32_t ast_write_filemark(struct ast_softc *, u_int8_t);
+static int32_t ast_read_position(struct ast_softc *, int32_t, struct ast_readposition *);
+static int32_t ast_space(struct ast_softc *, u_int8_t, u_int32_t);
+static int32_t ast_locate(struct ast_softc *, int32_t, int32_t);
+static int32_t ast_prevent_allow(struct ast_softc *stp, int32_t lock);
+static int32_t ast_load_unload(struct ast_softc *, u_int8_t);
+static int32_t ast_rewind(struct ast_softc *);
+static int32_t ast_erase(struct ast_softc *);
+
+/* internal vars */
+static u_int64_t ast_total = 0;
+MALLOC_DEFINE(M_AST, "AST driver", "ATAPI tape driver buffers");
+
+int32_t
+astattach(struct atapi_softc *atp)
+{
+ struct ast_softc *stp;
+ struct ast_readposition position;
+ dev_t dev;
+ static int32_t ast_cdev_done = 0, astnlun = 0;
+
+ if (!ast_cdev_done) {
+ cdevsw_add(&ast_cdevsw);
+ ast_cdev_done = 1;
+ }
+ stp = malloc(sizeof(struct ast_softc), M_AST, M_NOWAIT);
+ if (!stp) {
+ printf("ast: out of memory\n");
+ return -1;
+ }
+ bzero(stp, sizeof(struct ast_softc));
+ bufq_init(&stp->buf_queue);
+ stp->atp = atp;
+ stp->lun = astnlun++;
+ stp->atp->flags |= ATAPI_F_MEDIA_CHANGED;
+ if (ast_sense(stp)) {
+ free(stp, M_AST);
+ return -1;
+ }
+ ast_describe(stp);
+ if (!strcmp(stp->atp->atapi_parm->model, " OnStream DI-30")) {
+ struct ast_transferpage transfer;
+ struct ast_identifypage identify;
+
+ stp->flags |= F_ONSTREAM;
+ bzero(&transfer, sizeof(struct ast_transferpage));
+ ast_mode_sense(stp, ATAPI_TAPE_TRANSFER_PAGE,
+ &transfer, sizeof(transfer));
+#ifdef AST_DEBUG
+ printf("ast: rd32k=%d rd32k5=%d wr32k=%d wr32k5=%d stream=%d\n",
+ transfer.read32k, transfer.read32k5,
+ transfer.write32k, transfer.write32k5, transfer.streaming);
+#endif
+
+ bzero(&identify, sizeof(struct ast_identifypage));
+ ast_mode_sense(stp, ATAPI_TAPE_IDENTIFY_PAGE,
+ &identify, sizeof(identify));
+ strncpy(identify.ident, "FBSD", 4);
+ ast_mode_select(stp, &identify, sizeof(identify));
+ ast_read_position(stp, 0, &position);
+ }
+ devstat_add_entry(&stp->stats, "ast", stp->lun, DEV_BSIZE,
+ DEVSTAT_NO_ORDERED_TAGS,
+ DEVSTAT_TYPE_SEQUENTIAL | DEVSTAT_TYPE_IF_IDE,
+ DEVSTAT_PRIORITY_TAPE);
+ dev = make_dev(&ast_cdevsw, dkmakeminor(stp->lun, 0, 0),
+ UID_ROOT, GID_OPERATOR, 0640, "rast%d", stp->lun);
+ dev->si_drv1 = stp;
+ dev->si_iosize_max = 252 * DEV_BSIZE;
+ dev = make_dev(&ast_cdevsw, dkmakeminor(stp->lun, 0, 1),
+ UID_ROOT, GID_OPERATOR, 0640, "nrast%d", stp->lun);
+ dev->si_drv1 = stp;
+ dev->si_iosize_max = 252 * DEV_BSIZE;
+ if ((stp->atp->devname = malloc(8, M_AST, M_NOWAIT)))
+ sprintf(stp->atp->devname, "ast%d", stp->lun);
+ return 0;
+}
+
+static int32_t
+ast_sense(struct ast_softc *stp)
+{
+ int32_t count, error = 0;
+
+ /* get drive capabilities, some drives needs this repeated */
+ for (count = 0 ; count < 5 ; count++) {
+ if (!(error = ast_mode_sense(stp, ATAPI_TAPE_CAP_PAGE,
+ &stp->cap, sizeof(stp->cap))))
+ break;
+ }
+ if (error)
+ return 1;
+
+ stp->cap.max_speed = ntohs(stp->cap.max_speed);
+ stp->cap.max_defects = ntohs(stp->cap.max_defects);
+ stp->cap.ctl = ntohs(stp->cap.ctl);
+ stp->cap.speed = ntohs(stp->cap.speed);
+ stp->cap.buffer_size = ntohs(stp->cap.buffer_size);
+ if (stp->cap.blk32k)
+ stp->blksize = 32768;
+ if (stp->cap.blk1024)
+ stp->blksize = 1024;
+ if (stp->cap.blk512)
+ stp->blksize = 512;
+ return 0;
+}
+
+static void
+ast_describe(struct ast_softc *stp)
+{
+ int8_t model_buf[40+1];
+ int8_t revision_buf[8+1];
+
+ bpack(stp->atp->atapi_parm->model, model_buf, sizeof(model_buf));
+ bpack(stp->atp->atapi_parm->revision, revision_buf, sizeof(revision_buf));
+ printf("ast%d: <%s/%s> tape drive at ata%d as %s\n",
+ stp->lun, model_buf, revision_buf,
+ stp->atp->controller->lun,
+ (stp->atp->unit == ATA_MASTER) ? "master" : "slave ");
+ printf("ast%d: ", stp->lun);
+ printf("%dKB/s, ", stp->cap.max_speed);
+ printf("transfer limit %d blk%s, ", stp->cap.ctl, (stp->cap.ctl>1)?"s":"");
+ printf("%dKB buffer, ", (stp->cap.buffer_size * DEV_BSIZE) / 1024);
+ printf("%s\n", ata_mode2str(stp->atp->controller->mode[
+ (stp->atp->unit == ATA_MASTER) ? 0 : 1]));
+ printf("ast%d: ", stp->lun);
+ switch (stp->cap.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;
+ case 0xda: printf("OnStream ADR (15Gyte) media"); break;
+ default: printf("Unknown media (0x%x)", stp->cap.medium_type);
+ }
+ if (stp->cap.readonly) printf(", readonly");
+ if (stp->cap.reverse) printf(", reverse");
+ if (stp->cap.eformat) printf(", eformat");
+ if (stp->cap.qfa) printf(", qfa");
+ if (stp->cap.lock) printf(", lock");
+ if (stp->cap.locked) printf(", locked");
+ if (stp->cap.prevent) printf(", prevent");
+ if (stp->cap.eject) printf(", eject");
+ if (stp->cap.disconnect) printf(", disconnect");
+ if (stp->cap.ecc) printf(", ecc");
+ if (stp->cap.compress) printf(", compress");
+ if (stp->cap.blk512) printf(", 512b");
+ if (stp->cap.blk1024) printf(", 1024b");
+ if (stp->cap.blk32k) printf(", 32kb");
+ printf("\n");
+}
+
+static int
+astopen(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
+{
+ struct ast_softc *stp = dev->si_drv1;
+
+ if (!stp)
+ return ENXIO;
+
+ if (stp->flags == F_OPEN)
+ return EBUSY;
+
+ if (stp->cap.lock)
+ ast_prevent_allow(stp, 1);
+
+ if (ast_sense(stp))
+ printf("ast%d: sense media type failed\n", stp->lun);
+
+ stp->flags &= ~(F_DATA_WRITTEN | F_FM_WRITTEN);
+ stp->flags |= F_OPEN;
+ stp->atp->flags &= ~ATAPI_F_MEDIA_CHANGED;
+ ast_total = 0;
+ return 0;
+}
+
+static int
+astclose(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
+{
+ struct ast_softc *stp = dev->si_drv1;
+
+ /* flush buffers, some drives fail here, they should report ctl = 0 */
+ if (stp->cap.ctl && (stp->flags & F_DATA_WRITTEN))
+ ast_write_filemark(stp, 0);
+
+ /* write filemark if data written to tape */
+ if (!(stp->flags & F_ONSTREAM) &&
+ (stp->flags & (F_DATA_WRITTEN | F_FM_WRITTEN)) == F_DATA_WRITTEN)
+ ast_write_filemark(stp, WF_WRITE);
+
+ /* if minor is even rewind on close */
+ if (!(minor(dev) & 0x01))
+ ast_rewind(stp);
+
+ if (stp->cap.lock)
+ ast_prevent_allow(stp, 0);
+
+ stp->flags &= ~(F_OPEN | F_CTL_WARN);
+#ifdef AST_DEBUG
+ printf("ast%d: %llu total bytes transferred\n", stp->lun, ast_total);
+#endif
+ return 0;
+}
+
+static int
+astioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
+{
+ struct ast_softc *stp = dev->si_drv1;
+ int32_t error = 0;
+
+ 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 = stp->blksize;
+ g->mt_comp = stp->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:
+ {
+ int32_t i;
+ struct mtop *mt = (struct mtop *)addr;
+
+ switch ((int16_t) (mt->mt_op)) {
+
+ case MTWEOF:
+ for (i=0; i < mt->mt_count && !error; i++)
+ error = ast_write_filemark(stp, WF_WRITE);
+ break;
+
+ case MTFSF:
+ if (mt->mt_count)
+ error = ast_space(stp, SP_FM, mt->mt_count);
+ break;
+
+ case MTBSF:
+ if (mt->mt_count)
+ error = ast_space(stp, SP_FM, -(mt->mt_count));
+ break;
+
+ case MTREW:
+ error = ast_rewind(stp);
+ break;
+
+ case MTOFFL:
+ error = ast_load_unload(stp, SS_EJECT);
+ break;
+
+ case MTNOP:
+ error = ast_write_filemark(stp, 0);
+ break;
+
+ case MTERASE:
+ error = ast_erase(stp);
+ break;
+
+ case MTEOD:
+ error = ast_space(stp, SP_EOD, 0);
+ break;
+
+ case MTRETENS:
+ error = ast_load_unload(stp, SS_RETENSION | SS_LOAD);
+ break;
+
+ case MTFSR:
+ case MTBSR:
+ case MTCACHE:
+ case MTNOCACHE:
+ case MTSETBSIZ:
+ case MTSETDNSTY:
+ case MTCOMP:
+ default:
+ error = EINVAL;
+ }
+ break;
+ }
+ case MTIOCRDSPOS:
+ {
+ struct ast_readposition position;
+
+ if ((error = ast_read_position(stp, 0, &position)))
+ break;
+ *(u_int32_t *)addr = position.tape;
+ break;
+ }
+ case MTIOCRDHPOS:
+ {
+ struct ast_readposition position;
+
+ if ((error = ast_read_position(stp, 1, &position)))
+ break;
+ *(u_int32_t *)addr = position.tape;
+ break;
+ }
+ case MTIOCSLOCATE:
+ error = ast_locate(stp, 0, *(u_int32_t *)addr);
+ break;
+ case MTIOCHLOCATE:
+ error = ast_locate(stp, 1, *(u_int32_t *)addr);
+ break;
+ default:
+ error = ENOTTY;
+ }
+ return error;
+}
+
+static void
+aststrategy(struct buf *bp)
+{
+ struct ast_softc *stp = bp->b_dev->si_drv1;
+ int32_t s;
+
+ /* if it's a null transfer, return immediatly. */
+ if (bp->b_bcount == 0) {
+ bp->b_resid = 0;
+ biodone(bp);
+ return;
+ }
+ if (!(bp->b_flags & B_READ) && stp->flags & F_WRITEPROTECT) {
+ bp->b_error = EPERM;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+
+ /* check for != blocksize requests */
+ if (bp->b_bcount % stp->blksize) {
+ printf("ast%d: bad request, must be multiple of %d\n",
+ stp->lun, stp->blksize);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+
+ /* warn about transfers bigger than the device suggests */
+ if (bp->b_bcount > stp->blksize * stp->cap.ctl) {
+ if ((stp->flags & F_CTL_WARN) == 0) {
+ printf("ast%d: WARNING: CTL exceeded %ld>%d\n",
+ stp->lun, bp->b_bcount, stp->blksize * stp->cap.ctl);
+ stp->flags |= F_CTL_WARN;
+ }
+ }
+
+ s = splbio();
+ bufq_insert_tail(&stp->buf_queue, bp);
+ ast_start(stp);
+ splx(s);
+}
+
+static void
+ast_start(struct ast_softc *stp)
+{
+ struct buf *bp = bufq_first(&stp->buf_queue);
+ u_int32_t blkcount;
+ int8_t ccb[16];
+
+ if (!bp)
+ return;
+
+ bzero(ccb, sizeof(ccb));
+
+ if (bp->b_flags & B_READ) {
+ ccb[0] = ATAPI_READ;
+ if (!(stp->flags & ATAPI_F_DSC_USED))
+ atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 2*60, NULL, NULL, NULL);
+ }
+ else {
+ ccb[0] = ATAPI_WRITE;
+ if (!(stp->flags & ATAPI_F_DSC_USED))
+ atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 2*60, NULL, NULL, NULL);
+ }
+
+ bufq_remove(&stp->buf_queue, bp);
+ blkcount = bp->b_bcount / stp->blksize;
+
+ ccb[1] = 1;
+ ccb[2] = blkcount>>16;
+ ccb[3] = blkcount>>8;
+ ccb[4] = blkcount;
+
+ devstat_start_transaction(&stp->stats);
+
+ atapi_queue_cmd(stp->atp, ccb, bp->b_data, bp->b_bcount,
+ (bp->b_flags & B_READ) ? A_READ : 0, 60, ast_done, stp, bp);
+}
+
+static int32_t
+ast_done(struct atapi_request *request)
+{
+ struct buf *bp = request->bp;
+ struct ast_softc *stp = request->driver;
+
+ if (request->error) {
+ bp->b_error = request->error;
+ bp->b_flags |= B_ERROR;
+ }
+ else {
+ if (!(bp->b_flags & B_READ))
+ stp->flags |= F_DATA_WRITTEN;
+ bp->b_resid = request->bytecount;
+ ast_total += (bp->b_bcount - bp->b_resid);
+ }
+ devstat_end_transaction_buf(&stp->stats, bp);
+ biodone(bp);
+ ast_start(stp);
+ return 0;
+}
+
+static int32_t
+ast_mode_sense(struct ast_softc *stp, u_int8_t page,
+ void *pagebuf, int32_t pagesize)
+{
+ int8_t ccb[16] = { ATAPI_MODE_SENSE, 0x08, page, pagesize>>8, pagesize,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int32_t error;
+
+ error = atapi_queue_cmd(stp->atp, ccb, pagebuf, pagesize, A_READ, 10,
+ NULL, NULL, NULL);
+#ifdef AST_DEBUG
+ atapi_dump("ast: mode sense ", pagebuf, pagesize);
+#endif
+ return error;
+}
+
+static int32_t
+ast_mode_select(struct ast_softc *stp, void *pagebuf, int32_t pagesize)
+{
+ int8_t ccb[16] = { ATAPI_MODE_SELECT, 0x10, 0, pagesize>>8, pagesize,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+#ifdef AST_DEBUG
+ printf("ast: modeselect pagesize=%d\n", pagesize);
+ atapi_dump("ast: mode select ", pagebuf, pagesize);
+#endif
+ return atapi_queue_cmd(stp->atp, ccb, pagebuf, pagesize, 0, 10,
+ NULL, NULL, NULL);
+}
+
+static int32_t
+ast_write_filemark(struct ast_softc *stp, u_int8_t function)
+{
+ int8_t ccb[16] = { ATAPI_WEOF, 0x01, 0, 0, function, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int32_t error;
+
+ if (stp->flags & F_ONSTREAM)
+ ccb[4] = 0x00; /* only flush buffers supported */
+ else {
+ if (function) {
+ if (stp->flags & F_FM_WRITTEN)
+ stp->flags &= ~F_DATA_WRITTEN;
+ else
+ stp->flags |= F_FM_WRITTEN;
+ }
+ }
+ error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 10, NULL, NULL, NULL);
+ if (error)
+ return error;
+ return atapi_wait_ready(stp->atp, 5*60);
+}
+
+static int32_t
+ast_read_position(struct ast_softc *stp, int32_t hard,
+ struct ast_readposition *position)
+{
+ int8_t ccb[16] = { ATAPI_READ_POSITION, (hard ? 0x01 : 0), 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int32_t error;
+
+ error = atapi_queue_cmd(stp->atp, ccb, position,
+ sizeof(struct ast_readposition), A_READ, 10,
+ NULL, NULL, NULL);
+ position->tape = ntohl(position->tape);
+ position->host = ntohl(position->host);
+#ifdef AST_DEBUG
+ printf("ast%d: BOP=%d EOP=%d host=%ld tape=%ld in buf=%d error=%02x\n",
+ stp->lun, position->bop, position->eop, ntohl(position->host),
+ ntohl(position->tape), position->blks_in_buf, error);
+#endif
+ return error;
+}
+
+static int32_t
+ast_space(struct ast_softc *stp, u_int8_t function, u_int32_t count)
+{
+ int8_t ccb[16] = { ATAPI_SPACE, function, count>>16, count>>8, count,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 60*60, NULL, NULL, NULL);
+}
+
+static int32_t
+ast_locate(struct ast_softc *stp, int32_t hard, int32_t pos)
+{
+ int8_t ccb[16] = { ATAPI_LOCATE, 0x01 | (hard ? 0x4 : 0), 0,
+ pos>>24, pos>>16, pos>>8, pos,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int32_t error;
+
+ error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 10, NULL, NULL, NULL);
+ if (error)
+ return error;
+ return atapi_wait_ready(stp->atp, 60*60);
+}
+
+static int32_t
+ast_prevent_allow(struct ast_softc *stp, int32_t lock)
+{
+ int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0,30, NULL, NULL, NULL);
+}
+
+static int32_t
+ast_load_unload(struct ast_softc *stp, u_int8_t function)
+{
+ int8_t ccb[16] = { ATAPI_START_STOP, 0x01, 0, 0, function, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int32_t error;
+
+ if ((function & SS_EJECT) && !stp->cap.eject)
+ return 0;
+ error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 10, NULL, NULL, NULL);
+ if (error)
+ return error;
+ tsleep((caddr_t)&error, PRIBIO, "astlu", 1 * hz);
+ if (function == SS_EJECT)
+ return 0;
+ return atapi_wait_ready(stp->atp, 60*60);
+}
+
+static int32_t
+ast_rewind(struct ast_softc *stp)
+{
+ int8_t ccb[16] = { ATAPI_REWIND, 0x01, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int32_t error;
+
+ error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 10, NULL, NULL, NULL);
+ if (error)
+ return error;
+ return atapi_wait_ready(stp->atp, 60*60);
+}
+
+static int32_t
+ast_erase(struct ast_softc *stp)
+{
+ int8_t ccb[16] = { ATAPI_ERASE, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int32_t error;
+
+ if ((error = ast_rewind(stp)))
+ return error;
+
+ return atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 60*60, NULL, NULL, NULL);
+}
OpenPOWER on IntegriCloud