/* * Copyright (c) 1990, 1992 The Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * 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, Lawrence Berkeley Laboratory. * * %sccs.include.redist.c% * * %W% (Berkeley) %G% * * from: Header: sd.c,v 1.27 93/04/29 01:22:19 torek Exp * $Id: sd.c,v 1.5 1994/02/01 06:01:30 deraadt Exp $ */ /* * SCSI CCS (Command Command Set) disk driver. * * MACHINE INDEPENDENT (do not put machine dependent goo in here!) * * (from sd.c,v 1.7 90/12/15 14:11:26 van Exp) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef sparc /* XXX */ #define SUN_LABEL_HACK /* XXX */ #endif /* XXX */ #ifdef SUN_LABEL_HACK #include #endif /* * Per-disk variables. * * sd_dk contains all the `disk' specific stuff (label/partitions, * transfer rate, etc). We need only things that are special to * scsi disks. Note that our blocks are in terms of DEV_BSIZE blocks. */ struct sd_softc { struct dkdevice sc_dk; /* base disk device, must be first */ struct unit sc_unit; /* scsi unit */ pid_t sc_format_pid; /* process using "format" mode */ u_char sc_type; /* drive type */ u_char sc_bshift; /* convert device blocks to DEV_BSIZE blks */ short sc_flags; /* see below */ u_int sc_blks; /* number of blocks on device */ int sc_blksize; /* device block size in bytes */ /* should be in dkdevice?? */ struct buf sc_tab; /* transfer queue */ /* statistics */ long sc_resets; /* number of times reset */ long sc_transfers; /* count of total transfers */ long sc_partials; /* count of `partial' transfers */ /* for user formatting */ struct scsi_cdb sc_cmd; struct scsi_fmt_sense sc_sense; }; #define SDF_ALIVE 1 /* drive OK for regular kernel use */ /* definition of the autoconfig driver */ int sdmatch __P((struct device *, struct cfdata *, void *)); void sdattach __P((struct device *, struct device *, void *)); struct cfdriver sdcd = { NULL, "sd", sdmatch, sdattach, DV_DISK, sizeof(struct sd_softc) }; /* definition of the unit driver, for hba */ void sdigo __P((struct device *, struct scsi_cdb *)); void sdgo __P((struct device *, struct scsi_cdb *)); void sdintr __P((struct device *, int, int)); void sdreset __P((struct unit *)); static struct unitdriver sdunitdriver = { /*sdgo, sdintr*/ sdreset }; /* definition of the disk driver, for kernel */ void sdstrategy __P((struct buf *)); static struct dkdriver sddkdriver = { sdstrategy }; #ifdef DEBUG int sddebug = 1; #define SDB_ERROR 0x01 #define SDB_PARTIAL 0x02 #endif #define sdunit(x) (minor(x) >> 3) #define sdpart(x) (minor(x) & 0x7) #define b_cylin b_resid #define SDRETRY 2 /* * Table of scsi commands users are allowed to access via `format' * mode. 0 means not legal. 1 means `immediate' (doesn't need dma). * -1 means needs dma and/or wait for intr (i.e., `slow'). */ static char legal_cmds[256] = { /***** 0 1 2 3 4 5 6 7 8 9 A B C D E F */ /*00*/ 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*10*/ 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /*20*/ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*80*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*90*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*a0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*b0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*c0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*d0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*e0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*f0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; int sdmatch(parent, cf, aux) struct device *parent; register struct cfdata *cf; void *aux; { register struct scsi_attach_args *sa = aux; #ifdef DEBUG char *whynot; #endif /* * unit number must match, or be given as `any' */ if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != sa->sa_unit) return (0); /* * drive must be a disk, and of a kind we recognize */ if ((sa->sa_inq_status & STS_MASK) != STS_GOOD) { #ifdef DEBUG whynot = "INQUIRY failed"; #endif goto notdisk; } switch (sa->sa_si.si_type & TYPE_TYPE_MASK) { case TYPE_DAD: /* disk */ case TYPE_WORM: /* WORM */ case TYPE_ROM: /* CD-ROM */ case TYPE_MO: /* Magneto-optical */ case TYPE_JUKEBOX: /* medium changer */ break; default: notdisk: #ifdef DEBUG whynot = "not a disk"; printf("[not matching `sd' at unit %d: %s]\n", sa->sa_unit, whynot); #endif return (0); } /* * It is a disk of some kind; take it. We will figure out * the rest in the attach routine. */ return (1); } /* * Attach a disk (called after sdmatch returns true). * Note that this routine is never reentered (so we can use statics). */ void sdattach(parent, self, aux) struct device *parent, *self; void *aux; { register struct sd_softc *sc = (struct sd_softc *)self; register struct scsi_attach_args *sa = aux; register int i; char vendor[10], drive[17], rev[5]; static u_char capbuf[8]; static struct scsi_cdb cap = { CMD_READ_CAPACITY }; #ifdef SUN_LABEL_HACK static struct scsi_cdb rd0 = { CMD_READ10, 0, 0, 0, 0, 0, 0, 0, 1, 0 }; caddr_t sector; #endif /* * Declare our existence. */ sc->sc_unit.u_driver = &sdunitdriver; scsi_establish(&sc->sc_unit, &sc->sc_dk.dk_dev, sa->sa_unit); /* * Figure out what kind of disk this is. * We only accepted it if the inquiry succeeded, so * we can inspect those fields. */ i = (sa->sa_si.si_version >> VER_ANSI_SHIFT) & VER_ANSI_MASK; if (i == 1 || i == 2) { scsi_inq_ansi((struct scsi_inq_ansi *)&sa->sa_si, vendor, drive, rev); printf(": %s %s", vendor, drive); /* XXX should we even ever bother printing this? */ if (rev[0]) printf(" %s", rev); } else { /* bleah */ bcopy("", vendor, 10); bcopy("", drive, 10); printf(": type 0x%x, qual 0x%x, ver %d", sa->sa_si.si_type, sa->sa_si.si_qual, sa->sa_si.si_version); } CDB10(&cap)->cdb_lun_rel = sc->sc_unit.u_unit << 5; i = (*sc->sc_unit.u_hbd->hd_icmd)(sc->sc_unit.u_hba, sc->sc_unit.u_targ, &cap, (char *)capbuf, sizeof capbuf, B_READ); i &= STS_MASK; if (i == STS_GOOD) { #define NUMBER(p) (((p)[0] << 24) | ((p)[1] << 16) | ((p)[2] << 8) | (p)[3]) sc->sc_blks = NUMBER(&capbuf[0]); sc->sc_blksize = NUMBER(&capbuf[4]); } else if (i == STS_CHECKCOND && (strcmp(vendor, "HP") == 0 && strcmp(drive, "S6300.650A") == 0)) { /* XXX unformatted or nonexistent MO medium: fake it */ sc->sc_blks = 318664; sc->sc_blksize = 1024; } else { /* XXX shouldn't bail for removable media */ printf(": unable to determine drive capacity [sts=%x]\n", i); return; } /* return value from read capacity is last valid block, not nblocks */ sc->sc_blks++; printf(", %u %d byte blocks\n", sc->sc_blks, sc->sc_blksize); if (sc->sc_blksize != DEV_BSIZE) { for (i = sc->sc_blksize; i > DEV_BSIZE; i >>= 1) ++sc->sc_bshift; if (i != DEV_BSIZE) { printf("%s: blksize not multiple of %d: cannot use\n", sc->sc_dk.dk_dev.dv_xname, DEV_BSIZE); return; } sc->sc_blks <<= sc->sc_bshift; } sc->sc_type = sa->sa_si.si_type; /* sufficient? */ sc->sc_dk.dk_driver = &sddkdriver; #ifdef notyet dk_establish(&sc->sc_dk); /* READ DISK LABEL HERE, UNLESS REMOVABLE MEDIUM... NEEDS THOUGHT */ #else sc->sc_dk.dk_label.d_secsize = 512; /* XXX */ sc->sc_dk.dk_bps = (3600/60) * 32 * 512;/* XXX */ #ifdef SUN_LABEL_HACK sector = (caddr_t)malloc(sc->sc_blksize, M_DEVBUF, M_NOWAIT); CDB10(&rd0)->cdb_lun_rel = sc->sc_unit.u_unit << 5; i = (*sc->sc_unit.u_hbd->hd_icmd)(sc->sc_unit.u_hba, sc->sc_unit.u_targ, &rd0, sector, sc->sc_blksize, B_READ); if (i == STS_GOOD) { printf("%s: <%s>\n", sc->sc_dk.dk_dev.dv_xname, ((struct sun_disklabel *)sector)->sl_text); if (sun_disklabel(sector, &sc->sc_dk.dk_label)) sc->sc_flags |= SDF_ALIVE; else printf("%s: sun_disklabel fails\n", sc->sc_dk.dk_dev.dv_xname); } else printf("%s: could not read sector 0 for disk label\n", sc->sc_dk.dk_dev.dv_xname); free(sector, M_DEVBUF); #endif #endif /* notyet */ } /* * Reset a disk, after a SCSI bus reset. * * XXX untested and probably incomplete/incorrect */ void sdreset(u) register struct unit *u; { register struct sd_softc *sc = (struct sd_softc *)u->u_dev; printf(" %s", sc->sc_dk.dk_dev.dv_xname); sc->sc_resets++; } /* dev_t is short, must use prototype syntax */ int sdopen(dev_t dev, int flags, int ifmt, struct proc *p) { register int unit = sdunit(dev); register struct sd_softc *sc; if (unit >= sdcd.cd_ndevs || (sc = sdcd.cd_devs[unit]) == NULL) return (ENXIO); if ((sc->sc_flags & SDF_ALIVE) == 0 && suser(p->p_ucred, &p->p_acflag)) return (ENXIO); return (0); } int sdclose(dev_t dev, int flags, int ifmt, struct proc *p) { register struct sd_softc *sc = sdcd.cd_devs[sdunit(dev)]; sc->sc_format_pid = 0; return (0); } /* * This routine is called for partial block transfers and non-aligned * transfers (the latter only being possible on devices with a block size * larger than DEV_BSIZE). The operation is performed in three steps * using a locally allocated buffer: * 1. transfer any initial partial block * 2. transfer full blocks * 3. transfer any final partial block */ static void sdlblkstrat(bp, bsize) register struct buf *bp; register int bsize; { register int bn, resid, boff, count; register caddr_t addr, cbuf; struct buf *tbp; /* should probably use geteblk() here, but I fear consequences */ cbuf = (caddr_t)malloc(bsize, M_DEVBUF, M_WAITOK); tbp = (struct buf *)malloc(sizeof *tbp, M_DEVBUF, M_WAITOK); bzero((caddr_t)tbp, sizeof *tbp); tbp->b_proc = curproc; tbp->b_dev = bp->b_dev; bn = bp->b_blkno; resid = bp->b_bcount; addr = bp->b_un.b_addr; #ifdef DEBUG if (sddebug & SDB_PARTIAL) printf("sdlblkstrat: bp %x flags %x bn %x resid %x addr %x\n", bp, bp->b_flags, bn, resid, addr); #endif while (resid > 0) { boff = dbtob(bn) & (bsize - 1); if (boff || resid < bsize) { struct sd_softc *sc = sdcd.cd_devs[sdunit(bp->b_dev)]; sc->sc_partials++; count = min(resid, bsize - boff); tbp->b_flags = B_BUSY | B_READ; tbp->b_blkno = bn - btodb(boff); tbp->b_un.b_addr = cbuf; tbp->b_bcount = bsize; #ifdef DEBUG if (sddebug & SDB_PARTIAL) printf(" readahead: bn %x cnt %x off %x addr %x\n", tbp->b_blkno, count, boff, addr); #endif sdstrategy(tbp); biowait(tbp); if (tbp->b_flags & B_ERROR) { bp->b_flags |= B_ERROR; bp->b_error = tbp->b_error; break; } if (bp->b_flags & B_READ) { bcopy(&cbuf[boff], addr, count); goto done; } bcopy(addr, &cbuf[boff], count); #ifdef DEBUG if (sddebug & SDB_PARTIAL) printf(" writeback: bn %x cnt %x off %x addr %x\n", tbp->b_blkno, count, boff, addr); #endif } else { count = resid & ~(bsize - 1); tbp->b_blkno = bn; tbp->b_un.b_addr = addr; tbp->b_bcount = count; #ifdef DEBUG if (sddebug & SDB_PARTIAL) printf(" fulltrans: bn %x cnt %x addr %x\n", tbp->b_blkno, count, addr); #endif } tbp->b_flags = B_BUSY | (bp->b_flags & B_READ); sdstrategy(tbp); biowait(tbp); if (tbp->b_flags & B_ERROR) { bp->b_flags |= B_ERROR; bp->b_error = tbp->b_error; break; } done: bn += btodb(count); resid -= count; addr += count; #ifdef DEBUG if (sddebug & SDB_PARTIAL) printf(" done: bn %x resid %x addr %x\n", bn, resid, addr); #endif } free(cbuf, M_DEVBUF); free((caddr_t)tbp, M_DEVBUF); biodone(bp); } /* * Start a transfer on sc as described by bp * (i.e., call hba or target start). * If in format mode, we may not need dma. */ #define sdstart(sc, bp) { \ SD_TRACE(T_START, sc, bp); \ if ((sc)->sc_format_pid && legal_cmds[(sc)->sc_cmd.cdb_bytes[0]] > 0) \ (*(sc)->sc_unit.u_start)((sc)->sc_unit.u_updev, \ &(sc)->sc_unit.u_forw, (struct buf *)NULL, \ sdigo, &(sc)->sc_dk.dk_dev); \ else \ (*(sc)->sc_unit.u_start)((sc)->sc_unit.u_updev, \ &(sc)->sc_unit.u_forw, bp, sdgo, &(sc)->sc_dk.dk_dev); \ } void sdstrategy(bp) register struct buf *bp; { register struct sd_softc *sc = sdcd.cd_devs[sdunit(bp->b_dev)]; register int s; if (sc->sc_format_pid) { /* XXXXXXXXX SHOULD NOT COMPARE curproc IN HERE!?! */ /* * In format mode, only allow the owner to mess * with the drive. Skip all the partition checks. */ if (sc->sc_format_pid != curproc->p_pid) { bp->b_error = EPERM; bp->b_flags |= B_ERROR; biodone(bp); return; } bp->b_cylin = 0; } else { register daddr_t bn = bp->b_blkno; register int sz = howmany(bp->b_bcount, DEV_BSIZE); register struct partition *p; /* * Make sure transfer is within partition. * If it starts at the end, return EOF; if * it extends past the end, truncate it. */ p = &sc->sc_dk.dk_label.d_partitions[sdpart(bp->b_dev)]; if ((unsigned)bn >= p->p_size) { if ((unsigned)bn > p->p_size) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; } else bp->b_resid = bp->b_bcount; biodone(bp); return; } if (bn + sz > p->p_size) { sz = p->p_size - bn; bp->b_bcount = dbtob(sz); } /* * Non-aligned or partial-block transfers handled specially. * SHOULD THIS BE AT A HIGHER LEVEL? */ s = sc->sc_blksize - 1; if ((dbtob(bn) & s) || (bp->b_bcount & s)) { sdlblkstrat(bp, sc->sc_blksize); return; } bp->b_cylin = (bn + p->p_offset) >> sc->sc_bshift; } /* * Transfer valid, or format mode. Queue the request * on the drive, and maybe try to start it. */ s = splbio(); disksort(&sc->sc_tab, bp); if (sc->sc_tab.b_active == 0) { sc->sc_tab.b_active = 1; sdstart(sc, bp); } splx(s); } int sderror(sc, stat) register struct sd_softc *sc; register int stat; { register struct scsi_sense *sn; int retry = 0; sc->sc_sense.status = stat; if ((stat & STS_MASK) == STS_CHECKCOND) { sn = (struct scsi_sense *)sc->sc_sense.sense; stat = scsi_request_sense(sc->sc_unit.u_hba, sc->sc_unit.u_targ, sc->sc_unit.u_unit, (caddr_t)sn, sizeof sc->sc_sense.sense); sc->sc_sense.status = stat; /* ??? */ if ((stat & STS_MASK) != STS_GOOD) { printf("%s: sense failed, status %x\n", sc->sc_dk.dk_dev.dv_xname, stat); return (0); } printf("%s: scsi sense class %d, code %d", sc->sc_dk.dk_dev.dv_xname, SENSE_ECLASS(sn), SENSE_ECODE(sn)); if (SENSE_ISXSENSE(sn) && XSENSE_ISSTD(sn)) { int key; /* * Standard extended sense: can examine sense key * and (if valid) info. */ key = XSENSE_KEY(sn); printf(", key %d", key); if (XSENSE_IVALID(sn)) printf(", blk %d", XSENSE_INFO(sn)); /* no sense or recovered error, try again */ if (key == 0 || key == 1) retry = 1; } printf("\n"); } return (retry); } /* * sdigo is called from the hba driver when it has got the scsi bus * for us, and we were doing a format op that did not need dma. */ void sdigo(sc0, cdb) struct device *sc0; struct scsi_cdb *cdb; { register struct sd_softc *sc = (struct sd_softc *)sc0; register struct buf *bp = sc->sc_tab.b_actf; register int stat; stat = (*sc->sc_unit.u_hbd->hd_icmd)(sc->sc_unit.u_hba, sc->sc_unit.u_targ, &sc->sc_cmd, bp->b_un.b_addr, bp->b_bcount, bp->b_flags & B_READ); sc->sc_sense.status = stat; if (stat & 0xfe) { /* XXX */ (void) sderror(sc, stat); bp->b_flags |= B_ERROR; bp->b_error = EIO; } /* * Done with SCSI bus, before we `ought' to be. Release it. */ (*sc->sc_unit.u_rel)(sc->sc_unit.u_updev); bp->b_resid = 0; sc->sc_tab.b_errcnt = 0; sc->sc_tab.b_actf = bp->b_actf; biodone(bp); if ((bp = sc->sc_tab.b_actf) == NULL) sc->sc_tab.b_active = 0; else sdstart(sc, bp); } /* * sdgo is called from the hba driver or target code when it has * allocated the scsi bus and DMA resources and target datapath for us. */ void sdgo(sc0, cdb) struct device *sc0; register struct scsi_cdb *cdb; { register struct sd_softc *sc = (struct sd_softc *)sc0; register struct buf *bp = sc->sc_tab.b_actf; register int n; register unsigned int u; SD_TRACE(T_MKCDB, sc, bp); if (sc->sc_format_pid) { *cdb = sc->sc_cmd; n = 0; } else { CDB10(cdb)->cdb_cmd = bp->b_flags & B_READ ? CMD_READ10 : CMD_WRITE10; CDB10(cdb)->cdb_lun_rel = sc->sc_unit.u_unit << 5; u = bp->b_cylin; CDB10(cdb)->cdb_lbah = u >> 24; CDB10(cdb)->cdb_lbahm = u >> 16; CDB10(cdb)->cdb_lbalm = u >> 8; CDB10(cdb)->cdb_lbal = u; CDB10(cdb)->cdb_xxx = 0; n = sc->sc_blksize - 1; u = (bp->b_bcount + n) >> (DEV_BSHIFT + sc->sc_bshift); CDB10(cdb)->cdb_lenh = u >> 8; CDB10(cdb)->cdb_lenl = u; CDB10(cdb)->cdb_ctrl = 0; n = (bp->b_bcount & n) != 0; #ifdef DEBUG if (n) printf("%s: partial block xfer -- %x bytes\n", sc->sc_dk.dk_dev.dv_xname, bp->b_bcount); #endif sc->sc_transfers++; } if ((*sc->sc_unit.u_go)(sc->sc_unit.u_updev, sc->sc_unit.u_targ, sdintr, (void *)sc, bp, n) == 0) { #ifdef notyet sc->sc_dk.dk_busy = 1; sc->sc_dk.dk_seek++; /* XXX */ sc->sc_dk.dk_xfer++; sc->sc_dk.dk_wds += bp->b_bcount >> 6; #endif return; } /* * Some sort of nasty unrecoverable error: clobber the * transfer. Call the bus release function first, though. */ (*sc->sc_unit.u_rel)(sc->sc_unit.u_updev); #ifdef DEBUG if (sddebug & SDB_ERROR) printf("%s: sdgo: %s adr %d blk %d len %d ecnt %d\n", sc->sc_dk.dk_dev.dv_xname, bp->b_flags & B_READ? "read" : "write", bp->b_un.b_addr, bp->b_cylin, bp->b_bcount, sc->sc_tab.b_errcnt); #endif bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid = 0; sc->sc_tab.b_errcnt = 0; sc->sc_tab.b_actf = bp->b_actf; biodone(bp); if ((bp = sc->sc_tab.b_actf) == NULL) sc->sc_tab.b_active = 0; else sdstart(sc, bp); } /* * A transfer finished (or, someday, disconnected). * We are already off the target/hba queues. * Restart this one for error recovery, or start the next, as appropriate. */ void sdintr(sc0, stat, resid) struct device *sc0; int stat, resid; { register struct sd_softc *sc = (struct sd_softc *)sc0; register struct buf *bp = sc->sc_tab.b_actf; int retry; if (bp == NULL) panic("sdintr"); SD_TRACE(T_INTR, sc, bp); #ifdef notyet sc->sc_dk.dk_busy = 0; #endif if ((stat & STS_MASK) != STS_GOOD) { #ifdef DEBUG if (sddebug & SDB_ERROR) printf("%s: sdintr scsi status 0x%x resid %d\n", sc->sc_dk.dk_dev.dv_xname, stat, resid); #endif retry = sderror(sc, stat); if (retry && ++sc->sc_tab.b_errcnt <= SDRETRY) { printf("%s: retry %d\n", sc->sc_dk.dk_dev.dv_xname, sc->sc_tab.b_errcnt); goto restart; } bp->b_flags |= B_ERROR; bp->b_error = EIO; } bp->b_resid = resid; sc->sc_tab.b_errcnt = 0; sc->sc_tab.b_actf = bp->b_actf; biodone(bp); if ((bp = sc->sc_tab.b_actf) == NULL) sc->sc_tab.b_active = 0; else { restart: sdstart(sc, bp); } } int sdioctl(dev_t dev, int cmd, register caddr_t data, int flag, struct proc *p) { register struct sd_softc *sc = sdcd.cd_devs[sdunit(dev)]; #ifdef COMPAT_SUNOS int error; error = sun_dkioctl(&sc->sc_dk, cmd, data, sdpart(dev)); if (error >= 0) return (error); #endif switch (cmd) { case SDIOCSFORMAT: /* take this device into or out of "format" mode */ if (suser(p->p_ucred, &p->p_acflag)) return (EPERM); if (*(int *)data) { if (sc->sc_format_pid) return (EPERM); sc->sc_format_pid = p->p_pid; } else sc->sc_format_pid = 0; break; case SDIOCGFORMAT: /* find out who has the device in format mode */ *(int *)data = sc->sc_format_pid; break; case SDIOCSCSICOMMAND: #define cdb ((struct scsi_cdb *)data) /* * Save what user gave us as SCSI cdb to use with next * read or write to the char device. Be sure to replace * the lun field with the actual unit number. */ if (sc->sc_format_pid != p->p_pid) return (EPERM); if (legal_cmds[cdb->cdb_bytes[0]] == 0) return (EINVAL); sc->sc_cmd = *cdb; sc->sc_cmd.cdb_bytes[1] = (sc->sc_cmd.cdb_bytes[1] & ~(7 << 5)) | (sc->sc_unit.u_unit << 5); #undef cdb break; case SDIOCSENSE: /* * return the SCSI sense data saved after the last * operation that completed with "check condition" status. */ sc->sc_sense = *(struct scsi_fmt_sense *)data; break; case DIOCGDINFO: *(struct disklabel *)data = sc->sc_dk.dk_label; break; case DIOCGPART: ((struct partinfo *)data)->disklab = &sc->sc_dk.dk_label; ((struct partinfo *)data)->part = &sc->sc_dk.dk_label.d_partitions[sdpart(dev)]; break; default: return (ENOTTY); } return (0); } int sdsize(dev_t dev) { register int unit = sdunit(dev); register struct sd_softc *sc; if (unit >= sdcd.cd_ndevs || (sc = sdcd.cd_devs[unit]) == NULL || (sc->sc_flags & SDF_ALIVE) == 0) return (-1); return (sc->sc_dk.dk_label.d_partitions[sdpart(dev)].p_size); } /* * Write `len' bytes from address `addr' to drive and partition in `dev', * at block blkoff from the beginning of the partition. The address is * either kernel virtual or physical (some machines may never use one or * the other, but we need it in the protocol to stay machine-independent). */ int sddump(dev_t dev, daddr_t blkoff, caddr_t addr, int len) { register struct sd_softc *sc; register struct partition *p; register daddr_t bn, n, nblks; register struct hba_softc *hba; register int stat, unit; struct scsi_cdb cdb; /* drive ok? */ unit = sdunit(dev); if (unit >= sdcd.cd_ndevs || (sc = sdcd.cd_devs[unit]) == NULL || (sc->sc_flags & SDF_ALIVE) == 0) return (ENXIO); /* blocks in range? */ p = &sc->sc_dk.dk_label.d_partitions[sdpart(dev)]; n = (len + sc->sc_blksize - 1) >> DEV_BSHIFT; if (blkoff < 0 || blkoff >= p->p_size || blkoff + n > p->p_size) return (EINVAL); bn = blkoff + p->p_offset; bn >>= sc->sc_bshift; /* scsi bus idle? */ hba = sc->sc_unit.u_hba; if (hba->hba_head) { (*hba->hba_driver->hd_reset)(hba, 0); printf("[reset %s] ", sc->sc_dk.dk_dev.dv_xname); } CDB10(&cdb)->cdb_cmd = CMD_WRITE10; CDB10(&cdb)->cdb_lun_rel = sc->sc_unit.u_unit << 5; CDB10(&cdb)->cdb_xxx = 0; CDB10(&cdb)->cdb_ctrl = 0; #define DUMP_MAX (32 * 1024) /* no more than 32k per write */ for (;;) { if ((n = len) > DUMP_MAX) n = DUMP_MAX; CDB10(&cdb)->cdb_lbah = bn >> 24; CDB10(&cdb)->cdb_lbahm = bn >> 16; CDB10(&cdb)->cdb_lbalm = bn >> 8; CDB10(&cdb)->cdb_lbal = bn; nblks = n >> (DEV_BSHIFT + sc->sc_bshift); CDB10(&cdb)->cdb_lenh = nblks >> 8; CDB10(&cdb)->cdb_lenl = nblks; stat = hba->hba_driver->hd_dump(hba, sc->sc_unit.u_targ, &cdb, addr, n); if ((stat & STS_MASK) != STS_GOOD) { printf("%s: scsi write error 0x%x\ndump ", sc->sc_dk.dk_dev.dv_xname, stat); return (EIO); } if ((len -= n) == 0) return (0); addr += n; bn += nblks; } }