xref: /freebsd/usr.sbin/mpsutil/mps_cmd.c (revision 13ea0450a9c8742119d36f3bf8f47accdce46e54)
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_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
287     MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
288 {
289 	MPI2_CONFIG_REQUEST req;
290 	MPI2_CONFIG_REPLY reply;
291 
292 	bzero(&req, sizeof(req));
293 	req.Function = MPI2_FUNCTION_CONFIG;
294 	req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
295 	req.Header.PageType = PageType;
296 	req.Header.PageNumber = PageNumber;
297 	req.PageAddress = PageAddress;
298 
299 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
300 	    NULL, 0, NULL, 0, 30))
301 		return (errno);
302 
303 	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
304 		if (IOCStatus != NULL)
305 			*IOCStatus = reply.IOCStatus;
306 		return (EIO);
307 	}
308 	if (header == NULL)
309 		return (EINVAL);
310 	*header = reply.Header;
311 	return (0);
312 }
313 
314 int
315 mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber, U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *ExtPageLength, U16 *IOCStatus)
316 {
317 	MPI2_CONFIG_REQUEST req;
318 	MPI2_CONFIG_REPLY reply;
319 
320 	bzero(&req, sizeof(req));
321 	req.Function = MPI2_FUNCTION_CONFIG;
322 	req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
323 	req.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
324 	req.ExtPageType = ExtPageType;
325 	req.Header.PageNumber = PageNumber;
326 	req.PageAddress = PageAddress;
327 
328 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
329 	    NULL, 0, NULL, 0, 30))
330 		return (errno);
331 
332 	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
333 		if (IOCStatus != NULL)
334 			*IOCStatus = reply.IOCStatus;
335 		return (EIO);
336 	}
337 	if ((header == NULL) || (ExtPageLength == NULL))
338 		return (EINVAL);
339 	*header = reply.Header;
340 	*ExtPageLength = reply.ExtPageLength;
341 	return (0);
342 }
343 
344 void *
345 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
346     U16 *IOCStatus)
347 {
348 	MPI2_CONFIG_REQUEST req;
349 	MPI2_CONFIG_PAGE_HEADER header;
350 	MPI2_CONFIG_REPLY reply;
351 	void *buf;
352 	int error, len;
353 
354 	bzero(&header, sizeof(header));
355 	error = mps_read_config_page_header(fd, PageType, PageNumber,
356 	    PageAddress, &header, IOCStatus);
357 	if (error) {
358 		errno = error;
359 		return (NULL);
360 	}
361 
362 	bzero(&req, sizeof(req));
363 	req.Function = MPI2_FUNCTION_CONFIG;
364 	req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
365 	req.PageAddress = PageAddress;
366 	req.Header = header;
367 	if (req.Header.PageLength == 0)
368 		req.Header.PageLength = 4;
369 
370 	len = req.Header.PageLength * 4;
371 	buf = malloc(len);
372 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
373 	    buf, len, NULL, 0, 30)) {
374 		error = errno;
375 		free(buf);
376 		errno = error;
377 		return (NULL);
378 	}
379 	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
380 		if (IOCStatus != NULL)
381 			*IOCStatus = reply.IOCStatus;
382 		else
383 			warnx("Reading config page failed: 0x%x %s",
384 			    reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
385 		free(buf);
386 		errno = EIO;
387 		return (NULL);
388 	}
389 	return (buf);
390 }
391 
392 void *
393 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
394     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
395 {
396 	MPI2_CONFIG_REQUEST req;
397 	MPI2_CONFIG_PAGE_HEADER header;
398 	MPI2_CONFIG_REPLY reply;
399 	U16 pagelen;
400 	void *buf;
401 	int error, len;
402 
403 	if (IOCStatus != NULL)
404 		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
405 	bzero(&header, sizeof(header));
406 	error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber,
407 	    PageAddress, &header, &pagelen, IOCStatus);
408 	if (error) {
409 		errno = error;
410 		return (NULL);
411 	}
412 
413 	bzero(&req, sizeof(req));
414 	req.Function = MPI2_FUNCTION_CONFIG;
415 	req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
416 	req.PageAddress = PageAddress;
417 	req.Header = header;
418 	if (pagelen == 0)
419 		pagelen = 4;
420 	req.ExtPageLength = pagelen;
421 	req.ExtPageType = ExtPageType;
422 
423 	len = pagelen * 4;
424 	buf = malloc(len);
425 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
426 	    buf, len, NULL, 0, 30)) {
427 		error = errno;
428 		free(buf);
429 		errno = error;
430 		return (NULL);
431 	}
432 	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
433 		if (IOCStatus != NULL)
434 			*IOCStatus = reply.IOCStatus;
435 		else
436 			warnx("Reading extended config page failed: %s",
437 			    mps_ioc_status(reply.IOCStatus));
438 		free(buf);
439 		errno = EIO;
440 		return (NULL);
441 	}
442 	return (buf);
443 }
444 
445 int
446 mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
447 {
448 	MPI2_FW_DOWNLOAD_REQUEST req;
449 	MPI2_FW_DOWNLOAD_REPLY reply;
450 
451 	bzero(&req, sizeof(req));
452 	bzero(&reply, sizeof(reply));
453 	req.Function = MPI2_FUNCTION_FW_DOWNLOAD;
454 	req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
455 	req.TotalImageSize = len;
456 	req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT;
457 
458 	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
459 	    fw, len, 0)) {
460 		return (-1);
461 	}
462 	return (0);
463 }
464 
465 int
466 mps_firmware_get(int fd, unsigned char **firmware, bool bios)
467 {
468 	MPI2_FW_UPLOAD_REQUEST req;
469 	MPI2_FW_UPLOAD_REPLY reply;
470 	int size;
471 
472 	*firmware = NULL;
473 	bzero(&req, sizeof(req));
474 	bzero(&reply, sizeof(reply));
475 	req.Function = MPI2_FUNCTION_FW_UPLOAD;
476 	req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
477 
478 	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
479 	    NULL, 0, 0)) {
480 		return (-1);
481 	}
482 	if (reply.ActualImageSize == 0) {
483 		return (-1);
484 	}
485 
486 	size = reply.ActualImageSize;
487 	*firmware = calloc(size, sizeof(unsigned char));
488 	if (*firmware == NULL) {
489 		warn("calloc");
490 		return (-1);
491 	}
492 	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
493 	    *firmware, size, 0)) {
494 		free(*firmware);
495 		return (-1);
496 	}
497 
498 	return (size);
499 }
500 
501 #else
502 
503 int
504 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
505     MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
506 {
507 	struct mps_cfg_page_req req;
508 
509 	if (IOCStatus != NULL)
510 		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
511 	if (header == NULL)
512 		return (EINVAL);
513 	bzero(&req, sizeof(req));
514 	req.header.PageType = PageType;
515 	req.header.PageNumber = PageNumber;
516 	req.page_address = PageAddress;
517 	if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0)
518 		return (errno);
519 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
520 		if (IOCStatus != NULL)
521 			*IOCStatus = req.ioc_status;
522 		return (EIO);
523 	}
524 	bcopy(&req.header, header, sizeof(*header));
525 	return (0);
526 }
527 
528 void *
529 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
530     U16 *IOCStatus)
531 {
532 	struct mps_cfg_page_req req;
533 	void *buf;
534 	int error;
535 
536 	error = mps_read_config_page_header(fd, PageType, PageNumber,
537 	    PageAddress, &req.header, IOCStatus);
538 	if (error) {
539 		errno = error;
540 		return (NULL);
541 	}
542 
543 	if (req.header.PageLength == 0)
544 		req.header.PageLength = 4;
545 	req.len = req.header.PageLength * 4;
546 	buf = malloc(req.len);
547 	req.buf = buf;
548 	bcopy(&req.header, buf, sizeof(req.header));
549 	if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
550 		error = errno;
551 		free(buf);
552 		errno = error;
553 		return (NULL);
554 	}
555 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
556 		if (IOCStatus != NULL)
557 			*IOCStatus = req.ioc_status;
558 		else
559 			warnx("Reading config page failed: 0x%x %s",
560 			    req.ioc_status, mps_ioc_status(req.ioc_status));
561 		free(buf);
562 		errno = EIO;
563 		return (NULL);
564 	}
565 	return (buf);
566 }
567 
568 void *
569 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
570     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
571 {
572 	struct mps_ext_cfg_page_req req;
573 	void *buf;
574 	int error;
575 
576 	if (IOCStatus != NULL)
577 		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
578 	bzero(&req, sizeof(req));
579 	req.header.PageVersion = PageVersion;
580 	req.header.PageNumber = PageNumber;
581 	req.header.ExtPageType = ExtPageType;
582 	req.page_address = PageAddress;
583 	if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0)
584 		return (NULL);
585 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
586 		if (IOCStatus != NULL)
587 			*IOCStatus = req.ioc_status;
588 		else
589 			warnx("Reading extended config page header failed: %s",
590 			    mps_ioc_status(req.ioc_status));
591 		errno = EIO;
592 		return (NULL);
593 	}
594 	req.len = req.header.ExtPageLength * 4;
595 	buf = malloc(req.len);
596 	req.buf = buf;
597 	bcopy(&req.header, buf, sizeof(req.header));
598 	if (ioctl(fd, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) {
599 		error = errno;
600 		free(buf);
601 		errno = error;
602 		return (NULL);
603 	}
604 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
605 		if (IOCStatus != NULL)
606 			*IOCStatus = req.ioc_status;
607 		else
608 			warnx("Reading extended config page failed: %s",
609 			    mps_ioc_status(req.ioc_status));
610 		free(buf);
611 		errno = EIO;
612 		return (NULL);
613 	}
614 	return (buf);
615 }
616 #endif
617 
618 int
619 mps_open(int unit)
620 {
621 	char path[MAXPATHLEN];
622 
623 	snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
624 	return (open(path, O_RDWR));
625 }
626 
627 int
628 mps_user_command(int fd, void *req, uint32_t req_len, void *reply,
629         uint32_t reply_len, void *buffer, int len, uint32_t flags)
630 {
631 	struct mps_usr_command cmd;
632 
633 	bzero(&cmd, sizeof(struct mps_usr_command));
634 	cmd.req = req;
635 	cmd.req_len = req_len;
636 	cmd.rpl = reply;
637 	cmd.rpl_len = reply_len;
638 	cmd.buf = buffer;
639 	cmd.len = len;
640 	cmd.flags = flags;
641 
642 	if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
643 		return (errno);
644 	return (0);
645 }
646 
647 int
648 mps_pass_command(int fd, void *req, uint32_t req_len, void *reply,
649 	uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
650 	uint32_t dataout_len, uint32_t timeout)
651 {
652 	struct mprs_pass_thru pass;
653 
654 	pass.PtrRequest = (uint64_t)(uintptr_t)req;
655 	pass.PtrReply = (uint64_t)(uintptr_t)reply;
656 	pass.PtrData = (uint64_t)(uintptr_t)data_in;
657 	pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
658 	pass.RequestSize = req_len;
659 	pass.ReplySize = reply_len;
660 	pass.DataSize = datain_len;
661 	pass.DataOutSize = dataout_len;
662 	if (datain_len && dataout_len) {
663 		if (is_mps) {
664 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
665 		} else {
666 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
667 		}
668 	} else if (datain_len) {
669 		if (is_mps) {
670 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
671 		} else {
672 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
673 		}
674 	} else if (dataout_len) {
675 		if (is_mps) {
676 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
677 		} else {
678 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
679 		}
680 	} else {
681 		if (is_mps) {
682 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
683 		} else {
684 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
685 		}
686 	}
687 	pass.Timeout = timeout;
688 
689 	if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
690 		return (errno);
691 	return (0);
692 }
693 
694 MPI2_IOC_FACTS_REPLY *
695 mps_get_iocfacts(int fd)
696 {
697 	MPI2_IOC_FACTS_REPLY *facts;
698 	MPI2_IOC_FACTS_REQUEST req;
699 	int error;
700 
701 	facts = malloc(sizeof(MPI2_IOC_FACTS_REPLY));
702 	if (facts == NULL) {
703 		errno = ENOMEM;
704 		return (NULL);
705 	}
706 
707 	bzero(&req, sizeof(MPI2_IOC_FACTS_REQUEST));
708 	req.Function = MPI2_FUNCTION_IOC_FACTS;
709 
710 #if 1
711 	error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
712 	    facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, NULL, 0, 10);
713 #else
714 	error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
715 	    facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, 0);
716 #endif
717 	if (error) {
718 		free(facts);
719 		return (NULL);
720 	}
721 
722 	if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
723 		free(facts);
724 		errno = EINVAL;
725 		return (NULL);
726 	}
727 	return (facts);
728 }
729 
730