Import of the port of the new(er) FreeBSD atacontrol(8), as natacontrol(8).
authorThomas E. Spanjaard <tgen@dragonflybsd.org>
Tue, 5 Dec 2006 21:32:21 +0000 (21:32 +0000)
committerThomas E. Spanjaard <tgen@dragonflybsd.org>
Tue, 5 Dec 2006 21:32:21 +0000 (21:32 +0000)
It has not yet been hooked into the build, though it does build.

sbin/natacontrol/Makefile [new file with mode: 0644]
sbin/natacontrol/natacontrol.8 [new file with mode: 0644]
sbin/natacontrol/natacontrol.c [new file with mode: 0644]

diff --git a/sbin/natacontrol/Makefile b/sbin/natacontrol/Makefile
new file mode 100644 (file)
index 0000000..7a8b755
--- /dev/null
@@ -0,0 +1,7 @@
+#$FreeBSD: src/sbin/atacontrol/Makefile,v 1.8 2003/11/05 19:20:41 johan Exp $
+#$DragonFly: src/sbin/natacontrol/Makefile,v 1.1 2006/12/05 21:32:21 tgen Exp $
+
+PROG=  natacontrol
+MAN=   natacontrol.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/natacontrol/natacontrol.8 b/sbin/natacontrol/natacontrol.8
new file mode 100644 (file)
index 0000000..de928d9
--- /dev/null
@@ -0,0 +1,242 @@
+.\"
+.\" Copyright (c) 2000,2001,2002 Søren Schmidt <sos@FreeBSD.org>
+.\" 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: src/sbin/atacontrol/atacontrol.8,v 1.28 2005/11/18 10:32:09 ru Exp $
+.\" $DragonFly: src/sbin/natacontrol/natacontrol.8,v 1.1 2006/12/05 21:32:21 tgen Exp $
+.\"
+.Dd December 5, 2006
+.Dt NATACONTROL 8
+.Os
+.Sh NAME
+.Nm natacontrol
+.Nd NATA device driver control program
+.Sh SYNOPSIS
+.Nm
+.Aq Ar command
+.Ar args
+.Pp
+.Nm
+.Ic attach
+.Ar channel
+.Nm
+.Ic detach
+.Ar channel
+.Nm
+.Ic reinit
+.Ar channel
+.Nm
+.Ic create
+.Ar type Oo Ar interleave Oc Ar disk0 ... diskN
+.Nm
+.Ic delete
+.Ar raid
+.Nm
+.Ic addspare
+.Ar raid disk
+.Nm
+.Ic rebuild
+.Ar raid
+.Nm
+.Ic status
+.Ar raid
+.Nm
+.Ic mode
+.Ar device
+.Nm
+.Ic info
+.Ar channel
+.Nm
+.Ic cap
+.Ar device
+.Nm
+.Ic list
+.Sh DESCRIPTION
+The
+.Nm
+utility is a control program that provides the user access and control to the
+.Fx
+.Xr nata 4
+subsystem.
+.Pp
+The
+.Nm
+utility
+can cause severe system crashes and loss of data if used improperly.
+Please
+exercise caution when using this command!
+.Pp
+The
+.Ar channel
+argument is the ATA channel device (e.g., ata0) on which to operate.
+The following commands are supported:
+.Bl -tag -width "rebuild"
+.It Ic attach
+Attach an ATA
+.Ar channel .
+Devices on the channel are probed and attached as
+is done on boot.
+.It Ic detach
+Detach an ATA
+.Ar channel .
+Devices on the channel are removed from the kernel,
+and all outstanding transfers etc.\& are returned back to the system marked
+as failed.
+.It Ic reinit
+Reinitialize an ATA
+.Ar channel .
+Both devices on the channel are reset and
+initialized to the parameters the ATA driver has stored internally.
+Devices that have gone bad and no longer respond to the probe, or devices
+that have physically been removed, are removed from the kernel.
+Likewise are devices that show up during a reset, probed and attached.
+.It Ic create
+Create a
+.Ar type
+ATA RAID.
+The type can be
+.Cm RAID0
+(stripe),
+.Cm RAID1
+(mirror),
+.Cm RAID0+1 ,
+.Cm SPAN
+or
+.Cm JBOD .
+In case the RAID has a
+.Cm RAID0
+component,
+the
+.Ar interleave
+must be specified in number of sectors.
+The RAID will be created
+of the individual disks named
+.Bk -words
+.Ar disk0 ... diskN .
+.Ek
+.Pp
+Although the NATA driver allows for creating an ATA RAID on disks with any
+controller, there are restrictions.
+It is only possible to boot on
+an array if it is either located on a
+.Dq real
+ATA RAID controller like
+the Promise or Highpoint controllers, or if the RAID declared is of
+.Cm RAID1
+or
+.Cm SPAN
+type; in case of a
+.Cm SPAN ,
+the partition to boot must
+reside on the first disk in the SPAN.
+.It Ic delete
+Delete a RAID array on a RAID capable ATA controller.
+.It Ic addspare
+Add a spare disk to an existing RAID.
+.It Ic rebuild
+Rebuild a RAID1 array on a RAID capable ATA controller.
+.It Ic status
+Get the status of an ATA RAID.
+.It Ic mode
+Without the mode argument, the current transfer modes of the
+device are printed.
+If the mode argument is given, the NATA driver
+is asked to change the transfer mode to the one given.
+The NATA driver
+will reject modes that are not supported by the hardware.
+Modes are given like
+.Dq Li PIO3 ,
+.Dq Li udma2 ,
+.Dq Li udma100 ,
+case does not matter.
+.Pp
+Currently supported modes are:
+.Cm PIO0 , PIO1 , PIO2 , PIO3 , PIO4 ,
+.Cm WDMA2 ,
+.Cm UDMA2
+(alias
+.Cm UDMA33 ) ,
+.Cm UDMA4
+(alias
+.Cm UDMA66 ) ,
+.Cm UDMA5
+(alias
+.Cm UDMA100 )
+and
+.Cm UDMA6
+(alias
+.Cm UDMA133 ) .
+The device name and manufacture/version strings are shown.
+.It Ic cap
+Show detailed info about the device on
+.Ar device .
+.It Ic info
+Show info about the attached devices on the
+.Ar channel .
+.It Ic list
+Show info about all attached devices on all active controllers.
+.El
+.Sh EXAMPLES
+To get information on devices attached to a channel,
+use the command line:
+.Pp
+.Dl "natacontrol info ata0"
+.Pp
+To see the devices' current access modes, use the command line:
+.Pp
+.Dl "natacontrol mode ad0"
+.Pp
+which results in the modes of the devices being displayed as a string
+like this:
+.Pp
+.Dl "current mode = UDMA100"
+.Pp
+You can set the mode with
+.Nm
+and a string like the above,
+for example:
+.Pp
+.Dl "natacontrol mode ad0 PIO4"
+.Pp
+The new modes are set as soon as the
+.Nm
+command returns.
+.Sh SEE ALSO
+.Xr nata 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.6 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An S\(/oren Schmidt
+.Aq sos@FreeBSD.org .
+.Pp
+This manual page was written by
+.An S\(/oren Schmidt
+.Aq sos@FreeBSD.org .
diff --git a/sbin/natacontrol/natacontrol.c b/sbin/natacontrol/natacontrol.c
new file mode 100644 (file)
index 0000000..20085ac
--- /dev/null
@@ -0,0 +1,588 @@
+/*-
+ * Copyright (c) 2000 - 2006 Søren Schmidt <sos@FreeBSD.org>
+ * 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,
+ *    without modification, immediately at the beginning of the file.
+ * 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 ``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: src/sbin/atacontrol/atacontrol.c,v 1.42 2006/03/15 19:32:43 sos Exp $
+ * $DragonFly: src/sbin/natacontrol/natacontrol.c,v 1.1 2006/12/05 21:32:21 tgen Exp $
+ */
+
+#include <sys/nata.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+
+#include <sysexits.h>
+
+static const char *mode2str(int mode);
+static int     str2mode(char *str);
+static void    usage(void);
+static int     version(int ver);
+static void    param_print(struct ata_params *parm);
+static void    cap_print(struct ata_params *parm);
+static int     ata_cap_print(int fd);
+static int     info_print(int fd, int channel, int prchan);
+
+const char *
+mode2str(int mode)
+{
+       switch (mode) {
+       case ATA_PIO: return "BIOSPIO";
+       case ATA_PIO0: return "PIO0";
+       case ATA_PIO1: return "PIO1";
+       case ATA_PIO2: return "PIO2";
+       case ATA_PIO3: return "PIO3";
+       case ATA_PIO4: return "PIO4";
+       case ATA_WDMA2: return "WDMA2";
+       case ATA_UDMA2: return "UDMA33";
+       case ATA_UDMA4: return "UDMA66";
+       case ATA_UDMA5: return "UDMA100";
+       case ATA_UDMA6: return "UDMA133";
+       case ATA_SA150: return "SATA150";
+       case ATA_SA300: return "SATA300";
+       case ATA_USB: return "USB";
+       case ATA_USB1: return "USB1";
+       case ATA_USB2: return "USB2";
+       case ATA_DMA: return "BIOSDMA";
+       default: return "???";
+       }
+}
+
+int
+str2mode(char *str)
+{
+       if (!strcasecmp(str, "BIOSPIO")) return ATA_PIO;
+       if (!strcasecmp(str, "PIO0")) return ATA_PIO0;
+       if (!strcasecmp(str, "PIO1")) return ATA_PIO1;
+       if (!strcasecmp(str, "PIO2")) return ATA_PIO2;
+       if (!strcasecmp(str, "PIO3")) return ATA_PIO3;
+       if (!strcasecmp(str, "PIO4")) return ATA_PIO4;
+       if (!strcasecmp(str, "WDMA2")) return ATA_WDMA2;
+       if (!strcasecmp(str, "UDMA2")) return ATA_UDMA2;
+       if (!strcasecmp(str, "UDMA33")) return ATA_UDMA2;
+       if (!strcasecmp(str, "UDMA4")) return ATA_UDMA4;
+       if (!strcasecmp(str, "UDMA66")) return ATA_UDMA4;
+       if (!strcasecmp(str, "UDMA5")) return ATA_UDMA5;
+       if (!strcasecmp(str, "UDMA100")) return ATA_UDMA5;
+       if (!strcasecmp(str, "UDMA6")) return ATA_UDMA6;
+       if (!strcasecmp(str, "UDMA133")) return ATA_UDMA6;
+       if (!strcasecmp(str, "BIOSDMA")) return ATA_DMA;
+       return -1;
+}
+
+void
+usage()
+{
+       fprintf(stderr,
+               "usage:  atacontrol <command> args:\n"
+               "        atacontrol list\n"
+               "        atacontrol info channel\n"
+               "        atacontrol attach channel\n"
+               "        atacontrol detach channel\n"
+               "        atacontrol reinit channel\n"
+               "        atacontrol create type [interleave] disk0 ... diskN\n"
+               "        atacontrol delete array\n"
+               "        atacontrol addspare array disk\n"
+               "        atacontrol rebuild array\n"
+               "        atacontrol status array\n"
+               "        atacontrol mode device [mode]\n"
+               "        atacontrol cap device\n"
+       );
+       exit(EX_USAGE);
+}
+
+int
+version(int ver)
+{
+       int bit;
+
+       if (ver == 0xffff)
+               return 0;
+       for (bit = 15; bit >= 0; bit--)
+               if (ver & (1<<bit))
+                       return bit;
+       return 0;
+}
+
+void
+param_print(struct ata_params *parm)
+{
+       printf("<%.40s/%.8s> ", parm->model, parm->revision);
+       if (parm->satacapabilities && parm->satacapabilities != 0xffff) {
+               if (parm->satacapabilities & ATA_SATA_GEN2)
+                       printf("Serial ATA II\n");
+               else if (parm->satacapabilities & ATA_SATA_GEN1)
+                       printf("Serial ATA v1.0\n");
+               else
+                       printf("Unknown serial ATA version\n");
+       }
+       else
+               printf("ATA/ATAPI revision %d\n", version(parm->version_major));
+}
+
+void
+cap_print(struct ata_params *parm)
+{
+       u_int32_t lbasize = (u_int32_t)parm->lba_size_1 |
+                               ((u_int32_t)parm->lba_size_2 << 16);
+
+       u_int64_t lbasize48 = ((u_int64_t)parm->lba_size48_1) |
+                               ((u_int64_t)parm->lba_size48_2 << 16) |
+                               ((u_int64_t)parm->lba_size48_3 << 32) |
+                               ((u_int64_t)parm->lba_size48_4 << 48);
+
+       printf("\n");
+       printf("Protocol              ");
+       if (parm->satacapabilities && parm->satacapabilities != 0xffff) {
+               if (parm->satacapabilities & ATA_SATA_GEN2)
+                       printf("Serial ATA II\n");
+               else if (parm->satacapabilities & ATA_SATA_GEN1)
+                       printf("Serial ATA v1.0\n");
+               else
+                       printf("Unknown serial ATA version\n");
+       }
+       else
+               printf("ATA/ATAPI revision %d\n", version(parm->version_major));
+       printf("device model          %.40s\n", parm->model);
+       printf("serial number         %.20s\n", parm->serial);
+       printf("firmware revision     %.8s\n", parm->revision);
+
+       printf("cylinders             %d\n", parm->cylinders);
+       printf("heads                 %d\n", parm->heads);
+       printf("sectors/track         %d\n", parm->sectors);
+
+       printf("lba%ssupported         ",
+               parm->capabilities1 & ATA_SUPPORT_LBA ? " " : " not ");
+       if (lbasize)
+               printf("%d sectors\n", lbasize);
+       else
+               printf("\n");
+
+       printf("lba48%ssupported       ",
+               parm->support.command2 & ATA_SUPPORT_ADDRESS48 ? " " : " not ");
+       if (lbasize48)
+               printf("%llu sectors\n", (unsigned long long)lbasize48);
+       else
+               printf("\n");
+
+       printf("dma%ssupported\n",
+               parm->capabilities1 & ATA_SUPPORT_DMA ? " " : " not ");
+
+       printf("overlap%ssupported\n",
+               parm->capabilities1 & ATA_SUPPORT_OVERLAP ? " " : " not ");
+
+       printf("\nFeature                      "
+               "Support  Enable    Value           Vendor\n");
+
+       printf("write cache                    %s       %s\n",
+               parm->support.command1 & ATA_SUPPORT_WRITECACHE ? "yes" : "no",
+               parm->enabled.command1 & ATA_SUPPORT_WRITECACHE ? "yes" : "no");
+
+       printf("read ahead                     %s       %s\n",
+               parm->support.command1 & ATA_SUPPORT_LOOKAHEAD ? "yes" : "no",
+               parm->enabled.command1 & ATA_SUPPORT_LOOKAHEAD ? "yes" : "no");
+
+       if (parm->satacapabilities && parm->satacapabilities != 0xffff) {
+               printf("Native Command Queuing (NCQ)   %s       %s"
+                       "       %d/0x%02X\n",
+                       parm->satacapabilities & ATA_SUPPORT_NCQ ?
+                               "yes" : "no", " -",
+                       (parm->satacapabilities & ATA_SUPPORT_NCQ) ?
+                               ATA_QUEUE_LEN(parm->queue) : 0,
+                       (parm->satacapabilities & ATA_SUPPORT_NCQ) ?
+                               ATA_QUEUE_LEN(parm->queue) : 0);
+       }
+       printf("Tagged Command Queuing (TCQ)   %s       %s      %d/0x%02X\n",
+               parm->support.command2 & ATA_SUPPORT_QUEUED ? "yes" : "no",
+               parm->enabled.command2 & ATA_SUPPORT_QUEUED ? "yes" : "no",
+               ATA_QUEUE_LEN(parm->queue), ATA_QUEUE_LEN(parm->queue));
+
+       printf("SMART                          %s       %s\n",
+               parm->support.command1 & ATA_SUPPORT_SMART ? "yes" : "no",
+               parm->enabled.command1 & ATA_SUPPORT_SMART ? "yes" : "no");
+
+       printf("microcode download             %s       %s\n",
+               parm->support.command2 & ATA_SUPPORT_MICROCODE ? "yes" : "no",
+               parm->enabled.command2 & ATA_SUPPORT_MICROCODE ? "yes" : "no");
+
+       printf("security                       %s       %s\n",
+               parm->support.command1 & ATA_SUPPORT_SECURITY ? "yes" : "no",
+               parm->enabled.command1 & ATA_SUPPORT_SECURITY ? "yes" : "no");
+
+       printf("power management               %s       %s\n",
+               parm->support.command1 & ATA_SUPPORT_POWERMGT ? "yes" : "no",
+               parm->enabled.command1 & ATA_SUPPORT_POWERMGT ? "yes" : "no");
+
+       printf("advanced power management      %s       %s      %d/0x%02X\n",
+               parm->support.command2 & ATA_SUPPORT_APM ? "yes" : "no",
+               parm->enabled.command2 & ATA_SUPPORT_APM ? "yes" : "no",
+               parm->apm_value, parm->apm_value);
+
+       printf("automatic acoustic management  %s       %s      "
+               "%d/0x%02X      %d/0x%02X\n",
+               parm->support.command2 & ATA_SUPPORT_AUTOACOUSTIC ? "yes" :"no",
+               parm->enabled.command2 & ATA_SUPPORT_AUTOACOUSTIC ? "yes" :"no",
+               ATA_ACOUSTIC_CURRENT(parm->acoustic),
+               ATA_ACOUSTIC_CURRENT(parm->acoustic),
+               ATA_ACOUSTIC_VENDOR(parm->acoustic),
+               ATA_ACOUSTIC_VENDOR(parm->acoustic));
+}
+
+int
+ata_cap_print(int fd)
+{
+       struct ata_params params;
+
+       if (ioctl(fd, IOCATAGPARM, &params) < 0)
+               return errno;
+       cap_print(&params);
+       return 0;
+}
+
+int
+info_print(int fd, int channel, int prchan)
+{
+       struct ata_ioc_devices devices;
+
+       devices.channel = channel;
+
+       if (ioctl(fd, IOCATADEVICES, &devices) < 0)
+               return errno;
+
+       if (prchan)
+               printf("ATA channel %d:\n", channel);
+       printf("%sMaster: ", prchan ? "    " : "");
+       if (*devices.name[0]) {
+               printf("%4.4s ", devices.name[0]);
+               param_print(&devices.params[0]);
+       }
+       else
+               printf("     no device present\n");
+       printf("%sSlave:  ", prchan ? "    " : "");
+       if (*devices.name[1]) {
+               printf("%4.4s ", devices.name[1]);
+               param_print(&devices.params[1]);
+       }
+       else
+               printf("     no device present\n");
+       return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+       int fd;
+
+       if (argc < 2)
+               usage();
+
+       if (!strcmp(argv[1], "mode") && (argc == 3 || argc == 4)) {
+               int disk, mode;
+               char device[64];
+
+               if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
+                     sscanf(argv[2], "acd%d", &disk) == 1 ||
+                     sscanf(argv[2], "afd%d", &disk) == 1 ||
+                     sscanf(argv[2], "ast%d", &disk) == 1)) {
+                       fprintf(stderr, "atacontrol: Invalid device %s\n",
+                               argv[2]);
+                       exit(EX_USAGE);
+               }
+               sprintf(device, "/dev/%s", argv[2]);
+               if ((fd = open(device, O_RDONLY)) < 0)
+                       err(1, "device not found");
+               if (argc == 4) {
+                       mode = str2mode(argv[3]);
+                       if (ioctl(fd, IOCATASMODE, &mode) < 0)
+                               warn("ioctl(IOCATASMODE)");
+               }
+               if (argc == 3 || argc == 4) {
+                       if (ioctl(fd, IOCATAGMODE, &mode) < 0)
+                               err(1, "ioctl(IOCATAGMODE)");
+                       printf("current mode = %s\n", mode2str(mode));
+               }
+               exit(EX_OK);
+       }
+       if (!strcmp(argv[1], "cap") && argc == 3) {
+               int disk;
+               char device[64];
+
+               if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
+                     sscanf(argv[2], "acd%d", &disk) == 1 ||
+                     sscanf(argv[2], "afd%d", &disk) == 1 ||
+                     sscanf(argv[2], "ast%d", &disk) == 1)) {
+                       fprintf(stderr, "atacontrol: Invalid device %s\n",
+                               argv[2]);
+                       exit(EX_USAGE);
+               }
+               sprintf(device, "/dev/%s", argv[2]);
+               if ((fd = open(device, O_RDONLY)) < 0)
+                       err(1, "device not found");
+               ata_cap_print(fd);
+               exit(EX_OK);
+       }
+
+       if ((fd = open("/dev/ata", O_RDWR)) < 0)
+               err(1, "control device not found");
+
+       if (!strcmp(argv[1], "list") && argc == 2) {
+               int maxchannel, channel;
+
+               if (ioctl(fd, IOCATAGMAXCHANNEL, &maxchannel) < 0)
+                       err(1, "ioctl(IOCATAGMAXCHANNEL)");
+               for (channel = 0; channel < maxchannel; channel++)
+                       info_print(fd, channel, 1);
+               exit(EX_OK);
+       }
+       if (!strcmp(argv[1], "info") && argc == 3) {
+               int channel;
+
+               if (!(sscanf(argv[2], "ata%d", &channel) == 1)) {
+                       fprintf(stderr,
+                               "atacontrol: Invalid channel %s\n", argv[2]);
+                        exit(EX_USAGE);
+               }
+               info_print(fd, channel, 0);
+               exit(EX_OK);
+       }
+       if (!strcmp(argv[1], "detach") && argc == 3) {
+               int channel;
+
+               if (!(sscanf(argv[2], "ata%d", &channel) == 1)) {
+                       fprintf(stderr,
+                               "atacontrol: Invalid channel %s\n", argv[2]);
+                        exit(EX_USAGE);
+               }
+               if (ioctl(fd, IOCATADETACH, &channel) < 0)
+                       err(1, "ioctl(IOCATADETACH)");
+               exit(EX_OK);
+       }
+       if (!strcmp(argv[1], "attach") && argc == 3) {
+               int channel;
+
+               if (!(sscanf(argv[2], "ata%d", &channel) == 1)) {
+                       fprintf(stderr,
+                               "atacontrol: Invalid channel %s\n", argv[2]);
+                        exit(EX_USAGE);
+               }
+               if (ioctl(fd, IOCATAATTACH, &channel) < 0)
+                       err(1, "ioctl(IOCATAATTACH)");
+               info_print(fd, channel, 0);
+               exit(EX_OK);
+       }
+       if (!strcmp(argv[1], "reinit") && argc == 3) {
+               int channel;
+
+               if (!(sscanf(argv[2], "ata%d", &channel) == 1)) {
+                       fprintf(stderr,
+                               "atacontrol: Invalid channel %s\n", argv[2]);
+                        exit(EX_USAGE);
+               }
+               if (ioctl(fd, IOCATAREINIT, &channel) < 0)
+                       warn("ioctl(IOCATAREINIT)");
+               info_print(fd, channel, 0);
+               exit(EX_OK);
+       }
+       if (!strcmp(argv[1], "create")) {
+               int disk, dev, offset;
+               struct ata_ioc_raid_config config;
+
+               bzero(&config, sizeof(config));
+               if (argc > 2) {
+                       if (!strcasecmp(argv[2], "RAID0") ||
+                           !strcasecmp(argv[2], "stripe"))
+                               config.type = AR_RAID0;
+                       if (!strcasecmp(argv[2], "RAID1") ||
+                           !strcasecmp(argv[2],"mirror"))
+                               config.type = AR_RAID1;
+                       if (!strcasecmp(argv[2], "RAID0+1") ||
+                           !strcasecmp(argv[2],"RAID10"))
+                               config.type = AR_RAID01;
+                       if (!strcasecmp(argv[2], "RAID5"))
+                               config.type = AR_RAID5;
+                       if (!strcasecmp(argv[2], "SPAN"))
+                               config.type = AR_SPAN;
+                       if (!strcasecmp(argv[2], "JBOD"))
+                               config.type = AR_JBOD;
+               }
+               if (!config.type) {
+                       fprintf(stderr, "atacontrol: Invalid RAID type %s\n",
+                               argv[2]);
+                       fprintf(stderr, "atacontrol: Valid RAID types: \n");
+                       fprintf(stderr, "            stripe | mirror | "
+                                       "RAID0 | RAID1 | RAID0+1 | RAID5 | "
+                                       "SPAN | JBOD\n");
+                       exit(EX_USAGE);
+               }
+
+               if (config.type == AR_RAID0 ||
+                   config.type == AR_RAID01 ||
+                   config.type == AR_RAID5) {
+                       if (argc < 4 ||
+                           !sscanf(argv[3], "%d", &config.interleave) == 1) {
+                               fprintf(stderr,
+                                       "atacontrol: Invalid interleave %s\n",
+                                       argv[3]);
+                               exit(EX_USAGE);
+                       }
+                       offset = 4;
+               }
+               else
+                       offset = 3;
+
+               for (disk = 0; disk < 16 && (offset + disk) < argc; disk++) {
+                       if (!(sscanf(argv[offset + disk], "ad%d", &dev) == 1)) {
+                               fprintf(stderr,
+                                       "atacontrol: Invalid disk %s\n",
+                                       argv[offset + disk]);
+                               exit(EX_USAGE);
+                       }
+                       config.disks[disk] = dev;
+               }
+
+               if ((config.type == AR_RAID1 || config.type == AR_RAID01) &&
+                   disk < 2) {
+                       fprintf(stderr, "atacontrol: At least 2 disks must be "
+                               "specified\n");
+                       exit(EX_USAGE);
+               }
+
+               config.total_disks = disk;
+               if (ioctl(fd, IOCATARAIDCREATE, &config) < 0)
+                       err(1, "ioctl(IOCATARAIDCREATE)");
+               else
+                       printf("ar%d created\n", config.lun);
+               exit(EX_OK);
+       }
+       if (!strcmp(argv[1], "delete") && argc == 3) {
+               int array;
+
+               if (!(sscanf(argv[2], "ar%d", &array) == 1)) {
+                       fprintf(stderr,
+                               "atacontrol: Invalid array %s\n", argv[2]);
+                        exit(EX_USAGE);
+               }
+               if (ioctl(fd, IOCATARAIDDELETE, &array) < 0)
+                       warn("ioctl(IOCATARAIDDELETE)");
+               exit(EX_OK);
+       }
+       if (!strcmp(argv[1], "addspare") && argc == 4) {
+               struct ata_ioc_raid_config config;
+
+               if (!(sscanf(argv[2], "ar%d", &config.lun) == 1)) {
+                       fprintf(stderr,
+                               "atacontrol: Invalid array %s\n", argv[2]);
+                       usage();
+               }
+               if (!(sscanf(argv[3], "ad%d", &config.disks[0]) == 1)) {
+                       fprintf(stderr,
+                               "atacontrol: Invalid disk %s\n", argv[3]);
+                       usage();
+               }
+               if (ioctl(fd, IOCATARAIDADDSPARE, &config) < 0)
+                       warn("ioctl(IOCATARAIDADDSPARE)");
+               exit(EX_OK);
+       }
+       if (!strcmp(argv[1], "rebuild") && argc == 3) {
+               int array;
+
+               if (!(sscanf(argv[2], "ar%d", &array) == 1)) {
+                       fprintf(stderr,
+                               "atacontrol: Invalid array %s\n", argv[2]);
+                       usage();
+               }
+               if (ioctl(fd, IOCATARAIDREBUILD, &array) < 0)
+                       warn("ioctl(IOCATARAIDREBUILD)");
+               else {
+                       char buffer[128];
+                       sprintf(buffer, "/usr/bin/nice -n 20 /bin/dd "
+                               "if=/dev/ar%d of=/dev/null bs=1m &",
+                               array);
+                       if (system(buffer))
+                               warn("background dd");
+               }
+               exit(EX_OK);
+       }
+       if (!strcmp(argv[1], "status") && argc == 3) {
+               struct ata_ioc_raid_config config;
+               int i;
+
+               if (!(sscanf(argv[2], "ar%d", &config.lun) == 1)) {
+                       fprintf(stderr,
+                               "atacontrol: Invalid array %s\n", argv[2]);
+                       usage();
+               }
+               if (ioctl(fd, IOCATARAIDSTATUS, &config) < 0)
+                       err(1, "ioctl(IOCATARAIDSTATUS)");
+
+               printf("ar%d: ATA ", config.lun);
+               switch (config.type) {
+               case AR_RAID0:
+                       printf("RAID0 stripesize=%d", config.interleave);
+                       break;
+               case AR_RAID1:
+                       printf("RAID1");
+                       break;
+               case AR_RAID01:
+                       printf("RAID0+1 stripesize=%d", config.interleave);
+                       break;
+               case AR_RAID5:
+                       printf("RAID5 stripesize=%d", config.interleave);
+                       break;
+               case AR_JBOD:
+                       printf("JBOD");
+               case AR_SPAN:
+                       printf("SPAN");
+                       break;
+               }
+               printf(" subdisks: ");
+               for (i = 0; i < config.total_disks; i++) {
+                       if (config.disks[i] >= 0)
+                               printf("ad%d ", config.disks[i]);
+                       else
+                               printf("DOWN ");
+               }
+               printf("status: ");
+               switch (config.status) {
+               case AR_READY:
+                       printf("READY\n");
+                       break;
+               case AR_READY | AR_DEGRADED:
+                       printf("DEGRADED\n");
+                       break;
+               case AR_READY | AR_DEGRADED | AR_REBUILDING:
+                       printf("REBUILDING %d%% completed\n",
+                               config.progress);
+                       break;
+               default:
+                       printf("BROKEN\n");
+               }
+               exit(EX_OK);
+       }
+       usage();
+       exit(EX_OK);
+}