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

1465 lines
37 KiB
C
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
* 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;
}