/* * Per-port thread helper. This helper thread is responsible for * atomically retrieving and clearing the signal mask and calling * the machine-independant driver core. * * MPSAFE */ static void ahci_port_thread(void *arg) { struct ahci_port *ap = arg; int mask; /* * Sets us up as an interrupt support thread, meaning we are * given a higher priority and we can preempt normal threads. */ lwkt_set_interrupt_support_thread(); /* * The helper thread is responsible for the initial port init, * so all the ports can be inited in parallel. * * We also run the state machine which should do all probes. * Since CAM is not attached yet we will not get out-of-order * SCSI attachments. */ ahci_os_lock_port(ap); ahci_port_init(ap); atomic_clear_int(&ap->ap_signal, AP_SIGF_THREAD_SYNC); wakeup(&ap->ap_signal); ahci_port_state_machine(ap, 1); ahci_os_unlock_port(ap); atomic_clear_int(&ap->ap_signal, AP_SIGF_INIT); wakeup(&ap->ap_signal); /* * Then loop on the helper core. */ mask = ap->ap_signal; while ((mask & AP_SIGF_STOP) == 0) { ahci_port_thread_core(ap, mask); lockmgr(&ap->ap_sig_lock, LK_EXCLUSIVE); if (ap->ap_signal == 0) { lksleep(&ap->ap_thread, &ap->ap_sig_lock, 0, "ahport", 0); } mask = ap->ap_signal; atomic_clear_int(&ap->ap_signal, mask); lockmgr(&ap->ap_sig_lock, LK_RELEASE); } ap->ap_thread = NULL; }
/* * Action function - dispatch command */ static void ahci_xpt_action(struct cam_sim *sim, union ccb *ccb) { struct ahci_port *ap; struct ata_port *at, *atx; struct ccb_hdr *ccbh; int unit; /* XXX lock */ ap = cam_sim_softc(sim); atx = NULL; KKASSERT(ap != NULL); ccbh = &ccb->ccb_h; unit = cam_sim_unit(sim); /* * Early failure checks. These checks do not apply to XPT_PATH_INQ, * otherwise the bus rescan will not remove the dead devices when * unplugging a PM. * * For non-wildcards we have one target (0) and one lun (0), * unless we have a port multiplier. * * A wildcard target indicates only the general bus is being * probed. * * Calculate at and atx. at is always non-NULL. atx is only * NULL for direct-attached devices. It will be non-NULL for * devices behind a port multiplier. * * XXX What do we do with a LUN wildcard? */ if (ccbh->target_id != CAM_TARGET_WILDCARD && ccbh->func_code != XPT_PATH_INQ) { if (ap->ap_type == ATA_PORT_T_NONE) { ccbh->status = CAM_DEV_NOT_THERE; xpt_done(ccb); return; } if (ccbh->target_id < 0 || ccbh->target_id >= ap->ap_pmcount) { ccbh->status = CAM_DEV_NOT_THERE; xpt_done(ccb); return; } at = ap->ap_ata[ccbh->target_id]; if (ap->ap_type == ATA_PORT_T_PM) atx = at; if (ccbh->target_lun != CAM_LUN_WILDCARD && ccbh->target_lun) { ccbh->status = CAM_DEV_NOT_THERE; xpt_done(ccb); return; } } else { at = ap->ap_ata[0]; } /* * Switch on the meta XPT command */ switch(ccbh->func_code) { case XPT_ENG_EXEC: /* * This routine is called after a port multiplier has been * probed. */ ccbh->status = CAM_REQ_CMP; ahci_os_lock_port(ap); ahci_port_state_machine(ap, 0); ahci_os_unlock_port(ap); xpt_done(ccb); ahci_xpt_rescan(ap); break; case XPT_PATH_INQ: /* * This command always succeeds, otherwise the bus scan * will not detach dead devices. */ ccb->cpi.version_num = 1; ccb->cpi.hba_inquiry = 0; ccb->cpi.target_sprt = 0; ccb->cpi.hba_misc = PIM_SEQSCAN; ccb->cpi.hba_eng_cnt = 0; bzero(ccb->cpi.vuhba_flags, sizeof(ccb->cpi.vuhba_flags)); ccb->cpi.max_target = AHCI_MAX_PMPORTS - 1; ccb->cpi.max_lun = 0; ccb->cpi.async_flags = 0; ccb->cpi.hpath_id = 0; ccb->cpi.initiator_id = AHCI_MAX_PMPORTS - 1; ccb->cpi.unit_number = cam_sim_unit(sim); ccb->cpi.bus_id = cam_sim_bus(sim); ccb->cpi.base_transfer_speed = 150000; ccb->cpi.transport = XPORT_SATA; ccb->cpi.transport_version = 1; ccb->cpi.protocol = PROTO_SCSI; ccb->cpi.protocol_version = SCSI_REV_2; ccbh->status = CAM_REQ_CMP; if (ccbh->target_id == CAM_TARGET_WILDCARD) { ahci_os_lock_port(ap); ahci_port_state_machine(ap, 0); ahci_os_unlock_port(ap); } else { switch(ahci_pread(ap, AHCI_PREG_SSTS) & AHCI_PREG_SSTS_SPD) { case AHCI_PREG_SSTS_SPD_GEN1: ccb->cpi.base_transfer_speed = 150000; break; case AHCI_PREG_SSTS_SPD_GEN2: ccb->cpi.base_transfer_speed = 300000; break; case AHCI_PREG_SSTS_SPD_GEN3: ccb->cpi.base_transfer_speed = 600000; break; default: /* unknown */ ccb->cpi.base_transfer_speed = 1000; break; } #if 0 if (ap->ap_type == ATA_PORT_T_NONE) ccbh->status = CAM_DEV_NOT_THERE; #endif } xpt_done(ccb); break; case XPT_RESET_DEV: ahci_os_lock_port(ap); if (ap->ap_type == ATA_PORT_T_NONE) { ccbh->status = CAM_DEV_NOT_THERE; } else { ahci_port_reset(ap, atx, 0); ccbh->status = CAM_REQ_CMP; } ahci_os_unlock_port(ap); xpt_done(ccb); break; case XPT_RESET_BUS: ahci_os_lock_port(ap); ahci_port_reset(ap, NULL, 1); ahci_os_unlock_port(ap); ccbh->status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: ccbh->status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; case XPT_GET_TRAN_SETTINGS: ccb->cts.protocol = PROTO_SCSI; ccb->cts.protocol_version = SCSI_REV_2; ccb->cts.transport = XPORT_SATA; ccb->cts.transport_version = XPORT_VERSION_UNSPECIFIED; ccb->cts.proto_specific.valid = 0; ccb->cts.xport_specific.valid = 0; ccbh->status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_CALC_GEOMETRY: cam_calc_geometry(&ccb->ccg, 1); xpt_done(ccb); break; case XPT_SCSI_IO: /* * Our parallel startup code might have only probed through * to the IDENT, so do the last step if necessary. */ if (at->at_probe == ATA_PROBE_NEED_IDENT) ahci_cam_probe(ap, atx); if (at->at_probe != ATA_PROBE_GOOD) { ccbh->status = CAM_DEV_NOT_THERE; xpt_done(ccb); break; } switch(at->at_type) { case ATA_PORT_T_DISK: ahci_xpt_scsi_disk_io(ap, atx, ccb); break; case ATA_PORT_T_ATAPI: ahci_xpt_scsi_atapi_io(ap, atx, ccb); break; default: ccbh->status = CAM_REQ_INVALID; xpt_done(ccb); break; } break; case XPT_TRIM: { scsi_cdb_t cdb; struct ccb_scsiio *csio; csio = &ccb->csio; cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ? csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes); cdb->generic.opcode = TRIM; ahci_xpt_scsi_disk_io(ap, atx, ccb); break; } default: ccbh->status = CAM_REQ_INVALID; xpt_done(ccb); break; } }