This is a command-line management tool for RAID controllers managed by the

mlx(4) driver.
This commit is contained in:
Mike Smith 2000-04-11 03:01:45 +00:00
parent 9b58ae8693
commit b6a7bef2be
7 changed files with 1561 additions and 0 deletions

View File

@ -0,0 +1,8 @@
# $FreeBSD$
PROG= mlxcontrol
SRCS= command.c config.c interface.c util.c
MAN8= mlxcontrol.8
.include <bsd.prog.mk>

View File

@ -0,0 +1,717 @@
/*-
* Copyright (c) 1999 Michael Smith
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <fcntl.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#if 0
#include <sys/mlxio.h>
#include <sys/mlxreg.h>
#else
#include "../sys/dev/mlx/mlxio.h"
#include "../sys/dev/mlx/mlxreg.h"
#endif
#include "mlxcontrol.h"
static int cmd_status(int argc, char *argv[]);
static int cmd_rescan(int argc, char *argv[]);
static int cmd_detach(int argc, char *argv[]);
static int cmd_check(int argc, char *argv[]);
static int cmd_rebuild(int argc, char *argv[]);
#ifdef SUPPORT_PAUSE
static int cmd_pause(int argc, char *argv[]);
#endif
static int cmd_help(int argc, char *argv[]);
extern int cmd_config(int argc, char *argv[]);
struct
{
char *cmd;
int (*func)(int argc, char *argv[]);
char *desc;
char *text;
} commands[] = {
{"status", cmd_status,
"displays device status",
" status [-qv] [<drive>...]\n"
" Display status for <drive> or all drives if none is listed\n"
" -q Suppress output.\n"
" -v Display verbose information.\n"
" Returns 0 if all drives tested are online, 1 if one or more are\n"
" critical, and 2 if one or more are offline."},
{"rescan", cmd_rescan,
"scan for new system drives",
" rescan <controller> [<controller>...]\n"
" Rescan <controller> for system drives.\n"
" rescan -a\n"
" Rescan all controllers for system drives."},
{"detach", cmd_detach,
"detach system drives",
" detach <drive> [<drive>...]\n"
" Detaches <drive> from the controller.\n"
" detach -a <controller>\n"
" Detaches all drives on <controller>."},
{"check", cmd_check,
"consistency-check a system drive",
" check <drive>\n"
" Requests a check and rebuild of the parity information on <drive>.\n"
" Note that each controller can only check one system drive at a time."},
{"rebuild", cmd_rebuild,
"initiate a rebuild of a dead physical drive",
" rebuild <controller> <physdrive>\n"
" All system drives using space on the physical drive <physdrive>\n"
" are rebuilt, reconstructing all data on the drive.\n"
" Note that each controller can only perform one rebuild at a time."},
#ifdef SUPPORT_PAUSE
{"pause", cmd_pause,
"pauses controller channels",
" pause [-t <howlong>] [-d <delay>] <controller> [<channel>...]\n"
" Pauses SCSI I/O on <channel> and <controller>. If no channel is specified,\n"
" all channels are paused.\n"
" <howlong> How long (seconds) to pause for (default 30).\n"
" <delay> How long (seconds) to wait before pausing (default 30).\n"
" pause <controller> -c\n"
" Cancels any pending pause operation on <controller>."},
#endif
{"config", cmd_config,
"examine and update controller configuration",
" config <controller>\n"
" Print configuration for <controller>."},
{"help", cmd_help,
"give help on usage",
""},
{NULL, NULL, NULL, NULL}
};
/********************************************************************************
* Command dispatch and global options parsing.
*/
int
main(int argc, char *argv[])
{
int ch, i, oargc;
char **oargv;
oargc = argc;
oargv = argv;
while ((ch = getopt(argc, argv, "")) != -1)
switch(ch) {
default:
return(cmd_help(0, NULL));
}
argc -= optind;
argv += optind;
if (argc > 0)
for (i = 0; commands[i].cmd != NULL; i++)
if (!strcmp(argv[0], commands[i].cmd))
return(commands[i].func(argc, argv));
return(cmd_help(oargc, oargv));
}
/********************************************************************************
* Helptext output
*/
static int
cmd_help(int argc, char *argv[])
{
int i;
if (argc > 1)
for (i = 0; commands[i].cmd != NULL; i++)
if (!strcmp(argv[1], commands[i].cmd)) {
fprintf(stderr, "%s\n", commands[i].text);
fflush(stderr);
return(0);
}
if (argv != NULL)
fprintf(stderr, "Unknown command '%s'.\n", argv[1]);
fprintf(stderr, "Valid commands are:\n");
for (i = 0; commands[i].cmd != NULL; i++)
fprintf(stderr, " %-20s %s\n", commands[i].cmd, commands[i].desc);
fflush(stderr);
return(0);
}
/********************************************************************************
* Status output
*
* status [-qv] [<device> ...]
* Prints status for <device>, or all if none listed.
*
* -q Suppresses output, command returns 0 if devices are OK, 1 if one or
* more devices are critical, 2 if one or more devices are offline.
*/
static struct mlx_rebuild_status rs;
static int rs_ctrlr = -1;
static int status_result = 0;
/* XXX more verbosity! */
static void
status_print(int unit, void *arg)
{
int verbosity = *(int *)arg;
int fd, result, ctrlr, sysdrive, statvalid;
/* Find which controller and what system drive we are */
statvalid = 0;
if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) {
warnx("couldn't get controller/drive for %s", drivepath(unit));
} else {
/* If we don't have rebuild stats for this controller, get them */
if (rs_ctrlr == ctrlr) {
statvalid = 1;
} else {
if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) {
warn("can't open %s", ctrlrpath(ctrlr));
} else {
if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) {
warn("ioctl MLX_REBUILDSTAT");
} else {
rs_ctrlr = ctrlr;
statvalid = 1;
}
close(fd);
}
}
}
/* Get the device */
if ((fd = open(drivepath(unit), 0)) < 0) {
warn("can't open %s", drivepath(unit));
return;
}
/* Get its status */
if (ioctl(fd, MLXD_STATUS, &result) < 0) {
warn("ioctl MLXD_STATUS");
} else {
switch(result) {
case MLX_SYSD_ONLINE:
if (verbosity > 0)
printf("%s: online", drivename(unit));
break;
case MLX_SYSD_CRITICAL:
if (verbosity > 0)
printf("%s: critical", drivename(unit));
if (status_result < 1)
status_result = 1;
break;
case MLX_SYSD_OFFLINE:
if (verbosity > 0)
printf("%s: offline", drivename(unit));
if (status_result < 2)
status_result = 2;
break;
default:
if (verbosity > 0) {
printf("%s: unknown status 0x%x", drivename(unit), result);
}
}
if (verbosity > 0) {
/* rebuild/check in progress on this drive? */
if (statvalid && (rs_ctrlr == ctrlr) &&
(rs.rs_drive == sysdrive) && (rs.rs_code != MLX_REBUILDSTAT_IDLE)) {
switch(rs.rs_code) {
case MLX_REBUILDSTAT_REBUILDCHECK:
printf(" [consistency check");
break;
case MLX_REBUILDSTAT_ADDCAPACITY:
printf(" [add capacity");
break;
case MLX_REBUILDSTAT_ADDCAPACITYINIT:
printf(" [add capacity init");
break;
default:
printf(" [unknown operation");
}
printf(": %d/%d, %d%% complete]",
rs.rs_remaining, rs.rs_size,
((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
}
printf("\n");
}
}
close(fd);
}
static struct
{
int hwid;
char *name;
} mlx_controller_names[] = {
{0x01, "960P/PD"},
{0x02, "960PL"},
{0x10, "960PG"},
{0x11, "960PJ"},
{0x12, "960PR"},
{0x13, "960PT"},
{0x14, "960PTL0"},
{0x15, "960PRL"},
{0x16, "960PTL1"},
{0x20, "1100PVX"},
{-1, NULL}
};
static void
controller_print(int unit, void *arg)
{
struct mlx_enquiry2 enq;
struct mlx_phys_drv pd;
int verbosity = *(int *)arg;
static char buf[80];
char *model;
int i, channel, target;
if (verbosity == 0)
return;
/* fetch and print controller data */
if (mlx_enquiry(unit, &enq)) {
printf("mlx%d: error submitting ENQUIRY2\n", unit);
} else {
for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
if ((enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
model = mlx_controller_names[i].name;
break;
}
}
if (model == NULL) {
sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff);
model = buf;
}
printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
unit, model,
enq.me_actual_channels,
enq.me_actual_channels > 1 ? "s" : "",
enq.me_firmware_id & 0xff,
(enq.me_firmware_id >> 8) & 0xff,
(enq.me_firmware_id >> 16),
(enq.me_firmware_id >> 24) & 0xff,
enq.me_mem_size / (1024 * 1024));
if (verbosity > 1) {
printf(" Hardware ID 0x%08x\n", enq.me_hardware_id);
printf(" Firmware ID 0x%08x\n", enq.me_firmware_id);
printf(" Configured/Actual channels %d/%d\n", enq.me_configured_channels,
enq.me_actual_channels);
printf(" Max Targets %d\n", enq.me_max_targets);
printf(" Max Tags %d\n", enq.me_max_tags);
printf(" Max System Drives %d\n", enq.me_max_sys_drives);
printf(" Max Arms %d\n", enq.me_max_arms);
printf(" Max Spans %d\n", enq.me_max_spans);
printf(" DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size,
enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size);
printf(" DRAM type %d\n", enq.me_mem_type);
printf(" Clock Speed %dns\n", enq.me_clock_speed);
printf(" Hardware Speed %dns\n", enq.me_hardware_speed);
printf(" Max Commands %d\n", enq.me_max_commands);
printf(" Max SG Entries %d\n", enq.me_max_sg);
printf(" Max DP %d\n", enq.me_max_dp);
printf(" Max IOD %d\n", enq.me_max_iod);
printf(" Max Comb %d\n", enq.me_max_comb);
printf(" Latency %ds\n", enq.me_latency);
printf(" SCSI Timeout %ds\n", enq.me_scsi_timeout);
printf(" Min Free Lines %d\n", enq.me_min_freelines);
printf(" Rate Constant %d\n", enq.me_rate_const);
printf(" MAXBLK %d\n", enq.me_maxblk);
printf(" Blocking Factor %d sectors\n", enq.me_blocking_factor);
printf(" Cache Line Size %d blocks\n", enq.me_cacheline);
printf(" SCSI Capability %s%dMHz, %d bit\n",
enq.me_scsi_cap & (1<<4) ? "differential " : "",
(1 << ((enq.me_scsi_cap >> 2) & 3)) * 10,
8 << (enq.me_scsi_cap & 0x3));
printf(" Firmware Build Number %d\n", enq.me_firmware_build);
printf(" Fault Management Type %d\n", enq.me_fault_mgmt_type);
#if 0
printf(" Features %b\n", enq.me_firmware_features,
"\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
#endif
}
/* fetch and print physical drive data */
for (channel = 0; channel < enq.me_configured_channels; channel++) {
for (target = 0; target < enq.me_max_targets; target++) {
if ((mlx_get_device_state(unit, channel, target, &pd) == 0) &&
(pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) {
mlx_print_phys_drv(&pd, channel, target, " ", verbosity - 1);
if (verbosity > 1) {
/* XXX print device statistics? */
}
}
}
}
}
}
static int
cmd_status(int argc, char *argv[])
{
int ch, verbosity = 1, i, unit;
optreset = 1;
optind = 1;
while ((ch = getopt(argc, argv, "qv")) != -1)
switch(ch) {
case 'q':
verbosity = 0;
break;
case 'v':
verbosity = 2;
break;
default:
return(cmd_help(argc, argv));
}
argc -= optind;
argv += optind;
if (argc < 1) {
mlx_foreach(controller_print, &verbosity);
mlxd_foreach(status_print, &verbosity);
} else {
for (i = 0; i < argc; i++) {
if ((unit = driveunit(argv[i])) == -1) {
warnx("'%s' is not a valid drive", argv[i]);
} else {
status_print(unit, &verbosity);
}
}
}
return(status_result);
}
/********************************************************************************
* Recscan for system drives on one or more controllers.
*
* rescan <controller> [<controller>...]
* rescan -a
*/
static void
rescan_ctrlr(int unit, void *junk)
{
int fd;
/* Get the device */
if ((fd = open(ctrlrpath(unit), 0)) < 0) {
warn("can't open %s", ctrlrpath(unit));
return;
}
if (ioctl(fd, MLX_RESCAN_DRIVES) < 0)
warn("can't rescan %s", ctrlrname(unit));
close(fd);
}
static int
cmd_rescan(int argc, char *argv[])
{
int all = 0, i, ch, unit;
optreset = 1;
optind = 1;
while ((ch = getopt(argc, argv, "a")) != -1)
switch(ch) {
case 'a':
all = 1;
break;
default:
return(cmd_help(argc, argv));
}
argc -= optind;
argv += optind;
if (all) {
mlx_foreach(rescan_ctrlr, NULL);
} else {
for (i = 0; i < argc; i++) {
if ((unit = ctrlrunit(argv[i])) == -1) {
warnx("'%s' is not a valid controller", argv[i]);
} else {
rescan_ctrlr(unit, NULL);
}
}
}
return(0);
}
/********************************************************************************
* Detach one or more system drives from a controller.
*
* detach <drive> [<drive>...]
* Detach <drive>.
*
* detach -a <controller> [<controller>...]
* Detach all drives on <controller>.
*
*/
static void
detach_drive(int unit, void *arg)
{
int fd;
/* Get the device */
if ((fd = open(ctrlrpath(unit), 0)) < 0) {
warn("can't open %s", ctrlrpath(unit));
return;
}
if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0)
warn("can't detach %s", drivename(unit));
close(fd);
}
static int
cmd_detach(int argc, char *argv[])
{
struct mlxd_foreach_action ma;
int all = 0, i, ch, unit;
optreset = 1;
optind = 1;
while ((ch = getopt(argc, argv, "a")) != -1)
switch(ch) {
case 'a':
all = 1;
break;
default:
return(cmd_help(argc, argv));
}
argc -= optind;
argv += optind;
if (all) {
ma.func = detach_drive;
ma.arg = &unit;
for (i = 0; i < argc; i++) {
if ((unit = ctrlrunit(argv[i])) == -1) {
warnx("'%s' is not a valid controller", argv[i]);
} else {
mlxd_foreach_ctrlr(unit, &ma);
}
}
} else {
for (i = 0; i < argc; i++) {
if ((unit = driveunit(argv[i])) == -1) {
warnx("'%s' is not a valid drive", argv[i]);
} else {
/* run across all controllers to find this drive */
mlx_foreach(detach_drive, &unit);
}
}
}
return(0);
}
/********************************************************************************
* Initiate a consistency check on a system drive.
*
* check [<drive>]
* Start a check of <drive>
*
*/
static int
cmd_check(int argc, char *argv[])
{
int unit, fd, result;
if (argc != 2)
return(cmd_help(argc, argv));
if ((unit = driveunit(argv[1])) == -1) {
warnx("'%s' is not a valid drive", argv[1]);
} else {
/* Get the device */
if ((fd = open(drivepath(unit), 0)) < 0) {
warn("can't open %s", drivepath(unit));
} else {
/* Try to start the check */
if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) {
switch(result) {
case 0x0002:
warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]);
break;
case 0x0105:
warnx("drive %s is invalid, or not a drive which can be checked", argv[1]);
break;
case 0x0106:
warnx("drive rebuild or consistency check is already in progress on this controller");
break;
default:
warn("ioctl MLXD_CHECKASYNC");
}
}
}
}
return(0);
}
/********************************************************************************
* Initiate a physical drive rebuild
*
* rebuild <controller> <channel>:<target>
* Start a rebuild of <controller>:<channel>:<target>
*
*/
static int
cmd_rebuild(int argc, char *argv[])
{
struct mlx_rebuild_request rb;
int unit, fd;
if (argc != 3)
return(cmd_help(argc, argv));
/* parse arguments */
if ((unit = ctrlrunit(argv[1])) == -1) {
warnx("'%s' is not a valid controller", argv[1]);
return(1);
}
/* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) &&
(sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) {
warnx("'%s' is not a valid physical drive", argv[2]);
return(1);
}
/* get the device */
if ((fd = open(ctrlrpath(unit), 0)) < 0) {
warn("can't open %s", ctrlrpath(unit));
return(1);
}
/* try to start the rebuild */
if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) {
switch(rb.rr_status) {
case 0x0002:
warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target);
break;
case 0x0004:
warnx("drive failed during rebuild");
break;
case 0x0105:
warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target);
break;
case 0x0106:
warnx("drive rebuild or consistency check is already in progress on this controller");
break;
default:
warn("ioctl MLXD_CHECKASYNC");
}
}
return(0);
}
#ifdef SUPPORT_PAUSE
/********************************************************************************
* Pause one or more channels on a controller
*
* pause [-d <delay>] [-t <time>] <controller> [<channel>...]
* Pauses <channel> (or all channels) for <time> seconds after a
* delay of <delay> seconds.
* pause <controller> -c
* Cancels pending pause
*/
static int
cmd_pause(int argc, char *argv[])
{
struct mlx_pause mp;
int unit, i, ch, fd, cancel = 0;
char *cp;
int oargc = argc;
char **oargv = argv;
mp.mp_which = 0;
mp.mp_when = 30;
mp.mp_howlong = 30;
optreset = 1;
optind = 1;
while ((ch = getopt(argc, argv, "cd:t:")) != -1)
switch(ch) {
case 'c':
cancel = 1;
break;
case 'd':
mp.mp_when = strtol(optarg, &cp, 0);
if (*cp != 0)
return(cmd_help(argc, argv));
break;
case 't':
mp.mp_howlong = strtol(optarg, &cp, 0);
if (*cp != 0)
return(cmd_help(argc, argv));
break;
default:
return(cmd_help(argc, argv));
}
argc -= optind;
argv += optind;
/* get controller unit number that we're working on */
if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
return(cmd_help(oargc, oargv));
/* Get the device */
if ((fd = open(ctrlrpath(unit), 0)) < 0) {
warn("can't open %s", ctrlrpath(unit));
return(1);
}
if (argc == 1) {
/* controller-wide pause/cancel */
mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
} else {
for (i = 1; i < argc; i++) {
ch = strtol(argv[i], &cp, 0);
if (*cp != 0) {
warnx("bad channel number '%s'", argv[i]);
continue;
} else {
mp.mp_which |= (1 << ch);
}
}
}
if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
close(fd);
return(0);
}
#endif /* SUPPORT_PAUSE */

