コード例 #1
0
ファイル: sda_slot.c プロジェクト: metricinc/illumos-gate
void
sda_slot_handle_detect(sda_slot_t *slot)
{
	uint32_t	inserted;

	sda_slot_enter(slot);

	slot->s_stamp = ddi_get_time();
	slot->s_intransit = 1;
	slot->s_flags = 0;
	slot->s_rca = 0;
	slot->s_ready = B_FALSE;

	sda_getprop(slot, SDA_PROP_INSERTED, &inserted);
	slot->s_inserted = (inserted != 0);

	if (slot->s_inserted && !slot->s_failed) {
		/*
		 * We need to initialize the card, so we only support
		 * hipri commands for now.
		 */
		slot->s_init = B_TRUE;
		sda_slot_exit(slot);

		/*
		 * Card insertion occurred.  We have to run this on
		 * another task, to avoid deadlock as the task may
		 * need to dispatch commands.
		 */

		(void) ddi_taskq_dispatch(slot->s_hp_tq, sda_slot_insert, slot,
		    DDI_SLEEP);
	} else {

		/*
		 * Nuke in-flight commands.
		 */
		sda_slot_abort(slot, SDA_ENODEV);

		/*
		 * Restart the slot (incl. power cycle).  This gets the
		 * slot to a known good state.
		 */
		sda_slot_reset(slot);

		slot->s_intransit = 0;
		sda_slot_exit(slot);

		bd_state_change(slot->s_bdh);
	}

	sda_slot_wakeup(slot);
}
コード例 #2
0
ファイル: sda_slot.c プロジェクト: metricinc/illumos-gate
void
sda_slot_abort(sda_slot_t *slot, sda_err_t errno)
{
	sda_cmd_t	*cmdp;

	ASSERT(sda_slot_owned(slot));

	if ((cmdp = slot->s_xfrp) != NULL) {
		slot->s_xfrp = NULL;
		sda_cmd_notify(cmdp, 0, errno);
		list_insert_tail(&slot->s_abortlist, cmdp);
	}
	while ((cmdp = list_head(&slot->s_cmdlist)) != NULL) {
		list_remove(&slot->s_cmdlist, cmdp);
		sda_cmd_notify(cmdp, 0, errno);
		list_insert_tail(&slot->s_abortlist, cmdp);
	}

	sda_slot_wakeup(slot);
}
コード例 #3
0
void
sda_slot_handle_transfer(sda_slot_t *slot, sda_err_t errno)
{
	sda_cmd_t	*cmdp;

	sda_slot_enter(slot);

	if ((cmdp = slot->s_xfrp) != NULL) {

		slot->s_xfrp = NULL;
		slot->s_xfrtmo = 0;
		(void) sda_setprop(slot, SDA_PROP_LED, 0);
		sda_slot_exit(slot);

		sda_slot_wakeup(slot);

		sda_cmd_notify(cmdp, SDA_CMDF_DAT, errno);
	} else {
		sda_slot_exit(slot);
	}
}
コード例 #4
0
void
sda_slot_abort(sda_slot_t *slot, sda_err_t errno)
{
	sda_cmd_t	*cmdp;

	ASSERT(sda_slot_owned(slot));

	if ((cmdp = slot->s_xfrp) != NULL) {
		slot->s_xfrp = NULL;
		sda_cmd_notify(cmdp, SDA_CMDF_BUSY | SDA_CMDF_DAT, errno);
	}
	while ((cmdp = list_head(&slot->s_cmdlist)) != NULL) {
		list_remove(&slot->s_cmdlist, cmdp);
		sda_cmd_notify(cmdp, 0, errno);
		mutex_enter(&slot->s_evlock);
		list_insert_tail(&slot->s_abortlist, cmdp);
		mutex_exit(&slot->s_evlock);
	}

	sda_slot_wakeup(slot);
}
コード例 #5
0
void
sda_slot_mem_reset(sda_slot_t *slot, sda_err_t errno)
{
	sda_cmd_t	*cmdp;

	sda_slot_enter(slot);
	cmdp = list_head(&slot->s_cmdlist);
	while (cmdp != NULL) {
		sda_cmd_t	*next;
		next = list_next(&slot->s_cmdlist, cmdp);
		if (cmdp->sc_flags & SDA_CMDF_MEM) {
			list_remove(&slot->s_cmdlist, cmdp);
			sda_cmd_notify(cmdp, 0, errno);
			mutex_enter(&slot->s_evlock);
			list_insert_tail(&slot->s_abortlist, cmdp);
			mutex_exit(&slot->s_evlock);
		}
		cmdp = next;
	}
	sda_slot_exit(slot);

	/* wake up to process the abort list */
	sda_slot_wakeup(slot);
}
コード例 #6
0
void
sda_slot_thread(void *arg)
{
	sda_slot_t	*slot = arg;

	for (;;) {
		sda_cmd_t	*cmdp;
		boolean_t	datline;
		sda_err_t	rv;

		mutex_enter(&slot->s_evlock);

		/*
		 * Process any abort list first.
		 */
		if ((cmdp = list_head(&slot->s_abortlist)) != NULL) {
			list_remove(&slot->s_abortlist, cmdp);
			mutex_exit(&slot->s_evlock);
			/*
			 * EOK used here, to avoid clobbering previous
			 * error code.
			 */
			sda_cmd_notify(cmdp, SDA_CMDF_BUSY | SDA_CMDF_DAT,
			    SDA_EOK);
			continue;
		}

		if (slot->s_detach) {
			/* Parent is detaching the slot, bail out. */
			break;
		}

		if ((slot->s_suspend) && (slot->s_xfrp == NULL)) {
			/*
			 * Host wants to suspend, but don't do it if
			 * we have a transfer outstanding.
			 */
			break;
		}

		if (slot->s_detect) {
			slot->s_detect = B_FALSE;
			mutex_exit(&slot->s_evlock);

			sda_slot_handle_detect(slot);
			continue;
		}

		if (slot->s_xfrdone) {
			sda_err_t	errno;

			errno = slot->s_errno;
			slot->s_errno = SDA_EOK;
			slot->s_xfrdone = B_FALSE;
			mutex_exit(&slot->s_evlock);

			sda_slot_handle_transfer(slot, errno);
			continue;
		}

		if (slot->s_fault != SDA_FAULT_NONE) {
			sda_fault_t	fault;

			fault = slot->s_fault;
			slot->s_fault = SDA_FAULT_NONE;
			mutex_exit(&slot->s_evlock);

			sda_slot_handle_fault(slot, fault);
			continue;
		}

		if (slot->s_reap) {
			/*
			 * Do not sleep while holding the evlock.  If this
			 * fails, we'll just try again the next cycle.
			 */
			(void) ddi_taskq_dispatch(slot->s_hp_tq,
			    sda_nexus_reap, slot, DDI_NOSLEEP);
		}

		if ((slot->s_xfrp != NULL) && (gethrtime() > slot->s_xfrtmo)) {
			/*
			 * The device stalled processing the data request.
			 * At this point, we really have no choice but to
			 * nuke the request, and flag a fault.
			 */
			mutex_exit(&slot->s_evlock);
			sda_slot_handle_transfer(slot, SDA_ETIME);
			sda_slot_fault(slot, SDA_FAULT_TIMEOUT);
			continue;
		}

		/*
		 * If the slot has suspended, then we can't process
		 * any new commands yet.
		 */
		if ((slot->s_suspend) || (!slot->s_wake)) {

			/*
			 * We use a timed wait if we are waiting for a
			 * data transfer to complete, or if we might
			 * need to reap child nodes.  Otherwise we
			 * avoid the timed wait to avoid waking CPU
			 * (power savings.)
			 */

			if ((slot->s_xfrp != NULL) || (slot->s_reap)) {
				/* Wait 3 sec (reap attempts). */
				(void) cv_reltimedwait(&slot->s_evcv,
				    &slot->s_evlock, drv_usectohz(3000000),
				    TR_CLOCK_TICK);
			} else {
				(void) cv_wait(&slot->s_evcv, &slot->s_evlock);
			}

			mutex_exit(&slot->s_evlock);
			continue;
		}

		slot->s_wake = B_FALSE;

		/*
		 * Possibly reap child nodes.
		 */
		if (slot->s_reap) {
			slot->s_reap = B_FALSE;
			mutex_exit(&slot->s_evlock);
			sda_nexus_reap(slot);
		} else {
			mutex_exit(&slot->s_evlock);
		}

		/*
		 * We're awake now, so look for work to do.  First
		 * acquire access to the slot.
		 */
		sda_slot_enter(slot);


		/*
		 * If no more commands to process, go back to sleep.
		 */
		if ((cmdp = list_head(&slot->s_cmdlist)) == NULL) {
			sda_slot_exit(slot);
			continue;
		}

		/*
		 * If the current command is not an initialization
		 * command, but we are initializing, go back to sleep.
		 * (This happens potentially during a card reset or
		 * suspend/resume cycle, where the card has not been
		 * removed, but a reset is in progress.)
		 */
		if (slot->s_init && !(cmdp->sc_flags & SDA_CMDF_INIT)) {
			sda_slot_exit(slot);
			continue;
		}

		datline = ((cmdp->sc_flags & SDA_CMDF_DAT) != 0);

		if (datline) {
			/*
			 * If the current command has a data phase
			 * while a transfer is in progress, then go
			 * back to sleep.
			 */
			if (slot->s_xfrp != NULL) {
				sda_slot_exit(slot);
				continue;
			}

			/*
			 * Note that APP_CMD doesn't have a data phase,
			 * although the associated ACMD might.
			 */
			if (cmdp->sc_index != CMD_APP_CMD) {
				slot->s_xfrp = cmdp;
				/*
				 * All commands should complete in
				 * less than 5 seconds.  The worst
				 * case is actually somewhere around 4
				 * seconds, but that is when the clock
				 * is only 100 kHz.
				 */
				slot->s_xfrtmo = gethrtime() +
				    5000000000ULL;
				(void) sda_setprop(slot, SDA_PROP_LED, 1);
			}
		}

		/*
		 * We're committed to dispatching this command now,
		 * so remove it from the list.
		 */
		list_remove(&slot->s_cmdlist, cmdp);

		/*
		 * There could be more commands after this one, so we
		 * mark ourself so we stay awake for another cycle.
		 */
		sda_slot_wakeup(slot);

		/*
		 * Submit the command.  Note that we are holding the
		 * slot lock here, so it is critical that the caller
		 * *not* call back up into the framework.  The caller
		 * must break context.  But doing it this way prevents
		 * a critical race on card removal.
		 *
		 * Note that we don't resubmit memory to the device if
		 * it isn't flagged as ready (e.g. if the wrong device
		 * was inserted!)
		 */
		if ((!slot->s_ready) && (cmdp->sc_flags & SDA_CMDF_MEM)) {
			rv = SDA_ENODEV;
			if (!slot->s_warn) {
				sda_slot_err(slot,
				    "Device removed while in use.  "
				    "Please reinsert!");
				slot->s_warn = B_TRUE;
			}
		} else {
			rv = slot->s_ops.so_cmd(slot->s_prv, cmdp);
		}
		if (rv == SDA_EOK)
			rv = sda_slot_check_response(cmdp);

		if (rv == SDA_EOK) {
			/*
			 * If APP_CMD completed properly, then
			 * resubmit with ACMD index.  Note wake was
			 * already set above.
			 */
			if (cmdp->sc_index == CMD_APP_CMD) {
				if ((cmdp->sc_response[0] & R1_APP_CMD) == 0) {
					sda_slot_log(slot, "APP_CMD not set!");
				}
				sda_cmd_resubmit_acmd(slot, cmdp);
				sda_slot_exit(slot);

				continue;
			}

		} else if (datline) {
			/*
			 * If an error occurred and we were expecting
			 * a transfer phase, we have to clean up.
			 */
			(void) sda_setprop(slot, SDA_PROP_LED, 0);
			slot->s_xfrp = NULL;
			slot->s_xfrtmo = 0;

			/*
			 * And notify any waiter.
			 */
			sda_slot_exit(slot);
			sda_cmd_notify(cmdp, SDA_CMDF_BUSY | SDA_CMDF_DAT, rv);
			continue;
		}

		/*
		 * Wake any waiter.
		 */
		sda_slot_exit(slot);
		sda_cmd_notify(cmdp, SDA_CMDF_BUSY, rv);
	}

	mutex_exit(&slot->s_evlock);
}