/* * 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); }
/* * 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); }
/* * 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); }