]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mpsutil/mps_cmd.c
pfsync: Correctly check if bpf peers are present
[FreeBSD/FreeBSD.git] / usr.sbin / mpsutil / mps_cmd.c
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 }