xref: /dragonfly/sbin/natacontrol/natacontrol.c (revision 6a3cbbc2)
1 /*-
2  * Copyright (c) 2000 - 2006 Søren Schmidt <sos@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification, immediately at the beginning of the file.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sbin/atacontrol/atacontrol.c,v 1.42 2006/03/15 19:32:43 sos Exp $
27  */
28 
29 #include <sys/nata.h>
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <err.h>
37 
38 #include <sysexits.h>
39 #include <unistd.h>
40 
41 static const char *
42 mode2str(int mode)
43 {
44 	switch (mode & 0xff) {
45 	case ATA_PIO: return "BIOSPIO";
46 	case ATA_PIO0: return "PIO0";
47 	case ATA_PIO1: return "PIO1";
48 	case ATA_PIO2: return "PIO2";
49 	case ATA_PIO3: return "PIO3";
50 	case ATA_PIO4: return "PIO4";
51 	case ATA_WDMA0: return "WDMA0";
52 	case ATA_WDMA1: return "WDMA1";
53 	case ATA_WDMA2: return "WDMA2";
54 	case ATA_UDMA0: return "UDMA0";
55 	case ATA_UDMA1: return "UDMA1";
56 	case ATA_UDMA2: return "UDMA33";
57 	case ATA_UDMA3: return "UDMA44";
58 	case ATA_UDMA4: return "UDMA66";
59 	case ATA_UDMA5: return "UDMA100";
60 	case ATA_UDMA6: return "UDMA133";
61 	case ATA_SA150: return "SATA150";
62 	case ATA_SA300: return "SATA300";
63 	case ATA_USB: return "USB";
64 	case ATA_USB1: return "USB1";
65 	case ATA_USB2: return "USB2";
66 	case ATA_DMA: return "BIOSDMA";
67 	default: return "???";
68 	}
69 }
70 
71 static const char *
72 satarev2str(int mode)
73 {
74 	switch ((mode & 0xff00) >> 8) {
75 	case 0: return "";
76 	case 1: return "SATA 1.5Gb/s";
77 	case 2: return "SATA 3Gb/s";
78 	case 3: return "SATA 6Gb/s";
79 	case 0xff: return "SATA";
80 	default: return "???";
81 	}
82 }
83 
84 static int
85 str2mode(char *str)
86 {
87 	if (!strcasecmp(str, "BIOSPIO")) return ATA_PIO;
88 	if (!strcasecmp(str, "PIO0")) return ATA_PIO0;
89 	if (!strcasecmp(str, "PIO1")) return ATA_PIO1;
90 	if (!strcasecmp(str, "PIO2")) return ATA_PIO2;
91 	if (!strcasecmp(str, "PIO3")) return ATA_PIO3;
92 	if (!strcasecmp(str, "PIO4")) return ATA_PIO4;
93 	if (!strcasecmp(str, "WDMA0")) return ATA_WDMA0;
94 	if (!strcasecmp(str, "WDMA1")) return ATA_WDMA1;
95 	if (!strcasecmp(str, "WDMA2")) return ATA_WDMA2;
96 	if (!strcasecmp(str, "UDMA0")) return ATA_UDMA0;
97 	if (!strcasecmp(str, "UDMA16")) return ATA_UDMA0;
98 	if (!strcasecmp(str, "UDMA1")) return ATA_UDMA1;
99 	if (!strcasecmp(str, "UDMA25")) return ATA_UDMA1;
100 	if (!strcasecmp(str, "UDMA2")) return ATA_UDMA2;
101 	if (!strcasecmp(str, "UDMA33")) return ATA_UDMA2;
102 	if (!strcasecmp(str, "UDMA3")) return ATA_UDMA3;
103 	if (!strcasecmp(str, "UDMA44")) return ATA_UDMA3;
104 	if (!strcasecmp(str, "UDMA4")) return ATA_UDMA4;
105 	if (!strcasecmp(str, "UDMA66")) return ATA_UDMA4;
106 	if (!strcasecmp(str, "UDMA5")) return ATA_UDMA5;
107 	if (!strcasecmp(str, "UDMA100")) return ATA_UDMA5;
108 	if (!strcasecmp(str, "UDMA6")) return ATA_UDMA6;
109 	if (!strcasecmp(str, "UDMA133")) return ATA_UDMA6;
110 	if (!strcasecmp(str, "BIOSDMA")) return ATA_DMA;
111 	return -1;
112 }
113 
114 static void
115 usage(void)
116 {
117 	fprintf(stderr,
118 		"usage:  natacontrol <command> args:\n"
119 		"        natacontrol list\n"
120 		"        natacontrol info channel\n"
121 		"        natacontrol attach channel\n"
122 		"        natacontrol detach channel\n"
123 		"        natacontrol reinit channel\n"
124 		"        natacontrol create type [interleave] disk0 ... diskN\n"
125 		"        natacontrol delete array\n"
126 		"        natacontrol addspare array disk\n"
127 		"        natacontrol rebuild array\n"
128 		"        natacontrol status array\n"
129 		"        natacontrol mode device [mode]\n"
130 		"        natacontrol feature device apm apmlevel\n"
131 		"        natacontrol feature device acoustic soundsupplevel\n"
132 		"        natacontrol cap device\n"
133 		"        natacontrol spindown device [seconds]\n"
134 	);
135 	exit(EX_USAGE);
136 }
137 
138 static int
139 version(int ver)
140 {
141 	int bit;
142 
143 	if (ver == 0xffff)
144 		return 0;
145 	for (bit = 15; bit >= 0; bit--)
146 		if (ver & (1<<bit))
147 			return bit;
148 	return 0;
149 }
150 
151 static void
152 param_print(struct ata_params *parm)
153 {
154 	printf("<%.40s/%.8s> ", parm->model, parm->revision);
155 	if (parm->satacapabilities && parm->satacapabilities != 0xffff) {
156 		if (parm->satacapabilities & ATA_SATA_GEN2)
157 			printf("SATA revision 2.x\n");
158 		else if (parm->satacapabilities & ATA_SATA_GEN1)
159 			printf("SATA revision 1.x\n");
160 		else
161 			printf("Unknown SATA revision\n");
162 	}
163 	else
164 		printf("ATA/ATAPI revision %d\n", version(parm->version_major));
165 }
166 
167 static void
168 cap_print(struct ata_params *parm)
169 {
170 	u_int32_t lbasize = (u_int32_t)parm->lba_size_1 |
171 				((u_int32_t)parm->lba_size_2 << 16);
172 
173 	u_int64_t lbasize48 = ((u_int64_t)parm->lba_size48_1) |
174 				((u_int64_t)parm->lba_size48_2 << 16) |
175 				((u_int64_t)parm->lba_size48_3 << 32) |
176 				((u_int64_t)parm->lba_size48_4 << 48);
177 
178 	printf("\n");
179 	printf("Protocol              ");
180 	if (parm->satacapabilities && parm->satacapabilities != 0xffff) {
181 		if (parm->satacapabilities & ATA_SATA_GEN2)
182 			printf("SATA revision 2.x\n");
183 		else if (parm->satacapabilities & ATA_SATA_GEN1)
184 			printf("SATA revision 1.x\n");
185 		else
186 			printf("Unknown SATA revision\n");
187 	}
188 	else
189 		printf("ATA/ATAPI revision %d\n", version(parm->version_major));
190 	printf("device model          %.40s\n", parm->model);
191 	printf("serial number         %.20s\n", parm->serial);
192 	printf("firmware revision     %.8s\n", parm->revision);
193 
194 	printf("cylinders             %d\n", parm->cylinders);
195 	printf("heads                 %d\n", parm->heads);
196 	printf("sectors/track         %d\n", parm->sectors);
197 
198 	printf("lba%ssupported         ",
199 		parm->capabilities1 & ATA_SUPPORT_LBA ? " " : " not ");
200 	if (lbasize)
201 		printf("%d sectors\n", lbasize);
202 	else
203 		printf("\n");
204 
205 	printf("lba48%ssupported       ",
206 		parm->support.command2 & ATA_SUPPORT_ADDRESS48 ? " " : " not ");
207 	if (lbasize48)
208 		printf("%ju sectors\n", (uintmax_t)lbasize48);
209 	else
210 		printf("\n");
211 
212 	printf("dma%ssupported\n",
213 		parm->capabilities1 & ATA_SUPPORT_DMA ? " " : " not ");
214 
215 	printf("overlap%ssupported\n",
216 		parm->capabilities1 & ATA_SUPPORT_OVERLAP ? " " : " not ");
217 
218 	printf("\nFeature                      "
219 		"Support  Enable    Value           Vendor\n");
220 
221 	printf("write cache                    %s	%s\n",
222 		parm->support.command1 & ATA_SUPPORT_WRITECACHE ? "yes" : "no",
223 		parm->enabled.command1 & ATA_SUPPORT_WRITECACHE ? "yes" : "no");
224 
225 	printf("read ahead                     %s	%s\n",
226 		parm->support.command1 & ATA_SUPPORT_LOOKAHEAD ? "yes" : "no",
227 		parm->enabled.command1 & ATA_SUPPORT_LOOKAHEAD ? "yes" : "no");
228 
229 	if (parm->satacapabilities && parm->satacapabilities != 0xffff) {
230 		printf("Native Command Queuing (NCQ)   %s	%s"
231 			"	%d/0x%02X\n",
232 			parm->satacapabilities & ATA_SUPPORT_NCQ ?
233 				"yes" : "no", " -",
234 			(parm->satacapabilities & ATA_SUPPORT_NCQ) ?
235 				ATA_QUEUE_LEN(parm->queue) : 0,
236 			(parm->satacapabilities & ATA_SUPPORT_NCQ) ?
237 				ATA_QUEUE_LEN(parm->queue) : 0);
238 	}
239 	printf("Tagged Command Queuing (TCQ)   %s	%s	%d/0x%02X\n",
240 		parm->support.command2 & ATA_SUPPORT_QUEUED ? "yes" : "no",
241 		parm->enabled.command2 & ATA_SUPPORT_QUEUED ? "yes" : "no",
242 		ATA_QUEUE_LEN(parm->queue), ATA_QUEUE_LEN(parm->queue));
243 
244 	printf("SMART                          %s	%s\n",
245 		parm->support.command1 & ATA_SUPPORT_SMART ? "yes" : "no",
246 		parm->enabled.command1 & ATA_SUPPORT_SMART ? "yes" : "no");
247 
248 	printf("microcode download             %s	%s\n",
249 		parm->support.command2 & ATA_SUPPORT_MICROCODE ? "yes" : "no",
250 		parm->enabled.command2 & ATA_SUPPORT_MICROCODE ? "yes" : "no");
251 
252 	printf("security                       %s	%s\n",
253 		parm->support.command1 & ATA_SUPPORT_SECURITY ? "yes" : "no",
254 		parm->enabled.command1 & ATA_SUPPORT_SECURITY ? "yes" : "no");
255 
256 	printf("power management               %s	%s\n",
257 		parm->support.command1 & ATA_SUPPORT_POWERMGT ? "yes" : "no",
258 		parm->enabled.command1 & ATA_SUPPORT_POWERMGT ? "yes" : "no");
259 
260 	printf("advanced power management      %s	%s	%d/0x%02X\n",
261 		parm->support.command2 & ATA_SUPPORT_APM ? "yes" : "no",
262 		parm->enabled.command2 & ATA_SUPPORT_APM ? "yes" : "no",
263 		parm->apm_value, parm->apm_value);
264 
265 	printf("automatic acoustic management  %s	%s	"
266 		"%d/0x%02X	%d/0x%02X\n",
267 		parm->support.command2 & ATA_SUPPORT_AUTOACOUSTIC ? "yes" :"no",
268 		parm->enabled.command2 & ATA_SUPPORT_AUTOACOUSTIC ? "yes" :"no",
269 		ATA_ACOUSTIC_CURRENT(parm->acoustic),
270 		ATA_ACOUSTIC_CURRENT(parm->acoustic),
271 		ATA_ACOUSTIC_VENDOR(parm->acoustic),
272 		ATA_ACOUSTIC_VENDOR(parm->acoustic));
273 }
274 
275 static void
276 ata_cap_print(int fd)
277 {
278 	struct ata_params params;
279 
280 	if (ioctl(fd, IOCATAGPARM, &params) < 0)
281 		err(1, "ioctl(IOCATAGPARM)");
282 	cap_print(&params);
283 }
284 
285 static void
286 info_print(int fd, int channel, int prchan)
287 {
288 	struct ata_ioc_devices devices;
289 
290 	devices.channel = channel;
291 
292 	if (ioctl(fd, IOCATADEVICES, &devices) < 0) {
293 		if (!prchan)
294 			err(1, "ioctl(IOCATADEVICES)");
295 		return;
296 	}
297 	if (prchan)
298 		printf("ATA channel %d:\n", channel);
299 	printf("%sMaster: ", prchan ? "    " : "");
300 	if (*devices.name[0]) {
301 		printf("%4.4s ", devices.name[0]);
302 		param_print(&devices.params[0]);
303 	}
304 	else
305 		printf("     no device present\n");
306 	printf("%sSlave:  ", prchan ? "    " : "");
307 	if (*devices.name[1]) {
308 		printf("%4.4s ", devices.name[1]);
309 		param_print(&devices.params[1]);
310 	}
311 	else
312 		printf("     no device present\n");
313 }
314 
315 static void
316 ata_spindown(int fd, const char *dev, const char *arg)
317 {
318 	int tmo;
319 
320 	if (arg != NULL) {
321 		tmo = strtoul(arg, NULL, 0);
322 		if (ioctl(fd, IOCATASSPINDOWN, &tmo) < 0)
323 			err(1, "ioctl(IOCATASSPINDOWN)");
324 	} else {
325 		if (ioctl(fd, IOCATAGSPINDOWN, &tmo) < 0)
326 			err(1, "ioctl(IOCATAGSPINDOWN)");
327 		if (tmo == 0)
328 			printf("%s: idle spin down disabled\n", dev);
329 		else
330 			printf("%s: spin down after %d seconds idle\n",
331 			    dev, tmo);
332 	}
333 }
334 
335 static int
336 open_dev(const char *arg, int mode)
337 {
338 	int disk, fd;
339 	char device[64];
340 
341 	if (!(sscanf(arg, "ad%d", &disk) == 1 ||
342 	      sscanf(arg, "acd%d", &disk) == 1 ||
343 	      sscanf(arg, "afd%d", &disk) == 1 ||
344 	      sscanf(arg, "ast%d", &disk) == 1)) {
345 		fprintf(stderr, "natacontrol: Invalid device %s\n", arg);
346 		exit(EX_USAGE);
347 	}
348 	sprintf(device, "/dev/%s", arg);
349 	if ((fd = open(device, mode)) < 0)
350 		err(1, "device not found");
351 	return (fd);
352 }
353 
354 static int
355 ar_arg(const char *arg)
356 {
357 	int array;
358 
359 	if (!(sscanf(arg, "ar%d", &array) == 1)) {
360 		fprintf(stderr, "natacontrol: Invalid array %s\n", arg);
361 		exit(EX_USAGE);
362 	}
363 	return (array);
364 }
365 
366 static int
367 ata_arg(const char *arg)
368 {
369 	int channel;
370 
371 	if (!(sscanf(arg, "ata%d", &channel) == 1)) {
372 		fprintf(stderr, "natacontrol: Invalid channel %s\n", arg);
373 		exit(EX_USAGE);
374 	}
375 	return (channel);
376 }
377 
378 int
379 main(int argc, char **argv)
380 {
381 	int fd, mode, channel, array;
382 
383 	if (argc < 2)
384 		usage();
385 
386 	if (!strcmp(argv[1], "mode") && (argc == 3 || argc == 4)) {
387 		fd = open_dev(argv[2], O_RDONLY);
388 		if (argc == 4) {
389 			mode = str2mode(argv[3]);
390 			if (mode == -1)
391 				errx(1, "unknown mode");
392 			if (ioctl(fd, IOCATASMODE, &mode) < 0)
393 				warn("ioctl(IOCATASMODE)");
394 		}
395 		if (argc == 3 || argc == 4) {
396 			if (ioctl(fd, IOCATAGMODE, &mode) < 0)
397 				err(1, "ioctl(IOCATAGMODE)");
398 			printf("current mode = %s %s\n",
399 			    mode2str(mode), satarev2str(mode));
400 		}
401 		exit(EX_OK);
402 	}
403 	if (!strcmp(argv[1], "feature") && argc == 5) {
404 		struct ata_ioc_request request;
405 
406 		fd = open_dev(argv[2], O_RDONLY);
407 
408 		bzero(&request, sizeof(struct ata_ioc_request));
409 		request.u.ata.command = ATA_SETFEATURES;
410 		request.flags = ATA_CMD_CONTROL;
411 		request.timeout = 500;
412 		if (!strcmp(argv[3], "apm")) {
413 			if (!strcmp(argv[4], "off")) {
414 				request.u.ata.feature = ATA_SF_DIS_APM;
415 			} else if (!strcmp(argv[4], "maxperf")) {
416 				request.u.ata.feature = ATA_SF_ENAB_APM;
417 				request.u.ata.count = 0xfe;
418 			} else if (!strcmp(argv[4], "minpower")) {
419 				request.u.ata.feature = ATA_SF_ENAB_APM;
420 				request.u.ata.count = 0x01;
421 			} else {
422 				int offset = 0;
423 
424 				request.u.ata.feature = ATA_SF_ENAB_APM;
425 				if (argv[4][0] == 's') {
426 					offset = atoi(&argv[4][1]);
427 					request.u.ata.count = 0x01;
428 				} else {
429 					offset = atoi(&argv[4][1]);
430 					request.u.ata.count = 0x80;
431 				}
432 				if (offset >= 0 && offset <= 127)
433 					request.u.ata.count += offset;
434 			}
435 		} else if (!strcmp(argv[3], "acoustic")) {
436 			if (!strcmp(argv[4], "off")) {
437 				request.u.ata.feature = ATA_SF_DIS_ACCOUS;
438 			} else if (!strcmp(argv[4], "maxperf")) {
439 				request.u.ata.feature = ATA_SF_ENAB_ACCOUS;
440 				request.u.ata.count = 0xfe;
441 			} else if (!strcmp(argv[4], "maxquiet")) {
442 				request.u.ata.feature = ATA_SF_ENAB_ACCOUS;
443 				request.u.ata.count = 0x80;
444 			} else {
445 				request.u.ata.feature = ATA_SF_ENAB_ACCOUS;
446 				request.u.ata.count = atoi(argv[4]);
447 				if (request.u.ata.count > 124)
448 					request.u.ata.count = 124;
449 			}
450 		} else {
451 			usage();
452 		}
453 
454 		if (ioctl(fd, IOCATAREQUEST, &request) < 0)
455 			err(1, "ioctl(IOCATAREQUEST)");
456 
457 		if (request.error != 0) {
458 			fprintf(stderr,
459 			    "IOCATAREQUEST returned err status %d",
460 			    request.error);
461 			exit(EX_IOERR);
462 		}
463 		exit(EX_OK);
464 	}
465 	if (!strcmp(argv[1], "cap") && argc == 3) {
466 		fd = open_dev(argv[2], O_RDONLY);
467 		ata_cap_print(fd);
468 		exit(EX_OK);
469 	}
470 
471 	if (!strcmp(argv[1], "spindown") && (argc == 3 || argc == 4)) {
472 		fd = open_dev(argv[2], O_RDONLY);
473 		ata_spindown(fd, argv[2], argv[3]);
474 		exit(EX_OK);
475 	}
476 
477 	if ((fd = open("/dev/ata", O_RDWR)) < 0)
478 		err(1, "control device not found");
479 
480 	if (!strcmp(argv[1], "list") && argc == 2) {
481 		int maxchannel;
482 
483 		if (ioctl(fd, IOCATAGMAXCHANNEL, &maxchannel) < 0)
484 			err(1, "ioctl(IOCATAGMAXCHANNEL)");
485 		for (channel = 0; channel < maxchannel; channel++)
486 			info_print(fd, channel, 1);
487 		exit(EX_OK);
488 	}
489 	if (!strcmp(argv[1], "info") && argc == 3) {
490 		channel = ata_arg(argv[2]);
491 		info_print(fd, channel, 0);
492 		exit(EX_OK);
493 	}
494 	if (!strcmp(argv[1], "detach") && argc == 3) {
495 		channel = ata_arg(argv[2]);
496 		if (ioctl(fd, IOCATADETACH, &channel) < 0)
497 			err(1, "ioctl(IOCATADETACH)");
498 		exit(EX_OK);
499 	}
500 	if (!strcmp(argv[1], "attach") && argc == 3) {
501 		channel = ata_arg(argv[2]);
502 		if (ioctl(fd, IOCATAATTACH, &channel) < 0)
503 			err(1, "ioctl(IOCATAATTACH)");
504 		info_print(fd, channel, 0);
505 		exit(EX_OK);
506 	}
507 	if (!strcmp(argv[1], "reinit") && argc == 3) {
508 		channel = ata_arg(argv[2]);
509 		if (ioctl(fd, IOCATAREINIT, &channel) < 0)
510 			warn("ioctl(IOCATAREINIT)");
511 		info_print(fd, channel, 0);
512 		exit(EX_OK);
513 	}
514 	if (!strcmp(argv[1], "create")) {
515 		int disk, dev, offset;
516 		struct ata_ioc_raid_config config;
517 
518 		bzero(&config, sizeof(config));
519 		if (argc > 2) {
520 			if (!strcasecmp(argv[2], "RAID0") ||
521 			    !strcasecmp(argv[2], "stripe"))
522 				config.type = AR_RAID0;
523 			if (!strcasecmp(argv[2], "RAID1") ||
524 			    !strcasecmp(argv[2],"mirror"))
525 				config.type = AR_RAID1;
526 			if (!strcasecmp(argv[2], "RAID0+1") ||
527 			    !strcasecmp(argv[2],"RAID10"))
528 				config.type = AR_RAID01;
529 			if (!strcasecmp(argv[2], "RAID5"))
530 				config.type = AR_RAID5;
531 			if (!strcasecmp(argv[2], "SPAN"))
532 				config.type = AR_SPAN;
533 			if (!strcasecmp(argv[2], "JBOD"))
534 				config.type = AR_JBOD;
535 		}
536 		if (!config.type) {
537 			fprintf(stderr, "natacontrol: Invalid RAID type %s\n",
538 				argv[2]);
539 			fprintf(stderr, "natacontrol: Valid RAID types: \n");
540 			fprintf(stderr, "             stripe | mirror | "
541 					"RAID0 | RAID1 | RAID0+1 | RAID5 | "
542 					"SPAN | JBOD\n");
543 			exit(EX_USAGE);
544 		}
545 
546 		if (config.type == AR_RAID0 ||
547 		    config.type == AR_RAID01 ||
548 		    config.type == AR_RAID5) {
549 			if (argc < 4 ||
550 			    sscanf(argv[3], "%d", &config.interleave) != 1) {
551 				fprintf(stderr,
552 					"natacontrol: Invalid interleave %s\n",
553 					argv[3]);
554 				exit(EX_USAGE);
555 			}
556 			offset = 4;
557 		}
558 		else
559 			offset = 3;
560 
561 		for (disk = 0; disk < 16 && (offset + disk) < argc; disk++) {
562 			if (!(sscanf(argv[offset + disk], "ad%d", &dev) == 1)) {
563 				fprintf(stderr,
564 					"natacontrol: Invalid disk %s\n",
565 					argv[offset + disk]);
566 				exit(EX_USAGE);
567 			}
568 			config.disks[disk] = dev;
569 		}
570 
571 		if ((config.type == AR_RAID1 || config.type == AR_RAID01) &&
572 		    disk < 2) {
573 			fprintf(stderr, "natacontrol: At least 2 disks must be "
574 				"specified\n");
575 			exit(EX_USAGE);
576 		}
577 
578 		config.total_disks = disk;
579 		if (ioctl(fd, IOCATARAIDCREATE, &config) < 0)
580 			err(1, "ioctl(IOCATARAIDCREATE)");
581 		else
582 			printf("ar%d created\n", config.lun);
583 		exit(EX_OK);
584 	}
585 	if (!strcmp(argv[1], "delete") && argc == 3) {
586 		array = ar_arg(argv[2]);
587 		if (ioctl(fd, IOCATARAIDDELETE, &array) < 0)
588 			warn("ioctl(IOCATARAIDDELETE)");
589 		exit(EX_OK);
590 	}
591 	if (!strcmp(argv[1], "addspare") && argc == 4) {
592 		struct ata_ioc_raid_config config;
593 
594 		config.lun = ar_arg(argv[2]);
595 		if (!(sscanf(argv[3], "ad%d", &config.disks[0]) == 1)) {
596 			fprintf(stderr,
597 				"natacontrol: Invalid disk %s\n", argv[3]);
598 			usage();
599 		}
600 		if (ioctl(fd, IOCATARAIDADDSPARE, &config) < 0)
601 			warn("ioctl(IOCATARAIDADDSPARE)");
602 		exit(EX_OK);
603 	}
604 	if (!strcmp(argv[1], "rebuild") && argc == 3) {
605 		array = ar_arg(argv[2]);
606 		if (ioctl(fd, IOCATARAIDREBUILD, &array) < 0)
607 			warn("ioctl(IOCATARAIDREBUILD)");
608 		else {
609 			char device[64];
610 			char *buffer;
611 			ssize_t len;
612 			int arfd;
613 
614 			if (daemon(0, 1) == -1)
615 				err(1, "daemon");
616 			nice(20);
617 			snprintf(device, sizeof(device), "/dev/ar%d",
618 			    array);
619 			if ((arfd = open(device, O_RDONLY)) == -1)
620 				err(1, "open %s", device);
621 			if ((buffer = malloc(1024 * 1024)) == NULL)
622 				err(1, "malloc");
623 			while ((len = read(arfd, buffer, 1024 * 1024)) > 0)
624 				;
625 			if (len == -1)
626 				err(1, "read");
627 			else
628 				fprintf(stderr,
629 				    "natacontrol: ar%d rebuild completed\n",
630 				    array);
631 			free(buffer);
632 			close(arfd);
633 		}
634 		exit(EX_OK);
635 	}
636 	if (!strcmp(argv[1], "status") && argc == 3) {
637 		struct ata_ioc_raid_status status;
638 		int i, lun, state;
639 
640 		status.lun = ar_arg(argv[2]);
641 		if (ioctl(fd, IOCATARAIDSTATUS, &status) < 0)
642 			err(1, "ioctl(IOCATARAIDSTATUS)");
643 
644 		printf("ar%d: ATA ", status.lun);
645 		switch (status.type) {
646 		case AR_RAID0:
647 			printf("RAID0 stripesize=%d", status.interleave);
648 			break;
649 		case AR_RAID1:
650 			printf("RAID1");
651 			break;
652 		case AR_RAID01:
653 			printf("RAID0+1 stripesize=%d", status.interleave);
654 			break;
655 		case AR_RAID5:
656 			printf("RAID5 stripesize=%d", status.interleave);
657 			break;
658 		case AR_JBOD:
659 			printf("JBOD");
660 			break;
661 		case AR_SPAN:
662 			printf("SPAN");
663 			break;
664 		}
665 		printf(" status: ");
666 		switch (status.status) {
667 		case AR_READY:
668 			printf("READY\n");
669 			break;
670 		case AR_READY | AR_DEGRADED:
671 			printf("DEGRADED\n");
672 			break;
673 		case AR_READY | AR_DEGRADED | AR_REBUILDING:
674 			printf("REBUILDING %d%% completed\n",
675 				status.progress);
676 			break;
677 		default:
678 			printf("BROKEN\n");
679 		}
680 		printf(" subdisks:\n");
681 		for (i = 0; i < status.total_disks; i++) {
682 			printf("  %2d ", i);
683 			lun = status.disks[i].lun;
684 			state = status.disks[i].state;
685 			if (lun < 0)
686 				printf("---- ");
687 			else
688 				printf("ad%-2d ", lun);
689 			if (state & AR_DISK_ONLINE)
690 				printf("ONLINE");
691 			else if (state & AR_DISK_SPARE)
692 				printf("SPARE");
693 			else if (state & AR_DISK_PRESENT)
694 				printf("OFFLINE");
695 			else
696 				printf("MISSING");
697 			printf("\n");
698 		}
699 		exit(EX_OK);
700 	}
701 	usage();
702 	exit(EX_OK);
703 }
704