int sdactivate(struct device *self, int act) { struct sd_softc *sc = (struct sd_softc *)self; int rv = 0; switch (act) { case DVACT_SUSPEND: /* * Stop the disk. Stopping the disk should flush the * cache, but we are paranoid so we flush the cache * first. */ if ((sc->flags & SDF_DIRTY) != 0) sd_flush(sc, SCSI_AUTOCONF); scsi_start(sc->sc_link, SSS_STOP, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_AUTOCONF); break; case DVACT_RESUME: scsi_start(sc->sc_link, SSS_START, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_AUTOCONF); break; case DVACT_DEACTIVATE: sc->flags |= SDF_DYING; scsi_xsh_del(&sc->sc_xsh); break; } return (rv); }
/* * Close the device. Only called if we are the last occurrence of an open * device. Convenient now but usually a pain. */ int sdclose(dev_t dev, int flag, int fmt, struct proc *p) { struct sd_softc *sc; int part = DISKPART(dev); int error; sc = sdlookup(DISKUNIT(dev)); if (sc == NULL) return (ENXIO); if (sc->flags & SDF_DYING) { device_unref(&sc->sc_dev); return (ENXIO); } if ((error = sdlock(sc)) != 0) { device_unref(&sc->sc_dev); return (error); } switch (fmt) { case S_IFCHR: sc->sc_dk.dk_copenmask &= ~(1 << part); break; case S_IFBLK: sc->sc_dk.dk_bopenmask &= ~(1 << part); break; } sc->sc_dk.dk_openmask = sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; if (sc->sc_dk.dk_openmask == 0) { if ((sc->flags & SDF_DIRTY) != 0) sd_flush(sc, 0); if ((sc->sc_link->flags & SDEV_REMOVABLE) != 0) scsi_prevent(sc->sc_link, PR_ALLOW, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY | SCSI_SILENT); sc->sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED); if (sc->sc_link->flags & SDEV_EJECTING) { scsi_start(sc->sc_link, SSS_STOP|SSS_LOEJ, 0); sc->sc_link->flags &= ~SDEV_EJECTING; } timeout_del(&sc->sc_timeout); } sdunlock(sc); device_unref(&sc->sc_dev); return 0; }
int sdactivate(struct device *self, int act) { struct sd_softc *sc = (struct sd_softc *)self; int rv = 0; switch (act) { case DVACT_SUSPEND: /* * We flush the cache, since we our next step before * DVACT_POWERDOWN might be a hibernate operation. */ if ((sc->flags & SDF_DIRTY) != 0) sd_flush(sc, SCSI_AUTOCONF); break; case DVACT_POWERDOWN: /* * Stop the disk. Stopping the disk should flush the * cache, but we are paranoid so we flush the cache * first. */ if ((sc->flags & SDF_DIRTY) != 0) sd_flush(sc, SCSI_AUTOCONF); if (boothowto & RB_POWERDOWN) scsi_start(sc->sc_link, SSS_STOP, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_AUTOCONF); break; case DVACT_RESUME: scsi_start(sc->sc_link, SSS_START, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_AUTOCONF); break; case DVACT_DEACTIVATE: sc->flags |= SDF_DYING; scsi_xsh_del(&sc->sc_xsh); break; } return (rv); }
/* * Close the device. Only called if we are the last occurrence of an open * device. */ int cdclose(dev_t dev, int flag, int fmt, struct proc *p) { struct cd_softc *cd; int part = DISKPART(dev); int error; cd = cdlookup(DISKUNIT(dev)); if (cd == NULL) return ENXIO; if ((error = cdlock(cd)) != 0) { device_unref(&cd->sc_dev); return error; } switch (fmt) { case S_IFCHR: cd->sc_dk.dk_copenmask &= ~(1 << part); break; case S_IFBLK: cd->sc_dk.dk_bopenmask &= ~(1 << part); break; } cd->sc_dk.dk_openmask = cd->sc_dk.dk_copenmask | cd->sc_dk.dk_bopenmask; if (cd->sc_dk.dk_openmask == 0) { /* XXXX Must wait for I/O to complete! */ scsi_prevent(cd->sc_link, PR_ALLOW, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY | SCSI_SILENT); cd->sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED); if (cd->sc_link->flags & SDEV_EJECTING) { scsi_start(cd->sc_link, SSS_STOP|SSS_LOEJ, 0); cd->sc_link->flags &= ~SDEV_EJECTING; } timeout_del(&cd->sc_timeout); } cdunlock(cd); device_unref(&cd->sc_dev); return 0; }
/* * Check Errors */ int sd_interpret_sense(struct scsi_xfer *xs) { struct scsi_sense_data *sense = &xs->sense; struct scsi_link *sc_link = xs->sc_link; struct sd_softc *sc = sc_link->device_softc; u_int8_t serr = sense->error_code & SSD_ERRCODE; int retval; /* * Let the generic code handle everything except a few categories of * LUN not ready errors on open devices. */ if (((sc_link->flags & SDEV_OPEN) == 0) || (serr != SSD_ERRCODE_CURRENT && serr != SSD_ERRCODE_DEFERRED) || ((sense->flags & SSD_KEY) != SKEY_NOT_READY) || (sense->extra_len < 6)) return (scsi_interpret_sense(xs)); switch (ASC_ASCQ(sense)) { case SENSE_NOT_READY_BECOMING_READY: SC_DEBUG(sc_link, SDEV_DB1, ("becoming ready.\n")); retval = scsi_delay(xs, 5); break; case SENSE_NOT_READY_INIT_REQUIRED: SC_DEBUG(sc_link, SDEV_DB1, ("spinning up\n")); retval = scsi_start(sc->sc_link, SSS_START, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_NOSLEEP); if (retval == 0) retval = ERESTART; else if (retval == ENOMEM) /* Can't issue the command. Fall back on a delay. */ retval = scsi_delay(xs, 5); else SC_DEBUG(sc_link, SDEV_DB1, ("spin up failed (%#x)\n", retval)); break; default: retval = scsi_interpret_sense(xs); break; } return (retval); }
/* * Close the device. Only called if we are the last occurrence of an open * device. Convenient now but usually a pain. */ int sdclose(dev_t dev, int flag, int fmt, struct proc *p) { struct sd_softc *sc; int part = DISKPART(dev); sc = sdlookup(DISKUNIT(dev)); if (sc == NULL) return (ENXIO); if (sc->flags & SDF_DYING) { device_unref(&sc->sc_dev); return (ENXIO); } disk_lock_nointr(&sc->sc_dk); disk_closepart(&sc->sc_dk, part, fmt); if (sc->sc_dk.dk_openmask == 0) { if ((sc->flags & SDF_DIRTY) != 0) sd_flush(sc, 0); if ((sc->sc_link->flags & SDEV_REMOVABLE) != 0) scsi_prevent(sc->sc_link, PR_ALLOW, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_NOT_READY | SCSI_SILENT); sc->sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED); if (sc->sc_link->flags & SDEV_EJECTING) { scsi_start(sc->sc_link, SSS_STOP|SSS_LOEJ, 0); sc->sc_link->flags &= ~SDEV_EJECTING; } timeout_del(&sc->sc_timeout); scsi_xsh_del(&sc->sc_xsh); } disk_unlock(&sc->sc_dk); device_unref(&sc->sc_dev); return 0; }
void sd_shutdown(void *arg) { struct sd_softc *sc = (struct sd_softc *)arg; /* * If the disk cache needs to be flushed, and the disk supports * it, flush it. We're cold at this point, so we poll for * completion. */ if ((sc->flags & SDF_DIRTY) != 0) sd_flush(sc, SCSI_AUTOCONF); if (boothowto & RB_POWERDOWN) scsi_start(sc->sc_link, SSS_STOP, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_AUTOCONF); /* * There should be no outstanding IO at this point, but lets stop * it just in case. */ timeout_del(&sc->sc_timeout); scsi_xsh_del(&sc->sc_xsh); }
/* * Perform special action on behalf of the user. * Knows about the internals of this device */ int cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { struct cd_softc *cd; struct disklabel *lp; int part = DISKPART(dev); int error = 0; cd = cdlookup(DISKUNIT(dev)); if (cd == NULL) return ENXIO; SC_DEBUG(cd->sc_link, SDEV_DB2, ("cdioctl 0x%lx\n", cmd)); /* * If the device is not valid.. abandon ship */ if ((cd->sc_link->flags & SDEV_MEDIA_LOADED) == 0) { switch (cmd) { case DIOCWLABEL: case DIOCLOCK: case DIOCEJECT: case SCIOCIDENTIFY: case SCIOCCOMMAND: case SCIOCDEBUG: case CDIOCLOADUNLOAD: case SCIOCRESET: case CDIOCGETVOL: case CDIOCSETVOL: case CDIOCSETMONO: case CDIOCSETSTEREO: case CDIOCSETMUTE: case CDIOCSETLEFT: case CDIOCSETRIGHT: case CDIOCCLOSE: case CDIOCEJECT: case CDIOCALLOW: case CDIOCPREVENT: case CDIOCSETDEBUG: case CDIOCCLRDEBUG: case CDIOCRESET: case DVD_AUTH: case DVD_READ_STRUCT: case MTIOCTOP: if (part == RAW_PART) break; /* FALLTHROUGH */ default: if ((cd->sc_link->flags & SDEV_OPEN) == 0) error = ENODEV; else error = EIO; goto exit; } } switch (cmd) { case DIOCRLDINFO: lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK); cdgetdisklabel(dev, cd, lp, 0); bcopy(lp, cd->sc_dk.dk_label, sizeof(*lp)); free(lp, M_TEMP); break; case DIOCGDINFO: case DIOCGPDINFO: *(struct disklabel *)addr = *(cd->sc_dk.dk_label); break; case DIOCGPART: ((struct partinfo *)addr)->disklab = cd->sc_dk.dk_label; ((struct partinfo *)addr)->part = &cd->sc_dk.dk_label->d_partitions[DISKPART(dev)]; break; case DIOCWDINFO: case DIOCSDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } if ((error = cdlock(cd)) != 0) break; cd->flags |= CDF_LABELLING; error = setdisklabel(cd->sc_dk.dk_label, (struct disklabel *)addr, /*cd->sc_dk.dk_openmask : */0); if (error == 0) { } cd->flags &= ~CDF_LABELLING; cdunlock(cd); break; case DIOCWLABEL: error = EBADF; break; case CDIOCPLAYTRACKS: { struct ioc_play_track *args = (struct ioc_play_track *)addr; if ((error = cd_set_pa_immed(cd, 0)) != 0) break; error = cd_play_tracks(cd, args->start_track, args->start_index, args->end_track, args->end_index); break; } case CDIOCPLAYMSF: { struct ioc_play_msf *args = (struct ioc_play_msf *)addr; if ((error = cd_set_pa_immed(cd, 0)) != 0) break; error = cd_play_msf(cd, args->start_m, args->start_s, args->start_f, args->end_m, args->end_s, args->end_f); break; } case CDIOCPLAYBLOCKS: { struct ioc_play_blocks *args = (struct ioc_play_blocks *)addr; if ((error = cd_set_pa_immed(cd, 0)) != 0) break; error = cd_play(cd, args->blk, args->len); break; } case CDIOCREADSUBCHANNEL: { struct ioc_read_subchannel *args = (struct ioc_read_subchannel *)addr; struct cd_sub_channel_info data; int len = args->data_len; if (len > sizeof(data) || len < sizeof(struct cd_sub_channel_header)) { error = EINVAL; break; } error = cd_read_subchannel(cd, args->address_format, args->data_format, args->track, &data, len); if (error) break; len = min(len, _2btol(data.header.data_len) + sizeof(struct cd_sub_channel_header)); error = copyout(&data, args->data, len); break; } case CDIOREADTOCHEADER: { struct ioc_toc_header th; if ((error = cd_read_toc(cd, 0, 0, &th, sizeof(th), 0)) != 0) break; if (cd->sc_link->quirks & ADEV_LITTLETOC) th.len = letoh16(th.len); else th.len = betoh16(th.len); bcopy(&th, addr, sizeof(th)); break; } case CDIOREADTOCENTRYS: { struct cd_toc *toc; struct ioc_read_toc_entry *te = (struct ioc_read_toc_entry *)addr; struct ioc_toc_header *th; struct cd_toc_entry *cte; int len = te->data_len; int ntracks; toc = malloc(sizeof(*toc), M_TEMP, M_WAITOK | M_ZERO); th = &toc->header; if (len > sizeof(toc->entries) || len < sizeof(struct cd_toc_entry)) { free(toc, M_TEMP); error = EINVAL; break; } error = cd_read_toc(cd, te->address_format, te->starting_track, toc, len + sizeof(struct ioc_toc_header), 0); if (error) { free(toc, M_TEMP); break; } if (te->address_format == CD_LBA_FORMAT) for (ntracks = th->ending_track - th->starting_track + 1; ntracks >= 0; ntracks--) { cte = &toc->entries[ntracks]; cte->addr_type = CD_LBA_FORMAT; if (cd->sc_link->quirks & ADEV_LITTLETOC) { #if BYTE_ORDER == BIG_ENDIAN swap16_multi((u_int16_t *)&cte->addr, sizeof(cte->addr) / 2); #endif } else cte->addr.lba = betoh32(cte->addr.lba); } if (cd->sc_link->quirks & ADEV_LITTLETOC) { th->len = letoh16(th->len); } else th->len = betoh16(th->len); len = min(len, th->len - (sizeof(th->starting_track) + sizeof(th->ending_track))); error = copyout(toc->entries, te->data, len); free(toc, M_TEMP); break; } case CDIOREADMSADDR: { struct cd_toc *toc; int sessno = *(int *)addr; struct cd_toc_entry *cte; if (sessno != 0) { error = EINVAL; break; } toc = malloc(sizeof(*toc), M_TEMP, M_WAITOK | M_ZERO); error = cd_read_toc(cd, 0, 0, toc, sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry), 0x40 /* control word for "get MS info" */); if (error) { free(toc, M_TEMP); break; } cte = &toc->entries[0]; if (cd->sc_link->quirks & ADEV_LITTLETOC) { #if BYTE_ORDER == BIG_ENDIAN swap16_multi((u_int16_t *)&cte->addr, sizeof(cte->addr) / 2); #endif } else cte->addr.lba = betoh32(cte->addr.lba); if (cd->sc_link->quirks & ADEV_LITTLETOC) toc->header.len = letoh16(toc->header.len); else toc->header.len = betoh16(toc->header.len); *(int *)addr = (toc->header.len >= 10 && cte->track > 1) ? cte->addr.lba : 0; free(toc, M_TEMP); break; } case CDIOCSETPATCH: { struct ioc_patch *arg = (struct ioc_patch *)addr; error = cd_setchan(cd, arg->patch[0], arg->patch[1], arg->patch[2], arg->patch[3], 0); break; } case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol *)addr; error = cd_getvol(cd, arg, 0); break; } case CDIOCSETVOL: { struct ioc_vol *arg = (struct ioc_vol *)addr; error = cd_setvol(cd, arg, 0); break; } case CDIOCSETMONO: error = cd_setchan(cd, BOTH_CHANNEL, BOTH_CHANNEL, MUTE_CHANNEL, MUTE_CHANNEL, 0); break; case CDIOCSETSTEREO: error = cd_setchan(cd, LEFT_CHANNEL, RIGHT_CHANNEL, MUTE_CHANNEL, MUTE_CHANNEL, 0); break; case CDIOCSETMUTE: error = cd_setchan(cd, MUTE_CHANNEL, MUTE_CHANNEL, MUTE_CHANNEL, MUTE_CHANNEL, 0); break; case CDIOCSETLEFT: error = cd_setchan(cd, LEFT_CHANNEL, LEFT_CHANNEL, MUTE_CHANNEL, MUTE_CHANNEL, 0); break; case CDIOCSETRIGHT: error = cd_setchan(cd, RIGHT_CHANNEL, RIGHT_CHANNEL, MUTE_CHANNEL, MUTE_CHANNEL, 0); break; case CDIOCRESUME: error = cd_pause(cd, 1); break; case CDIOCPAUSE: error = cd_pause(cd, 0); break; case CDIOCSTART: error = scsi_start(cd->sc_link, SSS_START, 0); break; case CDIOCSTOP: error = scsi_start(cd->sc_link, SSS_STOP, 0); break; close_tray: case CDIOCCLOSE: error = scsi_start(cd->sc_link, SSS_START|SSS_LOEJ, SCSI_IGNORE_NOT_READY | SCSI_IGNORE_MEDIA_CHANGE); break; case MTIOCTOP: if (((struct mtop *)addr)->mt_op == MTRETEN) goto close_tray; if (((struct mtop *)addr)->mt_op != MTOFFL) { error = EIO; break; } /* FALLTHROUGH */ case CDIOCEJECT: /* FALLTHROUGH */ case DIOCEJECT: cd->sc_link->flags |= SDEV_EJECTING; break; case CDIOCALLOW: error = scsi_prevent(cd->sc_link, PR_ALLOW, 0); break; case CDIOCPREVENT: error = scsi_prevent(cd->sc_link, PR_PREVENT, 0); break; case DIOCLOCK: error = scsi_prevent(cd->sc_link, (*(int *)addr) ? PR_PREVENT : PR_ALLOW, 0); break; case CDIOCSETDEBUG: cd->sc_link->flags |= (SDEV_DB1 | SDEV_DB2); break; case CDIOCCLRDEBUG: cd->sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2); break; case CDIOCRESET: case SCIOCRESET: error = cd_reset(cd); break; case CDIOCLOADUNLOAD: { struct ioc_load_unload *args = (struct ioc_load_unload *)addr; error = cd_load_unload(cd, args->options, args->slot); break; } case DVD_AUTH: error = dvd_auth(cd, (union dvd_authinfo *)addr); break; case DVD_READ_STRUCT: error = dvd_read_struct(cd, (union dvd_struct *)addr); break; default: if (DISKPART(dev) != RAW_PART) { error = ENOTTY; break; } error = scsi_do_ioctl(cd->sc_link, dev, cmd, addr, flag, p); break; } exit: device_unref(&cd->sc_dev); return (error); }
/* * Open the device. Make sure the partition info is as up-to-date as can be. */ int cdopen(dev_t dev, int flag, int fmt, struct proc *p) { struct scsi_link *sc_link; struct cd_softc *cd; int error = 0, part, rawopen, unit; unit = DISKUNIT(dev); part = DISKPART(dev); rawopen = (part == RAW_PART) && (fmt == S_IFCHR); cd = cdlookup(unit); if (cd == NULL) return (ENXIO); sc_link = cd->sc_link; SC_DEBUG(sc_link, SDEV_DB1, ("cdopen: dev=0x%x (unit %d (of %d), partition %d)\n", dev, unit, cd_cd.cd_ndevs, part)); if ((error = cdlock(cd)) != 0) { device_unref(&cd->sc_dev); return (error); } if (cd->sc_dk.dk_openmask != 0) { /* * If any partition is open, but the disk has been invalidated, * disallow further opens. */ if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0) { if (rawopen) goto out; error = EIO; goto bad; } } else { /* * Check that it is still responding and ok. Drive can be in * progress of loading media so use increased retries number * and don't ignore NOT_READY. */ /* Use cd_interpret_sense() now. */ sc_link->flags |= SDEV_OPEN; error = scsi_test_unit_ready(sc_link, TEST_READY_RETRIES, (rawopen ? SCSI_SILENT : 0) | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE); /* Start the cd spinning if necessary. */ if (error == EIO) error = scsi_start(sc_link, SSS_START, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT); if (error) { if (rawopen) { error = 0; goto out; } else goto bad; } /* Lock the cd in. */ error = scsi_prevent(sc_link, PR_PREVENT, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT); if (error) goto bad; /* Load the physical device parameters. */ sc_link->flags |= SDEV_MEDIA_LOADED; if (cd_get_parms(cd, (rawopen ? SCSI_SILENT : 0) | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE)) { sc_link->flags &= ~SDEV_MEDIA_LOADED; error = ENXIO; goto bad; } SC_DEBUG(sc_link, SDEV_DB3, ("Params loaded\n")); /* Fabricate a disk label. */ cdgetdisklabel(dev, cd, cd->sc_dk.dk_label, 0); SC_DEBUG(sc_link, SDEV_DB3, ("Disklabel fabricated\n")); } /* Check that the partition exists. */ if (part != RAW_PART && (part >= cd->sc_dk.dk_label->d_npartitions || cd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { error = ENXIO; goto bad; } out: /* Insure only one open at a time. */ switch (fmt) { case S_IFCHR: cd->sc_dk.dk_copenmask |= (1 << part); break; case S_IFBLK: cd->sc_dk.dk_bopenmask |= (1 << part); break; } cd->sc_dk.dk_openmask = cd->sc_dk.dk_copenmask | cd->sc_dk.dk_bopenmask; sc_link->flags |= SDEV_OPEN; SC_DEBUG(sc_link, SDEV_DB3, ("open complete\n")); /* It's OK to fall through because dk_openmask is now non-zero. */ bad: if (cd->sc_dk.dk_openmask == 0) { scsi_prevent(sc_link, PR_ALLOW, SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT); sc_link->flags &= ~(SDEV_OPEN | SDEV_MEDIA_LOADED); } cdunlock(cd); device_unref(&cd->sc_dev); return (error); }
/* * 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); }
/* * 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); }
/* scsi_unit_not_ready() * * If the sense key is SCSI_NOT_READY then this function checks the additional * sense key & its qualifier to determine the final sense outcome. Return: * * SENSE_OK No error. Continue the operation. * SENSE_RETRY The drive is in a transitional state, retry the operation. * SENSE_FATAL The drive is not ready. Stop the operation. */ status_t scsi_unit_not_ready( SCSI_device_s * psDevice, SCSI_sense_s * psSense ) { int nError; /* Find out why the unit is not ready */ switch ( psSense->asc ) { case SCSI_NO_ASC_DATA: { kerndbg( KERN_DEBUG_LOW, "With additional code SCSI_NO_ASC_DATA\n" ); /* Probably worth trying again */ nError = SENSE_RETRY; break; } case SCSI_LOGICAL_UNIT_NOT_READY: { kerndbg( KERN_DEBUG_LOW, "With additional code SCSI_LOGICAL_UNIT_NOT_READY\n" ); /* Find out why it isn't ready */ switch ( psSense->ascq ) { case SCSI_NOT_REPORTABLE: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_NOT_REPORTABLE\n" ); /* We don't know what this error is, may as well try again */ nError = SENSE_RETRY; break; } case SCSI_BECOMING_READY: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_BECOMING_READY\n" ); /* Wait for the drive to become ready. Delay a little to give the drive a chance to spin up */ udelay( 1000 ); nError = SENSE_RETRY; break; } case SCSI_MUST_INITIALIZE: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_MUST_INITIALIZE\n" ); /* Need to send a START/STOP to the drive */ if( scsi_start( psDevice ) < 0 ) { nError = SENSE_FATAL; } else nError = SENSE_RETRY; break; } case SCSI_MANUAL_INTERVENTION: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_MANUAL_INTERVENTION\n" ); /* Nothing we can do */ nError = SENSE_FATAL; break; } case SCSI_FORMAT_IN_PROGRESS: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_FORMAT_IN_PROGRESS\n" ); /* Should never happen */ nError = SENSE_FATAL; break; } case SCSI_REBUILD_IN_PROGRESS: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_REBUILD_IN_PROGRESS\n" ); /* Should never happen */ nError = SENSE_FATAL; break; } case SCSI_RECALC_IN_PROGRESS: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_RECALC_IN_PROGRESS\n" ); /* Should never happen */ nError = SENSE_FATAL; break; } case SCSI_OP_IN_PROGRESS: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_OP_IN_PROGRESS\n" ); /* Should never happen */ nError = SENSE_FATAL; break; } case SCSI_LONG_WRITE_IN_PROGRESS: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_LONG_WRITE_IN_PROGRESS\n" ); /* Should never happen */ nError = SENSE_FATAL; break; } case SCSI_SELF_TEST_IN_PROGRESS: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_SELF_TEST_IN_PROGRESS\n" ); /* Should never happen */ nError = SENSE_FATAL; break; } case SCSI_ASSYM_ACCESS_STATE_TRANS: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_ASSYM_ACCESS_STATE_TRANS\n" ); /* Should never happen */ nError = SENSE_FATAL; break; } case SCSI_TARGET_PORT_STANDBY: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_TARGET_PORT_STANDBY\n" ); /* Should never happen */ nError = SENSE_FATAL; break; } case SCSI_TARGET_PORT_UNAVAILABLE: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_TARGET_PORT_UNAVAILABLE\n" ); /* Should never happen */ nError = SENSE_FATAL; break; } case SCSI_AUX_MEM_UNAVAILABLE: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_AUX_MEM_UNAVAILABLE\n" ); /* Should never happen */ nError = SENSE_FATAL; break; } case SCSI_NOTIFY_REQUIRED: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_NOTIFY_REQUIRED\n" ); /* Send notify? */ nError = SENSE_FATAL; break; } default: { kerndbg( KERN_DEBUG_LOW, "and an unknown qualifier 0x%04x\n", psSense->ascq ); /* We don't know what this error is */ nError = SENSE_FATAL; break; } } break; } case SCSI_NOT_RESPONDING: { kerndbg( KERN_DEBUG_LOW, "With additional code SCSI_NOT_RESPONDING\n" ); /* Give up */ nError = SENSE_FATAL; break; } case SCSI_MEDIUM: { kerndbg( KERN_DEBUG_LOW, "With additional code SCSI_MEDIUM\n" ); /* It helps if the disk is in the drive. Is it? */ switch ( psSense->ascq ) { case SCSI_MEDIUM_TRAY_OPEN: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_MEDIUM_TRAY_OPEN\n" ); /* We have the option of sending a START/STOP to close the drive but that could be evil, so we won't */ nError = SENSE_FATAL; break; } case SCSI_MEDIUM_NOT_PRESENT: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_MEDIUM_NOT_PRESENT\n" ); /* Put the disk in the drive, dum dum! */ nError = SENSE_FATAL; break; } case SCSI_MEDIUM_TRAY_CLOSED: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_MEDIUM_TRAY_CLOSED\n" ); /* This is a bit of a silly error really; why is it an error if the tray is closed? */ nError = SENSE_FATAL; break; } case SCSI_MEDIUM_LOADABLE: { kerndbg( KERN_DEBUG_LOW, "and a qualifier of SCSI_MEDIUM_LOADABLE\n" ); /* We can't load it ourselves */ nError = SENSE_FATAL; break; } default: { kerndbg( KERN_DEBUG_LOW, "and an unknown qualifier 0x%04x\n", psSense->ascq ); /* We don't know what this error is */ nError = SENSE_FATAL; break; } } break; } default: { kerndbg( KERN_WARNING, "With an unknown or unsupported additional code 0x%04x\n", psSense->asc ); /* The drive did something screwy, possibly a deprecated sense condition, but still.. */ nError = SENSE_FATAL; break; } } return nError; }