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