Esempio n. 1
0
/*
 * Perform a READ ELEMENT STATUS on behalf of the user, and return to
 * the user only the data the user is interested in.  This returns the
 * old data format.
 */
static int
ch_ousergetelemstatus(struct ch_softc *sc, int chet, u_int8_t *uptr)
{
	struct read_element_status_header *st_hdrp, st_hdr;
	struct read_element_status_page_header *pg_hdrp;
	struct read_element_status_descriptor *desc;
	size_t size, desclen;
	void *data;
	int avail, i, error = 0;
	u_int8_t user_data;

	/*
	 * If there are no elements of the requested type in the changer,
	 * the request is invalid.
	 */
	if (sc->sc_counts[chet] == 0)
		return (EINVAL);

	/*
	 * Do the request the user wants, but only read the status header.
	 * This will tell us the amount of storage we must allocate in
	 * order to read all data.
	 */
	error = ch_getelemstatus(sc, sc->sc_firsts[chet],
	    sc->sc_counts[chet], &st_hdr, sizeof(st_hdr), 0, 0);
	if (error)
		return (error);

	size = sizeof(struct read_element_status_header) +
	    _3btol(st_hdr.nbytes);

	/*
	 * We must have at least room for the status header and
	 * one page header (since we only ask for one element type
	 * at a time).
	 */
	if (size < (sizeof(struct read_element_status_header) +
	    sizeof(struct read_element_status_page_header)))
		return (EIO);

	/*
	 * Allocate the storage and do the request again.
	 */
	data = malloc(size, M_DEVBUF, M_WAITOK);
	error = ch_getelemstatus(sc, sc->sc_firsts[chet],
	    sc->sc_counts[chet], data, size, 0, 0);
	if (error)
		goto done;

	st_hdrp = (struct read_element_status_header *)data;
	pg_hdrp = (struct read_element_status_page_header *)((u_long)st_hdrp +
	    sizeof(struct read_element_status_header));
	desclen = _2btol(pg_hdrp->edl);

	/*
	 * Fill in the user status array.
	 */
	avail = _2btol(st_hdrp->count);

	if (avail != sc->sc_counts[chet])
		printf("%s: warning, READ ELEMENT STATUS avail != count\n",
		    device_xname(sc->sc_dev));

	desc = (struct read_element_status_descriptor *)((u_long)data +
	    sizeof(struct read_element_status_header) +
	    sizeof(struct read_element_status_page_header));
	for (i = 0; i < avail; ++i) {
		user_data = desc->flags1;
		error = copyout(&user_data, &uptr[i], avail);
		if (error)
			break;
		desc = (struct read_element_status_descriptor *)((u_long)desc
		    + desclen);
	}

 done:
	if (data != NULL)
		free(data, M_DEVBUF);
	return (error);
}
Esempio n. 2
0
/*
 * Perform a READ ELEMENT STATUS on behalf of the user.  This returns
 * the new (more complete) data format.
 */
static int
ch_usergetelemstatus(struct ch_softc *sc,
    struct changer_element_status_request *cesr)
{
	struct scsipi_channel *chan = sc->sc_periph->periph_channel;
	struct scsipi_periph *dtperiph;
	struct read_element_status_header *st_hdrp, st_hdr;
	struct read_element_status_page_header *pg_hdrp;
	struct read_element_status_descriptor *desc;
	struct changer_volume_tag *avol, *pvol;
	size_t size, desclen, stddesclen, offset;
	int first, avail, i, error = 0;
	void *data;
	void *uvendptr;
	struct changer_element_status ces;

	/*
	 * Check arguments.
	 */
	if (cesr->cesr_type > CHET_DT)
		return (EINVAL);
	if (sc->sc_counts[cesr->cesr_type] == 0)
		return (ENODEV);
	if (cesr->cesr_unit > (sc->sc_counts[cesr->cesr_type] - 1))
		return (ENODEV);
	if (cesr->cesr_count >
	    (sc->sc_counts[cesr->cesr_type] + cesr->cesr_unit))
		return (EINVAL);

	/*
	 * Do the request the user wants, but only read the status header.
	 * This will tell us the amount of storage we must allocate
	 * in order to read all the data.
	 */
	error = ch_getelemstatus(sc, sc->sc_firsts[cesr->cesr_type] +
	    cesr->cesr_unit, cesr->cesr_count, &st_hdr, sizeof(st_hdr), 0,
	    cesr->cesr_flags);
	if (error)
		return (error);

	size = sizeof(struct read_element_status_header) +
	    _3btol(st_hdr.nbytes);

