1/*-
2 * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
3 *
4 * Copyright (c) 2015 Netflix, Inc.
5 * All rights reserved.
6 * Written by: Scott Long <scottl@freebsd.org>
7 *
8 * Copyright (c) 2008 Yahoo!, Inc.
9 * All rights reserved.
10 * Written by: John Baldwin <jhb@FreeBSD.org>
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the author nor the names of any co-contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#include <sys/cdefs.h>
38__RCSID("$FreeBSD: stable/10/usr.sbin/mpsutil/mps_cmd.c 315600 2017-03-20 00:55:24Z pfg $");
39
40#include <sys/param.h>
41#include <sys/errno.h>
42#include <sys/ioctl.h>
43#if 0
44#include <sys/mps_ioctl.h>
45#else
46#include "mps_ioctl.h"
47#include "mpr_ioctl.h"
48#endif
49#include <sys/sysctl.h>
50#include <sys/uio.h>
51
52#include <err.h>
53#include <fcntl.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <unistd.h>
58
59#include "mpsutil.h"
60
61#ifndef USE_MPT_IOCTLS
62#define USE_MPT_IOCTLS
63#endif
64
65static const char *mps_ioc_status_codes[] = {
66	"Success",				/* 0x0000 */
67	"Invalid function",
68	"Busy",
69	"Invalid scatter-gather list",
70	"Internal error",
71	"Reserved",
72	"Insufficient resources",
73	"Invalid field",
74	"Invalid state",			/* 0x0008 */
75	"Operation state not supported",
76	NULL,
77	NULL,
78	NULL,
79	NULL,
80	NULL,
81	NULL,
82	NULL,					/* 0x0010 */
83	NULL,
84	NULL,
85	NULL,
86	NULL,
87	NULL,
88	NULL,
89	NULL,
90	NULL,					/* 0x0018 */
91	NULL,
92	NULL,
93	NULL,
94	NULL,
95	NULL,
96	NULL,
97	NULL,
98	"Invalid configuration action",		/* 0x0020 */
99	"Invalid configuration type",
100	"Invalid configuration page",
101	"Invalid configuration data",
102	"No configuration defaults",
103	"Unable to commit configuration change",
104	NULL,
105	NULL,
106	NULL,					/* 0x0028 */
107	NULL,
108	NULL,
109	NULL,
110	NULL,
111	NULL,
112	NULL,
113	NULL,
114	NULL,					/* 0x0030 */
115	NULL,
116	NULL,
117	NULL,
118	NULL,
119	NULL,
120	NULL,
121	NULL,
122	NULL,					/* 0x0038 */
123	NULL,
124	NULL,
125	NULL,
126	NULL,
127	NULL,
128	NULL,
129	NULL,
130	"Recovered SCSI error",			/* 0x0040 */
131	"Invalid SCSI bus",
132	"Invalid SCSI target ID",
133	"SCSI device not there",
134	"SCSI data overrun",
135	"SCSI data underrun",
136	"SCSI I/O error",
137	"SCSI protocol error",
138	"SCSI task terminated",			/* 0x0048 */
139	"SCSI residual mismatch",
140	"SCSI task management failed",
141	"SCSI I/O controller terminated",
142	"SCSI external controller terminated",
143	"EEDP guard error",
144	"EEDP reference tag error",
145	"EEDP application tag error",
146	NULL,					/* 0x0050 */
147	NULL,
148	NULL,
149	NULL,
150	NULL,
151	NULL,
152	NULL,
153	NULL,
154	NULL,					/* 0x0058 */
155	NULL,
156	NULL,
157	NULL,
158	NULL,
159	NULL,
160	NULL,
161	NULL,
162	"SCSI target priority I/O",		/* 0x0060 */
163	"Invalid SCSI target port",
164	"Invalid SCSI target I/O index",
165	"SCSI target aborted",
166	"No connection retryable",
167	"No connection",
168	"FC aborted",
169	"Invalid FC receive ID",
170	"FC did invalid",			/* 0x0068 */
171	"FC node logged out",
172	"Transfer count mismatch",
173	"STS data not set",
174	"FC exchange canceled",
175	"Data offset error",
176	"Too much write data",
177	"IU too short",
178	"ACK NAK timeout",			/* 0x0070 */
179	"NAK received",
180	NULL,
181	NULL,
182	NULL,
183	NULL,
184	NULL,
185	NULL,
186	NULL,					/* 0x0078 */
187	NULL,
188	NULL,
189	NULL,
190	NULL,
191	NULL,
192	NULL,
193	NULL,
194	"LAN device not found",			/* 0x0080 */
195	"LAN device failure",
196	"LAN transmit error",
197	"LAN transmit aborted",
198	"LAN receive error",
199	"LAN receive aborted",
200	"LAN partial packet",
201	"LAN canceled",
202	NULL,					/* 0x0088 */
203	NULL,
204	NULL,
205	NULL,
206	NULL,
207	NULL,
208	NULL,
209	NULL,
210	"SAS SMP request failed",		/* 0x0090 */
211	"SAS SMP data overrun",
212	NULL,
213	NULL,
214	NULL,
215	NULL,
216	NULL,
217	NULL,
218	"Inband aborted",			/* 0x0098 */
219	"No inband connection",
220	NULL,
221	NULL,
222	NULL,
223	NULL,
224	NULL,
225	NULL,
226	"Diagnostic released",			/* 0x00A0 */
227};
228
229struct mprs_pass_thru {
230        uint64_t        PtrRequest;
231        uint64_t        PtrReply;
232        uint64_t        PtrData;
233        uint32_t        RequestSize;
234        uint32_t        ReplySize;
235        uint32_t        DataSize;
236        uint32_t        DataDirection;
237        uint64_t        PtrDataOut;
238        uint32_t        DataOutSize;
239        uint32_t        Timeout;
240};
241
242struct mprs_btdh_mapping {
243        uint16_t        TargetID;
244        uint16_t        Bus;
245        uint16_t        DevHandle;
246        uint16_t        Reserved;
247};
248
249const char *
250mps_ioc_status(U16 IOCStatus)
251{
252	static char buffer[16];
253
254	IOCStatus &= MPI2_IOCSTATUS_MASK;
255	if (IOCStatus < sizeof(mps_ioc_status_codes) / sizeof(char *) &&
256	    mps_ioc_status_codes[IOCStatus] != NULL)
257		return (mps_ioc_status_codes[IOCStatus]);
258	snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
259	return (buffer);
260}
261
262#ifdef USE_MPT_IOCTLS
263int
264mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target)
265{
266	int error;
267	struct mprs_btdh_mapping map;
268
269	map.Bus = *bus;
270	map.TargetID = *target;
271	map.DevHandle = *devhandle;
272
273	if ((error = ioctl(fd, MPTIOCTL_BTDH_MAPPING, &map)) != 0) {
274		error = errno;
275		warn("Failed to map bus/target/device");
276		return (error);
277	}
278
279	*bus = map.Bus;
280	*target = map.TargetID;
281	*devhandle = map.DevHandle;
282
283	return (0);
284}
285
286int
287mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
288    MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
289{
290	MPI2_CONFIG_REQUEST req;
291	MPI2_CONFIG_REPLY reply;
292
293	bzero(&req, sizeof(req));
294	req.Function = MPI2_FUNCTION_CONFIG;
295	req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
296	req.Header.PageType = PageType;
297	req.Header.PageNumber = PageNumber;
298	req.PageAddress = PageAddress;
299
300	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
301	    NULL, 0, NULL, 0, 30))
302		return (errno);
303
304	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
305		if (IOCStatus != NULL)
306			*IOCStatus = reply.IOCStatus;
307		return (EIO);
308	}
309	if (header == NULL)
310		return (EINVAL);
311	*header = reply.Header;
312	return (0);
313}
314
315int
316mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber, U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *ExtPageLength, U16 *IOCStatus)
317{
318	MPI2_CONFIG_REQUEST req;
319	MPI2_CONFIG_REPLY reply;
320
321	bzero(&req, sizeof(req));
322	req.Function = MPI2_FUNCTION_CONFIG;
323	req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
324	req.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
325	req.ExtPageType = ExtPageType;
326	req.Header.PageNumber = PageNumber;
327	req.PageAddress = PageAddress;
328
329	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
330	    NULL, 0, NULL, 0, 30))
331		return (errno);
332
333	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
334		if (IOCStatus != NULL)
335			*IOCStatus = reply.IOCStatus;
336		return (EIO);
337	}
338	if ((header == NULL) || (ExtPageLength == NULL))
339		return (EINVAL);
340	*header = reply.Header;
341	*ExtPageLength = reply.ExtPageLength;
342	return (0);
343}
344
345void *
346mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
347    U16 *IOCStatus)
348{
349	MPI2_CONFIG_REQUEST req;
350	MPI2_CONFIG_PAGE_HEADER header;
351	MPI2_CONFIG_REPLY reply;
352	void *buf;
353	int error, len;
354
355	bzero(&header, sizeof(header));
356	error = mps_read_config_page_header(fd, PageType, PageNumber,
357	    PageAddress, &header, IOCStatus);
358	if (error) {
359		errno = error;
360		return (NULL);
361	}
362
363	bzero(&req, sizeof(req));
364	req.Function = MPI2_FUNCTION_CONFIG;
365	req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
366	req.PageAddress = PageAddress;
367	req.Header = header;
368	req.Header.PageLength = reply.Header.PageLength;
369	if (reply.Header.PageLength == 0)
370		req.Header.PageLength = 4;
371
372	len = req.Header.PageLength * 4;
373	buf = malloc(len);
374	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
375	    buf, len, NULL, 0, 30)) {
376		error = errno;
377		free(buf);
378		errno = error;
379		return (NULL);
380	}
381	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
382		if (IOCStatus != NULL)
383			*IOCStatus = reply.IOCStatus;
384		else
385			warnx("Reading config page failed: 0x%x %s",
386			    reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
387		free(buf);
388		errno = EIO;
389		return (NULL);
390	}
391	return (buf);
392}
393
394void *
395mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
396    U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
397{
398	MPI2_CONFIG_REQUEST req;
399	MPI2_CONFIG_PAGE_HEADER header;
400	MPI2_CONFIG_REPLY reply;
401	U16 pagelen;
402	void *buf;
403	int error, len;
404
405	if (IOCStatus != NULL)
406		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
407	bzero(&header, sizeof(header));
408	error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber,
409	    PageAddress, &header, &pagelen, IOCStatus);
410	if (error) {
411		errno = error;
412		return (NULL);
413	}
414
415	bzero(&req, sizeof(req));
416	req.Function = MPI2_FUNCTION_CONFIG;
417	req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
418	req.PageAddress = PageAddress;
419	req.Header = header;
420	if (pagelen == 0)
421		pagelen = 4;
422	req.ExtPageLength = pagelen;
423	req.ExtPageType = ExtPageType;
424
425	len = pagelen * 4;
426	buf = malloc(len);
427	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
428	    buf, len, NULL, 0, 30)) {
429		error = errno;
430		free(buf);
431		errno = error;
432		return (NULL);
433	}
434	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
435		if (IOCStatus != NULL)
436			*IOCStatus = reply.IOCStatus;
437		else
438			warnx("Reading extended config page failed: %s",
439			    mps_ioc_status(reply.IOCStatus));
440		free(buf);
441		errno = EIO;
442		return (NULL);
443	}
444	return (buf);
445}
446
447int
448mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
449{
450	MPI2_FW_DOWNLOAD_REQUEST req;
451	MPI2_FW_DOWNLOAD_REPLY reply;
452
453	bzero(&req, sizeof(req));
454	bzero(&reply, sizeof(reply));
455	req.Function = MPI2_FUNCTION_FW_DOWNLOAD;
456	req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
457	req.TotalImageSize = len;
458	req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT;
459
460	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
461	    fw, len, 0)) {
462		return (-1);
463	}
464	return (0);
465}
466
467int
468mps_firmware_get(int fd, unsigned char **firmware, bool bios)
469{
470	MPI2_FW_UPLOAD_REQUEST req;
471	MPI2_FW_UPLOAD_REPLY reply;
472	int size;
473
474	*firmware = NULL;
475	bzero(&req, sizeof(req));
476	bzero(&reply, sizeof(reply));
477	req.Function = MPI2_FUNCTION_FW_UPLOAD;
478	req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
479
480	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
481	    NULL, 0, 0)) {
482		return (-1);
483	}
484	if (reply.ActualImageSize == 0) {
485		return (-1);
486	}
487
488	size = reply.ActualImageSize;
489	*firmware = calloc(size, sizeof(unsigned char));
490	if (*firmware == NULL) {
491		warn("calloc");
492		return (-1);
493	}
494	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
495	    *firmware, size, 0)) {
496		free(*firmware);
497		return (-1);
498	}
499
500	return (size);
501}
502
503#else
504
505int
506mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
507    MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
508{
509	struct mps_cfg_page_req req;
510
511	if (IOCStatus != NULL)
512		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
513	if (header == NULL)
514		return (EINVAL);
515	bzero(&req, sizeof(req));
516	req.header.PageType = PageType;
517	req.header.PageNumber = PageNumber;
518	req.page_address = PageAddress;
519	if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0)
520		return (errno);
521	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
522		if (IOCStatus != NULL)
523			*IOCStatus = req.ioc_status;
524		return (EIO);
525	}
526	bcopy(&req.header, header, sizeof(*header));
527	return (0);
528}
529
530void *
531mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
532    U16 *IOCStatus)
533{
534	struct mps_cfg_page_req req;
535	void *buf;
536	int error;
537
538	error = mps_read_config_page_header(fd, PageType, PageNumber,
539	    PageAddress, &req.header, IOCStatus);
540	if (error) {
541		errno = error;
542		return (NULL);
543	}
544
545	if (req.header.PageLength == 0)
546		req.header.PageLength = 4;
547	req.len = req.header.PageLength * 4;
548	buf = malloc(req.len);
549	req.buf = buf;
550	bcopy(&req.header, buf, sizeof(req.header));
551	if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
552		error = errno;
553		free(buf);
554		errno = error;
555		return (NULL);
556	}
557	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
558		if (IOCStatus != NULL)
559			*IOCStatus = req.ioc_status;
560		else
561			warnx("Reading config page failed: 0x%x %s",
562			    req.ioc_status, mps_ioc_status(req.ioc_status));
563		free(buf);
564		errno = EIO;
565		return (NULL);
566	}
567	return (buf);
568}
569
570void *
571mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
572    U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
573{
574	struct mps_ext_cfg_page_req req;
575	void *buf;
576	int error;
577
578	if (IOCStatus != NULL)
579		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
580	bzero(&req, sizeof(req));
581	req.header.PageVersion = PageVersion;
582	req.header.PageNumber = PageNumber;
583	req.header.ExtPageType = ExtPageType;
584	req.page_address = PageAddress;
585	if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0)
586		return (NULL);
587	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
588		if (IOCStatus != NULL)
589			*IOCStatus = req.ioc_status;
590		else
591			warnx("Reading extended config page header failed: %s",
592			    mps_ioc_status(req.ioc_status));
593		errno = EIO;
594		return (NULL);
595	}
596	req.len = req.header.ExtPageLength * 4;
597	buf = malloc(req.len);
598	req.buf = buf;
599	bcopy(&req.header, buf, sizeof(req.header));
600	if (ioctl(fd, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) {
601		error = errno;
602		free(buf);
603		errno = error;
604		return (NULL);
605	}
606	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
607		if (IOCStatus != NULL)
608			*IOCStatus = req.ioc_status;
609		else
610			warnx("Reading extended config page failed: %s",
611			    mps_ioc_status(req.ioc_status));
612		free(buf);
613		errno = EIO;
614		return (NULL);
615	}
616	return (buf);
617}
618#endif
619
620int
621mps_open(int unit)
622{
623	char path[MAXPATHLEN];
624
625	snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
626	return (open(path, O_RDWR));
627}
628
629int
630mps_user_command(int fd, void *req, uint32_t req_len, void *reply,
631        uint32_t reply_len, void *buffer, int len, uint32_t flags)
632{
633	struct mps_usr_command cmd;
634
635	bzero(&cmd, sizeof(struct mps_usr_command));
636	cmd.req = req;
637	cmd.req_len = req_len;
638	cmd.rpl = reply;
639	cmd.rpl_len = reply_len;
640	cmd.buf = buffer;
641	cmd.len = len;
642	cmd.flags = flags;
643
644	if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
645		return (errno);
646	return (0);
647}
648
649int
650mps_pass_command(int fd, void *req, uint32_t req_len, void *reply,
651	uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
652	uint32_t dataout_len, uint32_t timeout)
653{
654	struct mprs_pass_thru pass;
655
656	pass.PtrRequest = (uint64_t)(uintptr_t)req;
657	pass.PtrReply = (uint64_t)(uintptr_t)reply;
658	pass.PtrData = (uint64_t)(uintptr_t)data_in;
659	pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
660	pass.RequestSize = req_len;
661	pass.ReplySize = reply_len;
662	pass.DataSize = datain_len;
663	pass.DataOutSize = dataout_len;
664	if (datain_len && dataout_len) {
665		if (is_mps) {
666			pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
667		} else {
668			pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
669		}
670	} else if (datain_len) {
671		if (is_mps) {
672			pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
673		} else {
674			pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
675		}
676	} else if (dataout_len) {
677		if (is_mps) {
678			pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
679		} else {
680			pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
681		}
682	} else {
683		if (is_mps) {
684			pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
685		} else {
686			pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
687		}
688	}
689	pass.Timeout = timeout;
690
691	if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
692		return (errno);
693	return (0);
694}
695
696MPI2_IOC_FACTS_REPLY *
697mps_get_iocfacts(int fd)
698{
699	MPI2_IOC_FACTS_REPLY *facts;
700	MPI2_IOC_FACTS_REQUEST req;
701	int error;
702
703	facts = malloc(sizeof(MPI2_IOC_FACTS_REPLY));
704	if (facts == NULL) {
705		errno = ENOMEM;
706		return (NULL);
707	}
708
709	bzero(&req, sizeof(MPI2_IOC_FACTS_REQUEST));
710	req.Function = MPI2_FUNCTION_IOC_FACTS;
711
712#if 1
713	error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
714	    facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, NULL, 0, 10);
715#else
716	error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
717	    facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, 0);
718#endif
719	if (error) {
720		free(facts);
721		return (NULL);
722	}
723
724	if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
725		free(facts);
726		errno = EINVAL;
727		return (NULL);
728	}
729	return (facts);
730}
731
732