1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * SD card nexus support. 28 * 29 * NB that this file contains a fair bit of non-DDI compliant code. 30 * But writing a nexus driver would be impossible to do with only DDI 31 * compliant interfaces. 32 */ 33 34 #include <sys/types.h> 35 #include <sys/modctl.h> 36 #include <sys/list.h> 37 #include <sys/mkdev.h> 38 #include <sys/file.h> 39 #include <sys/errno.h> 40 #include <sys/open.h> 41 #include <sys/cred.h> 42 #include <sys/stat.h> 43 #include <sys/conf.h> 44 #include <sys/sysmacros.h> 45 #include <sys/ddi.h> 46 #include <sys/sunddi.h> 47 #include <sys/sunndi.h> 48 #include <sys/sdcard/sda.h> 49 #include <sys/sdcard/sda_ioctl.h> 50 #include <sys/sdcard/sda_impl.h> 51 #include <sys/fs/dv_node.h> 52 53 /* 54 * Local prototypes. 55 */ 56 57 static sda_host_t *sda_nexus_lookup_dev(dev_t); 58 static int sda_nexus_ap_ioctl(sda_host_t *, int, int, intptr_t); 59 static int sda_nexus_ap_control(sda_host_t *, int, intptr_t, int); 60 static int sda_nexus_ap_disconnect(sda_slot_t *); 61 static int sda_nexus_ap_configure(sda_slot_t *); 62 static int sda_nexus_ap_unconfigure(sda_slot_t *); 63 static void sda_nexus_ap_getstate(sda_slot_t *, devctl_ap_state_t *); 64 static void sda_nexus_reinsert(sda_slot_t *); 65 static void sda_nexus_create(sda_slot_t *); 66 67 /* 68 * Static Variables. 69 */ 70 71 static kmutex_t sda_nexus_lock; 72 static list_t sda_nexus_list; 73 74 /* 75 * Minor number allocation. 76 * 77 * We have up to NBITSMINOR32 (18) bits available. 78 * 79 * For each instance, we need one minor number for each slot, and one 80 * minor number for the devctl node. 81 * 82 * For simplicity's sake, we use the lower 8 bits for AP and DEVCTL nodes, 83 * and the remaining 10 bits for the instance number. 84 */ 85 #define MINOR_DC 0xff 86 #define DEV_SLOT(dev) (getminor(dev) & 0xff) 87 #define DEV_INST(dev) (getminor(dev) >> 8) 88 #define MKMINOR_AP(inst, slot) (((slot) & 0xff) | ((inst) << 8)) 89 #define MKMINOR_DC(inst) (((inst) << 8) | MINOR_DC) 90 91 /* 92 * Implementation. 93 */ 94 95 void 96 sda_nexus_init(void) 97 { 98 list_create(&sda_nexus_list, sizeof (sda_host_t), 99 offsetof(struct sda_host, h_node)); 100 mutex_init(&sda_nexus_lock, NULL, MUTEX_DRIVER, NULL); 101 } 102 103 void 104 sda_nexus_fini(void) 105 { 106 list_destroy(&sda_nexus_list); 107 mutex_destroy(&sda_nexus_lock); 108 } 109 110 int 111 sda_nexus_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 112 void *arg, void *result) 113 { 114 switch (ctlop) { 115 case DDI_CTLOPS_REPORTDEV: 116 { 117 cmn_err(CE_CONT, "?SD-device: %s@%s, %s#%d\n", 118 ddi_node_name(rdip), ddi_get_name_addr(rdip), 119 ddi_driver_name(rdip), ddi_get_instance(rdip)); 120 121 return (DDI_SUCCESS); 122 } 123 124 case DDI_CTLOPS_INITCHILD: 125 { 126 dev_info_t *child_dip = (dev_info_t *)arg; 127 dev_info_t *ndip; 128 sda_slot_t *slot; 129 char addr[16]; 130 131 if ((slot = ddi_get_parent_data(child_dip)) == NULL) { 132 sda_slot_err(NULL, "Parent data struct missing!"); 133 return (DDI_NOT_WELL_FORMED); 134 } 135 136 /* 137 * TODO: SDIO: We will need to use x,y addresses for 138 * SDIO function numbers. Memory cards will always 139 * resid at address 0. Probably this can be passed in 140 * to this function using properties. 141 */ 142 (void) snprintf(addr, sizeof (addr), "%x", slot->s_slot_num); 143 144 /* 145 * Prevent duplicate nodes. 146 */ 147 ndip = ndi_devi_find(dip, ddi_node_name(child_dip), addr); 148 if (ndip && (ndip != child_dip)) { 149 sda_slot_err(slot, "Duplicate device node found " 150 "(%s@%s)", ddi_node_name(ndip), addr); 151 } 152 153 /* 154 * Stash the address in the devinfo node. 155 */ 156 ddi_set_name_addr(child_dip, addr); 157 158 return (DDI_SUCCESS); 159 } 160 161 case DDI_CTLOPS_UNINITCHILD: 162 { 163 dev_info_t *child_dip = (dev_info_t *)arg; 164 165 ddi_set_name_addr(child_dip, NULL); 166 ndi_prop_remove_all(child_dip); 167 return (DDI_SUCCESS); 168 } 169 170 case DDI_CTLOPS_SIDDEV: 171 /* 172 * All SDA target devices are self-identifying. 173 */ 174 return (DDI_SUCCESS); 175 176 case DDI_CTLOPS_SLAVEONLY: 177 /* 178 * We don't support DMA master for SDA targets. 179 */ 180 return (DDI_SUCCESS); 181 182 case DDI_CTLOPS_AFFINITY: 183 /* 184 * NB: We may want to revisit this later, so that functions 185 * on one card can see other functions on the same card. 186 * Right now there is no need. 187 */ 188 return (DDI_FAILURE); 189 190 case DDI_CTLOPS_DMAPMAPC: 191 case DDI_CTLOPS_REPORTINT: 192 case DDI_CTLOPS_POKE: 193 case DDI_CTLOPS_PEEK: 194 case DDI_CTLOPS_NREGS: 195 case DDI_CTLOPS_REGSIZE: 196 /* 197 * We don't support any of these (yet?). 198 */ 199 return (DDI_FAILURE); 200 201 default: 202 /* 203 * Everything else goes to the parent nexus. 204 */ 205 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 206 } 207 } 208 209 void 210 sda_nexus_register(sda_host_t *h) 211 { 212 int i; 213 int inst; 214 char name[16]; 215 216 mutex_enter(&sda_nexus_lock); 217 list_insert_tail(&sda_nexus_list, h); 218 mutex_exit(&sda_nexus_lock); 219 220 /* 221 * Now create minor nodes. Note that failures to create these nodes 222 * are mostly harmless, so we don't do much besides warn about it. 223 * (It means cfgadm will be useless, but most folks aren't likely 224 * to use cfgadm anyway.) 225 */ 226 227 inst = ddi_get_instance(h->h_dip); 228 229 /* 230 * Create the devctl minor node. 231 */ 232 if (ddi_create_minor_node(h->h_dip, "devctl", S_IFCHR, 233 MKMINOR_DC(inst), DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 234 sda_slot_err(NULL, "Unable to create devctl node"); 235 } 236 237 for (i = 0; i < h->h_nslot; i++) { 238 239 sda_slot_t *slot; 240 241 slot = &h->h_slots[i]; 242 /* 243 * Create the attachment point minor nodes. 244 */ 245 (void) snprintf(name, sizeof (name), "%d", i); 246 if (ddi_create_minor_node(h->h_dip, name, S_IFCHR, 247 MKMINOR_AP(inst, i), DDI_NT_SDCARD_ATTACHMENT_POINT, 248 0) != DDI_SUCCESS) { 249 sda_slot_err(slot, 250 "Unable to create attachment point node"); 251 } 252 } 253 } 254 255 void 256 sda_nexus_unregister(sda_host_t *h) 257 { 258 /* 259 * Remove all minor nodes. 260 */ 261 ddi_remove_minor_node(h->h_dip, NULL); 262 263 mutex_enter(&sda_nexus_lock); 264 list_remove(&sda_nexus_list, h); 265 mutex_exit(&sda_nexus_lock); 266 } 267 268 sda_host_t * 269 sda_nexus_lookup_dev(dev_t dev) 270 { 271 major_t maj; 272 int inst; 273 sda_host_t *h; 274 275 ASSERT(mutex_owned(&sda_nexus_lock)); 276 277 maj = getmajor(dev); 278 inst = DEV_INST(dev); 279 280 h = list_head(&sda_nexus_list); 281 while (h != NULL) { 282 if ((ddi_driver_major(h->h_dip) == maj) && 283 (ddi_get_instance(h->h_dip) == inst)) { 284 break; 285 } 286 h = list_next(&sda_nexus_list, h); 287 } 288 return (h); 289 } 290 291 void 292 sda_nexus_create(sda_slot_t *slot) 293 { 294 dev_info_t *pdip, *cdip; 295 int rv; 296 297 pdip = slot->s_hostp->h_dip; 298 299 /* 300 * SDIO: This whole function will need to be recrafted to 301 * support non-memory children. For SDIO, there could be 302 * multiple functions, which get inserted or removed together. 303 */ 304 305 if (ndi_devi_alloc(pdip, "sdcard", DEVI_SID_NODEID, &cdip) != 306 NDI_SUCCESS) { 307 sda_slot_err(slot, "Failed allocating devinfo node"); 308 return; 309 } 310 311 ddi_set_parent_data(cdip, slot); 312 slot->s_dip = NULL; 313 314 /* 315 * Make sure the child node gets suspend/resume events. 316 */ 317 rv = ndi_prop_update_int(DDI_DEV_T_NONE, cdip, "pm-capable", 1); 318 if (rv != 0) { 319 sda_slot_err(slot, "Failed creating pm-capable property"); 320 (void) ndi_devi_free(cdip); 321 return; 322 } 323 324 sda_slot_enter(slot); 325 slot->s_ready = B_TRUE; 326 sda_slot_exit(slot); 327 328 if (ndi_devi_online(cdip, NDI_ONLINE_ATTACH) != NDI_SUCCESS) { 329 sda_slot_err(slot, "Failed bringing node online"); 330 (void) ndi_devi_free(cdip); 331 } else { 332 slot->s_dip = cdip; 333 } 334 } 335 336 void 337 sda_nexus_reinsert(sda_slot_t *slot) 338 { 339 dev_info_t *cdip, *pdip; 340 int circ; 341 342 pdip = slot->s_hostp->h_dip; 343 344 ndi_devi_enter(pdip, &circ); 345 sda_slot_enter(slot); 346 if ((cdip = slot->s_dip) != NULL) { 347 mutex_enter(&DEVI(cdip)->devi_lock); 348 DEVI_SET_DEVICE_REINSERTED(cdip); 349 mutex_exit(&DEVI(cdip)->devi_lock); 350 } 351 sda_slot_exit(slot); 352 slot->s_warn = B_FALSE; 353 slot->s_ready = B_TRUE; 354 ndi_devi_exit(pdip, circ); 355 } 356 357 void 358 sda_nexus_insert(sda_slot_t *slot) 359 { 360 char uuid[40]; 361 boolean_t match; 362 363 if (slot->s_flags & SLOTF_MEMORY) { 364 (void) snprintf(uuid, sizeof (uuid), "%c%08X%08X%08X%08X", 365 slot->s_flags & SLOTF_MMC ? 'M' : 'S', 366 slot->s_rcid[0], slot->s_rcid[1], 367 slot->s_rcid[2], slot->s_rcid[3]); 368 } else { 369 /* 370 * SDIO: For SDIO, we can write the card's MANFID 371 * tuple in CIS to the UUID. Until we support SDIO, 372 * we just suppress creating devinfo nodes. 373 */ 374 sda_slot_err(slot, "Non-memory target not supported"); 375 uuid[0] = 0; 376 } 377 378 match = ((uuid[0] != 0) && (strcmp(slot->s_uuid, uuid) == 0)); 379 380 if (slot->s_dip != NULL) { 381 if (!match) { 382 sda_slot_err(slot, "Card removed while still in use."); 383 sda_slot_err(slot, "Please reinsert previous card."); 384 385 sda_nexus_remove(slot); 386 } else { 387 sda_nexus_reinsert(slot); 388 } 389 } else { 390 /* 391 * Remember the UUID. 392 */ 393 (void) strlcpy(slot->s_uuid, uuid, sizeof (slot->s_uuid)); 394 /* 395 * Create the children. 396 */ 397 if (uuid[0] != 0) 398 sda_nexus_create(slot); 399 } 400 } 401 402 void 403 sda_nexus_remove(sda_slot_t *slot) 404 { 405 sda_host_t *h = slot->s_hostp; 406 dev_info_t *pdip = h->h_dip; 407 dev_info_t *cdip; 408 int circ; 409 boolean_t reap = B_FALSE; 410 411 ndi_devi_enter(pdip, &circ); 412 if ((cdip = slot->s_dip) != NULL) { 413 reap = B_TRUE; 414 415 mutex_enter(&(DEVI(cdip))->devi_lock); 416 DEVI_SET_DEVICE_REMOVED(cdip); 417 mutex_exit(&(DEVI(cdip))->devi_lock); 418 } 419 ndi_devi_exit(pdip, circ); 420 421 if (reap) { 422 mutex_enter(&slot->s_evlock); 423 slot->s_reap = B_TRUE; 424 mutex_exit(&slot->s_evlock); 425 sda_slot_wakeup(slot); 426 } 427 } 428 429 void 430 sda_nexus_reap(void *arg) 431 { 432 sda_slot_t *slot = arg; 433 dev_info_t *pdip = slot->s_hostp->h_dip; 434 dev_info_t *cdip; 435 int circ; 436 char *devnm; 437 int rv; 438 439 devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP); 440 441 ndi_devi_enter(pdip, &circ); 442 sda_slot_enter(slot); 443 444 cdip = slot->s_dip; 445 446 if ((cdip != NULL) && DEVI_IS_DEVICE_REMOVED(cdip)) { 447 448 sda_slot_exit(slot); 449 450 if (i_ddi_node_state(cdip) < DS_INITIALIZED) { 451 rv = ddi_remove_child(cdip, 0); 452 } else { 453 (void) ddi_deviname(cdip, devnm); 454 (void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE); 455 rv = ndi_devi_unconfig_one(pdip, devnm + 1, NULL, 456 NDI_DEVI_REMOVE | NDI_UNCONFIG); 457 } 458 459 if (rv != NDI_SUCCESS) { 460 461 mutex_enter(&slot->s_evlock); 462 slot->s_reap = B_TRUE; 463 mutex_exit(&slot->s_evlock); 464 ndi_devi_exit(pdip, circ); 465 return; 466 } 467 sda_slot_enter(slot); 468 469 if (slot->s_dip == cdip) { 470 slot->s_dip = NULL; 471 } 472 } 473 sda_slot_exit(slot); 474 475 mutex_enter(&slot->s_evlock); 476 /* woohoo, done reaping nodes */ 477 slot->s_reap = B_FALSE; 478 mutex_exit(&slot->s_evlock); 479 480 ndi_devi_exit(pdip, circ); 481 kmem_free(devnm, MAXNAMELEN + 1); 482 } 483 484 /*ARGSUSED3*/ 485 int 486 sda_nexus_open(dev_t *devp, int flags, int otyp, cred_t *credp) 487 { 488 int rv = 0; 489 sda_host_t *h; 490 491 if (otyp != OTYP_CHR) 492 return (EINVAL); 493 494 mutex_enter(&sda_nexus_lock); 495 if ((h = sda_nexus_lookup_dev(*devp)) == NULL) { 496 mutex_exit(&sda_nexus_lock); 497 return (ENXIO); 498 } 499 500 if (flags & FEXCL) { 501 if ((h->h_flags & (HOST_SOPEN|HOST_XOPEN)) != 0) { 502 rv = EBUSY; 503 } else { 504 h->h_flags |= HOST_XOPEN; 505 } 506 } else { 507 if ((h->h_flags & HOST_XOPEN) != 0) { 508 rv = EBUSY; 509 } else { 510 h->h_flags |= HOST_SOPEN; 511 } 512 } 513 mutex_exit(&sda_nexus_lock); 514 return (rv); 515 } 516 517 /*ARGSUSED1*/ 518 int 519 sda_nexus_close(dev_t dev, int flag, int otyp, cred_t *credp) 520 { 521 sda_host_t *h; 522 523 if (otyp != OTYP_CHR) 524 return (EINVAL); 525 526 mutex_enter(&sda_nexus_lock); 527 if ((h = sda_nexus_lookup_dev(dev)) == NULL) { 528 mutex_exit(&sda_nexus_lock); 529 return (ENXIO); 530 } 531 h->h_flags &= ~(HOST_XOPEN | HOST_SOPEN); 532 mutex_exit(&sda_nexus_lock); 533 return (0); 534 } 535 536 void 537 sda_nexus_ap_getstate(sda_slot_t *slot, devctl_ap_state_t *ap_state) 538 { 539 dev_info_t *cdip; 540 dev_info_t *pdip = slot->s_hostp->h_dip; 541 int circ; 542 543 ndi_devi_enter(pdip, &circ); 544 sda_slot_enter(slot); 545 546 /* 547 * Default state. 548 */ 549 ap_state->ap_rstate = AP_RSTATE_EMPTY; 550 ap_state->ap_condition = AP_COND_OK; 551 ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED; 552 553 if (slot->s_inserted) { 554 ap_state->ap_rstate = AP_RSTATE_CONNECTED; 555 } 556 557 if ((cdip = slot->s_dip) != NULL) { 558 mutex_enter(&DEVI(cdip)->devi_lock); 559 if (DEVI_IS_DEVICE_REMOVED(cdip)) { 560 ap_state->ap_condition = AP_COND_UNUSABLE; 561 } 562 if (DEVI_IS_DEVICE_OFFLINE(cdip) || 563 DEVI_IS_DEVICE_DOWN(cdip)) { 564 ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED; 565 } else { 566 ap_state->ap_ostate = AP_OSTATE_CONFIGURED; 567 } 568 mutex_exit(&DEVI(cdip)->devi_lock); 569 } 570 571 if (slot->s_failed) { 572 ap_state->ap_condition = AP_COND_FAILED; 573 } 574 575 ap_state->ap_last_change = slot->s_stamp; 576 ap_state->ap_in_transition = slot->s_intransit; 577 578 sda_slot_exit(slot); 579 ndi_devi_exit(pdip, circ); 580 } 581 582 int 583 sda_nexus_ap_disconnect(sda_slot_t *slot) 584 { 585 dev_info_t *cdip; 586 dev_info_t *pdip = slot->s_hostp->h_dip; 587 int rv = 0; 588 int circ; 589 590 /* if a child node exists, try to delete it */ 591 ndi_devi_enter(pdip, &circ); 592 593 sda_slot_enter(slot); 594 if ((cdip = slot->s_dip) != NULL) { 595 if (ndi_devi_offline(cdip, NDI_DEVI_REMOVE) != NDI_SUCCESS) { 596 /* couldn't disconnect, why not? */ 597 rv = EBUSY; 598 goto done; 599 } 600 } 601 slot->s_stamp = ddi_get_time(); 602 slot->s_dip = NULL; 603 done: 604 sda_slot_exit(slot); 605 ndi_devi_exit(pdip, circ); 606 return (rv); 607 } 608 609 int 610 sda_nexus_ap_unconfigure(sda_slot_t *slot) 611 { 612 dev_info_t *cdip; 613 dev_info_t *pdip = slot->s_hostp->h_dip; 614 int rv = 0; 615 int circ; 616 617 /* attempt to unconfigure the node */ 618 ndi_devi_enter(pdip, &circ); 619 sda_slot_enter(slot); 620 if ((cdip = slot->s_dip) != NULL) { 621 if (ndi_devi_offline(cdip, NDI_UNCONFIG) != NDI_SUCCESS) { 622 /* failed to unconfigure the node (EBUSY?) */ 623 rv = EIO; 624 goto done; 625 } 626 } 627 slot->s_stamp = ddi_get_time(); 628 slot->s_dip = NULL; 629 done: 630 sda_slot_exit(slot); 631 ndi_devi_exit(pdip, circ); 632 return (rv); 633 } 634 635 int 636 sda_nexus_ap_configure(sda_slot_t *slot) 637 { 638 dev_info_t *cdip; 639 640 sda_slot_enter(slot); 641 if (slot->s_inserted == B_FALSE) { 642 /* device not present */ 643 sda_slot_exit(slot); 644 return (ENXIO); 645 } 646 647 /* attempt to configure the node */ 648 if ((cdip = slot->s_dip) == NULL) { 649 sda_slot_exit(slot); 650 /* node not there! */ 651 return (ENXIO); 652 } 653 sda_slot_exit(slot); 654 655 slot->s_intransit = 1; 656 657 if (ndi_devi_online(cdip, NDI_CONFIG) != NDI_SUCCESS) { 658 /* failed to configure the node */ 659 slot->s_intransit = 0; 660 return (EIO); 661 } 662 slot->s_intransit = 0; 663 slot->s_stamp = ddi_get_time(); 664 return (0); 665 } 666 667 int 668 sda_nexus_ap_ioctl(sda_host_t *h, int snum, int cmd, intptr_t arg) 669 { 670 struct devctl_iocdata *dcp = NULL; 671 devctl_ap_state_t ap_state; 672 sda_slot_t *slot; 673 int rv = 0; 674 675 /* 676 * In theory we could try to support this operation on the 677 * DEVCTL minor, but then we would need a slot member in the 678 * user nvlist. For now its easiest to assume a 1:1 relation 679 * between the AP minor node, and the slot number. 680 */ 681 if (snum >= h->h_nslot) { 682 return (ENXIO); 683 } 684 slot = &h->h_slots[snum]; 685 686 if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 687 return (EFAULT); 688 689 switch (cmd) { 690 case DEVCTL_AP_DISCONNECT: 691 rv = sda_nexus_ap_disconnect(slot); 692 break; 693 694 case DEVCTL_AP_UNCONFIGURE: 695 rv = sda_nexus_ap_unconfigure(slot); 696 break; 697 698 case DEVCTL_AP_CONFIGURE: 699 rv = sda_nexus_ap_configure(slot); 700 break; 701 702 case DEVCTL_AP_GETSTATE: 703 bzero(&ap_state, sizeof (ap_state)); 704 sda_nexus_ap_getstate(slot, &ap_state); 705 if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) { 706 rv = EFAULT; 707 } 708 break; 709 } 710 711 ndi_dc_freehdl(dcp); 712 713 return (rv); 714 } 715 716 int 717 sda_nexus_ap_control(sda_host_t *h, int snum, intptr_t arg, int mode) 718 { 719 struct sda_ap_control apc; 720 struct sda_ap_control32 apc32; 721 sda_slot_t *slot; 722 int rv = 0; 723 724 if (snum >= h->h_nslot) { 725 return (ENXIO); 726 } 727 slot = &h->h_slots[snum]; 728 729 switch (ddi_model_convert_from(mode & FMODELS)) { 730 case DDI_MODEL_ILP32: 731 if (ddi_copyin((void *)arg, &apc32, sizeof (apc32), mode) != 732 0) { 733 return (EFAULT); 734 } 735 apc.cmd = apc32.cmd; 736 apc.size = apc32.size; 737 apc.data = (caddr_t *)(intptr_t)apc32.data; 738 break; 739 case DDI_MODEL_NONE: 740 if (ddi_copyin((void *)arg, &apc, sizeof (apc), mode) != 0) { 741 return (EFAULT); 742 } 743 break; 744 } 745 746 switch (apc.cmd) { 747 case SDA_CFGA_GET_CARD_INFO: { 748 sda_card_info_t ci; 749 750 bzero(&ci, sizeof (ci)); 751 if (apc.size < sizeof (sda_card_info_t)) { 752 apc.size = sizeof (sda_card_info_t); 753 break; 754 } 755 sda_slot_enter(slot); 756 if (!slot->s_inserted) { 757 ci.ci_type = SDA_CT_UNKNOWN; 758 } else if (slot->s_flags & SLOTF_MMC) { 759 ci.ci_type = SDA_CT_MMC; 760 } else if (slot->s_flags & SLOTF_SDIO) { 761 if (slot->s_flags & SLOTF_MEMORY) { 762 ci.ci_type = SDA_CT_SDCOMBO; 763 } else { 764 ci.ci_type = SDA_CT_SDIO; 765 } 766 } else if (slot->s_flags & SLOTF_SDMEM) { 767 if (slot->s_flags & SLOTF_SDHC) { 768 ci.ci_type = SDA_CT_SDHC; 769 } else { 770 ci.ci_type = SDA_CT_SDMEM; 771 } 772 } else { 773 ci.ci_type = SDA_CT_UNKNOWN; 774 } 775 776 if (slot->s_flags & SLOTF_MEMORY) { 777 ci.ci_mfg = slot->s_mfg; 778 (void) strlcpy(ci.ci_oem, 779 slot->s_oem, sizeof (ci.ci_oem)); 780 (void) strlcpy(ci.ci_pid, 781 slot->s_prod, sizeof (ci.ci_pid)); 782 ci.ci_serial = slot->s_serial; 783 ci.ci_month = slot->s_month; 784 ci.ci_year = (slot->s_year - 1900) & 0xff; 785 ci.ci_major = slot->s_majver; 786 ci.ci_minor = slot->s_minver; 787 } 788 789 sda_slot_exit(slot); 790 791 if (ddi_copyout(&ci, apc.data, sizeof (ci), mode) != 0) { 792 return (EFAULT); 793 } 794 795 break; 796 } 797 798 case SDA_CFGA_GET_DEVICE_PATH: 799 { 800 char path[MAXPATHLEN]; 801 dev_info_t *cdip; 802 int slen; 803 804 sda_slot_enter(slot); 805 if ((cdip = slot->s_dip) == NULL) { 806 sda_slot_exit(slot); 807 return (ENOENT); 808 } 809 (void) strcpy(path, "/devices"); 810 (void) ddi_pathname(cdip, path + strlen(path)); 811 slen = strlen(path) + 1; 812 sda_slot_exit(slot); 813 if (apc.size < slen) { 814 apc.size = slen; 815 rv = ENOSPC; 816 break; 817 } 818 apc.size = slen; 819 if (ddi_copyout(path, apc.data, slen, mode) != 0) { 820 return (EFAULT); 821 } 822 break; 823 } 824 825 case SDA_CFGA_RESET_SLOT: 826 { 827 sda_slot_enter(slot); 828 slot->s_failed = B_FALSE; 829 sda_slot_exit(slot); 830 sda_slot_reset(slot); 831 sda_slot_detect(slot); 832 break; 833 } 834 835 default: 836 return (EINVAL); 837 } 838 839 switch (ddi_model_convert_from(mode & FMODELS)) { 840 case DDI_MODEL_ILP32: 841 apc32.cmd = apc.cmd; 842 apc32.size = (size32_t)apc.size; 843 apc32.data = (caddr32_t)(intptr_t)apc.data; 844 if (ddi_copyout(&apc32, (void *)arg, sizeof (apc32), mode) != 845 0) { 846 return (EFAULT); 847 } 848 break; 849 case DDI_MODEL_NONE: 850 if (ddi_copyout(&apc, (void *)arg, sizeof (apc), mode) != 0) { 851 return (EFAULT); 852 } 853 break; 854 } 855 return (rv); 856 } 857 858 /*ARGSUSED4*/ 859 int 860 sda_nexus_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 861 int *rvp) 862 { 863 sda_host_t *h; 864 865 mutex_enter(&sda_nexus_lock); 866 h = sda_nexus_lookup_dev(dev); 867 mutex_exit(&sda_nexus_lock); 868 869 if (h == NULL) 870 return (ENXIO); 871 872 switch (cmd) { 873 case DEVCTL_DEVICE_GETSTATE: 874 case DEVCTL_DEVICE_ONLINE: 875 case DEVCTL_DEVICE_OFFLINE: 876 case DEVCTL_DEVICE_REMOVE: 877 case DEVCTL_BUS_GETSTATE: 878 return (ndi_devctl_ioctl(h->h_dip, cmd, arg, mode, 0)); 879 880 case DEVCTL_AP_DISCONNECT: 881 case DEVCTL_AP_CONFIGURE: 882 case DEVCTL_AP_UNCONFIGURE: 883 case DEVCTL_AP_GETSTATE: 884 return (sda_nexus_ap_ioctl(h, DEV_SLOT(dev), cmd, arg)); 885 886 case DEVCTL_AP_CONTROL: 887 return (sda_nexus_ap_control(h, DEV_SLOT(dev), arg, mode)); 888 889 default: 890 return (ENOTSUP); 891 } 892 } 893 894 /*ARGSUSED*/ 895 int 896 sda_nexus_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp) 897 { 898 sda_host_t *h; 899 int rv; 900 901 rv = DDI_FAILURE; 902 903 switch (cmd) { 904 case DDI_INFO_DEVT2DEVINFO: 905 mutex_enter(&sda_nexus_lock); 906 h = sda_nexus_lookup_dev((dev_t)arg); 907 if (h != NULL) { 908 *resp = h->h_dip; 909 rv = DDI_SUCCESS; 910 } 911 mutex_exit(&sda_nexus_lock); 912 break; 913 914 case DDI_INFO_DEVT2INSTANCE: 915 *resp = (void *)(intptr_t)DEV_INST((dev_t)arg); 916 rv = DDI_SUCCESS; 917 break; 918 } 919 return (rv); 920 } 921