1
0
Fork 0
remarkable-linux/drivers/misc/rm-otgcontrol/otgcontrol_fsm.c

1448 lines
36 KiB
C

#include "otgcontrol_fsm.h"
#include "otgcontrol_onewire.h"
#include "otgcontrol_charging_ctrl.h"
#include "otgcontrol_dr_mode.h"
#include <linux/rm-otgcontrol.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/power_supply.h>
#include <linux/delay.h>
/******************************************************************************
* INTERNAL FUNCTIONS
*****************************************************************************/
static int otgcontrol_reset_fsm(struct rm_otgcontrol_data *otgc_data);
static int otgcontrol_handle_state_MANUAL_CONTROL(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param);
static int otgcontrol_handle_state_ONEWIRE_AUTH_NOT_CONNECTED(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param);
static int otgcontrol_handle_state_ONEWIRE_AUTH_WAIT_HANDSHAKE_RESPONS(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param);
static int otgcontrol_handle_state_ONEWIRE_AUTH_DEVICE_CONNECTED(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param);
static int otgcontrol_handle_state_USB_NO_AUTH_WAITING_CHALLENGE_RESPONSE(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param);
static int otgcontrol_handle_state_USB_NO_AUTH_NOT_CONNECTED(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param);
static int otgcontrol_handle_state_USB_NO_AUTH_DEVICE_CONNECTED(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param);
static int otgcontrol_handle_state_HOST_CONNECTED(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param);
static int otgcontrol_do_controller_mode_change_procedure(
struct rm_otgcontrol_data *otgc_data,
void *param);
static int otgcontrol_do_authenticate_calling_application(
struct rm_otgcontrol_data *otgc_data,
int mode_requested);
static int otgcontrol_do_verify_application_challenge_reply(
struct rm_otgcontrol_data *otgc_data,
void *param);
static int otgcontrol_do_verify_device_challenge_reply(
struct rm_otgcontrol_data *otgc_data,
void *param);
static int otgcontrol_do_set_controlmode(
struct rm_otgcontrol_data *otgc_data,
int mode);
static int otgcontrol_do_device_connected_procedure(
struct rm_otgcontrol_data *otgc_data,
bool authentication_required);
static int otgcontrol_do_device_disconnected_procedure(
struct rm_otgcontrol_data *otgc_data);
static int otgcontrol_do_start_onewire_authentication(
struct rm_otgcontrol_data *otgc_data);
static const char *otgcontrol_controllerstate_name(int state)
{
switch(state)
{
case OTG1_STATE__MANUAL_CONTROL:
return "OTG1_STATE__MANUAL_CONTROL";
break;
case OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED:
return "OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED";
break;
case OTG1_STATE__ONEWIRE_AUTH_WAIT_HANDSHAKE_RESPONS:
return "OTG1_STATE__ONEWIRE_AUTH_WAIT_HANDSHAKE_RESPONS";
break;
case OTG1_STATE__ONEWIRE_AUTH_DEVICE_CONNECTED:
return "OTG1_STATE__ONEWIRE_AUTH_DEVICE_CONNECTED";
break;
case OTG1_STATE__USB_NO_AUTH_WAITING_CHALLENGE_RESPONSE:
return "OTG1_STATE__USB_NO_AUTH_WAITING_CHALLENGE_RESPONSE";
break;
case OTG1_STATE__USB_NO_AUTH_NOT_CONNECTED:
return "OTG1_STATE__USB_NO_AUTH_NOT_CONNECTED";
break;
case OTG1_STATE__USB_NO_AUTH_DEVICE_CONNECTED:
return "OTG1_STATE__USB_NO_AUTH_DEVICE_CONNECTED";
break;
case OTG1_STATE__HOST_CONNECTED:
return "OTG1_STATE__HOST_CONNECTED";
break;
default:
return "INVALID STATE";
}
}
static bool otgcontrol_controllerstate_is_valid(int state)
{
switch(state)
{
case OTG1_STATE__MANUAL_CONTROL:
case OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED:
case OTG1_STATE__ONEWIRE_AUTH_WAIT_HANDSHAKE_RESPONS:
case OTG1_STATE__ONEWIRE_AUTH_DEVICE_CONNECTED:
case OTG1_STATE__USB_NO_AUTH_WAITING_CHALLENGE_RESPONSE:
case OTG1_STATE__USB_NO_AUTH_NOT_CONNECTED:
case OTG1_STATE__USB_NO_AUTH_DEVICE_CONNECTED:
case OTG1_STATE__HOST_CONNECTED:
return true;
default:
return false;
}
}
int otgcontrol_init_fsm(struct rm_otgcontrol_data *otgc_data)
{
/* For now, only a simple FSM reset is required
* init routine has been kept however, to possibly contain other
* initiation code when the solution evolves
*/
return otgcontrol_reset_fsm(otgc_data);
}
static int otgcontrol_reset_fsm(struct rm_otgcontrol_data *otgc_data)
{
int ret;
/* Initially, set the DR mode to device, shut off the OTG power, and
* mux the onewire at GPIO
*/
ret = otgcontrol_change_otg_charge_mode(otgc_data,
OTG1_CHARGERMODE_CHARGE);
if (ret > 0) {
dev_err(otgc_data->dev,
"%s: Failed to reset FSM "
"(unable to change charger mode)\n",
__func__);
return ret;
}
ret = otgcontrol_set_dr_mode(otgc_data, OTG1_DR_MODE__DEVICE);
if (ret > 0) {
dev_err(otgc_data->dev,
"%s: Failed to reset FSM "
"(unable to set defaul USB OTG DR mode)\n",
__func__);
return ret;
}
ret = otgcontrol_switch_one_wire_mux_state(otgc_data,
OTG1_ONEWIRE_STATE__GPIO);
if (ret > 0) {
dev_err(otgc_data->dev,
"%s: Failed to reset FSM "
"(unable to set default one-wire pinmux state)\n",
__func__);
return ret;
}
dev_dbg(otgc_data->dev,
"%s: Activating one-wire GPIO IRQ\n",
__func__);
otgcontrol_activate_gpio_irq(otgc_data);
dev_dbg(otgc_data->dev,
"%s: Checking if device is connected\n",
__func__);
if(otgcontrol_get_current_gpio_state(otgc_data) == 0) {
dev_dbg(otgc_data->dev,
"%s: Device is connected, "
"doing default authenticated device connection procedure",
__func__);
ret = otgcontrol_do_device_connected_procedure(otgc_data, false);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to reset FSM "
"(unable to complete device connection procedure)\n",
__func__);
dev_dbg(otgc_data->dev,
"%s: Just wait for device disconnect/connect\n",
__func__);
otgc_data->otg_controlstate =
OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
return ret;
}
return 0;
}
else {
dev_dbg(otgc_data->dev,
"%s: Device is not connected, so wait for device to connect\n",
__func__);
otgc_data->otg_controlstate =
OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
return 0;
}
}
int otgcontrol_handleInput(struct rm_otgcontrol_data *otgc_data,
int signal,
void* param)
{
dev_dbg(otgc_data->dev, "%s: Enter\n", __func__);
switch(otgc_data->otg_controlstate)
{
case OTG1_STATE__MANUAL_CONTROL:
return otgcontrol_handle_state_MANUAL_CONTROL(
otgc_data,
signal,
param);
break;
case OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED:
return otgcontrol_handle_state_ONEWIRE_AUTH_NOT_CONNECTED(
otgc_data,
signal,
param);
break;
case OTG1_STATE__ONEWIRE_AUTH_WAIT_HANDSHAKE_RESPONS:
return otgcontrol_handle_state_ONEWIRE_AUTH_WAIT_HANDSHAKE_RESPONS(
otgc_data,
signal,
param);
break;
case OTG1_STATE__ONEWIRE_AUTH_DEVICE_CONNECTED:
return otgcontrol_handle_state_ONEWIRE_AUTH_DEVICE_CONNECTED(
otgc_data,
signal,
param);
break;
case OTG1_STATE__USB_NO_AUTH_WAITING_CHALLENGE_RESPONSE:
return otgcontrol_handle_state_USB_NO_AUTH_WAITING_CHALLENGE_RESPONSE(
otgc_data,
signal,
param);
break;
case OTG1_STATE__USB_NO_AUTH_NOT_CONNECTED:
return otgcontrol_handle_state_USB_NO_AUTH_NOT_CONNECTED(
otgc_data,
signal,
param);
break;
case OTG1_STATE__USB_NO_AUTH_DEVICE_CONNECTED:
return otgcontrol_handle_state_USB_NO_AUTH_DEVICE_CONNECTED(
otgc_data,
signal,
param);
break;
case OTG1_STATE__HOST_CONNECTED:
return otgcontrol_handle_state_HOST_CONNECTED(
otgc_data,
signal,
param);
break;
default:
dev_dbg(otgc_data->dev,
"%s: Current control state is invalid, "
"resetting state machine to state "
"ONEWIRE_AUTH_NOT_CONNECTED\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
}
static int otgcontrol_handle_state_MANUAL_CONTROL(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param)
{
switch(signal)
{
case OTG1_EVENT__CHARGER_CONNECTED:
case OTG1_EVENT__CHARGER_DISCONNECTED:
case OTG1_EVENT__DEVICE_CONNECTED:
case OTG1_EVENT__DEVICE_DISCONNECTED:
case OTG1_EVENT__CHALLENGE_REPLY_RECEIVED:
case OTG1_EVENT__MODE_CHANGE_REQUESTED:
case OTG1_EVENT__MODE_CHANGE_CHALLENGE_REPLY:
case OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED:
case OTG1_EVENT__OTG_DR_MODE_CHANGE_REQUESTED:
case OTG1_EVENT__TIMEOUT:
dev_dbg(otgc_data->dev,
"%s: In manual control mode, \
no automatic action taken\n",
__func__);
default:
dev_dbg(otgc_data->dev,
"%s: Unknown signal/event (%d), \
but in manual mode so ignoring\n",
__func__, signal);
}
return 0;
}
static int otgcontrol_handle_state_ONEWIRE_AUTH_NOT_CONNECTED(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param)
{
int ret;
switch(signal)
{
case OTG1_EVENT__CHARGER_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is connected, disabling all OTG features "
"until host is disconnected\n",
__func__);
otgc_data->otg_controlstate = OTG1_STATE__HOST_CONNECTED;
return 0;
case OTG1_EVENT__CHARGER_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is disconnected (NOT EXPECTED), but just "
"ignore this and continue to wait for device "
"connection\n",
__func__);
return 0;
case OTG1_EVENT__DEVICE_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device connected, doing authenticated connection "
"procedure\n",
__func__);
ret = otgcontrol_do_device_connected_procedure(otgc_data,
false);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete device connection "
"procedure, resetting fsm as an attempt to "
"recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__DEVICE_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device disconnected, doing disconnection "
"procedure\n",
__func__);
ret = otgcontrol_do_device_disconnected_procedure(otgc_data);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete disconnection "
"procedure, resetting fsm as an attempt to "
"recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__CHALLENGE_REPLY_RECEIVED:
dev_dbg(otgc_data->dev,
"%s: Unexpected message received from device, it might "
"be in an unknown state, doing fsm reset procedure to "
"power-cycle device\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
case OTG1_EVENT__MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Controller-mode change requested, start caller "
"authentication\n",
__func__);
return otgcontrol_do_controller_mode_change_procedure(
otgc_data,
param);
case OTG1_EVENT__MODE_CHANGE_CHALLENGE_REPLY:
dev_dbg(otgc_data->dev,
"%s: Unexpected mode change challenge reply received, "
"application might be in an unknown state - ignoring\n",
__func__);
return 0;
case OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Charger-mode change requested, not valid in this "
"state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_DR_MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: USB OTG DR-mode change requested, not valid in "
"this state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__TIMEOUT:
dev_dbg(otgc_data->dev,
"%s: TIMEOUT, but not waiting for anyting other than "
"device connection - ignoring\n",
__func__);
return 0;
default:
dev_dbg(otgc_data->dev,
"%s: Unknown signal/event (%d)\n",
__func__,
signal);
return -EINVAL;
}
}
static int otgcontrol_handle_state_ONEWIRE_AUTH_WAIT_HANDSHAKE_RESPONS(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param)
{
int ret;
switch(signal)
{
case OTG1_EVENT__CHARGER_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is connected, disabling all OTG features "
"until host is disconnected\n",
__func__);
otgc_data->otg_controlstate = OTG1_STATE__HOST_CONNECTED;
return 0;
case OTG1_EVENT__CHARGER_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is disconnected (NOT EXPECTED), but just "
"ignore this and continue to wait for one-wire "
"handshake response\n",
__func__);
return 0;
case OTG1_EVENT__DEVICE_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device connected (UNEXPECTED), re-starting "
"authenticated connection procedure\n",
__func__);
ret = otgcontrol_do_device_connected_procedure(otgc_data, false);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete device connection "
"procedure, resetting fsm as an attempt to "
"recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__DEVICE_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device disconnected, doing disconnection "
"procedure\n",
__func__);
ret = otgcontrol_do_device_disconnected_procedure(otgc_data);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete disconnection "
"procedure, resetting fsm as an attempt to "
"recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__CHALLENGE_REPLY_RECEIVED:
dev_dbg(otgc_data->dev,
"%s: Challenge reply received from connected device\n",
__func__);
ret = otgcontrol_do_verify_device_challenge_reply(otgc_data,
param);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to verify device challenge reply, "
"resetting fsm as an attempt to recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Controller-mode change requested, start caller "
"authentication\n",
__func__);
return otgcontrol_do_controller_mode_change_procedure(otgc_data,
param);
case OTG1_EVENT__MODE_CHANGE_CHALLENGE_REPLY:
dev_dbg(otgc_data->dev,
"%s: Unexpected mode change challenge reply received, "
"application might be in an unknown state - ignoring\n",
__func__);
return 0;
case OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Charger-mode change requested, not valid in this "
"state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_DR_MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: USB OTG DR-mode change requested, not valid in "
"this state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__TIMEOUT:
dev_dbg(otgc_data->dev,
"%s: TIMEOUT, no response from connected device within "
"expected amount of time\n",
__func__);
dev_dbg(otgc_data->dev,
"%s: Reset fsm, shutting off power and waiting for "
"disconnect/re-connect\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
default:
dev_dbg(otgc_data->dev,
"%s: Unknown signal/event (%d)\n",
__func__,
signal);
return -EINVAL;
}
}
static int otgcontrol_handle_state_ONEWIRE_AUTH_DEVICE_CONNECTED(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param)
{
int ret;
switch(signal)
{
case OTG1_EVENT__CHARGER_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is connected, disabling all OTG features "
"until host is disconnected\n",
__func__);
otgc_data->otg_controlstate = OTG1_STATE__HOST_CONNECTED;
return 0;
case OTG1_EVENT__CHARGER_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is disconnected (NOT EXPECTED), but just "
"ignore this and continue being connected to "
"authenticated device\n",
__func__);
return 0;
case OTG1_EVENT__DEVICE_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device connected (UNEXPECTED), re-starting "
"authenticated connection procedure\n",
__func__);
ret = otgcontrol_do_device_connected_procedure(otgc_data, false);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete device connection "
"procedure, resetting fsm as an attempt to "
"recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__DEVICE_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device disconnected, doing disconnection "
"procedure\n",
__func__);
ret = otgcontrol_do_device_disconnected_procedure(otgc_data);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete disconnection procedure, "
"resetting fsm as an attempt to recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__CHALLENGE_REPLY_RECEIVED:
dev_dbg(otgc_data->dev,
"%s: Unexpected message received from device, it might "
"be in an unknown state, doing fsm reset procedure to "
"power-cycle device\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
case OTG1_EVENT__MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Controller-mode change requested\n",
__func__);
return otgcontrol_do_controller_mode_change_procedure(otgc_data,
param);
case OTG1_EVENT__MODE_CHANGE_CHALLENGE_REPLY:
dev_dbg(otgc_data->dev,
"%s: Unexpected mode change challenge "
"reply received, application might be "
"in an unknown state - ignoring\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Charger-mode change requested, not valid in this "
"state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_DR_MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: USB OTG DR-mode change requested, not valid in this "
"state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__TIMEOUT:
dev_dbg(otgc_data->dev,
"%s: TIMEOUT, but not waiting for anyting other than "
"device disconnection - ignoring\n",
__func__);
return 0;
default:
dev_dbg(otgc_data->dev,
"%s: Unknown signal/event (%d)\n",
__func__,
signal);
return -EINVAL;
}
}
static int otgcontrol_handle_state_USB_NO_AUTH_WAITING_CHALLENGE_RESPONSE(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param)
{
int ret;
switch(signal)
{
case OTG1_EVENT__CHARGER_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is connected, disabling all OTG features until "
"host is disconnected\n",
__func__);
otgc_data->otg_controlstate = OTG1_STATE__HOST_CONNECTED;
return 0;
case OTG1_EVENT__CHARGER_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is disconnected (NOT EXPECTED), but just "
"ignore this and continue to wait for challenge response "
"from application\n",
__func__);
return 0;
case OTG1_EVENT__DEVICE_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device connected (UNEXPECTED), re-starting "
"authenticated connection procedure\n",
__func__);
ret = otgcontrol_do_device_connected_procedure(otgc_data,
false);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete device connection "
"procedure, resetting fsm as an attempt to "
"recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__DEVICE_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device disconnected, doing disconnection "
"procedure\n",
__func__);
ret = otgcontrol_do_device_disconnected_procedure(otgc_data);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete disconnection procedure, "
"resetting fsm as an attempt to recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__CHALLENGE_REPLY_RECEIVED:
dev_dbg(otgc_data->dev,
"%s: Unexpected message received from device, it might "
"be in an unknown state, doing fsm reset procedure to "
"power-cycle device\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
case OTG1_EVENT__MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Controller-mode change requested while already "
"waiting for challenge response, restart procedure\n",
__func__);
return otgcontrol_do_controller_mode_change_procedure(otgc_data,
param);
case OTG1_EVENT__MODE_CHANGE_CHALLENGE_REPLY:
dev_dbg(otgc_data->dev,
"%s: Received expected challenge response, changing to "
"unauthenticated USB OTG mode\n",
__func__);
return otgcontrol_do_verify_application_challenge_reply(otgc_data,
param);
case OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Charger-mode change requested, not valid in this "
"state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_DR_MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: USB OTG DR-mode change requested, not valid in "
"this state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__TIMEOUT:
dev_dbg(otgc_data->dev,
"%s: TIMEOUT, no response from application requesting "
"mode change within expected amount of time\n",
__func__);
dev_dbg(otgc_data->dev,
"%s: Reset fsm, going back to authenticated mode, "
"requiring new mode change request\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
default:
dev_dbg(otgc_data->dev,
"%s: Unknown signal/event (%d)\n",
__func__,
signal);
return -EINVAL;
}
}
static int otgcontrol_handle_state_USB_NO_AUTH_NOT_CONNECTED(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param)
{
int ret;
switch(signal)
{
case OTG1_EVENT__CHARGER_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is connected, disabling all OTG features "
"until host is disconnected\n",
__func__);
otgc_data->otg_controlstate = OTG1_STATE__HOST_CONNECTED;
return 0;
case OTG1_EVENT__CHARGER_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is disconnected (NOT EXPECTED), but just "
"ignore this and continue to wait for device connection\n",
__func__);
return 0;
case OTG1_EVENT__DEVICE_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device connected, doing un-authenticated "
"connection procedure\n",
__func__);
ret = otgcontrol_do_device_connected_procedure(otgc_data,
false);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete device connection "
"procedure, resetting fsm as an attempt to "
"recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__DEVICE_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device disconnected, doing disconnection procedure\n",
__func__);
ret = otgcontrol_do_device_disconnected_procedure(otgc_data);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete disconnection procedure, "
"resetting fsm as an attempt to recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__CHALLENGE_REPLY_RECEIVED:
dev_dbg(otgc_data->dev,
"%s: Unexpected message received from device, it might "
"be in an unknown state, doing fsm reset procedure to "
"power-cycle device\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
case OTG1_EVENT__MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Controller-mode change requested, start caller "
"authentication\n",
__func__);
return otgcontrol_do_controller_mode_change_procedure(otgc_data,
param);
case OTG1_EVENT__MODE_CHANGE_CHALLENGE_REPLY:
dev_dbg(otgc_data->dev,
"%s: Unexpected mode change challenge reply received, "
"application might be in an unknown state - ignoring\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Charger-mode change requested, not valid in this "
"state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_DR_MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: USB OTG DR-mode change requested, not valid in "
"this state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__TIMEOUT:
dev_dbg(otgc_data->dev,
"%s: TIMEOUT, but not waiting for anyting other than "
"device connection - ignoring\n",
__func__);
return 0;
default:
dev_dbg(otgc_data->dev,
"%s: Unknown signal/event (%d)\n",
__func__,
signal);
return -EINVAL;
}
}
static int otgcontrol_handle_state_USB_NO_AUTH_DEVICE_CONNECTED(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param)
{
int ret;
switch(signal)
{
case OTG1_EVENT__CHARGER_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is connected, disabling all OTG features "
"until host is disconnected\n",
__func__);
otgc_data->otg_controlstate = OTG1_STATE__HOST_CONNECTED;
return 0;
case OTG1_EVENT__CHARGER_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is disconnected (NOT EXPECTED), but just "
"ignore this and continue being connected to authenticated "
"device\n",
__func__);
return 0;
case OTG1_EVENT__DEVICE_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device connected (UNEXPECTED), re-starting "
"un-authenticated connection procedure\n",
__func__);
ret = otgcontrol_do_device_connected_procedure(otgc_data,
false);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete device "
"connection procedure, resetting "
"fsm as an attempt to recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__DEVICE_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device disconnected, doing disconnection procedure\n",
__func__);
ret = otgcontrol_do_device_disconnected_procedure(otgc_data);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to complete disconnection procedure, "
"resetting fsm as an attempt to recover\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
}
return 0;
case OTG1_EVENT__CHALLENGE_REPLY_RECEIVED:
dev_dbg(otgc_data->dev,
"%s: Unexpected message received from device, it might "
"be in an unknown state, doing fsm reset procedure to "
"power-cycle device\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
case OTG1_EVENT__MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Controller-mode change requested\n",
__func__);
return otgcontrol_do_controller_mode_change_procedure(
otgc_data,
param);
case OTG1_EVENT__MODE_CHANGE_CHALLENGE_REPLY:
dev_dbg(otgc_data->dev,
"%s: Unexpected mode change challenge reply received, "
"application might be in an unknown state - ignoring\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Charger-mode change requested, not valid in this "
"state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_DR_MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: USB OTG DR-mode change requested, not valid in "
"this state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__TIMEOUT:
dev_dbg(otgc_data->dev,
"%s: TIMEOUT, but not waiting for anyting other than "
"device disconnection - ignoring\n",
__func__);
return 0;
default:
dev_dbg(otgc_data->dev,
"%s: Unknown signal/event (%d)\n",
__func__,
signal);
return -EINVAL;
}
}
static int otgcontrol_handle_state_HOST_CONNECTED(
struct rm_otgcontrol_data *otgc_data,
int signal,
void *param)
{
switch(signal)
{
case OTG1_EVENT__CHARGER_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host connection detected in already connected "
"state, keeping all OTG features disabled until host "
"is disconnected\n",
__func__);
otgc_data->otg_controlstate = OTG1_STATE__HOST_CONNECTED;
return 0;
case OTG1_EVENT__CHARGER_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Host is disconnected, reset fsm into authenticated "
"mode\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
case OTG1_EVENT__DEVICE_CONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device connected (UNEXPECTED), reset fsm into "
"authenticated mode\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
case OTG1_EVENT__DEVICE_DISCONNECTED:
dev_dbg(otgc_data->dev,
"%s: Device disconnected (UNEXPECTED), reset fsm into "
"authenticated mode\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
case OTG1_EVENT__CHALLENGE_REPLY_RECEIVED:
dev_dbg(otgc_data->dev,
"%s: Unexpected message received from device, reset "
"fsm into authenticated mode\n",
__func__);
return otgcontrol_reset_fsm(otgc_data);
case OTG1_EVENT__MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Controller-mode change requested, not valid in "
"this state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__MODE_CHANGE_CHALLENGE_REPLY:
dev_dbg(otgc_data->dev,
"%s: Unexpected mode change challenge reply received, "
"application might be in an unknown state - ignoring\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_CHARGERMODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: Charger-mode change requested, not valid in this "
"state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__OTG_DR_MODE_CHANGE_REQUESTED:
dev_dbg(otgc_data->dev,
"%s: USB OTG DR-mode change requested, not valid in "
"this state\n",
__func__);
return -EINVAL;
case OTG1_EVENT__TIMEOUT:
dev_dbg(otgc_data->dev,
"%s: TIMEOUT, but not waiting for anyting other than "
"host disconnection - ignoring\n",
__func__);
return 0;
default:
dev_dbg(otgc_data->dev,
"%s: Unknown signal/event (%d)\n",
__func__,
signal);
return -EINVAL;
}
}
static int otgcontrol_do_controller_mode_change_procedure(
struct rm_otgcontrol_data *otgc_data,
void *param)
{
int mode_requested = *(int*)param;
/* Depending on requested mode.. */
/* 1.
* Enable challenge/reply properties, and set challenge to be read by
* application */
/* 2.
* Wait for reply for a while */
/* But for now, just set mode */
switch(mode_requested)
{
case OTG_MODE__MANUAL_CONTROL:
case OTG_MODE__USB_NO_AUTH:
return otgcontrol_do_authenticate_calling_application(
otgc_data,
mode_requested);
break;
default:
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
return 0;
}
}
static int otgcontrol_do_authenticate_calling_application(
struct rm_otgcontrol_data *otgc_data,
int mode_requested)
{
dev_dbg(otgc_data->dev,
"%s: SKIPPING CALLING APPLICATION AUTHENTICATION FOR NOW ..\n",
__func__);
switch(mode_requested)
{
case OTG_MODE__MANUAL_CONTROL:
otgc_data->otg_controlstate = OTG1_STATE__MANUAL_CONTROL;
break;
case OTG_MODE__ONEWIRE_AUTH:
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
break;
case OTG_MODE__USB_NO_AUTH:
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
break;
default:
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
}
return 0;
}
static int otgcontrol_do_verify_application_challenge_reply(
struct rm_otgcontrol_data *otgc_data,
void *param)
{
dev_dbg(otgc_data->dev,
"%s: For now, just take the challenge to be good and activate "
"requested state\n",
__func__);
switch(otgc_data->mode_requested)
{
case OTG_MODE__MANUAL_CONTROL:
otgc_data->otg_controlstate = OTG1_STATE__MANUAL_CONTROL;
break;
case OTG_MODE__ONEWIRE_AUTH:
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
break;
case OTG_MODE__USB_NO_AUTH:
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
break;
default:
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
}
return 0;
}
static int otgcontrol_do_verify_device_challenge_reply(
struct rm_otgcontrol_data *otgc_data,
void *param)
{
int ret;
dev_dbg(otgc_data->dev,
"%s: For now, just take the challenge to be good and activate "
"USB OTG Host mode",
__func__);
ret = otgcontrol_set_dr_mode(otgc_data, OTG1_DR_MODE__HOST);
if (ret < 0) {
dev_err(otgc_data->dev,
"%s: Failed to set USB OTG host mode\n",
__func__);
return ret;
}
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_DEVICE_CONNECTED;
return 0;
}
static int otgcontrol_do_set_controlmode(
struct rm_otgcontrol_data *otgc_data,
int mode)
{
dev_dbg(otgc_data->dev,
"%s: Enter\n",
__func__);
switch(mode)
{
case OTG_MODE__MANUAL_CONTROL:
dev_dbg(otgc_data->dev,
"%s: setting MANUAL_CONTROL mode\n",
__func__);
break;
case OTG_MODE__ONEWIRE_AUTH:
dev_dbg(otgc_data->dev,
"%s: setting ONEWIRE_AUTH mode\n",
__func__);
break;
case OTG_MODE__USB_NO_AUTH:
dev_dbg(otgc_data->dev,
"%s: setting USB_NO_AUTH mode\n",
__func__);
break;
default:
dev_dbg(otgc_data->dev,
"%s: unable to set control mode (unknown mode %d)\n",
__func__,
mode);
return -EINVAL;
}
return 0;
}
static int otgcontrol_do_device_connected_procedure(
struct rm_otgcontrol_data *otgc_data,
bool authentication_required)
{
int i, ret;
char tty_device_name[50], buf[100];
dev_dbg(otgc_data->dev,
"%s: Powering up connected device (if no charger is connected)\n",
__func__);
ret = otgcontrol_change_otg_charge_mode(
otgc_data,
OTG1_CHARGERMODE_OTG); /* OTG POWER ON */
if (ret < 0) {
dev_dbg(otgc_data->dev,
"%s: Unable to turn on OTG power, check connections\n",
__func__);
return ret;
}
if (authentication_required) {
dev_dbg(otgc_data->dev,
"%s: Deactivating GPIO IRQ and sending authentication "
"challenge to connected device\n",
__func__);
otgcontrol_deactivate_gpio_irq(otgc_data);
/* Wait for device to boot */
dev_dbg(otgc_data->dev,
"%s: Waiting for device to boot\n",
__func__);
for(i = 0;i < 5000;i++) udelay(1000);
/* Send challenge response */
dev_dbg(otgc_data->dev,
"%s: Changing one-wire mux config (UART TX)\n",
__func__);
otgcontrol_switch_one_wire_mux_state(
otgc_data,
OTG1_ONEWIRE_STATE__UART_TX);
dev_dbg(otgc_data->dev,
"%s: Sending authentication challenge\n",
__func__);
/* sprintf(tty_device_name, "/dev/%s", otgc_data->one_wire_tty_name); */
ret = otgcontrol_onewire_write_tty(otgc_data,
"/dev/ttymxc5",
":0001ff#");
if (ret < 0)
{
dev_err(otgc_data->dev,
"%s: Failed to send authentication challenge to "
"connected device\n",
__func__);
return ret;
}
/* Wait for message to be sendt before switching direction */
dev_dbg(otgc_data->dev,
"%s: Waiting 100 ms approx. to let tx message leave "
"before switching direction\n",
__func__);
for(i = 0;i < 100;i++) udelay(1000);
dev_dbg(otgc_data->dev,
"%s: Changing one-wire mux config (UART RX)\n",
__func__);
ret = otgcontrol_switch_one_wire_mux_state(
otgc_data,
OTG1_ONEWIRE_STATE__UART_RX);
if (ret < 0) {
dev_dbg(otgc_data->dev,
"%s: Unable to switch one-wire mux config, "
"cannot authenticate connected device\n",
__func__);
return ret;
}
/* Wait for response or timeout */
dev_dbg(otgc_data->dev,
"%s: Waiting for response from connected device\n",
__func__);
/* For now, just block here until response has been received */
int count = otgcontrol_onewire_read_until_cr(otgc_data,
"/dev/ttymxc5",
buf, 100);
buf[count] = 0;
dev_dbg(otgc_data->dev, "%s: Read '%s'",
__func__,
buf);
/* Verify response */
/* For now just take it to be good and enable USB connection
* (i.e. enable host mode) */
dev_dbg(otgc_data->dev,
"%s: Taking response to be good and activating USB "
"connection (i.e enabling host mode)\n",
__func__);
ret = otgcontrol_do_set_controlmode(otgc_data,
OTG1_DR_MODE__HOST);
if (ret < 0) {
dev_dbg(otgc_data->dev,
"%s: Unable to set USB OTG host mode - "
"disconnect and re-connect connected device\n",
__func__);
return ret;
}
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_DEVICE_CONNECTED;
return 0;
}
else
{
dev_dbg(otgc_data->dev,
"%s: Authentication not required, activating USB "
"connection (i.e. enabling host mode)\n",
__func__);
otgcontrol_set_dr_mode(otgc_data,
OTG1_DR_MODE__HOST);
otgc_data->otg_controlstate = OTG1_STATE__USB_NO_AUTH_DEVICE_CONNECTED;
return 0;
}
}
static int otgcontrol_do_device_disconnected_procedure(
struct rm_otgcontrol_data *otgc_data)
{
int ret;
ret = otgcontrol_change_otg_charge_mode(
otgc_data,
OTG1_CHARGERMODE_CHARGE); /* OTG POWER OFF */
if (ret < 0) {
dev_dbg(otgc_data->dev,
"%s: Unable to turn off OTG power\n",
__func__);
return ret;
}
ret = otgcontrol_set_dr_mode(otgc_data,
OTG1_DR_MODE__DEVICE);
if (ret < 0) {
dev_dbg(otgc_data->dev,
"%s: Unable to set USB OTG device mode\n",
__func__);
return ret;
}
dev_dbg(otgc_data->dev,
"%s: Activating one-wire GPIO IRQ\n",
__func__);
otgcontrol_activate_gpio_irq(otgc_data);
return 0;
}
static int otgcontrol_do_start_onewire_authentication(
struct rm_otgcontrol_data *otgc_data)
{
dev_dbg(otgc_data->dev,
"%s: Enter\n",
__func__);
dev_dbg(otgc_data->dev,
"%s: PLEASE IMPLEMENT THIS !\n",
__func__);
return 0;
}