diff options
author | Chris Horne <Chris.Horne@Sun.COM> | 2008-11-05 14:45:45 -0700 |
---|---|---|
committer | Chris Horne <Chris.Horne@Sun.COM> | 2008-11-05 14:45:45 -0700 |
commit | d91393a8e4d666dd6b6588c980c59cec667835f4 (patch) | |
tree | fd7fb74e5082f8609f864a371c68b695e8318920 /usr/src | |
parent | 8d8453de6847166cd4079123d746fa7e23023bca (diff) | |
download | illumos-gate-d91393a8e4d666dd6b6588c980c59cec667835f4.tar.gz |
PSARC 2008/673 scsi_inquiry(9S) update
6321430 scsi_inquiry(9S) should be updated
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c | 8 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs_tape.c | 8 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci_tpgs.c | 9 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/conf/scsi_confdata.c | 34 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/conf/scsi_confsubr.c | 18 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/impl/scsi_hba.c | 15 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/impl/scsi_subr.c | 157 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/generic/inquiry.h | 297 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/impl/transport.h | 76 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/scsi_address.h | 35 |
10 files changed, 430 insertions, 227 deletions
diff --git a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c index 2885967d6f..e9c9e7aa5c 100644 --- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c +++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs.c @@ -64,7 +64,7 @@ std_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq, VHCI_DEBUG(6, (CE_NOTE, NULL, "std_device_probe: vidpid %s\n", inq->inq_vid)); - if (inq->inq_tpgs == 0) { + if (inq->inq_tpgs == TPGS_FAILOVER_NONE) { VHCI_DEBUG(4, (CE_WARN, NULL, "!std_device_probe: not a standard tpgs device")); return (SFO_DEVICE_PROBE_PHCI); @@ -84,21 +84,21 @@ std_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq, return (SFO_DEVICE_PROBE_PHCI); } - if (inq->inq_tpgs == SCSI_IMPLICIT_FAILOVER) { + if (inq->inq_tpgs == TPGS_FAILOVER_IMPLICIT) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_device_probe: Detected a " "Standard Asymmetric device " "with implicit failover\n")); return (SFO_DEVICE_PROBE_VHCI); } - if (inq->inq_tpgs == SCSI_EXPLICIT_FAILOVER) { + if (inq->inq_tpgs == TPGS_FAILOVER_EXPLICIT) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_device_probe: Detected a " "Standard Asymmetric device " "with explicit failover\n")); return (SFO_DEVICE_PROBE_VHCI); } - if (inq->inq_tpgs == SCSI_BOTH_FAILOVER) { + if (inq->inq_tpgs == TPGS_FAILOVER_BOTH) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!std_device_probe: Detected a " "Standard Asymmetric device " diff --git a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs_tape.c b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs_tape.c index fa75d1565a..d108596e1e 100644 --- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs_tape.c +++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/fops/tpgs_tape.c @@ -101,7 +101,7 @@ tpgs_tape_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq, VHCI_DEBUG(6, (CE_NOTE, NULL, "tpgs_tape_device_probe: vidpid %s\n", inq->inq_vid)); - if (inq->inq_tpgs == 0) { + if (inq->inq_tpgs == TPGS_FAILOVER_NONE) { VHCI_DEBUG(4, (CE_WARN, NULL, "!tpgs_tape_device_probe: not a standard tpgs device")); return (SFO_DEVICE_PROBE_PHCI); @@ -121,21 +121,21 @@ tpgs_tape_device_probe(struct scsi_device *sd, struct scsi_inquiry *inq, return (SFO_DEVICE_PROBE_PHCI); } - if (inq->inq_tpgs == SCSI_IMPLICIT_FAILOVER) { + if (inq->inq_tpgs == TPGS_FAILOVER_IMPLICIT) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!tpgs_tape_device_probe: Detected a " "Standard Asymmetric device " "with implicit failover\n")); return (SFO_DEVICE_PROBE_VHCI); } - if (inq->inq_tpgs == SCSI_EXPLICIT_FAILOVER) { + if (inq->inq_tpgs == TPGS_FAILOVER_EXPLICIT) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!tpgs_tape_device_probe: Detected a " "Standard Asymmetric device " "with explicit failover\n")); return (SFO_DEVICE_PROBE_VHCI); } - if (inq->inq_tpgs == SCSI_BOTH_FAILOVER) { + if (inq->inq_tpgs == TPGS_FAILOVER_BOTH) { VHCI_DEBUG(1, (CE_NOTE, NULL, "!tpgs_tape_device_probe: Detected a " "Standard Asymmetric device " diff --git a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci_tpgs.c b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci_tpgs.c index 1a3fed040b..26bedc5559 100644 --- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci_tpgs.c +++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci_tpgs.c @@ -22,7 +22,6 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" #include <sys/conf.h> #include <sys/file.h> @@ -265,17 +264,17 @@ vhci_tpgs_inquiry(struct scsi_address *ap, struct buf *bp, int *mode) return (1); } - if (inq.inq_tpgs == 0) { + if (inq.inq_tpgs == TPGS_FAILOVER_NONE) { VHCI_DEBUG(1, (CE_WARN, NULL, "!vhci_tpgs_inquiry: zero tpgs_bits")); return (1); } retval = 0; - if (inq.inq_tpgs == SCSI_IMPLICIT_FAILOVER) { + if (inq.inq_tpgs == TPGS_FAILOVER_IMPLICIT) { *mode = SCSI_IMPLICIT_FAILOVER; - } else if (inq.inq_tpgs == SCSI_EXPLICIT_FAILOVER) { + } else if (inq.inq_tpgs == TPGS_FAILOVER_EXPLICIT) { *mode = SCSI_EXPLICIT_FAILOVER; - } else if (inq.inq_tpgs == SCSI_BOTH_FAILOVER) { + } else if (inq.inq_tpgs == TPGS_FAILOVER_BOTH) { *mode = SCSI_BOTH_FAILOVER; } else { VHCI_DEBUG(1, (CE_WARN, NULL, diff --git a/usr/src/uts/common/io/scsi/conf/scsi_confdata.c b/usr/src/uts/common/io/scsi/conf/scsi_confdata.c index d171b7d53f..c41d105fc7 100644 --- a/usr/src/uts/common/io/scsi/conf/scsi_confdata.c +++ b/usr/src/uts/common/io/scsi/conf/scsi_confdata.c @@ -23,8 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef _KERNEL #include <sys/scsi/scsi_types.h> @@ -47,27 +45,29 @@ */ int scsi_options = - SCSI_OPTIONS_PARITY | - SCSI_OPTIONS_SYNC | - SCSI_OPTIONS_LINK | - SCSI_OPTIONS_TAG | - SCSI_OPTIONS_DR | - SCSI_OPTIONS_FAST | - SCSI_OPTIONS_FAST20 | - SCSI_OPTIONS_FAST40 | - SCSI_OPTIONS_FAST80 | - SCSI_OPTIONS_FAST160 | - SCSI_OPTIONS_FAST320 | - SCSI_OPTIONS_QAS | - SCSI_OPTIONS_WIDE; + SCSI_OPTIONS_DR | + SCSI_OPTIONS_LINK | + SCSI_OPTIONS_SYNC | + SCSI_OPTIONS_PARITY | + SCSI_OPTIONS_TAG | + SCSI_OPTIONS_FAST | + SCSI_OPTIONS_WIDE | + SCSI_OPTIONS_FAST20 | + SCSI_OPTIONS_FAST40 | + SCSI_OPTIONS_FAST80 | + SCSI_OPTIONS_FAST160 | + SCSI_OPTIONS_FAST320 | + SCSI_OPTIONS_NLUNS_DEFAULT | + SCSI_OPTIONS_QAS | + 0; /* - * Scsi bus or device reset recovery time (milli secondss.) + * Scsi bus or device reset recovery time in milliseconds. */ unsigned int scsi_reset_delay = SCSI_DEFAULT_RESET_DELAY; /* - * SCSI selection timeout in milli secondss. + * SCSI selection timeout in milliseconds. */ int scsi_selection_timeout = SCSI_DEFAULT_SELECTION_TIMEOUT; diff --git a/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c b/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c index 42fd8dc3ed..1c2da929eb 100644 --- a/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c +++ b/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c @@ -357,30 +357,28 @@ scsi_slave(struct scsi_device *devp, int (*callback)()) } } - /* * Undo scsi_slave - older interface, but still supported + * + * NOTE: The 'sd_inq' inquiry data is now freed by scsi_hba/scsi_vhci code + * as part of free of scsi_device(9S). */ +/*ARGSUSED*/ void scsi_unslave(struct scsi_device *devp) { - if (devp->sd_inq) { - kmem_free((caddr_t)devp->sd_inq, SUN_INQSIZE); - devp->sd_inq = (struct scsi_inquiry *)NULL; - } } - /* * Undo scsi_probe + * + * NOTE: The 'sd_inq' inquiry data is now freed by scsi_hba/scsi_vhci code + * as part of free of scsi_device(9S). */ +/*ARGSUSED*/ void scsi_unprobe(struct scsi_device *devp) { - if (devp->sd_inq) { - kmem_free((caddr_t)devp->sd_inq, SUN_INQSIZE); - devp->sd_inq = (struct scsi_inquiry *)NULL; - } } /* diff --git a/usr/src/uts/common/io/scsi/impl/scsi_hba.c b/usr/src/uts/common/io/scsi/impl/scsi_hba.c index 14e0f321d2..576c0c16c5 100644 --- a/usr/src/uts/common/io/scsi/impl/scsi_hba.c +++ b/usr/src/uts/common/io/scsi/impl/scsi_hba.c @@ -1156,6 +1156,21 @@ failure: if (hba->tran_tgt_free != NULL) { (*hba->tran_tgt_free) (dip, child_dip, hba, sd); } + + /* + * If a inquiry data is still allocated (by scsi_probe()) we + * free the allocation here. This keeps scsi_inq valid for the + * same duration as the corresponding inquiry properties. It + * also allows a tran_tgt_init() implementation that establishes + * sd_inq to deal with deallocation in its tran_tgt_free + * (setting sd_inq back to NULL) without upsetting the + * framework. + */ + if (sd->sd_inq) { + kmem_free(sd->sd_inq, SUN_INQSIZE); + sd->sd_inq = (struct scsi_inquiry *)NULL; + } + mutex_destroy(&sd->sd_mutex); if (hba->tran_hba_flags & SCSI_HBA_TRAN_CLONE) { kmem_free(hba, sizeof (scsi_hba_tran_t)); diff --git a/usr/src/uts/common/io/scsi/impl/scsi_subr.c b/usr/src/uts/common/io/scsi/impl/scsi_subr.c index a9c0ca2d69..f0288041f1 100644 --- a/usr/src/uts/common/io/scsi/impl/scsi_subr.c +++ b/usr/src/uts/common/io/scsi/impl/scsi_subr.c @@ -18,7 +18,6 @@ * * CDDL HEADER END */ - /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. @@ -397,39 +396,29 @@ static char scsi_tmpname[64]; char * scsi_dname(int dtyp) { - static char *dnames[] = { - "Direct Access", - "Sequential Access", - "Printer", - "Processor", - "Write-Once/Read-Many", - "Read-Only Direct Access", - "Scanner", - "Optical", - "Changer", - "Communications", - "Array Controller" - }; - - if ((dtyp & DTYPE_MASK) <= DTYPE_COMM) { - return (dnames[dtyp&DTYPE_MASK]); - } else if (dtyp == DTYPE_NOTPRESENT) { - return ("Not Present"); - } - return ("<unknown device type>"); - + static char *dnames[] = DTYPE_ASCII; + char *dname = NULL; + + if ((dtyp & DTYPE_MASK) < (sizeof (dnames) / sizeof (*dnames))) + dname = dnames[dtyp&DTYPE_MASK]; + else if (dtyp == DTYPE_NOTPRESENT) + dname = "Not Present"; + if ((dname == NULL) || (*dname == '\0')) + dname = "<unknown device type>"; + return (dname); } char * scsi_rname(uchar_t reason) { - static char *rnames[] = CMD_REASON_ASCII; - - if ((reason > CMD_DEV_GONE) || (reason == (CMD_TERMINATED + 1))) { - return ("<unknown reason>"); - } else { - return (rnames[reason]); - } + static char *rnames[] = CMD_REASON_ASCII; + char *rname = NULL; + + if (reason < (sizeof (rnames) / sizeof (*rnames))) + rname = rnames[reason]; + if ((rname == NULL) || (*rname == '\0')) + rname = "<unknown reason>"; + return (rname); } char * @@ -513,7 +502,7 @@ static struct scsi_asq_key_strings extended_sense_list[] = { 0x00, 0x01, "filemark detected", 0x00, 0x02, "end of partition/medium detected", 0x00, 0x03, "setmark detected", - 0x00, 0x04, "begining of partition/medium detected", + 0x00, 0x04, "beginning of partition/medium detected", 0x00, 0x05, "end of data detected", 0x00, 0x06, "i/o process terminated", 0x00, 0x11, "audio play operation in progress", @@ -942,7 +931,7 @@ static struct scsi_asq_key_strings extended_sense_list[] = { 0x5e, 0x03, "idle condition activated by command", 0x5e, 0x04, "standby condition activated by command", 0x60, 0x00, "lamp failure", - 0x61, 0x00, "video aquisition error", + 0x61, 0x00, "video acquisition error", 0x62, 0x00, "scan head positioning error", 0x63, 0x00, "end of user area encountered on this track", 0x63, 0x01, "packet does not fit in available space", @@ -966,11 +955,11 @@ static struct scsi_asq_key_strings extended_sense_list[] = { 0x69, 0x01, "multiple LUN failures", 0x69, 0x02, "parity/data mismatch", 0x6a, 0x00, "informational, refer to log", - 0x6b, 0x00, "state change has occured", + 0x6b, 0x00, "state change has occurred", 0x6b, 0x01, "redundancy level got better", 0x6b, 0x02, "redundancy level got worse", - 0x6c, 0x00, "rebuild failure occured", - 0x6d, 0x00, "recalculate failure occured", + 0x6c, 0x00, "rebuild failure occurred", + 0x6d, 0x00, "recalculate failure occurred", 0x6e, 0x00, "command to logical unit failed", 0x6f, 0x00, "copy protect key exchange failure authentication failure", 0x6f, 0x01, "copy protect key exchange failure key not present", @@ -1132,7 +1121,7 @@ scsi_generic_errmsg(struct scsi_device *devp, char *label, int severity, pad[i] = ' '; } - bzero(buf, 256); + bzero(buf, SCSI_ERRMSG_BUF_LEN); com = cmd_name; (void) sprintf(buf, "Error for Command: %s", scsi_cmd_name(com, cmdlist, tmpbuf)); @@ -1150,7 +1139,7 @@ scsi_generic_errmsg(struct scsi_device *devp, char *label, int severity, if (blkno != -1 || err_blkno != -1 && ((com & 0xf) == SCMD_READ) || ((com & 0xf) == SCMD_WRITE)) { - bzero(buf, 256); + bzero(buf, SCSI_ERRMSG_BUF_LEN); (void) sprintf(buf, "Requested Block: %ld", blkno); buflen = strlen(buf); if (buflen < SCSI_ERRMSG_COLUMN_LEN) { @@ -1165,7 +1154,7 @@ scsi_generic_errmsg(struct scsi_device *devp, char *label, int severity, impl_scsi_log(dev, label, CE_CONT, buf); } - bzero(buf, 256); + bzero(buf, SCSI_ERRMSG_BUF_LEN); (void) strcpy(buf, "Vendor: "); inq_fill(devp->sd_inq->inq_vid, 8, &buf[strlen(buf)]); buflen = strlen(buf); @@ -1187,18 +1176,18 @@ scsi_generic_errmsg(struct scsi_device *devp, char *label, int severity, NULL, NULL, &fru_code_ptr, NULL, NULL); fru_code = (fru_code_ptr ? *fru_code_ptr : 0); - bzero(buf, 256); + bzero(buf, SCSI_ERRMSG_BUF_LEN); (void) sprintf(buf, "Sense Key: %s\n", sense_keys[sense_key]); impl_scsi_log(dev, label, CE_CONT, buf); - bzero(buf, 256); + bzero(buf, SCSI_ERRMSG_BUF_LEN); if ((fru_code != 0) && (decode_fru != NULL)) { (*decode_fru)(devp, buf, SCSI_ERRMSG_BUF_LEN, fru_code); if (buf[0] != NULL) { - bzero(buf1, 256); + bzero(buf1, SCSI_ERRMSG_BUF_LEN); (void) sprintf(&buf1[strlen(buf1)], "ASC: 0x%x (%s)", asc, scsi_asc_ascq_name(asc, ascq, @@ -1450,6 +1439,94 @@ scsi_get_device_type_scsi_options(dev_info_t *dip, } /* + * Find the scsi_options for a scsi_device. The precedence is: + * + * target<%d>-scsi-options highest + * device-type-scsi-options + * per bus scsi-options (parent) + * global scsi-options + * default_scsi_options argument lowest + * + * If the global is used then it has already been established + * on the parent scsi_hba_attach_setup. + */ +int +scsi_get_scsi_options(struct scsi_device *sd, int default_scsi_options) +{ + dev_info_t *parent; + int options = -1; + int tgt; + char topt[32]; + + if ((sd == NULL) || (sd->sd_dev == NULL)) + return (default_scsi_options); + + parent = ddi_get_parent(sd->sd_dev); + + if ((tgt = ddi_prop_get_int(DDI_DEV_T_ANY, sd->sd_dev, + DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "target", -1)) != -1) { + (void) sprintf(topt, "target%d-scsi-options", tgt); + options = ddi_prop_get_int(DDI_DEV_T_ANY, parent, + DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, topt, -1); + } + + if (options == -1) + options = scsi_get_device_type_scsi_options(parent, sd, -1); + + if (options == -1) + options = ddi_prop_get_int(DDI_DEV_T_ANY, parent, + DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "scsi-options", -1); + + if (options == -1) + options = default_scsi_options; + + return (options); +} + +/* + * Use scsi-options to return the maximum number of LUNs. + */ +int +scsi_get_scsi_maxluns(struct scsi_device *sd) +{ + int options; + int maxluns; + + ASSERT(sd && sd->sd_inq); + options = scsi_get_scsi_options(sd, SCSI_OPTIONS_NLUNS_DEFAULT); + + switch (SCSI_OPTIONS_NLUNS(options)) { + default: + case SCSI_OPTIONS_NLUNS_DEFAULT: + /* based on scsi version of target */ + if (sd->sd_inq->inq_ansi < SCSI_VERSION_3) + maxluns = SCSI_8LUN_PER_TARGET; /* 8 */ + else + maxluns = SCSI_16LUNS_PER_TARGET; /* 16 */ + break; + case SCSI_OPTIONS_NLUNS_1: + maxluns = SCSI_1LUN_PER_TARGET; /* 1 */ + break; + case SCSI_OPTIONS_NLUNS_8: + maxluns = SCSI_8LUN_PER_TARGET; /* 8 */ + break; + case SCSI_OPTIONS_NLUNS_16: + maxluns = SCSI_16LUNS_PER_TARGET; /* 16 */ + break; + case SCSI_OPTIONS_NLUNS_32: + maxluns = SCSI_32LUNS_PER_TARGET; /* 32 */ + break; + } + + /* For SCSI-1 we never support > 8 LUNs */ + if ((sd->sd_inq->inq_ansi <= SCSI_VERSION_1) && + (maxluns > SCSI_8LUN_PER_TARGET)) + maxluns = SCSI_8LUN_PER_TARGET; + + return (maxluns); +} + +/* * Functions for format-neutral sense data functions */ int diff --git a/usr/src/uts/common/sys/scsi/generic/inquiry.h b/usr/src/uts/common/sys/scsi/generic/inquiry.h index eb2788a689..952144de51 100644 --- a/usr/src/uts/common/sys/scsi/generic/inquiry.h +++ b/usr/src/uts/common/sys/scsi/generic/inquiry.h @@ -26,78 +26,77 @@ #ifndef _SYS_SCSI_GENERIC_INQUIRY_H #define _SYS_SCSI_GENERIC_INQUIRY_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif /* - * SCSI Inquiry Data + * SCSI Standard Inquiry Data: * * Format of data returned as a result of an INQUIRY command. * + * NOTE: Fields marked 'DEPRECATED' are defined in older versions of t10 "SCSI + * Primary Command" spec, and are marked 'Obsolete' in newer versions. */ - #if defined(_BIT_FIELDS_LTOH) struct scsi_inquiry { - /* * byte 0 * * Bits 7-5 are the Peripheral Device Qualifier * Bits 4-0 are the Peripheral Device Type - * */ - uchar_t inq_dtype; /* byte 1 */ - uchar_t inq_qual : 7, /* device type qualifier */ - inq_rmb : 1; /* removable media */ + uchar_t inq_qual : 7, /* device type qualifier */ + inq_rmb : 1; /* removable media */ /* byte 2 */ - uchar_t inq_ansi : 3, /* ANSI version */ - inq_ecma : 3, /* ECMA version */ - inq_iso : 2; /* ISO version */ + uchar_t inq_ansi : 3, /* ANSI version */ + inq_ecma : 3, /* ECMA version */ + inq_iso : 2; /* ISO version */ /* byte 3 */ - uchar_t inq_rdf : 4, /* response data format */ - inq_hisup : 1, /* Hierarchial support */ + uchar_t inq_rdf : 4, /* response data format */ + inq_hisup : 1, /* hierarchical addressing model */ inq_normaca : 1, /* setting NACA bit supported */ - inq_trmiop : 1, /* TERMINATE I/O PROC msg */ - inq_aenc : 1; /* async event notification cap. */ + inq_trmiop : 1, /* DEPRECATED: terminate I/O proc */ + inq_aenc : 1; /* DEPRECATED: async event notify */ /* bytes 4-7 */ + uchar_t inq_len; /* additional length */ - uchar_t inq_len; /* additional length */ - - uchar_t : 4, /* reserved */ - inq_tpgs : 2, /* supports Target Port Group set */ - : 2; - uchar_t inq_addr16 : 1, /* supports 16 bit wide SCSI addr */ - inq_addr32 : 1, /* supports 32 bit wide SCSI addr */ - inq_ackqreqq : 1, /* data tranfer on Q cable */ - inq_mchngr : 1, /* embedded/attached to medium chngr */ - inq_dualp : 1, /* dual port device */ - inq_port : 1, /* port receiving inquiry cmd */ - : 1, /* reserved */ - inq_bque : 1; /* combined with cmdque */ - - uchar_t inq_sftre : 1, /* supports Soft Reset option */ - inq_cmdque : 1, /* supports command queueing */ - inq_trandis : 1, /* supports transfer disable messages */ - inq_linked : 1, /* supports linked commands */ - inq_sync : 1, /* supports synchronous data xfers */ - inq_wbus16 : 1, /* supports 16 bit wide data xfers */ - inq_wbus32 : 1, /* supports 32 bit wide data xfers */ - inq_reladdr : 1; /* supports relative addressing */ + uchar_t inq_protect : 1, /* supports protection information */ + inq_5_1 : 1, + inq_5_2 : 1, + inq_3pc : 1, /* third-party copy */ + inq_tpgs : 2, /* impl/expl asymmetric lun access */ + inq_acc : 1, /* access controls coordinator */ + inq_sccs : 1; /* embedded storage array */ + + uchar_t inq_addr16 : 1, /* SPI: 16-bit wide SCSI addr */ + inq_addr32 : 1, /* DEPRECATED: 32 bit wide address */ + inq_ackqreqq : 1, /* DEPRECATED: data xfer on Q cable */ + inq_mchngr : 1, /* DEPRECATED: embeded medium changer */ + inq_dualp : 1, /* multi port device */ + inq_port : 1, /* DEPRECATED: port rcv inquiry cmd */ + inq_encserv : 1, /* embedded enclosure services */ + inq_bque : 1; /* DEPRECATED: combined with cmdque */ + + uchar_t inq_sftre : 1, /* DEPRECATED: Soft Reset option */ + inq_cmdque : 1, /* supports command queueing */ + inq_trandis : 1, /* DEPRECATED: transfer disable msgs */ + inq_linked : 1, /* DEPRECATED: linked commands */ + inq_sync : 1, /* SPI: synchronous data xfers */ + inq_wbus16 : 1, /* SPI: 16-bit wide data xfers */ + inq_wbus32 : 1, /* DEPRECATED: 32 bit wide data xfers */ + inq_reladdr : 1; /* DEPRECATED: relative addressing */ /* bytes 8-35 */ - - char inq_vid[8]; /* vendor ID */ - char inq_pid[16]; /* product ID */ - char inq_revision[4]; /* revision level */ + char inq_vid[8]; /* vendor ID */ + char inq_pid[16]; /* product ID */ + char inq_revision[4]; /* revision level */ /* * Bytes 36-47 are reserved: @@ -110,23 +109,59 @@ struct scsi_inquiry { char inq_serial[12]; /* - * Bytes 48-95 are reserved. - * 96 to 'n' are vendor-specific parameter bytes + * Bytes 48-55 are reserved. + */ + uchar_t __inq_48 : 8; + uchar_t __inq_49 : 8; + uchar_t __inq_50 : 8; + uchar_t __inq_51 : 8; + uchar_t __inq_52 : 8; + uchar_t __inq_53 : 8; + uchar_t __inq_54 : 8; + uchar_t __inq_55 : 8; + + /* + * The meanings of byte 56 is specific to SPI-3. For protocols older + * or other than this these fields are reserved. + */ + uchar_t inq_ius : 1, /* SPI3: information units */ + inq_qas : 1, /* SPI3: quick arb sel */ + inq_clk : 2, /* SPI3: clocking */ + __inq_56_4 : 1, /* reserved */ + __inq_56_5 : 1, /* reserved */ + __inq_56_6 : 1, /* reserved */ + __inq_56_7 : 1; /* reserved */ + + uchar_t __inq_57 : 8; /* reserved */ + + /* + * byte pairs 58-73 are version descriptors + * See: Table 51: dpANS SCSI Primary Commands - 2 (SPC-2) T10/1236 + */ + struct inq_vd { + uchar_t inq_vd_msb; + uchar_t inq_vd_lsb; + } inq_vd[8]; + + /* + * Bytes 74-95 are reserved. + * 96 to 'n' are vendor-specific parameter bytes. + * + * Pad structure to 132 bytes so that access to some vendor-specific + * data is possible via scsi_device(9S) sd_inq (for mpxio). */ + uchar_t __inq_74_127[132 - 74]; }; #elif defined(_BIT_FIELDS_HTOL) struct scsi_inquiry { - /* * byte 0 * * Bits 7-5 are the Peripheral Device Qualifier * Bits 4-0 are the Peripheral Device Type - * */ - uchar_t inq_dtype; /* byte 1 */ @@ -139,44 +174,44 @@ struct scsi_inquiry { inq_ansi : 3; /* ANSI version */ /* byte 3 */ - uchar_t inq_aenc : 1, /* async event notification cap. */ - inq_trmiop : 1, /* supports TERMINATE I/O PROC msg */ + uchar_t inq_aenc : 1, /* DEPRECATED: async event notify */ + inq_trmiop : 1, /* DEPRECATED: terminate I/O proc */ inq_normaca : 1, /* setting NACA bit supported */ - inq_hisup : 1, /* hierachial support */ + inq_hisup : 1, /* hierarchical addressing model */ inq_rdf : 4; /* response data format */ /* bytes 4-7 */ - uchar_t inq_len; /* additional length */ - uchar_t : 2, /* reserved */ - inq_tpgs : 2, /* supports Target Port Group Set */ - : 4; - - uchar_t inq_bque : 1, /* combined with cmdque */ - : 1, /* reserved */ - inq_port : 1, /* port receiving inquiry cmd */ - inq_dualp : 1, /* dual port device */ - inq_mchngr : 1, /* embedded/attached to medium chngr */ - inq_ackqreqq : 1, /* data tranfer on Q cable */ - inq_addr32 : 1, /* supports 32 bit wide SCSI addr */ - inq_addr16 : 1; /* supports 16 bit wide SCSI addr */ - - uchar_t inq_reladdr : 1, /* supports relative addressing */ - inq_wbus32 : 1, /* supports 32 bit wide data xfers */ - inq_wbus16 : 1, /* supports 16 bit wide data xfers */ - inq_sync : 1, /* supports synchronous data xfers */ - inq_linked : 1, /* supports linked commands */ - inq_trandis : 1, /* supports transfer disable messages */ + uchar_t inq_sccs : 1, /* embedded storage array */ + inq_acc : 1, /* access controls coordinator */ + inq_tpgs : 2, /* impl/expl asymmetric lun access */ + inq_3pc : 1, /* third-party copy */ + inq_5_2 : 1, + inq_5_1 : 1, + inq_protect : 1; /* supports protection information */ + + uchar_t inq_bque : 1, /* DEPRECATED: combined with cmdque */ + inq_encserv : 1, /* embedded enclosure services */ + inq_port : 1, /* DEPRECATED: port rcv inquiry cmd */ + inq_dualp : 1, /* multi port device */ + inq_mchngr : 1, /* DEPRECATED: embeded medium changer */ + inq_ackqreqq : 1, /* DEPRECATED: data xfer on Q cable */ + inq_addr32 : 1, /* DEPRECATED: 32 bit wide address */ + inq_addr16 : 1; /* SPI: 16-bit wide SCSI addr */ + + uchar_t inq_reladdr : 1, /* DEPRECATED: relative addressing */ + inq_wbus32 : 1, /* DEPRECATED: 32 bit wide data xfers */ + inq_wbus16 : 1, /* SPI: 16-bit wide data xfers */ + inq_sync : 1, /* SPI: synchronous data xfers */ + inq_linked : 1, /* DEPRECATED: linked commands */ + inq_trandis : 1, /* DEPRECATED: transfer disable msgs */ inq_cmdque : 1, /* supports command queueing */ - inq_sftre : 1; /* supports Soft Reset option */ + inq_sftre : 1; /* DEPRECATED: Soft Reset option */ /* bytes 8-35 */ - char inq_vid[8]; /* vendor ID */ - char inq_pid[16]; /* product ID */ - char inq_revision[4]; /* revision level */ /* @@ -190,9 +225,48 @@ struct scsi_inquiry { char inq_serial[12]; /* - * Bytes 48-95 are reserved. - * 96 to 'n' are vendor-specific parameter bytes + * Bytes 48-55 are reserved. + */ + uchar_t __inq_48 : 8; + uchar_t __inq_49 : 8; + uchar_t __inq_50 : 8; + uchar_t __inq_51 : 8; + uchar_t __inq_52 : 8; + uchar_t __inq_53 : 8; + uchar_t __inq_54 : 8; + uchar_t __inq_55 : 8; + + /* + * The meanings of byte 56 is specific to SPI-3. For protocols older + * or other than this these fields are reserved. + */ + uchar_t __inq_56_7 : 1, /* reserved */ + __inq_56_6 : 1, /* reserved */ + __inq_56_5 : 1, /* reserved */ + __inq_56_4 : 1, /* reserved */ + inq_clk : 2, /* SPI3: clocking */ + inq_qas : 1, /* SPI3: quick arb sel */ + inq_ius : 1; /* SPI3: information units */ + + uchar_t __inq_57 : 8; /* reserved */ + + /* + * byte pairs 58-73 are version descriptors + * See: Table 51: dpANS SCSI Primary Commands - 2 (SPC-2) T10/1236 */ + struct inq_vd { + uchar_t inq_vd_msb; + uchar_t inq_vd_lsb; + } inq_vd[8]; + + /* + * Bytes 74-95 are reserved. + * 96 to 'n' are vendor-specific parameter bytes. + * + * Pad structure to 132 bytes so that access to some vendor-specific + * data is possible via scsi_device(9S) sd_inq (for mpxio). + */ + uchar_t __inq_74_127[132 - 74]; }; #else #error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined @@ -201,23 +275,22 @@ struct scsi_inquiry { /* * Defined Peripheral Device Types */ - -#define DTYPE_DIRECT 0x00 -#define DTYPE_SEQUENTIAL 0x01 +#define DTYPE_DIRECT 0x00 /* magnetic disk */ +#define DTYPE_SEQUENTIAL 0x01 /* magnetic tape */ #define DTYPE_PRINTER 0x02 #define DTYPE_PROCESSOR 0x03 -#define DTYPE_WORM 0x04 +#define DTYPE_WORM 0x04 /* some optical disks */ #define DTYPE_RODIRECT 0x05 #define DTYPE_SCANNER 0x06 /* obsolete */ #define DTYPE_OPTICAL 0x07 -#define DTYPE_CHANGER 0x08 +#define DTYPE_CHANGER 0x08 /* jukeboxes */ #define DTYPE_COMM 0x09 /* obsolete */ #define DTYPE_ARRAY_CTRL 0x0C -#define DTYPE_ESI 0x0D -#define DTYPE_RBC 0x0E -#define DTYPE_OCRW 0x0F +#define DTYPE_ESI 0x0D /* Enclosure services device */ +#define DTYPE_RBC 0x0E /* Simplified direct-access device */ +#define DTYPE_OCRW 0x0F /* Optical card reader/writer device */ #define DTYPE_BCC 0x10 -#define DTYPE_OSD 0x11 +#define DTYPE_OSD 0x11 /* Object-based Storage Device */ #define DTYPE_ADC 0x12 /* * Device types 0x13-0x1D are reserved in spc-3 (r23) @@ -225,33 +298,39 @@ struct scsi_inquiry { #define DTYPE_WELLKNOWN 0x1E #define DTYPE_UNKNOWN 0x1F - #define DTYPE_MASK 0x1F +/* ASCII mapping used by scsi_dname(9F) */ +#define DTYPE_ASCII { \ + "Direct Access", "Sequential Access", "Printer", "Processor", \ + "Write-Once/Read-Many", "Read-Only Direct Access", "Scanner", \ + "Optical", "Changer", "Communications", "Unknown-0A", \ + "Unknown-0B", "Array Controller", "Enclosure-Services", \ + "Simplified-Direct-Access", "Optical-Card", "Bridge", \ + "Object-Storage", NULL} + /* * The peripheral qualifier tells us more about a particular device. * (DPQ == DEVICE PERIPHERAL QUALIFIER). */ - +#define DPQ_MASK 0x60 /* DPQ bits */ #define DPQ_POSSIBLE 0x00 /* * The specified peripheral device type is * currently connected to this logical unit. - * If the target cannot detrermine whether + * If the target cannot determine whether * or not a physical device is currently * connected, it shall also return this * qualifier. */ - #define DPQ_SUPPORTED 0x20 /* * The target is capable of supporting the * specified peripheral device type on this - * logical unit, however the the physical - * device is not currently connected to this - * logical unit. + * logical unit, however the physical device + * is not currently connected to this logical + * unit. */ - #define DPQ_NEVER 0x60 /* * The target is not capable of supporting a @@ -261,12 +340,12 @@ struct scsi_inquiry { * in order to provide compatibility with * previous versions of SCSI. */ - #define DPQ_VUNIQ 0x80 /* * If this bit is set, this is a vendor * unique qualifier. */ + /* * To maintain compatibility with previous versions * of inquiry data formats, if a device peripheral @@ -280,22 +359,26 @@ struct scsi_inquiry { * should be checking for only the least 5 bits to see * whether the correct type is at the specified nexus. */ - #define DTYPE_NOTPRESENT (DPQ_NEVER | DTYPE_UNKNOWN) /* - * Defined Response Data Formats - * - * RDF_LEVEL0 means that this structure complies with SCSI-1 spec. - * - * RDF_CCS means that this structure complies with CCS pseudo-spec. - * - * RDF_SCSI2 means that the structure complies with the SCSI-2/3 spec. + * Defined Response Data Formats: */ +#define RDF_LEVEL0 0x00 /* no conformance claim (SCSI-1) */ +#define RDF_CCS 0x01 /* Obsolete (pseudo-spec) */ +#define RDF_SCSI2 0x02 /* Obsolete (SCSI-2/3 spec) */ +#define RDF_SCSI_SPC 0x03 /* ANSI INCITS 301-1997 (SPC) */ +#define RDF_SCSI_SPC2 0x04 /* ANSI INCITS 351-2001 (SPC-2) */ +#define RDF_SCSI_SPC3 0x05 /* ANSI INCITS 408-2005 (SPC-3) */ +#define RDF_SCSI_SPC4 0x06 /* t10 (SPC-4) */ -#define RDF_LEVEL0 0x00 -#define RDF_CCS 0x01 -#define RDF_SCSI2 0x02 +/* + * Defined Target Port Group Select values: + */ +#define TPGS_FAILOVER_NONE 0x0 +#define TPGS_FAILOVER_IMPLICIT 0x1 +#define TPGS_FAILOVER_EXPLICIT 0x2 +#define TPGS_FAILOVER_BOTH 0x3 /* * SPC-3 revision 21c, section 7.6.4.1 @@ -323,14 +406,14 @@ struct vpd_desc { #if defined(_BIT_FIELDS_LTOH) uchar_t code_set : 4, proto_id : 4; - uchar_t id_type : 4, + uchar_t id_type : 4, association : 2, : 1, piv : 1; #elif defined(_BIT_FIELDS_HTOL) uchar_t proto_id : 4, code_set : 4; - uchar_t piv : 1, + uchar_t piv : 1, : 1, association : 2, id_type : 4; diff --git a/usr/src/uts/common/sys/scsi/impl/transport.h b/usr/src/uts/common/sys/scsi/impl/transport.h index 20c6ea3854..f80c634ae4 100644 --- a/usr/src/uts/common/sys/scsi/impl/transport.h +++ b/usr/src/uts/common/sys/scsi/impl/transport.h @@ -26,7 +26,6 @@ #ifndef _SYS_SCSI_IMPL_TRANSPORT_H #define _SYS_SCSI_IMPL_TRANSPORT_H - /* * Include the loadable module wrapper. */ @@ -63,19 +62,17 @@ struct scsi_hba_tran { * Private fields for use by the HBA itself. */ void *tran_hba_private; /* HBA softstate */ - void *tran_tgt_private; /* target-specific info */ /* - * Only used to refer to a particular scsi device - * if the entire scsi_hba_tran structure is "cloned" - * per target device, otherwise NULL. + * The following two fields are only used in the SCSI_HBA_TRAN_CLONE + * case. */ + void *tran_tgt_private; /* target-specific info */ struct scsi_device *tran_sd; /* * Vectors to point to specific HBA entry points */ - int (*tran_tgt_init)( dev_info_t *hba_dip, dev_info_t *tgt_dip, @@ -274,6 +271,7 @@ struct scsi_hba_tran { ddi_dma_attr_t tran_dma_attr; void *tran_extension; + /* * An fm_capable HBA driver can set tran_fm_capable prior to * scsi_hba_attach_setup(). If not set, SCSA provides a default @@ -315,56 +313,57 @@ _NOTE(SCHEME_PROTECTS_DATA("serialized by target driver", \ * Prototypes for SCSI HBA interface functions * * All these functions are public interfaces, with the - * exception of scsi_initialize_hba_interface() and - * scsi_uninitialize_hba_interface(), called by the - * scsi module _init() and _fini(), respectively. + * exception of: + * interface called by + * scsi_initialize_hba_interface() _init() of scsi module + * scsi_uninitialize_hba_interface() _fini() of scsi module */ -extern void scsi_initialize_hba_interface(void); +void scsi_initialize_hba_interface(void); #ifdef NO_SCSI_FINI_YET -extern void scsi_uninitialize_hba_interface(void); +void scsi_uninitialize_hba_interface(void); #endif /* NO_SCSI_FINI_YET */ -extern int scsi_hba_init( +int scsi_hba_init( struct modlinkage *modlp); -extern void scsi_hba_fini( +void scsi_hba_fini( struct modlinkage *modlp); -extern int scsi_hba_attach( +int scsi_hba_attach( dev_info_t *hba_dip, ddi_dma_lim_t *hba_lim, scsi_hba_tran_t *hba_tran, int flags, void *hba_options); -extern int scsi_hba_attach_setup( +int scsi_hba_attach_setup( dev_info_t *hba_dip, ddi_dma_attr_t *hba_dma_attr, scsi_hba_tran_t *hba_tran, int flags); -extern int scsi_hba_detach( +int scsi_hba_detach( dev_info_t *hba_dip); -extern scsi_hba_tran_t *scsi_hba_tran_alloc( +scsi_hba_tran_t *scsi_hba_tran_alloc( dev_info_t *hba_dip, int flags); -extern int scsi_tran_ext_alloc( +int scsi_tran_ext_alloc( scsi_hba_tran_t *hba_tran, size_t length, int flags); -extern void scsi_tran_ext_free( +void scsi_tran_ext_free( scsi_hba_tran_t *hba_tran, size_t length); -extern void scsi_hba_tran_free( +void scsi_hba_tran_free( scsi_hba_tran_t *hba_tran); -extern int scsi_hba_probe( +int scsi_hba_probe( struct scsi_device *sd, int (*callback)(void)); @@ -373,12 +372,19 @@ char *scsi_get_device_type_string( dev_info_t *hba_dip, struct scsi_device *devp); -extern int scsi_get_device_type_scsi_options( +int scsi_get_scsi_maxluns( + struct scsi_device *sd); + +int scsi_get_scsi_options( + struct scsi_device *sd, + int default_scsi_options); + +int scsi_get_device_type_scsi_options( dev_info_t *hba_dip, struct scsi_device *devp, int default_scsi_options); -extern struct scsi_pkt *scsi_hba_pkt_alloc( +struct scsi_pkt *scsi_hba_pkt_alloc( dev_info_t *hba_dip, struct scsi_address *ap, int cmdlen, @@ -388,29 +394,29 @@ extern struct scsi_pkt *scsi_hba_pkt_alloc( int (*callback)(caddr_t), caddr_t arg); -extern void scsi_hba_pkt_free( +void scsi_hba_pkt_free( struct scsi_address *ap, struct scsi_pkt *pkt); -extern int scsi_hba_lookup_capstr( +int scsi_hba_lookup_capstr( char *capstr); -extern int scsi_hba_in_panic(void); +int scsi_hba_in_panic(void); -extern int scsi_hba_open( +int scsi_hba_open( dev_t *devp, int flags, int otyp, cred_t *credp); -extern int scsi_hba_close( +int scsi_hba_close( dev_t dev, int flag, int otyp, cred_t *credp); -extern int scsi_hba_ioctl( +int scsi_hba_ioctl( dev_t dev, int cmd, intptr_t arg, @@ -418,7 +424,7 @@ extern int scsi_hba_ioctl( cred_t *credp, int *rvalp); -extern void scsi_hba_nodename_compatible_get( +void scsi_hba_nodename_compatible_get( struct scsi_inquiry *inq, char *binding_set, int dtype_node, @@ -427,12 +433,12 @@ extern void scsi_hba_nodename_compatible_get( char ***compatiblep, int *ncompatiblep); -extern void scsi_hba_nodename_compatible_free( +void scsi_hba_nodename_compatible_free( char *nodename, char **compatible); -extern int scsi_hba_prop_update_inqstring( +int scsi_hba_prop_update_inqstring( struct scsi_device *devp, char *name, char *data, @@ -440,6 +446,11 @@ extern int scsi_hba_prop_update_inqstring( /* * Flags for scsi_hba_attach + * + * SCSI_HBA_TRAN_CLONE TRAN_CLONE is a KLUDGE to address current + * limitations of the scsi_address(9S) structure + * via duplication of scsi_hba_tran(9S) and + * introduction of tran_tgt_private. */ #define SCSI_HBA_TRAN_CLONE 0x01 /* clone scsi_hba_tran_t */ /* structure per target */ @@ -477,7 +488,6 @@ extern int scsi_hba_prop_update_inqstring( #endif /* _KERNEL */ - #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/sys/scsi/scsi_address.h b/usr/src/uts/common/sys/scsi/scsi_address.h index 29a47e7afa..f193b4f673 100644 --- a/usr/src/uts/common/sys/scsi/scsi_address.h +++ b/usr/src/uts/common/sys/scsi/scsi_address.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_SCSI_SCSI_ADDRESS_H #define _SYS_SCSI_SCSI_ADDRESS_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/scsi/scsi_types.h> #ifdef __cplusplus @@ -71,10 +68,34 @@ struct scsi_address { /* Not used */ }; -/* Address property names */ +/* Device unit-address property names */ #define SCSI_ADDR_PROP_TARGET "target" #define SCSI_ADDR_PROP_LUN "lun" +/* + * Normalized representation of a scsi_lun (with SCSI-2 lun positioned + * for compatibility). + */ +typedef uint64_t scsi_lun64_t; +#define PRIlun64 PRIx64 +#ifdef _LP64 +#define SCSI_LUN64_ILLEGAL (-1L) +#else /* _LP64 */ +#define SCSI_LUN64_ILLEGAL (-1LL) +#endif /* _LP64 */ + +/* Structure of a 64-bit SCSI LUN per SCSI standard */ +typedef struct scsi_lun { + uchar_t sl_lun1_msb; /* format */ + uchar_t sl_lun1_lsb; /* first level */ + uchar_t sl_lun2_msb; + uchar_t sl_lun2_lsb; /* second level */ + uchar_t sl_lun3_msb; + uchar_t sl_lun3_lsb; /* third level */ + uchar_t sl_lun4_msb; + uchar_t sl_lun4_lsb; /* fourth level */ +} scsi_lun_t; + #ifdef __cplusplus } #endif |