==== //depot/FreeBSD-ken/src/sbin/camcontrol/camcontrol.8#20 - /usr/home/ken/perforce/FreeBSD-ken/src/sbin/camcontrol/camcontrol.8 ==== *** /tmp/tmp.62585.0 Fri Aug 18 09:38:44 2006 --- /usr/home/ken/perforce/FreeBSD-ken/src/sbin/camcontrol/camcontrol.8 Fri Aug 18 07:57:09 2006 *************** *** 1,5 **** .\" ! .\" Copyright (c) 1998, 1999, 2000, 2002, 2005 Kenneth D. Merry. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without --- 1,5 ---- .\" ! .\" 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,64 **** --- 59,70 ---- .Op Fl S .Op Fl R .Nm + .Ic reportluns + .Op device id + .Op generic args + .Op Fl c + .Op Fl r Ar reporttype + .Nm .Ic start .Op device id .Op generic args *************** *** 266,271 **** --- 272,306 ---- .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 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. ==== //depot/FreeBSD-ken/src/sbin/camcontrol/camcontrol.c#33 - /usr/home/ken/perforce/FreeBSD-ken/src/sbin/camcontrol/camcontrol.c ==== *** /tmp/tmp.62585.1 Fri Aug 18 09:38:44 2006 --- /usr/home/ken/perforce/FreeBSD-ken/src/sbin/camcontrol/camcontrol.c Fri Aug 18 09:38:28 2006 *************** *** 1,5 **** /* ! * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005 Kenneth D. Merry * All rights reserved. * * Redistribution and use in source and binary forms, with or without --- 1,5 ---- /* ! * 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,74 **** --- 69,75 ---- CAM_CMD_TAG = 0x0000000e, CAM_CMD_RATE = 0x0000000f, CAM_CMD_DETACH = 0x00000010, + CAM_CMD_REPORTLUNS = 0x00000011 } cam_cmdmask; typedef enum { *************** *** 127,132 **** --- 128,134 ---- {"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, "cr:"}, #endif /* MINIMALISTIC */ {"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL}, {"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL}, *************** *** 203,208 **** --- 205,212 ---- 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,3157 **** --- 3156,3395 ---- 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; + 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; + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'c': + countonly++; + 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; + } + } + + /* + * 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; + } + + 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 *************** *** 3196,3201 **** --- 3434,3440 ---- "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,3241 **** --- 3475,3483 ---- "-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" + "-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" *************** *** 3546,3551 **** --- 3788,3798 ---- case CAM_CMD_FORMAT: 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: ==== //depot/FreeBSD-ken/src/sys/cam/scsi/scsi_all.c#31 - /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_all.c ==== *** /tmp/tmp.62585.2 Fri Aug 18 09:38:44 2006 --- /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_all.c Fri Aug 18 07:57:09 2006 *************** *** 2749,2756 **** 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) { struct scsi_report_luns *scsi_cmd; --- 2749,2757 ---- void scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries, 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) { struct scsi_report_luns *scsi_cmd; *************** *** 2767,2773 **** 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); } /* --- 2768,2775 ---- scsi_cmd = (struct scsi_report_luns *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = REPORT_LUNS; ! scsi_cmd->select_report = select_report; ! scsi_ulto4b(alloc_len, scsi_cmd->length); } /* ==== //depot/FreeBSD-ken/src/sys/cam/scsi/scsi_all.h#15 - /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_all.h ==== *** /tmp/tmp.62585.3 Fri Aug 18 09:38:44 2006 --- /usr/home/ken/perforce/FreeBSD-ken/src/sys/cam/scsi/scsi_all.h Fri Aug 18 07:57:09 2006 *************** *** 708,718 **** 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; }; struct scsi_report_luns_data { --- 708,723 ---- struct scsi_report_luns { ! 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,732 **** */ struct { u_int8_t lundata[8]; ! } luns[1]; }; #define RPL_LUNDATA_ATYP_MASK 0xc0 /* MBZ for type 0 lun */ ! #define RPL_LUNDATA_T0LUN 1 /* @ lundata[1] */ struct scsi_sense_data --- 728,749 ---- */ struct { u_int8_t lundata[8]; ! } 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_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,1045 **** 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 scsi_synchronize_cache(struct ccb_scsiio *csio, u_int32_t retries, --- 1052,1063 ---- 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, ! 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,