Add FreeBSD's mpr(4) driver for LSI Fusion-MPT 3/3.5 SAS controllers.
[dragonfly.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: head/usr.sbin/mpsutil/mps_cmd.c 321604 2017-07-27 05:31:48Z scottl $");
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         if (req.Header.PageLength == 0)
369                 req.Header.PageLength = 4;
370
371         len = req.Header.PageLength * 4;
372         buf = malloc(len);
373         if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
374             buf, len, NULL, 0, 30)) {
375                 error = errno;
376                 free(buf);
377                 errno = error;
378                 return (NULL);
379         }
380         if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
381                 if (IOCStatus != NULL)
382                         *IOCStatus = reply.IOCStatus;
383                 else
384                         warnx("Reading config page failed: 0x%x %s",
385                             reply.IOCStatus, mps_ioc_status(reply.IOCStatus));
386                 free(buf);
387                 errno = EIO;
388                 return (NULL);
389         }
390         return (buf);
391 }
392
393 void *
394 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
395     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
396 {
397         MPI2_CONFIG_REQUEST req;
398         MPI2_CONFIG_PAGE_HEADER header;
399         MPI2_CONFIG_REPLY reply;
400         U16 pagelen;
401         void *buf;
402         int error, len;
403
404         if (IOCStatus != NULL)
405                 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
406         bzero(&header, sizeof(header));
407         error = mps_read_ext_config_page_header(fd, ExtPageType, PageNumber,
408             PageAddress, &header, &pagelen, IOCStatus);
409         if (error) {
410                 errno = error;
411                 return (NULL);
412         }
413
414         bzero(&req, sizeof(req));
415         req.Function = MPI2_FUNCTION_CONFIG;
416         req.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
417         req.PageAddress = PageAddress;
418         req.Header = header;
419         if (pagelen == 0)
420                 pagelen = 4;
421         req.ExtPageLength = pagelen;
422         req.ExtPageType = ExtPageType;
423
424         len = pagelen * 4;
425         buf = malloc(len);
426         if (mps_pass_command(fd, &req, sizeof(req), &reply, sizeof(reply),
427             buf, len, NULL, 0, 30)) {
428                 error = errno;
429                 free(buf);
430                 errno = error;
431                 return (NULL);
432         }
433         if (!IOC_STATUS_SUCCESS(reply.IOCStatus)) {
434                 if (IOCStatus != NULL)
435                         *IOCStatus = reply.IOCStatus;
436                 else
437                         warnx("Reading extended config page failed: %s",
438                             mps_ioc_status(reply.IOCStatus));
439                 free(buf);
440                 errno = EIO;
441                 return (NULL);
442         }
443         return (buf);
444 }
445
446 int
447 mps_firmware_send(int fd, unsigned char *fw, uint32_t len, bool bios)
448 {
449         MPI2_FW_DOWNLOAD_REQUEST req;
450         MPI2_FW_DOWNLOAD_REPLY reply;
451
452         bzero(&req, sizeof(req));
453         bzero(&reply, sizeof(reply));
454         req.Function = MPI2_FUNCTION_FW_DOWNLOAD;
455         req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
456         req.TotalImageSize = len;
457         req.MsgFlags = MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT;
458
459         if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
460             fw, len, 0)) {
461                 return (-1);
462         }
463         return (0);
464 }
465
466 int
467 mps_firmware_get(int fd, unsigned char **firmware, bool bios)
468 {
469         MPI2_FW_UPLOAD_REQUEST req;
470         MPI2_FW_UPLOAD_REPLY reply;
471         int size;
472
473         *firmware = NULL;
474         bzero(&req, sizeof(req));
475         bzero(&reply, sizeof(reply));
476         req.Function = MPI2_FUNCTION_FW_UPLOAD;
477         req.ImageType = bios ? MPI2_FW_DOWNLOAD_ITYPE_BIOS : MPI2_FW_DOWNLOAD_ITYPE_FW;
478
479         if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
480             NULL, 0, 0)) {
481                 return (-1);
482         }
483         if (reply.ActualImageSize == 0) {
484                 return (-1);
485         }
486
487         size = reply.ActualImageSize;
488         *firmware = calloc(size, sizeof(unsigned char));
489         if (*firmware == NULL) {
490                 warn("calloc");
491                 return (-1);
492         }
493         if (mps_user_command(fd, &req, sizeof(req), &reply, sizeof(reply),
494             *firmware, size, 0)) {
495                 free(*firmware);
496                 return (-1);
497         }
498
499         return (size);
500 }
501
502 #else
503
504 int
505 mps_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
506     MPI2_CONFIG_PAGE_HEADER *header, U16 *IOCStatus)
507 {
508         struct mps_cfg_page_req req;
509
510         if (IOCStatus != NULL)
511                 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
512         if (header == NULL)
513                 return (EINVAL);
514         bzero(&req, sizeof(req));
515         req.header.PageType = PageType;
516         req.header.PageNumber = PageNumber;
517         req.page_address = PageAddress;
518         if (ioctl(fd, MPSIO_READ_CFG_HEADER, &req) < 0)
519                 return (errno);
520         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
521                 if (IOCStatus != NULL)
522                         *IOCStatus = req.ioc_status;
523                 return (EIO);
524         }
525         bcopy(&req.header, header, sizeof(*header));
526         return (0);
527 }
528
529 void *
530 mps_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress,
531     U16 *IOCStatus)
532 {
533         struct mps_cfg_page_req req;
534         void *buf;
535         int error;
536
537         error = mps_read_config_page_header(fd, PageType, PageNumber,
538             PageAddress, &req.header, IOCStatus);
539         if (error) {
540                 errno = error;
541                 return (NULL);
542         }
543
544         if (req.header.PageLength == 0)
545                 req.header.PageLength = 4;
546         req.len = req.header.PageLength * 4;
547         buf = malloc(req.len);
548         req.buf = buf;
549         bcopy(&req.header, buf, sizeof(req.header));
550         if (ioctl(fd, MPSIO_READ_CFG_PAGE, &req) < 0) {
551                 error = errno;
552                 free(buf);
553                 errno = error;
554                 return (NULL);
555         }
556         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
557                 if (IOCStatus != NULL)
558                         *IOCStatus = req.ioc_status;
559                 else
560                         warnx("Reading config page failed: 0x%x %s",
561                             req.ioc_status, mps_ioc_status(req.ioc_status));
562                 free(buf);
563                 errno = EIO;
564                 return (NULL);
565         }
566         return (buf);
567 }
568
569 void *
570 mps_read_extended_config_page(int fd, U8 ExtPageType, U8 PageVersion,
571     U8 PageNumber, U32 PageAddress, U16 *IOCStatus)
572 {
573         struct mps_ext_cfg_page_req req;
574         void *buf;
575         int error;
576
577         if (IOCStatus != NULL)
578                 *IOCStatus = MPI2_IOCSTATUS_SUCCESS;
579         bzero(&req, sizeof(req));
580         req.header.PageVersion = PageVersion;
581         req.header.PageNumber = PageNumber;
582         req.header.ExtPageType = ExtPageType;
583         req.page_address = PageAddress;
584         if (ioctl(fd, MPSIO_READ_EXT_CFG_HEADER, &req) < 0)
585                 return (NULL);
586         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
587                 if (IOCStatus != NULL)
588                         *IOCStatus = req.ioc_status;
589                 else
590                         warnx("Reading extended config page header failed: %s",
591                             mps_ioc_status(req.ioc_status));
592                 errno = EIO;
593                 return (NULL);
594         }
595         req.len = req.header.ExtPageLength * 4;
596         buf = malloc(req.len);
597         req.buf = buf;
598         bcopy(&req.header, buf, sizeof(req.header));
599         if (ioctl(fd, MPSIO_READ_EXT_CFG_PAGE, &req) < 0) {
600                 error = errno;
601                 free(buf);
602                 errno = error;
603                 return (NULL);
604         }
605         if (!IOC_STATUS_SUCCESS(req.ioc_status)) {
606                 if (IOCStatus != NULL)
607                         *IOCStatus = req.ioc_status;
608                 else
609                         warnx("Reading extended config page failed: %s",
610                             mps_ioc_status(req.ioc_status));
611                 free(buf);
612                 errno = EIO;
613                 return (NULL);
614         }
615         return (buf);
616 }
617 #endif
618
619 int
620 mps_open(int unit)
621 {
622         char path[MAXPATHLEN];
623
624         snprintf(path, sizeof(path), "/dev/mp%s%d", is_mps ? "s": "r", unit);
625         return (open(path, O_RDWR));
626 }
627
628 int
629 mps_user_command(int fd, void *req, uint32_t req_len, void *reply,
630         uint32_t reply_len, void *buffer, int len, uint32_t flags)
631 {
632         struct mps_usr_command cmd;
633
634         bzero(&cmd, sizeof(struct mps_usr_command));
635         cmd.req = req;
636         cmd.req_len = req_len;
637         cmd.rpl = reply;
638         cmd.rpl_len = reply_len;
639         cmd.buf = buffer;
640         cmd.len = len;
641         cmd.flags = flags;
642
643         if (ioctl(fd, is_mps ? MPSIO_MPS_COMMAND : MPRIO_MPR_COMMAND, &cmd) < 0)
644                 return (errno);
645         return (0);
646 }
647
648 int
649 mps_pass_command(int fd, void *req, uint32_t req_len, void *reply,
650         uint32_t reply_len, void *data_in, uint32_t datain_len, void *data_out,
651         uint32_t dataout_len, uint32_t timeout)
652 {
653         struct mprs_pass_thru pass;
654
655         pass.PtrRequest = (uint64_t)(uintptr_t)req;
656         pass.PtrReply = (uint64_t)(uintptr_t)reply;
657         pass.PtrData = (uint64_t)(uintptr_t)data_in;
658         pass.PtrDataOut = (uint64_t)(uintptr_t)data_out;
659         pass.RequestSize = req_len;
660         pass.ReplySize = reply_len;
661         pass.DataSize = datain_len;
662         pass.DataOutSize = dataout_len;
663         if (datain_len && dataout_len) {
664                 if (is_mps) {
665                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_BOTH;
666                 } else {
667                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_BOTH;
668                 }
669         } else if (datain_len) {
670                 if (is_mps) {
671                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_READ;
672                 } else {
673                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_READ;
674                 }
675         } else if (dataout_len) {
676                 if (is_mps) {
677                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_WRITE;
678                 } else {
679                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_WRITE;
680                 }
681         } else {
682                 if (is_mps) {
683                         pass.DataDirection = MPS_PASS_THRU_DIRECTION_NONE;
684                 } else {
685                         pass.DataDirection = MPR_PASS_THRU_DIRECTION_NONE;
686                 }
687         }
688         pass.Timeout = timeout;
689
690         if (ioctl(fd, MPTIOCTL_PASS_THRU, &pass) < 0)
691                 return (errno);
692         return (0);
693 }
694
695 MPI2_IOC_FACTS_REPLY *
696 mps_get_iocfacts(int fd)
697 {
698         MPI2_IOC_FACTS_REPLY *facts;
699         MPI2_IOC_FACTS_REQUEST req;
700         int error;
701
702         facts = malloc(sizeof(MPI2_IOC_FACTS_REPLY));
703         if (facts == NULL) {
704                 errno = ENOMEM;
705                 return (NULL);
706         }
707
708         bzero(&req, sizeof(MPI2_IOC_FACTS_REQUEST));
709         req.Function = MPI2_FUNCTION_IOC_FACTS;
710
711 #if 1
712         error = mps_pass_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
713             facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, NULL, 0, 10);
714 #else
715         error = mps_user_command(fd, &req, sizeof(MPI2_IOC_FACTS_REQUEST),
716             facts, sizeof(MPI2_IOC_FACTS_REPLY), NULL, 0, 0);
717 #endif
718         if (error) {
719                 free(facts);
720                 return (NULL);
721         }
722
723         if (!IOC_STATUS_SUCCESS(facts->IOCStatus)) {
724                 free(facts);
725                 errno = EINVAL;
726                 return (NULL);
727         }
728         return (facts);
729 }
730