summaryrefslogtreecommitdiff
path: root/sys/dev/ata/atascsi.c
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2007-11-16 02:17:28 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2007-11-16 02:17:28 +0000
commit306fdb2baf859591b47afb93ab83d5aa93da47f6 (patch)
tree9fb95aff1d29519b8b9fbd585cb0fd0faa431849 /sys/dev/ata/atascsi.c
parent92ef60dcc78dcff1adba9620206f33a6143b4e81 (diff)
accept ATA ioctls so tools like atactl work on disks behind the atascsi
layer. i intended to write better ways to deal with disks, but i have been too slack. shame on me. this is less code than the equivilent in wdc/pciide, which amuses me greatly.
Diffstat (limited to 'sys/dev/ata/atascsi.c')
-rw-r--r--sys/dev/ata/atascsi.c105
1 files changed, 103 insertions, 2 deletions
diff --git a/sys/dev/ata/atascsi.c b/sys/dev/ata/atascsi.c
index 19ce9058fcf..2ed77d52337 100644
--- a/sys/dev/ata/atascsi.c
+++ b/sys/dev/ata/atascsi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: atascsi.c,v 1.42 2007/10/01 15:34:48 krw Exp $ */
+/* $OpenBSD: atascsi.c,v 1.43 2007/11/16 02:17:27 dlg Exp $ */
/*
* Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
@@ -31,6 +31,8 @@
#include <dev/ata/atascsi.h>
+#include <sys/ataio.h>
+
struct atascsi {
struct device *as_dev;
void *as_cookie;
@@ -46,6 +48,8 @@ struct atascsi {
};
int atascsi_cmd(struct scsi_xfer *);
+int atascsi_ioctl(struct scsi_link *, u_long, caddr_t, int,
+ struct proc *);
/* template */
struct scsi_adapter atascsi_switch = {
@@ -53,7 +57,7 @@ struct scsi_adapter atascsi_switch = {
minphys, /* scsi_minphys */
NULL,
NULL,
- NULL /* ioctl */
+ atascsi_ioctl /* ioctl */
};
struct scsi_device atascsi_device = {
@@ -782,6 +786,103 @@ atascsi_stuffup(struct scsi_xfer *xs)
return (COMPLETE);
}
+int atascsi_ioctl_cmd(struct atascsi *, struct ata_port *, atareq_t *);
+void atascsi_ioctl_done(struct ata_xfer *);
+
+int
+atascsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flags,
+ struct proc *p)
+{
+ struct atascsi *as = link->adapter_softc;
+ struct ata_port *ap = as->as_ports[link->target];
+
+ switch (cmd) {
+ case ATAIOCCOMMAND:
+ return (atascsi_ioctl_cmd(as, ap, (atareq_t *)addr));
+ default:
+ return (ENOTTY);
+ }
+}
+
+int
+atascsi_ioctl_cmd(struct atascsi *as, struct ata_port *ap, atareq_t *atareq)
+{
+ struct ata_xfer *xa;
+ struct ata_fis_h2d *fis;
+ int s;
+
+ s = splbio();
+ xa = ata_get_xfer(ap, 0);
+ splx(s);
+ if (xa == NULL)
+ return (ENOMEM);
+
+ fis = xa->fis;
+ fis->flags = ATA_H2D_FLAGS_CMD;
+ fis->command = atareq->command;
+ fis->features = atareq->features;
+ fis->lba_low = atareq->sec_num;
+ fis->lba_mid = atareq->cylinder;
+ fis->lba_high = atareq->cylinder >> 8;
+ fis->device = atareq->head & 0x0f;
+ fis->sector_count = atareq->sec_count;
+
+ xa->data = atareq->databuf;
+ xa->datalen = atareq->datalen;
+ xa->complete = atascsi_ioctl_done;
+ xa->timeout = atareq->timeout;
+ xa->flags = 0;
+ if (atareq->flags & ATACMD_READ)
+ xa->flags |= ATA_F_READ;
+ if (atareq->flags & ATACMD_WRITE)
+ xa->flags |= ATA_F_WRITE;
+ xa->atascsi_private = NULL;
+
+ switch (as->as_methods->ata_cmd(xa)) {
+ case ATA_COMPLETE:
+ break;
+ case ATA_QUEUED:
+ while (xa->state == ATA_S_PENDING || xa->state == ATA_S_ONCHIP)
+ tsleep(xa, PRIBIO, "atascsi", 0);
+ break;
+ case ATA_ERROR:
+ s = splbio();
+ ata_put_xfer(xa);
+ splx(s);
+ atareq->retsts = ATACMD_ERROR;
+ return (EIO);
+ default:
+ panic("atascsi_ioctl_cmd: unexpected return from ata_cmd");
+ }
+
+ switch (xa->state) {
+ case ATA_S_COMPLETE:
+ atareq->retsts = ATACMD_OK;
+ break;
+ case ATA_S_ERROR:
+ atareq->retsts = ATACMD_ERROR;
+ break;
+ case ATA_S_TIMEOUT:
+ atareq->retsts = ATACMD_TIMEOUT;
+ break;
+ default:
+ panic("atascsi_ioctl_cmd: unexpected ata_xfer state (%d)",
+ xa->state);
+ }
+
+ s = splbio();
+ ata_put_xfer(xa);
+ splx(s);
+
+ return (0);
+}
+
+void
+atascsi_ioctl_done(struct ata_xfer *xa)
+{
+ wakeup(xa);
+}
+
int
ata_exec(struct atascsi *as, struct ata_xfer *xa)
{