Example #1
0
int
ahci_cam_attach(struct ahci_port *ap)
{
	struct cam_devq *devq;
	struct cam_sim *sim;
	int error;
	int unit;

	/*
	 * We want at least one ccb to be available for error processing
	 * so don't let CAM use more then ncmds - 1.
	 */
	unit = device_get_unit(ap->ap_sc->sc_dev);
	if (ap->ap_sc->sc_ncmds > 1)
		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds - 1);
	else
		devq = cam_simq_alloc(ap->ap_sc->sc_ncmds);
	if (devq == NULL) {
		return (ENOMEM);
	}

	/*
	 * Give the devq enough room to run with 32 max_dev_transactions,
	 * but set the overall max tags to 1 until NCQ is negotiated.
	 */
	sim = cam_sim_alloc(ahci_xpt_action, ahci_xpt_poll, "ahci",
			   (void *)ap, unit, &ap->ap_sim_lock,
			   32, 1, devq);
	cam_simq_release(devq);
	if (sim == NULL) {
		return (ENOMEM);
	}
	ap->ap_sim = sim;
	ahci_os_unlock_port(ap);
	lockmgr(&ap->ap_sim_lock, LK_EXCLUSIVE);
	error = xpt_bus_register(ap->ap_sim, ap->ap_num);
	lockmgr(&ap->ap_sim_lock, LK_RELEASE);
	ahci_os_lock_port(ap);
	if (error != CAM_SUCCESS) {
		ahci_cam_detach(ap);
		return (EINVAL);
	}
	ap->ap_flags |= AP_F_BUS_REGISTERED;

	if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
		error = ahci_cam_probe(ap, NULL);
	else
		error = 0;
	if (error) {
		ahci_cam_detach(ap);
		return (EIO);
	}
	ap->ap_flags |= AP_F_CAM_ATTACHED;

	return(0);
}
Example #2
0
/*
 * Poll function.
 *
 * Generally this function gets called heavily when interrupts might be
 * non-operational, during a halt/reboot or panic.
 */
static
void
ahci_xpt_poll(struct cam_sim *sim)
{
	struct ahci_port *ap;

	ap = cam_sim_softc(sim);
	crit_enter();
	ahci_os_lock_port(ap);
	ahci_port_intr(ap, 1);
	ahci_os_unlock_port(ap);
	crit_exit();
}
Example #3
0
/*
 * 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;
}
Example #4
0
/*
 * 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;
	}
}