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(1, sizeof(char) * size); 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 732