#ifndef _NETVSC_H
#define _NETVSC_H

/** @file
 *
 * Hyper-V network virtual service client
 *
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

/** Maximum supported NetVSC message length */
#define NETVSC_MTU 512

/** Maximum time to wait for a transaction to complete
 *
 * This is a policy decision.
 */
#define NETVSC_MAX_WAIT_MS 1000

/** Number of transmit ring entries
 *
 * Must be a power of two.  This is a policy decision.  This value
 * must be sufficiently small to guarantee that we never run out of
 * space in the VMBus outbound ring buffer.
 */
#define NETVSC_TX_NUM_DESC 32

/** RX data buffer page set ID
 *
 * This is a policy decision.
 */
#define NETVSC_RX_BUF_PAGESET 0xbead

/** RX data buffer length
 *
 * This is a policy decision.
 */
#define NETVSC_RX_BUF_LEN ( 16 * PAGE_SIZE )

/** Base transaction ID
 *
 * This is a policy decision.
 */
#define NETVSC_BASE_XID 0x18ae0000UL

/** Relative transaction IDs */
enum netvsc_xrid {
	/** Transmit descriptors (one per transmit buffer ID) */
	NETVSC_TX_BASE_XRID = 0,
	/** Initialisation */
	NETVSC_INIT_XRID = ( NETVSC_TX_BASE_XRID + NETVSC_TX_NUM_DESC ),
	/** NDIS version */
	NETVSC_NDIS_VERSION_XRID,
	/** Establish receive buffer */
	NETVSC_RX_ESTABLISH_XRID,
	/** Revoke receive buffer */
	NETVSC_RX_REVOKE_XRID,
};

/** NetVSC status codes */
enum netvsc_status {
	NETVSC_NONE = 0,
	NETVSC_OK = 1,
	NETVSC_FAIL = 2,
	NETVSC_TOO_NEW = 3,
	NETVSC_TOO_OLD = 4,
	NETVSC_BAD_PACKET = 5,
	NETVSC_BUSY = 6,
	NETVSC_UNSUPPORTED = 7,
};

/** NetVSC message header */
struct netvsc_header {
	/** Type */
	uint32_t type;
} __attribute__ (( packed ));

/** NetVSC initialisation message */
#define NETVSC_INIT_MSG 1

/** NetVSC initialisation message */
struct netvsc_init_message {
	/** Message header */
	struct netvsc_header header;
	/** Minimum supported protocol version */
	uint32_t min;
	/** Maximum supported protocol version */
	uint32_t max;
	/** Reserved */
	uint8_t reserved[20];
} __attribute__ (( packed ));

/** Oldest known NetVSC protocol version */
#define NETVSC_VERSION_1 2 /* sic */

/** NetVSC initialisation completion */
#define NETVSC_INIT_CMPLT 2

/** NetVSC initialisation completion */
struct netvsc_init_completion {
	/** Message header */
	struct netvsc_header header;
	/** Protocol version */
	uint32_t version;
	/** Maximum memory descriptor list length */
	uint32_t max_mdl_len;
	/** Status */
	uint32_t status;
	/** Reserved */
	uint8_t reserved[16];
} __attribute__ (( packed ));

/** NetVSC NDIS version message */
#define NETVSC_NDIS_VERSION_MSG 100

/** NetVSC NDIS version message */
struct netvsc_ndis_version_message {
	/** Message header */
	struct netvsc_header header;
	/** Major version */
	uint32_t major;
	/** Minor version */
	uint32_t minor;
	/** Reserved */
	uint8_t reserved[20];
} __attribute__ (( packed ));

/** NetVSC NDIS major version */
#define NETVSC_NDIS_MAJOR 6

/** NetVSC NDIS minor version */
#define NETVSC_NDIS_MINOR 1

/** NetVSC establish receive data buffer message */
#define NETVSC_RX_ESTABLISH_MSG 101

/** NetVSC establish receive data buffer completion */
#define NETVSC_RX_ESTABLISH_CMPLT 102

/** NetVSC revoke receive data buffer message */
#define NETVSC_RX_REVOKE_MSG 103

/** NetVSC establish transmit data buffer message */
#define NETVSC_TX_ESTABLISH_MSG 104

/** NetVSC establish transmit data buffer completion */
#define NETVSC_TX_ESTABLISH_CMPLT 105

/** NetVSC revoke transmit data buffer message */
#define NETVSC_TX_REVOKE_MSG 106

/** NetVSC establish data buffer message */
struct netvsc_establish_buffer_message {
	/** Message header */
	struct netvsc_header header;
	/** GPADL ID */
	uint32_t gpadl;
	/** Page set ID */
	uint16_t pageset;
	/** Reserved */
	uint8_t reserved[22];
} __attribute__ (( packed ));

/** NetVSC receive data buffer section */
struct netvsc_rx_buffer_section {
	/** Starting offset */
	uint32_t start;
	/** Subsection length */
	uint32_t len;
	/** Number of subsections */
	uint32_t count;
	/** Ending offset */
	uint32_t end;
} __attribute__ (( packed ));

/** NetVSC establish receive data buffer completion */
struct netvsc_rx_establish_buffer_completion {
	/** Message header */
	struct netvsc_header header;
	/** Status */
	uint32_t status;
	/** Number of sections (must be 1) */
	uint32_t count;
	/** Section descriptors */
	struct netvsc_rx_buffer_section section[1];
} __attribute__ (( packed ));

/** NetVSC establish transmit data buffer completion */
struct netvsc_tx_establish_buffer_completion {
	/** Message header */
	struct netvsc_header header;
	/** Status */
	uint32_t status;
	/** Section length */
	uint32_t len;
} __attribute__ (( packed ));