View File

@ -0,0 +1,162 @@
/*-
* Copyright (c) 1999 Michael Smith
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <fcntl.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#if 0
#include <sys/mlxio.h>
#include <sys/mlxreg.h>
#else
#include "../sys/dev/mlx/mlxio.h"
#include "../sys/dev/mlx/mlxreg.h"
#endif
#include "mlxcontrol.h"
static void print_span(struct mlx_sys_drv_span *span, int arms);
static void print_sys_drive(struct conf_config *conf, int drvno);
static void print_phys_drive(struct conf_config *conf, int chn, int targ);
/********************************************************************************
* Get the configuration from the selected controller.
*
* config <controller>
* Print the configuration for <controller>
*
* XXX update to support adding/deleting drives.
*/
int
cmd_config(int argc, char *argv[])
{
struct conf_config conf;
int unit = 0; /* XXX */
int i, j;
bzero(&conf.cc_cfg, sizeof(conf.cc_cfg));
if (mlx_read_configuration(unit, &conf.cc_cfg)) {
printf("mlx%d: error submitting READ CONFIGURATION\n", unit);
} else {
printf("# Controller <INSERT DETAILS HERE>\n");
printf("#\n# Physical devices connected:\n");
for (i = 0; i < 5; i++)
for (j = 0; j < 16; j++)
print_phys_drive(&conf, i, j);
printf("#\n# System Drives defined:\n");
for (i = 0; i < conf.cc_cfg.cc_num_sys_drives; i++)
print_sys_drive(&conf, i);
}
return(0);
}
/********************************************************************************
* Print details for the system drive (drvno) in a format that we will be
* able to parse later.
*
* drive?? <raidlevel> <writemode>
* span? 0x????????-0x???????? ????MB on <disk> [...]
* ...
*/
static void
print_span(struct mlx_sys_drv_span *span, int arms)
{
int i;
printf("0x%08x-0x%08x %uMB on", span->sp_start_lba, span->sp_start_lba + span->sp_nblks, span->sp_nblks / 2048);
for (i = 0; i < arms; i++)
printf(" disk%02d%02d", span->sp_arm[i] >> 4, span->sp_arm[i] & 0x0f);
printf("\n");
}
static void
print_sys_drive(struct conf_config *conf, int drvno)
{
struct mlx_sys_drv *drv = &conf->cc_cfg.cc_sys_drives[drvno];
int i;
printf("drive%02d ", drvno);
switch(drv->sd_raidlevel & 0xf) {
case MLX_SYS_DRV_RAID0:
printf("RAID0");
break;
case MLX_SYS_DRV_RAID1:
printf("RAID1");
break;
case MLX_SYS_DRV_RAID3:
printf("RAID3");
break;
case MLX_SYS_DRV_RAID5:
printf("RAID5");
break;
case MLX_SYS_DRV_RAID6:
printf("RAID6");
break;
case MLX_SYS_DRV_JBOD:
printf("JBOD");
break;
default:
printf("RAID?");
}
printf(" write%s\n", drv->sd_raidlevel & MLX_SYS_DRV_WRITEBACK ? "back" : "through");
for (i = 0; i < drv->sd_valid_spans; i++) {
printf(" span%d ", i);
print_span(&drv->sd_span[i], drv->sd_valid_arms);
}
}
/********************************************************************************
* Print details for the physical drive at chn/targ in a format suitable for
* human consumption.
*
* <type>CCTT (<state>) "<vendor>/<model>"
* ????MB <features>
*
*/
static void
print_phys_drive(struct conf_config *conf, int chn, int targ)
{
struct mlx_phys_drv *drv = &conf->cc_cfg.cc_phys_drives[chn * 16 + targ];
/* if the drive isn't present, don't print it */
if (!(drv->pd_flags1 & MLX_PHYS_DRV_PRESENT))
return;
mlx_print_phys_drv(drv, chn, targ, "# ", 1);
}

