Esempio n. 1
0
/* Called when leaving a state.  Do state clean up jobs here */
static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
{
	switch (old_state) {
	case OTG_STATE_B_IDLE:
		otg_del_timer(fsm, B_SE0_SRP);
		fsm->b_se0_srp = 0;
		fsm->adp_sns = 0;
		fsm->adp_prb = 0;
		break;
	case OTG_STATE_B_SRP_INIT:
		fsm->data_pulse = 0;
		fsm->b_srp_done = 0;
		break;
	case OTG_STATE_B_PERIPHERAL:
		if (fsm->otg->gadget)
			fsm->otg->gadget->host_request_flag = 0;
		break;
	case OTG_STATE_B_WAIT_ACON:
		otg_del_timer(fsm, B_ASE0_BRST);
		fsm->b_ase0_brst_tmout = 0;
		break;
	case OTG_STATE_B_HOST:
		break;
	case OTG_STATE_A_IDLE:
		fsm->adp_prb = 0;
		break;
	case OTG_STATE_A_WAIT_VRISE:
		otg_del_timer(fsm, A_WAIT_VRISE);
		fsm->a_wait_vrise_tmout = 0;
		break;
	case OTG_STATE_A_WAIT_BCON:
		otg_del_timer(fsm, A_WAIT_BCON);
		fsm->a_wait_bcon_tmout = 0;
		break;
	case OTG_STATE_A_HOST:
		otg_del_timer(fsm, A_WAIT_ENUM);
		break;
	case OTG_STATE_A_SUSPEND:
		otg_del_timer(fsm, A_AIDL_BDIS);
		fsm->a_aidl_bdis_tmout = 0;
		fsm->a_suspend_req_inf = 0;
		break;
	case OTG_STATE_A_PERIPHERAL:
		otg_del_timer(fsm, A_BIDL_ADIS);
		fsm->a_bidl_adis_tmout = 0;
		if (fsm->otg->gadget)
			fsm->otg->gadget->host_request_flag = 0;
		break;
	case OTG_STATE_A_WAIT_VFALL:
		otg_del_timer(fsm, A_WAIT_VFALL);
		fsm->a_wait_vfall_tmout = 0;
		otg_del_timer(fsm, A_WAIT_VRISE);
		break;
	case OTG_STATE_A_VBUS_ERR:
		break;
	default:
		break;
	}
}
static void ci_handle_vbus_glitch(struct ci_hdrc *ci)
{
	bool valid_vbus_change = false;

	if (hw_read_otgsc(ci, OTGSC_BSV)) {
		if (!ci_is_vbus_glitch(ci)) {
			if (ci_otg_is_fsm_mode(ci)) {
				ci->fsm.b_sess_vld = 1;
				ci->fsm.b_ssend_srp = 0;
				otg_del_timer(&ci->fsm, B_SSEND_SRP);
				otg_del_timer(&ci->fsm, B_SRP_FAIL);
			}
			valid_vbus_change = true;
		}
	} else {
		if (ci->vbus_active && !ci_otg_is_fsm_mode(ci))
			valid_vbus_change = true;
	}

	if (valid_vbus_change) {
		ci->b_sess_valid_event = true;
		ci_otg_queue_work(ci);
	}
}
Esempio n. 3
0
/* Called when leaving a state.  Do state clean up jobs here */
void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
{
	switch (old_state) {
	case OTG_STATE_B_IDLE:
		otg_del_timer(fsm, b_se0_srp_tmr);
		fsm->b_se0_srp = 0;
		break;
	case OTG_STATE_B_SRP_INIT:
		fsm->b_srp_done = 0;
		break;
	case OTG_STATE_B_PERIPHERAL:
		break;
	case OTG_STATE_B_WAIT_ACON:
		otg_del_timer(fsm, b_ase0_brst_tmr);
		fsm->b_ase0_brst_tmout = 0;
		break;
	case OTG_STATE_B_HOST:
		break;
	case OTG_STATE_A_IDLE:
		break;
	case OTG_STATE_A_WAIT_VRISE:
		otg_del_timer(fsm, a_wait_vrise_tmr);
		fsm->a_wait_vrise_tmout = 0;
		break;
	case OTG_STATE_A_WAIT_BCON:
		otg_del_timer(fsm, a_wait_bcon_tmr);
		fsm->a_wait_bcon_tmout = 0;
		break;
	case OTG_STATE_A_HOST:
		otg_del_timer(fsm, a_wait_enum_tmr);
		break;
	case OTG_STATE_A_SUSPEND:
		otg_del_timer(fsm, a_aidl_bdis_tmr);
		fsm->a_aidl_bdis_tmout = 0;
		fsm->a_suspend_req = 0;
		break;
	case OTG_STATE_A_PERIPHERAL:
		break;
	case OTG_STATE_A_WAIT_VFALL:
		otg_del_timer(fsm, a_wait_vrise_tmr);
		break;
	case OTG_STATE_A_VBUS_ERR:
		break;
	default:
		break;
	}
}
static int otg_handle_role_switch(struct otg_fsm *fsm, struct usb_device *udev)
{
	int err;
	enum usb_otg_state state = fsm->otg->state;

	if (state == OTG_STATE_A_HOST) {
		/* Set b_hnp_enable */
		if (!fsm->a_set_b_hnp_en) {
			err = usb_control_msg(udev,
				usb_sndctrlpipe(udev, 0),
				USB_REQ_SET_FEATURE, 0,
				USB_DEVICE_B_HNP_ENABLE,
				0, NULL, 0,
				USB_CTRL_SET_TIMEOUT);
			if (err < 0) {
				/* Continue polling */
				otg_add_timer(fsm, HNP_POLLING);
				return 0;
			} else {
				fsm->a_set_b_hnp_en = 1;
			}
		}
		fsm->a_bus_req = 0;
		if (fsm->tst_maint) {
			fsm->tst_maint = 0;
			fsm->otg_vbus_off = 0;
			otg_del_timer(fsm, A_TST_MAINT);
		}
		return HOST_REQUEST_FLAG;
	} else if (state == OTG_STATE_B_HOST) {
		fsm->b_bus_req = 0;
		return HOST_REQUEST_FLAG;
	}

	return -EINVAL;
}
/*
 * Called by host to poll peripheral if it wants to be host
 * Return value:
 * - host request flag(1) if the device wants to be host,
 * - host request flag(0) if the device keeps peripheral role,
 * - otherwise, error code.
 */
