2 * Copyright (c) 2002, Pierre David <Pierre.David@crc.u-strasbg.fr>
3 * Copyright (c) 2006, Jung-uk Kim <jkim@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice unmodified, this list of conditions, and the following
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/cdefs.h>
37 #include <sys/ioctl.h>
39 #include <machine/param.h>
41 #include <dev/pci/amrio.h>
42 #include <dev/pci/amrreg.h>
45 #define SLEEPTIME 100000 /* microseconds */
47 int nattempts
= NATTEMPTS
; /* # of attempts before giving up */
48 int sleeptime
= SLEEPTIME
; /* between attempts, in ms */
50 #define AMR_BUFSIZE 1024
52 int enq_result
= AMR_STATUS_FAILED
;
53 char enq_buffer
[AMR_BUFSIZE
];
55 #define AMR_MAX_NCTRLS 16
56 #define AMR_MAX_NSDEVS 16
61 * Include lookup tables, and a function to match a code to a string.
63 * XXX Lookup tables cannot be included, since they require symbols from
64 * amrreg.h which need in turn the _KERNEL define.
67 /* #define AMR_DEFINE_TABLES */
68 /* #include "amr_tables.h" */
70 int amr_ioctl_enquiry(int, u_int8_t
, u_int8_t
, u_int8_t
);
72 int describe_card(int, int, int);
73 char * describe_property(u_int8_t
, char *);
74 const char * describe_state(int, u_int8_t
);
75 void describe_battery(int, int, int, int, int);
76 void describe_one_volume(int, int, u_int32_t
, u_int8_t
, u_int8_t
);
77 void describe_one_drive(int, int, u_int8_t
);
78 void describe_drive(int, int, int, int, int);
81 * Offsets in an amr_user_ioctl.au_cmd [] array See amrio.h
90 #define FIRMWARE_40LD 1
91 #define FIRMWARE_8LD 2
95 const uint32_t signature
;
97 { "Series 431", AMR_SIG_431
},
98 { "Series 438", AMR_SIG_438
},
99 { "Series 762", AMR_SIG_762
},
100 { "Integrated HP NetRAID (T5)", AMR_SIG_T5
},
101 { "Series 466", AMR_SIG_466
},
102 { "Series 467", AMR_SIG_467
},
103 { "Integrated HP NetRAID (T7)", AMR_SIG_T7
},
104 { "Series 490", AMR_SIG_490
}
109 const char *ifyes
, *ifno
;
112 "writeback", "write-through" },
114 "read-ahead", "no-read-ahead" },
116 "adaptative-io", "no-adaptative-io" }
123 { AMR_DRV_OFFLINE
, "offline" },
124 { AMR_DRV_DEGRADED
, "degraded" },
125 { AMR_DRV_OPTIMAL
, "optimal" },
126 { AMR_DRV_ONLINE
, "online" },
127 { AMR_DRV_FAILED
, "failed" },
128 { AMR_DRV_REBUILD
, "rebuild" },
129 { AMR_DRV_HOTSPARE
, "hotspare" }
136 { AMR_BATT_MODULE_MISSING
, "not present" },
137 { AMR_BATT_LOW_VOLTAGE
, "low voltage" },
138 { AMR_BATT_TEMP_HIGH
, "high temperature" },
139 { AMR_BATT_PACK_MISSING
, "pack missing" },
140 { AMR_BATT_CYCLES_EXCEEDED
, "cycle exceeded" }
147 { AMR_BATT_CHARGE_DONE
, "charge done" },
148 { AMR_BATT_CHARGE_INPROG
, "charge in progress" },
149 { AMR_BATT_CHARGE_FAIL
, "charge failed" }
152 #define NTAB(tab) (sizeof tab / sizeof tab [0])
155 amr_ioctl_enquiry(int fd
, u_int8_t cmd
, u_int8_t cmdsub
, u_int8_t cmdqual
)
157 struct amr_user_ioctl am
;
160 am
.au_cmd
[MB_COMMAND
] = cmd
;
161 am
.au_cmd
[MB_CHANNEL
] = cmdsub
;
162 am
.au_cmd
[MB_PARAM
] = cmdqual
;
163 am
.au_cmd
[MB_PAD
] = 0;
164 am
.au_cmd
[MB_DRIVE
] = 0;
166 am
.au_buffer
= enq_buffer
;
167 am
.au_length
= AMR_BUFSIZE
;
168 am
.au_direction
= AMR_IO_READ
;
173 while (i
< nattempts
&& r
== -1) {
174 r
= ioctl(fd
, AMR_IO_COMMAND
, &am
);
176 if (errno
!= EBUSY
) {
177 perror("ioctl enquiry");
190 fprintf(stderr
, "usage: %s stat [-a num] [-b] "
191 "[-c ctlr|-f dev] [-g] [-l vol]\n\t\t"
192 "[-p drive|-s bus[:target]] [-t usec] [-v]\n\n\t"
193 "-a num\t\tnumber of retries\n\t"
194 "-b\t\tbattery status\n\t"
195 "-c ctrl\t\tcontroller ID\n\t"
196 "-f dev\t\tdevice path\n\t"
197 "-g\t\tprint global parameters\n\t"
198 "-l vol\t\tlogical volume ID\n\t"
199 "-p drive\tphysical drive ID\n\t"
200 "-s bus[:target]\tSCSI bus (and optinal target)\n\t"
201 "-t usec\t\tsleep time between retries\n\t"
202 "-v\t\tverbose output\n",
207 /******************************************************************************
212 describe_card(int fd
, int verbosity
, int globalparam
)
214 struct amr_enquiry
*ae
;
218 * Try the 40LD firmware interface
221 enq_result
= amr_ioctl_enquiry(fd
, AMR_CMD_CONFIG
,
222 AMR_CONFIG_PRODUCT_INFO
, 0);
223 if (enq_result
== AMR_STATUS_SUCCESS
) {
224 struct amr_prodinfo
*ap
;
226 ap
= (struct amr_prodinfo
*)enq_buffer
;
227 nschan
= ap
->ap_nschan
;
229 printf("Product\t\t\t<%.80s>\n", ap
->ap_product
);
230 printf("Firmware\t\t%.16s\n", ap
->ap_firmware
);
231 printf("BIOS\t\t\t%.16s\n", ap
->ap_bios
);
232 printf("SCSI channels\t\t%d\n", ap
->ap_nschan
);
233 printf("Fibre loops\t\t%d\n", ap
->ap_fcloops
);
234 printf("Memory size\t\t%d MB\n", ap
->ap_memsize
);
235 if (verbosity
>= 1) {
236 printf("Ioctl\t\t\t%d (%s)\n", FIRMWARE_40LD
,
238 printf("Signature\t\t0x%08x\n",
240 printf("Configsig\t\t0x%08x\n",
242 printf("Subsystem\t\t0x%04x\n",
244 printf("Subvendor\t\t0x%04x\n",
246 printf("Notify counters\t\t%d\n",
247 ap
->ap_numnotifyctr
);
250 return FIRMWARE_40LD
;
253 * Try the 8LD firmware interface
256 enq_result
= amr_ioctl_enquiry(fd
, AMR_CMD_EXT_ENQUIRY2
, 0, 0);
257 ae
= (struct amr_enquiry
*)enq_buffer
;
258 if (enq_result
== AMR_STATUS_SUCCESS
) {
259 cardtype
= ae
->ae_signature
;
261 enq_result
= amr_ioctl_enquiry(fd
, AMR_CMD_ENQUIRY
, 0, 0);
265 if (enq_result
== AMR_STATUS_SUCCESS
) {
268 const char *product
= NULL
;
269 char bios
[100], firmware
[100];
272 for (i
= 0; i
< NTAB(prodtable
); i
++) {
273 if (cardtype
== prodtable
[i
].signature
) {
274 product
= prodtable
[i
].product
;
279 product
= "unknown card signature";
282 * HP NetRaid controllers have a special encoding of
283 * the firmware and BIOS versions. The AMI version
284 * seems to have it as strings whereas the HP version
285 * does it with a leading uppercase character and two
289 if (ae
->ae_adapter
.aa_firmware
[2] >= 'A' &&
290 ae
->ae_adapter
.aa_firmware
[2] <= 'Z' &&
291 ae
->ae_adapter
.aa_firmware
[1] < ' ' &&
292 ae
->ae_adapter
.aa_firmware
[0] < ' ' &&
293 ae
->ae_adapter
.aa_bios
[2] >= 'A' &&
294 ae
->ae_adapter
.aa_bios
[2] <= 'Z' &&
295 ae
->ae_adapter
.aa_bios
[1] < ' ' &&
296 ae
->ae_adapter
.aa_bios
[0] < ' ') {
299 * looks like we have an HP NetRaid version
303 if (cardtype
== AMR_SIG_438
) {
305 * the AMI 438 is a NetRaid 3si in
308 product
= "HP NetRaid 3si";
310 sprintf(firmware
, "%c.%02d.%02d",
311 ae
->ae_adapter
.aa_firmware
[2],
312 ae
->ae_adapter
.aa_firmware
[1],
313 ae
->ae_adapter
.aa_firmware
[0]);
314 sprintf(bios
, "%c.%02d.%02d",
315 ae
->ae_adapter
.aa_bios
[2],
316 ae
->ae_adapter
.aa_bios
[1],
317 ae
->ae_adapter
.aa_bios
[0]);
319 sprintf(firmware
, "%.4s",
320 ae
->ae_adapter
.aa_firmware
);
321 sprintf(bios
, "%.4s", ae
->ae_adapter
.aa_bios
);
324 printf("Ioctl = %d (%s)\n", FIRMWARE_8LD
, "8LD");
325 printf("Product =\t<%s>\n", product
);
326 printf("Firmware =\t%s\n", firmware
);
327 printf("BIOS =\t%s\n", bios
);
328 /* printf ("SCSI Channels =\t%d\n", ae->ae_nschan); */
329 /* printf ("Fibre Loops =\t%d\n", ae->ae_fcloops); */
330 printf("Memory size =\t%d MB\n",
331 ae
->ae_adapter
.aa_memorysize
);
333 * printf ("Notify counters =\t%d\n",
334 * ae->ae_numnotifyctr) ;
340 * Neither firmware interface succeeded. Abort.
343 fprintf(stderr
, "Firmware interface not supported\n");
349 describe_property(u_int8_t prop
, char *buffer
)
354 for (i
= 0; i
< NTAB(proptable
); i
++) {
357 if (prop
& proptable
[i
].code
)
358 strcat(buffer
, proptable
[i
].ifyes
);
360 strcat(buffer
, proptable
[i
].ifno
);
368 describe_state(int verbosity
, u_int8_t state
)
372 if ((AMR_DRV_PREVSTATE(state
) == AMR_DRV_CURSTATE(state
)) &&
373 (AMR_DRV_CURSTATE(state
) == AMR_DRV_OFFLINE
) && verbosity
== 0)
376 for (i
= 0; i
< NTAB(statetable
); i
++)
377 if (AMR_DRV_CURSTATE(state
) == statetable
[i
].code
)
378 return (statetable
[i
].status
);
383 /******************************************************************************
387 describe_battery(int fd
, int verbosity
, int fwint
, int bflags
, int globalparam
)
389 u_int8_t batt_status
;
392 if (fwint
== FIRMWARE_40LD
) {
393 enq_result
= amr_ioctl_enquiry(fd
, AMR_CMD_CONFIG
,
394 AMR_CONFIG_ENQ3
, AMR_CONFIG_ENQ3_SOLICITED_FULL
);
395 if (enq_result
== AMR_STATUS_SUCCESS
) {
396 struct amr_enquiry3
*ae3
;
398 ae3
= (struct amr_enquiry3
*)enq_buffer
;
399 if (bflags
|| globalparam
) {
400 batt_status
= ae3
->ae_batterystatus
;
401 printf("Battery status\t\t");
402 for (i
= 0; i
< NTAB(battable
); i
++) {
403 if (batt_status
& battable
[i
].code
)
404 printf("%s, ", battable
[i
].status
);
407 (AMR_BATT_MODULE_MISSING
|AMR_BATT_PACK_MISSING
))) {
408 for (i
= 0; i
< NTAB(bcstatble
); i
++)
409 if (bcstatble
[i
].code
==
410 (batt_status
& AMR_BATT_CHARGE_MASK
))
411 printf("%s", bcstatble
[i
].status
);
413 printf("charge unknown");
415 printf(" (0x%02x)", batt_status
);
419 } else if (fwint
== FIRMWARE_8LD
) {
420 /* Nothing to do here. */
423 fprintf(stderr
, "Firmware interface not supported.\n");
430 /******************************************************************************
435 describe_one_volume(int ldrv
, int verbosity
,
436 u_int32_t size
, u_int8_t state
, u_int8_t prop
)
440 char propstr
[MAXPATHLEN
];
441 const char *statestr
;
443 szgb
= ((float)size
) / (1024 * 1024 * 2); /* size in GB */
445 raid_level
= prop
& AMR_DRV_RAID_MASK
;
447 printf("Logical volume %d\t", ldrv
);
448 statestr
= describe_state(verbosity
, state
);
449 printf("%s ", statestr
);
450 printf("(%.2f GB, RAID%d", szgb
, raid_level
);
451 if (verbosity
>= 1) {
452 describe_property(prop
, propstr
);
453 printf(" %s", propstr
);
458 /******************************************************************************
463 describe_one_drive(int pdrv
, int verbosity
, u_int8_t state
)
465 const char *statestr
;
467 statestr
= describe_state(verbosity
, state
);
470 printf("Physical drive %d:%d\t%s\n",
471 pdrv
/ AMR_MAX_NSDEVS
, pdrv
% AMR_MAX_NSDEVS
,
474 printf("Physical drive %d:\t%s\n", pdrv
, statestr
);
479 describe_drive(int verbosity
, int fwint
, int ldrv
, int sbus
, int sdev
)
483 if (sbus
> -1 && sdev
> -1)
484 pdrv
= (sbus
* AMR_MAX_NSDEVS
) + sdev
;
486 if (sbus
> -1 && sbus
>= nschan
) {
487 fprintf(stderr
, "SCSI channel %d does not exist.\n", sbus
);
489 } else if (sdev
> -1 && sdev
>= AMR_MAX_NSDEVS
) {
490 fprintf(stderr
, "SCSI device %d:%d does not exist.\n",
495 if (fwint
== FIRMWARE_40LD
) {
496 if (enq_result
== AMR_STATUS_SUCCESS
) {
497 struct amr_enquiry3
*ae3
;
499 ae3
= (struct amr_enquiry3
*)enq_buffer
;
500 if ((ldrv
< 0 && sbus
< 0) || ldrv
>= 0) {
501 if (ldrv
>= ae3
->ae_numldrives
) {
502 fprintf(stderr
, "Logical volume %d "
503 "does not exist.\n", ldrv
);
508 drv
< ae3
->ae_numldrives
;
510 describe_one_volume(drv
,
512 ae3
->ae_drivesize
[drv
],
513 ae3
->ae_drivestate
[drv
],
514 ae3
->ae_driveprop
[drv
]);
516 describe_one_volume(ldrv
,
518 ae3
->ae_drivesize
[ldrv
],
519 ae3
->ae_drivestate
[ldrv
],
520 ae3
->ae_driveprop
[ldrv
]);
523 if ((ldrv
< 0 && sbus
< 0) || sbus
>= 0) {
524 if (pdrv
>= AMR_40LD_MAXPHYSDRIVES
||
525 (nschan
!= 0 && pdrv
>= (nschan
* AMR_MAX_NSDEVS
))) {
526 fprintf(stderr
, "Physical drive %d "
527 "is out of range.\n", pdrv
);
532 drv
< AMR_40LD_MAXPHYSDRIVES
;
535 drv
>= (nschan
* AMR_MAX_NSDEVS
))
537 describe_one_drive(drv
,
539 ae3
->ae_pdrivestate
[drv
]);
541 } else if (sdev
< 0) {
542 for (drv
= sbus
* AMR_MAX_NSDEVS
;
543 drv
< ((sbus
+ 1) * AMR_MAX_NSDEVS
);
546 drv
>= (nschan
* AMR_MAX_NSDEVS
))
548 describe_one_drive(drv
,
550 ae3
->ae_pdrivestate
[drv
]);
554 pdrv
< (nschan
* AMR_MAX_NSDEVS
))
555 describe_one_drive(pdrv
, 1,
556 ae3
->ae_pdrivestate
[pdrv
]);
560 } else if (fwint
== FIRMWARE_8LD
) {
561 /* Nothing to do here. */
564 fprintf(stderr
, "Firmware interface not supported.\n");
569 /******************************************************************************
574 main(int argc
, char *argv
[])
578 int globalparam
= 0, verbosity
= 0;
579 int bflags
= 0, fflags
= 0, sflags
= 0;
580 int lvolno
= -1, physno
= -1;
581 int sbusno
= -1, targetno
= -1;
582 char filename
[MAXPATHLEN
];
583 char sdev
[MAXPATHLEN
];
594 if (strcmp(argv
[1], "stat") != 0) /* only stat implemented for now */
598 while ((i
= getopt(argc
, argv
, "a:bc:f:gl:p:s:t:v")) != -1)
601 nattempts
= atoi(optarg
);
607 snprintf(filename
, MAXPATHLEN
, "%s", optarg
);
608 filename
[MAXPATHLEN
- 1] = '\0';
615 lvolno
= atoi(optarg
);
618 physno
= atoi(optarg
);
621 snprintf(sdev
, MAXPATHLEN
, "%s", optarg
);
622 sdev
[MAXPATHLEN
- 1] = '\0';
626 sleeptime
= atoi(optarg
);
642 snprintf(filename
, MAXPATHLEN
, "/dev/amr0");
645 fd
= open(filename
, O_RDONLY
);
650 if (ioctl(fd
, AMR_IO_VERSION
, &i
) == -1) {
651 perror("ioctl version");
660 if ((pdev
= index(sdev
, ':')))
661 targetno
= atoi(++pdev
);
663 } else if (physno
> -1) {
664 sbusno
= physno
/ AMR_MAX_NSDEVS
;
665 targetno
= physno
% AMR_MAX_NSDEVS
;
668 if (globalparam
&& verbosity
>= 1)
669 printf("Version\t\t\t%d\n", i
);
672 fprintf(stderr
, "Driver version (%d) not supported\n", i
);
677 i
= describe_card(fd
, verbosity
, globalparam
);
678 describe_battery(fd
, verbosity
, i
, bflags
, globalparam
);
679 if (!bflags
|| lvolno
> -1 || physno
> -1 || sbusno
> -1 || targetno
> -1)
680 describe_drive(verbosity
, i
, lvolno
, sbusno
, targetno
);