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); }
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); }
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); } }
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); }
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); }