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 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 731