	/*
	 * We must have at least room for the status header and
	 * one page header (since we only ask for oen element type
	 * at a time).
	 */
	if (size < (sizeof(struct read_element_status_header) +
	    sizeof(struct read_element_status_page_header)))
		return (EIO);

	/*
	 * Allocate the storage and do the request again.
	 */
	data = malloc(size, M_DEVBUF, M_WAITOK);
	error = ch_getelemstatus(sc, sc->sc_firsts[cesr->cesr_type] +
	    cesr->cesr_unit, cesr->cesr_count, data, size, 0,
	    cesr->cesr_flags);
	if (error)
		goto done;

	st_hdrp = (struct read_element_status_header *)data;
	pg_hdrp = (struct read_element_status_page_header *)((u_long)st_hdrp +
	    sizeof(struct read_element_status_header));
	desclen = _2btol(pg_hdrp->edl);

	/*
	 * Fill in the user status array.
	 */
	first = _2btol(st_hdrp->fear);
	if (first <  (sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit) ||
	    first >= (sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit +
		      cesr->cesr_count)) {
		error = EIO;
		goto done;
	}
	first -= sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit;

	avail = _2btol(st_hdrp->count);
	if (avail <= 0 || avail > cesr->cesr_count) {
		error = EIO;
		goto done;
	}

	offset = sizeof(struct read_element_status_header) +
		 sizeof(struct read_element_status_page_header);

	for (i = 0; i < cesr->cesr_count; i++) {
		memset(&ces, 0, sizeof(ces));
		if (i < first || i >= (first + avail)) {
			error = copyout(&ces, &cesr->cesr_data[i],
			    sizeof(ces));
			if (error)
				goto done;
		}

		desc = (struct read_element_status_descriptor *)
		    ((char *)data + offset);
		stddesclen = sizeof(struct read_element_status_descriptor);
		offset += desclen;

		ces.ces_flags = CESTATUS_STATUS_VALID;

		/*
		 * The SCSI flags conveniently map directly to the
		 * chio API flags.
		 */
		ces.ces_flags |= (desc->flags1 & 0x3f);

		ces.ces_asc = desc->sense_code;
		ces.ces_ascq = desc->sense_qual;

		/*
		 * For Data Transport elemenets, get the SCSI ID and LUN,
		 * and attempt to map them to a device name if they're
		 * on the same SCSI bus.
		 */
		if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_IDVALID) {
			ces.ces_target = desc->dt_scsi_addr;
			ces.ces_flags |= CESTATUS_TARGET_VALID;
		}
		if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_LUVALID) {
			ces.ces_lun = desc->dt_scsi_flags &
			    READ_ELEMENT_STATUS_DT_LUNMASK;
			ces.ces_flags |= CESTATUS_LUN_VALID;
		}
		if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_NOTBUS)
			ces.ces_flags |= CESTATUS_NOTBUS;
		else if ((ces.ces_flags &
			  (CESTATUS_TARGET_VALID|CESTATUS_LUN_VALID)) ==
			 (CESTATUS_TARGET_VALID|CESTATUS_LUN_VALID)) {
			if (ces.ces_target < chan->chan_ntargets &&
			    ces.ces_lun < chan->chan_nluns &&
			    (dtperiph = scsipi_lookup_periph(chan,
			     ces.ces_target, ces.ces_lun)) != NULL &&
			    dtperiph->periph_dev != NULL) {
				strlcpy(ces.ces_xname,
				    device_xname(dtperiph->periph_dev),
				    sizeof(ces.ces_xname));
				ces.ces_flags |= CESTATUS_XNAME_VALID;
			}
		}

		if (desc->flags2 & READ_ELEMENT_STATUS_INVERT)
			ces.ces_flags |= CESTATUS_INVERTED;

		if (desc->flags2 & READ_ELEMENT_STATUS_SVALID) {
			if (ch_map_element(sc, _2btol(desc->ssea),
			    &ces.ces_from_type, &ces.ces_from_unit))
				ces.ces_flags |= CESTATUS_FROM_VALID;
		}

		/*
		 * Extract volume tag information.
		 */
		switch (pg_hdrp->flags &
		    (READ_ELEMENT_STATUS_PVOLTAG|READ_ELEMENT_STATUS_AVOLTAG)) {
		case (READ_ELEMENT_STATUS_PVOLTAG|READ_ELEMENT_STATUS_AVOLTAG):
			pvol = (struct changer_volume_tag *)(desc + 1);
			avol = pvol + 1;
			break;

		case READ_ELEMENT_STATUS_PVOLTAG:
			pvol = (struct changer_volume_tag *)(desc + 1);
			avol = NULL;
			break;

		case READ_ELEMENT_STATUS_AVOLTAG:
			pvol = NULL;
			avol = (struct changer_volume_tag *)(desc + 1);
			break;

		default:
			avol = pvol = NULL;
			break;
		}

		if (pvol != NULL) {
			ch_voltag_convert_in(pvol, &ces.ces_pvoltag);
			ces.ces_flags |= CESTATUS_PVOL_VALID;
			stddesclen += sizeof(struct changer_volume_tag);
		}
		if (avol != NULL) {
			ch_voltag_convert_in(avol, &ces.ces_avoltag);
			ces.ces_flags |= CESTATUS_AVOL_VALID;
			stddesclen += sizeof(struct changer_volume_tag);
		}

		/*
		 * Compute vendor-specific length.  Note the 4 reserved
		 * bytes between the volume tags and the vendor-specific
		 * data.  Copy it out of the user wants it.
		 */
		stddesclen += 4;
		if (desclen > stddesclen)
			ces.ces_vendor_len = desclen - stddesclen;

		if (ces.ces_vendor_len != 0 && cesr->cesr_vendor_data != NULL) {
			error = copyin(&cesr->cesr_vendor_data[i], &uvendptr,
			    sizeof(uvendptr));
			if (error)
				goto done;
			error = copyout((void *)((u_long)desc + stddesclen),
			    uvendptr, ces.ces_vendor_len);
			if (error)
				goto done;
		}

		/*
		 * Now copy out the status descriptor we've constructed.
		 */
		error = copyout(&ces, &cesr->cesr_data[i], sizeof(ces));
		if (error)
			goto done;
	}

 done:
	if (data != NULL)
		free(data, M_DEVBUF);
	return (error);
}
Esempio n. 3
0
/*
 * Perform a READ ELEMENT STATUS on behalf of the user, and return to
 * the user only the data the user is interested in (i.e. an array of
 * changer_element_status structures)
 */
