/*-
 * Copyright(c) 2002-2011 Exar Corp.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification are permitted provided the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 *    3. Neither the name of the Exar Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
/*$FreeBSD: release/9.0.0/sys/dev/vxge/vxgehal/vxgehal-channel.h 221167 2011-04-28 14:33:15Z gnn $*/

#ifndef	VXGE_HAL_CHANNEL_H
#define	VXGE_HAL_CHANNEL_H

__EXTERN_BEGIN_DECLS

/*
 * __hal_dtr_h - Handle to the desriptor object used for nonoffload
 *		send or receive. Generic handle which can be with txd or rxd
 */
typedef void *__hal_dtr_h;

/*
 * enum __hal_channel_type_e - Enumerated channel types.
 * @VXGE_HAL_CHANNEL_TYPE_UNKNOWN: Unknown channel.
 * @VXGE_HAL_CHANNEL_TYPE_FIFO: fifo.
 * @VXGE_HAL_CHANNEL_TYPE_RING: ring.
 * @VXGE_HAL_CHANNEL_TYPE_SQ: Send Queue
 * @VXGE_HAL_CHANNEL_TYPE_SRQ: Receive Queue
 * @VXGE_HAL_CHANNEL_TYPE_CQRQ: Receive queue completion queue
 * @VXGE_HAL_CHANNEL_TYPE_UMQ: Up message queue
 * @VXGE_HAL_CHANNEL_TYPE_DMQ: Down message queue
 * @VXGE_HAL_CHANNEL_TYPE_MAX: Maximum number of HAL-supported
 * (and recognized) channel types. Currently: 7.
 *
 * Enumerated channel types. Currently there are only two link-layer
 * channels - X3100 fifo and X3100 ring. In the future the list will grow.
 */
typedef enum __hal_channel_type_e {
	VXGE_HAL_CHANNEL_TYPE_UNKNOWN			= 0,
	VXGE_HAL_CHANNEL_TYPE_FIFO			= 1,
	VXGE_HAL_CHANNEL_TYPE_RING			= 2,
	VXGE_HAL_CHANNEL_TYPE_SEND_QUEUE		= 3,
	VXGE_HAL_CHANNEL_TYPE_RECEIVE_QUEUE		= 4,
	VXGE_HAL_CHANNEL_TYPE_COMPLETION_QUEUE		= 5,
	VXGE_HAL_CHANNEL_TYPE_UP_MESSAGE_QUEUE		= 6,
	VXGE_HAL_CHANNEL_TYPE_DOWN_MESSAGE_QUEUE	= 7,
	VXGE_HAL_CHANNEL_TYPE_MAX			= 8
} __hal_channel_type_e;

/*
 * __hal_dtr_item_t
 * @dtr: Pointer to the descriptors that contains the dma data
 *		to/from the device.
 * @hal_priv: HAL Private data related to the dtr.
 * @uld_priv: ULD Private data related to the dtr.
 */
typedef struct __hal_dtr_item_t {
	void   *dtr;
	void   *hal_priv;
	void   *uld_priv;
	u32	state;
#define	VXGE_HAL_CHANNEL_DTR_FREE	0
#define	VXGE_HAL_CHANNEL_DTR_RESERVED	1
#define	VXGE_HAL_CHANNEL_DTR_POSTED	2
#define	VXGE_HAL_CHANNEL_DTR_COMPLETED	3
} __hal_dtr_item_t;

/*
 * __hal_channel_t
 * @item: List item; used to maintain a list of open channels.
 * @type: Channel type. See vxge_hal_channel_type_e {}.
 * @devh: Device handle. HAL device object that contains _this_ channel.
 * @pdev: PCI Device object
 * @vph: Virtual path handle. Virtual Path Object that contains _this_ channel.
 * @length: Channel length. Currently allocated number of descriptors.
 *	The channel length "grows" when more descriptors get allocated.
 *	See _hal_mempool_grow.
 * @dtr_arr: Dtr array. Contains descriptors posted to the channel and their
 *	private data.
 *	Note that at any point in time @dtr_arr contains 3 types of
 *	descriptors:
 *	1) posted but not yet consumed by X3100 device;
 *	2) consumed but not yet completed;
 *	3) completed.
 * @post_index: Post index. At any point in time points on the
 *	position in the channel, which'll contain next to-be-posted
 *	descriptor.
 * @compl_index: Completion index. At any point in time points on the
 *	position in the channel, which will contain next
 *	to-be-completed descriptor.
 * @reserve_index: Reserve index. At any point in time points on the
 *	position in the channel, which will contain next
 *	to-be-reserved descriptor.
 * @free_dtr_count: Number of dtrs free.
 * @posted_dtr_count: Number of dtrs posted
 * @post_lock: Lock to serialize multiple concurrent "posters" of descriptors
 *		on the given channel.
 * @poll_bytes: Poll bytes.
 * @per_dtr_space: Per-descriptor space (in bytes) that channel user can utilize
 *		to store per-operation control information.
 * @stats: Pointer to common statistics
 * @userdata: Per-channel opaque (void *) user-defined context, which may be
 *	upper-layer driver object, ULP connection, etc.
 *	Once channel is open, @userdata is passed back to user via
 *	vxge_hal_channel_callback_f.
 *
 * HAL channel object.
 *
 * See also: vxge_hal_channel_type_e {}, vxge_hal_channel_flag_e
 */
