From 7f0ccdf947fabcfc0c34dffe169de18e4ee3aa1f Mon Sep 17 00:00:00 2001 From: ken Date: Tue, 30 Nov 2010 22:39:46 +0000 Subject: Add Serial Management Protocol (SMP) passthrough support to CAM. This includes support in the kernel, camcontrol(8), libcam and the mps(4) driver for SMP passthrough. The CAM SCSI probe code has been modified to fetch Inquiry VPD page 0x00 to determine supported pages, and will now fetch page 0x83 in addition to page 0x80 if supported. Add two new CAM CCBs, XPT_SMP_IO, and XPT_GDEV_ADVINFO. The SMP CCB is intended for SMP requests and responses. The ADVINFO is currently used to fetch cached VPD page 0x83 data from the transport layer, but is intended to be extensible to fetch other types of device-specific data. SMP-only devices are not currently represented in the CAM topology, and so the current semantics are that the SIM will route SMP CCBs to either the addressed device, if it contains an SMP target, or its parent, if it contains an SMP target. (This is noted in cam_ccb.h, since it will change later once we have the ability to have SMP-only devices in CAM's topology.) smp_all.c, smp_all.h: New helper routines for SMP. This includes SMP request building routines, response parsing routines, error decoding routines, and structure definitions for a number of SMP commands. libcam/Makefile: Add smp_all.c to libcam, so that SMP functionality is available to userland applications. camcontrol.8, camcontrol.c: Add smp passthrough support to camcontrol. Several new subcommands are now available: 'smpcmd' functions much like 'cmd', except that it allows the user to send generic SMP commands. 'smprg' sends the SMP report general command, and displays the decoded output. It will automatically fetch extended output if it is available. 'smppc' sends the SMP phy control command, with any number of potential options. Among other things, this allows the user to reset a phy on a SAS expander, or disable a phy on an expander. 'smpmaninfo' sends the SMP report manufacturer information and displays the decoded output. 'smpphylist' displays a list of phys on an expander, and the CAM devices attached to those phys, if any. cam.h, cam.c: Add a status value for SMP errors (CAM_SMP_STATUS_ERROR). Add a missing description for CAM_SCSI_IT_NEXUS_LOST. Add support for SMP commands to cam_error_string(). cam_ccb.h: Rename the CAM_DIR_RESV flag to CAM_DIR_BOTH. SMP commands are by nature bi-directional, and we may need to support bi-directional SCSI commands later. Add the XPT_SMP_IO CCB. Since SMP commands are bi-directional, there are pointers for both the request and response. Add a fill routine for SMP CCBs. Add the XPT_GDEV_ADVINFO CCB. This is currently used to fetch cached page 0x83 data from the transport later, but is extensible to fetch many other types of data. cam_periph.c: Add support in cam_periph_mapmem() for XPT_SMP_IO and XPT_GDEV_ADVINFO CCBs. cam_xpt.c: Add support for executing XPT_SMP_IO CCBs. cam_xpt_internal.h: Add fields for VPD pages 0x00 and 0x83 in struct cam_ed. scsi_all.c: Add scsi_get_sas_addr(), a function that parses VPD page 0x83 data and pulls out a SAS address. scsi_all.h: Add VPD page 0x00 and 0x83 structures, and a prototype for scsi_get_sas_addr(). scsi_pass.c: Add support for mapping buffers in XPT_SMP_IO and XPT_GDEV_ADVINFO CCBs. scsi_xpt.c: In the SCSI probe code, first ask the device for VPD page 0x00. If any VPD pages are supported, that page is required to be implemented. Based on the response, we may probe for the serial number (page 0x80) or device id (page 0x83). Add support for the XPT_GDEV_ADVINFO CCB. sys/conf/files: Add smp_all.c. mps.c: Add support for passing in a uio in mps_map_command(), so we can map a S/G list at once. Add support for SMP passthrough commands in mps_data_cb(). SMP is a special case, because the first buffer in the S/G list is outbound and the second buffer is inbound. Add support for warning the user if the busdma code comes back with more buffers than will work for the command. This will, for example, help the user determine why an SMP command failed if busdma comes back with three buffers. mps_pci.c: Add sys/uio.h. mps_sas.c: Add the SAS address and the parent handle to the list of fields we pull from device page 0 and cache in struct mpssas_target. These are needed for SMP passthrough. Add support for the XPT_SMP_IO CCB. For now, this CCB is routed to the addressed device if it supports SMP, or to its parent if it does not and the parent does. This is necessary because CAM does not currently support SMP-only nodes in the topology. Make SMP passthrough support conditional on __FreeBSD_version >= 900026. This will make it easier to MFC this change to the driver without MFCing the CAM changes as well. mps_user.c: Un-staticize mpi_init_sge() so we can use it for the SMP passthrough code. mpsvar.h: Add a uio and iovecs into struct mps_command for SMP passthrough commands. Add a cm_max_segs field to struct mps_command so that we can warn the user if busdma comes back with too many segments. Clear the cm_reply when a command gets freed. If it is not cleared, reply frames will eventually get freed into the pool multiple times and corrupt the pool. (This fix is from scottl.) Add a prototype for mpi_init_sge(). sys/param.h: Bump __FreeBSD_version to 900026 for the for the inclusion of the XPT_GDEV_ADVINFO and XPT_SMP_IO CAM CCBs. --- sbin/camcontrol/camcontrol.8 | 218 +++++- sbin/camcontrol/camcontrol.c | 1496 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 1620 insertions(+), 94 deletions(-) (limited to 'sbin') diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8 index b85f81d..fc0609a 100644 --- a/sbin/camcontrol/camcontrol.8 +++ b/sbin/camcontrol/camcontrol.8 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 1, 2010 +.Dd November 30, 2010 .Dt CAMCONTROL 8 .Os .Sh NAME @@ -131,6 +131,43 @@ .Op Fl r Ar fmt .Ek .Nm +.Ic smpcmd +.Op device id +.Op generic args +.Aq Fl r Ar len Ar fmt Op args +.Aq Fl R Ar len Ar fmt Op args +.Nm +.Ic smprg +.Op device id +.Op generic args +.Op Fl l +.Nm +.Ic smppc +.Op device id +.Op generic args +.Aq Fl p Ar phy +.Op Fl l +.Op Fl o Ar operation +.Op Fl d Ar name +.Op Fl m Ar rate +.Op Fl M Ar rate +.Op Fl T Ar pp_timeout +.Op Fl a Ar enable|disable +.Op Fl A Ar enable|disable +.Op Fl s Ar enable|disable +.Op Fl S Ar enable|disable +.Nm +.Ic smpphylist +.Op device id +.Op generic args +.Op Fl l +.Op Fl q +.Nm +.Ic smpmaninfo +.Op device id +.Op generic args +.Op Fl l +.Nm .Ic debug .Op Fl I .Op Fl P @@ -554,6 +591,177 @@ If the format is .Sq - , 11 result registers will be written to standard output in hex. .El +.It Ic smpcmd +Allows the user to send an arbitrary Serial +Management Protocol (SMP) command to a device. +The +.Ic smpcmd +function requires the +.Fl r +argument to specify the SMP request to be sent, and the +.Fl R +argument to specify the format of the SMP response. +The syntax for the SMP request and response arguments is documented in +.Xr cam_cdbparse 3 . +.Pp +Note that SAS adapters that support SMP passthrough (at least the currently +known adapters) do not accept CRC bytes from the user in the request and do +not pass CRC bytes back to the user in the response. +Therefore users should not include the CRC bytes in the length of the +request and not expect CRC bytes to be returned in the response. +.Bl -tag -width 17n +.It Fl r Ar len Ar fmt Op args +This specifies the size of the SMP request, without the CRC bytes, and the +SMP request format. If the format is +.Sq - , +.Ar len +bytes of data will be read from standard input and written as the SMP +request. +.It Fl R Ar len Ar fmt Op args +This specifies the size of the buffer allocated for the SMP response, and +the SMP response format. +If the format is +.Sq - , +.Ar len +bytes of data will be allocated for the response and the response will be +written to standard output. +.El +.It Ic smprg +Allows the user to send the Serial Management Protocol (SMP) Report General +command to a device. +.Nm +will display the data returned by the Report General command. +If the SMP target supports the long response format, the additional data +will be requested and displayed automatically. +.Bl -tag -width 8n +.It Fl l +Request the long response format only. +Not all SMP targets support the long response format. +This option causes +.Nm +to skip sending the initial report general request without the long bit set +and only issue a report general request with the long bit set. +.El +.It Ic smppc +Allows the user to issue the Serial Management Protocol (SMP) PHY Control +command to a device. +This function should be used with some caution, as it can render devices +inaccessible, and could potentially cause data corruption as well. +The +.Fl p +argument is required to specify the PHY to operate on. +.Bl -tag -width 17n +.It Fl p Ar phy +Specify the PHY to operate on. +This argument is required. +.It Fl l +Request the long request/response format. +Not all SMP targets support the long response format. +For the PHY Control command, this currently only affects whether the +request length is set to a value other than 0. +.It Fl o Ar operation +Specify a PHY control operation. +Only one +.Fl o +operation may be specified. +The operation may be specified numerically (in decimal, hexadecimal, or octal) +or one of the following operation names may be specified: +.Bl -tag -width 16n +.It nop +No operation. +It is not necessary to specify this argument. +.It linkreset +Send the LINK RESET command to the phy. +.It hardreset +Send the HARD RESET command to the phy. +.It disable +Send the DISABLE command to the phy. +Note that the LINK RESET or HARD RESET commands should re-enable the phy. +.It clearerrorlog +Send the CLEAR ERROR LOG command. +This clears the error log counters for the specified phy. +.It clearaffiliation +Send the CLEAR AFFILIATION command. +This clears the affiliation from the STP initiator port with the same SAS +address as the SMP initiator that requests the clear operation. +.It sataportsel +Send the TRANSMIT SATA PORT SELECTION SIGNAL command to the phy. +This will cause a SATA port selector to use the given phy as its active phy +and make the other phy inactive. +.It clearitnl +Send the CLEAR STP I_T NEXUS LOSS command to the PHY. +.It setdevname +Send the SET ATTACHED DEVICE NAME command to the PHY. +This requires the +.Fl d +argument to specify the device name. +.El +.It Fl d Ar name +Specify the attached device name. +This option is needed with the +.Fl o Ar setdevname +phy operation. +The name is a 64-bit number, and can be specified in decimal, hexadecimal +or octal format. +.It Fl m Ar rate +Set the minimum physical link rate for the phy. +This is a numeric argument. +Currently known link rates are: +.Bl -tag -width 5n +.It 0x0 +Do not change current value. +.It 0x8 +1.5 Gbps +.It 0x9 +3 Gbps +.It 0xa +6 Gbps +.El +.Pp +Other values may be specified for newer physical link rates. +.It Fl M Ar rate +Set the maximum physical link rate for the phy. +This is a numeric argument. +See the +.Fl m +argument description for known link rate arguments. +.It Fl T Ar pp_timeout +Set the partial pathway timeout value, in microseconds. +See the +.Tn ANSI +.Tn SAS +Protcol Layer (SPL) +specification for more information on this field. +.It Fl a Ar enable|disable +Enable or disable SATA slumber phy power conditions. +.It Fl A Ar enable|disable +Enable or disable SATA partial power conditions. +.It Fl s Ar enable|disable +Enable or disable SAS slumber phy power conditions. +.It Fl S Ar enable|disable +Enable or disable SAS partial phy power conditions. +.El +.It Ic smpphylist +List phys attached to a SAS expander, the address of the end device +attached to the phy, and the inquiry data for that device and peripheral +devices attached to that device. +The inquiry data and peripheral devices are displayed if available. +.Bl -tag -width 5n +.It Fl l +Turn on the long response format for the underlying SMP commands used for +this command. +.It Fl q +Only print out phys that are attached to a device in the CAM EDT (Existing +Device Table). +.El +.It Ic smpmaninfo +Send the SMP Report Manufacturer Information command to the device and +display the response. +.Bl -tag -width 5n +.It Fl l +Turn on the long response format for the underlying SMP commands used for +this command. +.El .It Ic debug Turn on CAM debugging printfs in the kernel. This requires options CAMDEBUG @@ -965,6 +1173,14 @@ camcontrol negotiate -n da -u 3 -R 20.000 -O 15 -a Negotiate a sync rate of 20MHz and an offset of 15 with da3. Then send a Test Unit Ready command to make the settings take effect. +.Pp +.Bd -literal -offset indent +camcontrol smpcmd ses0 -v -r 4 "40 0 00 0" -R 1020 "s9 i1" +.Ed +.Pp +Send the SMP REPORT GENERAL command to ses0, and display the number of PHYs +it contains. +Display SMP errors if the command fails. .Sh SEE ALSO .Xr cam 3 , .Xr cam_cdbparse 3 , diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index 2379a8e..88ae1ac 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -33,11 +33,14 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -50,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include "camcontrol.h" @@ -77,7 +81,12 @@ typedef enum { CAM_CMD_IDENTIFY = 0x00000013, CAM_CMD_IDLE = 0x00000014, CAM_CMD_STANDBY = 0x00000015, - CAM_CMD_SLEEP = 0x00000016 + CAM_CMD_SLEEP = 0x00000016, + CAM_CMD_SMP_CMD = 0x00000017, + CAM_CMD_SMP_RG = 0x00000018, + CAM_CMD_SMP_PC = 0x00000019, + CAM_CMD_SMP_PHYLIST = 0x0000001a, + CAM_CMD_SMP_MANINFO = 0x0000001b } cam_cmdmask; typedef enum { @@ -117,7 +126,7 @@ typedef enum { struct camcontrol_opts { const char *optname; - cam_cmdmask cmdnum; + uint32_t cmdnum; cam_argmask argnum; const char *subopt; }; @@ -126,6 +135,9 @@ struct camcontrol_opts { static const char scsicmd_opts[] = "a:c:dfi:o:r"; static const char readdefect_opts[] = "f:GP"; static const char negotiate_opts[] = "acD:M:O:qR:T:UW:"; +static const char smprg_opts[] = "l"; +static const char smppc_opts[] = "a:A:d:lm:M:o:p:s:S:T:"; +static const char smpphylist_opts[] = "lq"; #endif struct camcontrol_opts option_table[] = { @@ -145,6 +157,14 @@ struct camcontrol_opts option_table[] = { #ifndef MINIMALISTIC {"cmd", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts}, {"command", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts}, + {"smpcmd", CAM_CMD_SMP_CMD, CAM_ARG_NONE, "r:R:"}, + {"smprg", CAM_CMD_SMP_RG, CAM_ARG_NONE, smprg_opts}, + {"smpreportgeneral", CAM_CMD_SMP_RG, CAM_ARG_NONE, smprg_opts}, + {"smppc", CAM_CMD_SMP_PC, CAM_ARG_NONE, smppc_opts}, + {"smpphycontrol", CAM_CMD_SMP_PC, CAM_ARG_NONE, smppc_opts}, + {"smpplist", CAM_CMD_SMP_PHYLIST, CAM_ARG_NONE, smpphylist_opts}, + {"smpphylist", CAM_CMD_SMP_PHYLIST, CAM_ARG_NONE, smpphylist_opts}, + {"smpmaninfo", CAM_CMD_SMP_MANINFO, CAM_ARG_NONE, "l"}, {"defects", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts}, {"defectlist", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts}, #endif /* MINIMALISTIC */ @@ -173,11 +193,25 @@ typedef enum { CC_OR_FOUND } camcontrol_optret; +struct cam_devitem { + struct device_match_result dev_match; + int num_periphs; + struct periph_match_result *periph_matches; + struct scsi_vpd_device_id *device_id; + int device_id_len; + STAILQ_ENTRY(cam_devitem) links; +}; + +struct cam_devlist { + STAILQ_HEAD(, cam_devitem) dev_queue; + path_id_t path_id; +}; + cam_cmdmask cmdlist; cam_argmask arglist; - -camcontrol_optret getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum, +camcontrol_optret getoption(struct camcontrol_opts *table, char *arg, + uint32_t *cmdnum, cam_argmask *argnum, const char **subopt); #ifndef MINIMALISTIC static int getdevlist(struct cam_device *device); @@ -206,6 +240,21 @@ static void modepage(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); static int scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); +static int smpcmd(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); +static int smpreportgeneral(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); +static int smpphycontrol(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); +static int smpmaninfo(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); +static int getdevid(struct cam_devitem *item); +static int buildbusdevlist(struct cam_devlist *devlist); +static void freebusdevlist(struct cam_devlist *devlist); +static struct cam_devitem *findsasdevice(struct cam_devlist *devlist, + uint64_t sasaddr); +static int smpphylist(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); static int tagcontrol(struct cam_device *device, int argc, char **argv, char *combinedopt); static void cts_print(struct cam_device *device, @@ -234,13 +283,13 @@ static int atapm(struct cam_device *device, int argc, char **argv, #endif camcontrol_optret -getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum, - const char **subopt) +getoption(struct camcontrol_opts *table, char *arg, uint32_t *cmdnum, + cam_argmask *argnum, const char **subopt) { struct camcontrol_opts *opts; int num_matches = 0; - for (opts = option_table; (opts != NULL) && (opts->optname != NULL); + for (opts = table; (opts != NULL) && (opts->optname != NULL); opts++) { if (strncmp(opts->optname, arg, strlen(arg)) == 0) { *cmdnum = opts->cmdnum; @@ -2455,10 +2504,12 @@ scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt, if (((retval = cam_send_ccb(device, ccb)) < 0) || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + if (retval < 0) - warn("error sending command"); + warn(warnstr); else - warnx("error sending command"); + warnx(warnstr); if (arglist & CAM_ARG_VERBOSE) { cam_error_print(device, ccb, CAM_ESF_ALL, @@ -4274,125 +4325,1336 @@ bailout: } static int -atapm(struct cam_device *device, int argc, char **argv, - char *combinedopt, int retry_count, int timeout) +smpcmd(struct cam_device *device, int argc, char **argv, char *combinedopt, + int retry_count, int timeout) { + int c, error; union ccb *ccb; - int retval = 0; - int t = -1; - int c; - u_char cmd, sc; + uint8_t *smp_request = NULL, *smp_response = NULL; + int request_size = 0, response_size = 0; + int fd_request = 0, fd_response = 0; + char *datastr = NULL; + struct get_hook hook; + int retval; + int flags = 0; + /* + * Note that at the moment we don't support sending SMP CCBs to + * devices that aren't probed by CAM. + */ ccb = cam_getccb(device); - if (ccb == NULL) { - warnx("%s: error allocating ccb", __func__); + warnx("%s: error allocating CCB", __func__); return (1); } + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { - case 't': - t = atoi(optarg); + case 'R': + arglist |= CAM_ARG_CMD_IN; + response_size = strtol(optarg, NULL, 0); + if (response_size <= 0) { + warnx("invalid number of response bytes %d", + response_size); + error = 1; + goto smpcmd_bailout; + } + hook.argc = argc - optind; + hook.argv = argv + optind; + hook.got = 0; + optind++; + datastr = cget(&hook, NULL); + /* + * If the user supplied "-" instead of a format, he + * wants the data to be written to stdout. + */ + if ((datastr != NULL) + && (datastr[0] == '-')) + fd_response = 1; + + smp_response = (u_int8_t *)malloc(response_size); + if (smp_response == NULL) { + warn("can't malloc memory for SMP response"); + error = 1; + goto smpcmd_bailout; + } + break; + case 'r': + arglist |= CAM_ARG_CMD_OUT; + request_size = strtol(optarg, NULL, 0); + if (request_size <= 0) { + warnx("invalid number of request bytes %d", + request_size); + error = 1; + goto smpcmd_bailout; + } + hook.argc = argc - optind; + hook.argv = argv + optind; + hook.got = 0; + datastr = cget(&hook, NULL); + smp_request = (u_int8_t *)malloc(request_size); + if (smp_request == NULL) { + warn("can't malloc memory for SMP request"); + error = 1; + goto smpcmd_bailout; + } + bzero(smp_request, request_size); + /* + * If the user supplied "-" instead of a format, he + * wants the data to be read from stdin. + */ + if ((datastr != NULL) + && (datastr[0] == '-')) + fd_request = 1; + else + buff_encode_visit(smp_request, request_size, + datastr, + iget, &hook); + optind += hook.got; break; default: break; } } - if (strcmp(argv[1], "idle") == 0) { - if (t == -1) - cmd = ATA_IDLE_IMMEDIATE; - else - cmd = ATA_IDLE_CMD; - } else if (strcmp(argv[1], "standby") == 0) { - if (t == -1) - cmd = ATA_STANDBY_IMMEDIATE; - else - cmd = ATA_STANDBY_CMD; - } else { - cmd = ATA_SLEEP; - t = -1; + + /* + * If fd_data is set, and we're writing to the device, we need to + * read the data the user wants written from stdin. + */ + if ((fd_request == 1) && (arglist & CAM_ARG_CMD_OUT)) { + ssize_t amt_read; + int amt_to_read = request_size; + u_int8_t *buf_ptr = smp_request; + + for (amt_read = 0; amt_to_read > 0; + amt_read = read(STDIN_FILENO, buf_ptr, amt_to_read)) { + if (amt_read == -1) { + warn("error reading data from stdin"); + error = 1; + goto smpcmd_bailout; + } + amt_to_read -= amt_read; + buf_ptr += amt_read; + } } - if (t < 0) - sc = 0; - else if (t <= (240 * 5)) - sc = (t + 4) / 5; - else if (t <= (252 * 5)) - /* special encoding for 21 minutes */ - sc = 252; - else if (t <= (11 * 30 * 60)) - sc = (t - 1) / (30 * 60) + 241; - else - sc = 253; + if (((arglist & CAM_ARG_CMD_IN) == 0) + || ((arglist & CAM_ARG_CMD_OUT) == 0)) { + warnx("%s: need both the request (-r) and response (-R) " + "arguments", __func__); + error = 1; + goto smpcmd_bailout; + } - cam_fill_ataio(&ccb->ataio, - retry_count, - NULL, - /*flags*/CAM_DIR_NONE, - MSG_SIMPLE_Q_TAG, - /*data_ptr*/NULL, - /*dxfer_len*/0, - timeout ? timeout : 30 * 1000); - ata_28bit_cmd(&ccb->ataio, cmd, 0, 0, sc); + flags |= CAM_DEV_QFRZDIS; - /* Disable freezing the device queue */ - ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + cam_fill_smpio(&ccb->smpio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*flags*/ flags, + /*smp_request*/ smp_request, + /*smp_request_len*/ request_size, + /*smp_response*/ smp_response, + /*smp_response_len*/ response_size, + /*timeout*/ timeout ? timeout : 5000); - if (arglist & CAM_ARG_ERR_RECOVER) - ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + ccb->smpio.flags = SMP_FLAG_NONE; - if (cam_send_ccb(device, ccb) < 0) { - warn("error sending command"); + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; - if (arglist & CAM_ARG_VERBOSE) + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); + } + } - retval = 1; + if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) + && (response_size > 0)) { + if (fd_response == 0) { + buff_decode_visit(smp_response, response_size, + datastr, arg_put, NULL); + fprintf(stdout, "\n"); + } else { + ssize_t amt_written; + int amt_to_write = response_size; + u_int8_t *buf_ptr = smp_response; + + for (amt_written = 0; (amt_to_write > 0) && + (amt_written = write(STDOUT_FILENO, buf_ptr, + amt_to_write)) > 0;){ + amt_to_write -= amt_written; + buf_ptr += amt_written; + } + if (amt_written == -1) { + warn("error writing data to stdout"); + error = 1; + goto smpcmd_bailout; + } else if ((amt_written == 0) + && (amt_to_write > 0)) { + warnx("only wrote %u bytes out of %u", + response_size - amt_to_write, + response_size); + } + } + } +smpcmd_bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + if (smp_request != NULL) + free(smp_request); + + if (smp_response != NULL) + free(smp_response); + + return (error); +} + +static int +smpreportgeneral(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + union ccb *ccb; + struct smp_report_general_request *request = NULL; + struct smp_report_general_response *response = NULL; + struct sbuf *sb = NULL; + int error = 0; + int c, long_response = 0; + int retval; + + /* + * Note that at the moment we don't support sending SMP CCBs to + * devices that aren't probed by CAM. + */ + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'l': + long_response = 1; + break; + default: + break; + } + } + request = malloc(sizeof(*request)); + if (request == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*request)); + error = 1; goto bailout; } - if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { - cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); - retval = 1; + response = malloc(sizeof(*response)); + if (response == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*response)); + error = 1; + goto bailout; + } + +try_long: + smp_report_general(&ccb->smpio, + retry_count, + /*cbfcnp*/ NULL, + request, + /*request_len*/ sizeof(*request), + (uint8_t *)response, + /*response_len*/ sizeof(*response), + /*long_response*/ long_response, + timeout); + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + error = 1; + goto bailout; + } + + /* + * If the device supports the long response bit, try again and see + * if we can get all of the data. + */ + if ((response->long_response & SMP_RG_LONG_RESPONSE) + && (long_response == 0)) { + ccb->ccb_h.status = CAM_REQ_INPROG; + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + long_response = 1; + goto try_long; + } + + /* + * XXX KDM detect and decode SMP errors here. + */ + sb = sbuf_new_auto(); + if (sb == NULL) { + warnx("%s: error allocating sbuf", __func__); goto bailout; } + + smp_report_general_sbuf(response, sizeof(*response), sb); + + sbuf_finish(sb); + + printf("%s", sbuf_data(sb)); + bailout: - cam_freeccb(ccb); - return (retval); + if (ccb != NULL) + cam_freeccb(ccb); + + if (request != NULL) + free(request); + + if (response != NULL) + free(response); + + if (sb != NULL) + sbuf_delete(sb); + + return (error); } -#endif /* MINIMALISTIC */ +struct camcontrol_opts phy_ops[] = { + {"nop", SMP_PC_PHY_OP_NOP, CAM_ARG_NONE, NULL}, + {"linkreset", SMP_PC_PHY_OP_LINK_RESET, CAM_ARG_NONE, NULL}, + {"hardreset", SMP_PC_PHY_OP_HARD_RESET, CAM_ARG_NONE, NULL}, + {"disable", SMP_PC_PHY_OP_DISABLE, CAM_ARG_NONE, NULL}, + {"clearerrlog", SMP_PC_PHY_OP_CLEAR_ERR_LOG, CAM_ARG_NONE, NULL}, + {"clearaffiliation", SMP_PC_PHY_OP_CLEAR_AFFILIATON, CAM_ARG_NONE,NULL}, + {"sataportsel", SMP_PC_PHY_OP_TRANS_SATA_PSS, CAM_ARG_NONE, NULL}, + {"clearitnl", SMP_PC_PHY_OP_CLEAR_STP_ITN_LS, CAM_ARG_NONE, NULL}, + {"setdevname", SMP_PC_PHY_OP_SET_ATT_DEV_NAME, CAM_ARG_NONE, NULL}, + {NULL, 0, 0, NULL} +}; -void -usage(int verbose) +static int +smpphycontrol(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) { - fprintf(verbose ? stdout : stderr, -"usage: camcontrol [device id][generic args][command args]\n" -" camcontrol devlist [-v]\n" -#ifndef MINIMALISTIC -" camcontrol periphlist [dev_id][-n dev_name] [-u unit]\n" -" camcontrol tur [dev_id][generic args]\n" -" camcontrol inquiry [dev_id][generic args] [-D] [-S] [-R]\n" -" camcontrol identify [dev_id][generic args] [-v]\n" -" camcontrol reportluns [dev_id][generic args] [-c] [-l] [-r report]\n" -" camcontrol readcap [dev_id][generic args] [-b] [-h] [-H] [-N]\n" -" [-q] [-s]\n" -" camcontrol start [dev_id][generic args]\n" -" camcontrol stop [dev_id][generic args]\n" -" camcontrol load [dev_id][generic args]\n" -" camcontrol eject [dev_id][generic args]\n" -#endif /* MINIMALISTIC */ -" camcontrol rescan \n" -" camcontrol reset \n" -#ifndef MINIMALISTIC -" camcontrol defects [dev_id][generic args] <-f format> [-P][-G]\n" -" camcontrol modepage [dev_id][generic args] <-m page | -l>\n" -" [-P pagectl][-e | -b][-d]\n" -" camcontrol cmd [dev_id][generic args]\n" + union ccb *ccb; + struct smp_phy_control_request *request = NULL; + struct smp_phy_control_response *response = NULL; + int long_response = 0; + int retval = 0; + int phy = -1; + uint32_t phy_operation = SMP_PC_PHY_OP_NOP; + int phy_op_set = 0; + uint64_t attached_dev_name = 0; + int dev_name_set = 0; + uint32_t min_plr = 0, max_plr = 0; + uint32_t pp_timeout_val = 0; + int slumber_partial = 0; + int set_pp_timeout_val = 0; + int c; + + /* + * Note that at the moment we don't support sending SMP CCBs to + * devices that aren't probed by CAM. + */ + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'a': + case 'A': + case 's': + case 'S': { + int enable = -1; + + if (strcasecmp(optarg, "enable") == 0) + enable = 1; + else if (strcasecmp(optarg, "disable") == 0) + enable = 2; + else { + warnx("%s: Invalid argument %s", __func__, + optarg); + retval = 1; + goto bailout; + } + switch (c) { + case 's': + slumber_partial |= enable << + SMP_PC_SAS_SLUMBER_SHIFT; + break; + case 'S': + slumber_partial |= enable << + SMP_PC_SAS_PARTIAL_SHIFT; + break; + case 'a': + slumber_partial |= enable << + SMP_PC_SATA_SLUMBER_SHIFT; + break; + case 'A': + slumber_partial |= enable << + SMP_PC_SATA_PARTIAL_SHIFT; + break; + default: + warnx("%s: programmer error", __func__); + retval = 1; + goto bailout; + break; /*NOTREACHED*/ + } + break; + } + case 'd': + attached_dev_name = (uintmax_t)strtoumax(optarg, + NULL,0); + dev_name_set = 1; + break; + case 'l': + long_response = 1; + break; + case 'm': + /* + * We don't do extensive checking here, so this + * will continue to work when new speeds come out. + */ + min_plr = strtoul(optarg, NULL, 0); + if ((min_plr == 0) + || (min_plr > 0xf)) { + warnx("%s: invalid link rate %x", + __func__, min_plr); + retval = 1; + goto bailout; + } + break; + case 'M': + /* + * We don't do extensive checking here, so this + * will continue to work when new speeds come out. + */ + max_plr = strtoul(optarg, NULL, 0); + if ((max_plr == 0) + || (max_plr > 0xf)) { + warnx("%s: invalid link rate %x", + __func__, max_plr); + retval = 1; + goto bailout; + } + break; + case 'o': { + camcontrol_optret optreturn; + cam_argmask argnums; + const char *subopt; + + if (phy_op_set != 0) { + warnx("%s: only one phy operation argument " + "(-o) allowed", __func__); + retval = 1; + goto bailout; + } + + phy_op_set = 1; + + /* + * Allow the user to specify the phy operation + * numerically, as well as with a name. This will + * future-proof it a bit, so options that are added + * in future specs can be used. + */ + if (isdigit(optarg[0])) { + phy_operation = strtoul(optarg, NULL, 0); + if ((phy_operation == 0) + || (phy_operation > 0xff)) { + warnx("%s: invalid phy operation %#x", + __func__, phy_operation); + retval = 1; + goto bailout; + } + break; + } + optreturn = getoption(phy_ops, optarg, &phy_operation, + &argnums, &subopt); + + if (optreturn == CC_OR_AMBIGUOUS) { + warnx("%s: ambiguous option %s", __func__, + optarg); + usage(0); + retval = 1; + goto bailout; + } else if (optreturn == CC_OR_NOT_FOUND) { + warnx("%s: option %s not found", __func__, + optarg); + usage(0); + retval = 1; + goto bailout; + } + break; + } + case 'p': + phy = atoi(optarg); + break; + case 'T': + pp_timeout_val = strtoul(optarg, NULL, 0); + if (pp_timeout_val > 15) { + warnx("%s: invalid partial pathway timeout " + "value %u, need a value less than 16", + __func__, pp_timeout_val); + retval = 1; + goto bailout; + } + set_pp_timeout_val = 1; + break; + default: + break; + } + } + + if (phy == -1) { + warnx("%s: a PHY (-p phy) argument is required",__func__); + retval = 1; + goto bailout; + } + + if (((dev_name_set != 0) + && (phy_operation != SMP_PC_PHY_OP_SET_ATT_DEV_NAME)) + || ((phy_operation == SMP_PC_PHY_OP_SET_ATT_DEV_NAME) + && (dev_name_set == 0))) { + warnx("%s: -d name and -o setdevname arguments both " + "required to set device name", __func__); + retval = 1; + goto bailout; + } + + request = malloc(sizeof(*request)); + if (request == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*request)); + retval = 1; + goto bailout; + } + + response = malloc(sizeof(*response)); + if (response == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*request)); + retval = 1; + goto bailout; + } + + smp_phy_control(&ccb->smpio, + retry_count, + /*cbfcnp*/ NULL, + request, + sizeof(*request), + (uint8_t *)response, + sizeof(*response), + long_response, + /*expected_exp_change_count*/ 0, + phy, + phy_operation, + (set_pp_timeout_val != 0) ? 1 : 0, + attached_dev_name, + min_plr, + max_plr, + slumber_partial, + pp_timeout_val, + timeout); + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + /* + * Use CAM_EPF_NORMAL so we only get one line of + * SMP command decoding. + */ + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_NORMAL, stderr); + } + retval = 1; + goto bailout; + } + + /* XXX KDM print out something here for success? */ +bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + if (request != NULL) + free(request); + + if (response != NULL) + free(response); + + return (retval); +} + +static int +smpmaninfo(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + union ccb *ccb; + struct smp_report_manuf_info_request request; + struct smp_report_manuf_info_response response; + struct sbuf *sb = NULL; + int long_response = 0; + int retval = 0; + int c; + + /* + * Note that at the moment we don't support sending SMP CCBs to + * devices that aren't probed by CAM. + */ + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'l': + long_response = 1; + break; + default: + break; + } + } + bzero(&request, sizeof(request)); + bzero(&response, sizeof(response)); + + smp_report_manuf_info(&ccb->smpio, + retry_count, + /*cbfcnp*/ NULL, + &request, + sizeof(request), + (uint8_t *)&response, + sizeof(response), + long_response, + timeout); + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + retval = 1; + goto bailout; + } + + sb = sbuf_new_auto(); + if (sb == NULL) { + warnx("%s: error allocating sbuf", __func__); + goto bailout; + } + + smp_report_manuf_info_sbuf(&response, sizeof(response), sb); + + sbuf_finish(sb); + + printf("%s", sbuf_data(sb)); + +bailout: + + if (ccb != NULL) + cam_freeccb(ccb); + + if (sb != NULL) + sbuf_delete(sb); + + return (retval); +} + +static int +getdevid(struct cam_devitem *item) +{ + int retval = 0; + union ccb *ccb = NULL; + + struct cam_device *dev; + + dev = cam_open_btl(item->dev_match.path_id, + item->dev_match.target_id, + item->dev_match.target_lun, O_RDWR, NULL); + + if (dev == NULL) { + warnx("%s", cam_errbuf); + retval = 1; + goto bailout; + } + + item->device_id_len = CAM_SCSI_DEVID_MAXLEN; + item->device_id = malloc(item->device_id_len); + if (item->device_id == NULL) { + warn("%s: unable to allocate %d bytes", __func__, + item->device_id_len); + retval = 1; + goto bailout; + } + + ccb = cam_getccb(dev); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + retval = 1; + goto bailout; + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + ccb->ccb_h.func_code = XPT_GDEV_ADVINFO; + ccb->ccb_h.flags = CAM_DIR_IN; + ccb->cgdai.flags = CGDAI_FLAG_PROTO; + ccb->cgdai.buftype = CGDAI_TYPE_SCSI_DEVID; + ccb->cgdai.bufsiz = item->device_id_len; + ccb->cgdai.buf = (uint8_t *)item->device_id; + + if (cam_send_ccb(dev, ccb) < 0) { + warn("%s: error sending XPT_GDEV_ADVINFO CCB", __func__); + retval = 1; + goto bailout; + } + + if (ccb->ccb_h.status != CAM_REQ_CMP) { + warnx("%s: CAM status %#x", __func__, ccb->ccb_h.status); + retval = 1; + goto bailout; + } + +bailout: + if (dev != NULL) + cam_close_device(dev); + + if (ccb != NULL) + cam_freeccb(ccb); + + return (retval); +} + +/* + * XXX KDM merge this code with getdevtree()? + */ +static int +buildbusdevlist(struct cam_devlist *devlist) +{ + union ccb ccb; + int bufsize, fd = -1; + struct dev_match_pattern *patterns; + struct cam_devitem *item = NULL; + int skip_device = 0; + int retval = 0; + + if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { + warn("couldn't open %s", XPT_DEVICE); + return(1); + } + + bzero(&ccb, sizeof(union ccb)); + + ccb.ccb_h.path_id = CAM_XPT_PATH_ID; + ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; + ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + + ccb.ccb_h.func_code = XPT_DEV_MATCH; + bufsize = sizeof(struct dev_match_result) * 100; + ccb.cdm.match_buf_len = bufsize; + ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); + if (ccb.cdm.matches == NULL) { + warnx("can't malloc memory for matches"); + close(fd); + return(1); + } + ccb.cdm.num_matches = 0; + ccb.cdm.num_patterns = 2; + ccb.cdm.pattern_buf_len = sizeof(struct dev_match_pattern) * + ccb.cdm.num_patterns; + + patterns = (struct dev_match_pattern *)malloc(ccb.cdm.pattern_buf_len); + if (patterns == NULL) { + warnx("can't malloc memory for patterns"); + retval = 1; + goto bailout; + } + + ccb.cdm.patterns = patterns; + bzero(patterns, ccb.cdm.pattern_buf_len); + + patterns[0].type = DEV_MATCH_DEVICE; + patterns[0].pattern.device_pattern.flags = DEV_MATCH_PATH; + patterns[0].pattern.device_pattern.path_id = devlist->path_id; + patterns[1].type = DEV_MATCH_PERIPH; + patterns[1].pattern.periph_pattern.flags = PERIPH_MATCH_PATH; + patterns[1].pattern.periph_pattern.path_id = devlist->path_id; + + /* + * We do the ioctl multiple times if necessary, in case there are + * more than 100 nodes in the EDT. + */ + do { + unsigned int i; + + if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { + warn("error sending CAMIOCOMMAND ioctl"); + retval = 1; + goto bailout; + } + + if ((ccb.ccb_h.status != CAM_REQ_CMP) + || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) + && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { + warnx("got CAM error %#x, CDM error %d\n", + ccb.ccb_h.status, ccb.cdm.status); + retval = 1; + goto bailout; + } + + for (i = 0; i < ccb.cdm.num_matches; i++) { + switch (ccb.cdm.matches[i].type) { + case DEV_MATCH_DEVICE: { + struct device_match_result *dev_result; + + dev_result = + &ccb.cdm.matches[i].result.device_result; + + if ((dev_result->flags + & DEV_RESULT_UNCONFIGURED) + && ((arglist & CAM_ARG_VERBOSE) == 0)) { + skip_device = 1; + break; + } else + skip_device = 0; + + item = malloc(sizeof(*item)); + if (item == NULL) { + warn("%s: unable to allocate %zd bytes", + __func__, sizeof(*item)); + retval = 1; + goto bailout; + } + bzero(item, sizeof(*item)); + bcopy(dev_result, &item->dev_match, + sizeof(*dev_result)); + STAILQ_INSERT_TAIL(&devlist->dev_queue, item, + links); + + if (getdevid(item) != 0) { + retval = 1; + goto bailout; + } + break; + } + case DEV_MATCH_PERIPH: { + struct periph_match_result *periph_result; + + periph_result = + &ccb.cdm.matches[i].result.periph_result; + + if (skip_device != 0) + break; + item->num_periphs++; + item->periph_matches = realloc( + item->periph_matches, + item->num_periphs * + sizeof(struct periph_match_result)); + if (item->periph_matches == NULL) { + warn("%s: error allocating periph " + "list", __func__); + retval = 1; + goto bailout; + } + bcopy(periph_result, &item->periph_matches[ + item->num_periphs - 1], + sizeof(*periph_result)); + break; + } + default: + fprintf(stderr, "%s: unexpected match " + "type %d\n", __func__, + ccb.cdm.matches[i].type); + retval = 1; + goto bailout; + break; /*NOTREACHED*/ + } + } + } while ((ccb.ccb_h.status == CAM_REQ_CMP) + && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); +bailout: + + if (fd != -1) + close(fd); + + free(patterns); + + free(ccb.cdm.matches); + + if (retval != 0) + freebusdevlist(devlist); + + return (retval); +} + +static void +freebusdevlist(struct cam_devlist *devlist) +{ + struct cam_devitem *item, *item2; + + STAILQ_FOREACH_SAFE(item, &devlist->dev_queue, links, item2) { + STAILQ_REMOVE(&devlist->dev_queue, item, cam_devitem, + links); + free(item->device_id); + free(item->periph_matches); + free(item); + } +} + +static struct cam_devitem * +findsasdevice(struct cam_devlist *devlist, uint64_t sasaddr) +{ + struct cam_devitem *item; + + STAILQ_FOREACH(item, &devlist->dev_queue, links) { + uint8_t *item_addr; + + /* + * XXX KDM look for LUN IDs as well? + */ + item_addr = scsi_get_sas_addr(item->device_id, + item->device_id_len); + if (item_addr == NULL) + continue; + + if (scsi_8btou64(item_addr) == sasaddr) + return (item); + } + + return (NULL); +} + +static int +smpphylist(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + struct smp_report_general_request *rgrequest = NULL; + struct smp_report_general_response *rgresponse = NULL; + struct smp_discover_request *disrequest = NULL; + struct smp_discover_response *disresponse = NULL; + struct cam_devlist devlist; + union ccb *ccb; + int long_response = 0; + int num_phys = 0; + int quiet = 0; + int retval; + int i, c; + + /* + * Note that at the moment we don't support sending SMP CCBs to + * devices that aren't probed by CAM. + */ + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + rgrequest = malloc(sizeof(*rgrequest)); + if (rgrequest == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*rgrequest)); + retval = 1; + goto bailout; + } + + rgresponse = malloc(sizeof(*rgresponse)); + if (rgresponse == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*rgresponse)); + retval = 1; + goto bailout; + } + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'l': + long_response = 1; + break; + case 'q': + quiet = 1; + break; + default: + break; + } + } + + smp_report_general(&ccb->smpio, + retry_count, + /*cbfcnp*/ NULL, + rgrequest, + /*request_len*/ sizeof(*rgrequest), + (uint8_t *)rgresponse, + /*response_len*/ sizeof(*rgresponse), + /*long_response*/ long_response, + timeout); + + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + retval = 1; + goto bailout; + } + + num_phys = rgresponse->num_phys; + + if (num_phys == 0) { + if (quiet == 0) + fprintf(stdout, "%s: No Phys reported\n", __func__); + retval = 1; + goto bailout; + } + + STAILQ_INIT(&devlist.dev_queue); + devlist.path_id = device->path_id; + + retval = buildbusdevlist(&devlist); + if (retval != 0) + goto bailout; + + if (quiet == 0) { + fprintf(stdout, "%d PHYs:\n", num_phys); + fprintf(stdout, "PHY Attached SAS Address\n"); + } + + disrequest = malloc(sizeof(*disrequest)); + if (disrequest == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*disrequest)); + retval = 1; + goto bailout; + } + + disresponse = malloc(sizeof(*disresponse)); + if (disresponse == NULL) { + warn("%s: unable to allocate %zd bytes", __func__, + sizeof(*disresponse)); + retval = 1; + goto bailout; + } + + for (i = 0; i < num_phys; i++) { + struct cam_devitem *item; + struct device_match_result *dev_match; + char vendor[16], product[48], revision[16]; + char tmpstr[256]; + int j; + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + ccb->ccb_h.status = CAM_REQ_INPROG; + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + smp_discover(&ccb->smpio, + retry_count, + /*cbfcnp*/ NULL, + disrequest, + sizeof(*disrequest), + (uint8_t *)disresponse, + sizeof(*disresponse), + long_response, + /*ignore_zone_group*/ 0, + /*phy*/ i, + timeout); + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) + && (disresponse->function_result != SMP_FR_PHY_VACANT))) { + const char *warnstr = "error sending command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + + if (arglist & CAM_ARG_VERBOSE) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + } + retval = 1; + goto bailout; + } + + if (disresponse->function_result == SMP_FR_PHY_VACANT) { + if (quiet == 0) + fprintf(stdout, "%3d \n", i); + continue; + } + + item = findsasdevice(&devlist, + scsi_8btou64(disresponse->attached_sas_address)); + + if ((quiet == 0) + || (item != NULL)) { + fprintf(stdout, "%3d 0x%016jx", i, + (uintmax_t)scsi_8btou64( + disresponse->attached_sas_address)); + if (item == NULL) { + fprintf(stdout, "\n"); + continue; + } + } else if (quiet != 0) + continue; + + dev_match = &item->dev_match; + + if (dev_match->protocol == PROTO_SCSI) { + cam_strvis(vendor, dev_match->inq_data.vendor, + sizeof(dev_match->inq_data.vendor), + sizeof(vendor)); + cam_strvis(product, dev_match->inq_data.product, + sizeof(dev_match->inq_data.product), + sizeof(product)); + cam_strvis(revision, dev_match->inq_data.revision, + sizeof(dev_match->inq_data.revision), + sizeof(revision)); + sprintf(tmpstr, "<%s %s %s>", vendor, product, + revision); + } else if ((dev_match->protocol == PROTO_ATA) + || (dev_match->protocol == PROTO_SATAPM)) { + cam_strvis(product, dev_match->ident_data.model, + sizeof(dev_match->ident_data.model), + sizeof(product)); + cam_strvis(revision, dev_match->ident_data.revision, + sizeof(dev_match->ident_data.revision), + sizeof(revision)); + sprintf(tmpstr, "<%s %s>", product, revision); + } else { + sprintf(tmpstr, "<>"); + } + fprintf(stdout, " %-33s ", tmpstr); + + /* + * If we have 0 periphs, that's a bug... + */ + if (item->num_periphs == 0) { + fprintf(stdout, "\n"); + continue; + } + + fprintf(stdout, "("); + for (j = 0; j < item->num_periphs; j++) { + if (j > 0) + fprintf(stdout, ","); + + fprintf(stdout, "%s%d", + item->periph_matches[j].periph_name, + item->periph_matches[j].unit_number); + + } + fprintf(stdout, ")\n"); + } +bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + free(rgrequest); + + free(rgresponse); + + free(disrequest); + + free(disresponse); + + freebusdevlist(&devlist); + + return (retval); +} + +static int +atapm(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + union ccb *ccb; + int retval = 0; + int t = -1; + int c; + u_char cmd, sc; + + ccb = cam_getccb(device); + + if (ccb == NULL) { + warnx("%s: error allocating ccb", __func__); + return (1); + } + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 't': + t = atoi(optarg); + break; + default: + break; + } + } + if (strcmp(argv[1], "idle") == 0) { + if (t == -1) + cmd = ATA_IDLE_IMMEDIATE; + else + cmd = ATA_IDLE_CMD; + } else if (strcmp(argv[1], "standby") == 0) { + if (t == -1) + cmd = ATA_STANDBY_IMMEDIATE; + else + cmd = ATA_STANDBY_CMD; + } else { + cmd = ATA_SLEEP; + t = -1; + } + + if (t < 0) + sc = 0; + else if (t <= (240 * 5)) + sc = (t + 4) / 5; + else if (t <= (252 * 5)) + /* special encoding for 21 minutes */ + sc = 252; + else if (t <= (11 * 30 * 60)) + sc = (t - 1) / (30 * 60) + 241; + else + sc = 253; + + cam_fill_ataio(&ccb->ataio, + retry_count, + NULL, + /*flags*/CAM_DIR_NONE, + MSG_SIMPLE_Q_TAG, + /*data_ptr*/NULL, + /*dxfer_len*/0, + timeout ? timeout : 30 * 1000); + ata_28bit_cmd(&ccb->ataio, cmd, 0, 0, sc); + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (arglist & CAM_ARG_ERR_RECOVER) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + + if (cam_send_ccb(device, ccb) < 0) { + warn("error sending command"); + + if (arglist & CAM_ARG_VERBOSE) + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + + retval = 1; + goto bailout; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); + retval = 1; + goto bailout; + } +bailout: + cam_freeccb(ccb); + return (retval); +} + +#endif /* MINIMALISTIC */ + +void +usage(int verbose) +{ + fprintf(verbose ? stdout : stderr, +"usage: camcontrol [device id][generic args][command args]\n" +" camcontrol devlist [-v]\n" +#ifndef MINIMALISTIC +" camcontrol periphlist [dev_id][-n dev_name] [-u unit]\n" +" camcontrol tur [dev_id][generic args]\n" +" camcontrol inquiry [dev_id][generic args] [-D] [-S] [-R]\n" +" camcontrol identify [dev_id][generic args] [-v]\n" +" camcontrol reportluns [dev_id][generic args] [-c] [-l] [-r report]\n" +" camcontrol readcap [dev_id][generic args] [-b] [-h] [-H] [-N]\n" +" [-q] [-s]\n" +" camcontrol start [dev_id][generic args]\n" +" camcontrol stop [dev_id][generic args]\n" +" camcontrol load [dev_id][generic args]\n" +" camcontrol eject [dev_id][generic args]\n" +#endif /* MINIMALISTIC */ +" camcontrol rescan \n" +" camcontrol reset \n" +#ifndef MINIMALISTIC +" camcontrol defects [dev_id][generic args] <-f format> [-P][-G]\n" +" camcontrol modepage [dev_id][generic args] <-m page | -l>\n" +" [-P pagectl][-e | -b][-d]\n" +" camcontrol cmd [dev_id][generic args]\n" " <-a cmd [args] | -c cmd [args]>\n" " [-d] [-f] [-i len fmt|-o len fmt [args]] [-r fmt]\n" +" camcontrol smpcmd [dev_id][generic args]\n" +" <-r len fmt [args]> <-R len fmt [args]>\n" +" camcontrol smprg [dev_id][generic args][-l]\n" +" camcontrol smppc [dev_id][generic args] <-p phy> [-l]\n" +" [-o operation][-d name][-m rate][-M rate]\n" +" [-T pp_timeout][-a enable|disable]\n" +" [-A enable|disable][-s enable|disable]\n" +" [-S enable|disable]\n" +" camcontrol smpphylist [dev_id][generic args][-l][-q]\n" +" camcontrol smpmaninfo [dev_id][generic args][-l]\n" " camcontrol debug [-I][-P][-T][-S][-X][-c]\n" " \n" " camcontrol tags [dev_id][generic args] [-N tags] [-q] [-v]\n" @@ -4426,7 +5688,12 @@ usage(int verbose) "reset reset all busses, the given bus, or bus:target:lun\n" "defects read the defect list of the specified device\n" "modepage display or edit (-e) the given mode page\n" -"cmd send the given scsi command, may need -i or -o as well\n" +"cmd send the given SCSI command, may need -i or -o as well\n" +"smpcmd send the given SMP command, requires -o and -i\n" +"smprg send the SMP Report General command\n" +"smppc send the SMP PHY Control command, requires -p\n" +"smpphylist display phys attached to a SAS expander\n" +"smpmaninfo send the SMP Report Manufacturer Info command\n" "debug turn debugging on/off for a bus, target, or lun, or all devices\n" "tags report or set the number of transaction slots for a device\n" "negotiate report or set device negotiation parameters\n" @@ -4476,6 +5743,28 @@ usage(int verbose) "-c cdb [args] specify the SCSI CDB\n" "-i len fmt specify input data and input data format\n" "-o len fmt [args] specify output data and output data fmt\n" +"smpcmd arguments:\n" +"-r len fmt [args] specify the SMP command to be sent\n" +"-R len fmt [args] specify SMP response format\n" +"smprg arguments:\n" +"-l specify the long response format\n" +"smppc arguments:\n" +"-p phy specify the PHY to operate on\n" +"-l specify the long request/response format\n" +"-o operation specify the phy control operation\n" +"-d name set the attached device name\n" +"-m rate set the minimum physical link rate\n" +"-M rate set the maximum physical link rate\n" +"-T pp_timeout set the partial pathway timeout value\n" +"-a enable|disable enable or disable SATA slumber\n" +"-A enable|disable enable or disable SATA partial phy power\n" +"-s enable|disable enable or disable SAS slumber\n" +"-S enable|disable enable or disable SAS partial phy power\n" +"smpphylist arguments:\n" +"-l specify the long response format\n" +"-q only print phys with attached devices\n" +"smpmaninfo arguments:\n" +"-l specify the long response format\n" "debug arguments:\n" "-I CAM_DEBUG_INFO -- scsi commands, errors, data\n" "-T CAM_DEBUG_TRACE -- routine flow tracking\n" @@ -4537,7 +5826,7 @@ main(int argc, char **argv) /* * Get the base option. */ - optreturn = getoption(argv[1], &cmdlist, &arglist, &subopt); + optreturn = getoption(option_table,argv[1], &cmdlist, &arglist,&subopt); if (optreturn == CC_OR_AMBIGUOUS) { warnx("ambiguous option %s", argv[1]); @@ -4767,6 +6056,27 @@ main(int argc, char **argv) error = scsicmd(cam_dev, argc, argv, combinedopt, retry_count, timeout); break; + case CAM_CMD_SMP_CMD: + error = smpcmd(cam_dev, argc, argv, combinedopt, + retry_count, timeout); + break; + case CAM_CMD_SMP_RG: + error = smpreportgeneral(cam_dev, argc, argv, + combinedopt, retry_count, + timeout); + break; + case CAM_CMD_SMP_PC: + error = smpphycontrol(cam_dev, argc, argv, combinedopt, + retry_count, timeout); + break; + case CAM_CMD_SMP_PHYLIST: + error = smpphylist(cam_dev, argc, argv, combinedopt, + retry_count, timeout); + break; + case CAM_CMD_SMP_MANINFO: + error = smpmaninfo(cam_dev, argc, argv, combinedopt, + retry_count, timeout); + break; case CAM_CMD_DEBUG: error = camdebug(argc, argv, combinedopt); break; -- cgit v1.1