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
58static 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
222struct 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
235struct mprs_btdh_mapping {
236        uint16_t        TargetID;
237        uint16_t        Bus;
238        uint16_t        DevHandle;
239        uint16_t        Reserved;
240};
241
242static void adjust_iocfacts_endianness(MPI2_IOC_FACTS_REPLY *facts);
243
244const char *
245mps_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
258int
259mps_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
281int
282mps_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
304int
305mps_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
333int
334mps_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
363void *
364mps_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
412void *
413mps_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
466int
467mps_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
486int
487mps_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
524int
525mps_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
549void *
550mps_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
590void *
591mps_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
642int
643mps_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
651int
652mps_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
671int
672mps_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 */
724static size_t
725mps_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
745MPI2_IOC_FACTS_REPLY *
746mps_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
787static void
788adjust_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