int otg_hnp_polling(struct otg_fsm *fsm)
{
	struct usb_device *udev;
	int retval;
	enum usb_otg_state state = fsm->otg->state;
	struct usb_otg_descriptor *desc = NULL;

	if ((state != OTG_STATE_A_HOST || !fsm->b_hnp_enable) &&
					state != OTG_STATE_B_HOST)
		return -EINVAL;

	udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
	if (!udev) {
		dev_err(fsm->otg->host->controller,
			"no usb dev connected, can't start HNP polling\n");
		return -ENODEV;
	}

	if (udev->state != USB_STATE_CONFIGURED) {
		dev_dbg(&udev->dev, "the B dev is not resumed!\n");
		otg_add_timer(fsm, HNP_POLLING);
		return -EPERM;
	}

	/*
	 * Legacy otg test device does not support HNP polling,
	 * start HNP directly for legacy otg test device.
	 */
	if (fsm->tst_maint &&
		(__usb_get_extra_descriptor(udev->rawdescriptors[0],
		le16_to_cpu(udev->config[0].desc.wTotalLength),
				USB_DT_OTG, (void **) &desc) == 0)) {
		/* shorter bLength of OTG 1.3 or earlier */
		if (desc->bLength < 5) {
			fsm->a_bus_req = 0;
			fsm->tst_maint = 0;
			otg_del_timer(fsm, A_TST_MAINT);
			return HOST_REQUEST_FLAG;
		}
	}

	fsm->host_req_flag = 0;
	/* Get host request flag from connected USB device */
	retval = usb_control_msg(udev,
				usb_rcvctrlpipe(udev, 0),
				USB_REQ_GET_STATUS,
				USB_DIR_IN | USB_RECIP_DEVICE,
				0,
				OTG_STS_SELECTOR,
				&fsm->host_req_flag,
				1,
				USB_CTRL_GET_TIMEOUT);
	if (retval == 1) {
		if (fsm->host_req_flag == HOST_REQUEST_FLAG) {
			retval = otg_handle_role_switch(fsm, udev);
		} else if (fsm->host_req_flag == 0) {
			/* Continue polling */
			otg_add_timer(fsm, HNP_POLLING);
			retval = 0;
		} else {
			dev_err(&udev->dev, "host request flag is invalid\n");
			retval = -EINVAL;
		}
	} else {
		dev_warn(&udev->dev, "Get one byte OTG status failed\n");
		retval = -EIO;
	}
	return retval;
}