/* $OpenBSD: idesc.c,v 1.10 1999/07/27 08:49:46 niklas Exp $ */ /* $NetBSD: idesc.c,v 1.29 1996/12/23 09:10:12 veego Exp $ */ /* * Copyright (c) 1994 Michael L. Hitch * Copyright (c) 1993, 1994 Charles Hannum. * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * 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. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. * * @(#)wd.c 7.4 (Berkeley) 5/25/91 */ /* * Copyright (c) 1994 Michael L. Hitch * 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. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Brad Pepers * 4. 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. */ /* * A4000 IDE interface, emulating a SCSI controller */ #include "idesc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define b_cylin b_resid /* defines */ struct regs { volatile u_short ide_data; /* 00 */ char ____pad0[4]; volatile u_char ide_error; /* 06 */ #define ide_precomp ide_error char ____pad1[3]; volatile u_char ide_seccnt; /* 0a */ char ____pad2[3]; volatile u_char ide_sector; /* 0e */ char ____pad3[3]; volatile u_char ide_cyl_lo; /* 12 */ char ____pad4[3]; volatile u_char ide_cyl_hi; /* 16 */ char ____pad5[3]; volatile u_char ide_sdh; /* 1a */ char ____pad6[3]; volatile u_char ide_command; /* 1e */ #define ide_status ide_command char ____pad7; char ____pad8[0xfe0]; volatile short ide_intpnd; /* 1000 */ char ____pad9[24]; volatile u_char ide_altsts; /* 101a */ #define ide_ctlr ide_altsts }; typedef volatile struct regs *ide_regmap_p; #define IDES_BUSY 0x80 /* controller busy bit */ #define IDES_READY 0x40 /* selected drive is ready */ #define IDES_WRTFLT 0x20 /* Write fault */ #define IDES_SEEKCMPLT 0x10 /* Seek complete */ #define IDES_DRQ 0x08 /* Data request bit */ #define IDES_ECCCOR 0x04 /* ECC correction made in data */ #define IDES_INDEX 0x02 /* Index pulse from selected drive */ #define IDES_ERR 0x01 /* Error detect bit */ #define IDEC_RESTORE 0x10 #define IDEC_READ 0x20 #define IDEC_WRITE 0x30 #define IDEC_XXX 0x40 #define IDEC_FORMAT 0x50 #define IDEC_XXXX 0x70 #define IDEC_DIAGNOSE 0x90 #define IDEC_IDC 0x91 #define IDEC_READP 0xec #define IDECTL_IDS 0x02 /* Interrupt disable */ struct ideparams { /* drive info */ short idep_config; /* general configuration */ short idep_fixedcyl; /* number of non-removable cylinders */ short idep_removcyl; /* number of removable cylinders */ short idep_heads; /* number of heads */ short idep_unfbytespertrk; /* number of unformatted bytes/track */ short idep_unfbytes; /* number of unformatted bytes/sector */ short idep_sectors; /* number of sectors */ short idep_minisg; /* minimum bytes in inter-sector gap*/ short idep_minplo; /* minimum bytes in postamble */ short idep_vendstat; /* number of words of vendor status */ /* controller info */ char idep_cnsn[20]; /* controller serial number */ short idep_cntype; /* controller type */ #define IDETYPE_SINGLEPORTSECTOR 1 /* single port, single sector buffer */ #define IDETYPE_DUALPORTMULTI 2 /* dual port, multiple sector buffer */ #define IDETYPE_DUALPORTMULTICACHE 3 /* above plus track cache */ short idep_cnsbsz; /* sector buffer size, in sectors */ short idep_necc; /* ecc bytes appended */ char idep_rev[8]; /* firmware revision */ char idep_model[40]; /* model name */ short idep_nsecperint; /* sectors per interrupt */ short idep_usedmovsd; /* can use double word read/write? */ }; /* * Per drive structure. * N per controller (presently 2) (DRVS_PER_CTLR) */ struct ide_softc { struct device sc_dev; long sc_bcount; /* byte count left */ long sc_mbcount; /* total byte count left */ short sc_skip; /* blocks already transferred */ short sc_mskip; /* blocks already transfereed for multi */ long sc_blknum; /* starting block of active request */ u_char *sc_buf; /* buffer address of active request */ long sc_blkcnt; /* block count of active request */ int sc_flags; #define IDEF_ALIVE 0x01 /* it's a valid device */ short sc_error; char sc_drive; char sc_state; long sc_secpercyl; long sc_sectors; struct buf sc_dq; struct ideparams sc_params; }; struct ide_pending { TAILQ_ENTRY(ide_pending) link; struct scsi_xfer *xs; }; /* * Per controller structure. */ struct idec_softc { struct device sc_dev; struct isr sc_isr; struct scsi_link sc_link; /* proto for sub devices */ ide_regmap_p sc_cregs; /* driver specific regs */ volatile u_char *sc_a1200; /* A1200 interrupt control */ TAILQ_HEAD(,ide_pending) sc_xslist; /* LIFO */ struct ide_pending sc_xsstore[8][8]; /* one for every unit */ struct scsi_xfer *sc_xs; /* transfer from high level code */ int sc_flags; #define IDECF_ALIVE 0x01 /* Controller is alive */ #define IDECF_ACTIVE 0x02 #define IDECF_SINGLE 0x04 /* sector at a time mode */ #define IDECF_READ 0x08 /* Current operation is read */ #define IDECF_A1200 0x10 /* A1200 IDE */ struct ide_softc *sc_cur; /* drive we are currently doing work for */ int state; int saved; int retry; char sc_stat[2]; struct ide_softc sc_ide[2]; }; int ide_scsicmd __P((struct scsi_xfer *)); void idescattach __P((struct device *, struct device *, void *)); int idescmatch __P((struct device *, void *, void *)); int ideicmd __P((struct idec_softc *, int, void *, int, void *, int)); int idego __P((struct idec_softc *, struct scsi_xfer *)); int idegetsense __P((struct idec_softc *, struct scsi_xfer *)); void ideabort __P((struct idec_softc *, ide_regmap_p, char *)); void ideerror __P((struct idec_softc *, ide_regmap_p, u_char)); int idestart __P((struct idec_softc *)); int idereset __P((struct idec_softc *)); void idesetdelay __P((int)); void ide_scsidone __P((struct idec_softc *, int)); void ide_donextcmd __P((struct idec_softc *)); int idesc_intr __P((void *)); struct scsi_adapter idesc_scsiswitch = { ide_scsicmd, minphys, /* no max transfer len, at this level */ 0, /* no lun support */ 0, /* no lun support */ }; struct scsi_device idesc_scsidev = { NULL, /* use default error handler */ NULL, /* do not have a start functio */ NULL, /* have no async handler */ NULL, /* Use default done routine */ }; struct cfattach idesc_ca = { sizeof(struct idec_softc), idescmatch, idescattach }; struct cfdriver idesc_cd = { NULL, "idesc", DV_DULL, NULL, 0 }; struct { short ide_err; char scsi_sense_key; char scsi_sense_qual; } sense_convert[] = { { 0x0001, 0x03, 0x13}, /* Data address mark not found */ { 0x0002, 0x04, 0x06}, /* Reference position not found */ { 0x0004, 0x05, 0x20}, /* Invalid command */ { 0x0010, 0x03, 0x12}, /* ID address mark not found */ { 0x0040, 0x03, 0x11}, /* Unrecovered read error */ { 0x0080, 0x03, 0x11}, /* Bad block mark detected */ { 0x0000, 0x05, 0x00} /* unknown */ }; /* * protos. */ int idecommand __P((struct ide_softc *, int, int, int, int, int)); int idewait __P((struct idec_softc *, int)); int idegetctlr __P((struct ide_softc *)); int ideiread __P((struct ide_softc *, long, u_char *, int)); int ideiwrite __P((struct ide_softc *, long, u_char *, int)); #define wait_for_drq(ide) idewait(ide, IDES_DRQ) #define wait_for_ready(ide) idewait(ide, IDES_READY | IDES_SEEKCMPLT) #define wait_for_unbusy(ide) idewait(ide,0) int ide_no_int = 0; #ifdef DEBUG void ide_dump_regs __P((ide_regmap_p)); int ide_debug = 0; #define TRACE0(arg) if (ide_debug > 1) printf(arg) #define TRACE1(arg1,arg2) if (ide_debug > 1) printf(arg1,arg2) #define QPRINTF(a) if (ide_debug > 1) printf a #else /* !DEBUG */ #define TRACE0(arg) #define TRACE1(arg1,arg2) #define QPRINTF(a) #endif /* !DEBUG */ /* * if we are an A4000 we are here. */ int idescmatch(pdp, match, auxp) struct device *pdp; void *match, *auxp; { char *mbusstr; mbusstr = auxp; if ((is_a4000() || is_a1200()) && matchname(auxp, "idesc")) return(1); return(0); } void idescattach(pdp, dp, auxp) struct device *pdp, *dp; void *auxp; { ide_regmap_p rp; struct idec_softc *sc; int i; sc = (struct idec_softc *)dp; if (is_a4000()) sc->sc_cregs = rp = (ide_regmap_p) ztwomap(0xdd2020); else { /* Let's hope the A1200 will work with the same regs */ sc->sc_cregs = rp = (ide_regmap_p) ztwomap(0xda0000); sc->sc_a1200 = ztwomap(0xda8000 + 0x1000); sc->sc_flags |= IDECF_A1200; printf(" A1200 @ %p:%p", rp, sc->sc_a1200); } #ifdef DEBUG if (ide_debug) ide_dump_regs(rp); #endif rp->ide_error = 0x5a; rp->ide_cyl_lo = 0xa5; if (rp->ide_error == 0x5a || rp->ide_cyl_lo != 0xa5) { printf ("\n"); return; } /* test if controller will reset */ if (idereset(sc) != 0) { delay (500000); if (idereset(sc) != 0) { printf (" IDE controller did not reset\n"); return; } } /* Dummy up the unit structures */ sc->sc_ide[0].sc_dev.dv_parent = (void *) sc; sc->sc_ide[1].sc_dev.dv_parent = (void *) sc; #if 0 /* Amiga ROM does this; it also takes a lot of time on the Seacrate */ /* Execute a controller only command. */ if (idecommand(&sc->sc_ide[0], 0, 0, 0, 0, IDEC_DIAGNOSE) != 0 || wait_for_unbusy(sc) != 0) { printf (" ide attach failed\n"); return; } #endif #ifdef DEBUG if (ide_debug) ide_dump_regs(rp); #endif idereset(sc); for (i = 0; i < 2; ++i) { rp->ide_sdh = 0xa0 | (i << 4); sc->sc_ide[i].sc_drive = i; if ((rp->ide_status & IDES_READY) == 0) continue; sc->sc_ide[i].sc_flags |= IDEF_ALIVE; rp->ide_ctlr = 0; } printf ("\n"); sc->sc_link.adapter_softc = sc; sc->sc_link.adapter_target = 7; sc->sc_link.adapter = &idesc_scsiswitch; sc->sc_link.device = &idesc_scsidev; sc->sc_link.openings = 1; TAILQ_INIT(&sc->sc_xslist); sc->sc_isr.isr_intr = idesc_intr; sc->sc_isr.isr_arg = sc; sc->sc_isr.isr_ipl = 2; add_isr (&sc->sc_isr); /* * attach all "scsi" units on us */ config_found(dp, &sc->sc_link, scsiprint); } /* * used by specific ide controller * */ int ide_scsicmd(xs) struct scsi_xfer *xs; { struct ide_pending *pendp; struct idec_softc *dev; struct scsi_link *slp; int flags, s; slp = xs->sc_link; dev = slp->adapter_softc; flags = xs->flags; if (flags & SCSI_DATA_UIO) panic("ide: scsi data uio requested"); if (dev->sc_xs && flags & SCSI_POLL) panic("ide_scsicmd: busy"); s = splbio(); pendp = &dev->sc_xsstore[slp->target][slp->lun]; if (pendp->xs) { splx(s); return(TRY_AGAIN_LATER); } if (dev->sc_xs) { pendp->xs = xs; TAILQ_INSERT_TAIL(&dev->sc_xslist, pendp, link); splx(s); return(SUCCESSFULLY_QUEUED); } pendp->xs = NULL; dev->sc_xs = xs; splx(s); /* * nothing is pending do it now. */ ide_donextcmd(dev); if (flags & SCSI_POLL) return(COMPLETE); return(SUCCESSFULLY_QUEUED); } /* * entered with dev->sc_xs pointing to the next xfer to perform */ void ide_donextcmd(dev) struct idec_softc *dev; { struct scsi_xfer *xs; struct scsi_link *slp; int flags, stat; xs = dev->sc_xs; slp = xs->sc_link; flags = xs->flags; if (flags & SCSI_RESET) idereset(dev); dev->sc_stat[0] = -1; /* Weed out invalid targets & LUNs here */ if (slp->target > 1 || slp->lun != 0) { ide_scsidone(dev, -1); return; } if (flags & SCSI_POLL || ide_no_int) stat = ideicmd(dev, slp->target, xs->cmd, xs->cmdlen, xs->data, xs->datalen); else if (idego(dev, xs) == 0) return; else stat = dev->sc_stat[0]; ide_scsidone(dev, stat); } void ide_scsidone(dev, stat) struct idec_softc *dev; int stat; { struct ide_pending *pendp; struct scsi_xfer *xs; int s, donext; xs = dev->sc_xs; #ifdef DIAGNOSTIC if (xs == NULL) panic("ide_scsidone"); #endif /* * is this right? */ xs->status = stat; if (stat == 0) xs->resid = 0; else { switch(stat) { case SCSI_CHECK: if ((stat = idegetsense(dev, xs)) != 0) goto bad_sense; xs->error = XS_SENSE; break; case SCSI_BUSY: xs->error = XS_BUSY; break; bad_sense: default: xs->error = XS_DRIVER_STUFFUP; QPRINTF(("ide_scsicmd() bad %x\n", stat)); break; } } xs->flags |= ITSDONE; /* * grab next command before scsi_done() * this way no single device can hog scsi resources. */ s = splbio(); pendp = dev->sc_xslist.tqh_first; if (pendp == NULL) { donext = 0; dev->sc_xs = NULL; } else { donext = 1; TAILQ_REMOVE(&dev->sc_xslist, pendp, link); dev->sc_xs = pendp->xs; pendp->xs = NULL; } splx(s); scsi_done(xs); if (donext) ide_donextcmd(dev); } int idegetsense(dev, xs) struct idec_softc *dev; struct scsi_xfer *xs; { struct scsi_sense rqs; struct scsi_link *slp; slp = xs->sc_link; rqs.opcode = REQUEST_SENSE; rqs.byte2 = slp->lun << 5; #ifdef not_yet rqs.length = xs->req_sense_length ? xs->req_sense_length : sizeof(xs->sense); #else rqs.length = sizeof(xs->sense); #endif rqs.unused[0] = rqs.unused[1] = rqs.control = 0; return(ideicmd(dev, slp->target, &rqs, sizeof(rqs), &xs->sense, rqs.length)); } #ifdef DEBUG void ide_dump_regs(regs) ide_regmap_p regs; { printf ("ide regs: %04x %02x %02x %02x %02x %02x %02x %02x\n", regs->ide_data, regs->ide_error, regs->ide_seccnt, regs->ide_sector, regs->ide_cyl_lo, regs->ide_cyl_hi, regs->ide_sdh, regs->ide_command); } #endif int idereset(sc) struct idec_softc *sc; { return (0); } int idewait (sc, mask) struct idec_softc *sc; int mask; { ide_regmap_p regs = sc->sc_cregs; int timeout = 0; if ((regs->ide_status & IDES_BUSY) == 0 && (regs->ide_status & mask) == mask) return (0); #ifdef DEBUG if (ide_debug) printf ("idewait busy: %02x\n", regs->ide_status); #endif for (;;) { if ((regs->ide_status & IDES_BUSY) == 0 && (regs->ide_status & mask) == mask) break; if (regs->ide_status & IDES_ERR) break; if (++timeout > 10000) { printf ("idewait timeout %02x\n", regs->ide_status); return (-1); } delay (1000); } if (regs->ide_status & IDES_ERR) printf ("idewait: error %02x %02x\n", regs->ide_error, regs->ide_status); #ifdef DEBUG else if (ide_debug) printf ("idewait delay %d %02x\n", timeout, regs->ide_status); #endif return (regs->ide_status & IDES_ERR); } int idecommand (ide, cylin, head, sector, count, cmd) struct ide_softc *ide; int cylin, head, sector, count; int cmd; { struct idec_softc *idec = (void *)ide->sc_dev.dv_parent; ide_regmap_p regs = idec->sc_cregs; int stat; #ifdef DEBUG if (ide_debug) printf ("idecommand: cmd = %02x\n", cmd); #endif if (wait_for_unbusy(idec) < 0) return (-1); regs->ide_sdh = 0xa0 | (ide->sc_drive << 4) | head; if (cmd == IDEC_DIAGNOSE || cmd == IDEC_IDC) stat = wait_for_unbusy(idec); else stat = idewait(idec, IDES_READY); if (stat < 0) printf ("idecommand:%d stat %d\n", ide->sc_drive, stat); if (stat < 0) return (-1); regs->ide_precomp = 0; regs->ide_cyl_lo = cylin; regs->ide_cyl_hi = cylin >> 8; regs->ide_sector = sector; regs->ide_seccnt = count; regs->ide_command = cmd; return (0); } int idegetctlr(dev) struct ide_softc *dev; { struct idec_softc *idec = (void *)dev->sc_dev.dv_parent; ide_regmap_p regs = idec->sc_cregs; char tb[DEV_BSIZE]; short *tbp = (short *) tb; int i; if (idecommand(dev, 0, 0, 0, 0, IDEC_READP) != 0 || wait_for_drq(idec) != 0) { return (-1); } else { for (i = 0; i < DEV_BSIZE / 2; ++i) *tbp++ = ntohs(regs->ide_data); for (i = 0; i < DEV_BSIZE; i += 2) { char temp; temp = tb[i]; tb[i] = tb[i + 1]; tb[i + 1] = temp; } bcopy (tb, &dev->sc_params, sizeof (struct ideparams)); dev->sc_sectors = dev->sc_params.idep_sectors; dev->sc_secpercyl = dev->sc_sectors * dev->sc_params.idep_heads; } return (0); } int ideiread(ide, block, buf, nblks) struct ide_softc *ide; long block; u_char *buf; int nblks; { int cylin, head, sector; int stat; u_short *bufp = (u_short *) buf; int i; struct idec_softc *idec = (void *) ide->sc_dev.dv_parent; ide_regmap_p regs = idec->sc_cregs; cylin = block / ide->sc_secpercyl; head = (block % ide->sc_secpercyl) / ide->sc_sectors; sector = block % ide->sc_sectors + 1; stat = idecommand(ide, cylin, head, sector, nblks, IDEC_READ); if (stat != 0) return (-1); while (nblks--) { if (wait_for_drq(idec) != 0) return (-1); for (i = 0; i < DEV_BSIZE / 2 / 16; ++i) { *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; *bufp++ = regs->ide_data; } } idec->sc_stat[0] = 0; return (0); } int ideiwrite(ide, block, buf, nblks) struct ide_softc *ide; long block; u_char *buf; int nblks; { int cylin, head, sector; int stat; u_short *bufp = (u_short *) buf; int i; struct idec_softc *idec = (void *) ide->sc_dev.dv_parent; ide_regmap_p regs = idec->sc_cregs; cylin = block / ide->sc_secpercyl; head = (block % ide->sc_secpercyl) / ide->sc_sectors; sector = block % ide->sc_sectors + 1; stat = idecommand(ide, cylin, head, sector, nblks, IDEC_WRITE); if (stat != 0) return (-1); while (nblks--) { if (wait_for_drq(idec) != 0) return (-1); for (i = 0; i < DEV_BSIZE / 2 / 16; ++i) { regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; regs->ide_data = *bufp++; } if (wait_for_unbusy(idec) != 0) printf ("ideiwrite: timeout waiting for unbusy\n"); } idec->sc_stat[0] = 0; return (0); } int ideicmd(dev, target, cbuf, clen, buf, len) struct idec_softc *dev; int target; void *cbuf; int clen; void *buf; int len; { struct ide_softc *ide; int i; int lba; int nblks; struct scsi_inquiry_data *inqbuf; struct { struct scsi_mode_header header; struct scsi_blk_desc blk_desc; union scsi_disk_pages pages; } *mdsnbuf; #ifdef DEBUG if (ide_debug > 1) printf ("ideicmd: target %d cmd %02x\n", target, *((u_char *)cbuf)); #endif if (target > 1) return (-1); /* invalid unit */ ide = &dev->sc_ide[target]; if ((ide->sc_flags & IDEF_ALIVE) == 0) return (-1); if (*((u_char *)cbuf) != REQUEST_SENSE) ide->sc_error = 0; switch (*((u_char *)cbuf)) { case TEST_UNIT_READY: dev->sc_stat[0] = 0; return (0); case INQUIRY: dev->sc_stat[0] = idegetctlr(ide); if (dev->sc_stat[0] != 0) return (dev->sc_stat[0]); inqbuf = (void *) buf; bzero (buf, len); inqbuf->device = 0; /* XXX fixed disk */ inqbuf->dev_qual2 = 0; /* XXX check RMB */ inqbuf->version = 2; inqbuf->response_format = 2; inqbuf->additional_length = 31; for (i = 0; i < 8; ++i) inqbuf->vendor[i] = ide->sc_params.idep_model[i]; for (i = 0; i < 16; ++i) inqbuf->product[i] = ide->sc_params.idep_model[i+8]; for (i = 0; i < 4; ++i) inqbuf->revision[i] = ide->sc_params.idep_rev[i]; return (0); case READ_CAPACITY: *((long *)buf) = ide->sc_params.idep_sectors * ide->sc_params.idep_heads * ide->sc_params.idep_fixedcyl - 1; *((long *)buf + 1) = 512; /* XXX 512 byte blocks */ dev->sc_stat[0] = 0; return (0); case READ_BIG: lba = *((long *)(cbuf + 2)); nblks = *((u_short *)(cbuf + 7)); return (ideiread(ide, lba, buf, nblks)); case READ_COMMAND: lba = *((long *)cbuf) & 0x001fffff; nblks = *((u_char *)(cbuf + 4)); if (nblks == 0) nblks = 256; return (ideiread(ide, lba, buf, nblks)); case WRITE_BIG: lba = *((long *)(cbuf + 2)); nblks = *((u_short *)(cbuf + 7)); return (ideiwrite(ide, lba, buf, nblks)); case WRITE_COMMAND: lba = *((long *)cbuf) & 0x001fffff; nblks = *((u_char *)(cbuf + 4)); if (nblks == 0) nblks = 256; return (ideiwrite(ide, lba, buf, nblks)); case PREVENT_ALLOW: case START_STOP: /* and LOAD */ dev->sc_stat[0] = 0; return (0); case MODE_SENSE: mdsnbuf = (void*) buf; bzero(buf, *((u_char *)cbuf + 4)); switch (*((u_char *)cbuf + 2) & 0x3f) { case 4: mdsnbuf->header.data_length = 27; mdsnbuf->header.blk_desc_len = 8; mdsnbuf->blk_desc.blklen[1] = 512 >> 8; mdsnbuf->pages.rigid_geometry.pg_code = 4; mdsnbuf->pages.rigid_geometry.pg_length = 16; _lto3b(ide->sc_params.idep_fixedcyl, mdsnbuf->pages.rigid_geometry.ncyl); mdsnbuf->pages.rigid_geometry.nheads = ide->sc_params.idep_heads; dev->sc_stat[0] = 0; return (0); default: printf ("ide: mode sense page %x not simulated\n", *((u_char *)cbuf + 2) & 0x3f); return (-1); } case REQUEST_SENSE: /* convert sc_error to SCSI sense */ bzero (buf, *((u_char *)cbuf + 4)); *((u_char *) buf) = 0x70; *((u_char *) buf + 7) = 10; i = 0; while (sense_convert[i].ide_err) { if (sense_convert[i].ide_err & ide->sc_error) break; ++i; } *((u_char *) buf + 2) = sense_convert[i].scsi_sense_key; *((u_char *) buf + 12) = sense_convert[i].scsi_sense_qual; dev->sc_stat[0] = 0; printf("ide: request sense %02x -> %02x %02x\n", ide->sc_error, sense_convert[i].scsi_sense_key, sense_convert[i].scsi_sense_qual); return (0); case 0x01 /*REWIND*/: case 0x04 /*CMD_FORMAT_UNIT*/: case 0x05 /*READ_BLOCK_LIMITS*/: case REASSIGN_BLOCKS: case 0x10 /*WRITE_FILEMARKS*/: case 0x11 /*SPACE*/: case MODE_SELECT: default: printf ("ide: unhandled SCSI command %02x\n", *((u_char *)cbuf)); ide->sc_error = 0x04; dev->sc_stat[0] = SCSI_CHECK; return (SCSI_CHECK); } } int idego(dev, xs) struct idec_softc *dev; struct scsi_xfer *xs; { struct ide_softc *ide = &dev->sc_ide[xs->sc_link->target]; long lba; int nblks; #if 0 cdb->cdb[1] |= unit << 5; #endif ide->sc_buf = xs->data; ide->sc_bcount = xs->datalen; #ifdef DEBUG if (ide_debug > 1) printf ("ide_go: %02x\n", xs->cmd->opcode); #endif if (xs->cmd->opcode != READ_COMMAND && xs->cmd->opcode != READ_BIG && xs->cmd->opcode != WRITE_COMMAND && xs->cmd->opcode != WRITE_BIG) { ideicmd (dev, xs->sc_link->target, xs->cmd, xs->cmdlen, xs->data, xs->datalen); return (1); } switch (xs->cmd->opcode) { case READ_COMMAND: case WRITE_COMMAND: lba = *((long *)xs->cmd) & 0x001fffff; nblks = xs->cmd->bytes[3]; if (nblks == 0) nblks = 256; break; case READ_BIG: case WRITE_BIG: lba = *((long *)&xs->cmd->bytes[1]); nblks = *((short *)&xs->cmd->bytes[6]); break; default: panic ("idego bad SCSI command"); } ide->sc_blknum = lba; ide->sc_blkcnt = nblks; ide->sc_skip = ide->sc_mskip = 0; dev->sc_flags &= ~IDECF_READ; if (xs->cmd->opcode == READ_COMMAND || xs->cmd->opcode == READ_BIG) dev->sc_flags |= IDECF_READ; dev->sc_cur = ide; return (idestart (dev)); } int idestart(dev) struct idec_softc *dev; { long blknum, cylin, head, sector; int command, count; struct ide_softc *ide = dev->sc_cur; short *bf; int i; ide_regmap_p regs = dev->sc_cregs; dev->sc_flags |= IDECF_ACTIVE; blknum = ide->sc_blknum + ide->sc_skip; if (ide->sc_mskip == 0) { ide->sc_mbcount = ide->sc_bcount; } cylin = blknum / ide->sc_secpercyl; head = (blknum % ide->sc_secpercyl) / ide->sc_sectors; sector = blknum % ide->sc_sectors; ++sector; if (ide->sc_mskip == 0 || dev->sc_flags & IDECF_SINGLE) { count = howmany(ide->sc_mbcount, DEV_BSIZE); command = (dev->sc_flags & IDECF_READ) ? IDEC_READ : IDEC_WRITE; if (idecommand(ide, cylin, head, sector, count, command) != 0) { printf ("idestart: timeout waiting for unbusy\n"); #if 0 bp->b_error = EINVAL; bp->b_flags |= B_ERROR; idfinish(&dev->sc_ide[0], bp); #endif ide_scsidone(dev, dev->sc_stat[0]); return (1); } } dev->sc_stat[0] = 0; if (dev->sc_flags & IDECF_READ) return (0); if (wait_for_drq(dev) < 0) { printf ("idestart: timeout waiting for drq\n"); } #define W1 (regs->ide_data = *bf++) for (i = 0, bf = (short *) (ide->sc_buf + ide->sc_skip * DEV_BSIZE); i < DEV_BSIZE / 2 / 16; ++i) { W1; W1; W1; W1; W1; W1; W1; W1; W1; W1; W1; W1; W1; W1; W1; W1; } return (0); } int idesc_intr(arg) void *arg; { struct idec_softc *dev = arg; ide_regmap_p regs; struct ide_softc *ide; short dummy; short *bf; int i; #if 0 if (idesc_cd.cd_ndevs == 0 || (dev = idesc_cd.cd_devs[0]) == NULL) return (0); #endif regs = dev->sc_cregs; if (dev->sc_flags & IDECF_A1200) { if (*dev->sc_a1200 & 0x80) { #if 0 printf ("idesc_intr: A1200 interrupt %x\n", *dev->sc_a1200); #endif dummy = regs->ide_status; /* XXX */ *dev->sc_a1200 = 0x7c | (*dev->sc_a1200 & 0x03); } else return (0); } else { if (regs->ide_intpnd >= 0) return (0); dummy = regs->ide_status; } #ifdef DEBUG if (ide_debug) printf ("idesc_intr: %02x\n", dummy); #endif if ((dev->sc_flags & IDECF_ACTIVE) == 0) return (1); dev->sc_flags &= ~IDECF_ACTIVE; if (wait_for_unbusy(dev) < 0) printf ("idesc_intr: timeout waiting for unbusy\n"); ide = dev->sc_cur; if (dummy & IDES_ERR) { dev->sc_stat[0] = SCSI_CHECK; ide->sc_error = regs->ide_error; printf("idesc_intr: error %02x, %02x\n", dev->sc_stat[1], dummy); ide_scsidone(dev, dev->sc_stat[0]); } if (dev->sc_flags & IDECF_READ) { #define R2 (*bf++ = regs->ide_data) bf = (short *) (ide->sc_buf + ide->sc_skip * DEV_BSIZE); if (wait_for_drq(dev) != 0) printf ("idesc_intr: read error detected late\n"); for (i = 0; i < DEV_BSIZE / 2 / 16; ++i) { R2; R2; R2; R2; R2; R2; R2; R2; R2; R2; R2; R2; R2; R2; R2; R2; } } ide->sc_skip++; ide->sc_mskip++; ide->sc_bcount -= DEV_BSIZE; ide->sc_mbcount -= DEV_BSIZE; #ifdef DEBUG if (ide_debug) printf ("idesc_intr: sc_bcount %ld\n", ide->sc_bcount); #endif if (ide->sc_bcount == 0) ide_scsidone(dev, dev->sc_stat[0]); else /* Check return value here? */ idestart (dev); return (1); }