Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sbin / atacontrol / atacontrol.c
1 /*-
2  * Copyright (c) 2000,2001,2002 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  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD: src/sbin/atacontrol/atacontrol.c,v 1.11.2.5 2002/08/21 13:18:17 sos Exp $
29  */
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 #include <sys/ata.h>
38
39 char *
40 mode2str(int mode)
41 {
42         switch (mode) {
43         case ATA_PIO: return "BIOSPIO";
44         case ATA_PIO0: return "PIO0";
45         case ATA_PIO1: return "PIO1";
46         case ATA_PIO2: return "PIO2";
47         case ATA_PIO3: return "PIO3";
48         case ATA_PIO4: return "PIO4";
49         case ATA_WDMA2: return "WDMA2";
50         case ATA_UDMA2: return "UDMA33";
51         case ATA_UDMA4: return "UDMA66";
52         case ATA_UDMA5: return "UDMA100";
53         case ATA_UDMA6: return "UDMA133";
54         case ATA_DMA: return "BIOSDMA";
55         default: return "???";
56         }
57 }
58
59 int
60 str2mode(char *str)
61 {
62         if (!strcasecmp(str, "BIOSPIO")) return ATA_PIO;
63         if (!strcasecmp(str, "PIO0")) return ATA_PIO0;
64         if (!strcasecmp(str, "PIO1")) return ATA_PIO1;
65         if (!strcasecmp(str, "PIO2")) return ATA_PIO2;
66         if (!strcasecmp(str, "PIO3")) return ATA_PIO3;
67         if (!strcasecmp(str, "PIO4")) return ATA_PIO4;
68         if (!strcasecmp(str, "WDMA2")) return ATA_WDMA2;
69         if (!strcasecmp(str, "UDMA2")) return ATA_UDMA2;
70         if (!strcasecmp(str, "UDMA33")) return ATA_UDMA2;
71         if (!strcasecmp(str, "UDMA4")) return ATA_UDMA4;
72         if (!strcasecmp(str, "UDMA66")) return ATA_UDMA4;
73         if (!strcasecmp(str, "UDMA5")) return ATA_UDMA5;
74         if (!strcasecmp(str, "UDMA100")) return ATA_UDMA5;
75         if (!strcasecmp(str, "UDMA6")) return ATA_UDMA6;
76         if (!strcasecmp(str, "UDMA133")) return ATA_UDMA6;
77         if (!strcasecmp(str, "BIOSDMA")) return ATA_DMA;
78         return -1;
79 }
80
81
82 void
83 usage()
84 {
85         fprintf(stderr, "usage: atacontrol <command> channel [args]\n");
86         exit(1);
87 }
88
89 int
90 version(int version)
91 {
92         int bit;
93     
94         if (version == 0xffff)
95                 return 0;
96         for (bit = 15; bit >= 0; bit--)
97                 if (version & (1<<bit))
98                         return bit;
99         return 0;
100 }
101
102 void
103 param_print(struct ata_params *parm)
104 {
105         printf("<%.40s/%.8s> ATA/ATAPI rev %d\n",
106                 parm->model, parm->revision, version(parm->version_major)); 
107 }
108
109 void
110 cap_print(struct ata_params *parm)
111 {
112         printf("\n");
113         printf("ATA/ATAPI revision    %d\n", version(parm->version_major));
114         printf("device model          %.40s\n", parm->model);
115         printf("serial number         %.20s\n", parm->serial);
116         printf("firmware revision     %.8s\n", parm->revision);
117
118         printf("cylinders             %d\n", parm->cylinders);
119         printf("heads                 %d\n", parm->heads);
120         printf("sectors/track         %d\n", parm->sectors);    
121         
122         printf("lba%ssupported         ", parm->support_lba ? " " : " not ");
123         if (parm->lba_size)
124                 printf("%d sectors\n", parm->lba_size); 
125         else
126                 printf("\n");
127
128         printf("lba48%ssupported         ", parm->support.address48 ? " " : " not ");
129         if (parm->lba_size48)
130                 printf("%lld sectors\n", parm->lba_size48);     
131         else
132                 printf("\n");
133         printf("dma%ssupported\n", parm->support_dma ? " " : " not");
134
135         printf("overlap%ssupported\n", parm->support_queueing ? " " : " not ");
136   
137         printf("\nFeature                      Support  Enable    Value   Vendor\n");
138
139         printf("write cache                    %s       %s\n",
140                 parm->support.write_cache ? "yes" : "no",
141                 parm->enabled.write_cache ? "yes" : "no");      
142
143         printf("read ahead                     %s       %s\n",
144                 parm->support.look_ahead ? "yes" : "no",
145                 parm->enabled.look_ahead ? "yes" : "no");       
146
147         printf("dma queued                     %s       %s      %d/%02X\n",
148                 parm->support.queued ? "yes" : "no",
149                 parm->enabled.queued ? "yes" : "no",
150                 parm->queuelen, parm->queuelen);        
151
152         printf("SMART                          %s       %s\n",
153                 parm->support.smart ? "yes" : "no",
154                 parm->enabled.smart ? "yes" : "no");
155
156         printf("microcode download             %s       %s\n",
157                 parm->support.microcode ? "yes" : "no",
158                 parm->enabled.microcode ? "yes" : "no");        
159
160         printf("security                       %s       %s\n",
161                 parm->support.smart ? "yes" : "no",
162                 parm->enabled.smart ? "yes" : "no");    
163
164         printf("power management               %s       %s\n",
165                 parm->support.power_mngt ? "yes" : "no",
166                 parm->enabled.power_mngt ? "yes" : "no");       
167
168         printf("advanced power management      %s       %s      %d/%02X\n",
169                 parm->support.apm ? "yes" : "no",
170                 parm->enabled.apm ? "yes" : "no",
171                 parm->apm_value, parm->apm_value);
172
173         printf("automatic acoustic management  %s       %s      %d/%02X %d/%02X\n",
174                 parm->support.auto_acoustic ? "yes" : "no",
175                 parm->enabled.auto_acoustic ? "yes" : "no",
176                 parm->current_acoustic, parm->current_acoustic,
177                 parm->vendor_acoustic, parm->vendor_acoustic);  
178 }
179
180 int
181 ata_cap_print(int fd, int channel, int device)
182 {
183         struct ata_cmd iocmd;
184
185         bzero(&iocmd, sizeof(struct ata_cmd));
186
187         iocmd.channel = channel;
188         iocmd.device = -1;
189         iocmd.cmd = ATAGPARM;
190
191         if (ioctl(fd, IOCATA, &iocmd) < 0)
192                 return errno;
193
194         printf("ATA channel %d, %s", channel, device==0 ? "Master" : "Slave");
195
196         if (iocmd.u.param.type[device]) {
197                 printf(", device %s:\n", iocmd.u.param.name[device]);
198                 cap_print(&iocmd.u.param.params[device]);
199         }
200         else
201                 printf(": no device present\n");
202         return 0;
203 }
204
205 int
206 info_print(int fd, int channel, int prchan)
207 {
208         struct ata_cmd iocmd;
209
210         bzero(&iocmd, sizeof(struct ata_cmd));
211         iocmd.channel = channel;
212         iocmd.device = -1;
213         iocmd.cmd = ATAGPARM;
214         if (ioctl(fd, IOCATA, &iocmd) < 0)
215                 return errno;
216         if (prchan)
217                 printf("ATA channel %d:\n", channel);
218         printf("%sMaster: ", prchan ? "    " : "");
219         if (iocmd.u.param.type[0]) {
220                 printf("%4.4s ", iocmd.u.param.name[0]);
221                 param_print(&iocmd.u.param.params[0]);
222         }
223         else
224                 printf("     no device present\n");
225         printf("%sSlave:  ", prchan ? "    " : "");
226         if (iocmd.u.param.type[1]) {
227                 printf("%4.4s ", iocmd.u.param.name[1]);
228                 param_print(&iocmd.u.param.params[1]);
229         }
230         else
231                 printf("     no device present\n");
232         return 0;
233 }
234
235 int
236 main(int argc, char **argv)
237 {
238         struct ata_cmd iocmd;
239         int fd;
240
241         if ((fd = open("/dev/ata", O_RDWR)) < 0)
242                 err(1, "control device not found");
243
244         if (argc < 2)
245                 usage();
246
247         bzero(&iocmd, sizeof(struct ata_cmd));
248
249         if (argc > 2 && strcmp(argv[1], "create")) {
250                 int chan;
251
252                 if (!strcmp(argv[1], "delete") ||
253                     !strcmp(argv[1], "status") ||
254                     !strcmp(argv[1], "rebuild")) {
255                         if (!(sscanf(argv[2], "%d", &chan) == 1 ||
256                               sscanf(argv[2], "ar%d", &chan) == 1))
257                                 usage();
258                 }
259                 else {
260                         if (!(sscanf(argv[2], "%d", &chan) == 1 ||
261                               sscanf(argv[2], "ata%d", &chan) == 1))
262                                 usage();
263                 }
264                 iocmd.channel = chan;
265         }
266
267         if (!strcmp(argv[1], "list") && argc == 2) {
268                 int unit = 0;
269
270                 while (info_print(fd, unit++, 1) != ENXIO);
271         }
272         else if (!strcmp(argv[1], "info") && argc == 3) {
273                 info_print(fd, iocmd.channel, 0);
274         }
275         else if (!strcmp(argv[1], "cap") && argc == 4) {
276                 ata_cap_print(fd, iocmd.channel, atoi(argv[3]));
277         }
278         else if (!strcmp(argv[1], "enclosure") && argc == 4) {
279                 iocmd.device = atoi(argv[3]);
280                 iocmd.cmd = ATAENCSTAT;
281                 if (ioctl(fd, IOCATA, &iocmd) < 0)
282                         err(1, "ioctl(ATAENCSTAT)");
283                 printf("fan RPM: %d temp: %.1f 5V: %.2f 12V: %.2f\n",
284                         iocmd.u.enclosure.fan,
285                         (double)iocmd.u.enclosure.temp / 10,
286                         (double)iocmd.u.enclosure.v05 / 1000,
287                         (double)iocmd.u.enclosure.v12 / 1000);
288         }
289         else if (!strcmp(argv[1], "detach") && argc == 3) {
290                 iocmd.cmd = ATADETACH;
291                 if (ioctl(fd, IOCATA, &iocmd) < 0)
292                         err(1, "ioctl(ATADETACH)");
293         }
294         else if (!strcmp(argv[1], "attach") && argc == 3) {
295                 iocmd.cmd = ATAATTACH;
296                 if (ioctl(fd, IOCATA, &iocmd) < 0)
297                         err(1, "ioctl(ATAATTACH)");
298                 info_print(fd, iocmd.channel, 0);
299         }
300         else if (!strcmp(argv[1], "reinit") && argc == 3) {
301                 iocmd.cmd = ATAREINIT;
302                 if (ioctl(fd, IOCATA, &iocmd) < 0)
303                         warn("ioctl(ATAREINIT)");
304                 info_print(fd, iocmd.channel, 0);
305         }
306         else if (!strcmp(argv[1], "create")) {
307                 int disk, dev, offset;
308
309                 iocmd.cmd = ATARAIDCREATE;
310                 if (!strcmp(argv[2], "RAID0") || !strcmp(argv[2], "stripe"))
311                         iocmd.u.raid_setup.type = 1;
312                 if (!strcmp(argv[2], "RAID1") || !strcmp(argv[2],"mirror"))
313                         iocmd.u.raid_setup.type = 2;
314                 if (!strcmp(argv[2], "RAID0+1"))
315                         iocmd.u.raid_setup.type = 3;
316                 if (!strcmp(argv[2], "SPAN") || !strcmp(argv[2], "JBOD"))
317                         iocmd.u.raid_setup.type = 4;
318                 if (!iocmd.u.raid_setup.type)
319                      usage();
320                 
321                 if (iocmd.u.raid_setup.type & 1) {
322                         if (!sscanf(argv[3], "%d",
323                                     &iocmd.u.raid_setup.interleave) == 1)
324                                 usage();
325                         offset = 4;
326                 }
327                 else
328                         offset = 3;
329                 
330                 for (disk = 0; disk < 16 && (offset + disk) < argc; disk++) {
331                         if (!(sscanf(argv[offset + disk], "%d", &dev) == 1 ||
332                               sscanf(argv[offset + disk], "ad%d", &dev) == 1))
333                                 usage();
334                         iocmd.u.raid_setup.disks[disk] = dev;
335                 }
336                 iocmd.u.raid_setup.total_disks = disk;
337                 if (ioctl(fd, IOCATA, &iocmd) < 0)
338                         err(1, "ioctl(ATARAIDCREATE)");
339                 else
340                         printf("ar%d created\n", iocmd.u.raid_setup.unit);
341         }
342         else if (!strcmp(argv[1], "delete") && argc == 3) {
343                 iocmd.cmd = ATARAIDDELETE;
344                 if (ioctl(fd, IOCATA, &iocmd) < 0)
345                         warn("ioctl(ATARAIDDELETE)");
346         }
347         else if (!strcmp(argv[1], "rebuild") && argc == 3) {
348                 iocmd.cmd = ATARAIDREBUILD;
349                 if (ioctl(fd, IOCATA, &iocmd) < 0)
350                         warn("ioctl(ATARAIDREBUILD)");
351         }
352         else if (!strcmp(argv[1], "status") && argc == 3) {
353                 int i;
354
355                 iocmd.cmd = ATARAIDSTATUS;
356                 if (ioctl(fd, IOCATA, &iocmd) < 0)
357                         err(1, "ioctl(ATARAIDSTATUS)");
358                 printf("ar%d: ATA ", iocmd.channel);
359                 switch (iocmd.u.raid_status.type) {
360                 case AR_RAID0:
361                         printf("RAID0");
362                         break;
363                 case AR_RAID1:
364                         printf("RAID1");
365                         break;
366                 case AR_RAID0 | AR_RAID1:
367                         printf("RAID0+1");
368                         break;
369                 case AR_SPAN:
370                         printf("SPAN");
371                         break;
372                 }
373                 printf(" subdisks: ");
374                 for (i = 0; i < iocmd.u.raid_status.total_disks; i++) {
375                         if (iocmd.u.raid_status.disks[i] >= 0)
376                                 printf("ad%d ", iocmd.u.raid_status.disks[i]);
377                         else
378                                 printf("DOWN ");
379                 }
380                 printf("status: ");
381                 switch (iocmd.u.raid_status.status) {
382                 case AR_READY:
383                         printf("READY\n");
384                         break;
385                 case AR_READY | AR_DEGRADED:
386                         printf("DEGRADED\n");
387                         break;
388                 case AR_READY | AR_DEGRADED | AR_REBUILDING:
389                         printf("REBUILDING %d%% completed\n",
390                                 iocmd.u.raid_status.progress);
391                         break;
392                 default:
393                         printf("BROKEN\n");
394                 }
395         }
396         else if (!strcmp(argv[1], "mode") && (argc == 3 || argc == 5)) {
397                 if (argc == 5) {
398                         iocmd.cmd = ATASMODE;
399                         iocmd.device = -1;
400                         iocmd.u.mode.mode[0] = str2mode(argv[3]);
401                         iocmd.u.mode.mode[1] = str2mode(argv[4]);
402                         if (ioctl(fd, IOCATA, &iocmd) < 0)
403                                 warn("ioctl(ATASMODE)");
404                 }
405                 if (argc == 3 || argc == 5) {
406                         iocmd.cmd = ATAGMODE;
407                         iocmd.device = -1;
408                         if (ioctl(fd, IOCATA, &iocmd) < 0)
409                                 err(1, "ioctl(ATAGMODE)");
410                         printf("Master = %s \nSlave  = %s\n",
411                                 mode2str(iocmd.u.mode.mode[0]), 
412                                 mode2str(iocmd.u.mode.mode[1]));
413                 }
414         }
415         else
416                 usage();
417         exit(0);
418 }