/** NetVSC revoke data buffer message */
struct netvsc_revoke_buffer_message {
	/** Message header */
	struct netvsc_header header;
	/** Page set ID */
	uint16_t pageset;
	/** Reserved */
	uint8_t reserved[26];
} __attribute__ (( packed ));

/** NetVSC RNDIS message */
#define NETVSC_RNDIS_MSG 107

/** NetVSC RNDIS message */
struct netvsc_rndis_message {
	/** Message header */
	struct netvsc_header header;
	/** RNDIS channel */
	uint32_t channel;
	/** Buffer index (or NETVSC_RNDIS_NO_BUFFER) */
	uint32_t buffer;
	/** Buffer length */
	uint32_t len;
	/** Reserved */
	uint8_t reserved[16];
} __attribute__ (( packed ));

/** RNDIS data channel (for RNDIS_PACKET_MSG only) */
#define NETVSC_RNDIS_DATA 0

/** RNDIS control channel (for all other RNDIS messages) */
#define NETVSC_RNDIS_CONTROL 1

/** "No buffer used" index */
#define NETVSC_RNDIS_NO_BUFFER 0xffffffffUL

/** A NetVSC descriptor ring */
struct netvsc_ring {
	/** Number of descriptors */
	unsigned int count;
	/** I/O buffers, indexed by buffer ID */
	struct io_buffer **iobufs;
	/** Buffer ID ring */
	uint8_t *ids;
	/** Buffer ID producer counter */
	unsigned int id_prod;
	/** Buffer ID consumer counter */
	unsigned int id_cons;
};

/**
 * Initialise descriptor ring
 *
 * @v ring		Descriptor ring
 * @v count		Maximum number of used descriptors
 * @v iobufs		I/O buffers
 * @v ids		Buffer IDs
 */
static inline __attribute__ (( always_inline )) void
netvsc_init_ring ( struct netvsc_ring *ring, unsigned int count,
		   struct io_buffer **iobufs, uint8_t *ids ) {

	ring->count = count;
	ring->iobufs = iobufs;
	ring->ids = ids;
}

/**
 * Check whether or not descriptor ring is full
 *
 * @v ring		Descriptor ring
 * @v is_full		Ring is full
 */
static inline __attribute__ (( always_inline )) int
netvsc_ring_is_full ( struct netvsc_ring *ring ) {
	unsigned int fill_level;

	fill_level = ( ring->id_prod - ring->id_cons );
	assert ( fill_level <= ring->count );
	return ( fill_level >= ring->count );
}

/**
 * Check whether or not descriptor ring is empty
 *
 * @v ring		Descriptor ring
 * @v is_empty		Ring is empty
 */
static inline __attribute__ (( always_inline )) int
netvsc_ring_is_empty ( struct netvsc_ring *ring ) {

	return ( ring->id_prod == ring->id_cons );
}

/** A NetVSC data buffer */
struct netvsc_buffer {
	/** Transfer page set */
	struct vmbus_xfer_pages pages;
	/** Establish data buffer message type */
	uint8_t establish_type;
	/** Establish data buffer relative transaction ID */
	uint8_t establish_xrid;
	/** Revoke data buffer message type */
	uint8_t revoke_type;
	/** Revoke data buffer relative transaction ID */
	uint8_t revoke_xrid;
	/** Buffer length */
	size_t len;
	/** Buffer */
	userptr_t data;
	/** GPADL ID */
	unsigned int gpadl;
};

/**
 * Initialise data buffer
 *
 * @v buffer		Data buffer
 * @v pageset		Page set ID
 * @v op		Page set operations
 * @v establish_type	Establish data buffer message type
 * @v establish_xrid	Establish data buffer relative transaction ID
 * @v revoke_type	Revoke data buffer message type
 * @v revoke_type	Revoke data buffer relative transaction ID
 * @v len		Required length
 */
static inline __attribute__ (( always_inline )) void
netvsc_init_buffer ( struct netvsc_buffer *buffer, uint16_t pageset,
		     struct vmbus_xfer_pages_operations *op,
		     uint8_t establish_type, uint8_t establish_xrid,
		     uint8_t revoke_type, uint8_t revoke_xrid, size_t len ) {

	buffer->pages.pageset = cpu_to_le16 ( pageset );
	buffer->pages.op = op;
	buffer->establish_type = establish_type;
	buffer->establish_xrid = establish_xrid;
	buffer->revoke_type = revoke_type;
	buffer->revoke_xrid = revoke_xrid;
	buffer->len = len;
}

/** A NetVSC device */
struct netvsc_device {
	/** VMBus device */
	struct vmbus_device *vmdev;
	/** RNDIS device */
	struct rndis_device *rndis;
	/** Name */
	const char *name;

	/** Transmit ring */
	struct netvsc_ring tx;
	/** Transmit buffer IDs */
	uint8_t tx_ids[NETVSC_TX_NUM_DESC];
	/** Transmit I/O buffers */
	struct io_buffer *tx_iobufs[NETVSC_TX_NUM_DESC];

	/** Receive buffer */
	struct netvsc_buffer rx;

	/** Relative transaction ID for current blocking transaction */
	unsigned int wait_xrid;
	/** Return status code for current blocking transaction */
	int wait_rc;
};

/**
 * Check if NetVSC device is obsolete
 *
 * @v netvsc		NetVSC device
 * @v is_obsolete	NetVSC device is obsolete
 *
 * Check if NetVSC device is obsolete (i.e. was opened before the most
 * recent Hyper-V reset).
 */
static inline __attribute__ (( always_inline )) int
netvsc_is_obsolete ( struct netvsc_device *netvsc ) {

	return vmbus_gpadl_is_obsolete ( netvsc->rx.gpadl );
}

#endif /* _NETVSC_H */