typedef struct __hal_channel_t {
	vxge_list_t		item;
	__hal_channel_type_e	type;
	vxge_hal_device_h	devh;
	pci_dev_h		pdev;
	vxge_hal_vpath_h	vph;
	u32			length;
	u32			is_initd;
	__hal_dtr_item_t	*dtr_arr;
	u32			compl_index __vxge_os_attr_cacheline_aligned;
	u32			reserve_index __vxge_os_attr_cacheline_aligned;
	spinlock_t		post_lock;
	u32			poll_bytes;
	u32			per_dtr_space;
	vxge_hal_vpath_stats_sw_common_info_t *stats;
	void			*userdata;
} __hal_channel_t __vxge_os_attr_cacheline_aligned;

#define	__hal_channel_is_posted_dtr(channel, index) \
	    ((channel)->dtr_arr[index].state == VXGE_HAL_CHANNEL_DTR_POSTED)

#define	__hal_channel_for_each_posted_dtr(channel, dtrh, index) \
	for (index = (channel)->compl_index,\
	    dtrh = (channel)->dtr_arr[index].dtr; \
	    (index < (channel)->reserve_index) && \
	    ((channel)->dtr_arr[index].state == VXGE_HAL_CHANNEL_DTR_POSTED); \
	    index = (++index == (channel)->length)? 0 : index, \
	    dtrh = (channel)->dtr_arr[index].dtr)

#define	__hal_channel_for_each_dtr(channel, dtrh, index) \
	for (index = 0, dtrh = (channel)->dtr_arr[index].dtr; \
	    index < (channel)->length; \
	    dtrh = ((++index == (channel)->length)? 0 : \
	    (channel)->dtr_arr[index].dtr))

#define	__hal_channel_free_dtr_count(channel)			\
	(((channel)->reserve_index < (channel)->compl_index) ?	\
	((channel)->compl_index - (channel)->reserve_index) :	\
	(((channel)->length - (channel)->reserve_index) + \
	(channel)->reserve_index))

/* ========================== CHANNEL PRIVATE API ========================= */

__hal_channel_t *
vxge_hal_channel_allocate(
    vxge_hal_device_h devh,
    vxge_hal_vpath_h vph,
    __hal_channel_type_e type,
    u32 length,
    u32 per_dtr_space,
    void *userdata);

void
vxge_hal_channel_free(
    __hal_channel_t *channel);

vxge_hal_status_e
vxge_hal_channel_initialize(
    __hal_channel_t *channel);

vxge_hal_status_e
__hal_channel_reset(
    __hal_channel_t *channel);

void
vxge_hal_channel_terminate(
    __hal_channel_t *channel);

void
__hal_channel_init_pending_list(
    vxge_hal_device_h devh);

void
__hal_channel_insert_pending_list(
    __hal_channel_t * channel);

void
__hal_channel_process_pending_list(
    vxge_hal_device_h devhv);

void
__hal_channel_destroy_pending_list(
    vxge_hal_device_h devh);

#if defined(VXGE_DEBUG_FP) && (VXGE_DEBUG_FP & VXGE_DEBUG_FP_CHANNEL)
#define	__HAL_STATIC_CHANNEL
#define	__HAL_INLINE_CHANNEL
#else	/* VXGE_FASTPATH_EXTERN */
#define	__HAL_STATIC_CHANNEL static
#define	__HAL_INLINE_CHANNEL inline
#endif	/* VXGE_FASTPATH_INLINE */

