 e44ba033c5
			
		
	
	
	e44ba033c5
	
	
	
		
			
			Many stupid corrections of duplicated includes based on the output of scripts/checkincludes.pl. Signed-off-by: Vitaliy Ivanov <vitalivanov@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
		
			
				
	
	
		
			348 lines
		
	
	
	
		
			9.5 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			348 lines
		
	
	
	
		
			9.5 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * OTG Finite State Machine from OTG spec
 | |
|  *
 | |
|  * Copyright (C) 2007,2008 Freescale Semiconductor, Inc.
 | |
|  *
 | |
|  * Author:	Li Yang <LeoLi@freescale.com>
 | |
|  *		Jerry Huang <Chang-Ming.Huang@freescale.com>
 | |
|  *
 | |
|  * This program is free software; you can redistribute  it and/or modify it
 | |
|  * under  the terms of  the GNU General  Public License as published by the
 | |
|  * Free Software Foundation;  either version 2 of the  License, or (at your
 | |
|  * option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful, but
 | |
|  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the  GNU General Public License along
 | |
|  * with this program; if not, write  to the Free Software Foundation, Inc.,
 | |
|  * 675 Mass Ave, Cambridge, MA 02139, USA.
 | |
|  */
 | |
| 
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/types.h>
 | |
| #include <linux/spinlock.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/usb.h>
 | |
| #include <linux/usb/gadget.h>
 | |
| #include <linux/usb/otg.h>
 | |
| 
 | |
| #include "otg_fsm.h"
 | |
| 
 | |
| /* Change USB protocol when there is a protocol change */
 | |
| static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (fsm->protocol != protocol) {
 | |
| 		VDBG("Changing role fsm->protocol= %d; new protocol= %d\n",
 | |
| 			fsm->protocol, protocol);
 | |
| 		/* stop old protocol */
 | |
| 		if (fsm->protocol == PROTO_HOST)
 | |
| 			ret = fsm->ops->start_host(fsm, 0);
 | |
| 		else if (fsm->protocol == PROTO_GADGET)
 | |
| 			ret = fsm->ops->start_gadget(fsm, 0);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 
 | |
| 		/* start new protocol */
 | |
| 		if (protocol == PROTO_HOST)
 | |
| 			ret = fsm->ops->start_host(fsm, 1);
 | |
| 		else if (protocol == PROTO_GADGET)
 | |
| 			ret = fsm->ops->start_gadget(fsm, 1);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 
 | |
| 		fsm->protocol = protocol;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int state_changed;
 | |
| 
 | |
| /* 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;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Called when entering a state */
 | |
| int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
 | |
| {
 | |
| 	state_changed = 1;
 | |
| 	if (fsm->transceiver->state == new_state)
 | |
| 		return 0;
 | |
| 	VDBG("Set state: %s\n", otg_state_string(new_state));
 | |
| 	otg_leave_state(fsm, fsm->transceiver->state);
 | |
| 	switch (new_state) {
 | |
| 	case OTG_STATE_B_IDLE:
 | |
| 		otg_drv_vbus(fsm, 0);
 | |
| 		otg_chrg_vbus(fsm, 0);
 | |
| 		otg_loc_conn(fsm, 0);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_UNDEF);
 | |
| 		otg_add_timer(fsm, b_se0_srp_tmr);
 | |
| 		break;
 | |
| 	case OTG_STATE_B_SRP_INIT:
 | |
| 		otg_start_pulse(fsm);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_UNDEF);
 | |
| 		otg_add_timer(fsm, b_srp_fail_tmr);
 | |
| 		break;
 | |
| 	case OTG_STATE_B_PERIPHERAL:
 | |
| 		otg_chrg_vbus(fsm, 0);
 | |
| 		otg_loc_conn(fsm, 1);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_GADGET);
 | |
| 		break;
 | |
| 	case OTG_STATE_B_WAIT_ACON:
 | |
| 		otg_chrg_vbus(fsm, 0);
 | |
| 		otg_loc_conn(fsm, 0);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_HOST);
 | |
| 		otg_add_timer(fsm, b_ase0_brst_tmr);
 | |
| 		fsm->a_bus_suspend = 0;
 | |
| 		break;
 | |
| 	case OTG_STATE_B_HOST:
 | |
| 		otg_chrg_vbus(fsm, 0);
 | |
| 		otg_loc_conn(fsm, 0);
 | |
| 		otg_loc_sof(fsm, 1);
 | |
| 		otg_set_protocol(fsm, PROTO_HOST);
 | |
| 		usb_bus_start_enum(fsm->transceiver->host,
 | |
| 				fsm->transceiver->host->otg_port);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_IDLE:
 | |
| 		otg_drv_vbus(fsm, 0);
 | |
| 		otg_chrg_vbus(fsm, 0);
 | |
| 		otg_loc_conn(fsm, 0);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_HOST);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_WAIT_VRISE:
 | |
| 		otg_drv_vbus(fsm, 1);
 | |
| 		otg_loc_conn(fsm, 0);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_HOST);
 | |
| 		otg_add_timer(fsm, a_wait_vrise_tmr);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_WAIT_BCON:
 | |
| 		otg_drv_vbus(fsm, 1);
 | |
| 		otg_loc_conn(fsm, 0);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_HOST);
 | |
| 		otg_add_timer(fsm, a_wait_bcon_tmr);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_HOST:
 | |
| 		otg_drv_vbus(fsm, 1);
 | |
| 		otg_loc_conn(fsm, 0);
 | |
| 		otg_loc_sof(fsm, 1);
 | |
| 		otg_set_protocol(fsm, PROTO_HOST);
 | |
