]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/mpsutil/mps_cmd.c
MFC r318722:
[FreeBSD/stable/10.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  * All rights reserved.
6  * Written by: Scott Long <scottl@freebsd.org>
7  *
8  * Copyright (c) 2008 Yahoo!, Inc.
9  * All rights reserved.
10  * Written by: John Baldwin <jhb@FreeBSD.org>
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the author nor the names of any co-contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #include <sys/cdefs.h>
38 __RCSID("$FreeBSD$");
39
40 #include <sys/param.h>
41 #include <sys/errno.h>
42 #include <sys/ioctl.h>
43 #if 0
44 #include <sys/mps_ioctl.h>
45 #else
46 #include "mps_ioctl.h"
47 #include "mpr_ioctl.h"
48 #endif
49 #include <sys/sysctl.h>
50 #include <sys/uio.h>
51
52 #include <err.h>
53 #include <fcntl.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58
59 #include "mpsutil.h"
60
61 #ifndef USE_MPT_IOCTLS
62 #define USE_MPT_IOCTLS
63 #endif
64
65 static const char *mps_ioc_status_codes[] = {
66         "Success",                              /* 0x0000 */
67         "Invalid function",
68         "Busy",
69         "Invalid scatter-gather list",
70         "Internal error",
71         "Reserved",
72         "Insufficient resources",
73         "Invalid field",
74         "Invalid state",                        /* 0x0008 */
75         "Operation state not supported",
76         NULL,
77         NULL,
78         NULL,
79         NULL,
80         NULL,
81         NULL,
82         NULL,                                   /* 0x0010 */
83         NULL,
84         NULL,
85         NULL,
86         NULL,
87         NULL,
88         NULL,
89         NULL,
90         NULL,                                   /* 0x0018 */
91         NULL,
92         NULL,
93         NULL,
94         NULL,
95         NULL,
96         NULL,
97         NULL,
98         "Invalid configuration action",         /* 0x0020 */
99         "Invalid configuration type",
100         "Invalid configuration page",
101         "Invalid configuration data",
102         "No configuration defaults",
103         "Unable to commit configuration change",
104         NULL,
105         NULL,
106         NULL,                                   /* 0x0028 */
107         NULL,
108         NULL,
109         NULL,
110         NULL,
111         NULL,
112         NULL,
113         NULL,
114         NULL,                                   /* 0x0030 */
115         NULL,
116         NULL,
117         NULL,
118         NULL,
119         NULL,
120         NULL,
121         NULL,
122         NULL,                                   /* 0x0038 */
123         NULL,
124         NULL,
125         NULL,
126         NULL,
127         NULL,
128         NULL,
129         NULL,
130         "Recovered SCSI error",                 /* 0x0040 */
131         "Invalid SCSI bus",
132         "Invalid SCSI target ID",
133         "SCSI device not there",
134         "SCSI data overrun",
135         "SCSI data underrun",
136         "SCSI I/O error",
137         "SCSI protocol error",
138         "SCSI task terminated",                 /* 0x0048 */
139         "SCSI residual mismatch",
140         "SCSI task management failed",
141         "SCSI I/O controller terminated",
142         "SCSI external controller terminated",
143         "EEDP guard error",
144         "EEDP reference tag error",
145         "EEDP application tag error",
146         NULL,                                   /* 0x0050 */
147         NULL,
148         NULL,
149         NULL,
150         NULL,
151         NULL,
152         NULL,
153         NULL,
154         NULL,                                   /* 0x0058 */
155         NULL,
156         NULL,
157         NULL,
158         NULL,
159         NULL,
160         NULL,
161         NULL,
162         "SCSI target priority I/O",             /* 0x0060 */
163         "Invalid SCSI target port",
164         "Invalid SCSI target I/O index",
165         "SCSI target aborted",
166         "No connection retryable",
167         "No connection",
168         "FC aborted",
169         "Invalid FC receive ID",
170         "FC did invalid",                       /* 0x0068 */
171         "FC node logged out",
172         "Transfer count mismatch",
173         "STS data not set",
174         "FC exchange canceled",
175         "Data offset error",
176         "Too much write data",
177         "IU too short",
178         "ACK NAK timeout",                      /* 0x0070 */
179         "NAK received",
180         NULL,
181         NULL,
182         NULL,
183         NULL,
184         NULL,
185         NULL,
186         NULL,                                   /* 0x0078 */
187         NULL,
188         NULL,
189         NULL,
190         NULL,
191         NULL,
192         NULL,
193         NULL,
194         "LAN device not found",                 /* 0x0080 */
195         "LAN device failure",
196         "LAN transmit error",
197         "LAN transmit aborted",
198         "LAN receive error",
199         "LAN receive aborted",
200         "LAN partial packet",
201         "LAN canceled",
202         NULL,                                   /* 0x0088 */
203         NULL,
204         NULL,
205         NULL,
206         NULL,
207         NULL,
208         NULL,
209         NULL,
210         "SAS SMP request failed",               /* 0x0090 */
211         "SAS SMP data overrun",
212         NULL,
213         NULL,
214         NULL,
215         NULL,
216         NULL,
217         NULL,
218         "Inband aborted",                       /* 0x0098 */
219         "No inband connection",
220         NULL,
221         NULL,
222         NULL,
223         NULL,
224         NULL,
225         NULL,
226         "Diagnostic released",                  /* 0x00A0 */
227 };
228
229 struct mprs_pass_thru {
230         uint64_t        PtrRequest;
231         uint64_t        PtrReply;
232         uint64_t        PtrData;
233         uint32_t        RequestSize;
234         uint32_t        ReplySize;
235         uint32_t        DataSize;
236         uint32_t        DataDirection;
237         uint64_t        PtrDataOut;
238         uint32_t        DataOutSize;
239         uint32_t        Timeout;
240 };
241
242 struct mprs_btdh_mapping {
243         uint16_t        TargetID;
244         uint16_t        Bus;
245         uint16_t        DevHandle;
246         uint16_t        Reserved;
247 };
248
249 const char *
250 mps_ioc_status(U16 IOCStatus)
251 {
252         static char buffer[16];
253
254         IOCStatus &= MPI2_IOCSTATUS_MASK;
255         if (IOCStatus < sizeof(mps_ioc_status_codes) / sizeof(char *) &&
256             mps_ioc_status_codes[IOCStatus] != NULL)
257                 return (mps_ioc_status_codes[IOCStatus]);
258         snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus);
259         return (buffer);
260 }
261
262 #ifdef USE_MPT_IOCTLS
263 int
264 mps_map_btdh(int fd, uint16_t *devhandle, uint16_t *bus, uint16_t *target)
265 {
266         int error;
267         struct mprs_btdh_mapping map;
268
269         map.Bus = *bus;
270         map.TargetID = *target;
271         map.DevHandle = *devhandle;
272
273         if ((error = ioctl(fd, MPTIOCTL_BTDH_MAPPING, &map)) != 0) {
274                 error = errno;
275                 warn("Failed to map bus/target/device");
276                 return (error);
277         }
278
279         *bus = map.Bus;
280         *target = map.TargetID;
281         *devhandle = map.DevHandle;
282
283         return (0);
284 }
285
286 int
287 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
288     MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
289 {
290         MPI2_CONFIG_REQUEST req;
291         MPI2_CONFIG_REPLY reply;
292
293         bzero(&req, sizeof(req));
294         req.Function = MPI2_FUNCTION_CONFIG;
295         req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
296         req.Header.PageType = PageType;
297         req.Header.PageNumber = PageNumber;
298         req.PageAddress = PageAddress;
299
300         if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
301             NULL, 0, NULL, 0, 30))
302                 return (errno);
303
304         if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
305                 if (IOCStatus != NULL)
306                         *IOCStatus = reply.IOCStatus;
307                 return (EIO);
308         }
309         if (header == NULL)
310                 return (EINVAL);
311         *header = reply.Header;
312         return (0);
313 }
314
315 int
316 mps_read_ext_config_page_header(int fd, U8 ExtPageType, U8 PageNumber, U32 PageAddress, MPI2_CONFIG_PAGE_HEADER *header, U16 *ExtPageLength, U16 *IOCStatus)
317 {
318         MPI2_CONFIG_REQUEST req;
319         MPI2_CONFIG_REPLY reply;
320
321         bzero(&req, sizeof(req));
322         req.Function = MPI2_FUNCTION_CONFIG;
323         req.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
324         req.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
325         req.ExtPageType = ExtPageType;
326         req.Header.PageNumber = PageNumber;
327         req.PageAddress = PageAddress;
328
329         if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
330             NULL, 0, NULL, 0, 30))
331                 return (errno);
332
333         if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
334                 if (IOCStatus != NULL)
335                         *IOCStatus = reply.IOCStatus;
336                 return (EIO);
337         }
338         if ((header == NULL) || (ExtPageLength == NULL))
339                 return (EINVAL);
340         *header = reply.Header;
341         *ExtPageLength = reply.ExtPageLength;
342         return (0);
343 }
344
345 void *
346 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
347     U16 *IOCStatus)
348 {
349         MPI2_CONFIG_REQUEST req;
350         MPI2_CONFIG_PAGE_HEADER header;
351         MPI2_CONFIG_REPLY reply;
352         void *buf;
353         int error, len;
354
355         bzero(&header, sizeof(header));
356         error = mps_read_config_page_header(fd, PageType, PageNumber,
357             PageAddress, &header, IOCStatus);
358         if (error) {
359                 errno = error;
360                 return (NULL);
361         }
362
363         bzero(&req, sizeof(req));
364         req.Function = MPI2_FUNCTION_CONFIG;
365         req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
366         req.PageAddress = PageAddress;
367         req.Header = header;
368         req.Header.PageLength = reply.Header.PageLength;
369         if (reply.Header.PageLength == 0)
370                 req.Header.PageLength = 4;
371
372         len = req.Header.PageLength * 4;
373         buf = malloc(len);
374         if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
375             buf, len, NULL, 0, 30)) {
376                 error = errno;
377                 free(buf);
378                 errno = error;
379                 return (NULL);
380         }
381         if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
382                 if (IOCStatus != NULL)
383                         *IOCStatus = reply.IOCStatus;
384                 else
385                         warnx("Reading config page failed: 0x%x %s",
386                             reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
387                 free(buf);
388                 errno = EIO;
389                 return (NULL);
390         }
391         return (buf);
392 }
393
394 void *
395 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
396     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
397 {
398         MPI2_CONFIG_REQUEST req;
399         MPI2_CONFIG_PAGE_HEADER header;
400         MPI2_CONFIG_REPLY reply;
401         U16 pagelen;
402         void *buf;
403         int error, len;
404
405         if (IOCStatus != NULL)
406                 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
407         bzero(&header, sizeof(header));
408         error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber,
409             PageAddress, &header, &pagelen, IOCStatus);
410         if (error) {
411                 errno = error;
412                 return (NULL);
413         }
414
415         bzero(&req, sizeof(req));
416         req.Function = MPI2_FUNCTION_CONFIG;
417         req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
418         req.PageAddress = PageAddress;
419         req.Header = header;
420         if (pagelen == 0)
421                 pagelen = 4;
422         req.ExtPageLength = pagelen;
423         req.ExtPageType = ExtPageType;
424
425         len = pagelen * 4;
426         buf = malloc(len);
427         if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
428             buf, len, NULL, 0, 30)) {
429                 error = errno;
430                 free(buf);
431                 errno = error;
432                 return (NULL);
433         }
434         if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
435                 if (IOCStatus != NULL)
436                         *IOCStatus = reply.IOCStatus;
437                 else
438                         warnx("Reading extended config page failed: %s",
439                             mps_ioc_status(reply.IOCStatus));
440                 free(buf);
441                 errno = EIO;
442                 return (NULL);
443         }
444         return (buf);
445 }
446
447 int
448 mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
449 {
450         MPI2_FW_DOWNLOAD_REQUEST req;
451         MPI2_FW_DOWNLOAD_REPLY reply;
452
453         bzero(&req, sizeof(req));
454         bzero(&reply, sizeof(reply));
455         req.Function = MPI2_FUNCTION_FW_DOWNLOAD;
456         req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
457         req.TotalImageSize = len;
458         req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT;
459
460         if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
461             fw, len, 0)) {
462                 return (-1);
463         }
464         return (0);
465 }
466
467 int
468 mps_firmware_get(int fd, unsigned char **firmware, bool bios)
469 {
470         MPI2_FW_UPLOAD_REQUEST req;
471         MPI2_FW_UPLOAD_REPLY reply;
472         int size;
473
474         *firmware = NULL;
475         bzero(&req, sizeof(req));
476         bzero(&reply, sizeof(reply));
477         req.Function = MPI2_FUNCTION_FW_UPLOAD;
478         req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
479
480         if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
481             NULL, 0, 0)) {
482                 return (-1);
483         }
484         if (reply.ActualImageSize == 0) {
485                 return (-1);
486         }
487
488         size = reply.ActualImageSize;
489         *firmware = calloc(size, sizeof(unsigned char));
490         if (*firmware == NULL) {
491                 warn("calloc");
492                 return (-1);
493         }
494         if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
495             *firmware, size, 0)) {
496                 free(*firmware);
497                 return (-1);
498         }
499
500         return (size);
501 }
502
503 #else
504
505 int
506 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
507     MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
508 {
509         struct mps_cfg_page_req req;
510
511         if (IOCStatus != NULL)
512                 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
513         if (header == NULL)
514                 return (EINVAL);
515         bzero(&req, sizeof(req));
516         req.header.PageType = PageType;
517         req.header.PageNumber = PageNumber;
518         req.page_address = PageAddress;
519         if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0)
520                 return (errno);
521         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
522                 if (IOCStatus != NULL)
523                         *IOCStatus = req.ioc_status;
524                 return (EIO);
525         }
526         bcopy(&req.header, header, sizeof(*header));
527         return (0);
528 }
529
530 void *
531 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
532     U16 *IOCStatus)
533 {
534         struct mps_cfg_page_req req;
535         void *buf;
536         int error;
537
538         error = mps_read_config_page_header(fd, PageType, PageNumber,
539             PageAddress, &req.header, IOCStatus);
540         if (error) {
541                 errno = error;
542                 return (NULL);
543         }
544
545         if (req.header.PageLength == 0)
546                 req.header.PageLength = 4;
547         req.len = req.header.PageLength * 4;
548         buf = malloc(req.len);
549         req.buf = buf;
550         bcopy(&req.header, buf, sizeof(req.header));
551         if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
552                 error = errno;
553                 free(buf);
554                 errno = error;
555                 return (NULL);
556         }
557         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
558                 if (IOCStatus != NULL)
559                         *IOCStatus = req.ioc_status;
560                 else
561                         warnx("Reading config page failed: 0x%x %s",
562                             req.ioc_status, mps_ioc_status(req.ioc_status));
563                 free(buf);
564                 errno = EIO;
565                 return (NULL);
566         }
567         return (buf);
568 }
569
570 void *
571 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
572     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
573 {
574         struct mps_ext_cfg_page_req req;
575         void *buf;
576         int error;
577
578         if (IOCStatus != NULL)
579                 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
580         bzero(&req, sizeof(req));
581         req.header.PageVersion = PageVersion;
582         req.header.PageNumber = PageNumber;
583         req.header.ExtPageType = ExtPageType;
584         req.page_address = PageAddress;
585         if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0)
586                 return (NULL);
587         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
588                 if (IOCStatus != NULL)
589                         *IOCStatus = req.ioc_status;
590                 else
591                         warnx("Reading extended config page header failed: %s",
592                             mps_ioc_status(req.ioc_status));
593                 errno = EIO;
594                 return (NULL);
595         }
596         req.len = req.header.ExtPageLength * 4;
597         buf = malloc(req.len);
598         req.buf = buf;
599         bcopy(&req.header, buf, sizeof(req.header));
600         if (ioctl(fd, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) {
601                 error = errno;
602                 free(buf);
603                 errno = error;
604                 return (NULL);
605         }
606         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
607                 if (IOCStatus != NULL)
608                         *IOCStatus = req.ioc_status;
609                 else
610                         warnx("Reading extended config page failed: %s",
611                             mps_ioc_status(req.ioc_status));
612                 free(buf);
613                 errno = EIO;
614                 return (NULL);
615         }
616         return (buf);
617 }
618 #endif
619
620 int
621 mps_open(int unit)
622 {
623         char path[MAXPATHLEN];
624
625         snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
626         return (open(path, O_RDWR));
627 }
628
629 int
630 mps_user_command(int fd, void *req, uint32_t req_len, void *reply,
631         uint32_t reply_len, void *buffer, int len, uint32_t flags)
632 {
633         struct mps_usr_command cmd;
634
635         bzero(&cmd, sizeof(struct mps_usr_command));
636         cmd.req = req;
637         cmd.req_len = req_len;
638         cmd.rpl = reply;
639         cmd.rpl_len = reply_len;
640         cmd.buf = buffer;
641         cmd.len = len;
642         cmd.flags = flags;
643
644         if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
645                 return (errno);
646         return (0);
647 }
648
649 int
650 mps_pass_command(int fd, void *req, uint32_t req_len, void *reply,
651         uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
652         uint32_t dataout_len, uint32_t timeout)
653 {
654         struct mprs_pass_thru pass;
655
656         pass.PtrRequest = (uint64_t)(uintptr_t)req;
657         pass.PtrReply = (uint64_t)(uintptr_t)reply;
658         pass.PtrData = (uint64_t)(uintptr_t)data_in;
659         pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
660         pass.RequestSize = req_len;
661         pass.ReplySize = reply_len;
662         pass.DataSize = datain_len;
663         pass.DataOutSize = dataout_len;
664         if (datain_len && dataout_len) {
665                 if (is_mps) {
666                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
667                 } else {
668                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
669                 }
670         } else if (datain_len) {
671                 if (is_mps) {
672                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
673                 } else {
674                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
675                 }
676         } else if (dataout_len) {
677                 if (is_mps) {
678                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
679                 } else {
680                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
681                 }
682         } else {
683                 if (is_mps) {
684                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
685                 } else {
686                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
687                 }
688         }
689         pass.Timeout = timeout;
690
691         if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
692                 return (errno);
693         return (0);
694 }
695
696 MPI2_IOC_FACTS_REPLY *
697 mps_get_iocfacts(int fd)
698 {
699         MPI2_IOC_FACTS_REPLY *facts;
700         MPI2_IOC_FACTS_REQUEST req;
701         int error;
702
703         facts = malloc(sizeof(MPI2_IOC_FACTS_REPLY));
704         if (facts == NULL) {
705                 errno = ENOMEM;
706                 return (NULL);
707         }
708
709         bzero(&req, sizeof(MPI2_IOC_FACTS_REQUEST));
710         req.Function = MPI2_FUNCTION_IOC_FACTS;
711
712 #if 1
713         error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
714             facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, NULL, 0, 10);
715 #else
716         error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
717             facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, 0);
718 #endif
719         if (error) {
720                 free(facts);
721                 return (NULL);
722         }
723
724         if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
725                 free(facts);
726                 errno = EINVAL;
727                 return (NULL);
728         }
729         return (facts);
730 }
731