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