xref: /freebsd/usr.sbin/mpsutil/mps_cmd.c (revision 257e70f1d5ee61037c8c59b116538d3b6b1427a2)
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/param.h>
37 #include <sys/errno.h>
38 #include <sys/ioctl.h>
39 #include <sys/sysctl.h>
40 #include <sys/uio.h>
41 #include <sys/endian.h>
42 
43 #include <err.h>
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 
50 #include "mpsutil.h"
51 #include <dev/mps/mps_ioctl.h>
52 #include <dev/mpr/mpr_ioctl.h>
53 
54 #ifndef USE_MPT_IOCTLS
55 #define USE_MPT_IOCTLS
56 #endif
57 
58 static const char *mps_ioc_status_codes[] = {
59 	"Success",				/* 0x0000 */
60 	"Invalid function",
61 	"Busy",
62 	"Invalid scatter-gather list",
63 	"Internal error",
64 	"Reserved",
65 	"Insufficient resources",
66 	"Invalid field",
67 	"Invalid state",			/* 0x0008 */
68 	"Operation state not supported",
69 	NULL,
70 	NULL,
71 	NULL,
72 	NULL,
73 	NULL,
74 	NULL,
75 	NULL,					/* 0x0010 */
76 	NULL,
77 	NULL,
78 	NULL,
79 	NULL,
80 	NULL,
81 	NULL,
82 	NULL,
83 	NULL,					/* 0x0018 */
84 	NULL,
85 	NULL,
86 	NULL,
87 	NULL,
88 	NULL,
89 	NULL,
90 	NULL,
91 	"Invalid configuration action",		/* 0x0020 */
92 	"Invalid configuration type",
93 	"Invalid configuration page",
94 	"Invalid configuration data",
95 	"No configuration defaults",
96 	"Unable to commit configuration change",
97 	NULL,
98 	NULL,
99 	NULL,					/* 0x0028 */
100 	NULL,
101 	NULL,
102 	NULL,
103 	NULL,
104 	NULL,
105 	NULL,
106 	NULL,
107 	NULL,					/* 0x0030 */
108 	NULL,
109 	NULL,
110 	NULL,
111 	NULL,
112 	NULL,
113 	NULL,
114 	NULL,
115 	NULL,					/* 0x0038 */
116 	NULL,
117 	NULL,
118 	NULL,
119 	NULL,
120 	NULL,
121 	NULL,
122 	NULL,
123 	"Recovered SCSI error",			/* 0x0040 */
124 	"Invalid SCSI bus",
125 	"Invalid SCSI target ID",
126 	"SCSI device not there",
127 	"SCSI data overrun",
128 	"SCSI data underrun",
129 	"SCSI I/O error",
130 	"SCSI protocol error",
131 	"SCSI task terminated",			/* 0x0048 */
132 	"SCSI residual mismatch",
133 	"SCSI task management failed",
134 	"SCSI I/O controller terminated",
135 	"SCSI external controller terminated",
136 	"EEDP guard error",
137 	"EEDP reference tag error",
138 	"EEDP application tag error",
139 	NULL,					/* 0x0050 */
140 	NULL,
141 	NULL,
142 	NULL,
143 	NULL,
144 	NULL,
145 	NULL,
146 	NULL,
147 	NULL,					/* 0x0058 */
148 	NULL,
149 	NULL,
150 	NULL,
151 	NULL,
152 	NULL,
153 	NULL,
154 	NULL,
155 	"SCSI target priority I/O",		/* 0x0060 */
156 	"Invalid SCSI target port",
157 	"Invalid SCSI target I/O index",
158 	"SCSI target aborted",
159 	"No connection retryable",
160 	"No connection",
161 	"FC aborted",
162 	"Invalid FC receive ID",
163 	"FC did invalid",			/* 0x0068 */
164 	"FC node logged out",
165 	"Transfer count mismatch",
166 	"STS data not set",
167 	"FC exchange canceled",
168 	"Data offset error",
169 	"Too much write data",
170 	"IU too short",
171 	"ACK NAK timeout",			/* 0x0070 */
172 	"NAK received",
173 	NULL,
174 	NULL,
175 	NULL,
176 	NULL,
177 	NULL,
178 	NULL,
179 	NULL,					/* 0x0078 */
180 	NULL,
181 	NULL,
182 	NULL,
183 	NULL,
184 	NULL,
185 	NULL,
186 	NULL,
187 	"LAN device not found",			/* 0x0080 */
188 	"LAN device failure",
189 	"LAN transmit error",
190 	"LAN transmit aborted",
191 	"LAN receive error",
192 	"LAN receive aborted",
193 	"LAN partial packet",
194 	"LAN canceled",
195 	NULL,					/* 0x0088 */
196 	NULL,
197 	NULL,
198 	NULL,
199 	NULL,
200 	NULL,
201 	NULL,
202 	NULL,
203 	"SAS SMP request failed",		/* 0x0090 */
204 	"SAS SMP data overrun",
205 	NULL,
206 	NULL,
207 	NULL,
208 	NULL,
209 	NULL,
210 	NULL,
211 	"Inband aborted",			/* 0x0098 */
212 	"No inband connection",
213 	NULL,
214 	NULL,
215 	NULL,
216 	NULL,
217 	NULL,
218 	NULL,
219 	"Diagnostic released",			/* 0x00A0 */
220 };
221 
222 struct mprs_pass_thru {
223         uint64_t        PtrRequest;
224         uint64_t        PtrReply;
225         uint64_t        PtrData;
226         uint32_t        RequestSize;
227         uint32_t        ReplySize;
228         uint32_t        DataSize;
229         uint32_t        DataDirection;
230         uint64_t        PtrDataOut;
231         uint32_t        DataOutSize;
232         uint32_t        Timeout;
233 };
234 
235 struct mprs_btdh_mapping {
236         uint16_t        TargetID;
237         uint16_t        Bus;
238         uint16_t        DevHandle;
239         uint16_t        Reserved;
240 };
241 
242 static void adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts);
243 
244 const char *
245 mps_ioc_status(U16 IOCStatus)
246 {
247 	static char buffer[16];
248 
249 	IOCStatus &= MPI2_IOCSTATUS_MASK;
250 	if (IOCStatus < sizeof(mps_ioc_status_codes) / sizeof(char *) &&
251 	    mps_ioc_status_codes[IOCStatus] != NULL)
252 		return (mps_ioc_status_codes[IOCStatus]);
253 	snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
254 	return (buffer);
255 }
256 
257 #ifdef USE_MPT_IOCTLS
258 int
259 mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target)
260 {
261 	int error;
262 	struct mprs_btdh_mapping map;
263 
264 	map.Bus = *bus;
265 	map.TargetID = *target;
266 	map.DevHandle = *devhandle;
267 
268 	if ((error = ioctl(fd, MPTIOCTL_BTDH_MAPPING, &map)) != 0) {
269 		error = errno;
270 		warn("Failed to map bus/target/device");
271 		return (error);
272 	}
273 
274 	*bus = map.Bus;
275 	*target = map.TargetID;
276 	*devhandle = map.DevHandle;
277 
278 	return (0);
279 }
280 
281 int
282 mps_set_slot_status(int fd, U16 handle, U16 slot, U32 status)
283 {
284 	MPI2_SEP_REQUEST req;
285 	MPI2_SEP_REPLY reply;
286 
287 	bzero(&req, sizeof(req));
288 	req.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;
289 	req.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;
290 	req.Flags = MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS;
291 	req.EnclosureHandle = handle;
292 	req.Slot = slot;
293 	req.SlotStatus = status;
294 
295 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
296 	    NULL, 0, NULL, 0, 30) != 0)
297 		return (errno);
298 
299 	if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus)))
300 		return (EIO);
301 	return (0);
302 }
303 
304 int
305 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
306     MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
307 {
308 	MPI2_CONFIG_REQUEST req;
309 	MPI2_CONFIG_REPLY reply;
310 
311 	bzero(&req, sizeof(req));
312 	req.Function = MPI2_FUNCTION_CONFIG;
313 	req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
314 	req.Header.PageType = PageType;
315 	req.Header.PageNumber = PageNumber;
316 	req.PageAddress = PageAddress;
317 
318 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
319 	    NULL, 0, NULL, 0, 30))
320 		return (errno);
321 
322 	if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus))) {
323 		if (IOCStatus != NULL)
324 			*IOCStatus = reply.IOCStatus;
325 		return (EIO);
326 	}
327 	if (header == NULL)
328 		return (EINVAL);
329 	*header = reply.Header;
330 	return (0);
331 }
332 
333 int
334 mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber, U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *ExtPageLength, U16 *IOCStatus)
335 {
336 	MPI2_CONFIG_REQUEST req;
337 	MPI2_CONFIG_REPLY reply;
338 
339 	bzero(&req, sizeof(req));
340 	req.Function = MPI2_FUNCTION_CONFIG;
341 	req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
342 	req.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
343 	req.ExtPageType = ExtPageType;
344 	req.Header.PageNumber = PageNumber;
345 	req.PageAddress = htole32(PageAddress);
346 
347 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
348 	    NULL, 0, NULL, 0, 30))
349 		return (errno);
350 
351 	if (!IOC_STATUS_SUCCESS(le16toh(reply.IOCStatus))) {
352 		if (IOCStatus != NULL)
353 			*IOCStatus = le16toh(reply.IOCStatus);
354 		return (EIO);
355 	}
356 	if ((header == NULL) || (ExtPageLength == NULL))
357 		return (EINVAL);
358 	*header = reply.Header;
359 	*ExtPageLength = reply.ExtPageLength;
360 	return (0);
361 }
362 
363 void *
364 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
365     U16 *IOCStatus)
366 {
367 	MPI2_CONFIG_REQUEST req;
368 	MPI2_CONFIG_PAGE_HEADER header;
369 	MPI2_CONFIG_REPLY reply;
370 	void *buf;
371 	int error, len;
372 
373 	bzero(&header, sizeof(header));
374 	error = mps_read_config_page_header(fd, PageType, PageNumber,
375 	    PageAddress, &header, IOCStatus);
376 	if (error) {
377 		errno = error;
378 		return (NULL);
379 	}
380 
381 	bzero(&req, sizeof(req));
382 	req.Function = MPI2_FUNCTION_CONFIG;
383 	req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
384 	req.PageAddress = htole32(PageAddress);
385 	req.Header = header;
386 	if (req.Header.PageLength == 0)
387 		req.Header.PageLength = 4;
388 
389 	len = req.Header.PageLength * 4;
390 	buf = malloc(len);
391 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
392 	    buf, len, NULL, 0, 30)) {
393 		error = errno;
394 		free(buf);
395 		errno = error;
396 		return (NULL);
397 	}
398 	reply.IOCStatus = le16toh(reply.IOCStatus);
399 	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
400 		if (IOCStatus != NULL)
401 			*IOCStatus = reply.IOCStatus;
402 		else
403 			warnx("Reading config page failed: 0x%x %s",
404 			    reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
405 		free(buf);
406 		errno = EIO;
407 		return (NULL);
408 	}
409 	return (buf);
410 }
411 
412 void *
413 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
414     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
415 {
416 	MPI2_CONFIG_REQUEST req;
417 	MPI2_CONFIG_PAGE_HEADER header;
418 	MPI2_CONFIG_REPLY reply;
419 	U16 pagelen;
420 	void *buf;
421 	int error, len;
422 
423 	if (IOCStatus != NULL)
424 		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
425 	bzero(&header, sizeof(header));
426 	error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber,
427 	    PageAddress, &header, &pagelen, IOCStatus);
428 	if (error) {
429 		errno = error;
430 		return (NULL);
431 	}
432 
433 	bzero(&req, sizeof(req));
434 	req.Function = MPI2_FUNCTION_CONFIG;
435 	req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
436 	req.PageAddress = htole32(PageAddress);
437 	req.Header = header;
438 	if (pagelen == 0)
439 		pagelen = htole16(4);
440 	req.ExtPageLength = pagelen;
441 	req.ExtPageType = ExtPageType;
442 
443 	len = le16toh(pagelen) * 4;
444 	buf = malloc(len);
445 	if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
446 	    buf, len, NULL, 0, 30)) {
447 		error = errno;
448 		free(buf);
449 		errno = error;
450 		return (NULL);
451 	}
452 	reply.IOCStatus = le16toh(reply.IOCStatus);
453 	if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
454 		if (IOCStatus != NULL)
455 			*IOCStatus = reply.IOCStatus;
456 		else
457 			warnx("Reading extended config page failed: %s",
458 			    mps_ioc_status(reply.IOCStatus));
459 		free(buf);
460 		errno = EIO;
461 		return (NULL);
462 	}
463 	return (buf);
464 }
465 
466 int
467 mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
468 {
469 	MPI2_FW_DOWNLOAD_REQUEST req;
470 	MPI2_FW_DOWNLOAD_REPLY reply;
471 
472 	bzero(&req, sizeof(req));
473 	bzero(&reply, sizeof(reply));
474 	req.Function = MPI2_FUNCTION_FW_DOWNLOAD;
475 	req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
476 	req.TotalImageSize = htole32(len);
477 	req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT;
478 
479 	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
480 	    fw, len, 0)) {
481 		return (-1);
482 	}
483 	return (0);
484 }
485 
486 int
487 mps_firmware_get(int fd, unsigned char **firmware, bool bios)
488 {
489 	MPI2_FW_UPLOAD_REQUEST req;
490 	MPI2_FW_UPLOAD_REPLY reply;
491 	int size;
492 
493 	*firmware = NULL;
494 	bzero(&req, sizeof(req));
495 	bzero(&reply, sizeof(reply));
496 	req.Function = MPI2_FUNCTION_FW_UPLOAD;
497 	req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
498 
499 	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
500 	    NULL, 0, 0)) {
501 		return (-1);
502 	}
503 	if (reply.ActualImageSize == 0) {
504 		return (-1);
505 	}
506 
507 	size = le32toh(reply.ActualImageSize);
508 	*firmware = calloc(size, sizeof(unsigned char));
509 	if (*firmware == NULL) {
510 		warn("calloc");
511 		return (-1);
512 	}
513 	if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
514 	    *firmware, size, 0)) {
515 		free(*firmware);
516 		return (-1);
517 	}
518 
519 	return (size);
520 }
521 
522 #else
523 
524 int
525 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
526     MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
527 {
528 	struct mps_cfg_page_req req;
529 
530 	if (IOCStatus != NULL)
531 		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
532 	if (header == NULL)
533 		return (EINVAL);
534 	bzero(&req, sizeof(req));
535 	req.header.PageType = PageType;
536 	req.header.PageNumber = PageNumber;
537 	req.page_address = PageAddress;
538 	if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0)
539 		return (errno);
540 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
541 		if (IOCStatus != NULL)
542 			*IOCStatus = req.ioc_status;
543 		return (EIO);
544 	}
545 	bcopy(&req.header, header, sizeof(*header));
546 	return (0);
547 }
548 
549 void *
550 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
551     U16 *IOCStatus)
552 {
553 	struct mps_cfg_page_req req;
554 	void *buf;
555 	int error;
556 
557 	error = mps_read_config_page_header(fd, PageType, PageNumber,
558 	    PageAddress, &req.header, IOCStatus);
559 	if (error) {
560 		errno = error;
561 		return (NULL);
562 	}
563 
564 	if (req.header.PageLength == 0)
565 		req.header.PageLength = 4;
566 	req.len = req.header.PageLength * 4;
567 	buf = malloc(req.len);
568 	req.buf = buf;
569 	bcopy(&req.header, buf, sizeof(req.header));
570 	if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
571 		error = errno;
572 		free(buf);
573 		errno = error;
574 		return (NULL);
575 	}
576 	req.ioc_status = le16toh(req.ioc_status);
577 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
578 		if (IOCStatus != NULL)
579 			*IOCStatus = req.ioc_status;
580 		else
581 			warnx("Reading config page failed: 0x%x %s",
582 			    req.ioc_status, mps_ioc_status(req.ioc_status));
583 		free(buf);
584 		errno = EIO;
585 		return (NULL);
586 	}
587 	return (buf);
588 }
589 
590 void *
591 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
592     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
593 {
594 	struct mps_ext_cfg_page_req req;
595 	void *buf;
596 	int error;
597 
598 	if (IOCStatus != NULL)
599 		*IOCStatus = MPI2_IOCSTATUS_SUCCESS;
600 	bzero(&req, sizeof(req));
601 	req.header.PageVersion = PageVersion;
602 	req.header.PageNumber = PageNumber;
603 	req.header.ExtPageType = ExtPageType;
604 	req.page_address = htole32(PageAddress);
605 	if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0)
606 		return (NULL);
607 	req.ioc_status = le16toh(req.ioc_status);
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 	req.ioc_status = le16toh(req.ioc_status);
628 	if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
629 		if (IOCStatus != NULL)
630 			*IOCStatus = req.ioc_status;
631 		else
632 			warnx("Reading extended config page failed: %s",
633 			    mps_ioc_status(req.ioc_status));
634 		free(buf);
635 		errno = EIO;
636 		return (NULL);
637 	}
638 	return (buf);
639 }
640 #endif
641 
642 int
643 mps_open(int unit)
644 {
645 	char path[MAXPATHLEN];
646 
647 	snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
648 	return (open(path, O_RDWR));
649 }
650 
651 int
652 mps_user_command(int fd, void *req, uint32_t req_len, void *reply,
653         uint32_t reply_len, void *buffer, int len, uint32_t flags)
654 {
655 	struct mps_usr_command cmd;
656 
657 	bzero(&cmd, sizeof(struct mps_usr_command));
658 	cmd.req = req;
659 	cmd.req_len = req_len;
660 	cmd.rpl = reply;
661 	cmd.rpl_len = reply_len;
662 	cmd.buf = buffer;
663 	cmd.len = len;
664 	cmd.flags = flags;
665 
666 	if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
667 		return (errno);
668 	return (0);
669 }
670 
671 int
672 mps_pass_command(int fd, void *req, uint32_t req_len, void *reply,
673 	uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
674 	uint32_t dataout_len, uint32_t timeout)
675 {
676 	struct mprs_pass_thru pass;
677 
678 	bzero(&pass, sizeof(pass));
679 	pass.PtrRequest = (uint64_t)(uintptr_t)req;
680 	pass.PtrReply = (uint64_t)(uintptr_t)reply;
681 	pass.RequestSize = req_len;
682 	pass.ReplySize = reply_len;
683 	if (datain_len && dataout_len) {
684 		pass.PtrData = (uint64_t)(uintptr_t)data_in;
685 		pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
686 		pass.DataSize = datain_len;
687 		pass.DataOutSize = dataout_len;
688 		if (is_mps) {
689 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
690 		} else {
691 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
692 		}
693 	} else if (datain_len) {
694 		pass.PtrData = (uint64_t)(uintptr_t)data_in;
695 		pass.DataSize = datain_len;
696 		if (is_mps) {
697 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
698 		} else {
699 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
700 		}
701 	} else if (dataout_len) {
702 		pass.PtrData = (uint64_t)(uintptr_t)data_out;
703 		pass.DataSize = dataout_len;
704 		if (is_mps) {
705 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
706 		} else {
707 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
708 		}
709 	} else {
710 		if (is_mps) {
711 			pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
712 		} else {
713 			pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
714 		}
715 	}
716 	pass.Timeout = timeout;
717 
718 	if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
719 		return (errno);
720 	return (0);
721 }
722 
723 /* Return the length in bytes of the device's MPI2_IOC_FACTS reply */
724 static size_t
725 mps_get_ioc_factslen(int fd)
726 {
727 	MPI2_IOC_FACTS_REQUEST req;
728 	const size_t factslen = 4;
729 	char factsbuf[4] = {0};
730 	MPI2_IOC_FACTS_REPLY *facts = (MPI2_IOC_FACTS_REPLY*)factsbuf;
731 	int error;
732 
733 	bzero(&req, sizeof(req));
734 	req.Function = MPI2_FUNCTION_IOC_FACTS;
735 	error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
736 	    factsbuf, factslen, NULL, 0, NULL, 0, 10);
737 
738 	if (error)
739 		return (0);
740 
741 	/* The card's response is measured in dwords */
742 	return (facts->MsgLength * 4);
743 }
744 
745 MPI2_IOC_FACTS_REPLY *
746 mps_get_iocfacts(int fd)
747 {
748 	MPI2_IOC_FACTS_REPLY *facts;
749 	MPI2_IOC_FACTS_REQUEST req;
750 	size_t factslen;
751 	int error;
752 
753 	factslen = mps_get_ioc_factslen(fd);
754 	if (factslen == 0)
755 		return (NULL);
756 
757 	facts = malloc(factslen);
758 	if (facts == NULL) {
759 		errno = ENOMEM;
760 		return (NULL);
761 	}
762 
763 	bzero(&req, sizeof(req));
764 	req.Function = MPI2_FUNCTION_IOC_FACTS;
765 
766 #if 1
767 	error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
768 	    facts, factslen, NULL, 0, NULL, 0, 10);
769 #else
770 	error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
771 	    facts, factslen, NULL, 0, 0);
772 #endif
773 	if (error) {
774 		free(facts);
775 		return (NULL);
776 	}
777 
778 	if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
779 		free(facts);
780 		errno = EINVAL;
781 		return (NULL);
782 	}
783 	adjust_iocfacts_endianness(facts);
784 	return (facts);
785 }
786 
787 static void
788 adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts)
789 {
790 	facts->MsgVersion = le16toh(facts->MsgVersion);
791 	facts->HeaderVersion = le16toh(facts->HeaderVersion);
792 	facts->Reserved1 = le16toh(facts->Reserved1);
793 	facts->IOCExceptions = le16toh(facts->IOCExceptions);
794 	facts->IOCStatus = le16toh(facts->IOCStatus);
795 	facts->IOCLogInfo = le32toh(facts->IOCLogInfo);
796 	facts->RequestCredit = le16toh(facts->RequestCredit);
797 	facts->ProductID = le16toh(facts->ProductID);
798 	facts->IOCCapabilities = le32toh(facts->IOCCapabilities);
799 	facts->IOCRequestFrameSize =
800 	    le16toh(facts->IOCRequestFrameSize);
801 	facts->FWVersion.Word = le32toh(facts->FWVersion.Word);
802 	facts->MaxInitiators = le16toh(facts->MaxInitiators);
803 	facts->MaxTargets = le16toh(facts->MaxTargets);
804 	facts->MaxSasExpanders = le16toh(facts->MaxSasExpanders);
805 	facts->MaxEnclosures = le16toh(facts->MaxEnclosures);
806 	facts->ProtocolFlags = le16toh(facts->ProtocolFlags);
807 	facts->HighPriorityCredit = le16toh(facts->HighPriorityCredit);
808 	facts->MaxReplyDescriptorPostQueueDepth =
809 	    le16toh(facts->MaxReplyDescriptorPostQueueDepth);
810 	facts->MaxDevHandle = le16toh(facts->MaxDevHandle);
811 	facts->MaxPersistentEntries =
812 	    le16toh(facts->MaxPersistentEntries);
813 	facts->MinDevHandle = le16toh(facts->MinDevHandle);
814 }
815