Home | History | Annotate | Download | only in impl
      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