/* ========================== CHANNEL Fast Path API ========================= */
/*
 * __hal_channel_dtr_reserve- Reserve a dtr from the channel
 * @channelh: Channel
 * @dtrh: Buffer to return the DTR pointer
 *
 * Reserve a dtr from the reserve array.
 *
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL vxge_hal_status_e
/* LINTED */
__hal_channel_dtr_reserve(__hal_channel_t *channel, __hal_dtr_h *dtrh)
{
	vxge_hal_status_e status = VXGE_HAL_INF_OUT_OF_DESCRIPTORS;

	*dtrh = NULL;

	if (channel->dtr_arr[channel->reserve_index].state ==
	    VXGE_HAL_CHANNEL_DTR_FREE) {

		*dtrh = channel->dtr_arr[channel->reserve_index].dtr;

		channel->dtr_arr[channel->reserve_index].state =
		    VXGE_HAL_CHANNEL_DTR_RESERVED;

		if (++channel->reserve_index == channel->length)
			channel->reserve_index = 0;

		status = VXGE_HAL_OK;

	} else {

#if (VXGE_COMPONENT_HAL_CHANNEL & VXGE_DEBUG_MODULE_MASK)
		__hal_device_t *hldev = (__hal_device_t *) channel->devh;

		vxge_hal_info_log_channel("channel %d is full!", channel->type);
#endif

		channel->stats->full_cnt++;
	}

	return (status);
}

/*
 * __hal_channel_dtr_restore - Restores a dtr to the channel
 * @channelh: Channel
 * @dtr: DTR pointer
 *
 * Returns a dtr back to reserve array.
 *
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
/* LINTED */
__hal_channel_dtr_restore(__hal_channel_t *channel, __hal_dtr_h dtrh)
{
	u32 dtr_index;

	/*
	 * restore a previously allocated dtrh at current offset and update
	 * the available reserve length accordingly. If dtrh is null just
	 * update the reserve length, only
	 */

	if (channel->reserve_index == 0)
		dtr_index = channel->length;
	else
		dtr_index = channel->reserve_index - 1;

	if ((channel->dtr_arr[dtr_index].dtr == dtrh)) {

		channel->reserve_index = dtr_index;
		channel->dtr_arr[dtr_index].state = VXGE_HAL_CHANNEL_DTR_FREE;

#if (VXGE_COMPONENT_HAL_CHANNEL & VXGE_DEBUG_MODULE_MASK)

		__hal_device_t *hldev = (__hal_device_t *) channel->devh;
		vxge_hal_info_log_channel("dtrh 0x"VXGE_OS_STXFMT" \
		    restored for " "channel %d at reserve index %d, ",
		    (ptr_t) dtrh, channel->type,
		    channel->reserve_index);
#endif
	}
}

/*
 * __hal_channel_dtr_post - Post a dtr to the channel
 * @channelh: Channel
 * @dtr: DTR pointer
 *
 * Posts a dtr to work array.
 *
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
/* LINTED */
__hal_channel_dtr_post(__hal_channel_t *channel, u32 dtr_index)
{
	channel->dtr_arr[dtr_index].state =
	    VXGE_HAL_CHANNEL_DTR_POSTED;
}

/*
 * __hal_channel_dtr_try_complete - Returns next completed dtr
 * @channelh: Channel
 * @dtr: Buffer to return the next completed DTR pointer
 *
 * Returns the next completed dtr with out removing it from work array
 *
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
/* LINTED */
__hal_channel_dtr_try_complete(__hal_channel_t *channel, __hal_dtr_h *dtrh)
{
	vxge_assert(channel->dtr_arr);
	vxge_assert(channel->compl_index < channel->length);

	if (channel->dtr_arr[channel->compl_index].state ==
	    VXGE_HAL_CHANNEL_DTR_POSTED)
		*dtrh = channel->dtr_arr[channel->compl_index].dtr;
	else
		*dtrh = NULL;
}

/*
 * __hal_channel_dtr_complete - Removes next completed dtr from the work array
 * @channelh: Channel
 *
 * Removes the next completed dtr from work array
 *
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
/* LINTED */
__hal_channel_dtr_complete(__hal_channel_t *channel)
{
	channel->dtr_arr[channel->compl_index].state =
	VXGE_HAL_CHANNEL_DTR_COMPLETED;

	if (++channel->compl_index == channel->length)
		channel->compl_index = 0;

	channel->stats->total_compl_cnt++;
}

/*
 * __hal_channel_dtr_free - Frees a dtr
 * @channelh: Channel
 * @index:  Index of DTR
 *
 * Returns the dtr to free array
 *
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
/* LINTED */
__hal_channel_dtr_free(__hal_channel_t *channel, u32 dtr_index)
{
	channel->dtr_arr[dtr_index].state =
	    VXGE_HAL_CHANNEL_DTR_FREE;
}

__EXTERN_END_DECLS

#endif	/* VXGE_HAL_CHANNEL_H */
