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