/* * The routine called by the low level scsi routine when it discovers * a device suitable for this driver. */ void sdattach(struct device *parent, struct device *self, void *aux) { struct sd_softc *sc = (struct sd_softc *)self; struct scsi_attach_args *sa = aux; struct disk_parms *dp = &sc->params; struct scsi_link *sc_link = sa->sa_sc_link; int sd_autoconf = scsi_autoconf | SCSI_SILENT | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE; struct dk_cache dkc; int error, result; SC_DEBUG(sc_link, SDEV_DB2, ("sdattach:\n")); /* * Store information needed to contact our base driver */ sc->sc_link = sc_link; sc_link->interpret_sense = sd_interpret_sense; sc_link->device_softc = sc; if ((sc_link->flags & SDEV_ATAPI) && (sc_link->flags & SDEV_REMOVABLE)) sc_link->quirks |= SDEV_NOSYNCCACHE; if (!(sc_link->inqdata.flags & SID_RelAdr)) sc_link->quirks |= SDEV_ONLYBIG; /* * Note if this device is ancient. This is used in sdminphys(). */ if (!(sc_link->flags & SDEV_ATAPI) && SCSISPC(sa->sa_inqbuf->version) == 0) sc->flags |= SDF_ANCIENT; /* * Use the subdriver to request information regarding * the drive. We cannot use interrupts yet, so the * request must specify this. */ printf("\n"); scsi_xsh_set(&sc->sc_xsh, sc_link, sdstart); timeout_set(&sc->sc_timeout, (void (*)(void *))scsi_xsh_add, &sc->sc_xsh); /* Spin up non-UMASS devices ready or not. */ if ((sc->sc_link->flags & SDEV_UMASS) == 0) scsi_start(sc_link, SSS_START, sd_autoconf); /* * Some devices (e.g. Blackberry Pearl) won't admit they have * media loaded unless its been locked in. */ if ((sc_link->flags & SDEV_REMOVABLE) != 0) scsi_prevent(sc_link, PR_PREVENT, sd_autoconf); /* Check that it is still responding and ok. */ error = scsi_test_unit_ready(sc->sc_link, TEST_READY_RETRIES * 3, sd_autoconf); if (error) result = SDGP_RESULT_OFFLINE; else result = sd_get_parms(sc, &sc->params, sd_autoconf); if ((sc_link->flags & SDEV_REMOVABLE) != 0) scsi_prevent(sc_link, PR_ALLOW, sd_autoconf); switch (result) { case SDGP_RESULT_OK: printf("%s: %lluMB, %lu bytes/sector, %llu sectors", sc->sc_dev.dv_xname, dp->disksize / (1048576 / dp->secsize), dp->secsize, dp->disksize); printf("\n"); break; case SDGP_RESULT_OFFLINE: break; #ifdef DIAGNOSTIC default: panic("sdattach: unknown result (%#x) from get_parms", result); break; #endif } /* * Initialize disk structures. */ sc->sc_dk.dk_name = sc->sc_dev.dv_xname; bufq_init(&sc->sc_bufq, BUFQ_FIFO); /* * Enable write cache by default. */ memset(&dkc, 0, sizeof(dkc)); if (sd_ioctl_cache(sc, DIOCGCACHE, &dkc) == 0 && dkc.wrcache == 0) { dkc.wrcache = 1; sd_ioctl_cache(sc, DIOCSCACHE, &dkc); } /* * Establish a shutdown hook so that we can ensure that * our data has actually made it onto the platter at * shutdown time. Note that this relies on the fact * that the shutdown hook code puts us at the head of * the list (thus guaranteeing that our hook runs before * our ancestors'). */ if ((sc->sc_sdhook = shutdownhook_establish(sd_shutdown, sc)) == NULL) printf("%s: WARNING: unable to establish shutdown hook\n", sc->sc_dev.dv_xname); /* Attach disk. */ disk_attach(&sc->sc_dev, &sc->sc_dk); }
/* * Open the device. Make sure the partition info is as up-to-date as can be. */ int sdopen(dev_t dev, int flag, int fmt, struct proc *p) { struct scsi_link *sc_link; struct sd_softc *sc; int error = 0, part, rawopen, unit; unit = DISKUNIT(dev); part = DISKPART(dev); rawopen = (part == RAW_PART) && (fmt == S_IFCHR); sc = sdlookup(unit); if (sc == NULL) return (ENXIO); sc_link = sc->sc_link; if (sc->flags & SDF_DYING) { device_unref(&sc->sc_dev); return (ENXIO); } if (ISSET(flag, FWRITE) && ISSET(sc_link->flags, SDEV_READONLY)) { device_unref(&sc->sc_dev); return (EACCES); } SC_DEBUG(sc_link, SDEV_DB1, ("sdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit, sd_cd.cd_ndevs, part)); if ((error = disk_lock(&sc->sc_dk)) != 0) { device_unref(&sc->sc_dev); return (error); } if (sc->sc_dk.dk_openmask != 0) { /* * If any partition is open, but the disk has been invalidated, * disallow further opens of non-raw partition. */ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) { if (rawopen) goto out; error = EIO; goto bad; } } else { /* Spin up non-UMASS devices ready or not. */ if ((sc->sc_link->flags & SDEV_UMASS) == 0) scsi_start(sc_link, SSS_START, (rawopen ? SCSI_SILENT : 0) | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE); /* Use sd_interpret_sense() for sense errors. * * But only after spinning the disk up! Just in case a broken * device returns "Initialization command required." and causes * a loop of scsi_start() calls. */ sc_link->flags |= SDEV_OPEN; /* * Try to prevent the unloading of a removable device while * it's open. But allow the open to proceed if the device can't * be locked in. */ if ((sc_link->flags & SDEV_REMOVABLE) != 0) { scsi_prevent(sc_link, PR_PREVENT, SCSI_SILENT | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE); } /* Check that it is still responding and ok. */ error = scsi_test_unit_ready(sc_link, TEST_READY_RETRIES, SCSI_SILENT | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE); if (error) { if (rawopen) { error = 0; goto out; } else goto bad; } /* Load the physical device parameters. */ sc_link->flags |= SDEV_MEDIA_LOADED; if (sd_get_parms(sc, &sc->params, (rawopen ? SCSI_SILENT : 0)) == SDGP_RESULT_OFFLINE) { sc_link->flags &= ~SDEV_MEDIA_LOADED; error = ENXIO; goto bad; } SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded\n")); /* Load the partition info if not already loaded. */ if (sdgetdisklabel(dev, sc, sc->sc_dk.dk_label, 0) == EIO) { error = EIO; goto bad; } SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel loaded\n")); } out: if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0) goto bad; SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n")); /* It's OK to fall through because dk_openmask is now non-zero. */ bad: if (sc->sc_dk.dk_openmask == 0) { if ((sc->sc_link->flags & SDEV_REMOVABLE) != 0) scsi_prevent(sc_link, PR_ALLOW, SCSI_SILENT | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE); sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED); } disk_unlock(&sc->sc_dk); device_unref(&sc->sc_dev); return (error); }
/* * Open device (read drive parameters and disklabel) */ int sdopen(struct open_file *f, ...) { struct sd_softc *sd; struct scsi_test_unit_ready cmd; struct scsipi_inquiry_data *inqbuf; u_int bus, target, lun, part; int error; char buf[SCSIPI_INQUIRY_LENGTH_SCSI2]; va_list ap; va_start(ap, f); bus = 0; target = va_arg(ap, u_int); lun = va_arg(ap, u_int); part = va_arg(ap, u_int); va_end(ap); DPRINTF(("sdopen: sd(%d,%d,%d)\n", target, lun, part)); sd = alloc(sizeof(struct sd_softc)); if (sd == NULL) return ENOMEM; memset(sd, 0, sizeof(struct sd_softc)); sd->sc_part = part; sd->sc_lun = lun; sd->sc_target = target; sd->sc_bus = bus; if ((error = scsi_inquire(sd, sizeof(buf), buf)) != 0) return error; inqbuf = (struct scsipi_inquiry_data *)buf; sd->sc_type = inqbuf->device & SID_TYPE; /* * Determine the operating mode capabilities of the device. */ if ((inqbuf->version & SID_ANSII) >= 2) { // if ((inqbuf->flags3 & SID_CmdQue) != 0) // sd->sc_cap |= PERIPH_CAP_TQING; if ((inqbuf->flags3 & SID_Sync) != 0) sd->sc_cap |= PERIPH_CAP_SYNC; /* SPC-2 */ if ((inqbuf->version & SID_ANSII) >= 3) { /* * Report ST clocking though CAP_WIDExx/CAP_SYNC. * If the device only supports DT, clear these * flags (DT implies SYNC and WIDE) */ switch (inqbuf->flags4 & SID_Clocking) { case SID_CLOCKING_DT_ONLY: sd->sc_cap &= ~PERIPH_CAP_SYNC; break; } } } sd->sc_flags = (inqbuf->dev_qual2 & SID_REMOVABLE) ? FLAGS_REMOVABLE : 0; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = SCSI_TEST_UNIT_READY; if ((error = scsi_command(sd, (void *)&cmd, sizeof(cmd), NULL, 0)) != 0) return error; if (sd->sc_flags & FLAGS_REMOVABLE) { printf("XXXXX: removable device found. will not support\n"); } if (!(sd->sc_flags & FLAGS_MEDIA_LOADED)) sd->sc_flags |= FLAGS_MEDIA_LOADED; if ((error = sd_get_parms(sd)) != 0) return error; strncpy(sd->sc_label.d_typename, inqbuf->product, 16); if ((error = sdgetdisklabel(sd)) != 0) return error; f->f_devdata = sd; return 0; }