Implement 'camcontrol reportluns'. This allows users to send the SCSI
authorPeter Avalos <pavalos@dragonflybsd.org>
Sun, 2 Dec 2007 04:44:03 +0000 (04:44 +0000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sun, 2 Dec 2007 04:44:03 +0000 (04:44 +0000)
REPORT LUNS command to a device.

camcontrol.[c8]: Implement reportluns.  This tries to print the LUNs
out in a reasonable format.
scsi_all.[ch]: Revamp the report luns CDB structure and helper
functions.

Obtained-from: FreeBSD

sbin/camcontrol/camcontrol.8
sbin/camcontrol/camcontrol.c
sys/bus/cam/scsi/scsi_all.c
sys/bus/cam/scsi/scsi_all.h

index 2334517..0502649 100644 (file)
@@ -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
@@ -26,7 +26,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\" $FreeBSD: src/sbin/camcontrol/camcontrol.8,v 1.19.2.12 2003/01/08 17:55:02 njl Exp $
-.\" $DragonFly: src/sbin/camcontrol/camcontrol.8,v 1.7 2007/11/29 01:57:29 pavalos Exp $
+.\" $DragonFly: src/sbin/camcontrol/camcontrol.8,v 1.8 2007/12/02 04:44:03 pavalos Exp $
 .\"
 .Dd September 14, 1998
 .Dt CAMCONTROL 8
 .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
@@ -254,6 +261,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.
index a6d15f7..ca9d51c 100644 (file)
@@ -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
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sbin/camcontrol/camcontrol.c,v 1.21.2.13 2003/01/08 17:55:02 njl Exp $
- * $DragonFly: src/sbin/camcontrol/camcontrol.c,v 1.8 2007/11/29 01:57:29 pavalos Exp $
+ * $DragonFly: src/sbin/camcontrol/camcontrol.c,v 1.9 2007/12/02 04:44:03 pavalos Exp $
  */
 
 #include <sys/ioctl.h>
@@ -67,6 +67,7 @@ typedef enum {
        CAM_CMD_TAG             = 0x0000000e,
        CAM_CMD_RATE            = 0x0000000f,
        CAM_CMD_DETACH          = 0x00000010,
+       CAM_CMD_REPORTLUNS      = 0x00000011
 } cam_cmdmask;
 
 typedef enum {
@@ -125,6 +126,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},
@@ -193,6 +195,8 @@ static int  get_print_cts(struct cam_device *, int, int,
 static int     ratecontrol(struct cam_device *, int, int, int, char **,
                char *);
 static int     scsiformat(struct cam_device *, int, char **, char *, int, int);
+static int     scsireportluns(struct cam_device *device, int argc, char **argv,
+                              char *combinedopt, int retry_count, int timeout);
 #endif /* MINIMALISTIC */
 
 
@@ -3143,6 +3147,251 @@ scsiformat_bailout:
 
        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 
@@ -3155,6 +3404,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"
@@ -3187,6 +3437,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"
@@ -3227,6 +3478,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 <reporttype>   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"
@@ -3535,6 +3790,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);
index d0d727b..3f209d5 100644 (file)
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/scsi/scsi_all.c,v 1.14.2.11 2003/10/30 15:06:35 thomas Exp $
- * $DragonFly: src/sys/bus/cam/scsi/scsi_all.c,v 1.25 2007/11/29 03:10:24 pavalos Exp $
+ * $DragonFly: src/sys/bus/cam/scsi/scsi_all.c,v 1.26 2007/12/02 04:44:03 pavalos Exp $
  */
 
 #include <sys/param.h>
@@ -2680,8 +2680,9 @@ scsi_read_capacity(struct ccb_scsiio *csio, u_int32_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;
 
@@ -2698,7 +2699,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);
 }
 
 /*
index e42a870..8a4852c 100644 (file)
@@ -15,7 +15,7 @@
  * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
  *
  * $FreeBSD: src/sys/cam/scsi/scsi_all.h,v 1.14.2.5 2003/08/24 03:26:37 ken Exp $
- * $DragonFly: src/sys/bus/cam/scsi/scsi_all.h,v 1.12 2007/12/02 03:41:58 pavalos Exp $
+ * $DragonFly: src/sys/bus/cam/scsi/scsi_all.h,v 1.13 2007/12/02 04:44:03 pavalos Exp $
  */
 
 /*
@@ -714,11 +714,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 {
@@ -729,10 +734,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
@@ -1040,11 +1057,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,