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