summaryrefslogtreecommitdiffstats
path: root/sys/cam
diff options
context:
space:
mode:
authorken <ken@FreeBSD.org>2013-04-19 20:03:51 +0000
committerken <ken@FreeBSD.org>2013-04-19 20:03:51 +0000
commit9a543047eb5c6caa745ab785627c29aa050f3fe6 (patch)
treea42129446c56829e1277cb13ece82f7720559ea6 /sys/cam
parent120125784a1ca698c656c6ef5e21e219df994f64 (diff)
downloadFreeBSD-src-9a543047eb5c6caa745ab785627c29aa050f3fe6.zip
FreeBSD-src-9a543047eb5c6caa745ab785627c29aa050f3fe6.tar.gz
Update chio(1) and ch(4) to support reporting element designators.
This allows mapping a tape drive in a changer (as reported by 'chio status') to a sa(4) driver instance by comparing the serial numbers. The designators can be ASCII (which is printed out directly), binary (which is printed in hex format) or UTF-8, which is printed in either native UTF-8 format if the terminal can support it, or in %XX notation for non-ASCII characters. Thanks to Hiroki Sato <hrs@> for the explaining UTF-8 printing and example UTF-8 printing code. chio.h: Modify the changer_element_status structure to add new fields and definitions from the SMC3r16 spec. Rename the original CHIOGSTATUS ioctl to OCHIOGTATUS and define a new CHIOGSTATUS ioctl. Clean up some tab/space issues. chio.c: For the 'status' subcommand, print the designator field if it is supplied by a device. scsi_ch.h: Add new flags for DVCID and CURDATA to the READ ELEMENT STATUS command structure. Add a read_element_status_device_id structure for the data fields in the new standard. Add new unions, dt_or_obsolete and voltage_devid, to hold and address data from either SCSI-2 or newer devices. scsi_ch.c: Implement support for fetching device IDs with READ ELEMENT STATUS data. Add new arguments to scsi_read_element_status() to allow the user to request the DVCID and CURDATA bits. This isn't compiled into libcam (it's only an internal kernel interface), so we don't need any special handling for the API change. If the user issues the new CHIOGSTATUS ioctl, copy all of the available element status data out. If he issues the OCHIOGSTATUS ioctl, we don't copy the new fields in the structure. Fix a bug in chopen() that would result in the peripheral never getting unheld if chgetparams() failed. Sponsored by: Spectra Logic Submitted by: Po-Li Soong MFC After: 1 week
Diffstat (limited to 'sys/cam')
-rw-r--r--sys/cam/scsi/scsi_ch.c203
-rw-r--r--sys/cam/scsi/scsi_ch.h97
2 files changed, 242 insertions, 58 deletions
diff --git a/sys/cam/scsi/scsi_ch.c b/sys/cam/scsi/scsi_ch.c
index 38dcd48..03dd5cd 100644
--- a/sys/cam/scsi/scsi_ch.c
+++ b/sys/cam/scsi/scsi_ch.c
@@ -194,12 +194,14 @@ static int chexchange(struct cam_periph *periph,
static int chposition(struct cam_periph *periph,
struct changer_position *cp);
static int chgetelemstatus(struct cam_periph *periph,
+ int scsi_version, u_long cmd,
struct changer_element_status_request *csr);
static int chsetvoltag(struct cam_periph *periph,
struct changer_set_voltag_request *csvr);
static int chielem(struct cam_periph *periph,
unsigned int timeout);
static int chgetparams(struct cam_periph *periph);
+static int chscsiversion(struct cam_periph *periph);
static struct periph_driver chdriver =
{
@@ -474,6 +476,7 @@ chopen(struct cdev *dev, int flags, int fmt, struct thread *td)
* Load information about this changer device into the softc.
*/
if ((error = chgetparams(periph)) != 0) {
+ cam_periph_unhold(periph);
cam_periph_release_locked(periph);
cam_periph_unlock(periph);
return(error);
@@ -772,6 +775,7 @@ chioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
switch (cmd) {
case CHIOGPICKER:
case CHIOGPARAMS:
+ case OCHIOGSTATUS:
case CHIOGSTATUS:
break;
@@ -824,10 +828,26 @@ chioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
error = chielem(periph, *(unsigned int *)addr);
break;
+ case OCHIOGSTATUS:
+ {
+ error = chgetelemstatus(periph, SCSI_REV_2, cmd,
+ (struct changer_element_status_request *)addr);
+ break;
+ }
+
case CHIOGSTATUS:
{
- error = chgetelemstatus(periph,
- (struct changer_element_status_request *) addr);
+ int scsi_version;
+
+ scsi_version = chscsiversion(periph);
+ if (scsi_version >= SCSI_REV_0) {
+ error = chgetelemstatus(periph, scsi_version, cmd,
+ (struct changer_element_status_request *)addr);
+ }
+ else { /* unable to determine the SCSI version */
+ cam_periph_unlock(periph);
+ return (ENXIO);
+ }
break;
}
@@ -1034,18 +1054,20 @@ copy_voltag(struct changer_voltag *uvoltag, struct volume_tag *voltag)
}
/*
- * Copy an an element status descriptor to a user-mode
+ * Copy an element status descriptor to a user-mode
* changer_element_status structure.
*/
-
-static void
+static void
copy_element_status(struct ch_softc *softc,
u_int16_t flags,
struct read_element_status_descriptor *desc,
- struct changer_element_status *ces)
+ struct changer_element_status *ces,
+ int scsi_version)
{
u_int16_t eaddr = scsi_2btoul(desc->eaddr);
u_int16_t et;
+ struct volume_tag *pvol_tag = NULL, *avol_tag = NULL;
+ struct read_element_status_device_id *devid = NULL;
ces->ces_int_addr = eaddr;
/* set up logical address in element status */
@@ -1076,7 +1098,7 @@ copy_element_status(struct ch_softc *softc,
if ((softc->sc_firsts[et] <= eaddr)
&& ((softc->sc_firsts[et] + softc->sc_counts[et])
> eaddr)) {
- ces->ces_source_addr =
+ ces->ces_source_addr =
eaddr - softc->sc_firsts[et];
ces->ces_source_type = et;
ces->ces_flags |= CES_SOURCE_VALID;
@@ -1089,27 +1111,92 @@ copy_element_status(struct ch_softc *softc,
"address %ud to a valid element type\n",
eaddr);
}
-
+ /*
+ * pvoltag and avoltag are common between SCSI-2 and later versions
+ */
if (flags & READ_ELEMENT_STATUS_PVOLTAG)
- copy_voltag(&(ces->ces_pvoltag), &(desc->pvoltag));
+ pvol_tag = &desc->voltag_devid.pvoltag;
if (flags & READ_ELEMENT_STATUS_AVOLTAG)
- copy_voltag(&(ces->ces_avoltag), &(desc->avoltag));
+ avol_tag = (flags & READ_ELEMENT_STATUS_PVOLTAG) ?
+ &desc->voltag_devid.voltag[1] :&desc->voltag_devid.pvoltag;
+ /*
+ * For SCSI-3 and later, element status can carry designator and
+ * other information.
+ */
+ if (scsi_version >= SCSI_REV_SPC) {
+ if ((flags & READ_ELEMENT_STATUS_PVOLTAG) ^
+ (flags & READ_ELEMENT_STATUS_AVOLTAG))
+ devid = &desc->voltag_devid.pvol_and_devid.devid;
+ else if (!(flags & READ_ELEMENT_STATUS_PVOLTAG) &&
+ !(flags & READ_ELEMENT_STATUS_AVOLTAG))
+ devid = &desc->voltag_devid.devid;
+ else /* Have both PVOLTAG and AVOLTAG */
+ devid = &desc->voltag_devid.vol_tags_and_devid.devid;
+ }
- if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_IDVALID) {
- ces->ces_flags |= CES_SCSIID_VALID;
- ces->ces_scsi_id = desc->dt_scsi_addr;
+ if (pvol_tag)
+ copy_voltag(&(ces->ces_pvoltag), pvol_tag);
+ if (avol_tag)
+ copy_voltag(&(ces->ces_pvoltag), avol_tag);
+ if (devid != NULL) {
+ if (devid->designator_length > 0) {
+ bcopy((void *)devid->designator,
+ (void *)ces->ces_designator,
+ devid->designator_length);
+ ces->ces_designator_length = devid->designator_length;
+ /*
+ * Make sure we are always NUL terminated. The
+ * buffer should be sized for the maximum
+ * designator length plus 1, but this will make sure
+ * there is always a NUL at the end. This won't
+ * matter for the binary code set, since the user
+ * will only pay attention to the length field.
+ */
+ ces->ces_designator[
+ MIN(sizeof(ces->ces_designator) - 1,
+ devid->designator_length)]= '\0';
+ }
+ if (devid->piv_assoc_designator_type &
+ READ_ELEMENT_STATUS_PIV_SET) {
+ ces->ces_flags |= CES_PIV;
+ ces->ces_protocol_id =
+ READ_ELEMENT_STATUS_PROTOCOL_ID(
+ devid->prot_code_set);
+ }
+ ces->ces_code_set =
+ READ_ELEMENT_STATUS_CODE_SET(devid->prot_code_set);
+ ces->ces_assoc = READ_ELEMENT_STATUS_ASSOCIATION(
+ devid->piv_assoc_designator_type);
+ ces->ces_designator_type = READ_ELEMENT_STATUS_DESIGNATOR_TYPE(
+ devid->piv_assoc_designator_type);
+ } else if (scsi_version > SCSI_REV_2) {
+ /* SCSI-SPC and No devid, no designator */
+ ces->ces_designator_length = 0;
+ ces->ces_designator[0] = '\0';
+ ces->ces_protocol_id = CES_PROTOCOL_ID_FCP_4;
}
- if (desc->dt_scsi_addr & READ_ELEMENT_STATUS_DT_LUVALID) {
- ces->ces_flags |= CES_LUN_VALID;
- ces->ces_scsi_lun =
- desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_LUNMASK;
+ if (scsi_version <= SCSI_REV_2) {
+ if (desc->dt_or_obsolete.scsi_2.dt_scsi_flags &
+ READ_ELEMENT_STATUS_DT_IDVALID) {
+ ces->ces_flags |= CES_SCSIID_VALID;
+ ces->ces_scsi_id =
+ desc->dt_or_obsolete.scsi_2.dt_scsi_addr;
+ }
+
+ if (desc->dt_or_obsolete.scsi_2.dt_scsi_addr &
+ READ_ELEMENT_STATUS_DT_LUVALID) {
+ ces->ces_flags |= CES_LUN_VALID;
+ ces->ces_scsi_lun =
+ desc->dt_or_obsolete.scsi_2.dt_scsi_flags &
+ READ_ELEMENT_STATUS_DT_LUNMASK;
+ }
}
}
static int
-chgetelemstatus(struct cam_periph *periph,
+chgetelemstatus(struct cam_periph *periph, int scsi_version, u_long cmd,
struct changer_element_status_request *cesr)
{
struct read_element_status_header *st_hdr;
@@ -1155,6 +1242,8 @@ chgetelemstatus(struct cam_periph *periph,
/* tag_action */ MSG_SIMPLE_Q_TAG,
/* voltag */ want_voltags,
/* sea */ softc->sc_firsts[chet],
+ /* dvcid */ 1,
+ /* curdata */ 1,
/* count */ 1,
/* data_ptr */ data,
/* dxfer_len */ 1024,
@@ -1177,7 +1266,6 @@ chgetelemstatus(struct cam_periph *periph,
size = sizeof(struct read_element_status_header) +
sizeof(struct read_element_status_page_header) +
(desclen * cesr->cesr_element_count);
-
/*
* Reallocate storage for descriptors and get them from the
* device.
@@ -1193,12 +1281,14 @@ chgetelemstatus(struct cam_periph *periph,
/* voltag */ want_voltags,
/* sea */ softc->sc_firsts[chet]
+ cesr->cesr_element_base,
+ /* dvcid */ 1,
+ /* curdata */ 1,
/* count */ cesr->cesr_element_count,
/* data_ptr */ data,
/* dxfer_len */ size,
/* sense_len */ SSD_FULL_SIZE,
/* timeout */ CH_TIMEOUT_READ_ELEMENT_STATUS);
-
+
error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ CAM_RETRY_SELTO,
/*sense_flags*/ SF_RETRY_UA,
softc->device_stats);
@@ -1231,18 +1321,41 @@ chgetelemstatus(struct cam_periph *periph,
* Set up the individual element status structures
*/
for (i = 0; i < avail; ++i) {
- struct changer_element_status *ces = &(user_data[i]);
+ struct changer_element_status *ces;
- copy_element_status(softc, pg_hdr->flags, desc, ces);
+ /*
+ * In the changer_element_status structure, fields from
+ * the beginning to the field of ces_scsi_lun are common
+ * between SCSI-2 and SCSI-3, while all the rest are new
+ * from SCSI-3. In order to maintain backward compatibility
+ * of the chio command, the ces pointer, below, is computed
+ * such that it lines up with the structure boundary
+ * corresponding to the SCSI version.
+ */
+ ces = cmd == OCHIOGSTATUS ?
+ (struct changer_element_status *)
+ ((unsigned char *)user_data + i *
+ (offsetof(struct changer_element_status,ces_scsi_lun)+1)):
+ &user_data[i];
+
+ copy_element_status(softc, pg_hdr->flags, desc,
+ ces, scsi_version);
desc = (struct read_element_status_descriptor *)
- ((uintptr_t)desc + desclen);
+ ((unsigned char *)desc + desclen);
}
/* Copy element status structures out to userspace. */
- error = copyout(user_data,
- cesr->cesr_element_status,
- avail * sizeof(struct changer_element_status));
+ if (cmd == OCHIOGSTATUS)
+ error = copyout(user_data,
+ cesr->cesr_element_status,
+ avail* (offsetof(struct changer_element_status,
+ ces_scsi_lun) + 1));
+ else
+ error = copyout(user_data,
+ cesr->cesr_element_status,
+ avail * sizeof(struct changer_element_status));
+
cam_periph_lock(periph);
done:
@@ -1549,6 +1662,39 @@ chgetparams(struct cam_periph *periph)
return(error);
}
+static int
+chscsiversion(struct cam_periph *periph)
+{
+ struct scsi_inquiry_data *inq_data;
+ struct ccb_getdev *cgd;
+ int dev_scsi_version;
+ struct cam_sim *sim;
+
+ sim = xpt_path_sim(periph->path);
+ mtx_assert(sim->mtx, MA_OWNED);
+ if ((cgd = (struct ccb_getdev *)xpt_alloc_ccb_nowait()) == NULL)
+ return (-1);
+ /*
+ * Get the device information.
+ */
+ xpt_setup_ccb(&cgd->ccb_h,
+ periph->path,
+ CAM_PRIORITY_NORMAL);
+ cgd->ccb_h.func_code = XPT_GDEV_TYPE;
+ xpt_action((union ccb *)cgd);
+
+ if (cgd->ccb_h.status != CAM_REQ_CMP) {
+ xpt_free_ccb((union ccb *)cgd);
+ return -1;
+ }
+
+ inq_data = &cgd->inq_data;
+ dev_scsi_version = inq_data->version;
+ xpt_free_ccb((union ccb *)cgd);
+
+ return dev_scsi_version;
+}
+
void
scsi_move_medium(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
@@ -1654,6 +1800,7 @@ void
scsi_read_element_status(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
u_int8_t tag_action, int voltag, u_int32_t sea,
+ int curdata, int dvcid,
u_int32_t count, u_int8_t *data_ptr,
u_int32_t dxfer_len, u_int8_t sense_len,
u_int32_t timeout)
@@ -1668,6 +1815,10 @@ scsi_read_element_status(struct ccb_scsiio *csio, u_int32_t retries,
scsi_ulto2b(sea, scsi_cmd->sea);
scsi_ulto2b(count, scsi_cmd->count);
scsi_ulto3b(dxfer_len, scsi_cmd->len);
+ if (dvcid)
+ scsi_cmd->flags |= READ_ELEMENT_STATUS_DVCID;
+ if (curdata)
+ scsi_cmd->flags |= READ_ELEMENT_STATUS_CURDATA;
if (voltag)
scsi_cmd->byte2 |= READ_ELEMENT_STATUS_VOLTAG;
diff --git a/sys/cam/scsi/scsi_ch.h b/sys/cam/scsi/scsi_ch.h
index 5ee2244..fd317d2 100644
--- a/sys/cam/scsi/scsi_ch.h
+++ b/sys/cam/scsi/scsi_ch.h
@@ -136,11 +136,14 @@ struct scsi_position_to_element {
struct scsi_read_element_status {
u_int8_t opcode;
u_int8_t byte2;
-#define READ_ELEMENT_STATUS_VOLTAG 0x10 /* report volume tag info */
+#define READ_ELEMENT_STATUS_VOLTAG 0x10 /* report volume tag info */
/* ...next 4 bits are an element type code... */
u_int8_t sea[2]; /* starting element address */
u_int8_t count[2]; /* number of elements */
- u_int8_t reserved0;
+ u_int8_t flags;
+#define READ_ELEMENT_STATUS_DVCID 0x01 /* report device serial number */
+#define READ_ELEMENT_STATUS_CURDATA 0x02 /* allow motion during command */
+
u_int8_t len[3]; /* length of data buffer */
u_int8_t reserved1;
u_int8_t control;
@@ -149,7 +152,7 @@ struct scsi_read_element_status {
struct scsi_request_volume_element_address {
u_int8_t opcode;
u_int8_t byte2;
-#define REQUEST_VOLUME_ELEMENT_ADDRESS_VOLTAG 0x10
+#define REQUEST_VOLUME_ELEMENT_ADDRESS_VOLTAG 0x10
/* ...next 4 bits are an element type code... */
u_int8_t eaddr[2]; /* element address */
u_int8_t count[2]; /* number of elements */
@@ -182,8 +185,8 @@ struct read_element_status_header {
struct read_element_status_page_header {
u_int8_t type; /* element type code; see type codes below */
u_int8_t flags;
-#define READ_ELEMENT_STATUS_AVOLTAG 0x40
-#define READ_ELEMENT_STATUS_PVOLTAG 0x80
+#define READ_ELEMENT_STATUS_AVOLTAG 0x40
+#define READ_ELEMENT_STATUS_PVOLTAG 0x80
u_int8_t edl[2]; /* element descriptor length */
u_int8_t reserved;
u_int8_t nbytes[3]; /* byte count of all descriptors */
@@ -199,50 +202,79 @@ struct volume_tag {
u_int8_t vsn[2]; /* volume sequence number */
};
+struct read_element_status_device_id {
+ u_int8_t prot_code_set;
+#define READ_ELEMENT_STATUS_CODE_SET(p) ((p) & 0x0F)
+#define READ_ELEMENT_STATUS_PROTOCOL_ID(p) ((p) >> 4)
+
+ u_int8_t piv_assoc_designator_type;
+#define READ_ELEMENT_STATUS_PIV_SET 0x80
+#define READ_ELEMENT_STATUS_ASSOCIATION(p) ((p) >> 4)
+#define READ_ELEMENT_STATUS_DESIGNATOR_TYPE(p) ((p) & 0x0F)
+
+ u_int8_t reserved2;
+ u_int8_t designator_length;
+ u_int8_t designator[256]; /* Allocate max length */
+};
+
struct read_element_status_descriptor {
u_int8_t eaddr[2]; /* element address */
u_int8_t flags1;
-#define READ_ELEMENT_STATUS_FULL 0x01
-#define READ_ELEMENT_STATUS_IMPEXP 0x02
-#define READ_ELEMENT_STATUS_EXCEPT 0x04
-#define READ_ELEMENT_STATUS_ACCESS 0x08
-#define READ_ELEMENT_STATUS_EXENAB 0x10
-#define READ_ELEMENT_STATUS_INENAB 0x20
+#define READ_ELEMENT_STATUS_FULL 0x01
+#define READ_ELEMENT_STATUS_IMPEXP 0x02
+#define READ_ELEMENT_STATUS_EXCEPT 0x04
+#define READ_ELEMENT_STATUS_ACCESS 0x08
+#define READ_ELEMENT_STATUS_EXENAB 0x10
+#define READ_ELEMENT_STATUS_INENAB 0x20
-#define READ_ELEMENT_STATUS_MT_MASK1 0x05
-#define READ_ELEMENT_STATUS_ST_MASK1 0x0c
-#define READ_ELEMENT_STATUS_IE_MASK1 0x3f
-#define READ_ELEMENT_STATUS_DT_MASK1 0x0c
+#define READ_ELEMENT_STATUS_MT_MASK1 0x05
+#define READ_ELEMENT_STATUS_ST_MASK1 0x0c
+#define READ_ELEMENT_STATUS_IE_MASK1 0x3f
+#define READ_ELEMENT_STATUS_DT_MASK1 0x0c
u_int8_t reserved0;
u_int8_t sense_code;
u_int8_t sense_qual;
- /*
- * dt_scsi_flags and dt_scsi_addr are valid only on data transport
- * elements. These bytes are undefined for all other element types.
- */
- u_int8_t dt_scsi_flags;
+ union {
+ struct {
+ u_int8_t dt_scsi_flags;
-#define READ_ELEMENT_STATUS_DT_LUNMASK 0x07
-#define READ_ELEMENT_STATUS_DT_LUVALID 0x10
-#define READ_ELEMENT_STATUS_DT_IDVALID 0x20
-#define READ_ELEMENT_STATUS_DT_NOTBUS 0x80
+#define READ_ELEMENT_STATUS_DT_LUNMASK 0x07
+#define READ_ELEMENT_STATUS_DT_LUVALID 0x10
+#define READ_ELEMENT_STATUS_DT_IDVALID 0x20
+#define READ_ELEMENT_STATUS_DT_NOTBUS 0x80
- u_int8_t dt_scsi_addr;
+ u_int8_t dt_scsi_addr;
+ u_int8_t reserved1;
+ } scsi_2;
- u_int8_t reserved1;
+ /* reserved and obsolete (as of SCSI-3) fields */
+ u_int8_t reserved_or_obsolete[3];
+ } dt_or_obsolete;
u_int8_t flags2;
-#define READ_ELEMENT_STATUS_INVERT 0x40
-#define READ_ELEMENT_STATUS_SVALID 0x80
- u_int8_t ssea[2]; /* source storage element address */
+#define READ_ELEMENT_STATUS_INVERT 0x40
+#define READ_ELEMENT_STATUS_SVALID 0x80
+#define READ_ELEMENT_STATUS_ED 0x80
+#define READ_ELEMENT_STATUS_MEDIA_TYPE_MASK 0x07
- struct volume_tag pvoltag; /* omitted if PVOLTAG == 0 */
- struct volume_tag avoltag; /* omitted if AVOLTAG == 0 */
+ u_int8_t ssea[2]; /* source storage element address */
- /* Other data may follow */
+ union {
+ struct volume_tag pvoltag;
+ struct volume_tag voltag[2];
+ struct read_element_status_device_id devid;
+ struct {
+ struct volume_tag pvoltag;
+ struct read_element_status_device_id devid;
+ } pvol_and_devid;
+ struct {
+ struct volume_tag voltag[2];
+ struct read_element_status_device_id devid;
+ } vol_tags_and_devid;
+ } voltag_devid;
};
/* XXX add data returned by REQUEST VOLUME ELEMENT ADDRESS */
@@ -457,6 +489,7 @@ void scsi_position_to_element(struct ccb_scsiio *csio, u_int32_t retries,
void scsi_read_element_status(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
u_int8_t tag_action, int voltag, u_int32_t sea,
+ int curdata, int dvcid,
u_int32_t count, u_int8_t *data_ptr,
u_int32_t dxfer_len, u_int8_t sense_len,
u_int32_t timeout);
OpenPOWER on IntegriCloud