Index: head/sys/dev/ata/ata-all.c =================================================================== --- head/sys/dev/ata/ata-all.c (revision 83727) +++ head/sys/dev/ata/ata-all.c (revision 83728) @@ -1,1316 +1,1320 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "pci.h" #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __alpha__ #include #endif #include #include #include /* device structures */ static d_ioctl_t ataioctl; static struct cdevsw ata_cdevsw = { /* open */ nullopen, /* close */ nullclose, /* read */ noread, /* write */ nowrite, /* ioctl */ ataioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "ata", /* maj */ 159, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, }; /* prototypes */ static void ata_boot_attach(void); static void ata_intr(void *); static int ata_getparam(struct ata_softc *, int, u_int8_t); static int ata_service(struct ata_softc *); static char *active2str(int); static void bswap(int8_t *, int); static void btrim(int8_t *, int); static void bpack(int8_t *, int8_t *, int); static void ata_change_mode(struct ata_softc *, int, int); /* sysctl vars */ SYSCTL_NODE(_hw, OID_AUTO, ata, CTLFLAG_RD, 0, "ATA driver parameters"); /* global vars */ devclass_t ata_devclass; /* local vars */ static struct intr_config_hook *ata_delayed_attach = NULL; static MALLOC_DEFINE(M_ATA, "ATA generic", "ATA driver generic layer"); /* misc defines */ #define MASTER 0 #define SLAVE 1 int ata_probe(device_t dev) { struct ata_softc *scp; int rid; if (!dev) return ENXIO; scp = device_get_softc(dev); if (!scp) return ENXIO; if (scp->r_io || scp->r_altio || scp->r_irq) return EEXIST; /* initialize the softc basics */ scp->active = ATA_IDLE; scp->dev = dev; rid = ATA_IOADDR_RID; scp->r_io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, ATA_IOSIZE, RF_ACTIVE); if (!scp->r_io) goto failure; rid = ATA_ALTADDR_RID; scp->r_altio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, ATA_ALTIOSIZE, RF_ACTIVE); if (!scp->r_altio) goto failure; rid = ATA_BMADDR_RID; scp->r_bmio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, ATA_BMIOSIZE, RF_ACTIVE); if (bootverbose) ata_printf(scp, -1, "iobase=0x%04x altiobase=0x%04x bmaddr=0x%04x\n", (int)rman_get_start(scp->r_io), (int)rman_get_start(scp->r_altio), (scp->r_bmio) ? (int)rman_get_start(scp->r_bmio) : 0); ata_reset(scp); TAILQ_INIT(&scp->ata_queue); TAILQ_INIT(&scp->atapi_queue); return 0; failure: if (scp->r_io) bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, scp->r_io); if (scp->r_altio) bus_release_resource(dev, SYS_RES_IOPORT, ATA_ALTADDR_RID,scp->r_altio); if (scp->r_bmio) bus_release_resource(dev, SYS_RES_IOPORT, ATA_BMADDR_RID, scp->r_bmio); if (bootverbose) ata_printf(scp, -1, "probe allocation failed\n"); return ENXIO; } int ata_attach(device_t dev) { struct ata_softc *scp; int error, rid; if (!dev) return ENXIO; scp = device_get_softc(dev); if (!scp) return ENXIO; rid = ATA_IRQ_RID; scp->r_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (!scp->r_irq) { ata_printf(scp, -1, "unable to allocate interrupt\n"); return ENXIO; } if ((error = bus_setup_intr(dev, scp->r_irq, INTR_TYPE_BIO|INTR_ENTROPY, ata_intr, scp, &scp->ih))) return error; /* * do not attach devices if we are in early boot, this is done later * when interrupts are enabled by a hook into the boot process. * otherwise attach what the probe has found in scp->devices. */ if (!ata_delayed_attach) { if (scp->devices & ATA_ATA_SLAVE) if (ata_getparam(scp, ATA_SLAVE, ATA_C_ATA_IDENTIFY)) scp->devices &= ~ATA_ATA_SLAVE; if (scp->devices & ATA_ATAPI_SLAVE) if (ata_getparam(scp, ATA_SLAVE, ATA_C_ATAPI_IDENTIFY)) scp->devices &= ~ATA_ATAPI_SLAVE; if (scp->devices & ATA_ATA_MASTER) if (ata_getparam(scp, ATA_MASTER, ATA_C_ATA_IDENTIFY)) scp->devices &= ~ATA_ATA_MASTER; if (scp->devices & ATA_ATAPI_MASTER) if (ata_getparam(scp, ATA_MASTER,ATA_C_ATAPI_IDENTIFY)) scp->devices &= ~ATA_ATAPI_MASTER; #ifdef DEV_ATADISK if (scp->devices & ATA_ATA_MASTER) ad_attach(scp, ATA_MASTER); if (scp->devices & ATA_ATA_SLAVE) ad_attach(scp, ATA_SLAVE); #endif #if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) if (scp->devices & ATA_ATAPI_MASTER) atapi_attach(scp, ATA_MASTER); if (scp->devices & ATA_ATAPI_SLAVE) atapi_attach(scp, ATA_SLAVE); #endif } return 0; } int ata_detach(device_t dev) { struct ata_softc *scp; int s; if (!dev) return ENXIO; scp = device_get_softc(dev); if (!scp || !scp->devices) return ENXIO; /* make sure channel is not busy SOS XXX */ s = splbio(); while (!atomic_cmpset_int(&scp->active, ATA_IDLE, ATA_CONTROL)) tsleep((caddr_t)&s, PRIBIO, "atachm", hz/4); splx(s); /* disable interrupts on devices */ ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | ATA_MASTER); ATA_OUTB(scp->r_altio, ATA_ALTSTAT, ATA_A_IDS | ATA_A_4BIT); ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE); ATA_OUTB(scp->r_altio, ATA_ALTSTAT, ATA_A_IDS | ATA_A_4BIT); #ifdef DEV_ATADISK if (scp->devices & ATA_ATA_MASTER && scp->dev_softc[MASTER]) ad_detach(scp->dev_softc[MASTER], 1); if (scp->devices & ATA_ATA_SLAVE && scp->dev_softc[SLAVE]) ad_detach(scp->dev_softc[SLAVE], 1); #endif #if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) if (scp->devices & ATA_ATAPI_MASTER && scp->dev_softc[MASTER]) atapi_detach(scp->dev_softc[MASTER]); if (scp->devices & ATA_ATAPI_SLAVE && scp->dev_softc[SLAVE]) atapi_detach(scp->dev_softc[SLAVE]); #endif if (scp->dev_param[MASTER]) { free(scp->dev_param[MASTER], M_ATA); scp->dev_param[MASTER] = NULL; } if (scp->dev_param[SLAVE]) { free(scp->dev_param[SLAVE], M_ATA); scp->dev_param[SLAVE] = NULL; } scp->dev_softc[MASTER] = NULL; scp->dev_softc[SLAVE] = NULL; scp->mode[MASTER] = ATA_PIO; scp->mode[SLAVE] = ATA_PIO; scp->devices = 0; bus_teardown_intr(dev, scp->r_irq, scp->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, scp->r_irq); if (scp->r_bmio) bus_release_resource(dev, SYS_RES_IOPORT, ATA_BMADDR_RID, scp->r_bmio); bus_release_resource(dev, SYS_RES_IOPORT, ATA_ALTADDR_RID, scp->r_altio); bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, scp->r_io); scp->r_io = NULL; scp->r_altio = NULL; scp->r_bmio = NULL; scp->r_irq = NULL; scp->active = ATA_IDLE; return 0; } int ata_resume(device_t dev) { struct ata_softc *scp = device_get_softc(dev); ata_reinit(scp); return 0; } static int ataioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td) { struct ata_cmd *iocmd = (struct ata_cmd *)addr; device_t device; int error; if (cmd != IOCATA) return ENOTTY; if (iocmd->channel >= devclass_get_maxunit(ata_devclass)) return ENXIO; if (!(device = devclass_get_device(ata_devclass, iocmd->channel))) return ENODEV; switch (iocmd->cmd) { case ATAATTACH: { /* should enable channel HW on controller that can SOS XXX */ error = ata_probe(device); if (!error) error = ata_attach(device); return error; } case ATADETACH: { error = ata_detach(device); /* should disable channel HW on controller that can SOS XXX */ return error; } case ATAREINIT: { struct ata_softc *scp; int s; scp = device_get_softc(device); if (!scp) return ENODEV; /* make sure channel is not busy SOS XXX */ s = splbio(); while (!atomic_cmpset_int(&scp->active, ATA_IDLE, ATA_ACTIVE)) tsleep((caddr_t)&s, PRIBIO, "atachm", hz/4); error = ata_reinit(scp); splx(s); return error; } case ATAGMODE: { struct ata_softc *scp; scp = device_get_softc(device); if (!scp) return ENODEV; if (scp->dev_param[MASTER]) iocmd->u.mode.mode[MASTER] = scp->mode[MASTER]; else iocmd->u.mode.mode[MASTER] = -1; if (scp->dev_param[SLAVE]) iocmd->u.mode.mode[SLAVE] = scp->mode[SLAVE]; else iocmd->u.mode.mode[SLAVE] = -1; return 0; } case ATASMODE: { struct ata_softc *scp; scp = device_get_softc(device); if (!scp) return ENODEV; if (scp->dev_param[MASTER] && iocmd->u.mode.mode[MASTER] >= 0) { ata_change_mode(scp, ATA_MASTER, iocmd->u.mode.mode[MASTER]); iocmd->u.mode.mode[MASTER] = scp->mode[MASTER]; } else iocmd->u.mode.mode[MASTER] = -1; if (scp->dev_param[SLAVE] && iocmd->u.mode.mode[SLAVE] >= 0) { ata_change_mode(scp, ATA_SLAVE, iocmd->u.mode.mode[SLAVE]); iocmd->u.mode.mode[SLAVE] = scp->mode[SLAVE]; } else iocmd->u.mode.mode[SLAVE] = -1; return 0; } case ATAGPARM: { struct ata_softc *scp; scp = device_get_softc(device); if (!scp) return ENODEV; iocmd->u.param.type[MASTER] = scp->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER); iocmd->u.param.type[SLAVE] = scp->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE); if (scp->dev_name[MASTER]) strcpy(iocmd->u.param.name[MASTER], scp->dev_name[MASTER]); if (scp->dev_name[SLAVE]) strcpy(iocmd->u.param.name[SLAVE], scp->dev_name[SLAVE]); if (scp->dev_param[MASTER]) bcopy(scp->dev_param[MASTER], &iocmd->u.param.params[MASTER], sizeof(struct ata_params)); if (scp->dev_param[SLAVE]) bcopy(scp->dev_param[SLAVE], &iocmd->u.param.params[SLAVE], sizeof(struct ata_params)); return 0; } #if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) case ATAPICMD: { struct ata_softc *scp; struct atapi_softc *atp; caddr_t buf; scp = device_get_softc(device); if (!scp) return ENODEV; if (!scp->dev_softc[iocmd->device] || !(scp->devices & (iocmd->device == 0 ? ATA_ATAPI_MASTER : ATA_ATAPI_SLAVE))) return ENODEV; if (!(buf = malloc(iocmd->u.atapi.count, M_ATA, M_NOWAIT))) return ENOMEM; atp = scp->dev_softc[iocmd->device]; if (iocmd->u.atapi.flags & ATAPI_CMD_WRITE) { error = copyin(iocmd->u.atapi.data, buf, iocmd->u.atapi.count); if (error) return error; } error = atapi_queue_cmd(atp, iocmd->u.atapi.ccb, buf, iocmd->u.atapi.count, (iocmd->u.atapi.flags == ATAPI_CMD_READ ? ATPR_F_READ : 0) | ATPR_F_QUIET, iocmd->u.atapi.timeout, NULL, NULL); if (error) { iocmd->u.atapi.error = error; bcopy(&atp->sense, iocmd->u.atapi.sense_data, sizeof(struct atapi_reqsense)); error = 0; } else if (iocmd->u.atapi.flags & ATAPI_CMD_READ) error = copyout(buf, iocmd->u.atapi.data, iocmd->u.atapi.count); free(buf, M_ATA); return error; } #endif } return ENOTTY; } static int ata_getparam(struct ata_softc *scp, int device, u_int8_t command) { struct ata_params *ata_parm; - int8_t buffer[DEV_BSIZE]; int retry = 0; /* select drive */ ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | device); DELAY(1); /* enable interrupt */ ATA_OUTB(scp->r_altio, ATA_ALTSTAT, ATA_A_4BIT); DELAY(1); /* apparently some devices needs this repeated */ do { if (ata_command(scp, device, command, 0, 0, 0, 0, 0, ATA_WAIT_INTR)) { ata_printf(scp, device, "identify failed\n"); return -1; } if (retry++ > 4) { ata_printf(scp, device, "identify retries exceeded\n"); return -1; } } while (ata_wait(scp, device, ((command == ATA_C_ATAPI_IDENTIFY) ? ATA_S_DRQ : (ATA_S_READY | ATA_S_DSC | ATA_S_DRQ)))); - ATA_INSW(scp->r_io, ATA_DATA, (int16_t *)buffer, - sizeof(buffer)/sizeof(int16_t)); ata_parm = malloc(sizeof(struct ata_params), M_ATA, M_NOWAIT); if (!ata_parm) { + int i; + + for (i = 0; i < sizeof(struct ata_params)/sizeof(int16_t); i++) + ATA_INW(scp->r_io, ATA_DATA); ata_printf(scp, device, "malloc for identify data failed\n"); return -1; } - bcopy(buffer, ata_parm, sizeof(struct ata_params)); + + ATA_INSW(scp->r_io, ATA_DATA, (int16_t *)ata_parm, + sizeof(struct ata_params)/sizeof(int16_t)); + if (command == ATA_C_ATA_IDENTIFY || !((ata_parm->model[0] == 'N' && ata_parm->model[1] == 'E') || (ata_parm->model[0] == 'F' && ata_parm->model[1] == 'X'))) bswap(ata_parm->model, sizeof(ata_parm->model)); btrim(ata_parm->model, sizeof(ata_parm->model)); bpack(ata_parm->model, ata_parm->model, sizeof(ata_parm->model)); bswap(ata_parm->revision, sizeof(ata_parm->revision)); btrim(ata_parm->revision, sizeof(ata_parm->revision)); bpack(ata_parm->revision, ata_parm->revision, sizeof(ata_parm->revision)); scp->dev_param[ATA_DEV(device)] = ata_parm; return 0; } static void ata_boot_attach(void) { struct ata_softc *scp; int ctlr; /* * run through all ata devices and look for real ATA & ATAPI devices * using the hints we found in the early probe, this avoids some of * the delays probing of non-exsistent devices can cause. */ for (ctlr=0; ctlrdevices & ATA_ATA_SLAVE) if (ata_getparam(scp, ATA_SLAVE, ATA_C_ATA_IDENTIFY)) scp->devices &= ~ATA_ATA_SLAVE; if (scp->devices & ATA_ATAPI_SLAVE) if (ata_getparam(scp, ATA_SLAVE, ATA_C_ATAPI_IDENTIFY)) scp->devices &= ~ATA_ATAPI_SLAVE; if (scp->devices & ATA_ATA_MASTER) if (ata_getparam(scp, ATA_MASTER, ATA_C_ATA_IDENTIFY)) scp->devices &= ~ATA_ATA_MASTER; if (scp->devices & ATA_ATAPI_MASTER) if (ata_getparam(scp, ATA_MASTER, ATA_C_ATAPI_IDENTIFY)) scp->devices &= ~ATA_ATAPI_MASTER; } #ifdef DEV_ATADISK /* now we know whats there, do the real attach, first the ATA disks */ for (ctlr=0; ctlrdevices & ATA_ATA_MASTER) ad_attach(scp, ATA_MASTER); if (scp->devices & ATA_ATA_SLAVE) ad_attach(scp, ATA_SLAVE); } #endif #if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) /* then the atapi devices */ for (ctlr=0; ctlrdevices & ATA_ATAPI_MASTER) atapi_attach(scp, ATA_MASTER); if (scp->devices & ATA_ATAPI_SLAVE) atapi_attach(scp, ATA_SLAVE); } #endif if (ata_delayed_attach) { config_intrhook_disestablish(ata_delayed_attach); free(ata_delayed_attach, M_ATA); ata_delayed_attach = NULL; } } static void ata_intr(void *data) { struct ata_softc *scp = (struct ata_softc *)data; /* * on PCI systems we might share an interrupt line with another * device or our twin ATA channel, so call scp->intr_func to figure * out if it is really an interrupt we should process here */ if (scp->intr_func && scp->intr_func(scp)) return; /* if drive is busy it didn't interrupt */ if (ATA_INB(scp->r_altio, ATA_ALTSTAT) & ATA_S_BUSY) { DELAY(100); if (!(ATA_INB(scp->r_altio, ATA_ALTSTAT) & ATA_S_DRQ)) return; } /* clear interrupt and get status */ scp->status = ATA_INB(scp->r_io, ATA_STATUS); if (scp->status & ATA_S_ERROR) scp->error = ATA_INB(scp->r_io, ATA_ERROR); /* find & call the responsible driver to process this interrupt */ switch (scp->active) { #ifdef DEV_ATADISK case ATA_ACTIVE_ATA: if (!scp->running || ad_interrupt(scp->running) == ATA_OP_CONTINUES) return; break; #endif #if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) case ATA_ACTIVE_ATAPI: if (!scp->running || atapi_interrupt(scp->running) == ATA_OP_CONTINUES) return; break; #endif case ATA_WAIT_INTR: case ATA_WAIT_INTR | ATA_CONTROL: wakeup((caddr_t)scp); break; case ATA_WAIT_READY: case ATA_WAIT_READY | ATA_CONTROL: break; case ATA_IDLE: if (scp->flags & ATA_QUEUED) { scp->active = ATA_ACTIVE; /* XXX */ if (ata_service(scp) == ATA_OP_CONTINUES) return; } /* FALLTHROUGH */ default: #ifdef ATA_DEBUG { static int intr_count = 0; if (intr_count++ < 10) ata_printf(scp, -1, "unwanted interrupt %d %sstatus = %02x\n", intr_count, active2str(scp->active), scp->status); } #endif } scp->active &= ATA_CONTROL; if (scp->active & ATA_CONTROL) return; scp->running = NULL; ata_start(scp); return; } void ata_start(struct ata_softc *scp) { #ifdef DEV_ATADISK struct ad_request *ad_request; #endif #if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) struct atapi_request *atapi_request; #endif if (!atomic_cmpset_int(&scp->active, ATA_IDLE, ATA_ACTIVE)) return; #ifdef DEV_ATADISK /* find & call the responsible driver if anything on the ATA queue */ if (TAILQ_EMPTY(&scp->ata_queue)) { if (scp->devices & (ATA_ATA_MASTER) && scp->dev_softc[MASTER]) ad_start((struct ad_softc *)scp->dev_softc[MASTER]); if (scp->devices & (ATA_ATA_SLAVE) && scp->dev_softc[SLAVE]) ad_start((struct ad_softc *)scp->dev_softc[SLAVE]); } if ((ad_request = TAILQ_FIRST(&scp->ata_queue))) { TAILQ_REMOVE(&scp->ata_queue, ad_request, chain); scp->active = ATA_ACTIVE_ATA; scp->running = ad_request; if (ad_transfer(ad_request) == ATA_OP_CONTINUES) return; } #endif #if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) /* find & call the responsible driver if anything on the ATAPI queue */ if (TAILQ_EMPTY(&scp->atapi_queue)) { if (scp->devices & (ATA_ATAPI_MASTER) && scp->dev_softc[MASTER]) atapi_start((struct atapi_softc *)scp->dev_softc[MASTER]); if (scp->devices & (ATA_ATAPI_SLAVE) && scp->dev_softc[SLAVE]) atapi_start((struct atapi_softc *)scp->dev_softc[SLAVE]); } if ((atapi_request = TAILQ_FIRST(&scp->atapi_queue))) { TAILQ_REMOVE(&scp->atapi_queue, atapi_request, chain); scp->active = ATA_ACTIVE_ATAPI; scp->running = atapi_request; if (atapi_transfer(atapi_request) == ATA_OP_CONTINUES) return; } #endif scp->active = ATA_IDLE; } void ata_reset(struct ata_softc *scp) { u_int8_t lsb, msb, ostat0, ostat1; u_int8_t stat0 = ATA_S_BUSY, stat1 = ATA_S_BUSY; int mask = 0, timeout; /* do we have any signs of ATA/ATAPI HW being present ? */ ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | ATA_MASTER); DELAY(10); ostat0 = ATA_INB(scp->r_io, ATA_STATUS); if ((ostat0 & 0xf8) != 0xf8 && ostat0 != 0xa5) mask |= 0x01; ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE); DELAY(10); ostat1 = ATA_INB(scp->r_io, ATA_STATUS); if ((ostat1 & 0xf8) != 0xf8 && ostat1 != 0xa5) mask |= 0x02; scp->devices = 0; if (!mask) return; /* in some setups we dont want to test for a slave */ if (scp->flags & ATA_NO_SLAVE) mask &= ~0x02; if (bootverbose) ata_printf(scp, -1, "mask=%02x ostat0=%02x ostat2=%02x\n", mask, ostat0, ostat1); /* reset channel */ ATA_OUTB(scp->r_altio, ATA_ALTSTAT, ATA_A_IDS | ATA_A_RESET); DELAY(10000); ATA_OUTB(scp->r_altio, ATA_ALTSTAT, ATA_A_IDS); DELAY(100000); ATA_INB(scp->r_io, ATA_ERROR); /* wait for BUSY to go inactive */ for (timeout = 0; timeout < 310000; timeout++) { if (stat0 & ATA_S_BUSY) { ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | ATA_MASTER); DELAY(10); stat0 = ATA_INB(scp->r_io, ATA_STATUS); if (!(stat0 & ATA_S_BUSY)) { /* check for ATAPI signature while its still there */ lsb = ATA_INB(scp->r_io, ATA_CYL_LSB); msb = ATA_INB(scp->r_io, ATA_CYL_MSB); if (bootverbose) ata_printf(scp, ATA_MASTER, "ATAPI probe %02x %02x\n", lsb, msb); if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) scp->devices |= ATA_ATAPI_MASTER; } } if (stat1 & ATA_S_BUSY) { ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE); DELAY(10); stat1 = ATA_INB(scp->r_io, ATA_STATUS); if (!(stat1 & ATA_S_BUSY)) { /* check for ATAPI signature while its still there */ lsb = ATA_INB(scp->r_io, ATA_CYL_LSB); msb = ATA_INB(scp->r_io, ATA_CYL_MSB); if (bootverbose) ata_printf(scp, ATA_SLAVE, "ATAPI probe %02x %02x\n", lsb, msb); if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) scp->devices |= ATA_ATAPI_SLAVE; } } if (mask == 0x01) /* wait for master only */ if (!(stat0 & ATA_S_BUSY)) break; if (mask == 0x02) /* wait for slave only */ if (!(stat1 & ATA_S_BUSY)) break; if (mask == 0x03) /* wait for both master & slave */ if (!(stat0 & ATA_S_BUSY) && !(stat1 & ATA_S_BUSY)) break; DELAY(100); } DELAY(10); ATA_OUTB(scp->r_altio, ATA_ALTSTAT, ATA_A_4BIT); if (stat0 & ATA_S_BUSY) mask &= ~0x01; if (stat1 & ATA_S_BUSY) mask &= ~0x02; if (bootverbose) ata_printf(scp, -1, "mask=%02x stat0=%02x stat1=%02x\n", mask, stat0, stat1); if (!mask) return; if (mask & 0x01 && ostat0 != 0x00 && !(scp->devices & ATA_ATAPI_MASTER)) { ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | ATA_MASTER); DELAY(10); ATA_OUTB(scp->r_io, ATA_ERROR, 0x58); ATA_OUTB(scp->r_io, ATA_CYL_LSB, 0xa5); lsb = ATA_INB(scp->r_io, ATA_ERROR); msb = ATA_INB(scp->r_io, ATA_CYL_LSB); if (bootverbose) ata_printf(scp, ATA_MASTER, "ATA probe %02x %02x\n", lsb, msb); if (lsb != 0x58 && msb == 0xa5) scp->devices |= ATA_ATA_MASTER; } if (mask & 0x02 && ostat1 != 0x00 && !(scp->devices & ATA_ATAPI_SLAVE)) { ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE); DELAY(10); ATA_OUTB(scp->r_io, ATA_ERROR, 0x58); ATA_OUTB(scp->r_io, ATA_CYL_LSB, 0xa5); lsb = ATA_INB(scp->r_io, ATA_ERROR); msb = ATA_INB(scp->r_io, ATA_CYL_LSB); if (bootverbose) ata_printf(scp, ATA_SLAVE, "ATA probe %02x %02x\n", lsb, msb); if (lsb != 0x58 && msb == 0xa5) scp->devices |= ATA_ATA_SLAVE; } if (bootverbose) ata_printf(scp, -1, "devices=%02x\n", scp->devices); } int ata_reinit(struct ata_softc *scp) { int devices, misdev, newdev; if (!scp->r_io || !scp->r_altio || !scp->r_irq) return ENXIO; scp->active = ATA_CONTROL; scp->running = NULL; devices = scp->devices; ata_printf(scp, -1, "resetting devices .. "); ata_reset(scp); if ((misdev = devices & ~scp->devices)) { if (misdev) printf("\n"); #ifdef DEV_ATADISK if (misdev & ATA_ATA_MASTER && scp->dev_softc[MASTER]) ad_detach(scp->dev_softc[MASTER], 0); if (misdev & ATA_ATA_SLAVE && scp->dev_softc[SLAVE]) ad_detach(scp->dev_softc[SLAVE], 0); #endif #if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) if (misdev & ATA_ATAPI_MASTER && scp->dev_softc[MASTER]) atapi_detach(scp->dev_softc[MASTER]); if (misdev & ATA_ATAPI_SLAVE && scp->dev_softc[SLAVE]) atapi_detach(scp->dev_softc[SLAVE]); #endif if (misdev & ATA_ATA_MASTER || misdev & ATA_ATAPI_MASTER) { free(scp->dev_param[MASTER], M_ATA); scp->dev_param[MASTER] = NULL; } if (misdev & ATA_ATA_SLAVE || misdev & ATA_ATAPI_SLAVE) { free(scp->dev_param[SLAVE], M_ATA); scp->dev_param[SLAVE] = NULL; } } if ((newdev = ~devices & scp->devices)) { if (newdev & ATA_ATA_MASTER) if (ata_getparam(scp, ATA_MASTER, ATA_C_ATA_IDENTIFY)) newdev &= ~ATA_ATA_MASTER; if (newdev & ATA_ATA_SLAVE) if (ata_getparam(scp, ATA_SLAVE, ATA_C_ATA_IDENTIFY)) newdev &= ~ATA_ATA_SLAVE; if (newdev & ATA_ATAPI_MASTER) if (ata_getparam(scp, ATA_MASTER, ATA_C_ATAPI_IDENTIFY)) newdev &= ~ATA_ATAPI_MASTER; if (newdev & ATA_ATAPI_SLAVE) if (ata_getparam(scp, ATA_SLAVE, ATA_C_ATAPI_IDENTIFY)) newdev &= ~ATA_ATAPI_SLAVE; } if (!misdev && newdev) printf("\n"); #ifdef DEV_ATADISK if (newdev & ATA_ATA_MASTER && !scp->dev_softc[MASTER]) ad_attach(scp, ATA_MASTER); else if (scp->devices & ATA_ATA_MASTER && scp->dev_softc[MASTER]) ad_reinit((struct ad_softc *)scp->dev_softc[MASTER]); if (newdev & ATA_ATA_SLAVE && !scp->dev_softc[SLAVE]) ad_attach(scp, ATA_SLAVE); else if (scp->devices & (ATA_ATA_SLAVE) && scp->dev_softc[SLAVE]) ad_reinit((struct ad_softc *)scp->dev_softc[SLAVE]); #endif #if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) if (newdev & ATA_ATAPI_MASTER && !scp->dev_softc[MASTER]) atapi_attach(scp, ATA_MASTER); else if (scp->devices & (ATA_ATAPI_MASTER) && scp->dev_softc[MASTER]) atapi_reinit((struct atapi_softc *)scp->dev_softc[MASTER]); if (newdev & ATA_ATAPI_SLAVE && !scp->dev_softc[SLAVE]) atapi_attach(scp, ATA_SLAVE); else if (scp->devices & (ATA_ATAPI_SLAVE) && scp->dev_softc[SLAVE]) atapi_reinit((struct atapi_softc *)scp->dev_softc[SLAVE]); #endif printf("done\n"); scp->active = ATA_IDLE; ata_start(scp); return 0; } static int ata_service(struct ata_softc *scp) { /* do we have a SERVICE request from the drive ? */ if ((scp->status & (ATA_S_SERVICE|ATA_S_ERROR|ATA_S_DRQ)) == ATA_S_SERVICE){ ATA_OUTB(scp->r_bmio, ATA_BMSTAT_PORT, ata_dmastatus(scp) | ATA_BMSTAT_INTERRUPT); #ifdef DEV_ATADISK if ((ATA_INB(scp->r_io, ATA_DRIVE) & ATA_SLAVE) == ATA_MASTER) { if ((scp->devices & ATA_ATA_MASTER) && scp->dev_softc[MASTER]) return ad_service((struct ad_softc *)scp->dev_softc[MASTER], 0); } else { if ((scp->devices & ATA_ATA_SLAVE) && scp->dev_softc[SLAVE]) return ad_service((struct ad_softc *)scp->dev_softc[SLAVE], 0); } #endif } return ATA_OP_FINISHED; } int ata_wait(struct ata_softc *scp, int device, u_int8_t mask) { int timeout = 0; DELAY(1); while (timeout < 5000000) { /* timeout 5 secs */ scp->status = ATA_INB(scp->r_io, ATA_STATUS); /* if drive fails status, reselect the drive just to be sure */ if (scp->status == 0xff) { ata_printf(scp, device, "no status, reselecting device\n"); ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | device); DELAY(10); scp->status = ATA_INB(scp->r_io, ATA_STATUS); if (scp->status == 0xff) return -1; } /* are we done ? */ if (!(scp->status & ATA_S_BUSY)) break; if (timeout > 1000) { timeout += 1000; DELAY(1000); } else { timeout += 10; DELAY(10); } } if (scp->status & ATA_S_ERROR) scp->error = ATA_INB(scp->r_io, ATA_ERROR); if (timeout >= 5000000) return -1; if (!mask) return (scp->status & ATA_S_ERROR); /* Wait 50 msec for bits wanted. */ timeout = 5000; while (timeout--) { scp->status = ATA_INB(scp->r_io, ATA_STATUS); if ((scp->status & mask) == mask) { if (scp->status & ATA_S_ERROR) scp->error = ATA_INB(scp->r_io, ATA_ERROR); return (scp->status & ATA_S_ERROR); } DELAY (10); } return -1; } int ata_command(struct ata_softc *scp, int device, u_int8_t command, u_int16_t cylinder, u_int8_t head, u_int8_t sector, u_int8_t count, u_int8_t feature, int flags) { int error = 0; #ifdef ATA_DEBUG ata_printf(scp, device, "ata_command: addr=%04x, cmd=%02x, " "c=%d, h=%d, s=%d, count=%d, feature=%d, flags=%02x\n", rman_get_start(scp->r_io), command, cylinder, head, sector, count, feature, flags); /* sanity checks */ switch(scp->active) { case ATA_IDLE: break; case ATA_CONTROL: if (flags == ATA_WAIT_INTR || flags == ATA_WAIT_READY) break; goto out; case ATA_ACTIVE_ATA: case ATA_ACTIVE_ATAPI: if (flags == ATA_IMMEDIATE) break; default: out: printf("ata_command called %s flags=%s cmd=%02x\n", active2str(scp->active), active2str(flags), command); break; } #endif /* disable interrupt from device */ if (scp->flags & ATA_QUEUED) ATA_OUTB(scp->r_altio, ATA_ALTSTAT, ATA_A_IDS | ATA_A_4BIT); /* select device */ ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | device); /* ready to issue command ? */ if (ata_wait(scp, device, 0) < 0) { ata_printf(scp, device, "timeout waiting to give command=%02x s=%02x e=%02x\n", command, scp->status, scp->error); return -1; } ATA_OUTB(scp->r_io, ATA_FEATURE, feature); ATA_OUTB(scp->r_io, ATA_COUNT, count); ATA_OUTB(scp->r_io, ATA_SECTOR, sector); ATA_OUTB(scp->r_io, ATA_CYL_MSB, cylinder >> 8); ATA_OUTB(scp->r_io, ATA_CYL_LSB, cylinder); ATA_OUTB(scp->r_io, ATA_DRIVE, ATA_D_IBM | device | head); switch (flags) { case ATA_WAIT_INTR: scp->active |= ATA_WAIT_INTR; ATA_OUTB(scp->r_io, ATA_CMD, command); /* enable interrupt */ if (scp->flags & ATA_QUEUED) ATA_OUTB(scp->r_altio, ATA_ALTSTAT, ATA_A_4BIT); if (tsleep((caddr_t)scp, PRIBIO, "atacmd", 10 * hz) != 0) { ata_printf(scp, device, "ata_command: timeout waiting for intr\n"); scp->active &= ~ATA_WAIT_INTR; error = -1; } break; case ATA_WAIT_READY: scp->active |= ATA_WAIT_READY; ATA_OUTB(scp->r_io, ATA_CMD, command); if (ata_wait(scp, device, ATA_S_READY) < 0) { ata_printf(scp, device, "timeout waiting for command=%02x s=%02x e=%02x\n", command, scp->status, scp->error); error = -1; } scp->active &= ~ATA_WAIT_READY; break; case ATA_IMMEDIATE: ATA_OUTB(scp->r_io, ATA_CMD, command); break; default: ata_printf(scp, device, "DANGER: illegal interrupt flag=%s\n", active2str(flags)); } /* enable interrupt */ if (scp->flags & ATA_QUEUED) ATA_OUTB(scp->r_altio, ATA_ALTSTAT, ATA_A_4BIT); return error; } void -ata_set_name(struct ata_softc *scp, int device, char *name) +ata_set_name(struct ata_softc *scp, int device, char *name, int lun) { - scp->dev_name[ATA_DEV(device)] = malloc(strlen(name) + 1, M_ATA, M_NOWAIT); + scp->dev_name[ATA_DEV(device)] = malloc(strlen(name) + 4, M_ATA, M_NOWAIT); if (scp->dev_name[ATA_DEV(device)]) - strcpy(scp->dev_name[ATA_DEV(device)], name); + sprintf(scp->dev_name[ATA_DEV(device)], "%s%d", name, lun); } void ata_free_name(struct ata_softc *scp, int device) { if (scp->dev_name[ATA_DEV(device)]) free(scp->dev_name[ATA_DEV(device)], M_ATA); } int ata_get_lun(u_int32_t *map) { int lun = ffs(~*map) - 1; *map |= (1 << lun); return lun; } int ata_test_lun(u_int32_t *map, int lun) { return (*map & (1 << lun)); } void ata_free_lun(u_int32_t *map, int lun) { *map &= ~(1 << lun); } int ata_printf(struct ata_softc *scp, int device, const char * fmt, ...) { va_list ap; int ret; if (device == -1) ret = printf("ata%d: ", device_get_unit(scp->dev)); else { if (scp->dev_name[ATA_DEV(device)]) ret = printf("%s: ", scp->dev_name[ATA_DEV(device)]); else ret = printf("ata%d-%s: ", device_get_unit(scp->dev), (device == ATA_MASTER) ? "master" : "slave"); } va_start(ap, fmt); ret += vprintf(fmt, ap); va_end(ap); return ret; } char * ata_mode2str(int mode) { switch (mode) { case ATA_PIO: return "BIOSPIO"; 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_WDMA2: return "WDMA2"; case ATA_UDMA2: return "UDMA33"; case ATA_UDMA4: return "UDMA66"; case ATA_UDMA5: return "UDMA100"; case ATA_DMA: return "BIOSDMA"; default: return "???"; } } int ata_pio2mode(int pio) { switch (pio) { default: case 0: return ATA_PIO0; case 1: return ATA_PIO1; case 2: return ATA_PIO2; case 3: return ATA_PIO3; case 4: return ATA_PIO4; } } int ata_pmode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_64_70) { if (ap->apiomodes & 2) return 4; if (ap->apiomodes & 1) return 3; } if (ap->opiomode == 2) return 2; if (ap->opiomode == 1) return 1; if (ap->opiomode == 0) return 0; return -1; } int ata_wmode(struct ata_params *ap) { if (ap->wdmamodes & 4) return 2; if (ap->wdmamodes & 2) return 1; if (ap->wdmamodes & 1) return 0; return -1; } int ata_umode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_88) { if (ap->udmamodes & 0x20) return 5; if (ap->udmamodes & 0x10) return 4; if (ap->udmamodes & 0x08) return 3; if (ap->udmamodes & 0x04) return 2; if (ap->udmamodes & 0x02) return 1; if (ap->udmamodes & 0x01) return 0; } return -1; } static char * active2str(int active) { static char buf[64]; bzero(buf, sizeof(buf)); if (active & ATA_IDLE) strcat(buf, "ATA_IDLE "); if (active & ATA_IMMEDIATE) strcat(buf, "ATA_IMMEDIATE "); if (active & ATA_WAIT_INTR) strcat(buf, "ATA_WAIT_INTR "); if (active & ATA_WAIT_READY) strcat(buf, "ATA_WAIT_READY "); if (active & ATA_ACTIVE) strcat(buf, "ATA_ACTIVE "); if (active & ATA_ACTIVE_ATA) strcat(buf, "ATA_ACTIVE_ATA "); if (active & ATA_ACTIVE_ATAPI) strcat(buf, "ATA_ACTIVE_ATAPI "); if (active & ATA_CONTROL) strcat(buf, "ATA_CONTROL "); return buf; } 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 = ' '; 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; } static void ata_change_mode(struct ata_softc *scp, int device, int mode) { int umode, wmode, pmode; int s = splbio(); while (!atomic_cmpset_int(&scp->active, ATA_IDLE, ATA_ACTIVE)) tsleep((caddr_t)&s, PRIBIO, "atachm", hz/4); umode = ata_umode(ATA_PARAM(scp, device)); wmode = ata_wmode(ATA_PARAM(scp, device)); pmode = ata_pmode(ATA_PARAM(scp, device)); switch (mode & ATA_DMA_MASK) { case ATA_UDMA: if ((mode & ATA_MODE_MASK) < umode) umode = mode & ATA_MODE_MASK; break; case ATA_WDMA: if ((mode & ATA_MODE_MASK) < wmode) wmode = mode & ATA_MODE_MASK; umode = -1; break; default: if (((mode & ATA_MODE_MASK) - ATA_PIO0) < pmode) pmode = (mode & ATA_MODE_MASK) - ATA_PIO0; umode = -1; wmode = -1; } ata_dmainit(scp, device, pmode, wmode, umode); scp->active = ATA_IDLE; ata_start(scp); splx(s); } static void ata_init(void) { /* register controlling device */ make_dev(&ata_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "ata"); /* 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; } 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); } } SYSINIT(atadev, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_init, NULL) Index: head/sys/dev/ata/ata-all.h =================================================================== --- head/sys/dev/ata/ata-all.h (revision 83727) +++ head/sys/dev/ata/ata-all.h (revision 83728) @@ -1,267 +1,267 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* ATA register defines */ #define ATA_DATA 0x00 /* data register */ #define ATA_ERROR 0x01 /* (R) error register */ #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_FEATURE 0x01 /* (W) feature register */ #define ATA_F_DMA 0x01 /* enable DMA */ #define ATA_F_OVL 0x02 /* enable overlap */ #define ATA_COUNT 0x02 /* (W) sector count */ #define ATA_IREASON 0x02 /* (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_SECTOR 0x03 /* sector # */ #define ATA_CYL_LSB 0x04 /* cylinder# LSB */ #define ATA_CYL_MSB 0x05 /* cylinder# MSB */ #define ATA_DRIVE 0x06 /* Sector/Drive/Head register */ #define ATA_D_LBA 0x40 /* use LBA addressing */ #define ATA_D_IBM 0xa0 /* 512 byte sectors, ECC */ #define ATA_CMD 0x07 /* command register */ #define ATA_C_NOP 0x00 /* NOP command */ #define ATA_C_F_FLUSHQUEUE 0x00 /* flush queued cmd's */ #define ATA_C_F_AUTOPOLL 0x01 /* start autopoll function */ #define ATA_C_ATAPI_RESET 0x08 /* reset ATAPI device */ #define ATA_C_READ 0x20 /* read command */ #define ATA_C_WRITE 0x30 /* write command */ #define ATA_C_PACKET_CMD 0xa0 /* packet command */ #define ATA_C_ATAPI_IDENTIFY 0xa1 /* get ATAPI params*/ #define ATA_C_SERVICE 0xa2 /* service command */ #define ATA_C_READ_MUL 0xc4 /* read multi command */ #define ATA_C_WRITE_MUL 0xc5 /* write multi command */ #define ATA_C_SET_MULTI 0xc6 /* set multi size command */ #define ATA_C_READ_DMA_QUEUED 0xc7 /* read w/DMA QUEUED command */ #define ATA_C_READ_DMA 0xc8 /* read w/DMA command */ #define ATA_C_WRITE_DMA 0xca /* write w/DMA command */ #define ATA_C_WRITE_DMA_QUEUED 0xcc /* write w/DMA QUEUED command */ #define ATA_C_SLEEP 0xe6 /* sleep command */ #define ATA_C_FLUSHCACHE 0xe7 /* flush cache to disk */ #define ATA_C_ATA_IDENTIFY 0xec /* get ATA params */ #define ATA_C_SETFEATURES 0xef /* features command */ #define ATA_C_F_SETXFER 0x03 /* set transfer mode */ #define ATA_C_F_ENAB_WCACHE 0x02 /* enable write cache */ #define ATA_C_F_DIS_WCACHE 0x82 /* disable write cache */ #define ATA_C_F_ENAB_RCACHE 0xaa /* enable readahead cache */ #define ATA_C_F_DIS_RCACHE 0x55 /* disable readahead cache */ #define ATA_C_F_ENAB_RELIRQ 0x5d /* enable release interrupt */ #define ATA_C_F_DIS_RELIRQ 0xdd /* disable release interrupt */ #define ATA_C_F_ENAB_SRVIRQ 0x5e /* enable service interrupt */ #define ATA_C_F_DIS_SRVIRQ 0xde /* disable service interrupt */ #define ATA_STATUS 0x07 /* status register */ #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_ALTSTAT 0x00 /* alternate status register */ #define ATA_ALTOFFSET 0x206 /* alternate registers offset */ #define ATA_PCCARD_ALTOFFSET 0x0e /* do for PCCARD devices */ #define ATA_A_IDS 0x02 /* disable interrupts */ #define ATA_A_RESET 0x04 /* RESET controller */ #define ATA_A_4BIT 0x08 /* 4 head bits */ /* misc defines */ #define ATA_PRIMARY 0x1f0 #define ATA_SECONDARY 0x170 #define ATA_MASTER 0x00 #define ATA_SLAVE 0x10 #define ATA_IOSIZE 0x08 #define ATA_ALTIOSIZE 0x01 #define ATA_BMIOSIZE 0x08 #define ATA_OP_FINISHED 0x00 #define ATA_OP_CONTINUES 0x01 #define ATA_DEV(device) ((device == ATA_MASTER) ? 0 : 1) #define ATA_PARAM(scp, device) (scp->dev_param[ATA_DEV(device)]) #define ATA_IOADDR_RID 0 #define ATA_ALTADDR_RID 1 #define ATA_BMADDR_RID 2 #define ATA_IRQ_RID 0 /* busmaster DMA related defines */ #define ATA_DMA_ENTRIES 256 #define ATA_DMA_EOT 0x80000000 #define ATA_BMCMD_PORT 0x00 #define ATA_BMCMD_START_STOP 0x01 #define ATA_BMCMD_WRITE_READ 0x08 #define ATA_BMSTAT_PORT 0x02 #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_BMDTP_PORT 0x04 /* structure for holding DMA address data */ struct ata_dmaentry { u_int32_t base; u_int32_t count; }; /* structure describing an ATA device */ struct ata_softc { struct device *dev; /* device handle */ int channel; /* channel on this controller */ struct resource *r_io; /* io addr resource handle */ struct resource *r_altio; /* altio addr resource handle */ struct resource *r_bmio; /* bmio addr resource handle */ struct resource *r_irq; /* interrupt of this channel */ void *ih; /* interrupt handle */ int (*intr_func)(struct ata_softc *); /* interrupt function */ u_int32_t chiptype; /* pciid of controller chip */ u_int32_t alignment; /* dma engine min alignment */ char *dev_name[2]; /* name of device */ struct ata_params *dev_param[2]; /* ptr to devices params */ void *dev_softc[2]; /* ptr to devices softc's */ int mode[2]; /* transfer mode for devices */ int flags; /* controller flags */ #define ATA_DMA_ACTIVE 0x01 #define ATA_ATAPI_DMA_RO 0x02 #define ATA_USE_16BIT 0x04 #define ATA_NO_SLAVE 0x08 #define ATA_QUEUED 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 u_int8_t status; /* last controller status */ u_int8_t error; /* last controller error */ int active; /* active processing request */ #define ATA_IDLE 0x0000 #define ATA_IMMEDIATE 0x0001 #define ATA_WAIT_INTR 0x0002 #define ATA_WAIT_READY 0x0004 #define ATA_ACTIVE 0x0008 #define ATA_ACTIVE_ATA 0x0010 #define ATA_ACTIVE_ATAPI 0x0020 #define ATA_CONTROL 0x0040 TAILQ_HEAD(, ad_request) ata_queue; /* head of ATA queue */ TAILQ_HEAD(, atapi_request) atapi_queue; /* head of ATAPI queue */ void *running; /* currently running request */ }; /* externs */ extern devclass_t ata_devclass; /* public prototypes */ int ata_probe(device_t); int ata_attach(device_t); int ata_detach(device_t); int ata_resume(device_t); void ata_start(struct ata_softc *); void ata_reset(struct ata_softc *); int ata_reinit(struct ata_softc *); int ata_wait(struct ata_softc *, int, u_int8_t); int ata_command(struct ata_softc *, int, u_int8_t, u_int16_t, u_int8_t, u_int8_t, u_int8_t, u_int8_t, int); int ata_printf(struct ata_softc *, int, const char *, ...) __printflike(3, 4); -void ata_set_name(struct ata_softc *, int, char *); +void ata_set_name(struct ata_softc *, int, char *, int); void ata_free_name(struct ata_softc *, int); int ata_get_lun(u_int32_t *); int ata_test_lun(u_int32_t *, int); void ata_free_lun(u_int32_t *, int); char *ata_mode2str(int); int ata_pio2mode(int); int ata_pmode(struct ata_params *); int ata_wmode(struct ata_params *); int ata_umode(struct ata_params *); int ata_find_dev(device_t, u_int32_t, u_int32_t); void *ata_dmaalloc(struct ata_softc *, int); void ata_dmainit(struct ata_softc *, int, int, int, int); int ata_dmasetup(struct ata_softc *, int, struct ata_dmaentry *, caddr_t, int); void ata_dmastart(struct ata_softc *, int, struct ata_dmaentry *, int); int ata_dmastatus(struct ata_softc *); int ata_dmadone(struct ata_softc *); /* 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_INSL(res, offset, addr, count) \ bus_space_read_multi_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_OUTSL(res, offset, addr, count) \ bus_space_write_multi_4(rman_get_bustag((res)), \ rman_get_bushandle((res)), \ (offset), (addr), (count)) Index: head/sys/dev/ata/ata-card.c =================================================================== --- head/sys/dev/ata/ata-card.c (revision 83727) +++ head/sys/dev/ata/ata-card.c (revision 83728) @@ -1,108 +1,108 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int ata_pccard_probe(device_t dev) { struct ata_softc *scp = device_get_softc(dev); struct resource *io; int rid, len, start, end; u_long tmp; /* allocate the io range to get start and length */ rid = ATA_IOADDR_RID; len = bus_get_resource_count(dev, SYS_RES_IOPORT, rid); io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, ATA_IOSIZE, RF_ACTIVE); if (!io) return ENOMEM; /* reallocate the io address to only cover the io ports */ start = rman_get_start(io); end = start + ATA_IOSIZE - 1; bus_release_resource(dev, SYS_RES_IOPORT, rid, io); io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, start, end, ATA_IOSIZE, RF_ACTIVE); bus_release_resource(dev, SYS_RES_IOPORT, rid, io); /* * if we got more than the default ATA_IOSIZE ports, this is likely * a pccard system where the altio ports are located at offset 14 * otherwise its the normal altio offset */ if (bus_get_resource(dev, SYS_RES_IOPORT, ATA_ALTADDR_RID, &tmp, &tmp)) { if (len > ATA_IOSIZE) { bus_set_resource(dev, SYS_RES_IOPORT, ATA_ALTADDR_RID, start + ATA_PCCARD_ALTOFFSET, ATA_ALTIOSIZE); } else { bus_set_resource(dev, SYS_RES_IOPORT, ATA_ALTADDR_RID, start + ATA_ALTOFFSET, ATA_ALTIOSIZE); } } else return ENOMEM; scp->channel = 0; scp->flags |= (ATA_USE_16BIT | ATA_NO_SLAVE); return ata_probe(dev); } static device_method_t ata_pccard_methods[] = { /* device interface */ DEVMETHOD(device_probe, ata_pccard_probe), DEVMETHOD(device_attach, ata_attach), DEVMETHOD(device_detach, ata_detach), { 0, 0 } }; static driver_t ata_pccard_driver = { "ata", ata_pccard_methods, sizeof(struct ata_softc), }; DRIVER_MODULE(ata, pccard, ata_pccard_driver, ata_devclass, 0, 0); Index: head/sys/dev/ata/ata-disk.c =================================================================== --- head/sys/dev/ata/ata-disk.c (revision 83727) +++ head/sys/dev/ata/ata-disk.c (revision 83728) @@ -1,997 +1,995 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_global.h" #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* device structures */ static d_open_t adopen; static d_close_t adclose; static d_strategy_t adstrategy; static d_dump_t addump; static struct cdevsw ad_cdevsw = { /* open */ adopen, /* close */ adclose, /* read */ physread, /* write */ physwrite, /* ioctl */ noioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ adstrategy, /* name */ "ad", /* maj */ 116, /* dump */ addump, /* psize */ nopsize, /* flags */ D_DISK, }; static struct cdevsw addisk_cdevsw; /* prototypes */ static void ad_invalidatequeue(struct ad_softc *, struct ad_request *); static int ad_tagsupported(struct ad_softc *); static void ad_timeout(struct ad_request *); static void ad_free(struct ad_request *); static int ad_version(u_int16_t); /* internal vars */ static u_int32_t adp_lun_map = 0; static MALLOC_DEFINE(M_AD, "AD driver", "ATA disk driver"); static int ata_dma = 1; static int ata_wc = 0; static int ata_tags = 0; TUNABLE_INT("hw.ata.ata_dma", &ata_dma); TUNABLE_INT("hw.ata.wc", &ata_wc); TUNABLE_INT("hw.ata.tags", &ata_tags); /* sysctl vars */ SYSCTL_DECL(_hw_ata); SYSCTL_INT(_hw_ata, OID_AUTO, ata_dma, CTLFLAG_RD, &ata_dma, 0, "ATA disk DMA mode control"); SYSCTL_INT(_hw_ata, OID_AUTO, wc, CTLFLAG_RD, &ata_wc, 0, "ATA disk write caching"); SYSCTL_INT(_hw_ata, OID_AUTO, tags, CTLFLAG_RD, &ata_tags, 0, "ATA disk tagged queuing support"); /* defines */ #define AD_MAX_RETRIES 3 #define AD_PARAM ATA_PARAM(adp->controller, adp->unit) /* experimental cache flush on BIO_ORDERED */ #undef ATA_FLUSHCACHE_ON void ad_attach(struct ata_softc *scp, int device) { struct ad_softc *adp; dev_t dev; int secsperint; - char name[16]; if (!(adp = malloc(sizeof(struct ad_softc), M_AD, M_NOWAIT | M_ZERO))) { ata_printf(scp, device, "failed to allocate driver storage\n"); return; } adp->controller = scp; adp->unit = device; #ifdef ATA_STATIC_ID adp->lun = (device_get_unit(scp->dev) << 1) + ATA_DEV(device); #else adp->lun = ata_get_lun(&adp_lun_map); #endif - sprintf(name, "ad%d", adp->lun); - ata_set_name(scp, device, name); + ata_set_name(scp, device, "ad", adp->lun); adp->heads = AD_PARAM->heads; adp->sectors = AD_PARAM->sectors; adp->total_secs = AD_PARAM->cylinders * adp->heads * adp->sectors; if (AD_PARAM->cylinders == 16383 && adp->total_secs < AD_PARAM->lbasize) adp->total_secs = AD_PARAM->lbasize; if (ad_version(AD_PARAM->versmajor) && AD_PARAM->atavalid & ATA_FLAG_54_58 && AD_PARAM->lbasize) adp->flags |= AD_F_LBA_ENABLED; /* use multiple sectors/interrupt if device supports it */ adp->transfersize = DEV_BSIZE; if (ad_version(AD_PARAM->versmajor)) { secsperint = max(1, min(AD_PARAM->nsecperint, 16)); if (!ata_command(adp->controller, adp->unit, ATA_C_SET_MULTI, 0, 0, 0, secsperint, 0, ATA_WAIT_INTR) && !ata_wait(adp->controller, adp->unit, 0)) adp->transfersize *= secsperint; } /* enable read cacheing if not default on device */ if (ata_command(adp->controller, adp->unit, ATA_C_SETFEATURES, 0, 0, 0, 0, ATA_C_F_ENAB_RCACHE, ATA_WAIT_INTR)) ata_printf(scp, device, "enabling readahead cache failed\n"); /* enable write cacheing if allowed and not default on device */ if (ata_wc || ata_tags) { if (ata_command(adp->controller, adp->unit, ATA_C_SETFEATURES, 0, 0, 0, 0, ATA_C_F_ENAB_WCACHE, ATA_WAIT_INTR)) ata_printf(scp, device, "enabling write cache failed\n"); } else { if (ata_command(adp->controller, adp->unit, ATA_C_SETFEATURES, 0, 0, 0, 0, ATA_C_F_DIS_WCACHE, ATA_WAIT_INTR)) ata_printf(scp, device, "disabling write cache failed\n"); } /* use DMA if allowed and if drive/controller supports it */ if (ata_dma) ata_dmainit(adp->controller, adp->unit, ata_pmode(AD_PARAM), ata_wmode(AD_PARAM), ata_umode(AD_PARAM)); else ata_dmainit(adp->controller, adp->unit, ata_pmode(AD_PARAM), -1, -1); /* use tagged queueing if allowed and supported */ if (ata_tags && ad_tagsupported(adp)) { adp->num_tags = AD_PARAM->queuelen; adp->flags |= AD_F_TAG_ENABLED; adp->controller->flags |= ATA_QUEUED; if (ata_command(adp->controller, adp->unit, ATA_C_SETFEATURES, 0, 0, 0, 0, ATA_C_F_DIS_RELIRQ, ATA_WAIT_INTR)) ata_printf(scp, device, "disabling release interrupt failed\n"); if (ata_command(adp->controller, adp->unit, ATA_C_SETFEATURES, 0, 0, 0, 0, ATA_C_F_DIS_SRVIRQ, ATA_WAIT_INTR)) ata_printf(scp, device, "disabling service interrupt failed\n"); } devstat_add_entry(&adp->stats, "ad", adp->lun, DEV_BSIZE, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE, DEVSTAT_PRIORITY_DISK); dev = disk_create(adp->lun, &adp->disk, 0, &ad_cdevsw, &addisk_cdevsw); dev->si_drv1 = adp; dev->si_iosize_max = 256 * DEV_BSIZE; adp->dev = dev; bioq_init(&adp->queue); /* if this disk belongs to an ATA RAID dont print the probe */ if (!ar_probe(adp)) adp->flags |= AD_F_RAID_SUBDISK; else ad_print(adp, ""); /* construct the disklabel */ bzero(&adp->disk.d_label, sizeof(struct disklabel)); adp->disk.d_label.d_secsize = DEV_BSIZE; adp->disk.d_label.d_nsectors = adp->sectors; adp->disk.d_label.d_ntracks = adp->heads; adp->disk.d_label.d_ncylinders = adp->total_secs/(adp->heads*adp->sectors); adp->disk.d_label.d_secpercyl = adp->sectors * adp->heads; adp->disk.d_label.d_secperunit = adp->total_secs; /* store our softc signalling we are ready to go */ scp->dev_softc[ATA_DEV(device)] = adp; } void ad_detach(struct ad_softc *adp, int flush) { struct ad_request *request; struct bio *bp; adp->flags |= AD_F_DETACHING; if (adp->flags & AD_F_RAID_SUBDISK) printf("WARNING! detaching RAID subdisk, danger ahead\n"); ata_printf(adp->controller, adp->unit, "removed from configuration\n"); ad_invalidatequeue(adp, NULL); TAILQ_FOREACH(request, &adp->controller->ata_queue, chain) { if (request->device != adp) continue; TAILQ_REMOVE(&adp->controller->ata_queue, request, chain); biofinish(request->bp, NULL, ENXIO); ad_free(request); } while ((bp = bioq_first(&adp->queue))) { biofinish(bp, NULL, ENXIO); } disk_invalidate(&adp->disk); disk_destroy(adp->dev); devstat_remove_entry(&adp->stats); if (flush) { if (ata_command(adp->controller, adp->unit, ATA_C_FLUSHCACHE, 0, 0, 0, 0, 0, ATA_WAIT_READY)) ata_printf(adp->controller, adp->unit, "flushing cache on detach failed\n"); } ata_free_lun(&adp_lun_map, adp->lun); adp->controller->dev_softc[ATA_DEV(adp->unit)] = NULL; free(adp, M_AD); } static int adopen(dev_t dev, int flags, int fmt, struct thread *td) { struct ad_softc *adp = dev->si_drv1; if (adp->flags & AD_F_RAID_SUBDISK) return EBUSY; return 0; } static int adclose(dev_t dev, int flags, int fmt, struct thread *td) { struct ad_softc *adp = dev->si_drv1; if (ata_command(adp->controller, adp->unit, ATA_C_FLUSHCACHE, 0, 0, 0, 0, 0, ATA_WAIT_READY)) ata_printf(adp->controller, adp->unit, "flushing cache on close failed\n"); return 0; } static void adstrategy(struct bio *bp) { struct ad_softc *adp = bp->bio_dev->si_drv1; int s; if (adp->flags & AD_F_DETACHING) { biofinish(bp, NULL, ENXIO); return; } s = splbio(); bioqdisksort(&adp->queue, bp); ata_start(adp->controller); splx(s); } int addump(dev_t dev) { struct ad_softc *adp = dev->si_drv1; struct ad_request request; u_int count, blkno, secsize; vm_offset_t addr = 0; long blkcnt; int dumppages = MAXDUMPPGS; int error; int i; if ((error = disk_dumpcheck(dev, &count, &blkno, &secsize))) return error; if (!adp) return ENXIO; /* force PIO mode for dumps */ adp->controller->mode[ATA_DEV(adp->unit)] = ATA_PIO; ata_reinit(adp->controller); blkcnt = howmany(PAGE_SIZE, secsize); while (count > 0) { caddr_t va = NULL; DELAY(1000); if ((count / blkcnt) < dumppages) dumppages = count / blkcnt; for (i = 0; i < dumppages; ++i) { vm_offset_t a = addr + (i * PAGE_SIZE); if (is_physical_memory(a)) va = pmap_kenter_temporary(trunc_page(a), i); else va = pmap_kenter_temporary(trunc_page(0), i); } bzero(&request, sizeof(struct ad_request)); request.device = adp; request.blockaddr = blkno; request.bytecount = PAGE_SIZE * dumppages; request.data = va; while (request.bytecount > 0) { ad_transfer(&request); if (request.flags & ADR_F_ERROR) return EIO; request.donecount += request.currentsize; request.bytecount -= request.currentsize; DELAY(20); } if (dumpstatus(addr, (long)(count * DEV_BSIZE)) < 0) return EINTR; blkno += blkcnt * dumppages; count -= blkcnt * dumppages; addr += PAGE_SIZE * dumppages; } if (ata_wait(adp->controller, adp->unit, ATA_S_READY | ATA_S_DSC) < 0) ata_printf(adp->controller, adp->unit, "timeout waiting for final ready\n"); return 0; } void ad_start(struct ad_softc *adp) { struct bio *bp = bioq_first(&adp->queue); struct ad_request *request; int tag = 0; if (!bp) return; #ifdef ATA_FLUSHCACHE_ON /* * if BIO_ORDERED is set cache should be flushed, if there are * any outstanding requests, hold off and wait for them to finish */ if (adp->flags & AD_F_TAG_ENABLED && bp->bio_flags & BIO_ORDERED && adp->outstanding > 0) return; #endif /* if tagged queueing enabled get next free tag */ if (adp->flags & AD_F_TAG_ENABLED) { while (tag <= adp->num_tags && adp->tags[tag]) tag++; if (tag > adp->num_tags ) return; } if (!(request = malloc(sizeof(struct ad_request), M_AD, M_NOWAIT|M_ZERO))) { ata_printf(adp->controller, adp->unit, "out of memory in start\n"); return; } /* setup request */ request->device = adp; request->bp = bp; request->blockaddr = bp->bio_pblkno; request->bytecount = bp->bio_bcount; request->data = bp->bio_data; request->tag = tag; if (bp->bio_cmd == BIO_READ) request->flags |= ADR_F_READ; if (adp->controller->mode[ATA_DEV(adp->unit)] >= ATA_DMA) { if (!(request->dmatab = ata_dmaalloc(adp->controller, adp->unit))) adp->controller->mode[ATA_DEV(adp->unit)] = ATA_PIO; } /* insert in tag array */ adp->tags[tag] = request; /* remove from drive queue */ bioq_remove(&adp->queue, bp); /* link onto controller queue */ TAILQ_INSERT_TAIL(&adp->controller->ata_queue, request, chain); } int ad_transfer(struct ad_request *request) { struct ad_softc *adp; u_int32_t blkno, secsprcyl; u_int32_t cylinder, head, sector, count, cmd; /* get request params */ adp = request->device; /* calculate transfer details */ blkno = request->blockaddr + (request->donecount / DEV_BSIZE); if (request->donecount == 0) { /* start timeout for this transfer */ if (dumping) request->timeout_handle.callout = NULL; else request->timeout_handle = timeout((timeout_t*)ad_timeout, request, 10 * hz); /* setup transfer parameters */ count = howmany(request->bytecount, DEV_BSIZE); if (count > 256) { count = 256; ata_printf(adp->controller, adp->unit, "count %d size transfers not supported\n", count); } if (adp->flags & AD_F_LBA_ENABLED) { sector = (blkno >> 0) & 0xff; cylinder = (blkno >> 8) & 0xffff; head = ((blkno >> 24) & 0xf) | ATA_D_LBA; } else { secsprcyl = adp->sectors * adp->heads; cylinder = blkno / secsprcyl; head = (blkno % secsprcyl) / adp->sectors; sector = (blkno % adp->sectors) + 1; } /* setup first transfer length */ request->currentsize = min(request->bytecount, adp->transfersize); devstat_start_transaction(&adp->stats); /* does this drive & transfer work with DMA ? */ request->flags &= ~ADR_F_DMA_USED; if (adp->controller->mode[ATA_DEV(adp->unit)] >= ATA_DMA && !ata_dmasetup(adp->controller, adp->unit, request->dmatab, request->data, request->bytecount)) { request->flags |= ADR_F_DMA_USED; request->currentsize = request->bytecount; /* do we have tags enabled ? */ if (adp->flags & AD_F_TAG_ENABLED) { cmd = (request->flags & ADR_F_READ) ? ATA_C_READ_DMA_QUEUED : ATA_C_WRITE_DMA_QUEUED; if (ata_command(adp->controller, adp->unit, cmd, cylinder, head, sector, request->tag << 3, count, ATA_IMMEDIATE)) { ata_printf(adp->controller, adp->unit, "error executing command"); goto transfer_failed; } if (ata_wait(adp->controller, adp->unit, ATA_S_READY)) { ata_printf(adp->controller, adp->unit, "timeout waiting for READY\n"); goto transfer_failed; } adp->outstanding++; /* if ATA bus RELEASE check for SERVICE */ if (adp->flags & AD_F_TAG_ENABLED && ATA_INB(adp->controller->r_io, ATA_IREASON) & ATA_I_RELEASE) { return ad_service(adp, 1); } } else { cmd = (request->flags & ADR_F_READ) ? ATA_C_READ_DMA : ATA_C_WRITE_DMA; if (ata_command(adp->controller, adp->unit, cmd, cylinder, head, sector, count, 0, ATA_IMMEDIATE)) { ata_printf(adp->controller, adp->unit, "error executing command"); goto transfer_failed; } #if 0 /* * wait for data transfer phase * * well this should be here acording to specs, but * promise controllers doesn't like it, they lockup! * thats probably why tags doesn't work on the promise * as this is needed there... */ if (ata_wait(adp->controller, adp->unit, ATA_S_READY | ATA_S_DRQ)) { ata_printf(adp->controller, adp->unit, "timeout waiting for data phase\n"); goto transfer_failed; } #endif } /* start transfer, return and wait for interrupt */ ata_dmastart(adp->controller, adp->unit, request->dmatab, request->flags & ADR_F_READ); return ATA_OP_CONTINUES; } /* does this drive support multi sector transfers ? */ if (request->currentsize > DEV_BSIZE) cmd = request->flags&ADR_F_READ ? ATA_C_READ_MUL : ATA_C_WRITE_MUL; /* just plain old single sector transfer */ else cmd = request->flags&ADR_F_READ ? ATA_C_READ : ATA_C_WRITE; if (ata_command(adp->controller, adp->unit, cmd, cylinder, head, sector, count, 0, ATA_IMMEDIATE)) { ata_printf(adp->controller, adp->unit, "error executing command"); goto transfer_failed; } } /* calculate this transfer length */ request->currentsize = min(request->bytecount, adp->transfersize); /* if this is a PIO read operation, return and wait for interrupt */ if (request->flags & ADR_F_READ) return ATA_OP_CONTINUES; /* ready to write PIO data ? */ if (ata_wait(adp->controller, adp->unit, (ATA_S_READY | ATA_S_DSC | ATA_S_DRQ)) < 0) { ata_printf(adp->controller, adp->unit, "timeout waiting for DRQ"); goto transfer_failed; } /* output the data */ if (adp->controller->flags & ATA_USE_16BIT) ATA_OUTSW(adp->controller->r_io, ATA_DATA, (void *)((uintptr_t)request->data + request->donecount), request->currentsize / sizeof(int16_t)); else ATA_OUTSL(adp->controller->r_io, ATA_DATA, (void *)((uintptr_t)request->data + request->donecount), request->currentsize / sizeof(int32_t)); return ATA_OP_CONTINUES; transfer_failed: untimeout((timeout_t *)ad_timeout, request, request->timeout_handle); ad_invalidatequeue(adp, request); printf(" - resetting\n"); /* if retries still permit, reinject this request */ if (request->retries++ < AD_MAX_RETRIES) TAILQ_INSERT_HEAD(&adp->controller->ata_queue, request, chain); else { /* retries all used up, return error */ request->bp->bio_error = EIO; request->bp->bio_flags |= BIO_ERROR; request->bp->bio_resid = request->bytecount; biofinish(request->bp, &adp->stats, 0); ad_free(request); } ata_reinit(adp->controller); return ATA_OP_CONTINUES; } int ad_interrupt(struct ad_request *request) { struct ad_softc *adp = request->device; int dma_stat = 0; #ifdef ATA_FLUSHCACHE_ON if (request->flags & ADR_F_FLUSHCACHE) goto finish; #endif /* finish DMA transfer */ if (request->flags & ADR_F_DMA_USED) dma_stat = ata_dmadone(adp->controller); /* do we have a corrected soft error ? */ if (adp->controller->status & ATA_S_CORR) diskerr(request->bp, "soft error (ECC corrected)", request->blockaddr + (request->donecount / DEV_BSIZE), &adp->disk.d_label); /* did any real errors happen ? */ if ((adp->controller->status & ATA_S_ERROR) || (request->flags & ADR_F_DMA_USED && dma_stat & ATA_BMSTAT_ERROR)) { adp->controller->error = ATA_INB(adp->controller->r_io, ATA_ERROR); diskerr(request->bp, (adp->controller->error & ATA_E_ICRC) ? "UDMA ICRC error" : "hard error", request->blockaddr + (request->donecount / DEV_BSIZE), &adp->disk.d_label); /* if this is a UDMA CRC error, reinject request */ if (request->flags & ADR_F_DMA_USED && adp->controller->error & ATA_E_ICRC) { untimeout((timeout_t *)ad_timeout, request,request->timeout_handle); ad_invalidatequeue(adp, request); if (request->retries++ < AD_MAX_RETRIES) printf(" retrying\n"); else { ata_dmainit(adp->controller, adp->unit, ata_pmode(AD_PARAM), -1, -1); printf(" falling back to PIO mode\n"); } TAILQ_INSERT_HEAD(&adp->controller->ata_queue, request, chain); return ATA_OP_FINISHED; } /* if using DMA, try once again in PIO mode */ if (request->flags & ADR_F_DMA_USED) { untimeout((timeout_t *)ad_timeout, request,request->timeout_handle); ad_invalidatequeue(adp, request); ata_dmainit(adp->controller, adp->unit, ata_pmode(AD_PARAM), -1, -1); request->flags |= ADR_F_FORCE_PIO; TAILQ_INSERT_HEAD(&adp->controller->ata_queue, request, chain); return ATA_OP_FINISHED; } request->flags |= ADR_F_ERROR; printf(" status=%02x error=%02x\n", adp->controller->status, adp->controller->error); } /* if we arrived here with forced PIO mode, DMA doesn't work right */ if (request->flags & ADR_F_FORCE_PIO) ata_printf(adp->controller, adp->unit, "DMA problem fallback to PIO mode\n"); /* if this was a PIO read operation, get the data */ if (!(request->flags & ADR_F_DMA_USED) && (request->flags & (ADR_F_READ | ADR_F_ERROR)) == ADR_F_READ) { /* ready to receive data? */ if ((adp->controller->status & (ATA_S_READY | ATA_S_DSC | ATA_S_DRQ)) != (ATA_S_READY | ATA_S_DSC | ATA_S_DRQ)) ata_printf(adp->controller, adp->unit, "read interrupt arrived early"); if (ata_wait(adp->controller, adp->unit, (ATA_S_READY | ATA_S_DSC | ATA_S_DRQ)) != 0) { ata_printf(adp->controller, adp->unit, "read error detected (too) late"); request->flags |= ADR_F_ERROR; } else { /* data ready, read in */ if (adp->controller->flags & ATA_USE_16BIT) ATA_INSW(adp->controller->r_io, ATA_DATA, (void *)((uintptr_t)request->data + request->donecount), request->currentsize / sizeof(int16_t)); else ATA_INSL(adp->controller->r_io, ATA_DATA, (void *)((uintptr_t)request->data + request->donecount), request->currentsize / sizeof(int32_t)); } } /* finish up transfer */ if (request->flags & ADR_F_ERROR) { request->bp->bio_error = EIO; request->bp->bio_flags |= BIO_ERROR; } else { request->bytecount -= request->currentsize; request->donecount += request->currentsize; if (request->bytecount > 0) { ad_transfer(request); return ATA_OP_CONTINUES; } } /* disarm timeout for this transfer */ untimeout((timeout_t *)ad_timeout, request, request->timeout_handle); request->bp->bio_resid = request->bytecount; #ifdef ATA_FLUSHCACHE_ON if (request->bp->bio_flags & BIO_ORDERED) { request->flags |= ADR_F_FLUSHCACHE; if (ata_command(adp->controller, adp->unit, ATA_C_FLUSHCACHE, 0, 0, 0, 0, 0, ATA_IMMEDIATE)) ata_printf(adp->controller, adp->unit, "flushing cache failed\n"); else return ATA_OP_CONTINUES; } finish: #endif biofinish(request->bp, &adp->stats, 0); ad_free(request); adp->outstanding--; /* check for SERVICE (tagged operations only) */ return ad_service(adp, 1); } int ad_service(struct ad_softc *adp, int change) { /* do we have to check the other device on this channel ? */ if (adp->controller->flags & ATA_QUEUED && change) { int device = adp->unit; if (adp->unit == ATA_MASTER) { if (adp->controller->devices & ATA_ATA_SLAVE && ((struct ad_softc *) (adp->controller->dev_softc[ATA_DEV(ATA_SLAVE)]))->flags & AD_F_TAG_ENABLED) device = ATA_SLAVE; } else { if (adp->controller->devices & ATA_ATA_MASTER && ((struct ad_softc *) (adp->controller->dev_softc[ATA_DEV(ATA_MASTER)]))->flags & AD_F_TAG_ENABLED) device = ATA_MASTER; } if (device != adp->unit && ((struct ad_softc *) (adp->controller->dev_softc[ATA_DEV(device)]))->outstanding > 0) { ATA_OUTB(adp->controller->r_io, ATA_DRIVE, ATA_D_IBM | device); adp = adp->controller->dev_softc[ATA_DEV(device)]; DELAY(1); } } adp->controller->status = ATA_INB(adp->controller->r_altio, ATA_ALTSTAT); /* do we have a SERVICE request from the drive ? */ if (adp->flags & AD_F_TAG_ENABLED && adp->outstanding > 0 && adp->controller->status & ATA_S_SERVICE) { struct ad_request *request; int tag; /* check for error */ if (adp->controller->status & ATA_S_ERROR) { ata_printf(adp->controller, adp->unit, "Oops! controller says s=0x%02x e=0x%02x\n", adp->controller->status, adp->controller->error); ad_invalidatequeue(adp, NULL); return ATA_OP_FINISHED; } /* issue SERVICE cmd */ if (ata_command(adp->controller, adp->unit, ATA_C_SERVICE, 0, 0, 0, 0, 0, ATA_IMMEDIATE)) { ata_printf(adp->controller, adp->unit, "problem executing SERVICE cmd\n"); ad_invalidatequeue(adp, NULL); return ATA_OP_FINISHED; } /* setup the transfer environment when ready */ if (ata_wait(adp->controller, adp->unit, ATA_S_READY)) { ata_printf(adp->controller, adp->unit, "problem issueing SERVICE tag=%d s=0x%02x e=0x%02x\n", ATA_INB(adp->controller->r_io, ATA_COUNT) >> 3, adp->controller->status, adp->controller->error); ad_invalidatequeue(adp, NULL); return ATA_OP_FINISHED; } tag = ATA_INB(adp->controller->r_io, ATA_COUNT) >> 3; if (!(request = adp->tags[tag])) { ata_printf(adp->controller, adp->unit, "no request for tag=%d\n", tag); ad_invalidatequeue(adp, NULL); return ATA_OP_FINISHED; } adp->controller->active = ATA_ACTIVE_ATA; adp->controller->running = request; request->serv++; /* start DMA transfer when ready */ if (ata_wait(adp->controller, adp->unit, ATA_S_READY | ATA_S_DRQ)) { ata_printf(adp->controller, adp->unit, "timeout waiting for data phase s=%02x e=%02x\n", adp->controller->status, adp->controller->error); ad_invalidatequeue(adp, NULL); return ATA_OP_FINISHED; } ata_dmastart(adp->controller, adp->unit, request->dmatab, request->flags & ADR_F_READ); return ATA_OP_CONTINUES; } return ATA_OP_FINISHED; } static void ad_free(struct ad_request *request) { int s = splbio(); if (request->dmatab) free(request->dmatab, M_DEVBUF); request->device->tags[request->tag] = NULL; free(request, M_AD); splx(s); } static void ad_invalidatequeue(struct ad_softc *adp, struct ad_request *request) { /* if tags used invalidate all other tagged transfers */ if (adp->flags & AD_F_TAG_ENABLED) { struct ad_request *tmpreq; int tag; ata_printf(adp->controller, adp->unit,"invalidating queued requests\n"); for (tag = 0; tag <= adp->num_tags; tag++) { tmpreq = adp->tags[tag]; adp->tags[tag] = NULL; if (tmpreq == request || tmpreq == NULL) continue; untimeout((timeout_t *)ad_timeout, tmpreq, tmpreq->timeout_handle); TAILQ_INSERT_HEAD(&adp->controller->ata_queue, tmpreq, chain); } if (ata_command(adp->controller, adp->unit, ATA_C_NOP, 0, 0, 0, 0, ATA_C_F_FLUSHQUEUE, ATA_WAIT_READY)) ata_printf(adp->controller, adp->unit, "flush queue failed\n"); adp->outstanding = 0; } } static int ad_tagsupported(struct ad_softc *adp) { const char *drives[] = {"IBM-DPTA", "IBM-DTLA", NULL}; int i = 0; switch (adp->controller->chiptype) { case 0x4d33105a: /* Promises before TX2 doesn't work with tagged queuing */ case 0x4d38105a: case 0x0d30105a: case 0x4d30105a: return 0; } /* check that drive does DMA, has tags enabled, and is one we know works */ if (adp->controller->mode[ATA_DEV(adp->unit)] >= ATA_DMA && AD_PARAM->supqueued && AD_PARAM->enabqueued) { while (drives[i] != NULL) { if (!strncmp(AD_PARAM->model, drives[i], strlen(drives[i]))) return 1; i++; } /* * check IBM's new obscure way of naming drives * we want "IC" (IBM CORP) and "AT" or "AV" (ATA interface) * but doesn't care about the other info (size, capacity etc) */ if (!strncmp(AD_PARAM->model, "IC", 2) && (!strncmp(AD_PARAM->model + 8, "AT", 2) || !strncmp(AD_PARAM->model + 8, "AV", 2))) return 1; } return 0; } static void ad_timeout(struct ad_request *request) { struct ad_softc *adp = request->device; int s = splbio(); adp->controller->running = NULL; ata_printf(adp->controller, adp->unit, "%s command timeout tag=%d serv=%d - resetting\n", (request->flags & ADR_F_READ) ? "READ" : "WRITE", request->tag, request->serv); if (request->flags & ADR_F_DMA_USED) { ata_dmadone(adp->controller); ad_invalidatequeue(adp, request); if (request->retries == AD_MAX_RETRIES) { ata_dmainit(adp->controller, adp->unit, ata_pmode(AD_PARAM), -1, -1); ata_printf(adp->controller, adp->unit, "trying fallback to PIO mode\n"); request->retries = 0; } } /* if retries still permit, reinject this request */ if (request->retries++ < AD_MAX_RETRIES) { TAILQ_INSERT_HEAD(&adp->controller->ata_queue, request, chain); } else { /* retries all used up, return error */ request->bp->bio_error = EIO; request->bp->bio_flags |= BIO_ERROR; biofinish(request->bp, &adp->stats, 0); ad_free(request); } ata_reinit(adp->controller); splx(s); } void ad_reinit(struct ad_softc *adp) { /* reinit disk parameters */ ad_invalidatequeue(adp, NULL); ata_command(adp->controller, adp->unit, ATA_C_SET_MULTI, 0, 0, 0, adp->transfersize / DEV_BSIZE, 0, ATA_WAIT_INTR); if (adp->controller->mode[ATA_DEV(adp->unit)] >= ATA_DMA) ata_dmainit(adp->controller, adp->unit, ata_pmode(AD_PARAM), ata_wmode(AD_PARAM), ata_umode(AD_PARAM)); else ata_dmainit(adp->controller, adp->unit, ata_pmode(AD_PARAM), -1, -1); } void ad_print(struct ad_softc *adp, char *prepend) { if (prepend) printf("%s", prepend); if (bootverbose) { ata_printf(adp->controller, adp->unit, "<%.40s/%.8s> ATA-%d disk at ata%d-%s\n", AD_PARAM->model, AD_PARAM->revision, ad_version(AD_PARAM->versmajor), device_get_unit(adp->controller->dev), (adp->unit == ATA_MASTER) ? "master" : "slave"); ata_printf(adp->controller, adp->unit, "%luMB (%u sectors), %u C, %u H, %u S, %u B\n", adp->total_secs / ((1024L*1024L)/DEV_BSIZE), adp->total_secs, adp->total_secs / (adp->heads * adp->sectors), adp->heads, adp->sectors, DEV_BSIZE); ata_printf(adp->controller, adp->unit, "%d secs/int, %d depth queue, %s%s\n", adp->transfersize / DEV_BSIZE, adp->num_tags + 1, (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "", ata_mode2str(adp->controller->mode[ATA_DEV(adp->unit)])); ata_printf(adp->controller, adp->unit, "piomode=%d dmamode=%d udmamode=%d cblid=%d\n", ata_pmode(AD_PARAM), ata_wmode(AD_PARAM), ata_umode(AD_PARAM), AD_PARAM->cblid); } else ata_printf(adp->controller, adp->unit, "%luMB <%.40s> [%d/%d/%d] at ata%d-%s %s%s\n", adp->total_secs / ((1024L * 1024L) / DEV_BSIZE), AD_PARAM->model, adp->total_secs / (adp->heads*adp->sectors), adp->heads, adp->sectors, device_get_unit(adp->controller->dev), (adp->unit == ATA_MASTER) ? "master" : "slave", (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "", ata_mode2str(adp->controller->mode[ATA_DEV(adp->unit)])); } static int ad_version(u_int16_t version) { int bit; if (version == 0xffff) return 0; for (bit = 15; bit >= 0; bit--) if (version & (1< * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* structure describing an ATA disk request */ struct ad_request { struct ad_softc *device; /* ptr to parent device */ u_int32_t blockaddr; /* block number */ u_int32_t bytecount; /* bytes to transfer */ u_int32_t donecount; /* bytes transferred */ u_int32_t currentsize; /* size of current transfer */ struct callout_handle timeout_handle; /* handle for untimeout */ int retries; /* retry count */ int flags; #define ADR_F_READ 0x0001 #define ADR_F_ERROR 0x0002 #define ADR_F_DMA_USED 0x0004 #define ADR_F_QUEUED 0x0008 #define ADR_F_FORCE_PIO 0x0010 #define ADR_F_FLUSHCACHE 0x0020 caddr_t data; /* pointer to data buf */ struct bio *bp; /* associated bio ptr */ u_int8_t tag; /* tag ID of this request */ int serv; /* request had service */ struct ata_dmaentry *dmatab; /* DMA transfer table */ TAILQ_ENTRY(ad_request) chain; /* list management */ }; /* structure describing an ATA disk */ struct ad_softc { struct ata_softc *controller; /* ptr to parent ctrl */ int unit; /* ATA_MASTER or ATA_SLAVE */ int lun; /* logical unit number */ u_int32_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_DETACHING 0x0002 #define AD_F_LBA_ENABLED 0x0004 #define AD_F_32B_ENABLED 0x0008 #define AD_F_TAG_ENABLED 0x0010 #define AD_F_RAID_SUBDISK 0x0020 struct ad_request *tags[32]; /* tag array of requests */ int outstanding; /* tags not serviced yet */ struct bio_queue_head queue; /* head of request queue */ struct devstat stats; /* devstat entry */ struct disk disk; /* disklabel/slice stuff */ dev_t dev; /* device place holder */ }; void ad_attach(struct ata_softc *, int); void ad_detach(struct ad_softc *, int); void ad_start(struct ad_softc *); int ad_transfer(struct ad_request *); int ad_interrupt(struct ad_request *); int ad_service(struct ad_softc *, int); void ad_reinit(struct ad_softc *); void ad_print(struct ad_softc *, char *); Index: head/sys/dev/ata/ata-dma.c =================================================================== --- head/sys/dev/ata/ata-dma.c (revision 83727) +++ head/sys/dev/ata/ata-dma.c (revision 83728) @@ -1,1135 +1,1135 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "pci.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void cyrix_timing(struct ata_softc *, int, int); static void promise_timing(struct ata_softc *, int, int); static void hpt_timing(struct ata_softc *, int, int); /* misc defines */ #ifdef __alpha__ #undef vtophys #define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) #endif #define ATAPI_DEVICE(scp, device) \ ((device == ATA_MASTER && scp->devices & ATA_ATAPI_MASTER) || \ (device == ATA_SLAVE && scp->devices & ATA_ATAPI_SLAVE)) void * ata_dmaalloc(struct ata_softc *scp, int device) { void *dmatab; if ((dmatab = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT))) { if (((uintptr_t)dmatab >> PAGE_SHIFT) ^ (((uintptr_t)dmatab + PAGE_SIZE - 1) >> PAGE_SHIFT)) { ata_printf(scp, device, "dmatab crosses page boundary, no DMA\n"); free(dmatab, M_DEVBUF); dmatab = NULL; } } return dmatab; } void ata_dmainit(struct ata_softc *scp, int device, int apiomode, int wdmamode, int udmamode) { device_t parent = device_get_parent(scp->dev); int devno = (scp->channel << 1) + ATA_DEV(device); int error; /* set our most pessimistic default mode */ scp->mode[ATA_DEV(device)] = ATA_PIO; if (!scp->r_bmio) return; /* if simplex controller, only allow DMA on primary channel */ if (scp->channel == 1) { ATA_OUTB(scp->r_bmio, ATA_BMSTAT_PORT, ATA_INB(scp->r_bmio, ATA_BMSTAT_PORT) & (ATA_BMSTAT_DMA_MASTER | ATA_BMSTAT_DMA_SLAVE)); if (ATA_INB(scp->r_bmio, ATA_BMSTAT_PORT) & ATA_BMSTAT_DMA_SIMPLEX) { ata_printf(scp, device, "simplex device, DMA on primary only\n"); return; } } /* DMA engine address alignment is usually 1 word (2 bytes) */ scp->alignment = 0x1; if (udmamode > 2 && !ATA_PARAM(scp, device)->cblid) { ata_printf(scp, device, "DMA limited to UDMA33, non-ATA66 compliant cable\n"); udmamode = 2; } switch (scp->chiptype) { case 0x244a8086: /* Intel ICH2 mobile */ case 0x244b8086: /* Intel ICH2 */ if (udmamode >= 5) { int32_t mask48, new48; int16_t word54; word54 = pci_read_config(parent, 0x54, 2); if (word54 & (0x10 << devno)) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA5, ATA_C_F_SETXFER,ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA5 on Intel chip\n", (error) ? "failed" : "success"); if (!error) { mask48 = (1 << devno) + (3 << (16 + (devno << 2))); new48 = (1 << devno) + (1 << (16 + (devno << 2))); pci_write_config(parent, 0x48, (pci_read_config(parent, 0x48, 4) & ~mask48) | new48, 4); pci_write_config(parent, 0x54, word54 | (0x1000<mode[ATA_DEV(device)] = ATA_UDMA5; return; } } } /* make sure eventual ATA100 mode from the BIOS is disabled */ pci_write_config(parent, 0x54, pci_read_config(parent, 0x54, 2) & ~(0x1000<= 4) { int32_t mask48, new48; int16_t word54; word54 = pci_read_config(parent, 0x54, 2); if (word54 & (0x10 << devno)) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA4, ATA_C_F_SETXFER,ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA4 on Intel chip\n", (error) ? "failed" : "success"); if (!error) { mask48 = (1 << devno) + (3 << (16 + (devno << 2))); new48 = (1 << devno) + (2 << (16 + (devno << 2))); pci_write_config(parent, 0x48, (pci_read_config(parent, 0x48, 4) & ~mask48) | new48, 4); pci_write_config(parent, 0x54, word54 | (1 << devno), 2); scp->mode[ATA_DEV(device)] = ATA_UDMA4; return; } } } /* make sure eventual ATA66 mode from the BIOS is disabled */ pci_write_config(parent, 0x54, pci_read_config(parent, 0x54, 2) & ~(1 << devno), 2); /* FALLTHROUGH */ case 0x71118086: /* Intel PIIX4 */ case 0x71998086: /* Intel PIIX4e */ case 0x24218086: /* Intel ICH0 */ if (udmamode >= 2) { int32_t mask48, new48; error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on Intel chip\n", (error) ? "failed" : "success"); if (!error) { mask48 = (1 << devno) + (3 << (16 + (devno << 2))); new48 = (1 << devno) + (2 << (16 + (devno << 2))); pci_write_config(parent, 0x48, (pci_read_config(parent, 0x48, 4) & ~mask48) | new48, 4); scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } /* make sure eventual ATA33 mode from the BIOS is disabled */ pci_write_config(parent, 0x48, pci_read_config(parent, 0x48, 4) & ~(1 << devno), 4); /* FALLTHROUGH */ case 0x70108086: /* Intel PIIX3 */ if (wdmamode >= 2 && apiomode >= 4) { int32_t mask40, new40, mask44, new44; /* if SITRE not set doit for both channels */ if (!((pci_read_config(parent,0x40,4)>>(scp->channel<<8))&0x4000)) { new40 = pci_read_config(parent, 0x40, 4); new44 = pci_read_config(parent, 0x44, 4); if (!(new40 & 0x00004000)) { new44 &= ~0x0000000f; new44 |= ((new40&0x00003000)>>10)|((new40&0x00000300)>>8); } if (!(new40 & 0x40000000)) { new44 &= ~0x000000f0; new44 |= ((new40&0x30000000)>>22)|((new40&0x03000000)>>20); } new40 |= 0x40004000; pci_write_config(parent, 0x40, new40, 4); pci_write_config(parent, 0x44, new44, 4); } error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on Intel chip\n", (error) ? "failed" : "success"); if (!error) { if (device == ATA_MASTER) { mask40 = 0x0000330f; new40 = 0x00002307; mask44 = 0; new44 = 0; } else { mask40 = 0x000000f0; new40 = 0x00000070; mask44 = 0x0000000f; new44 = 0x0000000b; } if (scp->channel) { mask40 <<= 16; new40 <<= 16; mask44 <<= 4; new44 <<= 4; } pci_write_config(parent, 0x40, (pci_read_config(parent, 0x40, 4) & ~mask40)| new40, 4); pci_write_config(parent, 0x44, (pci_read_config(parent, 0x44, 4) & ~mask44)| new44, 4); scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } /* we could set PIO mode timings, but we assume the BIOS did that */ break; case 0x12308086: /* Intel PIIX */ if (wdmamode >= 2 && apiomode >= 4) { int32_t word40; word40 = pci_read_config(parent, 0x40, 4); word40 >>= scp->channel * 16; /* Check for timing config usable for DMA on controller */ if (!((word40 & 0x3300) == 0x2300 && ((word40 >> (device == ATA_MASTER ? 0 : 4)) & 1) == 1)) break; error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on Intel chip\n", (error) ? "failed" : "success"); if (!error) { scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } break; case 0x522910b9: /* AcerLabs Aladdin IV/V */ /* the older Aladdin doesn't support ATAPI DMA on both master & slave */ if (pci_get_revid(parent) < 0xC2 && scp->devices & ATA_ATAPI_MASTER && scp->devices & ATA_ATAPI_SLAVE) { ata_printf(scp, device, "Aladdin: two atapi devices on this channel, no DMA\n"); break; } if (udmamode >= 5 && pci_get_revid(parent) >= 0xC4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA5, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA5 on Acer chip\n", (error) ? "failed" : "success"); if (!error) { int32_t word54 = pci_read_config(parent, 0x54, 4); pci_write_config(parent, 0x4b, pci_read_config(parent, 0x4b, 1) | 0x01, 1); word54 &= ~(0x000f000f << (devno << 2)); word54 |= (0x000f0005 << (devno << 2)); pci_write_config(parent, 0x54, word54, 4); pci_write_config(parent, 0x53, pci_read_config(parent, 0x53, 1) | 0x03, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA5; return; } } if (udmamode >= 4 && pci_get_revid(parent) >= 0xC2) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA4, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA4 on Acer chip\n", (error) ? "failed" : "success"); if (!error) { int32_t word54 = pci_read_config(parent, 0x54, 4); pci_write_config(parent, 0x4b, pci_read_config(parent, 0x4b, 1) | 0x01, 1); word54 &= ~(0x000f000f << (devno << 2)); word54 |= (0x00080005 << (devno << 2)); pci_write_config(parent, 0x54, word54, 4); pci_write_config(parent, 0x53, pci_read_config(parent, 0x53, 1) | 0x03, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA4; return; } } if (udmamode >= 2 && pci_get_revid(parent) >= 0x20) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on Acer chip\n", (error) ? "failed" : "success"); if (!error) { int32_t word54 = pci_read_config(parent, 0x54, 4); word54 &= ~(0x000f000f << (devno << 2)); word54 |= (0x000a0005 << (devno << 2)); pci_write_config(parent, 0x54, word54, 4); pci_write_config(parent, 0x53, pci_read_config(parent, 0x53, 1) | 0x03, 1); scp->flags |= ATA_ATAPI_DMA_RO; scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } /* make sure eventual UDMA mode from the BIOS is disabled */ pci_write_config(parent, 0x56, pci_read_config(parent, 0x56, 2) & ~(0x0008 << (devno << 2)), 2); if (wdmamode >= 2 && apiomode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on Acer chip\n", (error) ? "failed" : "success"); if (!error) { pci_write_config(parent, 0x53, pci_read_config(parent, 0x53, 1) | 0x03, 1); scp->flags |= ATA_ATAPI_DMA_RO; scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } pci_write_config(parent, 0x53, (pci_read_config(parent, 0x53, 1) & ~0x01) | 0x02, 1); /* we could set PIO mode timings, but we assume the BIOS did that */ break; case 0x74111022: /* AMD 766 */ if (udmamode >= 5) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA5, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA5 on AMD chip\n", (error) ? "failed" : "success"); if (!error) { pci_write_config(parent, 0x53 - devno, 0xc6, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA5; return; } } /* FALLTHROUGH */ case 0x74091022: /* AMD 756 */ if (udmamode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA4, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA4 on AMD chip\n", (error) ? "failed" : "success"); if (!error) { pci_write_config(parent, 0x53 - devno, 0xc5, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA4; return; } } goto via_82c586; case 0x05711106: /* VIA 82C571, 82C586, 82C596, 82C686 */ if (ata_find_dev(parent, 0x06861106, 0x40) || ata_find_dev(parent, 0x30741106, 0)) { /* 82C686b */ if (udmamode >= 5) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA5, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA5 on VIA chip\n", (error) ? "failed" : "success"); if (!error) { pci_write_config(parent, 0x53 - devno, 0xf0, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA5; return; } } if (udmamode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA4, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA4 on VIA chip\n", (error) ? "failed" : "success"); if (!error) { pci_write_config(parent, 0x53 - devno, 0xf1, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA4; return; } } if (udmamode >= 2) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on VIA chip\n", (error) ? "failed" : "success"); if (!error) { pci_write_config(parent, 0x53 - devno, 0xf4, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } } else if (ata_find_dev(parent, 0x06861106, 0) || /* 82C686a */ ata_find_dev(parent, 0x05961106, 0x12)) { /* 82C596b */ if (udmamode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA4, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA4 on VIA chip\n", (error) ? "failed" : "success"); if (!error) { pci_write_config(parent, 0x53 - devno, 0xe8, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA4; return; } } if (udmamode >= 2) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on VIA chip\n", (error) ? "failed" : "success"); if (!error) { pci_write_config(parent, 0x53 - devno, 0xea, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } } else if (ata_find_dev(parent, 0x05961106, 0) || /* 82C596a */ ata_find_dev(parent, 0x05861106, 0x03)) { /* 82C586b */ via_82c586: if (udmamode >= 2) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on %s chip\n", (error) ? "failed" : "success", ((scp->chiptype == 0x74091022) || (scp->chiptype == 0x74111022)) ? "AMD" : "VIA"); if (!error) { pci_write_config(parent, 0x53 - devno, 0xc0, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } } if (wdmamode >= 2 && apiomode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on %s chip\n", (error) ? "failed" : "success", (scp->chiptype == 0x74091022) ? "AMD" : "VIA"); if (!error) { pci_write_config(parent, 0x53 - devno, 0x0b, 1); pci_write_config(parent, 0x4b - devno, 0x31, 1); scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } /* we could set PIO mode timings, but we assume the BIOS did that */ break; case 0x55131039: /* SiS 5591 */ if (udmamode >= 2 && pci_get_revid(parent) > 0xc1) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on SiS chip\n", (error) ? "failed" : "success"); if (!error) { pci_write_config(parent, 0x40 + (devno << 1), 0xa301, 2); scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } if (wdmamode >=2 && apiomode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on SiS chip\n", (error) ? "failed" : "success"); if (!error) { pci_write_config(parent, 0x40 + (devno << 1), 0x0301, 2); scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } /* we could set PIO mode timings, but we assume the BIOS did that */ break; case 0x06491095: /* CMD 649 ATA100 controller */ if (udmamode >= 5) { u_int8_t umode; error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA5, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA5 on CMD chip\n", (error) ? "failed" : "success"); if (!error) { umode = pci_read_config(parent, scp->channel ? 0x7b : 0x73, 1); umode &= ~(device == ATA_MASTER ? 0x35 : 0xca); umode |= (device == ATA_MASTER ? 0x05 : 0x0a); pci_write_config(parent, scp->channel ? 0x7b : 0x73, umode, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA5; return; } } /* FALLTHROUGH */ case 0x06481095: /* CMD 648 ATA66 controller */ if (udmamode >= 4) { u_int8_t umode; error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA4, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA4 on CMD chip\n", (error) ? "failed" : "success"); if (!error) { umode = pci_read_config(parent, scp->channel ? 0x7b : 0x73, 1); umode &= ~(device == ATA_MASTER ? 0x35 : 0xca); umode |= (device == ATA_MASTER ? 0x15 : 0x4a); pci_write_config(parent, scp->channel ? 0x7b : 0x73, umode, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA4; return; } } if (udmamode >= 2) { u_int8_t umode; error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on CMD chip\n", (error) ? "failed" : "success"); if (!error) { umode = pci_read_config(parent, scp->channel ? 0x7b : 0x73, 1); umode &= ~(device == ATA_MASTER ? 0x35 : 0xca); umode |= (device == ATA_MASTER ? 0x11 : 0x42); pci_write_config(parent, scp->channel ? 0x7b : 0x73, umode, 1); scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } /* make sure eventual UDMA mode from the BIOS is disabled */ pci_write_config(parent, scp->channel ? 0x7b : 0x73, pci_read_config(parent, scp->channel ? 0x7b : 0x73, 1)& ~(device == ATA_MASTER ? 0x35 : 0xca), 1); /* FALLTHROUGH */ case 0x06461095: /* CMD 646 ATA controller */ if (wdmamode >= 2 && apiomode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on CMD chip\n", error ? "failed" : "success"); if (!error) { int32_t offset = (devno < 3) ? (devno << 1) : 7; pci_write_config(parent, 0x54 + offset, 0x3f, 1); scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } /* we could set PIO mode timings, but we assume the BIOS did that */ break; case 0xc6931080: /* Cypress 82c693 ATA controller */ if (wdmamode >= 2 && apiomode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on Cypress chip\n", error ? "failed" : "success"); if (!error) { pci_write_config(scp->dev, scp->channel ? 0x4e:0x4c, 0x2020, 2); scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } /* we could set PIO mode timings, but we assume the BIOS did that */ break; case 0x01021078: /* Cyrix 5530 ATA33 controller */ scp->alignment = 0xf; /* DMA engine requires 16 byte alignment */ if (udmamode >= 2) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on Cyrix chip\n", (error) ? "failed" : "success"); if (!error) { cyrix_timing(scp, devno, ATA_UDMA2); scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } if (wdmamode >= 2 && apiomode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on Cyrix chip\n", (error) ? "failed" : "success"); if (!error) { cyrix_timing(scp, devno, ATA_WDMA2); scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ata_pio2mode(apiomode), ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting %s on Cyrix chip\n", (error) ? "failed" : "success", ata_mode2str(ata_pio2mode(apiomode))); cyrix_timing(scp, devno, ata_pio2mode(apiomode)); scp->mode[ATA_DEV(device)] = ata_pio2mode(apiomode); return; case 0x02111166: /* ServerWorks ROSB4 ATA33 controller */ if (udmamode >= 2) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on ServerWorks chip\n", (error) ? "failed" : "success"); if (!error) { u_int16_t reg56; pci_write_config(parent, 0x54, pci_read_config(parent, 0x54, 1) | (0x01 << devno), 1); reg56 = pci_read_config(parent, 0x56, 2); reg56 &= ~(0xf << (devno * 4)); reg56 |= (0x2 << (devno * 4)); pci_write_config(parent, 0x56, reg56, 2); scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } if (wdmamode >= 2 && apiomode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on ServerWorks chip\n", (error) ? "failed" : "success"); if (!error) { int offset = (scp->channel * 2) + (device == ATA_MASTER); int word44 = pci_read_config(parent, 0x44, 4); pci_write_config(parent, 0x54, pci_read_config(parent, 0x54, 1) & ~(0x01 << devno), 1); word44 &= ~(0xff << (offset << 8)); word44 |= (0x20 << (offset << 8)); pci_write_config(parent, 0x44, 0x20, 4); scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } /* we could set PIO mode timings, but we assume the BIOS did that */ break; case 0x4d30105a: /* Promise Ultra/FastTrak 100 controllers */ case 0x0d30105a: /* Promise OEM ATA100 controllers */ case 0x4d68105a: /* Promise TX2 ATA100 controllers */ case 0x6268105a: /* Promise TX2v2 ATA100 controllers */ if (!ATAPI_DEVICE(scp, device) && udmamode >= 5 && !(pci_read_config(parent, 0x50, 2)&(scp->channel ? 1<<11 : 1<<10))){ error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA5, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA5 on Promise chip\n", (error) ? "failed" : "success"); if (!error) { promise_timing(scp, devno, ATA_UDMA5); scp->mode[ATA_DEV(device)] = ATA_UDMA5; return; } } /* FALLTHROUGH */ case 0x4d38105a: /* Promise Ultra/FastTrak 66 controllers */ if (!ATAPI_DEVICE(scp, device) && udmamode >= 4 && !(pci_read_config(parent, 0x50, 2)&(scp->channel ? 1<<11 : 1<<10))){ error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA4, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA4 on Promise chip\n", (error) ? "failed" : "success"); if (!error) { promise_timing(scp, devno, ATA_UDMA4); scp->mode[ATA_DEV(device)] = ATA_UDMA4; return; } } /* FALLTHROUGH */ case 0x4d33105a: /* Promise Ultra/FastTrak 33 controllers */ if (!ATAPI_DEVICE(scp, device) && udmamode >= 2) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on Promise chip\n", (error) ? "failed" : "success"); if (!error) { promise_timing(scp, devno, ATA_UDMA2); scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } if (!ATAPI_DEVICE(scp, device) && wdmamode >= 2 && apiomode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on Promise chip\n", (error) ? "failed" : "success"); if (!error) { promise_timing(scp, devno, ATA_WDMA2); scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ata_pio2mode(apiomode), ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting PIO%d on Promise chip\n", (error) ? "failed" : "success", (apiomode >= 0) ? apiomode : 0); promise_timing(scp, devno, ata_pio2mode(apiomode)); scp->mode[ATA_DEV(device)] = ata_pio2mode(apiomode); return; case 0x00041103: /* HighPoint HPT366/368/370 controllers */ if (!ATAPI_DEVICE(scp, device) && udmamode >=5 && pci_get_revid(parent) >= 0x03 && !(pci_read_config(parent, 0x5a, 1) & (scp->channel ? 0x01:0x02))) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA5, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA5 on HighPoint chip\n", (error) ? "failed" : "success"); if (!error) { hpt_timing(scp, devno, ATA_UDMA5); scp->mode[ATA_DEV(device)] = ATA_UDMA5; return; } } if (!ATAPI_DEVICE(scp, device) && udmamode >=4 && !(pci_read_config(parent, 0x5a, 1) & (scp->channel ? 0x01:0x02))) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA4, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA4 on HighPoint chip\n", (error) ? "failed" : "success"); if (!error) { hpt_timing(scp, devno, ATA_UDMA4); scp->mode[ATA_DEV(device)] = ATA_UDMA4; return; } } if (!ATAPI_DEVICE(scp, device) && udmamode >= 2) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_UDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting UDMA2 on HighPoint chip\n", (error) ? "failed" : "success"); if (!error) { hpt_timing(scp, devno, ATA_UDMA2); scp->mode[ATA_DEV(device)] = ATA_UDMA2; return; } } if (!ATAPI_DEVICE(scp, device) && wdmamode >= 2 && apiomode >= 4) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on HighPoint chip\n", (error) ? "failed" : "success"); if (!error) { hpt_timing(scp, devno, ATA_WDMA2); scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ata_pio2mode(apiomode), ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting PIO%d on HighPoint chip\n", (error) ? "failed" : "success", (apiomode >= 0) ? apiomode : 0); hpt_timing(scp, devno, ata_pio2mode(apiomode)); scp->mode[ATA_DEV(device)] = ata_pio2mode(apiomode); return; default: /* unknown controller chip */ /* better not try generic DMA on ATAPI devices it almost never works */ if ((device == ATA_MASTER && scp->devices & ATA_ATAPI_MASTER) || (device == ATA_SLAVE && scp->devices & ATA_ATAPI_SLAVE)) break; /* if controller says its setup for DMA take the easy way out */ /* the downside is we dont know what DMA mode we are in */ if ((udmamode >= 0 || wdmamode > 1) && (ATA_INB(scp->r_bmio, ATA_BMSTAT_PORT) & ((device==ATA_MASTER) ? ATA_BMSTAT_DMA_MASTER : ATA_BMSTAT_DMA_SLAVE))) { scp->mode[ATA_DEV(device)] = ATA_DMA; return; } /* well, we have no support for this, but try anyways */ if ((wdmamode >= 2 && apiomode >= 4) && scp->r_bmio) { error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ATA_WDMA2, ATA_C_F_SETXFER, ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting WDMA2 on generic chip\n", (error) ? "failed" : "success"); if (!error) { scp->mode[ATA_DEV(device)] = ATA_WDMA2; return; } } } error = ata_command(scp, device, ATA_C_SETFEATURES, 0, 0, 0, ata_pio2mode(apiomode), ATA_C_F_SETXFER,ATA_WAIT_READY); if (bootverbose) ata_printf(scp, device, "%s setting PIO%d on generic chip\n", (error) ? "failed" : "success", apiomode < 0 ? 0 : apiomode); if (!error) scp->mode[ATA_DEV(device)] = ata_pio2mode(apiomode); else { if (bootverbose) ata_printf(scp, device, "using PIO mode set by BIOS\n"); scp->mode[ATA_DEV(device)] = ATA_PIO; } } int ata_dmasetup(struct ata_softc *scp, int device, struct ata_dmaentry *dmatab, caddr_t data, int32_t count) { u_int32_t dma_count, dma_base; int i = 0; if (((uintptr_t)data & scp->alignment) || (count & scp->alignment)) { ata_printf(scp, device, "non aligned DMA transfer attempted\n"); return -1; } if (!count) { ata_printf(scp, device, "zero length DMA transfer attempted\n"); return -1; } dma_base = vtophys(data); dma_count = min(count, (PAGE_SIZE - ((uintptr_t)data & PAGE_MASK))); data += dma_count; count -= dma_count; while (count) { dmatab[i].base = dma_base; dmatab[i].count = (dma_count & 0xffff); i++; if (i >= ATA_DMA_ENTRIES) { ata_printf(scp, device, "too many segments in DMA table\n"); return -1; } dma_base = vtophys(data); dma_count = min(count, PAGE_SIZE); data += min(count, PAGE_SIZE); count -= min(count, PAGE_SIZE); } dmatab[i].base = dma_base; dmatab[i].count = (dma_count & 0xffff) | ATA_DMA_EOT; return 0; } void ata_dmastart(struct ata_softc *scp, int device, struct ata_dmaentry *dmatab, int dir) { scp->flags |= ATA_DMA_ACTIVE; ATA_OUTL(scp->r_bmio, ATA_BMDTP_PORT, vtophys(dmatab)); ATA_OUTB(scp->r_bmio, ATA_BMCMD_PORT, dir ? ATA_BMCMD_WRITE_READ : 0); ATA_OUTB(scp->r_bmio, ATA_BMSTAT_PORT, (ATA_INB(scp->r_bmio, ATA_BMSTAT_PORT) | (ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR))); ATA_OUTB(scp->r_bmio, ATA_BMCMD_PORT, ATA_INB(scp->r_bmio, ATA_BMCMD_PORT) | ATA_BMCMD_START_STOP); } int ata_dmadone(struct ata_softc *scp) { int error; ATA_OUTB(scp->r_bmio, ATA_BMCMD_PORT, ATA_INB(scp->r_bmio, ATA_BMCMD_PORT) & ~ATA_BMCMD_START_STOP); scp->flags &= ~ATA_DMA_ACTIVE; error = ATA_INB(scp->r_bmio, ATA_BMSTAT_PORT); ATA_OUTB(scp->r_bmio, ATA_BMSTAT_PORT, error | ATA_BMSTAT_INTERRUPT | ATA_BMSTAT_ERROR); return error & ATA_BMSTAT_MASK; } int ata_dmastatus(struct ata_softc *scp) { return ATA_INB(scp->r_bmio, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK; } static void cyrix_timing(struct ata_softc *scp, int devno, int mode) { u_int32_t reg20 = 0x0000e132; u_int32_t reg24 = 0x00017771; switch (mode) { case ATA_PIO0: reg20 = 0x0000e132; break; case ATA_PIO1: reg20 = 0x00018121; break; case ATA_PIO2: reg20 = 0x00024020; break; case ATA_PIO3: reg20 = 0x00032010; break; case ATA_PIO4: reg20 = 0x00040010; break; case ATA_WDMA2: reg24 = 0x00002020; break; case ATA_UDMA2: reg24 = 0x00911030; break; } ATA_OUTL(scp->r_bmio, (devno << 3) + 0x20, reg20); ATA_OUTL(scp->r_bmio, (devno << 3) + 0x24, reg24); } static void promise_timing(struct ata_softc *scp, int devno, int mode) { u_int32_t timing = 0; struct promise_timing { u_int8_t pa:4; u_int8_t prefetch:1; u_int8_t iordy:1; u_int8_t errdy:1; u_int8_t syncin:1; u_int8_t pb:5; u_int8_t mb:3; u_int8_t mc:4; u_int8_t dmaw:1; u_int8_t dmar:1; u_int8_t iordyp:1; u_int8_t dmarqp:1; u_int8_t reserved:8; } *t = (struct promise_timing*)&timing; t->iordy = 1; t->iordyp = 1; if (mode >= ATA_DMA) { t->prefetch = 1; t->errdy = 1; t->syncin = 1; } switch (scp->chiptype) { case 0x4d33105a: /* Promise Ultra/Fasttrak 33 */ switch (mode) { default: case ATA_PIO0: t->pa = 9; t->pb = 19; t->mb = 7; t->mc = 15; break; case ATA_PIO1: t->pa = 5; t->pb = 12; t->mb = 7; t->mc = 15; break; case ATA_PIO2: t->pa = 3; t->pb = 8; t->mb = 7; t->mc = 15; break; case ATA_PIO3: t->pa = 2; t->pb = 6; t->mb = 7; t->mc = 15; break; case ATA_PIO4: t->pa = 1; t->pb = 4; t->mb = 7; t->mc = 15; break; case ATA_WDMA2: t->pa = 3; t->pb = 7; t->mb = 3; t->mc = 3; break; case ATA_UDMA2: t->pa = 3; t->pb = 7; t->mb = 1; t->mc = 1; break; } break; case 0x4d38105a: /* Promise Ultra/Fasttrak 66 */ case 0x4d30105a: /* Promise Ultra/Fasttrak 100 */ case 0x0d30105a: /* Promise OEM ATA 100 */ switch (mode) { default: case ATA_PIO0: t->pa = 15; t->pb = 31; t->mb = 7; t->mc = 15; break; case ATA_PIO1: t->pa = 10; t->pb = 24; t->mb = 7; t->mc = 15; break; case ATA_PIO2: t->pa = 6; t->pb = 16; t->mb = 7; t->mc = 15; break; case ATA_PIO3: t->pa = 4; t->pb = 12; t->mb = 7; t->mc = 15; break; case ATA_PIO4: t->pa = 2; t->pb = 8; t->mb = 7; t->mc = 15; break; case ATA_WDMA2: t->pa = 6; t->pb = 14; t->mb = 6; t->mc = 6; break; case ATA_UDMA2: t->pa = 6; t->pb = 14; t->mb = 2; t->mc = 2; break; case ATA_UDMA4: t->pa = 3; t->pb = 7; t->mb = 1; t->mc = 1; break; case ATA_UDMA5: t->pa = 3; t->pb = 7; t->mb = 1; t->mc = 1; break; } break; case 0x4d68105a: /* Promise TX2 ATA 100 */ case 0x6268105a: /* Promise TX2v2 ATA 100 */ return; } pci_write_config(device_get_parent(scp->dev), 0x60 + (devno<<2), timing, 4); } static void hpt_timing(struct ata_softc *scp, int devno, int mode) { device_t parent = device_get_parent(scp->dev); u_int32_t timing; if (pci_get_revid(parent) >= 0x03) { /* HPT370 */ switch (mode) { case ATA_PIO0: timing = 0x06914e57; break; case ATA_PIO1: timing = 0x06914e43; break; case ATA_PIO2: timing = 0x06514e33; break; case ATA_PIO3: timing = 0x06514e22; break; case ATA_PIO4: timing = 0x06514e21; break; case ATA_WDMA2: timing = 0x26514e21; break; case ATA_UDMA2: timing = 0x16494e31; break; case ATA_UDMA4: timing = 0x16454e31; break; case ATA_UDMA5: timing = 0x16454e31; break; default: timing = 0x06514e57; } pci_write_config(parent, 0x40 + (devno << 2) , timing, 4); pci_write_config(parent, 0x5b, 0x22, 1); } else { /* HPT36[68] */ switch (pci_read_config(parent, 0x41 + (devno << 2), 1)) { case 0x85: /* 25Mhz */ switch (mode) { case ATA_PIO0: timing = 0xc0d08585; break; case ATA_PIO1: timing = 0xc0d08572; break; case ATA_PIO2: timing = 0xc0ca8542; break; case ATA_PIO3: timing = 0xc0ca8532; break; case ATA_PIO4: timing = 0xc0ca8521; break; case ATA_WDMA2: timing = 0xa0ca8521; break; case ATA_UDMA2: timing = 0x90cf8521; break; case ATA_UDMA4: timing = 0x90c98521; break; default: timing = 0x01208585; } break; default: case 0xa7: /* 33MHz */ switch (mode) { case ATA_PIO0: timing = 0xc0d0a7aa; break; case ATA_PIO1: timing = 0xc0d0a7a3; break; case ATA_PIO2: timing = 0xc0d0a753; break; case ATA_PIO3: timing = 0xc0c8a742; break; case ATA_PIO4: timing = 0xc0c8a731; break; case ATA_WDMA2: timing = 0xa0c8a731; break; case ATA_UDMA2: timing = 0x90caa731; break; case ATA_UDMA4: timing = 0x90c9a731; break; default: timing = 0x0120a7a7; } break; case 0xd9: /* 40Mhz */ switch (mode) { case ATA_PIO0: timing = 0xc018d9d9; break; case ATA_PIO1: timing = 0xc010d9c7; break; case ATA_PIO2: timing = 0xc010d997; break; case ATA_PIO3: timing = 0xc010d974; break; case ATA_PIO4: timing = 0xc008d963; break; case ATA_WDMA2: timing = 0xa008d943; break; case ATA_UDMA2: timing = 0x900bd943; break; case ATA_UDMA4: timing = 0x900fd943; break; default: timing = 0x0120d9d9; } } pci_write_config(parent, 0x40 + (devno << 2), (timing & ~0x80000000),4); } } Index: head/sys/dev/ata/ata-isa.c =================================================================== --- head/sys/dev/ata/ata-isa.c (revision 83727) +++ head/sys/dev/ata/ata-isa.c (revision 83728) @@ -1,146 +1,146 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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 */ {0} }; static int ata_isa_probe(device_t dev) { struct ata_softc *scp = device_get_softc(dev); struct resource *io; int rid; u_long tmp; /* check isapnp ids */ if (ISA_PNP_PROBE(device_get_parent(dev), dev, ata_ids) == ENXIO) return ENXIO; /* allocate the io port range to get the start address */ rid = ATA_IOADDR_RID; io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, ATA_IOSIZE, RF_ACTIVE); if (!io) return ENOMEM; /* set the altport range */ if (bus_get_resource(dev, SYS_RES_IOPORT, ATA_ALTADDR_RID, &tmp, &tmp)) { bus_set_resource(dev, SYS_RES_IOPORT, ATA_ALTADDR_RID, rman_get_start(io) + ATA_ALTOFFSET, ATA_ALTIOSIZE); } bus_release_resource(dev, SYS_RES_IOPORT, rid, io); scp->channel = 0; scp->flags |= ATA_USE_16BIT; 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_resume, ata_resume), { 0, 0 } }; static driver_t ata_isa_driver = { "ata", ata_isa_methods, sizeof(struct ata_softc), }; DRIVER_MODULE(ata, isa, ata_isa_driver, ata_devclass, 0, 0); /* * the following is a bandaid to get ISA only setups to link, * since these are getting rare the ugliness is kept here */ #include "pci.h" #if NPCI == 0 void * ata_dmaalloc(struct ata_softc *scp, int device) { return 0; } void ata_dmainit(struct ata_softc *scp, int device, int piomode, int wdmamode, int udmamode) { } int ata_dmasetup(struct ata_softc *scp, int device, struct ata_dmaentry *dmatab, caddr_t data, int32_t count) { return -1; } void ata_dmastart(struct ata_softc *scp, int device, struct ata_dmaentry *dmatab, int dir) { } int ata_dmadone(struct ata_softc *scp) { return -1; } int ata_dmastatus(struct ata_softc *scp) { return -1; } #endif Index: head/sys/dev/ata/ata-pci.c =================================================================== --- head/sys/dev/ata/ata-pci.c (revision 83727) +++ head/sys/dev/ata/ata-pci.c (revision 83728) @@ -1,690 +1,690 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __alpha__ #include #endif #include #include #include #include /* misc defines */ #define IOMASK 0xfffffffc #define ATA_MASTERDEV(dev) ((pci_get_progif(dev) & 0x80) && \ (pci_get_progif(dev) & 0x05) != 0x05) struct ata_pci_softc { struct resource *bmio; int bmaddr; struct resource *irq; int irqcnt; }; int ata_find_dev(device_t dev, u_int32_t type, u_int32_t revid) { device_t *children, child; int nchildren, i; if (device_get_children(device_get_parent(dev), &children, &nchildren)) return 0; for (i = 0; i < nchildren; i++) { child = children[i]; /* check that it's on the same silicon and the device we want */ if (pci_get_slot(dev) == pci_get_slot(child) && pci_get_vendor(child) == (type & 0xffff) && pci_get_device(child) == ((type & 0xffff0000) >> 16) && pci_get_revid(child) >= revid) { free(children, M_TEMP); return 1; } } free(children, M_TEMP); return 0; } static const char * ata_pci_match(device_t dev) { if (pci_get_class(dev) != PCIC_STORAGE) return NULL; switch (pci_get_devid(dev)) { /* supported chipsets */ case 0x12308086: return "Intel PIIX ATA controller"; case 0x70108086: return "Intel PIIX3 ATA controller"; case 0x71118086: case 0x71998086: return "Intel PIIX4 ATA33 controller"; case 0x24218086: return "Intel ICH0 ATA33 controller"; case 0x24118086: return "Intel ICH ATA66 controller"; case 0x244a8086: case 0x244b8086: return "Intel ICH2 ATA100 controller"; case 0x522910b9: if (pci_get_revid(dev) < 0x20) return "AcerLabs Aladdin ATA controller"; else return "AcerLabs Aladdin ATA33 controller"; case 0x05711106: if (ata_find_dev(dev, 0x05861106, 0x02)) return "VIA 82C586 ATA33 controller"; if (ata_find_dev(dev, 0x05861106, 0)) return "VIA 82C586 ATA controller"; if (ata_find_dev(dev, 0x05961106, 0x12)) return "VIA 82C596 ATA66 controller"; if (ata_find_dev(dev, 0x05961106, 0)) return "VIA 82C596 ATA33 controller"; if (ata_find_dev(dev, 0x06861106, 0x40) || ata_find_dev(dev, 0x30741106, 0)) return "VIA 82C686 ATA100 controller"; if (ata_find_dev(dev, 0x06861106, 0)) return "VIA 82C686 ATA66 controller"; return "VIA Apollo ATA controller"; case 0x55131039: return "SiS 5591 ATA33 controller"; case 0x06491095: return "CMD 649 ATA100 controller"; case 0x06481095: return "CMD 648 ATA66 controller"; case 0x06461095: return "CMD 646 ATA controller"; case 0xc6931080: if (pci_get_subclass(dev) == PCIS_STORAGE_IDE) return "Cypress 82C693 ATA controller"; break; case 0x01021078: return "Cyrix 5530 ATA33 controller"; case 0x74091022: return "AMD 756 ATA66 controller"; case 0x74111022: return "AMD 766 ATA100 controller"; case 0x02111166: return "ServerWorks ROSB4 ATA33 controller"; case 0x4d33105a: return "Promise ATA33 controller"; case 0x4d38105a: return "Promise ATA66 controller"; case 0x0d30105a: case 0x4d30105a: return "Promise ATA100 controller"; case 0x4d68105a: case 0x6268105a: return "Promise TX2 ATA100 controller"; case 0x00041103: switch (pci_get_revid(dev)) { case 0x00: case 0x01: return "HighPoint HPT366 ATA66 controller"; case 0x02: return "HighPoint HPT368 ATA66 controller"; case 0x03: case 0x04: return "HighPoint HPT370 ATA100 controller"; default: return "Unknown revision HighPoint ATA controller"; } /* unsupported but known chipsets, generic DMA only */ case 0x10001042: case 0x10011042: return "RZ 100? ATA controller !WARNING! buggy chip data loss possible"; case 0x06401095: return "CMD 640 ATA controller !WARNING! buggy chip data loss possible"; /* unknown chipsets, try generic DMA if it seems possible */ default: if (pci_get_class(dev) == PCIC_STORAGE && (pci_get_subclass(dev) == PCIS_STORAGE_IDE)) return "Generic PCI ATA controller"; } return NULL; } static int ata_pci_probe(device_t dev) { const char *desc = ata_pci_match(dev); if (desc) { device_set_desc(dev, desc); return 0; } else return ENXIO; } static int ata_pci_add_child(device_t dev, int unit) { device_t child; /* check if this is located at one of the std addresses */ if (ATA_MASTERDEV(dev)) { if (!(child = device_add_child(dev, "ata", unit))) return ENOMEM; } else { if (!(child = device_add_child(dev, "ata", 2))) return ENOMEM; } return 0; } static int ata_pci_attach(device_t dev) { struct ata_pci_softc *sc = device_get_softc(dev); u_int8_t class, subclass; u_int32_t type, cmd; int rid; /* set up vendor-specific stuff */ type = pci_get_devid(dev); class = pci_get_class(dev); subclass = pci_get_subclass(dev); cmd = pci_read_config(dev, PCIR_COMMAND, 4); if (!(cmd & PCIM_CMD_PORTEN)) { device_printf(dev, "ATA channel disabled by BIOS\n"); return 0; } /* is busmastering supported ? */ if ((cmd & (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN)) == (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN)) { /* is there a valid port range to connect to ? */ rid = 0x20; sc->bmio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (!sc->bmio) device_printf(dev, "Busmastering DMA not configured\n"); } else device_printf(dev, "Busmastering DMA not supported\n"); /* do extra chipset specific setups */ switch (type) { case 0x522910b9: /* Aladdin need to activate the ATAPI FIFO */ pci_write_config(dev, 0x53, (pci_read_config(dev, 0x53, 1) & ~0x01) | 0x02, 1); break; case 0x4d38105a: /* Promise 66 & 100 (before TX2) need the clock changed */ case 0x4d30105a: case 0x0d30105a: ATA_OUTB(sc->bmio, 0x11, ATA_INB(sc->bmio, 0x11) | 0x0a); /* FALLTHROUGH */ case 0x4d33105a: /* Promise (before TX2) need burst mode turned on */ ATA_OUTB(sc->bmio, 0x1f, ATA_INB(sc->bmio, 0x1f) | 0x01); break; case 0x00041103: /* HighPoint */ switch (pci_get_revid(dev)) { case 0x00: case 0x01: /* turn off interrupt prediction */ pci_write_config(dev, 0x51, (pci_read_config(dev, 0x51, 1) & ~0x80), 1); break; case 0x02: case 0x03: case 0x04: /* turn off interrupt prediction */ pci_write_config(dev, 0x51, (pci_read_config(dev, 0x51, 1) & ~0x02), 1); pci_write_config(dev, 0x55, (pci_read_config(dev, 0x55, 1) & ~0x02), 1); /* turn on interrupts */ pci_write_config(dev, 0x5a, (pci_read_config(dev, 0x5a, 1) & ~0x10), 1); } break; case 0x05711106: case 0x74091022: case 0x74111022: /* VIA 82C586, '596, '686 & AMD 756, '766 default setup */ /* set prefetch, postwrite */ pci_write_config(dev, 0x41, pci_read_config(dev, 0x41, 1) | 0xf0, 1); /* 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); /* prepare for ATA-66 on the 82C686a and rev 0x12 and newer 82C596's */ if ((ata_find_dev(dev, 0x06861106, 0) && !ata_find_dev(dev, 0x06861106, 0x40)) || ata_find_dev(dev, 0x05961106, 0x12)) pci_write_config(dev, 0x50, 0x030b030b, 4); break; case 0x10001042: /* RZ 100? known bad, no DMA */ case 0x10011042: case 0x06401095: /* CMD 640 known bad, no DMA */ sc->bmio = NULL; device_printf(dev, "Busmastering DMA disabled\n"); } if (sc->bmio) { sc->bmaddr = rman_get_start(sc->bmio); BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT, rid, sc->bmio); sc->bmio = NULL; } /* * 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) == 0xc6931080 && pci_get_function(dev) > 1) return 0; ata_pci_add_child(dev, 0); if (ATA_MASTERDEV(dev) || pci_read_config(dev, 0x18, 4) & IOMASK) ata_pci_add_child(dev, 1); return bus_generic_attach(dev); } static int ata_pci_intr(struct ata_softc *scp) { u_int8_t dmastat; /* * since we might share the IRQ with another device, and in some * cases with our twin channel, we only want to process interrupts * that we know this channel generated. */ switch (scp->chiptype) { case 0x00041103: /* HighPoint HPT366/368/370 */ if (((dmastat = ata_dmastatus(scp)) & (ATA_BMSTAT_ACTIVE | ATA_BMSTAT_INTERRUPT)) != ATA_BMSTAT_INTERRUPT) return 1; ATA_OUTB(scp->r_bmio, ATA_BMSTAT_PORT, dmastat | ATA_BMSTAT_INTERRUPT); DELAY(1); return 0; case 0x06481095: /* CMD 648 */ case 0x06491095: /* CMD 649 */ if (!(pci_read_config(device_get_parent(scp->dev), 0x71, 1) & (scp->channel ? 0x08 : 0x04))) return 1; break; case 0x4d33105a: /* Promise Ultra/Fasttrak 33 */ case 0x4d38105a: /* Promise Ultra/Fasttrak 66 */ case 0x4d30105a: /* Promise Ultra/Fasttrak 100 */ case 0x0d30105a: /* Promise OEM ATA100 */ case 0x4d68105a: /* Promise TX2 ATA100 */ case 0x6268105a: /* Promise TX2v2 ATA100 */ if (!(ATA_INL(scp->r_bmio, (scp->channel ? 0x14 : 0x1c)) & (scp->channel ? 0x00004000 : 0x00000400))) return 1; break; } if (scp->flags & ATA_DMA_ACTIVE) { if (!((dmastat = ata_dmastatus(scp)) & ATA_BMSTAT_INTERRUPT)) return 1; ATA_OUTB(scp->r_bmio, ATA_BMSTAT_PORT, dmastat | ATA_BMSTAT_INTERRUPT); DELAY(1); } return 0; } static int ata_pci_print_child(device_t dev, device_t child) { struct ata_softc *scp = device_get_softc(child); int retval = 0; retval += bus_print_child_header(dev, child); retval += printf(": at 0x%lx", rman_get_start(scp->r_io)); if (ATA_MASTERDEV(dev)) retval += printf(" irq %d", 14 + scp->channel); retval += bus_print_child_footer(dev, child); return retval; } static 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_softc *sc = device_get_softc(dev); struct resource *res = NULL; int channel = ((struct ata_softc *)device_get_softc(child))->channel; int myrid; if (type == SYS_RES_IOPORT) { switch (*rid) { case ATA_IOADDR_RID: if (ATA_MASTERDEV(dev)) { myrid = 0; start = (channel ? ATA_SECONDARY : ATA_PRIMARY); end = start + ATA_IOSIZE - 1; count = ATA_IOSIZE; res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, SYS_RES_IOPORT, &myrid, start, end, count, flags); } else { myrid = 0x10 + 8 * channel; res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT, &myrid, start, end, count, flags); } break; case ATA_ALTADDR_RID: if (ATA_MASTERDEV(dev)) { myrid = 0; start = (channel ? ATA_SECONDARY : ATA_PRIMARY) + ATA_ALTOFFSET; end = start + ATA_ALTIOSIZE - 1; count = ATA_ALTIOSIZE; res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, SYS_RES_IOPORT, &myrid, start, end, count, flags); } else { myrid = 0x14 + 8 * channel; res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT, &myrid, start, end, count, flags); if (res) { start = rman_get_start(res) + 2; end = rman_get_start(res) + ATA_ALTIOSIZE - 1; count = ATA_ALTIOSIZE; BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT, myrid, res); res = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT, &myrid, start, end, count, flags); } } break; case ATA_BMADDR_RID: if (sc->bmaddr) { myrid = 0x20; start = (channel == 0 ? sc->bmaddr : sc->bmaddr + ATA_BMIOSIZE); end = start + ATA_BMIOSIZE - 1; count = ATA_BMIOSIZE; res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, SYS_RES_IOPORT, &myrid, start, end, count, flags); } } return res; } if (type == SYS_RES_IRQ && *rid == ATA_IRQ_RID) { if (ATA_MASTERDEV(dev)) { #ifdef __alpha__ return alpha_platform_alloc_ide_intr(channel); #else int irq = (channel == 0 ? 14 : 15); return BUS_ALLOC_RESOURCE(device_get_parent(dev), child, SYS_RES_IRQ, rid, irq, irq, 1, flags); #endif } else { /* primary and secondary channels share interrupt, keep track */ if (!sc->irq) sc->irq = BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, SYS_RES_IRQ, rid, 0, ~0, 1, flags); sc->irqcnt++; return sc->irq; } } return 0; } static int ata_pci_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct ata_pci_softc *sc = device_get_softc(dev); int channel = ((struct ata_softc *)device_get_softc(child))->channel; if (type == SYS_RES_IOPORT) { switch (rid) { case ATA_IOADDR_RID: if (ATA_MASTERDEV(dev)) return BUS_RELEASE_RESOURCE(device_get_parent(dev), child, SYS_RES_IOPORT, 0x0, r); else return BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT, 0x10+8*channel, r); break; case ATA_ALTADDR_RID: if (ATA_MASTERDEV(dev)) return BUS_RELEASE_RESOURCE(device_get_parent(dev), child, SYS_RES_IOPORT, 0x0, r); else return BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT, 0x14+8*channel, r); break; case ATA_BMADDR_RID: return BUS_RELEASE_RESOURCE(device_get_parent(dev), child, SYS_RES_IOPORT, 0x20, r); default: return ENOENT; } } if (type == SYS_RES_IRQ) { if (rid != ATA_IRQ_RID) return ENOENT; if (ATA_MASTERDEV(dev)) { #ifdef __alpha__ return alpha_platform_release_ide_intr(channel, r); #else return BUS_RELEASE_RESOURCE(device_get_parent(dev), child, SYS_RES_IRQ, rid, r); #endif } else { /* primary and secondary channels share interrupt, keep track */ if (--sc->irqcnt) return 0; sc->irq = 0; return BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, SYS_RES_IRQ, rid, r); } } return EINVAL; } static int ata_pci_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { if (ATA_MASTERDEV(dev)) { #ifdef __alpha__ return alpha_platform_setup_ide_intr(child, irq, intr, arg, cookiep); #else return BUS_SETUP_INTR(device_get_parent(dev), child, irq, flags, intr, arg, cookiep); #endif } else return BUS_SETUP_INTR(device_get_parent(dev), dev, irq, flags, intr, arg, cookiep); } static int ata_pci_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { if (ATA_MASTERDEV(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 return BUS_TEARDOWN_INTR(device_get_parent(dev), dev, irq, cookie); } static device_method_t ata_pci_methods[] = { /* device interface */ DEVMETHOD(device_probe, ata_pci_probe), DEVMETHOD(device_attach, ata_pci_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* bus methods */ DEVMETHOD(bus_print_child, ata_pci_print_child), 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 } }; static driver_t ata_pci_driver = { "atapci", ata_pci_methods, sizeof(struct ata_pci_softc), }; static devclass_t ata_pci_devclass; DRIVER_MODULE(atapci, pci, ata_pci_driver, ata_pci_devclass, 0, 0); static int ata_pcisub_probe(device_t dev) { struct ata_softc *scp = 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) scp->channel = i; } free(children, M_TEMP); scp->chiptype = pci_get_devid(device_get_parent(dev)); scp->intr_func = ata_pci_intr; return ata_probe(dev); } static device_method_t ata_pcisub_methods[] = { /* device interface */ DEVMETHOD(device_probe, ata_pcisub_probe), DEVMETHOD(device_attach, ata_attach), DEVMETHOD(device_detach, ata_detach), DEVMETHOD(device_resume, ata_resume), { 0, 0 } }; static driver_t ata_pcisub_driver = { "ata", ata_pcisub_methods, sizeof(struct ata_softc), }; DRIVER_MODULE(ata, atapci, ata_pcisub_driver, ata_devclass, 0, 0); Index: head/sys/dev/ata/ata-raid.c =================================================================== --- head/sys/dev/ata/ata-raid.c (revision 83727) +++ head/sys/dev/ata/ata-raid.c (revision 83728) @@ -1,546 +1,561 @@ /*- - * Copyright (c) 2000,2001 Søren Schmidt + * Copyright (c) 2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_global.h" #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* device structures */ static d_open_t aropen; static d_strategy_t arstrategy; static struct cdevsw ar_cdevsw = { /* open */ aropen, /* close */ nullclose, /* read */ physread, /* write */ physwrite, /* ioctl */ noioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ arstrategy, /* name */ "ar", /* maj */ 157, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, }; static struct cdevsw ardisk_cdevsw; /* prototypes */ static void ar_attach(struct ar_softc *); static void ar_done(struct bio *); static int ar_highpoint_conf(struct ad_softc *, struct ar_softc **); static int ar_promise_conf(struct ad_softc *, struct ar_softc **); static int ar_read(struct ad_softc *, u_int32_t, int, char *); /* internal vars */ static int ar_init = 0; -static struct ar_softc *ar_table[8]; +static struct ar_softc *ar_table[16]; static MALLOC_DEFINE(M_AR, "AR driver", "ATA RAID driver"); int ar_probe(struct ad_softc *adp) { if (!ar_init) { bzero(&ar_table, sizeof(ar_table)); ar_init = 1; } switch(adp->controller->chiptype) { case 0x4d33105a: case 0x4d38105a: case 0x4d30105a: case 0x0d30105a: case 0x4d68105a: case 0x6268105a: return (ar_promise_conf(adp, ar_table)); case 0x00041103: return (ar_highpoint_conf(adp, ar_table)); } return 1; } static void ar_attach(struct ar_softc *raid) { dev_t dev; int i; printf("ar%d: %luMB lun, raid->total_secs / ((1024L * 1024L) / DEV_BSIZE)); switch (raid->flags & (AR_F_RAID_0 | AR_F_RAID_1 | AR_F_SPAN)) { case AR_F_RAID_0: printf("RAID0 "); break; case AR_F_RAID_1: printf("RAID1 "); break; case AR_F_SPAN: printf("SPAN "); break; case (AR_F_RAID_0 | AR_F_RAID_1): printf("RAID0+1 "); break; default: printf("unknown array 0x%x ", raid->flags); return; } printf("array> [%d/%d/%d] subdisks:\n", raid->cylinders, raid->heads, raid->sectors); for (i = 0; i < raid->num_subdisks; i++) ad_print(raid->subdisk[i], " "); for (i = 0; i < raid->num_mirrordisks; i++) ad_print(raid->mirrordisk[i], " "); dev = disk_create(raid->lun, &raid->disk, 0, &ar_cdevsw, &ardisk_cdevsw); dev->si_drv1 = raid; dev->si_iosize_max = 256 * DEV_BSIZE; raid->dev = dev; } static int aropen(dev_t dev, int flags, int fmt, struct thread *td) { struct ar_softc *rdp = dev->si_drv1; struct disklabel *dl; dl = &rdp->disk.d_label; bzero(dl, sizeof *dl); dl->d_secsize = DEV_BSIZE; dl->d_nsectors = rdp->sectors; dl->d_ntracks = rdp->heads; dl->d_ncylinders = rdp->cylinders; dl->d_secpercyl = rdp->sectors * rdp->heads; dl->d_secperunit = rdp->total_secs; return 0; } static void arstrategy(struct bio *bp) { struct ar_softc *rdp = bp->bio_dev->si_drv1; int lba, count, chunk; caddr_t data; bp->bio_resid = bp->bio_bcount; lba = bp->bio_pblkno; data = bp->bio_data; for (count = howmany(bp->bio_bcount, DEV_BSIZE); count > 0; count -= chunk, lba += chunk, data += (chunk * DEV_BSIZE)) { struct ar_buf *buf1, *buf2; int plba; buf1 = malloc(sizeof(struct ar_buf), M_AR, M_NOWAIT | M_ZERO); if (rdp->flags & AR_F_SPAN) { plba = lba; while (plba >= (rdp->subdisk[buf1->drive]->total_secs-rdp->reserved) && buf1->drive < rdp->num_subdisks) plba-=(rdp->subdisk[buf1->drive++]->total_secs-rdp->reserved); buf1->bp.bio_pblkno = plba; chunk = min(rdp->subdisk[buf1->drive]->total_secs - rdp->reserved - plba, count); } else if (rdp->flags & AR_F_RAID_0) { plba = lba / rdp->interleave; chunk = lba % rdp->interleave; if (plba == rdp->total_secs / rdp->interleave) { int lastblksize = (rdp->total_secs-(plba*rdp->interleave))/rdp->num_subdisks; buf1->drive = chunk / lastblksize; buf1->bp.bio_pblkno = ((plba / rdp->num_subdisks) * rdp->interleave) + chunk % lastblksize; chunk = min(count, lastblksize); } else { buf1->drive = plba % rdp->num_subdisks; buf1->bp.bio_pblkno = ((plba / rdp->num_subdisks) * rdp->interleave) + chunk; chunk = min(count, rdp->interleave - chunk); } } else { buf1->bp.bio_pblkno = lba; buf1->drive = 0; chunk = count; } if (buf1->drive > 0) buf1->bp.bio_pblkno += rdp->offset; buf1->bp.bio_caller1 = (void *)rdp; buf1->bp.bio_bcount = chunk * DEV_BSIZE; buf1->bp.bio_data = data; buf1->bp.bio_cmd = bp->bio_cmd; buf1->bp.bio_flags = bp->bio_flags; buf1->bp.bio_done = ar_done; buf1->org = bp; /* simpleminded load balancing on RAID1 arrays */ if (rdp->flags & AR_F_RAID_1 && bp->bio_cmd == BIO_READ) { if (buf1->bp.bio_pblkno < (rdp->last_lba[buf1->drive][rdp->last_disk] - 100) || buf1->bp.bio_pblkno > (rdp->last_lba[buf1->drive][rdp->last_disk] + 100)) { rdp->last_disk = 1 - rdp->last_disk; rdp->last_lba[buf1->drive][rdp->last_disk] = buf1->bp.bio_pblkno; } if (rdp->last_disk) buf1->bp.bio_dev = rdp->mirrordisk[buf1->drive]->dev; else buf1->bp.bio_dev = rdp->subdisk[buf1->drive]->dev; } else buf1->bp.bio_dev = rdp->subdisk[buf1->drive]->dev; if (rdp->flags & AR_F_RAID_1 && bp->bio_cmd == BIO_WRITE) { buf2 = malloc(sizeof(struct ar_buf), M_AR, M_NOWAIT); bcopy(buf1, buf2, sizeof(struct ar_buf)); buf2->bp.bio_dev = rdp->mirrordisk[buf1->drive]->dev; buf2->mirror = buf1; buf1->mirror = buf2; buf2->bp.bio_dev->si_disk->d_devsw->d_strategy((struct bio *)buf2); } buf1->bp.bio_dev->si_disk->d_devsw->d_strategy((struct bio *)buf1); } } static void ar_done(struct bio *bp) { struct ar_softc *rdp = (struct ar_softc *)bp->bio_caller1; struct ar_buf *buf = (struct ar_buf *)bp; int s; s = splbio(); if (bp->bio_flags & BIO_ERROR) { if (bp->bio_cmd == BIO_WRITE || buf->done || !(rdp->flags&AR_F_RAID_1)){ buf->org->bio_flags |= BIO_ERROR; buf->org->bio_error = bp->bio_error; } printf("ar%d: subdisk error\n", rdp->lun); } if (rdp->flags & AR_F_RAID_1) { if (bp->bio_cmd == BIO_WRITE) { if (!buf->done) { buf->mirror->done = 1; goto done; } } else { if (!buf->done && bp->bio_flags & BIO_ERROR) { /* read error on this disk, try mirror */ buf->done = 1; buf->bp.bio_dev = rdp->mirrordisk[buf->drive]->dev; buf->bp.bio_dev->si_disk->d_devsw->d_strategy((struct bio*)buf); return; } } } buf->org->bio_resid -= bp->bio_bcount; if (buf->org->bio_resid == 0) biodone(buf->org); done: free(buf, M_AR); splx(s); } /* read the RAID info from a disk on a HighPoint controller */ static int ar_highpoint_conf(struct ad_softc *adp, struct ar_softc **raidp) { - struct highpoint_raid_conf info; + struct highpoint_raid_conf *info; struct ar_softc *raid; - int array; + int array, error = 1; - if (ar_read(adp, 0x09, DEV_BSIZE, (char *)&info)) { + if (!(info = (struct highpoint_raid_conf *) + malloc(sizeof(struct highpoint_raid_conf), M_AR, M_NOWAIT | M_ZERO))) + return error; + + if (ar_read(adp, 0x09, DEV_BSIZE, (char *)info)) { if (bootverbose) printf("HighPoint read conf failed\n"); - return 1; + goto highpoint_out; } /* check if this is a HighPoint RAID struct */ - if (info.magic != HPT_MAGIC_OK) { + if (info->magic != HPT_MAGIC_OK) { if (bootverbose) printf("HighPoint check1 failed\n"); - return 1; + goto highpoint_out; } /* now convert HighPoint config info into our generic form */ for (array = 0; array < 8; array++) { if (!raidp[array]) { raidp[array] = (struct ar_softc*)malloc(sizeof(struct ar_softc),M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { printf("ar: failed to allocate raid config storage\n"); - return 1; + goto highpoint_out; } } raid = raidp[array]; - switch (info.type) { + switch (info->type) { case HPT_T_RAID_0: /* check the order byte to determine what this really is */ - switch (info.order & (HPT_O_MIRROR | HPT_O_STRIPE)) { + switch (info->order & (HPT_O_MIRROR | HPT_O_STRIPE)) { case HPT_O_MIRROR: goto hpt_mirror; case HPT_O_STRIPE: - if (raid->magic_0 && raid->magic_0 != info.magic_0) + if (raid->magic_0 && raid->magic_0 != info->magic_0) continue; - raid->magic_0 = info.magic_0; + raid->magic_0 = info->magic_0; raid->flags |= (AR_F_RAID_0 | AR_F_RAID_1); - raid->interleave = 1 << info.raid0_shift; - raid->subdisk[info.disk_number] = adp; + raid->interleave = 1 << info->raid0_shift; + raid->subdisk[info->disk_number] = adp; raid->num_subdisks++; if ((raid->num_subdisks + raid->num_mirrordisks) == - (info.raid_disks * 2)) + (info->raid_disks * 2)) raid->flags |= AR_F_CONF_DONE; break; case (HPT_O_MIRROR | HPT_O_STRIPE): - if (raid->magic_1 && raid->magic_1 != info.magic_0) + if (raid->magic_1 && raid->magic_1 != info->magic_0) continue; - raid->magic_1 = info.magic_0; + raid->magic_1 = info->magic_0; raid->flags |= (AR_F_RAID_0 | AR_F_RAID_1); - raid->mirrordisk[info.disk_number] = adp; + raid->mirrordisk[info->disk_number] = adp; raid->num_mirrordisks++; if ((raid->num_subdisks + raid->num_mirrordisks) == - (info.raid_disks * 2)) + (info->raid_disks * 2)) raid->flags |= AR_F_CONF_DONE; break; default: - if (raid->magic_0 && raid->magic_0 != info.magic_0) + if (raid->magic_0 && raid->magic_0 != info->magic_0) continue; - raid->magic_0 = info.magic_0; + raid->magic_0 = info->magic_0; raid->flags |= AR_F_RAID_0; - raid->interleave = 1 << info.raid0_shift; - raid->subdisk[info.disk_number] = adp; + raid->interleave = 1 << info->raid0_shift; + raid->subdisk[info->disk_number] = adp; raid->num_subdisks++; - if (raid->num_subdisks == info.raid_disks) + if (raid->num_subdisks == info->raid_disks) raid->flags |= AR_F_CONF_DONE; } break; case HPT_T_RAID_1: hpt_mirror: - if (raid->magic_0 && raid->magic_0 != info.magic_0) + if (raid->magic_0 && raid->magic_0 != info->magic_0) continue; - raid->magic_0 = info.magic_0; + raid->magic_0 = info->magic_0; raid->flags |= AR_F_RAID_1; - if (info.disk_number == 0 && raid->num_subdisks == 0) { + if (info->disk_number == 0 && raid->num_subdisks == 0) { raid->subdisk[raid->num_subdisks] = adp; raid->num_subdisks = 1; } - if (info.disk_number != 0 && raid->num_mirrordisks == 0) { + if (info->disk_number != 0 && raid->num_mirrordisks == 0) { raid->mirrordisk[raid->num_mirrordisks] = adp; raid->num_mirrordisks = 1; } if ((raid->num_subdisks + - raid->num_mirrordisks) == (info.raid_disks * 2)) + raid->num_mirrordisks) == (info->raid_disks * 2)) raid->flags |= AR_F_CONF_DONE; break; case HPT_T_SPAN: - if (raid->magic_0 && raid->magic_0 != info.magic_0) + if (raid->magic_0 && raid->magic_0 != info->magic_0) continue; - raid->magic_0 = info.magic_0; + raid->magic_0 = info->magic_0; raid->flags |= AR_F_SPAN; - raid->subdisk[info.disk_number] = adp; + raid->subdisk[info->disk_number] = adp; raid->num_subdisks++; - if (raid->num_subdisks == info.raid_disks) + if (raid->num_subdisks == info->raid_disks) raid->flags |= AR_F_CONF_DONE; break; default: - printf("HighPoint unknown RAID type 0x%02x\n", info.type); + printf("HighPoint unknown RAID type 0x%02x\n", info->type); } /* do we have a complete array to attach to ? */ if (raid->flags & AR_F_CONF_DONE) { raid->lun = array; raid->heads = 255; raid->sectors = 63; - raid->cylinders = (info.total_secs - 9) / (63 * 255); - raid->total_secs = info.total_secs - (9 * raid->num_subdisks); + raid->cylinders = (info->total_secs - 9) / (63 * 255); + raid->total_secs = info->total_secs - (9 * raid->num_subdisks); raid->offset = 10; raid->reserved = 10; ar_attach(raid); } - return 0; + error = 0; } - return 1; + +highpoint_out: + free(info, M_AR); + return error; } /* read the RAID info from a disk on a Promise Fasttrak controller */ static int ar_promise_conf(struct ad_softc *adp, struct ar_softc **raidp) { - struct promise_raid_conf info; + struct promise_raid_conf *info; struct ar_softc *raid; u_int32_t lba, magic; u_int32_t cksum, *ckptr; - int count, disk_number, array; + int count, disk_number, array, error = 1; + if (!(info = (struct promise_raid_conf *) + malloc(sizeof(struct promise_raid_conf), M_AR, M_NOWAIT | M_ZERO))) + return error; + lba = ((adp->total_secs / (adp->heads * adp->sectors)) * adp->heads * adp->sectors) - adp->sectors; - if (ar_read(adp, lba, 4 * DEV_BSIZE, (char *)&info)) { + if (ar_read(adp, lba, 4 * DEV_BSIZE, (char *)info)) { if (bootverbose) printf("Promise read conf failed\n"); - return 1; + goto promise_out; } /* check if this is a Promise RAID struct */ - if (strncmp(info.promise_id, PR_MAGIC, sizeof(PR_MAGIC))) { + if (strncmp(info->promise_id, PR_MAGIC, sizeof(PR_MAGIC))) { if (bootverbose) printf("Promise check1 failed\n"); - return 1; + goto promise_out; } /* check if the checksum is OK */ - for (cksum = 0, ckptr = (int32_t *)&info, count = 0; count < 511; count++) + for (cksum = 0, ckptr = (int32_t *)info, count = 0; count < 511; count++) cksum += *ckptr++; if (cksum != *ckptr) { if (bootverbose) printf("Promise check2 failed\n"); - return 1; + goto promise_out; } /* now convert Promise config info into our generic form */ - if ((info.raid.flags != PR_F_CONFED) || - (((info.raid.status & (PR_S_DEFINED|PR_S_ONLINE)) != + if ((info->raid.flags != PR_F_CONFED) || + (((info->raid.status & (PR_S_DEFINED|PR_S_ONLINE)) != (PR_S_DEFINED|PR_S_ONLINE)))) { - return 1; + goto promise_out; } - magic = (adp->controller->chiptype >> 16) | info.raid.array_number << 16; + magic = (adp->controller->chiptype >> 16) | info->raid.array_number << 16; - array = info.raid.array_number; + array = info->raid.array_number; if (raidp[array]) { if (magic != raidp[array]->magic_0) - return 1; + goto promise_out; } else { if (!(raidp[array] = (struct ar_softc *) malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT))) { printf("ar: failed to allocate raid config storage\n"); - return 1; + goto promise_out; } else bzero(raidp[array], sizeof(struct ar_softc)); } raid = raidp[array]; raid->magic_0 = magic; - switch (info.raid.type) { + switch (info->raid.type) { case PR_T_STRIPE: raid->flags |= AR_F_RAID_0; - raid->interleave = 1 << info.raid.raid0_shift; + raid->interleave = 1 << info->raid.raid0_shift; break; case PR_T_MIRROR: raid->flags |= AR_F_RAID_1; break; case PR_T_SPAN: raid->flags |= AR_F_SPAN; break; default: - printf("Promise unknown RAID type 0x%02x\n", info.raid.type); - return 1; + printf("Promise unknown RAID type 0x%02x\n", info->raid.type); + goto promise_out; } /* find out where this disk is in the defined array */ - disk_number = info.raid.disk_number; - if (disk_number < info.raid.raid0_disks) { + disk_number = info->raid.disk_number; + if (disk_number < info->raid.raid0_disks) { raid->subdisk[disk_number] = adp; raid->num_subdisks++; if (raid->num_subdisks > 1 && !(raid->flags & AR_F_SPAN)) { raid->flags |= AR_F_RAID_0; - raid->interleave = 1 << info.raid.raid0_shift; + raid->interleave = 1 << info->raid.raid0_shift; } } else { - raid->mirrordisk[disk_number - info.raid.raid0_disks] = adp; + raid->mirrordisk[disk_number - info->raid.raid0_disks] = adp; raid->num_mirrordisks++; } /* do we have a complete array to attach to ? */ - if (raid->num_subdisks + raid->num_mirrordisks == info.raid.total_disks) { + if (raid->num_subdisks + raid->num_mirrordisks == info->raid.total_disks) { raid->flags |= AR_F_CONF_DONE; raid->lun = array; - raid->heads = info.raid.heads + 1; - raid->sectors = info.raid.sectors; - raid->cylinders = info.raid.cylinders + 1; - raid->total_secs = info.raid.total_secs; + raid->heads = info->raid.heads + 1; + raid->sectors = info->raid.sectors; + raid->cylinders = info->raid.cylinders + 1; + raid->total_secs = info->raid.total_secs; raid->offset = 0; raid->reserved = 63; ar_attach(raid); } - return 0; + error = 0; + +promise_out: + free(info, M_AR); + return error; } int ar_read(struct ad_softc *adp, u_int32_t lba, int count, char *data) { if (ata_command(adp->controller, adp->unit | ATA_D_LBA, (count > DEV_BSIZE) ? ATA_C_READ_MUL : ATA_C_READ, (lba >> 8) & 0xffff, (lba >> 24) & 0xff, lba & 0xff, count / DEV_BSIZE, 0, ATA_WAIT_INTR)) { ata_printf(adp->controller, adp->unit, "RAID read config failed\n"); return 1; } if (ata_wait(adp->controller, adp->unit, ATA_S_READY|ATA_S_DSC|ATA_S_DRQ)) { ata_printf(adp->controller, adp->unit, "RAID read config timeout\n"); return 1; } ATA_INSW(adp->controller->r_io, ATA_DATA, (int16_t *)data, count/sizeof(int16_t)); ATA_INB(adp->controller->r_io, ATA_STATUS); return 0; } Index: head/sys/dev/ata/ata-raid.h =================================================================== --- head/sys/dev/ata/ata-raid.h (revision 83727) +++ head/sys/dev/ata/ata-raid.h (revision 83728) @@ -1,174 +1,174 @@ /*- - * Copyright (c) 2000,2001 Søren Schmidt + * Copyright (c) 2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ struct ar_softc { int lun; int32_t magic_0; int32_t magic_1; int flags; #define AR_F_RAID_0 0x0001 /* STRIPE */ #define AR_F_RAID_1 0x0002 /* MIRROR */ #define AR_F_SPAN 0x0004 /* SPAN */ #define AR_F_CONF_DONE 0x0008 int num_subdisks; struct ad_softc *subdisk[8]; int num_mirrordisks; struct ad_softc *mirrordisk[8]; int interleave; int last_disk; int32_t last_lba[8][2]; u_int16_t heads; u_int16_t sectors; u_int32_t cylinders; u_int32_t total_secs; int reserved; /* sectors that are NOT to be used */ int offset; /* offset from start of disk */ struct disk disk; /* disklabel/slice stuff */ dev_t dev; /* device place holder */ }; struct ar_buf { struct bio bp; struct bio *org; int drive; struct ar_buf *mirror; int done; }; struct highpoint_raid_conf { int8_t filler1[32]; u_int32_t magic; /* 0x20 */ #define HPT_MAGIC_OK 0x5a7816f0 #define HPT_MAGIC_BAD 0x5a7816fd u_int32_t magic_0; u_int32_t magic_1; u_int32_t order; #define HPT_O_MIRROR 0x01 #define HPT_O_STRIPE 0x02 #define HPT_O_OK 0x04 u_int8_t raid_disks; u_int8_t raid0_shift; u_int8_t type; #define HPT_T_RAID_0 0x00 #define HPT_T_RAID_1 0x01 #define HPT_T_RAID_01_RAID_0 0x02 #define HPT_T_SPAN 0x03 #define HPT_T_RAID_3 0x04 #define HPT_T_RAID_5 0x05 #define HPT_T_SINGLEDISK 0x06 #define HPT_T_RAID_01_RAID_1 0x07 u_int8_t disk_number; u_int32_t total_secs; 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 HPT_R_REMOVED 0xfe #define HPT_R_BROKEN 0xff u_int8_t disk; u_int8_t status; u_int8_t sectors; u_int32_t lba; } errorlog[32]; int8_t filler2[60]; }; struct promise_raid_conf { char promise_id[24]; #define PR_MAGIC "Promise Technology, Inc." int32_t dummy_0; int32_t magic_0; int32_t dummy_1; int32_t magic_1; int16_t dummy_2; int8_t filler1[470]; struct { int32_t flags; /* 0x200 */ #define PR_F_CONFED 0x00000080 int8_t dummy_0; int8_t disk_number; int8_t channel; int8_t device; int32_t magic_0; int32_t dummy_1; int32_t dummy_2; /* 0x210 */ int32_t disk_secs; int32_t dummy_3; int16_t dummy_4; int8_t status; #define PR_S_DEFINED 0x01 #define PR_S_ONLINE 0x02 #define PR_S_OFFLINE 0x10 int8_t type; #define PR_T_STRIPE 0x00 #define PR_T_MIRROR 0x01 #define PR_T_STRIPE_MIRROR 0x04 #define PR_T_SPAN 0x08 u_int8_t total_disks; /* 0x220 */ u_int8_t raid0_shift; u_int8_t raid0_disks; u_int8_t array_number; u_int32_t total_secs; u_int16_t cylinders; u_int8_t heads; u_int8_t sectors; int32_t magic_1; int32_t dummy_5; /* 0x230 */ struct { int16_t dummy_0; int8_t channel; int8_t device; int32_t magic_0; int32_t disk_number; } disk[8]; } raid; int32_t filler2[346]; uint32_t checksum; }; int ar_probe(struct ad_softc *); Index: head/sys/dev/ata/atapi-all.c =================================================================== --- head/sys/dev/ata/atapi-all.c (revision 83727) +++ head/sys/dev/ata/atapi-all.c (revision 83728) @@ -1,756 +1,756 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_global.h" #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void atapi_read(struct atapi_request *, int); static void atapi_write(struct atapi_request *, int); static void atapi_finish(struct atapi_request *); static void atapi_timeout(struct atapi_request *); static char *atapi_type(int); static char *atapi_cmd2str(u_int8_t); static char *atapi_skey2str(u_int8_t); /* internal vars */ static MALLOC_DEFINE(M_ATAPI, "ATAPI generic", "ATAPI driver generic layer"); static int atapi_dma = 0; TUNABLE_INT("hw.ata.atapi_dma", &atapi_dma); /* systcl vars */ SYSCTL_DECL(_hw_ata); SYSCTL_INT(_hw_ata, OID_AUTO, atapi_dma, CTLFLAG_RD, &atapi_dma, 0, "ATAPI device DMA mode control"); /* defines */ #define ATAPI_MAX_RETRIES 3 #define ATP_PARAM ATA_PARAM(atp->controller, atp->unit) void atapi_attach(struct ata_softc *scp, int device) { struct atapi_softc *atp; if (!(atp = malloc(sizeof(struct atapi_softc), M_ATAPI, M_NOWAIT|M_ZERO))) { ata_printf(scp, device, "failed to allocate driver storage\n"); return; } atp->controller = scp; atp->unit = device; if (bootverbose) ata_printf(scp, device, "piomode=%d dmamode=%d udmamode=%d dmaflag=%d\n", ata_pmode(ATP_PARAM), ata_wmode(ATP_PARAM), ata_umode(ATP_PARAM), ATP_PARAM->dmaflag); if (atapi_dma && !(ATP_PARAM->drqtype == ATAPI_DRQT_INTR)){ ata_dmainit(atp->controller, atp->unit, (ata_pmode(ATP_PARAM) < 0) ? (ATP_PARAM->dmaflag ? 4 : 0) : ata_pmode(ATP_PARAM), (ata_wmode(ATP_PARAM) < 0) ? (ATP_PARAM->dmaflag ? 2 : 0) : ata_wmode(ATP_PARAM), ata_umode(ATP_PARAM)); } else ata_dmainit(atp->controller, atp->unit, ata_pmode(ATP_PARAM) < 0 ? 0 : ata_pmode(ATP_PARAM), -1,-1); switch (ATP_PARAM->device_type) { #ifdef DEV_ATAPICD case ATAPI_TYPE_CDROM: if (acdattach(atp)) goto notfound; break; #endif #ifdef DEV_ATAPIFD case ATAPI_TYPE_DIRECT: if (afdattach(atp)) goto notfound; break; #endif #ifdef DEV_ATAPIST case ATAPI_TYPE_TAPE: if (astattach(atp)) goto notfound; break; #endif notfound: default: ata_printf(scp, device, "<%.40s/%.8s> %s device - NO DRIVER!\n", ATP_PARAM->model, ATP_PARAM->revision, atapi_type(ATP_PARAM->device_type)); free(atp, M_ATAPI); atp = NULL; } /* store our softc signalling we are ready to go */ scp->dev_softc[ATA_DEV(device)] = atp; } void atapi_detach(struct atapi_softc *atp) { struct atapi_request *request; atp->flags |= ATAPI_F_DETACHING; ata_printf(atp->controller, atp->unit, "removed from configuration\n"); switch (ATP_PARAM->device_type) { #ifdef DEV_ATAPICD case ATAPI_TYPE_CDROM: acddetach(atp); break; #endif #ifdef DEV_ATAPIFD case ATAPI_TYPE_DIRECT: afddetach(atp); break; #endif #ifdef DEV_ATAPIST case ATAPI_TYPE_TAPE: astdetach(atp); break; #endif default: return; } TAILQ_FOREACH(request, &atp->controller->atapi_queue, chain) { if (request->device != atp) continue; TAILQ_REMOVE(&atp->controller->atapi_queue, request, chain); if (request->driver) { struct bio *bp = (struct bio *) request->driver; biofinish(bp, NULL, ENXIO); } if (request->dmatab) free(request->dmatab, M_DEVBUF); free(request, M_ATAPI); } atp->controller->dev_softc[ATA_DEV(atp->unit)] = NULL; free(atp, M_ATAPI); } int atapi_queue_cmd(struct atapi_softc *atp, int8_t *ccb, caddr_t data, int count, int flags, int timeout, atapi_callback_t callback, void *driver) { struct atapi_request *request; int error, s; if (!(request = malloc(sizeof(struct atapi_request), M_ATAPI, M_NOWAIT | M_ZERO))) return ENOMEM; request->device = atp; request->data = data; request->bytecount = count; request->flags = flags; request->timeout = timeout * hz; request->ccbsize = (ATP_PARAM->cmdsize) ? 16 : 12; bcopy(ccb, request->ccb, request->ccbsize); if (callback) { request->callback = callback; request->driver = driver; } if (atp->controller->mode[ATA_DEV(atp->unit)] >= ATA_DMA) { if (!(request->dmatab = ata_dmaalloc(atp->controller, atp->unit))) atp->controller->mode[ATA_DEV(atp->unit)] = ATA_PIO; } s = splbio(); /* append onto controller queue and try to start controller */ #ifdef ATAPI_DEBUG ata_printf(atp->controller, atp->unit, "queueing %s ", atapi_cmd2str(request->ccb[0])); atapi_dump("ccb = ", &request->ccb[0], sizeof(request->ccb)); #endif if (flags & ATPR_F_AT_HEAD) TAILQ_INSERT_HEAD(&atp->controller->atapi_queue, request, chain); else TAILQ_INSERT_TAIL(&atp->controller->atapi_queue, request, chain); ata_start(atp->controller); /* if callback used, then just return, gets called from interrupt context */ if (callback) { splx(s); return 0; } /* wait for request to complete */ tsleep((caddr_t)request, PRIBIO, "atprq", 0); splx(s); error = request->error; if (error) atp->sense = request->sense; if (request->dmatab) free(request->dmatab, M_DEVBUF); free(request, M_ATAPI); return error; } void atapi_start(struct atapi_softc *atp) { switch (ATP_PARAM->device_type) { #ifdef DEV_ATAPICD case ATAPI_TYPE_CDROM: acd_start(atp); break; #endif #ifdef DEV_ATAPIFD case ATAPI_TYPE_DIRECT: afd_start(atp); break; #endif #ifdef DEV_ATAPIST case ATAPI_TYPE_TAPE: ast_start(atp); break; #endif default: return; } } int atapi_transfer(struct atapi_request *request) { struct atapi_softc *atp = request->device; int timout; u_int8_t reason; #ifdef ATAPI_DEBUG ata_printf(atp->controller, atp->unit, "starting %s ", atapi_cmd2str(request->ccb[0])); atapi_dump("ccb = ", &request->ccb[0], sizeof(request->ccb)); #endif /* is this just a POLL DSC command ? */ if (request->ccb[0] == ATAPI_POLL_DSC) { ATA_OUTB(atp->controller->r_io, ATA_DRIVE, ATA_D_IBM | atp->unit); DELAY(10); if (ATA_INB(atp->controller->r_altio, ATA_ALTSTAT) & ATA_S_DSC) request->error = 0; else request->error = EBUSY; atapi_finish(request); return ATA_OP_FINISHED; } /* start timeout for this command */ request->timeout_handle = timeout((timeout_t *)atapi_timeout, request, request->timeout); if (!(request->flags & ATPR_F_INTERNAL)) atp->cmd = request->ccb[0]; /* if DMA enabled setup DMA hardware */ request->flags &= ~ATPR_F_DMA_USED; if ((atp->controller->mode[ATA_DEV(atp->unit)] >= ATA_DMA) && (request->ccb[0] == ATAPI_READ || request->ccb[0] == ATAPI_READ_BIG || ((request->ccb[0] == ATAPI_WRITE || request->ccb[0] == ATAPI_WRITE_BIG) && !(atp->controller->flags & ATA_ATAPI_DMA_RO))) && !ata_dmasetup(atp->controller, atp->unit, request->dmatab, (void *)request->data, request->bytecount)) { request->flags |= ATPR_F_DMA_USED; } /* start ATAPI operation */ if (ata_command(atp->controller, atp->unit, ATA_C_PACKET_CMD, request->bytecount, 0, 0, 0, (request->flags & ATPR_F_DMA_USED) ? ATA_F_DMA : 0, ATA_IMMEDIATE)) ata_printf(atp->controller, atp->unit, "failure to send ATAPI packet command\n"); if (request->flags & ATPR_F_DMA_USED) ata_dmastart(atp->controller, atp->unit, request->dmatab, request->flags & ATPR_F_READ); /* command interrupt device ? just return */ if (ATP_PARAM->drqtype == ATAPI_DRQT_INTR) return ATA_OP_CONTINUES; /* ready to write ATAPI command */ timout = 5000; /* might be less for fast devices */ while (timout--) { reason = ATA_INB(atp->controller->r_io, ATA_IREASON); atp->controller->status = ATA_INB(atp->controller->r_io, ATA_STATUS); if (((reason & (ATA_I_CMD | ATA_I_IN)) | (atp->controller->status&(ATA_S_DRQ|ATA_S_BUSY)))==ATAPI_P_CMDOUT) break; DELAY(20); } if (timout <= 0) { ata_printf(atp->controller, atp->unit, "failure to execute ATAPI packet command\n"); untimeout((timeout_t *)atapi_timeout, request, request->timeout_handle); request->error = EIO; atapi_finish(request); return ATA_OP_FINISHED; } /* this seems to be needed for some (slow) devices */ DELAY(10); /* send actual command */ ATA_OUTSW(atp->controller->r_io, ATA_DATA, (int16_t *)request->ccb, request->ccbsize / sizeof(int16_t)); return ATA_OP_CONTINUES; } int atapi_interrupt(struct atapi_request *request) { struct atapi_softc *atp = request->device; int reason, dma_stat = 0; reason = (ATA_INB(atp->controller->r_io, ATA_IREASON)&(ATA_I_CMD|ATA_I_IN))| (atp->controller->status & ATA_S_DRQ); if (reason == ATAPI_P_CMDOUT) { if (!(atp->controller->status & ATA_S_DRQ)) { ata_printf(atp->controller, atp->unit, "command interrupt without DRQ\n"); untimeout((timeout_t *)atapi_timeout, request, request->timeout_handle); request->error = EIO; atapi_finish(request); return ATA_OP_FINISHED; } ATA_OUTSW(atp->controller->r_io, ATA_DATA, (int16_t *)request->ccb, request->ccbsize / sizeof(int16_t)); return ATA_OP_CONTINUES; } if (request->flags & ATPR_F_DMA_USED) { dma_stat = ata_dmadone(atp->controller); if ((atp->controller->status & (ATA_S_ERROR | ATA_S_DWF)) || dma_stat & ATA_BMSTAT_ERROR) { request->result = ATA_INB(atp->controller->r_io, ATA_ERROR); } else { request->result = 0; request->donecount = request->bytecount; request->bytecount = 0; } } else { int length = ATA_INB(atp->controller->r_io, ATA_CYL_LSB) | ATA_INB(atp->controller->r_io, ATA_CYL_MSB) << 8; switch (reason) { case ATAPI_P_WRITE: if (request->flags & ATPR_F_READ) { request->result = ATA_INB(atp->controller->r_io, ATA_ERROR); ata_printf(atp->controller, atp->unit, "%s trying to write on read buffer\n", atapi_cmd2str(atp->cmd)); break; } atapi_write(request, length); return ATA_OP_CONTINUES; case ATAPI_P_READ: if (!(request->flags & ATPR_F_READ)) { request->result = ATA_INB(atp->controller->r_io, ATA_ERROR); ata_printf(atp->controller, atp->unit, "%s trying to read on write buffer\n", atapi_cmd2str(atp->cmd)); break; } atapi_read(request, length); return ATA_OP_CONTINUES; case ATAPI_P_DONEDRQ: ata_printf(atp->controller, atp->unit, "%s DONEDRQ\n", atapi_cmd2str(atp->cmd)); if (request->flags & ATPR_F_READ) atapi_read(request, length); else atapi_write(request, length); /* FALLTHROUGH */ case ATAPI_P_ABORT: case ATAPI_P_DONE: if (atp->controller->status & (ATA_S_ERROR | ATA_S_DWF)) request->result = ATA_INB(atp->controller->r_io, ATA_ERROR); else if (!(request->flags & ATPR_F_INTERNAL)) request->result = 0; break; default: ata_printf(atp->controller, atp->unit, "unknown transfer phase %d\n", reason); } } untimeout((timeout_t *)atapi_timeout, request, request->timeout_handle); /* check for error, if valid sense key, queue a request sense cmd */ if ((request->result & ATAPI_SK_MASK) && request->ccb[0] != ATAPI_REQUEST_SENSE) { bzero(request->ccb, request->ccbsize); request->ccb[0] = ATAPI_REQUEST_SENSE; request->ccb[4] = sizeof(struct atapi_reqsense); request->bytecount = sizeof(struct atapi_reqsense); request->flags &= ATPR_F_QUIET; request->flags |= ATPR_F_READ | ATPR_F_INTERNAL; TAILQ_INSERT_HEAD(&atp->controller->atapi_queue, request, chain); } else { if (request->result) { switch ((request->result & ATAPI_SK_MASK)) { case ATAPI_SK_NO_SENSE: request->error = 0; break; case ATAPI_SK_RECOVERED_ERROR: ata_printf(atp->controller, atp->unit, "%s - recovered error\n", atapi_cmd2str(atp->cmd)); request->error = 0; break; case ATAPI_SK_NOT_READY: request->error = EBUSY; break; case ATAPI_SK_UNIT_ATTENTION: atp->flags |= ATAPI_F_MEDIA_CHANGED; request->error = EIO; break; default: request->error = EIO; if (request->flags & ATPR_F_QUIET) break; ata_printf(atp->controller, atp->unit, "%s - %s asc=0x%02x ascq=0x%02x ", atapi_cmd2str(atp->cmd), atapi_skey2str(request->sense.sense_key), request->sense.asc, request->sense.ascq); if (request->sense.sksv) printf("sks=0x%02x 0x%02x 0x%02x ", request->sense.sk_specific, request->sense.sk_specific1, request->sense.sk_specific2); printf("error=0x%02x\n", request->result & ATAPI_E_MASK); } } else request->error = 0; atapi_finish(request); } return ATA_OP_FINISHED; } void atapi_reinit(struct atapi_softc *atp) { /* reinit device parameters */ if (atp->controller->mode[ATA_DEV(atp->unit)] >= ATA_DMA) ata_dmainit(atp->controller, atp->unit, (ata_pmode(ATP_PARAM) < 0) ? (ATP_PARAM->dmaflag ? 4 : 0) : ata_pmode(ATP_PARAM), (ata_wmode(ATP_PARAM) < 0) ? (ATP_PARAM->dmaflag ? 2 : 0) : ata_wmode(ATP_PARAM), ata_umode(ATP_PARAM)); else ata_dmainit(atp->controller, atp->unit, ata_pmode(ATP_PARAM)<0 ? 0 : ata_pmode(ATP_PARAM), -1, -1); } int atapi_test_ready(struct atapi_softc *atp) { int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return atapi_queue_cmd(atp, ccb, NULL, 0, 0, 30, NULL, NULL); } int atapi_wait_dsc(struct atapi_softc *atp, 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 = atapi_queue_cmd(atp, ccb, NULL, 0, 0, 0, NULL, NULL); if (error != EBUSY) break; tsleep((caddr_t)&error, PRIBIO, "atpwt", hz / 2); timeout -= (hz / 2); } return error; } void atapi_dump(char *label, void *data, int len) { u_int8_t *p = data; printf("%s %02x", label, *p++); while (--len > 0) printf ("-%02x", *p++); printf("\n"); } static void atapi_read(struct atapi_request *request, int length) { int8_t **buffer = (int8_t **)&request->data; int size = min(request->bytecount, length); struct ata_softc *scp = request->device->controller; int resid; if (request->flags & ATPR_F_INTERNAL) *buffer = (int8_t *)&request->sense; if (scp->flags & ATA_USE_16BIT || (size % sizeof(int32_t))) ATA_INSW(scp->r_io, ATA_DATA, (void *)((uintptr_t)*buffer), size / sizeof(int16_t)); else ATA_INSL(scp->r_io, ATA_DATA, (void *)((uintptr_t)*buffer), size / sizeof(int32_t)); if (request->bytecount < length) { ata_printf(scp, request->device->unit, "read data overrun %d/%d\n", length, request->bytecount); for (resid=request->bytecount; residr_io, ATA_DATA); } *buffer += size; request->bytecount -= size; request->donecount += size; } static void atapi_write(struct atapi_request *request, int length) { int8_t **buffer = (int8_t **)&request->data; int size = min(request->bytecount, length); struct ata_softc *scp = request->device->controller; int resid; if (request->flags & ATPR_F_INTERNAL) *buffer = (int8_t *)&request->sense; if (scp->flags & ATA_USE_16BIT || (size % sizeof(int32_t))) ATA_OUTSW(scp->r_io, ATA_DATA, (void *)((uintptr_t)*buffer), size / sizeof(int16_t)); else ATA_OUTSL(scp->r_io, ATA_DATA, (void *)((uintptr_t)*buffer), size / sizeof(int32_t)); if (request->bytecount < length) { ata_printf(scp, request->device->unit, "write data underrun %d/%d\n", length, request->bytecount); for (resid=request->bytecount; residr_io, ATA_DATA, 0); } *buffer += size; request->bytecount -= size; request->donecount += size; } static void atapi_finish(struct atapi_request *request) { #ifdef ATAPI_DEBUG ata_printf(request->device->controller, request->device->unit, "finished %s%s\n", request->callback ? "callback " : "", atapi_cmd2str(request->ccb[0])); #endif if (request->callback) { if (!((request->callback)(request))) { if (request->dmatab) free(request->dmatab, M_DEVBUF); free(request, M_ATAPI); } } else wakeup((caddr_t)request); } static void atapi_timeout(struct atapi_request *request) { struct atapi_softc *atp = request->device; int s = splbio(); atp->controller->running = NULL; ata_printf(atp->controller, atp->unit, "%s command timeout - resetting\n", atapi_cmd2str(request->ccb[0])); if (request->flags & ATPR_F_DMA_USED) { ata_dmadone(atp->controller); if (request->retries == ATAPI_MAX_RETRIES) { ata_dmainit(atp->controller, atp->unit, (ata_pmode(ATP_PARAM)<0)?0:ata_pmode(ATP_PARAM),-1,-1); ata_printf(atp->controller, atp->unit, "trying fallback to PIO mode\n"); request->retries = 0; } } /* if retries still permit, reinject this request */ if (request->retries++ < ATAPI_MAX_RETRIES) TAILQ_INSERT_HEAD(&atp->controller->atapi_queue, request, chain); else { /* retries all used up, return error */ request->error = EIO; wakeup((caddr_t)request); } ata_reinit(atp->controller); splx(s); } static char * atapi_type(int type) { switch (type) { case ATAPI_TYPE_CDROM: return "CDROM"; case ATAPI_TYPE_DIRECT: return "floppy"; case ATAPI_TYPE_TAPE: return "tape"; case ATAPI_TYPE_OPTICAL: return "optical"; default: return "Unknown"; } } static char * atapi_cmd2str(u_int8_t cmd) { switch (cmd) { case 0x00: return ("TEST_UNIT_READY"); case 0x01: return ("REZERO"); case 0x03: return ("REQUEST_SENSE"); case 0x04: return ("FORMAT_UNIT"); case 0x08: return ("READ"); case 0x0a: return ("WRITE"); case 0x10: return ("WEOF"); case 0x11: return ("SPACE"); 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 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 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"); default: { static char buffer[16]; sprintf(buffer, "unknown CMD (0x%02x)", cmd); return buffer; } } } static char * atapi_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"); } } Index: head/sys/dev/ata/atapi-all.h =================================================================== --- head/sys/dev/ata/atapi-all.h (revision 83727) +++ head/sys/dev/ata/atapi-all.h (revision 83728) @@ -1,204 +1,204 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* ATAPI misc defines */ #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 /* error register bits */ #define ATAPI_E_MASK 0x0f /* error mask */ #define ATAPI_E_ILI 0x01 /* illegal length indication */ #define ATAPI_E_EOM 0x02 /* end of media detected */ #define ATAPI_E_ABRT 0x04 /* command aborted */ #define ATAPI_E_MCR 0x08 /* media change requested */ #define ATAPI_SK_MASK 0xf0 /* sense key mask */ #define ATAPI_SK_NO_SENSE 0x00 /* no specific sense key info */ #define ATAPI_SK_RECOVERED_ERROR 0x10 /* command OK, data recovered */ #define ATAPI_SK_NOT_READY 0x20 /* no access to drive */ #define ATAPI_SK_MEDIUM_ERROR 0x30 /* non-recovered data error */ #define ATAPI_SK_HARDWARE_ERROR 0x40 /* non-recoverable HW failure */ #define ATAPI_SK_ILLEGAL_REQUEST 0x50 /* invalid command param(s) */ #define ATAPI_SK_UNIT_ATTENTION 0x60 /* media changed */ #define ATAPI_SK_DATA_PROTECT 0x70 /* write protect */ #define ATAPI_SK_BLANK_CHECK 0x80 /* blank check */ #define ATAPI_SK_VENDOR_SPECIFIC 0x90 /* vendor specific skey */ #define ATAPI_SK_COPY_ABORTED 0xa0 /* copy aborted */ #define ATAPI_SK_ABORTED_COMMAND 0xb0 /* command aborted, try again */ #define ATAPI_SK_EQUAL 0xc0 /* equal */ #define ATAPI_SK_VOLUME_OVERFLOW 0xd0 /* volume overflow */ #define ATAPI_SK_MISCOMPARE 0xe0 /* data dont match the medium */ #define ATAPI_SK_RESERVED 0xf0 /* ATAPI commands */ #define ATAPI_TEST_UNIT_READY 0x00 /* check if device is ready */ #define ATAPI_REZERO 0x01 /* rewind */ #define ATAPI_REQUEST_SENSE 0x03 /* get sense data */ #define ATAPI_FORMAT 0x04 /* format unit */ #define ATAPI_READ 0x08 /* read data */ #define ATAPI_WRITE 0x0a /* write data */ #define ATAPI_WEOF 0x10 /* write filemark */ #define WF_WRITE 0x01 #define ATAPI_SPACE 0x11 /* space command */ #define SP_FM 0x01 #define SP_EOD 0x03 #define ATAPI_MODE_SELECT 0x15 /* mode select */ #define ATAPI_ERASE 0x19 /* erase */ #define ATAPI_MODE_SENSE 0x1a /* mode sense */ #define ATAPI_START_STOP 0x1b /* start/stop unit */ #define SS_LOAD 0x01 #define SS_RETENSION 0x02 #define SS_EJECT 0x04 #define ATAPI_PREVENT_ALLOW 0x1e /* media removal */ #define ATAPI_READ_CAPACITY 0x25 /* get volume capacity */ #define ATAPI_READ_BIG 0x28 /* read data */ #define ATAPI_WRITE_BIG 0x2a /* write data */ #define ATAPI_LOCATE 0x2b /* locate to position */ #define ATAPI_READ_POSITION 0x34 /* read position */ #define ATAPI_SYNCHRONIZE_CACHE 0x35 /* flush buf, close channel */ #define ATAPI_WRITE_BUFFER 0x3b /* write device buffer */ #define ATAPI_READ_BUFFER 0x3c /* read device buffer */ #define ATAPI_READ_SUBCHANNEL 0x42 /* get subchannel info */ #define ATAPI_READ_TOC 0x43 /* get table of contents */ #define ATAPI_PLAY_10 0x45 /* play by lba */ #define ATAPI_PLAY_MSF 0x47 /* play by MSF address */ #define ATAPI_PLAY_TRACK 0x48 /* play by track number */ #define ATAPI_PAUSE 0x4b /* pause audio operation */ #define ATAPI_READ_DISK_INFO 0x51 /* get disk info structure */ #define ATAPI_READ_TRACK_INFO 0x52 /* get track info structure */ #define ATAPI_RESERVE_TRACK 0x53 /* reserve track */ #define ATAPI_SEND_OPC_INFO 0x54 /* send OPC structurek */ #define ATAPI_MODE_SELECT_BIG 0x55 /* set device parameters */ #define ATAPI_REPAIR_TRACK 0x58 /* repair track */ #define ATAPI_READ_MASTER_CUE 0x59 /* read master CUE info */ #define ATAPI_MODE_SENSE_BIG 0x5a /* get device parameters */ #define ATAPI_CLOSE_TRACK 0x5b /* close track/session */ #define ATAPI_READ_BUFFER_CAPACITY 0x5c /* get buffer capicity */ #define ATAPI_SEND_CUE_SHEET 0x5d /* send CUE sheet */ #define ATAPI_BLANK 0xa1 /* blank the media */ #define ATAPI_SEND_KEY 0xa3 /* send DVD key structure */ #define ATAPI_REPORT_KEY 0xa4 /* get DVD key structure */ #define ATAPI_PLAY_12 0xa5 /* play by lba */ #define ATAPI_LOAD_UNLOAD 0xa6 /* changer control command */ #define ATAPI_READ_STRUCTURE 0xad /* get DVD structure */ #define ATAPI_PLAY_CD 0xb4 /* universal play command */ #define ATAPI_SET_SPEED 0xbb /* set drive speed */ #define ATAPI_MECH_STATUS 0xbd /* get changer status */ #define ATAPI_READ_CD 0xbe /* read data */ #define ATAPI_POLL_DSC 0xff /* poll DSC status bit */ /* ATAPI request sense structure */ struct atapi_reqsense { u_int8_t error_code :7; /* current or deferred errors */ u_int8_t valid :1; /* follows ATAPI spec */ u_int8_t segment; /* Segment number */ u_int8_t sense_key :4; /* sense key */ u_int8_t reserved2_4 :1; /* reserved */ u_int8_t ili :1; /* incorrect length indicator */ u_int8_t eom :1; /* end of medium */ u_int8_t filemark :1; /* filemark */ /* cmd information */ u_int32_t cmd_info __attribute__((packed)); u_int8_t sense_length; /* additional sense len (n-7) */ /* additional cmd spec info */ u_int32_t cmd_specific_info __attribute__((packed)); u_int8_t asc; /* additional sense code */ u_int8_t ascq; /* additional sense code qual */ u_int8_t replaceable_unit_code; /* replaceable unit code */ u_int8_t sk_specific :7; /* sense key specific */ u_int8_t sksv :1; /* sense key specific info OK */ u_int8_t sk_specific1; /* sense key specific */ u_int8_t sk_specific2; /* sense key specific */ }; struct atapi_softc { struct ata_softc *controller; /* ptr to controller softc */ int unit; /* ATA_MASTER or ATA_SLAVE */ void *driver; /* ptr to subdriver softc */ u_int8_t cmd; /* last cmd executed */ struct atapi_reqsense sense; /* last cmd sense if error */ int flags; /* drive flags */ #define ATAPI_F_MEDIA_CHANGED 0x0001 #define ATAPI_F_DETACHING 0x0002 }; typedef int atapi_callback_t(struct atapi_request *); struct atapi_request { struct atapi_softc *device; /* ptr to parent device */ u_int8_t ccb[16]; /* command control block */ int ccbsize; /* size of ccb (12 | 16) */ u_int32_t bytecount; /* bytes to transfer */ u_int32_t donecount; /* bytes transferred */ int timeout; /* timeout for this cmd */ struct callout_handle timeout_handle; /* handle for untimeout */ int retries; /* retry count */ int result; /* result of this cmd */ int error; /* result translated to errno */ struct atapi_reqsense sense; /* sense data if error */ int flags; #define ATPR_F_READ 0x0001 #define ATPR_F_DMA_USED 0x0002 #define ATPR_F_AT_HEAD 0x0004 #define ATPR_F_INTERNAL 0x0008 #define ATPR_F_QUIET 0x0010 caddr_t data; /* pointer to data buf */ atapi_callback_t *callback; /* ptr to callback func */ struct ata_dmaentry *dmatab; /* DMA transfer table */ void *driver; /* driver specific */ TAILQ_ENTRY(atapi_request) chain; /* list management */ }; void atapi_attach(struct ata_softc *, int); void atapi_detach(struct atapi_softc *); void atapi_start(struct atapi_softc *); int atapi_transfer(struct atapi_request *); int atapi_interrupt(struct atapi_request *); int atapi_queue_cmd(struct atapi_softc *, int8_t [], caddr_t, int, int, int, atapi_callback_t, void *); void atapi_reinit(struct atapi_softc *); int atapi_test_ready(struct atapi_softc *); int atapi_wait_dsc(struct atapi_softc *, int); void atapi_request_sense(struct atapi_softc *, struct atapi_reqsense *); void atapi_dump(char *, void *, int); int acdattach(struct atapi_softc *); void acddetach(struct atapi_softc *); void acd_start(struct atapi_softc *); int afdattach(struct atapi_softc *); void afddetach(struct atapi_softc *); void afd_start(struct atapi_softc *); int astattach(struct atapi_softc *); void astdetach(struct atapi_softc *); void ast_start(struct atapi_softc *); Index: head/sys/dev/ata/atapi-cd.c =================================================================== --- head/sys/dev/ata/atapi-cd.c (revision 83727) +++ head/sys/dev/ata/atapi-cd.c (revision 83728) @@ -1,2006 +1,2008 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* device structures */ static d_open_t acdopen; static d_close_t acdclose; static d_ioctl_t acdioctl; static d_strategy_t acdstrategy; static struct cdevsw acd_cdevsw = { /* open */ acdopen, /* close */ acdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ acdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ acdstrategy, /* name */ "acd", /* maj */ 117, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK | D_TRACKCLOSE, }; /* prototypes */ static struct acd_softc *acd_init_lun(struct atapi_softc *, struct devstat *); static void acd_make_dev(struct acd_softc *); static void acd_describe(struct acd_softc *); 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_done(struct atapi_request *); static void acd_read_toc(struct acd_softc *); static int acd_play(struct acd_softc *, int, int); static int acd_setchan(struct acd_softc *, u_int8_t, u_int8_t, u_int8_t, u_int8_t); static void acd_select_slot(struct acd_softc *); static int acd_init_writer(struct acd_softc *, int); static int acd_fixate(struct acd_softc *, int); static int acd_init_track(struct acd_softc *, struct cdr_track *); static int acd_flush(struct acd_softc *); static int acd_read_track_info(struct acd_softc *, int32_t, struct acd_track_info *); static int acd_get_progress(struct acd_softc *, int *); static int acd_send_cue(struct acd_softc *, struct cdr_cuesheet *); static int acd_report_key(struct acd_softc *, struct dvd_authinfo *); static int acd_send_key(struct acd_softc *, struct dvd_authinfo *); static int acd_read_structure(struct acd_softc *, struct dvd_struct *); static int acd_eject(struct acd_softc *, int); static int acd_blank(struct acd_softc *, int); static int acd_prevent_allow(struct acd_softc *, int); static int acd_start_stop(struct acd_softc *, int); static int acd_pause_resume(struct acd_softc *, int); static int acd_mode_sense(struct acd_softc *, int, caddr_t, int); static int acd_mode_select(struct acd_softc *, caddr_t, int); static int acd_set_speed(struct acd_softc *, int, int); static void acd_get_cap(struct acd_softc *); /* internal vars */ static u_int32_t acd_lun_map = 0; static MALLOC_DEFINE(M_ACD, "ACD driver", "ATAPI CD driver buffers"); int acdattach(struct atapi_softc *atp) { struct acd_softc *cdp; struct changer *chp; - char name[16]; static int acd_cdev_done = 0; if (!acd_cdev_done) { cdevsw_add(&acd_cdevsw); acd_cdev_done++; } if ((cdp = acd_init_lun(atp, NULL)) == NULL) { ata_printf(atp->controller, atp->unit, "acd: out of memory\n"); return -1; } - sprintf(name, "acd%d", cdp->lun); - ata_set_name(atp->controller, atp->unit, name); + ata_set_name(atp->controller, atp->unit, "acd", cdp->lun); acd_get_cap(cdp); /* if this is a changer device, allocate the neeeded lun's */ if (cdp->cap.mech == MST_MECH_CHANGER) { int8_t ccb[16] = { ATAPI_MECH_STATUS, 0, 0, 0, 0, 0, 0, 0, sizeof(struct changer)>>8, sizeof(struct changer), 0, 0, 0, 0, 0, 0 }; chp = malloc(sizeof(struct changer), M_ACD, M_NOWAIT | M_ZERO); if (chp == NULL) { ata_printf(atp->controller, atp->unit, "out of memory\n"); free(cdp, M_ACD); return -1; } if (!atapi_queue_cmd(cdp->atp, ccb, (caddr_t)chp, sizeof(struct changer), ATPR_F_READ, 60, NULL, NULL)) { struct acd_softc *tmpcdp = cdp; struct acd_softc **cdparr; + char *name; int count; chp->table_length = htons(chp->table_length); if (!(cdparr = malloc(sizeof(struct acd_softc) * chp->slots, M_ACD, M_NOWAIT))) { ata_printf(atp->controller, atp->unit, "out of memory\n"); free(chp, M_ACD); free(cdp, M_ACD); return -1; } for (count = 0; count < chp->slots; count++) { if (count > 0) { - tmpcdp = acd_init_lun(atp, cdp->stats); + tmpcdp = acd_init_lun(atp, NULL); if (!tmpcdp) { ata_printf(atp->controller,atp->unit,"out of memory\n"); break; } } cdparr[count] = tmpcdp; tmpcdp->driver = cdparr; tmpcdp->slot = count; tmpcdp->changer_info = chp; acd_make_dev(tmpcdp); + devstat_add_entry(cdp->stats, "acd", tmpcdp->lun, DEV_BSIZE, + DEVSTAT_NO_ORDERED_TAGS, + DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_IDE, + DEVSTAT_PRIORITY_CD); } - sprintf(name, "acd%d-%d", cdp->lun, - cdp->lun + cdp->changer_info->slots - 1); + name = + malloc(strlen(atp->controller->dev_name[ATA_DEV(atp->unit)])+1, + M_ACD, M_NOWAIT); + strcpy(name, atp->controller->dev_name[ATA_DEV(atp->unit)]); ata_free_name(atp->controller, atp->unit); - ata_set_name(atp->controller, atp->unit, name); - devstat_add_entry(cdp->stats, name, tmpcdp->lun, DEV_BSIZE, - DEVSTAT_NO_ORDERED_TAGS, - DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_IDE, - DEVSTAT_PRIORITY_CD); + ata_set_name(atp->controller, atp->unit, name, + cdp->lun + cdp->changer_info->slots - 1); + free(name, M_ACD); } } else { acd_make_dev(cdp); devstat_add_entry(cdp->stats, "acd", cdp->lun, DEV_BSIZE, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_IDE, DEVSTAT_PRIORITY_CD); } cdp->atp->driver = cdp; acd_describe(cdp); return 0; } void acddetach(struct atapi_softc *atp) { struct acd_softc *cdp = atp->driver; struct acd_devlist *entry; struct bio *bp; int subdev; if (cdp->changer_info) { for (subdev = 0; subdev < cdp->changer_info->slots; subdev++) { if (cdp->driver[subdev] == cdp) continue; while ((bp = bioq_first(&cdp->driver[subdev]->queue))) { biofinish(bp, NULL, ENXIO); } destroy_dev(cdp->driver[subdev]->dev1); destroy_dev(cdp->driver[subdev]->dev2); while ((entry = TAILQ_FIRST(&cdp->driver[subdev]->dev_list))) { destroy_dev(entry->dev); TAILQ_REMOVE(&cdp->driver[subdev]->dev_list, entry, chain); free(entry, M_ACD); } devstat_remove_entry(cdp->driver[subdev]->stats); free(cdp->driver[subdev]->stats, M_ACD); ata_free_lun(&acd_lun_map, cdp->driver[subdev]->lun); free(cdp->driver[subdev], M_ACD); } free(cdp->driver, M_ACD); free(cdp->changer_info, M_ACD); } while ((bp = bioq_first(&cdp->queue))) { biofinish(bp, NULL, ENXIO); } destroy_dev(cdp->dev1); destroy_dev(cdp->dev2); while ((entry = TAILQ_FIRST(&cdp->dev_list))) { destroy_dev(entry->dev); TAILQ_REMOVE(&cdp->dev_list, entry, chain); free(entry, M_ACD); } devstat_remove_entry(cdp->stats); free(cdp->stats, M_ACD); ata_free_name(atp->controller, atp->unit); ata_free_lun(&acd_lun_map, cdp->lun); free(cdp, M_ACD); } static struct acd_softc * acd_init_lun(struct atapi_softc *atp, struct devstat *stats) { struct acd_softc *cdp; if (!(cdp = malloc(sizeof(struct acd_softc), M_ACD, M_NOWAIT | M_ZERO))) return NULL; TAILQ_INIT(&cdp->dev_list); bioq_init(&cdp->queue); cdp->atp = atp; cdp->lun = ata_get_lun(&acd_lun_map); cdp->block_size = 2048; cdp->slot = -1; cdp->changer_info = NULL; if (stats == NULL) { if (!(cdp->stats = malloc(sizeof(struct devstat), M_ACD, M_NOWAIT | M_ZERO))) { free(cdp, M_ACD); return NULL; } } else cdp->stats = stats; return cdp; } static void acd_make_dev(struct acd_softc *cdp) { dev_t dev; dev = make_dev(&acd_cdevsw, dkmakeminor(cdp->lun, 0, 0), UID_ROOT, GID_OPERATOR, 0644, "acd%da", cdp->lun); dev->si_drv1 = cdp; dev->si_iosize_max = 252 * DEV_BSIZE; dev->si_bsize_phys = 2048; /* XXX SOS */ cdp->dev1 = dev; dev = make_dev(&acd_cdevsw, dkmakeminor(cdp->lun, 0, RAW_PART), UID_ROOT, GID_OPERATOR, 0644, "acd%dc", cdp->lun); dev->si_drv1 = cdp; dev->si_iosize_max = 252 * DEV_BSIZE; dev->si_bsize_phys = 2048; /* XXX SOS */ cdp->dev2 = dev; cdp->atp->flags |= ATAPI_F_MEDIA_CHANGED; } static void acd_describe(struct acd_softc *cdp) { int comma = 0; char *mechanism; if (bootverbose) { ata_printf(cdp->atp->controller, cdp->atp->unit, "<%.40s/%.8s> %s drive at ata%d as %s\n", ATA_PARAM(cdp->atp->controller, cdp->atp->unit)->model, ATA_PARAM(cdp->atp->controller, cdp->atp->unit)->revision, (cdp->cap.write_dvdr) ? "DVD-R" : (cdp->cap.write_dvdram) ? "DVD-RAM" : (cdp->cap.write_cdrw) ? "CD-RW" : (cdp->cap.write_cdr) ? "CD-R" : (cdp->cap.read_dvdrom) ? "DVD-ROM" : "CDROM", device_get_unit(cdp->atp->controller->dev), (cdp->atp->unit == ATA_MASTER) ? "master" : "slave"); ata_printf(cdp->atp->controller, cdp->atp->unit, "%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.write_cdr || cdp->cap.write_cdrw || cdp->cap.write_dvdr || cdp->cap.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( cdp->atp->controller->mode[ATA_DEV(cdp->atp->unit)])); ata_printf(cdp->atp->controller, cdp->atp->unit, "Reads:"); comma = 0; if (cdp->cap.read_cdr) { printf(" CD-R"); comma = 1; } if (cdp->cap.read_cdrw) { printf("%s CD-RW", comma ? "," : ""); comma = 1; } if (cdp->cap.cd_da) { if (cdp->cap.cd_da_stream) printf("%s CD-DA stream", comma ? "," : ""); else printf("%s CD-DA", comma ? "," : ""); comma = 1; } if (cdp->cap.read_dvdrom) { printf("%s DVD-ROM", comma ? "," : ""); comma = 1; } if (cdp->cap.read_dvdr) { printf("%s DVD-R", comma ? "," : ""); comma = 1; } if (cdp->cap.read_dvdram) { printf("%s DVD-RAM", comma ? "," : ""); comma = 1; } if (cdp->cap.read_packet) printf("%s packet", comma ? "," : ""); printf("\n"); ata_printf(cdp->atp->controller, cdp->atp->unit, "Writes:"); if (cdp->cap.write_cdr || cdp->cap.write_cdrw || cdp->cap.write_dvdr || cdp->cap.write_dvdram) { comma = 0; if (cdp->cap.write_cdr) { printf(" CD-R" ); comma = 1; } if (cdp->cap.write_cdrw) { printf("%s CD-RW", comma ? "," : ""); comma = 1; } if (cdp->cap.write_dvdr) { printf("%s DVD-R", comma ? "," : ""); comma = 1; } if (cdp->cap.write_dvdram) { printf("%s DVD-RAM", comma ? "," : ""); comma = 1; } if (cdp->cap.test_write) { printf("%s test write", comma ? "," : ""); comma = 1; } if (cdp->cap.burnproof) printf("%s burnproof", comma ? "," : ""); } printf("\n"); if (cdp->cap.audio_play) { ata_printf(cdp->atp->controller, cdp->atp->unit, "Audio: "); if (cdp->cap.audio_play) printf("play"); if (cdp->cap.max_vol_levels) printf(", %d volume levels", cdp->cap.max_vol_levels); printf("\n"); } ata_printf(cdp->atp->controller, cdp->atp->unit, "Mechanism: "); switch (cdp->cap.mech) { 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.eject ? "ejectable " : "", mechanism); else if (cdp->cap.eject) printf("ejectable"); if (cdp->cap.lock) printf(cdp->cap.locked ? ", locked" : ", unlocked"); if (cdp->cap.prevent) printf(", lock protected"); printf("\n"); if (cdp->cap.mech != MST_MECH_CHANGER) { ata_printf(cdp->atp->controller, cdp->atp->unit, "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)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 { - char chg[32]; - - bzero(chg, sizeof(chg)); - if (cdp->changer_info) - sprintf(chg, " with %d CD changer", cdp->changer_info->slots); - - ata_printf(cdp->atp->controller, cdp->atp->unit, - "%s%s <%.40s> at ata%d-%s %s\n", + ata_printf(cdp->atp->controller, cdp->atp->unit, "%s ", (cdp->cap.write_dvdr) ? "DVD-R" : (cdp->cap.write_dvdram) ? "DVD-RAM" : (cdp->cap.write_cdrw) ? "CD-RW" : (cdp->cap.write_cdr) ? "CD-R" : - (cdp->cap.read_dvdrom) ? "DVD-ROM" : "CDROM", - chg, ATA_PARAM(cdp->atp->controller, cdp->atp->unit)->model, - device_get_unit(cdp->atp->controller->dev), - (cdp->atp->unit == ATA_MASTER) ? "master" : "slave", - ata_mode2str(cdp->atp->controller-> - mode[ATA_DEV(cdp->atp->unit)]) + (cdp->cap.read_dvdrom) ? "DVD-ROM" : "CDROM"); + + if (cdp->changer_info) + printf("with %d CD changer ", cdp->changer_info->slots); + + printf("<%.40s> at ata%d-%s %s\n", + ATA_PARAM(cdp->atp->controller, cdp->atp->unit)->model, + device_get_unit(cdp->atp->controller->dev), + (cdp->atp->unit == ATA_MASTER) ? "master" : "slave", + ata_mode2str(cdp->atp->controller->mode[ATA_DEV(cdp->atp->unit)]) ); } } static __inline 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 __inline u_int32_t msf2lba(u_int8_t m, u_int8_t s, u_int8_t f) { return (m * 60 + s) * 75 + f - 150; } static int acdopen(dev_t dev, int flags, int fmt, struct thread *td) { struct acd_softc *cdp = dev->si_drv1; if (!cdp) return ENXIO; if (flags & FWRITE) { if (count_dev(dev) > 1) return EBUSY; } if (count_dev(dev) == 1) { if (cdp->changer_info && cdp->slot != cdp->changer_info->current_slot) { acd_select_slot(cdp); tsleep(&cdp->changer_info, PRIBIO, "acdopn", 0); } acd_prevent_allow(cdp, 1); cdp->flags |= F_LOCKED; acd_read_toc(cdp); } return 0; } static int acdclose(dev_t dev, int flags, int fmt, struct thread *td) { struct acd_softc *cdp = dev->si_drv1; if (!cdp) return ENXIO; if (count_dev(dev) == 1) { if (cdp->changer_info && cdp->slot != cdp->changer_info->current_slot) { acd_select_slot(cdp); tsleep(&cdp->changer_info, PRIBIO, "acdclo", 0); } acd_prevent_allow(cdp, 0); cdp->flags &= ~F_LOCKED; } return 0; } static int acdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct acd_softc *cdp = dev->si_drv1; int error = 0; if (!cdp) return ENXIO; if (cdp->changer_info && cdp->slot != cdp->changer_info->current_slot) { acd_select_slot(cdp); tsleep(&cdp->changer_info, PRIBIO, "acdctl", 0); } if (cdp->atp->flags & ATAPI_F_MEDIA_CHANGED) switch (cmd) { case CDIOCRESET: atapi_test_ready(cdp->atp); break; default: acd_read_toc(cdp); acd_prevent_allow(cdp, 1); cdp->flags |= F_LOCKED; break; } switch (cmd) { case CDIOCRESUME: error = acd_pause_resume(cdp, 1); break; case CDIOCPAUSE: error = acd_pause_resume(cdp, 0); break; case CDIOCSTART: error = acd_start_stop(cdp, 1); break; case CDIOCSTOP: error = acd_start_stop(cdp, 0); break; case CDIOCALLOW: error = acd_prevent_allow(cdp, 0); cdp->flags &= ~F_LOCKED; break; case CDIOCPREVENT: error = acd_prevent_allow(cdp, 1); cdp->flags |= F_LOCKED; break; case CDIOCRESET: error = suser(td->td_proc); if (error) break; error = atapi_test_ready(cdp->atp); break; case CDIOCEJECT: if (count_dev(dev) > 1) { error = EBUSY; break; } error = acd_eject(cdp, 0); break; case CDIOCCLOSE: if (count_dev(dev) > 1) break; error = acd_eject(cdp, 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; - struct toc buf; int starting_track = te->starting_track; int len; - if (!cdp->toc.hdr.ending_track) { + 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; - buf = cdp->toc; - toc = &buf; + toc = malloc(sizeof(struct toc), M_ACD, M_NOWAIT | M_ZERO); + 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); } 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; - struct toc buf; u_char track = te->track; - if (!cdp->toc.hdr.ending_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; - buf = cdp->toc; - toc = &buf; + toc = malloc(sizeof(struct toc), M_ACD, M_NOWAIT | M_ZERO); + 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; case CDIOCREADSUBCHANNEL: { struct ioc_read_subchannel *args = (struct ioc_read_subchannel *)addr; - struct cd_sub_channel_info data; - int len = args->data_len; - int32_t abslba, rellba; + struct cd_sub_channel_info *data; 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 (len > sizeof(data) || - len < sizeof(struct cd_sub_channel_header)) { + if (args->data_len > sizeof(data) || + args->data_len < sizeof(struct cd_sub_channel_header)) { error = EINVAL; break; } if ((error = atapi_queue_cmd(cdp->atp, ccb, (caddr_t)&cdp->subchan, sizeof(cdp->subchan), ATPR_F_READ, 10, NULL, NULL))) { break; } - abslba = cdp->subchan.abslba; - rellba = cdp->subchan.rellba; + data = malloc(sizeof(struct cd_sub_channel_info), + M_ACD, M_NOWAIT | M_ZERO); + if (args->address_format == CD_MSF_FORMAT) { - lba2msf(ntohl(abslba), - &data.what.position.absaddr.msf.minute, - &data.what.position.absaddr.msf.second, - &data.what.position.absaddr.msf.frame); - lba2msf(ntohl(rellba), - &data.what.position.reladdr.msf.minute, - &data.what.position.reladdr.msf.second, - &data.what.position.reladdr.msf.frame); + lba2msf(ntohl(cdp->subchan.abslba), + &data->what.position.absaddr.msf.minute, + &data->what.position.absaddr.msf.second, + &data->what.position.absaddr.msf.frame); + lba2msf(ntohl(cdp->subchan.rellba), + &data->what.position.reladdr.msf.minute, + &data->what.position.reladdr.msf.second, + &data->what.position.reladdr.msf.frame); } else { - data.what.position.absaddr.lba = abslba; - data.what.position.reladdr.lba = rellba; + data->what.position.absaddr.lba = cdp->subchan.abslba; + data->what.position.reladdr.lba = cdp->subchan.rellba; } - data.header.audio_status = cdp->subchan.audio_status; - data.what.position.control = cdp->subchan.control & 0xf; - data.what.position.addr_type = cdp->subchan.control >> 4; - data.what.position.track_number = cdp->subchan.track; - data.what.position.index_number = cdp->subchan.indx; - error = copyout(&data, args->data, len); + data->header.audio_status = cdp->subchan.audio_status; + data->what.position.control = cdp->subchan.control & 0xf; + data->what.position.addr_type = cdp->subchan.control >> 4; + data->what.position.track_number = cdp->subchan.track; + data->what.position.index_number = cdp->subchan.indx; + error = copyout(data, args->data, args->data_len); + free(data, M_ACD); break; } case CDIOCPLAYMSF: { struct ioc_play_msf *args = (struct ioc_play_msf *)addr; error = acd_play(cdp, 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(cdp, 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(cdp, ntohl(cdp->toc.tab[t1].addr.lba), ntohl(cdp->toc.tab[t2].addr.lba)); break; } case CDIOCREADAUDIO: { struct ioc_read_audio *args = (struct ioc_read_audio *)addr; int32_t lba; caddr_t buffer, ubuf = args->buffer; int8_t ccb[16]; int frames; if (!cdp->toc.hdr.ending_track) { error = EIO; break; } if ((frames = args->nframes) < 0) { error = EINVAL; break; } if (args->address_format == CD_LBA_FORMAT) lba = args->address.lba; else if (args->address_format == CD_MSF_FORMAT) lba = msf2lba(args->address.msf.minute, args->address.msf.second, args->address.msf.frame); else { error = EINVAL; break; } #ifndef CD_BUFFER_BLOCKS #define CD_BUFFER_BLOCKS 13 #endif if (!(buffer = malloc(CD_BUFFER_BLOCKS * 2352, M_ACD, M_NOWAIT))){ error = ENOMEM; break; } bzero(ccb, sizeof(ccb)); while (frames > 0) { int8_t blocks; int size; blocks = (frames>CD_BUFFER_BLOCKS) ? CD_BUFFER_BLOCKS : frames; size = blocks * 2352; ccb[0] = ATAPI_READ_CD; ccb[1] = 4; ccb[2] = lba>>24; ccb[3] = lba>>16; ccb[4] = lba>>8; ccb[5] = lba; ccb[8] = blocks; ccb[9] = 0xf0; if ((error = atapi_queue_cmd(cdp->atp, ccb, buffer, size, ATPR_F_READ, 30, NULL,NULL))) break; if ((error = copyout(buffer, ubuf, size))) break; ubuf += size; frames -= blocks; lba += blocks; } free(buffer, M_ACD); if (args->address_format == CD_LBA_FORMAT) args->address.lba = lba; else if (args->address_format == CD_MSF_FORMAT) lba2msf(lba, &args->address.msf.minute, &args->address.msf.second, &args->address.msf.frame); break; } case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol *)addr; if ((error = acd_mode_sense(cdp, 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(cdp, 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(cdp, 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(cdp, (caddr_t)&cdp->au, sizeof(cdp->au)); break; } case CDIOCSETPATCH: { struct ioc_patch *arg = (struct ioc_patch *)addr; error = acd_setchan(cdp, arg->patch[0], arg->patch[1], arg->patch[2], arg->patch[3]); break; } case CDIOCSETMONO: error = acd_setchan(cdp, CHANNEL_0|CHANNEL_1, CHANNEL_0|CHANNEL_1, 0,0); break; case CDIOCSETSTEREO: error = acd_setchan(cdp, CHANNEL_0, CHANNEL_1, 0, 0); break; case CDIOCSETMUTE: error = acd_setchan(cdp, 0, 0, 0, 0); break; case CDIOCSETLEFT: error = acd_setchan(cdp, CHANNEL_0, CHANNEL_0, 0, 0); break; case CDIOCSETRIGHT: error = acd_setchan(cdp, CHANNEL_1, CHANNEL_1, 0, 0); break; case CDRIOCBLANK: error = acd_blank(cdp, (*(int *)addr)); break; case CDRIOCNEXTWRITEABLEADDR: { struct acd_track_info track_info; if ((error = acd_read_track_info(cdp, 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(cdp, (*(int *)addr)); break; case CDRIOCINITTRACK: error = acd_init_track(cdp, (struct cdr_track *)addr); break; case CDRIOCFLUSH: error = acd_flush(cdp); break; case CDRIOCFIXATE: error = acd_fixate(cdp, (*(int *)addr)); break; case CDRIOCREADSPEED: error = acd_set_speed(cdp, 177 * (*(int *)addr), -1); break; case CDRIOCWRITESPEED: error = acd_set_speed(cdp, -1, 177 * (*(int *)addr)); break; case CDRIOCGETBLOCKSIZE: *(int *)addr = cdp->block_size; break; case CDRIOCSETBLOCKSIZE: cdp->block_size = *(int *)addr; break; case CDRIOCGETPROGRESS: error = acd_get_progress(cdp, (int *)addr); break; case CDRIOCSENDCUE: error = acd_send_cue(cdp, (struct cdr_cuesheet *)addr); break; case DVDIOCREPORTKEY: if (!cdp->cap.read_dvdrom) error = EINVAL; else error = acd_report_key(cdp, (struct dvd_authinfo *)addr); break; case DVDIOCSENDKEY: if (!cdp->cap.read_dvdrom) error = EINVAL; else error = acd_send_key(cdp, (struct dvd_authinfo *)addr); break; case DVDIOCREADSTRUCTURE: if (!cdp->cap.read_dvdrom) error = EINVAL; else error = acd_read_structure(cdp, (struct dvd_struct *)addr); break; case DIOCGDINFO: *(struct disklabel *)addr = cdp->disklabel; break; case DIOCWDINFO: case DIOCSDINFO: if ((flags & FWRITE) == 0) error = EBADF; else error = setdisklabel(&cdp->disklabel, (struct disklabel *)addr, 0); break; case DIOCWLABEL: error = EBADF; break; case DIOCGPART: ((struct partinfo *)addr)->disklab = &cdp->disklabel; ((struct partinfo *)addr)->part = &cdp->disklabel.d_partitions[0]; break; default: error = ENOTTY; } return error; } static void acdstrategy(struct bio *bp) { struct acd_softc *cdp = bp->bio_dev->si_drv1; int s; if (cdp->atp->flags & ATAPI_F_DETACHING) { biofinish(bp, NULL, ENXIO); return; } /* if it's a null transfer, return immediatly. */ if (bp->bio_bcount == 0) { bp->bio_resid = 0; biodone(bp); return; } bp->bio_pblkno = bp->bio_blkno; bp->bio_resid = bp->bio_bcount; s = splbio(); bioqdisksort(&cdp->queue, bp); ata_start(cdp->atp->controller); splx(s); } void acd_start(struct atapi_softc *atp) { struct acd_softc *cdp = atp->driver; struct bio *bp = bioq_first(&cdp->queue); u_int32_t lba, lastlba, count; int8_t ccb[16]; int track, blocksize; if (cdp->changer_info) { int i; cdp = cdp->driver[cdp->changer_info->current_slot]; bp = bioq_first(&cdp->queue); /* check for work pending on any other slot */ for (i = 0; i < cdp->changer_info->slots; i++) { if (i == cdp->changer_info->current_slot) continue; if (bioq_first(&(cdp->driver[i]->queue))) { if (!bp || time_second > (cdp->timestamp + 10)) { acd_select_slot(cdp->driver[i]); return; } } } } if (!bp) return; bioq_remove(&cdp->queue, bp); /* reject all queued entries if media changed */ if (cdp->atp->flags & ATAPI_F_MEDIA_CHANGED) { -printf("reject due to media change\n"); biofinish(bp, NULL, EIO); return; } bzero(ccb, sizeof(ccb)); track = (bp->bio_dev->si_udev & 0x00ff0000) >> 16; 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; } if (bp->bio_bcount % blocksize != 0) { biofinish(bp, NULL, EINVAL); return; } count = bp->bio_bcount / 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) { bp->bio_resid = bp->bio_bcount; biodone(bp); 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; devstat_start_transaction(cdp->stats); atapi_queue_cmd(cdp->atp, ccb, bp->bio_data, count * blocksize, bp->bio_cmd == BIO_READ ? ATPR_F_READ : 0, (ccb[0] == ATAPI_WRITE_BIG) ? 60 : 30, acd_done, bp); } static int acd_done(struct atapi_request *request) { struct bio *bp = request->driver; struct acd_softc *cdp = request->device->driver; if (request->error) { bp->bio_error = request->error; bp->bio_flags |= BIO_ERROR; } else bp->bio_resid = bp->bio_bcount - request->donecount; biofinish(bp, cdp->stats, 0); return 0; } static void acd_read_toc(struct acd_softc *cdp) { struct acd_devlist *entry; int track, ntracks, len; u_int32_t sizes[2]; int8_t ccb[16]; bzero(&cdp->toc, sizeof(cdp->toc)); bzero(ccb, sizeof(ccb)); if (atapi_test_ready(cdp->atp) != 0) return; cdp->atp->flags &= ~ATAPI_F_MEDIA_CHANGED; len = sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry); ccb[0] = ATAPI_READ_TOC; ccb[7] = len>>8; ccb[8] = len; if (atapi_queue_cmd(cdp->atp, ccb, (caddr_t)&cdp->toc, len, ATPR_F_READ | ATPR_F_QUIET, 30, NULL, NULL)) { 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 (atapi_queue_cmd(cdp->atp, ccb, (caddr_t)&cdp->toc, len, ATPR_F_READ | ATPR_F_QUIET, 30, NULL, NULL)) { 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; #if 0 cdp->disk_size = ntohl(cdp->toc.tab[cdp->toc.hdr.ending_track].addr.lba); #else bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_READ_CAPACITY; if (atapi_queue_cmd(cdp->atp, ccb, (caddr_t)sizes, sizeof(sizes), ATPR_F_READ | ATPR_F_QUIET, 30, NULL, NULL)) { bzero(&cdp->toc, sizeof(cdp->toc)); return; } cdp->disk_size = ntohl(sizes[0]) + 1; #endif bzero(&cdp->disklabel, sizeof(struct disklabel)); strncpy(cdp->disklabel.d_typename, " ", sizeof(cdp->disklabel.d_typename)); strncpy(cdp->disklabel.d_typename, cdp->atp->controller->dev_name[ATA_DEV(cdp->atp->unit)], min(strlen(cdp->atp->controller->dev_name[ATA_DEV(cdp->atp->unit)]), sizeof(cdp->disklabel.d_typename) - 1)); strncpy(cdp->disklabel.d_packname, "unknown ", sizeof(cdp->disklabel.d_packname)); cdp->disklabel.d_secsize = cdp->block_size; cdp->disklabel.d_nsectors = 100; cdp->disklabel.d_ntracks = 1; cdp->disklabel.d_ncylinders = (cdp->disk_size / 100) + 1; cdp->disklabel.d_secpercyl = 100; cdp->disklabel.d_secperunit = cdp->disk_size; cdp->disklabel.d_rpm = 300; cdp->disklabel.d_interleave = 1; cdp->disklabel.d_flags = D_REMOVABLE; cdp->disklabel.d_npartitions = 1; cdp->disklabel.d_partitions[0].p_offset = 0; cdp->disklabel.d_partitions[0].p_size = cdp->disk_size; cdp->disklabel.d_partitions[0].p_fstype = FS_BSDFFS; cdp->disklabel.d_magic = DISKMAGIC; cdp->disklabel.d_magic2 = DISKMAGIC; cdp->disklabel.d_checksum = dkcksum(&cdp->disklabel); while ((entry = TAILQ_FIRST(&cdp->dev_list))) { destroy_dev(entry->dev); TAILQ_REMOVE(&cdp->dev_list, entry, chain); free(entry, M_ACD); } for (track = 1; track <= ntracks; track ++) { char name[16]; sprintf(name, "acd%dt%d", cdp->lun, track); entry = malloc(sizeof(struct acd_devlist), M_ACD, M_NOWAIT | M_ZERO); entry->dev = make_dev(&acd_cdevsw, (cdp->lun << 3) | (track << 16), 0, 0, 0644, name, NULL); entry->dev->si_drv1 = cdp->dev1->si_drv1; TAILQ_INSERT_TAIL(&cdp->dev_list, entry, chain); } #ifdef ACD_DEBUG if (cdp->disk_size && cdp->toc.hdr.ending_track) { ata_printf(cdp->atp->controller, cdp->atp->unit, "(%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 / 512); else printf("%d:%d audio\n", cdp->disk_size / 75 / 60, cdp->disk_size / 75 % 60); } #endif } static int acd_play(struct acd_softc *cdp, int start, int end) { int8_t ccb[16]; bzero(ccb, sizeof(ccb)); #if 1 ccb[0] = ATAPI_PLAY_MSF; lba2msf(start, &ccb[3], &ccb[4], &ccb[5]); lba2msf(end, &ccb[6], &ccb[7], &ccb[8]); #else ccb[0] = ATAPI_PLAY_12; ccb[2] = start>>24; ccb[3] = start>>16; ccb[4] = start>>8; ccb[5] = start; ccb[6] = (end - start)>>24; ccb[7] = (end - start)>>16; ccb[8] = (end - start)>>8; ccb[9] = (end - start); #endif return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, 10, NULL, NULL); } static int acd_setchan(struct acd_softc *cdp, u_int8_t c0, u_int8_t c1, u_int8_t c2, u_int8_t c3) { int error; if ((error = acd_mode_sense(cdp, 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(cdp, (caddr_t)&cdp->au, sizeof(cdp->au)); } static int acd_select_done1(struct atapi_request *request) { struct acd_softc *cdp = request->driver; cdp->changer_info->current_slot = cdp->slot; cdp->driver[cdp->changer_info->current_slot]->timestamp = time_second; wakeup(&cdp->changer_info); return 0; } static int acd_select_done(struct atapi_request *request) { struct acd_softc *cdp = request->driver; int8_t ccb[16] = { ATAPI_LOAD_UNLOAD, 0, 0, 0, 3, 0, 0, 0, cdp->slot, 0, 0, 0, 0, 0, 0, 0 }; /* load the wanted slot */ atapi_queue_cmd(cdp->atp, ccb, NULL, 0, ATPR_F_AT_HEAD, 30, acd_select_done1, cdp); return 0; } static void acd_select_slot(struct acd_softc *cdp) { int8_t ccb[16] = { ATAPI_LOAD_UNLOAD, 0, 0, 0, 2, 0, 0, 0, cdp->changer_info->current_slot, 0, 0, 0, 0, 0, 0, 0 }; /* unload the current media from player */ atapi_queue_cmd(cdp->atp, ccb, NULL, 0, ATPR_F_AT_HEAD, 30, acd_select_done, cdp); } static int acd_init_writer(struct acd_softc *cdp, int test_write) { struct write_param param; int8_t ccb[16]; int error; bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_REZERO; atapi_queue_cmd(cdp->atp, ccb, NULL, 0, ATPR_F_QUIET, 60, NULL, NULL); ccb[0] = ATAPI_SEND_OPC_INFO; ccb[1] = 0x01; atapi_queue_cmd(cdp->atp, ccb, NULL, 0, ATPR_F_QUIET, 30, NULL, NULL); if ((error = acd_mode_sense(cdp, ATAPI_CDROM_WRITE_PARAMETERS_PAGE, (caddr_t)¶m, sizeof(param)))) return error; param.data_length = 0; param.page_code = ATAPI_CDROM_WRITE_PARAMETERS_PAGE; param.page_length = 0x32; param.test_write = test_write ? 1 : 0; param.write_type = CDR_WTYPE_SESSION; param.session_type = CDR_SESS_NONE; param.fp = 0; param.packet_size = 0; param.track_mode = CDR_TMODE_AUDIO; param.datablock_type = CDR_DB_RAW; param.session_format = CDR_SESS_CDROM; return acd_mode_select(cdp, (caddr_t)¶m, param.page_length + 10); } static int acd_fixate(struct acd_softc *cdp, int multisession) { 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; struct write_param param; if ((error = acd_mode_sense(cdp, ATAPI_CDROM_WRITE_PARAMETERS_PAGE, (caddr_t)¶m, 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(cdp, (caddr_t)¶m, param.page_length + 10))) return error; error = atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, 30, NULL, NULL); if (error) return error; /* some drives just return ready, wait for the expected fixate time */ if ((error = atapi_test_ready(cdp->atp)) != EBUSY) { timeout = timeout / (cdp->cap.cur_write_speed / 177); tsleep(&error, PRIBIO, "acdfix", timeout * hz / 2); return atapi_test_ready(cdp->atp); } while (timeout-- > 0) { if ((error = atapi_test_ready(cdp->atp)) != EBUSY) return error; tsleep(&error, PRIBIO, "acdcld", hz/2); } return EIO; } static int acd_init_track(struct acd_softc *cdp, struct cdr_track *track) { struct write_param param; int error; if ((error = acd_mode_sense(cdp, ATAPI_CDROM_WRITE_PARAMETERS_PAGE, (caddr_t)¶m, 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.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; } return acd_mode_select(cdp, (caddr_t)¶m, param.page_length + 10); } static int acd_flush(struct acd_softc *cdp) { int8_t ccb[16] = { ATAPI_SYNCHRONIZE_CACHE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, ATPR_F_QUIET, 60, NULL,NULL); } static int acd_read_track_info(struct acd_softc *cdp, 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 = atapi_queue_cmd(cdp->atp, ccb, (caddr_t)info, sizeof(*info), ATPR_F_READ, 30, NULL, NULL))) 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(struct acd_softc *cdp, int *finished) { int8_t ccb[16] = { ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; char tmp[8]; if (atapi_test_ready(cdp->atp) != EBUSY) { if (atapi_queue_cmd(cdp->atp, ccb, tmp, sizeof(tmp), ATPR_F_READ, 30, NULL, NULL) != EBUSY) { *finished = 100; return 0; } } if (cdp->atp->sense.sksv) *finished = ((cdp->atp->sense.sk_specific2 | (cdp->atp->sense.sk_specific1 << 8)) * 100) / 65535; else *finished = 0; return 0; } static int acd_send_cue(struct acd_softc *cdp, struct cdr_cuesheet *cuesheet) { 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; #ifdef ACD_DEBUG int i; #endif buffer = malloc(cuesheet->len, M_ACD, M_NOWAIT); if (!buffer) return ENOMEM; if ((error = copyin(cuesheet->entries, buffer, cuesheet->len))) return error; #ifdef ACD_DEBUG printf("acd: cuesheet lenght = %d\n", cuesheet->len); for (i=0; ilen; i++) if (i%8) printf(" %02x", buffer[i]); else printf("\n%02x", buffer[i]); printf("\n"); #endif error = atapi_queue_cmd(cdp->atp, ccb, buffer, cuesheet->len, 0, 30, NULL, NULL); free(buffer, M_ACD); return error; } static int acd_report_key(struct acd_softc *cdp, struct dvd_authinfo *ai) { - struct { - u_int16_t length; - u_char reserved[2]; - u_char data[12]; - } d; + struct dvd_miscauth *d; 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; - bzero(&d, sizeof(d)); - d.length = htons(length - 2); - error = atapi_queue_cmd(cdp->atp, ccb, (caddr_t)&d, length, + + d = malloc(length, M_ACD, M_NOWAIT | M_ZERO); + d->length = htons(length - 2); + + error = atapi_queue_cmd(cdp->atp, ccb, (caddr_t)d, length, ai->format == DVD_INVALIDATE_AGID ? 0 : ATPR_F_READ, 10, NULL, NULL); - if (error) + if (error) { + free(d, M_ACD); return error; + } switch (ai->format) { case DVD_REPORT_AGID: - ai->agid = d.data[3] >> 6; + ai->agid = d->data[3] >> 6; break; case DVD_REPORT_CHALLENGE: - bcopy(&d.data[0], &ai->keychal[0], 10); + bcopy(&d->data[0], &ai->keychal[0], 10); break; case DVD_REPORT_KEY1: - bcopy(&d.data[0], &ai->keychal[0], 5); + 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); + 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; + 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]; + 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: - return EINVAL; + error = EINVAL; } - return 0; + free(d, M_ACD); + return error; } static int acd_send_key(struct acd_softc *cdp, struct dvd_authinfo *ai) { - struct { - u_int16_t length; - u_char reserved[2]; - u_char data[12]; - } d; + struct dvd_miscauth *d; int16_t length; int8_t ccb[16]; + int error; - bzero(&d, sizeof(d)); - switch (ai->format) { case DVD_SEND_CHALLENGE: length = 16; - bcopy(ai->keychal, &d.data[0], 10); + d = malloc(length, M_ACD, M_NOWAIT | M_ZERO); + bcopy(ai->keychal, &d->data[0], 10); break; case DVD_SEND_KEY2: length = 12; - bcopy(&ai->keychal[0], &d.data[0], 5); + d = malloc(length, M_ACD, M_NOWAIT | M_ZERO); + bcopy(&ai->keychal[0], &d->data[0], 5); break; case DVD_SEND_RPC: length = 8; - d.data[0] = ai->region; + d = malloc(length, M_ACD, M_NOWAIT | M_ZERO); + 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); - return atapi_queue_cmd(cdp->atp, ccb, (caddr_t)&d, length, 0, - 10, NULL, NULL); + d->length = htons(length - 2); + error = atapi_queue_cmd(cdp->atp, ccb, (caddr_t)d, length, 0, + 10, NULL, NULL); + free(d, M_ACD); + return error; } static int acd_read_structure(struct acd_softc *cdp, struct dvd_struct *s) { - struct { - u_int16_t length; - u_char reserved[2]; - u_char data[2048]; - } d; + struct dvd_miscauth *d; u_int16_t length; - int error = 0; int8_t ccb[16]; + int error = 0; - bzero(&d, sizeof(d)); - 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; } + d = malloc(length, M_ACD, M_NOWAIT | M_ZERO); + 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; - d.length = htons(length - 2); - error = atapi_queue_cmd(cdp->atp, ccb, (caddr_t)&d, length, ATPR_F_READ, + error = atapi_queue_cmd(cdp->atp, ccb, (caddr_t)d, length, ATPR_F_READ, 30, NULL, NULL); - if (error) + 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; + 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[0]; + s->cpst = d->data[0]; + s->rmi = d->data[0]; break; case DVD_STRUCT_DISCKEY: - bcopy(&d.data[0], &s->data[0], 2048); + 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); + 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); + s->length = ntohs(d->length); + bcopy(&d->data[0], &s->data[0], s->length); break; default: - return EINVAL; + error = EINVAL; } - return 0; + free(d, M_ACD); + return error; } static int acd_eject(struct acd_softc *cdp, int close) { int error; if ((error = acd_start_stop(cdp, 0)) == EBUSY) { if (!close) return 0; if ((error = acd_start_stop(cdp, 3))) return error; acd_read_toc(cdp); acd_prevent_allow(cdp, 1); cdp->flags |= F_LOCKED; return 0; } if (error) return error; if (close) return 0; acd_prevent_allow(cdp, 0); cdp->flags &= ~F_LOCKED; cdp->atp->flags |= ATAPI_F_MEDIA_CHANGED; return acd_start_stop(cdp, 2); } static int acd_blank(struct acd_softc *cdp, int blanktype) { int8_t ccb[16] = { ATAPI_BLANK, 0x10 | (blanktype & 0x7), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; cdp->atp->flags |= ATAPI_F_MEDIA_CHANGED; return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, 30, NULL, NULL); } static int acd_prevent_allow(struct acd_softc *cdp, 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 atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, 30, NULL, NULL); } static int acd_start_stop(struct acd_softc *cdp, 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 atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, 30, NULL, NULL); } static int acd_pause_resume(struct acd_softc *cdp, int pause) { int8_t ccb[16] = { ATAPI_PAUSE, 0, 0, 0, 0, 0, 0, 0, pause, 0, 0, 0, 0, 0, 0, 0 }; return atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, 30, NULL, NULL); } static int acd_mode_sense(struct acd_softc *cdp, 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 = atapi_queue_cmd(cdp->atp, ccb, pagebuf, pagesize, ATPR_F_READ, 10, NULL, NULL); #ifdef ACD_DEBUG atapi_dump("acd: mode sense ", pagebuf, pagesize); #endif return error; } static int acd_mode_select(struct acd_softc *cdp, 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 }; #ifdef ACD_DEBUG ata_printf(cdp->atp->controller, cdp->atp->unit, "modeselect pagesize=%d\n", pagesize); atapi_dump("mode select ", pagebuf, pagesize); #endif return atapi_queue_cmd(cdp->atp, ccb, pagebuf, pagesize, 0, 30, NULL, NULL); } static int acd_set_speed(struct acd_softc *cdp, 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 = atapi_queue_cmd(cdp->atp, ccb, NULL, 0, 0, 30, NULL, NULL); if (!error) acd_get_cap(cdp); return error; } static void acd_get_cap(struct acd_softc *cdp) { int retry = 5; /* get drive capabilities, some drives needs this repeated */ while (retry-- && acd_mode_sense(cdp, ATAPI_CDROM_CAP_PAGE, (caddr_t)&cdp->cap, sizeof(cdp->cap))) 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); } Index: head/sys/dev/ata/atapi-cd.h =================================================================== --- head/sys/dev/ata/atapi-cd.h (revision 83727) +++ head/sys/dev/ata/atapi-cd.h (revision 83728) @@ -1,332 +1,339 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* 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_int8_t unused[2]; 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_int8_t read_cdr :1; /* supports CD-R read */ u_int8_t read_cdrw :1; /* supports CD-RW read */ u_int8_t read_packet :1; /* supports reading packet tracks */ u_int8_t read_dvdrom :1; /* supports DVD-ROM read */ u_int8_t read_dvdr :1; /* supports DVD-R read */ u_int8_t read_dvdram :1; /* supports DVD-RAM read */ u_int8_t reserved2_67 :2; u_int8_t write_cdr :1; /* supports CD-R write */ u_int8_t write_cdrw :1; /* supports CD-RW write */ u_int8_t test_write :1; /* supports test writing */ u_int8_t reserved3_3 :1; u_int8_t write_dvdr :1; /* supports DVD-R write */ u_int8_t write_dvdram :1; /* supports DVD-RAM write */ u_int8_t reserved3_67 :2; u_int8_t audio_play :1; /* audio play supported */ u_int8_t composite :1; /* composite audio/video supported */ u_int8_t dport1 :1; /* digital audio on port 1 */ u_int8_t dport2 :1; /* digital audio on port 2 */ u_int8_t mode2_form1 :1; /* mode 2 form 1 (XA) read */ u_int8_t mode2_form2 :1; /* mode 2 form 2 format */ u_int8_t multisession :1; /* multi-session photo-CD */ u_int8_t burnproof :1; /* supports burnproof */ u_int8_t cd_da :1; /* audio-CD read supported */ u_int8_t cd_da_stream :1; /* CD-DA streaming */ u_int8_t rw :1; /* combined R-W subchannels */ u_int8_t rw_corr :1; /* R-W subchannel data corrected */ u_int8_t c2 :1; /* C2 error pointers supported */ u_int8_t isrc :1; /* can return the ISRC info */ u_int8_t upc :1; /* can return the catalog number UPC */ u_int8_t :1; u_int8_t lock :1; /* can be locked */ u_int8_t locked :1; /* current lock state */ u_int8_t prevent :1; /* prevent jumper installed */ u_int8_t eject :1; /* can eject */ u_int8_t :1; u_int8_t mech :3; /* loading mechanism type */ #define MST_MECH_CADDY 0 #define MST_MECH_TRAY 1 #define MST_MECH_POPUP 2 #define MST_MECH_CHANGER 4 #define MST_MECH_CARTRIDGE 5 u_int8_t sep_vol :1; /* independent volume of channels */ u_int8_t sep_mute :1; /* independent mute of channels */ u_int8_t:6; 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 bckf :1; /* data valid on failing edge of BCK */ u_int8_t rch :1; /* high LRCK indicates left channel */ u_int8_t lsbf :1; /* set if LSB first */ u_int8_t dlen :2; #define MST_DLEN_32 0 #define MST_DLEN_16 1 #define MST_DLEN_24 2 #define MST_DLEN_24_I2S 3 u_int8_t :3; 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; }; /* 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 */ #define CH_READY 0 #define CH_LOADING 1 #define CH_UNLOADING 2 #define CH_INITIALIZING 3 u_int8_t fault :1; /* fault in last operation */ u_int8_t reserved0 :5; u_int8_t cd_state :3; /* current mechanism state */ #define CD_IDLE 0 #define CD_AUDIO_ACTIVE 1 #define CD_AUDIO_SCAN 2 #define CD_HOST_ACTIVE 3 #define CD_NO_STATE 7 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 reserved2_5 :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 reserved5; 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; } __attribute__((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 */ }; struct acd_devlist { dev_t dev; TAILQ_ENTRY(acd_devlist) chain; /* list management */ }; /* Structure describing an ATAPI CDROM device */ struct acd_softc { struct atapi_softc *atp; /* controller structure */ int lun; /* logical device unit */ int flags; /* device state flags */ #define F_LOCKED 0x0001 /* this unit is locked */ struct bio_queue_head queue; /* queue of i/o requests */ TAILQ_HEAD(, acd_devlist) dev_list; /* list of "track" devices */ 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 { /* subchannel info */ u_int8_t void0; u_int8_t audio_status; u_int16_t data_length; u_int8_t data_format; u_int8_t control; u_int8_t track; u_int8_t indx; u_int32_t abslba; u_int32_t rellba; } subchan; 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 */ int disk_size; /* size of current media */ int block_size; /* blocksize currently used */ struct disklabel disklabel; /* fake disk label */ struct devstat *stats; /* devstat entry */ dev_t dev1, dev2; /* device place holders */ }; Index: head/sys/dev/ata/atapi-fd.c =================================================================== --- head/sys/dev/ata/atapi-fd.c (revision 83727) +++ head/sys/dev/ata/atapi-fd.c (revision 83728) @@ -1,451 +1,443 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* device structures */ static d_open_t afdopen; static d_close_t afdclose; static d_ioctl_t afdioctl; static d_strategy_t afdstrategy; static struct cdevsw afd_cdevsw = { /* open */ afdopen, /* close */ afdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ afdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ afdstrategy, /* name */ "afd", /* maj */ 118, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK | D_TRACKCLOSE, }; static struct cdevsw afddisk_cdevsw; /* prototypes */ static int afd_sense(struct afd_softc *); static void afd_describe(struct afd_softc *); static int afd_partial_done(struct atapi_request *); static int afd_done(struct atapi_request *); static int afd_eject(struct afd_softc *, int); static int afd_start_stop(struct afd_softc *, int); static int afd_prevent_allow(struct afd_softc *, int); /* internal vars */ static u_int32_t afd_lun_map = 0; static MALLOC_DEFINE(M_AFD, "AFD driver", "ATAPI floppy driver buffers"); int afdattach(struct atapi_softc *atp) { struct afd_softc *fdp; dev_t dev; - char name[16]; fdp = malloc(sizeof(struct afd_softc), M_AFD, M_NOWAIT | M_ZERO); if (!fdp) { ata_printf(atp->controller, atp->unit, "out of memory\n"); return -1; } fdp->atp = atp; fdp->lun = ata_get_lun(&afd_lun_map); - sprintf(name, "afd%d", fdp->lun); - ata_set_name(atp->controller, atp->unit, name); + ata_set_name(atp->controller, atp->unit, "afd", fdp->lun); bioq_init(&fdp->queue); if (afd_sense(fdp)) { free(fdp, M_AFD); return -1; } if (!strncmp(ATA_PARAM(fdp->atp->controller, fdp->atp->unit)->model, "IOMEGA ZIP", 10)) fdp->transfersize = 64; devstat_add_entry(&fdp->stats, "afd", fdp->lun, DEV_BSIZE, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE, DEVSTAT_PRIORITY_WFD); dev = disk_create(fdp->lun, &fdp->disk, 0, &afd_cdevsw, &afddisk_cdevsw); dev->si_drv1 = fdp; dev->si_iosize_max = 252 * DEV_BSIZE; fdp->dev = dev; fdp->atp->flags |= ATAPI_F_MEDIA_CHANGED; fdp->atp->driver = fdp; afd_describe(fdp); return 0; } void afddetach(struct atapi_softc *atp) { struct afd_softc *fdp = atp->driver; struct bio *bp; while ((bp = bioq_first(&fdp->queue))) { biofinish(bp, NULL, ENXIO); } disk_invalidate(&fdp->disk); disk_destroy(fdp->dev); devstat_remove_entry(&fdp->stats); ata_free_name(atp->controller, atp->unit); ata_free_lun(&afd_lun_map, fdp->lun); free(fdp, M_AFD); } static int afd_sense(struct afd_softc *fdp) { - int8_t buffer[256]; int8_t ccb[16] = { ATAPI_MODE_SENSE_BIG, 0, ATAPI_REWRITEABLE_CAP_PAGE, - 0, 0, 0, 0, sizeof(buffer)>>8, sizeof(buffer) & 0xff, - 0, 0, 0, 0, 0, 0, 0 }; + 0, 0, 0, 0, sizeof(struct afd_cappage) >> 8, + sizeof(struct afd_cappage) & 0xff, 0, 0, 0, 0, 0, 0, 0 }; int count, error = 0; /* The IOMEGA Clik! doesn't support reading the cap page, fake it */ if (!strncmp(ATA_PARAM(fdp->atp->controller, fdp->atp->unit)->model, "IOMEGA Clik!", 12)) { fdp->transfersize = 64; fdp->cap.transfer_rate = 500; fdp->cap.heads = 1; fdp->cap.sectors = 2; fdp->cap.cylinders = 39441; fdp->cap.sector_size = 512; atapi_test_ready(fdp->atp); return 0; } - bzero(buffer, sizeof(buffer)); /* get drive capabilities, some drives needs this repeated */ for (count = 0 ; count < 5 ; count++) { - if (!(error = atapi_queue_cmd(fdp->atp, ccb, buffer, sizeof(buffer), + if (!(error = atapi_queue_cmd(fdp->atp, ccb, (caddr_t)&fdp->cap, + sizeof(struct afd_cappage), ATPR_F_READ, 30, NULL, NULL))) break; } - if (error) - return error; - bcopy(buffer, &fdp->header, sizeof(struct afd_header)); - bcopy(buffer + sizeof(struct afd_header), &fdp->cap, - sizeof(struct afd_cappage)); - if (fdp->cap.page_code != ATAPI_REWRITEABLE_CAP_PAGE) + if (error || fdp->cap.page_code != ATAPI_REWRITEABLE_CAP_PAGE) return 1; fdp->cap.cylinders = ntohs(fdp->cap.cylinders); fdp->cap.sector_size = ntohs(fdp->cap.sector_size); fdp->cap.transfer_rate = ntohs(fdp->cap.transfer_rate); return 0; } static void afd_describe(struct afd_softc *fdp) { if (bootverbose) { ata_printf(fdp->atp->controller, fdp->atp->unit, "<%.40s/%.8s> rewriteable drive at ata%d as %s\n", ATA_PARAM(fdp->atp->controller, fdp->atp->unit)->model, ATA_PARAM(fdp->atp->controller, fdp->atp->unit)->revision, device_get_unit(fdp->atp->controller->dev), (fdp->atp->unit == ATA_MASTER) ? "master" : "slave"); ata_printf(fdp->atp->controller, fdp->atp->unit, "%luMB (%u sectors), %u cyls, %u heads, %u S/T, %u B/S\n", (fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors) / ((1024L * 1024L) / fdp->cap.sector_size), fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors, fdp->cap.cylinders, fdp->cap.heads, fdp->cap.sectors, fdp->cap.sector_size); ata_printf(fdp->atp->controller, fdp->atp->unit, "%dKB/s,", fdp->lun, fdp->cap.transfer_rate/8); if (fdp->transfersize) printf(" transfer limit %d blks,", fdp->transfersize); printf(" %s\n", ata_mode2str(fdp->atp->controller->mode[ ATA_DEV(fdp->atp->unit)])); - if (fdp->header.medium_type) { + if (fdp->cap.medium_type) { ata_printf(fdp->atp->controller, fdp->atp->unit, "Medium: "); - switch (fdp->header.medium_type) { + switch (fdp->cap.medium_type) { case MFD_2DD: printf("720KB DD disk"); break; case MFD_HD_12: printf("1.2MB HD disk"); break; case MFD_HD_144: printf("1.44MB HD disk"); break; case MFD_UHD: printf("120MB UHD disk"); break; default: - printf("Unknown (0x%x)", fdp->header.medium_type); + printf("Unknown (0x%x)", fdp->cap.medium_type); } - if (fdp->header.wp) printf(", writeprotected"); + if (fdp->cap.wp) printf(", writeprotected"); } printf("\n"); } else { ata_printf(fdp->atp->controller, fdp->atp->unit, "%luMB <%.40s> [%d/%d/%d] at ata%d-%s %s\n", - (fdp->cap.cylinders*fdp->cap.heads*fdp->cap.sectors) / + (fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors) / ((1024L * 1024L) / fdp->cap.sector_size), ATA_PARAM(fdp->atp->controller, fdp->atp->unit)->model, fdp->cap.cylinders, fdp->cap.heads, fdp->cap.sectors, device_get_unit(fdp->atp->controller->dev), (fdp->atp->unit == ATA_MASTER) ? "master" : "slave", ata_mode2str(fdp->atp->controller-> mode[ATA_DEV(fdp->atp->unit)])); } } static int afdopen(dev_t dev, int flags, int fmt, struct thread *td) { struct afd_softc *fdp = dev->si_drv1; struct disklabel *label = &fdp->disk.d_label; atapi_test_ready(fdp->atp); if (count_dev(dev) == 1) afd_prevent_allow(fdp, 1); if (afd_sense(fdp)) ata_printf(fdp->atp->controller, fdp->atp->unit, "sense media type failed\n"); fdp->atp->flags &= ~ATAPI_F_MEDIA_CHANGED; bzero(label, sizeof *label); label->d_secsize = fdp->cap.sector_size; label->d_nsectors = fdp->cap.sectors; label->d_ntracks = fdp->cap.heads; label->d_ncylinders = fdp->cap.cylinders; label->d_secpercyl = fdp->cap.sectors * fdp->cap.heads; label->d_secperunit = label->d_secpercyl * fdp->cap.cylinders; return 0; } static int afdclose(dev_t dev, int flags, int fmt, struct thread *td) { struct afd_softc *fdp = dev->si_drv1; if (count_dev(dev) == 1) afd_prevent_allow(fdp, 0); return 0; } static int afdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { struct afd_softc *fdp = dev->si_drv1; switch (cmd) { case CDIOCEJECT: if (count_dev(dev) > 1) return EBUSY; return afd_eject(fdp, 0); case CDIOCCLOSE: if (count_dev(dev) > 1) return 0; return afd_eject(fdp, 1); default: return ENOIOCTL; } } static void afdstrategy(struct bio *bp) { struct afd_softc *fdp = bp->bio_dev->si_drv1; int s; if (fdp->atp->flags & ATAPI_F_DETACHING) { biofinish(bp, NULL, ENXIO); return; } s = splbio(); bioqdisksort(&fdp->queue, bp); ata_start(fdp->atp->controller); splx(s); } void afd_start(struct atapi_softc *atp) { struct afd_softc *fdp = atp->driver; struct bio *bp = bioq_first(&fdp->queue); u_int32_t lba; u_int16_t count; int8_t ccb[16]; caddr_t data_ptr; if (!bp) return; bioq_remove(&fdp->queue, bp); /* should reject all queued entries if media have changed. */ if (fdp->atp->flags & ATAPI_F_MEDIA_CHANGED) { biofinish(bp, NULL, EIO); return; } lba = bp->bio_pblkno; count = bp->bio_bcount / fdp->cap.sector_size; data_ptr = bp->bio_data; bp->bio_resid = 0; bzero(ccb, sizeof(ccb)); if (bp->bio_cmd == BIO_READ) ccb[0] = ATAPI_READ_BIG; else ccb[0] = ATAPI_WRITE_BIG; devstat_start_transaction(&fdp->stats); while (fdp->transfersize && (count > fdp->transfersize)) { ccb[2] = lba>>24; ccb[3] = lba>>16; ccb[4] = lba>>8; ccb[5] = lba; ccb[7] = fdp->transfersize>>8; ccb[8] = fdp->transfersize; atapi_queue_cmd(fdp->atp, ccb, data_ptr, fdp->transfersize * fdp->cap.sector_size, (bp->bio_cmd == BIO_READ) ? ATPR_F_READ : 0, 30, afd_partial_done, bp); count -= fdp->transfersize; lba += fdp->transfersize; data_ptr += fdp->transfersize * fdp->cap.sector_size; } ccb[2] = lba>>24; ccb[3] = lba>>16; ccb[4] = lba>>8; ccb[5] = lba; ccb[7] = count>>8; ccb[8] = count; atapi_queue_cmd(fdp->atp, ccb, data_ptr, count * fdp->cap.sector_size, (bp->bio_cmd == BIO_READ) ? ATPR_F_READ : 0, 30, afd_done, bp); } static int afd_partial_done(struct atapi_request *request) { struct bio *bp = request->driver; if (request->error) { bp->bio_error = request->error; bp->bio_flags |= BIO_ERROR; } bp->bio_resid += request->bytecount; return 0; } static int afd_done(struct atapi_request *request) { struct bio *bp = request->driver; struct afd_softc *fdp = request->device->driver; if (request->error || (bp->bio_flags & BIO_ERROR)) { bp->bio_error = request->error; bp->bio_flags |= BIO_ERROR; } else bp->bio_resid += (bp->bio_bcount - request->donecount); biofinish(bp, &fdp->stats, 0); return 0; } static int afd_eject(struct afd_softc *fdp, int close) { int error; if ((error = afd_start_stop(fdp, 0)) == EBUSY) { if (!close) return 0; if ((error = afd_start_stop(fdp, 3))) return error; return afd_prevent_allow(fdp, 1); } if (error) return error; if (close) return 0; if ((error = afd_prevent_allow(fdp, 0))) return error; fdp->atp->flags |= ATAPI_F_MEDIA_CHANGED; return afd_start_stop(fdp, 2); } static int afd_start_stop(struct afd_softc *fdp, 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 atapi_queue_cmd(fdp->atp, ccb, NULL, 0, 0, 30, NULL, NULL); } static int afd_prevent_allow(struct afd_softc *fdp, int lock) { int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (!strncmp(ATA_PARAM(fdp->atp->controller, fdp->atp->unit)->model, "IOMEGA Clik!", 12)) return 0; return atapi_queue_cmd(fdp->atp, ccb, NULL, 0, 0, 30, NULL, NULL); } Index: head/sys/dev/ata/atapi-fd.h =================================================================== --- head/sys/dev/ata/atapi-fd.h (revision 83727) +++ head/sys/dev/ata/atapi-fd.h (revision 83728) @@ -1,84 +1,81 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -/* MODE SENSE parameter header */ -struct afd_header { +/* ATAPI Rewriteable drive Capabilities and Mechanical Status Page */ +struct afd_cappage { 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]; -}; -/* ATAPI Rewriteable drive Capabilities and Mechanical Status Page */ -struct afd_cappage { + /* 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_softc { struct atapi_softc *atp; /* controller structure */ int lun; /* logical device unit */ int transfersize; /* max size of each transfer */ struct bio_queue_head queue; /* queue of i/o requests */ - struct afd_header header; /* capabilities page info */ struct afd_cappage cap; /* capabilities page info */ struct disk disk; /* virtual drives */ struct devstat stats; dev_t dev; /* device place holder */ }; Index: head/sys/dev/ata/atapi-tape.c =================================================================== --- head/sys/dev/ata/atapi-tape.c (revision 83727) +++ head/sys/dev/ata/atapi-tape.c (revision 83728) @@ -1,666 +1,664 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* device structures */ static d_open_t astopen; static d_close_t astclose; static d_ioctl_t astioctl; static d_strategy_t aststrategy; static struct cdevsw ast_cdevsw = { /* open */ astopen, /* close */ astclose, /* read */ physread, /* write */ physwrite, /* ioctl */ astioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ aststrategy, /* name */ "ast", /* maj */ 119, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_TAPE | D_TRACKCLOSE, }; /* prototypes */ static int ast_sense(struct ast_softc *); static void ast_describe(struct ast_softc *); static int ast_done(struct atapi_request *); static int ast_mode_sense(struct ast_softc *, int, void *, int); static int ast_mode_select(struct ast_softc *, void *, int); static int ast_write_filemark(struct ast_softc *, u_int8_t); static int ast_read_position(struct ast_softc *, int, struct ast_readposition *); static int ast_space(struct ast_softc *, u_int8_t, int32_t); static int ast_locate(struct ast_softc *, int, u_int32_t); static int ast_prevent_allow(struct ast_softc *stp, int); static int ast_load_unload(struct ast_softc *, u_int8_t); static int ast_rewind(struct ast_softc *); static int ast_erase(struct ast_softc *); /* internal vars */ static u_int32_t ast_lun_map = 0; static u_int64_t ast_total = 0; static MALLOC_DEFINE(M_AST, "AST driver", "ATAPI tape driver buffers"); int astattach(struct atapi_softc *atp) { struct ast_softc *stp; struct ast_readposition position; dev_t dev; - char name[16]; static int ast_cdev_done = 0; if (!ast_cdev_done) { cdevsw_add(&ast_cdevsw); ast_cdev_done = 1; } stp = malloc(sizeof(struct ast_softc), M_AST, M_NOWAIT | M_ZERO); if (!stp) { ata_printf(atp->controller, atp->unit, "out of memory\n"); return -1; } stp->atp = atp; stp->lun = ata_get_lun(&ast_lun_map); - sprintf(name, "ast%d", stp->lun); - ata_set_name(atp->controller, atp->unit, name); + ata_set_name(atp->controller, atp->unit, "ast", stp->lun); bioq_init(&stp->queue); if (ast_sense(stp)) { free(stp, M_AST); return -1; } if (!strcmp(ATA_PARAM(stp->atp->controller, stp->atp->unit)->model, "OnStream DI-30")) { struct ast_transferpage transfer; struct ast_identifypage identify; stp->flags |= F_ONSTREAM; bzero(&transfer, sizeof(struct ast_transferpage)); ast_mode_sense(stp, ATAPI_TAPE_TRANSFER_PAGE, &transfer, sizeof(transfer)); bzero(&identify, sizeof(struct ast_identifypage)); ast_mode_sense(stp, ATAPI_TAPE_IDENTIFY_PAGE, &identify, sizeof(identify)); strncpy(identify.ident, "FBSD", 4); ast_mode_select(stp, &identify, sizeof(identify)); ast_read_position(stp, 0, &position); } devstat_add_entry(&stp->stats, "ast", stp->lun, DEV_BSIZE, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_SEQUENTIAL | DEVSTAT_TYPE_IF_IDE, DEVSTAT_PRIORITY_TAPE); dev = make_dev(&ast_cdevsw, dkmakeminor(stp->lun, 0, 0), UID_ROOT, GID_OPERATOR, 0640, "ast%d", stp->lun); dev->si_drv1 = stp; dev->si_iosize_max = 252 * DEV_BSIZE; stp->dev1 = dev; dev = make_dev(&ast_cdevsw, dkmakeminor(stp->lun, 0, 1), UID_ROOT, GID_OPERATOR, 0640, "nast%d", stp->lun); dev->si_drv1 = stp; dev->si_iosize_max = 252 * DEV_BSIZE; stp->dev2 = dev; stp->atp->flags |= ATAPI_F_MEDIA_CHANGED; stp->atp->driver = stp; ast_describe(stp); return 0; } void astdetach(struct atapi_softc *atp) { struct ast_softc *stp = atp->driver; struct bio *bp; while ((bp = bioq_first(&stp->queue))) { biofinish(bp, NULL, ENXIO); } destroy_dev(stp->dev1); destroy_dev(stp->dev2); devstat_remove_entry(&stp->stats); ata_free_name(atp->controller, atp->unit); ata_free_lun(&ast_lun_map, stp->lun); free(stp, M_AST); } static int ast_sense(struct ast_softc *stp) { int count, error = 0; /* get drive capabilities, some drives needs this repeated */ for (count = 0 ; count < 5 ; count++) { if (!(error = ast_mode_sense(stp, ATAPI_TAPE_CAP_PAGE, &stp->cap, sizeof(stp->cap)))) { 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 void ast_describe(struct ast_softc *stp) { if (bootverbose) { ata_printf(stp->atp->controller, stp->atp->unit, "<%.40s/%.8s> tape drive at ata%d as %s\n", ATA_PARAM(stp->atp->controller, stp->atp->unit)->model, ATA_PARAM(stp->atp->controller, stp->atp->unit)->revision, device_get_unit(stp->atp->controller->dev), (stp->atp->unit == ATA_MASTER) ? "master" : "slave"); ata_printf(stp->atp->controller, stp->atp->unit, "%dKB/s, ", stp->cap.max_speed); printf("transfer limit %d blk%s, ", stp->cap.ctl, (stp->cap.ctl > 1) ? "s" : ""); printf("%dKB buffer, ", (stp->cap.buffer_size * DEV_BSIZE) / 1024); printf("%s\n", ata_mode2str(stp->atp->controller->mode[ ATA_DEV(stp->atp->unit)])); ata_printf(stp->atp->controller, stp->atp->unit, "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 { ata_printf(stp->atp->controller, stp->atp->unit, "TAPE <%.40s> at ata%d-%s %s\n", ATA_PARAM(stp->atp->controller, stp->atp->unit)->model, device_get_unit(stp->atp->controller->dev), (stp->atp->unit == ATA_MASTER) ? "master" : "slave", ata_mode2str(stp->atp->controller-> mode[ATA_DEV(stp->atp->unit)])); } } static int astopen(dev_t dev, int flags, int fmt, struct thread *td) { struct ast_softc *stp = dev->si_drv1; if (!stp) return ENXIO; if (count_dev(dev) > 1) return EBUSY; atapi_test_ready(stp->atp); if (stp->cap.lock) ast_prevent_allow(stp, 1); if (ast_sense(stp)) ata_printf(stp->atp->controller, stp->atp->unit, "sense media type failed\n"); stp->atp->flags &= ~ATAPI_F_MEDIA_CHANGED; stp->flags &= ~(F_DATA_WRITTEN | F_FM_WRITTEN); ast_total = 0; return 0; } static int astclose(dev_t dev, int flags, int fmt, struct thread *td) { struct ast_softc *stp = dev->si_drv1; /* flush buffers, some drives fail here, they should report ctl = 0 */ if (stp->cap.ctl && (stp->flags & F_DATA_WRITTEN)) ast_write_filemark(stp, 0); /* write filemark if data written to tape */ if (!(stp->flags & F_ONSTREAM) && (stp->flags & (F_DATA_WRITTEN | F_FM_WRITTEN)) == F_DATA_WRITTEN) ast_write_filemark(stp, WF_WRITE); /* if minor is even rewind on close */ if (!(minor(dev) & 0x01)) ast_rewind(stp); if (stp->cap.lock && count_dev(dev) == 1) ast_prevent_allow(stp, 0); stp->flags &= F_CTL_WARN; #ifdef AST_DEBUG ata_printf(stp->atp->controller, stp->atp->unit, "%llu total bytes transferred\n", ast_total); #endif return 0; } static int astioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { struct ast_softc *stp = dev->si_drv1; int error = 0; switch (cmd) { case MTIOCGET: { struct mtget *g = (struct mtget *) addr; bzero(g, sizeof(struct mtget)); g->mt_type = 7; g->mt_density = 1; g->mt_blksiz = stp->blksize; g->mt_comp = stp->cap.compress; g->mt_density0 = 0; g->mt_density1 = 0; g->mt_density2 = 0; g->mt_density3 = 0; g->mt_blksiz0 = 0; g->mt_blksiz1 = 0; g->mt_blksiz2 = 0; g->mt_blksiz3 = 0; g->mt_comp0 = 0; g->mt_comp1 = 0; g->mt_comp2 = 0; g->mt_comp3 = 0; break; } case MTIOCTOP: { int i; struct mtop *mt = (struct mtop *)addr; switch ((int16_t) (mt->mt_op)) { case MTWEOF: for (i=0; i < mt->mt_count && !error; i++) error = ast_write_filemark(stp, WF_WRITE); break; case MTFSF: if (mt->mt_count) error = ast_space(stp, SP_FM, mt->mt_count); break; case MTBSF: if (mt->mt_count) error = ast_space(stp, SP_FM, -(mt->mt_count)); break; case MTREW: error = ast_rewind(stp); break; case MTOFFL: error = ast_load_unload(stp, SS_EJECT); break; case MTNOP: error = ast_write_filemark(stp, 0); break; case MTERASE: error = ast_erase(stp); break; case MTEOD: error = ast_space(stp, SP_EOD, 0); break; case MTRETENS: error = ast_load_unload(stp, SS_RETENSION | SS_LOAD); break; case MTFSR: case MTBSR: case MTCACHE: case MTNOCACHE: case MTSETBSIZ: case MTSETDNSTY: case MTCOMP: default: error = EINVAL; } break; } case MTIOCRDSPOS: { struct ast_readposition position; if ((error = ast_read_position(stp, 0, &position))) break; *(u_int32_t *)addr = position.tape; break; } case MTIOCRDHPOS: { struct ast_readposition position; if ((error = ast_read_position(stp, 1, &position))) break; *(u_int32_t *)addr = position.tape; break; } case MTIOCSLOCATE: error = ast_locate(stp, 0, *(u_int32_t *)addr); break; case MTIOCHLOCATE: error = ast_locate(stp, 1, *(u_int32_t *)addr); break; default: error = ENOTTY; } return error; } static void aststrategy(struct bio *bp) { struct ast_softc *stp = bp->bio_dev->si_drv1; int s; if (stp->atp->flags & ATAPI_F_DETACHING) { biofinish(bp, NULL, ENXIO); return; } /* 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) { ata_printf(stp->atp->controller, stp->atp->unit, "bad request, 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) { ata_printf(stp->atp->controller, stp->atp->unit, "WARNING: CTL exceeded %ld>%d\n", bp->bio_bcount, stp->blksize * stp->cap.ctl); stp->flags |= F_CTL_WARN; } } s = splbio(); bioq_insert_tail(&stp->queue, bp); ata_start(stp->atp->controller); splx(s); } void ast_start(struct atapi_softc *atp) { struct ast_softc *stp = atp->driver; struct bio *bp = bioq_first(&stp->queue); u_int32_t blkcount; int8_t ccb[16]; if (!bp) return; bzero(ccb, sizeof(ccb)); if (bp->bio_cmd == BIO_READ) ccb[0] = ATAPI_READ; else ccb[0] = ATAPI_WRITE; bioq_remove(&stp->queue, bp); blkcount = bp->bio_bcount / stp->blksize; ccb[1] = 1; ccb[2] = blkcount>>16; ccb[3] = blkcount>>8; ccb[4] = blkcount; devstat_start_transaction(&stp->stats); atapi_queue_cmd(stp->atp, ccb, bp->bio_data, blkcount * stp->blksize, (bp->bio_cmd == BIO_READ) ? ATPR_F_READ : 0, 120, ast_done, bp); } static int ast_done(struct atapi_request *request) { struct bio *bp = request->driver; struct ast_softc *stp = request->device->driver; if (request->error) { bp->bio_error = request->error; bp->bio_flags |= BIO_ERROR; } else { if (!(bp->bio_cmd == BIO_READ)) 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); return 0; } static int ast_mode_sense(struct ast_softc *stp, 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 = atapi_queue_cmd(stp->atp, ccb, pagebuf, pagesize, ATPR_F_READ, 10, NULL, NULL); #ifdef AST_DEBUG atapi_dump("ast: mode sense ", pagebuf, pagesize); #endif return error; } static int ast_mode_select(struct ast_softc *stp, 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 }; #ifdef AST_DEBUG ata_printf(stp->atp->controller, stp->atp->unit, "modeselect pagesize=%d\n", pagesize); atapi_dump("mode select ", pagebuf, pagesize); #endif return atapi_queue_cmd(stp->atp, ccb, pagebuf, pagesize, 0, 10, NULL, NULL); } static int ast_write_filemark(struct ast_softc *stp, u_int8_t function) { int8_t ccb[16] = { ATAPI_WEOF, 0x01, 0, 0, function, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 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 = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 10, NULL, NULL); if (error) return error; return atapi_wait_dsc(stp->atp, 10*60); } static int ast_read_position(struct ast_softc *stp, 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 = atapi_queue_cmd(stp->atp, ccb, (caddr_t)position, sizeof(struct ast_readposition), ATPR_F_READ, 10, NULL, NULL); position->tape = ntohl(position->tape); position->host = ntohl(position->host); return error; } static int ast_space(struct ast_softc *stp, 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 atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 60*60, NULL, NULL); } static int ast_locate(struct ast_softc *stp, 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 = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 10, NULL, NULL); if (error) return error; return atapi_wait_dsc(stp->atp, 60*60); } static int ast_prevent_allow(struct ast_softc *stp, 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 atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0,30, NULL, NULL); } static int ast_load_unload(struct ast_softc *stp, u_int8_t function) { int8_t ccb[16] = { ATAPI_START_STOP, 0x01, 0, 0, function, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; if ((function & SS_EJECT) && !stp->cap.eject) return 0; error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 10, NULL, NULL); if (error) return error; tsleep((caddr_t)&error, PRIBIO, "astlu", 1 * hz); if (function == SS_EJECT) return 0; return atapi_wait_dsc(stp->atp, 60*60); } static int ast_rewind(struct ast_softc *stp) { int8_t ccb[16] = { ATAPI_REZERO, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; error = atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 10, NULL, NULL); if (error) return error; return atapi_wait_dsc(stp->atp, 60*60); } static int ast_erase(struct ast_softc *stp) { int8_t ccb[16] = { ATAPI_ERASE, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; if ((error = ast_rewind(stp))) return error; return atapi_queue_cmd(stp->atp, ccb, NULL, 0, 0, 60*60, NULL, NULL); } Index: head/sys/dev/ata/atapi-tape.h =================================================================== --- head/sys/dev/ata/atapi-tape.h (revision 83727) +++ head/sys/dev/ata/atapi-tape.h (revision 83728) @@ -1,162 +1,162 @@ /*- - * Copyright (c) 1998,1999,2000,2001 Søren Schmidt + * Copyright (c) 1998,1999,2000,2001 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* 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 { struct atapi_softc *atp; /* controller structure */ int lun; /* logical device unit */ 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 bio_queue_head queue; /* queue of i/o requests */ struct atapi_params *param; /* drive parameters table */ struct ast_cappage cap; /* capabilities page info */ struct devstat stats; /* devstat entry */ dev_t dev1, dev2; /* device place holders */ };