/* Copyright 2019 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* USB State Machine Framework */

#ifndef __CROS_EC_USB_SM_H
#define __CROS_EC_USB_SM_H

#include "compiler.h"	/* for typeof() on Zephyr */

/* Function pointer that implements a portion of a usb state */
typedef void (*state_execution)(const int port);

/*
 * General usb state that can be used in multiple state machines.
 *
 * entry - Optional method that will be run when this state is entered
 * run   - Optional method that will be run repeatedly during state machine loop
 * exit  - Optional method that will be run when this state exists
 * parent- Optional parent usb_state that contains common entry/run/exit
 *	implementation among various child usb_states.
 *	entry: Parent function executes BEFORE child function.
 *	run: Parent function executes AFTER child function.
 *	exit: Parent function executes AFTER child function.
 *
 *	Note: When transitioning between two child states with a shared parent,
 *	that parent's exit and entry functions do not execute.
 */
struct usb_state {
	const state_execution entry;
	const state_execution run;
	const state_execution exit;
	const struct usb_state *parent;
};

typedef const struct usb_state *usb_state_ptr;

/* Defines the current context of the usb statemachine. */
struct sm_ctx {
	usb_state_ptr current;
	usb_state_ptr previous;
	/* We use intptr_t type to accommodate host tests ptr size variance */
	intptr_t internal[2];
};

/* Local state machine states */
enum sm_local_state {
	SM_INIT = 0, /* Ensure static variables initialize to SM_INIT */
	SM_RUN,
	SM_PAUSED,
};

/*
 * A state machine can use these debug levels to regulate the amount of debug
 * information printed on the EC console
 *
 * The states currently defined are
 *   Level 0: disabled
 *   Level 1: state names
 *
 * Note that higher log level causes timing changes and thus may affect
 * performance.
 */
enum debug_level {
	DEBUG_DISABLE,
	DEBUG_LEVEL_1,
	DEBUG_LEVEL_2,
	DEBUG_LEVEL_3,
	DEBUG_LEVEL_MAX = DEBUG_LEVEL_3
};

/**
 * Changes a state machines state. This handles exiting the previous state and
 * entering the target state. A common parent state will not exited nor be
 * re-entered.
 *
 * @param port      USB-C port number
 * @param ctx       State machine context
 * @param new_state State to transition to (NULL is valid and exits all states)
 */
void set_state(int port, struct sm_ctx *ctx, usb_state_ptr new_state);

/**
 * Runs one iteration of a state machine (including any parent states)
 *
 * @param port USB-C port number
 * @param ctx  State machine context
 */
void run_state(int port, struct sm_ctx *ctx);

#ifdef TEST_BUILD
/*
 * Struct for test builds that allow unit tests to easily iterate through
 * state machines
 */
struct test_sm_data {
	/* Base pointer of the state machine array */
	const usb_state_ptr base;
	/* Size fo the state machine array above */
	const int size;
	/* The array of names for states, can be NULL */
	const char * const * const names;
	/* The size of the above names array */
	const int names_size;
};
#endif

/* Creates a state machine state that will never link. Useful with IS_ENABLED */
#define GEN_NOT_SUPPORTED(state) extern typeof(state) state ## _NOT_SUPPORTED

#endif /* __CROS_EC_USB_SM_H */
