]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mpsutil/mps_cmd.c
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[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/cdefs.h>
37 __RCSID("$FreeBSD$");
38
39 #include <sys/param.h>
40 #include <sys/errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/sysctl.h>
43 #include <sys/uio.h>
44
45 #include <err.h>
46 #include <fcntl.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 #include "mpsutil.h"
53 #include <dev/mps/mps_ioctl.h>
54 #include <dev/mpr/mpr_ioctl.h>
55
56 #ifndef USE_MPT_IOCTLS
57 #define USE_MPT_IOCTLS
58 #endif
59
60 static const char *mps_ioc_status_codes[] = {
61         "Success",                              /* 0x0000 */
62         "Invalid function",
63         "Busy",
64         "Invalid scatter-gather list",
65         "Internal error",
66         "Reserved",
67         "Insufficient resources",
68         "Invalid field",
69         "Invalid state",                        /* 0x0008 */
70         "Operation state not supported",
71         NULL,
72         NULL,
73         NULL,
74         NULL,
75         NULL,
76         NULL,
77         NULL,                                   /* 0x0010 */
78         NULL,
79         NULL,
80         NULL,
81         NULL,
82         NULL,
83         NULL,
84         NULL,
85         NULL,                                   /* 0x0018 */
86         NULL,
87         NULL,
88         NULL,
89         NULL,
90         NULL,
91         NULL,
92         NULL,
93         "Invalid configuration action",         /* 0x0020 */
94         "Invalid configuration type",
95         "Invalid configuration page",
96         "Invalid configuration data",
97         "No configuration defaults",
98         "Unable to commit configuration change",
99         NULL,
100         NULL,
101         NULL,                                   /* 0x0028 */
102         NULL,
103         NULL,
104         NULL,
105         NULL,
106         NULL,
107         NULL,
108         NULL,
109         NULL,                                   /* 0x0030 */
110         NULL,
111         NULL,
112         NULL,
113         NULL,
114         NULL,
115         NULL,
116         NULL,
117         NULL,                                   /* 0x0038 */
118         NULL,
119         NULL,
120         NULL,
121         NULL,
122         NULL,
123         NULL,
124         NULL,
125         "Recovered SCSI error",                 /* 0x0040 */
126         "Invalid SCSI bus",
127         "Invalid SCSI target ID",
128         "SCSI device not there",
129         "SCSI data overrun",
130         "SCSI data underrun",
131         "SCSI I/O error",
132         "SCSI protocol error",
133         "SCSI task terminated",                 /* 0x0048 */
134         "SCSI residual mismatch",
135         "SCSI task management failed",
136         "SCSI I/O controller terminated",
137         "SCSI external controller terminated",
138         "EEDP guard error",
139         "EEDP reference tag error",
140         "EEDP application tag error",
141         NULL,                                   /* 0x0050 */
142         NULL,
143         NULL,
144         NULL,
145         NULL,
146         NULL,
147         NULL,
148         NULL,
149         NULL,                                   /* 0x0058 */
150         NULL,
151         NULL,
152         NULL,
153         NULL,
154         NULL,
155         NULL,
156         NULL,
157         "SCSI target priority I/O",             /* 0x0060 */
158         "Invalid SCSI target port",
159         "Invalid SCSI target I/O index",
160         "SCSI target aborted",
161         "No connection retryable",
162         "No connection",
163         "FC aborted",
164         "Invalid FC receive ID",
165         "FC did invalid",                       /* 0x0068 */
166         "FC node logged out",
167         "Transfer count mismatch",
168         "STS data not set",
169         "FC exchange canceled",
170         "Data offset error",
171         "Too much write data",
172         "IU too short",
173         "ACK NAK timeout",                      /* 0x0070 */
174         "NAK received",
175         NULL,
176         NULL,
177         NULL,
178         NULL,
179         NULL,
180         NULL,
181         NULL,                                   /* 0x0078 */
182         NULL,
183         NULL,
184         NULL,
185         NULL,
186         NULL,
187         NULL,
188         NULL,
189         "LAN device not found",                 /* 0x0080 */
190         "LAN device failure",
191         "LAN transmit error",
192         "LAN transmit aborted",
193         "LAN receive error",
194         "LAN receive aborted",
195         "LAN partial packet",
196         "LAN canceled",
197         NULL,                                   /* 0x0088 */
198         NULL,
199         NULL,
200         NULL,
201         NULL,
202         NULL,
203         NULL,
204         NULL,
205         "SAS SMP request failed",               /* 0x0090 */
206         "SAS SMP data overrun",
207         NULL,
208         NULL,
209         NULL,
210         NULL,
211         NULL,
212         NULL,
213         "Inband aborted",                       /* 0x0098 */
214         "No inband connection",
215         NULL,
216         NULL,
217         NULL,
218         NULL,
219         NULL,
220         NULL,
221         "Diagnostic released",                  /* 0x00A0 */
222 };
223
224 struct mprs_pass_thru {
225         uint64_t        PtrRequest;
226         uint64_t        PtrReply;
227         uint64_t        PtrData;
228         uint32_t        RequestSize;
229         uint32_t        ReplySize;
230         uint32_t        DataSize;
231         uint32_t        DataDirection;
232         uint64_t        PtrDataOut;
233         uint32_t        DataOutSize;
234         uint32_t        Timeout;
235 };
236
237 struct mprs_btdh_mapping {
238         uint16_t        TargetID;
239         uint16_t        Bus;
240         uint16_t        DevHandle;
241         uint16_t        Reserved;
242 };
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(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(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 = 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(reply.IOCStatus)) {
352                 if (IOCStatus != NULL)
353                         *IOCStatus = 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 = 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         if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
399                 if (IOCStatus != NULL)
400                         *IOCStatus = reply.IOCStatus;
401                 else
402                         warnx("Reading config page failed: 0x%x %s",
403                             reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
404                 free(buf);
405                 errno = EIO;
406                 return (NULL);
407         }
408         return (buf);
409 }
410
411 void *
412 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
413     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
414 {
415         MPI2_CONFIG_REQUEST req;
416         MPI2_CONFIG_PAGE_HEADER header;
417         MPI2_CONFIG_REPLY reply;
418         U16 pagelen;
419         void *buf;
420         int error, len;
421
422         if (IOCStatus != NULL)
423                 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
424         bzero(&header, sizeof(header));
425         error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber,
426             PageAddress, &header, &pagelen, IOCStatus);
427         if (error) {
428                 errno = error;
429                 return (NULL);
430         }
431
432         bzero(&req, sizeof(req));
433         req.Function = MPI2_FUNCTION_CONFIG;
434         req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
435         req.PageAddress = PageAddress;
436         req.Header = header;
437         if (pagelen == 0)
438                 pagelen = 4;
439         req.ExtPageLength = pagelen;
440         req.ExtPageType = ExtPageType;
441
442         len = pagelen * 4;
443         buf = malloc(len);
444         if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
445             buf, len, NULL, 0, 30)) {
446                 error = errno;
447                 free(buf);
448                 errno = error;
449                 return (NULL);
450         }
451         if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
452                 if (IOCStatus != NULL)
453                         *IOCStatus = reply.IOCStatus;
454                 else
455                         warnx("Reading extended config page failed: %s",
456                             mps_ioc_status(reply.IOCStatus));
457                 free(buf);
458                 errno = EIO;
459                 return (NULL);
460         }
461         return (buf);
462 }
463
464 int
465 mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
466 {
467         MPI2_FW_DOWNLOAD_REQUEST req;
468         MPI2_FW_DOWNLOAD_REPLY reply;
469
470         bzero(&req, sizeof(req));
471         bzero(&reply, sizeof(reply));
472         req.Function = MPI2_FUNCTION_FW_DOWNLOAD;
473         req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
474         req.TotalImageSize = len;
475         req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT;
476
477         if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
478             fw, len, 0)) {
479                 return (-1);
480         }
481         return (0);
482 }
483
484 int
485 mps_firmware_get(int fd, unsigned char **firmware, bool bios)
486 {
487         MPI2_FW_UPLOAD_REQUEST req;
488         MPI2_FW_UPLOAD_REPLY reply;
489         int size;
490
491         *firmware = NULL;
492         bzero(&req, sizeof(req));
493         bzero(&reply, sizeof(reply));
494         req.Function = MPI2_FUNCTION_FW_UPLOAD;
495         req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
496
497         if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
498             NULL, 0, 0)) {
499                 return (-1);
500         }
501         if (reply.ActualImageSize == 0) {
502                 return (-1);
503         }
504
505         size = reply.ActualImageSize;
506         *firmware = calloc(size, sizeof(unsigned char));
507         if (*firmware == NULL) {
508                 warn("calloc");
509                 return (-1);
510         }
511         if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
512             *firmware, size, 0)) {
513                 free(*firmware);
514                 return (-1);
515         }
516
517         return (size);
518 }
519
520 #else
521
522 int
523 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
524     MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
525 {
526         struct mps_cfg_page_req req;
527
528         if (IOCStatus != NULL)
529                 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
530         if (header == NULL)
531                 return (EINVAL);
532         bzero(&req, sizeof(req));
533         req.header.PageType = PageType;
534         req.header.PageNumber = PageNumber;
535         req.page_address = PageAddress;
536         if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0)
537                 return (errno);
538         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
539                 if (IOCStatus != NULL)
540                         *IOCStatus = req.ioc_status;
541                 return (EIO);
542         }
543         bcopy(&req.header, header, sizeof(*header));
544         return (0);
545 }
546
547 void *
548 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
549     U16 *IOCStatus)
550 {
551         struct mps_cfg_page_req req;
552         void *buf;
553         int error;
554
555         error = mps_read_config_page_header(fd, PageType, PageNumber,
556             PageAddress, &req.header, IOCStatus);
557         if (error) {
558                 errno = error;
559                 return (NULL);
560         }
561
562         if (req.header.PageLength == 0)
563                 req.header.PageLength = 4;
564         req.len = req.header.PageLength * 4;
565         buf = malloc(req.len);
566         req.buf = buf;
567         bcopy(&req.header, buf, sizeof(req.header));
568         if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
569                 error = errno;
570                 free(buf);
571                 errno = error;
572                 return (NULL);
573         }
574         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
575                 if (IOCStatus != NULL)
576                         *IOCStatus = req.ioc_status;
577                 else
578                         warnx("Reading config page failed: 0x%x %s",
579                             req.ioc_status, mps_ioc_status(req.ioc_status));
580                 free(buf);
581                 errno = EIO;
582                 return (NULL);
583         }
584         return (buf);
585 }
586
587 void *
588 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
589     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
590 {
591         struct mps_ext_cfg_page_req req;
592         void *buf;
593         int error;
594
595         if (IOCStatus != NULL)
596                 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
597         bzero(&req, sizeof(req));
598         req.header.PageVersion = PageVersion;
599         req.header.PageNumber = PageNumber;
600         req.header.ExtPageType = ExtPageType;
601         req.page_address = PageAddress;
602         if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0)
603                 return (NULL);
604         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
605                 if (IOCStatus != NULL)
606                         *IOCStatus = req.ioc_status;
607                 else
608                         warnx("Reading extended config page header failed: %s",
609                             mps_ioc_status(req.ioc_status));
610                 errno = EIO;
611                 return (NULL);
612         }
613         req.len = req.header.ExtPageLength * 4;
614         buf = malloc(req.len);
615         req.buf = buf;
616         bcopy(&req.header, buf, sizeof(req.header));
617         if (ioctl(fd, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) {
618                 error = errno;
619                 free(buf);
620                 errno = error;
621                 return (NULL);
622         }
623         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
624                 if (IOCStatus != NULL)
625                         *IOCStatus = req.ioc_status;
626                 else
627                         warnx("Reading extended config page failed: %s",
628                             mps_ioc_status(req.ioc_status));
629                 free(buf);
630                 errno = EIO;
631                 return (NULL);
632         }
633         return (buf);
634 }
635 #endif
636
637 int
638 mps_open(int unit)
639 {
640         char path[MAXPATHLEN];
641
642         snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
643         return (open(path, O_RDWR));
644 }
645
646 int
647 mps_user_command(int fd, void *req, uint32_t req_len, void *reply,
648         uint32_t reply_len, void *buffer, int len, uint32_t flags)
649 {
650         struct mps_usr_command cmd;
651
652         bzero(&cmd, sizeof(struct mps_usr_command));
653         cmd.req = req;
654         cmd.req_len = req_len;
655         cmd.rpl = reply;
656         cmd.rpl_len = reply_len;
657         cmd.buf = buffer;
658         cmd.len = len;
659         cmd.flags = flags;
660
661         if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
662                 return (errno);
663         return (0);
664 }
665
666 int
667 mps_pass_command(int fd, void *req, uint32_t req_len, void *reply,
668         uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
669         uint32_t dataout_len, uint32_t timeout)
670 {
671         struct mprs_pass_thru pass;
672
673         bzero(&pass, sizeof(pass));
674         pass.PtrRequest = (uint64_t)(uintptr_t)req;
675         pass.PtrReply = (uint64_t)(uintptr_t)reply;
676         pass.RequestSize = req_len;
677         pass.ReplySize = reply_len;
678         if (datain_len && dataout_len) {
679                 pass.PtrData = (uint64_t)(uintptr_t)data_in;
680                 pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
681                 pass.DataSize = datain_len;
682                 pass.DataOutSize = dataout_len;
683                 if (is_mps) {
684                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
685                 } else {
686                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
687                 }
688         } else if (datain_len) {
689                 pass.PtrData = (uint64_t)(uintptr_t)data_in;
690                 pass.DataSize = datain_len;
691                 if (is_mps) {
692                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
693                 } else {
694                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
695                 }
696         } else if (dataout_len) {
697                 pass.PtrData = (uint64_t)(uintptr_t)data_out;
698                 pass.DataSize = dataout_len;
699                 if (is_mps) {
700                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
701                 } else {
702                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
703                 }
704         } else {
705                 if (is_mps) {
706                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
707                 } else {
708                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
709                 }
710         }
711         pass.Timeout = timeout;
712
713         if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
714                 return (errno);
715         return (0);
716 }
717
718 MPI2_IOC_FACTS_REPLY *
719 mps_get_iocfacts(int fd)
720 {
721         MPI2_IOC_FACTS_REPLY *facts;
722         MPI2_IOC_FACTS_REQUEST req;
723         char msgver[8], sysctlname[128];
724         size_t len, factslen;
725         int error;
726
727         snprintf(sysctlname, sizeof(sysctlname), "dev.%s.%d.msg_version",
728             is_mps ? "mps" : "mpr", mps_unit);
729
730         factslen = sizeof(MPI2_IOC_FACTS_REPLY);
731         len = sizeof(msgver);
732         error = sysctlbyname(sysctlname, msgver, &len, NULL, 0);
733         if (error == 0) {
734                 if (strncmp(msgver, "2.6", sizeof(msgver)) == 0)
735                         factslen += 4;
736         }
737
738         facts = malloc(factslen);
739         if (facts == NULL) {
740                 errno = ENOMEM;
741                 return (NULL);
742         }
743
744         bzero(&req, factslen);
745         req.Function = MPI2_FUNCTION_IOC_FACTS;
746
747 #if 1
748         error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
749             facts, factslen, NULL, 0, NULL, 0, 10);
750 #else
751         error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
752             facts, factslen, NULL, 0, 0);
753 #endif
754         if (error) {
755                 free(facts);
756                 return (NULL);
757         }
758
759         if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
760                 free(facts);
761                 errno = EINVAL;
762                 return (NULL);
763         }
764         return (facts);
765 }
766