View File

@ -0,0 +1,292 @@
/*-
* Copyright (c) 1999 Michael Smith
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <cam/scsi/scsi_all.h>
#if 0
#include <sys/mlxio.h>
#include <sys/mlxreg.h>
#else
#include "../sys/dev/mlx/mlxio.h"
#include "../sys/dev/mlx/mlxreg.h"
#endif
#include "mlxcontrol.h"
/********************************************************************************
* Iterate over all mlx devices, call (func) with each ones' path and (arg)
*/
void
mlx_foreach(void (*func)(int unit, void *arg), void *arg)
{
int i, fd;
/* limit total count for sanity */
for (i = 0; i < 64; i++) {
/* verify we can open it */
if ((fd = open(ctrlrpath(i), 0)) >= 0)
close(fd);
/* if we can, do */
if (fd >= 0) {
func(i, arg);
}
}
}
/********************************************************************************
* Open the controller (unit) and give the fd to (func) along with (arg)
*/
void
mlx_perform(int unit, void (*func)(int fd, void *arg), void *arg)
{
int fd;
if ((fd = open(ctrlrpath(unit), 0)) >= 0) {
func(fd, arg);
close(fd);
}
}
/********************************************************************************
* Iterate over all mlxd devices, call (func) with each ones' path and (arg)
*/
void
mlxd_foreach_ctrlr(int unit, void *arg)
{
struct mlxd_foreach_action *ma = (struct mlxd_foreach_action *)arg;
int i, fd;
/* Get the device */
if ((fd = open(ctrlrpath(unit), 0)) < 0)
return;
for (i = -1; ;) {
/* Get the unit number of the next child device */
if (ioctl(fd, MLX_NEXT_CHILD, &i) < 0)
return;
/* check that we can open this unit */
if ((fd = open(drivepath(i), 0)) >= 0)
close(fd);
/* if we can, do */
if (fd >= 0) {
ma->func(i, ma->arg);
}
}
}
void
mlxd_foreach(void (*func)(int unit, void *arg), void *arg)
{
struct mlxd_foreach_action ma;
ma.func = func;
ma.arg = arg;
mlx_foreach(mlxd_foreach_ctrlr, &ma);
}
/********************************************************************************
* Find the controller that manages the drive (unit), return controller number
* and system drive number on that controller.
*/
static struct
{
int unit;
int ctrlr;
int sysdrive;
} mlxd_find_ctrlr_param;
static void
mlxd_find_ctrlr_search(int unit, void *arg)
{
int i, fd;
/* Get the device */
if ((fd = open(ctrlrpath(unit), 0)) >= 0) {
for (i = -1; ;) {
/* Get the unit number of the next child device */
if (ioctl(fd, MLX_NEXT_CHILD, &i) < 0)
break;
/* is this child the unit we want? */
if (i == mlxd_find_ctrlr_param.unit) {
mlxd_find_ctrlr_param.ctrlr = unit;
if (ioctl(fd, MLX_GET_SYSDRIVE, &i) == 0)
mlxd_find_ctrlr_param.sysdrive = i;
}
}
close(fd);
}
}
int
mlxd_find_ctrlr(int unit, int *ctrlr, int *sysdrive)
{
mlxd_find_ctrlr_param.unit = unit;
mlxd_find_ctrlr_param.ctrlr = -1;
mlxd_find_ctrlr_param.sysdrive = -1;
mlx_foreach(mlxd_find_ctrlr_search, NULL);
if ((mlxd_find_ctrlr_param.ctrlr != -1) && (mlxd_find_ctrlr_param.sysdrive != -1)) {
*ctrlr = mlxd_find_ctrlr_param.ctrlr;
*sysdrive = mlxd_find_ctrlr_param.sysdrive;
return(0);
}
return(1);
}
/********************************************************************************
* Send a command to the controller on (fd)
*/
void
mlx_command(int fd, void *arg)
{
struct mlx_usercommand *cmd = (struct mlx_usercommand *)arg;
int error;
error = ioctl(fd, MLX_COMMAND, cmd);
if (error != 0)
cmd->mu_error = error;
}
/********************************************************************************
* Perform an ENQUIRY2 command and return information related to the controller
* (unit)
*/
int
mlx_enquiry(int unit, struct mlx_enquiry2 *enq)
{
struct mlx_usercommand cmd;
/* build the command */
cmd.mu_datasize = sizeof(*enq);
cmd.mu_buf = enq;
cmd.mu_bufptr = 8;
cmd.mu_command[0] = MLX_CMD_ENQUIRY2;
/* hand it off for processing */
mlx_perform(unit, mlx_command, (void *)&cmd);
return(cmd.mu_status != 0);
}
/********************************************************************************
* Perform a READ CONFIGURATION command and return information related to the controller
* (unit)
*/
int
mlx_read_configuration(int unit, struct mlx_core_cfg *cfg)
{
struct mlx_usercommand cmd;
/* build the command */
cmd.mu_datasize = sizeof(*cfg);
cmd.mu_buf = cfg;
cmd.mu_bufptr = 8;
cmd.mu_command[0] = MLX_CMD_READ_CONFIG;
/* hand it off for processing */
mlx_perform(unit, mlx_command, (void *)&cmd);
return(cmd.mu_status != 0);
}
/********************************************************************************
* Perform a SCSI INQUIRY command and return pointers to the relevant data.
*/
int
mlx_scsi_inquiry(int unit, int channel, int target, char **vendor, char **device, char **revision)
{
struct mlx_usercommand cmd;
static struct {
struct mlx_dcdb dcdb;
union {
struct scsi_inquiry_data inq;
u_int8_t pad[SHORT_INQUIRY_LENGTH];
} d;
} __attribute__ ((packed)) dcdb_cmd;
struct scsi_inquiry *inq_cmd = (struct scsi_inquiry *)&dcdb_cmd.dcdb.dcdb_cdb[0];
/* build the command */
cmd.mu_datasize = sizeof(dcdb_cmd);
cmd.mu_buf = &dcdb_cmd;
cmd.mu_command[0] = MLX_CMD_DIRECT_CDB;
/* build the DCDB */
bzero(&dcdb_cmd, sizeof(dcdb_cmd));
dcdb_cmd.dcdb.dcdb_channel = channel;
dcdb_cmd.dcdb.dcdb_target = target;
dcdb_cmd.dcdb.dcdb_flags = MLX_DCDB_DATA_IN | MLX_DCDB_TIMEOUT_10S;
dcdb_cmd.dcdb.dcdb_datasize = SHORT_INQUIRY_LENGTH;
dcdb_cmd.dcdb.dcdb_cdb_length = 6;
dcdb_cmd.dcdb.dcdb_sense_length = SSD_FULL_SIZE;
/* build the cdb */
inq_cmd->opcode = INQUIRY;
inq_cmd->length = SHORT_INQUIRY_LENGTH;
/* hand it off for processing */
mlx_perform(unit, mlx_command, &cmd);
if (cmd.mu_status == 0) {
*vendor = &dcdb_cmd.d.inq.vendor[0];
*device = &dcdb_cmd.d.inq.product[0];
*revision = &dcdb_cmd.d.inq.revision[0];
}
return(cmd.mu_status);
}
/********************************************************************************
* Perform a GET DEVICE STATE command and return pointers to the relevant data.
*/
int
mlx_get_device_state(int unit, int channel, int target, struct mlx_phys_drv *drv)
{
struct mlx_usercommand cmd;
/* build the command */
cmd.mu_datasize = sizeof(*drv);
cmd.mu_buf = drv;
cmd.mu_bufptr = 8;
cmd.mu_command[0] = MLX_CMD_DEVICE_STATE;
cmd.mu_command[2] = channel;
cmd.mu_command[3] = target;
/* hand it off for processing */
mlx_perform(unit, mlx_command, (void *)&cmd);
return(cmd.mu_status != 0);
}

