xref: /freebsd/usr.sbin/mpsutil/mps_cmd.c (revision 3416500aef140042c64bc149cb1ec6620483bc44)
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 	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 
394 void *
395 mps_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 
447 int
448 mps_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 
467 int
468 mps_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 
505 int
506 mps_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 
530 void *
531 mps_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 
570 void *
571 mps_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 
620 int
621 mps_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 
629 int
630 mps_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 
649 int
650 mps_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 
696 MPI2_IOC_FACTS_REPLY *
697 mps_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