802 lines
19 KiB
C
802 lines
19 KiB
C
/*-
|
|
* Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
|
|
*
|
|
* Copyright (c) 2015 Netflix, Inc.
|
|
* Written by: Scott Long <scottl@freebsd.org>
|
|
*
|
|
* Copyright (c) 2008 Yahoo!, Inc.
|
|
* All rights reserved.
|
|
* Written by: John Baldwin <jhb@FreeBSD.org>
|
|
*
|
|
* 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, 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.
|
|
* 3. Neither the name of the author nor the names of any co-contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 <sys/cdefs.h>
|
|
#include <sys/param.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/endian.h>
|
|
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "mpsutil.h"
|
|
#include <dev/mps/mps_ioctl.h>
|
|
#include <dev/mpr/mpr_ioctl.h>
|
|
|
|
#ifndef USE_MPT_IOCTLS
|
|
#define USE_MPT_IOCTLS
|
|
#endif
|
|
|
|
static const char *mps_ioc_status_codes[] = {
|
|
"Success", /* 0x0000 */
|
|
"Invalid function",
|
|
"Busy",
|
|
"Invalid scatter-gather list",
|
|
"Internal error",
|
|
"Reserved",
|
|
"Insufficient resources",
|
|
"Invalid field",
|
|
"Invalid state", /* 0x0008 */
|
|
"Operation state not supported",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL, /* 0x0010 */
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL, /* 0x0018 */
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
"Invalid configuration action", /* 0x0020 */
|
|
"Invalid configuration type",
|
|
"Invalid configuration page",
|
|
"Invalid configuration data",
|
|
"No configuration defaults",
|
|
"Unable to commit configuration change",
|
|
NULL,
|
|
NULL,
|
|
NULL, /* 0x0028 */
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL, /* 0x0030 */
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL, /* 0x0038 */
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
"Recovered SCSI error", /* 0x0040 */
|
|
"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", /* 0x0048 */
|
|
"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, /* 0x0050 */
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL, /* 0x0058 */
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
"SCSI target priority I/O", /* 0x0060 */
|
|
"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", /* 0x0068 */
|
|
"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", /* 0x0070 */
|
|
"NAK received",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL, /* 0x0078 */
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
"LAN device not found", /* 0x0080 */
|
|
"LAN device failure",
|
|
"LAN transmit error",
|
|
"LAN transmit aborted",
|
|
"LAN receive error",
|
|
"LAN receive aborted",
|
|
"LAN partial packet",
|
|
"LAN canceled",
|
|
NULL, /* 0x0088 */
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
"SAS SMP request failed", /* 0x0090 */
|
|
"SAS SMP data overrun",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
"Inband aborted", /* 0x0098 */
|
|
"No inband connection",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
"Diagnostic released", /* 0x00A0 */
|
|
};
|
|
|
|
struct mprs_pass_thru {
|
|
uint64_t PtrRequest;
|
|
uint64_t PtrReply;
|
|
uint64_t PtrData;
|
|
uint32_t RequestSize;
|
|
uint32_t ReplySize;
|
|
uint32_t DataSize;
|
|
uint32_t DataDirection;
|
|
uint64_t PtrDataOut;
|
|
uint32_t DataOutSize;
|
|
uint32_t Timeout;
|
|
};
|
|
|
|
struct mprs_btdh_mapping {
|
|
uint16_t TargetID;
|
|
uint16_t Bus;
|
|
uint16_t DevHandle;
|
|
uint16_t Reserved;
|
|
};
|
|
|
|
static void adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts);
|
|
|
|
const char *
|
|
mps_ioc_status(U16 IOCStatus)
|
|
{
|
|
static char buffer[16];
|
|
|
|
IOCStatus &= MPI2_IOCSTATUS_MASK;
|
|
if (IOCStatus < sizeof(mps_ioc_status_codes) / sizeof(char *) &&
|
|
mps_ioc_status_codes[IOCStatus] != NULL)
|
|
return (mps_ioc_status_codes[IOCStatus]);
|
|
snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
|
|
return (buffer);
|
|
}
|
|
|
|
#ifdef USE_MPT_IOCTLS
|
|
int
|
|
mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target)
|
|
{
|
|
int error;
|
|
struct mprs_btdh_mapping map;
|
|
|
|
map.Bus = *bus;
|
|
map.TargetID = *target;
|
|
map.DevHandle = *devhandle;
|
|
|
|
if ((error = ioctl(fd, MPTIOCTL_BTDH_MAPPING, &map)) != 0) {
|
|
error = errno;
|
|
warn("Failed to map bus/target/device");
|
|
return (error);
|
|
}
|
|
|
|
*bus = map.Bus;
|
|
*target = map.TargetID;
|
|
*devhandle = map.DevHandle;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
mps_set_slot_status(int fd, U16 handle, U16 slot, U32 status)
|
|
{
|
|
MPI2_SEP_REQUEST req;
|
|
MPI2_SEP_REPLY reply;
|
|
|
|
bzero(&req, sizeof(req));
|
|
req.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;
|
|
req.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;
|
|
req.Flags = MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS;
|
|
req.EnclosureHandle = handle;
|
|
req.Slot = slot;
|
|
req.SlotStatus = status;
|
|
|
|
if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
|
|
NULL, 0, NULL, 0, 30) != 0)
|
|
return (errno);
|
|
|
|
if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus)))
|
|
return (EIO);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
|
|
MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
|
|
{
|
|
MPI2_CONFIG_REQUEST req;
|
|
MPI2_CONFIG_REPLY reply;
|
|
|
|
bzero(&req, sizeof(req));
|
|
req.Function = MPI2_FUNCTION_CONFIG;
|
|
req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
|
|
req.Header.PageType = PageType;
|
|
req.Header.PageNumber = PageNumber;
|
|
req.PageAddress = PageAddress;
|
|
|
|
if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
|
|
NULL, 0, NULL, 0, 30))
|
|
return (errno);
|
|
|
|
if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus))) {
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = reply.IOCStatus;
|
|
return (EIO);
|
|
}
|
|
if (header == NULL)
|
|
return (EINVAL);
|
|
*header = reply.Header;
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber, U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *ExtPageLength, U16 *IOCStatus)
|
|
{
|
|
MPI2_CONFIG_REQUEST req;
|
|
MPI2_CONFIG_REPLY reply;
|
|
|
|
bzero(&req, sizeof(req));
|
|
req.Function = MPI2_FUNCTION_CONFIG;
|
|
req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
|
|
req.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
|
|
req.ExtPageType = ExtPageType;
|
|
req.Header.PageNumber = PageNumber;
|
|
req.PageAddress = htole32(PageAddress);
|
|
|
|
if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
|
|
NULL, 0, NULL, 0, 30))
|
|
return (errno);
|
|
|
|
if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus))) {
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = le16toh(reply.IOCStatus);
|
|
return (EIO);
|
|
}
|
|
if ((header == NULL) || (ExtPageLength == NULL))
|
|
return (EINVAL);
|
|
*header = reply.Header;
|
|
*ExtPageLength = reply.ExtPageLength;
|
|
return (0);
|
|
}
|
|
|
|
void *
|
|
mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
|
|
U16 *IOCStatus)
|
|
{
|
|
MPI2_CONFIG_REQUEST req;
|
|
MPI2_CONFIG_PAGE_HEADER header;
|
|
MPI2_CONFIG_REPLY reply;
|
|
void *buf;
|
|
int error, len;
|
|
|
|
bzero(&header, sizeof(header));
|
|
error = mps_read_config_page_header(fd, PageType, PageNumber,
|
|
PageAddress, &header, IOCStatus);
|
|
if (error) {
|
|
errno = error;
|
|
return (NULL);
|
|
}
|
|
|
|
bzero(&req, sizeof(req));
|
|
req.Function = MPI2_FUNCTION_CONFIG;
|
|
req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
|
|
req.PageAddress = htole32(PageAddress);
|
|
req.Header = header;
|
|
if (req.Header.PageLength == 0)
|
|
req.Header.PageLength = 4;
|
|
|
|
len = req.Header.PageLength * 4;
|
|
buf = malloc(len);
|
|
if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
|
|
buf, len, NULL, 0, 30)) {
|
|
error = errno;
|
|
free(buf);
|
|
errno = error;
|
|
return (NULL);
|
|
}
|
|
reply.IOCStatus = le16toh(reply.IOCStatus);
|
|
if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = reply.IOCStatus;
|
|
else
|
|
warnx("Reading config page failed: 0x%x %s",
|
|
reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
|
|
free(buf);
|
|
errno = EIO;
|
|
return (NULL);
|
|
}
|
|
return (buf);
|
|
}
|
|
|
|
void *
|
|
mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
|
|
U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
|
|
{
|
|
MPI2_CONFIG_REQUEST req;
|
|
MPI2_CONFIG_PAGE_HEADER header;
|
|
MPI2_CONFIG_REPLY reply;
|
|
U16 pagelen;
|
|
void *buf;
|
|
int error, len;
|
|
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
|
|
bzero(&header, sizeof(header));
|
|
error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber,
|
|
PageAddress, &header, &pagelen, IOCStatus);
|
|
if (error) {
|
|
errno = error;
|
|
return (NULL);
|
|
}
|
|
|
|
bzero(&req, sizeof(req));
|
|
req.Function = MPI2_FUNCTION_CONFIG;
|
|
req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
|
|
req.PageAddress = htole32(PageAddress);
|
|
req.Header = header;
|
|
if (pagelen == 0)
|
|
pagelen = htole16(4);
|
|
req.ExtPageLength = pagelen;
|
|
req.ExtPageType = ExtPageType;
|
|
|
|
len = le16toh(pagelen) * 4;
|
|
buf = malloc(len);
|
|
if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
|
|
buf, len, NULL, 0, 30)) {
|
|
error = errno;
|
|
free(buf);
|
|
errno = error;
|
|
return (NULL);
|
|
}
|
|
reply.IOCStatus = le16toh(reply.IOCStatus);
|
|
if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = reply.IOCStatus;
|
|
else
|
|
warnx("Reading extended config page failed: %s",
|
|
mps_ioc_status(reply.IOCStatus));
|
|
free(buf);
|
|
errno = EIO;
|
|
return (NULL);
|
|
}
|
|
return (buf);
|
|
}
|
|
|
|
int
|
|
mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
|
|
{
|
|
MPI2_FW_DOWNLOAD_REQUEST req;
|
|
MPI2_FW_DOWNLOAD_REPLY reply;
|
|
|
|
bzero(&req, sizeof(req));
|
|
bzero(&reply, sizeof(reply));
|
|
req.Function = MPI2_FUNCTION_FW_DOWNLOAD;
|
|
req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
|
|
req.TotalImageSize = htole32(len);
|
|
req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT;
|
|
|
|
if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
|
|
fw, len, 0)) {
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
mps_firmware_get(int fd, unsigned char **firmware, bool bios)
|
|
{
|
|
MPI2_FW_UPLOAD_REQUEST req;
|
|
MPI2_FW_UPLOAD_REPLY reply;
|
|
int size;
|
|
|
|
*firmware = NULL;
|
|
bzero(&req, sizeof(req));
|
|
bzero(&reply, sizeof(reply));
|
|
req.Function = MPI2_FUNCTION_FW_UPLOAD;
|
|
req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
|
|
|
|
if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
|
|
NULL, 0, 0)) {
|
|
return (-1);
|
|
}
|
|
if (reply.ActualImageSize == 0) {
|
|
return (-1);
|
|
}
|
|
|
|
size = le32toh(reply.ActualImageSize);
|
|
*firmware = calloc(size, sizeof(unsigned char));
|
|
if (*firmware == NULL) {
|
|
warn("calloc");
|
|
return (-1);
|
|
}
|
|
if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
|
|
*firmware, size, 0)) {
|
|
free(*firmware);
|
|
return (-1);
|
|
}
|
|
|
|
return (size);
|
|
}
|
|
|
|
#else
|
|
|
|
int
|
|
mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
|
|
MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
|
|
{
|
|
struct mps_cfg_page_req req;
|
|
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
|
|
if (header == NULL)
|
|
return (EINVAL);
|
|
bzero(&req, sizeof(req));
|
|
req.header.PageType = PageType;
|
|
req.header.PageNumber = PageNumber;
|
|
req.page_address = PageAddress;
|
|
if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0)
|
|
return (errno);
|
|
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = req.ioc_status;
|
|
return (EIO);
|
|
}
|
|
bcopy(&req.header, header, sizeof(*header));
|
|
return (0);
|
|
}
|
|
|
|
void *
|
|
mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
|
|
U16 *IOCStatus)
|
|
{
|
|
struct mps_cfg_page_req req;
|
|
void *buf;
|
|
int error;
|
|
|
|
error = mps_read_config_page_header(fd, PageType, PageNumber,
|
|
PageAddress, &req.header, IOCStatus);
|
|
if (error) {
|
|
errno = error;
|
|
return (NULL);
|
|
}
|
|
|
|
if (req.header.PageLength == 0)
|
|
req.header.PageLength = 4;
|
|
req.len = req.header.PageLength * 4;
|
|
buf = malloc(req.len);
|
|
req.buf = buf;
|
|
bcopy(&req.header, buf, sizeof(req.header));
|
|
if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
|
|
error = errno;
|
|
free(buf);
|
|
errno = error;
|
|
return (NULL);
|
|
}
|
|
req.ioc_status = le16toh(req.ioc_status);
|
|
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = req.ioc_status;
|
|
else
|
|
warnx("Reading config page failed: 0x%x %s",
|
|
req.ioc_status, mps_ioc_status(req.ioc_status));
|
|
free(buf);
|
|
errno = EIO;
|
|
return (NULL);
|
|
}
|
|
return (buf);
|
|
}
|
|
|
|
void *
|
|
mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
|
|
U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
|
|
{
|
|
struct mps_ext_cfg_page_req req;
|
|
void *buf;
|
|
int error;
|
|
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
|
|
bzero(&req, sizeof(req));
|
|
req.header.PageVersion = PageVersion;
|
|
req.header.PageNumber = PageNumber;
|
|
req.header.ExtPageType = ExtPageType;
|
|
req.page_address = htole32(PageAddress);
|
|
if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0)
|
|
return (NULL);
|
|
req.ioc_status = le16toh(req.ioc_status);
|
|
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = req.ioc_status;
|
|
else
|
|
warnx("Reading extended config page header failed: %s",
|
|
mps_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, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) {
|
|
error = errno;
|
|
free(buf);
|
|
errno = error;
|
|
return (NULL);
|
|
}
|
|
req.ioc_status = le16toh(req.ioc_status);
|
|
if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
|
|
if (IOCStatus != NULL)
|
|
*IOCStatus = req.ioc_status;
|
|
else
|
|
warnx("Reading extended config page failed: %s",
|
|
mps_ioc_status(req.ioc_status));
|
|
free(buf);
|
|
errno = EIO;
|
|
return (NULL);
|
|
}
|
|
return (buf);
|
|
}
|
|
#endif
|
|
|
|
int
|
|
mps_open(int unit)
|
|
{
|
|
char path[MAXPATHLEN];
|
|
|
|
snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
|
|
return (open(path, O_RDWR));
|
|
}
|
|
|
|
int
|
|
mps_user_command(int fd, void *req, uint32_t req_len, void *reply,
|
|
uint32_t reply_len, void *buffer, int len, uint32_t flags)
|
|
{
|
|
struct mps_usr_command cmd;
|
|
|
|
bzero(&cmd, sizeof(struct mps_usr_command));
|
|
cmd.req = req;
|
|
cmd.req_len = req_len;
|
|
cmd.rpl = reply;
|
|
cmd.rpl_len = reply_len;
|
|
cmd.buf = buffer;
|
|
cmd.len = len;
|
|
cmd.flags = flags;
|
|
|
|
if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
|
|
return (errno);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
mps_pass_command(int fd, void *req, uint32_t req_len, void *reply,
|
|
uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
|
|
uint32_t dataout_len, uint32_t timeout)
|
|
{
|
|
struct mprs_pass_thru pass;
|
|
|
|
bzero(&pass, sizeof(pass));
|
|
pass.PtrRequest = (uint64_t)(uintptr_t)req;
|
|
pass.PtrReply = (uint64_t)(uintptr_t)reply;
|
|
pass.RequestSize = req_len;
|
|
pass.ReplySize = reply_len;
|
|
if (datain_len && dataout_len) {
|
|
pass.PtrData = (uint64_t)(uintptr_t)data_in;
|
|
pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
|
|
pass.DataSize = datain_len;
|
|
pass.DataOutSize = dataout_len;
|
|
if (is_mps) {
|
|
pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
|
|
} else {
|
|
pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
|
|
}
|
|
} else if (datain_len) {
|
|
pass.PtrData = (uint64_t)(uintptr_t)data_in;
|
|
pass.DataSize = datain_len;
|
|
if (is_mps) {
|
|
pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
|
|
} else {
|
|
pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
|
|
}
|
|
} else if (dataout_len) {
|
|
pass.PtrData = (uint64_t)(uintptr_t)data_out;
|
|
pass.DataSize = dataout_len;
|
|
if (is_mps) {
|
|
pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
|
|
} else {
|
|
pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
|
|
}
|
|
} else {
|
|
if (is_mps) {
|
|
pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
|
|
} else {
|
|
pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
|
|
}
|
|
}
|
|
pass.Timeout = timeout;
|
|
|
|
if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
|
|
return (errno);
|
|
return (0);
|
|
}
|
|
|
|
MPI2_IOC_FACTS_REPLY *
|
|
mps_get_iocfacts(int fd)
|
|
{
|
|
MPI2_IOC_FACTS_REPLY *facts;
|
|
MPI2_IOC_FACTS_REQUEST req;
|
|
char msgver[8], sysctlname[128];
|
|
size_t len, factslen;
|
|
int error;
|
|
|
|
snprintf(sysctlname, sizeof(sysctlname), "dev.%s.%d.msg_version",
|
|
is_mps ? "mps" : "mpr", mps_unit);
|
|
|
|
factslen = sizeof(MPI2_IOC_FACTS_REPLY);
|
|
len = sizeof(msgver);
|
|
error = sysctlbyname(sysctlname, msgver, &len, NULL, 0);
|
|
if (error == 0) {
|
|
if (strncmp(msgver, "2.6", sizeof(msgver)) == 0)
|
|
factslen += 4;
|
|
}
|
|
|
|
facts = malloc(factslen);
|
|
if (facts == NULL) {
|
|
errno = ENOMEM;
|
|
return (NULL);
|
|
}
|
|
|
|
bzero(&req, factslen);
|
|
req.Function = MPI2_FUNCTION_IOC_FACTS;
|
|
|
|
#if 1
|
|
error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
|
|
facts, factslen, NULL, 0, NULL, 0, 10);
|
|
#else
|
|
error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
|
|
facts, factslen, NULL, 0, 0);
|
|
#endif
|
|
if (error) {
|
|
free(facts);
|
|
return (NULL);
|
|
}
|
|
|
|
if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
|
|
free(facts);
|
|
errno = EINVAL;
|
|
return (NULL);
|
|
}
|
|
adjust_iocfacts_endianness(facts);
|
|
return (facts);
|
|
}
|
|
|
|
static void
|
|
adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts)
|
|
{
|
|
facts->MsgVersion = le16toh(facts->MsgVersion);
|
|
facts->HeaderVersion = le16toh(facts->HeaderVersion);
|
|
facts->Reserved1 = le16toh(facts->Reserved1);
|
|
facts->IOCExceptions = le16toh(facts->IOCExceptions);
|
|
facts->IOCStatus = le16toh(facts->IOCStatus);
|
|
facts->IOCLogInfo = le32toh(facts->IOCLogInfo);
|
|
facts->RequestCredit = le16toh(facts->RequestCredit);
|
|
facts->ProductID = le16toh(facts->ProductID);
|
|
facts->IOCCapabilities = le32toh(facts->IOCCapabilities);
|
|
facts->IOCRequestFrameSize =
|
|
le16toh(facts->IOCRequestFrameSize);
|
|
facts->FWVersion.Word = le32toh(facts->FWVersion.Word);
|
|
facts->MaxInitiators = le16toh(facts->MaxInitiators);
|
|
facts->MaxTargets = le16toh(facts->MaxTargets);
|
|
facts->MaxSasExpanders = le16toh(facts->MaxSasExpanders);
|
|
facts->MaxEnclosures = le16toh(facts->MaxEnclosures);
|
|
facts->ProtocolFlags = le16toh(facts->ProtocolFlags);
|
|
facts->HighPriorityCredit = le16toh(facts->HighPriorityCredit);
|
|
facts->MaxReplyDescriptorPostQueueDepth =
|
|
le16toh(facts->MaxReplyDescriptorPostQueueDepth);
|
|
facts->MaxDevHandle = le16toh(facts->MaxDevHandle);
|
|
facts->MaxPersistentEntries =
|
|
le16toh(facts->MaxPersistentEntries);
|
|
facts->MinDevHandle = le16toh(facts->MinDevHandle);
|
|
}
|