/*- * Copyright (c) 2002, Pierre David * Copyright (c) 2006, 2007, Jung-uk Kim * 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 unmodified, 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 ``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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #ifndef __packed #define __packed __attribute__ ((packed)) #endif #include "amrio.h" #include "amrreg.h" #include "amrstat.h" static void usage(char *prog) { fprintf(stderr, "usage: %s [-a num] [-b] " "[-c ctlr | -f dev] [-g] [-l vol]\n\t\t" "[-p drive | -s bus[:target]] [-t usec] [-v]\n\n\t" "-a num\t\tnumber of retries\n\t" "-b\t\tbattery status\n\t" "-c ctrl\t\tcontroller ID\n\t" "-f dev\t\tdevice path\n\t" "-g\t\tprint global parameters\n\t" "-l vol\t\tlogical volume ID\n\t" "-p drive\tphysical drive ID\n\t" "-s bus[:target]\tSCSI bus (and optinal target)\n\t" "-t usec\t\tsleep time between retries\n\t" "-v\t\tverbose output\n", prog); exit(1); } static void clean_exit(int fd) { if (fd >= 0) close(fd); if (amr_enq.buffer != NULL) free(amr_enq.buffer); exit(1); } static int amr_ioctl_enquiry(int fd, uint8_t cmd, uint8_t cmdsub, uint8_t cmdqual) { struct amr_user_ioctl am; int i, r; /* Use the cached result if it is available. */ if (amr_enq.cmd == cmd && amr_enq.cmdsub == cmdsub && amr_enq.cmdqual == cmdqual) return (amr_enq.result); amr_enq.result = AMR_STATUS_FAILED; am.au_cmd[MB_COMMAND] = cmd; am.au_cmd[MB_CHANNEL] = cmdsub; am.au_cmd[MB_PARAM] = cmdqual; am.au_cmd[MB_PAD] = 0; am.au_cmd[MB_DRIVE] = 0; am.au_buffer = amr_enq.buffer; am.au_length = AMR_BUFSIZE; am.au_direction = AMR_IO_READ; am.au_status = 0; i = 0; r = -1; while (i < nattempts && r == -1) { r = ioctl(fd, AMR_IO_COMMAND, &am); if (r == -1) { if (errno != EBUSY) { perror("ioctl enquiry"); clean_exit(fd); } else usleep(sleeptime); } i++; } /* Save the enquiry result. */ amr_enq.cmd = cmd; amr_enq.cmdsub = cmdsub; amr_enq.cmdqual = cmdqual; amr_enq.result = am.au_status; return (am.au_status); } /****************************************************************************** * Card description */ static int describe_card(int fd, int verbosity, int globalparam) { int enq_level; int enq_result; /* * Try the 40LD firmware interface */ enq_result = amr_ioctl_enquiry(fd, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0); if (enq_result == AMR_STATUS_SUCCESS) { struct amr_prodinfo *ap; ap = (struct amr_prodinfo *)amr_enq.buffer; nschan = ap->ap_nschan; if (globalparam) { printf("Product\t\t\t<%.80s>\n", ap->ap_product); printf("Firmware\t\t%.16s\n", ap->ap_firmware); printf("BIOS\t\t\t%.16s\n", ap->ap_bios); printf("SCSI channels\t\t%u\n", ap->ap_nschan); printf("Fibre loops\t\t%u\n", ap->ap_fcloops); printf("Memory size\t\t%u MB\n", ap->ap_memsize); if (verbosity >= 1) { printf("Ioctl\t\t\t%d (%s)\n", FIRMWARE_40LD, "40LD"); printf("Signature\t\t0x%08x\n", ap->ap_signature); printf("Configsig\t\t0x%08x\n", ap->ap_configsig); printf("Subsystem\t\t0x%04x\n", ap->ap_subsystem); printf("Subvendor\t\t0x%04x\n", ap->ap_subvendor); printf("Notify counters\t\t%u\n", ap->ap_numnotifyctr); } } return (FIRMWARE_40LD); } /* * Try the 8LD firmware interface */ enq_level = 2; enq_result = amr_ioctl_enquiry(fd, AMR_CMD_EXT_ENQUIRY2, 0, 0); if (enq_result != AMR_STATUS_SUCCESS) { enq_result = amr_ioctl_enquiry(fd, AMR_CMD_EXT_ENQUIRY, 0, 0); enq_level--; } if (enq_result != AMR_STATUS_SUCCESS) { enq_result = amr_ioctl_enquiry(fd, AMR_CMD_ENQUIRY, 0, 0); enq_level--; } if (enq_result == AMR_STATUS_SUCCESS) { struct amr_enquiry *ae; ae = (struct amr_enquiry *)amr_enq.buffer; nschan = ae->ae_adapter.aa_channels; if (globalparam) { const char *product = NULL; char bios[8], firmware[8]; int i; if (enq_level == 2) for (i = 0; i < NTAB(prodtable); i++) { if (ae->ae_signature == prodtable[i].signature) { product = prodtable[i].product; break; } } /* * HP NetRaid controllers have a special encoding of * the firmware and BIOS versions. The AMI version * seems to have it as strings whereas the HP version * does it with a leading uppercase character and two * binary numbers. */ if (ae->ae_adapter.aa_firmware[2] >= 'A' && ae->ae_adapter.aa_firmware[2] <= 'Z' && ae->ae_adapter.aa_firmware[1] < ' ' && ae->ae_adapter.aa_firmware[0] < ' ' && ae->ae_adapter.aa_bios[2] >= 'A' && ae->ae_adapter.aa_bios[2] <= 'Z' && ae->ae_adapter.aa_bios[1] < ' ' && ae->ae_adapter.aa_bios[0] < ' ') { /* * looks like we have an HP NetRaid version * of the MegaRaid */ if (enq_level == 2 && ae->ae_signature == AMR_SIG_438) { /* * the AMI 438 is a NetRaid 3si in * HP-land */ product = "HP NetRaid 3si"; } sprintf(firmware, "%c.%02u.%02u", ae->ae_adapter.aa_firmware[2], ae->ae_adapter.aa_firmware[1], ae->ae_adapter.aa_firmware[0]); sprintf(bios, "%c.%02u.%02u", ae->ae_adapter.aa_bios[2], ae->ae_adapter.aa_bios[1], ae->ae_adapter.aa_bios[0]); } else { sprintf(firmware, "%.4s", ae->ae_adapter.aa_firmware); sprintf(bios, "%.4s", ae->ae_adapter.aa_bios); } printf("Product\t\t\t<%.80s>\n", product ? product : "Unknown"); printf("Firmware\t\t%.7s\n", firmware); printf("BIOS\t\t\t%.7s\n", bios); printf("SCSI channels\t\t%u\n", ae->ae_adapter.aa_channels); printf("Memory size\t\t%u MB\n", ae->ae_adapter.aa_memorysize); if (verbosity >= 1) { printf("Ioctl\t\t\t%d (%s)\n", FIRMWARE_8LD, "8LD"); if (enq_level == 2) { printf("Signature\t\t0x%08x\n", ae->ae_signature); printf("Subsystem\t\t0x%04x\n", ae->ae_subsystem); printf("Subvendor\t\t0x%04x\n", ae->ae_subvendor); } } } return (FIRMWARE_8LD); } /* * Neither firmware interface succeeded. Abort. */ fprintf(stderr, "Firmware interface not supported\n"); clean_exit(fd); /* NOTREACHED */ return (-1); } static char * describe_property(uint8_t prop, char *buffer) { int i; strcpy(buffer, "<"); for (i = 0; i < NTAB(proptable); i++) { if (i > 0) strcat(buffer, ","); if (prop & proptable[i].code) strcat(buffer, proptable[i].on); else strcat(buffer, proptable[i].off); } strcat(buffer, ">"); return (buffer); } static const char * describe_state(int verbosity, uint8_t state) { int i; if ((AMR_DRV_PREVSTATE(state) == AMR_DRV_CURSTATE(state)) && (AMR_DRV_CURSTATE(state) == AMR_DRV_OFFLINE) && verbosity == 0) return (NULL); for (i = 0; i < NTAB(statetable); i++) if (AMR_DRV_CURSTATE(state) == statetable[i].code) return (statetable[i].status); return (NULL); } /****************************************************************************** * Battery status */ static void describe_battery(int fd, int verbosity, int fwint) { uint8_t batt_status = 0; int enq_result; int i; switch (fwint) { case FIRMWARE_40LD: enq_result = amr_ioctl_enquiry(fd, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3, AMR_CONFIG_ENQ3_SOLICITED_FULL); if (enq_result == AMR_STATUS_SUCCESS) { struct amr_enquiry3 *ae3; ae3 = (struct amr_enquiry3 *)amr_enq.buffer; batt_status = ae3->ae_batterystatus; } break; case FIRMWARE_8LD: enq_result = amr_enq.result; if (enq_result == AMR_STATUS_SUCCESS) { struct amr_enquiry *ae; ae = (struct amr_enquiry *)amr_enq.buffer; batt_status = ae->ae_adapter.aa_batterystatus; } break; default: fprintf(stderr, "Firmware interface not supported.\n"); clean_exit(fd); /* NOTREACHED */ enq_result = AMR_STATUS_FAILED; } if (enq_result != AMR_STATUS_SUCCESS) return; printf("Battery status\t\t"); for (i = 0; i < NTAB(battable); i++) { if (batt_status & battable[i].code) printf("%s, ", battable[i].status); } if (!(batt_status & (AMR_BATT_MODULE_MISSING | AMR_BATT_PACK_MISSING))) { for (i = 0; i < NTAB(bcstatble); i++) if (bcstatble[i].code == (batt_status & AMR_BATT_CHARGE_MASK)) printf("%s", bcstatble[i].status); } else printf("charge unknown"); if (verbosity) printf(" (0x%02x)", batt_status); printf("\n"); } /****************************************************************************** * Logical volumes */ static void describe_one_volume(int ldrv, int verbosity, uint32_t size, uint8_t state, uint8_t prop) { float szgb; int raid_level; char propstr[MAXPATHLEN]; const char *statestr; szgb = ((float)size) / (1024 * 1024 * 2); /* size in GB */ raid_level = prop & AMR_DRV_RAID_MASK; printf("Logical volume %d:\t", ldrv); statestr = describe_state(verbosity, state); printf("%s ", statestr); printf("(%.2f GB, RAID%d", szgb, raid_level); if (verbosity >= 1) { describe_property(prop, propstr); printf(" %s", propstr); } printf(")\n"); } /****************************************************************************** * Physical drives */ static void describe_one_drive(int pdrv, int verbosity, uint8_t state) { const char *statestr; statestr = describe_state(verbosity, state); if (statestr) { if (nschan > 0) printf("Physical drive %d:%d\t%s\n", pdrv / AMR_MAX_NSDEVS, pdrv % AMR_MAX_NSDEVS, statestr); else printf("Physical drive %d:\t%s\n", pdrv, statestr); } } static void describe_drive(int fd, int verbosity, int fwint, int ldrv, int sbus, int sdev) { uint32_t *ldrv_size = NULL; uint8_t *ldrv_prop = NULL, *ldrv_state = NULL, *pdrv_state = NULL; int drv, num_ldrv = 0, max_pdrv = 0, pdrv = -1; int enq_result; if (sbus > -1 && sdev > -1) pdrv = (sbus * AMR_MAX_NSDEVS) + sdev; if (nschan != 0) { if (sbus > -1 && sbus >= nschan) { fprintf(stderr, "SCSI channel %d does not exist.\n", sbus); clean_exit(fd); } else if (sdev > -1 && sdev >= AMR_MAX_NSDEVS) { fprintf(stderr, "SCSI device %d:%d does not exist.\n", sbus, sdev); clean_exit(fd); } } switch (fwint) { case FIRMWARE_40LD: enq_result = amr_ioctl_enquiry(fd, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3, AMR_CONFIG_ENQ3_SOLICITED_FULL); if (enq_result == AMR_STATUS_SUCCESS) { struct amr_enquiry3 *ae3; ae3 = (struct amr_enquiry3 *)amr_enq.buffer; num_ldrv = ae3->ae_numldrives; ldrv_size = ae3->ae_drivesize; ldrv_prop = ae3->ae_driveprop; ldrv_state = ae3->ae_drivestate; pdrv_state = ae3->ae_pdrivestate; max_pdrv = AMR_40LD_MAXPHYSDRIVES; } break; case FIRMWARE_8LD: enq_result = amr_enq.result; if (enq_result == AMR_STATUS_SUCCESS) { struct amr_enquiry *ae; ae = (struct amr_enquiry *)amr_enq.buffer; num_ldrv = ae->ae_ldrv.al_numdrives; ldrv_size = ae->ae_ldrv.al_size; ldrv_prop = ae->ae_ldrv.al_properties; ldrv_state = ae->ae_ldrv.al_state; pdrv_state = ae->ae_pdrv.ap_state; max_pdrv = AMR_8LD_MAXPHYSDRIVES; } break; default: fprintf(stderr, "Firmware interface not supported.\n"); clean_exit(fd); /* NOTREACHED */ enq_result = AMR_STATUS_FAILED; } if (enq_result != AMR_STATUS_SUCCESS) return; if ((ldrv < 0 && sbus < 0) || ldrv >= 0) { if (ldrv >= num_ldrv) { fprintf(stderr, "Logical volume %d " "does not exist.\n", ldrv); clean_exit(fd); } if (ldrv < 0) for (drv = 0; drv < num_ldrv; drv++) describe_one_volume(drv, verbosity, ldrv_size[drv], ldrv_state[drv], ldrv_prop[drv]); else describe_one_volume(ldrv, verbosity, ldrv_size[ldrv], ldrv_state[ldrv], ldrv_prop[ldrv]); } if ((ldrv < 0 && sbus < 0) || sbus >= 0) { if (pdrv >= max_pdrv || (nschan != 0 && pdrv >= (nschan * AMR_MAX_NSDEVS))) { fprintf(stderr, "Physical drive %d is out of range.\n", pdrv); clean_exit(fd); } if (sbus < 0) for (drv = 0; drv < max_pdrv; drv++) { if (nschan != 0 && drv >= (nschan * AMR_MAX_NSDEVS)) break; describe_one_drive(drv, verbosity, pdrv_state[drv]); } else if (sdev < 0) for (drv = sbus * AMR_MAX_NSDEVS; drv < ((sbus + 1) * AMR_MAX_NSDEVS); drv++) { if (nschan != 0 && drv >= (nschan * AMR_MAX_NSDEVS)) break; describe_one_drive(drv, verbosity, pdrv_state[drv]); } else if (nschan != 0 && pdrv < (nschan * AMR_MAX_NSDEVS)) describe_one_drive(pdrv, 1, pdrv_state[pdrv]); } } /****************************************************************************** * Main function */ int main(int argc, char *argv[]) { int i; int fd = -1; int globalparam = 0, verbosity = 0; int bflags = 0, cflags = 0, fflags = 0, sflags = 0; int ctrlno = 0, lvolno = -1, physno = -1; int sbusno = -1, targetno = -1; char filename[MAXPATHLEN]; char sdev[MAXPATHLEN]; char *pdev; extern char *optarg; extern int optind; /* * Parse arguments */ while ((i = getopt(argc, argv, "a:bc:f:gl:p:s:t:v")) != -1) switch (i) { case 'a': nattempts = atoi(optarg); break; case 'b': bflags++; break; case 'c': ctrlno = atoi(optarg); cflags++; break; case 'f': snprintf(filename, MAXPATHLEN, "%s", optarg); filename[MAXPATHLEN - 1] = '\0'; fflags++; break; case 'g': globalparam = 1; break; case 'l': lvolno = atoi(optarg); break; case 'p': physno = atoi(optarg); break; case 's': snprintf(sdev, MAXPATHLEN, "%s", optarg); sdev[MAXPATHLEN - 1] = '\0'; sflags++; break; case 't': sleeptime = atoi(optarg); break; case 'v': verbosity++; break; case '?': default: usage(argv[0]); } argc -= optind; argv += optind; if (argc != 0) usage(argv[0]); if (cflags && fflags) usage(argv[0]); else if (cflags) { snprintf(filename, MAXPATHLEN, "/dev/amr%d", ctrlno); filename[MAXPATHLEN - 1] = '\0'; fd = open(filename, O_RDONLY); } else if (fflags) { fd = open(filename, O_RDONLY); } else { for (ctrlno = 0; ctrlno < AMR_MAX_NCTRLS; ctrlno++) { snprintf(filename, MAXPATHLEN, "/dev/amr%d", ctrlno); filename[MAXPATHLEN - 1] = '\0'; fd = open(filename, O_RDONLY); if (fd != -1) break; } } if (fd < 0) { perror("open"); clean_exit(fd); } if (ioctl(fd, AMR_IO_VERSION, &i) == -1) { perror("ioctl version"); clean_exit(fd); } if (sflags) { if(physno > -1) usage(argv[0]); else { sbusno = atoi(sdev); if ((pdev = index(sdev, ':'))) targetno = atoi(++pdev); } } else if (physno > -1) { sbusno = physno / AMR_MAX_NSDEVS; targetno = physno % AMR_MAX_NSDEVS; } if (globalparam && verbosity >= 1) printf("Version\t\t\t%d\n", i); #if 0 if (i != 1) { fprintf(stderr, "Driver version (%d) not supported\n", i); clean_exit(fd); } #endif memset(&amr_enq, 0, sizeof(amr_enq)); if ((amr_enq.buffer = (char *)malloc(AMR_BUFSIZE)) == NULL) { fprintf(stderr, "Cannot allocate command buffer\n"); clean_exit(fd); } i = describe_card(fd, verbosity, globalparam); if (bflags || globalparam) describe_battery(fd, verbosity, i); if (!bflags || lvolno > -1 || physno > -1 || sbusno > -1 || targetno > -1) describe_drive(fd, verbosity, i, lvolno, sbusno, targetno); close(fd); free(amr_enq.buffer); return (0); }