View File

@ -0,0 +1,116 @@
.\"
.\" Copyright (c) 2000 Michael Smith
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd April 10, 2000
.Dt MLXCONTROL 8
.Os
.Sh NAME
.Nm mlxcontrol
.Nd Mylex DAC-family RAID management utility
.Sh SYNOPSIS
.Nm mlxcontrol
.Aq command
.Op args
.Nm mlxcontrol
status
.Op Fl qv
.Op Ar drive
.Nm mlxcontrol
rescan
.Ar controller
.Op Ar controller ...
.Nm mlxcontrol
detach
.Ar drive
.Op Ar drive ...
.Nm mlxcontrol
detach
.Fl a
.Nm mlxcontrol
check
.Ar drive
.Nm mlxcontrol
config
.Ar controller
.Nm mlxcontrol
help
.Ar command
.Sh DESCRIPTION
The
.Nm
utility provides status monitoring and management functions for devices attached
to the
.Xr mlx 4
driver.
.Pp
Controller names are of the form "mlxN" where N is the unit number of the controller.
Drive names are of the form "mlxdN" where N is the unit number of the drive. Do not
specify the path to a device node.
.Pp
.Bl -tag -width status
.It status
Print the status of controllers and system drives. If one or more drives are specified,
only print information about these drives, otherwise print information about all controllers
and drives in the system. With the
.Fl v
flag, display much more verbose information.
With the
.Fl q
flag, do not print any output. This command returns 0 if all drives tested are online, 1
if one or more drives are critical and 2 if one or more are offline.
.It rescan
Rescan one or more controllers for non-attached system drives (eg. drives that have been
detached or created subsequent to driver initialisation). If the
.Fl a
flag is supplied, rescan all controllers in the system.
.It detach
Detach one or more system drives. Drives must be unmounted and not opened by any other
utility for this command to succeed. If the
.Fl a
flag is supplied, detach all system drives from the nominated controller.
.It check
Initiate a consistency check and repair pass on a redundant system drive (eg. RAID1 or RAID5).
The controller will scan the system drive and repair any inconsistencies. This command
returns immediately; use the
.Ar status
command to monitor the progress of the check.
.It config
Print the current configuration from the nominated controller. This command will be
updated to allow addition/deletion of system drives from a configuration in a future
release.
.It help
Print usage information for
.Ar command.
.Sh BUGS
The
.Ar config
command does not yet support modifying system drive configuration.
.Pp
Error log extraction is not yet supported.
.Sh AUTHORS
The mlxcontrol utility was written by
.An Michael Smith
.Aq msmith@FreeBSD.org .

