/*
 * include/vservices/protocol/core.h
 *
 * Copyright (c) 2012-2018 General Dynamics
 * Copyright (c) 2014 Open Kernel Labs, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * These are the common generated definitions for the core protocol drivers;
 * specifically the message IDs and the protocol state representation.
 *
 * This is currently hand-generated, but will eventually be autogenerated,
 * from the protocol specifications in core.vs. Please keep it consistent
 * with that file.
 */

#define VSERVICE_CORE_PROTOCOL_NAME "com.ok-labs.core"
#define VSERVICE_CORE_PARAM_SIZE_SERVICE_INFO__PROTOCOL_NAME 32
#define VSERVICE_CORE_PARAM_SIZE_SERVICE_INFO__SERVICE_NAME 16

/*
 * Identifiers for in-band messages.
 *
 * This definition applies in both directions, because there is no practical
 * limit on message IDs (services are unlikely to define 2^16 distinct message
 * names).
 */
typedef enum {
	/** simple_protocol core **/
	/* message out startup */
	VSERVICE_CORE_MSG_STARTUP,

	/* message out shutdown */
	VSERVICE_CORE_MSG_SHUTDOWN,

	/* command in sync connect */
	VSERVICE_CORE_REQ_CONNECT,
	VSERVICE_CORE_ACK_CONNECT,
	VSERVICE_CORE_NACK_CONNECT,

	/* command in sync disconnect */
	VSERVICE_CORE_REQ_DISCONNECT,
	VSERVICE_CORE_ACK_DISCONNECT,
	VSERVICE_CORE_NACK_DISCONNECT,

	/* command in service_count */
	VSERVICE_CORE_REQ_SERVICE_COUNT,
	VSERVICE_CORE_ACK_SERVICE_COUNT,
	VSERVICE_CORE_NACK_SERVICE_COUNT,

	/* command in queued service_info */
	VSERVICE_CORE_REQ_SERVICE_INFO,
	VSERVICE_CORE_ACK_SERVICE_INFO,
	VSERVICE_CORE_NACK_SERVICE_INFO,

	/* message inout service_reset */
	VSERVICE_CORE_MSG_SERVICE_RESET,

	/* message inout service_ready */
	VSERVICE_CORE_MSG_SERVICE_READY,

	/* message out notification bits */
	VSERVICE_CORE_MSG_NOTIFICATION_BITS_INFO,

} vservice_core_message_id_t;

/*
 * Notification bits are defined separately for each direction because there
 * is relatively limited space to allocate them from (specifically, the bits in
 * a machine word). It is unlikely but possible for a protocol to reach this
 * limit.
 */

/* Bits in the in (client -> server) notification bitmask. */
typedef enum {
	/** simple_protocol core **/
	/* No in notifications */

	VSERVICE_CORE_NBIT_IN__COUNT = 0,
} vservice_core_nbit_in_t;

/* Masks for the in notification bits */
/* No in notifications */

/* Bits in the out (server -> client) notification bitmask. */
typedef enum {
	/** simple_protocol core **/
	/* notification out reenumerate */
	VSERVICE_CORE_NBIT_OUT_REENUMERATE = 0,

	VSERVICE_CORE_NBIT_OUT__COUNT,
} vservice_core_nbit_out_t;

/* Masks for the out notification bits */
#define VSERVICE_CORE_NMASK_OUT_REENUMERATE \
		(1 << VSERVICE_CORE_NBIT_OUT_REENUMERATE)

/* Valid states of the interface's generated state machine. */
typedef enum {
	/* state offline */
	VSERVICE_CORE_STATE_OFFLINE = 0,

	/* state disconnected */
	VSERVICE_CORE_STATE_DISCONNECTED,
	VSERVICE_CORE_STATE_DISCONNECTED__CONNECT,

	/* state connected */
	VSERVICE_CORE_STATE_CONNECTED,
	VSERVICE_CORE_STATE_CONNECTED__DISCONNECT,

	/* reset offline */
	VSERVICE_CORE_STATE__RESET = VSERVICE_CORE_STATE_OFFLINE,
} vservice_core_statenum_t;

typedef struct {
	vservice_core_statenum_t statenum;
	bool pending_service_count;
	unsigned pending_service_info;
} vservice_core_state_t;

#define VSERVICE_CORE_RESET_STATE (vservice_core_state_t) { \
	.statenum = VSERVICE_CORE_STATE__RESET, \
	.pending_service_count = false, \
	.pending_service_info = 0 }

#define VSERVICE_CORE_STATE_IS_OFFLINE(state) ( \
	((state).statenum == VSERVICE_CORE_STATE_OFFLINE))
#define VSERVICE_CORE_STATE_IS_DISCONNECTED(state) ( \
	((state).statenum == VSERVICE_CORE_STATE_DISCONNECTED) || \
	((state).statenum == VSERVICE_CORE_STATE_DISCONNECTED__CONNECT))
#define VSERVICE_CORE_STATE_IS_CONNECTED(state) ( \
	((state).statenum == VSERVICE_CORE_STATE_CONNECTED) || \
	((state).statenum == VSERVICE_CORE_STATE_CONNECTED__DISCONNECT))

#define VSERVICE_CORE_STATE_VALID(state) \
	VSERVICE_CORE_STATE_IS_OFFLINE(state) ? ( \
		((state).pending_service_count == false) && \
		((state).pending_service_info == 0)) : \
	VSERVICE_CORE_STATE_IS_DISCONNECTED(state) ? ( \
		((state).pending_service_count == false) && \
		((state).pending_service_info == 0)) : \
	VSERVICE_CORE_STATE_IS_CONNECTED(state) ? true : \
	false)
