#include <sys/param.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/mpt_ioctl.h>
#include <sys/sysctl.h>
#include <sys/uio.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mptutil.h"
static const char *mpt_ioc_status_codes[] = {
"Success",
"Invalid function",
"Busy",
"Invalid scatter-gather list",
"Internal error",
"Reserved",
"Insufficient resources",
"Invalid field",
"Invalid state",
"Operation state not supported",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"Invalid configuration action",
"Invalid configuration type",
"Invalid configuration page",
"Invalid configuration data",
"No configuration defaults",
"Unable to commit configuration change",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"Recovered SCSI error",
"Invalid SCSI bus",
"Invalid SCSI target ID",
"SCSI device not there",
"SCSI data overrun",
"SCSI data underrun",
"SCSI I/O error",
"SCSI protocol error",
"SCSI task terminated",
"SCSI residual mismatch",
"SCSI task management failed",
"SCSI I/O controller terminated",
"SCSI external controller terminated",
"EEDP guard error",
"EEDP reference tag error",
"EEDP application tag error",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"SCSI target priority I/O",
"Invalid SCSI target port",
"Invalid SCSI target I/O index",
"SCSI target aborted",
"No connection retryable",
"No connection",
"FC aborted",
"Invalid FC receive ID",
"FC did invalid",
"FC node logged out",
"Transfer count mismatch",
"STS data not set",
"FC exchange canceled",
"Data offset error",
"Too much write data",
"IU too short",
"ACK NAK timeout",
"NAK received",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"LAN device not found",
"LAN device failure",
"LAN transmit error",
"LAN transmit aborted",
"LAN receive error",
"LAN receive aborted",
"LAN partial packet",
"LAN canceled",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"SAS SMP request failed",
"SAS SMP data overrun",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"Inband aborted",
"No inband connection",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"Diagnostic released",
};
static const char *mpt_raid_action_status_codes[] = {
"Success",
"Invalid action",
"Failure",
"Operation in progress",
};
const char *
mpt_ioc_status(U16 IOCStatus)
{
static char buffer[16];
IOCStatus &= MPI_IOCSTATUS_MASK;
if (IOCStatus < sizeof(mpt_ioc_status_codes) / sizeof(char *) &&
mpt_ioc_status_codes[IOCStatus] != NULL)
return (mpt_ioc_status_codes[IOCStatus]);
snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
return (buffer);
}
const char *
mpt_raid_status(U16 ActionStatus)
{
static char buffer[16];
if (ActionStatus < sizeof(mpt_raid_action_status_codes) /
sizeof(char *))
return (mpt_raid_action_status_codes[ActionStatus]);
snprintf(buffer, sizeof(buffer), "Status: 0x%04x", ActionStatus);
return (buffer);
}
const char *
mpt_raid_level(U8 VolumeType)
{
static char buf[16];
switch (VolumeType) {
case MPI_RAID_VOL_TYPE_IS:
return ("RAID-0");
case MPI_RAID_VOL_TYPE_IM:
return ("RAID-1");
case MPI_RAID_VOL_TYPE_IME:
return ("RAID-1E");
case MPI_RAID_VOL_TYPE_RAID_5:
return ("RAID-5");
case MPI_RAID_VOL_TYPE_RAID_6:
return ("RAID-6");
case MPI_RAID_VOL_TYPE_RAID_10:
return ("RAID-10");
case MPI_RAID_VOL_TYPE_RAID_50:
return ("RAID-50");
default:
sprintf(buf, "LVL 0x%02x", VolumeType);
return (buf);
}
}
const char *
mpt_volume_name(U8 VolumeBus, U8 VolumeID)
{
static struct mpt_query_disk info;
static char buf[16];
if (mpt_query_disk(VolumeBus, VolumeID, &info) != 0) {
if (VolumeBus == 0)
snprintf(buf, sizeof(buf), "%d", VolumeID);
else
snprintf(buf, sizeof(buf), "%d:%d", VolumeBus,
VolumeID);
return (buf);
}
return (info.devname);
}
int
mpt_lookup_volume(int fd, const char *name, U8 *VolumeBus, U8 *VolumeID)
{
CONFIG_PAGE_IOC_2 *ioc2;
CONFIG_PAGE_IOC_2_RAID_VOL *vol;
struct mpt_query_disk info;
char *cp;
long bus, id;
int i;
bus = strtol(name, &cp, 0);
if (*cp == ':') {
id = strtol(cp + 1, &cp, 0);
if (*cp == '\0') {
if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) {
return (EINVAL);
}
*VolumeBus = bus;
*VolumeID = id;
return (0);
}
} else if (*cp == '\0') {
if (bus < 0 || bus > 0xff)
return (EINVAL);
*VolumeBus = 0;
*VolumeID = bus;
return (0);
}
ioc2 = mpt_read_ioc_page(fd, 2, NULL);
if (ioc2 == NULL)
return (errno);
vol = ioc2->RaidVolume;
for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
if (mpt_query_disk(vol->VolumeBus, vol->VolumeID, &info) != 0)
continue;
if (strcmp(name, info.devname) == 0) {
*VolumeBus = vol->VolumeBus;
*VolumeID = vol->VolumeID;
free(ioc2);
return (0);
}
}
free(ioc2);
return (EINVAL);
}
int
mpt_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
{
struct mpt_cfg_page_req req;
if (IOCStatus != NULL)
*IOCStatus = MPI_IOCSTATUS_SUCCESS;
bzero(&req, sizeof(req));
req.header.PageType = PageType;
req.header.PageNumber = PageNumber;
req.page_address = PageAddress;
if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
return (errno);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading config page header failed: %s",
mpt_ioc_status(req.ioc_status));
return (EIO);
}
*header = req.header;
return (0);
}
void *
mpt_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
U16 *IOCStatus)
{
struct mpt_cfg_page_req req;
void *buf;
int error;
if (IOCStatus != NULL)
*IOCStatus = MPI_IOCSTATUS_SUCCESS;
bzero(&req, sizeof(req));
req.header.PageType = PageType;
req.header.PageNumber = PageNumber;
req.page_address = PageAddress;
if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0)
return (NULL);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading config page header failed: %s",
mpt_ioc_status(req.ioc_status));
errno = EIO;
return (NULL);
}
req.len = req.header.PageLength * 4;
buf = malloc(req.len);
req.buf = buf;
bcopy(&req.header, buf, sizeof(req.header));
if (ioctl(fd, MPTIO_READ_CFG_PAGE, &req) < 0) {
error = errno;
free(buf);
errno = error;
return (NULL);
}
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading config page failed: %s",
mpt_ioc_status(req.ioc_status));
free(buf);
errno = EIO;
return (NULL);
}
return (buf);
}
void *
mpt_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
{
struct mpt_ext_cfg_page_req req;
void *buf;
int error;
if (IOCStatus != NULL)
*IOCStatus = MPI_IOCSTATUS_SUCCESS;
bzero(&req, sizeof(req));
req.header.PageVersion = PageVersion;
req.header.PageNumber = PageNumber;
req.header.ExtPageType = ExtPageType;
req.page_address = PageAddress;
if (ioctl(fd, MPTIO_READ_EXT_CFG_HEADER, &req) < 0)
return (NULL);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading extended config page header failed: %s",
mpt_ioc_status(req.ioc_status));
errno = EIO;
return (NULL);
}
req.len = req.header.ExtPageLength * 4;
buf = malloc(req.len);
req.buf = buf;
bcopy(&req.header, buf, sizeof(req.header));
if (ioctl(fd, MPTIO_READ_EXT_CFG_PAGE, &req) < 0) {
error = errno;
free(buf);
errno = error;
return (NULL);
}
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL)
*IOCStatus = req.ioc_status;
else
warnx("Reading extended config page failed: %s",
mpt_ioc_status(req.ioc_status));
free(buf);
errno = EIO;
return (NULL);
}
return (buf);
}
int
mpt_write_config_page(int fd, void *buf, U16 *IOCStatus)
{
CONFIG_PAGE_HEADER *hdr;
struct mpt_cfg_page_req req;
if (IOCStatus != NULL)
*IOCStatus = MPI_IOCSTATUS_SUCCESS;
bzero(&req, sizeof(req));
req.buf = buf;
hdr = buf;
req.len = hdr->PageLength * 4;
if (ioctl(fd, MPTIO_WRITE_CFG_PAGE, &req) < 0)
return (errno);
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
if (IOCStatus != NULL) {
*IOCStatus = req.ioc_status;
return (0);
}
warnx("Writing config page failed: %s",
mpt_ioc_status(req.ioc_status));
return (EIO);
}
return (0);
}
int
mpt_raid_action(int fd, U8 Action, U8 VolumeBus, U8 VolumeID, U8 PhysDiskNum,
U32 ActionDataWord, void *buf, int len, RAID_VOL0_STATUS *VolumeStatus,
U32 *ActionData, int datalen, U16 *IOCStatus, U16 *ActionStatus, int write)
{
struct mpt_raid_action raid_act;
if (IOCStatus != NULL)
*IOCStatus = MPI_IOCSTATUS_SUCCESS;
if (datalen < 0 || (unsigned)datalen > sizeof(raid_act.action_data))
return (EINVAL);
bzero(&raid_act, sizeof(raid_act));
raid_act.action = Action;
raid_act.volume_bus = VolumeBus;
raid_act.volume_id = VolumeID;
raid_act.phys_disk_num = PhysDiskNum;
raid_act.action_data_word = ActionDataWord;
if (buf != NULL && len != 0) {
raid_act.buf = buf;
raid_act.len = len;
raid_act.write = write;
}
if (ioctl(fd, MPTIO_RAID_ACTION, &raid_act) < 0)
return (errno);
if (!IOC_STATUS_SUCCESS(raid_act.ioc_status)) {
if (IOCStatus != NULL) {
*IOCStatus = raid_act.ioc_status;
return (0);
}
warnx("RAID action failed: %s",
mpt_ioc_status(raid_act.ioc_status));
return (EIO);
}
if (ActionStatus != NULL)
*ActionStatus = raid_act.action_status;
if (raid_act.action_status != MPI_RAID_ACTION_ASTATUS_SUCCESS) {
if (ActionStatus != NULL)
return (0);
warnx("RAID action failed: %s",
mpt_raid_status(raid_act.action_status));
return (EIO);
}
if (VolumeStatus != NULL)
*((U32 *)VolumeStatus) = raid_act.volume_status;
if (ActionData != NULL)
bcopy(raid_act.action_data, ActionData, datalen);
return (0);
}
int
mpt_open(int unit)
{
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "/dev/mpt%d", unit);
return (open(path, O_RDWR));
}
int
mpt_table_handler(struct mptutil_command **start, struct mptutil_command **end,
int ac, char **av)
{
struct mptutil_command **cmd;
if (ac < 2) {
warnx("The %s command requires a sub-command.", av[0]);
return (EINVAL);
}
for (cmd = start; cmd < end; cmd++) {
if (strcmp((*cmd)->name, av[1]) == 0)
return ((*cmd)->handler(ac - 1, av + 1));
}
warnx("%s is not a valid sub-command of %s.", av[1], av[0]);
return (ENOENT);
}
#ifdef DEBUG
void
hexdump(const void *ptr, int length, const char *hdr, int flags)
{
int i, j, k;
int cols;
const unsigned char *cp;
char delim;
if ((flags & HD_DELIM_MASK) != 0)
delim = (flags & HD_DELIM_MASK) >> 8;
else
delim = ' ';
if ((flags & HD_COLUMN_MASK) != 0)
cols = flags & HD_COLUMN_MASK;
else
cols = 16;
cp = ptr;
for (i = 0; i < length; i+= cols) {
if (hdr != NULL)
printf("%s", hdr);
if ((flags & HD_OMIT_COUNT) == 0)
printf("%04x ", i);
if ((flags & HD_OMIT_HEX) == 0) {
for (j = 0; j < cols; j++) {
k = i + j;
if (k < length)
printf("%c%02x", delim, cp[k]);
else
printf(" ");
}
}
if ((flags & HD_OMIT_CHARS) == 0) {
printf(" |");
for (j = 0; j < cols; j++) {
k = i + j;
if (k >= length)
printf(" ");
else if (cp[k] >= ' ' && cp[k] <= '~')
printf("%c", cp[k]);
else
printf(".");
}
printf("|");
}
printf("\n");
}
}
#endif