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