diff options
author | mav <mav@FreeBSD.org> | 2014-07-15 17:26:43 +0000 |
---|---|---|
committer | mav <mav@FreeBSD.org> | 2014-07-15 17:26:43 +0000 |
commit | 2c6230a1adb4e420bb9bd3ba65643b39d8f987d1 (patch) | |
tree | e3ae25b7a9b252173ebb42fbddebc3e96724ec8f /sys | |
parent | c0be426ce60674cc235ac3efb29fd8351ba70fe2 (diff) | |
download | FreeBSD-src-2c6230a1adb4e420bb9bd3ba65643b39d8f987d1.zip FreeBSD-src-2c6230a1adb4e420bb9bd3ba65643b39d8f987d1.tar.gz |
MFC r268240 (by ken):
Add persistent reservation support to camcontrol(8).
camcontrol(8) now supports a new 'persist' subcommand that allows users to
issue SCSI PERSISTENT RESERVE IN / OUT commands.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/cam/scsi/scsi_all.c | 1042 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_all.h | 325 |
2 files changed, 1333 insertions, 34 deletions
diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c index aab857c..8351aa8 100644 --- a/sys/cam/scsi/scsi_all.c +++ b/sys/cam/scsi/scsi_all.c @@ -44,11 +44,13 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/mutex.h> #include <sys/sysctl.h> +#include <sys/ctype.h> #else #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <ctype.h> #endif #include <cam/cam.h> @@ -5459,6 +5461,989 @@ scsi_get_devid(struct scsi_vpd_device_id *id, uint32_t page_len, return (NULL); } +int +scsi_transportid_sbuf(struct sbuf *sb, struct scsi_transportid_header *hdr, + uint32_t valid_len) +{ + switch (hdr->format_protocol & SCSI_TRN_PROTO_MASK) { + case SCSI_PROTO_FC: { + struct scsi_transportid_fcp *fcp; + uint64_t n_port_name; + + fcp = (struct scsi_transportid_fcp *)hdr; + + n_port_name = scsi_8btou64(fcp->n_port_name); + + sbuf_printf(sb, "FCP address: 0x%.16jx",(uintmax_t)n_port_name); + break; + } + case SCSI_PROTO_SPI: { + struct scsi_transportid_spi *spi; + + spi = (struct scsi_transportid_spi *)hdr; + + sbuf_printf(sb, "SPI address: %u,%u", + scsi_2btoul(spi->scsi_addr), + scsi_2btoul(spi->rel_trgt_port_id)); + break; + } + case SCSI_PROTO_SSA: + /* + * XXX KDM there is no transport ID defined in SPC-4 for + * SSA. + */ + break; + case SCSI_PROTO_1394: { + struct scsi_transportid_1394 *sbp; + uint64_t eui64; + + sbp = (struct scsi_transportid_1394 *)hdr; + + eui64 = scsi_8btou64(sbp->eui64); + sbuf_printf(sb, "SBP address: 0x%.16jx", (uintmax_t)eui64); + break; + } + case SCSI_PROTO_RDMA: { + struct scsi_transportid_rdma *rdma; + unsigned int i; + + rdma = (struct scsi_transportid_rdma *)hdr; + + sbuf_printf(sb, "RDMA address: 0x"); + for (i = 0; i < sizeof(rdma->initiator_port_id); i++) + sbuf_printf(sb, "%02x", rdma->initiator_port_id[i]); + break; + } + case SCSI_PROTO_ISCSI: { + uint32_t add_len, i; + uint8_t *iscsi_name = NULL; + int nul_found = 0; + + sbuf_printf(sb, "iSCSI address: "); + if ((hdr->format_protocol & SCSI_TRN_FORMAT_MASK) == + SCSI_TRN_ISCSI_FORMAT_DEVICE) { + struct scsi_transportid_iscsi_device *dev; + + dev = (struct scsi_transportid_iscsi_device *)hdr; + + /* + * Verify how much additional data we really have. + */ + add_len = scsi_2btoul(dev->additional_length); + add_len = MIN(add_len, valid_len - + __offsetof(struct scsi_transportid_iscsi_device, + iscsi_name)); + iscsi_name = &dev->iscsi_name[0]; + + } else if ((hdr->format_protocol & SCSI_TRN_FORMAT_MASK) == + SCSI_TRN_ISCSI_FORMAT_PORT) { + struct scsi_transportid_iscsi_port *port; + + port = (struct scsi_transportid_iscsi_port *)hdr; + + add_len = scsi_2btoul(port->additional_length); + add_len = MIN(add_len, valid_len - + __offsetof(struct scsi_transportid_iscsi_port, + iscsi_name)); + iscsi_name = &port->iscsi_name[0]; + } else { + sbuf_printf(sb, "unknown format %x", + (hdr->format_protocol & + SCSI_TRN_FORMAT_MASK) >> + SCSI_TRN_FORMAT_SHIFT); + break; + } + if (add_len == 0) { + sbuf_printf(sb, "not enough data"); + break; + } + /* + * This is supposed to be a NUL-terminated ASCII + * string, but you never know. So we're going to + * check. We need to do this because there is no + * sbuf equivalent of strncat(). + */ + for (i = 0; i < add_len; i++) { + if (iscsi_name[i] == '\0') { + nul_found = 1; + break; + } + } + /* + * If there is a NUL in the name, we can just use + * sbuf_cat(). Otherwise we need to use sbuf_bcat(). + */ + if (nul_found != 0) + sbuf_cat(sb, iscsi_name); + else + sbuf_bcat(sb, iscsi_name, add_len); + break; + } + case SCSI_PROTO_SAS: { + struct scsi_transportid_sas *sas; + uint64_t sas_addr; + + sas = (struct scsi_transportid_sas *)hdr; + + sas_addr = scsi_8btou64(sas->sas_address); + sbuf_printf(sb, "SAS address: 0x%.16jx", (uintmax_t)sas_addr); + break; + } + case SCSI_PROTO_ADITP: + case SCSI_PROTO_ATA: + case SCSI_PROTO_UAS: + /* + * No Transport ID format for ADI, ATA or USB is defined in + * SPC-4. + */ + sbuf_printf(sb, "No known Transport ID format for protocol " + "%#x", hdr->format_protocol & SCSI_TRN_PROTO_MASK); + break; + case SCSI_PROTO_SOP: { + struct scsi_transportid_sop *sop; + struct scsi_sop_routing_id_norm *rid; + + sop = (struct scsi_transportid_sop *)hdr; + rid = (struct scsi_sop_routing_id_norm *)sop->routing_id; + + /* + * Note that there is no alternate format specified in SPC-4 + * for the PCIe routing ID, so we don't really have a way + * to know whether the second byte of the routing ID is + * a device and function or just a function. So we just + * assume bus,device,function. + */ + sbuf_printf(sb, "SOP Routing ID: %u,%u,%u", + rid->bus, rid->devfunc >> SCSI_TRN_SOP_DEV_SHIFT, + rid->devfunc & SCSI_TRN_SOP_FUNC_NORM_MAX); + break; + } + case SCSI_PROTO_NONE: + default: + sbuf_printf(sb, "Unknown protocol %#x", + hdr->format_protocol & SCSI_TRN_PROTO_MASK); + break; + } + + return (0); +} + +struct scsi_nv scsi_proto_map[] = { + { "fcp", SCSI_PROTO_FC }, + { "spi", SCSI_PROTO_SPI }, + { "ssa", SCSI_PROTO_SSA }, + { "sbp", SCSI_PROTO_1394 }, + { "1394", SCSI_PROTO_1394 }, + { "srp", SCSI_PROTO_RDMA }, + { "rdma", SCSI_PROTO_RDMA }, + { "iscsi", SCSI_PROTO_ISCSI }, + { "iqn", SCSI_PROTO_ISCSI }, + { "sas", SCSI_PROTO_SAS }, + { "aditp", SCSI_PROTO_ADITP }, + { "ata", SCSI_PROTO_ATA }, + { "uas", SCSI_PROTO_UAS }, + { "usb", SCSI_PROTO_UAS }, + { "sop", SCSI_PROTO_SOP } +}; + +const char * +scsi_nv_to_str(struct scsi_nv *table, int num_table_entries, uint64_t value) +{ + int i; + + for (i = 0; i < num_table_entries; i++) { + if (table[i].value == value) + return (table[i].name); + } + + return (NULL); +} + +/* + * Given a name/value table, find a value matching the given name. + * Return values: + * SCSI_NV_FOUND - match found + * SCSI_NV_AMBIGUOUS - more than one match, none of them exact + * SCSI_NV_NOT_FOUND - no match found + */ +scsi_nv_status +scsi_get_nv(struct scsi_nv *table, int num_table_entries, + char *name, int *table_entry, scsi_nv_flags flags) +{ + int i, num_matches = 0; + + for (i = 0; i < num_table_entries; i++) { + size_t table_len, name_len; + + table_len = strlen(table[i].name); + name_len = strlen(name); + + if ((((flags & SCSI_NV_FLAG_IG_CASE) != 0) + && (strncasecmp(table[i].name, name, name_len) == 0)) + || (((flags & SCSI_NV_FLAG_IG_CASE) == 0) + && (strncmp(table[i].name, name, name_len) == 0))) { + *table_entry = i; + + /* + * Check for an exact match. If we have the same + * number of characters in the table as the argument, + * and we already know they're the same, we have + * an exact match. + */ + if (table_len == name_len) + return (SCSI_NV_FOUND); + + /* + * Otherwise, bump up the number of matches. We'll + * see later how many we have. + */ + num_matches++; + } + } + + if (num_matches > 1) + return (SCSI_NV_AMBIGUOUS); + else if (num_matches == 1) + return (SCSI_NV_FOUND); + else + return (SCSI_NV_NOT_FOUND); +} + +/* + * Parse transport IDs for Fibre Channel, 1394 and SAS. Since these are + * all 64-bit numbers, the code is similar. + */ +int +scsi_parse_transportid_64bit(int proto_id, char *id_str, + struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str, int error_str_len) +{ + uint64_t value; + char *endptr; + int retval; + size_t alloc_size; + + retval = 0; + + value = strtouq(id_str, &endptr, 0); + if (*endptr != '\0') { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: error " + "parsing ID %s, 64-bit number required", + __func__, id_str); + } + retval = 1; + goto bailout; + } + + switch (proto_id) { + case SCSI_PROTO_FC: + alloc_size = sizeof(struct scsi_transportid_fcp); + break; + case SCSI_PROTO_1394: + alloc_size = sizeof(struct scsi_transportid_1394); + break; + case SCSI_PROTO_SAS: + alloc_size = sizeof(struct scsi_transportid_sas); + break; + default: + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: unsupoprted " + "protocol %d", __func__, proto_id); + } + retval = 1; + goto bailout; + break; /* NOTREACHED */ + } +#ifdef _KERNEL + *hdr = malloc(alloc_size, type, flags); +#else /* _KERNEL */ + *hdr = malloc(alloc_size); +#endif /*_KERNEL */ + if (*hdr == NULL) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: unable to " + "allocate %zu bytes", __func__, alloc_size); + } + retval = 1; + goto bailout; + } + + *alloc_len = alloc_size; + + bzero(*hdr, alloc_size); + + switch (proto_id) { + case SCSI_PROTO_FC: { + struct scsi_transportid_fcp *fcp; + + fcp = (struct scsi_transportid_fcp *)(*hdr); + fcp->format_protocol = SCSI_PROTO_FC | + SCSI_TRN_FCP_FORMAT_DEFAULT; + scsi_u64to8b(value, fcp->n_port_name); + break; + } + case SCSI_PROTO_1394: { + struct scsi_transportid_1394 *sbp; + + sbp = (struct scsi_transportid_1394 *)(*hdr); + sbp->format_protocol = SCSI_PROTO_1394 | + SCSI_TRN_1394_FORMAT_DEFAULT; + scsi_u64to8b(value, sbp->eui64); + break; + } + case SCSI_PROTO_SAS: { + struct scsi_transportid_sas *sas; + + sas = (struct scsi_transportid_sas *)(*hdr); + sas->format_protocol = SCSI_PROTO_SAS | + SCSI_TRN_SAS_FORMAT_DEFAULT; + scsi_u64to8b(value, sas->sas_address); + break; + } + default: + break; + } +bailout: + return (retval); +} + +/* + * Parse a SPI (Parallel SCSI) address of the form: id,rel_tgt_port + */ +int +scsi_parse_transportid_spi(char *id_str, struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str, int error_str_len) +{ + unsigned long scsi_addr, target_port; + struct scsi_transportid_spi *spi; + char *tmpstr, *endptr; + int retval; + + retval = 0; + + tmpstr = strsep(&id_str, ","); + if (tmpstr == NULL) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, + "%s: no ID found", __func__); + } + retval = 1; + goto bailout; + } + scsi_addr = strtoul(tmpstr, &endptr, 0); + if (*endptr != '\0') { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: error " + "parsing SCSI ID %s, number required", + __func__, tmpstr); + } + retval = 1; + goto bailout; + } + + if (id_str == NULL) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: no relative " + "target port found", __func__); + } + retval = 1; + goto bailout; + } + + target_port = strtoul(id_str, &endptr, 0); + if (*endptr != '\0') { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: error " + "parsing relative target port %s, number " + "required", __func__, id_str); + } + retval = 1; + goto bailout; + } +#ifdef _KERNEL + spi = malloc(sizeof(*spi), type, flags); +#else + spi = malloc(sizeof(*spi)); +#endif + if (spi == NULL) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: unable to " + "allocate %zu bytes", __func__, + sizeof(*spi)); + } + retval = 1; + goto bailout; + } + *alloc_len = sizeof(*spi); + bzero(spi, sizeof(*spi)); + + spi->format_protocol = SCSI_PROTO_SPI | SCSI_TRN_SPI_FORMAT_DEFAULT; + scsi_ulto2b(scsi_addr, spi->scsi_addr); + scsi_ulto2b(target_port, spi->rel_trgt_port_id); + + *hdr = (struct scsi_transportid_header *)spi; +bailout: + return (retval); +} + +/* + * Parse an RDMA/SRP Initiator Port ID string. This is 32 hexadecimal digits, + * optionally prefixed by "0x" or "0X". + */ +int +scsi_parse_transportid_rdma(char *id_str, struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str, int error_str_len) +{ + struct scsi_transportid_rdma *rdma; + int retval; + size_t id_len, rdma_id_size; + uint8_t rdma_id[SCSI_TRN_RDMA_PORT_LEN]; + char *tmpstr; + unsigned int i, j; + + retval = 0; + id_len = strlen(id_str); + rdma_id_size = SCSI_TRN_RDMA_PORT_LEN; + + /* + * Check the size. It needs to be either 32 or 34 characters long. + */ + if ((id_len != (rdma_id_size * 2)) + && (id_len != ((rdma_id_size * 2) + 2))) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: RDMA ID " + "must be 32 hex digits (0x prefix " + "optional), only %zu seen", __func__, id_len); + } + retval = 1; + goto bailout; + } + + tmpstr = id_str; + /* + * If the user gave us 34 characters, the string needs to start + * with '0x'. + */ + if (id_len == ((rdma_id_size * 2) + 2)) { + if ((tmpstr[0] == '0') + && ((tmpstr[1] == 'x') || (tmpstr[1] == 'X'))) { + tmpstr += 2; + } else { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: RDMA " + "ID prefix, if used, must be \"0x\", " + "got %s", __func__, tmpstr); + } + retval = 1; + goto bailout; + } + } + bzero(rdma_id, sizeof(rdma_id)); + + /* + * Convert ASCII hex into binary bytes. There is no standard + * 128-bit integer type, and so no strtou128t() routine to convert + * from hex into a large integer. In the end, we're not going to + * an integer, but rather to a byte array, so that and the fact + * that we require the user to give us 32 hex digits simplifies the + * logic. + */ + for (i = 0; i < (rdma_id_size * 2); i++) { + int cur_shift; + unsigned char c; + + /* Increment the byte array one for every 2 hex digits */ + j = i >> 1; + + /* + * The first digit in every pair is the most significant + * 4 bits. The second is the least significant 4 bits. + */ + if ((i % 2) == 0) + cur_shift = 4; + else + cur_shift = 0; + + c = tmpstr[i]; + /* Convert the ASCII hex character into a number */ + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: " + "RDMA ID must be hex digits, got " + "invalid character %c", __func__, + tmpstr[i]); + } + retval = 1; + goto bailout; + } + /* + * The converted number can't be less than 0; the type is + * unsigned, and the subtraction logic will not give us + * a negative number. So we only need to make sure that + * the value is not greater than 0xf. (i.e. make sure the + * user didn't give us a value like "0x12jklmno"). + */ + if (c > 0xf) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: " + "RDMA ID must be hex digits, got " + "invalid character %c", __func__, + tmpstr[i]); + } + retval = 1; + goto bailout; + } + + rdma_id[j] |= c << cur_shift; + } + +#ifdef _KERNEL + rdma = malloc(sizeof(*rdma), type, flags); +#else + rdma = malloc(sizeof(*rdma)); +#endif + if (rdma == NULL) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: unable to " + "allocate %zu bytes", __func__, + sizeof(*rdma)); + } + retval = 1; + goto bailout; + } + *alloc_len = sizeof(*rdma); + bzero(rdma, sizeof(rdma)); + + rdma->format_protocol = SCSI_PROTO_RDMA | SCSI_TRN_RDMA_FORMAT_DEFAULT; + bcopy(rdma_id, rdma->initiator_port_id, SCSI_TRN_RDMA_PORT_LEN); + + *hdr = (struct scsi_transportid_header *)rdma; + +bailout: + return (retval); +} + +/* + * Parse an iSCSI name. The format is either just the name: + * + * iqn.2012-06.com.example:target0 + * or the name, separator and initiator session ID: + * + * iqn.2012-06.com.example:target0,i,0x123 + * + * The separator format is exact. + */ +int +scsi_parse_transportid_iscsi(char *id_str, struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str, int error_str_len) +{ + size_t id_len, sep_len, id_size, name_len; + int is_full_id, retval; + unsigned int i, sep_pos, sep_found; + const char *sep_template = ",i,0x"; + const char *iqn_prefix = "iqn."; + struct scsi_transportid_iscsi_device *iscsi; + + is_full_id = 0; + retval = 0; + sep_found = 0; + + id_len = strlen(id_str); + sep_len = strlen(sep_template); + + /* + * The separator is defined as exactly ',i,0x'. Any other commas, + * or any other form, is an error. So look for a comma, and once + * we find that, the next few characters must match the separator + * exactly. Once we get through the separator, there should be at + * least one character. + */ + for (i = 0, sep_pos = 0; i < id_len; i++) { + if (sep_pos == 0) { + if (id_str[i] == sep_template[sep_pos]) + sep_pos++; + + continue; + } + if (sep_pos < sep_len) { + if (id_str[i] == sep_template[sep_pos]) { + sep_pos++; + continue; + } + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: " + "invalid separator in iSCSI name " + "\"%s\"", + __func__, id_str); + } + retval = 1; + goto bailout; + } else { + sep_found = 1; + break; + } + } + + /* + * Check to see whether we have a separator but no digits after it. + */ + if ((sep_pos != 0) + && (sep_found == 0)) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: no digits " + "found after separator in iSCSI name \"%s\"", + __func__, id_str); + } + retval = 1; + goto bailout; + } + + /* + * The incoming ID string has the "iqn." prefix stripped off. We + * need enough space for the base structure (the structures are the + * same for the two iSCSI forms), the prefix, the ID string and a + * terminating NUL. + */ + id_size = sizeof(*iscsi) + strlen(iqn_prefix) + id_len + 1; + +#ifdef _KERNEL + iscsi = malloc(id_size, type, flags); +#else + iscsi = malloc(id_size); +#endif + if (iscsi == NULL) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: unable to " + "allocate %zu bytes", __func__, id_size); + } + retval = 1; + goto bailout; + } + *alloc_len = id_size; + bzero(iscsi, id_size); + + iscsi->format_protocol = SCSI_PROTO_ISCSI; + if (sep_found == 0) + iscsi->format_protocol |= SCSI_TRN_ISCSI_FORMAT_DEVICE; + else + iscsi->format_protocol |= SCSI_TRN_ISCSI_FORMAT_PORT; + name_len = id_size - sizeof(*iscsi); + scsi_ulto2b(name_len, iscsi->additional_length); + snprintf(iscsi->iscsi_name, name_len, "%s%s", iqn_prefix, id_str); + + *hdr = (struct scsi_transportid_header *)iscsi; + +bailout: + return (retval); +} + +/* + * Parse a SCSI over PCIe (SOP) identifier. The Routing ID can either be + * of the form 'bus,device,function' or 'bus,function'. + */ +int +scsi_parse_transportid_sop(char *id_str, struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str, int error_str_len) +{ + struct scsi_transportid_sop *sop; + unsigned long bus, device, function; + char *tmpstr, *endptr; + int retval, device_spec; + + retval = 0; + device_spec = 0; + device = 0; + + tmpstr = strsep(&id_str, ","); + if ((tmpstr == NULL) + || (*tmpstr == '\0')) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: no ID found", + __func__); + } + retval = 1; + goto bailout; + } + bus = strtoul(tmpstr, &endptr, 0); + if (*endptr != '\0') { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: error " + "parsing PCIe bus %s, number required", + __func__, tmpstr); + } + retval = 1; + goto bailout; + } + if ((id_str == NULL) + || (*id_str == '\0')) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: no PCIe " + "device or function found", __func__); + } + retval = 1; + goto bailout; + } + tmpstr = strsep(&id_str, ","); + function = strtoul(tmpstr, &endptr, 0); + if (*endptr != '\0') { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: error " + "parsing PCIe device/function %s, number " + "required", __func__, tmpstr); + } + retval = 1; + goto bailout; + } + /* + * Check to see whether the user specified a third value. If so, + * the second is the device. + */ + if (id_str != NULL) { + if (*id_str == '\0') { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: " + "no PCIe function found", __func__); + } + retval = 1; + goto bailout; + } + device = function; + device_spec = 1; + function = strtoul(id_str, &endptr, 0); + if (*endptr != '\0') { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: " + "error parsing PCIe function %s, " + "number required", __func__, id_str); + } + retval = 1; + goto bailout; + } + } + if (bus > SCSI_TRN_SOP_BUS_MAX) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: bus value " + "%lu greater than maximum %u", __func__, + bus, SCSI_TRN_SOP_BUS_MAX); + } + retval = 1; + goto bailout; + } + + if ((device_spec != 0) + && (device > SCSI_TRN_SOP_DEV_MASK)) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: device value " + "%lu greater than maximum %u", __func__, + device, SCSI_TRN_SOP_DEV_MAX); + } + retval = 1; + goto bailout; + } + + if (((device_spec != 0) + && (function > SCSI_TRN_SOP_FUNC_NORM_MAX)) + || ((device_spec == 0) + && (function > SCSI_TRN_SOP_FUNC_ALT_MAX))) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: function value " + "%lu greater than maximum %u", __func__, + function, (device_spec == 0) ? + SCSI_TRN_SOP_FUNC_ALT_MAX : + SCSI_TRN_SOP_FUNC_NORM_MAX); + } + retval = 1; + goto bailout; + } + +#ifdef _KERNEL + sop = malloc(sizeof(*sop), type, flags); +#else + sop = malloc(sizeof(*sop)); +#endif + if (sop == NULL) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: unable to " + "allocate %zu bytes", __func__, sizeof(*sop)); + } + retval = 1; + goto bailout; + } + *alloc_len = sizeof(*sop); + bzero(sop, sizeof(*sop)); + sop->format_protocol = SCSI_PROTO_SOP | SCSI_TRN_SOP_FORMAT_DEFAULT; + if (device_spec != 0) { + struct scsi_sop_routing_id_norm rid; + + rid.bus = bus; + rid.devfunc = (device << SCSI_TRN_SOP_DEV_SHIFT) | function; + bcopy(&rid, sop->routing_id, MIN(sizeof(rid), + sizeof(sop->routing_id))); + } else { + struct scsi_sop_routing_id_alt rid; + + rid.bus = bus; + rid.function = function; + bcopy(&rid, sop->routing_id, MIN(sizeof(rid), + sizeof(sop->routing_id))); + } + + *hdr = (struct scsi_transportid_header *)sop; +bailout: + return (retval); +} + +/* + * transportid_str: NUL-terminated string with format: protcol,id + * The ID is protocol specific. + * hdr: Storage will be allocated for the transport ID. + * alloc_len: The amount of memory allocated is returned here. + * type: Malloc bucket (kernel only). + * flags: Malloc flags (kernel only). + * error_str: If non-NULL, it will contain error information (without + * a terminating newline) if an error is returned. + * error_str_len: Allocated length of the error string. + * + * Returns 0 for success, non-zero for failure. + */ +int +scsi_parse_transportid(char *transportid_str, + struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str, int error_str_len) +{ + char *tmpstr; + scsi_nv_status status; + int retval, num_proto_entries, table_entry; + + retval = 0; + table_entry = 0; + + /* + * We do allow a period as well as a comma to separate the protocol + * from the ID string. This is to accommodate iSCSI names, which + * start with "iqn.". + */ + tmpstr = strsep(&transportid_str, ",."); + if (tmpstr == NULL) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, + "%s: transportid_str is NULL", __func__); + } + retval = 1; + goto bailout; + } + + num_proto_entries = sizeof(scsi_proto_map) / + sizeof(scsi_proto_map[0]); + status = scsi_get_nv(scsi_proto_map, num_proto_entries, tmpstr, + &table_entry, SCSI_NV_FLAG_IG_CASE); + if (status != SCSI_NV_FOUND) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: %s protocol " + "name %s", __func__, + (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : + "invalid", tmpstr); + } + retval = 1; + goto bailout; + } + switch (scsi_proto_map[table_entry].value) { + case SCSI_PROTO_FC: + case SCSI_PROTO_1394: + case SCSI_PROTO_SAS: + retval = scsi_parse_transportid_64bit( + scsi_proto_map[table_entry].value, transportid_str, hdr, + alloc_len, +#ifdef _KERNEL + type, flags, +#endif + error_str, error_str_len); + break; + case SCSI_PROTO_SPI: + retval = scsi_parse_transportid_spi(transportid_str, hdr, + alloc_len, +#ifdef _KERNEL + type, flags, +#endif + error_str, error_str_len); + break; + case SCSI_PROTO_RDMA: + retval = scsi_parse_transportid_rdma(transportid_str, hdr, + alloc_len, +#ifdef _KERNEL + type, flags, +#endif + error_str, error_str_len); + break; + case SCSI_PROTO_ISCSI: + retval = scsi_parse_transportid_iscsi(transportid_str, hdr, + alloc_len, +#ifdef _KERNEL + type, flags, +#endif + error_str, error_str_len); + break; + case SCSI_PROTO_SOP: + retval = scsi_parse_transportid_sop(transportid_str, hdr, + alloc_len, +#ifdef _KERNEL + type, flags, +#endif + error_str, error_str_len); + break; + case SCSI_PROTO_SSA: + case SCSI_PROTO_ADITP: + case SCSI_PROTO_ATA: + case SCSI_PROTO_UAS: + case SCSI_PROTO_NONE: + default: + /* + * There is no format defined for a Transport ID for these + * protocols. So even if the user gives us something, we + * have no way to turn it into a standard SCSI Transport ID. + */ + retval = 1; + if (error_str != NULL) { + snprintf(error_str, error_str_len, "%s: no Transport " + "ID format exists for protocol %s", + __func__, tmpstr); + } + goto bailout; + break; /* NOTREACHED */ + } +bailout: + return (retval); +} + void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), @@ -6410,6 +7395,63 @@ scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, } +void +scsi_persistent_reserve_in(struct ccb_scsiio *csio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, int service_action, + uint8_t *data_ptr, uint32_t dxfer_len, int sense_len, + int timeout) +{ + struct scsi_per_res_in *scsi_cmd; + + scsi_cmd = (struct scsi_per_res_in *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = PERSISTENT_RES_IN; + scsi_cmd->action = service_action; + scsi_ulto2b(dxfer_len, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + data_ptr, + dxfer_len, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_persistent_reserve_out(struct ccb_scsiio *csio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, int service_action, + int scope, int res_type, uint8_t *data_ptr, + uint32_t dxfer_len, int sense_len, int timeout) +{ + struct scsi_per_res_out *scsi_cmd; + + scsi_cmd = (struct scsi_per_res_out *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = PERSISTENT_RES_OUT; + scsi_cmd->action = service_action; + scsi_cmd->scope_type = scope | res_type; + scsi_ulto4b(dxfer_len, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_OUT, + tag_action, + data_ptr, + dxfer_len, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + /* * Try make as good a match as possible with * available sub drivers diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index ba92202..1c7aaee 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -278,6 +278,7 @@ struct scsi_per_res_in #define SPRI_RS 0x03 u_int8_t reserved[5]; u_int8_t length[2]; +#define SPRI_MAX_LEN 0xffff u_int8_t control; }; @@ -302,18 +303,21 @@ struct scsi_per_res_cap { uint8_t length[2]; uint8_t flags1; -#define SPRI_CRH 0x10 -#define SPRI_SIP_C 0x08 -#define SPRI_ATP_C 0x04 -#define SPRI_PTPL_C 0x01 +#define SPRI_RLR_C 0x80 +#define SPRI_CRH 0x10 +#define SPRI_SIP_C 0x08 +#define SPRI_ATP_C 0x04 +#define SPRI_PTPL_C 0x01 uint8_t flags2; -#define SPRI_TMV 0x80 -#define SPRI_ALLOW_MASK 0x70 -#define SPRI_ALLOW_0 0x00 -#define SPRI_ALLOW_1 0x10 -#define SPRI_ALLOW_2 0x20 -#define SPRI_ALLOW_3 0x30 -#define SPRI_PTPL_A 0x01 +#define SPRI_TMV 0x80 +#define SPRI_ALLOW_CMD_MASK 0x70 +#define SPRI_ALLOW_CMD_SHIFT 4 +#define SPRI_ALLOW_NA 0x00 +#define SPRI_ALLOW_1 0x10 +#define SPRI_ALLOW_2 0x20 +#define SPRI_ALLOW_3 0x30 +#define SPRI_ALLOW_4 0x40 +#define SPRI_PTPL_A 0x01 uint8_t type_mask[2]; #define SPRI_TM_WR_EX_AR 0x8000 #define SPRI_TM_EX_AC_RO 0x4000 @@ -327,7 +331,7 @@ struct scsi_per_res_cap struct scsi_per_res_in_rsrv_data { uint8_t reservation[8]; - uint8_t obsolete1[4]; + uint8_t scope_addr[4]; uint8_t reserved; uint8_t scopetype; #define SPRT_WE 0x01 @@ -336,7 +340,7 @@ struct scsi_per_res_in_rsrv_data #define SPRT_EARO 0x06 #define SPRT_WEAR 0x07 #define SPRT_EAAR 0x08 - uint8_t obsolete2[2]; + uint8_t extent_length[2]; }; struct scsi_per_res_in_rsrv @@ -345,6 +349,26 @@ struct scsi_per_res_in_rsrv struct scsi_per_res_in_rsrv_data data; }; +struct scsi_per_res_in_full_desc +{ + struct scsi_per_res_key res_key; + uint8_t reserved1[4]; + uint8_t flags; +#define SPRI_FULL_ALL_TG_PT 0x02 +#define SPRI_FULL_R_HOLDER 0x01 + uint8_t scopetype; + uint8_t reserved2[4]; + uint8_t rel_trgt_port_id[2]; + uint8_t additional_length[4]; + uint8_t transport_id[]; +}; + +struct scsi_per_res_in_full +{ + struct scsi_per_res_in_header header; + struct scsi_per_res_in_full_desc desc[]; +}; + struct scsi_per_res_out { u_int8_t opcode; @@ -357,13 +381,20 @@ struct scsi_per_res_out #define SPRO_PRE_ABO 0x05 #define SPRO_REG_IGNO 0x06 #define SPRO_REG_MOVE 0x07 +#define SPRO_REPL_LOST_RES 0x08 #define SPRO_ACTION_MASK 0x1f u_int8_t scope_type; #define SPR_SCOPE_MASK 0xf0 +#define SPR_SCOPE_SHIFT 4 #define SPR_LU_SCOPE 0x00 +#define SPR_EXTENT_SCOPE 0x10 +#define SPR_ELEMENT_SCOPE 0x20 #define SPR_TYPE_MASK 0x0f +#define SPR_TYPE_RD_SHARED 0x00 #define SPR_TYPE_WR_EX 0x01 +#define SPR_TYPE_RD_EX 0x02 #define SPR_TYPE_EX_AC 0x03 +#define SPR_TYPE_SHARED 0x04 #define SPR_TYPE_WR_EX_RO 0x05 #define SPR_TYPE_EX_AC_RO 0x06 #define SPR_TYPE_WR_EX_AR 0x07 @@ -377,15 +408,139 @@ struct scsi_per_res_out_parms { struct scsi_per_res_key res_key; u_int8_t serv_act_res_key[8]; - u_int8_t obsolete1[4]; + u_int8_t scope_spec_address[4]; u_int8_t flags; #define SPR_SPEC_I_PT 0x08 #define SPR_ALL_TG_PT 0x04 #define SPR_APTPL 0x01 u_int8_t reserved1; - u_int8_t obsolete2[2]; + u_int8_t extent_length[2]; + u_int8_t transport_id_list[]; +}; + +struct scsi_per_res_out_trans_ids { + u_int8_t additional_length[4]; + u_int8_t transport_ids[]; +}; + +/* + * Used with REGISTER AND MOVE serivce action of the PERSISTENT RESERVE OUT + * command. + */ +struct scsi_per_res_reg_move +{ + struct scsi_per_res_key res_key; + u_int8_t serv_act_res_key[8]; + u_int8_t reserved; + u_int8_t flags; +#define SPR_REG_MOVE_UNREG 0x02 +#define SPR_REG_MOVE_APTPL 0x01 + u_int8_t rel_trgt_port_id[2]; + u_int8_t transport_id_length[4]; + u_int8_t transport_id[]; +}; + +struct scsi_transportid_header +{ + uint8_t format_protocol; +#define SCSI_TRN_FORMAT_MASK 0xc0 +#define SCSI_TRN_FORMAT_SHIFT 6 +#define SCSI_TRN_PROTO_MASK 0x0f +}; + +struct scsi_transportid_fcp +{ + uint8_t format_protocol; +#define SCSI_TRN_FCP_FORMAT_DEFAULT 0x00 + uint8_t reserved1[7]; + uint8_t n_port_name[8]; + uint8_t reserved2[8]; +}; + +struct scsi_transportid_spi +{ + uint8_t format_protocol; +#define SCSI_TRN_SPI_FORMAT_DEFAULT 0x00 + uint8_t reserved1; + uint8_t scsi_addr[2]; + uint8_t obsolete[2]; + uint8_t rel_trgt_port_id[2]; + uint8_t reserved2[16]; +}; + +struct scsi_transportid_1394 +{ + uint8_t format_protocol; +#define SCSI_TRN_1394_FORMAT_DEFAULT 0x00 + uint8_t reserved1[7]; + uint8_t eui64[8]; + uint8_t reserved2[8]; +}; + +struct scsi_transportid_rdma +{ + uint8_t format_protocol; +#define SCSI_TRN_RDMA_FORMAT_DEFAULT 0x00 + uint8_t reserved[7]; +#define SCSI_TRN_RDMA_PORT_LEN 16 + uint8_t initiator_port_id[SCSI_TRN_RDMA_PORT_LEN]; }; +struct scsi_transportid_iscsi_device +{ + uint8_t format_protocol; +#define SCSI_TRN_ISCSI_FORMAT_DEVICE 0x00 + uint8_t reserved; + uint8_t additional_length[2]; + uint8_t iscsi_name[]; +}; + +struct scsi_transportid_iscsi_port +{ + uint8_t format_protocol; +#define SCSI_TRN_ISCSI_FORMAT_PORT 0x40 + uint8_t reserved; + uint8_t additional_length[2]; + uint8_t iscsi_name[]; + /* + * Followed by a separator and iSCSI initiator session ID + */ +}; + +struct scsi_transportid_sas +{ + uint8_t format_protocol; +#define SCSI_TRN_SAS_FORMAT_DEFAULT 0x00 + uint8_t reserved1[3]; + uint8_t sas_address[8]; + uint8_t reserved2[12]; +}; + +struct scsi_sop_routing_id_norm { + uint8_t bus; + uint8_t devfunc; +#define SCSI_TRN_SOP_BUS_MAX 0xff +#define SCSI_TRN_SOP_DEV_MAX 0x1f +#define SCSI_TRN_SOP_DEV_MASK 0xf8 +#define SCSI_TRN_SOP_DEV_SHIFT 3 +#define SCSI_TRN_SOP_FUNC_NORM_MASK 0x07 +#define SCSI_TRN_SOP_FUNC_NORM_MAX 0x07 +}; + +struct scsi_sop_routing_id_alt { + uint8_t bus; + uint8_t function; +#define SCSI_TRN_SOP_FUNC_ALT_MAX 0xff +}; + +struct scsi_transportid_sop +{ + uint8_t format_protocol; +#define SCSI_TRN_SOP_FORMAT_DEFAULT 0x00 + uint8_t reserved1; + uint8_t routing_id[2]; + uint8_t reserved2[20]; +}; struct scsi_log_sense { @@ -611,21 +766,41 @@ struct scsi_info_exceptions_page { u_int8_t report_count[4]; }; +/* + * SCSI protocol identifier values, current as of SPC4r36l. + */ +#define SCSI_PROTO_FC 0x00 /* Fibre Channel */ +#define SCSI_PROTO_SPI 0x01 /* Parallel SCSI */ +#define SCSI_PROTO_SSA 0x02 /* Serial Storage Arch. */ +#define SCSI_PROTO_1394 0x03 /* IEEE 1394 (Firewire) */ +#define SCSI_PROTO_RDMA 0x04 /* SCSI RDMA Protocol */ +#define SCSI_PROTO_ISCSI 0x05 /* Internet SCSI */ +#define SCSI_PROTO_iSCSI 0x05 /* Internet SCSI */ +#define SCSI_PROTO_SAS 0x06 /* SAS Serial SCSI Protocol */ +#define SCSI_PROTO_ADT 0x07 /* Automation/Drive Int. Trans. Prot.*/ +#define SCSI_PROTO_ADITP 0x07 /* Automation/Drive Int. Trans. Prot.*/ +#define SCSI_PROTO_ATA 0x08 /* AT Attachment Interface */ +#define SCSI_PROTO_UAS 0x09 /* USB Atached SCSI */ +#define SCSI_PROTO_SOP 0x0a /* SCSI over PCI Express */ +#define SCSI_PROTO_NONE 0x0f /* No specific protocol */ + struct scsi_proto_specific_page { u_int8_t page_code; #define SPSP_PAGE_SAVABLE 0x80 /* Page is savable */ u_int8_t page_length; u_int8_t protocol; -#define SPSP_PROTO_FC 0x00 -#define SPSP_PROTO_SPI 0x01 -#define SPSP_PROTO_SSA 0x02 -#define SPSP_PROTO_1394 0x03 -#define SPSP_PROTO_RDMA 0x04 -#define SPSP_PROTO_ISCSI 0x05 -#define SPSP_PROTO_SAS 0x06 -#define SPSP_PROTO_ADT 0x07 -#define SPSP_PROTO_ATA 0x08 -#define SPSP_PROTO_NONE 0x0f +#define SPSP_PROTO_FC SCSI_PROTO_FC +#define SPSP_PROTO_SPI SCSI_PROTO_SPI +#define SPSP_PROTO_SSA SCSI_PROTO_SSA +#define SPSP_PROTO_1394 SCSI_PROTO_1394 +#define SPSP_PROTO_RDMA SCSI_PROTO_RDMA +#define SPSP_PROTO_ISCSI SCSI_PROTO_ISCSI +#define SPSP_PROTO_SAS SCSI_PROTO_SAS +#define SPSP_PROTO_ADT SCSI_PROTO_ADITP +#define SPSP_PROTO_ATA SCSI_PROTO_ATA +#define SPSP_PROTO_UAS SCSI_PROTO_UAS +#define SPSP_PROTO_SOP SCSI_PROTO_SOP +#define SPSP_PROTO_NONE SCSI_PROTO_NONE }; struct scsi_reserve @@ -1423,15 +1598,9 @@ struct scsi_vpd_device_id struct scsi_vpd_id_descriptor { u_int8_t proto_codeset; -#define SCSI_PROTO_FC 0x00 -#define SCSI_PROTO_SPI 0x01 -#define SCSI_PROTO_SSA 0x02 -#define SCSI_PROTO_1394 0x03 -#define SCSI_PROTO_RDMA 0x04 -#define SCSI_PROTO_ISCSI 0x05 -#define SCSI_PROTO_SAS 0x06 -#define SCSI_PROTO_ADT 0x07 -#define SCSI_PROTO_ATA 0x08 + /* + * See the SCSI_PROTO definitions above for the protocols. + */ #define SVPD_ID_PROTO_SHIFT 4 #define SVPD_ID_CODESET_BINARY 0x01 #define SVPD_ID_CODESET_ASCII 0x02 @@ -2354,6 +2523,22 @@ typedef enum { SSS_FLAG_PRINT_COMMAND = 0x01 } scsi_sense_string_flags; +struct scsi_nv { + const char *name; + uint64_t value; +}; + +typedef enum { + SCSI_NV_FOUND, + SCSI_NV_AMBIGUOUS, + SCSI_NV_NOT_FOUND +} scsi_nv_status; + +typedef enum { + SCSI_NV_FLAG_NONE = 0x00, + SCSI_NV_FLAG_IG_CASE = 0x01 /* Case insensitive comparison */ +} scsi_nv_flags; + struct ccb_scsiio; struct cam_periph; union ccb; @@ -2495,6 +2680,64 @@ struct scsi_vpd_id_descriptor * scsi_get_devid(struct scsi_vpd_device_id *id, uint32_t len, scsi_devid_checkfn_t ck_fn); +int scsi_transportid_sbuf(struct sbuf *sb, + struct scsi_transportid_header *hdr, + uint32_t valid_len); + +const char * scsi_nv_to_str(struct scsi_nv *table, int num_table_entries, + uint64_t value); + +scsi_nv_status scsi_get_nv(struct scsi_nv *table, int num_table_entries, + char *name, int *table_entry, scsi_nv_flags flags); + +int scsi_parse_transportid_64bit(int proto_id, char *id_str, + struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str, int error_str_len); + +int scsi_parse_transportid_spi(char *id_str, + struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str, int error_str_len); + +int scsi_parse_transportid_rdma(char *id_str, + struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str, int error_str_len); + +int scsi_parse_transportid_iscsi(char *id_str, + struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str,int error_str_len); + +int scsi_parse_transportid_sop(char *id_str, + struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str,int error_str_len); + +int scsi_parse_transportid(char *transportid_str, + struct scsi_transportid_header **hdr, + unsigned int *alloc_len, +#ifdef _KERNEL + struct malloc_type *type, int flags, +#endif + char *error_str, int error_str_len); + void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), @@ -2690,6 +2933,20 @@ void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, u_int8_t tag_action, int start, int load_eject, int immediate, u_int8_t sense_len, u_int32_t timeout); +void scsi_persistent_reserve_in(struct ccb_scsiio *csio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *,union ccb *), + uint8_t tag_action, int service_action, + uint8_t *data_ptr, uint32_t dxfer_len, + int sense_len, int timeout); + +void scsi_persistent_reserve_out(struct ccb_scsiio *csio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, + union ccb *), + uint8_t tag_action, int service_action, + int scope, int res_type, uint8_t *data_ptr, + uint32_t dxfer_len, int sense_len, + int timeout); + int scsi_inquiry_match(caddr_t inqbuffer, caddr_t table_entry); int scsi_static_inquiry_match(caddr_t inqbuffer, caddr_t table_entry); |