| 		/*
 | |
| 		 * When HNP is triggered while a_bus_req = 0, a_host will
 | |
| 		 * suspend too fast to complete a_set_b_hnp_en
 | |
| 		 */
 | |
| 		if (!fsm->a_bus_req || fsm->a_suspend_req)
 | |
| 			otg_add_timer(fsm, a_wait_enum_tmr);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_SUSPEND:
 | |
| 		otg_drv_vbus(fsm, 1);
 | |
| 		otg_loc_conn(fsm, 0);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_HOST);
 | |
| 		otg_add_timer(fsm, a_aidl_bdis_tmr);
 | |
| 
 | |
| 		break;
 | |
| 	case OTG_STATE_A_PERIPHERAL:
 | |
| 		otg_loc_conn(fsm, 1);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_GADGET);
 | |
| 		otg_drv_vbus(fsm, 1);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_WAIT_VFALL:
 | |
| 		otg_drv_vbus(fsm, 0);
 | |
| 		otg_loc_conn(fsm, 0);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_HOST);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_VBUS_ERR:
 | |
| 		otg_drv_vbus(fsm, 0);
 | |
| 		otg_loc_conn(fsm, 0);
 | |
| 		otg_loc_sof(fsm, 0);
 | |
| 		otg_set_protocol(fsm, PROTO_UNDEF);
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	fsm->transceiver->state = new_state;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* State change judgement */
 | |
| int otg_statemachine(struct otg_fsm *fsm)
 | |
| {
 | |
| 	enum usb_otg_state state;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	spin_lock_irqsave(&fsm->lock, flags);
 | |
| 
 | |
| 	state = fsm->transceiver->state;
 | |
| 	state_changed = 0;
 | |
| 	/* State machine state change judgement */
 | |
| 
 | |
| 	switch (state) {
 | |
| 	case OTG_STATE_UNDEFINED:
 | |
| 		VDBG("fsm->id = %d\n", fsm->id);
 | |
| 		if (fsm->id)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_IDLE);
 | |
| 		else
 | |
| 			otg_set_state(fsm, OTG_STATE_A_IDLE);
 | |
| 		break;
 | |
| 	case OTG_STATE_B_IDLE:
 | |
| 		if (!fsm->id)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_IDLE);
 | |
| 		else if (fsm->b_sess_vld && fsm->transceiver->gadget)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
 | |
| 		else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_SRP_INIT);
 | |
| 		break;
 | |
| 	case OTG_STATE_B_SRP_INIT:
 | |
| 		if (!fsm->id || fsm->b_srp_done)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_IDLE);
 | |
| 		break;
 | |
| 	case OTG_STATE_B_PERIPHERAL:
 | |
| 		if (!fsm->id || !fsm->b_sess_vld)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_IDLE);
 | |
| 		else if (fsm->b_bus_req && fsm->transceiver->
 | |
| 				gadget->b_hnp_enable && fsm->a_bus_suspend)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_WAIT_ACON);
 | |
| 		break;
 | |
| 	case OTG_STATE_B_WAIT_ACON:
 | |
| 		if (fsm->a_conn)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_HOST);
 | |
| 		else if (!fsm->id || !fsm->b_sess_vld)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_IDLE);
 | |
| 		else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) {
 | |
| 			fsm->b_ase0_brst_tmout = 0;
 | |
| 			otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
 | |
| 		}
 | |
| 		break;
 | |
| 	case OTG_STATE_B_HOST:
 | |
| 		if (!fsm->id || !fsm->b_sess_vld)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_IDLE);
 | |
| 		else if (!fsm->b_bus_req || !fsm->a_conn)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_IDLE:
 | |
| 		if (fsm->id)
 | |
| 			otg_set_state(fsm, OTG_STATE_B_IDLE);
 | |
| 		else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det))
 | |
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_WAIT_VRISE:
 | |
| 		if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld ||
 | |
| 				fsm->a_wait_vrise_tmout) {
 | |
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
 | |
| 		}
 | |
| 		break;
 | |
| 	case OTG_STATE_A_WAIT_BCON:
 | |
| 		if (!fsm->a_vbus_vld)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
 | |
| 		else if (fsm->b_conn)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_HOST);
 | |
| 		else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_HOST:
 | |
| 		if ((!fsm->a_bus_req || fsm->a_suspend_req) &&
 | |
| 				fsm->transceiver->host->b_hnp_enable)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_SUSPEND);
 | |
| 		else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
 | |
| 		else if (!fsm->a_vbus_vld)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_SUSPEND:
 | |
| 		if (!fsm->b_conn && fsm->transceiver->host->b_hnp_enable)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_PERIPHERAL);
 | |
| 		else if (!fsm->b_conn && !fsm->transceiver->host->b_hnp_enable)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
 | |
| 		else if (fsm->a_bus_req || fsm->b_bus_resume)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_HOST);
 | |
| 		else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
 | |
| 		else if (!fsm->a_vbus_vld)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_PERIPHERAL:
 | |
| 		if (fsm->id || fsm->a_bus_drop)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
 | |
| 		else if (fsm->b_bus_suspend)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
 | |
| 		else if (!fsm->a_vbus_vld)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_WAIT_VFALL:
 | |
| 		if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld &&
 | |
| 					!fsm->b_conn))
 | |
| 			otg_set_state(fsm, OTG_STATE_A_IDLE);
 | |
| 		break;
 | |
| 	case OTG_STATE_A_VBUS_ERR:
 | |
| 		if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err)
 | |
| 			otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 	spin_unlock_irqrestore(&fsm->lock, flags);
 | |
| 
 | |
| 	VDBG("quit statemachine, changed = %d\n", state_changed);
 | |
| 	return state_changed;
 | |
| }
 |