summaryrefslogtreecommitdiff
path: root/sys/dev/ata
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ata')
-rw-r--r--sys/dev/ata/ata-all.c1051
-rw-r--r--sys/dev/ata/ata-all.h645
-rw-r--r--sys/dev/ata/ata-card.c172
-rw-r--r--sys/dev/ata/ata-cbus.c349
-rw-r--r--sys/dev/ata/ata-chipset.c5149
-rw-r--r--sys/dev/ata/ata-disk.c428
-rw-r--r--sys/dev/ata/ata-disk.h47
-rw-r--r--sys/dev/ata/ata-dma.c282
-rw-r--r--sys/dev/ata/ata-isa.c126
-rw-r--r--sys/dev/ata/ata-lowlevel.c837
-rw-r--r--sys/dev/ata/ata-pci.c677
-rw-r--r--sys/dev/ata/ata-pci.h457
-rw-r--r--sys/dev/ata/ata-queue.c757
-rw-r--r--sys/dev/ata/ata-raid.c4950
-rw-r--r--sys/dev/ata/ata-raid.h813
-rw-r--r--sys/dev/ata/ata-usb.c975
-rw-r--r--sys/dev/ata/ata_if.m78
-rw-r--r--sys/dev/ata/atapi-cam.c901
-rw-r--r--sys/dev/ata/atapi-cd.c1912
-rw-r--r--sys/dev/ata/atapi-cd.h314
-rw-r--r--sys/dev/ata/atapi-fd.c447
-rw-r--r--sys/dev/ata/atapi-fd.h86
-rw-r--r--sys/dev/ata/atapi-tape.c752
-rw-r--r--sys/dev/ata/atapi-tape.h157
24 files changed, 22362 insertions, 0 deletions
diff --git a/sys/dev/ata/ata-all.c b/sys/dev/ata/ata-all.c
new file mode 100644
index 000000000000..9bcaf86e02cb
--- /dev/null
+++ b/sys/dev/ata/ata-all.c
@@ -0,0 +1,1051 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/endian.h>
+#include <sys/ctype.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/bio.h>
+#include <sys/malloc.h>
+#include <sys/sysctl.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/stdarg.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#ifdef __alpha__
+#include <machine/md_var.h>
+#endif
+#include <dev/ata/ata-all.h>
+#include <ata_if.h>
+
+/* device structure */
+static d_ioctl_t ata_ioctl;
+static struct cdevsw ata_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT, /* we need this as newbus isn't mpsafe */
+ .d_ioctl = ata_ioctl,
+ .d_name = "ata",
+};
+
+/* prototypes */
+static void ata_boot_attach(void);
+static device_t ata_add_child(device_t, struct ata_device *, int);
+static int ata_getparam(struct ata_device *, int);
+static void bswap(int8_t *, int);
+static void btrim(int8_t *, int);
+static void bpack(int8_t *, int8_t *, int);
+
+/* global vars */
+MALLOC_DEFINE(M_ATA, "ata_generic", "ATA driver generic layer");
+int (*ata_raid_ioctl_func)(u_long cmd, caddr_t data) = NULL;
+struct intr_config_hook *ata_delayed_attach = NULL;
+devclass_t ata_devclass;
+uma_zone_t ata_request_zone;
+uma_zone_t ata_composite_zone;
+int ata_wc = 1;
+
+/* local vars */
+static int ata_dma = 1;
+static int atapi_dma = 1;
+
+/* sysctl vars */
+SYSCTL_NODE(_hw, OID_AUTO, ata, CTLFLAG_RD, 0, "ATA driver parameters");
+TUNABLE_INT("hw.ata.ata_dma", &ata_dma);
+SYSCTL_INT(_hw_ata, OID_AUTO, ata_dma, CTLFLAG_RDTUN, &ata_dma, 0,
+ "ATA disk DMA mode control");
+TUNABLE_INT("hw.ata.atapi_dma", &atapi_dma);
+SYSCTL_INT(_hw_ata, OID_AUTO, atapi_dma, CTLFLAG_RDTUN, &atapi_dma, 0,
+ "ATAPI device DMA mode control");
+TUNABLE_INT("hw.ata.wc", &ata_wc);
+SYSCTL_INT(_hw_ata, OID_AUTO, wc, CTLFLAG_RDTUN, &ata_wc, 0,
+ "ATA disk write caching");
+
+/*
+ * newbus device interface related functions
+ */
+int
+ata_probe(device_t dev)
+{
+ return 0;
+}
+
+int
+ata_attach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ int error, rid;
+
+ /* check that we have a virgin channel to attach */
+ if (ch->r_irq)
+ return EEXIST;
+
+ /* initialize the softc basics */
+ ch->dev = dev;
+ ch->state = ATA_IDLE;
+ bzero(&ch->state_mtx, sizeof(struct mtx));
+ mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF);
+ bzero(&ch->queue_mtx, sizeof(struct mtx));
+ mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF);
+ TAILQ_INIT(&ch->ata_queue);
+
+ /* reset the controller HW, the channel and device(s) */
+ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit)
+ tsleep(&error, PRIBIO, "ataatch", 1);
+ ATA_RESET(dev);
+ ATA_LOCKING(dev, ATA_LF_UNLOCK);
+
+ /* setup interrupt delivery */
+ rid = ATA_IRQ_RID;
+ ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (!ch->r_irq) {
+ device_printf(dev, "unable to allocate interrupt\n");
+ return ENXIO;
+ }
+ if ((error = bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS,
+ (driver_intr_t *)ata_interrupt, ch, &ch->ih))) {
+ device_printf(dev, "unable to setup interrupt\n");
+ return error;
+ }
+
+ /* probe and attach devices on this channel unless we are in early boot */
+ if (!ata_delayed_attach)
+ ata_identify(dev);
+ return 0;
+}
+
+int
+ata_detach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ device_t *children;
+ int nchildren, i;
+
+ /* check that we have a valid channel to detach */
+ if (!ch->r_irq)
+ return ENXIO;
+
+ /* grap the channel lock so no new requests gets launched */
+ mtx_lock(&ch->state_mtx);
+ ch->state |= ATA_STALL_QUEUE;
+ mtx_unlock(&ch->state_mtx);
+
+ /* detach & delete all children */
+ if (!device_get_children(dev, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ if (children[i])
+ device_delete_child(dev, children[i]);
+ free(children, M_TEMP);
+ }
+
+ /* release resources */
+ bus_teardown_intr(dev, ch->r_irq, ch->ih);
+ bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq);
+ ch->r_irq = NULL;
+ mtx_destroy(&ch->state_mtx);
+ mtx_destroy(&ch->queue_mtx);
+ return 0;
+}
+
+int
+ata_reinit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ struct ata_request *request;
+ device_t *children;
+ int nchildren, i;
+
+ /* check that we have a valid channel to reinit */
+ if (!ch || !ch->r_irq)
+ return ENXIO;
+
+ if (bootverbose)
+ device_printf(dev, "reiniting channel ..\n");
+
+ /* poll for locking the channel */
+ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit)
+ tsleep(&dev, PRIBIO, "atarini", 1);
+
+ /* catch eventual request in ch->running */
+ mtx_lock(&ch->state_mtx);
+ if ((request = ch->running))
+ callout_stop(&request->callout);
+ ch->running = NULL;
+
+ /* unconditionally grap the channel lock */
+ ch->state |= ATA_STALL_QUEUE;
+ mtx_unlock(&ch->state_mtx);
+
+ /* reset the controller HW, the channel and device(s) */
+ ATA_RESET(dev);
+
+ /* reinit the children and delete any that fails */
+ if (!device_get_children(dev, &children, &nchildren)) {
+ mtx_lock(&Giant); /* newbus suckage it needs Giant */
+ for (i = 0; i < nchildren; i++) {
+ /* did any children go missing ? */
+ if (children[i] && device_is_attached(children[i]) &&
+ ATA_REINIT(children[i])) {
+ /*
+ * if we had a running request and its device matches
+ * this child we need to inform the request that the
+ * device is gone.
+ */
+ if (request && request->dev == children[i]) {
+ request->result = ENXIO;
+ device_printf(request->dev, "FAILURE - device detached\n");
+
+ /* if not timeout finish request here */
+ if (!(request->flags & ATA_R_TIMEOUT))
+ ata_finish(request);
+ request = NULL;
+ }
+ device_delete_child(dev, children[i]);
+ }
+ }
+ free(children, M_TEMP);
+ mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */
+ }
+
+ /* if we still have a good request put it on the queue again */
+ if (request && !(request->flags & ATA_R_TIMEOUT)) {
+ device_printf(request->dev,
+ "WARNING - %s requeued due to channel reset",
+ ata_cmd2str(request));
+ if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
+ printf(" LBA=%ju", request->u.ata.lba);
+ printf("\n");
+ request->flags |= ATA_R_REQUEUE;
+ ata_queue_request(request);
+ }
+
+ /* we're done release the channel for new work */
+ mtx_lock(&ch->state_mtx);
+ ch->state = ATA_IDLE;
+ mtx_unlock(&ch->state_mtx);
+ ATA_LOCKING(dev, ATA_LF_UNLOCK);
+
+ if (bootverbose)
+ device_printf(dev, "reinit done ..\n");
+
+ /* kick off requests on the queue */
+ ata_start(dev);
+ return 0;
+}
+
+int
+ata_suspend(device_t dev)
+{
+ struct ata_channel *ch;
+
+ /* check for valid device */
+ if (!dev || !(ch = device_get_softc(dev)))
+ return ENXIO;
+
+ /* wait for the channel to be IDLE or detached before suspending */
+ while (ch->r_irq) {
+ mtx_lock(&ch->state_mtx);
+ if (ch->state == ATA_IDLE) {
+ ch->state = ATA_ACTIVE;
+ mtx_unlock(&ch->state_mtx);
+ break;
+ }
+ mtx_unlock(&ch->state_mtx);
+ tsleep(ch, PRIBIO, "atasusp", hz/10);
+ }
+ ATA_LOCKING(dev, ATA_LF_UNLOCK);
+ return 0;
+}
+
+int
+ata_resume(device_t dev)
+{
+ struct ata_channel *ch;
+ int error;
+
+ /* check for valid device */
+ if (!dev || !(ch = device_get_softc(dev)))
+ return ENXIO;
+
+ /* reinit the devices, we dont know what mode/state they are in */
+ error = ata_reinit(dev);
+
+ /* kick off requests on the queue */
+ ata_start(dev);
+ return error;
+}
+
+int
+ata_interrupt(void *data)
+{
+ struct ata_channel *ch = (struct ata_channel *)data;
+ struct ata_request *request;
+
+ mtx_lock(&ch->state_mtx);
+ do {
+ /* ignore interrupt if its not for us */
+ if (ch->hw.status && !ch->hw.status(ch->dev))
+ break;
+
+ /* do we have a running request */
+ if (!(request = ch->running))
+ break;
+
+ ATA_DEBUG_RQ(request, "interrupt");
+
+ /* safetycheck for the right state */
+ if (ch->state == ATA_IDLE) {
+ device_printf(request->dev, "interrupt on idle channel ignored\n");
+ break;
+ }
+
+ /*
+ * we have the HW locks, so end the transaction for this request
+ * if it finishes immediately otherwise wait for next interrupt
+ */
+ if (ch->hw.end_transaction(request) == ATA_OP_FINISHED) {
+ ch->running = NULL;
+ if (ch->state == ATA_ACTIVE)
+ ch->state = ATA_IDLE;
+ mtx_unlock(&ch->state_mtx);
+ ATA_LOCKING(ch->dev, ATA_LF_UNLOCK);
+ ata_finish(request);
+ return 1;
+ }
+ } while (0);
+ mtx_unlock(&ch->state_mtx);
+ return 0;
+}
+
+/*
+ * device related interfaces
+ */
+static int
+ata_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
+ int32_t flag, struct thread *td)
+{
+ device_t device, *children;
+ struct ata_ioc_devices *devices = (struct ata_ioc_devices *)data;
+ int *value = (int *)data;
+ int i, nchildren, error = ENOTTY;
+
+ switch (cmd) {
+ case IOCATAGMAXCHANNEL:
+ *value = devclass_get_maxunit(ata_devclass);
+ error = 0;
+ break;
+
+ case IOCATAREINIT:
+ if (*value > devclass_get_maxunit(ata_devclass) ||
+ !(device = devclass_get_device(ata_devclass, *value)))
+ return ENXIO;
+ error = ata_reinit(device);
+ ata_start(device);
+ break;
+
+ case IOCATAATTACH:
+ if (*value > devclass_get_maxunit(ata_devclass) ||
+ !(device = devclass_get_device(ata_devclass, *value)))
+ return ENXIO;
+ /* XXX SOS should enable channel HW on controller */
+ error = ata_attach(device);
+ break;
+
+ case IOCATADETACH:
+ if (*value > devclass_get_maxunit(ata_devclass) ||
+ !(device = devclass_get_device(ata_devclass, *value)))
+ return ENXIO;
+ error = ata_detach(device);
+ /* XXX SOS should disable channel HW on controller */
+ break;
+
+ case IOCATADEVICES:
+ if (devices->channel > devclass_get_maxunit(ata_devclass) ||
+ !(device = devclass_get_device(ata_devclass, devices->channel)))
+ return ENXIO;
+ bzero(devices->name[0], 32);
+ bzero(&devices->params[0], sizeof(struct ata_params));
+ bzero(devices->name[1], 32);
+ bzero(&devices->params[1], sizeof(struct ata_params));
+ if (!device_get_children(device, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++) {
+ if (children[i] && device_is_attached(children[i])) {
+ struct ata_device *atadev = device_get_softc(children[i]);
+
+ if (atadev->unit == ATA_MASTER) {
+ strncpy(devices->name[0],
+ device_get_nameunit(children[i]), 32);
+ bcopy(&atadev->param, &devices->params[0],
+ sizeof(struct ata_params));
+ }
+ if (atadev->unit == ATA_SLAVE) {
+ strncpy(devices->name[1],
+ device_get_nameunit(children[i]), 32);
+ bcopy(&atadev->param, &devices->params[1],
+ sizeof(struct ata_params));
+ }
+ }
+ }
+ free(children, M_TEMP);
+ error = 0;
+ }
+ else
+ error = ENODEV;
+ break;
+
+ default:
+ if (ata_raid_ioctl_func)
+ error = ata_raid_ioctl_func(cmd, data);
+ }
+ return error;
+}
+
+int
+ata_device_ioctl(device_t dev, u_long cmd, caddr_t data)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ata_ioc_request *ioc_request = (struct ata_ioc_request *)data;
+ struct ata_params *params = (struct ata_params *)data;
+ int *mode = (int *)data;
+ struct ata_request *request;
+ caddr_t buf;
+ int error;
+
+ switch (cmd) {
+ case IOCATAREQUEST:
+ if (!(buf = malloc(ioc_request->count, M_ATA, M_NOWAIT))) {
+ return ENOMEM;
+ }
+ if (!(request = ata_alloc_request())) {
+ free(buf, M_ATA);
+ return ENOMEM;
+ }
+ if (ioc_request->flags & ATA_CMD_WRITE) {
+ error = copyin(ioc_request->data, buf, ioc_request->count);
+ if (error) {
+ free(buf, M_ATA);
+ ata_free_request(request);
+ return error;
+ }
+ }
+ request->dev = dev;
+ if (ioc_request->flags & ATA_CMD_ATAPI) {
+ request->flags = ATA_R_ATAPI;
+ bcopy(ioc_request->u.atapi.ccb, request->u.atapi.ccb, 16);
+ }
+ else {
+ request->u.ata.command = ioc_request->u.ata.command;
+ request->u.ata.feature = ioc_request->u.ata.feature;
+ request->u.ata.lba = ioc_request->u.ata.lba;
+ request->u.ata.count = ioc_request->u.ata.count;
+ }
+ request->timeout = ioc_request->timeout;
+ request->data = buf;
+ request->bytecount = ioc_request->count;
+ request->transfersize = request->bytecount;
+ if (ioc_request->flags & ATA_CMD_CONTROL)
+ request->flags |= ATA_R_CONTROL;
+ if (ioc_request->flags & ATA_CMD_READ)
+ request->flags |= ATA_R_READ;
+ if (ioc_request->flags & ATA_CMD_WRITE)
+ request->flags |= ATA_R_WRITE;
+ ata_queue_request(request);
+ if (request->flags & ATA_R_ATAPI) {
+ bcopy(&request->u.atapi.sense, &ioc_request->u.atapi.sense,
+ sizeof(struct atapi_sense));
+ }
+ else {
+ ioc_request->u.ata.command = request->u.ata.command;
+ ioc_request->u.ata.feature = request->u.ata.feature;
+ ioc_request->u.ata.lba = request->u.ata.lba;
+ ioc_request->u.ata.count = request->u.ata.count;
+ }
+ ioc_request->error = request->result;
+ if (ioc_request->flags & ATA_CMD_READ)
+ error = copyout(buf, ioc_request->data, ioc_request->count);
+ else
+ error = 0;
+ free(buf, M_ATA);
+ ata_free_request(request);
+ return error;
+
+ case IOCATAGPARM:
+ ata_getparam(atadev, 0);
+ bcopy(&atadev->param, params, sizeof(struct ata_params));
+ return 0;
+
+ case IOCATASMODE:
+ atadev->mode = *mode;
+ ATA_SETMODE(device_get_parent(dev), dev);
+ return 0;
+
+ case IOCATAGMODE:
+ *mode = atadev->mode;
+ return 0;
+ default:
+ return ENOTTY;
+ }
+}
+
+static void
+ata_boot_attach(void)
+{
+ struct ata_channel *ch;
+ int ctlr;
+
+ mtx_lock(&Giant); /* newbus suckage it needs Giant */
+
+ /* kick of probe and attach on all channels */
+ for (ctlr = 0; ctlr < devclass_get_maxunit(ata_devclass); ctlr++) {
+ if ((ch = devclass_get_softc(ata_devclass, ctlr))) {
+ ata_identify(ch->dev);
+ }
+ }
+
+ /* release the hook that got us here, we are only needed once during boot */
+ if (ata_delayed_attach) {
+ config_intrhook_disestablish(ata_delayed_attach);
+ free(ata_delayed_attach, M_TEMP);
+ ata_delayed_attach = NULL;
+ }
+
+ mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */
+}
+
+
+/*
+ * misc support functions
+ */
+static device_t
+ata_add_child(device_t parent, struct ata_device *atadev, int unit)
+{
+ device_t child;
+
+ if ((child = device_add_child(parent, NULL, unit))) {
+ device_set_softc(child, atadev);
+ device_quiet(child);
+ atadev->dev = child;
+ atadev->max_iosize = DEV_BSIZE;
+ atadev->mode = ATA_PIO_MAX;
+ }
+ return child;
+}
+
+static int
+ata_getparam(struct ata_device *atadev, int init)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev));
+ struct ata_request *request;
+ u_int8_t command = 0;
+ int error = ENOMEM, retries = 2;
+
+ if (ch->devices &
+ (atadev->unit == ATA_MASTER ? ATA_ATA_MASTER : ATA_ATA_SLAVE))
+ command = ATA_ATA_IDENTIFY;
+ if (ch->devices &
+ (atadev->unit == ATA_MASTER ? ATA_ATAPI_MASTER : ATA_ATAPI_SLAVE))
+ command = ATA_ATAPI_IDENTIFY;
+ if (!command)
+ return ENXIO;
+
+ while (retries-- > 0 && error) {
+ if (!(request = ata_alloc_request()))
+ break;
+ request->dev = atadev->dev;
+ request->timeout = 1;
+ request->retries = 0;
+ request->u.ata.command = command;
+ request->flags = (ATA_R_READ|ATA_R_AT_HEAD|ATA_R_DIRECT|ATA_R_QUIET);
+ request->data = (void *)&atadev->param;
+ request->bytecount = sizeof(struct ata_params);
+ request->donecount = 0;
+ request->transfersize = DEV_BSIZE;
+ ata_queue_request(request);
+ error = request->result;
+ ata_free_request(request);
+ }
+
+ if (!error && (isprint(atadev->param.model[0]) ||
+ isprint(atadev->param.model[1]))) {
+ struct ata_params *atacap = &atadev->param;
+ char buffer[64];
+#if BYTE_ORDER == BIG_ENDIAN
+ int16_t *ptr;
+
+ for (ptr = (int16_t *)atacap;
+ ptr < (int16_t *)atacap + sizeof(struct ata_params)/2; ptr++) {
+ *ptr = bswap16(*ptr);
+ }
+#endif
+ if (!(!strncmp(atacap->model, "FX", 2) ||
+ !strncmp(atacap->model, "NEC", 3) ||
+ !strncmp(atacap->model, "Pioneer", 7) ||
+ !strncmp(atacap->model, "SHARP", 5))) {
+ bswap(atacap->model, sizeof(atacap->model));
+ bswap(atacap->revision, sizeof(atacap->revision));
+ bswap(atacap->serial, sizeof(atacap->serial));
+ }
+ btrim(atacap->model, sizeof(atacap->model));
+ bpack(atacap->model, atacap->model, sizeof(atacap->model));
+ btrim(atacap->revision, sizeof(atacap->revision));
+ bpack(atacap->revision, atacap->revision, sizeof(atacap->revision));
+ btrim(atacap->serial, sizeof(atacap->serial));
+ bpack(atacap->serial, atacap->serial, sizeof(atacap->serial));
+
+ if (bootverbose)
+ printf("ata%d-%s: pio=%s wdma=%s udma=%s cable=%s wire\n",
+ device_get_unit(ch->dev),
+ atadev->unit == ATA_MASTER ? "master" : "slave",
+ ata_mode2str(ata_pmode(atacap)),
+ ata_mode2str(ata_wmode(atacap)),
+ ata_mode2str(ata_umode(atacap)),
+ (atacap->hwres & ATA_CABLE_ID) ? "80":"40");
+
+ if (init) {
+ sprintf(buffer, "%.40s/%.8s", atacap->model, atacap->revision);
+ device_set_desc_copy(atadev->dev, buffer);
+ if ((atadev->param.config & ATA_PROTO_ATAPI) &&
+ (atadev->param.config != ATA_CFA_MAGIC1) &&
+ (atadev->param.config != ATA_CFA_MAGIC2)) {
+ if (atapi_dma && ch->dma &&
+ (atadev->param.config & ATA_DRQ_MASK) != ATA_DRQ_INTR &&
+ ata_umode(&atadev->param) >= ATA_UDMA2)
+ atadev->mode = ATA_DMA_MAX;
+ }
+ else {
+ if (ata_dma && ch->dma &&
+ (ata_umode(&atadev->param) > 0 ||
+ ata_wmode(&atadev->param) > 0))
+ atadev->mode = ATA_DMA_MAX;
+ }
+ }
+ }
+ else {
+ if (!error)
+ error = ENXIO;
+ }
+ return error;
+}
+
+int
+ata_identify(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ struct ata_device *master = NULL, *slave = NULL;
+ device_t master_child = NULL, slave_child = NULL;
+ int master_unit = -1, slave_unit = -1;
+
+ if (ch->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER)) {
+ if (!(master = malloc(sizeof(struct ata_device),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "out of memory\n");
+ return ENOMEM;
+ }
+ master->unit = ATA_MASTER;
+ }
+ if (ch->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE)) {
+ if (!(slave = malloc(sizeof(struct ata_device),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+ free(master, M_ATA);
+ device_printf(dev, "out of memory\n");
+ return ENOMEM;
+ }
+ slave->unit = ATA_SLAVE;
+ }
+
+#ifdef ATA_STATIC_ID
+ if (ch->devices & ATA_ATA_MASTER)
+ master_unit = (device_get_unit(dev) << 1);
+#endif
+ if (master && !(master_child = ata_add_child(dev, master, master_unit))) {
+ free(master, M_ATA);
+ master = NULL;
+ }
+#ifdef ATA_STATIC_ID
+ if (ch->devices & ATA_ATA_SLAVE)
+ slave_unit = (device_get_unit(dev) << 1) + 1;
+#endif
+ if (slave && !(slave_child = ata_add_child(dev, slave, slave_unit))) {
+ free(slave, M_ATA);
+ slave = NULL;
+ }
+
+ if (slave && ata_getparam(slave, 1)) {
+ device_delete_child(dev, slave_child);
+ free(slave, M_ATA);
+ }
+ if (master && ata_getparam(master, 1)) {
+ device_delete_child(dev, master_child);
+ free(master, M_ATA);
+ }
+
+ bus_generic_probe(dev);
+ bus_generic_attach(dev);
+ return 0;
+}
+
+void
+ata_default_registers(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* fill in the defaults from whats setup already */
+ ch->r_io[ATA_ERROR].res = ch->r_io[ATA_FEATURE].res;
+ ch->r_io[ATA_ERROR].offset = ch->r_io[ATA_FEATURE].offset;
+ ch->r_io[ATA_IREASON].res = ch->r_io[ATA_COUNT].res;
+ ch->r_io[ATA_IREASON].offset = ch->r_io[ATA_COUNT].offset;
+ ch->r_io[ATA_STATUS].res = ch->r_io[ATA_COMMAND].res;
+ ch->r_io[ATA_STATUS].offset = ch->r_io[ATA_COMMAND].offset;
+ ch->r_io[ATA_ALTSTAT].res = ch->r_io[ATA_CONTROL].res;
+ ch->r_io[ATA_ALTSTAT].offset = ch->r_io[ATA_CONTROL].offset;
+}
+
+void
+ata_modify_if_48bit(struct ata_request *request)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ struct ata_device *atadev = device_get_softc(request->dev);
+
+ atadev->flags &= ~ATA_D_48BIT_ACTIVE;
+
+ if ((request->u.ata.lba >= ATA_MAX_28BIT_LBA ||
+ request->u.ata.count > 256) &&
+ atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) {
+
+ /* translate command into 48bit version */
+ switch (request->u.ata.command) {
+ case ATA_READ:
+ request->u.ata.command = ATA_READ48;
+ break;
+ case ATA_READ_MUL:
+ request->u.ata.command = ATA_READ_MUL48;
+ break;
+ case ATA_READ_DMA:
+ if (ch->flags & ATA_NO_48BIT_DMA) {
+ if (request->transfersize > DEV_BSIZE)
+ request->u.ata.command = ATA_READ_MUL48;
+ else
+ request->u.ata.command = ATA_READ48;
+ request->flags &= ~ATA_R_DMA;
+ }
+ else
+ request->u.ata.command = ATA_READ_DMA48;
+ break;
+ case ATA_READ_DMA_QUEUED:
+ if (ch->flags & ATA_NO_48BIT_DMA) {
+ if (request->transfersize > DEV_BSIZE)
+ request->u.ata.command = ATA_READ_MUL48;
+ else
+ request->u.ata.command = ATA_READ48;
+ request->flags &= ~ATA_R_DMA;
+ }
+ else
+ request->u.ata.command = ATA_READ_DMA_QUEUED48;
+ break;
+ case ATA_WRITE:
+ request->u.ata.command = ATA_WRITE48;
+ break;
+ case ATA_WRITE_MUL:
+ request->u.ata.command = ATA_WRITE_MUL48;
+ break;
+ case ATA_WRITE_DMA:
+ if (ch->flags & ATA_NO_48BIT_DMA) {
+ if (request->transfersize > DEV_BSIZE)
+ request->u.ata.command = ATA_WRITE_MUL48;
+ else
+ request->u.ata.command = ATA_WRITE48;
+ request->flags &= ~ATA_R_DMA;
+ }
+ else
+ request->u.ata.command = ATA_WRITE_DMA48;
+ break;
+ case ATA_WRITE_DMA_QUEUED:
+ if (ch->flags & ATA_NO_48BIT_DMA) {
+ if (request->transfersize > DEV_BSIZE)
+ request->u.ata.command = ATA_WRITE_MUL48;
+ else
+ request->u.ata.command = ATA_WRITE48;
+ request->u.ata.command = ATA_WRITE48;
+ request->flags &= ~ATA_R_DMA;
+ }
+ else
+ request->u.ata.command = ATA_WRITE_DMA_QUEUED48;
+ break;
+ case ATA_FLUSHCACHE:
+ request->u.ata.command = ATA_FLUSHCACHE48;
+ break;
+ case ATA_READ_NATIVE_MAX_ADDDRESS:
+ request->u.ata.command = ATA_READ_NATIVE_MAX_ADDDRESS48;
+ break;
+ case ATA_SET_MAX_ADDRESS:
+ request->u.ata.command = ATA_SET_MAX_ADDRESS48;
+ break;
+ default:
+ return;
+ }
+ atadev->flags |= ATA_D_48BIT_ACTIVE;
+ }
+}
+
+void
+ata_udelay(int interval)
+{
+ /* for now just use DELAY, the timer/sleep subsytems are not there yet */
+ if (1 || interval < (1000000/hz) || ata_delayed_attach)
+ DELAY(interval);
+ else
+ tsleep(&interval, PRIBIO, "ataslp", interval/(1000000/hz));
+}
+
+char *
+ata_mode2str(int mode)
+{
+ switch (mode) {
+ case -1: return "UNSUPPORTED";
+ case ATA_PIO0: return "PIO0";
+ case ATA_PIO1: return "PIO1";
+ case ATA_PIO2: return "PIO2";
+ case ATA_PIO3: return "PIO3";
+ case ATA_PIO4: return "PIO4";
+ case ATA_WDMA0: return "WDMA0";
+ case ATA_WDMA1: return "WDMA1";
+ case ATA_WDMA2: return "WDMA2";
+ case ATA_UDMA0: return "UDMA16";
+ case ATA_UDMA1: return "UDMA25";
+ case ATA_UDMA2: return "UDMA33";
+ case ATA_UDMA3: return "UDMA40";
+ case ATA_UDMA4: return "UDMA66";
+ case ATA_UDMA5: return "UDMA100";
+ case ATA_UDMA6: return "UDMA133";
+ case ATA_SA150: return "SATA150";
+ case ATA_SA300: return "SATA300";
+ case ATA_USB: return "USB";
+ case ATA_USB1: return "USB1";
+ case ATA_USB2: return "USB2";
+ default:
+ if (mode & ATA_DMA_MASK)
+ return "BIOSDMA";
+ else
+ return "BIOSPIO";
+ }
+}
+
+int
+ata_pmode(struct ata_params *ap)
+{
+ if (ap->atavalid & ATA_FLAG_64_70) {
+ if (ap->apiomodes & 0x02)
+ return ATA_PIO4;
+ if (ap->apiomodes & 0x01)
+ return ATA_PIO3;
+ }
+ if (ap->mwdmamodes & 0x04)
+ return ATA_PIO4;
+ if (ap->mwdmamodes & 0x02)
+ return ATA_PIO3;
+ if (ap->mwdmamodes & 0x01)
+ return ATA_PIO2;
+ if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x200)
+ return ATA_PIO2;
+ if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x100)
+ return ATA_PIO1;
+ if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x000)
+ return ATA_PIO0;
+ return ATA_PIO0;
+}
+
+int
+ata_wmode(struct ata_params *ap)
+{
+ if (ap->mwdmamodes & 0x04)
+ return ATA_WDMA2;
+ if (ap->mwdmamodes & 0x02)
+ return ATA_WDMA1;
+ if (ap->mwdmamodes & 0x01)
+ return ATA_WDMA0;
+ return -1;
+}
+
+int
+ata_umode(struct ata_params *ap)
+{
+ if (ap->atavalid & ATA_FLAG_88) {
+ if (ap->udmamodes & 0x40)
+ return ATA_UDMA6;
+ if (ap->udmamodes & 0x20)
+ return ATA_UDMA5;
+ if (ap->udmamodes & 0x10)
+ return ATA_UDMA4;
+ if (ap->udmamodes & 0x08)
+ return ATA_UDMA3;
+ if (ap->udmamodes & 0x04)
+ return ATA_UDMA2;
+ if (ap->udmamodes & 0x02)
+ return ATA_UDMA1;
+ if (ap->udmamodes & 0x01)
+ return ATA_UDMA0;
+ }
+ return -1;
+}
+
+int
+ata_limit_mode(device_t dev, int mode, int maxmode)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if (maxmode && mode > maxmode)
+ mode = maxmode;
+
+ if (mode >= ATA_UDMA0 && ata_umode(&atadev->param) > 0)
+ return min(mode, ata_umode(&atadev->param));
+
+ if (mode >= ATA_WDMA0 && ata_wmode(&atadev->param) > 0)
+ return min(mode, ata_wmode(&atadev->param));
+
+ if (mode > ata_pmode(&atadev->param))
+ return min(mode, ata_pmode(&atadev->param));
+
+ return mode;
+}
+
+static void
+bswap(int8_t *buf, int len)
+{
+ u_int16_t *ptr = (u_int16_t*)(buf + len);
+
+ while (--ptr >= (u_int16_t*)buf)
+ *ptr = ntohs(*ptr);
+}
+
+static void
+btrim(int8_t *buf, int len)
+{
+ int8_t *ptr;
+
+ for (ptr = buf; ptr < buf+len; ++ptr)
+ if (!*ptr || *ptr == '_')
+ *ptr = ' ';
+ for (ptr = buf + len - 1; ptr >= buf && *ptr == ' '; --ptr)
+ *ptr = 0;
+}
+
+static void
+bpack(int8_t *src, int8_t *dst, int len)
+{
+ int i, j, blank;
+
+ for (i = j = blank = 0 ; i < len; i++) {
+ if (blank && src[i] == ' ') continue;
+ if (blank && src[i] != ' ') {
+ dst[j++] = src[i];
+ blank = 0;
+ continue;
+ }
+ if (src[i] == ' ') {
+ blank = 1;
+ if (i == 0)
+ continue;
+ }
+ dst[j++] = src[i];
+ }
+ if (j < len)
+ dst[j] = 0x00;
+}
+
+
+/*
+ * module handeling
+ */
+static int
+ata_module_event_handler(module_t mod, int what, void *arg)
+{
+ static struct cdev *atacdev;
+
+ switch (what) {
+ case MOD_LOAD:
+ /* register controlling device */
+ atacdev = make_dev(&ata_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "ata");
+
+ if (cold) {
+ /* register boot attach to be run when interrupts are enabled */
+ if (!(ata_delayed_attach = (struct intr_config_hook *)
+ malloc(sizeof(struct intr_config_hook),
+ M_TEMP, M_NOWAIT | M_ZERO))) {
+ printf("ata: malloc of delayed attach hook failed\n");
+ return EIO;
+ }
+ ata_delayed_attach->ich_func = (void*)ata_boot_attach;
+ if (config_intrhook_establish(ata_delayed_attach) != 0) {
+ printf("ata: config_intrhook_establish failed\n");
+ free(ata_delayed_attach, M_TEMP);
+ }
+ }
+ return 0;
+
+ case MOD_UNLOAD:
+ /* deregister controlling device */
+ destroy_dev(atacdev);
+ return 0;
+
+ default:
+ return EOPNOTSUPP;
+ }
+}
+
+static moduledata_t ata_moduledata = { "ata", ata_module_event_handler, NULL };
+DECLARE_MODULE(ata, ata_moduledata, SI_SUB_CONFIGURE, SI_ORDER_SECOND);
+MODULE_VERSION(ata, 1);
+
+static void
+ata_init(void)
+{
+ ata_request_zone = uma_zcreate("ata_request", sizeof(struct ata_request),
+ NULL, NULL, NULL, NULL, 0, 0);
+ ata_composite_zone = uma_zcreate("ata_composite",
+ sizeof(struct ata_composite),
+ NULL, NULL, NULL, NULL, 0, 0);
+}
+SYSINIT(ata_register, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_init, NULL);
+
+static void
+ata_uninit(void)
+{
+ uma_zdestroy(ata_composite_zone);
+ uma_zdestroy(ata_request_zone);
+}
+SYSUNINIT(ata_unregister, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_uninit, NULL);
diff --git a/sys/dev/ata/ata-all.h b/sys/dev/ata/ata-all.h
new file mode 100644
index 000000000000..f8b2fd5b8191
--- /dev/null
+++ b/sys/dev/ata/ata-all.h
@@ -0,0 +1,645 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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$
+ */
+
+/* ATA register defines */
+#define ATA_DATA 0 /* (RW) data */
+
+#define ATA_FEATURE 1 /* (W) feature */
+#define ATA_F_DMA 0x01 /* enable DMA */
+#define ATA_F_OVL 0x02 /* enable overlap */
+
+#define ATA_COUNT 2 /* (W) sector count */
+
+#define ATA_SECTOR 3 /* (RW) sector # */
+#define ATA_CYL_LSB 4 /* (RW) cylinder# LSB */
+#define ATA_CYL_MSB 5 /* (RW) cylinder# MSB */
+#define ATA_DRIVE 6 /* (W) Sector/Drive/Head */
+#define ATA_D_LBA 0x40 /* use LBA addressing */
+#define ATA_D_IBM 0xa0 /* 512 byte sectors, ECC */
+
+#define ATA_COMMAND 7 /* (W) command */
+
+#define ATA_ERROR 8 /* (R) error */
+#define ATA_E_ILI 0x01 /* illegal length */
+#define ATA_E_NM 0x02 /* no media */
+#define ATA_E_ABORT 0x04 /* command aborted */
+#define ATA_E_MCR 0x08 /* media change request */
+#define ATA_E_IDNF 0x10 /* ID not found */
+#define ATA_E_MC 0x20 /* media changed */
+#define ATA_E_UNC 0x40 /* uncorrectable data */
+#define ATA_E_ICRC 0x80 /* UDMA crc error */
+#define ATA_E_ATAPI_SENSE_MASK 0xf0 /* ATAPI sense key mask */
+
+#define ATA_IREASON 9 /* (R) interrupt reason */
+#define ATA_I_CMD 0x01 /* cmd (1) | data (0) */
+#define ATA_I_IN 0x02 /* read (1) | write (0) */
+#define ATA_I_RELEASE 0x04 /* released bus (1) */
+#define ATA_I_TAGMASK 0xf8 /* tag mask */
+
+#define ATA_STATUS 10 /* (R) status */
+#define ATA_ALTSTAT 11 /* (R) alternate status */
+#define ATA_S_ERROR 0x01 /* error */
+#define ATA_S_INDEX 0x02 /* index */
+#define ATA_S_CORR 0x04 /* data corrected */
+#define ATA_S_DRQ 0x08 /* data request */
+#define ATA_S_DSC 0x10 /* drive seek completed */
+#define ATA_S_SERVICE 0x10 /* drive needs service */
+#define ATA_S_DWF 0x20 /* drive write fault */
+#define ATA_S_DMA 0x20 /* DMA ready */
+#define ATA_S_READY 0x40 /* drive ready */
+#define ATA_S_BUSY 0x80 /* busy */
+
+#define ATA_CONTROL 12 /* (W) control */
+
+#define ATA_CTLOFFSET 0x206 /* control register offset */
+#define ATA_PCCARD_CTLOFFSET 0x0e /* do for PCCARD devices */
+#define ATA_PC98_CTLOFFSET 0x10c /* do for PC98 devices */
+#define ATA_A_IDS 0x02 /* disable interrupts */
+#define ATA_A_RESET 0x04 /* RESET controller */
+#define ATA_A_4BIT 0x08 /* 4 head bits */
+#define ATA_A_HOB 0x80 /* High Order Byte enable */
+
+/* SATA register defines */
+#define ATA_SSTATUS 13
+#define ATA_SS_DET_MASK 0x0000000f
+#define ATA_SS_DET_NO_DEVICE 0x00000000
+#define ATA_SS_DET_DEV_PRESENT 0x00000001
+#define ATA_SS_DET_PHY_ONLINE 0x00000003
+#define ATA_SS_DET_PHY_OFFLINE 0x00000004
+
+#define ATA_SS_SPD_MASK 0x000000f0
+#define ATA_SS_SPD_NO_SPEED 0x00000000
+#define ATA_SS_SPD_GEN1 0x00000010
+#define ATA_SS_SPD_GEN2 0x00000020
+
+#define ATA_SS_IPM_MASK 0x00000f00
+#define ATA_SS_IPM_NO_DEVICE 0x00000000
+#define ATA_SS_IPM_ACTIVE 0x00000100
+#define ATA_SS_IPM_PARTIAL 0x00000200
+#define ATA_SS_IPM_SLUMBER 0x00000600
+
+#define ATA_SS_CONWELL_MASK \
+ (ATA_SS_DET_MASK|ATA_SS_SPD_MASK|ATA_SS_IPM_MASK)
+#define ATA_SS_CONWELL_GEN1 \
+ (ATA_SS_DET_PHY_ONLINE|ATA_SS_SPD_GEN1|ATA_SS_IPM_ACTIVE)
+#define ATA_SS_CONWELL_GEN2 \
+ (ATA_SS_DET_PHY_ONLINE|ATA_SS_SPD_GEN2|ATA_SS_IPM_ACTIVE)
+
+#define ATA_SERROR 14
+#define ATA_SE_DATA_CORRECTED 0x00000001
+#define ATA_SE_COMM_CORRECTED 0x00000002
+#define ATA_SE_DATA_ERR 0x00000100
+#define ATA_SE_COMM_ERR 0x00000200
+#define ATA_SE_PROT_ERR 0x00000400
+#define ATA_SE_HOST_ERR 0x00000800
+#define ATA_SE_PHY_CHANGED 0x00010000
+#define ATA_SE_PHY_IERROR 0x00020000
+#define ATA_SE_COMM_WAKE 0x00040000
+#define ATA_SE_DECODE_ERR 0x00080000
+#define ATA_SE_PARITY_ERR 0x00100000
+#define ATA_SE_CRC_ERR 0x00200000
+#define ATA_SE_HANDSHAKE_ERR 0x00400000
+#define ATA_SE_LINKSEQ_ERR 0x00800000
+#define ATA_SE_TRANSPORT_ERR 0x01000000
+#define ATA_SE_UNKNOWN_FIS 0x02000000
+
+#define ATA_SCONTROL 15
+#define ATA_SC_DET_MASK 0x0000000f
+#define ATA_SC_DET_IDLE 0x00000000
+#define ATA_SC_DET_RESET 0x00000001
+#define ATA_SC_DET_DISABLE 0x00000004
+
+#define ATA_SC_SPD_MASK 0x000000f0
+#define ATA_SC_SPD_NO_SPEED 0x00000000
+#define ATA_SC_SPD_SPEED_GEN1 0x00000010
+#define ATA_SC_SPD_SPEED_GEN2 0x00000020
+
+#define ATA_SC_IPM_MASK 0x00000f00
+#define ATA_SC_IPM_NONE 0x00000000
+#define ATA_SC_IPM_DIS_PARTIAL 0x00000100
+#define ATA_SC_IPM_DIS_SLUMBER 0x00000200
+
+#define ATA_SACTIVE 16
+
+/* SATA AHCI v1.0 register defines */
+#define ATA_AHCI_CAP 0x00
+#define ATA_AHCI_NPMASK 0x1f
+#define ATA_AHCI_CAP_CLO 0x01000000
+#define ATA_AHCI_CAP_64BIT 0x80000000
+
+#define ATA_AHCI_GHC 0x04
+#define ATA_AHCI_GHC_AE 0x80000000
+#define ATA_AHCI_GHC_IE 0x00000002
+#define ATA_AHCI_GHC_HR 0x80000001
+
+#define ATA_AHCI_IS 0x08
+#define ATA_AHCI_PI 0x0c
+#define ATA_AHCI_VS 0x10
+
+#define ATA_AHCI_OFFSET 0x80
+
+#define ATA_AHCI_P_CLB 0x100
+#define ATA_AHCI_P_CLBU 0x104
+#define ATA_AHCI_P_FB 0x108
+#define ATA_AHCI_P_FBU 0x10c
+#define ATA_AHCI_P_IS 0x110
+#define ATA_AHCI_P_IE 0x114
+#define ATA_AHCI_P_IX_DHR 0x00000001
+#define ATA_AHCI_P_IX_PS 0x00000002
+#define ATA_AHCI_P_IX_DS 0x00000004
+#define ATA_AHCI_P_IX_SDB 0x00000008
+#define ATA_AHCI_P_IX_UF 0x00000010
+#define ATA_AHCI_P_IX_DP 0x00000020
+#define ATA_AHCI_P_IX_PC 0x00000040
+#define ATA_AHCI_P_IX_DI 0x00000080
+
+#define ATA_AHCI_P_IX_PRC 0x00400000
+#define ATA_AHCI_P_IX_IPM 0x00800000
+#define ATA_AHCI_P_IX_OF 0x01000000
+#define ATA_AHCI_P_IX_INF 0x04000000
+#define ATA_AHCI_P_IX_IF 0x08000000
+#define ATA_AHCI_P_IX_HBD 0x10000000
+#define ATA_AHCI_P_IX_HBF 0x20000000
+#define ATA_AHCI_P_IX_TFE 0x40000000
+#define ATA_AHCI_P_IX_CPD 0x80000000
+
+#define ATA_AHCI_P_CMD 0x118
+#define ATA_AHCI_P_CMD_ST 0x00000001
+#define ATA_AHCI_P_CMD_SUD 0x00000002
+#define ATA_AHCI_P_CMD_POD 0x00000004
+#define ATA_AHCI_P_CMD_CLO 0x00000008
+#define ATA_AHCI_P_CMD_FRE 0x00000010
+#define ATA_AHCI_P_CMD_CCS_MASK 0x00001f00
+#define ATA_AHCI_P_CMD_ISS 0x00002000
+#define ATA_AHCI_P_CMD_FR 0x00004000
+#define ATA_AHCI_P_CMD_CR 0x00008000
+#define ATA_AHCI_P_CMD_CPS 0x00010000
+#define ATA_AHCI_P_CMD_PMA 0x00020000
+#define ATA_AHCI_P_CMD_HPCP 0x00040000
+#define ATA_AHCI_P_CMD_ISP 0x00080000
+#define ATA_AHCI_P_CMD_CPD 0x00100000
+#define ATA_AHCI_P_CMD_ATAPI 0x01000000
+#define ATA_AHCI_P_CMD_DLAE 0x02000000
+#define ATA_AHCI_P_CMD_ALPE 0x04000000
+#define ATA_AHCI_P_CMD_ASP 0x08000000
+#define ATA_AHCI_P_CMD_ICC_MASK 0xf0000000
+#define ATA_AHCI_P_CMD_NOOP 0x00000000
+#define ATA_AHCI_P_CMD_ACTIVE 0x10000000
+#define ATA_AHCI_P_CMD_PARTIAL 0x20000000
+#define ATA_AHCI_P_CMD_SLUMPER 0x60000000
+
+#define ATA_AHCI_P_TFD 0x120
+#define ATA_AHCI_P_SIG 0x124
+#define ATA_AHCI_P_SSTS 0x128
+#define ATA_AHCI_P_SCTL 0x12c
+#define ATA_AHCI_P_SERR 0x130
+#define ATA_AHCI_P_SACT 0x134
+#define ATA_AHCI_P_CI 0x138
+
+#define ATA_AHCI_CL_SIZE 32
+#define ATA_AHCI_CL_OFFSET 0
+#define ATA_AHCI_FB_OFFSET 1024
+#define ATA_AHCI_CT_OFFSET 1024+256
+#define ATA_AHCI_CT_SG_OFFSET 128
+#define ATA_AHCI_CT_SIZE 256
+
+/* DMA register defines */
+#define ATA_DMA_ENTRIES 256
+#define ATA_DMA_EOT 0x80000000
+
+#define ATA_BMCMD_PORT 17
+#define ATA_BMCMD_START_STOP 0x01
+#define ATA_BMCMD_WRITE_READ 0x08
+
+#define ATA_BMDEVSPEC_0 18
+#define ATA_BMSTAT_PORT 19
+#define ATA_BMSTAT_ACTIVE 0x01
+#define ATA_BMSTAT_ERROR 0x02
+#define ATA_BMSTAT_INTERRUPT 0x04
+#define ATA_BMSTAT_MASK 0x07
+#define ATA_BMSTAT_DMA_MASTER 0x20
+#define ATA_BMSTAT_DMA_SLAVE 0x40
+#define ATA_BMSTAT_DMA_SIMPLEX 0x80
+
+#define ATA_BMDEVSPEC_1 20
+#define ATA_BMDTP_PORT 21
+
+#define ATA_IDX_ADDR 22
+#define ATA_IDX_DATA 23
+#define ATA_MAX_RES 24
+
+/* misc defines */
+#define ATA_PRIMARY 0x1f0
+#define ATA_SECONDARY 0x170
+#define ATA_PC98_BANK 0x432
+#define ATA_IOSIZE 0x08
+#define ATA_PC98_IOSIZE 0x10
+#define ATA_CTLIOSIZE 0x01
+#define ATA_BMIOSIZE 0x08
+#define ATA_PC98_BANKIOSIZE 0x01
+#define ATA_IOADDR_RID 0
+#define ATA_CTLADDR_RID 1
+#define ATA_BMADDR_RID 0x20
+#define ATA_PC98_CTLADDR_RID 8
+#define ATA_PC98_BANKADDR_RID 9
+#define ATA_IRQ_RID 0
+#define ATA_DEV(device) ((device == ATA_MASTER) ? 0 : 1)
+#define ATA_CFA_MAGIC1 0x844A
+#define ATA_CFA_MAGIC2 0x848A
+#define ATAPI_MAGIC_LSB 0x14
+#define ATAPI_MAGIC_MSB 0xeb
+#define ATAPI_P_READ (ATA_S_DRQ | ATA_I_IN)
+#define ATAPI_P_WRITE (ATA_S_DRQ)
+#define ATAPI_P_CMDOUT (ATA_S_DRQ | ATA_I_CMD)
+#define ATAPI_P_DONEDRQ (ATA_S_DRQ | ATA_I_CMD | ATA_I_IN)
+#define ATAPI_P_DONE (ATA_I_CMD | ATA_I_IN)
+#define ATAPI_P_ABORT 0
+#define ATA_INTR_FLAGS (INTR_MPSAFE|INTR_TYPE_BIO|INTR_ENTROPY)
+#define ATA_OP_CONTINUES 0
+#define ATA_OP_FINISHED 1
+#define ATA_MAX_28BIT_LBA 268435455UL
+
+/* structure used for composite atomic operations */
+#define MAX_COMPOSITES 32 /* u_int32_t bits */
+struct ata_composite {
+ struct mtx lock; /* control lock */
+ u_int32_t rd_needed; /* needed read subdisks */
+ u_int32_t rd_done; /* done read subdisks */
+ u_int32_t wr_needed; /* needed write subdisks */
+ u_int32_t wr_depend; /* write depends on subdisks */
+ u_int32_t wr_done; /* done write subdisks */
+ struct ata_request *request[MAX_COMPOSITES];
+ u_int32_t residual; /* bytes still to transfer */
+ caddr_t data_1;
+ caddr_t data_2;
+};
+
+/* structure used to queue an ATA/ATAPI request */
+struct ata_request {
+ device_t dev; /* device handle */
+ device_t parent; /* channel handle */
+ union {
+ struct {
+ u_int8_t command; /* command reg */
+ u_int16_t feature; /* feature reg */
+ u_int16_t count; /* count reg */
+ u_int64_t lba; /* lba reg */
+ } ata;
+ struct {
+ u_int8_t ccb[16]; /* ATAPI command block */
+ struct atapi_sense sense; /* ATAPI request sense data */
+ u_int8_t saved_cmd; /* ATAPI saved command */
+ } atapi;
+ } u;
+ u_int32_t bytecount; /* bytes to transfer */
+ u_int32_t transfersize; /* bytes pr transfer */
+ caddr_t data; /* pointer to data buf */
+ int flags;
+#define ATA_R_CONTROL 0x00000001
+#define ATA_R_READ 0x00000002
+#define ATA_R_WRITE 0x00000004
+#define ATA_R_ATAPI 0x00000008
+#define ATA_R_DMA 0x00000010
+#define ATA_R_QUIET 0x00000020
+#define ATA_R_TIMEOUT 0x00000040
+
+#define ATA_R_ORDERED 0x00000100
+#define ATA_R_AT_HEAD 0x00000200
+#define ATA_R_REQUEUE 0x00000400
+#define ATA_R_THREAD 0x00000800
+#define ATA_R_DIRECT 0x00001000
+
+#define ATA_R_DEBUG 0x10000000
+#define ATA_R_DANGER1 0x20000000
+#define ATA_R_DANGER2 0x40000000
+
+ u_int8_t status; /* ATA status */
+ u_int8_t error; /* ATA error */
+ u_int8_t dmastat; /* DMA status */
+ u_int32_t donecount; /* bytes transferred */
+ int result; /* result error code */
+ void (*callback)(struct ata_request *request);
+ struct sema done; /* request done sema */
+ int retries; /* retry count */
+ int timeout; /* timeout for this cmd */
+ struct callout callout; /* callout management */
+ struct task task; /* task management */
+ struct bio *bio; /* bio for this request */
+ int this; /* this request ID */
+ struct ata_composite *composite; /* for composite atomic ops */
+ void *driver; /* driver specific */
+ TAILQ_ENTRY(ata_request) chain; /* list management */
+};
+
+/* define this for debugging request processing */
+#if 0
+#define ATA_DEBUG_RQ(request, string) \
+ { \
+ if (request->flags & ATA_R_DEBUG) \
+ device_printf(request->dev, "req=%p %s " string "\n", \
+ request, ata_cmd2str(request)); \
+ }
+#else
+#define ATA_DEBUG_RQ(request, string)
+#endif
+
+
+/* structure describing an ATA/ATAPI device */
+struct ata_device {
+ device_t dev; /* device handle */
+ int unit; /* physical unit */
+#define ATA_MASTER 0x00
+#define ATA_SLAVE 0x10
+
+ struct ata_params param; /* ata param structure */
+ int mode; /* current transfermode */
+ u_int32_t max_iosize; /* max IO size */
+ int flags;
+#define ATA_D_USE_CHS 0x0001
+#define ATA_D_MEDIA_CHANGED 0x0002
+#define ATA_D_ENC_PRESENT 0x0004
+#define ATA_D_48BIT_ACTIVE 0x0008
+};
+
+/* structure for holding DMA Physical Region Descriptors (PRD) entries */
+struct ata_dma_prdentry {
+ u_int32_t addr;
+ u_int32_t count;
+};
+
+/* structure used by the setprd function */
+struct ata_dmasetprd_args {
+ void *dmatab;
+ int nsegs;
+ int error;
+};
+
+/* structure holding DMA related information */
+struct ata_dma {
+ bus_dma_tag_t dmatag; /* parent DMA tag */
+ bus_dma_tag_t sg_tag; /* SG list DMA tag */
+ bus_dmamap_t sg_map; /* SG list DMA map */
+ void *sg; /* DMA transfer table */
+ bus_addr_t sg_bus; /* bus address of dmatab */
+ bus_dma_tag_t data_tag; /* data DMA tag */
+ bus_dmamap_t data_map; /* data DMA map */
+ bus_dma_tag_t work_tag; /* workspace DMA tag */
+ bus_dmamap_t work_map; /* workspace DMA map */
+ u_int8_t *work; /* workspace */
+ bus_addr_t work_bus; /* bus address of dmatab */
+
+ u_int32_t alignment; /* DMA SG list alignment */
+ u_int32_t boundary; /* DMA SG list boundary */
+ u_int32_t segsize; /* DMA SG list segment size */
+ u_int32_t max_iosize; /* DMA data max IO size */
+ u_int32_t cur_iosize; /* DMA data current IO size */
+ int flags;
+#define ATA_DMA_READ 0x01 /* transaction is a read */
+#define ATA_DMA_LOADED 0x02 /* DMA tables etc loaded */
+#define ATA_DMA_ACTIVE 0x04 /* DMA transfer in progress */
+
+ void (*alloc)(device_t dev);
+ void (*free)(device_t dev);
+ void (*setprd)(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
+ int (*load)(device_t dev, caddr_t data, int32_t count, int dir, void *addr, int *nsegs);
+ int (*unload)(device_t dev);
+ int (*start)(device_t dev);
+ int (*stop)(device_t dev);
+ void (*reset)(device_t dev);
+};
+
+/* structure holding lowlevel functions */
+struct ata_lowlevel {
+ int (*status)(device_t dev);
+ int (*begin_transaction)(struct ata_request *request);
+ int (*end_transaction)(struct ata_request *request);
+ int (*command)(struct ata_request *request);
+};
+
+/* structure holding resources for an ATA channel */
+struct ata_resource {
+ struct resource *res;
+ int offset;
+};
+
+/* structure describing an ATA channel */
+struct ata_channel {
+ device_t dev; /* device handle */
+ int unit; /* physical channel */
+ struct ata_resource r_io[ATA_MAX_RES];/* I/O resources */
+ struct resource *r_irq; /* interrupt of this channel */
+ void *ih; /* interrupt handle */
+ struct ata_lowlevel hw; /* lowlevel HW functions */
+ struct ata_dma *dma; /* DMA data / functions */
+ int flags; /* channel flags */
+#define ATA_NO_SLAVE 0x01
+#define ATA_USE_16BIT 0x02
+#define ATA_ATAPI_DMA_RO 0x04
+#define ATA_NO_48BIT_DMA 0x08
+#define ATA_ALWAYS_DMASTAT 0x10
+
+ int devices; /* what is present */
+#define ATA_ATA_MASTER 0x01
+#define ATA_ATA_SLAVE 0x02
+#define ATA_ATAPI_MASTER 0x04
+#define ATA_ATAPI_SLAVE 0x08
+
+ struct mtx state_mtx; /* state lock */
+ int state; /* ATA channel state */
+#define ATA_IDLE 0x0000
+#define ATA_ACTIVE 0x0001
+#define ATA_STALL_QUEUE 0x0002
+
+ struct mtx queue_mtx; /* queue lock */
+ TAILQ_HEAD(, ata_request) ata_queue; /* head of ATA queue */
+ struct ata_request *freezepoint; /* composite freezepoint */
+ struct ata_request *running; /* currently running request */
+};
+
+/* disk bay/enclosure related */
+#define ATA_LED_OFF 0x00
+#define ATA_LED_RED 0x01
+#define ATA_LED_GREEN 0x02
+#define ATA_LED_ORANGE 0x03
+#define ATA_LED_MASK 0x03
+
+/* externs */
+extern int (*ata_raid_ioctl_func)(u_long cmd, caddr_t data);
+extern struct intr_config_hook *ata_delayed_attach;
+extern devclass_t ata_devclass;
+extern int ata_wc;
+
+/* public prototypes */
+/* ata-all.c: */
+int ata_probe(device_t dev);
+int ata_attach(device_t dev);
+int ata_detach(device_t dev);
+int ata_reinit(device_t dev);
+int ata_suspend(device_t dev);
+int ata_resume(device_t dev);
+int ata_interrupt(void *data);
+int ata_device_ioctl(device_t dev, u_long cmd, caddr_t data);
+int ata_identify(device_t dev);
+void ata_default_registers(device_t dev);
+void ata_modify_if_48bit(struct ata_request *request);
+void ata_udelay(int interval);
+char *ata_mode2str(int mode);
+int ata_pmode(struct ata_params *ap);
+int ata_wmode(struct ata_params *ap);
+int ata_umode(struct ata_params *ap);
+int ata_limit_mode(device_t dev, int mode, int maxmode);
+
+/* ata-queue.c: */
+int ata_controlcmd(device_t dev, u_int8_t command, u_int16_t feature, u_int64_t lba, u_int16_t count);
+int ata_atapicmd(device_t dev, u_int8_t *ccb, caddr_t data, int count, int flags, int timeout);
+void ata_queue_request(struct ata_request *request);
+void ata_start(device_t dev);
+void ata_finish(struct ata_request *request);
+void ata_timeout(struct ata_request *);
+void ata_catch_inflight(device_t dev);
+void ata_fail_requests(device_t dev);
+char *ata_cmd2str(struct ata_request *request);
+
+/* ata-lowlevel.c: */
+void ata_generic_hw(device_t dev);
+int ata_begin_transaction(struct ata_request *);
+int ata_end_transaction(struct ata_request *);
+void ata_generic_reset(device_t dev);
+int ata_generic_command(struct ata_request *request);
+
+/* macros for alloc/free of struct ata_request */
+extern uma_zone_t ata_request_zone;
+#define ata_alloc_request() uma_zalloc(ata_request_zone, M_NOWAIT | M_ZERO)
+#define ata_free_request(request) { \
+ if (!(request->flags & ATA_R_DANGER2)) \
+ uma_zfree(ata_request_zone, request); \
+ }
+/* macros for alloc/free of struct ata_composite */
+extern uma_zone_t ata_composite_zone;
+#define ata_alloc_composite() uma_zalloc(ata_composite_zone, M_NOWAIT | M_ZERO)
+#define ata_free_composite(composite) uma_zfree(ata_composite_zone, composite)
+
+MALLOC_DECLARE(M_ATA);
+
+/* misc newbus defines */
+#define GRANDPARENT(dev) device_get_parent(device_get_parent(dev))
+
+/* macros to hide busspace uglyness */
+#define ATA_INB(res, offset) \
+ bus_space_read_1(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), (offset))
+
+#define ATA_INW(res, offset) \
+ bus_space_read_2(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), (offset))
+#define ATA_INL(res, offset) \
+ bus_space_read_4(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), (offset))
+#define ATA_INSW(res, offset, addr, count) \
+ bus_space_read_multi_2(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), \
+ (offset), (addr), (count))
+#define ATA_INSW_STRM(res, offset, addr, count) \
+ bus_space_read_multi_stream_2(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), \
+ (offset), (addr), (count))
+#define ATA_INSL(res, offset, addr, count) \
+ bus_space_read_multi_4(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), \
+ (offset), (addr), (count))
+#define ATA_INSL_STRM(res, offset, addr, count) \
+ bus_space_read_multi_stream_4(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), \
+ (offset), (addr), (count))
+#define ATA_OUTB(res, offset, value) \
+ bus_space_write_1(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), (offset), (value))
+#define ATA_OUTW(res, offset, value) \
+ bus_space_write_2(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), (offset), (value))
+#define ATA_OUTL(res, offset, value) \
+ bus_space_write_4(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), (offset), (value))
+#define ATA_OUTSW(res, offset, addr, count) \
+ bus_space_write_multi_2(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), \
+ (offset), (addr), (count))
+#define ATA_OUTSW_STRM(res, offset, addr, count) \
+ bus_space_write_multi_stream_2(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), \
+ (offset), (addr), (count))
+#define ATA_OUTSL(res, offset, addr, count) \
+ bus_space_write_multi_4(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), \
+ (offset), (addr), (count))
+#define ATA_OUTSL_STRM(res, offset, addr, count) \
+ bus_space_write_multi_stream_4(rman_get_bustag((res)), \
+ rman_get_bushandle((res)), \
+ (offset), (addr), (count))
+
+#define ATA_IDX_INB(ch, idx) \
+ ATA_INB(ch->r_io[idx].res, ch->r_io[idx].offset)
+
+#define ATA_IDX_INW(ch, idx) \
+ ATA_INW(ch->r_io[idx].res, ch->r_io[idx].offset)
+
+#define ATA_IDX_INL(ch, idx) \
+ ATA_INL(ch->r_io[idx].res, ch->r_io[idx].offset)
+
+#define ATA_IDX_INSW(ch, idx, addr, count) \
+ ATA_INSW(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count)
+
+#define ATA_IDX_INSW_STRM(ch, idx, addr, count) \
+ ATA_INSW_STRM(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count)
+
+#define ATA_IDX_INSL(ch, idx, addr, count) \
+ ATA_INSL(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count)
+
+#define ATA_IDX_INSL_STRM(ch, idx, addr, count) \
+ ATA_INSL_STRM(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count)
+
+#define ATA_IDX_OUTB(ch, idx, value) \
+ ATA_OUTB(ch->r_io[idx].res, ch->r_io[idx].offset, value)
+
+#define ATA_IDX_OUTW(ch, idx, value) \
+ ATA_OUTW(ch->r_io[idx].res, ch->r_io[idx].offset, value)
+
+#define ATA_IDX_OUTL(ch, idx, value) \
+ ATA_OUTL(ch->r_io[idx].res, ch->r_io[idx].offset, value)
+
+#define ATA_IDX_OUTSW(ch, idx, addr, count) \
+ ATA_OUTSW(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count)
+
+#define ATA_IDX_OUTSW_STRM(ch, idx, addr, count) \
+ ATA_OUTSW_STRM(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count)
+
+#define ATA_IDX_OUTSL(ch, idx, addr, count) \
+ ATA_OUTSL(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count)
+
+#define ATA_IDX_OUTSL_STRM(ch, idx, addr, count) \
+ ATA_OUTSL_STRM(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count)
diff --git a/sys/dev/ata/ata-card.c b/sys/dev/ata/ata-card.c
new file mode 100644
index 000000000000..a6747183ee0e
--- /dev/null
+++ b/sys/dev/ata/ata-card.c
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/stdarg.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <dev/ata/ata-all.h>
+#include <dev/pccard/pccard_cis.h>
+#include <dev/pccard/pccardreg.h>
+#include <dev/pccard/pccardvar.h>
+#include <ata_if.h>
+
+#include "pccarddevs.h"
+
+static const struct pccard_product ata_pccard_products[] = {
+ PCMCIA_CARD(FREECOM, PCCARDIDE),
+ PCMCIA_CARD(EXP, EXPMULTIMEDIA),
+ PCMCIA_CARD(IODATA3, CBIDE2),
+ PCMCIA_CARD(OEM2, CDROM1),
+ PCMCIA_CARD(OEM2, IDE),
+ PCMCIA_CARD(PANASONIC, KXLC005),
+ PCMCIA_CARD(TEAC, IDECARDII),
+ {NULL}
+};
+
+static int
+ata_pccard_probe(device_t dev)
+{
+ const struct pccard_product *pp;
+ u_int32_t fcn = PCCARD_FUNCTION_UNSPEC;
+ int error = 0;
+
+ if ((error = pccard_get_function(dev, &fcn)))
+ return error;
+
+ /* if it says its a disk we should register it */
+ if (fcn == PCCARD_FUNCTION_DISK)
+ return 0;
+
+ /* match other devices here, primarily cdrom/dvd rom */
+ if ((pp = pccard_product_lookup(dev, ata_pccard_products,
+ sizeof(ata_pccard_products[0]), NULL))) {
+ if (pp->pp_name)
+ device_set_desc(dev, pp->pp_name);
+ return 0;
+ }
+ return ENXIO;
+}
+
+static int
+ata_pccard_attach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ struct resource *io, *ctlio;
+ int i, rid, err;
+
+ /* allocate the io range to get start and length */
+ rid = ATA_IOADDR_RID;
+ if (!(io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
+ ATA_IOSIZE, RF_ACTIVE)))
+ return (ENXIO);
+
+ /* setup the resource vectors */
+ for (i = ATA_DATA; i <= ATA_COMMAND; i++) {
+ ch->r_io[i].res = io;
+ ch->r_io[i].offset = i;
+ }
+ ch->r_io[ATA_IDX_ADDR].res = io;
+
+ /*
+ * if we got more than the default ATA_IOSIZE ports, this is a device
+ * where ctlio is located at offset 14 into "normal" io space.
+ */
+ if (rman_get_size(io) > ATA_IOSIZE) {
+ ch->r_io[ATA_CONTROL].res = io;
+ ch->r_io[ATA_CONTROL].offset = 14;
+ }
+ else {
+ rid = ATA_CTLADDR_RID;
+ if (!(ctlio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
+ ATA_CTLIOSIZE, RF_ACTIVE))) {
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, io);
+ for (i = ATA_DATA; i < ATA_MAX_RES; i++)
+ ch->r_io[i].res = NULL;
+ return (ENXIO);
+ }
+ ch->r_io[ATA_CONTROL].res = ctlio;
+ ch->r_io[ATA_CONTROL].offset = 0;
+ }
+ ata_default_registers(dev);
+
+ /* initialize softc for this channel */
+ ch->unit = 0;
+ ch->flags |= (ATA_USE_16BIT | ATA_NO_SLAVE);
+ ata_generic_hw(dev);
+ err = ata_probe(dev);
+ if (err)
+ return (err);
+ return (ata_attach(dev));
+}
+
+static int
+ata_pccard_detach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ int i;
+
+ ata_detach(dev);
+ if (ch->r_io[ATA_CONTROL].res != ch->r_io[ATA_DATA].res)
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_CTLADDR_RID,
+ ch->r_io[ATA_CONTROL].res);
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID,
+ ch->r_io[ATA_DATA].res);
+ for (i = ATA_DATA; i < ATA_MAX_RES; i++)
+ ch->r_io[i].res = NULL;
+ return 0;
+}
+
+static device_method_t ata_pccard_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_pccard_probe),
+ DEVMETHOD(device_attach, ata_pccard_attach),
+ DEVMETHOD(device_detach, ata_pccard_detach),
+
+ { 0, 0 }
+};
+
+static driver_t ata_pccard_driver = {
+ "ata",
+ ata_pccard_methods,
+ sizeof(struct ata_channel),
+};
+
+DRIVER_MODULE(ata, pccard, ata_pccard_driver, ata_devclass, 0, 0);
+MODULE_DEPEND(ata, ata, 1, 1, 1);
diff --git a/sys/dev/ata/ata-cbus.c b/sys/dev/ata/ata-cbus.c
new file mode 100644
index 000000000000..0eaee5ba060c
--- /dev/null
+++ b/sys/dev/ata/ata-cbus.c
@@ -0,0 +1,349 @@
+/*-
+ * Copyright (c) 2002 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/ata.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <isa/isavar.h>
+#include <dev/ata/ata-all.h>
+#include <ata_if.h>
+
+/* local vars */
+struct ata_cbus_controller {
+ struct resource *io;
+ struct resource *ctlio;
+ struct resource *bankio;
+ struct resource *irq;
+ void *ih;
+ struct mtx bank_mtx;
+ int locked_bank;
+ int restart_bank;
+ int hardware_bank;
+ struct {
+ void (*function)(void *);
+ void *argument;
+ } interrupt[2];
+};
+
+/* local prototypes */
+static void ata_cbus_intr(void *);
+static int ata_cbuschannel_banking(device_t dev, int flags);
+
+static int
+ata_cbus_probe(device_t dev)
+{
+ struct resource *io;
+ int rid;
+ u_long tmp;
+
+ /* dont probe PnP devices */
+ if (isa_get_vendorid(dev))
+ return (ENXIO);
+
+ /* allocate the ioport range */
+ rid = ATA_IOADDR_RID;
+ if (!(io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
+ ATA_PC98_IOSIZE, RF_ACTIVE)))
+ return ENOMEM;
+
+ /* calculate & set the altport range */
+ rid = ATA_PC98_CTLADDR_RID;
+ if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &tmp, &tmp)) {
+ bus_set_resource(dev, SYS_RES_IOPORT, rid,
+ rman_get_start(io)+ATA_PC98_CTLOFFSET, ATA_CTLIOSIZE);
+ }
+
+ /* calculate & set the bank range */
+ rid = ATA_PC98_BANKADDR_RID;
+ if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &tmp, &tmp)) {
+ bus_set_resource(dev, SYS_RES_IOPORT, rid,
+ ATA_PC98_BANK, ATA_PC98_BANKIOSIZE);
+ }
+
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, io);
+ return 0;
+}
+
+static int
+ata_cbus_attach(device_t dev)
+{
+ struct ata_cbus_controller *ctlr = device_get_softc(dev);
+ int rid;
+
+ /* allocate resources */
+ rid = ATA_IOADDR_RID;
+ if (!(ctlr->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
+ ATA_PC98_IOSIZE, RF_ACTIVE)))
+ return ENOMEM;
+
+ rid = ATA_PC98_CTLADDR_RID;
+ if (!(ctlr->ctlio =
+ bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+ rman_get_start(ctlr->io) + ATA_PC98_CTLOFFSET, ~0,
+ ATA_CTLIOSIZE, RF_ACTIVE))) {
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ctlr->io);
+ return ENOMEM;
+ }
+
+ rid = ATA_PC98_BANKADDR_RID;
+ if (!(ctlr->bankio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+ ATA_PC98_BANK, ~0,
+ ATA_PC98_BANKIOSIZE, RF_ACTIVE))) {
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ctlr->io);
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_CTLADDR_RID, ctlr->ctlio);
+ return ENOMEM;
+ }
+
+ rid = ATA_IRQ_RID;
+ if (!(ctlr->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE))) {
+ device_printf(dev, "unable to alloc interrupt\n");
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ctlr->io);
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_CTLADDR_RID, ctlr->ctlio);
+ bus_release_resource(dev, SYS_RES_IOPORT,
+ ATA_PC98_BANKADDR_RID, ctlr->bankio);
+ return ENXIO;
+ }
+
+ if ((bus_setup_intr(dev, ctlr->irq, ATA_INTR_FLAGS,
+ ata_cbus_intr, ctlr, &ctlr->ih))) {
+ device_printf(dev, "unable to setup interrupt\n");
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ctlr->io);
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_CTLADDR_RID, ctlr->ctlio);
+ bus_release_resource(dev, SYS_RES_IOPORT,
+ ATA_PC98_BANKADDR_RID, ctlr->bankio);
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IRQ_RID, ctlr->irq);
+ return ENXIO;
+ }
+
+ mtx_init(&ctlr->bank_mtx, "ATA cbus bank lock", NULL, MTX_DEF);
+ ctlr->hardware_bank = -1;
+ ctlr->locked_bank = -1;
+ ctlr->restart_bank = -1;
+
+ if (!device_add_child(dev, "ata", 0))
+ return ENOMEM;
+ if (!device_add_child(dev, "ata", 1))
+ return ENOMEM;
+
+ return bus_generic_attach(dev);
+}
+
+static struct resource *
+ata_cbus_alloc_resource(device_t dev, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct ata_cbus_controller *ctlr = device_get_softc(dev);
+
+ if (type == SYS_RES_IOPORT) {
+ switch (*rid) {
+ case ATA_IOADDR_RID:
+ return ctlr->io;
+ case ATA_CTLADDR_RID:
+ return ctlr->ctlio;
+ }
+ }
+ if (type == SYS_RES_IRQ)
+ return ctlr->irq;
+ return 0;
+}
+
+static int
+ata_cbus_setup_intr(device_t dev, device_t child, struct resource *irq,
+ int flags, driver_intr_t *intr, void *arg,
+ void **cookiep)
+{
+ struct ata_cbus_controller *controller = device_get_softc(dev);
+ int unit = ((struct ata_channel *)device_get_softc(child))->unit;
+
+ controller->interrupt[unit].function = intr;
+ controller->interrupt[unit].argument = arg;
+ *cookiep = controller;
+
+ return 0;
+}
+
+static int
+ata_cbus_print_child(device_t dev, device_t child)
+{
+ struct ata_channel *ch = device_get_softc(child);
+ int retval = 0;
+
+ retval += bus_print_child_header(dev, child);
+ retval += printf(" at bank %d", ch->unit);
+ retval += bus_print_child_footer(dev, child);
+ return retval;
+}
+
+static void
+ata_cbus_intr(void *data)
+{
+ struct ata_cbus_controller *ctlr = data;
+ struct ata_channel *ch;
+ int unit;
+
+ for (unit = 0; unit < 2; unit++) {
+ if (!(ch = ctlr->interrupt[unit].argument))
+ continue;
+ if (ata_cbuschannel_banking(ch->dev, ATA_LF_WHICH) == unit)
+ ctlr->interrupt[unit].function(ch);
+ }
+}
+
+static device_method_t ata_cbus_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_cbus_probe),
+ DEVMETHOD(device_attach, ata_cbus_attach),
+// DEVMETHOD(device_detach, ata_cbus_detach),
+
+ /* bus methods */
+ DEVMETHOD(bus_alloc_resource, ata_cbus_alloc_resource),
+ DEVMETHOD(bus_setup_intr, ata_cbus_setup_intr),
+ DEVMETHOD(bus_print_child, ata_cbus_print_child),
+
+ { 0, 0 }
+};
+
+static driver_t ata_cbus_driver = {
+ "atacbus",
+ ata_cbus_methods,
+ sizeof(struct ata_cbus_controller),
+};
+
+static devclass_t ata_cbus_devclass;
+
+DRIVER_MODULE(atacbus, isa, ata_cbus_driver, ata_cbus_devclass, 0, 0);
+
+static int
+ata_cbuschannel_probe(device_t dev)
+{
+ struct ata_cbus_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ device_t *children;
+ int count, i;
+
+ /* find channel number on this controller */
+ device_get_children(device_get_parent(dev), &children, &count);
+ for (i = 0; i < count; i++) {
+ if (children[i] == dev)
+ ch->unit = i;
+ }
+ free(children, M_TEMP);
+
+ /* setup the resource vectors */
+ for (i = ATA_DATA; i <= ATA_COMMAND; i ++) {
+ ch->r_io[i].res = ctlr->io;
+ ch->r_io[i].offset = i << 1;
+ }
+ ch->r_io[ATA_CONTROL].res = ctlr->ctlio;
+ ch->r_io[ATA_CONTROL].offset = 0;
+ ch->r_io[ATA_IDX_ADDR].res = ctlr->io;
+ ata_default_registers(dev);
+
+ /* initialize softc for this channel */
+ ch->flags |= ATA_USE_16BIT;
+ ata_generic_hw(dev);
+ return ata_probe(dev);
+}
+
+static int
+ata_cbuschannel_banking(device_t dev, int flags)
+{
+ struct ata_cbus_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int res;
+
+ mtx_lock(&ctlr->bank_mtx);
+ switch (flags) {
+ case ATA_LF_LOCK:
+ if (ctlr->locked_bank == -1)
+ ctlr->locked_bank = ch->unit;
+ if (ctlr->locked_bank == ch->unit) {
+ ctlr->hardware_bank = ch->unit;
+ ATA_OUTB(ctlr->bankio, 0, ch->unit);
+ }
+ else
+ ctlr->restart_bank = ch->unit;
+ break;
+
+ case ATA_LF_UNLOCK:
+ if (ctlr->locked_bank == ch->unit) {
+ ctlr->locked_bank = -1;
+ if (ctlr->restart_bank != -1) {
+ if ((ch = ctlr->interrupt[ctlr->restart_bank].argument)) {
+ ctlr->restart_bank = -1;
+ mtx_unlock(&ctlr->bank_mtx);
+ ata_start(ch->dev);
+ return -1;
+ }
+ }
+ }
+ break;
+
+ case ATA_LF_WHICH:
+ break;
+ }
+ res = ctlr->locked_bank;
+ mtx_unlock(&ctlr->bank_mtx);
+ return res;
+}
+
+static device_method_t ata_cbuschannel_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_cbuschannel_probe),
+ DEVMETHOD(device_attach, ata_attach),
+ DEVMETHOD(device_detach, ata_detach),
+ DEVMETHOD(device_suspend, ata_suspend),
+ DEVMETHOD(device_resume, ata_resume),
+
+ /* ATA methods */
+ DEVMETHOD(ata_locking, ata_cbuschannel_banking),
+ { 0, 0 }
+};
+
+static driver_t ata_cbuschannel_driver = {
+ "ata",
+ ata_cbuschannel_methods,
+ sizeof(struct ata_channel),
+};
+
+DRIVER_MODULE(ata, atacbus, ata_cbuschannel_driver, ata_devclass, 0, 0);
+MODULE_DEPEND(ata, ata, 1, 1, 1);
diff --git a/sys/dev/ata/ata-chipset.c b/sys/dev/ata/ata-chipset.c
new file mode 100644
index 000000000000..44a071045107
--- /dev/null
+++ b/sys/dev/ata/ata-chipset.c
@@ -0,0 +1,5149 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/ata.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/stdarg.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/ata-pci.h>
+#include <ata_if.h>
+
+/* local prototypes */
+/* ata-chipset.c */
+static int ata_generic_chipinit(device_t dev);
+static void ata_generic_intr(void *data);
+static void ata_generic_setmode(device_t dev, int mode);
+static void ata_sata_phy_enable(struct ata_channel *ch);
+static void ata_sata_phy_event(void *context, int dummy);
+static int ata_sata_connect(struct ata_channel *ch);
+static void ata_sata_setmode(device_t dev, int mode);
+static int ata_ahci_chipinit(device_t dev);
+static int ata_ahci_allocate(device_t dev);
+static int ata_ahci_status(device_t dev);
+static int ata_ahci_begin_transaction(struct ata_request *request);
+static int ata_ahci_end_transaction(struct ata_request *request);
+static void ata_ahci_reset(device_t dev);
+static void ata_ahci_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
+static void ata_ahci_dmainit(device_t dev);
+static int ata_ahci_setup_fis(u_int8_t *fis, struct ata_request *request);
+static int ata_acard_chipinit(device_t dev);
+static int ata_acard_allocate(device_t dev);
+static int ata_acard_status(device_t dev);
+static void ata_acard_850_setmode(device_t dev, int mode);
+static void ata_acard_86X_setmode(device_t dev, int mode);
+static int ata_ali_chipinit(device_t dev);
+static int ata_ali_allocate(device_t dev);
+static int ata_ali_sata_allocate(device_t dev);
+static void ata_ali_reset(device_t dev);
+static void ata_ali_setmode(device_t dev, int mode);
+static int ata_amd_chipinit(device_t dev);
+static int ata_ati_chipinit(device_t dev);
+static void ata_ati_setmode(device_t dev, int mode);
+static int ata_cyrix_chipinit(device_t dev);
+static void ata_cyrix_setmode(device_t dev, int mode);
+static int ata_cypress_chipinit(device_t dev);
+static void ata_cypress_setmode(device_t dev, int mode);
+static int ata_highpoint_chipinit(device_t dev);
+static int ata_highpoint_allocate(device_t dev);
+static void ata_highpoint_setmode(device_t dev, int mode);
+static int ata_highpoint_check_80pin(device_t dev, int mode);
+static int ata_intel_chipinit(device_t dev);
+static int ata_intel_allocate(device_t dev);
+static void ata_intel_reset(device_t dev);
+static void ata_intel_old_setmode(device_t dev, int mode);
+static void ata_intel_new_setmode(device_t dev, int mode);
+static int ata_intel_31244_allocate(device_t dev);
+static int ata_intel_31244_status(device_t dev);
+static int ata_intel_31244_command(struct ata_request *request);
+static void ata_intel_31244_reset(device_t dev);
+static int ata_ite_chipinit(device_t dev);
+static void ata_ite_setmode(device_t dev, int mode);
+static int ata_jmicron_chipinit(device_t dev);
+static int ata_jmicron_allocate(device_t dev);
+static void ata_jmicron_reset(device_t dev);
+static void ata_jmicron_dmainit(device_t dev);
+static void ata_jmicron_setmode(device_t dev, int mode);
+static int ata_marvell_chipinit(device_t dev);
+static int ata_marvell_allocate(device_t dev);
+static int ata_marvell_status(device_t dev);
+static int ata_marvell_begin_transaction(struct ata_request *request);
+static int ata_marvell_end_transaction(struct ata_request *request);
+static void ata_marvell_reset(device_t dev);
+static void ata_marvell_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
+static void ata_marvell_dmainit(device_t dev);
+static int ata_national_chipinit(device_t dev);
+static void ata_national_setmode(device_t dev, int mode);
+static int ata_nvidia_chipinit(device_t dev);
+static int ata_nvidia_allocate(device_t dev);
+static int ata_nvidia_status(device_t dev);
+static void ata_nvidia_reset(device_t dev);
+static int ata_promise_chipinit(device_t dev);
+static int ata_promise_allocate(device_t dev);
+static int ata_promise_status(device_t dev);
+static int ata_promise_dmastart(device_t dev);
+static int ata_promise_dmastop(device_t dev);
+static void ata_promise_dmareset(device_t dev);
+static void ata_promise_dmainit(device_t dev);
+static void ata_promise_setmode(device_t dev, int mode);
+static int ata_promise_tx2_allocate(device_t dev);
+static int ata_promise_tx2_status(device_t dev);
+static int ata_promise_mio_allocate(device_t dev);
+static void ata_promise_mio_intr(void *data);
+static int ata_promise_mio_status(device_t dev);
+static int ata_promise_mio_command(struct ata_request *request);
+static void ata_promise_mio_reset(device_t dev);
+static void ata_promise_mio_dmainit(device_t dev);
+static void ata_promise_mio_setmode(device_t dev, int mode);
+static void ata_promise_sx4_intr(void *data);
+static int ata_promise_sx4_command(struct ata_request *request);
+static int ata_promise_apkt(u_int8_t *bytep, struct ata_request *request);
+static void ata_promise_queue_hpkt(struct ata_pci_controller *ctlr, u_int32_t hpkt);
+static void ata_promise_next_hpkt(struct ata_pci_controller *ctlr);
+static int ata_serverworks_chipinit(device_t dev);
+static int ata_serverworks_allocate(device_t dev);
+static void ata_serverworks_setmode(device_t dev, int mode);
+static int ata_sii_chipinit(device_t dev);
+static int ata_cmd_allocate(device_t dev);
+static int ata_cmd_status(device_t dev);
+static void ata_cmd_setmode(device_t dev, int mode);
+static int ata_sii_allocate(device_t dev);
+static int ata_sii_status(device_t dev);
+static void ata_sii_reset(device_t dev);
+static void ata_sii_setmode(device_t dev, int mode);
+static int ata_sis_chipinit(device_t dev);
+static int ata_sis_allocate(device_t dev);
+static void ata_sis_reset(device_t dev);
+static void ata_sis_setmode(device_t dev, int mode);
+static int ata_via_chipinit(device_t dev);
+static int ata_via_allocate(device_t dev);
+static void ata_via_reset(device_t dev);
+static void ata_via_southbridge_fixup(device_t dev);
+static void ata_via_family_setmode(device_t dev, int mode);
+static struct ata_chip_id *ata_match_chip(device_t dev, struct ata_chip_id *index);
+static struct ata_chip_id *ata_find_chip(device_t dev, struct ata_chip_id *index, int slot);
+static int ata_setup_interrupt(device_t dev);
+static int ata_serialize(device_t dev, int flags);
+static void ata_print_cable(device_t dev, u_int8_t *who);
+static int ata_atapi(device_t dev);
+static int ata_check_80pin(device_t dev, int mode);
+static int ata_mode2idx(int mode);
+
+
+/*
+ * generic ATA support functions
+ */
+int
+ata_generic_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ device_set_desc(dev, "GENERIC ATA controller");
+ ctlr->chipinit = ata_generic_chipinit;
+ return 0;
+}
+
+static int
+ata_generic_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+ ctlr->setmode = ata_generic_setmode;
+ return 0;
+}
+
+static void
+ata_generic_intr(void *data)
+{
+ struct ata_pci_controller *ctlr = data;
+ struct ata_channel *ch;
+ int unit;
+
+ for (unit = 0; unit < ctlr->channels; unit++) {
+ if ((ch = ctlr->interrupt[unit].argument))
+ ctlr->interrupt[unit].function(ch);
+ }
+}
+
+static void
+ata_generic_setmode(device_t dev, int mode)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ mode = ata_limit_mode(dev, mode, ATA_UDMA2);
+ mode = ata_check_80pin(dev, mode);
+ if (!ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode))
+ atadev->mode = mode;
+}
+
+
+/*
+ * SATA support functions
+ */
+static void
+ata_sata_phy_enable(struct ata_channel *ch)
+{
+ int loop, retry;
+
+ if ((ATA_IDX_INL(ch, ATA_SCONTROL) & ATA_SC_DET_MASK) == ATA_SC_DET_IDLE) {
+ ata_sata_connect(ch);
+ return;
+ }
+
+ for (retry = 0; retry < 10; retry++) {
+ for (loop = 0; loop < 10; loop++) {
+ ATA_IDX_OUTL(ch, ATA_SCONTROL, ATA_SC_DET_RESET);
+ ata_udelay(100);
+ if ((ATA_IDX_INL(ch, ATA_SCONTROL) &
+ ATA_SC_DET_MASK) == ATA_SC_DET_RESET)
+ break;
+ }
+ ata_udelay(5000);
+ for (loop = 0; loop < 10; loop++) {
+ ATA_IDX_OUTL(ch, ATA_SCONTROL, ATA_SC_DET_IDLE |
+ ATA_SC_IPM_DIS_PARTIAL |
+ ATA_SC_IPM_DIS_SLUMBER);
+ ata_udelay(100);
+ if ((ATA_IDX_INL(ch, ATA_SCONTROL) & ATA_SC_DET_MASK) == 0) {
+ ata_sata_connect(ch);
+ return;
+ }
+ }
+ }
+}
+
+static void
+ata_sata_phy_event(void *context, int dummy)
+{
+ struct ata_connect_task *tp = (struct ata_connect_task *)context;
+ struct ata_channel *ch = device_get_softc(tp->dev);
+ device_t *children;
+ int nchildren, i;
+
+ mtx_lock(&Giant); /* newbus suckage it needs Giant */
+ if (tp->action == ATA_C_ATTACH) {
+ if (bootverbose)
+ device_printf(tp->dev, "CONNECTED\n");
+ ata_sata_connect(ch);
+ ata_identify(tp->dev);
+ }
+ if (tp->action == ATA_C_DETACH) {
+ if (!device_get_children(tp->dev, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ if (children[i])
+ device_delete_child(tp->dev, children[i]);
+ free(children, M_TEMP);
+ }
+ mtx_lock(&ch->state_mtx);
+ ch->state = ATA_IDLE;
+ mtx_unlock(&ch->state_mtx);
+ if (bootverbose)
+ device_printf(tp->dev, "DISCONNECTED\n");
+ }
+ mtx_unlock(&Giant); /* suckage code dealt with, release Giant */
+ free(tp, M_ATA);
+}
+
+static int
+ata_sata_connect(struct ata_channel *ch)
+{
+ u_int32_t status;
+ int timeout;
+
+ /* wait up to 1 second for "connect well" */
+ for (timeout = 0; timeout < 100 ; timeout++) {
+ status = ATA_IDX_INL(ch, ATA_SSTATUS);
+ if ((status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN1 ||
+ (status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN2)
+ break;
+ ata_udelay(10000);
+ }
+ if (timeout >= 100) {
+ if (bootverbose)
+ device_printf(ch->dev, "SATA connect status=%08x\n", status);
+ return 0;
+ }
+
+ /* clear SATA error register */
+ ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
+
+ /* find out what type device we got poll for spec'd 31 seconds */
+ /* XXX SOS 10 secs for now as I have little patience */
+ ch->devices = 0;
+ for (timeout = 0; timeout < 1000; timeout++) {
+ if (ATA_IDX_INB(ch, ATA_STATUS) & ATA_S_BUSY)
+ DELAY(10000);
+ else
+ break;
+ }
+ if (bootverbose)
+ device_printf(ch->dev, "SATA connect ready time=%dms\n", timeout * 10);
+ if (timeout < 1000) {
+ if ((ATA_IDX_INB(ch, ATA_CYL_LSB) == ATAPI_MAGIC_LSB) &&
+ (ATA_IDX_INB(ch, ATA_CYL_MSB) == ATAPI_MAGIC_MSB))
+ ch->devices = ATA_ATAPI_MASTER;
+ else
+ ch->devices = ATA_ATA_MASTER;
+ }
+ if (bootverbose)
+ device_printf(ch->dev, "sata_connect devices=0x%b\n",
+ ch->devices, "\20\3ATAPI_MASTER\1ATA_MASTER");
+ return 1;
+}
+
+static void
+ata_sata_setmode(device_t dev, int mode)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ /*
+ * if we detect that the device isn't a real SATA device we limit
+ * the transfer mode to UDMA5/ATA100.
+ * this works around the problems some devices has with the
+ * Marvell 88SX8030 SATA->PATA converters and UDMA6/ATA133.
+ */
+ if (atadev->param.satacapabilities != 0x0000 &&
+ atadev->param.satacapabilities != 0xffff) {
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+
+ /* on some drives we need to set the transfer mode */
+ ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0,
+ ata_limit_mode(dev, mode, ATA_UDMA6));
+
+ /* query SATA STATUS for the speed */
+ if (ch->r_io[ATA_SSTATUS].res &&
+ ((ATA_IDX_INL(ch, ATA_SSTATUS) & ATA_SS_CONWELL_MASK) ==
+ ATA_SS_CONWELL_GEN2))
+ atadev->mode = ATA_SA300;
+ else
+ atadev->mode = ATA_SA150;
+ }
+ else {
+ mode = ata_limit_mode(dev, mode, ATA_UDMA5);
+ if (!ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode))
+ atadev->mode = mode;
+ }
+}
+
+
+/*
+ * AHCI v1.0 compliant SATA chipset support functions
+ */
+struct ata_ahci_dma_prd {
+ u_int64_t dba;
+ u_int32_t reserved;
+ u_int32_t dbc; /* 0 based */
+#define ATA_AHCI_PRD_MASK 0x003fffff /* max 4MB */
+#define ATA_AHCI_PRD_IPC (1<<31)
+} __packed;
+
+struct ata_ahci_cmd_tab {
+ u_int8_t cfis[64];
+ u_int8_t acmd[32];
+ u_int8_t reserved[32];
+ struct ata_ahci_dma_prd prd_tab[16];
+} __packed;
+
+struct ata_ahci_cmd_list {
+ u_int16_t cmd_flags;
+ u_int16_t prd_length; /* PRD entries */
+ u_int32_t bytecount;
+ u_int64_t cmd_table_phys; /* 128byte aligned */
+} __packed;
+
+
+static int
+ata_ahci_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ u_int32_t version;
+
+ /* reset AHCI controller */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC,
+ ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) | ATA_AHCI_GHC_HR);
+ DELAY(1000000);
+ if (ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) & ATA_AHCI_GHC_HR) {
+ bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2);
+ device_printf(dev, "AHCI controller reset failure\n");
+ return ENXIO;
+ }
+
+ /* enable AHCI mode */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC,
+ ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) | ATA_AHCI_GHC_AE);
+
+ /* get the number of HW channels */
+ ctlr->channels = (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_NPMASK)+1;
+
+ /* clear interrupts */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, ATA_INL(ctlr->r_res2, ATA_AHCI_IS));
+
+ /* enable AHCI interrupts */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_GHC,
+ ATA_INL(ctlr->r_res2, ATA_AHCI_GHC) | ATA_AHCI_GHC_IE);
+
+ ctlr->reset = ata_ahci_reset;
+ ctlr->dmainit = ata_ahci_dmainit;
+ ctlr->allocate = ata_ahci_allocate;
+ ctlr->setmode = ata_sata_setmode;
+
+ /* enable PCI interrupt */
+ pci_write_config(dev, PCIR_COMMAND,
+ pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400, 2);
+
+ /* announce we support the HW */
+ version = ATA_INL(ctlr->r_res2, ATA_AHCI_VS);
+ device_printf(dev,
+ "AHCI Version %x%x.%x%x controller with %d ports detected\n",
+ (version >> 24) & 0xff, (version >> 16) & 0xff,
+ (version >> 8) & 0xff, version & 0xff, ctlr->channels);
+ return 0;
+}
+
+static int
+ata_ahci_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int offset = (ch->unit << 7);
+
+ /* setup legacy cruft we need */
+ ch->r_io[ATA_CYL_LSB].res = ctlr->r_res2;
+ ch->r_io[ATA_CYL_LSB].offset = ATA_AHCI_P_SIG + 1 + offset;
+ ch->r_io[ATA_CYL_MSB].res = ctlr->r_res2;
+ ch->r_io[ATA_CYL_MSB].offset = ATA_AHCI_P_SIG + 3 + offset;
+ ch->r_io[ATA_STATUS].res = ctlr->r_res2;
+ ch->r_io[ATA_STATUS].offset = ATA_AHCI_P_TFD + offset;
+ ch->r_io[ATA_ALTSTAT].res = ctlr->r_res2;
+ ch->r_io[ATA_ALTSTAT].offset = ATA_AHCI_P_TFD + offset;
+
+ /* set the SATA resources */
+ ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
+ ch->r_io[ATA_SSTATUS].offset = ATA_AHCI_P_SSTS + offset;
+ ch->r_io[ATA_SERROR].res = ctlr->r_res2;
+ ch->r_io[ATA_SERROR].offset = ATA_AHCI_P_SERR + offset;
+ ch->r_io[ATA_SCONTROL].res = ctlr->r_res2;
+ ch->r_io[ATA_SCONTROL].offset = ATA_AHCI_P_SCTL + offset;
+ ch->r_io[ATA_SACTIVE].res = ctlr->r_res2;
+ ch->r_io[ATA_SACTIVE].offset = ATA_AHCI_P_SACT + offset;
+
+ ch->hw.status = ata_ahci_status;
+ ch->hw.begin_transaction = ata_ahci_begin_transaction;
+ ch->hw.end_transaction = ata_ahci_end_transaction;
+ ch->hw.command = NULL; /* not used here */
+
+ /* setup the work areas */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset,
+ ch->dma->work_bus + ATA_AHCI_CL_OFFSET);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, 0x00000000);
+
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset,
+ ch->dma->work_bus + ATA_AHCI_FB_OFFSET);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBU + offset, 0x00000000);
+
+ /* enable wanted port interrupts */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset,
+ (ATA_AHCI_P_IX_CPD | ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |
+ ATA_AHCI_P_IX_HBD | ATA_AHCI_P_IX_IF | ATA_AHCI_P_IX_OF |
+ ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC | ATA_AHCI_P_IX_DP |
+ ATA_AHCI_P_IX_UF | ATA_AHCI_P_IX_SDB | ATA_AHCI_P_IX_DS |
+ ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR));
+
+ /* start operations on this channel */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+ (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
+ ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
+ return 0;
+}
+
+static int
+ata_ahci_status(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ struct ata_connect_task *tp;
+ u_int32_t action, istatus, sstatus, error, issued;
+ int offset = (ch->unit << 7);
+ int tag = 0;
+
+ action = ATA_INL(ctlr->r_res2, ATA_AHCI_IS);
+ if (action & (1 << ch->unit)) {
+ istatus = ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset);
+ issued = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CI + offset);
+ sstatus = ATA_INL(ctlr->r_res2, ATA_AHCI_P_SSTS + offset);
+ error = ATA_INL(ctlr->r_res2, ATA_AHCI_P_SERR + offset);
+
+ /* clear interrupt(s) */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_IS, action);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset, istatus);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_SERR + offset, error);
+
+ /* do we have cold connect surprise */
+ if (istatus & ATA_AHCI_P_IX_CPD) {
+ printf("ata_ahci_status status=%08x sstatus=%08x error=%08x\n",
+ istatus, sstatus, error);
+ }
+
+ /* check for and handle connect events */
+ if ((istatus & ATA_AHCI_P_IX_PC) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ if (bootverbose)
+ device_printf(ch->dev, "CONNECT requested\n");
+ tp->action = ATA_C_ATTACH;
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+
+ /* check for and handle disconnect events */
+ else if ((istatus & ATA_AHCI_P_IX_PRC) &&
+ !((sstatus & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN1 ||
+ (sstatus & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN2) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ if (bootverbose)
+ device_printf(ch->dev, "DISCONNECT requested\n");
+ tp->action = ATA_C_DETACH;
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+
+ /* do we have any device action ? */
+ if (!(issued & (1 << tag)))
+ return 1;
+ }
+ return 0;
+}
+
+/* must be called with ATA channel locked and state_mtx held */
+static int
+ata_ahci_begin_transaction(struct ata_request *request)
+{
+ struct ata_pci_controller *ctlr=device_get_softc(GRANDPARENT(request->dev));
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ struct ata_ahci_cmd_tab *ctp;
+ struct ata_ahci_cmd_list *clp;
+ int tag = 0, entries = 0;
+ int fis_size;
+
+ /* get a piece of the workspace for this request */
+ ctp = (struct ata_ahci_cmd_tab *)
+ (ch->dma->work + ATA_AHCI_CT_OFFSET + (ATA_AHCI_CT_SIZE * tag));
+
+ /* setup the FIS for this request */ /* XXX SOS ATAPI missing still */
+ if (!(fis_size = ata_ahci_setup_fis(&ctp->cfis[0], request))) {
+ device_printf(request->dev, "setting up SATA FIS failed\n");
+ request->result = EIO;
+ return ATA_OP_FINISHED;
+ }
+
+ /* if request moves data setup and load SG list */
+ if (request->flags & (ATA_R_READ | ATA_R_WRITE)) {
+ if (ch->dma->load(ch->dev, request->data, request->bytecount,
+ request->flags & ATA_R_READ,
+ ctp->prd_tab, &entries)) {
+ device_printf(request->dev, "setting up DMA failed\n");
+ request->result = EIO;
+ return ATA_OP_FINISHED;
+ }
+ }
+
+ /* setup the command list entry */
+ clp = (struct ata_ahci_cmd_list *)
+ (ch->dma->work + ATA_AHCI_CL_OFFSET + (ATA_AHCI_CL_SIZE * tag));
+
+ clp->prd_length = entries;
+ clp->cmd_flags = (request->flags & ATA_R_WRITE ? (1<<6) : 0) |
+ (request->flags & ATA_R_ATAPI ? (1<<5) : 0) |
+ (fis_size / sizeof(u_int32_t));
+ clp->bytecount = 0;
+ clp->cmd_table_phys = htole64(ch->dma->work_bus + ATA_AHCI_CT_OFFSET +
+ (ATA_AHCI_CT_SIZE * tag));
+
+ /* clear eventual ACTIVE bit */
+ ATA_IDX_OUTL(ch, ATA_SACTIVE, ATA_IDX_INL(ch, ATA_SACTIVE) & (1 << tag));
+
+ /* issue the command */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CI + (ch->unit << 7), (1 << tag));
+
+ /* start the timeout */
+ callout_reset(&request->callout, request->timeout * hz,
+ (timeout_t*)ata_timeout, request);
+ return ATA_OP_CONTINUES;
+}
+
+/* must be called with ATA channel locked and state_mtx held */
+static int
+ata_ahci_end_transaction(struct ata_request *request)
+{
+ struct ata_pci_controller *ctlr=device_get_softc(GRANDPARENT(request->dev));
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ struct ata_ahci_cmd_list *clp;
+ u_int32_t tf_data;
+ int tag = 0;
+
+ /* kill the timeout */
+ callout_stop(&request->callout);
+
+ /* get status */
+ tf_data = ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + (ch->unit << 7));
+ request->status = tf_data;
+
+ /* if error status get details */
+ if (request->status & ATA_S_ERROR)
+ request->error = tf_data >> 8;
+
+ /* record how much data we actually moved */
+ clp = (struct ata_ahci_cmd_list *)
+ (ch->dma->work + ATA_AHCI_CL_OFFSET + (ATA_AHCI_CL_SIZE * tag));
+ request->donecount = clp->bytecount;
+
+ /* release SG list etc */
+ ch->dma->unload(ch->dev);
+
+ return ATA_OP_FINISHED;
+}
+
+static void
+ata_ahci_reset(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ u_int32_t cmd;
+ int timeout, offset = (ch->unit << 7);
+
+ /* kill off all activity on this channel */
+ cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+ cmd & ~(ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
+
+ /* XXX SOS this is not entirely wrong */
+ timeout = 0;
+ do {
+ DELAY(1000);
+ if (timeout++ > 500)
+ device_printf(dev, "stopping AHCI engine failed\n");
+ break;
+ }
+ while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_CR);
+
+ /* issue Command List Override if supported */
+ if (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_CLO) {
+ cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
+ cmd |= ATA_AHCI_P_CMD_CLO;
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, cmd);
+ timeout = 0;
+ do {
+ DELAY(1000);
+ if (timeout++ > 500)
+ device_printf(dev, "executing CLO failed\n");
+ break;
+ }
+ while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD+offset)&ATA_AHCI_P_CMD_CLO);
+ }
+
+ /* spin up device */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, ATA_AHCI_P_CMD_SUD);
+
+ ata_sata_phy_enable(ch);
+
+ /* clear any interrupts pending on this channel */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset,
+ ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
+
+ /* start operations on this channel */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+ (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_FRE |
+ ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD | ATA_AHCI_P_CMD_ST));
+}
+
+static void
+ata_ahci_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct ata_dmasetprd_args *args = xsc;
+ struct ata_ahci_dma_prd *prd = args->dmatab;
+ int i;
+
+ if (!(args->error = error)) {
+ for (i = 0; i < nsegs; i++) {
+ prd[i].dba = htole64(segs[i].ds_addr);
+ prd[i].dbc = htole32((segs[i].ds_len - 1) & ATA_AHCI_PRD_MASK);
+ }
+ }
+ args->nsegs = nsegs;
+}
+
+static void
+ata_ahci_dmainit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ata_dmainit(dev);
+ if (ch->dma) {
+ /* note start and stop are not used here */
+ ch->dma->setprd = ata_ahci_dmasetprd;
+ ch->dma->max_iosize = 8192 * DEV_BSIZE;
+ }
+}
+
+static int
+ata_ahci_setup_fis(u_int8_t *fis, struct ata_request *request)
+{
+ struct ata_device *atadev = device_get_softc(request->dev);
+ int idx = 0;
+
+ /* XXX SOS add ATAPI commands support later */
+ ata_modify_if_48bit(request);
+
+ fis[idx++] = 0x27; /* host to device */
+ fis[idx++] = 0x80; /* command FIS (note PM goes here) */
+ fis[idx++] = request->u.ata.command;
+ fis[idx++] = request->u.ata.feature;
+
+ fis[idx++] = request->u.ata.lba;
+ fis[idx++] = request->u.ata.lba >> 8;
+ fis[idx++] = request->u.ata.lba >> 16;
+ fis[idx] = ATA_D_LBA | atadev->unit;
+ if (atadev->flags & ATA_D_48BIT_ACTIVE)
+ idx++;
+ else
+ fis[idx++] |= (request->u.ata.lba >> 24 & 0x0f);
+
+ fis[idx++] = request->u.ata.lba >> 24;
+ fis[idx++] = request->u.ata.lba >> 32;
+ fis[idx++] = request->u.ata.lba >> 40;
+ fis[idx++] = request->u.ata.feature >> 8;
+
+ fis[idx++] = request->u.ata.count;
+ fis[idx++] = request->u.ata.count >> 8;
+ fis[idx++] = 0x00;
+ fis[idx++] = ATA_A_4BIT;
+
+ fis[idx++] = 0x00;
+ fis[idx++] = 0x00;
+ fis[idx++] = 0x00;
+ fis[idx++] = 0x00;
+ return idx;
+}
+
+
+/*
+ * Acard chipset support functions
+ */
+int
+ata_acard_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_ATP850R, 0, ATPOLD, 0x00, ATA_UDMA2, "ATP850" },
+ { ATA_ATP860A, 0, 0, 0x00, ATA_UDMA4, "ATP860A" },
+ { ATA_ATP860R, 0, 0, 0x00, ATA_UDMA4, "ATP860R" },
+ { ATA_ATP865A, 0, 0, 0x00, ATA_UDMA6, "ATP865A" },
+ { ATA_ATP865R, 0, 0, 0x00, ATA_UDMA6, "ATP865R" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ sprintf(buffer, "Acard %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_acard_chipinit;
+ return 0;
+}
+
+static int
+ata_acard_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ ctlr->allocate = ata_acard_allocate;
+ if (ctlr->chip->cfg1 == ATPOLD) {
+ ctlr->setmode = ata_acard_850_setmode;
+ ctlr->locking = ata_serialize;
+ }
+ else
+ ctlr->setmode = ata_acard_86X_setmode;
+ return 0;
+}
+
+static int
+ata_acard_allocate(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* setup the usual register normal pci style */
+ if (ata_pci_allocate(dev))
+ return ENXIO;
+
+ ch->hw.status = ata_acard_status;
+ return 0;
+}
+
+static int
+ata_acard_status(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ctlr->chip->cfg1 == ATPOLD &&
+ ATA_LOCKING(ch->dev, ATA_LF_WHICH) != ch->unit)
+ return 0;
+ if (ch->dma && (ch->dma->flags & ATA_DMA_ACTIVE)) {
+ int bmstat = ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK;
+
+ if ((bmstat & (ATA_BMSTAT_ACTIVE | ATA_BMSTAT_INTERRUPT)) !=
+ ATA_BMSTAT_INTERRUPT)
+ return 0;
+ ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, bmstat & ~ATA_BMSTAT_ERROR);
+ DELAY(1);
+ ATA_IDX_OUTB(ch, ATA_BMCMD_PORT,
+ ATA_IDX_INB(ch, ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP);
+ DELAY(1);
+ }
+ if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
+ DELAY(100);
+ if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+ata_acard_850_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int error;
+
+ mode = ata_limit_mode(dev, mode,
+ ata_atapi(dev) ? ATA_PIO_MAX : ctlr->chip->max_dma);
+
+ /* XXX SOS missing WDMA0+1 + PIO modes */
+ if (mode >= ATA_WDMA2) {
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "",
+ ata_mode2str(mode), ctlr->chip->text);
+ if (!error) {
+ u_int8_t reg54 = pci_read_config(gparent, 0x54, 1);
+
+ reg54 &= ~(0x03 << (devno << 1));
+ if (mode >= ATA_UDMA0)
+ reg54 |= (((mode & ATA_MODE_MASK) + 1) << (devno << 1));
+ pci_write_config(gparent, 0x54, reg54, 1);
+ pci_write_config(gparent, 0x4a, 0xa6, 1);
+ pci_write_config(gparent, 0x40 + (devno << 1), 0x0301, 2);
+ atadev->mode = mode;
+ return;
+ }
+ }
+ /* we could set PIO mode timings, but we assume the BIOS did that */
+}
+
+static void
+ata_acard_86X_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int error;
+
+
+ mode = ata_limit_mode(dev, mode,
+ ata_atapi(dev) ? ATA_PIO_MAX : ctlr->chip->max_dma);
+
+ mode = ata_check_80pin(dev, mode);
+
+ /* XXX SOS missing WDMA0+1 + PIO modes */
+ if (mode >= ATA_WDMA2) {
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "",
+ ata_mode2str(mode), ctlr->chip->text);
+ if (!error) {
+ u_int16_t reg44 = pci_read_config(gparent, 0x44, 2);
+
+ reg44 &= ~(0x000f << (devno << 2));
+ if (mode >= ATA_UDMA0)
+ reg44 |= (((mode & ATA_MODE_MASK) + 1) << (devno << 2));
+ pci_write_config(gparent, 0x44, reg44, 2);
+ pci_write_config(gparent, 0x4a, 0xa6, 1);
+ pci_write_config(gparent, 0x40 + devno, 0x31, 1);
+ atadev->mode = mode;
+ return;
+ }
+ }
+ /* we could set PIO mode timings, but we assume the BIOS did that */
+}
+
+
+/*
+ * Acer Labs Inc (ALI) chipset support functions
+ */
+int
+ata_ali_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_ALI_5289, 0x00, 2, ALISATA, ATA_SA150, "M5289" },
+ { ATA_ALI_5288, 0x00, 4, ALISATA, ATA_SA300, "M5288" },
+ { ATA_ALI_5287, 0x00, 4, ALISATA, ATA_SA150, "M5287" },
+ { ATA_ALI_5281, 0x00, 2, ALISATA, ATA_SA150, "M5281" },
+ { ATA_ALI_5229, 0xc5, 0, ALINEW, ATA_UDMA6, "M5229" },
+ { ATA_ALI_5229, 0xc4, 0, ALINEW, ATA_UDMA5, "M5229" },
+ { ATA_ALI_5229, 0xc2, 0, ALINEW, ATA_UDMA4, "M5229" },
+ { ATA_ALI_5229, 0x20, 0, ALIOLD, ATA_UDMA2, "M5229" },
+ { ATA_ALI_5229, 0x00, 0, ALIOLD, ATA_WDMA2, "M5229" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ sprintf(buffer, "AcerLabs %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_ali_chipinit;
+ return 0;
+}
+
+static int
+ata_ali_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ switch (ctlr->chip->cfg2) {
+ case ALISATA:
+ ctlr->channels = ctlr->chip->cfg1;
+ ctlr->allocate = ata_ali_sata_allocate;
+ ctlr->setmode = ata_sata_setmode;
+
+ /* if we have a memory resource we can likely do AHCI */
+ ctlr->r_type2 = SYS_RES_MEMORY;
+ ctlr->r_rid2 = PCIR_BAR(5);
+ if ((ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2, RF_ACTIVE)))
+ return ata_ahci_chipinit(dev);
+
+ /* enable PCI interrupt */
+ pci_write_config(dev, PCIR_COMMAND,
+ pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400, 2);
+ break;
+
+ case ALINEW:
+ /* use device interrupt as byte count end */
+ pci_write_config(dev, 0x4a, pci_read_config(dev, 0x4a, 1) | 0x20, 1);
+
+ /* enable cable detection and UDMA support on newer chips */
+ pci_write_config(dev, 0x4b, pci_read_config(dev, 0x4b, 1) | 0x09, 1);
+
+ /* enable ATAPI UDMA mode */
+ pci_write_config(dev, 0x53, pci_read_config(dev, 0x53, 1) | 0x01, 1);
+
+ /* only chips with revision > 0xc4 can do 48bit DMA */
+ if (ctlr->chip->chiprev <= 0xc4)
+ device_printf(dev,
+ "using PIO transfers above 137GB as workaround for "
+ "48bit DMA access bug, expect reduced performance\n");
+ ctlr->allocate = ata_ali_allocate;
+ ctlr->reset = ata_ali_reset;
+ ctlr->setmode = ata_ali_setmode;
+ break;
+
+ case ALIOLD:
+ /* deactivate the ATAPI FIFO and enable ATAPI UDMA */
+ pci_write_config(dev, 0x53, pci_read_config(dev, 0x53, 1) | 0x03, 1);
+ ctlr->setmode = ata_ali_setmode;
+ break;
+ }
+ return 0;
+}
+
+static int
+ata_ali_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* setup the usual register normal pci style */
+ if (ata_pci_allocate(dev))
+ return ENXIO;
+
+ /* older chips can't do 48bit DMA transfers */
+ if (ctlr->chip->chiprev <= 0xc4)
+ ch->flags |= ATA_NO_48BIT_DMA;
+
+ return 0;
+}
+
+static int
+ata_ali_sata_allocate(device_t dev)
+{
+ device_t parent = device_get_parent(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(parent);
+ struct ata_channel *ch = device_get_softc(dev);
+ struct resource *io = NULL, *ctlio = NULL;
+ int unit01 = (ch->unit & 1), unit10 = (ch->unit & 2);
+ int i, rid;
+
+ rid = PCIR_BAR(0) + (unit01 ? 8 : 0);
+ io = bus_alloc_resource_any(parent, SYS_RES_IOPORT, &rid, RF_ACTIVE);
+ if (!io)
+ return ENXIO;
+
+ rid = PCIR_BAR(1) + (unit01 ? 8 : 0);
+ ctlio = bus_alloc_resource_any(parent, SYS_RES_IOPORT, &rid, RF_ACTIVE);
+ if (!ctlio) {
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, io);
+ return ENXIO;
+ }
+
+ for (i = ATA_DATA; i <= ATA_COMMAND; i ++) {
+ ch->r_io[i].res = io;
+ ch->r_io[i].offset = i + (unit10 ? 8 : 0);
+ }
+ ch->r_io[ATA_CONTROL].res = ctlio;
+ ch->r_io[ATA_CONTROL].offset = 2 + (unit10 ? 4 : 0);
+ ch->r_io[ATA_IDX_ADDR].res = io;
+ ata_default_registers(dev);
+ if (ctlr->r_res1) {
+ for (i = ATA_BMCMD_PORT; i <= ATA_BMDTP_PORT; i++) {
+ ch->r_io[i].res = ctlr->r_res1;
+ ch->r_io[i].offset = (i - ATA_BMCMD_PORT)+(ch->unit * ATA_BMIOSIZE);
+ }
+ }
+ ch->flags |= ATA_NO_SLAVE;
+
+ /* XXX SOS PHY handling awkward in ALI chip not supported yet */
+ ata_pci_hw(dev);
+ return 0;
+}
+
+static void
+ata_ali_reset(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ device_t *children;
+ int nchildren, i;
+
+ ata_generic_reset(dev);
+
+ /*
+ * workaround for datacorruption bug found on at least SUN Blade-100
+ * find the ISA function on the southbridge and disable then enable
+ * the ATA channel tristate buffer
+ */
+ if (ctlr->chip->chiprev == 0xc3 || ctlr->chip->chiprev == 0xc2) {
+ if (!device_get_children(GRANDPARENT(dev), &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++) {
+ if (pci_get_devid(children[i]) == ATA_ALI_1533) {
+ pci_write_config(children[i], 0x58,
+ pci_read_config(children[i], 0x58, 1) &
+ ~(0x04 << ch->unit), 1);
+ pci_write_config(children[i], 0x58,
+ pci_read_config(children[i], 0x58, 1) |
+ (0x04 << ch->unit), 1);
+ break;
+ }
+ }
+ free(children, M_TEMP);
+ }
+ }
+}
+
+static void
+ata_ali_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int error;
+
+ mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
+
+ if (ctlr->chip->cfg2 & ALINEW) {
+ if (mode > ATA_UDMA2 &&
+ pci_read_config(gparent, 0x4a, 1) & (1 << ch->unit)) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+ }
+ else
+ mode = ata_check_80pin(dev, mode);
+
+ if (ctlr->chip->cfg2 & ALIOLD) {
+ /* doesn't support ATAPI DMA on write */
+ ch->flags |= ATA_ATAPI_DMA_RO;
+ if (ch->devices & ATA_ATAPI_MASTER && ch->devices & ATA_ATAPI_SLAVE) {
+ /* doesn't support ATAPI DMA on two ATAPI devices */
+ device_printf(dev, "two atapi devices on this channel, no DMA\n");
+ mode = ata_limit_mode(dev, mode, ATA_PIO_MAX);
+ }
+ }
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "",
+ ata_mode2str(mode), ctlr->chip->text);
+ if (!error) {
+ if (mode >= ATA_UDMA0) {
+ u_int8_t udma[] = {0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0d};
+ u_int32_t word54 = pci_read_config(gparent, 0x54, 4);
+
+ word54 &= ~(0x000f000f << (devno << 2));
+ word54 |= (((udma[mode&ATA_MODE_MASK]<<16)|0x05)<<(devno<<2));
+ pci_write_config(gparent, 0x54, word54, 4);
+ pci_write_config(gparent, 0x58 + (ch->unit << 2),
+ 0x00310001, 4);
+ }
+ else {
+ u_int32_t piotimings[] =
+ { 0x006d0003, 0x00580002, 0x00440001, 0x00330001,
+ 0x00310001, 0x00440001, 0x00330001, 0x00310001};
+
+ pci_write_config(gparent, 0x54, pci_read_config(gparent, 0x54, 4) &
+ ~(0x0008000f << (devno << 2)), 4);
+ pci_write_config(gparent, 0x58 + (ch->unit << 2),
+ piotimings[ata_mode2idx(mode)], 4);
+ }
+ atadev->mode = mode;
+ }
+}
+
+
+/*
+ * American Micro Devices (AMD) chipset support functions
+ */
+int
+ata_amd_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_AMD756, 0x00, AMDNVIDIA, 0x00, ATA_UDMA4, "756" },
+ { ATA_AMD766, 0x00, AMDNVIDIA, AMDCABLE|AMDBUG, ATA_UDMA5, "766" },
+ { ATA_AMD768, 0x00, AMDNVIDIA, AMDCABLE, ATA_UDMA5, "768" },
+ { ATA_AMD8111, 0x00, AMDNVIDIA, AMDCABLE, ATA_UDMA6, "8111" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ sprintf(buffer, "AMD %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_amd_chipinit;
+ return 0;
+}
+
+static int
+ata_amd_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ /* disable/set prefetch, postwrite */
+ if (ctlr->chip->cfg2 & AMDBUG)
+ pci_write_config(dev, 0x41, pci_read_config(dev, 0x41, 1) & 0x0f, 1);
+ else
+ pci_write_config(dev, 0x41, pci_read_config(dev, 0x41, 1) | 0xf0, 1);
+
+ ctlr->setmode = ata_via_family_setmode;
+ return 0;
+}
+
+
+/*
+ * ATI chipset support functions
+ */
+int
+ata_ati_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_ATI_IXP200, 0x00, 0, 0, ATA_UDMA5, "IXP200" },
+ { ATA_ATI_IXP300, 0x00, 0, 0, ATA_UDMA6, "IXP300" },
+ { ATA_ATI_IXP400, 0x00, 0, 0, ATA_UDMA6, "IXP400" },
+ { ATA_ATI_IXP300_S1, 0x00, SIIMEMIO, 0, ATA_SA150, "IXP300" },
+ { ATA_ATI_IXP400_S1, 0x00, SIIMEMIO, 0, ATA_SA150, "IXP400" },
+ { ATA_ATI_IXP400_S2, 0x00, SIIMEMIO, 0, ATA_SA150, "IXP400" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ sprintf(buffer, "ATI %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+
+ /* the ATI SATA controller is actually a SiI 3112 controller*/
+ if (ctlr->chip->cfg1 & SIIMEMIO)
+ ctlr->chipinit = ata_sii_chipinit;
+ else
+ ctlr->chipinit = ata_ati_chipinit;
+ return 0;
+}
+
+static int
+ata_ati_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ ctlr->setmode = ata_ati_setmode;
+ return 0;
+}
+
+static void
+ata_ati_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int offset = (devno ^ 0x01) << 3;
+ int error;
+ u_int8_t piotimings[] = { 0x5d, 0x47, 0x34, 0x22, 0x20, 0x34, 0x22, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
+ u_int8_t dmatimings[] = { 0x77, 0x21, 0x20 };
+
+ mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
+
+ mode = ata_check_80pin(dev, mode);
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "",
+ ata_mode2str(mode), ctlr->chip->text);
+ if (!error) {
+ if (mode >= ATA_UDMA0) {
+ pci_write_config(gparent, 0x56,
+ (pci_read_config(gparent, 0x56, 2) &
+ ~(0xf << (devno << 2))) |
+ ((mode & ATA_MODE_MASK) << (devno << 2)), 2);
+ pci_write_config(gparent, 0x54,
+ pci_read_config(gparent, 0x54, 1) |
+ (0x01 << devno), 1);
+ pci_write_config(gparent, 0x44,
+ (pci_read_config(gparent, 0x44, 4) &
+ ~(0xff << offset)) |
+ (dmatimings[2] << offset), 4);
+ }
+ else if (mode >= ATA_WDMA0) {
+ pci_write_config(gparent, 0x54,
+ pci_read_config(gparent, 0x54, 1) &
+ ~(0x01 << devno), 1);
+ pci_write_config(gparent, 0x44,
+ (pci_read_config(gparent, 0x44, 4) &
+ ~(0xff << offset)) |
+ (dmatimings[mode & ATA_MODE_MASK] << offset), 4);
+ }
+ else
+ pci_write_config(gparent, 0x54,
+ pci_read_config(gparent, 0x54, 1) &
+ ~(0x01 << devno), 1);
+
+ pci_write_config(gparent, 0x4a,
+ (pci_read_config(gparent, 0x4a, 2) &
+ ~(0xf << (devno << 2))) |
+ (((mode - ATA_PIO0) & ATA_MODE_MASK) << (devno<<2)),2);
+ pci_write_config(gparent, 0x40,
+ (pci_read_config(gparent, 0x40, 4) &
+ ~(0xff << offset)) |
+ (piotimings[ata_mode2idx(mode)] << offset), 4);
+ atadev->mode = mode;
+ }
+}
+
+/*
+ * Cyrix chipset support functions
+ */
+int
+ata_cyrix_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (pci_get_devid(dev) == ATA_CYRIX_5530) {
+ device_set_desc(dev, "Cyrix 5530 ATA33 controller");
+ ctlr->chipinit = ata_cyrix_chipinit;
+ return 0;
+ }
+ return ENXIO;
+}
+
+static int
+ata_cyrix_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ if (ctlr->r_res1)
+ ctlr->setmode = ata_cyrix_setmode;
+ else
+ ctlr->setmode = ata_generic_setmode;
+ return 0;
+}
+
+static void
+ata_cyrix_setmode(device_t dev, int mode)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ u_int32_t piotiming[] =
+ { 0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010 };
+ u_int32_t dmatiming[] = { 0x00077771, 0x00012121, 0x00002020 };
+ u_int32_t udmatiming[] = { 0x00921250, 0x00911140, 0x00911030 };
+ int error;
+
+ ch->dma->alignment = 16;
+ ch->dma->max_iosize = 126 * DEV_BSIZE;
+
+ mode = ata_limit_mode(dev, mode, ATA_UDMA2);
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on Cyrix chip\n",
+ (error) ? "FAILURE " : "", ata_mode2str(mode));
+ if (!error) {
+ if (mode >= ATA_UDMA0) {
+ ATA_OUTL(ch->r_io[ATA_BMCMD_PORT].res,
+ 0x24 + (devno << 3), udmatiming[mode & ATA_MODE_MASK]);
+ }
+ else if (mode >= ATA_WDMA0) {
+ ATA_OUTL(ch->r_io[ATA_BMCMD_PORT].res,
+ 0x24 + (devno << 3), dmatiming[mode & ATA_MODE_MASK]);
+ }
+ else {
+ ATA_OUTL(ch->r_io[ATA_BMCMD_PORT].res,
+ 0x20 + (devno << 3), piotiming[mode & ATA_MODE_MASK]);
+ }
+ atadev->mode = mode;
+ }
+}
+
+
+/*
+ * Cypress chipset support functions
+ */
+int
+ata_cypress_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ /*
+ * the Cypress chip is a mess, it contains two ATA functions, but
+ * both channels are visible on the first one.
+ * simply ignore the second function for now, as the right
+ * solution (ignoring the second channel on the first function)
+ * doesn't work with the crappy ATA interrupt setup on the alpha.
+ */
+ if (pci_get_devid(dev) == ATA_CYPRESS_82C693 &&
+ pci_get_function(dev) == 1 &&
+ pci_get_subclass(dev) == PCIS_STORAGE_IDE) {
+ device_set_desc(dev, "Cypress 82C693 ATA controller");
+ ctlr->chipinit = ata_cypress_chipinit;
+ return 0;
+ }
+ return ENXIO;
+}
+
+static int
+ata_cypress_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ ctlr->setmode = ata_cypress_setmode;
+ return 0;
+}
+
+static void
+ata_cypress_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int error;
+
+ mode = ata_limit_mode(dev, mode, ATA_WDMA2);
+
+ /* XXX SOS missing WDMA0+1 + PIO modes */
+ if (mode == ATA_WDMA2) {
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+ if (bootverbose)
+ device_printf(dev, "%ssetting WDMA2 on Cypress chip\n",
+ error ? "FAILURE " : "");
+ if (!error) {
+ pci_write_config(gparent, ch->unit ? 0x4e : 0x4c, 0x2020, 2);
+ atadev->mode = mode;
+ return;
+ }
+ }
+ /* we could set PIO mode timings, but we assume the BIOS did that */
+}
+
+
+/*
+ * HighPoint chipset support functions
+ */
+int
+ata_highpoint_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_HPT374, 0x07, HPT374, 0x00, ATA_UDMA6, "HPT374" },
+ { ATA_HPT372, 0x02, HPT372, 0x00, ATA_UDMA6, "HPT372N" },
+ { ATA_HPT372, 0x01, HPT372, 0x00, ATA_UDMA6, "HPT372" },
+ { ATA_HPT371, 0x01, HPT372, 0x00, ATA_UDMA6, "HPT371" },
+ { ATA_HPT366, 0x05, HPT372, 0x00, ATA_UDMA6, "HPT372" },
+ { ATA_HPT366, 0x03, HPT370, 0x00, ATA_UDMA5, "HPT370" },
+ { ATA_HPT366, 0x02, HPT366, 0x00, ATA_UDMA4, "HPT368" },
+ { ATA_HPT366, 0x00, HPT366, HPTOLD, ATA_UDMA4, "HPT366" },
+ { ATA_HPT302, 0x01, HPT372, 0x00, ATA_UDMA6, "HPT302" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ strcpy(buffer, "HighPoint ");
+ strcat(buffer, idx->text);
+ if (idx->cfg1 == HPT374) {
+ if (pci_get_function(dev) == 0)
+ strcat(buffer, " (channel 0+1)");
+ if (pci_get_function(dev) == 1)
+ strcat(buffer, " (channel 2+3)");
+ }
+ sprintf(buffer, "%s %s controller", buffer, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_highpoint_chipinit;
+ return 0;
+}
+
+static int
+ata_highpoint_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ if (ctlr->chip->cfg2 == HPTOLD) {
+ /* disable interrupt prediction */
+ pci_write_config(dev, 0x51, (pci_read_config(dev, 0x51, 1) & ~0x80), 1);
+ }
+ else {
+ /* disable interrupt prediction */
+ pci_write_config(dev, 0x51, (pci_read_config(dev, 0x51, 1) & ~0x03), 1);
+ pci_write_config(dev, 0x55, (pci_read_config(dev, 0x55, 1) & ~0x03), 1);
+
+ /* enable interrupts */
+ pci_write_config(dev, 0x5a, (pci_read_config(dev, 0x5a, 1) & ~0x10), 1);
+
+ /* set clocks etc */
+ if (ctlr->chip->cfg1 < HPT372)
+ pci_write_config(dev, 0x5b, 0x22, 1);
+ else
+ pci_write_config(dev, 0x5b,
+ (pci_read_config(dev, 0x5b, 1) & 0x01) | 0x20, 1);
+ }
+ ctlr->allocate = ata_highpoint_allocate;
+ ctlr->setmode = ata_highpoint_setmode;
+ return 0;
+}
+
+static int
+ata_highpoint_allocate(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* setup the usual register normal pci style */
+ if (ata_pci_allocate(dev))
+ return ENXIO;
+
+ ch->flags |= ATA_ALWAYS_DMASTAT;
+ return 0;
+}
+
+static void
+ata_highpoint_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int error;
+ u_int32_t timings33[][4] = {
+ /* HPT366 HPT370 HPT372 HPT374 mode */
+ { 0x40d0a7aa, 0x06914e57, 0x0d029d5e, 0x0ac1f48a }, /* PIO 0 */
+ { 0x40d0a7a3, 0x06914e43, 0x0d029d26, 0x0ac1f465 }, /* PIO 1 */
+ { 0x40d0a753, 0x06514e33, 0x0c829ca6, 0x0a81f454 }, /* PIO 2 */
+ { 0x40c8a742, 0x06514e22, 0x0c829c84, 0x0a81f443 }, /* PIO 3 */
+ { 0x40c8a731, 0x06514e21, 0x0c829c62, 0x0a81f442 }, /* PIO 4 */
+ { 0x20c8a797, 0x26514e97, 0x2c82922e, 0x228082ea }, /* MWDMA 0 */
+ { 0x20c8a732, 0x26514e33, 0x2c829266, 0x22808254 }, /* MWDMA 1 */
+ { 0x20c8a731, 0x26514e21, 0x2c829262, 0x22808242 }, /* MWDMA 2 */
+ { 0x10c8a731, 0x16514e31, 0x1c829c62, 0x121882ea }, /* UDMA 0 */
+ { 0x10cba731, 0x164d4e31, 0x1c9a9c62, 0x12148254 }, /* UDMA 1 */
+ { 0x10caa731, 0x16494e31, 0x1c929c62, 0x120c8242 }, /* UDMA 2 */
+ { 0x10cfa731, 0x166d4e31, 0x1c8e9c62, 0x128c8242 }, /* UDMA 3 */
+ { 0x10c9a731, 0x16454e31, 0x1c8a9c62, 0x12ac8242 }, /* UDMA 4 */
+ { 0, 0x16454e31, 0x1c8a9c62, 0x12848242 }, /* UDMA 5 */
+ { 0, 0, 0x1c869c62, 0x12808242 } /* UDMA 6 */
+ };
+
+ mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
+
+ if (ctlr->chip->cfg1 == HPT366 && ata_atapi(dev))
+ mode = ata_limit_mode(dev, mode, ATA_PIO_MAX);
+
+ mode = ata_highpoint_check_80pin(dev, mode);
+
+ /*
+ * most if not all HPT chips cant really handle that the device is
+ * running at ATA_UDMA6/ATA133 speed, so we cheat at set the device to
+ * a max of ATA_UDMA5/ATA100 to guard against suboptimal performance
+ */
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0,
+ ata_limit_mode(dev, mode, ATA_UDMA5));
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on HighPoint chip\n",
+ (error) ? "FAILURE " : "", ata_mode2str(mode));
+ if (!error)
+ pci_write_config(gparent, 0x40 + (devno << 2),
+ timings33[ata_mode2idx(mode)][ctlr->chip->cfg1], 4);
+ atadev->mode = mode;
+}
+
+static int
+ata_highpoint_check_80pin(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ u_int8_t reg, val, res;
+
+ if (ctlr->chip->cfg1 == HPT374 && pci_get_function(gparent) == 1) {
+ reg = ch->unit ? 0x57 : 0x53;
+ val = pci_read_config(gparent, reg, 1);
+ pci_write_config(gparent, reg, val | 0x80, 1);
+ }
+ else {
+ reg = 0x5b;
+ val = pci_read_config(gparent, reg, 1);
+ pci_write_config(gparent, reg, val & 0xfe, 1);
+ }
+ res = pci_read_config(gparent, 0x5a, 1) & (ch->unit ? 0x1:0x2);
+ pci_write_config(gparent, reg, val, 1);
+
+ if (mode > ATA_UDMA2 && res) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+ return mode;
+}
+
+
+/*
+ * Intel chipset support functions
+ */
+int
+ata_intel_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_I82371FB, 0, 0, 0x00, ATA_WDMA2, "PIIX" },
+ { ATA_I82371SB, 0, 0, 0x00, ATA_WDMA2, "PIIX3" },
+ { ATA_I82371AB, 0, 0, 0x00, ATA_UDMA2, "PIIX4" },
+ { ATA_I82443MX, 0, 0, 0x00, ATA_UDMA2, "PIIX4" },
+ { ATA_I82451NX, 0, 0, 0x00, ATA_UDMA2, "PIIX4" },
+ { ATA_I82801AB, 0, 0, 0x00, ATA_UDMA2, "ICH0" },
+ { ATA_I82801AA, 0, 0, 0x00, ATA_UDMA4, "ICH" },
+ { ATA_I82372FB, 0, 0, 0x00, ATA_UDMA4, "ICH" },
+ { ATA_I82801BA, 0, 0, 0x00, ATA_UDMA5, "ICH2" },
+ { ATA_I82801BA_1, 0, 0, 0x00, ATA_UDMA5, "ICH2" },
+ { ATA_I82801CA, 0, 0, 0x00, ATA_UDMA5, "ICH3" },
+ { ATA_I82801CA_1, 0, 0, 0x00, ATA_UDMA5, "ICH3" },
+ { ATA_I82801DB, 0, 0, 0x00, ATA_UDMA5, "ICH4" },
+ { ATA_I82801DB_1, 0, 0, 0x00, ATA_UDMA5, "ICH4" },
+ { ATA_I82801EB, 0, 0, 0x00, ATA_UDMA5, "ICH5" },
+ { ATA_I82801EB_S1, 0, 0, 0x00, ATA_SA150, "ICH5" },
+ { ATA_I82801EB_R1, 0, 0, 0x00, ATA_SA150, "ICH5" },
+ { ATA_I6300ESB, 0, 0, 0x00, ATA_UDMA5, "6300ESB" },
+ { ATA_I6300ESB_S1, 0, 0, 0x00, ATA_SA150, "6300ESB" },
+ { ATA_I6300ESB_R1, 0, 0, 0x00, ATA_SA150, "6300ESB" },
+ { ATA_I82801FB, 0, 0, 0x00, ATA_UDMA5, "ICH6" },
+ { ATA_I82801FB_S1, 0, AHCI, 0x00, ATA_SA150, "ICH6" },
+ { ATA_I82801FB_R1, 0, AHCI, 0x00, ATA_SA150, "ICH6" },
+ { ATA_I82801FBM, 0, AHCI, 0x00, ATA_SA150, "ICH6M" },
+ { ATA_I82801GB, 0, 0, 0x00, ATA_UDMA5, "ICH7" },
+ { ATA_I82801GB_S1, 0, AHCI, 0x00, ATA_SA300, "ICH7" },
+ { ATA_I82801GB_R1, 0, AHCI, 0x00, ATA_SA300, "ICH7" },
+ { ATA_I82801GB_AH, 0, AHCI, 0x00, ATA_SA300, "ICH7" },
+ { ATA_I82801GBM_S1, 0, AHCI, 0x00, ATA_SA300, "ICH7M" },
+ { ATA_I82801GBM_R1, 0, AHCI, 0x00, ATA_SA300, "ICH7M" },
+ { ATA_I82801GBM_AH, 0, AHCI, 0x00, ATA_SA300, "ICH7M" },
+ { ATA_I63XXESB2, 0, 0, 0x00, ATA_UDMA5, "63XXESB2" },
+ { ATA_I63XXESB2_S1, 0, AHCI, 0x00, ATA_SA300, "63XXESB2" },
+ { ATA_I63XXESB2_S2, 0, AHCI, 0x00, ATA_SA300, "63XXESB2" },
+ { ATA_I63XXESB2_R1, 0, AHCI, 0x00, ATA_SA300, "63XXESB2" },
+ { ATA_I63XXESB2_R2, 0, AHCI, 0x00, ATA_SA300, "63XXESB2" },
+ { ATA_I82801HB_S1, 0, AHCI, 0x00, ATA_SA300, "ICH8" },
+ { ATA_I82801HB_S2, 0, AHCI, 0x00, ATA_SA300, "ICH8" },
+ { ATA_I82801HB_R1, 0, AHCI, 0x00, ATA_SA300, "ICH8" },
+ { ATA_I82801HB_AH4, 0, AHCI, 0x00, ATA_SA300, "ICH8" },
+ { ATA_I82801HB_AH6, 0, AHCI, 0x00, ATA_SA300, "ICH8" },
+ { ATA_I82801HBM_S1, 0, AHCI, 0x00, ATA_SA300, "ICH8M" },
+ { ATA_I82801HBM_S2, 0, AHCI, 0x00, ATA_SA300, "ICH8M" },
+ { ATA_I31244, 0, 0, 0x00, ATA_SA150, "31244" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ sprintf(buffer, "Intel %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_intel_chipinit;
+ return 0;
+}
+
+static int
+ata_intel_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ /* good old PIIX needs special treatment (not implemented) */
+ if (ctlr->chip->chipid == ATA_I82371FB) {
+ ctlr->setmode = ata_intel_old_setmode;
+ }
+
+ /* the intel 31244 needs special care if in DPA mode */
+ else if (ctlr->chip->chipid == ATA_I31244) {
+ if (pci_get_subclass(dev) != PCIS_STORAGE_IDE) {
+ ctlr->r_type2 = SYS_RES_MEMORY;
+ ctlr->r_rid2 = PCIR_BAR(0);
+ if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2,
+ RF_ACTIVE)))
+ return ENXIO;
+ ctlr->channels = 4;
+ ctlr->allocate = ata_intel_31244_allocate;
+ ctlr->reset = ata_intel_31244_reset;
+ }
+ ctlr->setmode = ata_sata_setmode;
+ }
+
+ /* non SATA intel chips goes here */
+ else if (ctlr->chip->max_dma < ATA_SA150) {
+ ctlr->allocate = ata_intel_allocate;
+ ctlr->setmode = ata_intel_new_setmode;
+ }
+
+ /* SATA parts can be either compat or AHCI */
+ else {
+ /* force all ports active "the legacy way" */
+ pci_write_config(dev, 0x92, pci_read_config(dev, 0x92, 2) | 0x0f,2);
+
+ ctlr->allocate = ata_intel_allocate;
+ ctlr->reset = ata_intel_reset;
+
+ /*
+ * if we have AHCI capability and BAR(5) as a memory resource
+ * and AHCI or RAID mode enabled in BIOS we go for AHCI mode
+ */
+ if ((ctlr->chip->cfg1 == AHCI) &&
+ (pci_read_config(dev, 0x90, 1) & 0xc0)) {
+ ctlr->r_type2 = SYS_RES_MEMORY;
+ ctlr->r_rid2 = PCIR_BAR(5);
+ if ((ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2,
+ RF_ACTIVE)))
+ return ata_ahci_chipinit(dev);
+ }
+ ctlr->setmode = ata_sata_setmode;
+
+ /* enable PCI interrupt */
+ pci_write_config(dev, PCIR_COMMAND,
+ pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400, 2);
+ }
+ return 0;
+}
+
+static int
+ata_intel_allocate(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* setup the usual register normal pci style */
+ if (ata_pci_allocate(dev))
+ return ENXIO;
+
+ ch->flags |= ATA_ALWAYS_DMASTAT;
+ return 0;
+}
+
+static void
+ata_intel_reset(device_t dev)
+{
+ device_t parent = device_get_parent(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(parent);
+ struct ata_channel *ch = device_get_softc(dev);
+ int mask, timeout;
+
+ /* ICH6 & ICH7 in compat mode has 4 SATA ports as master/slave on 2 ch's */
+ if (ctlr->chip->cfg1) {
+ mask = (0x0005 << ch->unit);
+ }
+ else {
+ /* ICH5 in compat mode has SATA ports as master/slave on 1 channel */
+ if (pci_read_config(parent, 0x90, 1) & 0x04)
+ mask = 0x0003;
+ else {
+ mask = (0x0001 << ch->unit);
+ /* XXX SOS should be in intel_allocate if we grow it */
+ ch->flags |= ATA_NO_SLAVE;
+ }
+ }
+ pci_write_config(parent, 0x92, pci_read_config(parent, 0x92, 2) & ~mask, 2);
+ DELAY(10);
+ pci_write_config(parent, 0x92, pci_read_config(parent, 0x92, 2) | mask, 2);
+
+ /* wait up to 1 sec for "connect well" */
+ for (timeout = 0; timeout < 100 ; timeout++) {
+ if (((pci_read_config(parent, 0x92, 2) & (mask << 4)) == (mask << 4)) &&
+ (ATA_IDX_INB(ch, ATA_STATUS) != 0xff))
+ break;
+ ata_udelay(10000);
+ }
+ ata_generic_reset(dev);
+}
+
+static void
+ata_intel_old_setmode(device_t dev, int mode)
+{
+ /* NOT YET */
+}
+
+static void
+ata_intel_new_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ u_int32_t reg40 = pci_read_config(gparent, 0x40, 4);
+ u_int8_t reg44 = pci_read_config(gparent, 0x44, 1);
+ u_int8_t reg48 = pci_read_config(gparent, 0x48, 1);
+ u_int16_t reg4a = pci_read_config(gparent, 0x4a, 2);
+ u_int16_t reg54 = pci_read_config(gparent, 0x54, 2);
+ u_int32_t mask40 = 0, new40 = 0;
+ u_int8_t mask44 = 0, new44 = 0;
+ int error;
+ u_int8_t timings[] = { 0x00, 0x00, 0x10, 0x21, 0x23, 0x10, 0x21, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23 };
+
+ mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
+
+ if ( mode > ATA_UDMA2 && !(reg54 & (0x10 << devno))) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "",
+ ata_mode2str(mode), ctlr->chip->text);
+ if (error)
+ return;
+
+ if (mode >= ATA_UDMA0) {
+ pci_write_config(gparent, 0x48, reg48 | (0x0001 << devno), 2);
+ pci_write_config(gparent, 0x4a,
+ (reg4a & ~(0x3 << (devno << 2))) |
+ ((0x01 + !(mode & 0x01)) << (devno << 2)), 2);
+ }
+ else {
+ pci_write_config(gparent, 0x48, reg48 & ~(0x0001 << devno), 2);
+ pci_write_config(gparent, 0x4a, (reg4a & ~(0x3 << (devno << 2))), 2);
+ }
+ reg54 |= 0x0400;
+ if (mode >= ATA_UDMA2)
+ pci_write_config(gparent, 0x54, reg54 | (0x1 << devno), 2);
+ else
+ pci_write_config(gparent, 0x54, reg54 & ~(0x1 << devno), 2);
+
+ if (mode >= ATA_UDMA5)
+ pci_write_config(gparent, 0x54, reg54 | (0x1000 << devno), 2);
+ else
+ pci_write_config(gparent, 0x54, reg54 & ~(0x1000 << devno), 2);
+
+ reg40 &= ~0x00ff00ff;
+ reg40 |= 0x40774077;
+
+ if (atadev->unit == ATA_MASTER) {
+ mask40 = 0x3300;
+ new40 = timings[ata_mode2idx(mode)] << 8;
+ }
+ else {
+ mask44 = 0x0f;
+ new44 = ((timings[ata_mode2idx(mode)] & 0x30) >> 2) |
+ (timings[ata_mode2idx(mode)] & 0x03);
+ }
+ if (ch->unit) {
+ mask40 <<= 16;
+ new40 <<= 16;
+ mask44 <<= 4;
+ new44 <<= 4;
+ }
+ pci_write_config(gparent, 0x40, (reg40 & ~mask40) | new40, 4);
+ pci_write_config(gparent, 0x44, (reg44 & ~mask44) | new44, 1);
+
+ atadev->mode = mode;
+}
+
+static int
+ata_intel_31244_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int i;
+ int ch_offset;
+
+ ch_offset = 0x200 + ch->unit * 0x200;
+
+ for (i = ATA_DATA; i < ATA_MAX_RES; i++)
+ ch->r_io[i].res = ctlr->r_res2;
+
+ /* setup ATA registers */
+ ch->r_io[ATA_DATA].offset = ch_offset + 0x00;
+ ch->r_io[ATA_FEATURE].offset = ch_offset + 0x06;
+ ch->r_io[ATA_COUNT].offset = ch_offset + 0x08;
+ ch->r_io[ATA_SECTOR].offset = ch_offset + 0x0c;
+ ch->r_io[ATA_CYL_LSB].offset = ch_offset + 0x10;
+ ch->r_io[ATA_CYL_MSB].offset = ch_offset + 0x14;
+ ch->r_io[ATA_DRIVE].offset = ch_offset + 0x18;
+ ch->r_io[ATA_COMMAND].offset = ch_offset + 0x1d;
+ ch->r_io[ATA_ERROR].offset = ch_offset + 0x04;
+ ch->r_io[ATA_STATUS].offset = ch_offset + 0x1c;
+ ch->r_io[ATA_ALTSTAT].offset = ch_offset + 0x28;
+ ch->r_io[ATA_CONTROL].offset = ch_offset + 0x29;
+
+ /* setup DMA registers */
+ ch->r_io[ATA_SSTATUS].offset = ch_offset + 0x100;
+ ch->r_io[ATA_SERROR].offset = ch_offset + 0x104;
+ ch->r_io[ATA_SCONTROL].offset = ch_offset + 0x108;
+
+ /* setup SATA registers */
+ ch->r_io[ATA_BMCMD_PORT].offset = ch_offset + 0x70;
+ ch->r_io[ATA_BMSTAT_PORT].offset = ch_offset + 0x72;
+ ch->r_io[ATA_BMDTP_PORT].offset = ch_offset + 0x74;
+
+ ch->flags |= ATA_NO_SLAVE;
+ ata_pci_hw(dev);
+ ch->hw.status = ata_intel_31244_status;
+ ch->hw.command = ata_intel_31244_command;
+
+ /* enable PHY state change interrupt */
+ ATA_OUTL(ctlr->r_res2, 0x4,
+ ATA_INL(ctlr->r_res2, 0x04) | (0x01 << (ch->unit << 3)));
+ return 0;
+}
+
+static int
+ata_intel_31244_status(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ u_int32_t status = ATA_IDX_INL(ch, ATA_SSTATUS);
+ u_int32_t error = ATA_IDX_INL(ch, ATA_SERROR);
+ struct ata_connect_task *tp;
+
+ /* check for PHY related interrupts on SATA capable HW */
+ if (error) {
+ /* clear error bits/interrupt */
+ ATA_IDX_OUTL(ch, ATA_SERROR, error);
+
+ /* if we have a connection event deal with it */
+ if ((error & ATA_SE_PHY_CHANGED) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ if ((status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN1) {
+ if (bootverbose)
+ device_printf(ch->dev, "CONNECT requested\n");
+ tp->action = ATA_C_ATTACH;
+ }
+ else {
+ if (bootverbose)
+ device_printf(ch->dev, "DISCONNECT requested\n");
+ tp->action = ATA_C_DETACH;
+ }
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+ }
+
+ /* any drive action to take care of ? */
+ return ata_pci_status(dev);
+}
+
+static int
+ata_intel_31244_command(struct ata_request *request)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ struct ata_device *atadev = device_get_softc(request->dev);
+ u_int64_t lba;
+
+ if (!(atadev->flags & ATA_D_48BIT_ACTIVE))
+ return (ata_generic_command(request));
+
+ lba = request->u.ata.lba;
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | atadev->unit);
+ /* enable interrupt */
+ ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_4BIT);
+ ATA_IDX_OUTW(ch, ATA_FEATURE, request->u.ata.feature);
+ ATA_IDX_OUTW(ch, ATA_COUNT, request->u.ata.count);
+ ATA_IDX_OUTW(ch, ATA_SECTOR, ((lba >> 16) & 0xff00) | (lba & 0x00ff));
+ ATA_IDX_OUTW(ch, ATA_CYL_LSB, ((lba >> 24) & 0xff00) |
+ ((lba >> 8) & 0x00ff));
+ ATA_IDX_OUTW(ch, ATA_CYL_MSB, ((lba >> 32) & 0xff00) |
+ ((lba >> 16) & 0x00ff));
+
+ /* issue command to controller */
+ ATA_IDX_OUTB(ch, ATA_COMMAND, request->u.ata.command);
+
+ return 0;
+}
+
+static void
+ata_intel_31244_reset(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ata_sata_phy_enable(ch);
+}
+
+
+/*
+ * Integrated Technology Express Inc. (ITE) chipset support functions
+ */
+int
+ata_ite_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_IT8212F, 0x00, 0x00, 0x00, ATA_UDMA6, "IT8212F" },
+ { ATA_IT8211F, 0x00, 0x00, 0x00, ATA_UDMA6, "IT8211F" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ sprintf(buffer, "ITE %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_ite_chipinit;
+ return 0;
+}
+
+static int
+ata_ite_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ ctlr->setmode = ata_ite_setmode;
+
+ /* set PCI mode and 66Mhz reference clock */
+ pci_write_config(dev, 0x50, pci_read_config(dev, 0x50, 1) & ~0x83, 1);
+
+ /* set default active & recover timings */
+ pci_write_config(dev, 0x54, 0x31, 1);
+ pci_write_config(dev, 0x56, 0x31, 1);
+ return 0;
+}
+
+static void
+ata_ite_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int error;
+
+ /* correct the mode for what the HW supports */
+ mode = ata_limit_mode(dev, mode, ATA_UDMA6);
+
+ /* check the CBLID bits for 80 conductor cable detection */
+ if (mode > ATA_UDMA2 && (pci_read_config(gparent, 0x40, 2) &
+ (ch->unit ? (1<<3) : (1<<2)))) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+
+ /* set the wanted mode on the device */
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%s setting %s on ITE8212F chip\n",
+ (error) ? "failed" : "success", ata_mode2str(mode));
+
+ /* if the device accepted the mode change, setup the HW accordingly */
+ if (!error) {
+ if (mode >= ATA_UDMA0) {
+ u_int8_t udmatiming[] =
+ { 0x44, 0x42, 0x31, 0x21, 0x11, 0xa2, 0x91 };
+
+ /* enable UDMA mode */
+ pci_write_config(gparent, 0x50,
+ pci_read_config(gparent, 0x50, 1) &
+ ~(1 << (devno + 3)), 1);
+
+ /* set UDMA timing */
+ pci_write_config(gparent,
+ 0x56 + (ch->unit << 2) + ATA_DEV(atadev->unit),
+ udmatiming[mode & ATA_MODE_MASK], 1);
+ }
+ else {
+ u_int8_t chtiming[] =
+ { 0xaa, 0xa3, 0xa1, 0x33, 0x31, 0x88, 0x32, 0x31 };
+
+ /* disable UDMA mode */
+ pci_write_config(gparent, 0x50,
+ pci_read_config(gparent, 0x50, 1) |
+ (1 << (devno + 3)), 1);
+
+ /* set active and recover timing (shared between master & slave) */
+ if (pci_read_config(gparent, 0x54 + (ch->unit << 2), 1) <
+ chtiming[ata_mode2idx(mode)])
+ pci_write_config(gparent, 0x54 + (ch->unit << 2),
+ chtiming[ata_mode2idx(mode)], 1);
+ }
+ atadev->mode = mode;
+ }
+}
+
+
+/*
+ * JMicron chipset support functions
+ */
+int
+ata_jmicron_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_JMB360, 0, 1, 0, ATA_SA300, "JMB360" },
+ { ATA_JMB361, 0, 1, 1, ATA_SA300, "JMB361" },
+ { ATA_JMB363, 0, 2, 1, ATA_SA300, "JMB363" },
+ { ATA_JMB365, 0, 1, 2, ATA_SA300, "JMB365" },
+ { ATA_JMB366, 0, 2, 2, ATA_SA300, "JMB366" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ if ((pci_read_config(dev, 0xdf, 1) & 0x40) &&
+ (pci_get_function(dev) == (pci_read_config(dev, 0x40, 1) & 0x02 >> 1)))
+ sprintf(buffer, "JMicron %s %s controller",
+ idx->text, ata_mode2str(ATA_UDMA6));
+ else
+ sprintf(buffer, "JMicron %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_jmicron_chipinit;
+ return 0;
+}
+
+static int
+ata_jmicron_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ int error;
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ /* do we have multiple PCI functions ? */
+ if (pci_read_config(dev, 0xdf, 1) & 0x40) {
+ /* if we have a memory BAR(5) we are on the AHCI part */
+ ctlr->r_type2 = SYS_RES_MEMORY;
+ ctlr->r_rid2 = PCIR_BAR(5);
+ if ((ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2, RF_ACTIVE)))
+ return ata_ahci_chipinit(dev);
+
+ /* otherwise we are on the PATA part */
+ ctlr->allocate = ata_pci_allocate;
+ ctlr->reset = ata_generic_reset;
+ ctlr->dmainit = ata_pci_dmainit;
+ ctlr->setmode = ata_jmicron_setmode;
+ ctlr->channels = ctlr->chip->cfg2;
+ }
+ else {
+ /* set controller configuration to a combined setup we support */
+ pci_write_config(dev, 0x40, 0x80c0a131, 4);
+ pci_write_config(dev, 0x80, 0x01200000, 4);
+
+ ctlr->r_type2 = SYS_RES_MEMORY;
+ ctlr->r_rid2 = PCIR_BAR(5);
+ if ((ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2, RF_ACTIVE))){
+ if ((error = ata_ahci_chipinit(dev)))
+ return error;
+ }
+
+ ctlr->allocate = ata_jmicron_allocate;
+ ctlr->reset = ata_jmicron_reset;
+ ctlr->dmainit = ata_jmicron_dmainit;
+ ctlr->setmode = ata_jmicron_setmode;
+
+ /* set the number of HW channels */
+ ctlr->channels = ctlr->chip->cfg1 + ctlr->chip->cfg2;
+ }
+ return 0;
+}
+
+static int
+ata_jmicron_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int error;
+
+ if (ch->unit >= ctlr->chip->cfg1) {
+ ch->unit -= ctlr->chip->cfg1;
+ error = ata_pci_allocate(dev);
+ ch->unit += ctlr->chip->cfg1;
+ }
+ else
+ error = ata_ahci_allocate(dev);
+ return error;
+}
+
+static void
+ata_jmicron_reset(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ch->unit >= ctlr->chip->cfg1)
+ ata_generic_reset(dev);
+ else
+ ata_ahci_reset(dev);
+}
+
+static void
+ata_jmicron_dmainit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ch->unit >= ctlr->chip->cfg1)
+ ata_pci_dmainit(dev);
+ else
+ ata_ahci_dmainit(dev);
+}
+
+static void
+ata_jmicron_setmode(device_t dev, int mode)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(GRANDPARENT(dev));
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+
+ if (pci_read_config(dev, 0xdf, 1) & 0x40 || ch->unit >= ctlr->chip->cfg1) {
+ struct ata_device *atadev = device_get_softc(dev);
+
+ /* check for 80pin cable present */
+ if (pci_read_config(dev, 0x40, 1) & 0x08)
+ mode = ata_limit_mode(dev, mode, ATA_UDMA2);
+ else
+ mode = ata_limit_mode(dev, mode, ATA_UDMA6);
+
+ if (!ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode))
+ atadev->mode = mode;
+ }
+ else
+ ata_sata_setmode(dev, mode);
+}
+
+
+/*
+ * Marvell chipset support functions
+ */
+#define ATA_MV_HOST_BASE(ch) \
+ ((ch->unit & 3) * 0x0100) + (ch->unit > 3 ? 0x30000 : 0x20000)
+#define ATA_MV_EDMA_BASE(ch) \
+ ((ch->unit & 3) * 0x2000) + (ch->unit > 3 ? 0x30000 : 0x20000)
+
+struct ata_marvell_response {
+ u_int16_t tag;
+ u_int8_t edma_status;
+ u_int8_t dev_status;
+ u_int32_t timestamp;
+};
+
+struct ata_marvell_dma_prdentry {
+ u_int32_t addrlo;
+ u_int32_t count;
+ u_int32_t addrhi;
+ u_int32_t reserved;
+};
+
+int
+ata_marvell_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_M88SX5040, 0, 4, MV5XXX, ATA_SA150, "88SX5040" },
+ { ATA_M88SX5041, 0, 4, MV5XXX, ATA_SA150, "88SX5041" },
+ { ATA_M88SX5080, 0, 8, MV5XXX, ATA_SA150, "88SX5080" },
+ { ATA_M88SX5081, 0, 8, MV5XXX, ATA_SA150, "88SX5081" },
+ { ATA_M88SX6041, 0, 4, MV6XXX, ATA_SA300, "88SX6041" },
+ { ATA_M88SX6081, 0, 8, MV6XXX, ATA_SA300, "88SX6081" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ sprintf(buffer, "Marvell %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_marvell_chipinit;
+ return 0;
+}
+
+static int
+ata_marvell_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ ctlr->r_type1 = SYS_RES_MEMORY;
+ ctlr->r_rid1 = PCIR_BAR(0);
+ if (!(ctlr->r_res1 = bus_alloc_resource_any(dev, ctlr->r_type1,
+ &ctlr->r_rid1, RF_ACTIVE)))
+ return ENXIO;
+
+ /* mask all host controller interrupts */
+ ATA_OUTL(ctlr->r_res1, 0x01d64, 0x00000000);
+
+ /* mask all PCI interrupts */
+ ATA_OUTL(ctlr->r_res1, 0x01d5c, 0x00000000);
+
+ ctlr->allocate = ata_marvell_allocate;
+ ctlr->reset = ata_marvell_reset;
+ ctlr->dmainit = ata_marvell_dmainit;
+ ctlr->setmode = ata_sata_setmode;
+ ctlr->channels = ctlr->chip->cfg1;
+
+ /* clear host controller interrupts */
+ ATA_OUTL(ctlr->r_res1, 0x20014, 0x00000000);
+ if (ctlr->chip->cfg1 > 4)
+ ATA_OUTL(ctlr->r_res1, 0x30014, 0x00000000);
+
+ /* clear PCI interrupts */
+ ATA_OUTL(ctlr->r_res1, 0x01d58, 0x00000000);
+
+ /* unmask PCI interrupts we want */
+ ATA_OUTL(ctlr->r_res1, 0x01d5c, 0x007fffff);
+
+ /* unmask host controller interrupts we want */
+ ATA_OUTL(ctlr->r_res1, 0x01d64, 0x000000ff/*HC0*/ | 0x0001fe00/*HC1*/ |
+ /*(1<<19) | (1<<20) | (1<<21) |*/(1<<22) | (1<<24) | (0x7f << 25));
+
+ /* enable PCI interrupt */
+ pci_write_config(dev, PCIR_COMMAND,
+ pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400, 2);
+ return 0;
+}
+
+static int
+ata_marvell_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ bus_addr_t work = ch->dma->work_bus;
+ int i;
+
+ /* clear work area */
+ bzero(ch->dma->work, 1024+256);
+
+ /* set legacy ATA resources */
+ for (i = ATA_DATA; i <= ATA_COMMAND; i++) {
+ ch->r_io[i].res = ctlr->r_res1;
+ ch->r_io[i].offset = 0x02100 + (i << 2) + ATA_MV_EDMA_BASE(ch);
+ }
+ ch->r_io[ATA_CONTROL].res = ctlr->r_res1;
+ ch->r_io[ATA_CONTROL].offset = 0x02120 + ATA_MV_EDMA_BASE(ch);
+ ch->r_io[ATA_IDX_ADDR].res = ctlr->r_res1;
+ ata_default_registers(dev);
+
+ /* set SATA resources */
+ switch (ctlr->chip->cfg2) {
+ case MV5XXX:
+ ch->r_io[ATA_SSTATUS].res = ctlr->r_res1;
+ ch->r_io[ATA_SSTATUS].offset = 0x00100 + ATA_MV_HOST_BASE(ch);
+ ch->r_io[ATA_SERROR].res = ctlr->r_res1;
+ ch->r_io[ATA_SERROR].offset = 0x00104 + ATA_MV_HOST_BASE(ch);
+ ch->r_io[ATA_SCONTROL].res = ctlr->r_res1;
+ ch->r_io[ATA_SCONTROL].offset = 0x00108 + ATA_MV_HOST_BASE(ch);
+ break;
+ case MV6XXX:
+ ch->r_io[ATA_SSTATUS].res = ctlr->r_res1;
+ ch->r_io[ATA_SSTATUS].offset = 0x02300 + ATA_MV_EDMA_BASE(ch);
+ ch->r_io[ATA_SERROR].res = ctlr->r_res1;
+ ch->r_io[ATA_SERROR].offset = 0x02304 + ATA_MV_EDMA_BASE(ch);
+ ch->r_io[ATA_SCONTROL].res = ctlr->r_res1;
+ ch->r_io[ATA_SCONTROL].offset = 0x02308 + ATA_MV_EDMA_BASE(ch);
+ ch->r_io[ATA_SACTIVE].res = ctlr->r_res1;
+ ch->r_io[ATA_SACTIVE].offset = 0x02350 + ATA_MV_EDMA_BASE(ch);
+ break;
+ }
+
+ ch->flags |= ATA_NO_SLAVE;
+ ch->flags |= ATA_USE_16BIT; /* XXX SOS needed ? */
+ ata_generic_hw(dev);
+ ch->hw.begin_transaction = ata_marvell_begin_transaction;
+ ch->hw.end_transaction = ata_marvell_end_transaction;
+ ch->hw.status = ata_marvell_status;
+
+ /* disable the EDMA machinery */
+ ATA_OUTL(ctlr->r_res1, 0x02028 + ATA_MV_EDMA_BASE(ch), 0x00000002);
+ DELAY(100000); /* SOS should poll for disabled */
+
+ /* set configuration to non-queued 128b read transfers stop on error */
+ ATA_OUTL(ctlr->r_res1, 0x02000 + ATA_MV_EDMA_BASE(ch), (1<<11) | (1<<13));
+
+ /* request queue base high */
+ ATA_OUTL(ctlr->r_res1, 0x02010 + ATA_MV_EDMA_BASE(ch), (work >> 16) >> 16);
+
+ /* request queue in ptr */
+ ATA_OUTL(ctlr->r_res1, 0x02014 + ATA_MV_EDMA_BASE(ch), work & 0xffffffff);
+
+ /* request queue out ptr */
+ ATA_OUTL(ctlr->r_res1, 0x02018 + ATA_MV_EDMA_BASE(ch), 0x0);
+
+ /* response queue base high */
+ work += 1024;
+ ATA_OUTL(ctlr->r_res1, 0x0201c + ATA_MV_EDMA_BASE(ch), (work >> 16) >> 16);
+
+ /* response queue in ptr */
+ ATA_OUTL(ctlr->r_res1, 0x02020 + ATA_MV_EDMA_BASE(ch), 0x0);
+
+ /* response queue out ptr */
+ ATA_OUTL(ctlr->r_res1, 0x02024 + ATA_MV_EDMA_BASE(ch), work & 0xffffffff);
+
+ /* clear SATA error register */
+ ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
+
+ /* clear any outstanding error interrupts */
+ ATA_OUTL(ctlr->r_res1, 0x02008 + ATA_MV_EDMA_BASE(ch), 0x0);
+
+ /* unmask all error interrupts */
+ ATA_OUTL(ctlr->r_res1, 0x0200c + ATA_MV_EDMA_BASE(ch), ~0x0);
+
+ /* enable EDMA machinery */
+ ATA_OUTL(ctlr->r_res1, 0x02028 + ATA_MV_EDMA_BASE(ch), 0x00000001);
+ return 0;
+}
+
+static int
+ata_marvell_status(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ u_int32_t cause = ATA_INL(ctlr->r_res1, 0x01d60);
+ int shift = (ch->unit << 1) + (ch->unit > 3);
+
+ /* do we have any errors flagged ? */
+ if (cause & (1 << shift)) {
+ struct ata_connect_task *tp;
+ u_int32_t error =
+ ATA_INL(ctlr->r_res1, 0x02008 + ATA_MV_EDMA_BASE(ch));
+
+ /* check for and handle disconnect events */
+ if ((error & 0x00000008) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ if (bootverbose)
+ device_printf(ch->dev, "DISCONNECT requested\n");
+ tp->action = ATA_C_DETACH;
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+
+ /* check for and handle connect events */
+ if ((error & 0x00000010) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ if (bootverbose)
+ device_printf(ch->dev, "CONNECT requested\n");
+ tp->action = ATA_C_ATTACH;
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+
+ /* clear SATA error register */
+ ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
+
+ /* clear any outstanding error interrupts */
+ ATA_OUTL(ctlr->r_res1, 0x02008 + ATA_MV_EDMA_BASE(ch), 0x0);
+ }
+
+ /* do we have any device action ? */
+ return (cause & (2 << shift));
+}
+
+/* must be called with ATA channel locked and state_mtx held */
+static int
+ata_marvell_begin_transaction(struct ata_request *request)
+{
+ struct ata_pci_controller *ctlr=device_get_softc(GRANDPARENT(request->dev));
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ u_int32_t req_in;
+ u_int8_t *bytep;
+ u_int16_t *wordp;
+ u_int32_t *quadp;
+ int i, tag = 0x07;
+ int dummy, error, slot;
+
+ /* only DMA R/W goes through the EMDA machine */
+ if (request->u.ata.command != ATA_READ_DMA &&
+ request->u.ata.command != ATA_WRITE_DMA) {
+
+ /* disable the EDMA machinery */
+ if (ATA_INL(ctlr->r_res1, 0x02028 + ATA_MV_EDMA_BASE(ch)) & 0x00000001)
+ ATA_OUTL(ctlr->r_res1, 0x02028 + ATA_MV_EDMA_BASE(ch), 0x00000002);
+ return ata_begin_transaction(request);
+ }
+
+ /* check for 48 bit access and convert if needed */
+ ata_modify_if_48bit(request);
+
+ /* check sanity, setup SG list and DMA engine */
+ if ((error = ch->dma->load(ch->dev, request->data, request->bytecount,
+ request->flags & ATA_R_READ, ch->dma->sg,
+ &dummy))) {
+ device_printf(request->dev, "setting up DMA failed\n");
+ request->result = error;
+ return ATA_OP_FINISHED;
+ }
+
+ /* get next free request queue slot */
+ req_in = ATA_INL(ctlr->r_res1, 0x02014 + ATA_MV_EDMA_BASE(ch));
+ slot = (((req_in & ~0xfffffc00) >> 5) + 0) & 0x1f;
+ bytep = (u_int8_t *)(ch->dma->work);
+ bytep += (slot << 5);
+ wordp = (u_int16_t *)bytep;
+ quadp = (u_int32_t *)bytep;
+
+ /* fill in this request */
+ quadp[0] = (long)ch->dma->sg_bus & 0xffffffff;
+ quadp[1] = (ch->dma->sg_bus & 0xffffffff00000000ull) >> 32;
+ wordp[4] = (request->flags & ATA_R_READ ? 0x01 : 0x00) | (tag<<1);
+
+ i = 10;
+ bytep[i++] = (request->u.ata.count >> 8) & 0xff;
+ bytep[i++] = 0x10 | ATA_COUNT;
+ bytep[i++] = request->u.ata.count & 0xff;
+ bytep[i++] = 0x10 | ATA_COUNT;
+
+ bytep[i++] = (request->u.ata.lba >> 24) & 0xff;
+ bytep[i++] = 0x10 | ATA_SECTOR;
+ bytep[i++] = request->u.ata.lba & 0xff;
+ bytep[i++] = 0x10 | ATA_SECTOR;
+
+ bytep[i++] = (request->u.ata.lba >> 32) & 0xff;
+ bytep[i++] = 0x10 | ATA_CYL_LSB;
+ bytep[i++] = (request->u.ata.lba >> 8) & 0xff;
+ bytep[i++] = 0x10 | ATA_CYL_LSB;
+
+ bytep[i++] = (request->u.ata.lba >> 40) & 0xff;
+ bytep[i++] = 0x10 | ATA_CYL_MSB;
+ bytep[i++] = (request->u.ata.lba >> 16) & 0xff;
+ bytep[i++] = 0x10 | ATA_CYL_MSB;
+
+ bytep[i++] = ATA_D_LBA | ATA_D_IBM | ((request->u.ata.lba >> 24) & 0xf);
+ bytep[i++] = 0x10 | ATA_DRIVE;
+
+ bytep[i++] = request->u.ata.command;
+ bytep[i++] = 0x90 | ATA_COMMAND;
+
+ /* enable EDMA machinery if needed */
+ if (!(ATA_INL(ctlr->r_res1, 0x02028 + ATA_MV_EDMA_BASE(ch)) & 0x00000001)) {
+ ATA_OUTL(ctlr->r_res1, 0x02028 + ATA_MV_EDMA_BASE(ch), 0x00000001);
+ while (!(ATA_INL(ctlr->r_res1,
+ 0x02028 + ATA_MV_EDMA_BASE(ch)) & 0x00000001))
+ DELAY(10);
+ }
+
+ /* tell EDMA it has a new request */
+ slot = (((req_in & ~0xfffffc00) >> 5) + 1) & 0x1f;
+ req_in &= 0xfffffc00;
+ req_in += (slot << 5);
+ ATA_OUTL(ctlr->r_res1, 0x02014 + ATA_MV_EDMA_BASE(ch), req_in);
+
+ return ATA_OP_CONTINUES;
+}
+
+/* must be called with ATA channel locked and state_mtx held */
+static int
+ata_marvell_end_transaction(struct ata_request *request)
+{
+ struct ata_pci_controller *ctlr=device_get_softc(GRANDPARENT(request->dev));
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ int offset = (ch->unit > 3 ? 0x30014 : 0x20014);
+ u_int32_t icr = ATA_INL(ctlr->r_res1, offset);
+ int res;
+
+ /* EDMA interrupt */
+ if ((icr & (0x0001 << (ch->unit & 3)))) {
+ struct ata_marvell_response *response;
+ u_int32_t rsp_in, rsp_out;
+ int slot;
+
+ /* unload SG list */
+ ch->dma->unload(ch->dev);
+
+ /* stop timeout */
+ callout_stop(&request->callout);
+
+ /* get response ptr's */
+ rsp_in = ATA_INL(ctlr->r_res1, 0x02020 + ATA_MV_EDMA_BASE(ch));
+ rsp_out = ATA_INL(ctlr->r_res1, 0x02024 + ATA_MV_EDMA_BASE(ch));
+ slot = (((rsp_in & ~0xffffff00) >> 3)) & 0x1f;
+ rsp_out &= 0xffffff00;
+ rsp_out += (slot << 3);
+ response = (struct ata_marvell_response *)
+ (ch->dma->work + 1024 + (slot << 3));
+
+ /* record status for this request */
+ request->status = response->dev_status;
+ request->error = 0;
+
+ /* ack response */
+ ATA_OUTL(ctlr->r_res1, 0x02024 + ATA_MV_EDMA_BASE(ch), rsp_out);
+
+ /* update progress */
+ if (!(request->status & ATA_S_ERROR) &&
+ !(request->flags & ATA_R_TIMEOUT))
+ request->donecount = request->bytecount;
+ res = ATA_OP_FINISHED;
+ }
+
+ /* legacy ATA interrupt */
+ else {
+ res = ata_end_transaction(request);
+ }
+
+ /* ack interrupt */
+ ATA_OUTL(ctlr->r_res1, offset, ~(icr & (0x0101 << (ch->unit & 3))));
+ return res;
+}
+
+static void
+ata_marvell_reset(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* disable the EDMA machinery */
+ ATA_OUTL(ctlr->r_res1, 0x02028 + ATA_MV_EDMA_BASE(ch), 0x00000002);
+ while ((ATA_INL(ctlr->r_res1, 0x02028 + ATA_MV_EDMA_BASE(ch)) & 0x00000001))
+ DELAY(10);
+
+ /* clear SATA error register */
+ ATA_IDX_OUTL(ch, ATA_SERROR, ATA_IDX_INL(ch, ATA_SERROR));
+
+ /* clear any outstanding error interrupts */
+ ATA_OUTL(ctlr->r_res1, 0x02008 + ATA_MV_EDMA_BASE(ch), 0x0);
+
+ /* unmask all error interrupts */
+ ATA_OUTL(ctlr->r_res1, 0x0200c + ATA_MV_EDMA_BASE(ch), ~0x0);
+
+ /* enable channel and test for devices */
+ ata_sata_phy_enable(ch);
+
+ /* enable EDMA machinery */
+ ATA_OUTL(ctlr->r_res1, 0x02028 + ATA_MV_EDMA_BASE(ch), 0x00000001);
+}
+
+static void
+ata_marvell_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct ata_dmasetprd_args *args = xsc;
+ struct ata_marvell_dma_prdentry *prd = args->dmatab;
+ int i;
+
+ if ((args->error = error))
+ return;
+
+ for (i = 0; i < nsegs; i++) {
+ prd[i].addrlo = htole32(segs[i].ds_addr);
+ prd[i].addrhi = 0;
+ prd[i].count = htole32(segs[i].ds_len);
+ }
+ prd[i - 1].count |= htole32(ATA_DMA_EOT);
+}
+
+static void
+ata_marvell_dmainit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ata_dmainit(dev);
+ if (ch->dma) {
+ /* note start and stop are not used here */
+ ch->dma->setprd = ata_marvell_dmasetprd;
+ }
+}
+
+
+/*
+ * National chipset support functions
+ */
+int
+ata_national_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ /* this chip is a clone of the Cyrix chip, bugs and all */
+ if (pci_get_devid(dev) == ATA_SC1100) {
+ device_set_desc(dev, "National Geode SC1100 ATA33 controller");
+ ctlr->chipinit = ata_national_chipinit;
+ return 0;
+ }
+ return ENXIO;
+}
+
+static int
+ata_national_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ ctlr->setmode = ata_national_setmode;
+ return 0;
+}
+
+static void
+ata_national_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ u_int32_t piotiming[] =
+ { 0x9172d132, 0x21717121, 0x00803020, 0x20102010, 0x00100010,
+ 0x00803020, 0x20102010, 0x00100010,
+ 0x00100010, 0x00100010, 0x00100010 };
+ u_int32_t dmatiming[] = { 0x80077771, 0x80012121, 0x80002020 };
+ u_int32_t udmatiming[] = { 0x80921250, 0x80911140, 0x80911030 };
+ int error;
+
+ ch->dma->alignment = 16;
+ ch->dma->max_iosize = 126 * DEV_BSIZE;
+
+ mode = ata_limit_mode(dev, mode, ATA_UDMA2);
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%s setting %s on National chip\n",
+ (error) ? "failed" : "success", ata_mode2str(mode));
+ if (!error) {
+ if (mode >= ATA_UDMA0) {
+ pci_write_config(gparent, 0x44 + (devno << 3),
+ udmatiming[mode & ATA_MODE_MASK], 4);
+ }
+ else if (mode >= ATA_WDMA0) {
+ pci_write_config(gparent, 0x44 + (devno << 3),
+ dmatiming[mode & ATA_MODE_MASK], 4);
+ }
+ else {
+ pci_write_config(gparent, 0x44 + (devno << 3),
+ pci_read_config(gparent, 0x44 + (devno << 3), 4) |
+ 0x80000000, 4);
+ }
+ pci_write_config(gparent, 0x40 + (devno << 3),
+ piotiming[ata_mode2idx(mode)], 4);
+ atadev->mode = mode;
+ }
+}
+
+
+/*
+ * nVidia chipset support functions
+ */
+int
+ata_nvidia_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_NFORCE1, 0, AMDNVIDIA, NVIDIA, ATA_UDMA5, "nForce" },
+ { ATA_NFORCE2, 0, AMDNVIDIA, NVIDIA, ATA_UDMA6, "nForce2" },
+ { ATA_NFORCE2_PRO, 0, AMDNVIDIA, NVIDIA, ATA_UDMA6, "nForce2 Pro" },
+ { ATA_NFORCE2_PRO_S1, 0, 0, 0, ATA_SA150, "nForce2 Pro" },
+ { ATA_NFORCE3, 0, AMDNVIDIA, NVIDIA, ATA_UDMA6, "nForce3" },
+ { ATA_NFORCE3_PRO, 0, AMDNVIDIA, NVIDIA, ATA_UDMA6, "nForce3 Pro" },
+ { ATA_NFORCE3_PRO_S1, 0, 0, 0, ATA_SA150, "nForce3 Pro" },
+ { ATA_NFORCE3_PRO_S2, 0, 0, 0, ATA_SA150, "nForce3 Pro" },
+ { ATA_NFORCE_MCP04, 0, AMDNVIDIA, NVIDIA, ATA_UDMA6, "nForce MCP" },
+ { ATA_NFORCE_MCP04_S1, 0, 0, NV4, ATA_SA150, "nForce MCP" },
+ { ATA_NFORCE_MCP04_S2, 0, 0, NV4, ATA_SA150, "nForce MCP" },
+ { ATA_NFORCE_CK804, 0, AMDNVIDIA, NVIDIA, ATA_UDMA6, "nForce CK804" },
+ { ATA_NFORCE_CK804_S1, 0, 0, NV4, ATA_SA300, "nForce CK804" },
+ { ATA_NFORCE_CK804_S2, 0, 0, NV4, ATA_SA300, "nForce CK804" },
+ { ATA_NFORCE_MCP51, 0, AMDNVIDIA, NVIDIA, ATA_UDMA6, "nForce MCP51" },
+ { ATA_NFORCE_MCP51_S1, 0, 0, NV4|NVQ, ATA_SA300, "nForce MCP51" },
+ { ATA_NFORCE_MCP51_S2, 0, 0, NV4|NVQ, ATA_SA300, "nForce MCP51" },
+ { ATA_NFORCE_MCP55, 0, AMDNVIDIA, NVIDIA, ATA_UDMA6, "nForce MCP55" },
+ { ATA_NFORCE_MCP55_S1, 0, 0, NV4|NVQ, ATA_SA300, "nForce MCP55" },
+ { ATA_NFORCE_MCP55_S2, 0, 0, NV4|NVQ, ATA_SA300, "nForce MCP55" },
+ { 0, 0, 0, 0, 0, 0}} ;
+ char buffer[64] ;
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ sprintf(buffer, "nVidia %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_nvidia_chipinit;
+ return 0;
+}
+
+static int
+ata_nvidia_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ if (ctlr->chip->max_dma >= ATA_SA150) {
+ if (pci_read_config(dev, PCIR_BAR(5), 1) & 1)
+ ctlr->r_type2 = SYS_RES_IOPORT;
+ else
+ ctlr->r_type2 = SYS_RES_MEMORY;
+ ctlr->r_rid2 = PCIR_BAR(5);
+ if ((ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2, RF_ACTIVE))) {
+ int offset = ctlr->chip->cfg2 & NV4 ? 0x0440 : 0x0010;
+
+ ctlr->allocate = ata_nvidia_allocate;
+ ctlr->reset = ata_nvidia_reset;
+
+ /* enable control access */
+ pci_write_config(dev, 0x50, pci_read_config(dev, 0x50, 1) | 0x04,1);
+
+ if (ctlr->chip->cfg2 & NVQ) {
+ /* clear interrupt status */
+ ATA_OUTL(ctlr->r_res2, offset, 0x00ff00ff);
+
+ /* enable device and PHY state change interrupts */
+ ATA_OUTL(ctlr->r_res2, offset + 4, 0x000d000d);
+
+ /* disable NCQ support */
+ ATA_OUTL(ctlr->r_res2, 0x0400,
+ ATA_INL(ctlr->r_res2, 0x0400) & 0xfffffff9);
+ }
+ else {
+ /* clear interrupt status */
+ ATA_OUTB(ctlr->r_res2, offset, 0xff);
+
+ /* enable device and PHY state change interrupts */
+ ATA_OUTB(ctlr->r_res2, offset + 1, 0xdd);
+ }
+
+ /* enable PCI interrupt */
+ pci_write_config(dev, PCIR_COMMAND,
+ pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400,2);
+
+ }
+ ctlr->setmode = ata_sata_setmode;
+ }
+ else {
+ /* disable prefetch, postwrite */
+ pci_write_config(dev, 0x51, pci_read_config(dev, 0x51, 1) & 0x0f, 1);
+ ctlr->setmode = ata_via_family_setmode;
+ }
+ return 0;
+}
+
+static int
+ata_nvidia_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* setup the usual register normal pci style */
+ if (ata_pci_allocate(dev))
+ return ENXIO;
+
+ ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
+ ch->r_io[ATA_SSTATUS].offset = (ch->unit << 6);
+ ch->r_io[ATA_SERROR].res = ctlr->r_res2;
+ ch->r_io[ATA_SERROR].offset = 0x04 + (ch->unit << 6);
+ ch->r_io[ATA_SCONTROL].res = ctlr->r_res2;
+ ch->r_io[ATA_SCONTROL].offset = 0x08 + (ch->unit << 6);
+
+ ch->hw.status = ata_nvidia_status;
+ ch->flags |= ATA_NO_SLAVE;
+
+ return 0;
+}
+
+static int
+ata_nvidia_status(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int offset = ctlr->chip->cfg2 & NV4 ? 0x0440 : 0x0010;
+ struct ata_connect_task *tp;
+ int shift = ch->unit << (ctlr->chip->cfg2 & NVQ ? 4 : 2);
+ u_int32_t status;
+
+ /* get and clear interrupt status */
+ if (ctlr->chip->cfg2 & NVQ) {
+ status = ATA_INL(ctlr->r_res2, offset);
+ ATA_OUTL(ctlr->r_res2, offset, (0x0f << shift) | 0x00f000f0);
+ }
+ else {
+ status = ATA_INB(ctlr->r_res2, offset);
+ ATA_OUTB(ctlr->r_res2, offset, (0x0f << shift));
+ }
+
+ /* check for and handle connect events */
+ if (((status & (0x0c << shift)) == (0x04 << shift)) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ if (bootverbose)
+ device_printf(ch->dev, "CONNECT requested\n");
+ tp->action = ATA_C_ATTACH;
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+
+ /* check for and handle disconnect events */
+ if ((status & (0x08 << shift)) &&
+ !((status & (0x04 << shift) && ATA_IDX_INL(ch, ATA_SSTATUS))) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ if (bootverbose)
+ device_printf(ch->dev, "DISCONNECT requested\n");
+ tp->action = ATA_C_DETACH;
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+
+ /* do we have any device action ? */
+ return (status & (0x01 << shift));
+}
+
+static void
+ata_nvidia_reset(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ata_sata_phy_enable(ch);
+}
+
+
+/*
+ * Promise chipset support functions
+ */
+#define ATA_PDC_APKT_OFFSET 0x00000010
+#define ATA_PDC_HPKT_OFFSET 0x00000040
+#define ATA_PDC_ASG_OFFSET 0x00000080
+#define ATA_PDC_LSG_OFFSET 0x000000c0
+#define ATA_PDC_HSG_OFFSET 0x00000100
+#define ATA_PDC_CHN_OFFSET 0x00000400
+#define ATA_PDC_BUF_BASE 0x00400000
+#define ATA_PDC_BUF_OFFSET 0x00100000
+#define ATA_PDC_MAX_HPKT 8
+#define ATA_PDC_WRITE_REG 0x00
+#define ATA_PDC_WRITE_CTL 0x0e
+#define ATA_PDC_WRITE_END 0x08
+#define ATA_PDC_WAIT_NBUSY 0x10
+#define ATA_PDC_WAIT_READY 0x18
+#define ATA_PDC_1B 0x20
+#define ATA_PDC_2B 0x40
+
+struct host_packet {
+u_int32_t addr;
+ TAILQ_ENTRY(host_packet) chain;
+};
+
+struct ata_promise_sx4 {
+ struct mtx mtx;
+ TAILQ_HEAD(, host_packet) queue;
+ int busy;
+};
+
+int
+ata_promise_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_PDC20246, 0, PROLD, 0x00, ATA_UDMA2, "PDC20246" },
+ { ATA_PDC20262, 0, PRNEW, 0x00, ATA_UDMA4, "PDC20262" },
+ { ATA_PDC20263, 0, PRNEW, 0x00, ATA_UDMA4, "PDC20263" },
+ { ATA_PDC20265, 0, PRNEW, 0x00, ATA_UDMA5, "PDC20265" },
+ { ATA_PDC20267, 0, PRNEW, 0x00, ATA_UDMA5, "PDC20267" },
+ { ATA_PDC20268, 0, PRTX, PRTX4, ATA_UDMA5, "PDC20268" },
+ { ATA_PDC20269, 0, PRTX, 0x00, ATA_UDMA6, "PDC20269" },
+ { ATA_PDC20270, 0, PRTX, PRTX4, ATA_UDMA5, "PDC20270" },
+ { ATA_PDC20271, 0, PRTX, 0x00, ATA_UDMA6, "PDC20271" },
+ { ATA_PDC20275, 0, PRTX, 0x00, ATA_UDMA6, "PDC20275" },
+ { ATA_PDC20276, 0, PRTX, PRSX6K, ATA_UDMA6, "PDC20276" },
+ { ATA_PDC20277, 0, PRTX, 0x00, ATA_UDMA6, "PDC20277" },
+ { ATA_PDC20318, 0, PRMIO, PRSATA, ATA_SA150, "PDC20318" },
+ { ATA_PDC20319, 0, PRMIO, PRSATA, ATA_SA150, "PDC20319" },
+ { ATA_PDC20371, 0, PRMIO, PRCMBO, ATA_SA150, "PDC20371" },
+ { ATA_PDC20375, 0, PRMIO, PRCMBO, ATA_SA150, "PDC20375" },
+ { ATA_PDC20376, 0, PRMIO, PRCMBO, ATA_SA150, "PDC20376" },
+ { ATA_PDC20377, 0, PRMIO, PRCMBO, ATA_SA150, "PDC20377" },
+ { ATA_PDC20378, 0, PRMIO, PRCMBO, ATA_SA150, "PDC20378" },
+ { ATA_PDC20379, 0, PRMIO, PRCMBO, ATA_SA150, "PDC20379" },
+ { ATA_PDC20571, 0, PRMIO, PRCMBO2, ATA_SA150, "PDC20571" },
+ { ATA_PDC20575, 0, PRMIO, PRCMBO2, ATA_SA150, "PDC20575" },
+ { ATA_PDC20579, 0, PRMIO, PRCMBO2, ATA_SA150, "PDC20579" },
+ { ATA_PDC20771, 0, PRMIO, PRCMBO2, ATA_SA300, "PDC20771" },
+ { ATA_PDC40775, 0, PRMIO, PRCMBO2, ATA_SA300, "PDC40775" },
+ { ATA_PDC20617, 0, PRMIO, PRPATA, ATA_UDMA6, "PDC20617" },
+ { ATA_PDC20618, 0, PRMIO, PRPATA, ATA_UDMA6, "PDC20618" },
+ { ATA_PDC20619, 0, PRMIO, PRPATA, ATA_UDMA6, "PDC20619" },
+ { ATA_PDC20620, 0, PRMIO, PRPATA, ATA_UDMA6, "PDC20620" },
+ { ATA_PDC20621, 0, PRMIO, PRSX4X, ATA_UDMA5, "PDC20621" },
+ { ATA_PDC20622, 0, PRMIO, PRSX4X, ATA_SA150, "PDC20622" },
+ { ATA_PDC40518, 0, PRMIO, PRSATA2, ATA_SA150, "PDC40518" },
+ { ATA_PDC40519, 0, PRMIO, PRSATA2, ATA_SA150, "PDC40519" },
+ { ATA_PDC40718, 0, PRMIO, PRSATA2, ATA_SA300, "PDC40718" },
+ { ATA_PDC40719, 0, PRMIO, PRSATA2, ATA_SA300, "PDC40719" },
+ { ATA_PDC40779, 0, PRMIO, PRSATA2, ATA_SA300, "PDC40779" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+ uintptr_t devid = 0;
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ /* if we are on a SuperTrak SX6000 dont attach */
+ if ((idx->cfg2 & PRSX6K) && pci_get_class(GRANDPARENT(dev))==PCIC_BRIDGE &&
+ !BUS_READ_IVAR(device_get_parent(GRANDPARENT(dev)),
+ GRANDPARENT(dev), PCI_IVAR_DEVID, &devid) &&
+ devid == ATA_I960RM)
+ return ENXIO;
+
+ strcpy(buffer, "Promise ");
+ strcat(buffer, idx->text);
+
+ /* if we are on a FastTrak TX4, adjust the interrupt resource */
+ if ((idx->cfg2 & PRTX4) && pci_get_class(GRANDPARENT(dev))==PCIC_BRIDGE &&
+ !BUS_READ_IVAR(device_get_parent(GRANDPARENT(dev)),
+ GRANDPARENT(dev), PCI_IVAR_DEVID, &devid) &&
+ ((devid == ATA_DEC_21150) || (devid == ATA_DEC_21150_1))) {
+ static long start = 0, end = 0;
+
+ if (pci_get_slot(dev) == 1) {
+ bus_get_resource(dev, SYS_RES_IRQ, 0, &start, &end);
+ strcat(buffer, " (channel 0+1)");
+ }
+ else if (pci_get_slot(dev) == 2 && start && end) {
+ bus_set_resource(dev, SYS_RES_IRQ, 0, start, end);
+ strcat(buffer, " (channel 2+3)");
+ }
+ else {
+ start = end = 0;
+ }
+ }
+ sprintf(buffer, "%s %s controller", buffer, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_promise_chipinit;
+ return 0;
+}
+
+static int
+ata_promise_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ int fake_reg, stat_reg;
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ switch (ctlr->chip->cfg1) {
+ case PRNEW:
+ /* setup clocks */
+ ATA_OUTB(ctlr->r_res1, 0x11, ATA_INB(ctlr->r_res1, 0x11) | 0x0a);
+
+ ctlr->dmainit = ata_promise_dmainit;
+ /* FALLTHROUGH */
+
+ case PROLD:
+ /* enable burst mode */
+ ATA_OUTB(ctlr->r_res1, 0x1f, ATA_INB(ctlr->r_res1, 0x1f) | 0x01);
+ ctlr->allocate = ata_promise_allocate;
+ ctlr->setmode = ata_promise_setmode;
+ return 0;
+
+ case PRTX:
+ ctlr->allocate = ata_promise_tx2_allocate;
+ ctlr->setmode = ata_promise_setmode;
+ return 0;
+
+ case PRMIO:
+ ctlr->r_type1 = SYS_RES_MEMORY;
+ ctlr->r_rid1 = PCIR_BAR(4);
+ if (!(ctlr->r_res1 = bus_alloc_resource_any(dev, ctlr->r_type1,
+ &ctlr->r_rid1, RF_ACTIVE)))
+ goto failnfree;
+
+ ctlr->r_type2 = SYS_RES_MEMORY;
+ ctlr->r_rid2 = PCIR_BAR(3);
+ if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2, RF_ACTIVE)))
+ goto failnfree;
+
+ if (ctlr->chip->cfg2 == PRSX4X) {
+ struct ata_promise_sx4 *hpkt;
+ u_int32_t dimm = ATA_INL(ctlr->r_res2, 0x000c0080);
+
+ if (bus_teardown_intr(dev, ctlr->r_irq, ctlr->handle) ||
+ bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS,
+ ata_promise_sx4_intr, ctlr, &ctlr->handle)) {
+ device_printf(dev, "unable to setup interrupt\n");
+ goto failnfree;
+ }
+
+ /* print info about cache memory */
+ device_printf(dev, "DIMM size %dMB @ 0x%08x%s\n",
+ (((dimm >> 16) & 0xff)-((dimm >> 24) & 0xff)+1) << 4,
+ ((dimm >> 24) & 0xff),
+ ATA_INL(ctlr->r_res2, 0x000c0088) & (1<<16) ?
+ " ECC enabled" : "" );
+
+ /* adjust cache memory parameters */
+ ATA_OUTL(ctlr->r_res2, 0x000c000c,
+ (ATA_INL(ctlr->r_res2, 0x000c000c) & 0xffff0000));
+
+ /* setup host packet controls */
+ hpkt = malloc(sizeof(struct ata_promise_sx4),
+ M_TEMP, M_NOWAIT | M_ZERO);
+ mtx_init(&hpkt->mtx, "ATA promise HPKT lock", NULL, MTX_DEF);
+ TAILQ_INIT(&hpkt->queue);
+ hpkt->busy = 0;
+ device_set_ivars(dev, hpkt);
+ ctlr->allocate = ata_promise_mio_allocate;
+ ctlr->reset = ata_promise_mio_reset;
+ ctlr->dmainit = ata_promise_mio_dmainit;
+ ctlr->setmode = ata_promise_setmode;
+ ctlr->channels = 4;
+ return 0;
+ }
+
+ /* mio type controllers need an interrupt intercept */
+ if (bus_teardown_intr(dev, ctlr->r_irq, ctlr->handle) ||
+ bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS,
+ ata_promise_mio_intr, ctlr, &ctlr->handle)) {
+ device_printf(dev, "unable to setup interrupt\n");
+ goto failnfree;
+ }
+
+ switch (ctlr->chip->cfg2) {
+ case PRPATA:
+ ctlr->channels = ((ATA_INL(ctlr->r_res2, 0x48) & 0x01) > 0) +
+ ((ATA_INL(ctlr->r_res2, 0x48) & 0x02) > 0) + 2;
+ goto sata150;
+ case PRCMBO:
+ ctlr->channels = 3;
+ goto sata150;
+ case PRSATA:
+ ctlr->channels = 4;
+sata150:
+ fake_reg = 0x60;
+ stat_reg = 0x6c;
+ break;
+
+ case PRCMBO2:
+ ctlr->channels = 3;
+ goto sataii;
+ case PRSATA2:
+ default:
+ ctlr->channels = 4;
+sataii:
+ fake_reg = 0x54;
+ stat_reg = 0x60;
+ break;
+ }
+
+ /* prime fake interrupt register */
+ ATA_OUTL(ctlr->r_res2, fake_reg, 0xffffffff);
+
+ /* clear SATA status */
+ ATA_OUTL(ctlr->r_res2, stat_reg, 0x000000ff);
+
+ ctlr->allocate = ata_promise_mio_allocate;
+ ctlr->reset = ata_promise_mio_reset;
+ ctlr->dmainit = ata_promise_mio_dmainit;
+ ctlr->setmode = ata_promise_mio_setmode;
+
+ return 0;
+ }
+
+failnfree:
+ if (ctlr->r_res2)
+ bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2);
+ if (ctlr->r_res1)
+ bus_release_resource(dev, ctlr->r_type1, ctlr->r_rid1, ctlr->r_res1);
+ return ENXIO;
+}
+
+static int
+ata_promise_allocate(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ata_pci_allocate(dev))
+ return ENXIO;
+
+ ch->hw.status = ata_promise_status;
+ return 0;
+}
+
+static int
+ata_promise_status(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ATA_INL(ctlr->r_res1, 0x1c) & (ch->unit ? 0x00004000 : 0x00000400)) {
+ return ata_pci_status(dev);
+ }
+ return 0;
+}
+
+static int
+ata_promise_dmastart(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(GRANDPARENT(dev));
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if (atadev->flags & ATA_D_48BIT_ACTIVE) {
+ ATA_OUTB(ctlr->r_res1, 0x11,
+ ATA_INB(ctlr->r_res1, 0x11) | (ch->unit ? 0x08 : 0x02));
+ ATA_OUTL(ctlr->r_res1, ch->unit ? 0x24 : 0x20,
+ ((ch->dma->flags & ATA_DMA_READ) ? 0x05000000 : 0x06000000) |
+ (ch->dma->cur_iosize >> 1));
+ }
+ ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, (ATA_IDX_INB(ch, ATA_BMSTAT_PORT) |
+ (ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR)));
+ ATA_IDX_OUTL(ch, ATA_BMDTP_PORT, ch->dma->sg_bus);
+ ATA_IDX_OUTB(ch, ATA_BMCMD_PORT,
+ ((ch->dma->flags & ATA_DMA_READ) ? ATA_BMCMD_WRITE_READ : 0) |
+ ATA_BMCMD_START_STOP);
+ ch->flags |= ATA_DMA_ACTIVE;
+ return 0;
+}
+
+static int
+ata_promise_dmastop(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(GRANDPARENT(dev));
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int error;
+
+ if (atadev->flags & ATA_D_48BIT_ACTIVE) {
+ ATA_OUTB(ctlr->r_res1, 0x11,
+ ATA_INB(ctlr->r_res1, 0x11) & ~(ch->unit ? 0x08 : 0x02));
+ ATA_OUTL(ctlr->r_res1, ch->unit ? 0x24 : 0x20, 0);
+ }
+ error = ATA_IDX_INB(ch, ATA_BMSTAT_PORT);
+ ATA_IDX_OUTB(ch, ATA_BMCMD_PORT,
+ ATA_IDX_INB(ch, ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP);
+ ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR);
+ ch->flags &= ~ATA_DMA_ACTIVE;
+ return error;
+}
+
+static void
+ata_promise_dmareset(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ATA_IDX_OUTB(ch, ATA_BMCMD_PORT,
+ ATA_IDX_INB(ch, ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP);
+ ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR);
+ ch->flags &= ~ATA_DMA_ACTIVE;
+}
+
+static void
+ata_promise_dmainit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ata_dmainit(dev);
+ if (ch->dma) {
+ ch->dma->start = ata_promise_dmastart;
+ ch->dma->stop = ata_promise_dmastop;
+ ch->dma->reset = ata_promise_dmareset;
+ }
+}
+
+static void
+ata_promise_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int error;
+ u_int32_t timings[][2] = {
+ /* PROLD PRNEW mode */
+ { 0x004ff329, 0x004fff2f }, /* PIO 0 */
+ { 0x004fec25, 0x004ff82a }, /* PIO 1 */
+ { 0x004fe823, 0x004ff026 }, /* PIO 2 */
+ { 0x004fe622, 0x004fec24 }, /* PIO 3 */
+ { 0x004fe421, 0x004fe822 }, /* PIO 4 */
+ { 0x004567f3, 0x004acef6 }, /* MWDMA 0 */
+ { 0x004467f3, 0x0048cef6 }, /* MWDMA 1 */
+ { 0x004367f3, 0x0046cef6 }, /* MWDMA 2 */
+ { 0x004367f3, 0x0046cef6 }, /* UDMA 0 */
+ { 0x004247f3, 0x00448ef6 }, /* UDMA 1 */
+ { 0x004127f3, 0x00436ef6 }, /* UDMA 2 */
+ { 0, 0x00424ef6 }, /* UDMA 3 */
+ { 0, 0x004127f3 }, /* UDMA 4 */
+ { 0, 0x004127f3 } /* UDMA 5 */
+ };
+
+ mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
+
+ switch (ctlr->chip->cfg1) {
+ case PROLD:
+ case PRNEW:
+ if (mode > ATA_UDMA2 && (pci_read_config(gparent, 0x50, 2) &
+ (ch->unit ? 1 << 11 : 1 << 10))) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+ if (ata_atapi(dev) && mode > ATA_PIO_MAX)
+ mode = ata_limit_mode(dev, mode, ATA_PIO_MAX);
+ break;
+
+ case PRTX:
+ ATA_IDX_OUTB(ch, ATA_BMDEVSPEC_0, 0x0b);
+ if (mode > ATA_UDMA2 &&
+ ATA_IDX_INB(ch, ATA_BMDEVSPEC_1) & 0x04) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+ break;
+
+ case PRMIO:
+ if (mode > ATA_UDMA2 &&
+ (ATA_INL(ctlr->r_res2,
+ (ctlr->chip->cfg2 & PRSX4X ? 0x000c0260 : 0x0260) +
+ (ch->unit << 7)) & 0x01000000)) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+ break;
+ }
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "",
+ ata_mode2str(mode), ctlr->chip->text);
+ if (!error) {
+ if (ctlr->chip->cfg1 < PRTX)
+ pci_write_config(gparent, 0x60 + (devno << 2),
+ timings[ata_mode2idx(mode)][ctlr->chip->cfg1], 4);
+ atadev->mode = mode;
+ }
+ return;
+}
+
+static int
+ata_promise_tx2_allocate(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ata_pci_allocate(dev))
+ return ENXIO;
+
+ ch->hw.status = ata_promise_tx2_status;
+ return 0;
+}
+
+static int
+ata_promise_tx2_status(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ATA_IDX_OUTB(ch, ATA_BMDEVSPEC_0, 0x0b);
+ if (ATA_IDX_INB(ch, ATA_BMDEVSPEC_1) & 0x20) {
+ return ata_pci_status(dev);
+ }
+ return 0;
+}
+
+static int
+ata_promise_mio_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int offset = (ctlr->chip->cfg2 & PRSX4X) ? 0x000c0000 : 0;
+ int i;
+
+ for (i = ATA_DATA; i <= ATA_COMMAND; i++) {
+ ch->r_io[i].res = ctlr->r_res2;
+ ch->r_io[i].offset = offset + 0x0200 + (i << 2) + (ch->unit << 7);
+ }
+ ch->r_io[ATA_CONTROL].res = ctlr->r_res2;
+ ch->r_io[ATA_CONTROL].offset = offset + 0x0238 + (ch->unit << 7);
+ ch->r_io[ATA_IDX_ADDR].res = ctlr->r_res2;
+ ata_default_registers(dev);
+ if ((ctlr->chip->cfg2 & (PRSATA | PRSATA2)) ||
+ ((ctlr->chip->cfg2 & (PRCMBO | PRCMBO2)) && ch->unit < 2)) {
+ ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
+ ch->r_io[ATA_SSTATUS].offset = 0x400 + (ch->unit << 8);
+ ch->r_io[ATA_SERROR].res = ctlr->r_res2;
+ ch->r_io[ATA_SERROR].offset = 0x404 + (ch->unit << 8);
+ ch->r_io[ATA_SCONTROL].res = ctlr->r_res2;
+ ch->r_io[ATA_SCONTROL].offset = 0x408 + (ch->unit << 8);
+ ch->flags |= ATA_NO_SLAVE;
+ }
+ ch->flags |= ATA_USE_16BIT;
+
+ ata_generic_hw(dev);
+ if (ctlr->chip->cfg2 & PRSX4X) {
+ ch->hw.command = ata_promise_sx4_command;
+ }
+ else {
+ ch->hw.command = ata_promise_mio_command;
+ ch->hw.status = ata_promise_mio_status;
+ }
+ return 0;
+}
+
+static void
+ata_promise_mio_intr(void *data)
+{
+ struct ata_pci_controller *ctlr = data;
+ struct ata_channel *ch;
+ u_int32_t vector;
+ int unit, fake_reg;
+
+ switch (ctlr->chip->cfg2) {
+ case PRPATA:
+ case PRCMBO:
+ case PRSATA:
+ fake_reg = 0x60;
+ break;
+ case PRCMBO2:
+ case PRSATA2:
+ default:
+ fake_reg = 0x54;
+ break;
+ }
+
+ /*
+ * since reading interrupt status register on early "mio" chips
+ * clears the status bits we cannot read it for each channel later on
+ * in the generic interrupt routine.
+ * store the bits in an unused register in the chip so we can read
+ * it from there safely to get around this "feature".
+ */
+ vector = ATA_INL(ctlr->r_res2, 0x040);
+ ATA_OUTL(ctlr->r_res2, 0x040, vector);
+ ATA_OUTL(ctlr->r_res2, fake_reg, vector);
+
+ for (unit = 0; unit < ctlr->channels; unit++) {
+ if ((ch = ctlr->interrupt[unit].argument))
+ ctlr->interrupt[unit].function(ch);
+ }
+
+ ATA_OUTL(ctlr->r_res2, fake_reg, 0xffffffff);
+}
+
+static int
+ata_promise_mio_status(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ struct ata_connect_task *tp;
+ u_int32_t fake_reg, stat_reg, vector, status;
+
+ switch (ctlr->chip->cfg2) {
+ case PRPATA:
+ case PRCMBO:
+ case PRSATA:
+ fake_reg = 0x60;
+ stat_reg = 0x6c;
+ break;
+ case PRCMBO2:
+ case PRSATA2:
+ default:
+ fake_reg = 0x54;
+ stat_reg = 0x60;
+ break;
+ }
+
+ /* read and acknowledge interrupt */
+ vector = ATA_INL(ctlr->r_res2, fake_reg);
+
+ /* read and clear interface status */
+ status = ATA_INL(ctlr->r_res2, stat_reg);
+ ATA_OUTL(ctlr->r_res2, stat_reg, status & (0x00000011 << ch->unit));
+
+ /* check for and handle disconnect events */
+ if ((status & (0x00000001 << ch->unit)) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ if (bootverbose)
+ device_printf(ch->dev, "DISCONNECT requested\n");
+ tp->action = ATA_C_DETACH;
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+
+ /* check for and handle connect events */
+ if ((status & (0x00000010 << ch->unit)) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ if (bootverbose)
+ device_printf(ch->dev, "CONNECT requested\n");
+ tp->action = ATA_C_ATTACH;
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+
+ /* do we have any device action ? */
+ return (vector & (1 << (ch->unit + 1)));
+}
+
+static int
+ata_promise_mio_command(struct ata_request *request)
+{
+ struct ata_pci_controller *ctlr=device_get_softc(GRANDPARENT(request->dev));
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ u_int32_t *wordp = (u_int32_t *)ch->dma->work;
+
+ ATA_OUTL(ctlr->r_res2, (ch->unit + 1) << 2, 0x00000001);
+
+ /* XXX SOS add ATAPI commands support later */
+ switch (request->u.ata.command) {
+ default:
+ return ata_generic_command(request);
+
+ case ATA_READ_DMA:
+ case ATA_READ_DMA48:
+ wordp[0] = htole32(0x04 | ((ch->unit + 1) << 16) | (0x00 << 24));
+ break;
+
+ case ATA_WRITE_DMA:
+ case ATA_WRITE_DMA48:
+ wordp[0] = htole32(0x00 | ((ch->unit + 1) << 16) | (0x00 << 24));
+ break;
+ }
+ wordp[1] = htole32(ch->dma->sg_bus);
+ wordp[2] = 0;
+ ata_promise_apkt((u_int8_t*)wordp, request);
+
+ ATA_OUTL(ctlr->r_res2, 0x0240 + (ch->unit << 7), ch->dma->work_bus);
+ return 0;
+}
+
+static void
+ata_promise_mio_reset(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ struct ata_promise_sx4 *hpktp;
+
+ switch (ctlr->chip->cfg2) {
+ case PRSX4X:
+
+ /* softreset channel ATA module */
+ hpktp = device_get_ivars(ctlr->dev);
+ ATA_OUTL(ctlr->r_res2, 0xc0260 + (ch->unit << 7), ch->unit + 1);
+ ata_udelay(1000);
+ ATA_OUTL(ctlr->r_res2, 0xc0260 + (ch->unit << 7),
+ (ATA_INL(ctlr->r_res2, 0xc0260 + (ch->unit << 7)) &
+ ~0x00003f9f) | (ch->unit + 1));
+
+ /* softreset HOST module */ /* XXX SOS what about other outstandings */
+ mtx_lock(&hpktp->mtx);
+ ATA_OUTL(ctlr->r_res2, 0xc012c,
+ (ATA_INL(ctlr->r_res2, 0xc012c) & ~0x00000f9f) | (1 << 11));
+ DELAY(10);
+ ATA_OUTL(ctlr->r_res2, 0xc012c,
+ (ATA_INL(ctlr->r_res2, 0xc012c) & ~0x00000f9f));
+ hpktp->busy = 0;
+ mtx_unlock(&hpktp->mtx);
+ ata_generic_reset(dev);
+ break;
+
+ case PRPATA:
+ case PRCMBO:
+ case PRSATA:
+ if ((ctlr->chip->cfg2 == PRSATA) ||
+ ((ctlr->chip->cfg2 == PRCMBO) && (ch->unit < 2))) {
+
+ /* mask plug/unplug intr */
+ ATA_OUTL(ctlr->r_res2, 0x06c, (0x00110000 << ch->unit));
+ }
+
+ /* softreset channels ATA module */
+ ATA_OUTL(ctlr->r_res2, 0x0260 + (ch->unit << 7), (1 << 11));
+ ata_udelay(10000);
+ ATA_OUTL(ctlr->r_res2, 0x0260 + (ch->unit << 7),
+ (ATA_INL(ctlr->r_res2, 0x0260 + (ch->unit << 7)) &
+ ~0x00003f9f) | (ch->unit + 1));
+
+ if ((ctlr->chip->cfg2 == PRSATA) ||
+ ((ctlr->chip->cfg2 == PRCMBO) && (ch->unit < 2))) {
+
+ ata_sata_phy_enable(ch);
+
+ /* reset and enable plug/unplug intr */
+ ATA_OUTL(ctlr->r_res2, 0x06c, (0x00000011 << ch->unit));
+ }
+ else
+ ata_generic_reset(dev);
+ break;
+
+ case PRCMBO2:
+ case PRSATA2:
+ if ((ctlr->chip->cfg2 == PRSATA2) ||
+ ((ctlr->chip->cfg2 == PRCMBO2) && (ch->unit < 2))) {
+ /* set portmultiplier port */
+ ATA_OUTL(ctlr->r_res2, 0x4e8 + (ch->unit << 8), 0x0f);
+
+ /* mask plug/unplug intr */
+ ATA_OUTL(ctlr->r_res2, 0x060, (0x00110000 << ch->unit));
+ }
+
+ /* softreset channels ATA module */
+ ATA_OUTL(ctlr->r_res2, 0x0260 + (ch->unit << 7), (1 << 11));
+ ata_udelay(10000);
+ ATA_OUTL(ctlr->r_res2, 0x0260 + (ch->unit << 7),
+ (ATA_INL(ctlr->r_res2, 0x0260 + (ch->unit << 7)) &
+ ~0x00003f9f) | (ch->unit + 1));
+
+ if ((ctlr->chip->cfg2 == PRSATA2) ||
+ ((ctlr->chip->cfg2 == PRCMBO2) && (ch->unit < 2))) {
+
+ /* set PHY mode to "improved" */
+ ATA_OUTL(ctlr->r_res2, 0x414 + (ch->unit << 8),
+ (ATA_INL(ctlr->r_res2, 0x414 + (ch->unit << 8)) &
+ ~0x00000003) | 0x00000001);
+
+ ata_sata_phy_enable(ch);
+
+ /* reset and enable plug/unplug intr */
+ ATA_OUTL(ctlr->r_res2, 0x060, (0x00000011 << ch->unit));
+
+ /* set portmultiplier port */
+ ATA_OUTL(ctlr->r_res2, 0x4e8 + (ch->unit << 8), 0x00);
+ }
+ else
+ ata_generic_reset(dev);
+ break;
+
+ }
+}
+
+static void
+ata_promise_mio_dmainit(device_t dev)
+{
+ /* note start and stop are not used here */
+ ata_dmainit(dev);
+}
+
+static void
+ata_promise_mio_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+
+ if ( (ctlr->chip->cfg2 == PRSATA) ||
+ ((ctlr->chip->cfg2 == PRCMBO) && (ch->unit < 2)) ||
+ (ctlr->chip->cfg2 == PRSATA2) ||
+ ((ctlr->chip->cfg2 == PRCMBO2) && (ch->unit < 2)))
+ ata_sata_setmode(dev, mode);
+ else
+ ata_promise_setmode(dev, mode);
+}
+
+static void
+ata_promise_sx4_intr(void *data)
+{
+ struct ata_pci_controller *ctlr = data;
+ struct ata_channel *ch;
+ u_int32_t vector = ATA_INL(ctlr->r_res2, 0x000c0480);
+ int unit;
+
+ for (unit = 0; unit < ctlr->channels; unit++) {
+ if (vector & (1 << (unit + 1)))
+ if ((ch = ctlr->interrupt[unit].argument))
+ ctlr->interrupt[unit].function(ch);
+ if (vector & (1 << (unit + 5)))
+ if ((ch = ctlr->interrupt[unit].argument))
+ ata_promise_queue_hpkt(ctlr,
+ htole32((ch->unit * ATA_PDC_CHN_OFFSET) +
+ ATA_PDC_HPKT_OFFSET));
+ if (vector & (1 << (unit + 9))) {
+ ata_promise_next_hpkt(ctlr);
+ if ((ch = ctlr->interrupt[unit].argument))
+ ctlr->interrupt[unit].function(ch);
+ }
+ if (vector & (1 << (unit + 13))) {
+ ata_promise_next_hpkt(ctlr);
+ if ((ch = ctlr->interrupt[unit].argument))
+ ATA_OUTL(ctlr->r_res2, 0x000c0240 + (ch->unit << 7),
+ htole32((ch->unit * ATA_PDC_CHN_OFFSET) +
+ ATA_PDC_APKT_OFFSET));
+ }
+ }
+}
+
+static int
+ata_promise_sx4_command(struct ata_request *request)
+{
+ device_t gparent = GRANDPARENT(request->dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ struct ata_dma_prdentry *prd = ch->dma->sg;
+ caddr_t window = rman_get_virtual(ctlr->r_res1);
+ u_int32_t *wordp;
+ int i, idx, length = 0;
+
+ /* XXX SOS add ATAPI commands support later */
+ switch (request->u.ata.command) {
+
+ default:
+ return -1;
+
+ case ATA_ATA_IDENTIFY:
+ case ATA_READ:
+ case ATA_READ48:
+ case ATA_READ_MUL:
+ case ATA_READ_MUL48:
+ case ATA_WRITE:
+ case ATA_WRITE48:
+ case ATA_WRITE_MUL:
+ case ATA_WRITE_MUL48:
+ ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit + 1) << 2), 0x00000001);
+ return ata_generic_command(request);
+
+ case ATA_SETFEATURES:
+ case ATA_FLUSHCACHE:
+ case ATA_FLUSHCACHE48:
+ case ATA_SLEEP:
+ case ATA_SET_MULTI:
+ wordp = (u_int32_t *)
+ (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_APKT_OFFSET);
+ wordp[0] = htole32(0x08 | ((ch->unit + 1)<<16) | (0x00 << 24));
+ wordp[1] = 0;
+ wordp[2] = 0;
+ ata_promise_apkt((u_int8_t *)wordp, request);
+ ATA_OUTL(ctlr->r_res2, 0x000c0484, 0x00000001);
+ ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit + 1) << 2), 0x00000001);
+ ATA_OUTL(ctlr->r_res2, 0x000c0240 + (ch->unit << 7),
+ htole32((ch->unit * ATA_PDC_CHN_OFFSET)+ATA_PDC_APKT_OFFSET));
+ return 0;
+
+ case ATA_READ_DMA:
+ case ATA_READ_DMA48:
+ case ATA_WRITE_DMA:
+ case ATA_WRITE_DMA48:
+ wordp = (u_int32_t *)
+ (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_HSG_OFFSET);
+ i = idx = 0;
+ do {
+ wordp[idx++] = prd[i].addr;
+ wordp[idx++] = prd[i].count;
+ length += (prd[i].count & ~ATA_DMA_EOT);
+ } while (!(prd[i++].count & ATA_DMA_EOT));
+
+ wordp = (u_int32_t *)
+ (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_LSG_OFFSET);
+ wordp[0] = htole32((ch->unit * ATA_PDC_BUF_OFFSET) + ATA_PDC_BUF_BASE);
+ wordp[1] = htole32(request->bytecount | ATA_DMA_EOT);
+
+ wordp = (u_int32_t *)
+ (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_ASG_OFFSET);
+ wordp[0] = htole32((ch->unit * ATA_PDC_BUF_OFFSET) + ATA_PDC_BUF_BASE);
+ wordp[1] = htole32(request->bytecount | ATA_DMA_EOT);
+
+ wordp = (u_int32_t *)
+ (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_HPKT_OFFSET);
+ if (request->flags & ATA_R_READ)
+ wordp[0] = htole32(0x14 | ((ch->unit+9)<<16) | ((ch->unit+5)<<24));
+ if (request->flags & ATA_R_WRITE)
+ wordp[0] = htole32(0x00 | ((ch->unit+13)<<16) | (0x00<<24));
+ wordp[1] = htole32((ch->unit * ATA_PDC_CHN_OFFSET)+ATA_PDC_HSG_OFFSET);
+ wordp[2] = htole32((ch->unit * ATA_PDC_CHN_OFFSET)+ATA_PDC_LSG_OFFSET);
+ wordp[3] = 0;
+
+ wordp = (u_int32_t *)
+ (window + (ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_APKT_OFFSET);
+ if (request->flags & ATA_R_READ)
+ wordp[0] = htole32(0x04 | ((ch->unit+5)<<16) | (0x00<<24));
+ if (request->flags & ATA_R_WRITE)
+ wordp[0] = htole32(0x10 | ((ch->unit+1)<<16) | ((ch->unit+13)<<24));
+ wordp[1] = htole32((ch->unit * ATA_PDC_CHN_OFFSET)+ATA_PDC_ASG_OFFSET);
+ wordp[2] = 0;
+ ata_promise_apkt((u_int8_t *)wordp, request);
+ ATA_OUTL(ctlr->r_res2, 0x000c0484, 0x00000001);
+
+ if (request->flags & ATA_R_READ) {
+ ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit+5)<<2), 0x00000001);
+ ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit+9)<<2), 0x00000001);
+ ATA_OUTL(ctlr->r_res2, 0x000c0240 + (ch->unit << 7),
+ htole32((ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_APKT_OFFSET));
+ }
+ if (request->flags & ATA_R_WRITE) {
+ ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit+1)<<2), 0x00000001);
+ ATA_OUTL(ctlr->r_res2, 0x000c0400 + ((ch->unit+13)<<2), 0x00000001);
+ ata_promise_queue_hpkt(ctlr,
+ htole32((ch->unit * ATA_PDC_CHN_OFFSET) + ATA_PDC_HPKT_OFFSET));
+ }
+ return 0;
+ }
+}
+
+static int
+ata_promise_apkt(u_int8_t *bytep, struct ata_request *request)
+{
+ struct ata_device *atadev = device_get_softc(request->dev);
+ int i = 12;
+
+ bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_PDC_WAIT_NBUSY|ATA_DRIVE;
+ bytep[i++] = ATA_D_IBM | ATA_D_LBA | atadev->unit;
+ bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_CTL;
+ bytep[i++] = ATA_A_4BIT;
+
+ if (atadev->flags & ATA_D_48BIT_ACTIVE) {
+ bytep[i++] = ATA_PDC_2B | ATA_PDC_WRITE_REG | ATA_FEATURE;
+ bytep[i++] = request->u.ata.feature >> 8;
+ bytep[i++] = request->u.ata.feature;
+ bytep[i++] = ATA_PDC_2B | ATA_PDC_WRITE_REG | ATA_COUNT;
+ bytep[i++] = request->u.ata.count >> 8;
+ bytep[i++] = request->u.ata.count;
+ bytep[i++] = ATA_PDC_2B | ATA_PDC_WRITE_REG | ATA_SECTOR;
+ bytep[i++] = request->u.ata.lba >> 24;
+ bytep[i++] = request->u.ata.lba;
+ bytep[i++] = ATA_PDC_2B | ATA_PDC_WRITE_REG | ATA_CYL_LSB;
+ bytep[i++] = request->u.ata.lba >> 32;
+ bytep[i++] = request->u.ata.lba >> 8;
+ bytep[i++] = ATA_PDC_2B | ATA_PDC_WRITE_REG | ATA_CYL_MSB;
+ bytep[i++] = request->u.ata.lba >> 40;
+ bytep[i++] = request->u.ata.lba >> 16;
+ bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_DRIVE;
+ bytep[i++] = ATA_D_LBA | atadev->unit;
+ }
+ else {
+ bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_FEATURE;
+ bytep[i++] = request->u.ata.feature;
+ bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_COUNT;
+ bytep[i++] = request->u.ata.count;
+ bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_SECTOR;
+ bytep[i++] = request->u.ata.lba;
+ bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_CYL_LSB;
+ bytep[i++] = request->u.ata.lba >> 8;
+ bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_CYL_MSB;
+ bytep[i++] = request->u.ata.lba >> 16;
+ bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_REG | ATA_DRIVE;
+ bytep[i++] = (atadev->flags & ATA_D_USE_CHS ? 0 : ATA_D_LBA) |
+ ATA_D_IBM | atadev->unit | ((request->u.ata.lba >> 24)&0xf);
+ }
+ bytep[i++] = ATA_PDC_1B | ATA_PDC_WRITE_END | ATA_COMMAND;
+ bytep[i++] = request->u.ata.command;
+ return i;
+}
+
+static void
+ata_promise_queue_hpkt(struct ata_pci_controller *ctlr, u_int32_t hpkt)
+{
+ struct ata_promise_sx4 *hpktp = device_get_ivars(ctlr->dev);
+
+ mtx_lock(&hpktp->mtx);
+ if (hpktp->busy) {
+ struct host_packet *hp =
+ malloc(sizeof(struct host_packet), M_TEMP, M_NOWAIT | M_ZERO);
+ hp->addr = hpkt;
+ TAILQ_INSERT_TAIL(&hpktp->queue, hp, chain);
+ }
+ else {
+ hpktp->busy = 1;
+ ATA_OUTL(ctlr->r_res2, 0x000c0100, hpkt);
+ }
+ mtx_unlock(&hpktp->mtx);
+}
+
+static void
+ata_promise_next_hpkt(struct ata_pci_controller *ctlr)
+{
+ struct ata_promise_sx4 *hpktp = device_get_ivars(ctlr->dev);
+ struct host_packet *hp;
+
+ mtx_lock(&hpktp->mtx);
+ if ((hp = TAILQ_FIRST(&hpktp->queue))) {
+ TAILQ_REMOVE(&hpktp->queue, hp, chain);
+ ATA_OUTL(ctlr->r_res2, 0x000c0100, hp->addr);
+ free(hp, M_TEMP);
+ }
+ else
+ hpktp->busy = 0;
+ mtx_unlock(&hpktp->mtx);
+}
+
+
+/*
+ * ServerWorks chipset support functions
+ */
+int
+ata_serverworks_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_ROSB4, 0x00, SWKS33, 0, ATA_UDMA2, "ROSB4" },
+ { ATA_CSB5, 0x92, SWKS100, 0, ATA_UDMA5, "CSB5" },
+ { ATA_CSB5, 0x00, SWKS66, 0, ATA_UDMA4, "CSB5" },
+ { ATA_CSB6, 0x00, SWKS100, 0, ATA_UDMA5, "CSB6" },
+ { ATA_CSB6_1, 0x00, SWKS66, 0, ATA_UDMA4, "CSB6" },
+ { ATA_HT1000, 0x00, SWKS100, 0, ATA_UDMA5, "HT1000" },
+ { ATA_HT1000_S1, 0x00, SWKS100, 4, ATA_SA150, "HT1000" },
+ { ATA_HT1000_S2, 0x00, SWKSMIO, 4, ATA_SA150, "HT1000" },
+ { ATA_K2, 0x00, SWKSMIO, 4, ATA_SA150, "K2" },
+ { ATA_FRODO4, 0x00, SWKSMIO, 4, ATA_SA150, "Frodo4" },
+ { ATA_FRODO8, 0x00, SWKSMIO, 8, ATA_SA150, "Frodo8" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ sprintf(buffer, "ServerWorks %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_serverworks_chipinit;
+ return 0;
+}
+
+static int
+ata_serverworks_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ if (ctlr->chip->cfg1 == SWKSMIO) {
+ ctlr->r_type2 = SYS_RES_MEMORY;
+ ctlr->r_rid2 = PCIR_BAR(5);
+ if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2, RF_ACTIVE)))
+ return ENXIO;
+
+ ctlr->channels = ctlr->chip->cfg2;
+ ctlr->allocate = ata_serverworks_allocate;
+ ctlr->setmode = ata_sata_setmode;
+ return 0;
+ }
+ else if (ctlr->chip->cfg1 == SWKS33) {
+ device_t *children;
+ int nchildren, i;
+
+ /* locate the ISA part in the southbridge and enable UDMA33 */
+ if (!device_get_children(device_get_parent(dev), &children,&nchildren)){
+ for (i = 0; i < nchildren; i++) {
+ if (pci_get_devid(children[i]) == ATA_ROSB4_ISA) {
+ pci_write_config(children[i], 0x64,
+ (pci_read_config(children[i], 0x64, 4) &
+ ~0x00002000) | 0x00004000, 4);
+ break;
+ }
+ }
+ free(children, M_TEMP);
+ }
+ }
+ else {
+ pci_write_config(dev, 0x5a,
+ (pci_read_config(dev, 0x5a, 1) & ~0x40) |
+ (ctlr->chip->cfg1 == SWKS100) ? 0x03 : 0x02, 1);
+ }
+ ctlr->setmode = ata_serverworks_setmode;
+ return 0;
+}
+
+static int
+ata_serverworks_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int ch_offset;
+ int i;
+
+ ch_offset = ch->unit * 0x100;
+
+ for (i = ATA_DATA; i < ATA_MAX_RES; i++)
+ ch->r_io[i].res = ctlr->r_res2;
+
+ /* setup ATA registers */
+ ch->r_io[ATA_DATA].offset = ch_offset + 0x00;
+ ch->r_io[ATA_FEATURE].offset = ch_offset + 0x04;
+ ch->r_io[ATA_COUNT].offset = ch_offset + 0x08;
+ ch->r_io[ATA_SECTOR].offset = ch_offset + 0x0c;
+ ch->r_io[ATA_CYL_LSB].offset = ch_offset + 0x10;
+ ch->r_io[ATA_CYL_MSB].offset = ch_offset + 0x14;
+ ch->r_io[ATA_DRIVE].offset = ch_offset + 0x18;
+ ch->r_io[ATA_COMMAND].offset = ch_offset + 0x1c;
+ ch->r_io[ATA_CONTROL].offset = ch_offset + 0x20;
+ ata_default_registers(dev);
+
+ /* setup DMA registers */
+ ch->r_io[ATA_BMCMD_PORT].offset = ch_offset + 0x30;
+ ch->r_io[ATA_BMSTAT_PORT].offset = ch_offset + 0x32;
+ ch->r_io[ATA_BMDTP_PORT].offset = ch_offset + 0x34;
+
+ /* setup SATA registers */
+ ch->r_io[ATA_SSTATUS].offset = ch_offset + 0x40;
+ ch->r_io[ATA_SERROR].offset = ch_offset + 0x44;
+ ch->r_io[ATA_SCONTROL].offset = ch_offset + 0x48;
+
+ ch->flags |= ATA_NO_SLAVE;
+ ata_pci_hw(dev);
+ return 0;
+}
+
+static void
+ata_serverworks_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int offset = (devno ^ 0x01) << 3;
+ int error;
+ u_int8_t piotimings[] = { 0x5d, 0x47, 0x34, 0x22, 0x20, 0x34, 0x22, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
+ u_int8_t dmatimings[] = { 0x77, 0x21, 0x20 };
+
+ mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
+
+ mode = ata_check_80pin(dev, mode);
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "",
+ ata_mode2str(mode), ctlr->chip->text);
+ if (!error) {
+ if (mode >= ATA_UDMA0) {
+ pci_write_config(gparent, 0x56,
+ (pci_read_config(gparent, 0x56, 2) &
+ ~(0xf << (devno << 2))) |
+ ((mode & ATA_MODE_MASK) << (devno << 2)), 2);
+ pci_write_config(gparent, 0x54,
+ pci_read_config(gparent, 0x54, 1) |
+ (0x01 << devno), 1);
+ pci_write_config(gparent, 0x44,
+ (pci_read_config(gparent, 0x44, 4) &
+ ~(0xff << offset)) |
+ (dmatimings[2] << offset), 4);
+ }
+ else if (mode >= ATA_WDMA0) {
+ pci_write_config(gparent, 0x54,
+ pci_read_config(gparent, 0x54, 1) &
+ ~(0x01 << devno), 1);
+ pci_write_config(gparent, 0x44,
+ (pci_read_config(gparent, 0x44, 4) &
+ ~(0xff << offset)) |
+ (dmatimings[mode & ATA_MODE_MASK] << offset), 4);
+ }
+ else
+ pci_write_config(gparent, 0x54,
+ pci_read_config(gparent, 0x54, 1) &
+ ~(0x01 << devno), 1);
+
+ pci_write_config(gparent, 0x40,
+ (pci_read_config(gparent, 0x40, 4) &
+ ~(0xff << offset)) |
+ (piotimings[ata_mode2idx(mode)] << offset), 4);
+ atadev->mode = mode;
+ }
+}
+
+
+/*
+ * Silicon Image Inc. (SiI) (former CMD) chipset support functions
+ */
+int
+ata_sii_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_SII3114, 0x00, SIIMEMIO, SII4CH, ATA_SA150, "SiI 3114" },
+ { ATA_SII3512, 0x02, SIIMEMIO, 0, ATA_SA150, "SiI 3512" },
+ { ATA_SII3112, 0x02, SIIMEMIO, 0, ATA_SA150, "SiI 3112" },
+ { ATA_SII3112_1, 0x02, SIIMEMIO, 0, ATA_SA150, "SiI 3112" },
+ { ATA_SII3512, 0x00, SIIMEMIO, SIIBUG, ATA_SA150, "SiI 3512" },
+ { ATA_SII3112, 0x00, SIIMEMIO, SIIBUG, ATA_SA150, "SiI 3112" },
+ { ATA_SII3112_1, 0x00, SIIMEMIO, SIIBUG, ATA_SA150, "SiI 3112" },
+ { ATA_SII0680, 0x00, SIIMEMIO, SIISETCLK, ATA_UDMA6, "SiI 0680" },
+ { ATA_CMD649, 0x00, 0, SIIINTR, ATA_UDMA5, "CMD 649" },
+ { ATA_CMD648, 0x00, 0, SIIINTR, ATA_UDMA4, "CMD 648" },
+ { ATA_CMD646, 0x07, 0, 0, ATA_UDMA2, "CMD 646U2" },
+ { ATA_CMD646, 0x00, 0, 0, ATA_WDMA2, "CMD 646" },
+ { 0, 0, 0, 0, 0, 0}};
+ char buffer[64];
+
+ if (!(idx = ata_match_chip(dev, ids)))
+ return ENXIO;
+
+ sprintf(buffer, "%s %s controller", idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_sii_chipinit;
+ return 0;
+}
+
+static int
+ata_sii_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ if (ctlr->chip->cfg1 == SIIMEMIO) {
+ ctlr->r_type2 = SYS_RES_MEMORY;
+ ctlr->r_rid2 = PCIR_BAR(5);
+ if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2, RF_ACTIVE)))
+ return ENXIO;
+
+ if (ctlr->chip->cfg2 & SIISETCLK) {
+ if ((pci_read_config(dev, 0x8a, 1) & 0x30) != 0x10)
+ pci_write_config(dev, 0x8a,
+ (pci_read_config(dev, 0x8a, 1) & 0xcf)|0x10,1);
+ if ((pci_read_config(dev, 0x8a, 1) & 0x30) != 0x10)
+ device_printf(dev, "%s could not set ATA133 clock\n",
+ ctlr->chip->text);
+ }
+
+ /* if we have 4 channels enable the second set */
+ if (ctlr->chip->cfg2 & SII4CH) {
+ ATA_OUTL(ctlr->r_res2, 0x0200, 0x00000002);
+ ctlr->channels = 4;
+ }
+
+ /* enable PCI interrupt as BIOS might not */
+ pci_write_config(dev, 0x8a, (pci_read_config(dev, 0x8a, 1) & 0x3f), 1);
+
+ /* dont block interrupts from any channel */
+ pci_write_config(dev, 0x48,
+ (pci_read_config(dev, 0x48, 4) & ~0x03c00000), 4);
+
+ ctlr->allocate = ata_sii_allocate;
+ if (ctlr->chip->max_dma >= ATA_SA150) {
+ ctlr->reset = ata_sii_reset;
+ ctlr->setmode = ata_sata_setmode;
+ }
+ else
+ ctlr->setmode = ata_sii_setmode;
+ }
+ else {
+ if ((pci_read_config(dev, 0x51, 1) & 0x08) != 0x08) {
+ device_printf(dev, "HW has secondary channel disabled\n");
+ ctlr->channels = 1;
+ }
+
+ /* enable interrupt as BIOS might not */
+ pci_write_config(dev, 0x71, 0x01, 1);
+
+ ctlr->allocate = ata_cmd_allocate;
+ ctlr->setmode = ata_cmd_setmode;
+ }
+ return 0;
+}
+
+static int
+ata_cmd_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* setup the usual register normal pci style */
+ if (ata_pci_allocate(dev))
+ return ENXIO;
+
+ if (ctlr->chip->cfg2 & SIIINTR)
+ ch->hw.status = ata_cmd_status;
+
+ return 0;
+}
+
+static int
+ata_cmd_status(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ u_int8_t reg71;
+
+ if (((reg71 = pci_read_config(device_get_parent(ch->dev), 0x71, 1)) &
+ (ch->unit ? 0x08 : 0x04))) {
+ pci_write_config(device_get_parent(ch->dev), 0x71,
+ reg71 & ~(ch->unit ? 0x04 : 0x08), 1);
+ return ata_pci_status(dev);
+ }
+ return 0;
+}
+
+static void
+ata_cmd_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int error;
+
+ mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
+
+ mode = ata_check_80pin(dev, mode);
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "",
+ ata_mode2str(mode), ctlr->chip->text);
+ if (!error) {
+ int treg = 0x54 + ((devno < 3) ? (devno << 1) : 7);
+ int ureg = ch->unit ? 0x7b : 0x73;
+
+ if (mode >= ATA_UDMA0) {
+ int udmatimings[][2] = { { 0x31, 0xc2 }, { 0x21, 0x82 },
+ { 0x11, 0x42 }, { 0x25, 0x8a },
+ { 0x15, 0x4a }, { 0x05, 0x0a } };
+
+ u_int8_t umode = pci_read_config(gparent, ureg, 1);
+
+ umode &= ~(atadev->unit == ATA_MASTER ? 0x35 : 0xca);
+ umode |= udmatimings[mode & ATA_MODE_MASK][ATA_DEV(atadev->unit)];
+ pci_write_config(gparent, ureg, umode, 1);
+ }
+ else if (mode >= ATA_WDMA0) {
+ int dmatimings[] = { 0x87, 0x32, 0x3f };
+
+ pci_write_config(gparent, treg, dmatimings[mode & ATA_MODE_MASK],1);
+ pci_write_config(gparent, ureg,
+ pci_read_config(gparent, ureg, 1) &
+ ~(atadev->unit == ATA_MASTER ? 0x35 : 0xca), 1);
+ }
+ else {
+ int piotimings[] = { 0xa9, 0x57, 0x44, 0x32, 0x3f };
+ pci_write_config(gparent, treg,
+ piotimings[(mode & ATA_MODE_MASK) - ATA_PIO0], 1);
+ pci_write_config(gparent, ureg,
+ pci_read_config(gparent, ureg, 1) &
+ ~(atadev->unit == ATA_MASTER ? 0x35 : 0xca), 1);
+ }
+ atadev->mode = mode;
+ }
+}
+
+static int
+ata_sii_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int unit01 = (ch->unit & 1), unit10 = (ch->unit & 2);
+ int i;
+
+ for (i = ATA_DATA; i <= ATA_COMMAND; i++) {
+ ch->r_io[i].res = ctlr->r_res2;
+ ch->r_io[i].offset = 0x80 + i + (unit01 << 6) + (unit10 << 8);
+ }
+ ch->r_io[ATA_CONTROL].res = ctlr->r_res2;
+ ch->r_io[ATA_CONTROL].offset = 0x8a + (unit01 << 6) + (unit10 << 8);
+ ch->r_io[ATA_IDX_ADDR].res = ctlr->r_res2;
+ ata_default_registers(dev);
+
+ ch->r_io[ATA_BMCMD_PORT].res = ctlr->r_res2;
+ ch->r_io[ATA_BMCMD_PORT].offset = 0x00 + (unit01 << 3) + (unit10 << 8);
+ ch->r_io[ATA_BMSTAT_PORT].res = ctlr->r_res2;
+ ch->r_io[ATA_BMSTAT_PORT].offset = 0x02 + (unit01 << 3) + (unit10 << 8);
+ ch->r_io[ATA_BMDTP_PORT].res = ctlr->r_res2;
+ ch->r_io[ATA_BMDTP_PORT].offset = 0x04 + (unit01 << 3) + (unit10 << 8);
+ ch->r_io[ATA_BMDEVSPEC_0].res = ctlr->r_res2;
+ ch->r_io[ATA_BMDEVSPEC_0].offset = 0xa1 + (unit01 << 6) + (unit10 << 8);
+
+ if (ctlr->chip->max_dma >= ATA_SA150) {
+ ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
+ ch->r_io[ATA_SSTATUS].offset = 0x104 + (unit01 << 7) + (unit10 << 8);
+ ch->r_io[ATA_SERROR].res = ctlr->r_res2;
+ ch->r_io[ATA_SERROR].offset = 0x108 + (unit01 << 7) + (unit10 << 8);
+ ch->r_io[ATA_SCONTROL].res = ctlr->r_res2;
+ ch->r_io[ATA_SCONTROL].offset = 0x100 + (unit01 << 7) + (unit10 << 8);
+ ch->flags |= ATA_NO_SLAVE;
+
+ /* enable PHY state change interrupt */
+ ATA_OUTL(ctlr->r_res2, 0x148 + (unit01 << 7) + (unit10 << 8),(1 << 16));
+ }
+
+ if ((ctlr->chip->cfg2 & SIIBUG) && ch->dma) {
+ /* work around errata in early chips */
+ ch->dma->boundary = 16 * DEV_BSIZE;
+ ch->dma->segsize = 15 * DEV_BSIZE;
+ }
+
+ ata_pci_hw(dev);
+ ch->hw.status = ata_sii_status;
+ return 0;
+}
+
+static int
+ata_sii_status(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* check for PHY related interrupts on SATA capable HW */
+ if (ctlr->chip->max_dma >= ATA_SA150) {
+ u_int32_t status = ATA_IDX_INL(ch, ATA_SSTATUS);
+ u_int32_t error = ATA_IDX_INL(ch, ATA_SERROR);
+ struct ata_connect_task *tp;
+
+ if (error) {
+ /* clear error bits/interrupt */
+ ATA_IDX_OUTL(ch, ATA_SERROR, error);
+
+ /* if we have a connection event deal with it */
+ if ((error & ATA_SE_PHY_CHANGED) &&
+ (tp = (struct ata_connect_task *)
+ malloc(sizeof(struct ata_connect_task),
+ M_ATA, M_NOWAIT | M_ZERO))) {
+
+ if ((status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN1) {
+ if (bootverbose)
+ device_printf(ch->dev, "CONNECT requested\n");
+ tp->action = ATA_C_ATTACH;
+ }
+ else {
+ if (bootverbose)
+ device_printf(ch->dev, "DISCONNECT requested\n");
+ tp->action = ATA_C_DETACH;
+ }
+ tp->dev = ch->dev;
+ TASK_INIT(&tp->task, 0, ata_sata_phy_event, tp);
+ taskqueue_enqueue(taskqueue_thread, &tp->task);
+ }
+ }
+ }
+
+ /* any drive action to take care of ? */
+ if (ATA_IDX_INB(ch, ATA_BMDEVSPEC_0) & 0x08)
+ return ata_pci_status(dev);
+ else
+ return 0;
+}
+
+static void
+ata_sii_reset(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int offset = ((ch->unit & 1) << 7) + ((ch->unit & 2) << 8);
+
+ /* disable PHY state change interrupt */
+ ATA_OUTL(ctlr->r_res2, 0x148 + offset, ~(1 << 16));
+
+ /* reset controller part for this channel */
+ ATA_OUTL(ctlr->r_res2, 0x48,
+ ATA_INL(ctlr->r_res2, 0x48) | (0xc0 >> ch->unit));
+ DELAY(1000);
+ ATA_OUTL(ctlr->r_res2, 0x48,
+ ATA_INL(ctlr->r_res2, 0x48) & ~(0xc0 >> ch->unit));
+
+ ata_sata_phy_enable(ch);
+
+ /* enable PHY state change interrupt */
+ ATA_OUTL(ctlr->r_res2, 0x148 + offset, (1 << 16));
+}
+
+static void
+ata_sii_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int rego = (ch->unit << 4) + (ATA_DEV(atadev->unit) << 1);
+ int mreg = ch->unit ? 0x84 : 0x80;
+ int mask = 0x03 << (ATA_DEV(atadev->unit) << 2);
+ int mval = pci_read_config(gparent, mreg, 1) & ~mask;
+ int error;
+
+ mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
+
+ if (ctlr->chip->cfg2 & SIISETCLK) {
+ if (mode > ATA_UDMA2 && (pci_read_config(gparent, 0x79, 1) &
+ (ch->unit ? 0x02 : 0x01))) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+ }
+ else
+ mode = ata_check_80pin(dev, mode);
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "",
+ ata_mode2str(mode), ctlr->chip->text);
+ if (error)
+ return;
+
+ if (mode >= ATA_UDMA0) {
+ u_int8_t udmatimings[] = { 0xf, 0xb, 0x7, 0x5, 0x3, 0x2, 0x1 };
+ u_int8_t ureg = 0xac + rego;
+
+ pci_write_config(gparent, mreg,
+ mval | (0x03 << (ATA_DEV(atadev->unit) << 2)), 1);
+ pci_write_config(gparent, ureg,
+ (pci_read_config(gparent, ureg, 1) & ~0x3f) |
+ udmatimings[mode & ATA_MODE_MASK], 1);
+
+ }
+ else if (mode >= ATA_WDMA0) {
+ u_int8_t dreg = 0xa8 + rego;
+ u_int16_t dmatimings[] = { 0x2208, 0x10c2, 0x10c1 };
+
+ pci_write_config(gparent, mreg,
+ mval | (0x02 << (ATA_DEV(atadev->unit) << 2)), 1);
+ pci_write_config(gparent, dreg, dmatimings[mode & ATA_MODE_MASK], 2);
+
+ }
+ else {
+ u_int8_t preg = 0xa4 + rego;
+ u_int16_t piotimings[] = { 0x328a, 0x2283, 0x1104, 0x10c3, 0x10c1 };
+
+ pci_write_config(gparent, mreg,
+ mval | (0x01 << (ATA_DEV(atadev->unit) << 2)), 1);
+ pci_write_config(gparent, preg, piotimings[mode & ATA_MODE_MASK], 2);
+ }
+ atadev->mode = mode;
+}
+
+
+/*
+ * Silicon Integrated Systems Corp. (SiS) chipset support functions
+ */
+int
+ata_sis_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_SIS182, 0x00, SISSATA, 0, ATA_SA150, "182" }, /* south */
+ { ATA_SIS181, 0x00, SISSATA, 0, ATA_SA150, "181" }, /* south */
+ { ATA_SIS180, 0x00, SISSATA, 0, ATA_SA150, "180" }, /* south */
+ { ATA_SIS965, 0x00, SIS133NEW, 0, ATA_UDMA6, "965" }, /* south */
+ { ATA_SIS964, 0x00, SIS133NEW, 0, ATA_UDMA6, "964" }, /* south */
+ { ATA_SIS963, 0x00, SIS133NEW, 0, ATA_UDMA6, "963" }, /* south */
+ { ATA_SIS962, 0x00, SIS133NEW, 0, ATA_UDMA6, "962" }, /* south */
+
+ { ATA_SIS745, 0x00, SIS100NEW, 0, ATA_UDMA5, "745" }, /* 1chip */
+ { ATA_SIS735, 0x00, SIS100NEW, 0, ATA_UDMA5, "735" }, /* 1chip */
+ { ATA_SIS733, 0x00, SIS100NEW, 0, ATA_UDMA5, "733" }, /* 1chip */
+ { ATA_SIS730, 0x00, SIS100OLD, 0, ATA_UDMA5, "730" }, /* 1chip */
+
+ { ATA_SIS635, 0x00, SIS100NEW, 0, ATA_UDMA5, "635" }, /* 1chip */
+ { ATA_SIS633, 0x00, SIS100NEW, 0, ATA_UDMA5, "633" }, /* unknown */
+ { ATA_SIS630, 0x30, SIS100OLD, 0, ATA_UDMA5, "630S"}, /* 1chip */
+ { ATA_SIS630, 0x00, SIS66, 0, ATA_UDMA4, "630" }, /* 1chip */
+ { ATA_SIS620, 0x00, SIS66, 0, ATA_UDMA4, "620" }, /* 1chip */
+
+ { ATA_SIS550, 0x00, SIS66, 0, ATA_UDMA5, "550" },
+ { ATA_SIS540, 0x00, SIS66, 0, ATA_UDMA4, "540" },
+ { ATA_SIS530, 0x00, SIS66, 0, ATA_UDMA4, "530" },
+
+ { ATA_SIS5513, 0xc2, SIS33, 1, ATA_UDMA2, "5513" },
+ { ATA_SIS5513, 0x00, SIS33, 1, ATA_WDMA2, "5513" },
+ { 0, 0, 0, 0, 0, 0 }};
+ char buffer[64];
+ int found = 0;
+
+ if (!(idx = ata_find_chip(dev, ids, -pci_get_slot(dev))))
+ return ENXIO;
+
+ if (idx->cfg2 && !found) {
+ u_int8_t reg57 = pci_read_config(dev, 0x57, 1);
+
+ pci_write_config(dev, 0x57, (reg57 & 0x7f), 1);
+ if (pci_read_config(dev, PCIR_DEVVENDOR, 4) == ATA_SIS5518) {
+ found = 1;
+ idx->cfg1 = SIS133NEW;
+ idx->max_dma = ATA_UDMA6;
+ sprintf(buffer, "SiS 962/963 %s controller",
+ ata_mode2str(idx->max_dma));
+ }
+ pci_write_config(dev, 0x57, reg57, 1);
+ }
+ if (idx->cfg2 && !found) {
+ u_int8_t reg4a = pci_read_config(dev, 0x4a, 1);
+
+ pci_write_config(dev, 0x4a, (reg4a | 0x10), 1);
+ if (pci_read_config(dev, PCIR_DEVVENDOR, 4) == ATA_SIS5517) {
+ struct ata_chip_id id[] =
+ {{ ATA_SISSOUTH, 0x10, 0, 0, 0, "" }, { 0, 0, 0, 0, 0, 0 }};
+
+ found = 1;
+ if (ata_find_chip(dev, id, pci_get_slot(dev))) {
+ idx->cfg1 = SIS133OLD;
+ idx->max_dma = ATA_UDMA6;
+ }
+ else {
+ idx->cfg1 = SIS100NEW;
+ idx->max_dma = ATA_UDMA5;
+ }
+ sprintf(buffer, "SiS 961 %s controller",ata_mode2str(idx->max_dma));
+ }
+ pci_write_config(dev, 0x4a, reg4a, 1);
+ }
+ if (!found)
+ sprintf(buffer,"SiS %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_sis_chipinit;
+ return 0;
+}
+
+static int
+ata_sis_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ switch (ctlr->chip->cfg1) {
+ case SIS33:
+ break;
+ case SIS66:
+ case SIS100OLD:
+ pci_write_config(dev, 0x52, pci_read_config(dev, 0x52, 1) & ~0x04, 1);
+ break;
+ case SIS100NEW:
+ case SIS133OLD:
+ pci_write_config(dev, 0x49, pci_read_config(dev, 0x49, 1) & ~0x01, 1);
+ break;
+ case SIS133NEW:
+ pci_write_config(dev, 0x50, pci_read_config(dev, 0x50, 2) | 0x0008, 2);
+ pci_write_config(dev, 0x52, pci_read_config(dev, 0x52, 2) | 0x0008, 2);
+ break;
+ case SISSATA:
+ ctlr->r_type2 = SYS_RES_IOPORT;
+ ctlr->r_rid2 = PCIR_BAR(5);
+ if ((ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2, RF_ACTIVE))) {
+ ctlr->allocate = ata_sis_allocate;
+ ctlr->reset = ata_sis_reset;
+
+ /* enable PCI interrupt */
+ pci_write_config(dev, PCIR_COMMAND,
+ pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400,2);
+ }
+ ctlr->setmode = ata_sata_setmode;
+ return 0;
+ default:
+ return ENXIO;
+ }
+ ctlr->setmode = ata_sis_setmode;
+ return 0;
+}
+
+static int
+ata_sis_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int offset = ch->unit << ((ctlr->chip->chipid == ATA_SIS182) ? 5 : 6);
+
+ /* setup the usual register normal pci style */
+ if (ata_pci_allocate(dev))
+ return ENXIO;
+
+ ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
+ ch->r_io[ATA_SSTATUS].offset = 0x00 + offset;
+ ch->r_io[ATA_SERROR].res = ctlr->r_res2;
+ ch->r_io[ATA_SERROR].offset = 0x04 + offset;
+ ch->r_io[ATA_SCONTROL].res = ctlr->r_res2;
+ ch->r_io[ATA_SCONTROL].offset = 0x08 + offset;
+ ch->flags |= ATA_NO_SLAVE;
+
+ /* XXX SOS PHY hotplug handling missing in SiS chip ?? */
+ /* XXX SOS unknown how to enable PHY state change interrupt */
+ return 0;
+}
+
+static void
+ata_sis_reset(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ata_sata_phy_enable(ch);
+}
+
+
+static void
+ata_sis_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int error;
+
+ mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
+
+ if (ctlr->chip->cfg1 == SIS133NEW) {
+ if (mode > ATA_UDMA2 &&
+ pci_read_config(gparent, ch->unit ? 0x52 : 0x50,2) & 0x8000) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+ }
+ else {
+ if (mode > ATA_UDMA2 &&
+ pci_read_config(gparent, 0x48, 1)&(ch->unit ? 0x20 : 0x10)) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+ }
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "",
+ ata_mode2str(mode), ctlr->chip->text);
+ if (!error) {
+ switch (ctlr->chip->cfg1) {
+ case SIS133NEW: {
+ u_int32_t timings[] =
+ { 0x28269008, 0x0c266008, 0x04263008, 0x0c0a3008, 0x05093008,
+ 0x22196008, 0x0c0a3008, 0x05093008, 0x050939fc, 0x050936ac,
+ 0x0509347c, 0x0509325c, 0x0509323c, 0x0509322c, 0x0509321c};
+ u_int32_t reg;
+
+ reg = (pci_read_config(gparent, 0x57, 1)&0x40?0x70:0x40)+(devno<<2);
+ pci_write_config(gparent, reg, timings[ata_mode2idx(mode)], 4);
+ break;
+ }
+ case SIS133OLD: {
+ u_int16_t timings[] =
+ { 0x00cb, 0x0067, 0x0044, 0x0033, 0x0031, 0x0044, 0x0033, 0x0031,
+ 0x8f31, 0x8a31, 0x8731, 0x8531, 0x8331, 0x8231, 0x8131 };
+
+ u_int16_t reg = 0x40 + (devno << 1);
+
+ pci_write_config(gparent, reg, timings[ata_mode2idx(mode)], 2);
+ break;
+ }
+ case SIS100NEW: {
+ u_int16_t timings[] =
+ { 0x00cb, 0x0067, 0x0044, 0x0033, 0x0031, 0x0044, 0x0033,
+ 0x0031, 0x8b31, 0x8731, 0x8531, 0x8431, 0x8231, 0x8131 };
+ u_int16_t reg = 0x40 + (devno << 1);
+
+ pci_write_config(gparent, reg, timings[ata_mode2idx(mode)], 2);
+ break;
+ }
+ case SIS100OLD:
+ case SIS66:
+ case SIS33: {
+ u_int16_t timings[] =
+ { 0x0c0b, 0x0607, 0x0404, 0x0303, 0x0301, 0x0404, 0x0303,
+ 0x0301, 0xf301, 0xd301, 0xb301, 0xa301, 0x9301, 0x8301 };
+ u_int16_t reg = 0x40 + (devno << 1);
+
+ pci_write_config(gparent, reg, timings[ata_mode2idx(mode)], 2);
+ break;
+ }
+ }
+ atadev->mode = mode;
+ }
+}
+
+
+/* VIA Technologies Inc. chipset support functions */
+int
+ata_via_ident(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ struct ata_chip_id *idx;
+ static struct ata_chip_id ids[] =
+ {{ ATA_VIA82C586, 0x02, VIA33, 0x00, ATA_UDMA2, "82C586B" },
+ { ATA_VIA82C586, 0x00, VIA33, 0x00, ATA_WDMA2, "82C586" },
+ { ATA_VIA82C596, 0x12, VIA66, VIACLK, ATA_UDMA4, "82C596B" },
+ { ATA_VIA82C596, 0x00, VIA33, 0x00, ATA_UDMA2, "82C596" },
+ { ATA_VIA82C686, 0x40, VIA100, VIABUG, ATA_UDMA5, "82C686B"},
+ { ATA_VIA82C686, 0x10, VIA66, VIACLK, ATA_UDMA4, "82C686A" },
+ { ATA_VIA82C686, 0x00, VIA33, 0x00, ATA_UDMA2, "82C686" },
+ { ATA_VIA8231, 0x00, VIA100, VIABUG, ATA_UDMA5, "8231" },
+ { ATA_VIA8233, 0x00, VIA100, 0x00, ATA_UDMA5, "8233" },
+ { ATA_VIA8233C, 0x00, VIA100, 0x00, ATA_UDMA5, "8233C" },
+ { ATA_VIA8233A, 0x00, VIA133, 0x00, ATA_UDMA6, "8233A" },
+ { ATA_VIA8235, 0x00, VIA133, 0x00, ATA_UDMA6, "8235" },
+ { ATA_VIA8237, 0x00, VIA133, 0x00, ATA_UDMA6, "8237" },
+ { ATA_VIA8237A, 0x00, VIA133, 0x00, ATA_UDMA6, "8237A" },
+ { ATA_VIA8251, 0x00, VIA133, 0x00, ATA_UDMA6, "8251" },
+ { 0, 0, 0, 0, 0, 0 }};
+ static struct ata_chip_id new_ids[] =
+ {{ ATA_VIA6410, 0x00, 0, 0x00, ATA_UDMA6, "6410" },
+ { ATA_VIA6420, 0x00, 7, 0x00, ATA_SA150, "6420" },
+ { ATA_VIA6421, 0x00, 6, VIABAR, ATA_SA150, "6421" },
+ { ATA_VIA8237A, 0x00, 0, 0x00, ATA_SA150, "8237A" },
+ { ATA_VIA8251, 0x00, 0, VIAAHCI, ATA_SA300, "8251" },
+ { 0, 0, 0, 0, 0, 0 }};
+ char buffer[64];
+
+ if (pci_get_devid(dev) == ATA_VIA82C571) {
+ if (!(idx = ata_find_chip(dev, ids, -99)))
+ return ENXIO;
+ }
+ else {
+ if (!(idx = ata_match_chip(dev, new_ids)))
+ return ENXIO;
+ }
+
+ sprintf(buffer, "VIA %s %s controller",
+ idx->text, ata_mode2str(idx->max_dma));
+ device_set_desc_copy(dev, buffer);
+ ctlr->chip = idx;
+ ctlr->chipinit = ata_via_chipinit;
+ return 0;
+}
+
+static int
+ata_via_chipinit(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+
+ if (ata_setup_interrupt(dev))
+ return ENXIO;
+
+ if (ctlr->chip->max_dma >= ATA_SA150) {
+ if (ctlr->chip->cfg2 == VIAAHCI) {
+ ctlr->r_type2 = SYS_RES_MEMORY;
+ ctlr->r_rid2 = PCIR_BAR(5);
+ if ((ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2,
+ RF_ACTIVE))) {
+ return ata_ahci_chipinit(dev);
+ }
+ }
+ ctlr->r_type2 = SYS_RES_IOPORT;
+ ctlr->r_rid2 = PCIR_BAR(5);
+ if ((ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
+ &ctlr->r_rid2, RF_ACTIVE))) {
+ ctlr->allocate = ata_via_allocate;
+ ctlr->reset = ata_via_reset;
+
+ /* enable PCI interrupt */
+ pci_write_config(dev, PCIR_COMMAND,
+ pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400,2);
+ }
+ ctlr->setmode = ata_sata_setmode;
+ return 0;
+ }
+
+ /* prepare for ATA-66 on the 82C686a and 82C596b */
+ if (ctlr->chip->cfg2 & VIACLK)
+ pci_write_config(dev, 0x50, 0x030b030b, 4);
+
+ /* the southbridge might need the data corruption fix */
+ if (ctlr->chip->cfg2 & VIABUG)
+ ata_via_southbridge_fixup(dev);
+
+ /* set fifo configuration half'n'half */
+ pci_write_config(dev, 0x43,
+ (pci_read_config(dev, 0x43, 1) & 0x90) | 0x2a, 1);
+
+ /* set status register read retry */
+ pci_write_config(dev, 0x44, pci_read_config(dev, 0x44, 1) | 0x08, 1);
+
+ /* set DMA read & end-of-sector fifo flush */
+ pci_write_config(dev, 0x46,
+ (pci_read_config(dev, 0x46, 1) & 0x0c) | 0xf0, 1);
+
+ /* set sector size */
+ pci_write_config(dev, 0x60, DEV_BSIZE, 2);
+ pci_write_config(dev, 0x68, DEV_BSIZE, 2);
+
+ ctlr->setmode = ata_via_family_setmode;
+ return 0;
+}
+
+static int
+ata_via_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* newer SATA chips has resources in one BAR for each channel */
+ if (ctlr->chip->cfg2 & VIABAR) {
+ struct resource *r_io;
+ int i, rid;
+
+ rid = PCIR_BAR(ch->unit);
+ if (!(r_io = bus_alloc_resource_any(device_get_parent(dev),
+ SYS_RES_IOPORT,
+ &rid, RF_ACTIVE)))
+ return ENXIO;
+
+ for (i = ATA_DATA; i <= ATA_COMMAND; i ++) {
+ ch->r_io[i].res = r_io;
+ ch->r_io[i].offset = i;
+ }
+ ch->r_io[ATA_CONTROL].res = r_io;
+ ch->r_io[ATA_CONTROL].offset = 2 + ATA_IOSIZE;
+ ch->r_io[ATA_IDX_ADDR].res = r_io;
+ ata_default_registers(dev);
+ for (i = ATA_BMCMD_PORT; i <= ATA_BMDTP_PORT; i++) {
+ ch->r_io[i].res = ctlr->r_res1;
+ ch->r_io[i].offset = (i - ATA_BMCMD_PORT)+(ch->unit * ATA_BMIOSIZE);
+ }
+ ata_pci_hw(dev);
+ }
+ else {
+ /* setup the usual register normal pci style */
+ if (ata_pci_allocate(dev))
+ return ENXIO;
+ }
+
+ ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
+ ch->r_io[ATA_SSTATUS].offset = (ch->unit << ctlr->chip->cfg1);
+ ch->r_io[ATA_SERROR].res = ctlr->r_res2;
+ ch->r_io[ATA_SERROR].offset = 0x04 + (ch->unit << ctlr->chip->cfg1);
+ ch->r_io[ATA_SCONTROL].res = ctlr->r_res2;
+ ch->r_io[ATA_SCONTROL].offset = 0x08 + (ch->unit << ctlr->chip->cfg1);
+ ch->flags |= ATA_NO_SLAVE;
+
+ /* XXX SOS PHY hotplug handling missing in VIA chip ?? */
+ /* XXX SOS unknown how to enable PHY state change interrupt */
+ return 0;
+}
+
+static void
+ata_via_reset(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ata_sata_phy_enable(ch);
+}
+
+static void
+ata_via_southbridge_fixup(device_t dev)
+{
+ device_t *children;
+ int nchildren, i;
+
+ if (device_get_children(device_get_parent(dev), &children, &nchildren))
+ return;
+
+ for (i = 0; i < nchildren; i++) {
+ if (pci_get_devid(children[i]) == ATA_VIA8363 ||
+ pci_get_devid(children[i]) == ATA_VIA8371 ||
+ pci_get_devid(children[i]) == ATA_VIA8662 ||
+ pci_get_devid(children[i]) == ATA_VIA8361) {
+ u_int8_t reg76 = pci_read_config(children[i], 0x76, 1);
+
+ if ((reg76 & 0xf0) != 0xd0) {
+ device_printf(dev,
+ "Correcting VIA config for southbridge data corruption bug\n");
+ pci_write_config(children[i], 0x75, 0x80, 1);
+ pci_write_config(children[i], 0x76, (reg76 & 0x0f) | 0xd0, 1);
+ }
+ break;
+ }
+ }
+ free(children, M_TEMP);
+}
+
+
+/* common code for VIA, AMD & nVidia */
+static void
+ata_via_family_setmode(device_t dev, int mode)
+{
+ device_t gparent = GRANDPARENT(dev);
+ struct ata_pci_controller *ctlr = device_get_softc(gparent);
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ u_int8_t timings[] = { 0xa8, 0x65, 0x42, 0x22, 0x20, 0x42, 0x22, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
+ int modes[][7] = {
+ { 0xc2, 0xc1, 0xc0, 0x00, 0x00, 0x00, 0x00 }, /* VIA ATA33 */
+ { 0xee, 0xec, 0xea, 0xe9, 0xe8, 0x00, 0x00 }, /* VIA ATA66 */
+ { 0xf7, 0xf6, 0xf4, 0xf2, 0xf1, 0xf0, 0x00 }, /* VIA ATA100 */
+ { 0xf7, 0xf7, 0xf6, 0xf4, 0xf2, 0xf1, 0xf0 }, /* VIA ATA133 */
+ { 0xc2, 0xc1, 0xc0, 0xc4, 0xc5, 0xc6, 0xc7 }}; /* AMD/nVIDIA */
+ int devno = (ch->unit << 1) + ATA_DEV(atadev->unit);
+ int reg = 0x53 - devno;
+ int error;
+
+ mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
+
+ if (ctlr->chip->cfg2 & AMDCABLE) {
+ if (mode > ATA_UDMA2 &&
+ !(pci_read_config(gparent, 0x42, 1) & (1 << devno))) {
+ ata_print_cable(dev, "controller");
+ mode = ATA_UDMA2;
+ }
+ }
+ else
+ mode = ata_check_80pin(dev, mode);
+
+ if (ctlr->chip->cfg2 & NVIDIA)
+ reg += 0x10;
+
+ if (ctlr->chip->cfg1 != VIA133)
+ pci_write_config(gparent, reg - 0x08, timings[ata_mode2idx(mode)], 1);
+
+ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
+
+ if (bootverbose)
+ device_printf(dev, "%ssetting %s on %s chip\n",
+ (error) ? "FAILURE " : "", ata_mode2str(mode),
+ ctlr->chip->text);
+ if (!error) {
+ if (mode >= ATA_UDMA0)
+ pci_write_config(gparent, reg,
+ modes[ctlr->chip->cfg1][mode & ATA_MODE_MASK], 1);
+ else
+ pci_write_config(gparent, reg, 0x8b, 1);
+ atadev->mode = mode;
+ }
+}
+
+
+/* misc functions */
+static struct ata_chip_id *
+ata_match_chip(device_t dev, struct ata_chip_id *index)
+{
+ while (index->chipid != 0) {
+ if (pci_get_devid(dev) == index->chipid &&
+ pci_get_revid(dev) >= index->chiprev)
+ return index;
+ index++;
+ }
+ return NULL;
+}
+
+static struct ata_chip_id *
+ata_find_chip(device_t dev, struct ata_chip_id *index, int slot)
+{
+ device_t *children;
+ int nchildren, i;
+
+ if (device_get_children(device_get_parent(dev), &children, &nchildren))
+ return 0;
+
+ while (index->chipid != 0) {
+ for (i = 0; i < nchildren; i++) {
+ if (((slot >= 0 && pci_get_slot(children[i]) == slot) ||
+ (slot < 0 && pci_get_slot(children[i]) <= -slot)) &&
+ pci_get_devid(children[i]) == index->chipid &&
+ pci_get_revid(children[i]) >= index->chiprev) {
+ free(children, M_TEMP);
+ return index;
+ }
+ }
+ index++;
+ }
+ free(children, M_TEMP);
+ return NULL;
+}
+
+static int
+ata_setup_interrupt(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ int rid = ATA_IRQ_RID;
+
+ if (!ata_legacy(dev)) {
+ if (!(ctlr->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE))) {
+ device_printf(dev, "unable to map interrupt\n");
+ return ENXIO;
+ }
+ if ((bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS,
+ ata_generic_intr, ctlr, &ctlr->handle))) {
+ device_printf(dev, "unable to setup interrupt\n");
+ return ENXIO;
+ }
+ }
+ return 0;
+}
+
+struct ata_serialize {
+ struct mtx locked_mtx;
+ int locked_ch;
+ int restart_ch;
+};
+
+static int
+ata_serialize(device_t dev, int flags)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ struct ata_serialize *serial;
+ static int inited = 0;
+ int res;
+
+ if (!inited) {
+ serial = malloc(sizeof(struct ata_serialize),
+ M_TEMP, M_NOWAIT | M_ZERO);
+ mtx_init(&serial->locked_mtx, "ATA serialize lock", NULL, MTX_DEF);
+ serial->locked_ch = -1;
+ serial->restart_ch = -1;
+ device_set_ivars(ctlr->dev, serial);
+ inited = 1;
+ }
+ else
+ serial = device_get_ivars(ctlr->dev);
+
+ mtx_lock(&serial->locked_mtx);
+ switch (flags) {
+ case ATA_LF_LOCK:
+ if (serial->locked_ch == -1)
+ serial->locked_ch = ch->unit;
+ if (serial->locked_ch != ch->unit)
+ serial->restart_ch = ch->unit;
+ break;
+
+ case ATA_LF_UNLOCK:
+ if (serial->locked_ch == ch->unit) {
+ serial->locked_ch = -1;
+ if (serial->restart_ch != -1) {
+ if ((ch = ctlr->interrupt[serial->restart_ch].argument)) {
+ serial->restart_ch = -1;
+ mtx_unlock(&serial->locked_mtx);
+ ata_start(ch->dev);
+ return -1;
+ }
+ }
+ }
+ break;
+
+ case ATA_LF_WHICH:
+ break;
+ }
+ res = serial->locked_ch;
+ mtx_unlock(&serial->locked_mtx);
+ return res;
+}
+
+static void
+ata_print_cable(device_t dev, u_int8_t *who)
+{
+ device_printf(dev,
+ "DMA limited to UDMA33, %s found non-ATA66 cable\n", who);
+}
+
+static int
+ata_atapi(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+
+ return ((atadev->unit == ATA_MASTER && ch->devices & ATA_ATAPI_MASTER) ||
+ (atadev->unit == ATA_SLAVE && ch->devices & ATA_ATAPI_SLAVE));
+}
+
+static int
+ata_check_80pin(device_t dev, int mode)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if (mode > ATA_UDMA2 && !(atadev->param.hwres & ATA_CABLE_ID)) {
+ ata_print_cable(dev, "device");
+ mode = ATA_UDMA2;
+ }
+ return mode;
+}
+
+static int
+ata_mode2idx(int mode)
+{
+ if ((mode & ATA_DMA_MASK) == ATA_UDMA0)
+ return (mode & ATA_MODE_MASK) + 8;
+ if ((mode & ATA_DMA_MASK) == ATA_WDMA0)
+ return (mode & ATA_MODE_MASK) + 5;
+ return (mode & ATA_MODE_MASK) - ATA_PIO0;
+}
diff --git a/sys/dev/ata/ata-disk.c b/sys/dev/ata/ata-disk.c
new file mode 100644
index 000000000000..cd0d92a309b9
--- /dev/null
+++ b/sys/dev/ata/ata-disk.c
@@ -0,0 +1,428 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/disk.h>
+#include <sys/cons.h>
+#include <sys/sysctl.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/md_var.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <geom/geom_disk.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/ata-pci.h>
+#include <dev/ata/ata-disk.h>
+#include <dev/ata/ata-raid.h>
+#include <ata_if.h>
+
+/* prototypes */
+static void ad_init(device_t);
+static void ad_done(struct ata_request *);
+static void ad_describe(device_t dev);
+static int ad_version(u_int16_t);
+static disk_strategy_t ad_strategy;
+static disk_ioctl_t ad_ioctl;
+static dumper_t ad_dump;
+
+/* local vars */
+static MALLOC_DEFINE(M_AD, "ad_driver", "ATA disk driver");
+
+static int
+ad_probe(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if (!(atadev->param.config & ATA_PROTO_ATAPI) ||
+ (atadev->param.config == ATA_CFA_MAGIC1) ||
+ (atadev->param.config == ATA_CFA_MAGIC2))
+ return 0;
+ else
+ return ENXIO;
+}
+
+static int
+ad_attach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ad_softc *adp;
+ u_int32_t lbasize;
+ u_int64_t lbasize48;
+
+ /* check that we have a virgin disk to attach */
+ if (device_get_ivars(dev))
+ return EEXIST;
+
+ if (!(adp = malloc(sizeof(struct ad_softc), M_AD, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "out of memory\n");
+ return ENOMEM;
+ }
+ device_set_ivars(dev, adp);
+
+ if ((atadev->param.atavalid & ATA_FLAG_54_58) &&
+ atadev->param.current_heads && atadev->param.current_sectors) {
+ adp->heads = atadev->param.current_heads;
+ adp->sectors = atadev->param.current_sectors;
+ adp->total_secs = (u_int32_t)atadev->param.current_size_1 |
+ ((u_int32_t)atadev->param.current_size_2 << 16);
+ }
+ else {
+ adp->heads = atadev->param.heads;
+ adp->sectors = atadev->param.sectors;
+ adp->total_secs = atadev->param.cylinders * adp->heads * adp->sectors;
+ }
+ lbasize = (u_int32_t)atadev->param.lba_size_1 |
+ ((u_int32_t)atadev->param.lba_size_2 << 16);
+
+ /* does this device need oldstyle CHS addressing */
+ if (!ad_version(atadev->param.version_major) || !lbasize)
+ atadev->flags |= ATA_D_USE_CHS;
+
+ /* use the 28bit LBA size if valid or bigger than the CHS mapping */
+ if (atadev->param.cylinders == 16383 || adp->total_secs < lbasize)
+ adp->total_secs = lbasize;
+
+ /* use the 48bit LBA size if valid */
+ lbasize48 = ((u_int64_t)atadev->param.lba_size48_1) |
+ ((u_int64_t)atadev->param.lba_size48_2 << 16) |
+ ((u_int64_t)atadev->param.lba_size48_3 << 32) |
+ ((u_int64_t)atadev->param.lba_size48_4 << 48);
+ if ((atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) &&
+ lbasize48 > ATA_MAX_28BIT_LBA)
+ adp->total_secs = lbasize48;
+
+ /* init device parameters */
+ ad_init(dev);
+
+ /* announce we are here */
+ ad_describe(dev);
+
+ /* create the disk device */
+ adp->disk = disk_alloc();
+ adp->disk->d_strategy = ad_strategy;
+ adp->disk->d_ioctl = ad_ioctl;
+ adp->disk->d_dump = ad_dump;
+ adp->disk->d_name = "ad";
+ adp->disk->d_drv1 = dev;
+ if (ch->dma)
+ adp->disk->d_maxsize = ch->dma->max_iosize;
+ else
+ adp->disk->d_maxsize = DFLTPHYS;
+ adp->disk->d_sectorsize = DEV_BSIZE;
+ adp->disk->d_mediasize = DEV_BSIZE * (off_t)adp->total_secs;
+ adp->disk->d_fwsectors = adp->sectors;
+ adp->disk->d_fwheads = adp->heads;
+ adp->disk->d_unit = device_get_unit(dev);
+ disk_create(adp->disk, DISK_VERSION);
+ device_add_child(dev, "subdisk", device_get_unit(dev));
+ bus_generic_attach(dev);
+ return 0;
+}
+
+static int
+ad_detach(device_t dev)
+{
+ struct ad_softc *adp = device_get_ivars(dev);
+ device_t *children;
+ int nchildren, i;
+
+ /* check that we have a valid disk to detach */
+ if (!device_get_ivars(dev))
+ return ENXIO;
+
+ /* detach & delete all children */
+ if (!device_get_children(dev, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ if (children[i])
+ device_delete_child(dev, children[i]);
+ free(children, M_TEMP);
+ }
+
+ /* detroy disk from the system so we dont get any further requests */
+ disk_destroy(adp->disk);
+
+ /* fail requests on the queue and any thats "in flight" for this device */
+ ata_fail_requests(dev);
+
+ /* dont leave anything behind */
+ device_set_ivars(dev, NULL);
+ free(adp, M_AD);
+ return 0;
+}
+
+static void
+ad_shutdown(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
+ ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0);
+}
+
+static int
+ad_reinit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+
+ /* if detach pending, return error */
+ if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATA_MASTER)) ||
+ ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATA_SLAVE))) {
+ return 1;
+ }
+ ad_init(dev);
+ return 0;
+}
+
+static void
+ad_strategy(struct bio *bp)
+{
+ device_t dev = bp->bio_disk->d_drv1;
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ata_request *request;
+
+ if (!(request = ata_alloc_request())) {
+ device_printf(dev, "FAILURE - out of memory in start\n");
+ biofinish(bp, NULL, ENOMEM);
+ return;
+ }
+
+ /* setup request */
+ request->dev = dev;
+ request->bio = bp;
+ request->callback = ad_done;
+ request->timeout = 5;
+ request->retries = 2;
+ request->data = bp->bio_data;
+ request->bytecount = bp->bio_bcount;
+ request->u.ata.lba = bp->bio_pblkno;
+ request->u.ata.count = request->bytecount / DEV_BSIZE;
+ request->transfersize = min(bp->bio_bcount, atadev->max_iosize);
+
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ request->flags = ATA_R_READ;
+ if (atadev->mode >= ATA_DMA) {
+ request->u.ata.command = ATA_READ_DMA;
+ request->flags |= ATA_R_DMA;
+ }
+ else if (request->transfersize > DEV_BSIZE)
+ request->u.ata.command = ATA_READ_MUL;
+ else
+ request->u.ata.command = ATA_READ;
+ break;
+ case BIO_WRITE:
+ request->flags = ATA_R_WRITE;
+ if (atadev->mode >= ATA_DMA) {
+ request->u.ata.command = ATA_WRITE_DMA;
+ request->flags |= ATA_R_DMA;
+ }
+ else if (request->transfersize > DEV_BSIZE)
+ request->u.ata.command = ATA_WRITE_MUL;
+ else
+ request->u.ata.command = ATA_WRITE;
+ break;
+ default:
+ device_printf(dev, "FAILURE - unknown BIO operation\n");
+ ata_free_request(request);
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+ request->flags |= ATA_R_ORDERED;
+ ata_queue_request(request);
+}
+
+static void
+ad_done(struct ata_request *request)
+{
+ struct bio *bp = request->bio;
+
+ /* finish up transfer */
+ if ((bp->bio_error = request->result))
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_resid = bp->bio_bcount - request->donecount;
+ biodone(bp);
+ ata_free_request(request);
+}
+
+static int
+ad_ioctl(struct disk *disk, u_long cmd, void *data, int flag, struct thread *td)
+{
+ return ata_device_ioctl(disk->d_drv1, cmd, data);
+}
+
+static int
+ad_dump(void *arg, void *virtual, vm_offset_t physical,
+ off_t offset, size_t length)
+{
+ struct disk *dp = arg;
+ struct bio bp;
+
+ /* length zero is special and really means flush buffers to media */
+ if (!length) {
+ struct ata_device *atadev = device_get_softc(dp->d_drv1);
+ int error = 0;
+
+ if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
+ error = ata_controlcmd(dp->d_drv1, ATA_FLUSHCACHE, 0, 0, 0);
+ return error;
+ }
+
+ bzero(&bp, sizeof(struct bio));
+ bp.bio_disk = dp;
+ bp.bio_pblkno = offset / DEV_BSIZE;
+ bp.bio_bcount = length;
+ bp.bio_data = virtual;
+ bp.bio_cmd = BIO_WRITE;
+ ad_strategy(&bp);
+ return bp.bio_error;
+}
+
+static void
+ad_init(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ ATA_SETMODE(device_get_parent(dev), dev);
+
+ /* enable readahead caching */
+ if (atadev->param.support.command1 & ATA_SUPPORT_LOOKAHEAD)
+ ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0);
+
+ /* enable write caching if supported and configured */
+ if (atadev->param.support.command1 & ATA_SUPPORT_WRITECACHE) {
+ if (ata_wc)
+ ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0);
+ else
+ ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_DIS_WCACHE, 0, 0);
+ }
+
+ /* use multiple sectors/interrupt if device supports it */
+ if (ad_version(atadev->param.version_major)) {
+ int secsperint = max(1, min(atadev->param.sectors_intr, 16));
+
+ if (!ata_controlcmd(dev, ATA_SET_MULTI, 0, 0, secsperint))
+ atadev->max_iosize = secsperint * DEV_BSIZE;
+ }
+ else
+ atadev->max_iosize = DEV_BSIZE;
+}
+
+void
+ad_describe(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ad_softc *adp = device_get_ivars(dev);
+ u_int8_t *marker, vendor[64], product[64];
+
+ /* try to seperate the ATA model string into vendor and model parts */
+ if ((marker = index(atadev->param.model, ' ')) ||
+ (marker = index(atadev->param.model, '-'))) {
+ int len = (marker - atadev->param.model);
+
+ strncpy(vendor, atadev->param.model, len);
+ vendor[len++] = 0;
+ strcat(vendor, " ");
+ strncpy(product, atadev->param.model + len, 40 - len);
+ vendor[40 - len] = 0;
+ }
+ else {
+ if (!strncmp(atadev->param.model, "ST", 2))
+ strcpy(vendor, "Seagate ");
+ else
+ strcpy(vendor, "");
+ strncpy(product, atadev->param.model, 40);
+ }
+
+ device_printf(dev, "%juMB <%s%s %.8s> at ata%d-%s %s%s\n",
+ adp->total_secs / (1048576 / DEV_BSIZE),
+ vendor, product, atadev->param.revision,
+ device_get_unit(ch->dev),
+ (atadev->unit == ATA_MASTER) ? "master" : "slave",
+ (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "",
+ ata_mode2str(atadev->mode));
+ if (bootverbose) {
+ device_printf(dev, "%ju sectors [%juC/%dH/%dS] "
+ "%d sectors/interrupt %d depth queue\n", adp->total_secs,
+ adp->total_secs / (adp->heads * adp->sectors),
+ adp->heads, adp->sectors, atadev->max_iosize / DEV_BSIZE,
+ adp->num_tags + 1);
+ }
+}
+
+static int
+ad_version(u_int16_t version)
+{
+ int bit;
+
+ if (version == 0xffff)
+ return 0;
+ for (bit = 15; bit >= 0; bit--)
+ if (version & (1<<bit))
+ return bit;
+ return 0;
+}
+
+static device_method_t ad_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ad_probe),
+ DEVMETHOD(device_attach, ad_attach),
+ DEVMETHOD(device_detach, ad_detach),
+ DEVMETHOD(device_shutdown, ad_shutdown),
+
+ /* ATA methods */
+ DEVMETHOD(ata_reinit, ad_reinit),
+
+ { 0, 0 }
+};
+
+static driver_t ad_driver = {
+ "ad",
+ ad_methods,
+ 0,
+};
+
+devclass_t ad_devclass;
+
+DRIVER_MODULE(ad, ata, ad_driver, ad_devclass, NULL, NULL);
+MODULE_VERSION(ad, 1);
+MODULE_DEPEND(ad, ata, 1, 1, 1);
diff --git a/sys/dev/ata/ata-disk.h b/sys/dev/ata/ata-disk.h
new file mode 100644
index 000000000000..c602f2e06b2e
--- /dev/null
+++ b/sys/dev/ata/ata-disk.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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$
+ */
+
+/* structure describing an ATA disk */
+struct ad_softc {
+ u_int64_t total_secs; /* total # of sectors (LBA) */
+ u_int8_t heads;
+ u_int8_t sectors;
+ u_int32_t transfersize; /* size of each transfer */
+ int num_tags; /* number of tags supported */
+ int flags; /* drive flags */
+#define AD_F_LABELLING 0x0001
+#define AD_F_CHS_USED 0x0002
+#define AD_F_32B_ENABLED 0x0004
+#define AD_F_TAG_ENABLED 0x0008
+#define AD_F_RAID_SUBDISK 0x0010
+
+ struct disk *disk; /* disklabel/slice stuff */
+};
+
+extern devclass_t ad_devclass;
+
diff --git a/sys/dev/ata/ata-dma.c b/sys/dev/ata/ata-dma.c
new file mode 100644
index 000000000000..6f94084d3b66
--- /dev/null
+++ b/sys/dev/ata/ata-dma.c
@@ -0,0 +1,282 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <dev/pci/pcivar.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/ata-pci.h>
+
+/* prototypes */
+static void ata_dmaalloc(device_t);
+static void ata_dmafree(device_t);
+static void ata_dmasetprd(void *, bus_dma_segment_t *, int, int);
+static int ata_dmaload(device_t, caddr_t, int32_t, int, void *, int *);
+static int ata_dmaunload(device_t);
+
+/* local vars */
+static MALLOC_DEFINE(M_ATADMA, "ata_dma", "ATA driver DMA");
+
+/* misc defines */
+#define MAXTABSZ PAGE_SIZE
+#define MAXWSPCSZ PAGE_SIZE*2
+
+struct ata_dc_cb_args {
+ bus_addr_t maddr;
+ int error;
+};
+
+void
+ata_dmainit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if ((ch->dma = malloc(sizeof(struct ata_dma), M_ATADMA, M_NOWAIT|M_ZERO))) {
+ ch->dma->alloc = ata_dmaalloc;
+ ch->dma->free = ata_dmafree;
+ ch->dma->setprd = ata_dmasetprd;
+ ch->dma->load = ata_dmaload;
+ ch->dma->unload = ata_dmaunload;
+ ch->dma->alignment = 2;
+ ch->dma->boundary = 128 * DEV_BSIZE;
+ ch->dma->segsize = 128 * DEV_BSIZE;
+ ch->dma->max_iosize = 128 * DEV_BSIZE;
+ }
+}
+
+static void
+ata_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct ata_dc_cb_args *cba = (struct ata_dc_cb_args *)xsc;
+
+ if (!(cba->error = error))
+ cba->maddr = segs[0].ds_addr;
+}
+
+static void
+ata_dmaalloc(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ struct ata_dc_cb_args ccba;
+
+ if (bus_dma_tag_create(NULL, ch->dma->alignment, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+ NULL, NULL, ch->dma->max_iosize,
+ ATA_DMA_ENTRIES, ch->dma->segsize,
+ 0, NULL, NULL, &ch->dma->dmatag))
+ goto error;
+
+ if (bus_dma_tag_create(ch->dma->dmatag, PAGE_SIZE, PAGE_SIZE,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+ NULL, NULL, MAXTABSZ, 1, MAXTABSZ,
+ 0, NULL, NULL, &ch->dma->sg_tag))
+ goto error;
+
+ if (bus_dma_tag_create(ch->dma->dmatag,ch->dma->alignment,ch->dma->boundary,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+ NULL, NULL, ch->dma->max_iosize,
+ ATA_DMA_ENTRIES, ch->dma->segsize,
+ 0, NULL, NULL, &ch->dma->data_tag))
+ goto error;
+
+ if (bus_dmamem_alloc(ch->dma->sg_tag, (void **)&ch->dma->sg, 0,
+ &ch->dma->sg_map))
+ goto error;
+
+ if (bus_dmamap_load(ch->dma->sg_tag, ch->dma->sg_map, ch->dma->sg,
+ MAXTABSZ, ata_dmasetupc_cb, &ccba, 0) || ccba.error) {
+ bus_dmamem_free(ch->dma->sg_tag, ch->dma->sg, ch->dma->sg_map);
+ goto error;
+ }
+ ch->dma->sg_bus = ccba.maddr;
+
+ if (bus_dmamap_create(ch->dma->data_tag, 0, &ch->dma->data_map))
+ goto error;
+
+ if (bus_dma_tag_create(ch->dma->dmatag, PAGE_SIZE, 64 * 1024,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+ NULL, NULL, MAXWSPCSZ, 1, MAXWSPCSZ,
+ 0, NULL, NULL, &ch->dma->work_tag))
+ goto error;
+
+ if (bus_dmamem_alloc(ch->dma->work_tag, (void **)&ch->dma->work, 0,
+ &ch->dma->work_map))
+ goto error;
+
+ if (bus_dmamap_load(ch->dma->work_tag, ch->dma->work_map,ch->dma->work,
+ MAXWSPCSZ, ata_dmasetupc_cb, &ccba, 0) || ccba.error) {
+ bus_dmamem_free(ch->dma->work_tag,ch->dma->work, ch->dma->work_map);
+ goto error;
+ }
+ ch->dma->work_bus = ccba.maddr;
+
+ return;
+
+error:
+ device_printf(dev, "WARNING - DMA allocation failed, disabling DMA\n");
+ ata_dmafree(dev);
+ free(ch->dma, M_ATADMA);
+ ch->dma = NULL;
+}
+
+static void
+ata_dmafree(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ch->dma->work_bus) {
+ bus_dmamap_unload(ch->dma->work_tag, ch->dma->work_map);
+ bus_dmamem_free(ch->dma->work_tag, ch->dma->work, ch->dma->work_map);
+ ch->dma->work_bus = 0;
+ ch->dma->work_map = NULL;
+ ch->dma->work = NULL;
+ }
+ if (ch->dma->work_tag) {
+ bus_dma_tag_destroy(ch->dma->work_tag);
+ ch->dma->work_tag = NULL;
+ }
+ if (ch->dma->sg_bus) {
+ bus_dmamap_unload(ch->dma->sg_tag, ch->dma->sg_map);
+ bus_dmamem_free(ch->dma->sg_tag, ch->dma->sg, ch->dma->sg_map);
+ ch->dma->sg_bus = 0;
+ ch->dma->sg_map = NULL;
+ ch->dma->sg = NULL;
+ }
+ if (ch->dma->data_map) {
+ bus_dmamap_destroy(ch->dma->data_tag, ch->dma->data_map);
+ ch->dma->data_map = NULL;
+ }
+ if (ch->dma->sg_tag) {
+ bus_dma_tag_destroy(ch->dma->sg_tag);
+ ch->dma->sg_tag = NULL;
+ }
+ if (ch->dma->data_tag) {
+ bus_dma_tag_destroy(ch->dma->data_tag);
+ ch->dma->data_tag = NULL;
+ }
+ if (ch->dma->dmatag) {
+ bus_dma_tag_destroy(ch->dma->dmatag);
+ ch->dma->dmatag = NULL;
+ }
+}
+
+static void
+ata_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct ata_dmasetprd_args *args = xsc;
+ struct ata_dma_prdentry *prd = args->dmatab;
+ int i;
+
+ if ((args->error = error))
+ return;
+
+ for (i = 0; i < nsegs; i++) {
+ prd[i].addr = htole32(segs[i].ds_addr);
+ prd[i].count = htole32(segs[i].ds_len);
+ }
+ prd[i - 1].count |= htole32(ATA_DMA_EOT);
+ args->nsegs = nsegs;
+}
+
+static int
+ata_dmaload(device_t dev, caddr_t data, int32_t count, int dir,
+ void *addr, int *entries)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ struct ata_dmasetprd_args cba;
+ int error;
+
+ if (ch->dma->flags & ATA_DMA_LOADED) {
+ device_printf(dev, "FAILURE - already active DMA on this device\n");
+ return EIO;
+ }
+ if (!count) {
+ device_printf(dev, "FAILURE - zero length DMA transfer attempted\n");
+ return EIO;
+ }
+ if (((uintptr_t)data & (ch->dma->alignment - 1)) ||
+ (count & (ch->dma->alignment - 1))) {
+ device_printf(dev, "FAILURE - non aligned DMA transfer attempted\n");
+ return EIO;
+ }
+ if (count > ch->dma->max_iosize) {
+ device_printf(dev, "FAILURE - oversized DMA transfer attempt %d > %d\n",
+ count, ch->dma->max_iosize);
+ return EIO;
+ }
+
+ cba.dmatab = addr;
+
+ if ((error = bus_dmamap_load(ch->dma->data_tag, ch->dma->data_map,
+ data, count, ch->dma->setprd, &cba,
+ BUS_DMA_NOWAIT)) || (error = cba.error))
+ return error;
+
+ *entries = cba.nsegs;
+
+ bus_dmamap_sync(ch->dma->sg_tag, ch->dma->sg_map, BUS_DMASYNC_PREWRITE);
+
+ bus_dmamap_sync(ch->dma->data_tag, ch->dma->data_map,
+ dir ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
+
+ ch->dma->cur_iosize = count;
+ ch->dma->flags = dir ? (ATA_DMA_LOADED | ATA_DMA_READ) : ATA_DMA_LOADED;
+ return 0;
+}
+
+int
+ata_dmaunload(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ch->dma->flags & ATA_DMA_LOADED) {
+ bus_dmamap_sync(ch->dma->sg_tag, ch->dma->sg_map,
+ BUS_DMASYNC_POSTWRITE);
+
+ bus_dmamap_sync(ch->dma->data_tag, ch->dma->data_map,
+ (ch->dma->flags & ATA_DMA_READ) != 0 ?
+ BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(ch->dma->data_tag, ch->dma->data_map);
+
+ ch->dma->cur_iosize = 0;
+ ch->dma->flags &= ~ATA_DMA_LOADED;
+ }
+ return 0;
+}
diff --git a/sys/dev/ata/ata-isa.c b/sys/dev/ata/ata-isa.c
new file mode 100644
index 000000000000..a5ac568e4bdf
--- /dev/null
+++ b/sys/dev/ata/ata-isa.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/stdarg.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <isa/isavar.h>
+#include <dev/ata/ata-all.h>
+#include <ata_if.h>
+
+/* local vars */
+static struct isa_pnp_id ata_ids[] = {
+ {0x0006d041, "Generic ESDI/IDE/ATA controller"}, /* PNP0600 */
+ {0x0106d041, "Plus Hardcard II"}, /* PNP0601 */
+ {0x0206d041, "Plus Hardcard IIXL/EZ"}, /* PNP0602 */
+ {0x0306d041, "Generic ATA"}, /* PNP0603 */
+ /* PNP0680 */
+ {0x8006d041, "Standard bus mastering IDE hard disk controller"},
+ {0}
+};
+
+static int
+ata_isa_probe(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ struct resource *io = NULL, *ctlio = NULL;
+ u_long tmp;
+ int i, rid;
+
+ /* check isapnp ids */
+ if (ISA_PNP_PROBE(device_get_parent(dev), dev, ata_ids) == ENXIO)
+ return ENXIO;
+
+ /* allocate the io port range */
+ rid = ATA_IOADDR_RID;
+ if (!(io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
+ ATA_IOSIZE, RF_ACTIVE)))
+ return ENXIO;
+
+ /* set the altport range */
+ if (bus_get_resource(dev, SYS_RES_IOPORT, ATA_CTLADDR_RID, &tmp, &tmp)) {
+ bus_set_resource(dev, SYS_RES_IOPORT, ATA_CTLADDR_RID,
+ rman_get_start(io) + ATA_CTLOFFSET, ATA_CTLIOSIZE);
+ }
+
+ /* allocate the altport range */
+ rid = ATA_CTLADDR_RID;
+ if (!(ctlio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
+ ATA_CTLIOSIZE, RF_ACTIVE))) {
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, io);
+ return ENXIO;
+ }
+
+ /* setup the resource vectors */
+ for (i = ATA_DATA; i <= ATA_COMMAND; i++) {
+ ch->r_io[i].res = io;
+ ch->r_io[i].offset = i;
+ }
+ ch->r_io[ATA_CONTROL].res = ctlio;
+ ch->r_io[ATA_CONTROL].offset = 0;
+ ch->r_io[ATA_IDX_ADDR].res = io;
+ ata_default_registers(dev);
+
+ /* initialize softc for this channel */
+ ch->unit = 0;
+ ch->flags |= ATA_USE_16BIT;
+ ata_generic_hw(dev);
+ return ata_probe(dev);
+}
+
+static device_method_t ata_isa_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_isa_probe),
+ DEVMETHOD(device_attach, ata_attach),
+ DEVMETHOD(device_suspend, ata_suspend),
+ DEVMETHOD(device_resume, ata_resume),
+
+ { 0, 0 }
+};
+
+static driver_t ata_isa_driver = {
+ "ata",
+ ata_isa_methods,
+ sizeof(struct ata_channel),
+};
+
+DRIVER_MODULE(ata, isa, ata_isa_driver, ata_devclass, 0, 0);
+MODULE_DEPEND(ata, ata, 1, 1, 1);
diff --git a/sys/dev/ata/ata-lowlevel.c b/sys/dev/ata/ata-lowlevel.c
new file mode 100644
index 000000000000..ca5afc2e8678
--- /dev/null
+++ b/sys/dev/ata/ata-lowlevel.c
@@ -0,0 +1,837 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/ata.h>
+#include <sys/conf.h>
+#include <sys/ctype.h>
+#include <sys/bus.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/ata-pci.h>
+#include <ata_if.h>
+
+/* prototypes */
+static int ata_generic_status(device_t dev);
+static int ata_wait(struct ata_channel *ch, struct ata_device *, u_int8_t);
+static void ata_pio_read(struct ata_request *, int);
+static void ata_pio_write(struct ata_request *, int);
+
+/*
+ * low level ATA functions
+ */
+void
+ata_generic_hw(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ch->hw.begin_transaction = ata_begin_transaction;
+ ch->hw.end_transaction = ata_end_transaction;
+ ch->hw.status = ata_generic_status;
+ ch->hw.command = ata_generic_command;
+}
+
+/* must be called with ATA channel locked and state_mtx held */
+int
+ata_begin_transaction(struct ata_request *request)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ struct ata_device *atadev = device_get_softc(request->dev);
+ int dummy, error;
+
+ ATA_DEBUG_RQ(request, "begin transaction");
+
+ /* disable ATAPI DMA writes if HW doesn't support it */
+ if ((ch->flags & ATA_ATAPI_DMA_RO) &&
+ ((request->flags & (ATA_R_ATAPI | ATA_R_DMA | ATA_R_WRITE)) ==
+ (ATA_R_ATAPI | ATA_R_DMA | ATA_R_WRITE)))
+ request->flags &= ~ATA_R_DMA;
+
+ /* check for 48 bit access and convert if needed */
+ ata_modify_if_48bit(request);
+
+ switch (request->flags & (ATA_R_ATAPI | ATA_R_DMA)) {
+
+ /* ATA PIO data transfer and control commands */
+ default:
+ {
+ /* record command direction here as our request might be gone later */
+ int write = (request->flags & ATA_R_WRITE);
+
+ /* issue command */
+ if (ch->hw.command(request)) {
+ device_printf(request->dev, "error issuing %s command\n",
+ ata_cmd2str(request));
+ request->result = EIO;
+ goto begin_finished;
+ }
+
+ /* device reset doesn't interrupt */
+ if (request->u.ata.command == ATA_DEVICE_RESET) {
+ int timeout = 1000000;
+ do {
+ DELAY(10);
+ request->status = ATA_IDX_INB(ch, ATA_STATUS);
+ } while (request->status & ATA_S_BUSY && timeout--);
+ if (request->status & ATA_S_ERROR)
+ request->error = ATA_IDX_INB(ch, ATA_ERROR);
+ goto begin_finished;
+ }
+
+ /* if write command output the data */
+ if (write) {
+ if (ata_wait(ch, atadev, (ATA_S_READY | ATA_S_DRQ)) < 0) {
+ device_printf(request->dev,
+ "timeout waiting for write DRQ\n");
+ request->result = EIO;
+ goto begin_finished;
+ }
+ ata_pio_write(request, request->transfersize);
+ }
+ }
+ goto begin_continue;
+
+ /* ATA DMA data transfer commands */
+ case ATA_R_DMA:
+ /* check sanity, setup SG list and DMA engine */
+ if ((error = ch->dma->load(ch->dev, request->data, request->bytecount,
+ request->flags & ATA_R_READ, ch->dma->sg,
+ &dummy))) {
+ device_printf(request->dev, "setting up DMA failed\n");
+ request->result = error;
+ goto begin_finished;
+ }
+
+ /* issue command */
+ if (ch->hw.command(request)) {
+ device_printf(request->dev, "error issuing %s command\n",
+ ata_cmd2str(request));
+ request->result = EIO;
+ goto begin_finished;
+ }
+
+ /* start DMA engine */
+ if (ch->dma->start && ch->dma->start(request->dev)) {
+ device_printf(request->dev, "error starting DMA\n");
+ request->result = EIO;
+ goto begin_finished;
+ }
+ goto begin_continue;
+
+ /* ATAPI PIO commands */
+ case ATA_R_ATAPI:
+ /* is this just a POLL DSC command ? */
+ if (request->u.atapi.ccb[0] == ATAPI_POLL_DSC) {
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | atadev->unit);
+ DELAY(10);
+ if (!(ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_DSC))
+ request->result = EBUSY;
+ goto begin_finished;
+ }
+
+ /* start ATAPI operation */
+ if (ch->hw.command(request)) {
+ device_printf(request->dev, "error issuing ATA PACKET command\n");
+ request->result = EIO;
+ goto begin_finished;
+ }
+ goto begin_continue;
+
+ /* ATAPI DMA commands */
+ case ATA_R_ATAPI|ATA_R_DMA:
+ /* is this just a POLL DSC command ? */
+ if (request->u.atapi.ccb[0] == ATAPI_POLL_DSC) {
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | atadev->unit);
+ DELAY(10);
+ if (!(ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_DSC))
+ request->result = EBUSY;
+ goto begin_finished;
+ }
+
+ /* check sanity, setup SG list and DMA engine */
+ if ((error = ch->dma->load(ch->dev, request->data, request->bytecount,
+ request->flags & ATA_R_READ, ch->dma->sg,
+ &dummy))) {
+ device_printf(request->dev, "setting up DMA failed\n");
+ request->result = error;
+ goto begin_finished;
+ }
+
+ /* start ATAPI operation */
+ if (ch->hw.command(request)) {
+ device_printf(request->dev, "error issuing ATA PACKET command\n");
+ request->result = EIO;
+ goto begin_finished;
+ }
+
+ /* start DMA engine */
+ if (ch->dma->start && ch->dma->start(request->dev)) {
+ request->result = EIO;
+ goto begin_finished;
+ }
+ goto begin_continue;
+ }
+ /* NOT REACHED */
+ printf("ata_begin_transaction OOPS!!!\n");
+
+begin_finished:
+ if (ch->dma && ch->dma->flags & ATA_DMA_LOADED)
+ ch->dma->unload(ch->dev);
+ return ATA_OP_FINISHED;
+
+begin_continue:
+ callout_reset(&request->callout, request->timeout * hz,
+ (timeout_t*)ata_timeout, request);
+ return ATA_OP_CONTINUES;
+}
+
+/* must be called with ATA channel locked and state_mtx held */
+int
+ata_end_transaction(struct ata_request *request)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ struct ata_device *atadev = device_get_softc(request->dev);
+ int length;
+
+ ATA_DEBUG_RQ(request, "end transaction");
+
+ /* clear interrupt and get status */
+ request->status = ATA_IDX_INB(ch, ATA_STATUS);
+
+ switch (request->flags & (ATA_R_ATAPI | ATA_R_DMA | ATA_R_CONTROL)) {
+
+ /* ATA PIO data transfer and control commands */
+ default:
+
+ /* on timeouts we have no data or anything so just return */
+ if (request->flags & ATA_R_TIMEOUT)
+ goto end_finished;
+
+ /* on control commands read back registers to the request struct */
+ if (request->flags & ATA_R_CONTROL) {
+ if (atadev->flags & ATA_D_48BIT_ACTIVE) {
+ ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_4BIT | ATA_A_HOB);
+ request->u.ata.count = (ATA_IDX_INB(ch, ATA_COUNT) << 8);
+ request->u.ata.lba =
+ ((u_int64_t)(ATA_IDX_INB(ch, ATA_SECTOR)) << 24) |
+ ((u_int64_t)(ATA_IDX_INB(ch, ATA_CYL_LSB)) << 32) |
+ ((u_int64_t)(ATA_IDX_INB(ch, ATA_CYL_MSB)) << 40);
+
+ ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_4BIT);
+ request->u.ata.count |= ATA_IDX_INB(ch, ATA_COUNT);
+ request->u.ata.lba |=
+ (ATA_IDX_INB(ch, ATA_SECTOR) |
+ (ATA_IDX_INB(ch, ATA_CYL_LSB) << 8) |
+ (ATA_IDX_INB(ch, ATA_CYL_MSB) << 16));
+ }
+ else {
+ request->u.ata.count = ATA_IDX_INB(ch, ATA_COUNT);
+ request->u.ata.lba = ATA_IDX_INB(ch, ATA_SECTOR) |
+ (ATA_IDX_INB(ch, ATA_CYL_LSB) << 8) |
+ (ATA_IDX_INB(ch, ATA_CYL_MSB) << 16) |
+ ((ATA_IDX_INB(ch, ATA_DRIVE) & 0xf) << 24);
+ }
+ }
+
+ /* if we got an error we are done with the HW */
+ if (request->status & ATA_S_ERROR) {
+ request->error = ATA_IDX_INB(ch, ATA_ERROR);
+ goto end_finished;
+ }
+
+ /* are we moving data ? */
+ if (request->flags & (ATA_R_READ | ATA_R_WRITE)) {
+
+ /* if read data get it */
+ if (request->flags & ATA_R_READ) {
+ int flags = ATA_S_DRQ;
+
+ if (request->u.ata.command != ATA_ATAPI_IDENTIFY)
+ flags |= ATA_S_READY;
+ if (ata_wait(ch, atadev, flags) < 0) {
+ device_printf(request->dev,
+ "timeout waiting for read DRQ\n");
+ request->result = EIO;
+ goto end_finished;
+ }
+ ata_pio_read(request, request->transfersize);
+ }
+
+ /* update how far we've gotten */
+ request->donecount += request->transfersize;
+
+ /* do we need a scoop more ? */
+ if (request->bytecount > request->donecount) {
+
+ /* set this transfer size according to HW capabilities */
+ request->transfersize =
+ min((request->bytecount - request->donecount),
+ request->transfersize);
+
+ /* if data write command, output the data */
+ if (request->flags & ATA_R_WRITE) {
+
+ /* if we get an error here we are done with the HW */
+ if (ata_wait(ch, atadev, (ATA_S_READY | ATA_S_DRQ)) < 0) {
+ device_printf(request->dev,
+ "timeout waiting for write DRQ\n");
+ request->status = ATA_IDX_INB(ch, ATA_STATUS);
+ goto end_finished;
+ }
+
+ /* output data and return waiting for new interrupt */
+ ata_pio_write(request, request->transfersize);
+ goto end_continue;
+ }
+
+ /* if data read command, return & wait for interrupt */
+ if (request->flags & ATA_R_READ)
+ goto end_continue;
+ }
+ }
+ /* done with HW */
+ goto end_finished;
+
+ /* ATA DMA data transfer commands */
+ case ATA_R_DMA:
+
+ /* stop DMA engine and get status */
+ if (ch->dma->stop)
+ request->dmastat = ch->dma->stop(request->dev);
+
+ /* did we get error or data */
+ if (request->status & ATA_S_ERROR)
+ request->error = ATA_IDX_INB(ch, ATA_ERROR);
+ else if (request->dmastat & ATA_BMSTAT_ERROR)
+ request->status |= ATA_S_ERROR;
+ else if (!(request->flags & ATA_R_TIMEOUT))
+ request->donecount = request->bytecount;
+
+ /* release SG list etc */
+ ch->dma->unload(ch->dev);
+
+ /* done with HW */
+ goto end_finished;
+
+ /* ATAPI PIO commands */
+ case ATA_R_ATAPI:
+ length = ATA_IDX_INB(ch, ATA_CYL_LSB)|(ATA_IDX_INB(ch, ATA_CYL_MSB)<<8);
+
+ /* on timeouts we have no data or anything so just return */
+ if (request->flags & ATA_R_TIMEOUT)
+ goto end_finished;
+
+ switch ((ATA_IDX_INB(ch, ATA_IREASON) & (ATA_I_CMD | ATA_I_IN)) |
+ (request->status & ATA_S_DRQ)) {
+
+ case ATAPI_P_CMDOUT:
+ /* this seems to be needed for some (slow) devices */
+ DELAY(10);
+
+ if (!(request->status & ATA_S_DRQ)) {
+ device_printf(request->dev, "command interrupt without DRQ\n");
+ request->status = ATA_S_ERROR;
+ goto end_finished;
+ }
+ ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (int16_t *)request->u.atapi.ccb,
+ (atadev->param.config &
+ ATA_PROTO_MASK)== ATA_PROTO_ATAPI_12 ? 6 : 8);
+ /* return wait for interrupt */
+ goto end_continue;
+
+ case ATAPI_P_WRITE:
+ if (request->flags & ATA_R_READ) {
+ request->status = ATA_S_ERROR;
+ device_printf(request->dev,
+ "%s trying to write on read buffer\n",
+ ata_cmd2str(request));
+ goto end_finished;
+ break;
+ }
+ ata_pio_write(request, length);
+ request->donecount += length;
+
+ /* set next transfer size according to HW capabilities */
+ request->transfersize = min((request->bytecount-request->donecount),
+ request->transfersize);
+ /* return wait for interrupt */
+ goto end_continue;
+
+ case ATAPI_P_READ:
+ if (request->flags & ATA_R_WRITE) {
+ request->status = ATA_S_ERROR;
+ device_printf(request->dev,
+ "%s trying to read on write buffer\n",
+ ata_cmd2str(request));
+ goto end_finished;
+ }
+ ata_pio_read(request, length);
+ request->donecount += length;
+
+ /* set next transfer size according to HW capabilities */
+ request->transfersize = min((request->bytecount-request->donecount),
+ request->transfersize);
+ /* return wait for interrupt */
+ goto end_continue;
+
+ case ATAPI_P_DONEDRQ:
+ device_printf(request->dev,
+ "WARNING - %s DONEDRQ non conformant device\n",
+ ata_cmd2str(request));
+ if (request->flags & ATA_R_READ) {
+ ata_pio_read(request, length);
+ request->donecount += length;
+ }
+ else if (request->flags & ATA_R_WRITE) {
+ ata_pio_write(request, length);
+ request->donecount += length;
+ }
+ else
+ request->status = ATA_S_ERROR;
+ /* FALLTHROUGH */
+
+ case ATAPI_P_ABORT:
+ case ATAPI_P_DONE:
+ if (request->status & (ATA_S_ERROR | ATA_S_DWF))
+ request->error = ATA_IDX_INB(ch, ATA_ERROR);
+ goto end_finished;
+
+ default:
+ device_printf(request->dev, "unknown transfer phase\n");
+ request->status = ATA_S_ERROR;
+ }
+
+ /* done with HW */
+ goto end_finished;
+
+ /* ATAPI DMA commands */
+ case ATA_R_ATAPI|ATA_R_DMA:
+
+ /* stop DMA engine and get status */
+ if (ch->dma->stop)
+ request->dmastat = ch->dma->stop(request->dev);
+
+ /* did we get error or data */
+ if (request->status & (ATA_S_ERROR | ATA_S_DWF))
+ request->error = ATA_IDX_INB(ch, ATA_ERROR);
+ else if (request->dmastat & ATA_BMSTAT_ERROR)
+ request->status |= ATA_S_ERROR;
+ else if (!(request->flags & ATA_R_TIMEOUT))
+ request->donecount = request->bytecount;
+
+ /* release SG list etc */
+ ch->dma->unload(ch->dev);
+
+ /* done with HW */
+ goto end_finished;
+ }
+ /* NOT REACHED */
+ printf("ata_end_transaction OOPS!!\n");
+
+end_finished:
+ callout_stop(&request->callout);
+ return ATA_OP_FINISHED;
+
+end_continue:
+ return ATA_OP_CONTINUES;
+}
+
+/* must be called with ATA channel locked and state_mtx held */
+void
+ata_generic_reset(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ u_int8_t ostat0 = 0, stat0 = 0, ostat1 = 0, stat1 = 0;
+ u_int8_t err = 0, lsb = 0, msb = 0;
+ int mask = 0, timeout;
+
+ /* do we have any signs of ATA/ATAPI HW being present ? */
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_MASTER);
+ DELAY(10);
+ ostat0 = ATA_IDX_INB(ch, ATA_STATUS);
+ if ((ostat0 & 0xf8) != 0xf8 && ostat0 != 0xa5) {
+ stat0 = ATA_S_BUSY;
+ mask |= 0x01;
+ }
+
+ /* in some setups we dont want to test for a slave */
+ if (!(ch->flags & ATA_NO_SLAVE)) {
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_SLAVE);
+ DELAY(10);
+ ostat1 = ATA_IDX_INB(ch, ATA_STATUS);
+ if ((ostat1 & 0xf8) != 0xf8 && ostat1 != 0xa5) {
+ stat1 = ATA_S_BUSY;
+ mask |= 0x02;
+ }
+ }
+
+ if (bootverbose)
+ device_printf(dev, "reset tp1 mask=%02x ostat0=%02x ostat1=%02x\n",
+ mask, ostat0, ostat1);
+
+ /* if nothing showed up there is no need to get any further */
+ /* XXX SOS is that too strong?, we just might loose devices here */
+ ch->devices = 0;
+ if (!mask)
+ return;
+
+ /* reset (both) devices on this channel */
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_MASTER);
+ DELAY(10);
+ ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_IDS | ATA_A_RESET);
+ ata_udelay(10000);
+ ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_IDS);
+ ata_udelay(100000);
+ ATA_IDX_INB(ch, ATA_ERROR);
+
+ /* wait for BUSY to go inactive */
+ for (timeout = 0; timeout < 310; timeout++) {
+ if ((mask & 0x01) && (stat0 & ATA_S_BUSY)) {
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
+ DELAY(10);
+ err = ATA_IDX_INB(ch, ATA_ERROR);
+ lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
+ msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
+ stat0 = ATA_IDX_INB(ch, ATA_STATUS);
+ if (bootverbose)
+ device_printf(dev,
+ "stat0=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n",
+ stat0, err, lsb, msb);
+ if (stat0 == err && lsb == err && msb == err &&
+ timeout > (stat0 & ATA_S_BUSY ? 100 : 10))
+ mask &= ~0x01;
+ if (!(stat0 & ATA_S_BUSY)) {
+ if ((err & 0x7f) == ATA_E_ILI) {
+ if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) {
+ ch->devices |= ATA_ATAPI_MASTER;
+ }
+ else if (stat0 & ATA_S_READY) {
+ ch->devices |= ATA_ATA_MASTER;
+ }
+ }
+ else if ((stat0 & 0x0f) && err == lsb && err == msb) {
+ stat0 |= ATA_S_BUSY;
+ }
+ }
+ }
+
+ if ((mask & 0x02) && (stat1 & ATA_S_BUSY) &&
+ !((mask & 0x01) && (stat0 & ATA_S_BUSY))) {
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
+ DELAY(10);
+ err = ATA_IDX_INB(ch, ATA_ERROR);
+ lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
+ msb = ATA_IDX_INB(ch, ATA_CYL_MSB);
+ stat1 = ATA_IDX_INB(ch, ATA_STATUS);
+ if (bootverbose)
+ device_printf(dev,
+ "stat1=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n",
+ stat1, err, lsb, msb);
+ if (stat1 == err && lsb == err && msb == err &&
+ timeout > (stat1 & ATA_S_BUSY ? 100 : 10))
+ mask &= ~0x02;
+ if (!(stat1 & ATA_S_BUSY)) {
+ if ((err & 0x7f) == ATA_E_ILI) {
+ if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) {
+ ch->devices |= ATA_ATAPI_SLAVE;
+ }
+ else if (stat1 & ATA_S_READY) {
+ ch->devices |= ATA_ATA_SLAVE;
+ }
+ }
+ else if ((stat1 & 0x0f) && err == lsb && err == msb) {
+ stat1 |= ATA_S_BUSY;
+ }
+ }
+ }
+
+ if (mask == 0x00) /* nothing to wait for */
+ break;
+ if (mask == 0x01) /* wait for master only */
+ if (!(stat0 & ATA_S_BUSY) || (stat0 == 0xff && timeout > 10))
+ break;
+ if (mask == 0x02) /* wait for slave only */
+ if (!(stat1 & ATA_S_BUSY) || (stat1 == 0xff && timeout > 10))
+ break;
+ if (mask == 0x03) { /* wait for both master & slave */
+ if (!(stat0 & ATA_S_BUSY) && !(stat1 & ATA_S_BUSY))
+ break;
+ if ((stat0 == 0xff) && (timeout > 20))
+ mask &= ~0x01;
+ if ((stat1 == 0xff) && (timeout > 20))
+ mask &= ~0x02;
+ }
+ ata_udelay(100000);
+ }
+
+ if (bootverbose)
+ device_printf(dev, "reset tp2 stat0=%02x stat1=%02x devices=0x%b\n",
+ stat0, stat1, ch->devices,
+ "\20\4ATAPI_SLAVE\3ATAPI_MASTER\2ATA_SLAVE\1ATA_MASTER");
+}
+
+/* must be called with ATA channel locked and state_mtx held */
+int
+ata_generic_status(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
+ DELAY(100);
+ if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
+ return 0;
+ }
+ return 1;
+}
+
+static int
+ata_wait(struct ata_channel *ch, struct ata_device *atadev, u_int8_t mask)
+{
+ u_int8_t status;
+ int timeout = 0;
+
+ DELAY(1);
+
+ /* wait at max 1 second for device to get !BUSY */
+ while (timeout < 1000000) {
+ status = ATA_IDX_INB(ch, ATA_ALTSTAT);
+
+ /* if drive fails status, reselect the drive and try again */
+ if (status == 0xff) {
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | atadev->unit);
+ timeout += 1000;
+ DELAY(1000);
+ continue;
+ }
+
+ /* are we done ? */
+ if (!(status & ATA_S_BUSY))
+ break;
+
+ if (timeout > 1000) {
+ timeout += 1000;
+ DELAY(1000);
+ }
+ else {
+ timeout += 10;
+ DELAY(10);
+ }
+ }
+ if (timeout >= 1000000)
+ return -2;
+ if (!mask)
+ return (status & ATA_S_ERROR);
+
+ DELAY(1);
+
+ /* wait 50 msec for bits wanted */
+ timeout = 5000;
+ while (timeout--) {
+ status = ATA_IDX_INB(ch, ATA_ALTSTAT);
+ if ((status & mask) == mask)
+ return (status & ATA_S_ERROR);
+ DELAY(10);
+ }
+ return -3;
+}
+
+int
+ata_generic_command(struct ata_request *request)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ struct ata_device *atadev = device_get_softc(request->dev);
+
+ /* select device */
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | atadev->unit);
+
+ /* ready to issue command ? */
+ if (ata_wait(ch, atadev, 0) < 0) {
+ device_printf(request->dev, "timeout waiting to issue command\n");
+ return -1;
+ }
+
+ /* enable interrupt */
+ ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_4BIT);
+
+ if (request->flags & ATA_R_ATAPI) {
+ int timeout = 5000;
+
+ /* issue packet command to controller */
+ if (request->flags & ATA_R_DMA) {
+ ATA_IDX_OUTB(ch, ATA_FEATURE, ATA_F_DMA);
+ ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0);
+ ATA_IDX_OUTB(ch, ATA_CYL_MSB, 0);
+ }
+ else {
+ ATA_IDX_OUTB(ch, ATA_FEATURE, 0);
+ ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->transfersize);
+ ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->transfersize >> 8);
+ }
+ ATA_IDX_OUTB(ch, ATA_COMMAND, ATA_PACKET_CMD);
+
+ /* command interrupt device ? just return and wait for interrupt */
+ if ((atadev->param.config & ATA_DRQ_MASK) == ATA_DRQ_INTR)
+ return 0;
+
+ /* wait for ready to write ATAPI command block */
+ while (timeout--) {
+ int reason = ATA_IDX_INB(ch, ATA_IREASON);
+ int status = ATA_IDX_INB(ch, ATA_STATUS);
+
+ if (((reason & (ATA_I_CMD | ATA_I_IN)) |
+ (status & (ATA_S_DRQ | ATA_S_BUSY))) == ATAPI_P_CMDOUT)
+ break;
+ DELAY(20);
+ }
+ if (timeout <= 0) {
+ device_printf(request->dev, "timeout waiting for ATAPI ready\n");
+ request->result = EIO;
+ return -1;
+ }
+
+ /* this seems to be needed for some (slow) devices */
+ DELAY(10);
+
+ /* output command block */
+ ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (int16_t *)request->u.atapi.ccb,
+ (atadev->param.config & ATA_PROTO_MASK) ==
+ ATA_PROTO_ATAPI_12 ? 6 : 8);
+ }
+ else {
+ if (atadev->flags & ATA_D_48BIT_ACTIVE) {
+ ATA_IDX_OUTB(ch, ATA_FEATURE, request->u.ata.feature >> 8);
+ ATA_IDX_OUTB(ch, ATA_FEATURE, request->u.ata.feature);
+ ATA_IDX_OUTB(ch, ATA_COUNT, request->u.ata.count >> 8);
+ ATA_IDX_OUTB(ch, ATA_COUNT, request->u.ata.count);
+ ATA_IDX_OUTB(ch, ATA_SECTOR, request->u.ata.lba >> 24);
+ ATA_IDX_OUTB(ch, ATA_SECTOR, request->u.ata.lba);
+ ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->u.ata.lba >> 32);
+ ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->u.ata.lba >> 8);
+ ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->u.ata.lba >> 40);
+ ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->u.ata.lba >> 16);
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_LBA | atadev->unit);
+ }
+ else {
+ ATA_IDX_OUTB(ch, ATA_FEATURE, request->u.ata.feature);
+ ATA_IDX_OUTB(ch, ATA_COUNT, request->u.ata.count);
+ if (atadev->flags & ATA_D_USE_CHS) {
+ int heads, sectors;
+
+ if (atadev->param.atavalid & ATA_FLAG_54_58) {
+ heads = atadev->param.current_heads;
+ sectors = atadev->param.current_sectors;
+ }
+ else {
+ heads = atadev->param.heads;
+ sectors = atadev->param.sectors;
+ }
+ ATA_IDX_OUTB(ch, ATA_SECTOR, (request->u.ata.lba % sectors)+1);
+ ATA_IDX_OUTB(ch, ATA_CYL_LSB,
+ (request->u.ata.lba / (sectors * heads)));
+ ATA_IDX_OUTB(ch, ATA_CYL_MSB,
+ (request->u.ata.lba / (sectors * heads)) >> 8);
+ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | atadev->unit |
+ (((request->u.ata.lba% (sectors * heads)) /
+ sectors) & 0xf));
+ }
+ else {
+ ATA_IDX_OUTB(ch, ATA_SECTOR, request->u.ata.lba);
+ ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->u.ata.lba >> 8);
+ ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->u.ata.lba >> 16);
+ ATA_IDX_OUTB(ch, ATA_DRIVE,
+ ATA_D_IBM | ATA_D_LBA | atadev->unit |
+ ((request->u.ata.lba >> 24) & 0x0f));
+ }
+ }
+
+ /* issue command to controller */
+ ATA_IDX_OUTB(ch, ATA_COMMAND, request->u.ata.command);
+ }
+
+ return 0;
+}
+
+static void
+ata_pio_read(struct ata_request *request, int length)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ int size = min(request->transfersize, length);
+ int resid;
+
+ if (ch->flags & ATA_USE_16BIT || (size % sizeof(int32_t)))
+ ATA_IDX_INSW_STRM(ch, ATA_DATA,
+ (void*)((uintptr_t)request->data+request->donecount),
+ size / sizeof(int16_t));
+ else
+ ATA_IDX_INSL_STRM(ch, ATA_DATA,
+ (void*)((uintptr_t)request->data+request->donecount),
+ size / sizeof(int32_t));
+
+ if (request->transfersize < length) {
+ device_printf(request->dev, "WARNING - %s read data overrun %d>%d\n",
+ ata_cmd2str(request), length, request->transfersize);
+ for (resid = request->transfersize; resid < length;
+ resid += sizeof(int16_t))
+ ATA_IDX_INW(ch, ATA_DATA);
+ }
+}
+
+static void
+ata_pio_write(struct ata_request *request, int length)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
+ int size = min(request->transfersize, length);
+ int resid;
+
+ if (ch->flags & ATA_USE_16BIT || (size % sizeof(int32_t)))
+ ATA_IDX_OUTSW_STRM(ch, ATA_DATA,
+ (void*)((uintptr_t)request->data+request->donecount),
+ size / sizeof(int16_t));
+ else
+ ATA_IDX_OUTSL_STRM(ch, ATA_DATA,
+ (void*)((uintptr_t)request->data+request->donecount),
+ size / sizeof(int32_t));
+
+ if (request->transfersize < length) {
+ device_printf(request->dev, "WARNING - %s write data underrun %d>%d\n",
+ ata_cmd2str(request), length, request->transfersize);
+ for (resid = request->transfersize; resid < length;
+ resid += sizeof(int16_t))
+ ATA_IDX_OUTW(ch, ATA_DATA, 0);
+ }
+}
diff --git a/sys/dev/ata/ata-pci.c b/sys/dev/ata/ata-pci.c
new file mode 100644
index 000000000000..38d3c068542f
--- /dev/null
+++ b/sys/dev/ata/ata-pci.c
@@ -0,0 +1,677 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/ata.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/stdarg.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#ifdef __alpha__
+#include <machine/md_var.h>
+#endif
+#include <sys/rman.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/ata-pci.h>
+#include <ata_if.h>
+
+/* local vars */
+static MALLOC_DEFINE(M_ATAPCI, "ata_pci", "ATA driver PCI");
+
+/* misc defines */
+#define IOMASK 0xfffffffc
+#define ATA_PROBE_OK -10
+
+int
+ata_legacy(device_t dev)
+{
+ return ((pci_read_config(dev, PCIR_PROGIF, 1)&PCIP_STORAGE_IDE_MASTERDEV) &&
+ ((pci_read_config(dev, PCIR_PROGIF, 1) &
+ (PCIP_STORAGE_IDE_MODEPRIM | PCIP_STORAGE_IDE_MODESEC)) !=
+ (PCIP_STORAGE_IDE_MODEPRIM | PCIP_STORAGE_IDE_MODESEC)));
+}
+
+int
+ata_pci_probe(device_t dev)
+{
+ if (pci_get_class(dev) != PCIC_STORAGE)
+ return ENXIO;
+
+ switch (pci_get_vendor(dev)) {
+ case ATA_ACARD_ID:
+ if (!ata_acard_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_ACER_LABS_ID:
+ if (!ata_ali_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_AMD_ID:
+ if (!ata_amd_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_ATI_ID:
+ if (!ata_ati_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_CYRIX_ID:
+ if (!ata_cyrix_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_CYPRESS_ID:
+ if (!ata_cypress_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_HIGHPOINT_ID:
+ if (!ata_highpoint_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_INTEL_ID:
+ if (!ata_intel_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_ITE_ID:
+ if (!ata_ite_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_JMICRON_ID:
+ if (!ata_jmicron_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_MARVELL_ID:
+ if (!ata_marvell_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_NATIONAL_ID:
+ if (!ata_national_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_NVIDIA_ID:
+ if (!ata_nvidia_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_PROMISE_ID:
+ if (!ata_promise_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_SERVERWORKS_ID:
+ if (!ata_serverworks_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_SILICON_IMAGE_ID:
+ if (!ata_sii_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_SIS_ID:
+ if (!ata_sis_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_VIA_ID:
+ if (!ata_via_ident(dev))
+ return ATA_PROBE_OK;
+ break;
+ case ATA_CENATEK_ID:
+ if (pci_get_devid(dev) == ATA_CENATEK_ROCKET) {
+ ata_generic_ident(dev);
+ device_set_desc(dev, "Cenatek Rocket Drive controller");
+ return ATA_PROBE_OK;
+ }
+ break;
+ case ATA_MICRON_ID:
+ if (pci_get_devid(dev) == ATA_MICRON_RZ1000 ||
+ pci_get_devid(dev) == ATA_MICRON_RZ1001) {
+ ata_generic_ident(dev);
+ device_set_desc(dev,
+ "RZ 100? ATA controller !WARNING! data loss/corruption risk");
+ return ATA_PROBE_OK;
+ }
+ break;
+ }
+
+ /* unknown chipset, try generic DMA if it seems possible */
+ if ((pci_get_class(dev) == PCIC_STORAGE) &&
+ (pci_get_subclass(dev) == PCIS_STORAGE_IDE)) {
+ if (!ata_generic_ident(dev))
+ return ATA_PROBE_OK;
+ }
+ return ENXIO;
+}
+
+int
+ata_pci_attach(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ u_int32_t cmd;
+ int unit;
+
+ /* do chipset specific setups only needed once */
+ if (ata_legacy(dev) || pci_read_config(dev, PCIR_BAR(2), 4) & IOMASK)
+ ctlr->channels = 2;
+ else
+ ctlr->channels = 1;
+ ctlr->allocate = ata_pci_allocate;
+ ctlr->dmainit = ata_pci_dmainit;
+ ctlr->dev = dev;
+
+ /* if needed try to enable busmastering */
+ cmd = pci_read_config(dev, PCIR_COMMAND, 2);
+ if (!(cmd & PCIM_CMD_BUSMASTEREN)) {
+ pci_write_config(dev, PCIR_COMMAND, cmd | PCIM_CMD_BUSMASTEREN, 2);
+ cmd = pci_read_config(dev, PCIR_COMMAND, 2);
+ }
+
+ /* if busmastering mode "stuck" use it */
+ if ((cmd & PCIM_CMD_BUSMASTEREN) == PCIM_CMD_BUSMASTEREN) {
+ ctlr->r_type1 = SYS_RES_IOPORT;
+ ctlr->r_rid1 = ATA_BMADDR_RID;
+ ctlr->r_res1 = bus_alloc_resource_any(dev, ctlr->r_type1, &ctlr->r_rid1,
+ RF_ACTIVE);
+ }
+
+ if (ctlr->chipinit(dev))
+ return ENXIO;
+
+ /* attach all channels on this controller */
+ for (unit = 0; unit < ctlr->channels; unit++) {
+ if (unit == 0 && (pci_get_progif(dev) & 0x81) == 0x80) {
+ device_add_child(dev, "ata", unit);
+ continue;
+ }
+ if (unit == 1 && (pci_get_progif(dev) & 0x84) == 0x80) {
+ device_add_child(dev, "ata", unit);
+ continue;
+ }
+ device_add_child(dev, "ata", devclass_find_free_unit(ata_devclass, 2));
+ }
+ bus_generic_attach(dev);
+ return 0;
+}
+
+int
+ata_pci_detach(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(dev);
+ device_t *children;
+ int nchildren, i;
+
+ /* detach & delete all children */
+ if (!device_get_children(dev, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ device_delete_child(dev, children[i]);
+ free(children, M_TEMP);
+ }
+
+ if (ctlr->r_irq) {
+ bus_teardown_intr(dev, ctlr->r_irq, ctlr->handle);
+ bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ctlr->r_irq);
+ }
+ if (ctlr->r_res2)
+ bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2);
+ if (ctlr->r_res1)
+ bus_release_resource(dev, ctlr->r_type1, ctlr->r_rid1, ctlr->r_res1);
+
+ return 0;
+}
+
+struct resource *
+ata_pci_alloc_resource(device_t dev, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct ata_pci_controller *controller = device_get_softc(dev);
+ int unit = ((struct ata_channel *)device_get_softc(child))->unit;
+ struct resource *res = NULL;
+ int myrid;
+
+ if (type == SYS_RES_IOPORT) {
+ switch (*rid) {
+ case ATA_IOADDR_RID:
+ if (ata_legacy(dev)) {
+ start = (unit ? ATA_SECONDARY : ATA_PRIMARY);
+ count = ATA_IOSIZE;
+ end = start + count - 1;
+ }
+ myrid = PCIR_BAR(0) + (unit << 3);
+ res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev,
+ SYS_RES_IOPORT, &myrid,
+ start, end, count, flags);
+ break;
+
+ case ATA_CTLADDR_RID:
+ if (ata_legacy(dev)) {
+ start = (unit ? ATA_SECONDARY : ATA_PRIMARY) + ATA_CTLOFFSET;
+ count = ATA_CTLIOSIZE;
+ end = start + count - 1;
+ }
+ myrid = PCIR_BAR(1) + (unit << 3);
+ res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev,
+ SYS_RES_IOPORT, &myrid,
+ start, end, count, flags);
+ break;
+ }
+ }
+ if (type == SYS_RES_IRQ && *rid == ATA_IRQ_RID) {
+ if (ata_legacy(dev)) {
+#ifdef __alpha__
+ res = alpha_platform_alloc_ide_intr(unit);
+#else
+ int irq = (unit == 0 ? 14 : 15);
+
+ res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
+ SYS_RES_IRQ, rid, irq, irq, 1, flags);
+#endif
+ }
+ else
+ res = controller->r_irq;
+ }
+ return res;
+}
+
+int
+ata_pci_release_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *r)
+{
+ int unit = ((struct ata_channel *)device_get_softc(child))->unit;
+
+ if (type == SYS_RES_IOPORT) {
+ switch (rid) {
+ case ATA_IOADDR_RID:
+ return BUS_RELEASE_RESOURCE(device_get_parent(dev), dev,
+ SYS_RES_IOPORT,
+ PCIR_BAR(0) + (unit << 3), r);
+ break;
+
+ case ATA_CTLADDR_RID:
+ return BUS_RELEASE_RESOURCE(device_get_parent(dev), dev,
+ SYS_RES_IOPORT,
+ PCIR_BAR(1) + (unit << 3), r);
+ break;
+ default:
+ return ENOENT;
+ }
+ }
+ if (type == SYS_RES_IRQ) {
+ if (rid != ATA_IRQ_RID)
+ return ENOENT;
+
+ if (ata_legacy(dev)) {
+#ifdef __alpha__
+ return alpha_platform_release_ide_intr(unit, r);
+#else
+ return BUS_RELEASE_RESOURCE(device_get_parent(dev), child,
+ SYS_RES_IRQ, rid, r);
+#endif
+ }
+ else
+ return 0;
+ }
+ return EINVAL;
+}
+
+int
+ata_pci_setup_intr(device_t dev, device_t child, struct resource *irq,
+ int flags, driver_intr_t *function, void *argument,
+ void **cookiep)
+{
+ if (ata_legacy(dev)) {
+#ifdef __alpha__
+ return alpha_platform_setup_ide_intr(child, irq, function, argument,
+ cookiep);
+#else
+ return BUS_SETUP_INTR(device_get_parent(dev), child, irq,
+ flags, function, argument, cookiep);
+#endif
+ }
+ else {
+ struct ata_pci_controller *controller = device_get_softc(dev);
+ int unit = ((struct ata_channel *)device_get_softc(child))->unit;
+
+ controller->interrupt[unit].function = function;
+ controller->interrupt[unit].argument = argument;
+ *cookiep = controller;
+ return 0;
+ }
+}
+
+int
+ata_pci_teardown_intr(device_t dev, device_t child, struct resource *irq,
+ void *cookie)
+{
+ if (ata_legacy(dev)) {
+#ifdef __alpha__
+ return alpha_platform_teardown_ide_intr(child, irq, cookie);
+#else
+ return BUS_TEARDOWN_INTR(device_get_parent(dev), child, irq, cookie);
+#endif
+ }
+ else {
+ struct ata_pci_controller *controller = device_get_softc(dev);
+ int unit = ((struct ata_channel *)device_get_softc(child))->unit;
+
+ controller->interrupt[unit].function = NULL;
+ controller->interrupt[unit].argument = NULL;
+ return 0;
+ }
+}
+
+int
+ata_pci_allocate(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ struct resource *io = NULL, *ctlio = NULL;
+ int i, rid;
+
+ rid = ATA_IOADDR_RID;
+ if (!(io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE)))
+ return ENXIO;
+
+ rid = ATA_CTLADDR_RID;
+ if (!(ctlio = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,RF_ACTIVE))){
+ bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, io);
+ return ENXIO;
+ }
+
+ for (i = ATA_DATA; i <= ATA_COMMAND; i ++) {
+ ch->r_io[i].res = io;
+ ch->r_io[i].offset = i;
+ }
+ ch->r_io[ATA_CONTROL].res = ctlio;
+ ch->r_io[ATA_CONTROL].offset = ata_legacy(device_get_parent(dev)) ? 0 : 2;
+ ch->r_io[ATA_IDX_ADDR].res = io;
+ ata_default_registers(dev);
+ if (ctlr->r_res1) {
+ for (i = ATA_BMCMD_PORT; i <= ATA_BMDTP_PORT; i++) {
+ ch->r_io[i].res = ctlr->r_res1;
+ ch->r_io[i].offset = (i - ATA_BMCMD_PORT) + (ch->unit*ATA_BMIOSIZE);
+ }
+ }
+
+ ata_pci_hw(dev);
+ return 0;
+}
+
+void
+ata_pci_hw(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ata_generic_hw(dev);
+ ch->hw.status = ata_pci_status;
+}
+
+int
+ata_pci_status(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if ((dumping || !ata_legacy(device_get_parent(dev))) &&
+ ch->dma && ((ch->flags & ATA_ALWAYS_DMASTAT) ||
+ (ch->dma->flags & ATA_DMA_ACTIVE))) {
+ int bmstat = ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK;
+
+ if ((bmstat & (ATA_BMSTAT_ACTIVE | ATA_BMSTAT_INTERRUPT)) !=
+ ATA_BMSTAT_INTERRUPT)
+ return 0;
+ ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, bmstat & ~ATA_BMSTAT_ERROR);
+ DELAY(1);
+ }
+ if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
+ DELAY(100);
+ if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
+ return 0;
+ }
+ return 1;
+}
+
+static int
+ata_pci_dmastart(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+
+ ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, (ATA_IDX_INB(ch, ATA_BMSTAT_PORT) |
+ (ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR)));
+ ATA_IDX_OUTL(ch, ATA_BMDTP_PORT, ch->dma->sg_bus);
+ ch->dma->flags |= ATA_DMA_ACTIVE;
+ ATA_IDX_OUTB(ch, ATA_BMCMD_PORT,
+ (ATA_IDX_INB(ch, ATA_BMCMD_PORT) & ~ATA_BMCMD_WRITE_READ) |
+ ((ch->dma->flags & ATA_DMA_READ) ? ATA_BMCMD_WRITE_READ : 0) |
+ ATA_BMCMD_START_STOP);
+ return 0;
+}
+
+static int
+ata_pci_dmastop(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ int error;
+
+ ATA_IDX_OUTB(ch, ATA_BMCMD_PORT,
+ ATA_IDX_INB(ch, ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP);
+ ch->dma->flags &= ~ATA_DMA_ACTIVE;
+ error = ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK;
+ ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR);
+ return error;
+}
+
+static void
+ata_pci_dmareset(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ATA_IDX_OUTB(ch, ATA_BMCMD_PORT,
+ ATA_IDX_INB(ch, ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP);
+ ch->dma->flags &= ~ATA_DMA_ACTIVE;
+ ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR);
+ ch->dma->unload(dev);
+}
+
+void
+ata_pci_dmainit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ ata_dmainit(dev);
+ if (ch->dma) {
+ ch->dma->start = ata_pci_dmastart;
+ ch->dma->stop = ata_pci_dmastop;
+ ch->dma->reset = ata_pci_dmareset;
+ }
+}
+
+static device_method_t ata_pci_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_pci_probe),
+ DEVMETHOD(device_attach, ata_pci_attach),
+ DEVMETHOD(device_detach, ata_pci_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* bus methods */
+ DEVMETHOD(bus_alloc_resource, ata_pci_alloc_resource),
+ DEVMETHOD(bus_release_resource, ata_pci_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_setup_intr, ata_pci_setup_intr),
+ DEVMETHOD(bus_teardown_intr, ata_pci_teardown_intr),
+
+ { 0, 0 }
+};
+
+devclass_t atapci_devclass;
+
+static driver_t ata_pci_driver = {
+ "atapci",
+ ata_pci_methods,
+ sizeof(struct ata_pci_controller),
+};
+
+DRIVER_MODULE(atapci, pci, ata_pci_driver, atapci_devclass, 0, 0);
+MODULE_VERSION(atapci, 1);
+MODULE_DEPEND(atapci, ata, 1, 1, 1);
+
+static int
+ata_pcichannel_probe(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ device_t *children;
+ int count, i;
+ char buffer[32];
+
+ /* take care of green memory */
+ bzero(ch, sizeof(struct ata_channel));
+
+ /* find channel number on this controller */
+ device_get_children(device_get_parent(dev), &children, &count);
+ for (i = 0; i < count; i++) {
+ if (children[i] == dev)
+ ch->unit = i;
+ }
+ free(children, M_TEMP);
+
+ sprintf(buffer, "ATA channel %d", ch->unit);
+ device_set_desc_copy(dev, buffer);
+
+ return ata_probe(dev);
+}
+
+static int
+ata_pcichannel_attach(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int error;
+
+ if (ctlr->dmainit)
+ ctlr->dmainit(dev);
+ if (ch->dma)
+ ch->dma->alloc(dev);
+
+ if ((error = ctlr->allocate(dev)))
+ return error;
+
+ return ata_attach(dev);
+}
+
+static int
+ata_pcichannel_detach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ int error;
+
+ if ((error = ata_detach(dev)))
+ return error;
+
+ if (ch->dma)
+ ch->dma->free(dev);
+
+ /* XXX SOS free resources for io and ctlio ?? */
+
+ return 0;
+}
+
+static int
+ata_pcichannel_locking(device_t dev, int mode)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ctlr->locking)
+ return ctlr->locking(dev, mode);
+ else
+ return ch->unit;
+}
+
+static void
+ata_pcichannel_reset(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* if DMA engine present reset it */
+ if (ch->dma) {
+ if (ch->dma->reset)
+ ch->dma->reset(dev);
+ ch->dma->unload(dev);
+ }
+
+ /* reset the controller HW */
+ if (ctlr->reset)
+ ctlr->reset(dev);
+ else
+ ata_generic_reset(dev);
+}
+
+static void
+ata_pcichannel_setmode(device_t parent, device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(GRANDPARENT(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ int mode = atadev->mode;
+
+ ctlr->setmode(dev, ATA_PIO_MAX);
+ if (mode >= ATA_DMA)
+ ctlr->setmode(dev, mode);
+}
+
+static device_method_t ata_pcichannel_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_pcichannel_probe),
+ DEVMETHOD(device_attach, ata_pcichannel_attach),
+ DEVMETHOD(device_detach, ata_pcichannel_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, ata_suspend),
+ DEVMETHOD(device_resume, ata_resume),
+
+ /* ATA methods */
+ DEVMETHOD(ata_setmode, ata_pcichannel_setmode),
+ DEVMETHOD(ata_locking, ata_pcichannel_locking),
+ DEVMETHOD(ata_reset, ata_pcichannel_reset),
+
+ { 0, 0 }
+};
+
+driver_t ata_pcichannel_driver = {
+ "ata",
+ ata_pcichannel_methods,
+ sizeof(struct ata_channel),
+};
+
+DRIVER_MODULE(ata, atapci, ata_pcichannel_driver, ata_devclass, 0, 0);
diff --git a/sys/dev/ata/ata-pci.h b/sys/dev/ata/ata-pci.h
new file mode 100644
index 000000000000..4d1192a8215a
--- /dev/null
+++ b/sys/dev/ata/ata-pci.h
@@ -0,0 +1,457 @@
+/*-
+ * Copyright (c) 2003 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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$
+ */
+
+/* structure holding chipset config info */
+struct ata_chip_id {
+ u_int32_t chipid;
+ u_int8_t chiprev;
+ int cfg1;
+ int cfg2;
+ u_int8_t max_dma;
+ char *text;
+};
+
+/* structure describing a PCI ATA controller */
+struct ata_pci_controller {
+ device_t dev;
+ int r_type1;
+ int r_rid1;
+ struct resource *r_res1;
+ int r_type2;
+ int r_rid2;
+ struct resource *r_res2;
+ struct resource *r_irq;
+ void *handle;
+ struct ata_chip_id *chip;
+ int channels;
+ int (*chipinit)(device_t);
+ int (*allocate)(device_t);
+ int (*locking)(device_t, int);
+ void (*reset)(device_t);
+ void (*dmainit)(device_t);
+ void (*setmode)(device_t, int);
+ struct {
+ void (*function)(void *);
+ void *argument;
+ } interrupt[8]; /* XXX SOS max ch# for now */
+};
+
+/* structure for SATA connection update hotplug/hotswap support */
+struct ata_connect_task {
+ struct task task;
+ device_t dev;
+ int action;
+#define ATA_C_ATTACH 1
+#define ATA_C_DETACH 2
+};
+
+/* defines for known chipset PCI id's */
+#define ATA_ACARD_ID 0x1191
+#define ATA_ATP850 0x00021191
+#define ATA_ATP850A 0x00041191
+#define ATA_ATP850R 0x00051191
+#define ATA_ATP860A 0x00061191
+#define ATA_ATP860R 0x00071191
+#define ATA_ATP865A 0x00081191
+#define ATA_ATP865R 0x00091191
+
+#define ATA_AMD_ID 0x1022
+#define ATA_AMD755 0x74011022
+#define ATA_AMD756 0x74091022
+#define ATA_AMD766 0x74111022
+#define ATA_AMD768 0x74411022
+#define ATA_AMD8111 0x74691022
+
+#define ATA_ACER_LABS_ID 0x10b9
+#define ATA_ALI_1533 0x153310b9
+#define ATA_ALI_5229 0x522910b9
+#define ATA_ALI_5281 0x528110b9
+#define ATA_ALI_5287 0x528710b9
+#define ATA_ALI_5288 0x528810b9
+#define ATA_ALI_5289 0x528910b9
+
+#define ATA_ATI_ID 0x1002
+#define ATA_ATI_IXP200 0x43491002
+#define ATA_ATI_IXP300 0x43691002
+#define ATA_ATI_IXP400 0x43761002
+#define ATA_ATI_IXP300_S1 0x436e1002
+#define ATA_ATI_IXP400_S1 0x43791002
+#define ATA_ATI_IXP400_S2 0x437a1002
+
+#define ATA_CENATEK_ID 0x16ca
+#define ATA_CENATEK_ROCKET 0x000116ca
+
+#define ATA_CYRIX_ID 0x1078
+#define ATA_CYRIX_5530 0x01021078
+
+#define ATA_CYPRESS_ID 0x1080
+#define ATA_CYPRESS_82C693 0xc6931080
+
+#define ATA_DEC_21150 0x00221011
+#define ATA_DEC_21150_1 0x00231011
+
+#define ATA_HIGHPOINT_ID 0x1103
+#define ATA_HPT366 0x00041103
+#define ATA_HPT372 0x00051103
+#define ATA_HPT302 0x00061103
+#define ATA_HPT371 0x00071103
+#define ATA_HPT374 0x00081103
+
+#define ATA_INTEL_ID 0x8086
+#define ATA_I960RM 0x09628086
+#define ATA_I82371FB 0x12308086
+#define ATA_I82371SB 0x70108086
+#define ATA_I82371AB 0x71118086
+#define ATA_I82443MX 0x71998086
+#define ATA_I82451NX 0x84ca8086
+#define ATA_I82372FB 0x76018086
+#define ATA_I82801AB 0x24218086
+#define ATA_I82801AA 0x24118086
+#define ATA_I82801BA 0x244a8086
+#define ATA_I82801BA_1 0x244b8086
+#define ATA_I82801CA 0x248a8086
+#define ATA_I82801CA_1 0x248b8086
+#define ATA_I82801DB 0x24cb8086
+#define ATA_I82801DB_1 0x24ca8086
+#define ATA_I82801EB 0x24db8086
+#define ATA_I82801EB_S1 0x24d18086
+#define ATA_I82801EB_R1 0x24df8086
+#define ATA_I6300ESB 0x25a28086
+#define ATA_I6300ESB_S1 0x25a38086
+#define ATA_I6300ESB_R1 0x25b08086
+#define ATA_I63XXESB2 0x269e8086
+#define ATA_I63XXESB2_S1 0x26808086
+#define ATA_I63XXESB2_S2 0x26818086
+#define ATA_I63XXESB2_R1 0x26828086
+#define ATA_I63XXESB2_R2 0x26838086
+#define ATA_I82801FB 0x266f8086
+#define ATA_I82801FB_S1 0x26518086
+#define ATA_I82801FB_R1 0x26528086
+#define ATA_I82801FBM 0x26538086
+#define ATA_I82801GB 0x27df8086
+#define ATA_I82801GB_S1 0x27c08086
+#define ATA_I82801GB_AH 0x27c18086
+#define ATA_I82801GB_R1 0x27c38086
+#define ATA_I82801GBM_S1 0x27c48086
+#define ATA_I82801GBM_AH 0x27c58086
+#define ATA_I82801GBM_R1 0x27c68086
+#define ATA_I82801HB_S1 0x28208086
+#define ATA_I82801HB_AH6 0x28218086
+#define ATA_I82801HB_R1 0x28228086
+#define ATA_I82801HB_AH4 0x28248086
+#define ATA_I82801HB_S2 0x28258086
+#define ATA_I82801HBM_S1 0x28298086
+#define ATA_I82801HBM_S2 0x282a8086
+#define ATA_I31244 0x32008086
+
+#define ATA_ITE_ID 0x1283
+#define ATA_IT8211F 0x82111283
+#define ATA_IT8212F 0x82121283
+
+#define ATA_JMICRON_ID 0x197b
+#define ATA_JMB360 0x2360197b
+#define ATA_JMB361 0x2361197b
+#define ATA_JMB363 0x2363197b
+#define ATA_JMB365 0x2365197b
+#define ATA_JMB366 0x2366197b
+
+#define ATA_MARVELL_ID 0x11ab
+#define ATA_M88SX5040 0x504011ab
+#define ATA_M88SX5041 0x504111ab
+#define ATA_M88SX5080 0x508011ab
+#define ATA_M88SX5081 0x508111ab
+#define ATA_M88SX6041 0x604111ab
+#define ATA_M88SX6081 0x608111ab
+
+#define ATA_MICRON_ID 0x1042
+#define ATA_MICRON_RZ1000 0x10001042
+#define ATA_MICRON_RZ1001 0x10011042
+
+#define ATA_NATIONAL_ID 0x100b
+#define ATA_SC1100 0x0502100b
+
+#define ATA_NVIDIA_ID 0x10de
+#define ATA_NFORCE1 0x01bc10de
+#define ATA_NFORCE2 0x006510de
+#define ATA_NFORCE2_PRO 0x008510de
+#define ATA_NFORCE2_PRO_S1 0x008e10de
+#define ATA_NFORCE3 0x00d510de
+#define ATA_NFORCE3_PRO 0x00e510de
+#define ATA_NFORCE3_PRO_S1 0x00e310de
+#define ATA_NFORCE3_PRO_S2 0x00ee10de
+#define ATA_NFORCE_MCP04 0x003510de
+#define ATA_NFORCE_MCP04_S1 0x003610de
+#define ATA_NFORCE_MCP04_S2 0x003e10de
+#define ATA_NFORCE_CK804 0x005310de
+#define ATA_NFORCE_CK804_S1 0x005410de
+#define ATA_NFORCE_CK804_S2 0x005510de
+#define ATA_NFORCE_MCP51 0x026510de
+#define ATA_NFORCE_MCP51_S1 0x026610de
+#define ATA_NFORCE_MCP51_S2 0x026710de
+#define ATA_NFORCE_MCP55 0x036e10de
+#define ATA_NFORCE_MCP55_S1 0x037e10de
+#define ATA_NFORCE_MCP55_S2 0x037f10de
+
+#define ATA_PROMISE_ID 0x105a
+#define ATA_PDC20246 0x4d33105a
+#define ATA_PDC20262 0x4d38105a
+#define ATA_PDC20263 0x0d38105a
+#define ATA_PDC20265 0x0d30105a
+#define ATA_PDC20267 0x4d30105a
+#define ATA_PDC20268 0x4d68105a
+#define ATA_PDC20269 0x4d69105a
+#define ATA_PDC20270 0x6268105a
+#define ATA_PDC20271 0x6269105a
+#define ATA_PDC20275 0x1275105a
+#define ATA_PDC20276 0x5275105a
+#define ATA_PDC20277 0x7275105a
+#define ATA_PDC20318 0x3318105a
+#define ATA_PDC20319 0x3319105a
+#define ATA_PDC20371 0x3371105a
+#define ATA_PDC20375 0x3375105a
+#define ATA_PDC20376 0x3376105a
+#define ATA_PDC20377 0x3377105a
+#define ATA_PDC20378 0x3373105a
+#define ATA_PDC20379 0x3372105a
+#define ATA_PDC20571 0x3571105a
+#define ATA_PDC20575 0x3d75105a
+#define ATA_PDC20579 0x3574105a
+#define ATA_PDC20771 0x3570105a
+#define ATA_PDC40518 0x3d18105a
+#define ATA_PDC40519 0x3519105a
+#define ATA_PDC40718 0x3d17105a
+#define ATA_PDC40719 0x3515105a
+#define ATA_PDC40775 0x3d73105a
+#define ATA_PDC40779 0x3577105a
+#define ATA_PDC20617 0x6617105a
+#define ATA_PDC20618 0x6626105a
+#define ATA_PDC20619 0x6629105a
+#define ATA_PDC20620 0x6620105a
+#define ATA_PDC20621 0x6621105a
+#define ATA_PDC20622 0x6622105a
+#define ATA_PDC20624 0x6624105a
+#define ATA_PDC81518 0x8002105a
+
+#define ATA_SERVERWORKS_ID 0x1166
+#define ATA_ROSB4_ISA 0x02001166
+#define ATA_ROSB4 0x02111166
+#define ATA_CSB5 0x02121166
+#define ATA_CSB6 0x02131166
+#define ATA_CSB6_1 0x02171166
+#define ATA_HT1000 0x02141166
+#define ATA_HT1000_S1 0x024b1166
+#define ATA_HT1000_S2 0x024a1166
+#define ATA_K2 0x02401166
+#define ATA_FRODO4 0x02411166
+#define ATA_FRODO8 0x02421166
+
+#define ATA_SILICON_IMAGE_ID 0x1095
+#define ATA_SII3114 0x31141095
+#define ATA_SII3512 0x35121095
+#define ATA_SII3112 0x31121095
+#define ATA_SII3112_1 0x02401095
+#define ATA_SII0680 0x06801095
+#define ATA_CMD646 0x06461095
+#define ATA_CMD648 0x06481095
+#define ATA_CMD649 0x06491095
+
+#define ATA_SIS_ID 0x1039
+#define ATA_SISSOUTH 0x00081039
+#define ATA_SIS5511 0x55111039
+#define ATA_SIS5513 0x55131039
+#define ATA_SIS5517 0x55171039
+#define ATA_SIS5518 0x55181039
+#define ATA_SIS5571 0x55711039
+#define ATA_SIS5591 0x55911039
+#define ATA_SIS5596 0x55961039
+#define ATA_SIS5597 0x55971039
+#define ATA_SIS5598 0x55981039
+#define ATA_SIS5600 0x56001039
+#define ATA_SIS530 0x05301039
+#define ATA_SIS540 0x05401039
+#define ATA_SIS550 0x05501039
+#define ATA_SIS620 0x06201039
+#define ATA_SIS630 0x06301039
+#define ATA_SIS635 0x06351039
+#define ATA_SIS633 0x06331039
+#define ATA_SIS640 0x06401039
+#define ATA_SIS645 0x06451039
+#define ATA_SIS646 0x06461039
+#define ATA_SIS648 0x06481039
+#define ATA_SIS650 0x06501039
+#define ATA_SIS651 0x06511039
+#define ATA_SIS652 0x06521039
+#define ATA_SIS655 0x06551039
+#define ATA_SIS658 0x06581039
+#define ATA_SIS661 0x06611039
+#define ATA_SIS730 0x07301039
+#define ATA_SIS733 0x07331039
+#define ATA_SIS735 0x07351039
+#define ATA_SIS740 0x07401039
+#define ATA_SIS745 0x07451039
+#define ATA_SIS746 0x07461039
+#define ATA_SIS748 0x07481039
+#define ATA_SIS750 0x07501039
+#define ATA_SIS751 0x07511039
+#define ATA_SIS752 0x07521039
+#define ATA_SIS755 0x07551039
+#define ATA_SIS961 0x09611039
+#define ATA_SIS962 0x09621039
+#define ATA_SIS963 0x09631039
+#define ATA_SIS964 0x09641039
+#define ATA_SIS965 0x09651039
+#define ATA_SIS180 0x01801039
+#define ATA_SIS181 0x01811039
+#define ATA_SIS182 0x01821039
+
+#define ATA_VIA_ID 0x1106
+#define ATA_VIA82C571 0x05711106
+#define ATA_VIA82C586 0x05861106
+#define ATA_VIA82C596 0x05961106
+#define ATA_VIA82C686 0x06861106
+#define ATA_VIA8231 0x82311106
+#define ATA_VIA8233 0x30741106
+#define ATA_VIA8233A 0x31471106
+#define ATA_VIA8233C 0x31091106
+#define ATA_VIA8235 0x31771106
+#define ATA_VIA8237 0x32271106
+#define ATA_VIA8237A 0x05911106
+#define ATA_VIA8251 0x33491106
+#define ATA_VIA8361 0x31121106
+#define ATA_VIA8363 0x03051106
+#define ATA_VIA8371 0x03911106
+#define ATA_VIA8662 0x31021106
+#define ATA_VIA6410 0x31641106
+#define ATA_VIA6420 0x31491106
+#define ATA_VIA6421 0x32491106
+
+/* chipset setup related defines */
+#define AHCI 1
+#define ATPOLD 1
+
+#define ALIOLD 0x01
+#define ALINEW 0x02
+#define ALISATA 0x04
+
+#define HPT366 0
+#define HPT370 1
+#define HPT372 2
+#define HPT374 3
+#define HPTOLD 0x01
+
+#define MV5XXX 5
+#define MV6XXX 6
+
+#define PROLD 0
+#define PRNEW 1
+#define PRTX 2
+#define PRMIO 3
+#define PRTX4 0x01
+#define PRSX4X 0x02
+#define PRSX6K 0x04
+#define PRPATA 0x08
+#define PRCMBO 0x10
+#define PRCMBO2 0x20
+#define PRSATA 0x40
+#define PRSATA2 0x80
+
+#define SWKS33 0
+#define SWKS66 1
+#define SWKS100 2
+#define SWKSMIO 3
+
+#define SIIMEMIO 1
+#define SIIINTR 0x01
+#define SIISETCLK 0x02
+#define SIIBUG 0x04
+#define SII4CH 0x08
+
+#define SIS_SOUTH 1
+#define SISSATA 2
+#define SIS133NEW 3
+#define SIS133OLD 4
+#define SIS100NEW 5
+#define SIS100OLD 6
+#define SIS66 7
+#define SIS33 8
+
+#define VIA33 0
+#define VIA66 1
+#define VIA100 2
+#define VIA133 3
+#define AMDNVIDIA 4
+
+#define AMDCABLE 0x0001
+#define AMDBUG 0x0002
+#define NVIDIA 0x0004
+#define NV4 0x0010
+#define NVQ 0x0020
+#define VIACLK 0x0100
+#define VIABUG 0x0200
+#define VIABAR 0x0400
+#define VIAAHCI 0x0800
+
+
+/* global prototypes ata-pci.c */
+int ata_pci_probe(device_t dev);
+int ata_pci_attach(device_t dev);
+int ata_pci_detach(device_t dev);
+struct resource * ata_pci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags);
+int ata_pci_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r);
+int ata_pci_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *function, void *argument, void **cookiep);
+ int ata_pci_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie);
+int ata_pci_allocate(device_t dev);
+void ata_pci_hw(device_t dev);
+int ata_pci_status(device_t dev);
+void ata_pci_dmainit(device_t);
+
+
+/* global prototypes ata-chipset.c */
+int ata_generic_ident(device_t);
+int ata_acard_ident(device_t);
+int ata_ali_ident(device_t);
+int ata_amd_ident(device_t);
+int ata_ati_ident(device_t);
+int ata_cyrix_ident(device_t);
+int ata_cypress_ident(device_t);
+int ata_highpoint_ident(device_t);
+int ata_intel_ident(device_t);
+int ata_ite_ident(device_t);
+int ata_jmicron_ident(device_t);
+int ata_marvell_ident(device_t);
+int ata_national_ident(device_t);
+int ata_nvidia_ident(device_t);
+int ata_promise_ident(device_t);
+int ata_serverworks_ident(device_t);
+int ata_sii_ident(device_t);
+int ata_sis_ident(device_t);
+int ata_via_ident(device_t);
+int ata_legacy(device_t);
+
+/* global prototypes ata-dma.c */
+void ata_dmainit(device_t);
diff --git a/sys/dev/ata/ata-queue.c b/sys/dev/ata/ata-queue.c
new file mode 100644
index 000000000000..75ef9284e5ef
--- /dev/null
+++ b/sys/dev/ata/ata-queue.c
@@ -0,0 +1,757 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <dev/ata/ata-all.h>
+#include <ata_if.h>
+
+/* prototypes */
+static void ata_completed(void *, int);
+static void ata_sort_queue(struct ata_channel *ch, struct ata_request *request);
+static char *ata_skey2str(u_int8_t);
+
+void
+ata_queue_request(struct ata_request *request)
+{
+ struct ata_channel *ch;
+
+ /* treat request as virgin (this might be an ATA_R_REQUEUE) */
+ request->result = request->status = request->error = 0;
+
+ /* check that that the device is still valid */
+ if (!(request->parent = device_get_parent(request->dev))) {
+ request->result = ENXIO;
+ if (request->callback)
+ (request->callback)(request);
+ return;
+ }
+ ch = device_get_softc(request->parent);
+ callout_init_mtx(&request->callout, &ch->state_mtx, CALLOUT_RETURNUNLOCKED);
+ if (!request->callback && !(request->flags & ATA_R_REQUEUE))
+ sema_init(&request->done, 0, "ATA request done");
+
+ /* in ATA_STALL_QUEUE state we call HW directly */
+ if ((ch->state & ATA_STALL_QUEUE) && (request->flags & ATA_R_CONTROL)) {
+ mtx_lock(&ch->state_mtx);
+ ch->running = request;
+ if (ch->hw.begin_transaction(request) == ATA_OP_FINISHED) {
+ ch->running = NULL;
+ if (!request->callback)
+ sema_destroy(&request->done);
+ mtx_unlock(&ch->state_mtx);
+ return;
+ }
+ mtx_unlock(&ch->state_mtx);
+ }
+ /* otherwise put request on the locked queue at the specified location */
+ else {
+ mtx_lock(&ch->queue_mtx);
+ if (request->flags & ATA_R_AT_HEAD)
+ TAILQ_INSERT_HEAD(&ch->ata_queue, request, chain);
+ else if (request->flags & ATA_R_ORDERED)
+ ata_sort_queue(ch, request);
+ else
+ TAILQ_INSERT_TAIL(&ch->ata_queue, request, chain);
+ mtx_unlock(&ch->queue_mtx);
+ ATA_DEBUG_RQ(request, "queued");
+ ata_start(ch->dev);
+ }
+
+ /* if this is a requeued request callback/sleep we're done */
+ if (request->flags & ATA_R_REQUEUE)
+ return;
+
+ /* if this is not a callback wait until request is completed */
+ if (!request->callback) {
+ ATA_DEBUG_RQ(request, "wait for completition");
+ if (!dumping &&
+ sema_timedwait(&request->done, request->timeout * hz * 4)) {
+ device_printf(request->dev,
+ "WARNING - %s taskqueue timeout "
+ "- completing request directly\n",
+ ata_cmd2str(request));
+ request->flags |= ATA_R_DANGER1;
+ ata_completed(request, 0);
+ }
+ sema_destroy(&request->done);
+ }
+}
+
+int
+ata_controlcmd(device_t dev, u_int8_t command, u_int16_t feature,
+ u_int64_t lba, u_int16_t count)
+{
+ struct ata_request *request = ata_alloc_request();
+ int error = ENOMEM;
+
+ if (request) {
+ request->dev = dev;
+ request->u.ata.command = command;
+ request->u.ata.lba = lba;
+ request->u.ata.count = count;
+ request->u.ata.feature = feature;
+ request->flags = ATA_R_CONTROL;
+ request->timeout = 1;
+ request->retries = 0;
+ ata_queue_request(request);
+ error = request->result;
+ ata_free_request(request);
+ }
+ return error;
+}
+
+int
+ata_atapicmd(device_t dev, u_int8_t *ccb, caddr_t data,
+ int count, int flags, int timeout)
+{
+ struct ata_request *request = ata_alloc_request();
+ struct ata_device *atadev = device_get_softc(dev);
+ int error = ENOMEM;
+
+ if (request) {
+ request->dev = dev;
+ if ((atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12)
+ bcopy(ccb, request->u.atapi.ccb, 12);
+ else
+ bcopy(ccb, request->u.atapi.ccb, 16);
+ request->data = data;
+ request->bytecount = count;
+ request->transfersize = min(request->bytecount, 65534);
+ request->flags = flags | ATA_R_ATAPI;
+ request->timeout = timeout;
+ request->retries = 0;
+ ata_queue_request(request);
+ error = request->result;
+ ata_free_request(request);
+ }
+ return error;
+}
+
+void
+ata_start(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ struct ata_request *request;
+ struct ata_composite *cptr;
+ int dependencies = 0;
+
+ /* if we have a request on the queue try to get it running */
+ mtx_lock(&ch->queue_mtx);
+ if ((request = TAILQ_FIRST(&ch->ata_queue))) {
+
+ /* we need the locking function to get the lock for this channel */
+ if (ATA_LOCKING(dev, ATA_LF_LOCK) == ch->unit) {
+
+ /* check for composite dependencies */
+ if ((cptr = request->composite)) {
+ mtx_lock(&cptr->lock);
+ if ((request->flags & ATA_R_WRITE) &&
+ (cptr->wr_depend & cptr->rd_done) != cptr->wr_depend) {
+ dependencies = 1;
+ }
+ mtx_unlock(&cptr->lock);
+ }
+
+ /* check we are in the right state and has no dependencies */
+ mtx_lock(&ch->state_mtx);
+ if (ch->state == ATA_IDLE && !dependencies) {
+ ATA_DEBUG_RQ(request, "starting");
+ TAILQ_REMOVE(&ch->ata_queue, request, chain);
+ ch->running = request;
+ ch->state = ATA_ACTIVE;
+
+ /* if we are the freezing point release it */
+ if (ch->freezepoint == request)
+ ch->freezepoint = NULL;
+
+ if (ch->hw.begin_transaction(request) == ATA_OP_FINISHED) {
+ ch->running = NULL;
+ ch->state = ATA_IDLE;
+ mtx_unlock(&ch->state_mtx);
+ mtx_unlock(&ch->queue_mtx);
+ ATA_LOCKING(dev, ATA_LF_UNLOCK);
+ ata_finish(request);
+ return;
+ }
+ if (dumping) {
+ mtx_unlock(&ch->state_mtx);
+ mtx_unlock(&ch->queue_mtx);
+ while (!ata_interrupt(ch))
+ DELAY(10);
+ return;
+ }
+ }
+ mtx_unlock(&ch->state_mtx);
+ }
+ }
+ mtx_unlock(&ch->queue_mtx);
+}
+
+void
+ata_finish(struct ata_request *request)
+{
+ struct ata_channel *ch = device_get_softc(request->parent);
+
+ /*
+ * if in ATA_STALL_QUEUE state or request has ATA_R_DIRECT flags set
+ * we need to call ata_complete() directly here (no taskqueue involvement)
+ */
+ if (dumping ||
+ (ch->state & ATA_STALL_QUEUE) || (request->flags & ATA_R_DIRECT)) {
+ ATA_DEBUG_RQ(request, "finish directly");
+ ata_completed(request, 0);
+ }
+ else {
+ /* put request on the proper taskqueue for completition */
+ if (request->bio && !(request->flags & (ATA_R_THREAD | ATA_R_TIMEOUT))){
+ ATA_DEBUG_RQ(request, "finish bio_taskqueue");
+ bio_taskqueue(request->bio, (bio_task_t *)ata_completed, request);
+ }
+ else {
+ TASK_INIT(&request->task, 0, ata_completed, request);
+ ATA_DEBUG_RQ(request, "finish taskqueue_swi");
+ taskqueue_enqueue(taskqueue_swi, &request->task);
+ }
+ }
+}
+
+static void
+ata_completed(void *context, int dummy)
+{
+ struct ata_request *request = (struct ata_request *)context;
+ struct ata_channel *ch = device_get_softc(request->parent);
+ struct ata_device *atadev = device_get_softc(request->dev);
+ struct ata_composite *composite;
+
+ if (request->flags & ATA_R_DANGER2) {
+ device_printf(request->dev,
+ "WARNING - %s freeing taskqueue zombie request\n",
+ ata_cmd2str(request));
+ request->flags &= ~(ATA_R_DANGER1 | ATA_R_DANGER2);
+ ata_free_request(request);
+ return;
+ }
+ if (request->flags & ATA_R_DANGER1)
+ request->flags |= ATA_R_DANGER2;
+
+ ATA_DEBUG_RQ(request, "completed entered");
+
+ /* if we had a timeout, reinit channel and deal with the falldown */
+ if (request->flags & ATA_R_TIMEOUT) {
+ /*
+ * if the channel is still present and
+ * reinit succeeds and
+ * the device doesn't get detached and
+ * there are retries left we reinject this request
+ */
+ if (ch && !ata_reinit(ch->dev) && !request->result &&
+ (request->retries-- > 0)) {
+ if (!(request->flags & ATA_R_QUIET)) {
+ device_printf(request->dev,
+ "TIMEOUT - %s retrying (%d retr%s left)",
+ ata_cmd2str(request), request->retries,
+ request->retries == 1 ? "y" : "ies");
+ if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
+ printf(" LBA=%ju", request->u.ata.lba);
+ printf("\n");
+ }
+ request->flags &= ~(ATA_R_TIMEOUT | ATA_R_DEBUG);
+ request->flags |= (ATA_R_AT_HEAD | ATA_R_REQUEUE);
+ ATA_DEBUG_RQ(request, "completed reinject");
+ ata_queue_request(request);
+ return;
+ }
+
+ /* ran out of good intentions so finish with error */
+ if (!request->result) {
+ if (!(request->flags & ATA_R_QUIET)) {
+ if (request->dev) {
+ device_printf(request->dev, "FAILURE - %s timed out",
+ ata_cmd2str(request));
+ if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
+ printf(" LBA=%ju", request->u.ata.lba);
+ printf("\n");
+ }
+ }
+ request->result = EIO;
+ }
+ }
+ else if (!(request->flags & ATA_R_ATAPI) ){
+ /* if this is a soft ECC error warn about it */
+ /* XXX SOS we could do WARF here */
+ if ((request->status & (ATA_S_CORR | ATA_S_ERROR)) == ATA_S_CORR) {
+ device_printf(request->dev,
+ "WARNING - %s soft error (ECC corrected)",
+ ata_cmd2str(request));
+ if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
+ printf(" LBA=%ju", request->u.ata.lba);
+ printf("\n");
+ }
+
+ /* if this is a UDMA CRC error we reinject if there are retries left */
+ if (request->flags & ATA_R_DMA && request->error & ATA_E_ICRC) {
+ if (request->retries-- > 0) {
+ device_printf(request->dev,
+ "WARNING - %s UDMA ICRC error (retrying request)",
+ ata_cmd2str(request));
+ if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
+ printf(" LBA=%ju", request->u.ata.lba);
+ printf("\n");
+ request->flags |= (ATA_R_AT_HEAD | ATA_R_REQUEUE);
+ ata_queue_request(request);
+ return;
+ }
+ }
+ }
+
+ switch (request->flags & ATA_R_ATAPI) {
+
+ /* ATA errors */
+ default:
+ if (!request->result && request->status & ATA_S_ERROR) {
+ if (!(request->flags & ATA_R_QUIET)) {
+ device_printf(request->dev,
+ "FAILURE - %s status=%b error=%b",
+ ata_cmd2str(request),
+ request->status, "\20\10BUSY\7READY\6DMA_READY"
+ "\5DSC\4DRQ\3CORRECTABLE\2INDEX\1ERROR",
+ request->error, "\20\10ICRC\7UNCORRECTABLE"
+ "\6MEDIA_CHANGED\5NID_NOT_FOUND"
+ "\4MEDIA_CHANGE_REQEST"
+ "\3ABORTED\2NO_MEDIA\1ILLEGAL_LENGTH");
+ if ((request->flags & ATA_R_DMA) &&
+ (request->dmastat & ATA_BMSTAT_ERROR))
+ printf(" dma=0x%02x", request->dmastat);
+ if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
+ printf(" LBA=%ju", request->u.ata.lba);
+ printf("\n");
+ }
+ request->result = EIO;
+ }
+ break;
+
+ /* ATAPI errors */
+ case ATA_R_ATAPI:
+ /* skip if result already set */
+ if (request->result)
+ break;
+
+ /* if we have a sensekey -> request sense from device */
+ if ((request->error & ATA_E_ATAPI_SENSE_MASK) &&
+ (request->u.atapi.ccb[0] != ATAPI_REQUEST_SENSE)) {
+ static u_int8_t ccb[16] = { ATAPI_REQUEST_SENSE, 0, 0, 0,
+ sizeof(struct atapi_sense),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ request->u.atapi.saved_cmd = request->u.atapi.ccb[0];
+ bcopy(ccb, request->u.atapi.ccb, 16);
+ request->data = (caddr_t)&request->u.atapi.sense;
+ request->bytecount = sizeof(struct atapi_sense);
+ request->donecount = 0;
+ request->transfersize = sizeof(struct atapi_sense);
+ request->timeout = 5;
+ request->flags &= (ATA_R_ATAPI | ATA_R_QUIET);
+ request->flags |= (ATA_R_READ | ATA_R_AT_HEAD | ATA_R_REQUEUE);
+ ATA_DEBUG_RQ(request, "autoissue request sense");
+ ata_queue_request(request);
+ return;
+ }
+
+ switch (request->u.atapi.sense.key & ATA_SENSE_KEY_MASK) {
+ case ATA_SENSE_RECOVERED_ERROR:
+ device_printf(request->dev, "WARNING - %s recovered error\n",
+ ata_cmd2str(request));
+ /* FALLTHROUGH */
+
+ case ATA_SENSE_NO_SENSE:
+ request->result = 0;
+ break;
+
+ case ATA_SENSE_NOT_READY:
+ request->result = EBUSY;
+ break;
+
+ case ATA_SENSE_UNIT_ATTENTION:
+ atadev->flags |= ATA_D_MEDIA_CHANGED;
+ request->result = EIO;
+ break;
+
+ default:
+ request->result = EIO;
+ if (request->flags & ATA_R_QUIET)
+ break;
+
+ device_printf(request->dev,
+ "FAILURE - %s %s asc=0x%02x ascq=0x%02x ",
+ ata_cmd2str(request), ata_skey2str(
+ (request->u.atapi.sense.key & ATA_SENSE_KEY_MASK)),
+ request->u.atapi.sense.asc,
+ request->u.atapi.sense.ascq);
+ if (request->u.atapi.sense.specific & ATA_SENSE_SPEC_VALID)
+ printf("sks=0x%02x 0x%02x 0x%02x\n",
+ request->u.atapi.sense.specific & ATA_SENSE_SPEC_MASK,
+ request->u.atapi.sense.specific1,
+ request->u.atapi.sense.specific2);
+ else
+ printf("\n");
+ }
+
+ if ((request->u.atapi.sense.key & ATA_SENSE_KEY_MASK ?
+ request->u.atapi.sense.key & ATA_SENSE_KEY_MASK :
+ request->error))
+ request->result = EIO;
+ }
+
+ ATA_DEBUG_RQ(request, "completed callback/wakeup");
+
+ /* if we are part of a composite operation we need to maintain progress */
+ if ((composite = request->composite)) {
+ int index = 0;
+
+ mtx_lock(&composite->lock);
+
+ /* update whats done */
+ if (request->flags & ATA_R_READ)
+ composite->rd_done |= (1 << request->this);
+ if (request->flags & ATA_R_WRITE)
+ composite->wr_done |= (1 << request->this);
+
+ /* find ready to go dependencies */
+ if (composite->wr_depend &&
+ (composite->rd_done & composite->wr_depend)==composite->wr_depend &&
+ (composite->wr_needed & (~composite->wr_done))) {
+ index = composite->wr_needed & ~composite->wr_done;
+ }
+
+ mtx_unlock(&composite->lock);
+
+ /* if we have any ready candidates kick them off */
+ if (index) {
+ int bit;
+
+ for (bit = 0; bit < MAX_COMPOSITES; bit++) {
+ if (index & (1 << bit))
+ ata_start(device_get_parent(composite->request[bit]->dev));
+ }
+ }
+ }
+
+ /* get results back to the initiator for this request */
+ if (request->callback)
+ (request->callback)(request);
+ else
+ sema_post(&request->done);
+
+ /* only call ata_start if channel is present */
+ if (ch)
+ ata_start(ch->dev);
+}
+
+void
+ata_timeout(struct ata_request *request)
+{
+ struct ata_channel *ch = device_get_softc(request->parent);
+
+ //request->flags |= ATA_R_DEBUG;
+ ATA_DEBUG_RQ(request, "timeout");
+
+ /*
+ * if we have an ATA_ACTIVE request running, we flag the request
+ * ATA_R_TIMEOUT so ata_finish will handle it correctly
+ * also NULL out the running request so we wont loose
+ * the race with an eventual interrupt arriving late
+ */
+ if (ch->state == ATA_ACTIVE) {
+ request->flags |= ATA_R_TIMEOUT;
+ mtx_unlock(&ch->state_mtx);
+ ATA_LOCKING(ch->dev, ATA_LF_UNLOCK);
+ ata_finish(request);
+ }
+ else {
+ mtx_unlock(&ch->state_mtx);
+ }
+}
+
+void
+ata_fail_requests(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_request *request, *tmp;
+ TAILQ_HEAD(, ata_request) fail_requests;
+ TAILQ_INIT(&fail_requests);
+
+ /* grap all channel locks to avoid races */
+ mtx_lock(&ch->queue_mtx);
+ mtx_lock(&ch->state_mtx);
+
+ /* do we have any running request to care about ? */
+ if ((request = ch->running) && (!dev || request->dev == dev)) {
+ callout_stop(&request->callout);
+ ch->running = NULL;
+ request->result = ENXIO;
+ TAILQ_INSERT_TAIL(&fail_requests, request, chain);
+ }
+
+ /* fail all requests queued on this channel for device dev if !NULL */
+ TAILQ_FOREACH_SAFE(request, &ch->ata_queue, chain, tmp) {
+ if (!dev || request->dev == dev) {
+ TAILQ_REMOVE(&ch->ata_queue, request, chain);
+ request->result = ENXIO;
+ TAILQ_INSERT_TAIL(&fail_requests, request, chain);
+ }
+ }
+
+ mtx_unlock(&ch->state_mtx);
+ mtx_unlock(&ch->queue_mtx);
+
+ /* finish up all requests collected above */
+ TAILQ_FOREACH_SAFE(request, &fail_requests, chain, tmp) {
+ TAILQ_REMOVE(&fail_requests, request, chain);
+ ata_finish(request);
+ }
+}
+
+static u_int64_t
+ata_get_lba(struct ata_request *request)
+{
+ if (request->flags & ATA_R_ATAPI) {
+ switch (request->u.atapi.ccb[0]) {
+ case ATAPI_READ_BIG:
+ case ATAPI_WRITE_BIG:
+ case ATAPI_READ_CD:
+ return (request->u.atapi.ccb[5]) | (request->u.atapi.ccb[4]<<8) |
+ (request->u.atapi.ccb[3]<<16)|(request->u.atapi.ccb[2]<<24);
+ case ATAPI_READ:
+ case ATAPI_WRITE:
+ return (request->u.atapi.ccb[4]) | (request->u.atapi.ccb[3]<<8) |
+ (request->u.atapi.ccb[2]<<16);
+ default:
+ return 0;
+ }
+ }
+ else
+ return request->u.ata.lba;
+}
+
+static void
+ata_sort_queue(struct ata_channel *ch, struct ata_request *request)
+{
+ struct ata_request *this, *next;
+
+ this = TAILQ_FIRST(&ch->ata_queue);
+
+ /* if the queue is empty just insert */
+ if (!this) {
+ if (request->composite)
+ ch->freezepoint = request;
+ TAILQ_INSERT_TAIL(&ch->ata_queue, request, chain);
+ return;
+ }
+
+ /* dont sort frozen parts of the queue */
+ if (ch->freezepoint)
+ this = ch->freezepoint;
+
+ /* if position is less than head we add after tipping point */
+ if (ata_get_lba(request) < ata_get_lba(this)) {
+ while ((next = TAILQ_NEXT(this, chain))) {
+
+ /* have we reached the tipping point */
+ if (ata_get_lba(next) < ata_get_lba(this)) {
+
+ /* sort the insert */
+ do {
+ if (ata_get_lba(request) < ata_get_lba(next))
+ break;
+ this = next;
+ } while ((next = TAILQ_NEXT(this, chain)));
+ break;
+ }
+ this = next;
+ }
+ }
+
+ /* we are after head so sort the insert before tipping point */
+ else {
+ while ((next = TAILQ_NEXT(this, chain))) {
+ if (ata_get_lba(next) < ata_get_lba(this) ||
+ ata_get_lba(request) < ata_get_lba(next))
+ break;
+ this = next;
+ }
+ }
+
+ if (request->composite)
+ ch->freezepoint = request;
+ TAILQ_INSERT_AFTER(&ch->ata_queue, this, request, chain);
+}
+
+char *
+ata_cmd2str(struct ata_request *request)
+{
+ static char buffer[20];
+
+ if (request->flags & ATA_R_ATAPI) {
+ switch (request->u.atapi.sense.key ?
+ request->u.atapi.saved_cmd : request->u.atapi.ccb[0]) {
+ case 0x00: return ("TEST_UNIT_READY");
+ case 0x01: return ("REZERO");
+ case 0x03: return ("REQUEST_SENSE");
+ case 0x04: return ("FORMAT");
+ case 0x08: return ("READ");
+ case 0x0a: return ("WRITE");
+ case 0x10: return ("WEOF");
+ case 0x11: return ("SPACE");
+ case 0x12: return ("INQUIRY");
+ case 0x15: return ("MODE_SELECT");
+ case 0x19: return ("ERASE");
+ case 0x1a: return ("MODE_SENSE");
+ case 0x1b: return ("START_STOP");
+ case 0x1e: return ("PREVENT_ALLOW");
+ case 0x23: return ("ATAPI_READ_FORMAT_CAPACITIES");
+ case 0x25: return ("READ_CAPACITY");
+ case 0x28: return ("READ_BIG");
+ case 0x2a: return ("WRITE_BIG");
+ case 0x2b: return ("LOCATE");
+ case 0x34: return ("READ_POSITION");
+ case 0x35: return ("SYNCHRONIZE_CACHE");
+ case 0x3b: return ("WRITE_BUFFER");
+ case 0x3c: return ("READ_BUFFER");
+ case 0x42: return ("READ_SUBCHANNEL");
+ case 0x43: return ("READ_TOC");
+ case 0x45: return ("PLAY_10");
+ case 0x47: return ("PLAY_MSF");
+ case 0x48: return ("PLAY_TRACK");
+ case 0x4b: return ("PAUSE");
+ case 0x51: return ("READ_DISK_INFO");
+ case 0x52: return ("READ_TRACK_INFO");
+ case 0x53: return ("RESERVE_TRACK");
+ case 0x54: return ("SEND_OPC_INFO");
+ case 0x55: return ("MODE_SELECT_BIG");
+ case 0x58: return ("REPAIR_TRACK");
+ case 0x59: return ("READ_MASTER_CUE");
+ case 0x5a: return ("MODE_SENSE_BIG");
+ case 0x5b: return ("CLOSE_TRACK/SESSION");
+ case 0x5c: return ("READ_BUFFER_CAPACITY");
+ case 0x5d: return ("SEND_CUE_SHEET");
+ case 0x96: return ("SERVICE_ACTION_IN");
+ case 0xa1: return ("BLANK_CMD");
+ case 0xa3: return ("SEND_KEY");
+ case 0xa4: return ("REPORT_KEY");
+ case 0xa5: return ("PLAY_12");
+ case 0xa6: return ("LOAD_UNLOAD");
+ case 0xad: return ("READ_DVD_STRUCTURE");
+ case 0xb4: return ("PLAY_CD");
+ case 0xbb: return ("SET_SPEED");
+ case 0xbd: return ("MECH_STATUS");
+ case 0xbe: return ("READ_CD");
+ case 0xff: return ("POLL_DSC");
+ }
+ }
+ else {
+ switch (request->u.ata.command) {
+ case 0x00: return ("NOP");
+ case 0x08: return ("DEVICE_RESET");
+ case 0x20: return ("READ");
+ case 0x24: return ("READ48");
+ case 0x25: return ("READ_DMA48");
+ case 0x26: return ("READ_DMA_QUEUED48");
+ case 0x29: return ("READ_MUL48");
+ case 0x30: return ("WRITE");
+ case 0x34: return ("WRITE48");
+ case 0x35: return ("WRITE_DMA48");
+ case 0x36: return ("WRITE_DMA_QUEUED48");
+ case 0x39: return ("WRITE_MUL48");
+ case 0x70: return ("SEEK");
+ case 0xa0: return ("PACKET_CMD");
+ case 0xa1: return ("ATAPI_IDENTIFY");
+ case 0xa2: return ("SERVICE");
+ case 0xc0: return ("CFA ERASE");
+ case 0xc4: return ("READ_MUL");
+ case 0xc5: return ("WRITE_MUL");
+ case 0xc6: return ("SET_MULTI");
+ case 0xc7: return ("READ_DMA_QUEUED");
+ case 0xc8: return ("READ_DMA");
+ case 0xca: return ("WRITE_DMA");
+ case 0xcc: return ("WRITE_DMA_QUEUED");
+ case 0xe6: return ("SLEEP");
+ case 0xe7: return ("FLUSHCACHE");
+ case 0xea: return ("FLUSHCACHE48");
+ case 0xec: return ("ATA_IDENTIFY");
+ case 0xef:
+ switch (request->u.ata.feature) {
+ case 0x03: return ("SETFEATURES SET TRANSFER MODE");
+ case 0x02: return ("SETFEATURES ENABLE WCACHE");
+ case 0x82: return ("SETFEATURES DISABLE WCACHE");
+ case 0xaa: return ("SETFEATURES ENABLE RCACHE");
+ case 0x55: return ("SETFEATURES DISABLE RCACHE");
+ }
+ sprintf(buffer, "SETFEATURES 0x%02x", request->u.ata.feature);
+ return buffer;
+ }
+ }
+ sprintf(buffer, "unknown CMD (0x%02x)", request->u.ata.command);
+ return buffer;
+}
+
+static char *
+ata_skey2str(u_int8_t skey)
+{
+ switch (skey) {
+ case 0x00: return ("NO SENSE");
+ case 0x01: return ("RECOVERED ERROR");
+ case 0x02: return ("NOT READY");
+ case 0x03: return ("MEDIUM ERROR");
+ case 0x04: return ("HARDWARE ERROR");
+ case 0x05: return ("ILLEGAL REQUEST");
+ case 0x06: return ("UNIT ATTENTION");
+ case 0x07: return ("DATA PROTECT");
+ case 0x08: return ("BLANK CHECK");
+ case 0x09: return ("VENDOR SPECIFIC");
+ case 0x0a: return ("COPY ABORTED");
+ case 0x0b: return ("ABORTED COMMAND");
+ case 0x0c: return ("EQUAL");
+ case 0x0d: return ("VOLUME OVERFLOW");
+ case 0x0e: return ("MISCOMPARE");
+ case 0x0f: return ("RESERVED");
+ default: return("UNKNOWN");
+ }
+}
diff --git a/sys/dev/ata/ata-raid.c b/sys/dev/ata/ata-raid.c
new file mode 100644
index 000000000000..78afd7b752b0
--- /dev/null
+++ b/sys/dev/ata/ata-raid.c
@@ -0,0 +1,4950 @@
+/*-
+ * Copyright (c) 2000 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/endian.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/disk.h>
+#include <sys/cons.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <dev/pci/pcivar.h>
+#include <geom/geom_disk.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/ata-disk.h>
+#include <dev/ata/ata-raid.h>
+#include <dev/ata/ata-pci.h>
+#include <ata_if.h>
+
+/* prototypes */
+static void ata_raid_done(struct ata_request *request);
+static void ata_raid_config_changed(struct ar_softc *rdp, int writeback);
+static int ata_raid_status(struct ata_ioc_raid_config *config);
+static int ata_raid_create(struct ata_ioc_raid_config *config);
+static int ata_raid_delete(int array);
+static int ata_raid_addspare(struct ata_ioc_raid_config *config);
+static int ata_raid_rebuild(int array);
+static int ata_raid_read_metadata(device_t subdisk);
+static int ata_raid_write_metadata(struct ar_softc *rdp);
+static int ata_raid_wipe_metadata(struct ar_softc *rdp);
+static int ata_raid_adaptec_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_hptv2_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_hptv2_write_meta(struct ar_softc *rdp);
+static int ata_raid_hptv3_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_intel_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_intel_write_meta(struct ar_softc *rdp);
+static int ata_raid_ite_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_jmicron_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_jmicron_write_meta(struct ar_softc *rdp);
+static int ata_raid_lsiv2_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_lsiv3_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_nvidia_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_promise_read_meta(device_t dev, struct ar_softc **raidp, int native);
+static int ata_raid_promise_write_meta(struct ar_softc *rdp);
+static int ata_raid_sii_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_sis_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_sis_write_meta(struct ar_softc *rdp);
+static int ata_raid_via_read_meta(device_t dev, struct ar_softc **raidp);
+static int ata_raid_via_write_meta(struct ar_softc *rdp);
+static struct ata_request *ata_raid_init_request(struct ar_softc *rdp, struct bio *bio);
+static int ata_raid_send_request(struct ata_request *request);
+static int ata_raid_rw(device_t dev, u_int64_t lba, void *data, u_int bcount, int flags);
+static char * ata_raid_format(struct ar_softc *rdp);
+static char * ata_raid_type(struct ar_softc *rdp);
+static char * ata_raid_flags(struct ar_softc *rdp);
+
+/* debugging only */
+static void ata_raid_print_meta(struct ar_softc *meta);
+static void ata_raid_adaptec_print_meta(struct adaptec_raid_conf *meta);
+static void ata_raid_hptv2_print_meta(struct hptv2_raid_conf *meta);
+static void ata_raid_hptv3_print_meta(struct hptv3_raid_conf *meta);
+static void ata_raid_intel_print_meta(struct intel_raid_conf *meta);
+static void ata_raid_ite_print_meta(struct ite_raid_conf *meta);
+static void ata_raid_jmicron_print_meta(struct jmicron_raid_conf *meta);
+static void ata_raid_lsiv2_print_meta(struct lsiv2_raid_conf *meta);
+static void ata_raid_lsiv3_print_meta(struct lsiv3_raid_conf *meta);
+static void ata_raid_nvidia_print_meta(struct nvidia_raid_conf *meta);
+static void ata_raid_promise_print_meta(struct promise_raid_conf *meta);
+static void ata_raid_sii_print_meta(struct sii_raid_conf *meta);
+static void ata_raid_sis_print_meta(struct sis_raid_conf *meta);
+static void ata_raid_via_print_meta(struct via_raid_conf *meta);
+
+/* internal vars */
+static struct ar_softc *ata_raid_arrays[MAX_ARRAYS];
+static MALLOC_DEFINE(M_AR, "ar_driver", "ATA PseudoRAID driver");
+static devclass_t ata_raid_sub_devclass;
+static int testing = 0;
+
+/* device structures */
+static disk_strategy_t ata_raid_strategy;
+static dumper_t ata_raid_dump;
+
+static void
+ata_raid_attach(struct ar_softc *rdp, int writeback)
+{
+ char buffer[32];
+ int disk;
+
+ mtx_init(&rdp->lock, "ATA PseudoRAID metadata lock", NULL, MTX_DEF);
+ ata_raid_config_changed(rdp, writeback);
+
+ /* sanitize arrays total_size % (width * interleave) == 0 */
+ if (rdp->type == AR_T_RAID0 || rdp->type == AR_T_RAID01 ||
+ rdp->type == AR_T_RAID5) {
+ rdp->total_sectors = (rdp->total_sectors/(rdp->interleave*rdp->width))*
+ (rdp->interleave * rdp->width);
+ sprintf(buffer, " (stripe %d KB)",
+ (rdp->interleave * DEV_BSIZE) / 1024);
+ }
+ else
+ buffer[0] = '\0';
+ rdp->disk = disk_alloc();
+ rdp->disk->d_strategy = ata_raid_strategy;
+ rdp->disk->d_dump = ata_raid_dump;
+ rdp->disk->d_name = "ar";
+ rdp->disk->d_sectorsize = DEV_BSIZE;
+ rdp->disk->d_mediasize = (off_t)rdp->total_sectors * DEV_BSIZE;
+ rdp->disk->d_fwsectors = rdp->sectors;
+ rdp->disk->d_fwheads = rdp->heads;
+ rdp->disk->d_maxsize = 128 * DEV_BSIZE;
+ rdp->disk->d_drv1 = rdp;
+ rdp->disk->d_unit = rdp->lun;
+ disk_create(rdp->disk, DISK_VERSION);
+
+ printf("ar%d: %juMB <%s %s%s> status: %s\n", rdp->lun,
+ rdp->total_sectors / ((1024L * 1024L) / DEV_BSIZE),
+ ata_raid_format(rdp), ata_raid_type(rdp),
+ buffer, ata_raid_flags(rdp));
+
+ if (testing || bootverbose)
+ printf("ar%d: %ju sectors [%dC/%dH/%dS] <%s> subdisks defined as:\n",
+ rdp->lun, rdp->total_sectors,
+ rdp->cylinders, rdp->heads, rdp->sectors, rdp->name);
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ printf("ar%d: disk%d ", rdp->lun, disk);
+ if (rdp->disks[disk].dev) {
+ if (rdp->disks[disk].flags & AR_DF_PRESENT) {
+ /* status of this disk in the array */
+ if (rdp->disks[disk].flags & AR_DF_ONLINE)
+ printf("READY ");
+ else if (rdp->disks[disk].flags & AR_DF_SPARE)
+ printf("SPARE ");
+ else
+ printf("FREE ");
+
+ /* what type of disk is this in the array */
+ switch (rdp->type) {
+ case AR_T_RAID1:
+ case AR_T_RAID01:
+ if (disk < rdp->width)
+ printf("(master) ");
+ else
+ printf("(mirror) ");
+ }
+
+ /* which physical disk is used */
+ printf("using %s at ata%d-%s\n",
+ device_get_nameunit(rdp->disks[disk].dev),
+ device_get_unit(device_get_parent(rdp->disks[disk].dev)),
+ (((struct ata_device *)
+ device_get_softc(rdp->disks[disk].dev))->unit ==
+ ATA_MASTER) ? "master" : "slave");
+ }
+ else if (rdp->disks[disk].flags & AR_DF_ASSIGNED)
+ printf("DOWN\n");
+ else
+ printf("INVALID no RAID config on this subdisk\n");
+ }
+ else
+ printf("DOWN no device found for this subdisk\n");
+ }
+}
+
+static int
+ata_raid_ioctl(u_long cmd, caddr_t data)
+{
+ struct ata_ioc_raid_config *config = (struct ata_ioc_raid_config *)data;
+ int *lun = (int *)data;
+ int error = EOPNOTSUPP;
+
+ switch (cmd) {
+ case IOCATARAIDSTATUS:
+ error = ata_raid_status(config);
+ break;
+
+ case IOCATARAIDCREATE:
+ error = ata_raid_create(config);
+ break;
+
+ case IOCATARAIDDELETE:
+ error = ata_raid_delete(*lun);
+ break;
+
+ case IOCATARAIDADDSPARE:
+ error = ata_raid_addspare(config);
+ break;
+
+ case IOCATARAIDREBUILD:
+ error = ata_raid_rebuild(*lun);
+ break;
+ }
+ return error;
+}
+
+static void
+ata_raid_strategy(struct bio *bp)
+{
+ struct ar_softc *rdp = bp->bio_disk->d_drv1;
+ struct ata_request *request;
+ caddr_t data;
+ u_int64_t blkno, lba, blk = 0;
+ int count, chunk, drv, par = 0, change = 0;
+
+ if (!(rdp->status & AR_S_READY) ||
+ (bp->bio_cmd != BIO_READ && bp->bio_cmd != BIO_WRITE)) {
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+
+ bp->bio_resid = bp->bio_bcount;
+ for (count = howmany(bp->bio_bcount, DEV_BSIZE),
+ blkno = bp->bio_pblkno, data = bp->bio_data;
+ count > 0;
+ count -= chunk, blkno += chunk, data += (chunk * DEV_BSIZE)) {
+
+ switch (rdp->type) {
+ case AR_T_RAID1:
+ drv = 0;
+ lba = blkno;
+ chunk = count;
+ break;
+
+ case AR_T_JBOD:
+ case AR_T_SPAN:
+ drv = 0;
+ lba = blkno;
+ while (lba >= rdp->disks[drv].sectors)
+ lba -= rdp->disks[drv++].sectors;
+ chunk = min(rdp->disks[drv].sectors - lba, count);
+ break;
+
+ case AR_T_RAID0:
+ case AR_T_RAID01:
+ chunk = blkno % rdp->interleave;
+ drv = (blkno / rdp->interleave) % rdp->width;
+ lba = (((blkno/rdp->interleave)/rdp->width)*rdp->interleave)+chunk;
+ chunk = min(count, rdp->interleave - chunk);
+ break;
+
+ case AR_T_RAID5:
+ drv = (blkno / rdp->interleave) % (rdp->width - 1);
+ par = rdp->width - 1 -
+ (blkno / (rdp->interleave * (rdp->width - 1))) % rdp->width;
+ if (drv >= par)
+ drv++;
+ lba = ((blkno/rdp->interleave)/(rdp->width-1))*(rdp->interleave) +
+ ((blkno%(rdp->interleave*(rdp->width-1)))%rdp->interleave);
+ chunk = min(count, rdp->interleave - (lba % rdp->interleave));
+ break;
+
+ default:
+ printf("ar%d: unknown array type in ata_raid_strategy\n", rdp->lun);
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+
+ /* offset on all but "first on HPTv2" */
+ if (!(drv == 0 && rdp->format == AR_F_HPTV2_RAID))
+ lba += rdp->offset_sectors;
+
+ if (!(request = ata_raid_init_request(rdp, bp))) {
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+ request->data = data;
+ request->bytecount = chunk * DEV_BSIZE;
+ request->u.ata.lba = lba;
+ request->u.ata.count = request->bytecount / DEV_BSIZE;
+
+ switch (rdp->type) {
+ case AR_T_JBOD:
+ case AR_T_SPAN:
+ case AR_T_RAID0:
+ if (((rdp->disks[drv].flags & (AR_DF_PRESENT|AR_DF_ONLINE)) ==
+ (AR_DF_PRESENT|AR_DF_ONLINE) && !rdp->disks[drv].dev)) {
+ rdp->disks[drv].flags &= ~AR_DF_ONLINE;
+ ata_raid_config_changed(rdp, 1);
+ ata_free_request(request);
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+ request->this = drv;
+ request->dev = rdp->disks[request->this].dev;
+ ata_raid_send_request(request);
+ break;
+
+ case AR_T_RAID1:
+ case AR_T_RAID01:
+ if ((rdp->disks[drv].flags &
+ (AR_DF_PRESENT|AR_DF_ONLINE))==(AR_DF_PRESENT|AR_DF_ONLINE) &&
+ !rdp->disks[drv].dev) {
+ rdp->disks[drv].flags &= ~AR_DF_ONLINE;
+ change = 1;
+ }
+ if ((rdp->disks[drv + rdp->width].flags &
+ (AR_DF_PRESENT|AR_DF_ONLINE))==(AR_DF_PRESENT|AR_DF_ONLINE) &&
+ !rdp->disks[drv + rdp->width].dev) {
+ rdp->disks[drv + rdp->width].flags &= ~AR_DF_ONLINE;
+ change = 1;
+ }
+ if (change)
+ ata_raid_config_changed(rdp, 1);
+ if (!(rdp->status & AR_S_READY)) {
+ ata_free_request(request);
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+
+ if (rdp->status & AR_S_REBUILDING)
+ blk = ((lba / rdp->interleave) * rdp->width) * rdp->interleave +
+ (rdp->interleave * (drv % rdp->width)) +
+ lba % rdp->interleave;;
+
+ if (bp->bio_cmd == BIO_READ) {
+ int src_online =
+ (rdp->disks[drv].flags & AR_DF_ONLINE);
+ int mir_online =
+ (rdp->disks[drv+rdp->width].flags & AR_DF_ONLINE);
+
+ /* if mirror gone or close to last access on source */
+ if (!mir_online ||
+ ((src_online) &&
+ bp->bio_pblkno >=
+ (rdp->disks[drv].last_lba - AR_PROXIMITY) &&
+ bp->bio_pblkno <=
+ (rdp->disks[drv].last_lba + AR_PROXIMITY))) {
+ rdp->toggle = 0;
+ }
+ /* if source gone or close to last access on mirror */
+ else if (!src_online ||
+ ((mir_online) &&
+ bp->bio_pblkno >=
+ (rdp->disks[drv+rdp->width].last_lba-AR_PROXIMITY) &&
+ bp->bio_pblkno <=
+ (rdp->disks[drv+rdp->width].last_lba+AR_PROXIMITY))) {
+ drv += rdp->width;
+ rdp->toggle = 1;
+ }
+ /* not close to any previous access, toggle */
+ else {
+ if (rdp->toggle)
+ rdp->toggle = 0;
+ else {
+ drv += rdp->width;
+ rdp->toggle = 1;
+ }
+ }
+
+ if ((rdp->status & AR_S_REBUILDING) &&
+ (blk <= rdp->rebuild_lba) &&
+ ((blk + chunk) > rdp->rebuild_lba)) {
+ struct ata_composite *composite;
+ struct ata_request *rebuild;
+ int this;
+
+ /* figure out what part to rebuild */
+ if (drv < rdp->width)
+ this = drv + rdp->width;
+ else
+ this = drv - rdp->width;
+
+ /* do we have a spare to rebuild on ? */
+ if (rdp->disks[this].flags & AR_DF_SPARE) {
+ if ((composite = ata_alloc_composite())) {
+ if ((rebuild = ata_alloc_request())) {
+ rdp->rebuild_lba = blk + chunk;
+ bcopy(request, rebuild,
+ sizeof(struct ata_request));
+ rebuild->this = this;
+ rebuild->dev = rdp->disks[this].dev;
+ rebuild->flags &= ~ATA_R_READ;
+ rebuild->flags |= ATA_R_WRITE;
+ mtx_init(&composite->lock,
+ "ATA PseudoRAID rebuild lock",
+ NULL, MTX_DEF);
+ composite->residual = request->bytecount;
+ composite->rd_needed |= (1 << drv);
+ composite->wr_depend |= (1 << drv);
+ composite->wr_needed |= (1 << this);
+ composite->request[drv] = request;
+ composite->request[this] = rebuild;
+ request->composite = composite;
+ rebuild->composite = composite;
+ ata_raid_send_request(rebuild);
+ }
+ else {
+ ata_free_composite(composite);
+ printf("DOH! ata_alloc_request failed!\n");
+ }
+ }
+ else {
+ printf("DOH! ata_alloc_composite failed!\n");
+ }
+ }
+ else if (rdp->disks[this].flags & AR_DF_ONLINE) {
+ /*
+ * if we got here we are a chunk of a RAID01 that
+ * does not need a rebuild, but we need to increment
+ * the rebuild_lba address to get the rebuild to
+ * move to the next chunk correctly
+ */
+ rdp->rebuild_lba = blk + chunk;
+ }
+ else
+ printf("DOH! we didn't find the rebuild part\n");
+ }
+ }
+ if (bp->bio_cmd == BIO_WRITE) {
+ if ((rdp->disks[drv+rdp->width].flags & AR_DF_ONLINE) ||
+ ((rdp->status & AR_S_REBUILDING) &&
+ (rdp->disks[drv+rdp->width].flags & AR_DF_SPARE) &&
+ ((blk < rdp->rebuild_lba) ||
+ ((blk <= rdp->rebuild_lba) &&
+ ((blk + chunk) > rdp->rebuild_lba))))) {
+ if ((rdp->disks[drv].flags & AR_DF_ONLINE) ||
+ ((rdp->status & AR_S_REBUILDING) &&
+ (rdp->disks[drv].flags & AR_DF_SPARE) &&
+ ((blk < rdp->rebuild_lba) ||
+ ((blk <= rdp->rebuild_lba) &&
+ ((blk + chunk) > rdp->rebuild_lba))))) {
+ struct ata_request *mirror;
+ struct ata_composite *composite;
+ int this = drv + rdp->width;
+
+ if ((composite = ata_alloc_composite())) {
+ if ((mirror = ata_alloc_request())) {
+ if ((blk <= rdp->rebuild_lba) &&
+ ((blk + chunk) > rdp->rebuild_lba))
+ rdp->rebuild_lba = blk + chunk;
+ bcopy(request, mirror,
+ sizeof(struct ata_request));
+ mirror->this = this;
+ mirror->dev = rdp->disks[this].dev;
+ mtx_init(&composite->lock,
+ "ATA PseudoRAID mirror lock",
+ NULL, MTX_DEF);
+ composite->residual = request->bytecount;
+ composite->wr_needed |= (1 << drv);
+ composite->wr_needed |= (1 << this);
+ composite->request[drv] = request;
+ composite->request[this] = mirror;
+ request->composite = composite;
+ mirror->composite = composite;
+ ata_raid_send_request(mirror);
+ rdp->disks[this].last_lba =
+ bp->bio_pblkno + chunk;
+ }
+ else {
+ ata_free_composite(composite);
+ printf("DOH! ata_alloc_request failed!\n");
+ }
+ }
+ else {
+ printf("DOH! ata_alloc_composite failed!\n");
+ }
+ }
+ else
+ drv += rdp->width;
+ }
+ }
+ request->this = drv;
+ request->dev = rdp->disks[request->this].dev;
+ ata_raid_send_request(request);
+ rdp->disks[request->this].last_lba = bp->bio_pblkno + chunk;
+ break;
+
+ case AR_T_RAID5:
+ if (((rdp->disks[drv].flags & (AR_DF_PRESENT|AR_DF_ONLINE)) ==
+ (AR_DF_PRESENT|AR_DF_ONLINE) && !rdp->disks[drv].dev)) {
+ rdp->disks[drv].flags &= ~AR_DF_ONLINE;
+ change = 1;
+ }
+ if (((rdp->disks[par].flags & (AR_DF_PRESENT|AR_DF_ONLINE)) ==
+ (AR_DF_PRESENT|AR_DF_ONLINE) && !rdp->disks[par].dev)) {
+ rdp->disks[par].flags &= ~AR_DF_ONLINE;
+ change = 1;
+ }
+ if (change)
+ ata_raid_config_changed(rdp, 1);
+ if (!(rdp->status & AR_S_READY)) {
+ ata_free_request(request);
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+ if (rdp->status & AR_S_DEGRADED) {
+ /* do the XOR game if possible */
+ }
+ else {
+ request->this = drv;
+ request->dev = rdp->disks[request->this].dev;
+ if (bp->bio_cmd == BIO_READ) {
+ ata_raid_send_request(request);
+ }
+ if (bp->bio_cmd == BIO_WRITE) {
+ ata_raid_send_request(request);
+ // sikre at læs-modify-skriv til hver disk er atomarisk.
+ // par kopi af request
+ // læse orgdata fra drv
+ // skriv nydata til drv
+ // læse parorgdata fra par
+ // skriv orgdata xor parorgdata xor nydata til par
+ }
+ }
+ break;
+
+ default:
+ printf("ar%d: unknown array type in ata_raid_strategy\n", rdp->lun);
+ }
+ }
+}
+
+static void
+ata_raid_done(struct ata_request *request)
+{
+ struct ar_softc *rdp = request->driver;
+ struct ata_composite *composite = NULL;
+ struct bio *bp = request->bio;
+ int i, mirror, finished = 0;
+
+ switch (rdp->type) {
+ case AR_T_JBOD:
+ case AR_T_SPAN:
+ case AR_T_RAID0:
+ if (request->result) {
+ rdp->disks[request->this].flags &= ~AR_DF_ONLINE;
+ ata_raid_config_changed(rdp, 1);
+ bp->bio_error = request->result;
+ finished = 1;
+ }
+ else {
+ bp->bio_resid -= request->donecount;
+ if (!bp->bio_resid)
+ finished = 1;
+ }
+ break;
+
+ case AR_T_RAID1:
+ case AR_T_RAID01:
+ if (request->this < rdp->width)
+ mirror = request->this + rdp->width;
+ else
+ mirror = request->this - rdp->width;
+ if (request->result) {
+ rdp->disks[request->this].flags &= ~AR_DF_ONLINE;
+ ata_raid_config_changed(rdp, 1);
+ }
+ if (rdp->status & AR_S_READY) {
+ u_int64_t blk = 0;
+
+ if (rdp->status & AR_S_REBUILDING)
+ blk = ((request->u.ata.lba / rdp->interleave) * rdp->width) *
+ rdp->interleave + (rdp->interleave *
+ (request->this % rdp->width)) +
+ request->u.ata.lba % rdp->interleave;
+
+ if (bp->bio_cmd == BIO_READ) {
+
+ /* is this a rebuild composite */
+ if ((composite = request->composite)) {
+ mtx_lock(&composite->lock);
+
+ /* handle the read part of a rebuild composite */
+ if (request->flags & ATA_R_READ) {
+
+ /* if read failed array is now broken */
+ if (request->result) {
+ rdp->disks[request->this].flags &= ~AR_DF_ONLINE;
+ ata_raid_config_changed(rdp, 1);
+ bp->bio_error = request->result;
+ rdp->rebuild_lba = blk;
+ finished = 1;
+ }
+
+ /* good data, update how far we've gotten */
+ else {
+ bp->bio_resid -= request->donecount;
+ composite->residual -= request->donecount;
+ if (!composite->residual) {
+ if (composite->wr_done & (1 << mirror))
+ finished = 1;
+ }
+ }
+ }
+
+ /* handle the write part of a rebuild composite */
+ else if (request->flags & ATA_R_WRITE) {
+ if (composite->rd_done & (1 << mirror)) {
+ if (request->result) {
+ printf("DOH! rebuild failed\n"); /* XXX SOS */
+ rdp->rebuild_lba = blk;
+ }
+ if (!composite->residual)
+ finished = 1;
+ }
+ }
+ mtx_unlock(&composite->lock);
+ }
+
+ /* if read failed retry on the mirror */
+ else if (request->result) {
+ request->dev = rdp->disks[mirror].dev;
+ request->flags &= ~ATA_R_TIMEOUT;
+ ata_raid_send_request(request);
+ return;
+ }
+
+ /* we have good data */
+ else {
+ bp->bio_resid -= request->donecount;
+ if (!bp->bio_resid)
+ finished = 1;
+ }
+ }
+ else if (bp->bio_cmd == BIO_WRITE) {
+ /* do we have a mirror or rebuild to deal with ? */
+ if ((composite = request->composite)) {
+ mtx_lock(&composite->lock);
+ if (composite->wr_done & (1 << mirror)) {
+ if (request->result) {
+ if (composite->request[mirror]->result) {
+ printf("DOH! all disks failed and got here\n");
+ bp->bio_error = EIO;
+ }
+ if (rdp->status & AR_S_REBUILDING) {
+ rdp->rebuild_lba = blk;
+ printf("DOH! rebuild failed\n"); /* XXX SOS */
+ }
+ bp->bio_resid -=
+ composite->request[mirror]->donecount;
+ composite->residual -=
+ composite->request[mirror]->donecount;
+ }
+ else {
+ bp->bio_resid -= request->donecount;
+ composite->residual -= request->donecount;
+ }
+ if (!composite->residual)
+ finished = 1;
+ }
+ mtx_unlock(&composite->lock);
+ }
+ /* no mirror we are done */
+ else {
+ bp->bio_resid -= request->donecount;
+ if (!bp->bio_resid)
+ finished = 1;
+ }
+ }
+ }
+ else
+ biofinish(bp, NULL, request->result);
+ break;
+
+ case AR_T_RAID5:
+ if (request->result) {
+ rdp->disks[request->this].flags &= ~AR_DF_ONLINE;
+ ata_raid_config_changed(rdp, 1);
+ if (rdp->status & AR_S_READY) {
+ if (bp->bio_cmd == BIO_READ) {
+ /* do the XOR game to recover data */
+ }
+ if (bp->bio_cmd == BIO_WRITE) {
+ /* if the parity failed we're OK sortof */
+ /* otherwise wee need to do the XOR long dance */
+ }
+ finished = 1;
+ }
+ else
+ biofinish(bp, NULL, request->result);
+ }
+ else {
+ // did we have an XOR game going ??
+ bp->bio_resid -= request->donecount;
+ if (!bp->bio_resid)
+ finished = 1;
+ }
+ break;
+
+ default:
+ printf("ar%d: unknown array type in ata_raid_done\n", rdp->lun);
+ }
+
+ if (finished) {
+ if ((rdp->status & AR_S_REBUILDING) &&
+ rdp->rebuild_lba >= rdp->total_sectors) {
+ int disk;
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if ((rdp->disks[disk].flags &
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_SPARE)) ==
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_SPARE)) {
+ rdp->disks[disk].flags &= ~AR_DF_SPARE;
+ rdp->disks[disk].flags |= AR_DF_ONLINE;
+ }
+ }
+ rdp->status &= ~AR_S_REBUILDING;
+ ata_raid_config_changed(rdp, 1);
+ }
+ if (!bp->bio_resid)
+ biodone(bp);
+ }
+
+ if (composite) {
+ if (finished) {
+ /* we are done with this composite, free all resources */
+ for (i = 0; i < 32; i++) {
+ if (composite->rd_needed & (1 << i) ||
+ composite->wr_needed & (1 << i)) {
+ ata_free_request(composite->request[i]);
+ }
+ }
+ mtx_destroy(&composite->lock);
+ ata_free_composite(composite);
+ }
+ }
+ else
+ ata_free_request(request);
+}
+
+static int
+ata_raid_dump(void *arg, void *virtual, vm_offset_t physical,
+ off_t offset, size_t length)
+{
+ struct disk *dp = arg;
+ struct ar_softc *rdp = dp->d_drv1;
+ struct bio bp;
+
+ /* length zero is special and really means flush buffers to media */
+ if (!length) {
+ int disk, error;
+
+ for (disk = 0, error = 0; disk < rdp->total_disks; disk++)
+ if (rdp->disks[disk].dev)
+ error |= ata_controlcmd(rdp->disks[disk].dev,
+ ATA_FLUSHCACHE, 0, 0, 0);
+ return (error ? EIO : 0);
+ }
+
+ bzero(&bp, sizeof(struct bio));
+ bp.bio_disk = dp;
+ bp.bio_pblkno = offset / DEV_BSIZE;
+ bp.bio_bcount = length;
+ bp.bio_data = virtual;
+ bp.bio_cmd = BIO_WRITE;
+ ata_raid_strategy(&bp);
+ return bp.bio_error;
+}
+
+static void
+ata_raid_config_changed(struct ar_softc *rdp, int writeback)
+{
+ int disk, count, status;
+
+ mtx_lock(&rdp->lock);
+ /* set default all working mode */
+ status = rdp->status;
+ rdp->status &= ~AR_S_DEGRADED;
+ rdp->status |= AR_S_READY;
+
+ /* make sure all lost drives are accounted for */
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (!(rdp->disks[disk].flags & AR_DF_PRESENT))
+ rdp->disks[disk].flags &= ~AR_DF_ONLINE;
+ }
+
+ /* depending on RAID type figure out our health status */
+ switch (rdp->type) {
+ case AR_T_JBOD:
+ case AR_T_SPAN:
+ case AR_T_RAID0:
+ for (disk = 0; disk < rdp->total_disks; disk++)
+ if (!(rdp->disks[disk].flags & AR_DF_ONLINE))
+ rdp->status &= ~AR_S_READY;
+ break;
+
+ case AR_T_RAID1:
+ case AR_T_RAID01:
+ for (disk = 0; disk < rdp->width; disk++) {
+ if (!(rdp->disks[disk].flags & AR_DF_ONLINE) &&
+ !(rdp->disks[disk + rdp->width].flags & AR_DF_ONLINE)) {
+ rdp->status &= ~AR_S_READY;
+ }
+ else if (((rdp->disks[disk].flags & AR_DF_ONLINE) &&
+ !(rdp->disks[disk + rdp->width].flags & AR_DF_ONLINE)) ||
+ (!(rdp->disks[disk].flags & AR_DF_ONLINE) &&
+ (rdp->disks [disk + rdp->width].flags & AR_DF_ONLINE))) {
+ rdp->status |= AR_S_DEGRADED;
+ }
+ }
+ break;
+
+ case AR_T_RAID5:
+ for (count = 0, disk = 0; disk < rdp->total_disks; disk++) {
+ if (!(rdp->disks[disk].flags & AR_DF_ONLINE))
+ count++;
+ }
+ if (count) {
+ if (count > 1)
+ rdp->status &= ~AR_S_READY;
+ else
+ rdp->status |= AR_S_DEGRADED;
+ }
+ break;
+ default:
+ rdp->status &= ~AR_S_READY;
+ }
+
+ if (rdp->status != status) {
+ if (!(rdp->status & AR_S_READY)) {
+ printf("ar%d: FAILURE - %s array broken\n",
+ rdp->lun, ata_raid_type(rdp));
+ }
+ else if (rdp->status & AR_S_DEGRADED) {
+ if (rdp->type & (AR_T_RAID1 | AR_T_RAID01))
+ printf("ar%d: WARNING - mirror", rdp->lun);
+ else
+ printf("ar%d: WARNING - parity", rdp->lun);
+ printf(" protection lost. %s array in DEGRADED mode\n",
+ ata_raid_type(rdp));
+ }
+ }
+ mtx_unlock(&rdp->lock);
+ if (writeback)
+ ata_raid_write_metadata(rdp);
+
+}
+
+static int
+ata_raid_status(struct ata_ioc_raid_config *config)
+{
+ struct ar_softc *rdp;
+ int i;
+
+ if (!(rdp = ata_raid_arrays[config->lun]))
+ return ENXIO;
+
+ config->type = rdp->type;
+ config->total_disks = rdp->total_disks;
+ for (i = 0; i < rdp->total_disks; i++ ) {
+ if ((rdp->disks[i].flags & AR_DF_PRESENT) && rdp->disks[i].dev)
+ config->disks[i] = device_get_unit(rdp->disks[i].dev);
+ else
+ config->disks[i] = -1;
+ }
+ config->interleave = rdp->interleave;
+ config->status = rdp->status;
+ config->progress = 100 * rdp->rebuild_lba / rdp->total_sectors;
+ return 0;
+}
+
+static int
+ata_raid_create(struct ata_ioc_raid_config *config)
+{
+ struct ar_softc *rdp;
+ device_t subdisk;
+ int array, disk;
+ int ctlr = 0, disk_size = 0, total_disks = 0;
+
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!ata_raid_arrays[array])
+ break;
+ }
+ if (array >= MAX_ARRAYS)
+ return ENOSPC;
+
+ if (!(rdp = (struct ar_softc*)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO))) {
+ printf("ar%d: no memory for metadata storage\n", array);
+ return ENOMEM;
+ }
+
+ for (disk = 0; disk < config->total_disks; disk++) {
+ if ((subdisk = devclass_get_device(ata_raid_sub_devclass,
+ config->disks[disk]))) {
+ struct ata_raid_subdisk *ars = device_get_softc(subdisk);
+
+ /* is device already assigned to another array ? */
+ if (ars->raid[rdp->volume]) {
+ config->disks[disk] = -1;
+ free(rdp, M_AR);
+ return EBUSY;
+ }
+ rdp->disks[disk].dev = device_get_parent(subdisk);
+
+ switch (pci_get_vendor(GRANDPARENT(rdp->disks[disk].dev))) {
+ case ATA_HIGHPOINT_ID:
+ /*
+ * we need some way to decide if it should be v2 or v3
+ * for now just use v2 since the v3 BIOS knows how to
+ * handle that as well.
+ */
+ ctlr = AR_F_HPTV2_RAID;
+ rdp->disks[disk].sectors = HPTV3_LBA(rdp->disks[disk].dev);
+ break;
+
+ case ATA_INTEL_ID:
+ ctlr = AR_F_INTEL_RAID;
+ rdp->disks[disk].sectors = INTEL_LBA(rdp->disks[disk].dev);
+ break;
+
+ case ATA_ITE_ID:
+ ctlr = AR_F_ITE_RAID;
+ rdp->disks[disk].sectors = ITE_LBA(rdp->disks[disk].dev);
+ break;
+
+ case ATA_JMICRON_ID:
+ ctlr = AR_F_JMICRON_RAID;
+ rdp->disks[disk].sectors = JMICRON_LBA(rdp->disks[disk].dev);
+ break;
+
+ case 0: /* XXX SOS cover up for bug in our PCI code */
+ case ATA_PROMISE_ID:
+ ctlr = AR_F_PROMISE_RAID;
+ rdp->disks[disk].sectors = PROMISE_LBA(rdp->disks[disk].dev);
+ break;
+
+ case ATA_SIS_ID:
+ ctlr = AR_F_SIS_RAID;
+ rdp->disks[disk].sectors = SIS_LBA(rdp->disks[disk].dev);
+ break;
+
+ case ATA_ATI_ID:
+ case ATA_VIA_ID:
+ ctlr = AR_F_VIA_RAID;
+ rdp->disks[disk].sectors = VIA_LBA(rdp->disks[disk].dev);
+ break;
+
+ default:
+ /* XXX SOS
+ * right, so here we are, we have an ATA chip and we want
+ * to create a RAID and store the metadata.
+ * we need to find a way to tell what kind of metadata this
+ * hardware's BIOS might be using (good ideas are welcomed)
+ * for now we just use our own native FreeBSD format.
+ * the only way to get support for the BIOS format is to
+ * setup the RAID from there, in that case we pickup the
+ * metadata format from the disks (if we support it).
+ */
+ printf("WARNING!! - not able to determine metadata format\n"
+ "WARNING!! - Using FreeBSD PseudoRAID metadata\n"
+ "If that is not what you want, use the BIOS to "
+ "create the array\n");
+ ctlr = AR_F_FREEBSD_RAID;
+ rdp->disks[disk].sectors = PROMISE_LBA(rdp->disks[disk].dev);
+ break;
+ }
+
+ /* we need all disks to be of the same format */
+ if ((rdp->format & AR_F_FORMAT_MASK) &&
+ (rdp->format & AR_F_FORMAT_MASK) != (ctlr & AR_F_FORMAT_MASK)) {
+ free(rdp, M_AR);
+ return EXDEV;
+ }
+ else
+ rdp->format = ctlr;
+
+ /* use the smallest disk of the lots size */
+ /* gigabyte boundry ??? XXX SOS */
+ if (disk_size)
+ disk_size = min(rdp->disks[disk].sectors, disk_size);
+ else
+ disk_size = rdp->disks[disk].sectors;
+ rdp->disks[disk].flags =
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE);
+
+ total_disks++;
+ }
+ else {
+ config->disks[disk] = -1;
+ free(rdp, M_AR);
+ return ENXIO;
+ }
+ }
+
+ if (total_disks != config->total_disks) {
+ free(rdp, M_AR);
+ return ENODEV;
+ }
+
+ switch (config->type) {
+ case AR_T_JBOD:
+ case AR_T_SPAN:
+ case AR_T_RAID0:
+ break;
+
+ case AR_T_RAID1:
+ if (total_disks != 2) {
+ free(rdp, M_AR);
+ return EPERM;
+ }
+ break;
+
+ case AR_T_RAID01:
+ if (total_disks % 2 != 0) {
+ free(rdp, M_AR);
+ return EPERM;
+ }
+ break;
+
+ case AR_T_RAID5:
+ if (total_disks < 3) {
+ free(rdp, M_AR);
+ return EPERM;
+ }
+ break;
+
+ default:
+ free(rdp, M_AR);
+ return EOPNOTSUPP;
+ }
+ rdp->type = config->type;
+ rdp->lun = array;
+ if (rdp->type == AR_T_RAID0 || rdp->type == AR_T_RAID01 ||
+ rdp->type == AR_T_RAID5) {
+ int bit = 0;
+
+ while (config->interleave >>= 1)
+ bit++;
+ rdp->interleave = 1 << bit;
+ }
+ rdp->offset_sectors = 0;
+
+ /* values that depend on metadata format */
+ switch (rdp->format) {
+ case AR_F_ADAPTEC_RAID:
+ rdp->interleave = min(max(32, rdp->interleave), 128); /*+*/
+ break;
+
+ case AR_F_HPTV2_RAID:
+ rdp->interleave = min(max(8, rdp->interleave), 128); /*+*/
+ rdp->offset_sectors = HPTV2_LBA(x) + 1;
+ break;
+
+ case AR_F_HPTV3_RAID:
+ rdp->interleave = min(max(32, rdp->interleave), 4096); /*+*/
+ break;
+
+ case AR_F_INTEL_RAID:
+ rdp->interleave = min(max(8, rdp->interleave), 256); /*+*/
+ break;
+
+ case AR_F_ITE_RAID:
+ rdp->interleave = min(max(2, rdp->interleave), 128); /*+*/
+ break;
+
+ case AR_F_JMICRON_RAID:
+ rdp->interleave = min(max(8, rdp->interleave), 256); /*+*/
+ break;
+
+ case AR_F_LSIV2_RAID:
+ rdp->interleave = min(max(2, rdp->interleave), 4096);
+ break;
+
+ case AR_F_LSIV3_RAID:
+ rdp->interleave = min(max(2, rdp->interleave), 256);
+ break;
+
+ case AR_F_PROMISE_RAID:
+ rdp->interleave = min(max(2, rdp->interleave), 2048); /*+*/
+ break;
+
+ case AR_F_SII_RAID:
+ rdp->interleave = min(max(8, rdp->interleave), 256); /*+*/
+ break;
+
+ case AR_F_SIS_RAID:
+ rdp->interleave = min(max(32, rdp->interleave), 512); /*+*/
+ break;
+
+ case AR_F_VIA_RAID:
+ rdp->interleave = min(max(8, rdp->interleave), 128); /*+*/
+ break;
+ }
+
+ rdp->total_disks = total_disks;
+ rdp->width = total_disks / (rdp->type & (AR_RAID1 | AR_T_RAID01) ? 2 : 1);
+ rdp->total_sectors = disk_size * (rdp->width - (rdp->type == AR_RAID5));
+ rdp->heads = 255;
+ rdp->sectors = 63;
+ rdp->cylinders = rdp->total_sectors / (255 * 63);
+ rdp->rebuild_lba = 0;
+ rdp->status |= AR_S_READY;
+
+ /* we are committed to this array, grap the subdisks */
+ for (disk = 0; disk < config->total_disks; disk++) {
+ if ((subdisk = devclass_get_device(ata_raid_sub_devclass,
+ config->disks[disk]))) {
+ struct ata_raid_subdisk *ars = device_get_softc(subdisk);
+
+ ars->raid[rdp->volume] = rdp;
+ ars->disk_number[rdp->volume] = disk;
+ }
+ }
+ ata_raid_attach(rdp, 1);
+ ata_raid_arrays[array] = rdp;
+ config->lun = array;
+ return 0;
+}
+
+static int
+ata_raid_delete(int array)
+{
+ struct ar_softc *rdp;
+ device_t subdisk;
+ int disk;
+
+ if (!(rdp = ata_raid_arrays[array]))
+ return ENXIO;
+
+ rdp->status &= ~AR_S_READY;
+ if (rdp->disk)
+ disk_destroy(rdp->disk);
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if ((rdp->disks[disk].flags & AR_DF_PRESENT) && rdp->disks[disk].dev) {
+ if ((subdisk = devclass_get_device(ata_raid_sub_devclass,
+ device_get_unit(rdp->disks[disk].dev)))) {
+ struct ata_raid_subdisk *ars = device_get_softc(subdisk);
+
+ if (ars->raid[rdp->volume] != rdp) /* XXX SOS */
+ device_printf(subdisk, "DOH! this disk doesn't belong\n");
+ if (ars->disk_number[rdp->volume] != disk) /* XXX SOS */
+ device_printf(subdisk, "DOH! this disk number is wrong\n");
+ ars->raid[rdp->volume] = NULL;
+ ars->disk_number[rdp->volume] = -1;
+ }
+ rdp->disks[disk].flags = 0;
+ }
+ }
+ ata_raid_wipe_metadata(rdp);
+ ata_raid_arrays[array] = NULL;
+ free(rdp, M_AR);
+ return 0;
+}
+
+static int
+ata_raid_addspare(struct ata_ioc_raid_config *config)
+{
+ struct ar_softc *rdp;
+ device_t subdisk;
+ int disk;
+
+ if (!(rdp = ata_raid_arrays[config->lun]))
+ return ENXIO;
+ if (!(rdp->status & AR_S_DEGRADED) || !(rdp->status & AR_S_READY))
+ return ENXIO;
+ if (rdp->status & AR_S_REBUILDING)
+ return EBUSY;
+ switch (rdp->type) {
+ case AR_T_RAID1:
+ case AR_T_RAID01:
+ case AR_T_RAID5:
+ for (disk = 0; disk < rdp->total_disks; disk++ ) {
+
+ if (((rdp->disks[disk].flags & (AR_DF_PRESENT | AR_DF_ONLINE)) ==
+ (AR_DF_PRESENT | AR_DF_ONLINE)) && rdp->disks[disk].dev)
+ continue;
+
+ if ((subdisk = devclass_get_device(ata_raid_sub_devclass,
+ config->disks[0] ))) {
+ struct ata_raid_subdisk *ars = device_get_softc(subdisk);
+
+ if (ars->raid[rdp->volume])
+ return EBUSY;
+
+ /* XXX SOS validate size etc etc */
+ ars->raid[rdp->volume] = rdp;
+ ars->disk_number[rdp->volume] = disk;
+ rdp->disks[disk].dev = device_get_parent(subdisk);
+ rdp->disks[disk].flags =
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_SPARE);
+
+ device_printf(rdp->disks[disk].dev,
+ "inserted into ar%d disk%d as spare\n",
+ rdp->lun, disk);
+ ata_raid_config_changed(rdp, 1);
+ return 0;
+ }
+ }
+ return ENXIO;
+
+ default:
+ return EPERM;
+ }
+}
+
+static int
+ata_raid_rebuild(int array)
+{
+ struct ar_softc *rdp;
+ int disk, count;
+
+ if (!(rdp = ata_raid_arrays[array]))
+ return ENXIO;
+ /* XXX SOS we should lock the rdp softc here */
+ if (!(rdp->status & AR_S_DEGRADED) || !(rdp->status & AR_S_READY))
+ return ENXIO;
+ if (rdp->status & AR_S_REBUILDING)
+ return EBUSY;
+
+ switch (rdp->type) {
+ case AR_T_RAID1:
+ case AR_T_RAID01:
+ case AR_T_RAID5:
+ for (count = 0, disk = 0; disk < rdp->total_disks; disk++ ) {
+ if (((rdp->disks[disk].flags &
+ (AR_DF_PRESENT|AR_DF_ASSIGNED|AR_DF_ONLINE|AR_DF_SPARE)) ==
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_SPARE)) &&
+ rdp->disks[disk].dev) {
+ count++;
+ }
+ }
+
+ if (count) {
+ rdp->rebuild_lba = 0;
+ rdp->status |= AR_S_REBUILDING;
+ return 0;
+ }
+ return EIO;
+
+ default:
+ return EPERM;
+ }
+}
+
+static int
+ata_raid_read_metadata(device_t subdisk)
+{
+ devclass_t pci_devclass = devclass_find("pci");
+ devclass_t devclass=device_get_devclass(GRANDPARENT(GRANDPARENT(subdisk)));
+
+ /* prioritize vendor native metadata layout if possible */
+ if (devclass == pci_devclass) {
+ switch (pci_get_vendor(GRANDPARENT(device_get_parent(subdisk)))) {
+ case ATA_HIGHPOINT_ID:
+ if (ata_raid_hptv3_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+ if (ata_raid_hptv2_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+ break;
+
+ case ATA_INTEL_ID:
+ if (ata_raid_intel_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+ break;
+
+ case ATA_ITE_ID:
+ if (ata_raid_ite_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+ break;
+
+ case ATA_JMICRON_ID:
+ if (ata_raid_jmicron_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+ break;
+
+ case ATA_NVIDIA_ID:
+ if (ata_raid_nvidia_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+ break;
+
+ case 0: /* XXX SOS cover up for bug in our PCI code */
+ case ATA_PROMISE_ID:
+ if (ata_raid_promise_read_meta(subdisk, ata_raid_arrays, 0))
+ return 0;
+ break;
+
+ case ATA_ATI_ID:
+ case ATA_SILICON_IMAGE_ID:
+ if (ata_raid_sii_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+ break;
+
+ case ATA_SIS_ID:
+ if (ata_raid_sis_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+ break;
+
+ case ATA_VIA_ID:
+ if (ata_raid_via_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+ break;
+ }
+ }
+
+ /* handle controllers that have multiple layout possibilities */
+ /* NOTE: the order of these are not insignificant */
+
+ /* Adaptec HostRAID */
+ if (ata_raid_adaptec_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+
+ /* LSILogic v3 and v2 */
+ if (ata_raid_lsiv3_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+ if (ata_raid_lsiv2_read_meta(subdisk, ata_raid_arrays))
+ return 0;
+
+ /* if none of the above matched, try FreeBSD native format */
+ return ata_raid_promise_read_meta(subdisk, ata_raid_arrays, 1);
+}
+
+static int
+ata_raid_write_metadata(struct ar_softc *rdp)
+{
+ switch (rdp->format) {
+ case AR_F_FREEBSD_RAID:
+ case AR_F_PROMISE_RAID:
+ return ata_raid_promise_write_meta(rdp);
+
+ case AR_F_HPTV3_RAID:
+ case AR_F_HPTV2_RAID:
+ /*
+ * always write HPT v2 metadata, the v3 BIOS knows it as well.
+ * this is handy since we cannot know what version BIOS is on there
+ */
+ return ata_raid_hptv2_write_meta(rdp);
+
+ case AR_F_INTEL_RAID:
+ return ata_raid_intel_write_meta(rdp);
+
+ case AR_F_JMICRON_RAID:
+ return ata_raid_jmicron_write_meta(rdp);
+
+ case AR_F_SIS_RAID:
+ return ata_raid_sis_write_meta(rdp);
+
+ case AR_F_VIA_RAID:
+ return ata_raid_via_write_meta(rdp);
+#if 0
+ case AR_F_HPTV3_RAID:
+ return ata_raid_hptv3_write_meta(rdp);
+
+ case AR_F_ADAPTEC_RAID:
+ return ata_raid_adaptec_write_meta(rdp);
+
+ case AR_F_ITE_RAID:
+ return ata_raid_ite_write_meta(rdp);
+
+ case AR_F_LSIV2_RAID:
+ return ata_raid_lsiv2_write_meta(rdp);
+
+ case AR_F_LSIV3_RAID:
+ return ata_raid_lsiv3_write_meta(rdp);
+
+ case AR_F_NVIDIA_RAID:
+ return ata_raid_nvidia_write_meta(rdp);
+
+ case AR_F_SII_RAID:
+ return ata_raid_sii_write_meta(rdp);
+
+#endif
+ default:
+ printf("ar%d: writing of %s metadata is NOT supported yet\n",
+ rdp->lun, ata_raid_format(rdp));
+ }
+ return -1;
+}
+
+static int
+ata_raid_wipe_metadata(struct ar_softc *rdp)
+{
+ int disk, error = 0;
+ u_int64_t lba;
+ u_int32_t size;
+ u_int8_t *meta;
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (rdp->disks[disk].dev) {
+ switch (rdp->format) {
+ case AR_F_ADAPTEC_RAID:
+ lba = ADP_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct adaptec_raid_conf);
+ break;
+
+ case AR_F_HPTV2_RAID:
+ lba = HPTV2_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct hptv2_raid_conf);
+ break;
+
+ case AR_F_HPTV3_RAID:
+ lba = HPTV3_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct hptv3_raid_conf);
+ break;
+
+ case AR_F_INTEL_RAID:
+ lba = INTEL_LBA(rdp->disks[disk].dev);
+ size = 3 * 512; /* XXX SOS */
+ break;
+
+ case AR_F_ITE_RAID:
+ lba = ITE_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct ite_raid_conf);
+ break;
+
+ case AR_F_JMICRON_RAID:
+ lba = JMICRON_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct jmicron_raid_conf);
+ break;
+
+ case AR_F_LSIV2_RAID:
+ lba = LSIV2_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct lsiv2_raid_conf);
+ break;
+
+ case AR_F_LSIV3_RAID:
+ lba = LSIV3_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct lsiv3_raid_conf);
+ break;
+
+ case AR_F_NVIDIA_RAID:
+ lba = NVIDIA_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct nvidia_raid_conf);
+ break;
+
+ case AR_F_FREEBSD_RAID:
+ case AR_F_PROMISE_RAID:
+ lba = PROMISE_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct promise_raid_conf);
+ break;
+
+ case AR_F_SII_RAID:
+ lba = SII_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct sii_raid_conf);
+ break;
+
+ case AR_F_SIS_RAID:
+ lba = SIS_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct sis_raid_conf);
+ break;
+
+ case AR_F_VIA_RAID:
+ lba = VIA_LBA(rdp->disks[disk].dev);
+ size = sizeof(struct via_raid_conf);
+ break;
+
+ default:
+ printf("ar%d: wiping of %s metadata is NOT supported yet\n",
+ rdp->lun, ata_raid_format(rdp));
+ return ENXIO;
+ }
+ if (!(meta = malloc(size, M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+ if (ata_raid_rw(rdp->disks[disk].dev, lba, meta, size,
+ ATA_R_WRITE | ATA_R_DIRECT)) {
+ device_printf(rdp->disks[disk].dev, "wipe metadata failed\n");
+ error = EIO;
+ }
+ free(meta, M_AR);
+ }
+ }
+ return error;
+}
+
+/* Adaptec HostRAID Metadata */
+static int
+ata_raid_adaptec_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct adaptec_raid_conf *meta;
+ struct ar_softc *raid;
+ int array, disk, retval = 0;
+
+ if (!(meta = (struct adaptec_raid_conf *)
+ malloc(sizeof(struct adaptec_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, ADP_LBA(parent),
+ meta, sizeof(struct adaptec_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "Adaptec read metadata failed\n");
+ goto adaptec_out;
+ }
+
+ /* check if this is a Adaptec RAID struct */
+ if (meta->magic_0 != ADP_MAGIC_0 || meta->magic_3 != ADP_MAGIC_3) {
+ if (testing || bootverbose)
+ device_printf(parent, "Adaptec check1 failed\n");
+ goto adaptec_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_adaptec_print_meta(meta);
+
+ /* now convert Adaptec metadata into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto adaptec_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->format && (raid->format != AR_F_ADAPTEC_RAID))
+ continue;
+
+ if (raid->magic_0 && raid->magic_0 != meta->configs[0].magic_0)
+ continue;
+
+ if (!meta->generation || be32toh(meta->generation) > raid->generation) {
+ switch (meta->configs[0].type) {
+ case ADP_T_RAID0:
+ raid->magic_0 = meta->configs[0].magic_0;
+ raid->type = AR_T_RAID0;
+ raid->interleave = 1 << (meta->configs[0].stripe_shift >> 1);
+ raid->width = be16toh(meta->configs[0].total_disks);
+ break;
+
+ case ADP_T_RAID1:
+ raid->magic_0 = meta->configs[0].magic_0;
+ raid->type = AR_T_RAID1;
+ raid->width = be16toh(meta->configs[0].total_disks) / 2;
+ break;
+
+ default:
+ device_printf(parent, "Adaptec unknown RAID type 0x%02x\n",
+ meta->configs[0].type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto adaptec_out;
+ }
+
+ raid->format = AR_F_ADAPTEC_RAID;
+ raid->generation = be32toh(meta->generation);
+ raid->total_disks = be16toh(meta->configs[0].total_disks);
+ raid->total_sectors = be32toh(meta->configs[0].sectors);
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = 0;
+ raid->rebuild_lba = 0;
+ raid->lun = array;
+ strncpy(raid->name, meta->configs[0].name,
+ min(sizeof(raid->name), sizeof(meta->configs[0].name)));
+
+ /* clear out any old info */
+ if (raid->generation) {
+ for (disk = 0; disk < raid->total_disks; disk++) {
+ raid->disks[disk].dev = NULL;
+ raid->disks[disk].flags = 0;
+ }
+ }
+ }
+ if (be32toh(meta->generation) >= raid->generation) {
+ struct ata_device *atadev = device_get_softc(parent);
+ struct ata_channel *ch = device_get_softc(GRANDPARENT(dev));
+ int disk_number = (ch->unit << !(ch->flags & ATA_NO_SLAVE)) +
+ ATA_DEV(atadev->unit);
+
+ raid->disks[disk_number].dev = parent;
+ raid->disks[disk_number].sectors =
+ be32toh(meta->configs[disk_number + 1].sectors);
+ raid->disks[disk_number].flags =
+ (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk_number;
+ retval = 1;
+ }
+ break;
+ }
+
+adaptec_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+/* Highpoint V2 RocketRAID Metadata */
+static int
+ata_raid_hptv2_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct hptv2_raid_conf *meta;
+ struct ar_softc *raid = NULL;
+ int array, disk_number = 0, retval = 0;
+
+ if (!(meta = (struct hptv2_raid_conf *)
+ malloc(sizeof(struct hptv2_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, HPTV2_LBA(parent),
+ meta, sizeof(struct hptv2_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "HighPoint (v2) read metadata failed\n");
+ goto hptv2_out;
+ }
+
+ /* check if this is a HighPoint v2 RAID struct */
+ if (meta->magic != HPTV2_MAGIC_OK && meta->magic != HPTV2_MAGIC_BAD) {
+ if (testing || bootverbose)
+ device_printf(parent, "HighPoint (v2) check1 failed\n");
+ goto hptv2_out;
+ }
+
+ /* is this disk defined, or an old leftover/spare ? */
+ if (!meta->magic_0) {
+ if (testing || bootverbose)
+ device_printf(parent, "HighPoint (v2) check2 failed\n");
+ goto hptv2_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_hptv2_print_meta(meta);
+
+ /* now convert HighPoint (v2) metadata into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto hptv2_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->format && (raid->format != AR_F_HPTV2_RAID))
+ continue;
+
+ switch (meta->type) {
+ case HPTV2_T_RAID0:
+ if ((meta->order & (HPTV2_O_RAID0|HPTV2_O_OK)) ==
+ (HPTV2_O_RAID0|HPTV2_O_OK))
+ goto highpoint_raid1;
+ if (meta->order & (HPTV2_O_RAID0 | HPTV2_O_RAID1))
+ goto highpoint_raid01;
+ if (raid->magic_0 && raid->magic_0 != meta->magic_0)
+ continue;
+ raid->magic_0 = meta->magic_0;
+ raid->type = AR_T_RAID0;
+ raid->interleave = 1 << meta->stripe_shift;
+ disk_number = meta->disk_number;
+ if (!(meta->order & HPTV2_O_OK))
+ meta->magic = 0; /* mark bad */
+ break;
+
+ case HPTV2_T_RAID1:
+highpoint_raid1:
+ if (raid->magic_0 && raid->magic_0 != meta->magic_0)
+ continue;
+ raid->magic_0 = meta->magic_0;
+ raid->type = AR_T_RAID1;
+ disk_number = (meta->disk_number > 0);
+ break;
+
+ case HPTV2_T_RAID01_RAID0:
+highpoint_raid01:
+ if (meta->order & HPTV2_O_RAID0) {
+ if ((raid->magic_0 && raid->magic_0 != meta->magic_0) ||
+ (raid->magic_1 && raid->magic_1 != meta->magic_1))
+ continue;
+ raid->magic_0 = meta->magic_0;
+ raid->magic_1 = meta->magic_1;
+ raid->type = AR_T_RAID01;
+ raid->interleave = 1 << meta->stripe_shift;
+ disk_number = meta->disk_number;
+ }
+ else {
+ if (raid->magic_1 && raid->magic_1 != meta->magic_1)
+ continue;
+ raid->magic_1 = meta->magic_1;
+ raid->type = AR_T_RAID01;
+ raid->interleave = 1 << meta->stripe_shift;
+ disk_number = meta->disk_number + meta->array_width;
+ if (!(meta->order & HPTV2_O_RAID1))
+ meta->magic = 0; /* mark bad */
+ }
+ break;
+
+ case HPTV2_T_SPAN:
+ if (raid->magic_0 && raid->magic_0 != meta->magic_0)
+ continue;
+ raid->magic_0 = meta->magic_0;
+ raid->type = AR_T_SPAN;
+ disk_number = meta->disk_number;
+ break;
+
+ default:
+ device_printf(parent, "Highpoint (v2) unknown RAID type 0x%02x\n",
+ meta->type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto hptv2_out;
+ }
+
+ raid->format |= AR_F_HPTV2_RAID;
+ raid->disks[disk_number].dev = parent;
+ raid->disks[disk_number].flags = (AR_DF_PRESENT | AR_DF_ASSIGNED);
+ raid->lun = array;
+ strncpy(raid->name, meta->name_1,
+ min(sizeof(raid->name), sizeof(meta->name_1)));
+ if (meta->magic == HPTV2_MAGIC_OK) {
+ raid->disks[disk_number].flags |= AR_DF_ONLINE;
+ raid->width = meta->array_width;
+ raid->total_sectors = meta->total_sectors;
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = HPTV2_LBA(parent) + 1;
+ raid->rebuild_lba = meta->rebuild_lba;
+ raid->disks[disk_number].sectors =
+ raid->total_sectors / raid->width;
+ }
+ else
+ raid->disks[disk_number].flags &= ~AR_DF_ONLINE;
+
+ if ((raid->type & AR_T_RAID0) && (raid->total_disks < raid->width))
+ raid->total_disks = raid->width;
+ if (disk_number >= raid->total_disks)
+ raid->total_disks = disk_number + 1;
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk_number;
+ retval = 1;
+ break;
+ }
+
+hptv2_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+static int
+ata_raid_hptv2_write_meta(struct ar_softc *rdp)
+{
+ struct hptv2_raid_conf *meta;
+ struct timeval timestamp;
+ int disk, error = 0;
+
+ if (!(meta = (struct hptv2_raid_conf *)
+ malloc(sizeof(struct hptv2_raid_conf), M_AR, M_NOWAIT | M_ZERO))) {
+ printf("ar%d: failed to allocate metadata storage\n", rdp->lun);
+ return ENOMEM;
+ }
+
+ microtime(&timestamp);
+ rdp->magic_0 = timestamp.tv_sec + 2;
+ rdp->magic_1 = timestamp.tv_sec;
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if ((rdp->disks[disk].flags & (AR_DF_PRESENT | AR_DF_ONLINE)) ==
+ (AR_DF_PRESENT | AR_DF_ONLINE))
+ meta->magic = HPTV2_MAGIC_OK;
+ if (rdp->disks[disk].flags & AR_DF_ASSIGNED) {
+ meta->magic_0 = rdp->magic_0;
+ if (strlen(rdp->name))
+ strncpy(meta->name_1, rdp->name, sizeof(meta->name_1));
+ else
+ strcpy(meta->name_1, "FreeBSD");
+ }
+ meta->disk_number = disk;
+
+ switch (rdp->type) {
+ case AR_T_RAID0:
+ meta->type = HPTV2_T_RAID0;
+ strcpy(meta->name_2, "RAID 0");
+ if (rdp->disks[disk].flags & AR_DF_ONLINE)
+ meta->order = HPTV2_O_OK;
+ break;
+
+ case AR_T_RAID1:
+ meta->type = HPTV2_T_RAID0;
+ strcpy(meta->name_2, "RAID 1");
+ meta->disk_number = (disk < rdp->width) ? disk : disk + 5;
+ meta->order = HPTV2_O_RAID0 | HPTV2_O_OK;
+ break;
+
+ case AR_T_RAID01:
+ meta->type = HPTV2_T_RAID01_RAID0;
+ strcpy(meta->name_2, "RAID 0+1");
+ if (rdp->disks[disk].flags & AR_DF_ONLINE) {
+ if (disk < rdp->width) {
+ meta->order = (HPTV2_O_RAID0 | HPTV2_O_RAID1);
+ meta->magic_0 = rdp->magic_0 - 1;
+ }
+ else {
+ meta->order = HPTV2_O_RAID1;
+ meta->disk_number -= rdp->width;
+ }
+ }
+ else
+ meta->magic_0 = rdp->magic_0 - 1;
+ meta->magic_1 = rdp->magic_1;
+ break;
+
+ case AR_T_SPAN:
+ meta->type = HPTV2_T_SPAN;
+ strcpy(meta->name_2, "SPAN");
+ break;
+ default:
+ free(meta, M_AR);
+ return ENODEV;
+ }
+
+ meta->array_width = rdp->width;
+ meta->stripe_shift = (rdp->width > 1) ? (ffs(rdp->interleave)-1) : 0;
+ meta->total_sectors = rdp->total_sectors;
+ meta->rebuild_lba = rdp->rebuild_lba;
+ if (testing || bootverbose)
+ ata_raid_hptv2_print_meta(meta);
+ if (rdp->disks[disk].dev) {
+ if (ata_raid_rw(rdp->disks[disk].dev,
+ HPTV2_LBA(rdp->disks[disk].dev), meta,
+ sizeof(struct promise_raid_conf),
+ ATA_R_WRITE | ATA_R_DIRECT)) {
+ device_printf(rdp->disks[disk].dev, "write metadata failed\n");
+ error = EIO;
+ }
+ }
+ }
+ free(meta, M_AR);
+ return error;
+}
+
+/* Highpoint V3 RocketRAID Metadata */
+static int
+ata_raid_hptv3_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct hptv3_raid_conf *meta;
+ struct ar_softc *raid = NULL;
+ int array, disk_number, retval = 0;
+
+ if (!(meta = (struct hptv3_raid_conf *)
+ malloc(sizeof(struct hptv3_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, HPTV3_LBA(parent),
+ meta, sizeof(struct hptv3_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "HighPoint (v3) read metadata failed\n");
+ goto hptv3_out;
+ }
+
+ /* check if this is a HighPoint v3 RAID struct */
+ if (meta->magic != HPTV3_MAGIC) {
+ if (testing || bootverbose)
+ device_printf(parent, "HighPoint (v3) check1 failed\n");
+ goto hptv3_out;
+ }
+
+ /* check if there are any config_entries */
+ if (meta->config_entries < 1) {
+ if (testing || bootverbose)
+ device_printf(parent, "HighPoint (v3) check2 failed\n");
+ goto hptv3_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_hptv3_print_meta(meta);
+
+ /* now convert HighPoint (v3) metadata into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto hptv3_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->format && (raid->format != AR_F_HPTV3_RAID))
+ continue;
+
+ if ((raid->format & AR_F_HPTV3_RAID) && raid->magic_0 != meta->magic_0)
+ continue;
+
+ switch (meta->configs[0].type) {
+ case HPTV3_T_RAID0:
+ raid->type = AR_T_RAID0;
+ raid->width = meta->configs[0].total_disks;
+ disk_number = meta->configs[0].disk_number;
+ break;
+
+ case HPTV3_T_RAID1:
+ raid->type = AR_T_RAID1;
+ raid->width = meta->configs[0].total_disks / 2;
+ disk_number = meta->configs[0].disk_number;
+ break;
+
+ case HPTV3_T_RAID5:
+ raid->type = AR_T_RAID5;
+ raid->width = meta->configs[0].total_disks;
+ disk_number = meta->configs[0].disk_number;
+ break;
+
+ case HPTV3_T_SPAN:
+ raid->type = AR_T_SPAN;
+ raid->width = meta->configs[0].total_disks;
+ disk_number = meta->configs[0].disk_number;
+ break;
+
+ default:
+ device_printf(parent, "Highpoint (v3) unknown RAID type 0x%02x\n",
+ meta->configs[0].type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto hptv3_out;
+ }
+ if (meta->config_entries == 2) {
+ switch (meta->configs[1].type) {
+ case HPTV3_T_RAID1:
+ if (raid->type == AR_T_RAID0) {
+ raid->type = AR_T_RAID01;
+ disk_number = meta->configs[1].disk_number +
+ (meta->configs[0].disk_number << 1);
+ break;
+ }
+ default:
+ device_printf(parent, "Highpoint (v3) unknown level 2 0x%02x\n",
+ meta->configs[1].type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto hptv3_out;
+ }
+ }
+
+ raid->magic_0 = meta->magic_0;
+ raid->format = AR_F_HPTV3_RAID;
+ raid->generation = meta->timestamp;
+ raid->interleave = 1 << meta->configs[0].stripe_shift;
+ raid->total_disks = meta->configs[0].total_disks +
+ meta->configs[1].total_disks;
+ raid->total_sectors = meta->configs[0].total_sectors +
+ ((u_int64_t)meta->configs_high[0].total_sectors << 32);
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = 0;
+ raid->rebuild_lba = meta->configs[0].rebuild_lba +
+ ((u_int64_t)meta->configs_high[0].rebuild_lba << 32);
+ raid->lun = array;
+ strncpy(raid->name, meta->name,
+ min(sizeof(raid->name), sizeof(meta->name)));
+ raid->disks[disk_number].sectors = raid->total_sectors /
+ (raid->type == AR_T_RAID5 ? raid->width - 1 : raid->width);
+ raid->disks[disk_number].dev = parent;
+ raid->disks[disk_number].flags =
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk_number;
+ retval = 1;
+ break;
+ }
+
+hptv3_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+/* Intel MatrixRAID Metadata */
+static int
+ata_raid_intel_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct intel_raid_conf *meta;
+ struct intel_raid_mapping *map;
+ struct ar_softc *raid = NULL;
+ u_int32_t checksum, *ptr;
+ int array, count, disk, volume = 1, retval = 0;
+ char *tmp;
+
+ if (!(meta = (struct intel_raid_conf *)
+ malloc(1536, M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, INTEL_LBA(parent), meta, 1024, ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "Intel read metadata failed\n");
+ goto intel_out;
+ }
+ tmp = (char *)meta;
+ bcopy(tmp, tmp+1024, 512);
+ bcopy(tmp+512, tmp, 1024);
+ bzero(tmp+1024, 512);
+
+ /* check if this is a Intel RAID struct */
+ if (strncmp(meta->intel_id, INTEL_MAGIC, strlen(INTEL_MAGIC))) {
+ if (testing || bootverbose)
+ device_printf(parent, "Intel check1 failed\n");
+ goto intel_out;
+ }
+
+ for (checksum = 0, ptr = (u_int32_t *)meta, count = 0;
+ count < (meta->config_size / sizeof(u_int32_t)); count++) {
+ checksum += *ptr++;
+ }
+ checksum -= meta->checksum;
+ if (checksum != meta->checksum) {
+ if (testing || bootverbose)
+ device_printf(parent, "Intel check2 failed\n");
+ goto intel_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_intel_print_meta(meta);
+
+ map = (struct intel_raid_mapping *)&meta->disk[meta->total_disks];
+
+ /* now convert Intel metadata into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto intel_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->format && (raid->format != AR_F_INTEL_RAID))
+ continue;
+
+ if ((raid->format & AR_F_INTEL_RAID) &&
+ (raid->magic_0 != meta->config_id))
+ continue;
+
+ /*
+ * update our knowledge about the array config based on generation
+ * NOTE: there can be multiple volumes on a disk set
+ */
+ if (!meta->generation || meta->generation > raid->generation) {
+ switch (map->type) {
+ case INTEL_T_RAID0:
+ raid->type = AR_T_RAID0;
+ raid->width = map->total_disks;
+ break;
+
+ case INTEL_T_RAID1:
+ if (map->total_disks == 4)
+ raid->type = AR_T_RAID01;
+ else
+ raid->type = AR_T_RAID1;
+ raid->width = map->total_disks / 2;
+ break;
+
+ case INTEL_T_RAID5:
+ raid->type = AR_T_RAID5;
+ raid->width = map->total_disks;
+ break;
+
+ default:
+ device_printf(parent, "Intel unknown RAID type 0x%02x\n",
+ map->type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto intel_out;
+ }
+
+ switch (map->status) {
+ case INTEL_S_READY:
+ raid->status = AR_S_READY;
+ break;
+ case INTEL_S_DEGRADED:
+ raid->status |= AR_S_DEGRADED;
+ break;
+ case INTEL_S_DISABLED:
+ case INTEL_S_FAILURE:
+ raid->status = 0;
+ }
+
+ raid->magic_0 = meta->config_id;
+ raid->format = AR_F_INTEL_RAID;
+ raid->generation = meta->generation;
+ raid->interleave = map->stripe_sectors;
+ raid->total_disks = map->total_disks;
+ raid->total_sectors = map->total_sectors;
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = map->offset;
+ raid->rebuild_lba = 0;
+ raid->lun = array;
+ raid->volume = volume - 1;
+ strncpy(raid->name, map->name,
+ min(sizeof(raid->name), sizeof(map->name)));
+
+ /* clear out any old info */
+ for (disk = 0; disk < raid->total_disks; disk++) {
+ raid->disks[disk].dev = NULL;
+ bcopy(meta->disk[map->disk_idx[disk]].serial,
+ raid->disks[disk].serial,
+ sizeof(raid->disks[disk].serial));
+ raid->disks[disk].sectors =
+ meta->disk[map->disk_idx[disk]].sectors;
+ raid->disks[disk].flags = 0;
+ if (meta->disk[map->disk_idx[disk]].flags & INTEL_F_ONLINE)
+ raid->disks[disk].flags |= AR_DF_ONLINE;
+ if (meta->disk[map->disk_idx[disk]].flags & INTEL_F_ASSIGNED)
+ raid->disks[disk].flags |= AR_DF_ASSIGNED;
+ if (meta->disk[map->disk_idx[disk]].flags & INTEL_F_SPARE) {
+ raid->disks[disk].flags &= ~(AR_DF_ONLINE | AR_DF_ASSIGNED);
+ raid->disks[disk].flags |= AR_DF_SPARE;
+ }
+ if (meta->disk[map->disk_idx[disk]].flags & INTEL_F_DOWN)
+ raid->disks[disk].flags &= ~AR_DF_ONLINE;
+ }
+ }
+ if (meta->generation >= raid->generation) {
+ for (disk = 0; disk < raid->total_disks; disk++) {
+ struct ata_device *atadev = device_get_softc(parent);
+
+ if (!strncmp(raid->disks[disk].serial, atadev->param.serial,
+ sizeof(raid->disks[disk].serial))) {
+ raid->disks[disk].dev = parent;
+ raid->disks[disk].flags |= (AR_DF_PRESENT | AR_DF_ONLINE);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk;
+ retval = 1;
+ }
+ }
+ }
+ else
+ goto intel_out;
+
+ if (retval) {
+ if (volume < meta->total_volumes) {
+ map = (struct intel_raid_mapping *)
+ &map->disk_idx[map->total_disks];
+ volume++;
+ retval = 0;
+ continue;
+ }
+ break;
+ }
+ else {
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ if (volume == 2)
+ retval = 1;
+ }
+ }
+
+intel_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+static int
+ata_raid_intel_write_meta(struct ar_softc *rdp)
+{
+ struct intel_raid_conf *meta;
+ struct intel_raid_mapping *map;
+ struct timeval timestamp;
+ u_int32_t checksum, *ptr;
+ int count, disk, error = 0;
+ char *tmp;
+
+ if (!(meta = (struct intel_raid_conf *)
+ malloc(1536, M_AR, M_NOWAIT | M_ZERO))) {
+ printf("ar%d: failed to allocate metadata storage\n", rdp->lun);
+ return ENOMEM;
+ }
+
+ rdp->generation++;
+ microtime(&timestamp);
+
+ bcopy(INTEL_MAGIC, meta->intel_id, sizeof(meta->intel_id));
+ bcopy(INTEL_VERSION_1100, meta->version, sizeof(meta->version));
+ meta->config_id = timestamp.tv_sec;
+ meta->generation = rdp->generation;
+ meta->total_disks = rdp->total_disks;
+ meta->total_volumes = 1; /* XXX SOS */
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (rdp->disks[disk].dev) {
+ struct ata_channel *ch =
+ device_get_softc(device_get_parent(rdp->disks[disk].dev));
+ struct ata_device *atadev =
+ device_get_softc(rdp->disks[disk].dev);
+
+ bcopy(atadev->param.serial, meta->disk[disk].serial,
+ sizeof(rdp->disks[disk].serial));
+ meta->disk[disk].sectors = rdp->disks[disk].sectors;
+ meta->disk[disk].id = (ch->unit << 16) | ATA_DEV(atadev->unit);
+ }
+ else
+ meta->disk[disk].sectors = rdp->total_sectors / rdp->width;
+ meta->disk[disk].flags = 0;
+ if (rdp->disks[disk].flags & AR_DF_SPARE)
+ meta->disk[disk].flags |= INTEL_F_SPARE;
+ else {
+ if (rdp->disks[disk].flags & AR_DF_ONLINE)
+ meta->disk[disk].flags |= INTEL_F_ONLINE;
+ else
+ meta->disk[disk].flags |= INTEL_F_DOWN;
+ if (rdp->disks[disk].flags & AR_DF_ASSIGNED)
+ meta->disk[disk].flags |= INTEL_F_ASSIGNED;
+ }
+ }
+ map = (struct intel_raid_mapping *)&meta->disk[meta->total_disks];
+
+ bcopy(rdp->name, map->name, sizeof(rdp->name));
+ map->total_sectors = rdp->total_sectors;
+ map->state = 12; /* XXX SOS */
+ map->offset = rdp->offset_sectors;
+ map->stripe_count = rdp->total_sectors / (rdp->interleave*rdp->total_disks);
+ map->stripe_sectors = rdp->interleave;
+ map->disk_sectors = rdp->total_sectors / rdp->width;
+ map->status = INTEL_S_READY; /* XXX SOS */
+ switch (rdp->type) {
+ case AR_T_RAID0:
+ map->type = INTEL_T_RAID0;
+ break;
+ case AR_T_RAID1:
+ map->type = INTEL_T_RAID1;
+ break;
+ case AR_T_RAID01:
+ map->type = INTEL_T_RAID1;
+ break;
+ case AR_T_RAID5:
+ map->type = INTEL_T_RAID5;
+ break;
+ default:
+ free(meta, M_AR);
+ return ENODEV;
+ }
+ map->total_disks = rdp->total_disks;
+ map->magic[0] = 0x02;
+ map->magic[1] = 0xff;
+ map->magic[2] = 0x01;
+ for (disk = 0; disk < rdp->total_disks; disk++)
+ map->disk_idx[disk] = disk;
+
+ meta->config_size = (char *)&map->disk_idx[disk] - (char *)meta;
+ for (checksum = 0, ptr = (u_int32_t *)meta, count = 0;
+ count < (meta->config_size / sizeof(u_int32_t)); count++) {
+ checksum += *ptr++;
+ }
+ meta->checksum = checksum;
+
+ if (testing || bootverbose)
+ ata_raid_intel_print_meta(meta);
+
+ tmp = (char *)meta;
+ bcopy(tmp, tmp+1024, 512);
+ bcopy(tmp+512, tmp, 1024);
+ bzero(tmp+1024, 512);
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (rdp->disks[disk].dev) {
+ if (ata_raid_rw(rdp->disks[disk].dev,
+ INTEL_LBA(rdp->disks[disk].dev),
+ meta, 1024, ATA_R_WRITE | ATA_R_DIRECT)) {
+ device_printf(rdp->disks[disk].dev, "write metadata failed\n");
+ error = EIO;
+ }
+ }
+ }
+ free(meta, M_AR);
+ return error;
+}
+
+
+/* Integrated Technology Express Metadata */
+static int
+ata_raid_ite_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct ite_raid_conf *meta;
+ struct ar_softc *raid = NULL;
+ int array, disk_number, count, retval = 0;
+ u_int16_t *ptr;
+
+ if (!(meta = (struct ite_raid_conf *)
+ malloc(sizeof(struct ite_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, ITE_LBA(parent),
+ meta, sizeof(struct ite_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "ITE read metadata failed\n");
+ goto ite_out;
+ }
+
+ /* check if this is a ITE RAID struct */
+ for (ptr = (u_int16_t *)meta->ite_id, count = 0;
+ count < sizeof(meta->ite_id)/sizeof(uint16_t); count++)
+ ptr[count] = be16toh(ptr[count]);
+
+ if (strncmp(meta->ite_id, ITE_MAGIC, strlen(ITE_MAGIC))) {
+ if (testing || bootverbose)
+ device_printf(parent, "ITE check1 failed\n");
+ goto ite_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_ite_print_meta(meta);
+
+ /* now convert ITE metadata into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if ((raid = raidp[array])) {
+ if (raid->format != AR_F_ITE_RAID)
+ continue;
+ if (raid->magic_0 != *((u_int64_t *)meta->timestamp_0))
+ continue;
+ }
+
+ /* if we dont have a disks timestamp the RAID is invalidated */
+ if (*((u_int64_t *)meta->timestamp_1) == 0)
+ goto ite_out;
+
+ if (!raid) {
+ raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc),
+ M_AR, M_NOWAIT | M_ZERO);
+ if (!(raid = raidp[array])) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto ite_out;
+ }
+ }
+
+ switch (meta->type) {
+ case ITE_T_RAID0:
+ raid->type = AR_T_RAID0;
+ raid->width = meta->array_width;
+ raid->total_disks = meta->array_width;
+ disk_number = meta->disk_number;
+ break;
+
+ case ITE_T_RAID1:
+ raid->type = AR_T_RAID1;
+ raid->width = 1;
+ raid->total_disks = 2;
+ disk_number = meta->disk_number;
+ break;
+
+ case ITE_T_RAID01:
+ raid->type = AR_T_RAID01;
+ raid->width = meta->array_width;
+ raid->total_disks = 4;
+ disk_number = ((meta->disk_number & 0x02) >> 1) |
+ ((meta->disk_number & 0x01) << 1);
+ break;
+
+ case ITE_T_SPAN:
+ raid->type = AR_T_SPAN;
+ raid->width = 1;
+ raid->total_disks = meta->array_width;
+ disk_number = meta->disk_number;
+ break;
+
+ default:
+ device_printf(parent, "ITE unknown RAID type 0x%02x\n", meta->type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto ite_out;
+ }
+
+ raid->magic_0 = *((u_int64_t *)meta->timestamp_0);
+ raid->format = AR_F_ITE_RAID;
+ raid->generation = 0;
+ raid->interleave = meta->stripe_sectors;
+ raid->total_sectors = meta->total_sectors;
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = 0;
+ raid->rebuild_lba = 0;
+ raid->lun = array;
+
+ raid->disks[disk_number].dev = parent;
+ raid->disks[disk_number].sectors = raid->total_sectors / raid->width;
+ raid->disks[disk_number].flags =
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk_number;
+ retval = 1;
+ break;
+ }
+ite_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+/* JMicron Technology Corp Metadata */
+static int
+ata_raid_jmicron_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct jmicron_raid_conf *meta;
+ struct ar_softc *raid = NULL;
+ u_int16_t checksum, *ptr;
+ u_int64_t disk_size;
+ int count, array, disk, total_disks, retval = 0;
+
+ if (!(meta = (struct jmicron_raid_conf *)
+ malloc(sizeof(struct jmicron_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, JMICRON_LBA(parent),
+ meta, sizeof(struct jmicron_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent,
+ "JMicron read metadata failed\n");
+ }
+
+ /* check for JMicron signature */
+ if (strncmp(meta->signature, JMICRON_MAGIC, 2)) {
+ if (testing || bootverbose)
+ device_printf(parent, "JMicron check1 failed\n");
+ goto jmicron_out;
+ }
+
+ /* calculate checksum and compare for valid */
+ for (checksum = 0, ptr = (u_int16_t *)meta, count = 0; count < 64; count++)
+ checksum += *ptr++;
+ if (checksum) {
+ if (testing || bootverbose)
+ device_printf(parent, "JMicron check2 failed\n");
+ goto jmicron_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_jmicron_print_meta(meta);
+
+ /* now convert JMicron meta into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+jmicron_next:
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto jmicron_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->format && (raid->format != AR_F_JMICRON_RAID))
+ continue;
+
+ for (total_disks = 0, disk = 0; disk < JM_MAX_DISKS; disk++) {
+ if (meta->disks[disk]) {
+ if (raid->format == AR_F_JMICRON_RAID) {
+ if (bcmp(&meta->disks[disk],
+ raid->disks[disk].serial, sizeof(u_int32_t))) {
+ array++;
+ goto jmicron_next;
+ }
+ }
+ else
+ bcopy(&meta->disks[disk],
+ raid->disks[disk].serial, sizeof(u_int32_t));
+ total_disks++;
+ }
+ }
+ /* handle spares XXX SOS */
+
+ switch (meta->type) {
+ case JM_T_RAID0:
+ raid->type = AR_T_RAID0;
+ raid->width = total_disks;
+ break;
+
+ case JM_T_RAID1:
+ raid->type = AR_T_RAID1;
+ raid->width = 1;
+ break;
+
+ case JM_T_RAID01:
+ raid->type = AR_T_RAID01;
+ raid->width = total_disks / 2;
+ break;
+
+ case JM_T_RAID5:
+ raid->type = AR_T_RAID5;
+ raid->width = total_disks;
+ break;
+
+ case JM_T_JBOD:
+ raid->type = AR_T_SPAN;
+ raid->width = 1;
+ break;
+
+ default:
+ device_printf(parent,
+ "JMicron unknown RAID type 0x%02x\n", meta->type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto jmicron_out;
+ }
+ disk_size = (meta->disk_sectors_high << 16) + meta->disk_sectors_low;
+ raid->format = AR_F_JMICRON_RAID;
+ strncpy(raid->name, meta->name, sizeof(meta->name));
+ raid->generation = 0;
+ raid->interleave = 2 << meta->stripe_shift;
+ raid->total_disks = total_disks;
+ raid->total_sectors = disk_size * (raid->width-(raid->type==AR_RAID5));
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = meta->offset * 16;
+ raid->rebuild_lba = 0;
+ raid->lun = array;
+
+ for (disk = 0; disk < raid->total_disks; disk++) {
+ if (meta->disks[disk] == meta->disk_id) {
+ raid->disks[disk].dev = parent;
+ raid->disks[disk].sectors = disk_size;
+ raid->disks[disk].flags =
+ (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk;
+ retval = 1;
+ break;
+ }
+ }
+ break;
+ }
+jmicron_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+static int
+ata_raid_jmicron_write_meta(struct ar_softc *rdp)
+{
+ struct jmicron_raid_conf *meta;
+ u_int64_t disk_sectors;
+ int disk, error = 0;
+
+ if (!(meta = (struct jmicron_raid_conf *)
+ malloc(sizeof(struct jmicron_raid_conf), M_AR, M_NOWAIT | M_ZERO))) {
+ printf("ar%d: failed to allocate metadata storage\n", rdp->lun);
+ return ENOMEM;
+ }
+
+ rdp->generation++;
+ switch (rdp->type) {
+ case AR_T_JBOD:
+ meta->type = JM_T_JBOD;
+ break;
+
+ case AR_T_RAID0:
+ meta->type = JM_T_RAID0;
+ break;
+
+ case AR_T_RAID1:
+ meta->type = JM_T_RAID1;
+ break;
+
+ case AR_T_RAID5:
+ meta->type = JM_T_RAID5;
+ break;
+
+ case AR_T_RAID01:
+ meta->type = JM_T_RAID01;
+ break;
+
+ default:
+ free(meta, M_AR);
+ return ENODEV;
+ }
+ bcopy(JMICRON_MAGIC, meta->signature, sizeof(JMICRON_MAGIC));
+ meta->version = JMICRON_VERSION;
+ meta->offset = rdp->offset_sectors / 16;
+ disk_sectors = rdp->total_sectors / (rdp->width - (rdp->type == AR_RAID5));
+ meta->disk_sectors_low = disk_sectors & 0xffff;
+ meta->disk_sectors_high = disk_sectors >> 16;
+ strncpy(meta->name, rdp->name, sizeof(meta->name));
+ meta->stripe_shift = ffs(rdp->interleave) - 2;
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (rdp->disks[disk].serial[0])
+ bcopy(rdp->disks[disk].serial,&meta->disks[disk],sizeof(u_int32_t));
+ else
+ meta->disks[disk] = (u_int32_t)(uintptr_t)rdp->disks[disk].dev;
+ }
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (rdp->disks[disk].dev) {
+ u_int16_t checksum = 0, *ptr;
+ int count;
+
+ meta->disk_id = meta->disks[disk];
+ meta->checksum = 0;
+ for (ptr = (u_int16_t *)meta, count = 0; count < 64; count++)
+ checksum += *ptr++;
+ meta->checksum -= checksum;
+
+ if (testing || bootverbose)
+ ata_raid_jmicron_print_meta(meta);
+
+ if (ata_raid_rw(rdp->disks[disk].dev,
+ JMICRON_LBA(rdp->disks[disk].dev),
+ meta, sizeof(struct jmicron_raid_conf),
+ ATA_R_WRITE | ATA_R_DIRECT)) {
+ device_printf(rdp->disks[disk].dev, "write metadata failed\n");
+ error = EIO;
+ }
+ }
+ }
+ /* handle spares XXX SOS */
+
+ free(meta, M_AR);
+ return error;
+}
+
+/* LSILogic V2 MegaRAID Metadata */
+static int
+ata_raid_lsiv2_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct lsiv2_raid_conf *meta;
+ struct ar_softc *raid = NULL;
+ int array, retval = 0;
+
+ if (!(meta = (struct lsiv2_raid_conf *)
+ malloc(sizeof(struct lsiv2_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, LSIV2_LBA(parent),
+ meta, sizeof(struct lsiv2_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "LSI (v2) read metadata failed\n");
+ goto lsiv2_out;
+ }
+
+ /* check if this is a LSI RAID struct */
+ if (strncmp(meta->lsi_id, LSIV2_MAGIC, strlen(LSIV2_MAGIC))) {
+ if (testing || bootverbose)
+ device_printf(parent, "LSI (v2) check1 failed\n");
+ goto lsiv2_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_lsiv2_print_meta(meta);
+
+ /* now convert LSI (v2) config meta into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ int raid_entry, conf_entry;
+
+ if (!raidp[array + meta->raid_number]) {
+ raidp[array + meta->raid_number] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array + meta->raid_number]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto lsiv2_out;
+ }
+ }
+ raid = raidp[array + meta->raid_number];
+ if (raid->format && (raid->format != AR_F_LSIV2_RAID))
+ continue;
+
+ if (raid->magic_0 &&
+ ((raid->magic_0 != meta->timestamp) ||
+ (raid->magic_1 != meta->raid_number)))
+ continue;
+
+ array += meta->raid_number;
+
+ raid_entry = meta->raid_number;
+ conf_entry = (meta->configs[raid_entry].raid.config_offset >> 4) +
+ meta->disk_number - 1;
+
+ switch (meta->configs[raid_entry].raid.type) {
+ case LSIV2_T_RAID0:
+ raid->magic_0 = meta->timestamp;
+ raid->magic_1 = meta->raid_number;
+ raid->type = AR_T_RAID0;
+ raid->interleave = meta->configs[raid_entry].raid.stripe_sectors;
+ raid->width = meta->configs[raid_entry].raid.array_width;
+ break;
+
+ case LSIV2_T_RAID1:
+ raid->magic_0 = meta->timestamp;
+ raid->magic_1 = meta->raid_number;
+ raid->type = AR_T_RAID1;
+ raid->width = meta->configs[raid_entry].raid.array_width;
+ break;
+
+ case LSIV2_T_RAID0 | LSIV2_T_RAID1:
+ raid->magic_0 = meta->timestamp;
+ raid->magic_1 = meta->raid_number;
+ raid->type = AR_T_RAID01;
+ raid->interleave = meta->configs[raid_entry].raid.stripe_sectors;
+ raid->width = meta->configs[raid_entry].raid.array_width;
+ break;
+
+ default:
+ device_printf(parent, "LSI v2 unknown RAID type 0x%02x\n",
+ meta->configs[raid_entry].raid.type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto lsiv2_out;
+ }
+
+ raid->format = AR_F_LSIV2_RAID;
+ raid->generation = 0;
+ raid->total_disks = meta->configs[raid_entry].raid.disk_count;
+ raid->total_sectors = meta->configs[raid_entry].raid.total_sectors;
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = 0;
+ raid->rebuild_lba = 0;
+ raid->lun = array;
+
+ if (meta->configs[conf_entry].disk.device != LSIV2_D_NONE) {
+ raid->disks[meta->disk_number].dev = parent;
+ raid->disks[meta->disk_number].sectors =
+ meta->configs[conf_entry].disk.disk_sectors;
+ raid->disks[meta->disk_number].flags =
+ (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = meta->disk_number;
+ retval = 1;
+ }
+ else
+ raid->disks[meta->disk_number].flags &= ~AR_DF_ONLINE;
+
+ break;
+ }
+
+lsiv2_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+/* LSILogic V3 MegaRAID Metadata */
+static int
+ata_raid_lsiv3_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct lsiv3_raid_conf *meta;
+ struct ar_softc *raid = NULL;
+ u_int8_t checksum, *ptr;
+ int array, entry, count, disk_number, retval = 0;
+
+ if (!(meta = (struct lsiv3_raid_conf *)
+ malloc(sizeof(struct lsiv3_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, LSIV3_LBA(parent),
+ meta, sizeof(struct lsiv3_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "LSI (v3) read metadata failed\n");
+ goto lsiv3_out;
+ }
+
+ /* check if this is a LSI RAID struct */
+ if (strncmp(meta->lsi_id, LSIV3_MAGIC, strlen(LSIV3_MAGIC))) {
+ if (testing || bootverbose)
+ device_printf(parent, "LSI (v3) check1 failed\n");
+ goto lsiv3_out;
+ }
+
+ /* check if the checksum is OK */
+ for (checksum = 0, ptr = meta->lsi_id, count = 0; count < 512; count++)
+ checksum += *ptr++;
+ if (checksum) {
+ if (testing || bootverbose)
+ device_printf(parent, "LSI (v3) check2 failed\n");
+ goto lsiv3_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_lsiv3_print_meta(meta);
+
+ /* now convert LSI (v3) config meta into our generic form */
+ for (array = 0, entry = 0; array < MAX_ARRAYS && entry < 8;) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto lsiv3_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->format && (raid->format != AR_F_LSIV3_RAID)) {
+ array++;
+ continue;
+ }
+
+ if ((raid->format == AR_F_LSIV3_RAID) &&
+ (raid->magic_0 != meta->timestamp)) {
+ array++;
+ continue;
+ }
+
+ switch (meta->raid[entry].total_disks) {
+ case 0:
+ entry++;
+ continue;
+ case 1:
+ if (meta->raid[entry].device == meta->device) {
+ disk_number = 0;
+ break;
+ }
+ if (raid->format)
+ array++;
+ entry++;
+ continue;
+ case 2:
+ disk_number = (meta->device & (LSIV3_D_DEVICE|LSIV3_D_CHANNEL))?1:0;
+ break;
+ default:
+ device_printf(parent, "lsiv3 > 2 disk support untested!!\n");
+ disk_number = (meta->device & LSIV3_D_DEVICE ? 1 : 0) +
+ (meta->device & LSIV3_D_CHANNEL ? 2 : 0);
+ break;
+ }
+
+ switch (meta->raid[entry].type) {
+ case LSIV3_T_RAID0:
+ raid->type = AR_T_RAID0;
+ raid->width = meta->raid[entry].total_disks;
+ break;
+
+ case LSIV3_T_RAID1:
+ raid->type = AR_T_RAID1;
+ raid->width = meta->raid[entry].array_width;
+ break;
+
+ default:
+ device_printf(parent, "LSI v3 unknown RAID type 0x%02x\n",
+ meta->raid[entry].type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ entry++;
+ continue;
+ }
+
+ raid->magic_0 = meta->timestamp;
+ raid->format = AR_F_LSIV3_RAID;
+ raid->generation = 0;
+ raid->interleave = meta->raid[entry].stripe_pages * 8;
+ raid->total_disks = meta->raid[entry].total_disks;
+ raid->total_sectors = raid->width * meta->raid[entry].sectors;
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = meta->raid[entry].offset;
+ raid->rebuild_lba = 0;
+ raid->lun = array;
+
+ raid->disks[disk_number].dev = parent;
+ raid->disks[disk_number].sectors = raid->total_sectors / raid->width;
+ raid->disks[disk_number].flags =
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk_number;
+ retval = 1;
+ entry++;
+ array++;
+ }
+
+lsiv3_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+/* nVidia MediaShield Metadata */
+static int
+ata_raid_nvidia_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct nvidia_raid_conf *meta;
+ struct ar_softc *raid = NULL;
+ u_int32_t checksum, *ptr;
+ int array, count, retval = 0;
+
+ if (!(meta = (struct nvidia_raid_conf *)
+ malloc(sizeof(struct nvidia_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, NVIDIA_LBA(parent),
+ meta, sizeof(struct nvidia_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "nVidia read metadata failed\n");
+ goto nvidia_out;
+ }
+
+ /* check if this is a nVidia RAID struct */
+ if (strncmp(meta->nvidia_id, NV_MAGIC, strlen(NV_MAGIC))) {
+ if (testing || bootverbose)
+ device_printf(parent, "nVidia check1 failed\n");
+ goto nvidia_out;
+ }
+
+ /* check if the checksum is OK */
+ for (checksum = 0, ptr = (u_int32_t*)meta, count = 0;
+ count < meta->config_size; count++)
+ checksum += *ptr++;
+ if (checksum) {
+ if (testing || bootverbose)
+ device_printf(parent, "nVidia check2 failed\n");
+ goto nvidia_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_nvidia_print_meta(meta);
+
+ /* now convert nVidia meta into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto nvidia_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->format && (raid->format != AR_F_NVIDIA_RAID))
+ continue;
+
+ if (raid->format == AR_F_NVIDIA_RAID &&
+ ((raid->magic_0 != meta->magic_1) ||
+ (raid->magic_1 != meta->magic_2))) {
+ continue;
+ }
+
+ switch (meta->type) {
+ case NV_T_SPAN:
+ raid->type = AR_T_SPAN;
+ break;
+
+ case NV_T_RAID0:
+ raid->type = AR_T_RAID0;
+ break;
+
+ case NV_T_RAID1:
+ raid->type = AR_T_RAID1;
+ break;
+
+ case NV_T_RAID5:
+ raid->type = AR_T_RAID5;
+ break;
+
+ case NV_T_RAID01:
+ raid->type = AR_T_RAID01;
+ break;
+
+ default:
+ device_printf(parent, "nVidia unknown RAID type 0x%02x\n",
+ meta->type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto nvidia_out;
+ }
+ raid->magic_0 = meta->magic_1;
+ raid->magic_1 = meta->magic_2;
+ raid->format = AR_F_NVIDIA_RAID;
+ raid->generation = 0;
+ raid->interleave = meta->stripe_sectors;
+ raid->width = meta->array_width;
+ raid->total_disks = meta->total_disks;
+ raid->total_sectors = meta->total_sectors;
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = 0;
+ raid->rebuild_lba = meta->rebuild_lba;
+ raid->lun = array;
+ raid->status = AR_S_READY;
+ if (meta->status & NV_S_DEGRADED)
+ raid->status |= AR_S_DEGRADED;
+
+ raid->disks[meta->disk_number].dev = parent;
+ raid->disks[meta->disk_number].sectors =
+ raid->total_sectors / raid->width;
+ raid->disks[meta->disk_number].flags =
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = meta->disk_number;
+ retval = 1;
+ break;
+ }
+
+nvidia_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+/* Promise FastTrak Metadata */
+static int
+ata_raid_promise_read_meta(device_t dev, struct ar_softc **raidp, int native)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct promise_raid_conf *meta;
+ struct ar_softc *raid;
+ u_int32_t checksum, *ptr;
+ int array, count, disk, disksum = 0, retval = 0;
+
+ if (!(meta = (struct promise_raid_conf *)
+ malloc(sizeof(struct promise_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, PROMISE_LBA(parent),
+ meta, sizeof(struct promise_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "%s read metadata failed\n",
+ native ? "FreeBSD" : "Promise");
+ goto promise_out;
+ }
+
+ /* check the signature */
+ if (native) {
+ if (strncmp(meta->promise_id, ATA_MAGIC, strlen(ATA_MAGIC))) {
+ if (testing || bootverbose)
+ device_printf(parent, "FreeBSD check1 failed\n");
+ goto promise_out;
+ }
+ }
+ else {
+ if (strncmp(meta->promise_id, PR_MAGIC, strlen(PR_MAGIC))) {
+ if (testing || bootverbose)
+ device_printf(parent, "Promise check1 failed\n");
+ goto promise_out;
+ }
+ }
+
+ /* check if the checksum is OK */
+ for (checksum = 0, ptr = (u_int32_t *)meta, count = 0; count < 511; count++)
+ checksum += *ptr++;
+ if (checksum != *ptr) {
+ if (testing || bootverbose)
+ device_printf(parent, "%s check2 failed\n",
+ native ? "FreeBSD" : "Promise");
+ goto promise_out;
+ }
+
+ /* check on disk integrity status */
+ if (meta->raid.integrity != PR_I_VALID) {
+ if (testing || bootverbose)
+ device_printf(parent, "%s check3 failed\n",
+ native ? "FreeBSD" : "Promise");
+ goto promise_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_promise_print_meta(meta);
+
+ /* now convert Promise metadata into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto promise_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->format &&
+ (raid->format != (native ? AR_F_FREEBSD_RAID : AR_F_PROMISE_RAID)))
+ continue;
+
+ if ((raid->format == (native ? AR_F_FREEBSD_RAID : AR_F_PROMISE_RAID))&&
+ !(meta->raid.magic_1 == (raid->magic_1)))
+ continue;
+
+ /* update our knowledge about the array config based on generation */
+ if (!meta->raid.generation || meta->raid.generation > raid->generation){
+ switch (meta->raid.type) {
+ case PR_T_SPAN:
+ raid->type = AR_T_SPAN;
+ break;
+
+ case PR_T_JBOD:
+ raid->type = AR_T_JBOD;
+ break;
+
+ case PR_T_RAID0:
+ raid->type = AR_T_RAID0;
+ break;
+
+ case PR_T_RAID1:
+ raid->type = AR_T_RAID1;
+ if (meta->raid.array_width > 1)
+ raid->type = AR_T_RAID01;
+ break;
+
+ case PR_T_RAID5:
+ raid->type = AR_T_RAID5;
+ break;
+
+ default:
+ device_printf(parent, "%s unknown RAID type 0x%02x\n",
+ native ? "FreeBSD" : "Promise", meta->raid.type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto promise_out;
+ }
+ raid->magic_1 = meta->raid.magic_1;
+ raid->format = (native ? AR_F_FREEBSD_RAID : AR_F_PROMISE_RAID);
+ raid->generation = meta->raid.generation;
+ raid->interleave = 1 << meta->raid.stripe_shift;
+ raid->width = meta->raid.array_width;
+ raid->total_disks = meta->raid.total_disks;
+ raid->heads = meta->raid.heads + 1;
+ raid->sectors = meta->raid.sectors;
+ raid->cylinders = meta->raid.cylinders + 1;
+ raid->total_sectors = meta->raid.total_sectors;
+ raid->offset_sectors = 0;
+ raid->rebuild_lba = meta->raid.rebuild_lba;
+ raid->lun = array;
+ if ((meta->raid.status &
+ (PR_S_VALID | PR_S_ONLINE | PR_S_INITED | PR_S_READY)) ==
+ (PR_S_VALID | PR_S_ONLINE | PR_S_INITED | PR_S_READY)) {
+ raid->status |= AR_S_READY;
+ if (meta->raid.status & PR_S_DEGRADED)
+ raid->status |= AR_S_DEGRADED;
+ }
+ else
+ raid->status &= ~AR_S_READY;
+
+ /* convert disk flags to our internal types */
+ for (disk = 0; disk < meta->raid.total_disks; disk++) {
+ raid->disks[disk].dev = NULL;
+ raid->disks[disk].flags = 0;
+ *((u_int64_t *)(raid->disks[disk].serial)) =
+ meta->raid.disk[disk].magic_0;
+ disksum += meta->raid.disk[disk].flags;
+ if (meta->raid.disk[disk].flags & PR_F_ONLINE)
+ raid->disks[disk].flags |= AR_DF_ONLINE;
+ if (meta->raid.disk[disk].flags & PR_F_ASSIGNED)
+ raid->disks[disk].flags |= AR_DF_ASSIGNED;
+ if (meta->raid.disk[disk].flags & PR_F_SPARE) {
+ raid->disks[disk].flags &= ~(AR_DF_ONLINE | AR_DF_ASSIGNED);
+ raid->disks[disk].flags |= AR_DF_SPARE;
+ }
+ if (meta->raid.disk[disk].flags & (PR_F_REDIR | PR_F_DOWN))
+ raid->disks[disk].flags &= ~AR_DF_ONLINE;
+ }
+ if (!disksum) {
+ device_printf(parent, "%s subdisks has no flags\n",
+ native ? "FreeBSD" : "Promise");
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto promise_out;
+ }
+ }
+ if (meta->raid.generation >= raid->generation) {
+ int disk_number = meta->raid.disk_number;
+
+ if (raid->disks[disk_number].flags && (meta->magic_0 ==
+ *((u_int64_t *)(raid->disks[disk_number].serial)))) {
+ raid->disks[disk_number].dev = parent;
+ raid->disks[disk_number].flags |= AR_DF_PRESENT;
+ raid->disks[disk_number].sectors = meta->raid.disk_sectors;
+ if ((raid->disks[disk_number].flags &
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE)) ==
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE)) {
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk_number;
+ retval = 1;
+ }
+ }
+ }
+ break;
+ }
+
+promise_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+static int
+ata_raid_promise_write_meta(struct ar_softc *rdp)
+{
+ struct promise_raid_conf *meta;
+ struct timeval timestamp;
+ u_int32_t *ckptr;
+ int count, disk, drive, error = 0;
+
+ if (!(meta = (struct promise_raid_conf *)
+ malloc(sizeof(struct promise_raid_conf), M_AR, M_NOWAIT))) {
+ printf("ar%d: failed to allocate metadata storage\n", rdp->lun);
+ return ENOMEM;
+ }
+
+ rdp->generation++;
+ microtime(&timestamp);
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ for (count = 0; count < sizeof(struct promise_raid_conf); count++)
+ *(((u_int8_t *)meta) + count) = 255 - (count % 256);
+ meta->dummy_0 = 0x00020000;
+ meta->raid.disk_number = disk;
+
+ if (rdp->disks[disk].dev) {
+ struct ata_device *atadev = device_get_softc(rdp->disks[disk].dev);
+ struct ata_channel *ch =
+ device_get_softc(device_get_parent(rdp->disks[disk].dev));
+
+ meta->raid.channel = ch->unit;
+ meta->raid.device = ATA_DEV(atadev->unit);
+ meta->raid.disk_sectors = rdp->disks[disk].sectors;
+ meta->raid.disk_offset = rdp->offset_sectors;
+ }
+ else {
+ meta->raid.channel = 0;
+ meta->raid.device = 0;
+ meta->raid.disk_sectors = 0;
+ meta->raid.disk_offset = 0;
+ }
+ meta->magic_0 = PR_MAGIC0(meta->raid) | timestamp.tv_sec;
+ meta->magic_1 = timestamp.tv_sec >> 16;
+ meta->magic_2 = timestamp.tv_sec;
+ meta->raid.integrity = PR_I_VALID;
+ meta->raid.magic_0 = meta->magic_0;
+ meta->raid.rebuild_lba = rdp->rebuild_lba;
+ meta->raid.generation = rdp->generation;
+
+ if (rdp->status & AR_S_READY) {
+ meta->raid.flags = (PR_F_VALID | PR_F_ASSIGNED | PR_F_ONLINE);
+ meta->raid.status =
+ (PR_S_VALID | PR_S_ONLINE | PR_S_INITED | PR_S_READY);
+ if (rdp->status & AR_S_DEGRADED)
+ meta->raid.status |= PR_S_DEGRADED;
+ else
+ meta->raid.status |= PR_S_FUNCTIONAL;
+ }
+ else {
+ meta->raid.flags = PR_F_DOWN;
+ meta->raid.status = 0;
+ }
+
+ switch (rdp->type) {
+ case AR_T_RAID0:
+ meta->raid.type = PR_T_RAID0;
+ break;
+ case AR_T_RAID1:
+ meta->raid.type = PR_T_RAID1;
+ break;
+ case AR_T_RAID01:
+ meta->raid.type = PR_T_RAID1;
+ break;
+ case AR_T_RAID5:
+ meta->raid.type = PR_T_RAID5;
+ break;
+ case AR_T_SPAN:
+ meta->raid.type = PR_T_SPAN;
+ break;
+ case AR_T_JBOD:
+ meta->raid.type = PR_T_JBOD;
+ break;
+ default:
+ free(meta, M_AR);
+ return ENODEV;
+ }
+
+ meta->raid.total_disks = rdp->total_disks;
+ meta->raid.stripe_shift = ffs(rdp->interleave) - 1;
+ meta->raid.array_width = rdp->width;
+ meta->raid.array_number = rdp->lun;
+ meta->raid.total_sectors = rdp->total_sectors;
+ meta->raid.cylinders = rdp->cylinders - 1;
+ meta->raid.heads = rdp->heads - 1;
+ meta->raid.sectors = rdp->sectors;
+ meta->raid.magic_1 = (u_int64_t)meta->magic_2<<16 | meta->magic_1;
+
+ bzero(&meta->raid.disk, 8 * 12);
+ for (drive = 0; drive < rdp->total_disks; drive++) {
+ meta->raid.disk[drive].flags = 0;
+ if (rdp->disks[drive].flags & AR_DF_PRESENT)
+ meta->raid.disk[drive].flags |= PR_F_VALID;
+ if (rdp->disks[drive].flags & AR_DF_ASSIGNED)
+ meta->raid.disk[drive].flags |= PR_F_ASSIGNED;
+ if (rdp->disks[drive].flags & AR_DF_ONLINE)
+ meta->raid.disk[drive].flags |= PR_F_ONLINE;
+ else
+ if (rdp->disks[drive].flags & AR_DF_PRESENT)
+ meta->raid.disk[drive].flags = (PR_F_REDIR | PR_F_DOWN);
+ if (rdp->disks[drive].flags & AR_DF_SPARE)
+ meta->raid.disk[drive].flags |= PR_F_SPARE;
+ meta->raid.disk[drive].dummy_0 = 0x0;
+ if (rdp->disks[drive].dev) {
+ struct ata_channel *ch =
+ device_get_softc(device_get_parent(rdp->disks[drive].dev));
+ struct ata_device *atadev =
+ device_get_softc(rdp->disks[drive].dev);
+
+ meta->raid.disk[drive].channel = ch->unit;
+ meta->raid.disk[drive].device = ATA_DEV(atadev->unit);
+ }
+ meta->raid.disk[drive].magic_0 =
+ PR_MAGIC0(meta->raid.disk[drive]) | timestamp.tv_sec;
+ }
+
+ if (rdp->disks[disk].dev) {
+ if ((rdp->disks[disk].flags & (AR_DF_PRESENT | AR_DF_ONLINE)) ==
+ (AR_DF_PRESENT | AR_DF_ONLINE)) {
+ if (rdp->format == AR_F_FREEBSD_RAID)
+ bcopy(ATA_MAGIC, meta->promise_id, sizeof(ATA_MAGIC));
+ else
+ bcopy(PR_MAGIC, meta->promise_id, sizeof(PR_MAGIC));
+ }
+ else
+ bzero(meta->promise_id, sizeof(meta->promise_id));
+ meta->checksum = 0;
+ for (ckptr = (int32_t *)meta, count = 0; count < 511; count++)
+ meta->checksum += *ckptr++;
+ if (testing || bootverbose)
+ ata_raid_promise_print_meta(meta);
+ if (ata_raid_rw(rdp->disks[disk].dev,
+ PROMISE_LBA(rdp->disks[disk].dev),
+ meta, sizeof(struct promise_raid_conf),
+ ATA_R_WRITE | ATA_R_DIRECT)) {
+ device_printf(rdp->disks[disk].dev, "write metadata failed\n");
+ error = EIO;
+ }
+ }
+ }
+ free(meta, M_AR);
+ return error;
+}
+
+/* Silicon Image Medley Metadata */
+static int
+ata_raid_sii_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct sii_raid_conf *meta;
+ struct ar_softc *raid = NULL;
+ u_int16_t checksum, *ptr;
+ int array, count, disk, retval = 0;
+
+ if (!(meta = (struct sii_raid_conf *)
+ malloc(sizeof(struct sii_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, SII_LBA(parent),
+ meta, sizeof(struct sii_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "Silicon Image read metadata failed\n");
+ goto sii_out;
+ }
+
+ /* check if this is a Silicon Image (Medley) RAID struct */
+ for (checksum = 0, ptr = (u_int16_t *)meta, count = 0; count < 160; count++)
+ checksum += *ptr++;
+ if (checksum) {
+ if (testing || bootverbose)
+ device_printf(parent, "Silicon Image check1 failed\n");
+ goto sii_out;
+ }
+
+ for (checksum = 0, ptr = (u_int16_t *)meta, count = 0; count < 256; count++)
+ checksum += *ptr++;
+ if (checksum != meta->checksum_1) {
+ if (testing || bootverbose)
+ device_printf(parent, "Silicon Image check2 failed\n");
+ goto sii_out;
+ }
+
+ /* check verison */
+ if (meta->version_major != 0x0002 ||
+ (meta->version_minor != 0x0000 && meta->version_minor != 0x0001)) {
+ if (testing || bootverbose)
+ device_printf(parent, "Silicon Image check3 failed\n");
+ goto sii_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_sii_print_meta(meta);
+
+ /* now convert Silicon Image meta into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto sii_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->format && (raid->format != AR_F_SII_RAID))
+ continue;
+
+ if (raid->format == AR_F_SII_RAID &&
+ (raid->magic_0 != *((u_int64_t *)meta->timestamp))) {
+ continue;
+ }
+
+ /* update our knowledge about the array config based on generation */
+ if (!meta->generation || meta->generation > raid->generation) {
+ switch (meta->type) {
+ case SII_T_RAID0:
+ raid->type = AR_T_RAID0;
+ break;
+
+ case SII_T_RAID1:
+ raid->type = AR_T_RAID1;
+ break;
+
+ case SII_T_RAID01:
+ raid->type = AR_T_RAID01;
+ break;
+
+ case SII_T_SPARE:
+ device_printf(parent, "Silicon Image SPARE disk\n");
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto sii_out;
+
+ default:
+ device_printf(parent,"Silicon Image unknown RAID type 0x%02x\n",
+ meta->type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto sii_out;
+ }
+ raid->magic_0 = *((u_int64_t *)meta->timestamp);
+ raid->format = AR_F_SII_RAID;
+ raid->generation = meta->generation;
+ raid->interleave = meta->stripe_sectors;
+ raid->width = (meta->raid0_disks != 0xff) ? meta->raid0_disks : 1;
+ raid->total_disks =
+ ((meta->raid0_disks != 0xff) ? meta->raid0_disks : 0) +
+ ((meta->raid1_disks != 0xff) ? meta->raid1_disks : 0);
+ raid->total_sectors = meta->total_sectors;
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = 0;
+ raid->rebuild_lba = meta->rebuild_lba;
+ raid->lun = array;
+ strncpy(raid->name, meta->name,
+ min(sizeof(raid->name), sizeof(meta->name)));
+
+ /* clear out any old info */
+ if (raid->generation) {
+ for (disk = 0; disk < raid->total_disks; disk++) {
+ raid->disks[disk].dev = NULL;
+ raid->disks[disk].flags = 0;
+ }
+ }
+ }
+ if (meta->generation >= raid->generation) {
+ /* XXX SOS add check for the right physical disk by serial# */
+ if (meta->status & SII_S_READY) {
+ int disk_number = (raid->type == AR_T_RAID01) ?
+ meta->raid1_ident + (meta->raid0_ident << 1) :
+ meta->disk_number;
+
+ raid->disks[disk_number].dev = parent;
+ raid->disks[disk_number].sectors =
+ raid->total_sectors / raid->width;
+ raid->disks[disk_number].flags =
+ (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk_number;
+ retval = 1;
+ }
+ }
+ break;
+ }
+
+sii_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+/* Silicon Integrated Systems Metadata */
+static int
+ata_raid_sis_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct sis_raid_conf *meta;
+ struct ar_softc *raid = NULL;
+ int array, disk_number, drive, retval = 0;
+
+ if (!(meta = (struct sis_raid_conf *)
+ malloc(sizeof(struct sis_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, SIS_LBA(parent),
+ meta, sizeof(struct sis_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent,
+ "Silicon Integrated Systems read metadata failed\n");
+ }
+
+ /* check for SiS magic */
+ if (meta->magic != SIS_MAGIC) {
+ if (testing || bootverbose)
+ device_printf(parent,
+ "Silicon Integrated Systems check1 failed\n");
+ goto sis_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_sis_print_meta(meta);
+
+ /* now convert SiS meta into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto sis_out;
+ }
+ }
+
+ raid = raidp[array];
+ if (raid->format && (raid->format != AR_F_SIS_RAID))
+ continue;
+
+ if ((raid->format == AR_F_SIS_RAID) &&
+ ((raid->magic_0 != meta->controller_pci_id) ||
+ (raid->magic_1 != meta->timestamp))) {
+ continue;
+ }
+
+ switch (meta->type_total_disks & SIS_T_MASK) {
+ case SIS_T_JBOD:
+ raid->type = AR_T_JBOD;
+ raid->width = (meta->type_total_disks & SIS_D_MASK);
+ raid->total_sectors += SIS_LBA(parent);
+ break;
+
+ case SIS_T_RAID0:
+ raid->type = AR_T_RAID0;
+ raid->width = (meta->type_total_disks & SIS_D_MASK);
+ if (!raid->total_sectors ||
+ (raid->total_sectors > (raid->width * SIS_LBA(parent))))
+ raid->total_sectors = raid->width * SIS_LBA(parent);
+ break;
+
+ case SIS_T_RAID1:
+ raid->type = AR_T_RAID1;
+ raid->width = 1;
+ if (!raid->total_sectors || (raid->total_sectors > SIS_LBA(parent)))
+ raid->total_sectors = SIS_LBA(parent);
+ break;
+
+ default:
+ device_printf(parent, "Silicon Integrated Systems "
+ "unknown RAID type 0x%08x\n", meta->magic);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto sis_out;
+ }
+ raid->magic_0 = meta->controller_pci_id;
+ raid->magic_1 = meta->timestamp;
+ raid->format = AR_F_SIS_RAID;
+ raid->generation = 0;
+ raid->interleave = meta->stripe_sectors;
+ raid->total_disks = (meta->type_total_disks & SIS_D_MASK);
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = 0;
+ raid->rebuild_lba = 0;
+ raid->lun = array;
+ /* XXX SOS if total_disks > 2 this doesn't float */
+ if (((meta->disks & SIS_D_MASTER) >> 4) == meta->disk_number)
+ disk_number = 0;
+ else
+ disk_number = 1;
+
+ for (drive = 0; drive < raid->total_disks; drive++) {
+ raid->disks[drive].sectors = raid->total_sectors/raid->width;
+ if (drive == disk_number) {
+ raid->disks[disk_number].dev = parent;
+ raid->disks[disk_number].flags =
+ (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk_number;
+ }
+ }
+ retval = 1;
+ break;
+ }
+
+sis_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+static int
+ata_raid_sis_write_meta(struct ar_softc *rdp)
+{
+ struct sis_raid_conf *meta;
+ struct timeval timestamp;
+ int disk, error = 0;
+
+ if (!(meta = (struct sis_raid_conf *)
+ malloc(sizeof(struct sis_raid_conf), M_AR, M_NOWAIT | M_ZERO))) {
+ printf("ar%d: failed to allocate metadata storage\n", rdp->lun);
+ return ENOMEM;
+ }
+
+ rdp->generation++;
+ microtime(&timestamp);
+
+ meta->magic = SIS_MAGIC;
+ /* XXX SOS if total_disks > 2 this doesn't float */
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (rdp->disks[disk].dev) {
+ struct ata_channel *ch =
+ device_get_softc(device_get_parent(rdp->disks[disk].dev));
+ struct ata_device *atadev = device_get_softc(rdp->disks[disk].dev);
+ int disk_number = 1 + ATA_DEV(atadev->unit) + (ch->unit << 1);
+
+ meta->disks |= disk_number << ((1 - disk) << 2);
+ }
+ }
+ switch (rdp->type) {
+ case AR_T_JBOD:
+ meta->type_total_disks = SIS_T_JBOD;
+ break;
+
+ case AR_T_RAID0:
+ meta->type_total_disks = SIS_T_RAID0;
+ break;
+
+ case AR_T_RAID1:
+ meta->type_total_disks = SIS_T_RAID1;
+ break;
+
+ default:
+ free(meta, M_AR);
+ return ENODEV;
+ }
+ meta->type_total_disks |= (rdp->total_disks & SIS_D_MASK);
+ meta->stripe_sectors = rdp->interleave;
+ meta->timestamp = timestamp.tv_sec;
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (rdp->disks[disk].dev) {
+ struct ata_channel *ch =
+ device_get_softc(device_get_parent(rdp->disks[disk].dev));
+ struct ata_device *atadev = device_get_softc(rdp->disks[disk].dev);
+
+ meta->controller_pci_id =
+ (pci_get_vendor(GRANDPARENT(rdp->disks[disk].dev)) << 16) |
+ pci_get_device(GRANDPARENT(rdp->disks[disk].dev));
+ bcopy(atadev->param.model, meta->model, sizeof(meta->model));
+
+ /* XXX SOS if total_disks > 2 this may not float */
+ meta->disk_number = 1 + ATA_DEV(atadev->unit) + (ch->unit << 1);
+
+ if (testing || bootverbose)
+ ata_raid_sis_print_meta(meta);
+
+ if (ata_raid_rw(rdp->disks[disk].dev,
+ SIS_LBA(rdp->disks[disk].dev),
+ meta, sizeof(struct sis_raid_conf),
+ ATA_R_WRITE | ATA_R_DIRECT)) {
+ device_printf(rdp->disks[disk].dev, "write metadata failed\n");
+ error = EIO;
+ }
+ }
+ }
+ free(meta, M_AR);
+ return error;
+}
+
+/* VIA Tech V-RAID Metadata */
+static int
+ata_raid_via_read_meta(device_t dev, struct ar_softc **raidp)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ device_t parent = device_get_parent(dev);
+ struct via_raid_conf *meta;
+ struct ar_softc *raid = NULL;
+ u_int8_t checksum, *ptr;
+ int array, count, disk, retval = 0;
+
+ if (!(meta = (struct via_raid_conf *)
+ malloc(sizeof(struct via_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+
+ if (ata_raid_rw(parent, VIA_LBA(parent),
+ meta, sizeof(struct via_raid_conf), ATA_R_READ)) {
+ if (testing || bootverbose)
+ device_printf(parent, "VIA read metadata failed\n");
+ goto via_out;
+ }
+
+ /* check if this is a VIA RAID struct */
+ if (meta->magic != VIA_MAGIC) {
+ if (testing || bootverbose)
+ device_printf(parent, "VIA check1 failed\n");
+ goto via_out;
+ }
+
+ /* calculate checksum and compare for valid */
+ for (checksum = 0, ptr = (u_int8_t *)meta, count = 0; count < 50; count++)
+ checksum += *ptr++;
+ if (checksum != meta->checksum) {
+ if (testing || bootverbose)
+ device_printf(parent, "VIA check2 failed\n");
+ goto via_out;
+ }
+
+ if (testing || bootverbose)
+ ata_raid_via_print_meta(meta);
+
+ /* now convert VIA meta into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ device_printf(parent, "failed to allocate metadata storage\n");
+ goto via_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->format && (raid->format != AR_F_VIA_RAID))
+ continue;
+
+ if (raid->format == AR_F_VIA_RAID && (raid->magic_0 != meta->disks[0]))
+ continue;
+
+ switch (meta->type & VIA_T_MASK) {
+ case VIA_T_RAID0:
+ raid->type = AR_T_RAID0;
+ raid->width = meta->stripe_layout & VIA_L_DISKS;
+ if (!raid->total_sectors ||
+ (raid->total_sectors > (raid->width * meta->disk_sectors)))
+ raid->total_sectors = raid->width * meta->disk_sectors;
+ break;
+
+ case VIA_T_RAID1:
+ raid->type = AR_T_RAID1;
+ raid->width = 1;
+ raid->total_sectors = meta->disk_sectors;
+ break;
+
+ case VIA_T_RAID01:
+ raid->type = AR_T_RAID01;
+ raid->width = meta->stripe_layout & VIA_L_DISKS;
+ if (!raid->total_sectors ||
+ (raid->total_sectors > (raid->width * meta->disk_sectors)))
+ raid->total_sectors = raid->width * meta->disk_sectors;
+ break;
+
+ case VIA_T_RAID5:
+ raid->type = AR_T_RAID5;
+ raid->width = meta->stripe_layout & VIA_L_DISKS;
+ if (!raid->total_sectors ||
+ (raid->total_sectors > ((raid->width - 1)*meta->disk_sectors)))
+ raid->total_sectors = (raid->width - 1) * meta->disk_sectors;
+ break;
+
+ case VIA_T_SPAN:
+ raid->type = AR_T_SPAN;
+ raid->width = 1;
+ raid->total_sectors += meta->disk_sectors;
+ break;
+
+ default:
+ device_printf(parent,"VIA unknown RAID type 0x%02x\n", meta->type);
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto via_out;
+ }
+ raid->magic_0 = meta->disks[0];
+ raid->format = AR_F_VIA_RAID;
+ raid->generation = 0;
+ raid->interleave =
+ 0x08 << ((meta->stripe_layout & VIA_L_MASK) >> VIA_L_SHIFT);
+ for (count = 0, disk = 0; disk < 8; disk++)
+ if (meta->disks[disk])
+ count++;
+ raid->total_disks = count;
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = raid->total_sectors / (63 * 255);
+ raid->offset_sectors = 0;
+ raid->rebuild_lba = 0;
+ raid->lun = array;
+
+ for (disk = 0; disk < raid->total_disks; disk++) {
+ if (meta->disks[disk] == meta->disk_id) {
+ raid->disks[disk].dev = parent;
+ bcopy(&meta->disk_id, raid->disks[disk].serial,
+ sizeof(u_int32_t));
+ raid->disks[disk].sectors = meta->disk_sectors;
+ raid->disks[disk].flags =
+ (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED);
+ ars->raid[raid->volume] = raid;
+ ars->disk_number[raid->volume] = disk;
+ retval = 1;
+ break;
+ }
+ }
+ break;
+ }
+
+via_out:
+ free(meta, M_AR);
+ return retval;
+}
+
+static int
+ata_raid_via_write_meta(struct ar_softc *rdp)
+{
+ struct via_raid_conf *meta;
+ int disk, error = 0;
+
+ if (!(meta = (struct via_raid_conf *)
+ malloc(sizeof(struct via_raid_conf), M_AR, M_NOWAIT | M_ZERO))) {
+ printf("ar%d: failed to allocate metadata storage\n", rdp->lun);
+ return ENOMEM;
+ }
+
+ rdp->generation++;
+
+ meta->magic = VIA_MAGIC;
+ meta->dummy_0 = 0x02;
+ switch (rdp->type) {
+ case AR_T_SPAN:
+ meta->type = VIA_T_SPAN;
+ meta->stripe_layout = (rdp->total_disks & VIA_L_DISKS);
+ break;
+
+ case AR_T_RAID0:
+ meta->type = VIA_T_RAID0;
+ meta->stripe_layout = ((rdp->interleave >> 1) & VIA_L_MASK);
+ meta->stripe_layout |= (rdp->total_disks & VIA_L_DISKS);
+ break;
+
+ case AR_T_RAID1:
+ meta->type = VIA_T_RAID1;
+ meta->stripe_layout = (rdp->total_disks & VIA_L_DISKS);
+ break;
+
+ case AR_T_RAID5:
+ meta->type = VIA_T_RAID5;
+ meta->stripe_layout = ((rdp->interleave >> 1) & VIA_L_MASK);
+ meta->stripe_layout |= (rdp->total_disks & VIA_L_DISKS);
+ break;
+
+ case AR_T_RAID01:
+ meta->type = VIA_T_RAID01;
+ meta->stripe_layout = ((rdp->interleave >> 1) & VIA_L_MASK);
+ meta->stripe_layout |= (rdp->width & VIA_L_DISKS);
+ break;
+
+ default:
+ free(meta, M_AR);
+ return ENODEV;
+ }
+ meta->type |= VIA_T_BOOTABLE; /* XXX SOS */
+ meta->disk_sectors =
+ rdp->total_sectors / (rdp->width - (rdp->type == AR_RAID5));
+ for (disk = 0; disk < rdp->total_disks; disk++)
+ meta->disks[disk] = (u_int32_t)(uintptr_t)rdp->disks[disk].dev;
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (rdp->disks[disk].dev) {
+ u_int8_t *ptr;
+ int count;
+
+ meta->disk_index = disk * sizeof(u_int32_t);
+ if (rdp->type == AR_T_RAID01)
+ meta->disk_index = ((meta->disk_index & 0x08) << 2) |
+ (meta->disk_index & ~0x08);
+ meta->disk_id = meta->disks[disk];
+ meta->checksum = 0;
+ for (ptr = (u_int8_t *)meta, count = 0; count < 50; count++)
+ meta->checksum += *ptr++;
+
+ if (testing || bootverbose)
+ ata_raid_via_print_meta(meta);
+
+ if (ata_raid_rw(rdp->disks[disk].dev,
+ VIA_LBA(rdp->disks[disk].dev),
+ meta, sizeof(struct via_raid_conf),
+ ATA_R_WRITE | ATA_R_DIRECT)) {
+ device_printf(rdp->disks[disk].dev, "write metadata failed\n");
+ error = EIO;
+ }
+ }
+ }
+ free(meta, M_AR);
+ return error;
+}
+
+static struct ata_request *
+ata_raid_init_request(struct ar_softc *rdp, struct bio *bio)
+{
+ struct ata_request *request;
+
+ if (!(request = ata_alloc_request())) {
+ printf("FAILURE - out of memory in ata_raid_init_request\n");
+ return NULL;
+ }
+ request->timeout = 5;
+ request->retries = 2;
+ request->callback = ata_raid_done;
+ request->driver = rdp;
+ request->bio = bio;
+ switch (request->bio->bio_cmd) {
+ case BIO_READ:
+ request->flags = ATA_R_READ;
+ break;
+ case BIO_WRITE:
+ request->flags = ATA_R_WRITE;
+ break;
+ }
+ return request;
+}
+
+static int
+ata_raid_send_request(struct ata_request *request)
+{
+ struct ata_device *atadev = device_get_softc(request->dev);
+
+ request->transfersize = min(request->bytecount, atadev->max_iosize);
+ if (request->flags & ATA_R_READ) {
+ if (atadev->mode >= ATA_DMA) {
+ request->flags |= ATA_R_DMA;
+ request->u.ata.command = ATA_READ_DMA;
+ }
+ else if (atadev->max_iosize > DEV_BSIZE)
+ request->u.ata.command = ATA_READ_MUL;
+ else
+ request->u.ata.command = ATA_READ;
+ }
+ else if (request->flags & ATA_R_WRITE) {
+ if (atadev->mode >= ATA_DMA) {
+ request->flags |= ATA_R_DMA;
+ request->u.ata.command = ATA_WRITE_DMA;
+ }
+ else if (atadev->max_iosize > DEV_BSIZE)
+ request->u.ata.command = ATA_WRITE_MUL;
+ else
+ request->u.ata.command = ATA_WRITE;
+ }
+ else {
+ device_printf(request->dev, "FAILURE - unknown IO operation\n");
+ ata_free_request(request);
+ return EIO;
+ }
+ request->flags |= (ATA_R_ORDERED | ATA_R_THREAD);
+ ata_queue_request(request);
+ return 0;
+}
+
+static int
+ata_raid_rw(device_t dev, u_int64_t lba, void *data, u_int bcount, int flags)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ata_request *request;
+ int error;
+
+ if (bcount % DEV_BSIZE) {
+ device_printf(dev, "FAILURE - transfers must be modulo sectorsize\n");
+ return ENOMEM;
+ }
+
+ if (!(request = ata_alloc_request())) {
+ device_printf(dev, "FAILURE - out of memory in ata_raid_rw\n");
+ return ENOMEM;
+ }
+
+ /* setup request */
+ request->dev = dev;
+ request->timeout = 10;
+ request->retries = 0;
+ request->data = data;
+ request->bytecount = bcount;
+ request->transfersize = DEV_BSIZE;
+ request->u.ata.lba = lba;
+ request->u.ata.count = request->bytecount / DEV_BSIZE;
+ request->flags = flags;
+
+ if (flags & ATA_R_READ) {
+ if (atadev->mode >= ATA_DMA) {
+ request->u.ata.command = ATA_READ_DMA;
+ request->flags |= ATA_R_DMA;
+ }
+ else
+ request->u.ata.command = ATA_READ;
+ ata_queue_request(request);
+ }
+ else if (flags & ATA_R_WRITE) {
+ if (atadev->mode >= ATA_DMA) {
+ request->u.ata.command = ATA_WRITE_DMA;
+ request->flags |= ATA_R_DMA;
+ }
+ else
+ request->u.ata.command = ATA_WRITE;
+ ata_queue_request(request);
+ }
+ else {
+ device_printf(dev, "FAILURE - unknown IO operation\n");
+ request->result = EIO;
+ }
+ error = request->result;
+ ata_free_request(request);
+ return error;
+}
+
+/*
+ * module handeling
+ */
+static int
+ata_raid_subdisk_probe(device_t dev)
+{
+ device_quiet(dev);
+ return 0;
+}
+
+static int
+ata_raid_subdisk_attach(device_t dev)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ int volume;
+
+ for (volume = 0; volume < MAX_VOLUMES; volume++) {
+ ars->raid[volume] = NULL;
+ ars->disk_number[volume] = -1;
+ }
+ ata_raid_read_metadata(dev);
+ return 0;
+}
+
+static int
+ata_raid_subdisk_detach(device_t dev)
+{
+ struct ata_raid_subdisk *ars = device_get_softc(dev);
+ int volume;
+
+ for (volume = 0; volume < MAX_VOLUMES; volume++) {
+ if (ars->raid[volume]) {
+ ars->raid[volume]->disks[ars->disk_number[volume]].flags &=
+ ~(AR_DF_PRESENT | AR_DF_ONLINE);
+ ars->raid[volume]->disks[ars->disk_number[volume]].dev = NULL;
+ if (mtx_initialized(&ars->raid[volume]->lock))
+ ata_raid_config_changed(ars->raid[volume], 1);
+ ars->raid[volume] = NULL;
+ ars->disk_number[volume] = -1;
+ }
+ }
+ return 0;
+}
+
+static device_method_t ata_raid_sub_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_raid_subdisk_probe),
+ DEVMETHOD(device_attach, ata_raid_subdisk_attach),
+ DEVMETHOD(device_detach, ata_raid_subdisk_detach),
+ { 0, 0 }
+};
+
+static driver_t ata_raid_sub_driver = {
+ "subdisk",
+ ata_raid_sub_methods,
+ sizeof(struct ata_raid_subdisk)
+};
+
+DRIVER_MODULE(subdisk, ad, ata_raid_sub_driver, ata_raid_sub_devclass, NULL, NULL);
+
+static int
+ata_raid_module_event_handler(module_t mod, int what, void *arg)
+{
+ int i;
+
+ switch (what) {
+ case MOD_LOAD:
+ if (testing || bootverbose)
+ printf("ATA PseudoRAID loaded\n");
+#if 0
+ /* setup table to hold metadata for all ATA PseudoRAID arrays */
+ ata_raid_arrays = malloc(sizeof(struct ar_soft *) * MAX_ARRAYS,
+ M_AR, M_NOWAIT | M_ZERO);
+ if (!ata_raid_arrays) {
+ printf("ataraid: no memory for metadata storage\n");
+ return ENOMEM;
+ }
+#endif
+ /* attach found PseudoRAID arrays */
+ for (i = 0; i < MAX_ARRAYS; i++) {
+ struct ar_softc *rdp = ata_raid_arrays[i];
+
+ if (!rdp || !rdp->format)
+ continue;
+ if (testing || bootverbose)
+ ata_raid_print_meta(rdp);
+ ata_raid_attach(rdp, 0);
+ }
+ ata_raid_ioctl_func = ata_raid_ioctl;
+ return 0;
+
+ case MOD_UNLOAD:
+ /* detach found PseudoRAID arrays */
+ for (i = 0; i < MAX_ARRAYS; i++) {
+ struct ar_softc *rdp = ata_raid_arrays[i];
+
+ if (!rdp || !rdp->status)
+ continue;
+ if (mtx_initialized(&rdp->lock))
+ mtx_destroy(&rdp->lock);
+ if (rdp->disk)
+ disk_destroy(rdp->disk);
+ }
+ if (testing || bootverbose)
+ printf("ATA PseudoRAID unloaded\n");
+#if 0
+ free(ata_raid_arrays, M_AR);
+#endif
+ ata_raid_ioctl_func = NULL;
+ return 0;
+
+ default:
+ return EOPNOTSUPP;
+ }
+}
+
+static moduledata_t ata_raid_moduledata =
+ { "ataraid", ata_raid_module_event_handler, NULL };
+DECLARE_MODULE(ata, ata_raid_moduledata, SI_SUB_RAID, SI_ORDER_FIRST);
+MODULE_VERSION(ataraid, 1);
+MODULE_DEPEND(ataraid, ata, 1, 1, 1);
+MODULE_DEPEND(ataraid, ad, 1, 1, 1);
+
+static char *
+ata_raid_format(struct ar_softc *rdp)
+{
+ switch (rdp->format) {
+ case AR_F_FREEBSD_RAID: return "FreeBSD PseudoRAID";
+ case AR_F_ADAPTEC_RAID: return "Adaptec HostRAID";
+ case AR_F_HPTV2_RAID: return "HighPoint v2 RocketRAID";
+ case AR_F_HPTV3_RAID: return "HighPoint v3 RocketRAID";
+ case AR_F_INTEL_RAID: return "Intel MatrixRAID";
+ case AR_F_ITE_RAID: return "Integrated Technology Express";
+ case AR_F_JMICRON_RAID: return "JMicron Technology Corp";
+ case AR_F_LSIV2_RAID: return "LSILogic v2 MegaRAID";
+ case AR_F_LSIV3_RAID: return "LSILogic v3 MegaRAID";
+ case AR_F_NVIDIA_RAID: return "nVidia MediaShield";
+ case AR_F_PROMISE_RAID: return "Promise Fasttrak";
+ case AR_F_SII_RAID: return "Silicon Image Medley";
+ case AR_F_SIS_RAID: return "Silicon Integrated Systems";
+ case AR_F_VIA_RAID: return "VIA Tech V-RAID";
+ default: return "UNKNOWN";
+ }
+}
+
+static char *
+ata_raid_type(struct ar_softc *rdp)
+{
+ switch (rdp->type) {
+ case AR_T_JBOD: return "JBOD";
+ case AR_T_SPAN: return "SPAN";
+ case AR_T_RAID0: return "RAID0";
+ case AR_T_RAID1: return "RAID1";
+ case AR_T_RAID3: return "RAID3";
+ case AR_T_RAID4: return "RAID4";
+ case AR_T_RAID5: return "RAID5";
+ case AR_T_RAID01: return "RAID0+1";
+ default: return "UNKNOWN";
+ }
+}
+
+static char *
+ata_raid_flags(struct ar_softc *rdp)
+{
+ switch (rdp->status & (AR_S_READY | AR_S_DEGRADED | AR_S_REBUILDING)) {
+ case AR_S_READY: return "READY";
+ case AR_S_READY | AR_S_DEGRADED: return "DEGRADED";
+ case AR_S_READY | AR_S_REBUILDING:
+ case AR_S_READY | AR_S_DEGRADED | AR_S_REBUILDING: return "REBUILDING";
+ default: return "BROKEN";
+ }
+}
+
+/* debugging gunk */
+static void
+ata_raid_print_meta(struct ar_softc *raid)
+{
+ int i;
+
+ printf("********** ATA PseudoRAID ar%d Metadata **********\n", raid->lun);
+ printf("=================================================\n");
+ printf("format %s\n", ata_raid_format(raid));
+ printf("type %s\n", ata_raid_type(raid));
+ printf("flags 0x%02x %b\n", raid->status, raid->status,
+ "\20\3REBUILDING\2DEGRADED\1READY\n");
+ printf("magic_0 0x%016jx\n", raid->magic_0);
+ printf("magic_1 0x%016jx\n",raid->magic_1);
+ printf("generation %u\n", raid->generation);
+ printf("total_sectors %ju\n", raid->total_sectors);
+ printf("offset_sectors %ju\n", raid->offset_sectors);
+ printf("heads %u\n", raid->heads);
+ printf("sectors %u\n", raid->sectors);
+ printf("cylinders %u\n", raid->cylinders);
+ printf("width %u\n", raid->width);
+ printf("interleave %u\n", raid->interleave);
+ printf("total_disks %u\n", raid->total_disks);
+ for (i = 0; i < raid->total_disks; i++) {
+ printf(" disk %d: flags = 0x%02x %b\n", i, raid->disks[i].flags,
+ raid->disks[i].flags, "\20\4ONLINE\3SPARE\2ASSIGNED\1PRESENT\n");
+ if (raid->disks[i].dev) {
+ printf(" ");
+ device_printf(raid->disks[i].dev, " sectors %jd\n",
+ raid->disks[i].sectors);
+ }
+ }
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_adaptec_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case ADP_T_RAID0: return "RAID0";
+ case ADP_T_RAID1: return "RAID1";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_adaptec_print_meta(struct adaptec_raid_conf *meta)
+{
+ int i;
+
+ printf("********* ATA Adaptec HostRAID Metadata *********\n");
+ printf("magic_0 <0x%08x>\n", be32toh(meta->magic_0));
+ printf("generation 0x%08x\n", be32toh(meta->generation));
+ printf("dummy_0 0x%04x\n", be16toh(meta->dummy_0));
+ printf("total_configs %u\n", be16toh(meta->total_configs));
+ printf("dummy_1 0x%04x\n", be16toh(meta->dummy_1));
+ printf("checksum 0x%04x\n", be16toh(meta->checksum));
+ printf("dummy_2 0x%08x\n", be32toh(meta->dummy_2));
+ printf("dummy_3 0x%08x\n", be32toh(meta->dummy_3));
+ printf("flags 0x%08x\n", be32toh(meta->flags));
+ printf("timestamp 0x%08x\n", be32toh(meta->timestamp));
+ printf("dummy_4 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ be32toh(meta->dummy_4[0]), be32toh(meta->dummy_4[1]),
+ be32toh(meta->dummy_4[2]), be32toh(meta->dummy_4[3]));
+ printf("dummy_5 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ be32toh(meta->dummy_5[0]), be32toh(meta->dummy_5[1]),
+ be32toh(meta->dummy_5[2]), be32toh(meta->dummy_5[3]));
+
+ for (i = 0; i < be16toh(meta->total_configs); i++) {
+ printf(" %d total_disks %u\n", i,
+ be16toh(meta->configs[i].disk_number));
+ printf(" %d generation %u\n", i,
+ be16toh(meta->configs[i].generation));
+ printf(" %d magic_0 0x%08x\n", i,
+ be32toh(meta->configs[i].magic_0));
+ printf(" %d dummy_0 0x%02x\n", i, meta->configs[i].dummy_0);
+ printf(" %d type %s\n", i,
+ ata_raid_adaptec_type(meta->configs[i].type));
+ printf(" %d dummy_1 0x%02x\n", i, meta->configs[i].dummy_1);
+ printf(" %d flags %d\n", i,
+ be32toh(meta->configs[i].flags));
+ printf(" %d dummy_2 0x%02x\n", i, meta->configs[i].dummy_2);
+ printf(" %d dummy_3 0x%02x\n", i, meta->configs[i].dummy_3);
+ printf(" %d dummy_4 0x%02x\n", i, meta->configs[i].dummy_4);
+ printf(" %d dummy_5 0x%02x\n", i, meta->configs[i].dummy_5);
+ printf(" %d disk_number %u\n", i,
+ be32toh(meta->configs[i].disk_number));
+ printf(" %d dummy_6 0x%08x\n", i,
+ be32toh(meta->configs[i].dummy_6));
+ printf(" %d sectors %u\n", i,
+ be32toh(meta->configs[i].sectors));
+ printf(" %d stripe_shift %u\n", i,
+ be16toh(meta->configs[i].stripe_shift));
+ printf(" %d dummy_7 0x%08x\n", i,
+ be32toh(meta->configs[i].dummy_7));
+ printf(" %d dummy_8 0x%08x 0x%08x 0x%08x 0x%08x\n", i,
+ be32toh(meta->configs[i].dummy_8[0]),
+ be32toh(meta->configs[i].dummy_8[1]),
+ be32toh(meta->configs[i].dummy_8[2]),
+ be32toh(meta->configs[i].dummy_8[3]));
+ printf(" %d name <%s>\n", i, meta->configs[i].name);
+ }
+ printf("magic_1 <0x%08x>\n", be32toh(meta->magic_1));
+ printf("magic_2 <0x%08x>\n", be32toh(meta->magic_2));
+ printf("magic_3 <0x%08x>\n", be32toh(meta->magic_3));
+ printf("magic_4 <0x%08x>\n", be32toh(meta->magic_4));
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_hptv2_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case HPTV2_T_RAID0: return "RAID0";
+ case HPTV2_T_RAID1: return "RAID1";
+ case HPTV2_T_RAID01_RAID0: return "RAID01_RAID0";
+ case HPTV2_T_SPAN: return "SPAN";
+ case HPTV2_T_RAID_3: return "RAID3";
+ case HPTV2_T_RAID_5: return "RAID5";
+ case HPTV2_T_JBOD: return "JBOD";
+ case HPTV2_T_RAID01_RAID1: return "RAID01_RAID1";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_hptv2_print_meta(struct hptv2_raid_conf *meta)
+{
+ int i;
+
+ printf("****** ATA Highpoint V2 RocketRAID Metadata *****\n");
+ printf("magic 0x%08x\n", meta->magic);
+ printf("magic_0 0x%08x\n", meta->magic_0);
+ printf("magic_1 0x%08x\n", meta->magic_1);
+ printf("order 0x%08x\n", meta->order);
+ printf("array_width %u\n", meta->array_width);
+ printf("stripe_shift %u\n", meta->stripe_shift);
+ printf("type %s\n", ata_raid_hptv2_type(meta->type));
+ printf("disk_number %u\n", meta->disk_number);
+ printf("total_sectors %u\n", meta->total_sectors);
+ printf("disk_mode 0x%08x\n", meta->disk_mode);
+ printf("boot_mode 0x%08x\n", meta->boot_mode);
+ printf("boot_disk 0x%02x\n", meta->boot_disk);
+ printf("boot_protect 0x%02x\n", meta->boot_protect);
+ printf("log_entries 0x%02x\n", meta->error_log_entries);
+ printf("log_index 0x%02x\n", meta->error_log_index);
+ if (meta->error_log_entries) {
+ printf(" timestamp reason disk status sectors lba\n");
+ for (i = meta->error_log_index;
+ i < meta->error_log_index + meta->error_log_entries; i++)
+ printf(" 0x%08x 0x%02x 0x%02x 0x%02x 0x%02x 0x%08x\n",
+ meta->errorlog[i%32].timestamp,
+ meta->errorlog[i%32].reason,
+ meta->errorlog[i%32].disk, meta->errorlog[i%32].status,
+ meta->errorlog[i%32].sectors, meta->errorlog[i%32].lba);
+ }
+ printf("rebuild_lba 0x%08x\n", meta->rebuild_lba);
+ printf("dummy_1 0x%02x\n", meta->dummy_1);
+ printf("name_1 <%.15s>\n", meta->name_1);
+ printf("dummy_2 0x%02x\n", meta->dummy_2);
+ printf("name_2 <%.15s>\n", meta->name_2);
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_hptv3_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case HPTV3_T_SPARE: return "SPARE";
+ case HPTV3_T_JBOD: return "JBOD";
+ case HPTV3_T_SPAN: return "SPAN";
+ case HPTV3_T_RAID0: return "RAID0";
+ case HPTV3_T_RAID1: return "RAID1";
+ case HPTV3_T_RAID3: return "RAID3";
+ case HPTV3_T_RAID5: return "RAID5";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_hptv3_print_meta(struct hptv3_raid_conf *meta)
+{
+ int i;
+
+ printf("****** ATA Highpoint V3 RocketRAID Metadata *****\n");
+ printf("magic 0x%08x\n", meta->magic);
+ printf("magic_0 0x%08x\n", meta->magic_0);
+ printf("checksum_0 0x%02x\n", meta->checksum_0);
+ printf("mode 0x%02x\n", meta->mode);
+ printf("user_mode 0x%02x\n", meta->user_mode);
+ printf("config_entries 0x%02x\n", meta->config_entries);
+ for (i = 0; i < meta->config_entries; i++) {
+ printf("config %d:\n", i);
+ printf(" total_sectors %ju\n",
+ meta->configs[0].total_sectors +
+ ((u_int64_t)meta->configs_high[0].total_sectors << 32));
+ printf(" type %s\n",
+ ata_raid_hptv3_type(meta->configs[i].type));
+ printf(" total_disks %u\n", meta->configs[i].total_disks);
+ printf(" disk_number %u\n", meta->configs[i].disk_number);
+ printf(" stripe_shift %u\n", meta->configs[i].stripe_shift);
+ printf(" status %b\n", meta->configs[i].status,
+ "\20\2RAID5\1NEED_REBUILD\n");
+ printf(" critical_disks %u\n", meta->configs[i].critical_disks);
+ printf(" rebuild_lba %ju\n",
+ meta->configs_high[0].rebuild_lba +
+ ((u_int64_t)meta->configs_high[0].rebuild_lba << 32));
+ }
+ printf("name <%.16s>\n", meta->name);
+ printf("timestamp 0x%08x\n", meta->timestamp);
+ printf("description <%.16s>\n", meta->description);
+ printf("creator <%.16s>\n", meta->creator);
+ printf("checksum_1 0x%02x\n", meta->checksum_1);
+ printf("dummy_0 0x%02x\n", meta->dummy_0);
+ printf("dummy_1 0x%02x\n", meta->dummy_1);
+ printf("flags %b\n", meta->flags,
+ "\20\4RCACHE\3WCACHE\2NCQ\1TCQ\n");
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_intel_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case INTEL_T_RAID0: return "RAID0";
+ case INTEL_T_RAID1: return "RAID1";
+ case INTEL_T_RAID5: return "RAID5";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_intel_print_meta(struct intel_raid_conf *meta)
+{
+ struct intel_raid_mapping *map;
+ int i, j;
+
+ printf("********* ATA Intel MatrixRAID Metadata *********\n");
+ printf("intel_id <%.24s>\n", meta->intel_id);
+ printf("version <%.6s>\n", meta->version);
+ printf("checksum 0x%08x\n", meta->checksum);
+ printf("config_size 0x%08x\n", meta->config_size);
+ printf("config_id 0x%08x\n", meta->config_id);
+ printf("generation 0x%08x\n", meta->generation);
+ printf("total_disks %u\n", meta->total_disks);
+ printf("total_volumes %u\n", meta->total_volumes);
+ printf("DISK# serial disk_sectors disk_id flags\n");
+ for (i = 0; i < meta->total_disks; i++ ) {
+ printf(" %d <%.16s> %u 0x%08x 0x%08x\n", i,
+ meta->disk[i].serial, meta->disk[i].sectors,
+ meta->disk[i].id, meta->disk[i].flags);
+ }
+ map = (struct intel_raid_mapping *)&meta->disk[meta->total_disks];
+ for (j = 0; j < meta->total_volumes; j++) {
+ printf("name %.16s\n", map->name);
+ printf("total_sectors %ju\n", map->total_sectors);
+ printf("state %u\n", map->state);
+ printf("reserved %u\n", map->reserved);
+ printf("offset %u\n", map->offset);
+ printf("disk_sectors %u\n", map->disk_sectors);
+ printf("stripe_count %u\n", map->stripe_count);
+ printf("stripe_sectors %u\n", map->stripe_sectors);
+ printf("status %u\n", map->status);
+ printf("type %s\n", ata_raid_intel_type(map->type));
+ printf("total_disks %u\n", map->total_disks);
+ printf("magic[0] 0x%02x\n", map->magic[0]);
+ printf("magic[1] 0x%02x\n", map->magic[1]);
+ printf("magic[2] 0x%02x\n", map->magic[2]);
+ for (i = 0; i < map->total_disks; i++ ) {
+ printf(" disk %d at disk_idx 0x%08x\n", i, map->disk_idx[i]);
+ }
+ map = (struct intel_raid_mapping *)&map->disk_idx[map->total_disks];
+ }
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_ite_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case ITE_T_RAID0: return "RAID0";
+ case ITE_T_RAID1: return "RAID1";
+ case ITE_T_RAID01: return "RAID0+1";
+ case ITE_T_SPAN: return "SPAN";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_ite_print_meta(struct ite_raid_conf *meta)
+{
+ printf("*** ATA Integrated Technology Express Metadata **\n");
+ printf("ite_id <%.40s>\n", meta->ite_id);
+ printf("timestamp_0 %04x/%02x/%02x %02x:%02x:%02x.%02x\n",
+ *((u_int16_t *)meta->timestamp_0), meta->timestamp_0[2],
+ meta->timestamp_0[3], meta->timestamp_0[5], meta->timestamp_0[4],
+ meta->timestamp_0[7], meta->timestamp_0[6]);
+ printf("total_sectors %jd\n", meta->total_sectors);
+ printf("type %s\n", ata_raid_ite_type(meta->type));
+ printf("stripe_1kblocks %u\n", meta->stripe_1kblocks);
+ printf("timestamp_1 %04x/%02x/%02x %02x:%02x:%02x.%02x\n",
+ *((u_int16_t *)meta->timestamp_1), meta->timestamp_1[2],
+ meta->timestamp_1[3], meta->timestamp_1[5], meta->timestamp_1[4],
+ meta->timestamp_1[7], meta->timestamp_1[6]);
+ printf("stripe_sectors %u\n", meta->stripe_sectors);
+ printf("array_width %u\n", meta->array_width);
+ printf("disk_number %u\n", meta->disk_number);
+ printf("disk_sectors %u\n", meta->disk_sectors);
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_jmicron_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case JM_T_RAID0: return "RAID0";
+ case JM_T_RAID1: return "RAID1";
+ case JM_T_RAID01: return "RAID0+1";
+ case JM_T_JBOD: return "JBOD";
+ case JM_T_RAID5: return "RAID5";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_jmicron_print_meta(struct jmicron_raid_conf *meta)
+{
+ int i;
+
+ printf("***** ATA JMicron Technology Corp Metadata ******\n");
+ printf("signature %.2s\n", meta->signature);
+ printf("version 0x%04x\n", meta->version);
+ printf("checksum 0x%04x\n", meta->checksum);
+ printf("disk_id 0x%08x\n", meta->disk_id);
+ printf("offset 0x%08x\n", meta->offset);
+ printf("disk_sectors_low 0x%08x\n", meta->disk_sectors_low);
+ printf("disk_sectors_high 0x%08x\n", meta->disk_sectors_high);
+ printf("name %.16s\n", meta->name);
+ printf("type %s\n", ata_raid_jmicron_type(meta->type));
+ printf("stripe_shift %d\n", meta->stripe_shift);
+ printf("flags 0x%04x\n", meta->flags);
+ printf("spare:\n");
+ for (i=0; i < 2 && meta->spare[i]; i++)
+ printf(" %d 0x%08x\n", i, meta->spare[i]);
+ printf("disks:\n");
+ for (i=0; i < 8 && meta->disks[i]; i++)
+ printf(" %d 0x%08x\n", i, meta->disks[i]);
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_lsiv2_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case LSIV2_T_RAID0: return "RAID0";
+ case LSIV2_T_RAID1: return "RAID1";
+ case LSIV2_T_SPARE: return "SPARE";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_lsiv2_print_meta(struct lsiv2_raid_conf *meta)
+{
+ int i;
+
+ printf("******* ATA LSILogic V2 MegaRAID Metadata *******\n");
+ printf("lsi_id <%s>\n", meta->lsi_id);
+ printf("dummy_0 0x%02x\n", meta->dummy_0);
+ printf("flags 0x%02x\n", meta->flags);
+ printf("version 0x%04x\n", meta->version);
+ printf("config_entries 0x%02x\n", meta->config_entries);
+ printf("raid_count 0x%02x\n", meta->raid_count);
+ printf("total_disks 0x%02x\n", meta->total_disks);
+ printf("dummy_1 0x%02x\n", meta->dummy_1);
+ printf("dummy_2 0x%04x\n", meta->dummy_2);
+ for (i = 0; i < meta->config_entries; i++) {
+ printf(" type %s\n",
+ ata_raid_lsiv2_type(meta->configs[i].raid.type));
+ printf(" dummy_0 %02x\n", meta->configs[i].raid.dummy_0);
+ printf(" stripe_sectors %u\n",
+ meta->configs[i].raid.stripe_sectors);
+ printf(" array_width %u\n",
+ meta->configs[i].raid.array_width);
+ printf(" disk_count %u\n", meta->configs[i].raid.disk_count);
+ printf(" config_offset %u\n",
+ meta->configs[i].raid.config_offset);
+ printf(" dummy_1 %u\n", meta->configs[i].raid.dummy_1);
+ printf(" flags %02x\n", meta->configs[i].raid.flags);
+ printf(" total_sectors %u\n",
+ meta->configs[i].raid.total_sectors);
+ }
+ printf("disk_number 0x%02x\n", meta->disk_number);
+ printf("raid_number 0x%02x\n", meta->raid_number);
+ printf("timestamp 0x%08x\n", meta->timestamp);
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_lsiv3_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case LSIV3_T_RAID0: return "RAID0";
+ case LSIV3_T_RAID1: return "RAID1";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_lsiv3_print_meta(struct lsiv3_raid_conf *meta)
+{
+ int i;
+
+ printf("******* ATA LSILogic V3 MegaRAID Metadata *******\n");
+ printf("lsi_id <%.6s>\n", meta->lsi_id);
+ printf("dummy_0 0x%04x\n", meta->dummy_0);
+ printf("version 0x%04x\n", meta->version);
+ printf("dummy_0 0x%04x\n", meta->dummy_1);
+ printf("RAID configs:\n");
+ for (i = 0; i < 8; i++) {
+ if (meta->raid[i].total_disks) {
+ printf("%02d stripe_pages %u\n", i,
+ meta->raid[i].stripe_pages);
+ printf("%02d type %s\n", i,
+ ata_raid_lsiv3_type(meta->raid[i].type));
+ printf("%02d total_disks %u\n", i,
+ meta->raid[i].total_disks);
+ printf("%02d array_width %u\n", i,
+ meta->raid[i].array_width);
+ printf("%02d sectors %u\n", i, meta->raid[i].sectors);
+ printf("%02d offset %u\n", i, meta->raid[i].offset);
+ printf("%02d device 0x%02x\n", i,
+ meta->raid[i].device);
+ }
+ }
+ printf("DISK configs:\n");
+ for (i = 0; i < 6; i++) {
+ if (meta->disk[i].disk_sectors) {
+ printf("%02d disk_sectors %u\n", i,
+ meta->disk[i].disk_sectors);
+ printf("%02d flags 0x%02x\n", i, meta->disk[i].flags);
+ }
+ }
+ printf("device 0x%02x\n", meta->device);
+ printf("timestamp 0x%08x\n", meta->timestamp);
+ printf("checksum_1 0x%02x\n", meta->checksum_1);
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_nvidia_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case NV_T_SPAN: return "SPAN";
+ case NV_T_RAID0: return "RAID0";
+ case NV_T_RAID1: return "RAID1";
+ case NV_T_RAID3: return "RAID3";
+ case NV_T_RAID5: return "RAID5";
+ case NV_T_RAID01: return "RAID0+1";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_nvidia_print_meta(struct nvidia_raid_conf *meta)
+{
+ printf("******** ATA nVidia MediaShield Metadata ********\n");
+ printf("nvidia_id <%.8s>\n", meta->nvidia_id);
+ printf("config_size %d\n", meta->config_size);
+ printf("checksum 0x%08x\n", meta->checksum);
+ printf("version 0x%04x\n", meta->version);
+ printf("disk_number %d\n", meta->disk_number);
+ printf("dummy_0 0x%02x\n", meta->dummy_0);
+ printf("total_sectors %d\n", meta->total_sectors);
+ printf("sectors_size %d\n", meta->sector_size);
+ printf("serial %.16s\n", meta->serial);
+ printf("revision %.4s\n", meta->revision);
+ printf("dummy_1 0x%08x\n", meta->dummy_1);
+ printf("magic_0 0x%08x\n", meta->magic_0);
+ printf("magic_1 0x%016jx\n", meta->magic_1);
+ printf("magic_2 0x%016jx\n", meta->magic_2);
+ printf("flags 0x%02x\n", meta->flags);
+ printf("array_width %d\n", meta->array_width);
+ printf("total_disks %d\n", meta->total_disks);
+ printf("dummy_2 0x%02x\n", meta->dummy_2);
+ printf("type %s\n", ata_raid_nvidia_type(meta->type));
+ printf("dummy_3 0x%04x\n", meta->dummy_3);
+ printf("stripe_sectors %d\n", meta->stripe_sectors);
+ printf("stripe_bytes %d\n", meta->stripe_bytes);
+ printf("stripe_shift %d\n", meta->stripe_shift);
+ printf("stripe_mask 0x%08x\n", meta->stripe_mask);
+ printf("stripe_sizesectors %d\n", meta->stripe_sizesectors);
+ printf("stripe_sizebytes %d\n", meta->stripe_sizebytes);
+ printf("rebuild_lba %d\n", meta->rebuild_lba);
+ printf("dummy_4 0x%08x\n", meta->dummy_4);
+ printf("dummy_5 0x%08x\n", meta->dummy_5);
+ printf("status 0x%08x\n", meta->status);
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_promise_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case PR_T_RAID0: return "RAID0";
+ case PR_T_RAID1: return "RAID1";
+ case PR_T_RAID3: return "RAID3";
+ case PR_T_RAID5: return "RAID5";
+ case PR_T_SPAN: return "SPAN";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_promise_print_meta(struct promise_raid_conf *meta)
+{
+ int i;
+
+ printf("********* ATA Promise FastTrak Metadata *********\n");
+ printf("promise_id <%s>\n", meta->promise_id);
+ printf("dummy_0 0x%08x\n", meta->dummy_0);
+ printf("magic_0 0x%016jx\n", meta->magic_0);
+ printf("magic_1 0x%04x\n", meta->magic_1);
+ printf("magic_2 0x%08x\n", meta->magic_2);
+ printf("integrity 0x%08x %b\n", meta->raid.integrity,
+ meta->raid.integrity, "\20\10VALID\n" );
+ printf("flags 0x%02x %b\n",
+ meta->raid.flags, meta->raid.flags,
+ "\20\10READY\7DOWN\6REDIR\5DUPLICATE\4SPARE"
+ "\3ASSIGNED\2ONLINE\1VALID\n");
+ printf("disk_number %d\n", meta->raid.disk_number);
+ printf("channel 0x%02x\n", meta->raid.channel);
+ printf("device 0x%02x\n", meta->raid.device);
+ printf("magic_0 0x%016jx\n", meta->raid.magic_0);
+ printf("disk_offset %u\n", meta->raid.disk_offset);
+ printf("disk_sectors %u\n", meta->raid.disk_sectors);
+ printf("rebuild_lba 0x%08x\n", meta->raid.rebuild_lba);
+ printf("generation 0x%04x\n", meta->raid.generation);
+ printf("status 0x%02x %b\n",
+ meta->raid.status, meta->raid.status,
+ "\20\6MARKED\5DEGRADED\4READY\3INITED\2ONLINE\1VALID\n");
+ printf("type %s\n", ata_raid_promise_type(meta->raid.type));
+ printf("total_disks %u\n", meta->raid.total_disks);
+ printf("stripe_shift %u\n", meta->raid.stripe_shift);
+ printf("array_width %u\n", meta->raid.array_width);
+ printf("array_number %u\n", meta->raid.array_number);
+ printf("total_sectors %u\n", meta->raid.total_sectors);
+ printf("cylinders %u\n", meta->raid.cylinders);
+ printf("heads %u\n", meta->raid.heads);
+ printf("sectors %u\n", meta->raid.sectors);
+ printf("magic_1 0x%016jx\n", meta->raid.magic_1);
+ printf("DISK# flags dummy_0 channel device magic_0\n");
+ for (i = 0; i < 8; i++) {
+ printf(" %d %b 0x%02x 0x%02x 0x%02x ",
+ i, meta->raid.disk[i].flags,
+ "\20\10READY\7DOWN\6REDIR\5DUPLICATE\4SPARE"
+ "\3ASSIGNED\2ONLINE\1VALID\n", meta->raid.disk[i].dummy_0,
+ meta->raid.disk[i].channel, meta->raid.disk[i].device);
+ printf("0x%016jx\n", meta->raid.disk[i].magic_0);
+ }
+ printf("checksum 0x%08x\n", meta->checksum);
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_sii_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case SII_T_RAID0: return "RAID0";
+ case SII_T_RAID1: return "RAID1";
+ case SII_T_RAID01: return "RAID0+1";
+ case SII_T_SPARE: return "SPARE";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_sii_print_meta(struct sii_raid_conf *meta)
+{
+ printf("******* ATA Silicon Image Medley Metadata *******\n");
+ printf("total_sectors %ju\n", meta->total_sectors);
+ printf("dummy_0 0x%04x\n", meta->dummy_0);
+ printf("dummy_1 0x%04x\n", meta->dummy_1);
+ printf("controller_pci_id 0x%08x\n", meta->controller_pci_id);
+ printf("version_minor 0x%04x\n", meta->version_minor);
+ printf("version_major 0x%04x\n", meta->version_major);
+ printf("timestamp 20%02x/%02x/%02x %02x:%02x:%02x\n",
+ meta->timestamp[5], meta->timestamp[4], meta->timestamp[3],
+ meta->timestamp[2], meta->timestamp[1], meta->timestamp[0]);
+ printf("stripe_sectors %u\n", meta->stripe_sectors);
+ printf("dummy_2 0x%04x\n", meta->dummy_2);
+ printf("disk_number %u\n", meta->disk_number);
+ printf("type %s\n", ata_raid_sii_type(meta->type));
+ printf("raid0_disks %u\n", meta->raid0_disks);
+ printf("raid0_ident %u\n", meta->raid0_ident);
+ printf("raid1_disks %u\n", meta->raid1_disks);
+ printf("raid1_ident %u\n", meta->raid1_ident);
+ printf("rebuild_lba %ju\n", meta->rebuild_lba);
+ printf("generation 0x%08x\n", meta->generation);
+ printf("status 0x%02x %b\n",
+ meta->status, meta->status,
+ "\20\1READY\n");
+ printf("base_raid1_position %02x\n", meta->base_raid1_position);
+ printf("base_raid0_position %02x\n", meta->base_raid0_position);
+ printf("position %02x\n", meta->position);
+ printf("dummy_3 %04x\n", meta->dummy_3);
+ printf("name <%.16s>\n", meta->name);
+ printf("checksum_0 0x%04x\n", meta->checksum_0);
+ printf("checksum_1 0x%04x\n", meta->checksum_1);
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_sis_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case SIS_T_JBOD: return "JBOD";
+ case SIS_T_RAID0: return "RAID0";
+ case SIS_T_RAID1: return "RAID1";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_sis_print_meta(struct sis_raid_conf *meta)
+{
+ printf("**** ATA Silicon Integrated Systems Metadata ****\n");
+ printf("magic 0x%04x\n", meta->magic);
+ printf("disks 0x%02x\n", meta->disks);
+ printf("type %s\n",
+ ata_raid_sis_type(meta->type_total_disks & SIS_T_MASK));
+ printf("total_disks %u\n", meta->type_total_disks & SIS_D_MASK);
+ printf("dummy_0 0x%08x\n", meta->dummy_0);
+ printf("controller_pci_id 0x%08x\n", meta->controller_pci_id);
+ printf("stripe_sectors %u\n", meta->stripe_sectors);
+ printf("dummy_1 0x%04x\n", meta->dummy_1);
+ printf("timestamp 0x%08x\n", meta->timestamp);
+ printf("model %.40s\n", meta->model);
+ printf("disk_number %u\n", meta->disk_number);
+ printf("dummy_2 0x%02x 0x%02x 0x%02x\n",
+ meta->dummy_2[0], meta->dummy_2[1], meta->dummy_2[2]);
+ printf("=================================================\n");
+}
+
+static char *
+ata_raid_via_type(int type)
+{
+ static char buffer[16];
+
+ switch (type) {
+ case VIA_T_RAID0: return "RAID0";
+ case VIA_T_RAID1: return "RAID1";
+ case VIA_T_RAID5: return "RAID5";
+ case VIA_T_RAID01: return "RAID0+1";
+ case VIA_T_SPAN: return "SPAN";
+ default: sprintf(buffer, "UNKNOWN 0x%02x", type);
+ return buffer;
+ }
+}
+
+static void
+ata_raid_via_print_meta(struct via_raid_conf *meta)
+{
+ int i;
+
+ printf("*************** ATA VIA Metadata ****************\n");
+ printf("magic 0x%02x\n", meta->magic);
+ printf("dummy_0 0x%02x\n", meta->dummy_0);
+ printf("type %s\n",
+ ata_raid_via_type(meta->type & VIA_T_MASK));
+ printf("bootable %d\n", meta->type & VIA_T_BOOTABLE);
+ printf("unknown %d\n", meta->type & VIA_T_UNKNOWN);
+ printf("disk_index 0x%02x\n", meta->disk_index);
+ printf("stripe_layout 0x%02x\n", meta->stripe_layout);
+ printf(" stripe_disks %d\n", meta->stripe_layout & VIA_L_DISKS);
+ printf(" stripe_sectors %d\n",
+ 0x08 << ((meta->stripe_layout & VIA_L_MASK) >> VIA_L_SHIFT));
+ printf("disk_sectors %ju\n", meta->disk_sectors);
+ printf("disk_id 0x%08x\n", meta->disk_id);
+ printf("DISK# disk_id\n");
+ for (i = 0; i < 8; i++) {
+ if (meta->disks[i])
+ printf(" %d 0x%08x\n", i, meta->disks[i]);
+ }
+ printf("checksum 0x%02x\n", meta->checksum);
+ printf("=================================================\n");
+}
diff --git a/sys/dev/ata/ata-raid.h b/sys/dev/ata/ata-raid.h
new file mode 100644
index 000000000000..12a099f314b5
--- /dev/null
+++ b/sys/dev/ata/ata-raid.h
@@ -0,0 +1,813 @@
+/*-
+ * Copyright (c) 2000 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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$
+ */
+
+/* misc defines */
+#define MAX_ARRAYS 16
+#define MAX_VOLUMES 4
+#define MAX_DISKS 16
+#define AR_PROXIMITY 2048 /* how many sectors is "close" */
+
+#define ATA_MAGIC "FreeBSD ATA driver RAID "
+
+struct ata_raid_subdisk {
+ struct ar_softc *raid[MAX_VOLUMES];
+ int disk_number[MAX_VOLUMES];
+};
+
+/* ATA PseudoRAID Metadata */
+struct ar_softc {
+ int lun;
+ u_int8_t name[32];
+ int volume;
+ u_int64_t magic_0;
+ u_int64_t magic_1;
+ int type;
+#define AR_T_JBOD 0x0001
+#define AR_T_SPAN 0x0002
+#define AR_T_RAID0 0x0004
+#define AR_T_RAID1 0x0008
+#define AR_T_RAID01 0x0010
+#define AR_T_RAID3 0x0020
+#define AR_T_RAID4 0x0040
+#define AR_T_RAID5 0x0080
+
+ int status;
+#define AR_S_READY 0x0001
+#define AR_S_DEGRADED 0x0002
+#define AR_S_REBUILDING 0x0004
+
+ int format;
+#define AR_F_FREEBSD_RAID 0x0001
+#define AR_F_ADAPTEC_RAID 0x0002
+#define AR_F_HPTV2_RAID 0x0004
+#define AR_F_HPTV3_RAID 0x0008
+#define AR_F_INTEL_RAID 0x0010
+#define AR_F_ITE_RAID 0x0020
+#define AR_F_JMICRON_RAID 0x0040
+#define AR_F_LSIV2_RAID 0x0080
+#define AR_F_LSIV3_RAID 0x0100
+#define AR_F_NVIDIA_RAID 0x0200
+#define AR_F_PROMISE_RAID 0x0400
+#define AR_F_SII_RAID 0x0800
+#define AR_F_SIS_RAID 0x1000
+#define AR_F_VIA_RAID 0x2000
+#define AR_F_FORMAT_MASK 0x3fff
+
+ u_int generation;
+ u_int64_t total_sectors;
+ u_int64_t offset_sectors; /* offset from start of disk */
+ u_int16_t heads;
+ u_int16_t sectors;
+ u_int32_t cylinders;
+ u_int width; /* array width in disks */
+ u_int interleave; /* interleave in sectors */
+ u_int total_disks; /* number of disks in this array */
+ struct ar_disk {
+ device_t dev;
+ u_int8_t serial[16]; /* serial # of physical disk */
+ u_int64_t sectors; /* useable sectors on this disk */
+ off_t last_lba; /* last lba used (for performance) */
+ u_int flags;
+#define AR_DF_PRESENT 0x0001 /* this HW pos has a disk present */
+#define AR_DF_ASSIGNED 0x0002 /* this HW pos assigned to an array */
+#define AR_DF_SPARE 0x0004 /* this HW pos is a spare */
+#define AR_DF_ONLINE 0x0008 /* this HW pos is online and in use */
+
+ } disks[MAX_DISKS];
+ int toggle; /* performance hack for RAID1's */
+ u_int64_t rebuild_lba; /* rebuild progress indicator */
+ struct mtx lock; /* metadata lock */
+ struct disk *disk; /* disklabel/slice stuff */
+ struct proc *pid; /* rebuilder process id */
+};
+
+/* Adaptec HostRAID Metadata */
+#define ADP_LBA(dev) \
+ (((struct ad_softc *)device_get_ivars(dev))->total_secs - 17)
+
+/* note all entries are big endian */
+struct adaptec_raid_conf {
+ u_int32_t magic_0;
+#define ADP_MAGIC_0 0xc4650790
+
+ u_int32_t generation;
+ u_int16_t dummy_0;
+ u_int16_t total_configs;
+ u_int16_t dummy_1;
+ u_int16_t checksum;
+ u_int32_t dummy_2;
+ u_int32_t dummy_3;
+ u_int32_t flags;
+ u_int32_t timestamp;
+ u_int32_t dummy_4[4];
+ u_int32_t dummy_5[4];
+ struct {
+ u_int16_t total_disks;
+ u_int16_t generation;
+ u_int32_t magic_0;
+ u_int8_t dummy_0;
+ u_int8_t type;
+#define ADP_T_RAID0 0x00
+#define ADP_T_RAID1 0x01
+ u_int8_t dummy_1;
+ u_int8_t flags;
+
+ u_int8_t dummy_2;
+ u_int8_t dummy_3;
+ u_int8_t dummy_4;
+ u_int8_t dummy_5;
+
+ u_int32_t disk_number;
+ u_int32_t dummy_6;
+ u_int32_t sectors;
+ u_int16_t stripe_shift;
+ u_int16_t dummy_7;
+
+ u_int32_t dummy_8[4];
+ u_int8_t name[16];
+ } configs[127];
+ u_int32_t dummy_6[13];
+ u_int32_t magic_1;
+#define ADP_MAGIC_1 0x9ff85009
+ u_int32_t dummy_7[3];
+ u_int32_t magic_2;
+ u_int32_t dummy_8[46];
+ u_int32_t magic_3;
+#define ADP_MAGIC_3 0x4d545044
+ u_int32_t magic_4;
+#define ADP_MAGIC_4 0x9ff85009
+ u_int32_t dummy_9[62];
+} __packed;
+
+
+/* Highpoint V2 RocketRAID Metadata */
+#define HPTV2_LBA(dev) 9
+
+struct hptv2_raid_conf {
+ int8_t filler1[32];
+ u_int32_t magic;
+#define HPTV2_MAGIC_OK 0x5a7816f0
+#define HPTV2_MAGIC_BAD 0x5a7816fd
+
+ u_int32_t magic_0;
+ u_int32_t magic_1;
+ u_int32_t order;
+#define HPTV2_O_RAID0 0x01
+#define HPTV2_O_RAID1 0x02
+#define HPTV2_O_OK 0x04
+
+ u_int8_t array_width;
+ u_int8_t stripe_shift;
+ u_int8_t type;
+#define HPTV2_T_RAID0 0x00
+#define HPTV2_T_RAID1 0x01
+#define HPTV2_T_RAID01_RAID0 0x02
+#define HPTV2_T_SPAN 0x03
+#define HPTV2_T_RAID_3 0x04
+#define HPTV2_T_RAID_5 0x05
+#define HPTV2_T_JBOD 0x06
+#define HPTV2_T_RAID01_RAID1 0x07
+
+ u_int8_t disk_number;
+ u_int32_t total_sectors;
+ u_int32_t disk_mode;
+ u_int32_t boot_mode;
+ u_int8_t boot_disk;
+ u_int8_t boot_protect;
+ u_int8_t error_log_entries;
+ u_int8_t error_log_index;
+ struct {
+ u_int32_t timestamp;
+ u_int8_t reason;
+#define HPTV2_R_REMOVED 0xfe
+#define HPTV2_R_BROKEN 0xff
+
+ u_int8_t disk;
+ u_int8_t status;
+ u_int8_t sectors;
+ u_int32_t lba;
+ } errorlog[32];
+ int8_t filler2[16];
+ u_int32_t rebuild_lba;
+ u_int8_t dummy_1;
+ u_int8_t name_1[15];
+ u_int8_t dummy_2;
+ u_int8_t name_2[15];
+ int8_t filler3[8];
+} __packed;
+
+
+/* Highpoint V3 RocketRAID Metadata */
+#define HPTV3_LBA(dev) \
+ (((struct ad_softc *)device_get_ivars(dev))->total_secs - 11)
+
+struct hptv3_raid_conf {
+ u_int32_t magic;
+#define HPTV3_MAGIC 0x5a7816f3
+
+ u_int32_t magic_0;
+ u_int8_t checksum_0;
+ u_int8_t mode;
+#define HPTV3_BOOT_MARK 0x01
+#define HPTV3_USER_MODE 0x02
+
+ u_int8_t user_mode;
+ u_int8_t config_entries;
+ struct {
+ u_int32_t total_sectors;
+ u_int8_t type;
+#define HPTV3_T_SPARE 0x00
+#define HPTV3_T_JBOD 0x03
+#define HPTV3_T_SPAN 0x04
+#define HPTV3_T_RAID0 0x05
+#define HPTV3_T_RAID1 0x06
+#define HPTV3_T_RAID3 0x07
+#define HPTV3_T_RAID5 0x08
+
+ u_int8_t total_disks;
+ u_int8_t disk_number;
+ u_int8_t stripe_shift;
+ u_int16_t status;
+#define HPTV3_T_NEED_REBUILD 0x01
+#define HPTV3_T_RAID5_FLAG 0x02
+
+ u_int16_t critical_disks;
+ u_int32_t rebuild_lba;
+ } __packed configs[2];
+ u_int8_t name[16];
+ u_int32_t timestamp;
+ u_int8_t description[64];
+ u_int8_t creator[16];
+ u_int8_t checksum_1;
+ u_int8_t dummy_0;
+ u_int8_t dummy_1;
+ u_int8_t flags;
+#define HPTV3_T_ENABLE_TCQ 0x01
+#define HPTV3_T_ENABLE_NCQ 0x02
+#define HPTV3_T_ENABLE_WCACHE 0x04
+#define HPTV3_T_ENABLE_RCACHE 0x08
+
+ struct {
+ u_int32_t total_sectors;
+ u_int32_t rebuild_lba;
+ } __packed configs_high[2];
+ u_int32_t filler[87];
+} __packed;
+
+
+/* Intel MatrixRAID Metadata */
+#define INTEL_LBA(dev) \
+ (((struct ad_softc *)device_get_ivars(dev))->total_secs - 3)
+
+struct intel_raid_conf {
+ u_int8_t intel_id[24];
+#define INTEL_MAGIC "Intel Raid ISM Cfg Sig. "
+
+ u_int8_t version[6];
+#define INTEL_VERSION_1100 "1.1.00"
+#define INTEL_VERSION_1201 "1.2.01"
+#define INTEL_VERSION_1202 "1.2.02"
+
+ u_int8_t dummy_0[2];
+ u_int32_t checksum;
+ u_int32_t config_size;
+ u_int32_t config_id;
+ u_int32_t generation;
+ u_int32_t dummy_1[2];
+ u_int8_t total_disks;
+ u_int8_t total_volumes;
+ u_int8_t dummy_2[2];
+ u_int32_t filler_0[39];
+ struct {
+ u_int8_t serial[16];
+ u_int32_t sectors;
+ u_int32_t id;
+ u_int32_t flags;
+#define INTEL_F_SPARE 0x01
+#define INTEL_F_ASSIGNED 0x02
+#define INTEL_F_DOWN 0x04
+#define INTEL_F_ONLINE 0x08
+
+ u_int32_t filler[5];
+ } __packed disk[1];
+ u_int32_t filler_1[62];
+} __packed;
+
+struct intel_raid_mapping {
+ u_int8_t name[16];
+ u_int64_t total_sectors __packed;
+ u_int32_t state;
+ u_int32_t reserved;
+ u_int32_t filler_0[20];
+ u_int32_t offset;
+ u_int32_t disk_sectors;
+ u_int32_t stripe_count;
+ u_int16_t stripe_sectors;
+ u_int8_t status;
+#define INTEL_S_READY 0x00
+#define INTEL_S_DISABLED 0x01
+#define INTEL_S_DEGRADED 0x02
+#define INTEL_S_FAILURE 0x03
+
+ u_int8_t type;
+#define INTEL_T_RAID0 0x00
+#define INTEL_T_RAID1 0x01
+#define INTEL_T_RAID5 0x05
+
+ u_int8_t total_disks;
+ u_int8_t magic[3];
+ u_int32_t filler_1[7];
+ u_int32_t disk_idx[1];
+} __packed;
+
+
+/* Integrated Technology Express Metadata */
+#define ITE_LBA(dev) \
+ (((struct ad_softc *)device_get_ivars(dev))->total_secs - 2)
+
+struct ite_raid_conf {
+ u_int32_t filler_1[5];
+ u_int8_t timestamp_0[8];
+ u_int32_t dummy_1;
+ u_int32_t filler_2[5];
+ u_int16_t filler_3;
+ u_int8_t ite_id[40];
+#define ITE_MAGIC "Integrated Technology Express Inc "
+
+ u_int16_t filler_4;
+ u_int32_t filler_5[6];
+ u_int32_t dummy_2;
+ u_int32_t dummy_3;
+ u_int32_t filler_6[12];
+ u_int32_t dummy_4;
+ u_int32_t filler_7[5];
+ u_int64_t total_sectors __packed;
+ u_int32_t filler_8[12];
+
+ u_int16_t filler_9;
+ u_int8_t type;
+#define ITE_T_RAID0 0x00
+#define ITE_T_RAID1 0x01
+#define ITE_T_RAID01 0x02
+#define ITE_T_SPAN 0x03
+
+ u_int8_t filler_10;
+ u_int32_t dummy_5[8];
+ u_int8_t stripe_1kblocks;
+ u_int8_t filler_11[3];
+ u_int32_t filler_12[54];
+
+ u_int32_t dummy_6[4];
+ u_int8_t timestamp_1[8];
+ u_int32_t filler_13[9];
+ u_int8_t stripe_sectors;
+ u_int8_t filler_14[3];
+ u_int8_t array_width;
+ u_int8_t filler_15[3];
+ u_int32_t filler_16;
+ u_int8_t filler_17;
+ u_int8_t disk_number;
+ u_int32_t disk_sectors;
+ u_int16_t filler_18;
+ u_int32_t dummy_7[4];
+ u_int32_t filler_20[104];
+} __packed;
+
+
+/* JMicron Technology Corp Metadata */
+#define JMICRON_LBA(dev) \
+ (((struct ad_softc *)device_get_ivars(dev))->total_secs - 1)
+#define JM_MAX_DISKS 8
+
+struct jmicron_raid_conf {
+ u_int8_t signature[2];
+#define JMICRON_MAGIC "JM"
+
+ u_int16_t version;
+#define JMICRON_VERSION 0x0001
+
+ u_int16_t checksum;
+ u_int8_t filler_1[10];
+ u_int32_t disk_id;
+ u_int32_t offset;
+ u_int32_t disk_sectors_high;
+ u_int16_t disk_sectors_low;
+ u_int8_t filler_2[2];
+ u_int8_t name[16];
+ u_int8_t type;
+#define JM_T_RAID0 0
+#define JM_T_RAID1 1
+#define JM_T_RAID01 2
+#define JM_T_JBOD 3
+#define JM_T_RAID5 5
+
+ u_int8_t stripe_shift;
+ u_int16_t flags;
+#define JM_F_READY 0x0001
+#define JM_F_BOOTABLE 0x0002
+#define JM_F_BAD 0x0004
+#define JM_F_ACTIVE 0c0010
+#define JM_F_UNSYNC 0c0020
+#define JM_F_NEWEST 0c0040
+
+ u_int8_t filler_3[4];
+ u_int32_t spare[2];
+ u_int32_t disks[JM_MAX_DISKS];
+ u_int8_t filler_4[32];
+ u_int8_t filler_5[384];
+};
+
+
+/* LSILogic V2 MegaRAID Metadata */
+#define LSIV2_LBA(dev) \
+ (((struct ad_softc *)device_get_ivars(dev))->total_secs - 1)
+
+struct lsiv2_raid_conf {
+ u_int8_t lsi_id[6];
+#define LSIV2_MAGIC "$XIDE$"
+
+ u_int8_t dummy_0;
+ u_int8_t flags;
+ u_int16_t version;
+ u_int8_t config_entries;
+ u_int8_t raid_count;
+ u_int8_t total_disks;
+ u_int8_t dummy_1;
+ u_int16_t dummy_2;
+
+ union {
+ struct {
+ u_int8_t type;
+#define LSIV2_T_RAID0 0x01
+#define LSIV2_T_RAID1 0x02
+#define LSIV2_T_SPARE 0x08
+
+ u_int8_t dummy_0;
+ u_int16_t stripe_sectors;
+ u_int8_t array_width;
+ u_int8_t disk_count;
+ u_int8_t config_offset;
+ u_int8_t dummy_1;
+ u_int8_t flags;
+#define LSIV2_R_DEGRADED 0x02
+
+ u_int32_t total_sectors;
+ u_int8_t filler[3];
+ } __packed raid;
+ struct {
+ u_int8_t device;
+#define LSIV2_D_MASTER 0x00
+#define LSIV2_D_SLAVE 0x01
+#define LSIV2_D_CHANNEL0 0x00
+#define LSIV2_D_CHANNEL1 0x10
+#define LSIV2_D_NONE 0xff
+
+ u_int8_t dummy_0;
+ u_int32_t disk_sectors;
+ u_int8_t disk_number;
+ u_int8_t raid_number;
+ u_int8_t flags;
+#define LSIV2_D_GONE 0x02
+
+ u_int8_t filler[7];
+ } __packed disk;
+ } configs[30];
+ u_int8_t disk_number;
+ u_int8_t raid_number;
+ u_int32_t timestamp;
+ u_int8_t filler[10];
+} __packed;
+
+
+/* LSILogic V3 MegaRAID Metadata */
+#define LSIV3_LBA(dev) \
+ (((struct ad_softc *)device_get_ivars(dev))->total_secs - 4)
+
+struct lsiv3_raid_conf {
+ u_int32_t magic_0; /* 0xa0203200 */
+ u_int32_t filler_0[3];
+ u_int8_t magic_1[4]; /* "SATA" */
+ u_int32_t filler_1[40];
+ u_int32_t dummy_0; /* 0x0d000003 */
+ u_int32_t filler_2[7];
+ u_int32_t dummy_1; /* 0x0d000003 */
+ u_int32_t filler_3[70];
+ u_int8_t magic_2[8]; /* "$_ENQ$31" */
+ u_int8_t filler_4[7];
+ u_int8_t checksum_0;
+ u_int8_t filler_5[512*2];
+ u_int8_t lsi_id[6];
+#define LSIV3_MAGIC "$_IDE$"
+
+ u_int16_t dummy_2; /* 0x33de for OK disk */
+ u_int16_t version; /* 0x0131 for this version */
+ u_int16_t dummy_3; /* 0x0440 always */
+ u_int32_t filler_6;
+
+ struct {
+ u_int16_t stripe_pages;
+ u_int8_t type;
+#define LSIV3_T_RAID0 0x00
+#define LSIV3_T_RAID1 0x01
+
+ u_int8_t dummy_0;
+ u_int8_t total_disks;
+ u_int8_t array_width;
+ u_int8_t filler_0[10];
+
+ u_int32_t sectors;
+ u_int16_t dummy_1;
+ u_int32_t offset;
+ u_int16_t dummy_2;
+ u_int8_t device;
+#define LSIV3_D_DEVICE 0x01
+#define LSIV3_D_CHANNEL 0x10
+
+ u_int8_t dummy_3;
+ u_int8_t dummy_4;
+ u_int8_t dummy_5;
+ u_int8_t filler_1[16];
+ } __packed raid[8];
+ struct {
+ u_int32_t disk_sectors;
+ u_int32_t dummy_0;
+ u_int32_t dummy_1;
+ u_int8_t dummy_2;
+ u_int8_t dummy_3;
+ u_int8_t flags;
+#define LSIV3_D_MIRROR 0x00
+#define LSIV3_D_STRIPE 0xff
+ u_int8_t dummy_4;
+ } __packed disk[6];
+ u_int8_t filler_7[7];
+ u_int8_t device;
+ u_int32_t timestamp;
+ u_int8_t filler_8[3];
+ u_int8_t checksum_1;
+} __packed;
+
+
+/* nVidia MediaShield Metadata */
+#define NVIDIA_LBA(dev) \
+ (((struct ad_softc *)device_get_ivars(dev))->total_secs - 2)
+
+struct nvidia_raid_conf {
+ u_int8_t nvidia_id[8];
+#define NV_MAGIC "NVIDIA "
+
+ u_int32_t config_size;
+ u_int32_t checksum;
+ u_int16_t version;
+ u_int8_t disk_number;
+ u_int8_t dummy_0;
+ u_int32_t total_sectors;
+ u_int32_t sector_size;
+ u_int8_t serial[16];
+ u_int8_t revision[4];
+ u_int32_t dummy_1;
+
+ u_int32_t magic_0;
+#define NV_MAGIC0 0x00640044
+
+ u_int64_t magic_1;
+ u_int64_t magic_2;
+ u_int8_t flags;
+ u_int8_t array_width;
+ u_int8_t total_disks;
+ u_int8_t dummy_2;
+ u_int16_t type;
+#define NV_T_RAID0 0x00000080
+#define NV_T_RAID1 0x00000081
+#define NV_T_RAID3 0x00000083
+#define NV_T_RAID5 0x00000085
+#define NV_T_RAID01 0x00008180
+#define NV_T_SPAN 0x000000ff
+
+ u_int16_t dummy_3;
+ u_int32_t stripe_sectors;
+ u_int32_t stripe_bytes;
+ u_int32_t stripe_shift;
+ u_int32_t stripe_mask;
+ u_int32_t stripe_sizesectors;
+ u_int32_t stripe_sizebytes;
+ u_int32_t rebuild_lba;
+ u_int32_t dummy_4;
+ u_int32_t dummy_5;
+ u_int32_t status;
+#define NV_S_BOOTABLE 0x00000001
+#define NV_S_DEGRADED 0x00000002
+
+ u_int32_t filler[98];
+} __packed;
+
+
+/* Promise FastTrak Metadata */
+#define PROMISE_LBA(dev) \
+ (((((struct ad_softc *)device_get_ivars(dev))->total_secs / (((struct ad_softc *)device_get_ivars(dev))->heads * ((struct ad_softc *)device_get_ivars(dev))->sectors)) * ((struct ad_softc *)device_get_ivars(dev))->heads * ((struct ad_softc *)device_get_ivars(dev))->sectors) - ((struct ad_softc *)device_get_ivars(dev))->sectors)
+
+struct promise_raid_conf {
+ char promise_id[24];
+#define PR_MAGIC "Promise Technology, Inc."
+
+ u_int32_t dummy_0;
+ u_int64_t magic_0;
+#define PR_MAGIC0(x) (((u_int64_t)(x.channel) << 48) | \
+ ((u_int64_t)(x.device != 0) << 56))
+ u_int16_t magic_1;
+ u_int32_t magic_2;
+ u_int8_t filler1[470];
+ struct {
+ u_int32_t integrity;
+#define PR_I_VALID 0x00000080
+
+ u_int8_t flags;
+#define PR_F_VALID 0x00000001
+#define PR_F_ONLINE 0x00000002
+#define PR_F_ASSIGNED 0x00000004
+#define PR_F_SPARE 0x00000008
+#define PR_F_DUPLICATE 0x00000010
+#define PR_F_REDIR 0x00000020
+#define PR_F_DOWN 0x00000040
+#define PR_F_READY 0x00000080
+
+ u_int8_t disk_number;
+ u_int8_t channel;
+ u_int8_t device;
+ u_int64_t magic_0 __packed;
+ u_int32_t disk_offset;
+ u_int32_t disk_sectors;
+ u_int32_t rebuild_lba;
+ u_int16_t generation;
+ u_int8_t status;
+#define PR_S_VALID 0x01
+#define PR_S_ONLINE 0x02
+#define PR_S_INITED 0x04
+#define PR_S_READY 0x08
+#define PR_S_DEGRADED 0x10
+#define PR_S_MARKED 0x20
+#define PR_S_FUNCTIONAL 0x80
+
+ u_int8_t type;
+#define PR_T_RAID0 0x00
+#define PR_T_RAID1 0x01
+#define PR_T_RAID3 0x02
+#define PR_T_RAID5 0x04
+#define PR_T_SPAN 0x08
+#define PR_T_JBOD 0x10
+
+ u_int8_t total_disks;
+ u_int8_t stripe_shift;
+ u_int8_t array_width;
+ u_int8_t array_number;
+ u_int32_t total_sectors;
+ u_int16_t cylinders;
+ u_int8_t heads;
+ u_int8_t sectors;
+ u_int64_t magic_1 __packed;
+ struct {
+ u_int8_t flags;
+ u_int8_t dummy_0;
+ u_int8_t channel;
+ u_int8_t device;
+ u_int64_t magic_0 __packed;
+ } disk[8];
+ } raid;
+ int32_t filler2[346];
+ u_int32_t checksum;
+} __packed;
+
+
+/* Silicon Image Medley Metadata */
+#define SII_LBA(dev) \
+ ( ((struct ad_softc *)device_get_ivars(dev))->total_secs - 1)
+
+struct sii_raid_conf {
+ u_int16_t ata_params_00_53[54];
+ u_int64_t total_sectors;
+ u_int16_t ata_params_58_79[70];
+ u_int16_t dummy_0;
+ u_int16_t dummy_1;
+ u_int32_t controller_pci_id;
+ u_int16_t version_minor;
+ u_int16_t version_major;
+ u_int8_t timestamp[6];
+ u_int16_t stripe_sectors;
+ u_int16_t dummy_2;
+ u_int8_t disk_number;
+ u_int8_t type;
+#define SII_T_RAID0 0x00
+#define SII_T_RAID1 0x01
+#define SII_T_RAID01 0x02
+#define SII_T_SPARE 0x03
+
+ u_int8_t raid0_disks;
+ u_int8_t raid0_ident;
+ u_int8_t raid1_disks;
+ u_int8_t raid1_ident;
+ u_int64_t rebuild_lba;
+ u_int32_t generation;
+ u_int8_t status;
+#define SII_S_READY 0x01
+
+ u_int8_t base_raid1_position;
+ u_int8_t base_raid0_position;
+ u_int8_t position;
+ u_int16_t dummy_3;
+ u_int8_t name[16];
+ u_int16_t checksum_0;
+ int8_t filler1[190];
+ u_int16_t checksum_1;
+} __packed;
+
+
+/* Silicon Integrated Systems RAID Metadata */
+#define SIS_LBA(dev) \
+ ( ((struct ad_softc *)device_get_ivars(dev))->total_secs - 16)
+
+struct sis_raid_conf {
+ u_int16_t magic;
+#define SIS_MAGIC 0x0010
+
+ u_int8_t disks;
+#define SIS_D_MASTER 0xf0
+#define SIS_D_MIRROR 0x0f
+
+ u_int8_t type_total_disks;
+#define SIS_D_MASK 0x0f
+#define SIS_T_MASK 0xf0
+#define SIS_T_JBOD 0x10
+#define SIS_T_RAID0 0x20
+#define SIS_T_RAID1 0x30
+
+ u_int32_t dummy_0;
+ u_int32_t controller_pci_id;
+ u_int16_t stripe_sectors;
+ u_int16_t dummy_1;
+ u_int32_t timestamp;
+ u_int8_t model[40];
+ u_int8_t disk_number;
+ u_int8_t dummy_2[3];
+ int8_t filler1[448];
+} __packed;
+
+
+/* VIA Tech V-RAID Metadata */
+#define VIA_LBA(dev) \
+ ( ((struct ad_softc *)device_get_ivars(dev))->total_secs - 1)
+
+struct via_raid_conf {
+ u_int16_t magic;
+#define VIA_MAGIC 0xaa55
+
+ u_int8_t dummy_0;
+ u_int8_t type;
+#define VIA_T_MASK 0x7e
+#define VIA_T_BOOTABLE 0x01
+#define VIA_T_RAID0 0x04
+#define VIA_T_RAID1 0x0c
+#define VIA_T_RAID01 0x4c
+#define VIA_T_RAID5 0x2c
+#define VIA_T_SPAN 0x44
+#define VIA_T_UNKNOWN 0x80
+
+ u_int8_t disk_index;
+#define VIA_D_MASK 0x0f
+#define VIA_D_DEGRADED 0x10
+#define VIA_D_HIGH_IDX 0x20
+
+ u_int8_t stripe_layout;
+#define VIA_L_DISKS 0x07
+#define VIA_L_MASK 0xf0
+#define VIA_L_SHIFT 4
+
+ u_int64_t disk_sectors;
+ u_int32_t disk_id;
+ u_int32_t disks[8];
+ u_int8_t checksum;
+ u_int8_t filler_1[461];
+} __packed;
diff --git a/sys/dev/ata/ata-usb.c b/sys/dev/ata/ata-usb.c
new file mode 100644
index 000000000000..5c1fbfb68050
--- /dev/null
+++ b/sys/dev/ata/ata-usb.c
@@ -0,0 +1,975 @@
+/*-
+ * Copyright (c) 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+#include <sys/ata.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/sysctl.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/ata/ata-all.h>
+#include <ata_if.h>
+
+/* Command Block Wrapper */
+struct bbb_cbw {
+ u_int8_t signature[4];
+#define CBWSIGNATURE 0x43425355
+
+ u_int8_t tag[4];
+ u_int8_t transfer_length[4];
+ u_int8_t flags;
+#define CBWFLAGS_OUT 0x00
+#define CBWFLAGS_IN 0x80
+
+ u_int8_t lun;
+ u_int8_t length;
+#define CBWCDBLENGTH 16
+
+ u_int8_t cdb[CBWCDBLENGTH];
+};
+
+/* Command Status Wrapper */
+struct bbb_csw {
+ u_int8_t signature[4];
+#define CSWSIGNATURE 0x53425355
+
+ u_int8_t tag[4];
+ u_int8_t residue[4];
+ u_int8_t status;
+#define CSWSTATUS_GOOD 0x0
+#define CSWSTATUS_FAILED 0x1
+#define CSWSTATUS_PHASE 0x2
+};
+
+/* USB-ATA 'controller' softc */
+struct atausb_softc {
+ device_t dev; /* base device */
+ usbd_interface_handle iface; /* interface */
+ int ifaceno; /* interface number */
+ u_int8_t bulkin; /* endpoint address's */
+ u_int8_t bulkout;
+ u_int8_t bulkirq;
+ usbd_pipe_handle bulkin_pipe; /* pipe handle's */
+ usbd_pipe_handle bulkout_pipe;
+ usbd_pipe_handle bulkirq_pipe;
+ int maxlun;
+ int timeout;
+ struct ata_request *ata_request;
+ usb_device_request_t usb_request;
+ struct bbb_cbw cbw;
+ struct bbb_csw csw;
+
+#define ATAUSB_T_BBB_CBW 0
+#define ATAUSB_T_BBB_DATA 1
+#define ATAUSB_T_BBB_DCLEAR 2
+#define ATAUSB_T_BBB_CSW1 3
+#define ATAUSB_T_BBB_CSW2 4
+#define ATAUSB_T_BBB_SCLEAR 5
+#define ATAUSB_T_BBB_RESET1 6
+#define ATAUSB_T_BBB_RESET2 7
+#define ATAUSB_T_BBB_RESET3 8
+#define ATAUSB_T_MAX 9
+ usbd_xfer_handle transfer[ATAUSB_T_MAX];
+
+ int state;
+#define ATAUSB_S_ATTACH 0
+#define ATAUSB_S_IDLE 1
+#define ATAUSB_S_BBB_COMMAND 2
+#define ATAUSB_S_BBB_DATA 3
+#define ATAUSB_S_BBB_DCLEAR 4
+#define ATAUSB_S_BBB_STATUS1 5
+#define ATAUSB_S_BBB_SCLEAR 6
+#define ATAUSB_S_BBB_STATUS2 7
+#define ATAUSB_S_BBB_RESET1 8
+#define ATAUSB_S_BBB_RESET2 9
+#define ATAUSB_S_BBB_RESET3 10
+#define ATAUSB_S_DETACH 11
+
+ struct mtx locked_mtx;
+ struct ata_channel *locked_ch;
+ struct ata_channel *restart_ch;
+};
+
+static int atausbdebug = 0;
+
+/* prototypes*/
+static usbd_status atausb_start(struct atausb_softc *sc, usbd_pipe_handle pipe, void *buffer, int buflen, int flags, usbd_xfer_handle xfer);
+static usbd_status atausb_ctl_start(struct atausb_softc *sc, usbd_device_handle udev, usb_device_request_t *req, void *buffer, int buflen, int flags, usbd_xfer_handle xfer);
+static void atausb_clear_stall(struct atausb_softc *sc, u_int8_t endpt, usbd_pipe_handle pipe, int state, usbd_xfer_handle xfer);
+static void atausb_bbb_reset(struct atausb_softc *sc);
+static int atausb_bbb_start(struct ata_request *request);
+static void atausb_bbb_finish(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status err);
+int ata_usbchannel_begin_transaction(struct ata_request *request);
+int ata_usbchannel_end_transaction(struct ata_request *request);
+
+
+/*
+ * USB frontend part
+ */
+USB_DECLARE_DRIVER(atausb);
+DRIVER_MODULE(atausb, uhub, atausb_driver, atausb_devclass, 0, 0);
+MODULE_VERSION(atausb, 1);
+
+static int
+atausb_match(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ usb_interface_descriptor_t *id;
+
+ if (uaa->iface == NULL)
+ return UMATCH_NONE;
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (!id || id->bInterfaceClass != UICLASS_MASS)
+ return UMATCH_NONE;
+
+ switch (id->bInterfaceSubClass) {
+ case UISUBCLASS_QIC157:
+ case UISUBCLASS_RBC:
+ case UISUBCLASS_SCSI:
+ case UISUBCLASS_SFF8020I:
+ case UISUBCLASS_SFF8070I:
+ case UISUBCLASS_UFI:
+ switch (id->bInterfaceProtocol) {
+ case UIPROTO_MASS_CBI:
+ case UIPROTO_MASS_CBI_I:
+ case UIPROTO_MASS_BBB:
+ case UIPROTO_MASS_BBB_OLD:
+ return UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO;
+ default:
+ return UMATCH_IFACECLASS_IFACESUBCLASS;
+ }
+ break;
+ default:
+ return UMATCH_IFACECLASS;
+ }
+}
+
+static int
+atausb_attach(device_t dev)
+{
+ struct atausb_softc *sc = device_get_softc(dev);
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usbd_device_handle udev;
+ usb_device_request_t request;
+ char devinfo[1024], *proto, *subclass;
+ u_int8_t maxlun;
+ int err, i;
+
+ sc->dev = dev;
+ usbd_devinfo(uaa->device, 0, devinfo);
+ device_set_desc_copy(dev, devinfo);
+ sc->bulkin = sc->bulkout = sc->bulkirq = -1;
+ sc->bulkin_pipe = sc->bulkout_pipe= sc->bulkirq_pipe = NULL;
+ sc->iface = uaa->iface;
+ sc->ifaceno = uaa->ifaceno;
+ sc->maxlun = 0;
+ sc->timeout = 5000;
+ sc->locked_ch = NULL;
+ sc->restart_ch = NULL;
+ mtx_init(&sc->locked_mtx, "ATAUSB lock", NULL, MTX_DEF);
+
+ id = usbd_get_interface_descriptor(sc->iface);
+ switch (id->bInterfaceProtocol) {
+ case UIPROTO_MASS_BBB:
+ case UIPROTO_MASS_BBB_OLD:
+ proto = "Bulk-Only";
+ break;
+ case UIPROTO_MASS_CBI:
+ proto = "CBI";
+ break;
+ case UIPROTO_MASS_CBI_I:
+ proto = "CBI with CCI";
+ break;
+ default:
+ proto = "Unknown";
+ }
+ switch (id->bInterfaceSubClass) {
+ case UISUBCLASS_RBC:
+ subclass = "RBC";
+ break;
+ case UISUBCLASS_QIC157:
+ case UISUBCLASS_SFF8020I:
+ case UISUBCLASS_SFF8070I:
+ subclass = "ATAPI";
+ break;
+ case UISUBCLASS_SCSI:
+ subclass = "SCSI";
+ break;
+ case UISUBCLASS_UFI:
+ subclass = "UFI";
+ break;
+ default:
+ subclass = "Unknown";
+ }
+ device_printf(dev, "using %s over %s\n", subclass, proto);
+ if (strcmp(proto, "Bulk-Only") ||
+ (strcmp(subclass, "ATAPI") && strcmp(subclass, "SCSI")))
+ return ENXIO;
+
+ for (i = 0 ; i < id->bNumEndpoints ; i++) {
+ if (!(ed = usbd_interface2endpoint_descriptor(sc->iface, i))) {
+ device_printf(sc->dev, "could not read endpoint descriptor\n");
+ return ENXIO;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ sc->bulkin = ed->bEndpointAddress;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ sc->bulkout = ed->bEndpointAddress;
+ }
+ if (id->bInterfaceProtocol == UIPROTO_MASS_CBI_I &&
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
+ sc->bulkirq = ed->bEndpointAddress;
+ }
+ }
+
+ /* check whether we found at least the endpoints we need */
+ if (!sc->bulkin || !sc->bulkout) {
+ device_printf(sc->dev, "needed endpoints not found (%d,%d)\n",
+ sc->bulkin, sc->bulkout);
+ atausb_detach(dev);
+ return ENXIO;
+ }
+
+ /* open the pipes */
+ if (usbd_open_pipe(sc->iface, sc->bulkout,
+ USBD_EXCLUSIVE_USE, &sc->bulkout_pipe)) {
+ device_printf(sc->dev, "cannot open bulkout pipe (%d)\n", sc->bulkout);
+ atausb_detach(dev);
+ return ENXIO;
+ }
+ if (usbd_open_pipe(sc->iface, sc->bulkin,
+ USBD_EXCLUSIVE_USE, &sc->bulkin_pipe)) {
+ device_printf(sc->dev, "cannot open bulkin pipe (%d)\n", sc->bulkin);
+ atausb_detach(dev);
+ return ENXIO;
+ }
+ if (id->bInterfaceProtocol == UIPROTO_MASS_CBI_I) {
+ if (usbd_open_pipe(sc->iface, sc->bulkirq,
+ USBD_EXCLUSIVE_USE, &sc->bulkirq_pipe)) {
+ device_printf(sc->dev, "cannot open bulkirq pipe (%d)\n",
+ sc->bulkirq);
+ atausb_detach(dev);
+ return ENXIO;
+ }
+ }
+ sc->state = ATAUSB_S_ATTACH;
+
+ /* alloc needed number of transfer handles */
+ for (i = 0; i < ATAUSB_T_MAX; i++) {
+ sc->transfer[i] = usbd_alloc_xfer(uaa->device);
+ if (!sc->transfer[i]) {
+ device_printf(sc->dev, "out of memory\n");
+ atausb_detach(dev);
+ return ENXIO;
+ }
+ }
+
+ /* driver is ready to process requests here */
+ sc->state = ATAUSB_S_IDLE;
+
+ /* get number of devices so we can add matching channels */
+ usbd_interface2device_handle(sc->iface, &udev);
+ request.bmRequestType = UT_READ_CLASS_INTERFACE;
+ request.bRequest = 0xfe; //GET_MAX_LUN;
+ USETW(request.wValue, 0);
+ USETW(request.wIndex, sc->ifaceno);
+ USETW(request.wLength, sizeof(maxlun));
+ switch ((err = usbd_do_request(udev, &request, &maxlun))) {
+ case USBD_NORMAL_COMPLETION:
+ if (bootverbose)
+ device_printf(sc->dev, "maxlun=%d\n", maxlun);
+ sc->maxlun = maxlun;
+ break;
+ default:
+ if (bootverbose)
+ device_printf(sc->dev, "get maxlun not supported %s\n",
+ usbd_errstr(err));
+ }
+
+ /* ata channels are children to this USB control device */
+ for (i = 0; i <= sc->maxlun; i++) {
+ if (!device_add_child(sc->dev, "ata",
+ devclass_find_free_unit(ata_devclass, 2))) {
+ device_printf(sc->dev, "failed to attach ata child device\n");
+ atausb_detach(dev);
+ return ENXIO;
+ }
+ }
+ bus_generic_attach(sc->dev);
+ return 0;
+}
+
+static int
+atausb_detach(device_t dev)
+{
+ struct atausb_softc *sc = device_get_softc(dev);
+ usbd_device_handle udev;
+ device_t *children;
+ int nchildren, i;
+
+ /* signal that device is going away */
+ sc->state = ATAUSB_S_DETACH;
+
+ /* abort all the pipes in case there are active transfers */
+ usbd_interface2device_handle(sc->iface, &udev);
+ usbd_abort_default_pipe(udev);
+ if (sc->bulkout_pipe)
+ usbd_abort_pipe(sc->bulkout_pipe);
+ if (sc->bulkin_pipe)
+ usbd_abort_pipe(sc->bulkin_pipe);
+ if (sc->bulkirq_pipe)
+ usbd_abort_pipe(sc->bulkirq_pipe);
+
+ /* detach & delete all children */
+ if (!device_get_children(dev, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ device_delete_child(dev, children[i]);
+ free(children, M_TEMP);
+ }
+
+ /* free the transfers */
+ for (i = 0; i < ATAUSB_T_MAX; i++)
+ if (sc->transfer[i])
+ usbd_free_xfer(sc->transfer[i]);
+
+ /* remove all the pipes */
+ if (sc->bulkout_pipe)
+ usbd_close_pipe(sc->bulkout_pipe);
+ if (sc->bulkin_pipe)
+ usbd_close_pipe(sc->bulkin_pipe);
+ if (sc->bulkirq_pipe)
+ usbd_close_pipe(sc->bulkirq_pipe);
+
+ mtx_destroy(&sc->locked_mtx);
+ return 0;
+}
+
+
+/*
+ * Generic USB transfer routines
+ */
+static usbd_status
+atausb_start(struct atausb_softc *sc, usbd_pipe_handle pipe,
+ void *buffer, int buflen, int flags, usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ if (sc->state == ATAUSB_S_DETACH)
+ return USBD_NOT_STARTED;
+
+ usbd_setup_xfer(xfer, pipe, (void *)sc, buffer, buflen, flags,
+ sc->timeout, atausb_bbb_finish);
+ err = usbd_transfer(xfer);
+ if (err && (err != USBD_IN_PROGRESS)) {
+ if (atausbdebug)
+ device_printf(sc->dev, "failed to setup transfer, %s\n",
+ usbd_errstr(err));
+ return err;
+ }
+ return USBD_NORMAL_COMPLETION;
+}
+
+static usbd_status
+atausb_ctl_start(struct atausb_softc *sc, usbd_device_handle udev,
+ usb_device_request_t *req, void *buffer, int buflen, int flags,
+ usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ if (sc->state == ATAUSB_S_DETACH)
+ return USBD_NOT_STARTED;
+
+ usbd_setup_default_xfer(xfer, udev, (void *)sc, sc->timeout, req,
+ buffer, buflen, flags, atausb_bbb_finish);
+ err = usbd_transfer(xfer);
+ if (err && (err != USBD_IN_PROGRESS)) {
+ if (atausbdebug)
+ device_printf(sc->dev, "failed to setup ctl transfer, %s\n",
+ usbd_errstr(err));
+ return err;
+ }
+ return USBD_NORMAL_COMPLETION;
+}
+
+static void
+atausb_clear_stall(struct atausb_softc *sc, u_int8_t endpt,
+ usbd_pipe_handle pipe, int state, usbd_xfer_handle xfer)
+{
+ usbd_device_handle udev;
+
+ if (atausbdebug)
+ device_printf(sc->dev, "clear endpoint 0x%02x stall\n", endpt);
+ usbd_interface2device_handle(sc->iface, &udev);
+ sc->state = state;
+ usbd_clear_endpoint_toggle(pipe);
+ sc->usb_request.bmRequestType = UT_WRITE_ENDPOINT;
+ sc->usb_request.bRequest = UR_CLEAR_FEATURE;
+ USETW(sc->usb_request.wValue, UF_ENDPOINT_HALT);
+ USETW(sc->usb_request.wIndex, endpt);
+ USETW(sc->usb_request.wLength, 0);
+ atausb_ctl_start(sc, udev, &sc->usb_request, NULL, 0, 0, xfer);
+}
+
+
+/*
+ * Bulk-Only transport part
+ */
+static void
+atausb_bbb_reset(struct atausb_softc *sc)
+{
+ usbd_device_handle udev;
+
+ if (atausbdebug)
+ device_printf(sc->dev, "Bulk Reset\n");
+ sc->timeout = 5000;
+ sc->state = ATAUSB_S_BBB_RESET1;
+ usbd_interface2device_handle(sc->iface, &udev);
+ sc->usb_request.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ sc->usb_request.bRequest = 0xff; /* bulk-only reset */
+ USETW(sc->usb_request.wValue, 0);
+ USETW(sc->usb_request.wIndex, sc->ifaceno);
+ USETW(sc->usb_request.wLength, 0);
+ atausb_ctl_start(sc, udev, &sc->usb_request, NULL,
+ 0, 0, sc->transfer[ATAUSB_T_BBB_RESET1]);
+}
+
+static int
+atausb_bbb_start(struct ata_request *request)
+{
+ struct atausb_softc *sc =
+ device_get_softc(device_get_parent(request->parent));
+ struct ata_channel *ch = device_get_softc(request->parent);
+
+ sc->timeout = (request->timeout * 1000) + 5000;
+ USETDW(sc->cbw.signature, CBWSIGNATURE);
+ USETDW(sc->cbw.tag, UGETDW(sc->cbw.tag) + 1);
+ USETDW(sc->cbw.transfer_length, request->bytecount);
+ sc->cbw.flags = (request->flags & ATA_R_READ) ? CBWFLAGS_IN : CBWFLAGS_OUT;
+ sc->cbw.lun = ch->unit;
+ sc->cbw.length = 16;
+ bzero(sc->cbw.cdb, 16);
+ bcopy(request->u.atapi.ccb, sc->cbw.cdb, 12); /* XXX SOS */
+ sc->state = ATAUSB_S_BBB_COMMAND;
+ if (atausb_start(sc, sc->bulkout_pipe, &sc->cbw, sizeof(struct bbb_cbw),
+ 0, sc->transfer[ATAUSB_T_BBB_CBW])) {
+ request->result = EIO;
+ if (atausbdebug)
+ device_printf(request->dev, "cannot setup USB transfer\n");
+ atausb_bbb_reset(sc);
+ return ATA_OP_FINISHED;
+ }
+ return ATA_OP_CONTINUES;
+}
+
+static void
+atausb_bbb_finish(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ struct atausb_softc *sc = (struct atausb_softc *)priv;
+ struct ata_request *request = sc->ata_request;
+ usbd_xfer_handle next_xfer;
+
+ //device_printf(sc->dev, "BBB state %d: %s\n", sc->state, usbd_errstr(err));
+
+ if (sc->state == ATAUSB_S_DETACH) {
+ device_printf(sc->dev, "WARNING - device has been removed\n");
+ return;
+ }
+
+ switch (sc->state) {
+ case ATAUSB_S_BBB_COMMAND: /* command transport phase */
+ if (err) {
+ if (atausbdebug)
+ device_printf(sc->dev, "failed to send CBW\n");
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+
+ /* next is data transport phase, setup transfer */
+ sc->state = ATAUSB_S_BBB_DATA;
+ if (request->flags & ATA_R_READ) {
+ if (atausb_start(sc, sc->bulkin_pipe,
+ request->data, request->bytecount,
+ USBD_SHORT_XFER_OK,
+ sc->transfer[ATAUSB_T_BBB_DATA])) {
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ }
+ return;
+ }
+ if (request->flags & ATA_R_WRITE) {
+ if (atausb_start(sc, sc->bulkout_pipe,
+ request->data, request->bytecount,
+ 0, sc->transfer[ATAUSB_T_BBB_DATA])) {
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ }
+ return;
+ }
+ /* FALLTHROUGH */
+
+ case ATAUSB_S_BBB_DATA: /* data transport phase */
+ if (request->flags & (ATA_R_READ | ATA_R_WRITE)) {
+ usbd_get_xfer_status(xfer, NULL, NULL, &request->donecount, NULL);
+ if (err) {
+ if (atausbdebug)
+ device_printf(sc->dev, "data %s count %d failed: %s\n",
+ (request->flags & ATA_R_READ?"read":"write"),
+ request->bytecount, usbd_errstr(err));
+ if (err == USBD_STALLED) {
+ atausb_clear_stall(sc,
+ (request->flags & ATA_R_READ ?
+ sc->bulkin : sc->bulkout),
+ (request->flags & ATA_R_READ ?
+ sc->bulkin_pipe : sc->bulkout_pipe),
+ ATAUSB_S_BBB_DCLEAR,
+ sc->transfer[ATAUSB_T_BBB_DCLEAR]);
+ }
+ else {
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ }
+ return;
+ }
+ }
+ /* FALLTHROUGH */
+
+ case ATAUSB_S_BBB_DCLEAR: /* stall clear after data phase */
+ case ATAUSB_S_BBB_SCLEAR: /* stall clear after status phase */
+ if (err) {
+ if (atausbdebug)
+ device_printf(sc->dev, "bulk%s stall clear failed %s\n",
+ (request->flags & ATA_R_READ ? "in" : "out"),
+ usbd_errstr(err));
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+
+ if (sc->state == ATAUSB_S_BBB_COMMAND ||
+ sc->state == ATAUSB_S_BBB_DATA ||
+ sc->state == ATAUSB_S_BBB_DCLEAR) {
+ /* first attempt on status transport phase setup transfer */
+ sc->state = ATAUSB_S_BBB_STATUS1;
+ next_xfer = sc->transfer[ATAUSB_T_BBB_CSW1];
+ }
+ else {
+ /* second attempt of fetching status */
+ sc->state = ATAUSB_S_BBB_STATUS2;
+ next_xfer = sc->transfer[ATAUSB_T_BBB_CSW2];
+ }
+ if (atausb_start(sc, sc->bulkin_pipe, &sc->csw, sizeof(struct bbb_csw),
+ USBD_SHORT_XFER_OK, next_xfer)) {
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ }
+ return;
+
+ case ATAUSB_S_BBB_STATUS1: /* status transfer first attempt */
+ case ATAUSB_S_BBB_STATUS2: /* status transfer second attempt */
+ if (err) {
+ if (atausbdebug)
+ device_printf(sc->dev, "cannot get CSW, %s%s\n",
+ usbd_errstr(err),
+ sc->state == ATAUSB_S_BBB_STATUS1 ? ", retry":"");
+ if (sc->state == ATAUSB_S_BBB_STATUS1) {
+ atausb_clear_stall(sc, sc->bulkin, sc->bulkin_pipe,
+ ATAUSB_S_BBB_SCLEAR,
+ sc->transfer[ATAUSB_T_BBB_SCLEAR]);
+ }
+ else {
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ }
+ return;
+ }
+
+ int residue = UGETDW(sc->csw.residue);
+
+ if (!residue &&
+ (request->bytecount - request->donecount))
+ residue = request->bytecount - request->donecount;
+
+ /* check CSW and handle eventual error */
+ if (UGETDW(sc->csw.signature) != CSWSIGNATURE) {
+ if (atausbdebug)
+ device_printf(sc->dev, "bad CSW signature 0x%08x != 0x%08x\n",
+ UGETDW(sc->csw.signature), CSWSIGNATURE);
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+ else if (UGETDW(sc->csw.tag) != UGETDW(sc->cbw.tag)) {
+ if (atausbdebug)
+ device_printf(sc->dev, "bad CSW tag %d != %d\n",
+ UGETDW(sc->csw.tag), UGETDW(sc->cbw.tag));
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+ else if (sc->csw.status > CSWSTATUS_PHASE) {
+ if (atausbdebug)
+ device_printf(sc->dev, "bad CSW status %d > %d\n",
+ sc->csw.status, CSWSTATUS_PHASE);
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+ else if (sc->csw.status == CSWSTATUS_PHASE) {
+ if (atausbdebug)
+ device_printf(sc->dev, "phase error residue = %d\n", residue);
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+ else if (request->donecount > request->bytecount) {
+ if (atausbdebug)
+ device_printf(sc->dev, "buffer overrun %d > %d",
+ request->donecount, request->bytecount);
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+ else if (sc->csw.status == CSWSTATUS_FAILED) {
+ if (atausbdebug)
+ device_printf(sc->dev, "CSWSTATUS_FAILED\n");
+ request->error = ATA_E_ATAPI_SENSE_MASK ;
+ sc->state = ATAUSB_S_IDLE;
+ ata_interrupt(device_get_softc(request->parent));
+ return;
+ }
+ else {
+ sc->state = ATAUSB_S_IDLE;
+ ata_interrupt(device_get_softc(request->parent));
+ return;
+ }
+ /* NOT REACHED */
+
+ case ATAUSB_S_BBB_RESET1:
+ if (err)
+ if (atausbdebug)
+ device_printf(sc->dev,
+ "BBB reset failure: %s\n", usbd_errstr(err));
+ atausb_clear_stall(sc, sc->bulkin, sc->bulkin_pipe,
+ ATAUSB_S_BBB_RESET2,
+ sc->transfer[ATAUSB_T_BBB_RESET2]);
+ return;
+
+ case ATAUSB_S_BBB_RESET2:
+ if (err)
+ if (atausbdebug)
+ device_printf(sc->dev, "BBB bulkin clear stall failure: %s\n",
+ usbd_errstr(err));
+ atausb_clear_stall(sc, sc->bulkout, sc->bulkout_pipe,
+ ATAUSB_S_BBB_RESET3,
+ sc->transfer[ATAUSB_T_BBB_RESET3]);
+ return;
+
+ case ATAUSB_S_BBB_RESET3:
+ if (err)
+ if (atausbdebug)
+ device_printf(sc->dev, "BBB bulk-out clear stall failure: %s\n",
+ usbd_errstr(err));
+ sc->state = ATAUSB_S_IDLE;
+ if (request) {
+ if (err)
+ request->result = ENXIO;
+ else
+ request->result = EIO;
+ ata_interrupt(device_get_softc(request->parent));
+ }
+ return;
+
+ default:
+ if (atausbdebug)
+ device_printf(sc->dev, "unknown state %d", sc->state);
+ }
+}
+
+
+/*
+ * ATA backend part
+ */
+struct atapi_inquiry {
+ u_int8_t device_type;
+ u_int8_t device_modifier;
+ u_int8_t version;
+ u_int8_t response_format;
+ u_int8_t length;
+ u_int8_t reserved[2];
+ u_int8_t flags;
+ u_int8_t vendor[8];
+ u_int8_t product[16];
+ u_int8_t revision[4];
+ //u_int8_t crap[60];
+};
+
+int
+ata_usbchannel_begin_transaction(struct ata_request *request)
+{
+ struct atausb_softc *sc =
+ device_get_softc(device_get_parent(request->parent));
+
+ if (atausbdebug > 1)
+ device_printf(request->dev, "begin_transaction %s\n",
+ ata_cmd2str(request));
+
+ /* sanity just in case */
+ if (sc->state != ATAUSB_S_IDLE) {
+ printf("begin is busy (%d)\n", sc->state);
+ request->result = EBUSY;
+ return ATA_OP_FINISHED;
+ }
+
+ /* XXX SOS convert the request into the format used, only BBB for now*/
+ sc->ata_request = request;
+
+ /* ATA/ATAPI IDENTIFY needs special treatment */
+ if (!(request->flags & ATA_R_ATAPI)) {
+ if (request->u.ata.command != ATA_ATAPI_IDENTIFY) {
+ device_printf(request->dev,"%s unsupported\n",ata_cmd2str(request));
+ request->result = EIO;
+ return ATA_OP_FINISHED;
+ }
+ request->flags |= ATA_R_ATAPI;
+ bzero(request->u.atapi.ccb, 16);
+ request->u.atapi.ccb[0] = ATAPI_INQUIRY;
+ request->u.atapi.ccb[4] = 255; //sizeof(struct atapi_inquiry);
+ request->data += 256; /* arbitrary offset into ata_param */
+ request->bytecount = 255; //sizeof(struct atapi_inquiry);
+ }
+ return atausb_bbb_start(request);
+}
+
+int
+ata_usbchannel_end_transaction(struct ata_request *request)
+{
+ if (atausbdebug > 1)
+ device_printf(request->dev, "end_transaction %s\n",
+ ata_cmd2str(request));
+
+ /* XXX SOS convert the request from the format used, only BBB for now*/
+
+ /* ATA/ATAPI IDENTIFY needs special treatment */
+ if ((request->flags & ATA_R_ATAPI) &&
+ (request->u.atapi.ccb[0] == ATAPI_INQUIRY)) {
+ struct ata_device *atadev = device_get_softc(request->dev);
+ struct atapi_inquiry *inquiry = (struct atapi_inquiry *)request->data;
+ u_int16_t *ptr;
+
+ /* convert inquiry data into simple ata_param like format */
+ atadev->param.config = ATA_PROTO_ATAPI | ATA_PROTO_ATAPI_12;
+ atadev->param.config |= (inquiry->device_type & 0x1f) << 8;
+ bzero(atadev->param.model, sizeof(atadev->param.model));
+ strncpy(atadev->param.model, inquiry->vendor, 8);
+ strcpy(atadev->param.model, " ");
+ strncpy(atadev->param.model, inquiry->product, 16);
+ ptr = (u_int16_t*)(atadev->param.model + sizeof(atadev->param.model));
+ while (--ptr >= (u_int16_t*)atadev->param.model)
+ *ptr = ntohs(*ptr);
+ strncpy(atadev->param.revision, inquiry->revision, 4);
+ ptr=(u_int16_t*)(atadev->param.revision+sizeof(atadev->param.revision));
+ while (--ptr >= (u_int16_t*)atadev->param.revision)
+ *ptr = ntohs(*ptr);
+ request->result = 0;
+ }
+ return ATA_OP_FINISHED;
+}
+
+static int
+ata_usbchannel_probe(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ device_t *children;
+ int count, i;
+ char buffer[32];
+
+ /* take care of green memory */
+ bzero(ch, sizeof(struct ata_channel));
+
+ /* find channel number on this controller */
+ device_get_children(device_get_parent(dev), &children, &count);
+ for (i = 0; i < count; i++) {
+ if (children[i] == dev)
+ ch->unit = i;
+ }
+ free(children, M_TEMP);
+
+ sprintf(buffer, "USB lun %d", ch->unit);
+ device_set_desc_copy(dev, buffer);
+
+ return 0;
+}
+
+static int
+ata_usbchannel_attach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* initialize the softc basics */
+ ch->dev = dev;
+ ch->state = ATA_IDLE;
+ ch->hw.begin_transaction = ata_usbchannel_begin_transaction;
+ ch->hw.end_transaction = ata_usbchannel_end_transaction;
+ ch->hw.status = NULL;
+ ch->hw.command = NULL;
+ bzero(&ch->state_mtx, sizeof(struct mtx));
+ mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF);
+ bzero(&ch->queue_mtx, sizeof(struct mtx));
+ mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF);
+ TAILQ_INIT(&ch->ata_queue);
+
+ /* XXX SOS reset the controller HW, the channel and device(s) */
+ //ATA_RESET(dev);
+
+ /* probe and attach device on this channel */
+ ch->devices = ATA_ATAPI_MASTER;
+ if (!ata_delayed_attach)
+ ata_identify(dev);
+ return 0;
+}
+
+static int
+ata_usbchannel_detach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ device_t *children;
+ int nchildren, i;
+
+ /* detach & delete all children */
+ if (!device_get_children(dev, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ if (children[i])
+ device_delete_child(dev, children[i]);
+ free(children, M_TEMP);
+ }
+ mtx_destroy(&ch->state_mtx);
+ mtx_destroy(&ch->queue_mtx);
+ return 0;
+}
+
+static void
+ata_usbchannel_setmode(device_t parent, device_t dev)
+{
+ struct atausb_softc *sc = device_get_softc(GRANDPARENT(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ usbd_device_handle udev;
+
+ usbd_interface2device_handle(sc->iface, &udev);
+ if (usbd_get_speed(udev) == USB_SPEED_HIGH)
+ atadev->mode = ATA_USB2;
+ else
+ atadev->mode = ATA_USB1;
+}
+
+static int
+ata_usbchannel_locking(device_t dev, int flags)
+{
+ struct atausb_softc *sc = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int res = -1;
+
+
+ mtx_lock(&sc->locked_mtx);
+ switch (flags) {
+ case ATA_LF_LOCK:
+ if (sc->locked_ch == NULL)
+ sc->locked_ch = ch;
+ if (sc->locked_ch != ch)
+ sc->restart_ch = ch;
+ break;
+
+ case ATA_LF_UNLOCK:
+ if (sc->locked_ch == ch) {
+ sc->locked_ch = NULL;
+ if (sc->restart_ch) {
+ ch = sc->restart_ch;
+ sc->restart_ch = NULL;
+ mtx_unlock(&sc->locked_mtx);
+ ata_start(ch->dev);
+ return res;
+ }
+ }
+ break;
+
+ case ATA_LF_WHICH:
+ break;
+ }
+ if (sc->locked_ch)
+ res = sc->locked_ch->unit;
+ mtx_unlock(&sc->locked_mtx);
+ return res;
+}
+
+static device_method_t ata_usbchannel_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_usbchannel_probe),
+ DEVMETHOD(device_attach, ata_usbchannel_attach),
+ DEVMETHOD(device_detach, ata_usbchannel_detach),
+
+ /* ATA methods */
+ DEVMETHOD(ata_setmode, ata_usbchannel_setmode),
+ DEVMETHOD(ata_locking, ata_usbchannel_locking),
+ //DEVMETHOD(ata_reset, ata_usbchannel_reset),
+
+ { 0, 0 }
+};
+
+static driver_t ata_usbchannel_driver = {
+ "ata",
+ ata_usbchannel_methods,
+ sizeof(struct ata_channel),
+};
+
+DRIVER_MODULE(ata, atausb, ata_usbchannel_driver, ata_devclass, 0, 0);
+MODULE_DEPEND(atausb, ata, 1, 1, 1);
diff --git a/sys/dev/ata/ata_if.m b/sys/dev/ata/ata_if.m
new file mode 100644
index 000000000000..19d6987d6206
--- /dev/null
+++ b/sys/dev/ata/ata_if.m
@@ -0,0 +1,78 @@
+# Copyright (c) 2004 - 2006 Søren Schmidt <sos@FreeBSD.org>
+# 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.
+#
+# 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 <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/callout.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/bus.h>
+#include <sys/ata.h>
+#include <dev/ata/ata-all.h>
+
+INTERFACE ata;
+
+CODE {
+ static int ata_null_locking(device_t dev, int mode)
+ {
+ struct ata_channel *ch = device_get_softc(dev);
+
+ return ch->unit;
+ }
+};
+METHOD int locking {
+ device_t channel;
+ int mode;
+} DEFAULT ata_null_locking;
+HEADER {
+#define ATA_LF_LOCK 0x0001
+#define ATA_LF_UNLOCK 0x0002
+#define ATA_LF_WHICH 0x0004
+};
+
+CODE {
+ static void ata_null_setmode(device_t parent, device_t dev)
+ {
+ struct ata_device *atadev = device_get_softc(dev);
+
+ atadev->mode = ata_limit_mode(dev, atadev->mode, ATA_PIO_MAX);
+ }
+};
+METHOD void setmode {
+ device_t channel;
+ device_t dev;
+} DEFAULT ata_null_setmode;;
+
+METHOD void reset {
+ device_t channel;
+} DEFAULT ata_generic_reset;
+
+METHOD int reinit {
+ device_t dev;
+};
diff --git a/sys/dev/ata/atapi-cam.c b/sys/dev/ata/atapi-cam.c
new file mode 100644
index 000000000000..8a2463898c24
--- /dev/null
+++ b/sys/dev/ata/atapi-cam.c
@@ -0,0 +1,901 @@
+/*-
+ * Copyright (c) 2001-2003 Thomas Quinot <thomas@cuivre.fr.eu.org>
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/ata.h>
+#include <sys/taskqueue.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sema.h>
+#include <vm/uma.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_periph.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+#include <cam/scsi/scsi_all.h>
+
+#include <dev/ata/ata-all.h>
+#include <ata_if.h>
+
+/* private data associated with an ATA bus */
+struct atapi_xpt_softc {
+ struct ata_device atapi_cam_dev; /* must be first */
+ device_t dev;
+ device_t parent;
+ struct ata_channel *ata_ch;
+ struct cam_path *path;
+ struct cam_sim *sim;
+ int flags;
+#define BUS_REGISTERED 0x01
+#define RESOURCE_SHORTAGE 0x02
+#define DETACHING 0x04
+
+ TAILQ_HEAD(,atapi_hcb) pending_hcbs;
+ struct ata_device *atadev[2];
+ struct mtx state_lock;
+};
+
+/* hardware command descriptor block */
+struct atapi_hcb {
+ struct atapi_xpt_softc *softc;
+ int unit;
+ int bus;
+ int target;
+ int lun;
+ union ccb *ccb;
+ int flags;
+#define QUEUED 0x0001
+#define AUTOSENSE 0x0002
+ char *dxfer_alloc;
+ TAILQ_ENTRY(atapi_hcb) chain;
+};
+
+enum reinit_reason { BOOT_ATTACH, ATTACH, RESET };
+
+/* Device methods */
+static void atapi_cam_identify(device_t *dev, device_t parent);
+static int atapi_cam_probe(device_t dev);
+static int atapi_cam_attach(device_t dev);
+static int atapi_cam_detach(device_t dev);
+static int atapi_cam_reinit(device_t dev);
+
+/* CAM XPT methods */
+static void atapi_action(struct cam_sim *, union ccb *);
+static void atapi_poll(struct cam_sim *);
+static void atapi_async(void *, u_int32_t, struct cam_path *, void *);
+static void atapi_cb(struct ata_request *);
+
+/* Module methods */
+static int atapi_cam_event_handler(module_t mod, int what, void *arg);
+
+/* internal functions */
+static void reinit_bus(struct atapi_xpt_softc *scp, enum reinit_reason reason);
+static void setup_async_cb(struct atapi_xpt_softc *, uint32_t);
+static void cam_rescan_callback(struct cam_periph *, union ccb *);
+static void cam_rescan(struct cam_sim *);
+static void free_hcb_and_ccb_done(struct atapi_hcb *, u_int32_t);
+static struct atapi_hcb *allocate_hcb(struct atapi_xpt_softc *, int, int, union ccb *);
+static void free_hcb(struct atapi_hcb *hcb);
+static void free_softc(struct atapi_xpt_softc *scp);
+
+static MALLOC_DEFINE(M_ATACAM, "ata_cam", "ATA driver CAM-XPT layer");
+
+static device_method_t atapi_cam_methods[] = {
+ DEVMETHOD(device_identify, atapi_cam_identify),
+ DEVMETHOD(device_probe, atapi_cam_probe),
+ DEVMETHOD(device_attach, atapi_cam_attach),
+ DEVMETHOD(device_detach, atapi_cam_detach),
+ DEVMETHOD(ata_reinit, atapi_cam_reinit),
+ {0, 0}
+};
+
+static driver_t atapi_cam_driver = {
+ "atapicam",
+ atapi_cam_methods,
+ sizeof(struct atapi_xpt_softc)
+};
+
+static devclass_t atapi_cam_devclass;
+DRIVER_MODULE(atapicam, ata,
+ atapi_cam_driver,
+ atapi_cam_devclass,
+ atapi_cam_event_handler,
+ /*arg*/NULL);
+MODULE_VERSION(atapicam, 1);
+MODULE_DEPEND(atapicam, ata, 1, 1, 1);
+MODULE_DEPEND(atapicam, cam, 1, 1, 1);
+
+static void
+atapi_cam_identify(device_t *dev, device_t parent)
+{
+ struct atapi_xpt_softc *scp =
+ malloc (sizeof (struct atapi_xpt_softc), M_ATACAM, M_NOWAIT|M_ZERO);
+ device_t child;
+
+ if (scp == NULL) {
+ printf ("atapi_cam_identify: out of memory");
+ return;
+ }
+
+ /* Assume one atapicam instance per parent channel instance. */
+ child = device_add_child(parent, "atapicam", -1);
+ if (child == NULL) {
+ printf ("atapi_cam_identify: out of memory, can't add child");
+ free (scp, M_ATACAM);
+ return;
+ }
+ scp->atapi_cam_dev.unit = -1;
+ scp->atapi_cam_dev.dev = child;
+ device_quiet(child);
+ device_set_softc(child, scp);
+}
+
+static int
+atapi_cam_probe(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc (dev);
+
+ KASSERT(atadev != NULL, ("expect valid struct ata_device"));
+ if (atadev->unit < 0) {
+ device_set_desc(dev, "ATAPI CAM Attachment");
+ return (0);
+ } else {
+ return ENXIO;
+ }
+}
+
+static int
+atapi_cam_attach(device_t dev)
+{
+ struct atapi_xpt_softc *scp = NULL;
+ struct cam_devq *devq = NULL;
+ struct cam_sim *sim = NULL;
+ struct cam_path *path = NULL;
+ int unit, error;
+
+ scp = (struct atapi_xpt_softc *)device_get_softc(dev);
+ if (scp == NULL) {
+ device_printf(dev, "Cannot get softc\n");
+ return (ENOMEM);
+ }
+
+ mtx_init(&scp->state_lock, "ATAPICAM lock", NULL, MTX_DEF);
+
+ scp->dev = dev;
+ scp->parent = device_get_parent(dev);
+ scp->ata_ch = device_get_softc(scp->parent);
+ TAILQ_INIT(&scp->pending_hcbs);
+ unit = device_get_unit(dev);
+
+ if ((devq = cam_simq_alloc(16)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ if ((sim = cam_sim_alloc(atapi_action, atapi_poll, "ata",
+ (void *)scp, unit, 1, 1, devq)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ scp->sim = sim;
+
+ if (xpt_bus_register(sim, 0) != CAM_SUCCESS) {
+ error = EINVAL;
+ goto out;
+ }
+ scp->flags |= BUS_REGISTERED;
+
+ if (xpt_create_path(&path, /*periph*/ NULL,
+ cam_sim_path(sim), CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ error = ENOMEM;
+ goto out;
+ }
+ scp->path = path;
+
+ CAM_DEBUG(path, CAM_DEBUG_TRACE, ("Registered SIM for ata%d\n", unit));
+
+ setup_async_cb(scp, AC_LOST_DEVICE);
+ reinit_bus(scp, cold ? BOOT_ATTACH : ATTACH);
+ error = 0;
+
+out:
+ if (error != 0)
+ free_softc(scp);
+
+ return (error);
+}
+
+static int
+atapi_cam_detach(device_t dev)
+{
+ struct atapi_xpt_softc *scp = device_get_softc(dev);
+
+ mtx_lock(&Giant);
+ xpt_freeze_simq(scp->sim, 1 /*count*/);
+ mtx_unlock(&Giant);
+ mtx_lock(&scp->state_lock);
+ scp->flags |= DETACHING;
+ mtx_unlock(&scp->state_lock);
+ free_softc(scp);
+ return (0);
+}
+
+static int
+atapi_cam_reinit(device_t dev) {
+ struct atapi_xpt_softc *scp = device_get_softc(dev);
+
+ /*
+ * scp might be null if the bus is being reinitialised during
+ * the boot-up sequence, before the ATAPI bus is registered.
+ */
+
+ if (scp != NULL) {
+ reinit_bus(scp, RESET);
+ }
+ return (0);
+}
+
+static void
+reinit_bus(struct atapi_xpt_softc *scp, enum reinit_reason reason) {
+ struct ata_device *atadev;
+ device_t *children;
+ int nchildren, i;
+
+ if (device_get_children(scp->parent, &children, &nchildren) != 0) {
+ return;
+ }
+
+ mtx_lock(&scp->state_lock);
+ scp->atadev[0] = NULL;
+ scp->atadev[1] = NULL;
+
+ for (i = 0; i < nchildren; i++) {
+ /* XXX Does the child need to actually be attached yet? */
+ if (children[i] != NULL) {
+ atadev = device_get_softc(children[i]);
+ if ((atadev->unit == ATA_MASTER) &&
+ (scp->ata_ch->devices & ATA_ATAPI_MASTER) != 0)
+ scp->atadev[0] = atadev;
+ if ((atadev->unit == ATA_SLAVE) &&
+ (scp->ata_ch->devices & ATA_ATAPI_SLAVE) != 0)
+ scp->atadev[1] = atadev;
+ }
+ }
+ mtx_unlock(&scp->state_lock);
+ free(children, M_TEMP);
+
+ switch (reason) {
+ case BOOT_ATTACH:
+ break;
+ case RESET:
+ xpt_async(AC_BUS_RESET, scp->path, NULL);
+ /*FALLTHROUGH*/
+ case ATTACH:
+ cam_rescan(scp->sim);
+ break;
+ }
+}
+
+static void
+setup_async_cb(struct atapi_xpt_softc *scp, uint32_t events)
+{
+ struct ccb_setasync csa;
+
+ mtx_lock(&Giant);
+ xpt_setup_ccb(&csa.ccb_h, scp->path, /*priority*/ 5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = events;
+ csa.callback = &atapi_async;
+ csa.callback_arg = scp->sim;
+ xpt_action((union ccb *) &csa);
+ mtx_unlock(&Giant);
+}
+
+static void
+atapi_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct atapi_xpt_softc *softc = (struct atapi_xpt_softc*)cam_sim_softc(sim);
+ struct ccb_hdr *ccb_h = &ccb->ccb_h;
+ struct atapi_hcb *hcb = NULL;
+ struct ata_request *request = NULL;
+ int unit = cam_sim_unit(sim);
+ int bus = cam_sim_bus(sim);
+ int len;
+ char *buf;
+
+ switch (ccb_h->func_code) {
+ case XPT_PATH_INQ: {
+ struct ccb_pathinq *cpi = &ccb->cpi;
+ int tid = ccb_h->target_id;
+
+ cpi->version_num = 1;
+ cpi->hba_inquiry = 0;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = PIM_NO_6_BYTE;
+ cpi->hba_eng_cnt = 0;
+ bzero(cpi->vuhba_flags, sizeof(cpi->vuhba_flags));
+ cpi->max_target = 1;
+ cpi->max_lun = 0;
+ cpi->async_flags = 0;
+ cpi->hpath_id = 0;
+ cpi->initiator_id = 7;
+ strncpy(cpi->sim_vid, "FreeBSD", sizeof(cpi->sim_vid));
+ strncpy(cpi->hba_vid, "ATAPI", sizeof(cpi->hba_vid));
+ strncpy(cpi->dev_name, cam_sim_name(sim), sizeof cpi->dev_name);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->bus_id = cam_sim_bus(sim);
+ cpi->base_transfer_speed = 3300;
+
+ if (softc->ata_ch && tid != CAM_TARGET_WILDCARD) {
+ mtx_lock(&softc->state_lock);
+ if (softc->atadev[tid] == NULL) {
+ ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+ xpt_done(ccb);
+ mtx_unlock(&softc->state_lock);
+ return;
+ }
+ switch (softc->atadev[ccb_h->target_id]->mode) {
+ case ATA_PIO1:
+ cpi->base_transfer_speed = 5200;
+ break;
+ case ATA_PIO2:
+ cpi->base_transfer_speed = 7000;
+ break;
+ case ATA_PIO3:
+ cpi->base_transfer_speed = 11000;
+ break;
+ case ATA_PIO4:
+ case ATA_DMA:
+ case ATA_WDMA2:
+ cpi->base_transfer_speed = 16000;
+ break;
+ case ATA_UDMA2:
+ cpi->base_transfer_speed = 33000;
+ break;
+ case ATA_UDMA4:
+ cpi->base_transfer_speed = 66000;
+ break;
+ case ATA_UDMA5:
+ cpi->base_transfer_speed = 100000;
+ break;
+ case ATA_UDMA6:
+ cpi->base_transfer_speed = 133000;
+ break;
+ default:
+ break;
+ }
+ mtx_unlock(&softc->state_lock);
+ }
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+ }
+
+ case XPT_RESET_DEV: {
+ int tid = ccb_h->target_id;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("dev reset\n"));
+ ata_controlcmd(softc->atadev[tid]->dev, ATA_DEVICE_RESET, 0, 0, 0);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+ }
+
+ case XPT_RESET_BUS:
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("bus reset\n"));
+ ata_reinit(softc->parent);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+
+ case XPT_SET_TRAN_SETTINGS:
+ /* ignore these, we're not doing SCSI here */
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE,
+ ("SET_TRAN_SETTINGS not supported\n"));
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ xpt_done(ccb);
+ return;
+
+ case XPT_GET_TRAN_SETTINGS: {
+ struct ccb_trans_settings *cts = &ccb->cts;
+
+ /*
+ * XXX The default CAM transport code is very SCSI-specific and
+ * doesn't understand IDE speeds very well. Be silent about it
+ * here and let it default to what is set in XPT_PATH_INQ
+ */
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("GET_TRAN_SETTINGS\n"));
+ cts->valid = (CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID);
+ cts->flags &= ~(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return;
+ }
+
+ case XPT_CALC_GEOMETRY: {
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("CALC_GEOMETRY\n"));
+ cam_calc_geometry(&ccb->ccg, /*extended*/1);
+ xpt_done(ccb);
+ return;
+ }
+
+ case XPT_SCSI_IO: {
+ struct ccb_scsiio *csio = &ccb->csio;
+ int tid = ccb_h->target_id, lid = ccb_h->target_lun;
+ int request_flags = ATA_R_QUIET | ATA_R_ATAPI;
+
+ CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("XPT_SCSI_IO\n"));
+
+ mtx_lock(&softc->state_lock);
+ if (softc->flags & DETACHING) {
+ ccb->ccb_h.status = CAM_REQ_ABORTED;
+ xpt_done(ccb);
+ mtx_unlock(&softc->state_lock);
+ return;
+ }
+
+ if (softc->atadev[tid] == NULL) {
+ ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+ xpt_done(ccb);
+ mtx_unlock(&softc->state_lock);
+ return;
+ }
+
+ /* check that this request was not aborted already */
+ if ((ccb_h->status & CAM_STATUS_MASK) != CAM_REQ_INPROG) {
+ printf("XPT_SCSI_IO received but already in progress?\n");
+ xpt_done(ccb);
+ mtx_unlock(&softc->state_lock);
+ return;
+ }
+ if (lid > 0) {
+ CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE,
+ ("SCSI IO received for invalid lun %d\n", lid));
+ goto action_invalid;
+ }
+ if (csio->cdb_len > sizeof request->u.atapi.ccb) {
+ CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE,
+ ("CAM CCB too long for ATAPI"));
+ goto action_invalid;
+ }
+ if ((ccb_h->flags & CAM_SCATTER_VALID)) {
+ /* scatter-gather not supported */
+ xpt_print_path(ccb_h->path);
+ printf("ATAPI/CAM does not support scatter-gather yet!\n");
+ goto action_invalid;
+ }
+
+ switch (ccb_h->flags & CAM_DIR_MASK) {
+ case CAM_DIR_IN:
+ request_flags |= ATA_R_READ|ATA_R_DMA;
+ break;
+ case CAM_DIR_OUT:
+ request_flags |= ATA_R_WRITE|ATA_R_DMA;
+ break;
+ case CAM_DIR_NONE:
+ /* No flags need to be set */
+ break;
+ default:
+ device_printf(softc->dev, "unknown IO operation\n");
+ goto action_invalid;
+ }
+ if (softc->atadev[tid]->mode < ATA_DMA)
+ request_flags &= ~ATA_R_DMA;
+
+ if ((hcb = allocate_hcb(softc, unit, bus, ccb)) == NULL) {
+ printf("cannot allocate ATAPI/CAM hcb\n");
+ goto action_oom;
+ }
+ if ((request = ata_alloc_request()) == NULL) {
+ printf("cannot allocate ATAPI/CAM request\n");
+ goto action_oom;
+ }
+
+ bcopy((ccb_h->flags & CAM_CDB_POINTER) ?
+ csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes,
+ request->u.atapi.ccb, csio->cdb_len);
+#ifdef CAMDEBUG
+ if (CAM_DEBUGGED(ccb_h->path, CAM_DEBUG_CDB)) {
+ char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1];
+
+ printf("atapi_action: hcb@%p: %s\n", hcb,
+ scsi_cdb_string(request->u.atapi.ccb, cdb_str, sizeof(cdb_str)));
+ }
+#endif
+
+ len = csio->dxfer_len;
+ buf = csio->data_ptr;
+
+ /* some SCSI commands require special processing */
+ switch (request->u.atapi.ccb[0]) {
+ case INQUIRY: {
+ /*
+ * many ATAPI devices seem to report more than
+ * SHORT_INQUIRY_LENGTH bytes of available INQUIRY
+ * information, but respond with some incorrect condition
+ * when actually asked for it, so we are going to pretend
+ * that only SHORT_INQUIRY_LENGTH are expected, anyway.
+ */
+ struct scsi_inquiry *inq = (struct scsi_inquiry *) &request->u.atapi.ccb[0];
+
+ if (inq->byte2 == 0 && inq->page_code == 0 &&
+ inq->length > SHORT_INQUIRY_LENGTH) {
+ bzero(buf, len);
+ len = inq->length = SHORT_INQUIRY_LENGTH;
+ }
+ break;
+ }
+ case READ_6:
+ /* FALLTHROUGH */
+
+ case WRITE_6:
+ CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE,
+ ("Translating %s into _10 equivalent\n",
+ (request->u.atapi.ccb[0] == READ_6) ? "READ_6" : "WRITE_6"));
+ request->u.atapi.ccb[0] |= 0x20;
+ request->u.atapi.ccb[9] = request->u.atapi.ccb[5];
+ request->u.atapi.ccb[8] = request->u.atapi.ccb[4];
+ request->u.atapi.ccb[7] = 0;
+ request->u.atapi.ccb[6] = 0;
+ request->u.atapi.ccb[5] = request->u.atapi.ccb[3];
+ request->u.atapi.ccb[4] = request->u.atapi.ccb[2];
+ request->u.atapi.ccb[3] = request->u.atapi.ccb[1] & 0x1f;
+ request->u.atapi.ccb[2] = 0;
+ request->u.atapi.ccb[1] = 0;
+ break;
+ }
+
+ if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN && (len & 1)) {
+ /* ATA always transfers an even number of bytes */
+ if ((buf = hcb->dxfer_alloc
+ = malloc(++len, M_ATACAM, M_NOWAIT | M_ZERO)) == NULL) {
+ printf("cannot allocate ATAPI/CAM buffer\n");
+ goto action_oom;
+ }
+ }
+ request->dev = softc->atadev[tid]->dev;
+ request->driver = hcb;
+ request->data = buf;
+ request->bytecount = len;
+ request->transfersize = min(request->bytecount, 65534);
+ request->timeout = ccb_h->timeout / 1000; /* XXX lost granularity */
+ request->retries = 2;
+ request->callback = &atapi_cb;
+ request->flags = request_flags;
+
+ TAILQ_INSERT_TAIL(&softc->pending_hcbs, hcb, chain);
+ hcb->flags |= QUEUED;
+ ccb_h->status |= CAM_SIM_QUEUED;
+ mtx_unlock(&softc->state_lock);
+
+ ata_queue_request(request);
+ return;
+ }
+
+ default:
+ CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE,
+ ("unsupported function code 0x%02x\n", ccb_h->func_code));
+ goto action_invalid;
+ }
+
+ /* NOTREACHED */
+
+action_oom:
+ if (request != NULL)
+ ata_free_request(request);
+ if (hcb != NULL)
+ free_hcb(hcb);
+ mtx_unlock(&softc->state_lock);
+ mtx_lock(&Giant);
+ xpt_print_path(ccb_h->path);
+ printf("out of memory, freezing queue.\n");
+ softc->flags |= RESOURCE_SHORTAGE;
+ xpt_freeze_simq(sim, /*count*/ 1);
+ mtx_unlock(&Giant);
+ ccb_h->status = CAM_REQUEUE_REQ;
+ xpt_done(ccb);
+ return;
+
+action_invalid:
+ mtx_unlock(&softc->state_lock);
+ ccb_h->status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ return;
+}
+
+static void
+atapi_poll(struct cam_sim *sim)
+{
+ /* do nothing - we do not actually service any interrupts */
+ printf("atapi_poll called!\n");
+}
+
+static void
+atapi_cb(struct ata_request *request)
+{
+ struct atapi_xpt_softc *scp;
+ struct atapi_hcb *hcb;
+ struct ccb_scsiio *csio;
+ u_int32_t rc;
+
+ hcb = (struct atapi_hcb *)request->driver;
+ scp = hcb->softc;
+ csio = &hcb->ccb->csio;
+
+#ifdef CAMDEBUG
+# define err (request->u.atapi.sense.key)
+ if (CAM_DEBUGGED(csio->ccb_h.path, CAM_DEBUG_CDB)) {
+ printf("atapi_cb: hcb@%p error = %02x: (sk = %02x%s%s%s)\n",
+ hcb, err, err >> 4,
+ (err & 4) ? " ABRT" : "",
+ (err & 2) ? " EOM" : "",
+ (err & 1) ? " ILI" : "");
+ printf("dev %s: cmd %02x status %02x result %02x\n",
+ device_get_nameunit(request->dev), request->u.atapi.ccb[0],
+ request->status, request->result);
+ }
+#endif
+
+ if ((hcb->flags & AUTOSENSE) != 0) {
+ rc = CAM_SCSI_STATUS_ERROR;
+ if (request->result == 0) {
+ csio->ccb_h.status |= CAM_AUTOSNS_VALID;
+ }
+ } else if (request->result != 0) {
+ rc = CAM_SCSI_STATUS_ERROR;
+ csio->scsi_status = SCSI_STATUS_CHECK_COND;
+
+ if ((csio->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) {
+#if 0
+ static const int8_t ccb[16] = { ATAPI_REQUEST_SENSE, 0, 0, 0,
+ sizeof(struct atapi_sense), 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0 };
+
+ bcopy (ccb, request->u.atapi.ccb, sizeof ccb);
+ request->data = (caddr_t)&csio->sense_data;
+ request->bytecount = sizeof(struct atapi_sense);
+ request->transfersize = min(request->bytecount, 65534);
+ request->timeout = csio->ccb_h.timeout / 1000;
+ request->retries = 2;
+ request->flags = ATA_R_QUIET|ATA_R_ATAPI|ATA_R_IMMEDIATE;
+ hcb->flags |= AUTOSENSE;
+
+ ata_queue_request(request);
+ return;
+#else
+ /* The ATA driver has already requested sense for us. */
+ if (request->error == 0) {
+ /* The ATA autosense suceeded. */
+ bcopy (&request->u.atapi.sense, &csio->sense_data, sizeof(struct atapi_sense));
+ csio->ccb_h.status |= CAM_AUTOSNS_VALID;
+ }
+#endif
+ }
+ } else {
+ rc = CAM_REQ_CMP;
+ csio->scsi_status = SCSI_STATUS_OK;
+ if (((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) &&
+ hcb->dxfer_alloc != NULL)
+ {
+ bcopy(hcb->dxfer_alloc, csio->data_ptr, csio->dxfer_len);
+ }
+ }
+
+ mtx_lock(&scp->state_lock);
+ free_hcb_and_ccb_done(hcb, rc);
+ mtx_unlock(&scp->state_lock);
+
+ ata_free_request(request);
+}
+
+static void
+free_hcb_and_ccb_done(struct atapi_hcb *hcb, u_int32_t status)
+{
+ struct atapi_xpt_softc *softc;
+ union ccb *ccb;
+
+ if (hcb == NULL)
+ return;
+
+ softc = hcb->softc;
+ ccb = hcb->ccb;
+
+ /* we're about to free a hcb, so the shortage has ended */
+ if (softc->flags & RESOURCE_SHORTAGE) {
+ softc->flags &= ~RESOURCE_SHORTAGE;
+ status |= CAM_RELEASE_SIMQ;
+ }
+ free_hcb(hcb);
+ ccb->ccb_h.status =
+ status | (ccb->ccb_h.status & ~(CAM_STATUS_MASK | CAM_SIM_QUEUED));
+ xpt_done(ccb);
+}
+
+static void
+atapi_async(void *callback_arg, u_int32_t code,
+ struct cam_path* path, void *arg)
+{
+ struct atapi_xpt_softc *softc;
+ struct cam_sim *sim;
+ int targ;
+
+ GIANT_REQUIRED;
+
+ sim = (struct cam_sim *) callback_arg;
+ softc = (struct atapi_xpt_softc *) cam_sim_softc(sim);
+ switch (code) {
+ case AC_LOST_DEVICE:
+ targ = xpt_path_target_id(path);
+ xpt_print_path(path);
+ if (targ == -1)
+ printf("Lost host adapter\n");
+ else
+ printf("Lost target %d???\n", targ);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
+{
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("Rescan failed, 0x%04x\n", ccb->ccb_h.status));
+ } else {
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("Rescan succeeded\n"));
+ }
+ xpt_free_path(ccb->ccb_h.path);
+ free(ccb, M_ATACAM);
+}
+
+static void
+cam_rescan(struct cam_sim *sim)
+{
+ struct cam_path *path;
+ union ccb *ccb = malloc(sizeof(union ccb), M_ATACAM, M_WAITOK | M_ZERO);
+
+ mtx_lock(&Giant);
+ if (xpt_create_path(&path, xpt_periph, cam_sim_path(sim),
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ mtx_unlock(&Giant);
+ free(ccb, M_ATACAM);
+ return;
+ }
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("Rescanning ATAPI bus.\n"));
+ xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/);
+ ccb->ccb_h.func_code = XPT_SCAN_BUS;
+ ccb->ccb_h.cbfcnp = cam_rescan_callback;
+ ccb->crcn.flags = CAM_FLAG_NONE;
+ xpt_action(ccb);
+ /* scan is in progress now */
+ mtx_unlock(&Giant);
+}
+
+static struct atapi_hcb *
+allocate_hcb(struct atapi_xpt_softc *softc, int unit, int bus, union ccb *ccb)
+{
+ struct atapi_hcb *hcb = (struct atapi_hcb *)
+ malloc(sizeof(struct atapi_hcb), M_ATACAM, M_NOWAIT | M_ZERO);
+
+ if (hcb != NULL) {
+ hcb->softc = softc;
+ hcb->unit = unit;
+ hcb->bus = bus;
+ hcb->ccb = ccb;
+ }
+ return hcb;
+}
+
+static void
+free_hcb(struct atapi_hcb *hcb)
+{
+ if ((hcb->flags & QUEUED) != 0)
+ TAILQ_REMOVE(&hcb->softc->pending_hcbs, hcb, chain);
+ if (hcb->dxfer_alloc != NULL)
+ free(hcb->dxfer_alloc, M_ATACAM);
+ free(hcb, M_ATACAM);
+}
+
+static void
+free_softc(struct atapi_xpt_softc *scp)
+{
+ struct atapi_hcb *hcb;
+
+ if (scp != NULL) {
+ mtx_lock(&scp->state_lock);
+ TAILQ_FOREACH(hcb, &scp->pending_hcbs, chain) {
+ free_hcb_and_ccb_done(hcb, CAM_UNREC_HBA_ERROR);
+ }
+ mtx_unlock(&scp->state_lock);
+ mtx_lock(&Giant);
+ if (scp->path != NULL) {
+ setup_async_cb(scp, 0);
+ xpt_free_path(scp->path);
+ }
+ if ((scp->flags & BUS_REGISTERED) != 0) {
+ if (xpt_bus_deregister(cam_sim_path(scp->sim)) == CAM_REQ_CMP)
+ scp->flags &= ~BUS_REGISTERED;
+ }
+ if (scp->sim != NULL) {
+ if ((scp->flags & BUS_REGISTERED) == 0)
+ cam_sim_free(scp->sim, /*free_devq*/TRUE);
+ else
+ printf("Can't free %s SIM (still registered)\n",
+ cam_sim_name(scp->sim));
+ }
+ mtx_unlock(&Giant);
+ mtx_destroy(&scp->state_lock);
+ }
+}
+
+static int
+atapi_cam_event_handler(module_t mod, int what, void *arg) {
+ device_t *devlist;
+ int devcount;
+
+ switch (what) {
+ case MOD_UNLOAD:
+ if (devclass_get_devices(atapi_cam_devclass, &devlist, &devcount)
+ != 0)
+ return ENXIO;
+ if (devlist != NULL) {
+ while (devlist != NULL && devcount > 0) {
+ device_t child = devlist[--devcount];
+ struct atapi_xpt_softc *scp = device_get_softc(child);
+
+ device_delete_child(device_get_parent(child),child);
+ if (scp != NULL)
+ free(scp, M_ATACAM);
+ }
+ free(devlist, M_TEMP);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
diff --git a/sys/dev/ata/atapi-cd.c b/sys/dev/ata/atapi-cd.c
new file mode 100644
index 000000000000..1fd092984f07
--- /dev/null
+++ b/sys/dev/ata/atapi-cd.c
@@ -0,0 +1,1912 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/cdio.h>
+#include <sys/cdrio.h>
+#include <sys/dvdio.h>
+#include <sys/disk.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/ctype.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/bus.h>
+#include <geom/geom.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/atapi-cd.h>
+#include <ata_if.h>
+
+/* prototypes */
+static void acd_geom_attach(void *, int);
+static void acd_geom_detach(void *, int);
+static void acd_set_ioparm(device_t);
+static void acd_describe(device_t);
+static void lba2msf(u_int32_t, u_int8_t *, u_int8_t *, u_int8_t *);
+static u_int32_t msf2lba(u_int8_t, u_int8_t, u_int8_t);
+static int acd_geom_access(struct g_provider *, int, int, int);
+static g_ioctl_t acd_geom_ioctl;
+static void acd_geom_start(struct bio *);
+static void acd_strategy(struct bio *);
+static void acd_done(struct ata_request *);
+static void acd_read_toc(device_t);
+static int acd_play(device_t, int, int);
+static int acd_setchan(device_t, u_int8_t, u_int8_t, u_int8_t, u_int8_t);
+static int acd_init_writer(device_t, int);
+static int acd_fixate(device_t, int);
+static int acd_init_track(device_t, struct cdr_track *);
+static int acd_flush(device_t);
+static int acd_read_track_info(device_t, int32_t, struct acd_track_info *);
+static int acd_get_progress(device_t, int *);
+static int acd_send_cue(device_t, struct cdr_cuesheet *);
+static int acd_report_key(device_t, struct dvd_authinfo *);
+static int acd_send_key(device_t, struct dvd_authinfo *);
+static int acd_read_structure(device_t, struct dvd_struct *);
+static int acd_tray(device_t, int);
+static int acd_blank(device_t, int);
+static int acd_prevent_allow(device_t, int);
+static int acd_start_stop(device_t, int);
+static int acd_pause_resume(device_t, int);
+static int acd_mode_sense(device_t, int, caddr_t, int);
+static int acd_mode_select(device_t, caddr_t, int);
+static int acd_set_speed(device_t, int, int);
+static void acd_get_cap(device_t);
+static int acd_read_format_caps(device_t, struct cdr_format_capacities *);
+static int acd_format(device_t, struct cdr_format_params *);
+static int acd_test_ready(device_t);
+
+/* internal vars */
+static MALLOC_DEFINE(M_ACD, "acd_driver", "ATAPI CD driver buffers");
+static struct g_class acd_class = {
+ .name = "ACD",
+ .version = G_VERSION,
+ .access = acd_geom_access,
+ .ioctl = acd_geom_ioctl,
+ .start = acd_geom_start,
+};
+//DECLARE_GEOM_CLASS(acd_class, acd);
+
+static int
+acd_probe(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if ((atadev->param.config & ATA_PROTO_ATAPI) &&
+ (atadev->param.config & ATA_ATAPI_TYPE_MASK) == ATA_ATAPI_TYPE_CDROM)
+ return 0;
+ else
+ return ENXIO;
+}
+
+static int
+acd_attach(device_t dev)
+{
+ struct acd_softc *cdp;
+
+ if (!(cdp = malloc(sizeof(struct acd_softc), M_ACD, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "out of memory\n");
+ return ENOMEM;
+ }
+ cdp->block_size = 2048;
+ device_set_ivars(dev, cdp);
+ ATA_SETMODE(device_get_parent(dev), dev);
+ ata_controlcmd(dev, ATA_DEVICE_RESET, 0, 0, 0);
+ acd_get_cap(dev);
+ g_post_event(acd_geom_attach, dev, M_WAITOK, NULL);
+
+ /* announce we are here */
+ acd_describe(dev);
+ return 0;
+}
+
+static int
+acd_detach(device_t dev)
+{
+ g_waitfor_event(acd_geom_detach, dev, M_WAITOK, NULL);
+ return 0;
+}
+
+static void
+acd_shutdown(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
+ ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0);
+}
+
+static int
+acd_reinit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ struct acd_softc *cdp = device_get_ivars(dev);
+
+ if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATAPI_MASTER)) ||
+ ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATAPI_SLAVE))) {
+ device_set_ivars(dev, NULL);
+ free(cdp, M_ACD);
+ return 1;
+ }
+ ATA_SETMODE(device_get_parent(dev), dev);
+ return 0;
+}
+
+static void
+acd_geom_attach(void *arg, int flag)
+{
+ struct ata_device *atadev = device_get_softc(arg);
+ struct acd_softc *cdp = device_get_ivars(arg);
+ struct g_geom *gp;
+ struct g_provider *pp;
+
+ g_topology_assert();
+ gp = g_new_geomf(&acd_class, "acd%d", device_get_unit(arg));
+ gp->softc = arg;
+ cdp->gp = gp;
+ pp = g_new_providerf(gp, "acd%d", device_get_unit(arg));
+ pp->index = 0;
+ cdp->pp[0] = pp;
+ g_error_provider(pp, 0);
+ atadev->flags |= ATA_D_MEDIA_CHANGED;
+ acd_set_ioparm(arg);
+}
+
+static void
+acd_geom_detach(void *arg, int flag)
+{
+ struct acd_softc *cdp = device_get_ivars(arg);
+
+ /* signal geom so we dont get any further requests */
+ g_wither_geom(cdp->gp, ENXIO);
+
+ /* fail requests on the queue and any thats "in flight" for this device */
+ ata_fail_requests(arg);
+
+ /* dont leave anything behind */
+ device_set_ivars(arg, NULL);
+ free(cdp, M_ACD);
+}
+
+static int
+acd_geom_ioctl(struct g_provider *pp, u_long cmd, void *addr, int fflag, struct thread *td)
+{
+ device_t dev = pp->geom->softc;
+ struct ata_device *atadev = device_get_softc(dev);
+ struct acd_softc *cdp = device_get_ivars(dev);
+ int error = 0, nocopyout = 0;
+
+ if (!cdp)
+ return ENXIO;
+
+ if (atadev->flags & ATA_D_MEDIA_CHANGED) {
+ switch (cmd) {
+ case CDIOCRESET:
+ acd_test_ready(dev);
+ break;
+
+ default:
+ acd_read_toc(dev);
+ acd_prevent_allow(dev, 1);
+ cdp->flags |= F_LOCKED;
+ break;
+ }
+ }
+
+ switch (cmd) {
+
+ case CDIOCRESUME:
+ error = acd_pause_resume(dev, 1);
+ break;
+
+ case CDIOCPAUSE:
+ error = acd_pause_resume(dev, 0);
+ break;
+
+ case CDIOCSTART:
+ error = acd_start_stop(dev, 1);
+ break;
+
+ case CDIOCSTOP:
+ error = acd_start_stop(dev, 0);
+ break;
+
+ case CDIOCALLOW:
+ error = acd_prevent_allow(dev, 0);
+ cdp->flags &= ~F_LOCKED;
+ break;
+
+ case CDIOCPREVENT:
+ error = acd_prevent_allow(dev, 1);
+ cdp->flags |= F_LOCKED;
+ break;
+
+ case CDIOCRESET:
+ error = suser(td);
+ if (error)
+ break;
+ error = acd_test_ready(dev);
+ break;
+
+ case CDIOCEJECT:
+ if (pp->acr != 1) {
+ error = EBUSY;
+ break;
+ }
+ error = acd_tray(dev, 0);
+ break;
+
+ case CDIOCCLOSE:
+ if (pp->acr != 1)
+ break;
+ error = acd_tray(dev, 1);
+ break;
+
+ case CDIOREADTOCHEADER:
+ if (!cdp->toc.hdr.ending_track) {
+ error = EIO;
+ break;
+ }
+ bcopy(&cdp->toc.hdr, addr, sizeof(cdp->toc.hdr));
+ break;
+
+ case CDIOREADTOCENTRYS:
+ {
+ struct ioc_read_toc_entry *te = (struct ioc_read_toc_entry *)addr;
+ struct toc *toc = &cdp->toc;
+ int starting_track = te->starting_track;
+ int len;
+
+ if (!toc->hdr.ending_track) {
+ error = EIO;
+ break;
+ }
+
+ if (te->data_len < sizeof(toc->tab[0]) ||
+ (te->data_len % sizeof(toc->tab[0])) != 0 ||
+ (te->address_format != CD_MSF_FORMAT &&
+ te->address_format != CD_LBA_FORMAT)) {
+ error = EINVAL;
+ break;
+ }
+
+ if (!starting_track)
+ starting_track = toc->hdr.starting_track;
+ else if (starting_track == 170)
+ starting_track = toc->hdr.ending_track + 1;
+ else if (starting_track < toc->hdr.starting_track ||
+ starting_track > toc->hdr.ending_track + 1) {
+ error = EINVAL;
+ break;
+ }
+
+ len = ((toc->hdr.ending_track + 1 - starting_track) + 1) *
+ sizeof(toc->tab[0]);
+ if (te->data_len < len)
+ len = te->data_len;
+ if (len > sizeof(toc->tab)) {
+ error = EINVAL;
+ break;
+ }
+
+ if (te->address_format == CD_MSF_FORMAT) {
+ struct cd_toc_entry *entry;
+
+ if (!(toc = malloc(sizeof(struct toc), M_ACD, M_NOWAIT))) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&cdp->toc, toc, sizeof(struct toc));
+ entry = toc->tab + (toc->hdr.ending_track + 1 -
+ toc->hdr.starting_track) + 1;
+ while (--entry >= toc->tab) {
+ lba2msf(ntohl(entry->addr.lba), &entry->addr.msf.minute,
+ &entry->addr.msf.second, &entry->addr.msf.frame);
+ entry->addr_type = CD_MSF_FORMAT;
+ }
+ }
+ error = copyout(toc->tab + starting_track - toc->hdr.starting_track,
+ te->data, len);
+ if (te->address_format == CD_MSF_FORMAT)
+ free(toc, M_ACD);
+ }
+ break;
+
+ case CDIOREADTOCENTRY:
+ {
+ struct ioc_read_toc_single_entry *te =
+ (struct ioc_read_toc_single_entry *)addr;
+ struct toc *toc = &cdp->toc;
+ u_char track = te->track;
+
+ if (!toc->hdr.ending_track) {
+ error = EIO;
+ break;
+ }
+
+ if (te->address_format != CD_MSF_FORMAT &&
+ te->address_format != CD_LBA_FORMAT) {
+ error = EINVAL;
+ break;
+ }
+
+ if (!track)
+ track = toc->hdr.starting_track;
+ else if (track == 170)
+ track = toc->hdr.ending_track + 1;
+ else if (track < toc->hdr.starting_track ||
+ track > toc->hdr.ending_track + 1) {
+ error = EINVAL;
+ break;
+ }
+
+ if (te->address_format == CD_MSF_FORMAT) {
+ struct cd_toc_entry *entry;
+
+ if (!(toc = malloc(sizeof(struct toc), M_ACD, M_NOWAIT))) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&cdp->toc, toc, sizeof(struct toc));
+ entry = toc->tab + (track - toc->hdr.starting_track);
+ lba2msf(ntohl(entry->addr.lba), &entry->addr.msf.minute,
+ &entry->addr.msf.second, &entry->addr.msf.frame);
+ }
+ bcopy(toc->tab + track - toc->hdr.starting_track,
+ &te->entry, sizeof(struct cd_toc_entry));
+ if (te->address_format == CD_MSF_FORMAT)
+ free(toc, M_ACD);
+ }
+ break;
+
+#if __FreeBSD_version > 600008
+ case CDIOCREADSUBCHANNEL_SYSSPACE:
+ nocopyout = 1;
+ /* FALLTHROUGH */
+
+#endif
+ case CDIOCREADSUBCHANNEL:
+ {
+ struct ioc_read_subchannel *args =
+ (struct ioc_read_subchannel *)addr;
+ u_int8_t format;
+ int8_t ccb[16] = { ATAPI_READ_SUBCHANNEL, 0, 0x40, 1, 0, 0, 0,
+ sizeof(cdp->subchan)>>8, sizeof(cdp->subchan),
+ 0, 0, 0, 0, 0, 0, 0 };
+
+ if (args->data_len > sizeof(struct cd_sub_channel_info) ||
+ args->data_len < sizeof(struct cd_sub_channel_header)) {
+ error = EINVAL;
+ break;
+ }
+
+ format = args->data_format;
+ if ((format != CD_CURRENT_POSITION) &&
+ (format != CD_MEDIA_CATALOG) && (format != CD_TRACK_INFO)) {
+ error = EINVAL;
+ break;
+ }
+
+ ccb[1] = args->address_format & CD_MSF_FORMAT;
+
+ if ((error = ata_atapicmd(dev, ccb, (caddr_t)&cdp->subchan,
+ sizeof(cdp->subchan), ATA_R_READ, 10)))
+ break;
+
+ if ((format == CD_MEDIA_CATALOG) || (format == CD_TRACK_INFO)) {
+ if (cdp->subchan.header.audio_status == 0x11) {
+ error = EINVAL;
+ break;
+ }
+
+ ccb[3] = format;
+ if (format == CD_TRACK_INFO)
+ ccb[6] = args->track;
+
+ if ((error = ata_atapicmd(dev, ccb, (caddr_t)&cdp->subchan,
+ sizeof(cdp->subchan),ATA_R_READ,10))){
+ break;
+ }
+ }
+ if (nocopyout == 0) {
+ error = copyout(&cdp->subchan, args->data, args->data_len);
+ } else {
+ error = 0;
+ bcopy(&cdp->subchan, args->data, args->data_len);
+ }
+ }
+ break;
+
+ case CDIOCPLAYMSF:
+ {
+ struct ioc_play_msf *args = (struct ioc_play_msf *)addr;
+
+ error =
+ acd_play(dev,
+ msf2lba(args->start_m, args->start_s, args->start_f),
+ msf2lba(args->end_m, args->end_s, args->end_f));
+ }
+ break;
+
+ case CDIOCPLAYBLOCKS:
+ {
+ struct ioc_play_blocks *args = (struct ioc_play_blocks *)addr;
+
+ error = acd_play(dev, args->blk, args->blk + args->len);
+ }
+ break;
+
+ case CDIOCPLAYTRACKS:
+ {
+ struct ioc_play_track *args = (struct ioc_play_track *)addr;
+ int t1, t2;
+
+ if (!cdp->toc.hdr.ending_track) {
+ error = EIO;
+ break;
+ }
+ if (args->end_track < cdp->toc.hdr.ending_track + 1)
+ ++args->end_track;
+ if (args->end_track > cdp->toc.hdr.ending_track + 1)
+ args->end_track = cdp->toc.hdr.ending_track + 1;
+ t1 = args->start_track - cdp->toc.hdr.starting_track;
+ t2 = args->end_track - cdp->toc.hdr.starting_track;
+ if (t1 < 0 || t2 < 0 ||
+ t1 > (cdp->toc.hdr.ending_track-cdp->toc.hdr.starting_track)) {
+ error = EINVAL;
+ break;
+ }
+ error = acd_play(dev, ntohl(cdp->toc.tab[t1].addr.lba),
+ ntohl(cdp->toc.tab[t2].addr.lba));
+ }
+ break;
+
+ case CDIOCGETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+
+ if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE,
+ (caddr_t)&cdp->au, sizeof(cdp->au))))
+ break;
+
+ if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE) {
+ error = EIO;
+ break;
+ }
+ arg->vol[0] = cdp->au.port[0].volume;
+ arg->vol[1] = cdp->au.port[1].volume;
+ arg->vol[2] = cdp->au.port[2].volume;
+ arg->vol[3] = cdp->au.port[3].volume;
+ }
+ break;
+
+ case CDIOCSETVOL:
+ {
+ struct ioc_vol *arg = (struct ioc_vol *)addr;
+
+ if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE,
+ (caddr_t)&cdp->au, sizeof(cdp->au))))
+ break;
+ if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE) {
+ error = EIO;
+ break;
+ }
+ if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE_MASK,
+ (caddr_t)&cdp->aumask,
+ sizeof(cdp->aumask))))
+ break;
+ cdp->au.data_length = 0;
+ cdp->au.port[0].channels = CHANNEL_0;
+ cdp->au.port[1].channels = CHANNEL_1;
+ cdp->au.port[0].volume = arg->vol[0] & cdp->aumask.port[0].volume;
+ cdp->au.port[1].volume = arg->vol[1] & cdp->aumask.port[1].volume;
+ cdp->au.port[2].volume = arg->vol[2] & cdp->aumask.port[2].volume;
+ cdp->au.port[3].volume = arg->vol[3] & cdp->aumask.port[3].volume;
+ error = acd_mode_select(dev, (caddr_t)&cdp->au, sizeof(cdp->au));
+ }
+ break;
+
+ case CDIOCSETPATCH:
+ {
+ struct ioc_patch *arg = (struct ioc_patch *)addr;
+
+ error = acd_setchan(dev, arg->patch[0], arg->patch[1],
+ arg->patch[2], arg->patch[3]);
+ }
+ break;
+
+ case CDIOCSETMONO:
+ error = acd_setchan(dev, CHANNEL_0|CHANNEL_1, CHANNEL_0|CHANNEL_1, 0,0);
+ break;
+
+ case CDIOCSETSTEREO:
+ error = acd_setchan(dev, CHANNEL_0, CHANNEL_1, 0, 0);
+ break;
+
+ case CDIOCSETMUTE:
+ error = acd_setchan(dev, 0, 0, 0, 0);
+ break;
+
+ case CDIOCSETLEFT:
+ error = acd_setchan(dev, CHANNEL_0, CHANNEL_0, 0, 0);
+ break;
+
+ case CDIOCSETRIGHT:
+ error = acd_setchan(dev, CHANNEL_1, CHANNEL_1, 0, 0);
+ break;
+
+ case CDRIOCBLANK:
+ error = acd_blank(dev, (*(int *)addr));
+ break;
+
+ case CDRIOCNEXTWRITEABLEADDR:
+ {
+ struct acd_track_info track_info;
+
+ if ((error = acd_read_track_info(dev, 0xff, &track_info)))
+ break;
+
+ if (!track_info.nwa_valid) {
+ error = EINVAL;
+ break;
+ }
+ *(int*)addr = track_info.next_writeable_addr;
+ }
+ break;
+
+ case CDRIOCINITWRITER:
+ error = acd_init_writer(dev, (*(int *)addr));
+ break;
+
+ case CDRIOCINITTRACK:
+ error = acd_init_track(dev, (struct cdr_track *)addr);
+ break;
+
+ case CDRIOCFLUSH:
+ error = acd_flush(dev);
+ break;
+
+ case CDRIOCFIXATE:
+ error = acd_fixate(dev, (*(int *)addr));
+ break;
+
+ case CDRIOCREADSPEED:
+ {
+ int speed = *(int *)addr;
+
+ /* Preserve old behavior: units in multiples of CDROM speed */
+ if (speed < 177)
+ speed *= 177;
+ error = acd_set_speed(dev, speed, CDR_MAX_SPEED);
+ }
+ break;
+
+ case CDRIOCWRITESPEED:
+ {
+ int speed = *(int *)addr;
+
+ if (speed < 177)
+ speed *= 177;
+ error = acd_set_speed(dev, CDR_MAX_SPEED, speed);
+ }
+ break;
+
+ case CDRIOCGETBLOCKSIZE:
+ *(int *)addr = cdp->block_size;
+ break;
+
+ case CDRIOCSETBLOCKSIZE:
+ cdp->block_size = *(int *)addr;
+ pp->sectorsize = cdp->block_size; /* hack for GEOM SOS */
+ acd_set_ioparm(dev);
+ break;
+
+ case CDRIOCGETPROGRESS:
+ error = acd_get_progress(dev, (int *)addr);
+ break;
+
+ case CDRIOCSENDCUE:
+ error = acd_send_cue(dev, (struct cdr_cuesheet *)addr);
+ break;
+
+ case CDRIOCREADFORMATCAPS:
+ error = acd_read_format_caps(dev, (struct cdr_format_capacities *)addr);
+ break;
+
+ case CDRIOCFORMAT:
+ error = acd_format(dev, (struct cdr_format_params *)addr);
+ break;
+
+ case DVDIOCREPORTKEY:
+ if (cdp->cap.media & MST_READ_DVDROM)
+ error = acd_report_key(dev, (struct dvd_authinfo *)addr);
+ else
+ error = EINVAL;
+ break;
+
+ case DVDIOCSENDKEY:
+ if (cdp->cap.media & MST_READ_DVDROM)
+ error = acd_send_key(dev, (struct dvd_authinfo *)addr);
+ else
+ error = EINVAL;
+ break;
+
+ case DVDIOCREADSTRUCTURE:
+ if (cdp->cap.media & MST_READ_DVDROM)
+ error = acd_read_structure(dev, (struct dvd_struct *)addr);
+ else
+ error = EINVAL;
+ break;
+
+ default:
+ error = ata_device_ioctl(dev, cmd, addr);
+ }
+ return error;
+}
+
+static int
+acd_geom_access(struct g_provider *pp, int dr, int dw, int de)
+{
+ device_t dev = pp->geom->softc;
+ struct acd_softc *cdp = device_get_ivars(dev);
+ struct ata_request *request;
+ int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int timeout = 60, track;
+
+ if (!(request = ata_alloc_request()))
+ return ENOMEM;
+
+ /* wait if drive is not finished loading the medium */
+ while (timeout--) {
+ bzero(request, sizeof(struct ata_request));
+ request->dev = dev;
+ bcopy(ccb, request->u.atapi.ccb, 16);
+ request->flags = ATA_R_ATAPI;
+ request->timeout = 5;
+ ata_queue_request(request);
+ if (!request->error &&
+ (request->u.atapi.sense.key == 2 ||
+ request->u.atapi.sense.key == 7) &&
+ request->u.atapi.sense.asc == 4 &&
+ request->u.atapi.sense.ascq == 1)
+ tsleep(&timeout, PRIBIO, "acdld", hz / 2);
+ else
+ break;
+ }
+ ata_free_request(request);
+
+ if (pp->acr == 0) {
+ acd_prevent_allow(dev, 1);
+ cdp->flags |= F_LOCKED;
+ acd_read_toc(dev);
+ }
+
+ if (dr + pp->acr == 0) {
+ acd_prevent_allow(dev, 0);
+ cdp->flags &= ~F_LOCKED;
+ }
+
+ if ((track = pp->index)) {
+ pp->sectorsize = (cdp->toc.tab[track - 1].control & 4) ? 2048 : 2352;
+ pp->mediasize = ntohl(cdp->toc.tab[track].addr.lba) -
+ ntohl(cdp->toc.tab[track - 1].addr.lba);
+ }
+ else {
+ pp->sectorsize = cdp->block_size;
+ pp->mediasize = cdp->disk_size;
+ }
+ pp->mediasize *= pp->sectorsize;
+
+ return 0;
+}
+
+static void
+acd_geom_start(struct bio *bp)
+{
+ device_t dev = bp->bio_to->geom->softc;
+ struct acd_softc *cdp = device_get_ivars(dev);
+
+ if (bp->bio_cmd != BIO_READ && bp->bio_cmd != BIO_WRITE) {
+ g_io_deliver(bp, EOPNOTSUPP);
+ return;
+ }
+
+ if (bp->bio_cmd == BIO_READ && cdp->disk_size == -1) {
+ g_io_deliver(bp, EIO);
+ return;
+ }
+
+ /* GEOM classes must do their own request limiting */
+ if (bp->bio_length <= cdp->iomax) {
+ bp->bio_pblkno = bp->bio_offset / bp->bio_to->sectorsize;
+ acd_strategy(bp);
+ }
+ else {
+ u_int pos, size = cdp->iomax - cdp->iomax % bp->bio_to->sectorsize;
+ struct bio *bp2, *bp3;
+
+ if (!(bp2 = g_clone_bio(bp)))
+ g_io_deliver(bp, EIO);
+
+ for (pos = 0; bp2; pos += size) {
+ bp3 = NULL;
+ bp2->bio_done = g_std_done;
+ bp2->bio_to = bp->bio_to;
+ bp2->bio_offset += pos;
+ bp2->bio_data += pos;
+ bp2->bio_length = bp->bio_length - pos;
+ if (bp2->bio_length > size) {
+ bp2->bio_length = size;
+ if (!(bp3 = g_clone_bio(bp)))
+ bp->bio_error = ENOMEM;
+ }
+ bp2->bio_pblkno = bp2->bio_offset / bp2->bio_to->sectorsize;
+ acd_strategy(bp2);
+ bp2 = bp3;
+ }
+ }
+}
+
+static void
+acd_strategy(struct bio *bp)
+{
+ device_t dev = bp->bio_to->geom->softc;
+ struct ata_device *atadev = device_get_softc(dev);
+ struct acd_softc *cdp = device_get_ivars(dev);
+ struct ata_request *request;
+ u_int32_t lba, lastlba, count;
+ int8_t ccb[16];
+ int track, blocksize;
+
+ /* reject all queued entries if media changed */
+ if (atadev->flags & ATA_D_MEDIA_CHANGED) {
+ g_io_deliver(bp, EIO);
+ return;
+ }
+
+ bzero(ccb, sizeof(ccb));
+
+ track = bp->bio_to->index;
+
+ if (track) {
+ blocksize = (cdp->toc.tab[track - 1].control & 4) ? 2048 : 2352;
+ lastlba = ntohl(cdp->toc.tab[track].addr.lba);
+ lba = bp->bio_offset / blocksize;
+ lba += ntohl(cdp->toc.tab[track - 1].addr.lba);
+ }
+ else {
+ blocksize = cdp->block_size;
+ lastlba = cdp->disk_size;
+ lba = bp->bio_offset / blocksize;
+ }
+
+ count = bp->bio_length / blocksize;
+
+ if (bp->bio_cmd == BIO_READ) {
+ /* if transfer goes beyond range adjust it to be within limits */
+ if (lba + count > lastlba) {
+ /* if we are entirely beyond EOM return EOF */
+ if (lastlba <= lba) {
+ g_io_deliver(bp, 0);
+ return;
+ }
+ count = lastlba - lba;
+ }
+ switch (blocksize) {
+ case 2048:
+ ccb[0] = ATAPI_READ_BIG;
+ break;
+
+ case 2352:
+ ccb[0] = ATAPI_READ_CD;
+ ccb[9] = 0xf8;
+ break;
+
+ default:
+ ccb[0] = ATAPI_READ_CD;
+ ccb[9] = 0x10;
+ }
+ }
+ else
+ ccb[0] = ATAPI_WRITE_BIG;
+
+ ccb[1] = 0;
+ ccb[2] = lba>>24;
+ ccb[3] = lba>>16;
+ ccb[4] = lba>>8;
+ ccb[5] = lba;
+ ccb[6] = count>>16;
+ ccb[7] = count>>8;
+ ccb[8] = count;
+
+ if (!(request = ata_alloc_request())) {
+ g_io_deliver(bp, ENOMEM);
+ return;
+ }
+ request->dev = dev;
+ request->bio = bp;
+ bcopy(ccb, request->u.atapi.ccb,
+ (atadev->param.config & ATA_PROTO_MASK) ==
+ ATA_PROTO_ATAPI_12 ? 16 : 12);
+ request->data = bp->bio_data;
+ request->bytecount = count * blocksize;
+ request->transfersize = min(request->bytecount, 65534);
+ request->timeout = (ccb[0] == ATAPI_WRITE_BIG) ? 60 : 30;
+ request->retries = 2;
+ request->callback = acd_done;
+ request->flags = ATA_R_ATAPI;
+ if (atadev->mode >= ATA_DMA)
+ request->flags |= ATA_R_DMA;
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ request->flags |= ATA_R_READ;
+ break;
+ case BIO_WRITE:
+ request->flags |= ATA_R_WRITE;
+ break;
+ default:
+ device_printf(dev, "unknown BIO operation\n");
+ ata_free_request(request);
+ g_io_deliver(bp, EIO);
+ return;
+ }
+ ata_queue_request(request);
+}
+
+static void
+acd_done(struct ata_request *request)
+{
+ struct bio *bp = request->bio;
+
+ /* finish up transfer */
+ bp->bio_completed = request->donecount;
+ g_io_deliver(bp, request->result);
+ ata_free_request(request);
+}
+
+static void
+acd_set_ioparm(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct acd_softc *cdp = device_get_ivars(dev);
+
+ if (ch->dma)
+ cdp->iomax = min(ch->dma->max_iosize, 65534);
+ else
+ cdp->iomax = min(DFLTPHYS, 65534);
+}
+
+static void
+lba2msf(u_int32_t lba, u_int8_t *m, u_int8_t *s, u_int8_t *f)
+{
+ lba += 150;
+ lba &= 0xffffff;
+ *m = lba / (60 * 75);
+ lba %= (60 * 75);
+ *s = lba / 75;
+ *f = lba % 75;
+}
+
+static u_int32_t
+msf2lba(u_int8_t m, u_int8_t s, u_int8_t f)
+{
+ return (m * 60 + s) * 75 + f - 150;
+}
+
+static void
+acd_read_toc(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+ struct acd_softc *cdp = device_get_ivars(dev);
+ struct g_provider *pp;
+ u_int32_t sizes[2];
+ int8_t ccb[16];
+ int track, ntracks, len;
+
+ atadev->flags &= ~ATA_D_MEDIA_CHANGED;
+ bzero(&cdp->toc, sizeof(cdp->toc));
+ cdp->disk_size = -1; /* hack for GEOM SOS */
+
+ if (acd_test_ready(dev))
+ return;
+
+ bzero(ccb, sizeof(ccb));
+ len = sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry);
+ ccb[0] = ATAPI_READ_TOC;
+ ccb[7] = len>>8;
+ ccb[8] = len;
+ if (ata_atapicmd(dev, ccb, (caddr_t)&cdp->toc, len,
+ ATA_R_READ | ATA_R_QUIET, 30)) {
+ bzero(&cdp->toc, sizeof(cdp->toc));
+ return;
+ }
+ ntracks = cdp->toc.hdr.ending_track - cdp->toc.hdr.starting_track + 1;
+ if (ntracks <= 0 || ntracks > MAXTRK) {
+ bzero(&cdp->toc, sizeof(cdp->toc));
+ return;
+ }
+
+ len = sizeof(struct ioc_toc_header)+(ntracks+1)*sizeof(struct cd_toc_entry);
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_READ_TOC;
+ ccb[7] = len>>8;
+ ccb[8] = len;
+ if (ata_atapicmd(dev, ccb, (caddr_t)&cdp->toc, len,
+ ATA_R_READ | ATA_R_QUIET, 30)) {
+ bzero(&cdp->toc, sizeof(cdp->toc));
+ return;
+ }
+ cdp->toc.hdr.len = ntohs(cdp->toc.hdr.len);
+
+ cdp->block_size = (cdp->toc.tab[0].control & 4) ? 2048 : 2352;
+ acd_set_ioparm(dev);
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_READ_CAPACITY;
+ if (ata_atapicmd(dev, ccb, (caddr_t)sizes, sizeof(sizes),
+ ATA_R_READ | ATA_R_QUIET, 30)) {
+ bzero(&cdp->toc, sizeof(cdp->toc));
+ return;
+ }
+ cdp->disk_size = ntohl(sizes[0]) + 1;
+
+ for (track = 1; track <= ntracks; track ++) {
+ if (cdp->pp[track] != NULL)
+ continue;
+ pp = g_new_providerf(cdp->gp, "acd%dt%02d", device_get_unit(dev),track);
+ pp->index = track;
+ cdp->pp[track] = pp;
+ g_error_provider(pp, 0);
+ }
+ for (; track < MAXTRK; track ++) {
+ if (cdp->pp[track] == NULL)
+ continue;
+ cdp->pp[track]->flags |= G_PF_WITHER;
+ g_orphan_provider(cdp->pp[track], ENXIO);
+ cdp->pp[track] = NULL;
+ }
+
+#ifdef ACD_DEBUG
+ if (cdp->disk_size && cdp->toc.hdr.ending_track) {
+ device_printf(dev, "(%d sectors (%d bytes)), %d tracks ",
+ cdp->disk_size, cdp->block_size,
+ cdp->toc.hdr.ending_track-cdp->toc.hdr.starting_track+1);
+ if (cdp->toc.tab[0].control & 4)
+ printf("%dMB\n", cdp->disk_size * cdp->block_size / 1048576);
+ else
+ printf("%d:%d audio\n",
+ cdp->disk_size / 75 / 60, cdp->disk_size / 75 % 60);
+ }
+#endif
+}
+
+static int
+acd_play(device_t dev, int start, int end)
+{
+ int8_t ccb[16];
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_PLAY_MSF;
+ lba2msf(start, &ccb[3], &ccb[4], &ccb[5]);
+ lba2msf(end, &ccb[6], &ccb[7], &ccb[8]);
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 10);
+}
+
+static int
+acd_setchan(device_t dev, u_int8_t c0, u_int8_t c1, u_int8_t c2, u_int8_t c3)
+{
+ struct acd_softc *cdp = device_get_ivars(dev);
+ int error;
+
+ if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE, (caddr_t)&cdp->au,
+ sizeof(cdp->au))))
+ return error;
+ if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE)
+ return EIO;
+ cdp->au.data_length = 0;
+ cdp->au.port[0].channels = c0;
+ cdp->au.port[1].channels = c1;
+ cdp->au.port[2].channels = c2;
+ cdp->au.port[3].channels = c3;
+ return acd_mode_select(dev, (caddr_t)&cdp->au, sizeof(cdp->au));
+}
+
+static int
+acd_init_writer(device_t dev, int test_write)
+{
+ int8_t ccb[16];
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_REZERO;
+ ata_atapicmd(dev, ccb, NULL, 0, ATA_R_QUIET, 60);
+ ccb[0] = ATAPI_SEND_OPC_INFO;
+ ccb[1] = 0x01;
+ ata_atapicmd(dev, ccb, NULL, 0, ATA_R_QUIET, 30);
+ return 0;
+}
+
+static int
+acd_fixate(device_t dev, int multisession)
+{
+ struct acd_softc *cdp = device_get_ivars(dev);
+ int8_t ccb[16] = { ATAPI_CLOSE_TRACK, 0x01, 0x02, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int timeout = 5*60*2;
+ int error, dummy;
+ struct write_param param;
+
+ if ((error = acd_mode_sense(dev, ATAPI_CDROM_WRITE_PARAMETERS_PAGE,
+ (caddr_t)&param, sizeof(param))))
+ return error;
+
+ param.data_length = 0;
+ if (multisession)
+ param.session_type = CDR_SESS_MULTI;
+ else
+ param.session_type = CDR_SESS_NONE;
+
+ if ((error = acd_mode_select(dev, (caddr_t)&param, param.page_length + 10)))
+ return error;
+
+ error = ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+ if (error)
+ return error;
+
+ /* some drives just return ready, wait for the expected fixate time */
+ if ((error = acd_test_ready(dev)) != EBUSY) {
+ timeout = timeout / (cdp->cap.cur_write_speed / 177);
+ tsleep(&error, PRIBIO, "acdfix", timeout * hz / 2);
+ return acd_test_ready(dev);
+ }
+
+ while (timeout-- > 0) {
+ if ((error = acd_get_progress(dev, &dummy)))
+ return error;
+ if ((error = acd_test_ready(dev)) != EBUSY)
+ return error;
+ tsleep(&error, PRIBIO, "acdcld", hz / 2);
+ }
+ return EIO;
+}
+
+static int
+acd_init_track(device_t dev, struct cdr_track *track)
+{
+ struct acd_softc *cdp = device_get_ivars(dev);
+ struct write_param param;
+ int error;
+
+ if ((error = acd_mode_sense(dev, ATAPI_CDROM_WRITE_PARAMETERS_PAGE,
+ (caddr_t)&param, sizeof(param))))
+ return error;
+
+ param.data_length = 0;
+ param.page_code = ATAPI_CDROM_WRITE_PARAMETERS_PAGE;
+ param.page_length = 0x32;
+ param.test_write = track->test_write ? 1 : 0;
+ param.write_type = CDR_WTYPE_TRACK;
+ param.session_type = CDR_SESS_NONE;
+ param.fp = 0;
+ param.packet_size = 0;
+
+ if (cdp->cap.capabilities & MST_BURNPROOF)
+ param.burnproof = 1;
+
+ switch (track->datablock_type) {
+
+ case CDR_DB_RAW:
+ if (track->preemp)
+ param.track_mode = CDR_TMODE_AUDIO_PREEMP;
+ else
+ param.track_mode = CDR_TMODE_AUDIO;
+ cdp->block_size = 2352;
+ param.datablock_type = CDR_DB_RAW;
+ param.session_format = CDR_SESS_CDROM;
+ break;
+
+ case CDR_DB_ROM_MODE1:
+ cdp->block_size = 2048;
+ param.track_mode = CDR_TMODE_DATA;
+ param.datablock_type = CDR_DB_ROM_MODE1;
+ param.session_format = CDR_SESS_CDROM;
+ break;
+
+ case CDR_DB_ROM_MODE2:
+ cdp->block_size = 2336;
+ param.track_mode = CDR_TMODE_DATA;
+ param.datablock_type = CDR_DB_ROM_MODE2;
+ param.session_format = CDR_SESS_CDROM;
+ break;
+
+ case CDR_DB_XA_MODE1:
+ cdp->block_size = 2048;
+ param.track_mode = CDR_TMODE_DATA;
+ param.datablock_type = CDR_DB_XA_MODE1;
+ param.session_format = CDR_SESS_CDROM_XA;
+ break;
+
+ case CDR_DB_XA_MODE2_F1:
+ cdp->block_size = 2056;
+ param.track_mode = CDR_TMODE_DATA;
+ param.datablock_type = CDR_DB_XA_MODE2_F1;
+ param.session_format = CDR_SESS_CDROM_XA;
+ break;
+
+ case CDR_DB_XA_MODE2_F2:
+ cdp->block_size = 2324;
+ param.track_mode = CDR_TMODE_DATA;
+ param.datablock_type = CDR_DB_XA_MODE2_F2;
+ param.session_format = CDR_SESS_CDROM_XA;
+ break;
+
+ case CDR_DB_XA_MODE2_MIX:
+ cdp->block_size = 2332;
+ param.track_mode = CDR_TMODE_DATA;
+ param.datablock_type = CDR_DB_XA_MODE2_MIX;
+ param.session_format = CDR_SESS_CDROM_XA;
+ break;
+ }
+ acd_set_ioparm(dev);
+ return acd_mode_select(dev, (caddr_t)&param, param.page_length + 10);
+}
+
+static int
+acd_flush(device_t dev)
+{
+ int8_t ccb[16] = { ATAPI_SYNCHRONIZE_CACHE, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, NULL, 0, ATA_R_QUIET, 60);
+}
+
+static int
+acd_read_track_info(device_t dev, int32_t lba, struct acd_track_info *info)
+{
+ int8_t ccb[16] = { ATAPI_READ_TRACK_INFO, 1,
+ lba>>24, lba>>16, lba>>8, lba, 0,
+ sizeof(*info)>>8, sizeof(*info),
+ 0, 0, 0, 0, 0, 0, 0 };
+ int error;
+
+ if ((error = ata_atapicmd(dev, ccb, (caddr_t)info, sizeof(*info),
+ ATA_R_READ, 30)))
+ return error;
+ info->track_start_addr = ntohl(info->track_start_addr);
+ info->next_writeable_addr = ntohl(info->next_writeable_addr);
+ info->free_blocks = ntohl(info->free_blocks);
+ info->fixed_packet_size = ntohl(info->fixed_packet_size);
+ info->track_length = ntohl(info->track_length);
+ return 0;
+}
+
+static int
+acd_get_progress(device_t dev, int *finished)
+{
+ int8_t ccb[16] = { ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ struct ata_request *request;
+ int8_t dummy[8];
+
+ if (!(request = ata_alloc_request()))
+ return ENOMEM;
+
+ request->dev = dev;
+ bcopy(ccb, request->u.atapi.ccb, 16);
+ request->data = dummy;
+ request->bytecount = sizeof(dummy);
+ request->transfersize = min(request->bytecount, 65534);
+ request->flags = ATA_R_ATAPI | ATA_R_READ;
+ request->timeout = 30;
+ ata_queue_request(request);
+ if (!request->error && request->u.atapi.sense.error & ATA_SENSE_VALID)
+ *finished = ((request->u.atapi.sense.specific2 |
+ (request->u.atapi.sense.specific1 << 8)) * 100) / 65535;
+ else
+ *finished = 0;
+ ata_free_request(request);
+ return 0;
+}
+
+static int
+acd_send_cue(device_t dev, struct cdr_cuesheet *cuesheet)
+{
+ struct acd_softc *cdp = device_get_ivars(dev);
+ struct write_param param;
+ int8_t ccb[16] = { ATAPI_SEND_CUE_SHEET, 0, 0, 0, 0, 0,
+ cuesheet->len>>16, cuesheet->len>>8, cuesheet->len,
+ 0, 0, 0, 0, 0, 0, 0 };
+ int8_t *buffer;
+ int32_t error;
+
+ if ((error = acd_mode_sense(dev, ATAPI_CDROM_WRITE_PARAMETERS_PAGE,
+ (caddr_t)&param, sizeof(param))))
+ return error;
+
+ param.data_length = 0;
+ param.page_code = ATAPI_CDROM_WRITE_PARAMETERS_PAGE;
+ param.page_length = 0x32;
+ param.test_write = cuesheet->test_write ? 1 : 0;
+ param.write_type = CDR_WTYPE_SESSION;
+ param.session_type = cuesheet->session_type;
+ param.fp = 0;
+ param.packet_size = 0;
+ param.track_mode = CDR_TMODE_AUDIO;
+ param.datablock_type = CDR_DB_RAW;
+ param.session_format = cuesheet->session_format;
+ if (cdp->cap.capabilities & MST_BURNPROOF)
+ param.burnproof = 1;
+
+ if ((error = acd_mode_select(dev, (caddr_t)&param, param.page_length + 10)))
+ return error;
+
+ if (!(buffer = malloc(cuesheet->len, M_ACD, M_NOWAIT)))
+ return ENOMEM;
+
+ if (!(error = copyin(cuesheet->entries, buffer, cuesheet->len)))
+ error = ata_atapicmd(dev, ccb, buffer, cuesheet->len, 0, 30);
+ free(buffer, M_ACD);
+ return error;
+}
+
+static int
+acd_report_key(device_t dev, struct dvd_authinfo *ai)
+{
+ struct dvd_miscauth *d = NULL;
+ u_int32_t lba = 0;
+ int16_t length;
+ int8_t ccb[16];
+ int error;
+
+ switch (ai->format) {
+ case DVD_REPORT_AGID:
+ case DVD_REPORT_ASF:
+ case DVD_REPORT_RPC:
+ length = 8;
+ break;
+ case DVD_REPORT_KEY1:
+ length = 12;
+ break;
+ case DVD_REPORT_TITLE_KEY:
+ length = 12;
+ lba = ai->lba;
+ break;
+ case DVD_REPORT_CHALLENGE:
+ length = 16;
+ break;
+ case DVD_INVALIDATE_AGID:
+ length = 0;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_REPORT_KEY;
+ ccb[2] = (lba >> 24) & 0xff;
+ ccb[3] = (lba >> 16) & 0xff;
+ ccb[4] = (lba >> 8) & 0xff;
+ ccb[5] = lba & 0xff;
+ ccb[8] = (length >> 8) & 0xff;
+ ccb[9] = length & 0xff;
+ ccb[10] = (ai->agid << 6) | ai->format;
+
+ if (length) {
+ if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+ d->length = htons(length - 2);
+ }
+
+ error = ata_atapicmd(dev, ccb, (caddr_t)d, length,
+ ai->format == DVD_INVALIDATE_AGID ? 0 : ATA_R_READ,10);
+ if (error) {
+ if (length)
+ free(d, M_ACD);
+ return error;
+ }
+
+ switch (ai->format) {
+ case DVD_REPORT_AGID:
+ ai->agid = d->data[3] >> 6;
+ break;
+
+ case DVD_REPORT_CHALLENGE:
+ bcopy(&d->data[0], &ai->keychal[0], 10);
+ break;
+
+ case DVD_REPORT_KEY1:
+ bcopy(&d->data[0], &ai->keychal[0], 5);
+ break;
+
+ case DVD_REPORT_TITLE_KEY:
+ ai->cpm = (d->data[0] >> 7);
+ ai->cp_sec = (d->data[0] >> 6) & 0x1;
+ ai->cgms = (d->data[0] >> 4) & 0x3;
+ bcopy(&d->data[1], &ai->keychal[0], 5);
+ break;
+
+ case DVD_REPORT_ASF:
+ ai->asf = d->data[3] & 1;
+ break;
+
+ case DVD_REPORT_RPC:
+ ai->reg_type = (d->data[0] >> 6);
+ ai->vend_rsts = (d->data[0] >> 3) & 0x7;
+ ai->user_rsts = d->data[0] & 0x7;
+ ai->region = d->data[1];
+ ai->rpc_scheme = d->data[2];
+ break;
+
+ case DVD_INVALIDATE_AGID:
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ if (length)
+ free(d, M_ACD);
+ return error;
+}
+
+static int
+acd_send_key(device_t dev, struct dvd_authinfo *ai)
+{
+ struct dvd_miscauth *d;
+ int16_t length;
+ int8_t ccb[16];
+ int error;
+
+ switch (ai->format) {
+ case DVD_SEND_CHALLENGE:
+ length = 16;
+ if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+ bcopy(ai->keychal, &d->data[0], 10);
+ break;
+
+ case DVD_SEND_KEY2:
+ length = 12;
+ if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+ bcopy(&ai->keychal[0], &d->data[0], 5);
+ break;
+
+ case DVD_SEND_RPC:
+ length = 8;
+ if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+ d->data[0] = ai->region;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_SEND_KEY;
+ ccb[8] = (length >> 8) & 0xff;
+ ccb[9] = length & 0xff;
+ ccb[10] = (ai->agid << 6) | ai->format;
+ d->length = htons(length - 2);
+ error = ata_atapicmd(dev, ccb, (caddr_t)d, length, 0, 10);
+ free(d, M_ACD);
+ return error;
+}
+
+static int
+acd_read_structure(device_t dev, struct dvd_struct *s)
+{
+ struct dvd_miscauth *d;
+ u_int16_t length;
+ int8_t ccb[16];
+ int error = 0;
+
+ switch(s->format) {
+ case DVD_STRUCT_PHYSICAL:
+ length = 21;
+ break;
+
+ case DVD_STRUCT_COPYRIGHT:
+ length = 8;
+ break;
+
+ case DVD_STRUCT_DISCKEY:
+ length = 2052;
+ break;
+
+ case DVD_STRUCT_BCA:
+ length = 192;
+ break;
+
+ case DVD_STRUCT_MANUFACT:
+ length = 2052;
+ break;
+
+ case DVD_STRUCT_DDS:
+ case DVD_STRUCT_PRERECORDED:
+ case DVD_STRUCT_UNIQUEID:
+ case DVD_STRUCT_LIST:
+ case DVD_STRUCT_CMI:
+ case DVD_STRUCT_RMD_LAST:
+ case DVD_STRUCT_RMD_RMA:
+ case DVD_STRUCT_DCB:
+ return ENOSYS;
+
+ default:
+ return EINVAL;
+ }
+
+ if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO)))
+ return ENOMEM;
+ d->length = htons(length - 2);
+
+ bzero(ccb, sizeof(ccb));
+ ccb[0] = ATAPI_READ_STRUCTURE;
+ ccb[6] = s->layer_num;
+ ccb[7] = s->format;
+ ccb[8] = (length >> 8) & 0xff;
+ ccb[9] = length & 0xff;
+ ccb[10] = s->agid << 6;
+ error = ata_atapicmd(dev, ccb, (caddr_t)d, length, ATA_R_READ, 30);
+ if (error) {
+ free(d, M_ACD);
+ return error;
+ }
+
+ switch (s->format) {
+ case DVD_STRUCT_PHYSICAL: {
+ struct dvd_layer *layer = (struct dvd_layer *)&s->data[0];
+
+ layer->book_type = d->data[0] >> 4;
+ layer->book_version = d->data[0] & 0xf;
+ layer->disc_size = d->data[1] >> 4;
+ layer->max_rate = d->data[1] & 0xf;
+ layer->nlayers = (d->data[2] >> 5) & 3;
+ layer->track_path = (d->data[2] >> 4) & 1;
+ layer->layer_type = d->data[2] & 0xf;
+ layer->linear_density = d->data[3] >> 4;
+ layer->track_density = d->data[3] & 0xf;
+ layer->start_sector = d->data[5] << 16 | d->data[6] << 8 | d->data[7];
+ layer->end_sector = d->data[9] << 16 | d->data[10] << 8 | d->data[11];
+ layer->end_sector_l0 = d->data[13] << 16 | d->data[14] << 8|d->data[15];
+ layer->bca = d->data[16] >> 7;
+ break;
+ }
+
+ case DVD_STRUCT_COPYRIGHT:
+ s->cpst = d->data[0];
+ s->rmi = d->data[1];
+ break;
+
+ case DVD_STRUCT_DISCKEY:
+ bcopy(&d->data[0], &s->data[0], 2048);
+ break;
+
+ case DVD_STRUCT_BCA:
+ s->length = ntohs(d->length);
+ bcopy(&d->data[0], &s->data[0], s->length);
+ break;
+
+ case DVD_STRUCT_MANUFACT:
+ s->length = ntohs(d->length);
+ bcopy(&d->data[0], &s->data[0], s->length);
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ free(d, M_ACD);
+ return error;
+}
+
+static int
+acd_tray(device_t dev, int close)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+ struct acd_softc *cdp = device_get_ivars(dev);
+ int error = ENODEV;
+
+ if (cdp->cap.mechanism & MST_EJECT) {
+ if (close) {
+ if (!(error = acd_start_stop(dev, 3))) {
+ acd_read_toc(dev);
+ acd_prevent_allow(dev, 1);
+ cdp->flags |= F_LOCKED;
+ }
+ }
+ else {
+ acd_start_stop(dev, 0);
+ acd_prevent_allow(dev, 0);
+ cdp->flags &= ~F_LOCKED;
+ atadev->flags |= ATA_D_MEDIA_CHANGED;
+ error = acd_start_stop(dev, 2);
+ }
+ }
+ return error;
+}
+
+static int
+acd_blank(device_t dev, int blanktype)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+ int8_t ccb[16] = { ATAPI_BLANK, 0x10 | (blanktype & 0x7), 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ atadev->flags |= ATA_D_MEDIA_CHANGED;
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+}
+
+static int
+acd_prevent_allow(device_t dev, int lock)
+{
+ int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+}
+
+static int
+acd_start_stop(device_t dev, int start)
+{
+ int8_t ccb[16] = { ATAPI_START_STOP, 0, 0, 0, start,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+}
+
+static int
+acd_pause_resume(device_t dev, int pause)
+{
+ int8_t ccb[16] = { ATAPI_PAUSE, 0, 0, 0, 0, 0, 0, 0, pause,
+ 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+}
+
+static int
+acd_mode_sense(device_t dev, int page, caddr_t pagebuf, int pagesize)
+{
+ int8_t ccb[16] = { ATAPI_MODE_SENSE_BIG, 0, page, 0, 0, 0, 0,
+ pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 };
+ int error;
+
+ error = ata_atapicmd(dev, ccb, pagebuf, pagesize, ATA_R_READ, 10);
+ return error;
+}
+
+static int
+acd_mode_select(device_t dev, caddr_t pagebuf, int pagesize)
+{
+ int8_t ccb[16] = { ATAPI_MODE_SELECT_BIG, 0x10, 0, 0, 0, 0, 0,
+ pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, pagebuf, pagesize, 0, 30);
+}
+
+static int
+acd_set_speed(device_t dev, int rdspeed, int wrspeed)
+{
+ int8_t ccb[16] = { ATAPI_SET_SPEED, 0, rdspeed >> 8, rdspeed,
+ wrspeed >> 8, wrspeed, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int error;
+
+ error = ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+ if (!error)
+ acd_get_cap(dev);
+ return error;
+}
+
+static void
+acd_get_cap(device_t dev)
+{
+ struct acd_softc *cdp = device_get_ivars(dev);
+ int count;
+
+ /* get drive capabilities, some bugridden drives needs this repeated */
+ for (count = 0 ; count < 5 ; count++) {
+ if (!acd_mode_sense(dev, ATAPI_CDROM_CAP_PAGE,
+ (caddr_t)&cdp->cap, sizeof(cdp->cap)) &&
+ cdp->cap.page_code == ATAPI_CDROM_CAP_PAGE) {
+ cdp->cap.max_read_speed = ntohs(cdp->cap.max_read_speed);
+ cdp->cap.cur_read_speed = ntohs(cdp->cap.cur_read_speed);
+ cdp->cap.max_write_speed = ntohs(cdp->cap.max_write_speed);
+ cdp->cap.cur_write_speed = max(ntohs(cdp->cap.cur_write_speed),177);
+ cdp->cap.max_vol_levels = ntohs(cdp->cap.max_vol_levels);
+ cdp->cap.buf_size = ntohs(cdp->cap.buf_size);
+ }
+ }
+}
+
+static int
+acd_read_format_caps(device_t dev, struct cdr_format_capacities *caps)
+{
+ int8_t ccb[16] = { ATAPI_READ_FORMAT_CAPACITIES, 0, 0, 0, 0, 0, 0,
+ (sizeof(struct cdr_format_capacities) >> 8) & 0xff,
+ sizeof(struct cdr_format_capacities) & 0xff,
+ 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, (caddr_t)caps,
+ sizeof(struct cdr_format_capacities), ATA_R_READ, 30);
+}
+
+static int
+acd_format(device_t dev, struct cdr_format_params* params)
+{
+ int8_t ccb[16] = { ATAPI_FORMAT, 0x11, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0 };
+ int error;
+
+ error = ata_atapicmd(dev, ccb, (u_int8_t *)params,
+ sizeof(struct cdr_format_params), 0, 30);
+ return error;
+}
+
+static int
+acd_test_ready(device_t dev)
+{
+ int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+}
+
+static void
+acd_describe(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ struct acd_softc *cdp = device_get_ivars(dev);
+ int comma = 0;
+ char *mechanism;
+
+ if (bootverbose) {
+ device_printf(dev, "<%.40s/%.8s> %s drive at ata%d as %s\n",
+ atadev->param.model, atadev->param.revision,
+ (cdp->cap.media & MST_WRITE_DVDR) ? "DVDR" :
+ (cdp->cap.media & MST_WRITE_DVDRAM) ? "DVDRAM" :
+ (cdp->cap.media & MST_WRITE_CDRW) ? "CDRW" :
+ (cdp->cap.media & MST_WRITE_CDR) ? "CDR" :
+ (cdp->cap.media & MST_READ_DVDROM) ? "DVDROM":"CDROM",
+ device_get_unit(ch->dev),
+ (atadev->unit == ATA_MASTER) ? "master" : "slave");
+
+ device_printf(dev, "%s", "");
+ if (cdp->cap.cur_read_speed) {
+ printf("read %dKB/s", cdp->cap.cur_read_speed * 1000 / 1024);
+ if (cdp->cap.max_read_speed)
+ printf(" (%dKB/s)", cdp->cap.max_read_speed * 1000 / 1024);
+ if ((cdp->cap.cur_write_speed) &&
+ (cdp->cap.media & (MST_WRITE_CDR | MST_WRITE_CDRW |
+ MST_WRITE_DVDR | MST_WRITE_DVDRAM))) {
+ printf(" write %dKB/s", cdp->cap.cur_write_speed * 1000 / 1024);
+ if (cdp->cap.max_write_speed)
+ printf(" (%dKB/s)", cdp->cap.max_write_speed * 1000 / 1024);
+ }
+ comma = 1;
+ }
+ if (cdp->cap.buf_size) {
+ printf("%s %dKB buffer", comma ? "," : "", cdp->cap.buf_size);
+ comma = 1;
+ }
+ printf("%s %s\n", comma ? "," : "", ata_mode2str(atadev->mode));
+
+ device_printf(dev, "Reads:");
+ comma = 0;
+ if (cdp->cap.media & MST_READ_CDR) {
+ printf(" CDR"); comma = 1;
+ }
+ if (cdp->cap.media & MST_READ_CDRW) {
+ printf("%s CDRW", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.capabilities & MST_READ_CDDA) {
+ if (cdp->cap.capabilities & MST_CDDA_STREAM)
+ printf("%s CDDA stream", comma ? "," : "");
+ else
+ printf("%s CDDA", comma ? "," : "");
+ comma = 1;
+ }
+ if (cdp->cap.media & MST_READ_DVDROM) {
+ printf("%s DVDROM", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.media & MST_READ_DVDR) {
+ printf("%s DVDR", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.media & MST_READ_DVDRAM) {
+ printf("%s DVDRAM", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.media & MST_READ_PACKET)
+ printf("%s packet", comma ? "," : "");
+
+ printf("\n");
+ device_printf(dev, "Writes:");
+ if (cdp->cap.media & (MST_WRITE_CDR | MST_WRITE_CDRW |
+ MST_WRITE_DVDR | MST_WRITE_DVDRAM)) {
+ comma = 0;
+ if (cdp->cap.media & MST_WRITE_CDR) {
+ printf(" CDR" ); comma = 1;
+ }
+ if (cdp->cap.media & MST_WRITE_CDRW) {
+ printf("%s CDRW", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.media & MST_WRITE_DVDR) {
+ printf("%s DVDR", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.media & MST_WRITE_DVDRAM) {
+ printf("%s DVDRAM", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.media & MST_WRITE_TEST) {
+ printf("%s test write", comma ? "," : ""); comma = 1;
+ }
+ if (cdp->cap.capabilities & MST_BURNPROOF)
+ printf("%s burnproof", comma ? "," : "");
+ }
+ printf("\n");
+ if (cdp->cap.capabilities & MST_AUDIO_PLAY) {
+ device_printf(dev, "Audio: ");
+ if (cdp->cap.capabilities & MST_AUDIO_PLAY)
+ printf("play");
+ if (cdp->cap.max_vol_levels)
+ printf(", %d volume levels", cdp->cap.max_vol_levels);
+ printf("\n");
+ }
+ device_printf(dev, "Mechanism: ");
+ switch (cdp->cap.mechanism & MST_MECH_MASK) {
+ case MST_MECH_CADDY:
+ mechanism = "caddy"; break;
+ case MST_MECH_TRAY:
+ mechanism = "tray"; break;
+ case MST_MECH_POPUP:
+ mechanism = "popup"; break;
+ case MST_MECH_CHANGER:
+ mechanism = "changer"; break;
+ case MST_MECH_CARTRIDGE:
+ mechanism = "cartridge"; break;
+ default:
+ mechanism = 0; break;
+ }
+ if (mechanism)
+ printf("%s%s", (cdp->cap.mechanism & MST_EJECT) ?
+ "ejectable " : "", mechanism);
+ else if (cdp->cap.mechanism & MST_EJECT)
+ printf("ejectable");
+
+ if (cdp->cap.mechanism & MST_LOCKABLE)
+ printf((cdp->cap.mechanism & MST_LOCKED) ? ", locked":", unlocked");
+ if (cdp->cap.mechanism & MST_PREVENT)
+ printf(", lock protected");
+ printf("\n");
+
+ if ((cdp->cap.mechanism & MST_MECH_MASK) != MST_MECH_CHANGER) {
+ device_printf(dev, "Medium: ");
+ switch (cdp->cap.medium_type & MST_TYPE_MASK_HIGH) {
+ case MST_CDROM:
+ printf("CD-ROM "); break;
+ case MST_CDR:
+ printf("CD-R "); break;
+ case MST_CDRW:
+ printf("CD-RW "); break;
+ case MST_DOOR_OPEN:
+ printf("door open"); break;
+ case MST_NO_DISC:
+ printf("no/blank disc"); break;
+ case MST_FMT_ERROR:
+ printf("medium format error"); break;
+ }
+ if ((cdp->cap.medium_type & MST_TYPE_MASK_HIGH)<MST_TYPE_MASK_HIGH){
+ switch (cdp->cap.medium_type & MST_TYPE_MASK_LOW) {
+ case MST_DATA_120:
+ printf("120mm data disc"); break;
+ case MST_AUDIO_120:
+ printf("120mm audio disc"); break;
+ case MST_COMB_120:
+ printf("120mm data/audio disc"); break;
+ case MST_PHOTO_120:
+ printf("120mm photo disc"); break;
+ case MST_DATA_80:
+ printf("80mm data disc"); break;
+ case MST_AUDIO_80:
+ printf("80mm audio disc"); break;
+ case MST_COMB_80:
+ printf("80mm data/audio disc"); break;
+ case MST_PHOTO_80:
+ printf("80mm photo disc"); break;
+ case MST_FMT_NONE:
+ switch (cdp->cap.medium_type & MST_TYPE_MASK_HIGH) {
+ case MST_CDROM:
+ printf("unknown"); break;
+ case MST_CDR:
+ case MST_CDRW:
+ printf("blank"); break;
+ }
+ break;
+ default:
+ printf("unknown (0x%x)", cdp->cap.medium_type); break;
+ }
+ }
+ printf("\n");
+ }
+ }
+ else {
+ device_printf(dev, "%s ",
+ (cdp->cap.media & MST_WRITE_DVDR) ? "DVDR" :
+ (cdp->cap.media & MST_WRITE_DVDRAM) ? "DVDRAM" :
+ (cdp->cap.media & MST_WRITE_CDRW) ? "CDRW" :
+ (cdp->cap.media & MST_WRITE_CDR) ? "CDR" :
+ (cdp->cap.media & MST_READ_DVDROM) ? "DVDROM" :
+ "CDROM");
+ if (cdp->changer_info)
+ printf("with %d CD changer ", cdp->changer_info->slots);
+ printf("<%.40s/%.8s> at ata%d-%s %s\n",
+ atadev->param.model, atadev->param.revision,
+ device_get_unit(ch->dev),
+ (atadev->unit == ATA_MASTER) ? "master" : "slave",
+ ata_mode2str(atadev->mode) );
+ }
+}
+
+static device_method_t acd_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, acd_probe),
+ DEVMETHOD(device_attach, acd_attach),
+ DEVMETHOD(device_detach, acd_detach),
+ DEVMETHOD(device_shutdown, acd_shutdown),
+
+ /* ATA methods */
+ DEVMETHOD(ata_reinit, acd_reinit),
+
+ { 0, 0 }
+};
+
+static driver_t acd_driver = {
+ "acd",
+ acd_methods,
+ 0,
+};
+
+static devclass_t acd_devclass;
+
+static int
+acd_modevent(module_t mod, int what, void *arg)
+{
+ g_modevent(0, what, &acd_class);
+ return 0;
+}
+
+DRIVER_MODULE(acd, ata, acd_driver, acd_devclass, acd_modevent, NULL);
+MODULE_VERSION(acd, 1);
+MODULE_DEPEND(acd, ata, 1, 1, 1);
diff --git a/sys/dev/ata/atapi-cd.h b/sys/dev/ata/atapi-cd.h
new file mode 100644
index 000000000000..22a7b1f3af78
--- /dev/null
+++ b/sys/dev/ata/atapi-cd.h
@@ -0,0 +1,314 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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$
+ */
+
+/* CDROM Table Of Contents */
+#define MAXTRK 99
+struct toc {
+ struct ioc_toc_header hdr;
+ struct cd_toc_entry tab[MAXTRK + 1];
+};
+
+/* DVD CSS authentication */
+struct dvd_miscauth {
+ u_int16_t length;
+ u_int16_t reserved;
+ u_int8_t data[2048];
+};
+
+/* CDROM Audio Control Parameters Page */
+struct audiopage {
+ /* mode page data header */
+ u_int16_t data_length;
+ u_int8_t medium_type;
+ u_int8_t dev_spec;
+ u_int8_t unused[2];
+ u_int16_t blk_desc_len;
+
+ /* audio control page */
+ u_int8_t page_code;
+#define ATAPI_CDROM_AUDIO_PAGE 0x0e
+#define ATAPI_CDROM_AUDIO_PAGE_MASK 0x4e
+
+ u_int8_t param_len;
+ u_int8_t flags;
+#define CD_PA_SOTC 0x02
+#define CD_PA_IMMED 0x04
+
+ u_int8_t reserved3;
+ u_int8_t reserved4;
+ u_int8_t reserved5;
+ u_int16_t lb_per_sec;
+ struct port_control {
+ u_int8_t channels:4;
+#define CHANNEL_0 1
+#define CHANNEL_1 2
+#define CHANNEL_2 4
+#define CHANNEL_3 8
+
+ u_int8_t volume;
+ } port[4];
+};
+
+
+/* CDROM Capabilities and Mechanical Status Page */
+struct cappage {
+ /* mode page data header */
+ u_int16_t data_length;
+ u_int8_t medium_type;
+#define MST_TYPE_MASK_LOW 0x0f
+#define MST_FMT_NONE 0x00
+#define MST_DATA_120 0x01
+#define MST_AUDIO_120 0x02
+#define MST_COMB_120 0x03
+#define MST_PHOTO_120 0x04
+#define MST_DATA_80 0x05
+#define MST_AUDIO_80 0x06
+#define MST_COMB_80 0x07
+#define MST_PHOTO_80 0x08
+
+#define MST_TYPE_MASK_HIGH 0x70
+#define MST_CDROM 0x00
+#define MST_CDR 0x10
+#define MST_CDRW 0x20
+
+#define MST_NO_DISC 0x70
+#define MST_DOOR_OPEN 0x71
+#define MST_FMT_ERROR 0x72
+
+ u_int8_t dev_spec;
+ u_int16_t unused;
+ u_int16_t blk_desc_len;
+
+ /* capabilities page */
+ u_int8_t page_code;
+#define ATAPI_CDROM_CAP_PAGE 0x2a
+
+ u_int8_t param_len;
+
+ u_int16_t media;
+#define MST_READ_CDR 0x0001
+#define MST_READ_CDRW 0x0002
+#define MST_READ_PACKET 0x0004
+#define MST_READ_DVDROM 0x0008
+#define MST_READ_DVDR 0x0010
+#define MST_READ_DVDRAM 0x0020
+#define MST_WRITE_CDR 0x0100
+#define MST_WRITE_CDRW 0x0200
+#define MST_WRITE_TEST 0x0400
+#define MST_WRITE_DVDR 0x1000
+#define MST_WRITE_DVDRAM 0x2000
+
+ u_int16_t capabilities;
+#define MST_AUDIO_PLAY 0x0001
+#define MST_COMPOSITE 0x0002
+#define MST_AUDIO_P1 0x0004
+#define MST_AUDIO_P2 0x0008
+#define MST_MODE2_f1 0x0010
+#define MST_MODE2_f2 0x0020
+#define MST_MULTISESSION 0x0040
+#define MST_BURNPROOF 0x0080
+#define MST_READ_CDDA 0x0100
+#define MST_CDDA_STREAM 0x0200
+#define MST_COMBINED_RW 0x0400
+#define MST_CORRECTED_RW 0x0800
+#define MST_SUPPORT_C2 0x1000
+#define MST_ISRC 0x2000
+#define MST_UPC 0x4000
+
+ u_int8_t mechanism;
+#define MST_LOCKABLE 0x01
+#define MST_LOCKED 0x02
+#define MST_PREVENT 0x04
+#define MST_EJECT 0x08
+#define MST_MECH_MASK 0xe0
+#define MST_MECH_CADDY 0x00
+#define MST_MECH_TRAY 0x20
+#define MST_MECH_POPUP 0x40
+#define MST_MECH_CHANGER 0x80
+#define MST_MECH_CARTRIDGE 0xa0
+
+ uint8_t audio;
+#define MST_SEP_VOL 0x01
+#define MST_SEP_MUTE 0x02
+
+ u_int16_t max_read_speed; /* max raw data rate in bytes/1000 */
+ u_int16_t max_vol_levels; /* number of discrete volume levels */
+ u_int16_t buf_size; /* internal buffer size in bytes/1024 */
+ u_int16_t cur_read_speed; /* current data rate in bytes/1000 */
+
+ u_int8_t reserved3;
+ u_int8_t misc;
+
+ u_int16_t max_write_speed; /* max raw data rate in bytes/1000 */
+ u_int16_t cur_write_speed; /* current data rate in bytes/1000 */
+ u_int16_t copy_protect_rev;
+ u_int16_t reserved4;
+};
+
+#define CH_READY 0
+#define CH_LOADING 1
+#define CH_UNLOADING 2
+#define CH_INITIALIZING 3
+
+#define CD_IDLE 0
+#define CD_AUDIO_ACTIVE 1
+#define CD_AUDIO_SCAN 2
+#define CD_HOST_ACTIVE 3
+#define CD_NO_STATE 7
+
+/* CDROM Changer mechanism status structure */
+struct changer {
+ u_int8_t current_slot :5; /* active changer slot */
+ u_int8_t mech_state :2; /* current changer state */
+
+ u_int8_t fault :1; /* fault in last operation */
+ u_int8_t reserved0 :5;
+ u_int8_t cd_state :3; /* current mechanism state */
+
+ u_int8_t current_lba[3]; /* current LBA */
+ u_int8_t slots; /* number of available slots */
+ u_int16_t table_length; /* slot table length */
+ struct {
+ u_int8_t changed :1; /* media has changed in this slot */
+ u_int8_t unused :6;
+ u_int8_t present :1; /* slot has a CD present */
+ u_int8_t reserved0;
+ u_int8_t reserved1;
+ u_int8_t reserved2;
+ } slot[32];
+};
+
+/* CDROM Write Parameters Mode Page (Burners ONLY) */
+struct write_param {
+ /* mode page data header */
+ u_int16_t data_length;
+ u_int8_t medium_type;
+ u_int8_t dev_spec;
+ u_int8_t unused[2];
+ u_int16_t blk_desc_len;
+
+ /* write parameters page */
+ u_int8_t page_code;
+#define ATAPI_CDROM_WRITE_PARAMETERS_PAGE 0x05
+
+ u_int8_t page_length; /* 0x32 */
+ u_int8_t write_type :4; /* write stream type */
+#define CDR_WTYPE_PACKET 0x00
+#define CDR_WTYPE_TRACK 0x01
+#define CDR_WTYPE_SESSION 0x02
+#define CDR_WTYPE_RAW 0x03
+
+ u_int8_t test_write :1; /* test write enable */
+ u_int8_t link_size_valid :1;
+ u_int8_t burnproof :1; /* BurnProof enable */
+ u_int8_t reserved2_7 :1;
+ u_int8_t track_mode :4; /* track mode */
+#define CDR_TMODE_AUDIO 0x00
+#define CDR_TMODE_AUDIO_PREEMP 0x01
+#define CDR_TMODE_ALLOW_COPY 0x02
+#define CDR_TMODE_DATA 0x04
+#define CDR_TMODE_QUAD_AUDIO 0x08
+
+ u_int8_t copy :1; /* generation stamp */
+ u_int8_t fp :1; /* fixed packet type */
+ u_int8_t session_type :2; /* session type */
+#define CDR_SESS_NONE 0x00
+#define CDR_SESS_FINAL 0x01
+#define CDR_SESS_RESERVED 0x02
+#define CDR_SESS_MULTI 0x03
+
+ u_int8_t datablock_type :4; /* data type code (see cdrio.h) */
+ u_int8_t reserved4_4567 :4;
+ u_int8_t link_size;
+ u_int8_t reserved6;
+ u_int8_t host_app_code :6; /* host application code */
+ u_int8_t reserved7_67 :2;
+ u_int8_t session_format; /* session format */
+#define CDR_SESS_CDROM 0x00
+#define CDR_SESS_CDI 0x10
+#define CDR_SESS_CDROM_XA 0x20
+
+ u_int8_t reserved9;
+ u_int32_t packet_size; /* packet size in bytes */
+ u_int16_t audio_pause_length; /* audio pause length in secs */
+ u_int8_t media_catalog_number[16];
+ u_int8_t isr_code[16];
+ u_int8_t sub_hdr_byte0;
+ u_int8_t sub_hdr_byte1;
+ u_int8_t sub_hdr_byte2;
+ u_int8_t sub_hdr_byte3;
+ u_int8_t vendor_specific_byte0;
+ u_int8_t vendor_specific_byte1;
+ u_int8_t vendor_specific_byte2;
+ u_int8_t vendor_specific_byte3;
+} __packed;
+
+/* CDROM Read Track Information structure */
+struct acd_track_info {
+ u_int16_t data_length;
+ u_int8_t track_number; /* current track number */
+ u_int8_t session_number; /* current session number */
+ u_int8_t reserved4;
+ u_int8_t track_mode :4; /* mode of this track */
+ u_int8_t copy :1; /* generation stamp */
+ u_int8_t damage :1; /* damaged track */
+ u_int8_t reserved5_67 :2;
+ u_int8_t data_mode :4; /* data mode of this disc */
+ u_int8_t fp :1; /* fixed packet */
+ u_int8_t packet :1; /* packet track */
+ u_int8_t blank :1; /* blank (empty) track */
+ u_int8_t rt :1; /* reserved track */
+ u_int8_t nwa_valid :1; /* next_writeable_addr field valid */
+ u_int8_t reserved7_17 :7;
+ u_int track_start_addr; /* start of this track */
+ u_int next_writeable_addr; /* next writeable addr on this disc */
+ u_int free_blocks; /* free block on this disc */
+ u_int fixed_packet_size; /* size of packets on this track */
+ u_int track_length; /* length of this track */
+};
+
+/* Structure describing an ATAPI CDROM device */
+struct acd_softc {
+ int flags; /* device state flags */
+#define F_LOCKED 0x0001 /* this unit is locked */
+
+ struct toc toc; /* table of disc contents */
+ struct audiopage au; /* audio page info */
+ struct audiopage aumask; /* audio page mask */
+ struct cappage cap; /* capabilities page info */
+ struct cd_sub_channel_info subchan; /* subchannel info */
+ struct changer *changer_info; /* changer info */
+ struct acd_softc **driver; /* softc's of changer slots */
+ int slot; /* this instance slot number */
+ time_t timestamp; /* this instance timestamp */
+ u_int32_t disk_size; /* size of current media */
+ u_int32_t block_size; /* blocksize currently used */
+ u_int32_t iomax; /* Max I/O request (bytes) */
+ struct g_geom *gp; /* geom instance */
+ struct g_provider *pp[MAXTRK+1]; /* providers */
+};
diff --git a/sys/dev/ata/atapi-fd.c b/sys/dev/ata/atapi-fd.c
new file mode 100644
index 000000000000..5d839d3cb400
--- /dev/null
+++ b/sys/dev/ata/atapi-fd.c
@@ -0,0 +1,447 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/cdio.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/bus.h>
+#include <geom/geom_disk.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/atapi-fd.h>
+#include <ata_if.h>
+
+
+/* prototypes */
+static disk_open_t afd_open;
+static disk_close_t afd_close;
+static disk_strategy_t afd_strategy;
+static disk_ioctl_t afd_ioctl;
+static int afd_sense(device_t);
+static void afd_describe(device_t);
+static void afd_done(struct ata_request *);
+static int afd_prevent_allow(device_t, int);
+static int afd_test_ready(device_t);
+
+/* internal vars */
+static MALLOC_DEFINE(M_AFD, "afd_driver", "ATAPI floppy driver buffers");
+
+static int
+afd_probe(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+ if ((atadev->param.config & ATA_PROTO_ATAPI) &&
+ (atadev->param.config & ATA_ATAPI_TYPE_MASK) == ATA_ATAPI_TYPE_DIRECT)
+ return 0;
+ else
+ return ENXIO;
+}
+
+static int
+afd_attach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ struct afd_softc *fdp;
+
+ if (!(fdp = malloc(sizeof(struct afd_softc), M_AFD, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "out of memory\n");
+ return ENOMEM;
+ }
+ device_set_ivars(dev, fdp);
+ ATA_SETMODE(device_get_parent(dev), dev);
+
+ if (afd_sense(dev)) {
+ device_set_ivars(dev, NULL);
+ free(fdp, M_AFD);
+ return ENXIO;
+ }
+ atadev->flags |= ATA_D_MEDIA_CHANGED;
+
+ /* announce we are here */
+ afd_describe(dev);
+
+ /* create the disk device */
+ fdp->disk = disk_alloc();
+ fdp->disk->d_open = afd_open;
+ fdp->disk->d_close = afd_close;
+ fdp->disk->d_strategy = afd_strategy;
+ fdp->disk->d_ioctl = afd_ioctl;
+ fdp->disk->d_name = "afd";
+ fdp->disk->d_drv1 = dev;
+ if (ch->dma)
+ fdp->disk->d_maxsize = ch->dma->max_iosize;
+ else
+ fdp->disk->d_maxsize = DFLTPHYS;
+ fdp->disk->d_unit = device_get_unit(dev);
+ disk_create(fdp->disk, DISK_VERSION);
+ return 0;
+}
+
+static int
+afd_detach(device_t dev)
+{
+ struct afd_softc *fdp = device_get_ivars(dev);
+
+ /* check that we have a valid device to detach */
+ if (!device_get_ivars(dev))
+ return ENXIO;
+
+ /* detroy disk from the system so we dont get any further requests */
+ disk_destroy(fdp->disk);
+
+ /* fail requests on the queue and any thats "in flight" for this device */
+ ata_fail_requests(dev);
+
+ /* dont leave anything behind */
+ device_set_ivars(dev, NULL);
+ free(fdp, M_AFD);
+ return 0;
+}
+
+static void
+afd_shutdown(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
+ ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0);
+}
+
+static int
+afd_reinit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ struct afd_softc *fdp = device_get_ivars(dev);
+
+ if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATAPI_MASTER)) ||
+ ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATAPI_SLAVE))) {
+ device_set_ivars(dev, NULL);
+ free(fdp, M_AFD);
+ return 1;
+ }
+ ATA_SETMODE(device_get_parent(dev), dev);
+ return 0;
+}
+
+static int
+afd_open(struct disk *dp)
+{
+ device_t dev = dp->d_drv1;
+ struct ata_device *atadev = device_get_softc(dev);
+ struct afd_softc *fdp = device_get_ivars(dev);
+
+ if (!fdp)
+ return ENXIO;
+ if (!device_is_attached(dev))
+ return EBUSY;
+
+ afd_test_ready(dev);
+ afd_prevent_allow(dev, 1);
+
+ if (afd_sense(dev))
+ device_printf(dev, "sense media type failed\n");
+ atadev->flags &= ~ATA_D_MEDIA_CHANGED;
+
+ if (!fdp->mediasize)
+ return ENXIO;
+
+ fdp->disk->d_sectorsize = fdp->sectorsize;
+ fdp->disk->d_mediasize = fdp->mediasize;
+ fdp->disk->d_fwsectors = fdp->sectors;
+ fdp->disk->d_fwheads = fdp->heads;
+ return 0;
+}
+
+static int
+afd_close(struct disk *dp)
+{
+ device_t dev = dp->d_drv1;
+
+ afd_prevent_allow(dev, 0);
+ return 0;
+}
+
+static void
+afd_strategy(struct bio *bp)
+{
+ device_t dev = bp->bio_disk->d_drv1;
+ struct ata_device *atadev = device_get_softc(dev);
+ struct afd_softc *fdp = device_get_ivars(dev);
+ struct ata_request *request;
+ u_int16_t count;
+ int8_t ccb[16];
+
+ /* if it's a null transfer, return immediatly. */
+ if (bp->bio_bcount == 0) {
+ bp->bio_resid = 0;
+ biodone(bp);
+ return;
+ }
+
+ /* should reject all queued entries if media have changed. */
+ if (atadev->flags & ATA_D_MEDIA_CHANGED) {
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+
+ count = bp->bio_bcount / fdp->sectorsize;
+ bp->bio_resid = bp->bio_bcount;
+
+ bzero(ccb, sizeof(ccb));
+
+ if (bp->bio_cmd == BIO_READ)
+ ccb[0] = ATAPI_READ_BIG;
+ else
+ ccb[0] = ATAPI_WRITE_BIG;
+
+ ccb[2] = bp->bio_pblkno >> 24;
+ ccb[3] = bp->bio_pblkno >> 16;
+ ccb[4] = bp->bio_pblkno >> 8;
+ ccb[5] = bp->bio_pblkno;
+ ccb[7] = count>>8;
+ ccb[8] = count;
+
+ if (!(request = ata_alloc_request())) {
+ biofinish(bp, NULL, ENOMEM);
+ return;
+ }
+ request->dev = dev;
+ request->bio = bp;
+ bcopy(ccb, request->u.atapi.ccb,
+ (atadev->param.config & ATA_PROTO_MASK) ==
+ ATA_PROTO_ATAPI_12 ? 16 : 12);
+ request->data = bp->bio_data;
+ request->bytecount = count * fdp->sectorsize;
+ request->transfersize = min(request->bytecount, 65534);
+ request->timeout = (ccb[0] == ATAPI_WRITE_BIG) ? 60 : 30;
+ request->retries = 2;
+ request->callback = afd_done;
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ request->flags = (ATA_R_ATAPI | ATA_R_READ);
+ break;
+ case BIO_WRITE:
+ request->flags = (ATA_R_ATAPI | ATA_R_WRITE);
+ break;
+ default:
+ device_printf(dev, "unknown BIO operation\n");
+ ata_free_request(request);
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+ if (atadev->mode >= ATA_DMA)
+ request->flags |= ATA_R_DMA;
+ request->flags |= ATA_R_ORDERED;
+ ata_queue_request(request);
+}
+
+static void
+afd_done(struct ata_request *request)
+{
+ struct bio *bp = request->bio;
+
+ /* finish up transfer */
+ if ((bp->bio_error = request->result))
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_resid = bp->bio_bcount - request->donecount;
+ biodone(bp);
+ ata_free_request(request);
+}
+
+static int
+afd_ioctl(struct disk *disk, u_long cmd, void *data, int flag,struct thread *td)
+{
+ return ata_device_ioctl(disk->d_drv1, cmd, data);
+}
+
+static int
+afd_sense(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+ struct afd_softc *fdp = device_get_ivars(dev);
+ struct afd_capacity capacity;
+ struct afd_capacity_big capacity_big;
+ struct afd_capabilities capabilities;
+ int8_t ccb1[16] = { ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int8_t ccb2[16] = { ATAPI_SERVICE_ACTION_IN, 0x10, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, sizeof(struct afd_capacity_big) & 0xff, 0, 0 };
+ int8_t ccb3[16] = { ATAPI_MODE_SENSE_BIG, 0, ATAPI_REWRITEABLE_CAP_PAGE,
+ 0, 0, 0, 0, sizeof(struct afd_capabilities) >> 8,
+ sizeof(struct afd_capabilities) & 0xff,
+ 0, 0, 0, 0, 0, 0, 0 };
+ int timeout = 20;
+ int error, count;
+
+ fdp->mediasize = 0;
+
+ /* wait for device to get ready */
+ while ((error = afd_test_ready(dev)) && timeout--) {
+ DELAY(100000);
+ }
+ if (error == EBUSY)
+ return 1;
+
+ /* The IOMEGA Clik! doesn't support reading the cap page, fake it */
+ if (!strncmp(atadev->param.model, "IOMEGA Clik!", 12)) {
+ fdp->heads = 1;
+ fdp->sectors = 2;
+ fdp->mediasize = 39441 * 1024;
+ fdp->sectorsize = 512;
+ afd_test_ready(dev);
+ return 0;
+ }
+
+ /* get drive capacity */
+ if (!ata_atapicmd(dev, ccb1, (caddr_t)&capacity,
+ sizeof(struct afd_capacity), ATA_R_READ, 30)) {
+ fdp->heads = 16;
+ fdp->sectors = 63;
+ fdp->sectorsize = be32toh(capacity.blocksize);
+ fdp->mediasize = (u_int64_t)be32toh(capacity.capacity)*fdp->sectorsize;
+ afd_test_ready(dev);
+ return 0;
+ }
+
+ /* get drive capacity big */
+ if (!ata_atapicmd(dev, ccb2, (caddr_t)&capacity_big,
+ sizeof(struct afd_capacity_big),
+ ATA_R_READ | ATA_R_QUIET, 30)) {
+ fdp->heads = 16;
+ fdp->sectors = 63;
+ fdp->sectorsize = be32toh(capacity_big.blocksize);
+ fdp->mediasize = be64toh(capacity_big.capacity)*fdp->sectorsize;
+ afd_test_ready(dev);
+ return 0;
+ }
+
+ /* get drive capabilities, some bugridden drives needs this repeated */
+ for (count = 0 ; count < 5 ; count++) {
+ if (!ata_atapicmd(dev, ccb3, (caddr_t)&capabilities,
+ sizeof(struct afd_capabilities), ATA_R_READ, 30) &&
+ capabilities.page_code == ATAPI_REWRITEABLE_CAP_PAGE) {
+ fdp->heads = capabilities.heads;
+ fdp->sectors = capabilities.sectors;
+ fdp->sectorsize = be16toh(capabilities.sector_size);
+ fdp->mediasize = be16toh(capabilities.cylinders) *
+ fdp->heads * fdp->sectors * fdp->sectorsize;
+ if (!capabilities.medium_type)
+ fdp->mediasize = 0;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+afd_prevent_allow(device_t dev, int lock)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+ int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ if (!strncmp(atadev->param.model, "IOMEGA Clik!", 12))
+ return 0;
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+}
+
+static int
+afd_test_ready(device_t dev)
+{
+ int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+}
+
+static void
+afd_describe(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ struct afd_softc *fdp = device_get_ivars(dev);
+ char sizestring[16];
+
+ if (fdp->mediasize > 1048576 * 5)
+ sprintf(sizestring, "%juMB", fdp->mediasize / 1048576);
+ else if (fdp->mediasize)
+ sprintf(sizestring, "%juKB", fdp->mediasize / 1024);
+ else
+ strcpy(sizestring, "(no media)");
+
+ device_printf(dev, "%s <%.40s %.8s> at ata%d-%s %s\n",
+ sizestring, atadev->param.model, atadev->param.revision,
+ device_get_unit(ch->dev),
+ (atadev->unit == ATA_MASTER) ? "master" : "slave",
+ ata_mode2str(atadev->mode));
+ if (bootverbose) {
+ device_printf(dev, "%ju sectors [%juC/%dH/%dS]\n",
+ fdp->mediasize / fdp->sectorsize,
+ fdp->mediasize /(fdp->sectorsize*fdp->sectors*fdp->heads),
+ fdp->heads, fdp->sectors);
+ }
+}
+
+static device_method_t afd_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, afd_probe),
+ DEVMETHOD(device_attach, afd_attach),
+ DEVMETHOD(device_detach, afd_detach),
+ DEVMETHOD(device_shutdown, afd_shutdown),
+
+ /* ATA methods */
+ DEVMETHOD(ata_reinit, afd_reinit),
+
+ { 0, 0 }
+};
+
+static driver_t afd_driver = {
+ "afd",
+ afd_methods,
+ 0,
+};
+
+static devclass_t afd_devclass;
+
+DRIVER_MODULE(afd, ata, afd_driver, afd_devclass, NULL, NULL);
+MODULE_VERSION(afd, 1);
+MODULE_DEPEND(afd, ata, 1, 1, 1);
+
diff --git a/sys/dev/ata/atapi-fd.h b/sys/dev/ata/atapi-fd.h
new file mode 100644
index 000000000000..0a3588a982a3
--- /dev/null
+++ b/sys/dev/ata/atapi-fd.h
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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$
+ */
+
+/* ATAPI Rewriteable drive Capabilities and Mechanical Status Page */
+struct afd_capabilities {
+ u_int16_t data_length;
+ u_int8_t medium_type;
+#define MFD_2DD_UN 0x10
+#define MFD_2DD 0x11
+#define MFD_HD_UN 0x20
+#define MFD_HD_12_98 0x22
+#define MFD_HD_12 0x23
+#define MFD_HD_144 0x24
+#define MFD_UHD 0x31
+
+#define MFD_UNKNOWN 0x00
+#define MFD_NO_DISC 0x70
+#define MFD_DOOR_OPEN 0x71
+#define MFD_FMT_ERROR 0x72
+
+ u_int8_t reserved0 :7;
+ u_int8_t wp :1; /* write protect */
+ u_int8_t unused[4];
+
+ /* capabilities page */
+ u_int8_t page_code :6;
+#define ATAPI_REWRITEABLE_CAP_PAGE 0x05
+
+ u_int8_t reserved1_6 :1;
+ u_int8_t ps :1; /* page save supported */
+ u_int8_t page_length; /* page length */
+ u_int16_t transfer_rate; /* in kilobits per second */
+ u_int8_t heads; /* number of heads */
+ u_int8_t sectors; /* number of sectors pr track */
+ u_int16_t sector_size; /* number of bytes per sector */
+ u_int16_t cylinders; /* number of cylinders */
+ u_int8_t reserved10[10];
+ u_int8_t motor_delay; /* motor off delay */
+ u_int8_t reserved21[7];
+ u_int16_t rpm; /* rotations per minute */
+ u_int8_t reserved30[2];
+};
+
+struct afd_capacity {
+ u_int32_t capacity;
+ u_int32_t blocksize;
+};
+
+struct afd_capacity_big {
+ u_int64_t capacity;
+ u_int32_t blocksize;
+};
+
+struct afd_softc {
+ u_int64_t mediasize;
+ u_int32_t heads;
+ u_int32_t sectors;
+ u_int32_t sectorsize;
+ struct disk *disk; /* virtual drives */
+};
+
diff --git a/sys/dev/ata/atapi-tape.c b/sys/dev/ata/atapi-tape.c
new file mode 100644
index 000000000000..81f04fadc261
--- /dev/null
+++ b/sys/dev/ata/atapi-tape.c
@@ -0,0 +1,752 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/conf.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/mtio.h>
+#include <sys/devicestat.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/bus.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/atapi-tape.h>
+#include <ata_if.h>
+
+/* device structure */
+static d_open_t ast_open;
+static d_close_t ast_close;
+static d_ioctl_t ast_ioctl;
+static d_strategy_t ast_strategy;
+static struct cdevsw ast_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = ast_open,
+ .d_close = ast_close,
+ .d_read = physread,
+ .d_write = physwrite,
+ .d_ioctl = ast_ioctl,
+ .d_strategy = ast_strategy,
+ .d_name = "ast",
+ .d_flags = D_TAPE | D_TRACKCLOSE,
+};
+
+/* prototypes */
+static int ast_sense(device_t);
+static void ast_describe(device_t);
+static void ast_done(struct ata_request *);
+static int ast_mode_sense(device_t, int, void *, int);
+static int ast_mode_select(device_t, void *, int);
+static int ast_write_filemark(device_t, u_int8_t);
+static int ast_read_position(device_t, int, struct ast_readposition *);
+static int ast_space(device_t, u_int8_t, int32_t);
+static int ast_locate(device_t, int, u_int32_t);
+static int ast_prevent_allow(device_t, int);
+static int ast_load_unload(device_t, u_int8_t);
+static int ast_rewind(device_t);
+static int ast_erase(device_t);
+static int ast_test_ready(device_t);
+static int ast_wait_dsc(device_t, int);
+
+/* internal vars */
+static u_int64_t ast_total = 0;
+static MALLOC_DEFINE(M_AST, "ast_driver", "ATAPI tape driver buffers");
+
+static int
+ast_probe(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if ((atadev->param.config & ATA_PROTO_ATAPI) &&
+ (atadev->param.config & ATA_ATAPI_TYPE_MASK) == ATA_ATAPI_TYPE_TAPE)
+ return 0;
+ else
+ return ENXIO;
+}
+
+static int
+ast_attach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ast_softc *stp;
+ struct ast_readposition position;
+ struct cdev *device;
+
+ if (!(stp = malloc(sizeof(struct ast_softc), M_AST, M_NOWAIT | M_ZERO))) {
+ device_printf(dev, "out of memory\n");
+ return ENOMEM;
+ }
+ device_set_ivars(dev, stp);
+ ATA_SETMODE(device_get_parent(dev), dev);
+
+ if (ast_sense(dev)) {
+ device_set_ivars(dev, NULL);
+ free(stp, M_AST);
+ return ENXIO;
+ }
+ if (!strcmp(atadev->param.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(dev, ATAPI_TAPE_TRANSFER_PAGE,
+ &transfer, sizeof(transfer));
+ bzero(&identify, sizeof(struct ast_identifypage));
+ ast_mode_sense(dev, ATAPI_TAPE_IDENTIFY_PAGE,
+ &identify, sizeof(identify));
+ strncpy(identify.ident, "FBSD", 4);
+ ast_mode_select(dev, &identify, sizeof(identify));
+ ast_read_position(dev, 0, &position);
+ }
+
+ stp->stats = devstat_new_entry("ast", device_get_unit(dev), DEV_BSIZE,
+ DEVSTAT_NO_ORDERED_TAGS,
+ DEVSTAT_TYPE_SEQUENTIAL | DEVSTAT_TYPE_IF_IDE,
+ DEVSTAT_PRIORITY_TAPE);
+ device = make_dev(&ast_cdevsw, 2 * device_get_unit(dev),
+ UID_ROOT, GID_OPERATOR, 0640, "ast%d",
+ device_get_unit(dev));
+ device->si_drv1 = dev;
+ if (ch->dma)
+ device->si_iosize_max = ch->dma->max_iosize;
+ else
+ device->si_iosize_max = DFLTPHYS;
+ stp->dev1 = device;
+ device = make_dev(&ast_cdevsw, 2 * device_get_unit(dev) + 1,
+ UID_ROOT, GID_OPERATOR, 0640, "nast%d",
+ device_get_unit(dev));
+ device->si_drv1 = dev;
+ if (ch->dma)
+ device->si_iosize_max = ch->dma->max_iosize;
+ else
+ device->si_iosize_max = DFLTPHYS;
+ stp->dev2 = device;
+
+ /* announce we are here and ready */
+ ast_describe(dev);
+ return 0;
+}
+
+static int
+ast_detach(device_t dev)
+{
+ struct ast_softc *stp = device_get_ivars(dev);
+
+ /* detroy devices from the system so we dont get any further requests */
+ destroy_dev(stp->dev1);
+ destroy_dev(stp->dev2);
+
+ /* fail requests on the queue and any thats "in flight" for this device */
+ ata_fail_requests(dev);
+
+ /* dont leave anything behind */
+ devstat_remove_entry(stp->stats);
+ device_set_ivars(dev, NULL);
+ free(stp, M_AST);
+ return 0;
+}
+
+static void
+ast_shutdown(device_t dev)
+{
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE)
+ ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0);
+}
+
+static int
+ast_reinit(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ast_softc *stp = device_get_ivars(dev);
+
+ if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATAPI_MASTER)) ||
+ ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATAPI_SLAVE))) {
+ device_set_ivars(dev, NULL);
+ free(stp, M_AST);
+ return 1;
+ }
+ ATA_SETMODE(device_get_parent(dev), dev);
+ return 0;
+}
+
+static int
+ast_open(struct cdev *cdev, int flags, int fmt, struct thread *td)
+{
+ device_t dev = cdev->si_drv1;
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ast_softc *stp = device_get_ivars(dev);
+
+ if (!stp)
+ return ENXIO;
+ if (!device_is_attached(dev))
+ return EBUSY;
+
+ ast_test_ready(dev);
+ if (stp->cap.lock)
+ ast_prevent_allow(dev, 1);
+ if (ast_sense(dev))
+ device_printf(dev, "sense media type failed\n");
+
+ atadev->flags &= ~ATA_D_MEDIA_CHANGED;
+ stp->flags &= ~(F_DATA_WRITTEN | F_FM_WRITTEN);
+ ast_total = 0;
+ return 0;
+}
+
+static int
+ast_close(struct cdev *cdev, int flags, int fmt, struct thread *td)
+{
+ device_t dev = cdev->si_drv1;
+ struct ast_softc *stp = device_get_ivars(dev);
+
+ /* flush buffers, some drives fail here, they should report ctl = 0 */
+ if (stp->cap.ctl && (stp->flags & F_DATA_WRITTEN))
+ ast_write_filemark(dev, 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(dev, ATAPI_WF_WRITE);
+
+ /* if minor is even rewind on close */
+ if (!(minor(cdev) & 0x01))
+ ast_rewind(dev);
+
+ if (stp->cap.lock && count_dev(cdev) == 1)
+ ast_prevent_allow(dev, 0);
+
+ stp->flags &= ~F_CTL_WARN;
+#ifdef AST_DEBUG
+ device_printf(dev, "%ju total bytes transferred\n", (uintmax_t)ast_total);
+#endif
+ return 0;
+}
+
+static int
+ast_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flag, struct thread *td)
+{
+ device_t dev = cdev->si_drv1;
+ struct ast_softc *stp = device_get_ivars(dev);
+ int error = 0;
+
+ switch (cmd) {
+ case MTIOCGET:
+ {
+ struct mtget *g = (struct mtget *) data;
+
+ 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:
+ {
+ int i;
+ struct mtop *mt = (struct mtop *)data;
+
+ switch ((int16_t) (mt->mt_op)) {
+
+ case MTWEOF:
+ for (i=0; i < mt->mt_count && !error; i++)
+ error = ast_write_filemark(dev, ATAPI_WF_WRITE);
+ break;
+
+ case MTFSF:
+ if (mt->mt_count)
+ error = ast_space(dev, ATAPI_SP_FM, mt->mt_count);
+ break;
+
+ case MTBSF:
+ if (mt->mt_count)
+ error = ast_space(dev, ATAPI_SP_FM, -(mt->mt_count));
+ break;
+
+ case MTREW:
+ error = ast_rewind(dev);
+ break;
+
+ case MTOFFL:
+ error = ast_load_unload(dev, ATAPI_SS_EJECT);
+ break;
+
+ case MTNOP:
+ error = ast_write_filemark(dev, 0);
+ break;
+
+ case MTERASE:
+ error = ast_erase(dev);
+ break;
+
+ case MTEOD:
+ error = ast_space(dev, ATAPI_SP_EOD, 0);
+ break;
+
+ case MTRETENS:
+ error = ast_load_unload(dev, ATAPI_SS_RETENSION|ATAPI_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(dev, 0, &position)))
+ break;
+ *(u_int32_t *)data = position.tape;
+ }
+ break;
+
+ case MTIOCRDHPOS:
+ {
+ struct ast_readposition position;
+
+ if ((error = ast_read_position(dev, 1, &position)))
+ break;
+ *(u_int32_t *)data = position.tape;
+ }
+ break;
+
+ case MTIOCSLOCATE:
+ error = ast_locate(dev, 0, *(u_int32_t *)data);
+ break;
+
+ case MTIOCHLOCATE:
+ error = ast_locate(dev, 1, *(u_int32_t *)data);
+ break;
+
+ default:
+ error = ata_device_ioctl(dev, cmd, data);
+ }
+ return error;
+}
+
+static void
+ast_strategy(struct bio *bp)
+{
+ device_t dev = bp->bio_dev->si_drv1;
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ast_softc *stp = device_get_ivars(dev);
+ struct ata_request *request;
+ u_int32_t blkcount;
+ int8_t ccb[16];
+
+ /* if it's a null transfer, return immediatly. */
+ if (bp->bio_bcount == 0) {
+ bp->bio_resid = 0;
+ biodone(bp);
+ return;
+ }
+ if (!(bp->bio_cmd == BIO_READ) && stp->flags & F_WRITEPROTECT) {
+ biofinish(bp, NULL, EPERM);
+ return;
+ }
+
+ /* check for != blocksize requests */
+ if (bp->bio_bcount % stp->blksize) {
+ device_printf(dev, "transfers must be multiple of %d\n", stp->blksize);
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+
+ /* warn about transfers bigger than the device suggests */
+ if (bp->bio_bcount > stp->blksize * stp->cap.ctl) {
+ if ((stp->flags & F_CTL_WARN) == 0) {
+ device_printf(dev, "WARNING: CTL exceeded %ld>%d\n",
+ bp->bio_bcount, stp->blksize * stp->cap.ctl);
+ stp->flags |= F_CTL_WARN;
+ }
+ }
+
+ bzero(ccb, sizeof(ccb));
+
+ if (bp->bio_cmd == BIO_READ)
+ ccb[0] = ATAPI_READ;
+ else
+ ccb[0] = ATAPI_WRITE;
+
+ blkcount = bp->bio_bcount / stp->blksize;
+
+ ccb[1] = 1;
+ ccb[2] = blkcount >> 16;
+ ccb[3] = blkcount >> 8;
+ ccb[4] = blkcount;
+
+ if (!(request = ata_alloc_request())) {
+ biofinish(bp, NULL, ENOMEM);
+ return;
+ }
+ request->dev = dev;
+ request->driver = bp;
+ bcopy(ccb, request->u.atapi.ccb,
+ (atadev->param.config & ATA_PROTO_MASK) ==
+ ATA_PROTO_ATAPI_12 ? 16 : 12);
+ request->data = bp->bio_data;
+ request->bytecount = blkcount * stp->blksize;
+ request->transfersize = min(request->bytecount, 65534);
+ request->timeout = (ccb[0] == ATAPI_WRITE_BIG) ? 180 : 120;
+ request->retries = 2;
+ request->callback = ast_done;
+ switch (bp->bio_cmd) {
+ case BIO_READ:
+ request->flags |= (ATA_R_ATAPI | ATA_R_READ);
+ break;
+ case BIO_WRITE:
+ request->flags |= (ATA_R_ATAPI | ATA_R_WRITE);
+ break;
+ default:
+ device_printf(dev, "unknown BIO operation\n");
+ ata_free_request(request);
+ biofinish(bp, NULL, EIO);
+ return;
+ }
+ devstat_start_transaction_bio(stp->stats, bp);
+ ata_queue_request(request);
+}
+
+static void
+ast_done(struct ata_request *request)
+{
+ struct ast_softc *stp = device_get_ivars(request->dev);
+ struct bio *bp = request->driver;
+
+ /* finish up transfer */
+ if ((bp->bio_error = request->result))
+ bp->bio_flags |= BIO_ERROR;
+ if (bp->bio_cmd == BIO_WRITE)
+ stp->flags |= F_DATA_WRITTEN;
+ bp->bio_resid = bp->bio_bcount - request->donecount;
+ ast_total += (bp->bio_bcount - bp->bio_resid);
+ biofinish(bp, stp->stats, 0);
+ ata_free_request(request);
+}
+
+static int
+ast_sense(device_t dev)
+{
+ struct ast_softc *stp = device_get_ivars(dev);
+ int count;
+
+ /* get drive capabilities, some bugridden drives needs this repeated */
+ for (count = 0 ; count < 5 ; count++) {
+ if (!ast_mode_sense(dev, ATAPI_TAPE_CAP_PAGE,
+ &stp->cap, sizeof(stp->cap)) &&
+ stp->cap.page_code == ATAPI_TAPE_CAP_PAGE) {
+ if (stp->cap.blk32k)
+ stp->blksize = 32768;
+ if (stp->cap.blk1024)
+ stp->blksize = 1024;
+ if (stp->cap.blk512)
+ stp->blksize = 512;
+ if (!stp->blksize)
+ continue;
+ 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);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+ast_mode_sense(device_t dev, int page, void *pagebuf, int pagesize)
+{
+ int8_t ccb[16] = { ATAPI_MODE_SENSE, 0x08, page, pagesize>>8, pagesize,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int error;
+
+ error = ata_atapicmd(dev, ccb, pagebuf, pagesize, ATA_R_READ, 10);
+ return error;
+}
+
+static int
+ast_mode_select(device_t dev, void *pagebuf, int pagesize)
+{
+ int8_t ccb[16] = { ATAPI_MODE_SELECT, 0x10, 0, pagesize>>8, pagesize,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, pagebuf, pagesize, 0, 10);
+}
+
+static int
+ast_write_filemark(device_t dev, u_int8_t function)
+{
+ struct ast_softc *stp = device_get_ivars(dev);
+ int8_t ccb[16] = { ATAPI_WEOF, 0x01, 0, 0, function, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int 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 = ata_atapicmd(dev, ccb, NULL, 0, 0, 10);
+ if (error)
+ return error;
+ return ast_wait_dsc(dev, 10*60);
+}
+
+static int
+ast_read_position(device_t dev, int 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 };
+ int error;
+
+ error = ata_atapicmd(dev, ccb, (caddr_t)position,
+ sizeof(struct ast_readposition), ATA_R_READ, 10);
+ position->tape = ntohl(position->tape);
+ position->host = ntohl(position->host);
+ return error;
+}
+
+static int
+ast_space(device_t dev, u_int8_t function, 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 ata_atapicmd(dev, ccb, NULL, 0, 0, 60*60);
+}
+
+static int
+ast_locate(device_t dev, int hard, u_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 };
+ int error;
+
+ error = ata_atapicmd(dev, ccb, NULL, 0, 0, 10);
+ if (error)
+ return error;
+ return ast_wait_dsc(dev, 60*60);
+}
+
+static int
+ast_prevent_allow(device_t dev, int lock)
+{
+ int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+}
+
+static int
+ast_load_unload(device_t dev, u_int8_t function)
+{
+ struct ast_softc *stp = device_get_ivars(dev);
+ int8_t ccb[16] = { ATAPI_START_STOP, 0x01, 0, 0, function, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int error;
+
+ if ((function & ATAPI_SS_EJECT) && !stp->cap.eject)
+ return 0;
+ error = ata_atapicmd(dev, ccb, NULL, 0, 0, 10);
+ if (error)
+ return error;
+ tsleep((caddr_t)&error, PRIBIO, "astlu", 1 * hz);
+ if (function == ATAPI_SS_EJECT)
+ return 0;
+ return ast_wait_dsc(dev, 60*60);
+}
+
+static int
+ast_rewind(device_t dev)
+{
+ int8_t ccb[16] = { ATAPI_REZERO, 0x01, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int error;
+
+ error = ata_atapicmd(dev, ccb, NULL, 0, 0, 10);
+ if (error)
+ return error;
+ return ast_wait_dsc(dev, 60*60);
+}
+
+static int
+ast_erase(device_t dev)
+{
+ int8_t ccb[16] = { ATAPI_ERASE, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ int error;
+
+ if ((error = ast_rewind(dev)))
+ return error;
+
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 60*60);
+}
+
+static int
+ast_test_ready(device_t dev)
+{
+ int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return ata_atapicmd(dev, ccb, NULL, 0, 0, 30);
+}
+
+static int
+ast_wait_dsc(device_t dev, int timeout)
+{
+ int error = 0;
+ int8_t ccb[16] = { ATAPI_POLL_DSC, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ timeout *= hz;
+ while (timeout > 0) {
+ error = ata_atapicmd(dev, ccb, NULL, 0, 0, 0);
+ if (error != EBUSY)
+ break;
+ tsleep(&error, PRIBIO, "atpwt", hz / 2);
+ timeout -= (hz / 2);
+ }
+ return error;
+}
+
+static void
+ast_describe(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ struct ast_softc *stp = device_get_ivars(dev);
+
+ if (bootverbose) {
+ device_printf(dev, "<%.40s/%.8s> tape drive at ata%d as %s\n",
+ atadev->param.model, atadev->param.revision,
+ device_get_unit(ch->dev),
+ (atadev->unit == ATA_MASTER) ? "master" : "slave");
+ device_printf(dev, "%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(atadev->mode));
+ device_printf(dev, "Medium: ");
+ switch (stp->cap.medium_type) {
+ case 0x00:
+ printf("none"); break;
+ case 0x17:
+ printf("Travan 1 (400 Mbyte)"); break;
+ case 0xb6:
+ printf("Travan 4 (4 Gbyte)"); break;
+ case 0xda:
+ printf("OnStream ADR (15Gyte)"); break;
+ default:
+ printf("unknown (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");
+ }
+ else {
+ device_printf(dev, "TAPE <%.40s/%.8s> at ata%d-%s %s\n",
+ atadev->param.model, atadev->param.revision,
+ device_get_unit(ch->dev),
+ (atadev->unit == ATA_MASTER) ? "master" : "slave",
+ ata_mode2str(atadev->mode));
+ }
+}
+
+static device_method_t ast_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ast_probe),
+ DEVMETHOD(device_attach, ast_attach),
+ DEVMETHOD(device_detach, ast_detach),
+ DEVMETHOD(device_shutdown, ast_shutdown),
+
+ /* ATA methods */
+ DEVMETHOD(ata_reinit, ast_reinit),
+
+ { 0, 0 }
+};
+
+static driver_t ast_driver = {
+ "ast",
+ ast_methods,
+ 0,
+};
+
+static devclass_t ast_devclass;
+
+DRIVER_MODULE(ast, ata, ast_driver, ast_devclass, NULL, NULL);
+MODULE_VERSION(ast, 1);
+MODULE_DEPEND(ast, ata, 1, 1, 1);
diff --git a/sys/dev/ata/atapi-tape.h b/sys/dev/ata/atapi-tape.h
new file mode 100644
index 000000000000..b85c8e449805
--- /dev/null
+++ b/sys/dev/ata/atapi-tape.h
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 1998 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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.
+ *
+ * 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$
+ */
+
+/* ATAPI tape drive Capabilities and Mechanical Status Page */
+struct ast_cappage {
+ /* mode page data header */
+ u_int8_t data_length; /* total length of data */
+ u_int8_t medium_type; /* medium type (if any) */
+ u_int8_t reserved :4;
+ u_int8_t mode :3; /* buffering mode */
+ u_int8_t write_protect :1; /* media is writeprotected */
+ u_int8_t blk_desc_len; /* block Descriptor Length */
+
+ /* capabilities page */
+ u_int8_t page_code :6;
+#define ATAPI_TAPE_CAP_PAGE 0x2a
+
+ u_int8_t reserved0_6 :1;
+ u_int8_t ps :1; /* parameters saveable */
+ u_int8_t page_length; /* page Length == 0x12 */
+ u_int8_t reserved2;
+ u_int8_t reserved3;
+ u_int8_t readonly :1; /* read Only Mode */
+ u_int8_t reserved4_1234 :4;
+ u_int8_t reverse :1; /* supports reverse direction */
+ u_int8_t reserved4_67 :2;
+ u_int8_t reserved5_012 :3;
+ u_int8_t eformat :1; /* supports ERASE formatting */
+ u_int8_t reserved5_4 :1;
+ u_int8_t qfa :1; /* supports QFA formats */
+ u_int8_t reserved5_67 :2;
+ u_int8_t lock :1; /* supports locking media */
+ u_int8_t locked :1; /* the media is locked */
+ u_int8_t prevent :1; /* defaults to prevent state */
+ u_int8_t eject :1; /* supports eject */
+ u_int8_t disconnect :1; /* can break request > ctl */
+ u_int8_t reserved6_5 :1;
+ u_int8_t ecc :1; /* supports error correction */
+ u_int8_t compress :1; /* supports data compression */
+ u_int8_t reserved7_0 :1;
+ u_int8_t blk512 :1; /* supports 512b block size */
+ u_int8_t blk1024 :1; /* supports 1024b block size */
+ u_int8_t reserved7_3456 :4;
+ u_int8_t blk32k :1; /* supports 32kb block size */
+ u_int16_t max_speed; /* supported speed in KBps */
+ u_int16_t max_defects; /* max stored defect entries */
+ u_int16_t ctl; /* continuous transfer limit */
+ u_int16_t speed; /* current Speed, in KBps */
+ u_int16_t buffer_size; /* buffer Size, in 512 bytes */
+ u_int8_t reserved18;
+ u_int8_t reserved19;
+};
+
+/* ATAPI OnStream ADR data transfer mode page (ADR unique) */
+struct ast_transferpage {
+ /* mode page data header */
+ u_int8_t data_length; /* total length of data */
+ u_int8_t medium_type; /* medium type (if any) */
+ u_int8_t dsp; /* device specific parameter */
+ u_int8_t blk_desc_len; /* block Descriptor Length */
+
+ /* data transfer page */
+ u_int8_t page_code :6;
+#define ATAPI_TAPE_TRANSFER_PAGE 0x30
+
+ u_int8_t reserved0_6 :1;
+ u_int8_t ps :1; /* parameters saveable */
+ u_int8_t page_length; /* page Length == 0x02 */
+ u_int8_t reserved2;
+ u_int8_t read32k :1; /* 32k blk size (data only) */
+ u_int8_t read32k5 :1; /* 32.5k blk size (data&AUX) */
+ u_int8_t reserved3_23 :2;
+ u_int8_t write32k :1; /* 32k blk size (data only) */
+ u_int8_t write32k5 :1; /* 32.5k blk size (data&AUX) */
+ u_int8_t reserved3_6 :1;
+ u_int8_t streaming :1; /* streaming mode enable */
+};
+
+/* ATAPI OnStream ADR vendor identification mode page (ADR unique) */
+struct ast_identifypage {
+ /* mode page data header */
+ u_int8_t data_length; /* total length of data */
+ u_int8_t medium_type; /* medium type (if any) */
+ u_int8_t dsp; /* device specific parameter */
+ u_int8_t blk_desc_len; /* block Descriptor Length */
+
+ /* data transfer page */
+ u_int8_t page_code :6;
+#define ATAPI_TAPE_IDENTIFY_PAGE 0x36
+
+ u_int8_t reserved0_6 :1;
+ u_int8_t ps :1; /* parameters saveable */
+ u_int8_t page_length; /* page Length == 0x06 */
+ u_int8_t ident[4]; /* host id string */
+ u_int8_t reserved6;
+ u_int8_t reserved7;
+};
+
+/* ATAPI read position structure */
+struct ast_readposition {
+ u_int8_t reserved0_05 :6;
+ u_int8_t eop :1; /* end of partition */
+ u_int8_t bop :1; /* beginning of partition */
+ u_int8_t reserved1;
+ u_int8_t reserved2;
+ u_int8_t reserved3;
+ u_int32_t host; /* frame address in buffer */
+ u_int32_t tape; /* frame address on tape */
+ u_int8_t reserved12;
+ u_int8_t reserved13;
+ u_int8_t reserved14;
+ u_int8_t blks_in_buf; /* blocks in buffer */
+ u_int8_t reserved16;
+ u_int8_t reserved17;
+ u_int8_t reserved18;
+ u_int8_t reserved19;
+};
+
+struct ast_softc {
+ int flags; /* device state flags */
+#define F_CTL_WARN 0x0001 /* warned about CTL wrong? */
+#define F_WRITEPROTECT 0x0002 /* media is writeprotected */
+#define F_DATA_WRITTEN 0x0004 /* data has been written */
+#define F_FM_WRITTEN 0x0008 /* filemark has been written */
+#define F_ONSTREAM 0x0100 /* OnStream ADR device */
+
+ int blksize; /* block size (512 | 1024) */
+ struct atapi_params *param; /* drive parameters table */
+ struct ast_cappage cap; /* capabilities page info */
+ struct devstat *stats; /* devstat entry */
+ struct cdev *dev1, *dev2; /* device place holders */
+};