View File

@ -0,0 +1,89 @@
/*-
* Copyright (c) 1999 Michael Smith
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/queue.h>
#define debug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args)
struct mlxd_foreach_action
{
void (*func)(int unit, void *arg);
void *arg;
};
extern void mlx_foreach(void (*func)(int unit, void *arg), void *arg);
void mlxd_foreach_ctrlr(int unit, void *arg);
extern void mlxd_foreach(void (*func)(int unit, void *arg), void *arg);
extern int mlxd_find_ctrlr(int unit, int *ctrlr, int *sysdrive);
extern void mlx_perform(int unit, void (*func)(int fd, void *arg), void *arg);
extern void mlx_command(int fd, void *arg);
extern int mlx_enquiry(int unit, struct mlx_enquiry2 *enq);
extern int mlx_read_configuration(int unit, struct mlx_core_cfg *cfg);
extern int mlx_scsi_inquiry(int unit, int bus, int target, char **vendor, char **device, char **revision);
extern int mlx_get_device_state(int fd, int channel, int target, struct mlx_phys_drv *drv);
extern char *ctrlrpath(int unit);
extern char *ctrlrname(int unit);
extern char *drivepath(int unit);
extern char *drivename(int unit);
extern int ctrlrunit(char *str);
extern int driveunit(char *str);
extern void mlx_print_phys_drv(struct mlx_phys_drv *drv, int channel, int target, char *prefix, int verbose);
struct conf_phys_drv
{
TAILQ_ENTRY(conf_phys_drv) pd_link;
int pd_bus;
int pd_target;
struct mlx_phys_drv pd_drv;
};
struct conf_span
{
TAILQ_ENTRY(conf_span) s_link;
struct conf_phys_drv *s_drvs[8];
struct mlx_sys_drv_span s_span;
};
struct conf_sys_drv
{
TAILQ_ENTRY(conf_sys_drv) sd_link;
struct conf_span *sd_spans[4];
struct mlx_sys_drv sd_drv;
};
struct conf_config
{
TAILQ_HEAD(,conf_phys_drv) cc_phys_drvs;
TAILQ_HEAD(,conf_span) cc_spans;
TAILQ_HEAD(,conf_sys_drv) cc_sys_drvs;
struct conf_sys_drv *cc_drives[32];
struct mlx_core_cfg cc_cfg;
};

177
usr.sbin/mlxcontrol/util.c Normal file
View File

@ -0,0 +1,177 @@
/*-
* Copyright (c) 1999 Michael Smith
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <stdio.h>
#include <paths.h>
#include <string.h>
#if 0
#include <sys/mlxio.h>
#include <sys/mlxreg.h>
#else
#include "../sys/dev/mlx/mlxio.h"
#include "../sys/dev/mlx/mlxreg.h"
#endif
#include "mlxcontrol.h"
/********************************************************************************
* Various name-producing and -parsing functions
*/
/* return path of controller (unit) */
char *
ctrlrpath(int unit)
{
static char buf[32];
sprintf(buf, "%s%s", _PATH_DEV, ctrlrname(unit));
return(buf);
}
/* return name of controller (unit) */
char *
ctrlrname(int unit)
{
static char buf[32];
sprintf(buf, "mlx%d", unit);
return(buf);
}
/* return path of drive (unit) */
char *
drivepath(int unit)
{
static char buf[32];
sprintf(buf, "%s%s", _PATH_DEV, drivename(unit));
return(buf);
}
/* return name of drive (unit) */
char *
drivename(int unit)
{
static char buf[32];
sprintf(buf, "mlxd%d", unit);
return(buf);
}
/* get controller unit number from name in (str) */
int
ctrlrunit(char *str)
{
int unit;
if (sscanf(str, "mlx%d", &unit) == 1)
return(unit);
return(-1);
}
/* get drive unit number from name in (str) */
int
driveunit(char *str)
{
int unit;
if (sscanf(str, "mlxd%d", &unit) == 1)
return(unit);
return(-1);
}
/********************************************************************************
* Standardised output of various data structures.
*/
void
mlx_print_phys_drv(struct mlx_phys_drv *drv, int chn, int targ, char *prefix, int verbose)
{
char *type, *device, *vendor, *revision;
switch(drv->pd_flags2 & 0x03) {
case MLX_PHYS_DRV_DISK:
type = "disk";
break;
case MLX_PHYS_DRV_SEQUENTIAL:
type = "tape";
break;
case MLX_PHYS_DRV_CDROM:
type= "cdrom";
break;
case MLX_PHYS_DRV_OTHER:
default:
type = "unknown";
break;
}
printf("%s%s%02d%02d ", prefix, type, chn, targ);
switch(drv->pd_status) {
case MLX_PHYS_DRV_DEAD:
printf(" (dead) ");
break;
case MLX_PHYS_DRV_WRONLY:
printf(" (write-only) ");
break;
case MLX_PHYS_DRV_ONLINE:
printf(" (online) ");
break;
case MLX_PHYS_DRV_STANDBY:
printf(" (standby) ");
break;
default:
printf(" (0x%02x) ", drv->pd_status);
}
printf("\n");
if (verbose) {
printf("%s ", prefix);
if (!mlx_scsi_inquiry(0, chn, targ, &vendor, &device, &revision)) {
printf("'%8.8s' '%16.16s' '%4.4s'", vendor, device, revision);
} else {
printf("<IDENTIFY FAILED>");
}
printf(" %dMB ", drv->pd_config_size / 2048);
if (drv->pd_flags2 & MLX_PHYS_DRV_FAST20) {
printf(" fast20");
} else if (drv->pd_flags2 & MLX_PHYS_DRV_FAST) {
printf(" fast");
}
if (drv->pd_flags2 & MLX_PHYS_DRV_WIDE)
printf(" wide");
if (drv->pd_flags2 & MLX_PHYS_DRV_SYNC)
printf(" sync");
if (drv->pd_flags2 & MLX_PHYS_DRV_TAG)
printf(" tag-enabled");
printf("\n");
}
}