int
ch_usergetelemstatus(struct ch_softc *sc,
    struct changer_element_status_request *cesr)
{
	struct changer_element_status *user_data = NULL;
	struct read_element_status_header *st_hdr;
	struct read_element_status_page_header *pg_hdr;
	caddr_t desc;
	caddr_t data = NULL;
	size_t size, desclen, udsize;
	int chet = cesr->cesr_type;
	int avail, i, error = 0;
	int want_voltags = (cesr->cesr_flags & CESR_VOLTAGS) ? 1 : 0;

	/*
	 * If there are no elements of the requested type in the changer,
	 * the request is invalid.
	 */
	if (sc->sc_counts[chet] == 0)
		return (EINVAL);

	/*
	 * Request one descriptor for the given element type.  This
	 * is used to determine the size of the descriptor so that
	 * we can allocate enough storage for all of them.  We assume
	 * that the first one can fit into 1k.
	 */
	size = 1024;
	data = dma_alloc(size, PR_WAITOK);
	error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, size,
	    want_voltags);
	if (error)
		goto done;

	st_hdr = (struct read_element_status_header *)data;
	pg_hdr = (struct read_element_status_page_header *) (st_hdr + 1);
	desclen = _2btol(pg_hdr->edl);

	dma_free(data, size);

	/*
	 * Reallocate storage for descriptors and get them from the
	 * device.
	 */
	size = sizeof(struct read_element_status_header) +
	    sizeof(struct read_element_status_page_header) +
	    (desclen * sc->sc_counts[chet]);
	data = dma_alloc(size, PR_WAITOK);
	error = ch_getelemstatus(sc, sc->sc_firsts[chet],
	    sc->sc_counts[chet], data, size, want_voltags);
	if (error)
		goto done;

	/*
	 * Fill in the user status array.
	 */
	st_hdr = (struct read_element_status_header *)data;
	pg_hdr = (struct read_element_status_page_header *) (st_hdr + 1);

	avail = _2btol(st_hdr->count);
	if (avail != sc->sc_counts[chet]) {
		error = EINVAL;
		goto done;
	}

	user_data = mallocarray(avail, sizeof(struct changer_element_status),
	    M_DEVBUF, M_WAITOK | M_ZERO);
	udsize = avail * sizeof(struct changer_element_status);

	desc = (caddr_t)(pg_hdr + 1);
	for (i = 0; i < avail; ++i) {
		struct changer_element_status *ces = &(user_data[i]);
		copy_element_status(pg_hdr->flags,
		    (struct read_element_status_descriptor *)desc, ces);
		desc += desclen;
	}

	/* Copy array out to userspace. */
	error = copyout(user_data, cesr->cesr_data, udsize);

 done:
	if (data != NULL)
		dma_free(data, size);
	if (user_data != NULL)
		free(user_data, M_DEVBUF, udsize);
	return (error);
}