/*
 * Copyright (c) 2022 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief USB virtual bus service
 */

#ifndef ZEPHYR_INCLUDE_UVB
#define ZEPHYR_INCLUDE_UVB

#include <zephyr/sys/atomic.h>
#include <zephyr/sys/dlist.h>
#include <zephyr/sys/iterable_sections.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief Virtual bus event types
 */
enum uvb_event_type {
	/** VBUS ready event */
	UVB_EVT_VBUS_READY,
	/** VBUS removed event */
	UVB_EVT_VBUS_REMOVED,
	/** Device resume event */
	UVB_EVT_RESUME,
	/** Device suspended event */
	UVB_EVT_SUSPEND,
	/** Port reset detected */
	UVB_EVT_RESET,
	/** Endpoint request event */
	UVB_EVT_REQUEST,
	/** Endpoint request reply event */
	UVB_EVT_REPLY,
	/** Device activity event */
	UVB_EVT_DEVICE_ACT,
};

/**
 * @brief Virtual bus device activity type
 */
enum uvb_device_act {
	/** Device issue remote wakeup */
	UVB_DEVICE_ACT_RWUP,
	/** Low speed connection detected */
	UVB_DEVICE_ACT_LS,
	/** Full speed connection detected */
	UVB_DEVICE_ACT_FS,
	/** High speed connection detected */
	UVB_DEVICE_ACT_HS,
	/** Super speed connection detected */
	UVB_DEVICE_ACT_SS,
	/** Connection removed, issued when a device is disabled */
	UVB_DEVICE_ACT_REMOVED,
};

/**
 * @brief Virtual bus host request type
 */
enum uvb_request {
	/** Setup request */
	UVB_REQUEST_SETUP,
	/** Data request */
	UVB_REQUEST_DATA,
};

/**
 * @brief Virtual bus device reply type
 */
enum uvb_reply {
	/** Default value */
	UVB_REPLY_TIMEOUT,
	/** Reply ACK handshake to a request */
	UVB_REPLY_ACK,
	/** Reply NACK handshake to a request */
	UVB_REPLY_NACK,
	/** Reply STALL handshake to a request */
	UVB_REPLY_STALL,
};

/**
 * USB virtual bus packet
 */
struct uvb_packet {
	/** slist node (TBD) */
	sys_snode_t node;
	/** Consecutive packet sequence number */
	uint32_t seq;

	/** Request type */
	enum uvb_request request: 8;
	/** Reply handshake */
	enum uvb_reply reply : 8;
	/** Device (peripheral) address */
	unsigned int addr : 8;
	/** Endpoint address */
	unsigned int ep : 8;

	/** Pointer to a data chunk */
	uint8_t *data;
	/** Length of the data chunk */
	size_t length;
};

/**
 * USB virtual bus node
 */
struct uvb_node {
	union {
		/** dlist device node */
		sys_dnode_t node;
		/** dlist host list */
		sys_dlist_t list;
	};
	/** Name of the UVB node */
	const char *name;
	/** Pointer to the notify callback of the UVB node */
	void (*notify)(const void *const priv,
		       const enum uvb_event_type type,
		       const void *data);
	/** Internally used atomic value */
	atomic_t subscribed;
	/** True for a host node */
	bool head;
	/** Pointer to the node's private data */
	const void *priv;
};

/**
 * @brief Allocate UVB packet for the request or reply.
 *
 * @param[in] request Request type
 * @param[in] addr    Device (peripheral) address
 * @param[in] ep      Endpoint address
 * @param[in] data    Pointer to a chunk of the net_buf data
 * @param[in] length  Data chunk length
 *
 * @return pointer to allocated packet or NULL on error.
 */
struct uvb_packet *uvb_alloc_pkt(const enum uvb_request request,
				 const uint8_t addr, const uint8_t ep,
				 uint8_t *const data,
				 const size_t length);

/**
 * @brief Free UVB packet
 *
 * @param[in] pkt    Pointer to UVB packet
 */
void uvb_free_pkt(struct uvb_packet *const pkt);

/**
 * @brief Advert UVB event on virtual bus
 *
 * All devices subscribed to a controller are advertised.
 * Events like UVB_EVT_REQUEST are to be filtered by using device address.
 *
 * @param[in] host_node Pointer to host controller UVB node
 * @param[in] type      UVB event type
 * @param[in] pkt       Pointer to UVB packet or NULL
 *
 * @return 0 on success, all other values should be treated as error.
 */
int uvb_advert(const struct uvb_node *const host_node,
	       const enum uvb_event_type type,
	       const struct uvb_packet *const pkt);

/**
 * @brief Submit UVB event to host controller node
 *
 * Intended for use by virtual device for the request reply
 * UVB_EVT_REPLY and device activity event UVB_EVT_DEVICE_ACT
 *
 * @param[in] dev_node  Pointer to device controller UVB node
 * @param[in] type      UVB event type
 * @param[in] pkt       Pointer to UVB packet or NULL
 *
 * @return 0 on success, all other values should be treated as error.
 */
int uvb_to_host(const struct uvb_node *const dev_node,
		const enum uvb_event_type type,
		const struct uvb_packet *const pkt);

/**
 * @brief Subscribe to the adverts of the specific host node.
 *
 * Intended for use by virtual device during UDC API init call.
 *
 * @param[in] name     Name of the host node.
 * @param[in] dev_node Pointer to device controller UVB node
 *
 * @return 0 on success, all other values should be treated as error.
 */
int uvb_subscribe(const char *name, struct uvb_node *const dev_node);

/**
 * @brief Unsubsribe from the adverts of the specific host node.
 *
 * Intended for use by virtual device during UDC API shutdown call.
 *
 * @param[in] name     Name of the host node.
 * @param[in] dev_node Pointer to device controller UVB node
 *
 * @return 0 on success, all other values should be treated as error.
 */
int uvb_unsubscribe(const char *name, struct uvb_node *const dev_node);

/**
 * @brief Advert request UVB event on virtual bus
 *
 * @param[in] host_node Pointer to host controller UVB node
 * @param[in] pkt       Pointer to UVB packet
 *
 * @return 0 on success, all other values should be treated as error.
 */
static inline int uvb_advert_pkt(const struct uvb_node *const host_node,
				 const struct uvb_packet *const pkt)
{
	return uvb_advert(host_node, UVB_EVT_REQUEST, pkt);
}

/**
 * @brief Reply to UVB request
 *
 * @param[in] dev_node Pointer to host controller UVB node
 * @param[in] pkt      Pointer to UVB packet
 *
 * @return 0 on success, all other values should be treated as error.
 */
static inline int uvb_reply_pkt(const struct uvb_node *const dev_node,
				const struct uvb_packet *const pkt)
{
	return uvb_to_host(dev_node, UVB_EVT_REPLY, pkt);
}

/** @brief Helper to define UVB host controller node
 *
 *  @param host        UVB host node structure name
 *  @param host_name   UVB host node name
 *  @param host_notify Pointer to host notify callback
 */
#define UVB_HOST_NODE_DEFINE(host, host_name, host_notify)			\
	STRUCT_SECTION_ITERABLE(uvb_node, host) = {				\
		.name = host_name,						\
		.head = true,							\
		.notify = host_notify,						\
	}

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_UVB */
