diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8 index 3923d5a5a5b0..dcae09110d45 100644 --- a/sbin/camcontrol/camcontrol.8 +++ b/sbin/camcontrol/camcontrol.8 @@ -1,5 +1,5 @@ .\" -.\" Copyright (c) 1998, 1999, 2000, 2002, 2005 Kenneth D. Merry. +.\" Copyright (c) 1998, 1999, 2000, 2002, 2005, 2006 Kenneth D. Merry. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -59,6 +59,13 @@ .Op Fl S .Op Fl R .Nm +.Ic reportluns +.Op device id +.Op generic args +.Op Fl c +.Op Fl l +.Op Fl r Ar reporttype +.Nm .Ic start .Op device id .Op generic args @@ -266,6 +273,37 @@ This is to aid in script writing. .It Fl R Print out transfer rate information. .El +.It Ic reportluns +Send the SCSI REPORT LUNS (0xA0) command to the given device. +By default, +.Nm +will print out the list of logical units (LUNs) supported by the target device. +There are a couple of options to modify the output: +.Bl -tag -width 01234567890123 +.It Fl c +Just print out a count of LUNs, not the actual LUN numbers. +.It Fl l +Just print out the LUNs, and don't print out the count. +.It Fl r Ar reporttype +Specify the type of report to request from the target: +.Bl -tag -width 012345678 +.It default +Return the default report. +This is the +.Nm +default. +Most targets will support this report if they support the REPORT LUNS +command. +.It wellknown +Return only well known LUNs. +.It all +Return all available LUNs. +.El +.El +.Pp +.Nm +will try to print out LUN numbers in a reasonable format. +It can understand the peripheral, flat, LUN and extended LUN formats. .It Ic start Send the SCSI Start/Stop Unit (0x1B) command to the given device with the start bit set. diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index a8447eea2d17..510a33bbdb9b 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005 Kenneth D. Merry + * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005, 2006 Kenneth D. Merry * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -69,6 +69,7 @@ typedef enum { CAM_CMD_TAG = 0x0000000e, CAM_CMD_RATE = 0x0000000f, CAM_CMD_DETACH = 0x00000010, + CAM_CMD_REPORTLUNS = 0x00000011 } cam_cmdmask; typedef enum { @@ -127,6 +128,7 @@ struct camcontrol_opts option_table[] = { {"stop", CAM_CMD_STARTSTOP, CAM_ARG_NONE, NULL}, {"load", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT | CAM_ARG_EJECT, NULL}, {"eject", CAM_CMD_STARTSTOP, CAM_ARG_EJECT, NULL}, + {"reportluns", CAM_CMD_REPORTLUNS, CAM_ARG_NONE, "clr:"}, #endif /* MINIMALISTIC */ {"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL}, {"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL}, @@ -203,6 +205,8 @@ static int ratecontrol(struct cam_device *device, int retry_count, int timeout, int argc, char **argv, char *combinedopt); static int scsiformat(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); +static int scsireportluns(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout); #endif /* MINIMALISTIC */ camcontrol_optret @@ -3152,6 +3156,251 @@ scsiformat(struct cam_device *device, int argc, char **argv, return(error); } + +static int +scsireportluns(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout) +{ + union ccb *ccb; + int c, countonly, lunsonly; + struct scsi_report_luns_data *lundata; + int alloc_len; + uint8_t report_type; + uint32_t list_len, i, j; + int retval; + + retval = 0; + lundata = NULL; + report_type = RPL_REPORT_DEFAULT; + ccb = cam_getccb(device); + + if (ccb == NULL) { + warnx("%s: error allocating ccb", __func__); + return (1); + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); + + countonly = 0; + lunsonly = 0; + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'c': + countonly++; + break; + case 'l': + lunsonly++; + break; + case 'r': + if (strcasecmp(optarg, "default") == 0) + report_type = RPL_REPORT_DEFAULT; + else if (strcasecmp(optarg, "wellknown") == 0) + report_type = RPL_REPORT_WELLKNOWN; + else if (strcasecmp(optarg, "all") == 0) + report_type = RPL_REPORT_ALL; + else { + warnx("%s: invalid report type \"%s\"", + __func__, optarg); + retval = 1; + goto bailout; + } + break; + default: + break; + } + } + + if ((countonly != 0) + && (lunsonly != 0)) { + warnx("%s: you can only specify one of -c or -l", __func__); + retval = 1; + goto bailout; + } + /* + * According to SPC-4, the allocation length must be at least 16 + * bytes -- enough for the header and one LUN. + */ + alloc_len = sizeof(*lundata) + 8; + +retry: + + lundata = malloc(alloc_len); + + if (lundata == NULL) { + warn("%s: error mallocing %d bytes", __func__, alloc_len); + retval = 1; + goto bailout; + } + + scsi_report_luns(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*select_report*/ report_type, + /*rpl_buf*/ lundata, + /*alloc_len*/ alloc_len, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 5000); + + /* 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 REPORT LUNS 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; + } + + + list_len = scsi_4btoul(lundata->length); + + /* + * If we need to list the LUNs, and our allocation + * length was too short, reallocate and retry. + */ + if ((countonly == 0) + && (list_len > (alloc_len - sizeof(*lundata)))) { + alloc_len = list_len + sizeof(*lundata); + free(lundata); + goto retry; + } + + if (lunsonly == 0) + fprintf(stdout, "%u LUN%s found\n", list_len / 8, + ((list_len / 8) > 1) ? "s" : ""); + + if (countonly != 0) + goto bailout; + + for (i = 0; i < (list_len / 8); i++) { + int no_more; + + no_more = 0; + for (j = 0; j < sizeof(lundata->luns[i].lundata); j += 2) { + if (j != 0) + fprintf(stdout, ","); + switch (lundata->luns[i].lundata[j] & + RPL_LUNDATA_ATYP_MASK) { + case RPL_LUNDATA_ATYP_PERIPH: + if ((lundata->luns[i].lundata[j] & + RPL_LUNDATA_PERIPH_BUS_MASK) != 0) + fprintf(stdout, "%d:", + lundata->luns[i].lundata[j] & + RPL_LUNDATA_PERIPH_BUS_MASK); + else if ((j == 0) + && ((lundata->luns[i].lundata[j+2] & + RPL_LUNDATA_PERIPH_BUS_MASK) == 0)) + no_more = 1; + + fprintf(stdout, "%d", + lundata->luns[i].lundata[j+1]); + break; + case RPL_LUNDATA_ATYP_FLAT: { + uint8_t tmplun[2]; + tmplun[0] = lundata->luns[i].lundata[j] & + RPL_LUNDATA_FLAT_LUN_MASK; + tmplun[1] = lundata->luns[i].lundata[j+1]; + + fprintf(stdout, "%d", scsi_2btoul(tmplun)); + no_more = 1; + break; + } + case RPL_LUNDATA_ATYP_LUN: + fprintf(stdout, "%d:%d:%d", + (lundata->luns[i].lundata[j+1] & + RPL_LUNDATA_LUN_BUS_MASK) >> 5, + lundata->luns[i].lundata[j] & + RPL_LUNDATA_LUN_TARG_MASK, + lundata->luns[i].lundata[j+1] & + RPL_LUNDATA_LUN_LUN_MASK); + break; + case RPL_LUNDATA_ATYP_EXTLUN: { + int field_len, field_len_code, eam_code; + + eam_code = lundata->luns[i].lundata[j] & + RPL_LUNDATA_EXT_EAM_MASK; + field_len_code = (lundata->luns[i].lundata[j] & + RPL_LUNDATA_EXT_LEN_MASK) >> 4; + field_len = field_len_code * 2; + + if ((eam_code == RPL_LUNDATA_EXT_EAM_WK) + && (field_len_code == 0x00)) { + fprintf(stdout, "%d", + lundata->luns[i].lundata[j+1]); + } else if ((eam_code == + RPL_LUNDATA_EXT_EAM_NOT_SPEC) + && (field_len_code == 0x03)) { + uint8_t tmp_lun[8]; + + /* + * This format takes up all 8 bytes. + * If we aren't starting at offset 0, + * that's a bug. + */ + if (j != 0) { + fprintf(stdout, "Invalid " + "offset %d for " + "Extended LUN not " + "specified format", j); + no_more = 1; + break; + } + bzero(tmp_lun, sizeof(tmp_lun)); + bcopy(&lundata->luns[i].lundata[j+1], + &tmp_lun[1], sizeof(tmp_lun) - 1); + fprintf(stdout, "%#jx", + (intmax_t)scsi_8btou64(tmp_lun)); + no_more = 1; + } else { + fprintf(stderr, "Unknown Extended LUN" + "Address method %#x, length " + "code %#x", eam_code, + field_len_code); + no_more = 1; + } + break; + } + default: + fprintf(stderr, "Unknown LUN address method " + "%#x\n", lundata->luns[i].lundata[0] & + RPL_LUNDATA_ATYP_MASK); + break; + } + /* + * For the flat addressing method, there are no + * other levels after it. + */ + if (no_more != 0) + break; + } + fprintf(stdout, "\n"); + } + +bailout: + + cam_freeccb(ccb); + + free(lundata); + + return (retval); +} + #endif /* MINIMALISTIC */ void @@ -3164,6 +3413,7 @@ usage(int verbose) " 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 reportluns [dev_id][generic args] [-c] [-l] [-r report]\n" " camcontrol start [dev_id][generic args]\n" " camcontrol stop [dev_id][generic args]\n" " camcontrol load [dev_id][generic args]\n" @@ -3196,6 +3446,7 @@ usage(int verbose) "periphlist list all CAM peripheral drivers attached to a device\n" "tur send a test unit ready to the named device\n" "inquiry send a SCSI inquiry command to the named device\n" +"reportluns send a SCSI report luns command to the device\n" "start send a Start Unit command to the device\n" "stop send a Stop Unit command to the device\n" "load send a Start Unit command to the device with the load bit set\n" @@ -3236,6 +3487,10 @@ usage(int verbose) "-D get the standard inquiry data\n" "-S get the serial number\n" "-R get the transfer rate, etc.\n" +"reportluns arguments:\n" +"-c only report a count of available LUNs\n" +"-l only print out luns, and not a count\n" +"-r specify \"default\", \"wellknown\" or \"all\"\n" "cmd arguments:\n" "-c cdb [args] specify the SCSI CDB\n" "-i len fmt specify input data and input data format\n" @@ -3547,6 +3802,11 @@ main(int argc, char **argv) error = scsiformat(cam_dev, argc, argv, combinedopt, retry_count, timeout); break; + case CAM_CMD_REPORTLUNS: + error = scsireportluns(cam_dev, argc, argv, + combinedopt, retry_count, + timeout); + break; #endif /* MINIMALISTIC */ case CAM_CMD_USAGE: usage(1); diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c index 4c5114821d30..adaef35cc965 100644 --- a/sys/cam/scsi/scsi_all.c +++ b/sys/cam/scsi/scsi_all.c @@ -2749,8 +2749,9 @@ scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries, void scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), - u_int8_t tag_action, struct scsi_report_luns_data *rpl_buf, - u_int32_t alloc_len, u_int8_t sense_len, u_int32_t timeout) + u_int8_t tag_action, u_int8_t select_report, + struct scsi_report_luns_data *rpl_buf, u_int32_t alloc_len, + u_int8_t sense_len, u_int32_t timeout) { struct scsi_report_luns *scsi_cmd; @@ -2767,7 +2768,8 @@ scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries, scsi_cmd = (struct scsi_report_luns *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = REPORT_LUNS; - scsi_ulto4b(alloc_len, scsi_cmd->addr); + scsi_cmd->select_report = select_report; + scsi_ulto4b(alloc_len, scsi_cmd->length); } /* diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index 33b26de83dc4..8d320d9d4c6a 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -708,11 +708,16 @@ struct scsi_read_capacity_data_long struct scsi_report_luns { - u_int8_t opcode; - u_int8_t byte2; - u_int8_t unused[3]; - u_int8_t addr[4]; - u_int8_t control; + uint8_t opcode; + uint8_t reserved1; +#define RPL_REPORT_DEFAULT 0x00 +#define RPL_REPORT_WELLKNOWN 0x01 +#define RPL_REPORT_ALL 0x02 + uint8_t select_report; + uint8_t reserved2[3]; + uint8_t length[4]; + uint8_t reserved3; + uint8_t control; }; struct scsi_report_luns_data { @@ -723,10 +728,22 @@ struct scsi_report_luns_data { */ struct { u_int8_t lundata[8]; - } luns[1]; + } luns[0]; }; +#define RPL_LUNDATA_PERIPH_BUS_MASK 0x3f +#define RPL_LUNDATA_FLAT_LUN_MASK 0x3f +#define RPL_LUNDATA_LUN_TARG_MASK 0x3f +#define RPL_LUNDATA_LUN_BUS_MASK 0xe0 +#define RPL_LUNDATA_LUN_LUN_MASK 0x1f +#define RPL_LUNDATA_EXT_LEN_MASK 0x30 +#define RPL_LUNDATA_EXT_EAM_MASK 0x0f +#define RPL_LUNDATA_EXT_EAM_WK 0x01 +#define RPL_LUNDATA_EXT_EAM_NOT_SPEC 0x0f #define RPL_LUNDATA_ATYP_MASK 0xc0 /* MBZ for type 0 lun */ -#define RPL_LUNDATA_T0LUN 1 /* @ lundata[1] */ +#define RPL_LUNDATA_ATYP_PERIPH 0x00 +#define RPL_LUNDATA_ATYP_FLAT 0x40 +#define RPL_LUNDATA_ATYP_LUN 0x80 +#define RPL_LUNDATA_ATYP_EXTLUN 0xc0 struct scsi_sense_data @@ -1035,11 +1052,12 @@ void scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries, uint32_t timeout); void scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries, - void (*cbfcnp)(struct cam_periph *, - union ccb *), u_int8_t tag_action, - struct scsi_report_luns_data *, - u_int32_t alloc_len, u_int8_t sense_len, - u_int32_t timeout); + void (*cbfcnp)(struct cam_periph *, + union ccb *), u_int8_t tag_action, + u_int8_t select_report, + struct scsi_report_luns_data *rpl_buf, + u_int32_t alloc_len, u_int8_t sense_len, + u_int32_t timeout); void scsi_synchronize_cache(struct ccb_scsiio *csio, u_int32_t retries,