summaryrefslogtreecommitdiffstats
path: root/sys/cam/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cam/scsi')
-rw-r--r--sys/cam/scsi/scsi_all.c203
-rw-r--r--sys/cam/scsi/scsi_all.h67
-rw-r--r--sys/cam/scsi/scsi_pass.c4
-rw-r--r--sys/cam/scsi/scsi_xpt.c80
4 files changed, 303 insertions, 51 deletions
diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c
index 7ededa1..7361c42 100644
--- a/sys/cam/scsi/scsi_all.c
+++ b/sys/cam/scsi/scsi_all.c
@@ -3552,32 +3552,63 @@ scsi_calc_syncparam(u_int period)
return (period/400);
}
-uint8_t *
-scsi_get_sas_addr(struct scsi_vpd_device_id *id, uint32_t len)
+int
+scsi_devid_is_naa_ieee_reg(uint8_t *bufp)
{
- uint8_t *bufp, *buf_end;
struct scsi_vpd_id_descriptor *descr;
struct scsi_vpd_id_naa_basic *naa;
- bufp = buf_end = (uint8_t *)id;
- bufp += SVPD_DEVICE_ID_HDR_LEN;
- buf_end += len;
- while (bufp < buf_end) {
- descr = (struct scsi_vpd_id_descriptor *)bufp;
- bufp += SVPD_DEVICE_ID_DESC_HDR_LEN;
- /* Right now, we only care about SAS NAA IEEE Reg addrs */
- if (((descr->id_type & SVPD_ID_PIV) != 0)
- && (descr->proto_codeset >> SVPD_ID_PROTO_SHIFT) ==
- SCSI_PROTO_SAS
- && (descr->id_type & SVPD_ID_TYPE_MASK) == SVPD_ID_TYPE_NAA){
- naa = (struct scsi_vpd_id_naa_basic *)bufp;
- if ((naa->naa >> 4) == SVPD_ID_NAA_IEEE_REG)
- return bufp;
- }
- bufp += descr->length;
+ descr = (struct scsi_vpd_id_descriptor *)bufp;
+ naa = (struct scsi_vpd_id_naa_basic *)descr->identifier;
+ if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_NAA)
+ return 0;
+ if (descr->length < sizeof(struct scsi_vpd_id_naa_ieee_reg))
+ return 0;
+ if ((naa->naa >> SVPD_ID_NAA_NAA_SHIFT) != SVPD_ID_NAA_IEEE_REG)
+ return 0;
+ return 1;
+}
+
+int
+scsi_devid_is_sas_target(uint8_t *bufp)
+{
+ struct scsi_vpd_id_descriptor *descr;
+
+ descr = (struct scsi_vpd_id_descriptor *)bufp;
+ if (!scsi_devid_is_naa_ieee_reg(bufp))
+ return 0;
+ if ((descr->id_type & SVPD_ID_PIV) == 0) /* proto field reserved */
+ return 0;
+ if ((descr->proto_codeset >> SVPD_ID_PROTO_SHIFT) != SCSI_PROTO_SAS)
+ return 0;
+ return 1;
+}
+
+uint8_t *
+scsi_get_devid(struct scsi_vpd_device_id *id, uint32_t page_len,
+ scsi_devid_checkfn_t ck_fn)
+{
+ struct scsi_vpd_id_descriptor *desc;
+ uint8_t *page_end;
+ uint8_t *desc_buf_end;
+
+ page_end = (uint8_t *)id + page_len;
+ if (page_end < id->desc_list)
+ return (NULL);
+
+ desc_buf_end = MIN(id->desc_list + scsi_2btoul(id->length), page_end);
+
+ for (desc = (struct scsi_vpd_id_descriptor *)id->desc_list;
+ desc->identifier <= desc_buf_end
+ && desc->identifier + desc->length <= desc_buf_end;
+ desc = (struct scsi_vpd_id_descriptor *)(desc->identifier
+ + desc->length)) {
+
+ if (ck_fn == NULL || ck_fn((uint8_t *)desc) != 0)
+ return (desc->identifier);
}
- return NULL;
+ return (NULL);
}
void
@@ -4174,6 +4205,77 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries,
timeout);
}
+void
+scsi_receive_diagnostic_results(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb*),
+ uint8_t tag_action, int pcv, uint8_t page_code,
+ uint8_t *data_ptr, uint16_t allocation_length,
+ uint8_t sense_len, uint32_t timeout)
+{
+ struct scsi_receive_diag *scsi_cmd;
+
+ scsi_cmd = (struct scsi_receive_diag *)&csio->cdb_io.cdb_bytes;
+ memset(scsi_cmd, 0, sizeof(*scsi_cmd));
+ scsi_cmd->opcode = RECEIVE_DIAGNOSTIC;
+ if (pcv) {
+ scsi_cmd->byte2 |= SRD_PCV;
+ scsi_cmd->page_code = page_code;
+ }
+ scsi_ulto2b(allocation_length, scsi_cmd->length);
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_IN,
+ tag_action,
+ data_ptr,
+ allocation_length,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
+void
+scsi_send_diagnostic(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ uint8_t tag_action, int unit_offline, int device_offline,
+ int self_test, int page_format, int self_test_code,
+ uint8_t *data_ptr, uint16_t param_list_length,
+ uint8_t sense_len, uint32_t timeout)
+{
+ struct scsi_send_diag *scsi_cmd;
+
+ scsi_cmd = (struct scsi_send_diag *)&csio->cdb_io.cdb_bytes;
+ memset(scsi_cmd, 0, sizeof(*scsi_cmd));
+ scsi_cmd->opcode = SEND_DIAGNOSTIC;
+
+ /*
+ * The default self-test mode control and specific test
+ * control are mutually exclusive.
+ */
+ if (self_test)
+ self_test_code = SSD_SELF_TEST_CODE_NONE;
+
+ scsi_cmd->byte2 = ((self_test_code << SSD_SELF_TEST_CODE_SHIFT)
+ & SSD_SELF_TEST_CODE_MASK)
+ | (unit_offline ? SSD_UNITOFFL : 0)
+ | (device_offline ? SSD_DEVOFFL : 0)
+ | (self_test ? SSD_SELFTEST : 0)
+ | (page_format ? SSD_PF : 0);
+ scsi_ulto2b(param_list_length, scsi_cmd->length);
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/param_list_length ? CAM_DIR_OUT : CAM_DIR_NONE,
+ tag_action,
+ data_ptr,
+ param_list_length,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
void
scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
@@ -4206,7 +4308,6 @@ scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries,
sense_len,
sizeof(*scsi_cmd),
timeout);
-
}
@@ -4264,6 +4365,66 @@ scsi_static_inquiry_match(caddr_t inqbuffer, caddr_t table_entry)
return (-1);
}
+/**
+ * Compare two buffers of vpd device descriptors for a match.
+ *
+ * \param lhs Pointer to first buffer of descriptors to compare.
+ * \param lhs_len The length of the first buffer.
+ * \param rhs Pointer to second buffer of descriptors to compare.
+ * \param rhs_len The length of the second buffer.
+ *
+ * \return 0 on a match, -1 otherwise.
+ *
+ * Treat rhs and lhs as arrays of vpd device id descriptors. Walk lhs matching
+ * agains each element in rhs until all data are exhausted or we have found
+ * a match.
+ */
+int
+scsi_devid_match(uint8_t *lhs, size_t lhs_len, uint8_t *rhs, size_t rhs_len)
+{
+ struct scsi_vpd_id_descriptor *lhs_id;
+ struct scsi_vpd_id_descriptor *lhs_last;
+ struct scsi_vpd_id_descriptor *rhs_last;
+ uint8_t *lhs_end;
+ uint8_t *rhs_end;
+
+ lhs_end = lhs + lhs_len;
+ rhs_end = rhs + rhs_len;
+
+ /*
+ * rhs_last and lhs_last are the last posible position of a valid
+ * descriptor assuming it had a zero length identifier. We use
+ * these variables to insure we can safely dereference the length
+ * field in our loop termination tests.
+ */
+ lhs_last = (struct scsi_vpd_id_descriptor *)
+ (lhs_end - __offsetof(struct scsi_vpd_id_descriptor, identifier));
+ rhs_last = (struct scsi_vpd_id_descriptor *)
+ (rhs_end - __offsetof(struct scsi_vpd_id_descriptor, identifier));
+
+ lhs_id = (struct scsi_vpd_id_descriptor *)lhs;
+ while (lhs_id <= lhs_last
+ && (lhs_id->identifier + lhs_id->length) <= lhs_end) {
+ struct scsi_vpd_id_descriptor *rhs_id;
+
+ rhs_id = (struct scsi_vpd_id_descriptor *)rhs;
+ while (rhs_id <= rhs_last
+ && (rhs_id->identifier + rhs_id->length) <= rhs_end) {
+
+ if (rhs_id->length == lhs_id->length
+ && memcmp(rhs_id->identifier, lhs_id->identifier,
+ rhs_id->length) == 0)
+ return (0);
+
+ rhs_id = (struct scsi_vpd_id_descriptor *)
+ (rhs_id->identifier + rhs_id->length);
+ }
+ lhs_id = (struct scsi_vpd_id_descriptor *)
+ (lhs_id->identifier + lhs_id->length);
+ }
+ return (-1);
+}
+
#ifdef _KERNEL
static void
init_scsi_delay(void)
diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h
index 0a7a58f..93b11d5 100644
--- a/sys/cam/scsi/scsi_all.h
+++ b/sys/cam/scsi/scsi_all.h
@@ -115,6 +115,7 @@ struct scsi_request_sense
{
u_int8_t opcode;
u_int8_t byte2;
+#define SRS_DESC 0x01
u_int8_t unused[2];
u_int8_t length;
u_int8_t control;
@@ -128,17 +129,33 @@ struct scsi_test_unit_ready
u_int8_t control;
};
-struct scsi_send_diag
-{
- u_int8_t opcode;
- u_int8_t byte2;
-#define SSD_UOL 0x01
-#define SSD_DOL 0x02
-#define SSD_SELFTEST 0x04
-#define SSD_PF 0x10
- u_int8_t unused[1];
- u_int8_t paramlen[2];
- u_int8_t control;
+struct scsi_receive_diag {
+ uint8_t opcode;
+ uint8_t byte2;
+#define SRD_PCV 0x01
+ uint8_t page_code;
+ uint8_t length[2];
+ uint8_t control;
+};
+
+struct scsi_send_diag {
+ uint8_t opcode;
+ uint8_t byte2;
+#define SSD_UNITOFFL 0x01
+#define SSD_DEVOFFL 0x02
+#define SSD_SELFTEST 0x04
+#define SSD_PF 0x10
+#define SSD_SELF_TEST_CODE_MASK 0xE0
+#define SSD_SELF_TEST_CODE_SHIFT 5
+#define SSD_SELF_TEST_CODE_NONE 0x00
+#define SSD_SELF_TEST_CODE_BG_SHORT 0x01
+#define SSD_SELF_TEST_CODE_BG_EXTENDED 0x02
+#define SSD_SELF_TEST_CODE_BG_ABORT 0x04
+#define SSD_SELF_TEST_CODE_FG_SHORT 0x05
+#define SSD_SELF_TEST_CODE_FG_EXTENDED 0x06
+ uint8_t reserved;
+ uint8_t length[2];
+ uint8_t control;
};
struct scsi_sense
@@ -894,11 +911,12 @@ struct scsi_vpd_id_naa_basic
uint8_t naa : 4;
uint8_t naa_desig : 4;
*/
+#define SVPD_ID_NAA_NAA_SHIFT 4
#define SVPD_ID_NAA_IEEE_EXT 0x02
#define SVPD_ID_NAA_LOCAL_REG 0x03
#define SVPD_ID_NAA_IEEE_REG 0x05
#define SVPD_ID_NAA_IEEE_REG_EXT 0x06
- uint8_t naa_data[0];
+ uint8_t naa_data[];
};
struct scsi_vpd_id_naa_ieee_extended_id
@@ -1322,7 +1340,12 @@ void scsi_print_inquiry(struct scsi_inquiry_data *inq_data);
u_int scsi_calc_syncsrate(u_int period_factor);
u_int scsi_calc_syncparam(u_int period);
-uint8_t * scsi_get_sas_addr(struct scsi_vpd_device_id *id, uint32_t len);
+
+typedef int (*scsi_devid_checkfn_t)(uint8_t *);
+int scsi_devid_is_naa_ieee_reg(uint8_t *bufp);
+int scsi_devid_is_sas_target(uint8_t *bufp);
+uint8_t * scsi_get_devid(struct scsi_vpd_device_id *id, uint32_t len,
+ scsi_devid_checkfn_t ck_fn);
void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *,
@@ -1439,6 +1462,22 @@ void scsi_synchronize_cache(struct ccb_scsiio *csio,
u_int32_t begin_lba, u_int16_t lb_count,
u_int8_t sense_len, u_int32_t timeout);
+void scsi_receive_diagnostic_results(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *,
+ union ccb*),
+ uint8_t tag_action, int pcv,
+ uint8_t page_code, uint8_t *data_ptr,
+ uint16_t allocation_length,
+ uint8_t sense_len, uint32_t timeout);
+
+void scsi_send_diagnostic(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ uint8_t tag_action, int unit_offline,
+ int device_offline, int self_test, int page_format,
+ int self_test_code, uint8_t *data_ptr,
+ uint16_t param_list_length, uint8_t sense_len,
+ uint32_t timeout);
+
void scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
u_int8_t tag_action, int readop, u_int8_t byte2,
@@ -1455,6 +1494,8 @@ void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries,
int scsi_inquiry_match(caddr_t inqbuffer, caddr_t table_entry);
int scsi_static_inquiry_match(caddr_t inqbuffer,
caddr_t table_entry);
+int scsi_devid_match(uint8_t *rhs, size_t rhs_len,
+ uint8_t *lhs, size_t lhs_len);
static __inline void scsi_extract_sense(struct scsi_sense_data *sense,
int *error_code, int *sense_key,
diff --git a/sys/cam/scsi/scsi_pass.c b/sys/cam/scsi/scsi_pass.c
index e7ecb35..5b24d82 100644
--- a/sys/cam/scsi/scsi_pass.c
+++ b/sys/cam/scsi/scsi_pass.c
@@ -548,8 +548,8 @@ passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb)
&& ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE))
|| (ccb->ccb_h.func_code == XPT_DEV_MATCH)
|| (ccb->ccb_h.func_code == XPT_SMP_IO)
- || ((ccb->ccb_h.func_code == XPT_GDEV_ADVINFO)
- && (ccb->cgdai.bufsiz > 0)))) {
+ || ((ccb->ccb_h.func_code == XPT_DEV_ADVINFO)
+ && (ccb->cdai.bufsiz > 0)))) {
bzero(&mapinfo, sizeof(mapinfo));
diff --git a/sys/cam/scsi/scsi_xpt.c b/sys/cam/scsi/scsi_xpt.c
index 2a38128..1b507ca 100644
--- a/sys/cam/scsi/scsi_xpt.c
+++ b/sys/cam/scsi/scsi_xpt.c
@@ -542,6 +542,7 @@ static const int scsi_quirk_table_size =
static cam_status proberegister(struct cam_periph *periph,
void *arg);
static void probeschedule(struct cam_periph *probe_periph);
+static int device_has_vpd(struct cam_ed *device, uint8_t page_id);
static void probestart(struct cam_periph *periph, union ccb *start_ccb);
static void proberequestdefaultnegotiation(struct cam_periph *periph);
static int proberequestbackoff(struct cam_periph *periph,
@@ -1460,7 +1461,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
path->device->device_id = (uint8_t *)devid;
}
} else if (cam_periph_error(done_ccb, 0,
- SF_RETRY_UA|SF_NO_PRINT,
+ SF_RETRY_UA,
&softc->saved_ccb) == ERESTART) {
return;
} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
@@ -1506,9 +1507,9 @@ probe_device_check:
(u_int8_t *)malloc((serial_buf->length + 1),
M_CAMXPT, M_NOWAIT);
if (path->device->serial_num != NULL) {
- bcopy(serial_buf->serial_num,
- path->device->serial_num,
- serial_buf->length);
+ memcpy(path->device->serial_num,
+ serial_buf->serial_num,
+ serial_buf->length);
path->device->serial_num_len =
serial_buf->length;
path->device->serial_num[serial_buf->length]
@@ -2433,28 +2434,77 @@ scsi_devise_transport(struct cam_path *path)
}
static void
-scsi_getdev_advinfo(union ccb *start_ccb)
+scsi_dev_advinfo(union ccb *start_ccb)
{
struct cam_ed *device;
- struct ccb_getdev_advinfo *cgdai;
+ struct ccb_dev_advinfo *cdai;
off_t amt;
device = start_ccb->ccb_h.path->device;
- cgdai = &start_ccb->cgdai;
- switch(cgdai->buftype) {
- case CGDAI_TYPE_SCSI_DEVID:
- cgdai->provsiz = device->device_id_len;
+ cdai = &start_ccb->cdai;
+ switch(cdai->buftype) {
+ case CDAI_TYPE_SCSI_DEVID:
+ if (cdai->flags & CDAI_FLAG_STORE)
+ break;
+ cdai->provsiz = device->device_id_len;
if (device->device_id_len == 0)
break;
amt = device->device_id_len;
- if (cgdai->provsiz > cgdai->bufsiz)
- amt = cgdai->bufsiz;
- bcopy(device->device_id, cgdai->buf, amt);
+ if (cdai->provsiz > cdai->bufsiz)
+ amt = cdai->bufsiz;
+ memcpy(cdai->buf, device->device_id, amt);
+ break;
+ case CDAI_TYPE_SERIAL_NUM:
+ if (cdai->flags & CDAI_FLAG_STORE)
+ break;
+ cdai->provsiz = device->serial_num_len;
+ if (device->serial_num_len == 0)
+ break;
+ amt = device->serial_num_len;
+ if (cdai->provsiz > cdai->bufsiz)
+ amt = cdai->bufsiz;
+ memcpy(cdai->buf, device->serial_num, amt);
+ break;
+ case CDAI_TYPE_PHYS_PATH:
+ if (cdai->flags & CDAI_FLAG_STORE) {
+ if (device->physpath != NULL)
+ free(device->physpath, M_CAMXPT);
+ device->physpath_len = cdai->bufsiz;
+ /* Clear existing buffer if zero length */
+ if (cdai->bufsiz == 0)
+ break;
+ device->physpath = malloc(cdai->bufsiz, M_CAMXPT, M_NOWAIT);
+ if (device->physpath == NULL) {
+ start_ccb->ccb_h.status = CAM_REQ_ABORTED;
+ return;
+ }
+ memcpy(device->physpath, cdai->buf, cdai->bufsiz);
+ } else {
+ cdai->provsiz = device->physpath_len;
+ if (device->physpath_len == 0)
+ break;
+ amt = device->physpath_len;
+ if (cdai->provsiz > cdai->bufsiz)
+ amt = cdai->bufsiz;
+ memcpy(cdai->buf, device->physpath, amt);
+ }
break;
default:
break;
}
start_ccb->ccb_h.status = CAM_REQ_CMP;
+
+ if (cdai->flags & CDAI_FLAG_STORE) {
+ int owned;
+
+ owned = mtx_owned(start_ccb->ccb_h.path->bus->sim->mtx);
+ if (owned == 0)
+ mtx_lock(start_ccb->ccb_h.path->bus->sim->mtx);
+ xpt_async(AC_ADVINFO_CHANGED, start_ccb->ccb_h.path,
+ (void *)(uintptr_t)cdai->buftype);
+ if (owned == 0)
+ mtx_unlock(start_ccb->ccb_h.path->bus->sim->mtx);
+ }
}
static void
@@ -2486,9 +2536,9 @@ scsi_action(union ccb *start_ccb)
(*(sim->sim_action))(sim, start_ccb);
break;
}
- case XPT_GDEV_ADVINFO:
+ case XPT_DEV_ADVINFO:
{
- scsi_getdev_advinfo(start_ccb);
+ scsi_dev_advinfo(start_ccb);
break;
}
default:
OpenPOWER on IntegriCloud