Esempio n. 1
0
File: sd.c Progetto: sofuture/bitrig
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);
}
Esempio n. 2
0
/*
 * 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;
}
Esempio n. 3
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);
}
Esempio n. 4
0
/*
 * 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;
}
Esempio n. 5
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);
}
Esempio n. 6
0
/*
 * 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;
}
Esempio n. 7
0
File: sd.c Progetto: sofuture/bitrig
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);
}
Esempio n. 8
0
/*
 * 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);
}
Esempio n. 9
0
/*
 * 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);
}
Esempio n. 10
0
/*
 * 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);
}
Esempio n. 11
0
/*
 * 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);
}
Esempio n. 12
0
/* 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;
}