1465 lines
37 KiB
C
1465 lines
37 KiB
C
|
/*
|
|||
|
* reMarkable OTG Control
|
|||
|
*
|
|||
|
* Copyright (C) 2019 reMarkable AS - http://www.remarkable.com/
|
|||
|
*
|
|||
|
* Author: Steinar Bakkemo <steinar.bakkemo@remarkable.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 version 2.
|
|||
|
*
|
|||
|
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
|||
|
* kind, whether express or implied; without even the implied warranty
|
|||
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
* GNU General Public License for more details.
|
|||
|
*/
|
|||
|
|
|||
|
#include "otgcontrol.h"
|
|||
|
#include "otgcontrol_fsm.h"
|
|||
|
#include "otgcontrol_onewire.h"
|
|||
|
#include "otgcontrol_charging_ctrl.h"
|
|||
|
#include "otgcontrol_dr_mode.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_charger_mode_int(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);*/
|
|||
|
otgcontrol_deactivate_gpio_irq(otgc_data);
|
|||
|
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"%s: Checking if device is connected and initiating default "
|
|||
|
"state\n",
|
|||
|
__func__);
|
|||
|
|
|||
|
otgc_data->otg1_controllermode = OTG_MODE__ONEWIRE_AUTH;
|
|||
|
if(otgcontrol_get_current_gpio_state(otgc_data) ==
|
|||
|
OTG1_ONEWIRE_GPIO_STATE__DEVICE_CONNECTED) {
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"Device is connected, "
|
|||
|
"doing default authenticated device connection procedure\n");
|
|||
|
|
|||
|
ret = otgcontrol_do_device_connected_procedure(otgc_data, true);
|
|||
|
if (ret < 0) {
|
|||
|
dev_err(otgc_data->dev,
|
|||
|
"Unable to complete device connection procedure\n");
|
|||
|
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"Waiting for device disconnect/connect\n");
|
|||
|
|
|||
|
otgc_data->otg_controlstate =
|
|||
|
OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
|
|||
|
}
|
|||
|
}
|
|||
|
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)
|
|||
|
{
|
|||
|
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_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__);
|
|||
|
break;
|
|||
|
|
|||
|
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);
|
|||
|
break;
|
|||
|
|
|||
|
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:
|
|||
|
dev_err(otgc_data->dev,
|
|||
|
"%s: Change to manual or non-authenticated mode "
|
|||
|
"requires authentication of calling application\n",
|
|||
|
__func__);
|
|||
|
|
|||
|
return otgcontrol_do_authenticate_calling_application(
|
|||
|
otgc_data,
|
|||
|
mode_requested);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
/* Invalid mode => authenticated mode */
|
|||
|
dev_err(otgc_data->dev,
|
|||
|
"%s: Invalid controller mode or authenticated mode "
|
|||
|
"requested (%d), setting one-wire authenticated mode\n",
|
|||
|
__func__,
|
|||
|
mode_requested);
|
|||
|
|
|||
|
otgcontrol_do_set_controlmode(otgc_data,
|
|||
|
OTG_MODE__ONEWIRE_AUTH);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static int otgcontrol_do_authenticate_calling_application(
|
|||
|
struct rm_otgcontrol_data *otgc_data,
|
|||
|
int mode_requested)
|
|||
|
{
|
|||
|
switch(mode_requested)
|
|||
|
{
|
|||
|
case OTG_MODE__MANUAL_CONTROL:
|
|||
|
case OTG_MODE__USB_NO_AUTH:
|
|||
|
/* NORMALLY, A CHALLENGE WOULD BE SENT TO CALLING APPLICATION */
|
|||
|
/* IDEA:
|
|||
|
* SHOW CHALLENGE PROPERTY
|
|||
|
* WAIT FOR APPLICATION TO READ THIS
|
|||
|
* SHOW REPLY PROPERTY, TO BE PRESENT WHEN APPLICATION
|
|||
|
* HAS READ THE CHALLENGE
|
|||
|
* GET REPLY AND VERIFY
|
|||
|
* HIDE CHALLENGE/REPLY PROPERTIES
|
|||
|
*
|
|||
|
* FOR NOW, JUST CALL REPLY VERIFY ROUTINE, WHICH CURRENTLY
|
|||
|
* HAS NO VERIFICATION ALGORITHM IMPLEMENTED, AND
|
|||
|
* WILL DO A SIMPLE CONNECTION CHECK AND SET STATE ACCORDING
|
|||
|
* TO THE REQUESTED MODE AND THE CURRENT CONNECTION STATE
|
|||
|
*/
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"%s: Authentication of calling application not"
|
|||
|
"implemented, skipping\n",
|
|||
|
__func__);
|
|||
|
|
|||
|
otgc_data->mode_requested = mode_requested;
|
|||
|
otgcontrol_do_verify_application_challenge_reply(otgc_data,
|
|||
|
NULL);
|
|||
|
break;
|
|||
|
|
|||
|
case OTG_MODE__ONEWIRE_AUTH:
|
|||
|
default:
|
|||
|
/* No authentication required to enable authenticated mode */
|
|||
|
otgcontrol_do_set_controlmode(otgc_data,
|
|||
|
mode_requested);
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
static int otgcontrol_do_verify_application_challenge_reply(
|
|||
|
struct rm_otgcontrol_data *otgc_data,
|
|||
|
void *param)
|
|||
|
{
|
|||
|
switch(otgc_data->mode_requested)
|
|||
|
{
|
|||
|
case OTG_MODE__MANUAL_CONTROL:
|
|||
|
otgcontrol_do_set_controlmode(otgc_data,
|
|||
|
otgc_data->mode_requested);
|
|||
|
break;
|
|||
|
|
|||
|
case OTG_MODE__USB_NO_AUTH:
|
|||
|
otgcontrol_do_set_controlmode(otgc_data,
|
|||
|
otgc_data->mode_requested);
|
|||
|
break;
|
|||
|
|
|||
|
case OTG_MODE__ONEWIRE_AUTH:
|
|||
|
default:
|
|||
|
otgcontrol_do_set_controlmode(otgc_data,
|
|||
|
otgc_data->mode_requested);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
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)
|
|||
|
{
|
|||
|
switch(mode)
|
|||
|
{
|
|||
|
case OTG_MODE__MANUAL_CONTROL:
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"%s: setting MANUAL_CONTROL mode\n",
|
|||
|
__func__);
|
|||
|
|
|||
|
otgc_data->otg_controlstate = OTG1_STATE__MANUAL_CONTROL;
|
|||
|
otgc_data->otg1_controllermode = OTG_MODE__MANUAL_CONTROL;
|
|||
|
|
|||
|
otgcontrol_deactivate_gpio_irq(otgc_data);
|
|||
|
break;
|
|||
|
|
|||
|
case OTG_MODE__ONEWIRE_AUTH:
|
|||
|
if (otgcontrol_get_current_gpio_state(otgc_data) ==
|
|||
|
OTG1_ONEWIRE_GPIO_STATE__DEVICE_CONNECTED) {
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"%s: Enabling ONE-WIRE AUTHENTICATED mode "
|
|||
|
"(DEVICE CONNECTED)\n",
|
|||
|
__func__);
|
|||
|
|
|||
|
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_DEVICE_CONNECTED;
|
|||
|
}
|
|||
|
else {
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"%s: Enabling ONE-WIRE AUTHENTICATED mode "
|
|||
|
"(DEVICE NOT CONNECTED)\n",
|
|||
|
__func__);
|
|||
|
|
|||
|
otgc_data->otg_controlstate = OTG1_STATE__ONEWIRE_AUTH_NOT_CONNECTED;
|
|||
|
}
|
|||
|
otgc_data->otg1_controllermode = OTG_MODE__ONEWIRE_AUTH;
|
|||
|
|
|||
|
otgcontrol_deactivate_gpio_irq(otgc_data);
|
|||
|
|
|||
|
/* Also do a disconnect procedure, setting the charger back
|
|||
|
* to charger mode and the OTG USB device back to device mode
|
|||
|
* until proper authenticated device connection has been
|
|||
|
* implemented
|
|||
|
*/
|
|||
|
otgcontrol_do_device_disconnected_procedure(otgc_data);
|
|||
|
break;
|
|||
|
|
|||
|
case OTG_MODE__USB_NO_AUTH:
|
|||
|
if (otgcontrol_get_current_gpio_state(otgc_data) ==
|
|||
|
OTG1_ONEWIRE_GPIO_STATE__DEVICE_CONNECTED) {
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"%s: Enabling ONE-WIRE NON-AUTHENTICATED mode "
|
|||
|
"(DEVICE CONNECTED)\n",
|
|||
|
__func__);
|
|||
|
|
|||
|
/* Due to the fact that this mode is intended for the
|
|||
|
* test-application requiring un-authenticated USB
|
|||
|
* equipment to be connected, just enable OTG mode
|
|||
|
* since the device is already connected when switching
|
|||
|
* to this mode, to prevent need for unplug and replug
|
|||
|
*/
|
|||
|
otgcontrol_do_device_connected_procedure(otgc_data, false);
|
|||
|
otgc_data->otg_controlstate = OTG1_STATE__USB_NO_AUTH_DEVICE_CONNECTED;
|
|||
|
}
|
|||
|
else {
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"%s: Enabling ONE-WIRE NON-AUTHENTICATED mode "
|
|||
|
"(DEVICE NOT CONNECTED)\n",
|
|||
|
__func__);
|
|||
|
|
|||
|
/* Due to the fact that this device disconnection could
|
|||
|
* not be detected in other modes, just disable OTG mode
|
|||
|
* to make sure the charger mode is enabled since the
|
|||
|
* device is not connected - just to be sure
|
|||
|
*/
|
|||
|
otgcontrol_do_device_disconnected_procedure(otgc_data);
|
|||
|
otgc_data->otg_controlstate = OTG1_STATE__USB_NO_AUTH_NOT_CONNECTED;
|
|||
|
}
|
|||
|
otgc_data->otg1_controllermode = OTG_MODE__USB_NO_AUTH;
|
|||
|
|
|||
|
otgcontrol_activate_gpio_irq(otgc_data);
|
|||
|
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 ret;
|
|||
|
|
|||
|
if (authentication_required) {
|
|||
|
dev_warn(otgc_data->dev,
|
|||
|
"Authenticated connection not supported, ignoring..\n");
|
|||
|
return -ENOTSUPP;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"Authentication not required, activating USB connection\n");
|
|||
|
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"Powering up connected device (if no charger is connected)\n");
|
|||
|
|
|||
|
ret = otgcontrol_change_otg_charger_mode_int(
|
|||
|
otgc_data,
|
|||
|
OTG1_CHARGERMODE_OTG); /* OTG POWER ON */
|
|||
|
if (ret < 0) {
|
|||
|
dev_dbg(otgc_data->dev,
|
|||
|
"Unable to turn on OTG power, check connections\n");
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
/* Sleep to avoid race, let USB driver handle itself before setting DR mode */
|
|||
|
usleep_range(300000, 400000);
|
|||
|
|
|||
|
otgcontrol_set_dr_mode(otgc_data,
|
|||
|
OTG1_DR_MODE__HOST);
|
|||
|
otgc_data->otg_controlstate = OTG1_STATE__USB_NO_AUTH_DEVICE_CONNECTED;
|
|||
|
otgc_data->otg1_controllermode = OTG_MODE__USB_NO_AUTH;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static int otgcontrol_do_device_disconnected_procedure(
|
|||
|
struct rm_otgcontrol_data *otgc_data)
|
|||
|
{
|
|||
|
int ret;
|
|||
|
|
|||
|
ret = otgcontrol_change_otg_charger_mode_int(
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/* otgcontrol_activate_gpio_irq(otgc_data);*/
|
|||
|
if (otgc_data->otg1_controllermode == OTG_MODE__USB_NO_AUTH) {
|
|||
|
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;
|
|||
|
}
|