summaryrefslogtreecommitdiff
path: root/sbin/bioctl/bioctl.c
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2005-07-18 15:10:58 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2005-07-18 15:10:58 +0000
commit69c687924e0aac6b65efd331cab86c86bfb61fc4 (patch)
tree4940df3520abad4620c539904122cae61ba9c405 /sbin/bioctl/bioctl.c
parent266c19464df004208d3eb2fc4d421579a43b28cd (diff)
revert marcos bio changes, mickey not ok
Diffstat (limited to 'sbin/bioctl/bioctl.c')
-rw-r--r--sbin/bioctl/bioctl.c1018
1 files changed, 961 insertions, 57 deletions
diff --git a/sbin/bioctl/bioctl.c b/sbin/bioctl/bioctl.c
index d3067e1d739..1395715eb61 100644
--- a/sbin/bioctl/bioctl.c
+++ b/sbin/bioctl/bioctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bioctl.c,v 1.14 2005/07/18 14:34:41 jmc Exp $ */
+/* $OpenBSD: bioctl.c,v 1.15 2005/07/18 15:10:56 dlg Exp $ */
/*
* Copyright (c) 2004, 2005 Marco Peereboom
* All rights reserved.
@@ -26,6 +26,13 @@
*
*/
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util.h>
+
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/queue.h>
@@ -34,20 +41,18 @@
#include <scsi/scsi_ses.h>
#include <dev/biovar.h>
-#include <errno.h>
-#include <err.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <util.h>
-
#include "bioctl.h"
/* globals */
const char *bio_device = "/dev/bio";
+SLIST_HEAD(dev_list, dev);
+
+/* RAID card device list */
+struct dev_list devices = SLIST_HEAD_INITIALIZER(dev);
+/* User provided device list*/
+struct dev_list ul = SLIST_HEAD_INITIALIZER(dev);
+
int devh = -1;
int debug = 0;
@@ -58,37 +63,79 @@ main(int argc, char *argv[])
{
extern char *optarg;
- u_int64_t func = 0;
- /* u_int64_t subfunc = 0; */
+ bioc_capabilities bc;
+ bioc_alarm ba;
int ch;
int rv;
+ unsigned char *pl;
char *bioc_dev = NULL;
char *sd_dev = NULL;
char *realname = NULL;
+ char *al_arg = NULL; /* argument to alarm */
+ char *ss_arg = NULL; /* argument to start/stop */
+ char inq[INQSIZE];
+
+ struct dev *delm;
+
+ u_int64_t func = 0, subfunc = 0;
+ u_int32_t devlist = 0;
if (argc < 2)
usage();
atexit(cleanup);
- while ((ch = getopt(argc, argv, "Dd:f:hi")) != -1) {
+ while ((ch = getopt(argc, argv, "a:b:Dd:ef:hl:pst:u:")) != -1) {
switch (ch) {
- case 'D': /* debug */
+ case 'a': /* alarm */
+ func |= BIOC_ALARM;
+ al_arg = optarg;
+ break;
+ case 'b': /* LED blink/unblink */
+ func |= BIOC_BLINK;
+ al_arg = optarg;
+ break;
+
+ case 'D': /* enable debug */
debug = 1;
break;
- case 'd': /* bio device */
+ case 'd': /* device */
bioc_dev = optarg;
break;
- case 'f': /* scsi device */
+ case 'e': /* enumerate */
+ func |= BIOC_SCSICMD;
+ subfunc |= F_ENUM;
+ break;
+
+ case 'f': /* device */
sd_dev = optarg;
break;
- case 'i': /* inquiry */
- func |= BIOC_INQ;
+ case 'l': /* device list, separated for now use one dev only*/
+ func |= PARSELIST;
+ pl = optarg;
+ break;
+
+ case 'p': /* ping */
+ func |= BIOC_PING;
+ break;
+
+ case 's': /* status */
+ func |= BIOC_STATUS;
+ break;
+
+ case 't':
+ func |= BIOC_SCSICMD;
+ subfunc |= parse_passthru(optarg);
+ break;
+
+ case 'u': /* start/stop */
+ func |= BIOC_STARTSTOP;
+ ss_arg = optarg;
break;
case 'h': /* help/usage */
@@ -124,8 +171,80 @@ main(int argc, char *argv[])
if (debug)
warnx("cookie = %p", bl.cookie);
- if (func & BIOC_INQ) {
- bio_inq();
+ if (func & PARSELIST)
+ parse_devlist(pl);
+
+ if (!bio_get_capabilities(&bc))
+ warnx("could not retrieve capabilities.");
+ else {
+ /* we should have everything setup by now so get to work */
+ if (func & BIOC_ALARM)
+ if (bc.ioctls & BIOC_ALARM)
+ bio_alarm(al_arg);
+ else
+ warnx("alarms are not supported.");
+
+ if (func & BIOC_BLINK)
+ if (bc.ioctls & BIOC_BLINK)
+ SLIST_FOREACH(delm, &ul, next) {
+ bio_blink(al_arg, delm->channel,
+ delm->target);
+ }
+ else
+ warnx("blink is not supported.");
+
+ if (func & BIOC_PING)
+ if (bc.ioctls & BIOC_PING)
+ bio_ping();
+ else
+ warnx("ping not supported.");
+
+ if (func & BIOC_STATUS) {
+ if (bc.ioctls & BIOC_STATUS)
+ bio_status();
+ else
+ warnx("status function not supported.");
+ }
+
+ if (func & BIOC_STARTSTOP) {
+ if (bc.ioctls & BIOC_STARTSTOP) {
+ SLIST_FOREACH(delm, &ul, next) {
+ bio_startstop(ss_arg,
+ delm->channel, delm->target);
+ }
+ } else
+ warnx("start/stop unit not supported.");
+ }
+
+ if (func & BIOC_SCSICMD) {
+ if (bc.ioctls & BIOC_SCSICMD) {
+ if (subfunc & F_READCAP) {
+ SLIST_FOREACH(delm, &ul, next) {
+ bio_pt_readcap(delm->channel,
+ delm->target, F_NOISY);
+ }
+ }
+
+ if (subfunc & F_INQUIRY) {
+ SLIST_FOREACH(delm, &ul, next) {
+ bio_pt_inquire(delm->channel,
+ delm->target, F_NOISY,
+ &inq[0]);
+ }
+ }
+
+ if (subfunc & F_TUR) {
+ SLIST_FOREACH(delm, &ul, next) {
+ bio_pt_tur(delm->channel,
+ delm->target);
+ }
+ }
+
+ if (subfunc & F_ENUM)
+ bio_pt_enum();
+ } else
+ warnx("passthrough not supported.");
+ }
}
return (0);
@@ -136,7 +255,9 @@ usage(void)
{
extern char *__progname;
- fprintf(stderr, "usage: %s [-Dhi] -d device | -f device\n", __progname);
+ fprintf(stderr, "usage: %s [-Dehpt] [-a function] [-b function] "
+ "[-l device list]\n"
+ "\t[-u function] [-d device | -f disk]\n", __progname);
exit(1);
}
@@ -144,72 +265,855 @@ usage(void)
void
cleanup(void)
{
+ struct dev *delm;
+
if (debug)
printf("atexit\n");
+ while (devices.slh_first != NULL) {
+ delm = devices.slh_first;
+ SLIST_REMOVE_HEAD(&devices, next);
+ if (debug)
+ printf("free device: %p\n", delm);
+ free(delm);
+ }
+
+ while (ul.slh_first != NULL) {
+ delm = ul.slh_first;
+ SLIST_REMOVE_HEAD(&ul, next);
+ if (debug)
+ printf("free ul: %p\n", delm);
+ free(delm);
+ }
+
if (devh != -1)
close(devh);
}
+u_int64_t
+parse_passthru(char *f)
+{
+ if (debug)
+ printf("get_subfunc: %s, ", f);
+
+ switch (f[0]) {
+ case 'i': /* INQUIRY */
+ if (debug)
+ printf("inquiry\n");
+ return (F_INQUIRY);
+
+ case 'e': /* ENUMERATE, not a pass through hmmm */
+ if (debug)
+ printf("enumerate\n");
+ return (F_ENUM);
+
+ case 'r': /* READ CAPACITY */
+ if (debug)
+ printf("read cap\n");
+ return (F_READCAP);
+
+ case 't': /* TUR */
+ if (debug)
+ printf("TUR\n");
+ return (F_TUR);
+
+ default:
+ errx(1, "invalid pass through function");
+ }
+}
+
void
-bio_inq(void)
+parse_devlist(char *dl)
{
- bioc_inq bi;
- bioc_vol bv;
- bioc_disk bd;
+ u_int8_t c , t, done = 0;
+ char *es, *s;
+ struct dev *delm;
+
+ es = NULL;
+ s = dl;
- int rv, i, d;
+ if (debug)
+ printf("parse: %s\n", dl);
- memset(&bi, 0, sizeof(bi));
+ while (!done) {
+ c = strtol(s, &es, 10);
+ if (debug)
+ printf("%p %p %u %c\n", s, es, c, es[0]);
+ s = es;
+ if (es[0] == ':') {
+ s++;
+ t = strtol(s, &es, 10);
+ if (debug)
+ printf("%p %p %u %c\n", s, es, t, es[0]);
+ s = es;
+
+ if (c > 4)
+ errx(1, "invalid channel number");
+ if (t > 16)
+ errx(1, "invalid target number");
+
+ delm = malloc(sizeof(struct dev));
+ if (!delm)
+ errx(1, "not enough memory");
+
+ delm->target = t;
+ delm->channel = c;
+ SLIST_INSERT_HEAD(&ul, delm, next);
+ }
+ if (es[0] == ',') {
+ s++;
+ continue;
+ }
+ if (es[0] == '\0') {
+ done = 1;
+ continue;
+ }
+ done = 2;
+ }
+
+ if (done == 2) {
+ /* boink */
+ errx(1, "invalid device list.");
+ }
+}
+
+int
+bio_get_capabilities(bioc_capabilities *bc)
+{
+ int rv;
+
+ bc->cookie = bl.cookie;
+ rv = ioctl(devh, BIOCCAPABILITIES, bc);
+ if (rv == -1) {
+ warnx("Error calling bioc_ioctl() via bio_ioctl()");
+ return 0;
+ }
+
+ if (debug) {
+ printf("ioctls = %016llx\n", bc->ioctls);
+ printf("raid_types = %08lx\n", bc->raid_types);
+ }
+
+ return (1);
+}
+
+void
+bio_alarm(char *arg)
+{
+ int rv;
+ bioc_alarm ba;
if (debug)
- printf("bio_inq\n");
+ printf("alarm in: %s, ", arg);
+
+ ba.cookie = bl.cookie;
- bi.cookie = bl.cookie;
+ switch (arg[0]) {
+ case 'q': /* silence alarm */
+ /* FALLTHROUGH */
+ case 's':
+ if (debug)
+ printf("silence\n");
+ ba.opcode = BIOCSALARM_SILENCE;
+ break;
- rv = ioctl(devh, BIOCINQ, &bi);
+ case 'e': /* enable alarm */
+ if (debug)
+ printf("enable\n");
+ ba.opcode = BIOCSALARM_ENABLE;
+ break;
+
+ case 'd': /* disable alarm */
+ if (debug)
+ printf("disable\n");
+ ba.opcode = BIOCSALARM_DISABLE;
+ break;
+
+ case 't': /* test alarm */
+ if (debug)
+ printf("test\n");
+ ba.opcode = BIOCSALARM_TEST;
+ break;
+
+ case 'g': /* get alarm state */
+ if (debug)
+ printf("get state\n");
+ ba.opcode = BIOCGALARM_STATE;
+ break;
+
+ default:
+ warnx("invalid alarm function: %s", arg);
+ return;
+ }
+
+ rv = ioctl(devh, BIOCALARM, &ba);
if (rv == -1) {
warnx("bioc_ioctl() call failed");
return;
}
- printf("RAID volumes : %d\n", bi.novol);
- printf("Physical disks : %d\n\n", bi.nodisk);
+ if (arg[0] == 'g') {
+ printf("alarm is currently %s\n",
+ ba.state ? "enabled" : "disabled");
+ }
+}
+
+void
+ses_verbose(u_int8_t *rc, u_int8_t len)
+{
+ struct ses_config_page *scp;
+ struct ses_type_desc_hdr *tdh;
+
+ char *str;
+ u_int8_t i;
- for (i = 0; i < bi.novol; i++) {
- memset(&bv, 0, sizeof(bv));
- bv.cookie = bl.cookie;
- bv.volid = i;
+ scp = (struct ses_config_page *)rc;
+ printf("element types: %d, id: %s\n", scp->nr_elem_typ,
+ scp->enc_vendor_id);
- rv = ioctl(devh, BIOCVOL, &bv);
- if (rv == -1) {
- warnx("bioc_ioctl() call failed");
+ str = (char *)
+ (&scp->enc_desc_len + scp->enc_desc_len + 1 +
+ (scp->nr_elem_typ * sizeof(struct ses_type_desc_hdr)));
+
+ for (i = 0; i < scp->nr_elem_typ; i++) {
+ tdh = (struct ses_type_desc_hdr *)
+ (&scp->enc_desc_len + scp->enc_desc_len + 1 +
+ (i * sizeof(struct ses_type_desc_hdr)));
+
+ printf("type: %d, count: %d, sub enclosure id: %d, "
+ "len: %d, text: %s\n",
+ tdh->elem_type, tdh->nr_elem, tdh->sub_enc_id,
+ tdh->type_desc_len, str);
+
+ str += tdh->type_desc_len;
+ }
+}
+
+void
+bio_blink_userland(u_int8_t opc, u_int8_t c, u_int8_t t)
+{
+ struct dev *delm;
+
+ /* page 1 stuff */
+ struct ses_enc_ctrl_diag_page *cdp;
+ struct ses_config_page *scp;
+ struct ses_type_desc_hdr *tdh;
+
+ /* page 2 stuff */
+ struct ses_enc_stat_diag_page *esdp;
+ struct ses_dev_elmt_status_diag *desd;
+
+ u_int8_t rc[SESSIZE];
+ u_int8_t rc2[SESSIZE];
+ u_int8_t i, elements, found = 0;
+
+ /* FIXME if the raid controllers are clustered we might have more
+ * than one proc device. */
+
+ bio_pt_enum();
+
+ SLIST_FOREACH(delm, &devices, next) {
+ if (delm->channel != c)
+ continue;
+
+ if (delm->type != T_PROCESSOR)
+ continue;
+
+ if (debug)
+ printf("proc at channel: %d target: %2d\n",
+ delm->channel, delm->target);
+
+ /* figure out what we have */
+ if (!get_ses_page(delm->channel, delm->target,
+ SES_CFG_DIAG_PAGE, &rc[0], sizeof(rc))) {
return;
}
- printf("\tvolume id: %d\n", bv.volid);
- printf("\tstatus : %d\n", bv.status);
- printf("\tsize : %lld\n", bv.size);
- printf("\traid : %d\n", bv.level);
- printf("\tnr disks : %d\n", bv.nodisk);
+ if (debug)
+ ses_verbose(&rc[0], sizeof(rc));
- for (d = 0; d < bv.nodisk; d++) {
- memset(&bd, 0, sizeof(bd));
- bd.cookie = bl.cookie;
- bd.diskid = 0;
+ /* find first disk element */
+ elements = 0;
+ scp = (struct ses_config_page *)rc;
+ for (i = 0; i < scp->nr_elem_typ; i++) {
+ tdh = (struct ses_type_desc_hdr *)
+ (&scp->enc_desc_len + scp->enc_desc_len + 1 +
+ (i * sizeof(struct ses_type_desc_hdr)));
- rv = ioctl(devh, BIOCDISK, &bd);
- if (rv == -1) {
- warnx("bioc_ioctl() call failed");
- return;
+ if (tdh->elem_type == STDH_DEVICE) {
+ found = 1;
+ break;
}
+ elements += tdh->nr_elem;
+ }
- printf("\t\tdisk id : %d\n", bd.diskid);
- printf("\t\tstatus : %d\n", bd.status);
- printf("\t\tvolume id: %d\n", bd.volid);
- printf("\t\tsize : %lld\n", bd.size);
- printf("\t\tvendor : %s\n", bd.vendor);
+ if (debug) {
+ printf("tdh->elem_type: %d, tdh->nr_elem: %d, "
+ "elements: %d\n",
+ tdh->elem_type, tdh->nr_elem, elements);
}
+
+ if (!found) {
+ if (debug)
+ printf("no devices found\n");
+
+ return;
+ }
+
+ /* get ses page so that we can modify bits for blink */
+ if (!get_ses_page(delm->channel, delm->target,
+ SES_CTRL_DIAG_PAGE, &rc2[0], sizeof(rc2))) {
+ return;
+ }
+
+ esdp = (struct ses_enc_stat_diag_page *)rc2;
+ desd = (struct ses_dev_elmt_status_diag *)
+ (esdp->elmts + elements); /* FIXME do we need padding? */
+
+ /* loop through all slots to see if target is available */
+ found = 0;
+ for (i = 0; i < tdh->nr_elem; i++) {
+ if (debug)
+ printf("stat: %d, addr: %d, b3: %d, b4: %d\n",
+ desd->common_status,
+ desd->slot_addr,
+ desd->byte3,
+ desd->byte4);
+
+ if (t == desd->slot_addr) {
+ found = 1;
+ break;
+ }
+
+ desd += 1; /* next element */
+ }
+
+ if (!found) {
+ printf("target: %d not found\n", t);
+
+ return;
+ }
+
+ cdp = (struct ses_enc_ctrl_diag_page *)rc2;
+ cdp->elmts[i].common_ctrl = SDECD_SELECT;
+ switch (opc) {
+ case BIOCSBLINK_ALERT:
+ cdp->elmts[i].byte4 = SDECD_RQST_FAULT;
+ cdp->elmts[i].byte3 = 0x00;
+ break;
+
+ case BIOCSBLINK_BLINK:
+ cdp->elmts[i].byte3 = SDECD_RQST_IDENT;
+ cdp->elmts[i].byte4 = 0x00;
+ break;
+
+ case BIOCSBLINK_UNBLINK:
+ cdp->elmts[i].byte3 = 0x00;
+ cdp->elmts[i].byte4 = 0x00;
+ break;
+
+ default:
+ return;
+ }
+
+ if (!set_ses_page(delm->channel, delm->target,
+ &rc2[0], sizeof(rc2))) {
+ return;
+ }
+
+ return; /* done */
+ }
+}
+
+void
+bio_blink(char * arg, u_int8_t c, u_int8_t t)
+{
+ int rv;
+ bioc_blink bb;
+
+ if (debug)
+ printf("blink in: %s, ", arg);
+
+ bb.cookie = bl.cookie;
+
+ switch (arg[0]) {
+ case 'a': /* blink amber or alert led */
+ if (debug)
+ printf("blink alert\n");
+ bb.opcode = BIOCSBLINK_ALERT;
+ break;
+
+ case 'b': /* blink hdd */
+ if (debug)
+ printf("blink\n");
+ bb.opcode = BIOCSBLINK_BLINK;
+ break;
+
+ case 'u': /* unblink hdd */
+ if (debug)
+ printf("unblink\n");
+ bb.opcode = BIOCSBLINK_UNBLINK;
+ break;
+
+ default:
+ warnx("invalid blink function: %s", arg);
+ return;
+ }
+
+ rv = ioctl(devh, BIOCBLINK, &bb);
+ if (rv == -1) {
+ if (errno == EOPNOTSUPP) {
+ /* operation is not supported in kernel, do it here */
+ if (debug)
+ printf("doing blink in userland\n");
+ bio_blink_userland(bb.opcode, c, t);
+ }
+ else
+ warnx("bioc_ioctl() call failed");
+ }
+}
+
+void
+bio_ping(void)
+{
+ int rv;
+ bioc_ping bp;
+
+ bp.cookie = bl.cookie;
+ bp.x = 0;
+ rv = ioctl(devh, BIOCPING, &bp);
+ if (rv == -1) {
+ warnx("Error calling bioc_ioctl() via bio_ioctl()");
+ return;
+ }
+
+ printf("x after ioctl() = %i\n", bp.x);
+}
+
+void
+bio_startstop(char *arg, u_int8_t c, u_int8_t t)
+{
+ int rv;
+ bioc_startstop bs;
+
+ if (debug)
+ printf("startstop in: %s, ", arg);
+
+ bs.cookie = bl.cookie;
+
+ switch (arg[0]) {
+ case 's': /* stop unit */
+ if (debug)
+ printf("stop\n");
+ bs.opcode = BIOCSUNIT_STOP;
+ break;
+
+ case 'g': /* start or go unit */
+ if (debug)
+ printf("start\n");
+ bs.opcode = BIOCSUNIT_START;
+ break;
+
+ default:
+ warnx("invalid start/stop function: %s", arg);
+ return;
+ }
+
+ bs.channel = c;
+ bs.target = t;
+
+ rv = ioctl(devh, BIOCSTARTSTOP, &bs);
+ if (rv == -1) {
+ warnx("bioc_ioctl() call failed");
+ return;
+ }
+
+ if (debug)
+ printf("startstop done\n");
+}
+
+/* get status, for now only do all */
+void
+bio_status(void)
+{
+ int rv;
+ bioc_status bs;
+
+ if (debug)
+ printf("status()\n");
+
+ bs.cookie = bl.cookie;
+ bs.opcode = BIOCGSTAT_ALL;
+
+ rv = ioctl(devh, BIOCSTATUS, &bs);
+ if (rv == -1) {
+ warnx("bioc_ioctl() call failed");
+ return;
+ }
+
+ if (debug)
+ printf("status done\n");
+}
+
+/* read capacity for disk c,t */
+u_int64_t
+bio_pt_readcap(u_int8_t c, u_int8_t t, u_int8_t flags)
+{
+ bioc_scsicmd bpt;
+ struct read_cap rc;
+ int rv;
+ u_int64_t size;
+
+ memset(&bpt, 0, sizeof(bpt));
+ bpt.cookie = bl.cookie;
+ bpt.channel = c;
+ bpt.target = t;
+ bpt.cdblen = 10;
+ bpt.cdb[0] = READ_CAPACITY;
+ bpt.data = &rc; /* set up return data pointer */
+ bpt.datalen = sizeof(rc);
+ bpt.direction = BIOC_DIRIN;
+ bpt.senselen = 32; /* silly since the kernel overrides it */
+
+ rv = ioctl(devh, BIOCSCSICMD, &bpt);
+ if (rv == -1) {
+ warnx("READ CAPACITY failed %x", bpt.status);
+ return (0);
+ }
+ else if (bpt.status) {
+ if (bpt.sensebuf[0] == 0x70 || bpt.sensebuf[0] == 0x71)
+ print_sense(&bpt.sensebuf[0], bpt.senselen);
+ else
+ printf("channel: %d target: %2d READ CAPACITY failed "
+ "without sense data\n", c, t);
+
+ return (0);
+ }
+
+ rc.maxlba = betoh32(rc.maxlba);
+ rc.bsize = betoh32(rc.bsize);
+
+ size = (u_int64_t)rc.maxlba * (u_int64_t)rc.bsize;
+
+ if (debug)
+ printf("\nREAD CAPACITY: %lu * %lu = %llu\n",
+ rc.maxlba, rc.bsize, size);
+
+ if (flags & F_NOISY) {
+ printf("channel: %d target: %2d READ CAPACITY %llu", c, t,
+ size);
+ print_cap(size);
+ printf("\n");
+ }
+
+ return (size);
+}
+
+
+/* inquire device */
+u_int32_t
+bio_pt_inquire(u_int8_t c, u_int8_t t, u_int8_t flags, u_int8_t *inq)
+{
+ bioc_scsicmd bpt;
+ int rv, i;
+
+ memset(&bpt, 0, sizeof(bpt));
+ bpt.cookie = bl.cookie;
+ bpt.channel = c;
+ bpt.target = t;
+ bpt.cdblen = 6;
+ bpt.cdb[0] = INQUIRY;
+ bpt.cdb[4] = INQSIZE; /* LENGTH */
+ bpt.data = inq; /* set up return data pointer */
+ bpt.datalen = INQSIZE; /* minimum INQ size */
+ bpt.direction = BIOC_DIRIN;
+ bpt.senselen = 32; /* silly since the kernel overrides it */
+
+ rv = ioctl(devh, BIOCSCSICMD, &bpt);
+ if (rv == -1) {
+ warnx("INQUIRY failed %x", bpt.status);
+ return 0;
+ }
+ else if (bpt.status) {
+ if (bpt.sensebuf[0] == 0x70 || bpt.sensebuf[0] == 0x71)
+ print_sense(&bpt.sensebuf[0], bpt.senselen);
+ else
+ if (flags & F_NOISY)
+ printf("device %d:%d did not respond to "
+ "INQUIRY command\n", c, t);
+
+ return 0;
+ }
+
+ printf("channel: %u target: %2u ", c, t);
+ print_inquiry(flags, inq, bpt.datalen);
+
+ if (flags & F_NOISY)
printf("\n");
+
+ return 1;
+}
+
+/* TUR for disk c,t */
+u_int32_t
+bio_pt_tur(u_int8_t c, u_int8_t t)
+{
+ bioc_scsicmd bpt;
+ int rv;
+
+ if (debug)
+ printf("tur\n");
+
+ memset(&bpt, 0, sizeof(bpt));
+ bpt.cookie = bl.cookie;
+ bpt.channel = c;
+ bpt.target = t;
+ bpt.cdblen = 6;
+ bpt.cdb[0] = TEST_UNIT_READY;
+ bpt.direction = BIOC_DIRNONE;
+ rv = ioctl(devh, BIOCSCSICMD, &bpt);
+ if (rv == -1) {
+ warnx("passthrough failed");
+ return (0);
+ }
+
+ if (bpt.status) {
+ if (bpt.sensebuf[0] == 0x70 || bpt.sensebuf[0] == 0x71)
+ print_sense(&bpt.sensebuf[0], bpt.senselen);
+ else
+ printf("channel: %d target: %2d: TUR failed without "
+ "sense data\n", c, t);
+
+ return (0);
+ }
+
+ printf("channel: %d target: %2d: TUR completed\n", c, t);
+
+ return (1);
+}
+
+
+/* enumerate all disks */
+void
+bio_pt_enum(void)
+{
+ bioc_scsicmd bpt;
+ u_int32_t c, t, i, d;
+ int rv;
+ u_int8_t inq[INQSIZE];
+
+ struct dev *delm;
+
+ d = 0;
+ for (c = 0; c < 4 /* FIXME */; c++) {
+ for (t = 0; t < 16 /* FIXME */; t++) {
+ if (bio_pt_inquire(c, t, F_SILENCE, &inq[0])) {
+ if (inq[0] & SID_QUAL)
+ continue; /* invalid device */
+
+ delm = malloc(sizeof(struct dev));
+ if (delm == NULL)
+ errx(1, "not enough memory");
+ delm->id = d++;
+ delm->target = t;
+ delm->channel = c;
+ delm->type = inq[0];
+ if (delm->type == T_DIRECT) {
+ /* FIXME check the return value */
+ delm->capacity = bio_pt_readcap(
+ delm->channel, delm->target,
+ F_SILENCE);
+ print_cap(delm->capacity);
+ }
+ printf("\n");
+
+ SLIST_INSERT_HEAD(&devices, delm, next);
+ }
+ } /* for t */
+ } /* for c */
+}
+
+/* printf sense data */
+void
+print_sense(u_int8_t *sensebuf, u_int8_t sensebuflen)
+{
+ u_int8_t i;
+
+ if (debug)
+ printf("print_sense() %p, %u\n", sensebuf, sensebuflen);
+
+ for (i = 0; i < sensebuflen; i++) {
+ printf("%02x ", sensebuf[i]);
}
+ printf("\n");
+
+ /* FIXME add some pretty decoding here */
+}
+
+void
+print_inquiry(u_int8_t flags, u_int8_t *inq, u_int8_t inqlen)
+{
+ u_int8_t i;
+
+ if (inqlen < INQSIZE) {
+ /* INQUIRY shall return at least 36 bytes */
+ printf("invalid INQUIRY buffer size\n");
+ return;
+ }
+
+ if (SID_QUAL & inq[0]) {
+ printf("invalid device\n");
+ return;
+ }
+
+ switch (SID_TYPE & inq[0]) {
+ case T_DIRECT:
+ printf("disk ");
+ break;
+
+ case T_PROCESSOR:
+ printf("proc ");
+ break;
+
+ default:
+ printf("unsuported device type\n");
+ return;
+ }
+
+ for (i = 0; i < inqlen; i++) {
+ if (i < 8) {
+ if ((flags & F_NOISY) || debug)
+ printf("%02x ", inq[i]);
+ }
+ else
+ printf("%c", inq[i] < ' ' ? ' ' : inq[i]);
+ }
+}
+
+void print_cap(u_int64_t cap)
+{
+ if (cap / S_TERA > 1) {
+ printf(" %3llu TB", cap / S_TERA);
+ return;
+ }
+
+ if (cap / S_GIGA > 1) {
+ printf(" %3llu GB", cap / S_GIGA);
+ return;
+ }
+
+ if (cap / S_MEGA > 1) {
+ printf(" %3llu MB", cap / S_MEGA);
+ return;
+ }
+
+ if (cap / S_KILO > 1) {
+ printf(" %3llu MB", cap / S_KILO);
+ return;
+ }
+
+ printf(" %llu B", cap);
+}
+
+#if 0
+ /* in case we want to do SAFTE this is the format */
+ /* SAF-TE */
+ memset(&bpt, 0, sizeof(bpt));
+ bpt.cookie = bl.cookie;
+ bpt.channel = delm->channel;
+ bpt.target = delm->target;
+ bpt.cdblen = 10;
+ bpt.cdb[0] = 0x3c; /* READ BUFFER */
+ bpt.cdb[1] = 0x01; /* SAF-TE command */
+ bpt.cdb[8] = sizeof(rc); /* LSB size, FIXME */
+ bpt.data = &rc[0]; /* set up return data pointer */
+ bpt.datalen = sizeof(rc);
+ bpt.direction = BIOC_DIRIN;
+ bpt.senselen = 32; /* silly since the kernel overrides it */
+#endif
+
+int
+get_ses_page(u_int8_t c, u_int8_t t, u_int8_t p, u_int8_t *buf, u_int8_t buflen)
+{
+ bioc_scsicmd bpt;
+ int rv;
+
+ memset(&bpt, 0, sizeof(bpt));
+ bpt.cookie = bl.cookie;
+ bpt.channel = c;
+ bpt.target = t;
+ bpt.cdblen = 6;
+ bpt.cdb[0] = RECEIVE_DIAGNOSTIC;
+ /* FIXME add this cdb struct + #defines to scsi_all.h */
+ bpt.cdb[1] = 0x01; /* set PCV bit for SES commands */
+ bpt.cdb[2] = p; /* SES page nr */
+ bpt.cdb[4] = buflen;
+ bpt.data = buf; /* set up return data pointer */
+ bpt.datalen = buflen;
+ bpt.direction = BIOC_DIRIN;
+ bpt.senselen = 32; /* silly since the kernel overrides it */
+
+ rv = ioctl(devh, BIOCSCSICMD, &bpt);
+ if (rv == -1) {
+ warnx("RECEIVE_DIAGNOSTIC failed %x", bpt.status);
+ return (0);
+ }
+ else if (bpt.status) {
+ if (bpt.sensebuf[0] == 0x70 || bpt.sensebuf[0] == 0x71)
+ print_sense(&bpt.sensebuf[0], bpt.senselen);
+ else
+ printf("channel: %d target: %2d RECEIVE_DIAGNOSTIC "
+ "failed without sense data\n", c, t);
+
+ return (0);
+ }
+
+ if (debug) {
+ /* abuse print sense a little */
+ print_sense(buf, bpt.datalen);
+ }
+
+ return (1);
+}
+
+int
+set_ses_page(u_int8_t c, u_int8_t t, u_int8_t *buf, u_int8_t buflen)
+{
+ bioc_scsicmd bpt;
+ int rv;
+
+ memset(&bpt, 0, sizeof(bpt));
+ bpt.cookie = bl.cookie;
+ bpt.channel = c;
+ bpt.target = t;
+ bpt.cdblen = 6;
+ bpt.cdb[0] = SEND_DIAGNOSTIC;
+ bpt.cdb[1] = SSD_PF;
+ bpt.cdb[4] = buflen;
+ bpt.data = buf; /* set up return data pointer */
+ bpt.datalen = buflen;
+ bpt.direction = BIOC_DIROUT;
+ bpt.senselen = 32; /* silly since the kernel overrides it */
+
+ rv = ioctl(devh, BIOCSCSICMD, &bpt);
+ if (rv == -1) {
+ warnx("SEND_DIAGNOSTIC failed %x", bpt.status);
+ return (0);
+ }
+ else if (bpt.status) {
+ if (bpt.sensebuf[0] == 0x70 || bpt.sensebuf[0] == 0x71)
+ print_sense(&bpt.sensebuf[0], bpt.senselen);
+ else
+ printf("channel: %d target: %2d SEND_DIAGNOSTIC "
+ "failed without sense data\n", c, t);
+
+ return (0);
+ }
+
+ if (debug) {
+ /* abuse print sense a little */
+ print_sense(buf, bpt.datalen);
+ }
+
+ return (1);
}