root/usr/src/uts/common/io/xge/hal/xgehal/xgehal-device-fp.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright (c) 2002-2006 Neterion, Inc.
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifdef XGE_DEBUG_FP
#include "xgehal-device.h"
#endif

#include "xgehal-ring.h"
#include "xgehal-fifo.h"

/**
 * xge_hal_device_bar0 - Get BAR0 mapped address.
 * @hldev: HAL device handle.
 *
 * Returns:     BAR0 address of the     specified device.
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     char *
xge_hal_device_bar0(xge_hal_device_t *hldev)
{
        return hldev->bar0;
}

/**
 * xge_hal_device_isrbar0 -     Get     BAR0 mapped     address.
 * @hldev: HAL device handle.
 *
 * Returns:     BAR0 address of the     specified device.
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     char *
xge_hal_device_isrbar0(xge_hal_device_t *hldev)
{
        return hldev->isrbar0;
}

/**
 * xge_hal_device_bar1 - Get BAR1 mapped address.
 * @hldev: HAL device handle.
 *
 * Returns:     BAR1 address of the     specified device.
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     char *
xge_hal_device_bar1(xge_hal_device_t *hldev)
{
        return hldev->bar1;
}

/**
 * xge_hal_device_bar0_set - Set BAR0 mapped address.
 * @hldev: HAL device handle.
 * @bar0: BAR0 mapped address.
 * * Set BAR0 address in the HAL device object.
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_bar0_set(xge_hal_device_t *hldev, char *bar0)
{
        xge_assert(bar0);
        hldev->bar0     = bar0;
}

/**
 * xge_hal_device_isrbar0_set - Set     BAR0 mapped     address.
 * @hldev: HAL device handle.
 * @isrbar0: BAR0 mapped address.
 * * Set BAR0 address in the HAL device object.
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_isrbar0_set(xge_hal_device_t     *hldev, char *isrbar0)
{
        xge_assert(isrbar0);
        hldev->isrbar0 = isrbar0;
}

/**
 * xge_hal_device_bar1_set - Set BAR1 mapped address.
 * @hldev: HAL device handle.
 * @channelh: Channel handle.
 * @bar1: BAR1 mapped address.
 *
 * Set BAR1     address for     the     given channel.
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_bar1_set(xge_hal_device_t *hldev, xge_hal_channel_h channelh,
                           char *bar1)
{
        xge_hal_fifo_t *fifo = (xge_hal_fifo_t *)channelh;

        xge_assert(bar1);
        xge_assert(fifo);

        /* Initializing the     BAR1 address as the     start of
         * the FIFO     queue pointer and as a location of FIFO control
         * word. */
        fifo->hw_pair =
                        (xge_hal_fifo_hw_pair_t *) (bar1 +
                                (fifo->channel.post_qid * XGE_HAL_FIFO_HW_PAIR_OFFSET));
        hldev->bar1     = bar1;
}


/**
 * xge_hal_device_rev - Get     Device revision number.
 * @hldev: HAL device handle.
 *
 * Returns:     Device revision number
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     int
xge_hal_device_rev(xge_hal_device_t     *hldev)
{
                return hldev->revision;
}


/**
 * xge_hal_device_begin_irq     - Begin IRQ     processing.
 * @hldev: HAL device handle.
 * @reason:     "Reason" for the interrupt,     the     value of Xframe's
 *                      general_int_status register.
 *
 * The function performs two actions, It first checks whether (shared IRQ) the
 * interrupt was raised by the device. Next, it masks the device interrupts.
 *
 * Note:
 * xge_hal_device_begin_irq() does not flush MMIO writes through the
 * bridge. Therefore, two back-to-back interrupts are potentially possible.
 * It is the responsibility     of the ULD to make sure that only one
 * xge_hal_device_continue_irq() runs at a time.
 *
 * Returns:     0, if the interrupt     is not "ours" (note     that in this case the
 * device remain enabled).
 * Otherwise, xge_hal_device_begin_irq() returns 64bit general adapter
 * status.
 * See also: xge_hal_device_handle_irq()
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     xge_hal_status_e
xge_hal_device_begin_irq(xge_hal_device_t *hldev, u64 *reason)
{
        u64     val64;
        xge_hal_pci_bar0_t *isrbar0     = (xge_hal_pci_bar0_t *)hldev->isrbar0;

        hldev->stats.sw_dev_info_stats.total_intr_cnt++;

        val64 = xge_os_pio_mem_read64(hldev->pdev,
                                  hldev->regh0, &isrbar0->general_int_status);
        if (xge_os_unlikely(!val64)) {
                /* not Xframe interrupt */
                hldev->stats.sw_dev_info_stats.not_xge_intr_cnt++;
                *reason = 0;
                        return XGE_HAL_ERR_WRONG_IRQ;
        }

        if (xge_os_unlikely(val64 == XGE_HAL_ALL_FOXES)) {
                                u64     adapter_status =
                                                xge_os_pio_mem_read64(hldev->pdev, hldev->regh0,
                                                  &isrbar0->adapter_status);
                                if (adapter_status == XGE_HAL_ALL_FOXES)  {
                                        (void) xge_queue_produce(hldev->queueh,
                                                 XGE_HAL_EVENT_SLOT_FREEZE,
                                                 hldev,
                                                 1,      /*     critical: slot freeze */
                                                 sizeof(u64),
                                                 (void*)&adapter_status);
                        *reason = 0;
                        return XGE_HAL_ERR_CRITICAL;
                }
        }

        *reason = val64;

        /* separate     fast path, i.e. no errors */
        if (val64 &     XGE_HAL_GEN_INTR_RXTRAFFIC)     {
                hldev->stats.sw_dev_info_stats.rx_traffic_intr_cnt++;
                return XGE_HAL_OK;
        }
        if (val64 &     XGE_HAL_GEN_INTR_TXTRAFFIC)     {
                hldev->stats.sw_dev_info_stats.tx_traffic_intr_cnt++;
                return XGE_HAL_OK;
        }

        hldev->stats.sw_dev_info_stats.not_traffic_intr_cnt++;
        if (xge_os_unlikely(val64 &     XGE_HAL_GEN_INTR_TXPIC)) {
                xge_hal_status_e status;
                hldev->stats.sw_dev_info_stats.txpic_intr_cnt++;
                status = __hal_device_handle_txpic(hldev, val64);
                if (status != XGE_HAL_OK) {
                        return status;
                }
        }

        if (xge_os_unlikely(val64 &     XGE_HAL_GEN_INTR_TXDMA)) {
                xge_hal_status_e status;
                hldev->stats.sw_dev_info_stats.txdma_intr_cnt++;
                status = __hal_device_handle_txdma(hldev, val64);
                if (status != XGE_HAL_OK) {
                        return status;
                }
        }

        if (xge_os_unlikely(val64 &     XGE_HAL_GEN_INTR_TXMAC)) {
                xge_hal_status_e status;
                hldev->stats.sw_dev_info_stats.txmac_intr_cnt++;
                status = __hal_device_handle_txmac(hldev, val64);
                if (status != XGE_HAL_OK) {
                        return status;
                }
        }

        if (xge_os_unlikely(val64 &     XGE_HAL_GEN_INTR_TXXGXS)) {
                xge_hal_status_e status;
                hldev->stats.sw_dev_info_stats.txxgxs_intr_cnt++;
                status = __hal_device_handle_txxgxs(hldev, val64);
                if (status != XGE_HAL_OK) {
                        return status;
                }
        }

        if (xge_os_unlikely(val64 &     XGE_HAL_GEN_INTR_RXPIC)) {
                xge_hal_status_e status;
                hldev->stats.sw_dev_info_stats.rxpic_intr_cnt++;
                status = __hal_device_handle_rxpic(hldev, val64);
                if (status != XGE_HAL_OK) {
                        return status;
                }
        }

        if (xge_os_unlikely(val64 &     XGE_HAL_GEN_INTR_RXDMA)) {
                xge_hal_status_e status;
                hldev->stats.sw_dev_info_stats.rxdma_intr_cnt++;
                status = __hal_device_handle_rxdma(hldev, val64);
                if (status != XGE_HAL_OK) {
                        return status;
                }
        }

        if (xge_os_unlikely(val64 &     XGE_HAL_GEN_INTR_RXMAC)) {
                xge_hal_status_e status;
                hldev->stats.sw_dev_info_stats.rxmac_intr_cnt++;
                status = __hal_device_handle_rxmac(hldev, val64);
                if (status != XGE_HAL_OK) {
                        return status;
                }
        }

        if (xge_os_unlikely(val64 &     XGE_HAL_GEN_INTR_RXXGXS)) {
                xge_hal_status_e status;
                hldev->stats.sw_dev_info_stats.rxxgxs_intr_cnt++;
                status = __hal_device_handle_rxxgxs(hldev, val64);
                if (status != XGE_HAL_OK) {
                        return status;
                }
        }

        if (xge_os_unlikely(val64 &     XGE_HAL_GEN_INTR_MC)) {
                xge_hal_status_e status;
                hldev->stats.sw_dev_info_stats.mc_intr_cnt++;
                status = __hal_device_handle_mc(hldev, val64);
                if (status != XGE_HAL_OK) {
                        return status;
                }
        }

        return XGE_HAL_OK;
}

/**
 * xge_hal_device_clear_rx - Acknowledge (that is, clear) the
 * condition that has caused the RX     interrupt.
 * @hldev: HAL device handle.
 *
 * Acknowledge (that is, clear) the     condition that has caused
 * the Rx interrupt.
 * See also: xge_hal_device_begin_irq(), xge_hal_device_continue_irq(),
 * xge_hal_device_clear_tx(), xge_hal_device_mask_rx().
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_clear_rx(xge_hal_device_t *hldev)
{
        xge_hal_pci_bar0_t *isrbar0     = (xge_hal_pci_bar0_t *)hldev->isrbar0;

        xge_os_pio_mem_write64(hldev->pdev,     hldev->regh0,
                                 0xFFFFFFFFFFFFFFFFULL,
                                 &isrbar0->rx_traffic_int);
}

/**
 * xge_hal_device_clear_tx - Acknowledge (that is, clear) the
 * condition that has caused the TX     interrupt.
 * @hldev: HAL device handle.
 *
 * Acknowledge (that is, clear) the     condition that has caused
 * the Tx interrupt.
 * See also: xge_hal_device_begin_irq(), xge_hal_device_continue_irq(),
 * xge_hal_device_clear_rx(), xge_hal_device_mask_tx().
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_clear_tx(xge_hal_device_t *hldev)
{
        xge_hal_pci_bar0_t *isrbar0     = (xge_hal_pci_bar0_t *)hldev->isrbar0;

        xge_os_pio_mem_write64(hldev->pdev,     hldev->regh0,
                                 0xFFFFFFFFFFFFFFFFULL,
                                 &isrbar0->tx_traffic_int);
}

/**
 * xge_hal_device_poll_rx_channel -     Poll Rx channel for     completed
 * descriptors and process the same.
 * @channel: HAL channel.
 * @got_rx: Buffer to return the flag set if receive interrupt is occured
 *
 * The function polls the Rx channel for the completed  descriptors     and     calls
 * the upper-layer driver (ULD) via     supplied completion     callback.
 *
 * Returns:     XGE_HAL_OK,     if the polling is completed     successful.
 * XGE_HAL_COMPLETIONS_REMAIN: There are still more     completed
 * descriptors available which are yet to be processed.
 *
 * See also: xge_hal_device_poll_tx_channel()
 */
__HAL_STATIC_DEVICE __HAL_INLINE_DEVICE xge_hal_status_e
xge_hal_device_poll_rx_channel(xge_hal_channel_t *channel, int *got_rx)
{
        xge_hal_status_e ret = XGE_HAL_OK;
        xge_hal_dtr_h first_dtrh;
        xge_hal_device_t *hldev = (xge_hal_device_t *)channel->devh;
        u8 t_code;
        int got_bytes;

        /* for each opened rx channel */
        got_bytes = *got_rx = 0;
        ((xge_hal_ring_t *)channel)->cmpl_cnt = 0;
        channel->poll_bytes = 0;
        if ((ret = xge_hal_ring_dtr_next_completed (channel, &first_dtrh,
                &t_code)) == XGE_HAL_OK) {
                if (channel->callback(channel, first_dtrh,
                        t_code, channel->userdata) != XGE_HAL_OK) {
                        (*got_rx) += ((xge_hal_ring_t *)channel)->cmpl_cnt + 1;
                        got_bytes += channel->poll_bytes + 1;
                        ret = XGE_HAL_COMPLETIONS_REMAIN;
                } else {
                        (*got_rx) += ((xge_hal_ring_t *)channel)->cmpl_cnt + 1;
                        got_bytes += channel->poll_bytes + 1;
                }
        }

        if (*got_rx) {
                hldev->irq_workload_rxd[channel->post_qid] += *got_rx;
                hldev->irq_workload_rxcnt[channel->post_qid] ++;
        }
        hldev->irq_workload_rxlen[channel->post_qid] += got_bytes;

        return ret;
}

/**
 * xge_hal_device_poll_tx_channel -     Poll Tx channel for     completed
 * descriptors and process the same.
 * @channel: HAL channel.
 * @got_tx: Buffer to return the flag set if transmit interrupt is occured
 *
 * The function polls the Tx channel for the completed  descriptors     and     calls
 * the upper-layer driver (ULD) via     supplied completion     callback.
 *
 * Returns:     XGE_HAL_OK,     if the polling is completed     successful.
 * XGE_HAL_COMPLETIONS_REMAIN: There are still more     completed
 * descriptors available which are yet to be processed.
 *
 * See also: xge_hal_device_poll_rx_channel().
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE xge_hal_status_e
xge_hal_device_poll_tx_channel(xge_hal_channel_t *channel, int *got_tx)
{
        xge_hal_dtr_h first_dtrh;
        xge_hal_device_t *hldev = (xge_hal_device_t *)channel->devh;
        u8 t_code;
        int got_bytes;

        /* for each opened tx channel */
        got_bytes = *got_tx = 0;
        channel->poll_bytes = 0;
        if (xge_hal_fifo_dtr_next_completed (channel, &first_dtrh,
                &t_code) ==     XGE_HAL_OK)     {
                if (channel->callback(channel, first_dtrh,
                        t_code, channel->userdata) != XGE_HAL_OK) {
                        (*got_tx)++;
                        got_bytes += channel->poll_bytes + 1;
                        return XGE_HAL_COMPLETIONS_REMAIN;
                }
                (*got_tx)++;
                got_bytes += channel->poll_bytes + 1;
        }

        if (*got_tx) {
                hldev->irq_workload_txd[channel->post_qid] += *got_tx;
                hldev->irq_workload_txcnt[channel->post_qid] ++;
        }
        hldev->irq_workload_txlen[channel->post_qid] += got_bytes;

        return XGE_HAL_OK;
}

/**
 * xge_hal_device_poll_rx_channels - Poll Rx channels for completed
 * descriptors and process the same.
 * @hldev: HAL device handle.
 * @got_rx: Buffer to return flag set if receive is ready
 *
 * The function polls the Rx channels for the completed descriptors     and     calls
 * the upper-layer driver (ULD) via     supplied completion     callback.
 *
 * Returns:     XGE_HAL_OK,     if the polling is completed     successful.
 * XGE_HAL_COMPLETIONS_REMAIN: There are still more     completed
 * descriptors available which are yet to be processed.
 *
 * See also: xge_hal_device_poll_tx_channels(), xge_hal_device_continue_irq().
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     xge_hal_status_e
xge_hal_device_poll_rx_channels(xge_hal_device_t *hldev, int *got_rx)
{
        xge_list_t *item;
        xge_hal_channel_t *channel;

        /* for each opened rx channel */
        xge_list_for_each(item, &hldev->ring_channels) {
                if (hldev->terminating)
                        return XGE_HAL_OK;
                channel = xge_container_of(item, xge_hal_channel_t,     item);
                if (!(channel->flags & XGE_HAL_CHANNEL_FLAG_USE_RX_POLLING)) {
                        (void) xge_hal_device_poll_rx_channel(channel, got_rx);
                }
        }

        return XGE_HAL_OK;
}

/**
 * xge_hal_device_poll_tx_channels - Poll Tx channels for completed
 * descriptors and process the same.
 * @hldev: HAL device handle.
 * @got_tx: Buffer to return flag set if transmit is ready
 *
 * The function polls the Tx channels for the completed descriptors     and     calls
 * the upper-layer driver (ULD) via     supplied completion     callback.
 *
 * Returns:     XGE_HAL_OK,     if the polling is completed     successful.
 * XGE_HAL_COMPLETIONS_REMAIN: There are still more     completed
 * descriptors available which are yet to be processed.
 *
 * See also: xge_hal_device_poll_rx_channels(), xge_hal_device_continue_irq().
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     xge_hal_status_e
xge_hal_device_poll_tx_channels(xge_hal_device_t *hldev, int *got_tx)
{
        xge_list_t *item;
        xge_hal_channel_t *channel;

        /* for each opened tx channel */
        xge_list_for_each(item, &hldev->fifo_channels) {
                if (hldev->terminating)
                        return XGE_HAL_OK;
                channel = xge_container_of(item, xge_hal_channel_t, item);
                (void) xge_hal_device_poll_tx_channel(channel, got_tx);
        }

        return XGE_HAL_OK;
}

/**
 *
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_rx_channel_enable_polling(xge_hal_channel_t *channel)
{
        channel->flags |= XGE_HAL_CHANNEL_FLAG_USE_RX_POLLING;
}

__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_rx_channel_disable_polling(xge_hal_channel_t *channel)
{
        channel->flags &= ~XGE_HAL_CHANNEL_FLAG_USE_RX_POLLING;
}

/**
 * xge_hal_device_mask_tx -     Mask Tx interrupts.
 * @hldev: HAL device handle.
 *
 * Mask Tx device interrupts.
 *
 * See also: xge_hal_device_unmask_tx(), xge_hal_device_mask_rx(),
 * xge_hal_device_clear_tx().
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_mask_tx(xge_hal_device_t *hldev)
{
        xge_hal_pci_bar0_t *isrbar0     = (xge_hal_pci_bar0_t *)hldev->isrbar0;

        xge_os_pio_mem_write64(hldev->pdev,     hldev->regh0,
                                   0xFFFFFFFFFFFFFFFFULL,
                                   &isrbar0->tx_traffic_mask);
}

/**
 * xge_hal_device_mask_rx -     Mask Rx interrupts.
 * @hldev: HAL device handle.
 *
 * Mask Rx device interrupts.
 *
 * See also: xge_hal_device_unmask_rx(), xge_hal_device_mask_tx(),
 * xge_hal_device_clear_rx().
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_mask_rx(xge_hal_device_t *hldev)
{
        xge_hal_pci_bar0_t *isrbar0     = (xge_hal_pci_bar0_t *)hldev->isrbar0;

        xge_os_pio_mem_write64(hldev->pdev,     hldev->regh0,
                                   0xFFFFFFFFFFFFFFFFULL,
                                   &isrbar0->rx_traffic_mask);
}

/**
 * xge_hal_device_mask_all - Mask all device interrupts.
 * @hldev: HAL device handle.
 *
 * Mask all     device interrupts.
 *
 * See also: xge_hal_device_unmask_all()
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_mask_all(xge_hal_device_t *hldev)
{
        xge_hal_pci_bar0_t *isrbar0     = (xge_hal_pci_bar0_t *)hldev->isrbar0;

        xge_os_pio_mem_write64(hldev->pdev,     hldev->regh0,
                                   0xFFFFFFFFFFFFFFFFULL,
                                   &isrbar0->general_int_mask);
}

/**
 * xge_hal_device_unmask_tx     - Unmask Tx     interrupts.
 * @hldev: HAL device handle.
 *
 * Unmask Tx device     interrupts.
 *
 * See also: xge_hal_device_mask_tx(), xge_hal_device_clear_tx().
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_unmask_tx(xge_hal_device_t *hldev)
{
        xge_hal_pci_bar0_t *isrbar0     = (xge_hal_pci_bar0_t *)hldev->isrbar0;

        xge_os_pio_mem_write64(hldev->pdev,     hldev->regh0,
                                   0x0ULL,
                                   &isrbar0->tx_traffic_mask);
}

/**
 * xge_hal_device_unmask_rx     - Unmask Rx     interrupts.
 * @hldev: HAL device handle.
 *
 * Unmask Rx device     interrupts.
 *
 * See also: xge_hal_device_mask_rx(), xge_hal_device_clear_rx().
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_unmask_rx(xge_hal_device_t *hldev)
{
        xge_hal_pci_bar0_t *isrbar0     = (xge_hal_pci_bar0_t *)hldev->isrbar0;

        xge_os_pio_mem_write64(hldev->pdev,     hldev->regh0,
                                   0x0ULL,
                                   &isrbar0->rx_traffic_mask);
}

/**
 * xge_hal_device_unmask_all - Unmask all device interrupts.
 * @hldev: HAL device handle.
 *
 * Unmask all device interrupts.
 *
 * See also: xge_hal_device_mask_all()
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     void
xge_hal_device_unmask_all(xge_hal_device_t *hldev)
{
        xge_hal_pci_bar0_t *isrbar0     = (xge_hal_pci_bar0_t *)hldev->isrbar0;

        xge_os_pio_mem_write64(hldev->pdev,     hldev->regh0,
                                   0x0ULL,
                                   &isrbar0->general_int_mask);
}


/**
 * xge_hal_device_continue_irq - Continue handling IRQ: process all
 * completed descriptors.
 * @hldev: HAL device handle.
 *
 * Process completed descriptors and unmask     the     device interrupts.
 *
 * The xge_hal_device_continue_irq() walks all open     channels
 * and calls upper-layer driver (ULD) via supplied completion
 * callback. Note that the completion callback is specified     at channel open
 * time, see xge_hal_channel_open().
 *
 * Note that the xge_hal_device_continue_irq is part of the     _fast_ path.
 * To optimize the processing, the function     does _not_ check for
 * errors and alarms.
 *
 * The latter is done in a polling fashion,     via     xge_hal_device_poll().
 *
 * Returns:     XGE_HAL_OK.
 *
 * See also: xge_hal_device_handle_irq(), xge_hal_device_poll(),
 * xge_hal_ring_dtr_next_completed(),
 * xge_hal_fifo_dtr_next_completed(), xge_hal_channel_callback_f{}.
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     xge_hal_status_e
xge_hal_device_continue_irq(xge_hal_device_t *hldev)
{
        int     got_rx = 1,     got_tx = 1;
        int     isr_polling_cnt = hldev->config.isr_polling_cnt;
        int     count = 0;

        do
        {
                if (got_rx)
                        (void) xge_hal_device_poll_rx_channels(hldev, &got_rx);
                if (got_tx && hldev->tti_enabled)
                        (void) xge_hal_device_poll_tx_channels(hldev, &got_tx);

                if (!got_rx && !got_tx)
                        break;

                count += (got_rx + got_tx);
        }while (isr_polling_cnt--);

        if (!count)
                hldev->stats.sw_dev_info_stats.not_traffic_intr_cnt++;

        return XGE_HAL_OK;
}

/**
 * xge_hal_device_handle_irq - Handle device IRQ.
 * @hldev: HAL device handle.
 *
 * Perform the complete handling of     the     line interrupt. The     function
 * performs     two     calls.
 * First it     uses xge_hal_device_begin_irq() to      check the reason for
 * the interrupt and mask the device interrupts.
 * Second, it calls     xge_hal_device_continue_irq() to process all
 * completed descriptors and re-enable the interrupts.
 *
 * Returns:     XGE_HAL_OK - success;
 * XGE_HAL_ERR_WRONG_IRQ - (shared)     IRQ     produced by     other device.
 *
 * See also: xge_hal_device_begin_irq(), xge_hal_device_continue_irq().
 */
__HAL_STATIC_DEVICE     __HAL_INLINE_DEVICE     xge_hal_status_e
xge_hal_device_handle_irq(xge_hal_device_t *hldev)
{
        u64     reason;
        xge_hal_status_e status;

        xge_hal_device_mask_all(hldev);

        status = xge_hal_device_begin_irq(hldev, &reason);
        if (status != XGE_HAL_OK) {
                xge_hal_device_unmask_all(hldev);
                return status;
        }

        if (reason & XGE_HAL_GEN_INTR_RXTRAFFIC) {
                xge_hal_device_clear_rx(hldev);
        }

        status = xge_hal_device_continue_irq(hldev);

        xge_hal_device_clear_tx(hldev);

        xge_hal_device_unmask_all(hldev);

        return status;
}

#if     defined(XGE_HAL_CONFIG_LRO)


__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
__hal_lro_check_for_session_match(lro_t *lro, tcplro_t *tcp, iplro_t *ip)
{

        /* Match Source address field */
        if ((lro->ip_hdr->saddr != ip->saddr))
                return XGE_HAL_FAIL;

        /* Match Destination address field */
        if ((lro->ip_hdr->daddr != ip->daddr))
                return XGE_HAL_FAIL;

        /* Match Source Port field */
        if ((lro->tcp_hdr->source != tcp->source))
                return XGE_HAL_FAIL;

        /* Match Destination Port field */
        if ((lro->tcp_hdr->dest != tcp->dest))
                return XGE_HAL_FAIL;

        return XGE_HAL_OK;
}

/*
 * __hal_tcp_seg_len: Find the tcp seg len.
 * @ip: ip header.
 * @tcp: tcp header.
 * returns:     Tcp     seg     length.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL u16
__hal_tcp_seg_len(iplro_t *ip, tcplro_t *tcp)
{
        u16     ret;

        ret     =  (xge_os_ntohs(ip->tot_len) -
                   ((ip->version_ihl & 0x0F)<<2) -
                   ((tcp->doff_res)>>2));
        return (ret);
}

/*
 * __hal_ip_lro_capable: Finds whether ip is lro capable.
 * @ip: ip header.
 * @ext_info:  descriptor info.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
__hal_ip_lro_capable(iplro_t *ip,
                         xge_hal_dtr_info_t     *ext_info)
{

#ifdef XGE_LL_DEBUG_DUMP_PKT
                {
                        u16     i;
                        u8 ch, *iph     = (u8 *)ip;

                        xge_debug_ring(XGE_TRACE, "Dump Ip:" );
                        for     (i =0; i < 40; i++)     {
                                ch = ntohs(*((u8 *)(iph + i)) );
                                printf("i:%d %02x, ",i,ch);
                        }
                }
#endif

        if (ip->version_ihl     != IP_FAST_PATH_HDR_MASK) {
                xge_debug_ring(XGE_ERR, "iphdr !=45     :%d",ip->version_ihl);
                return XGE_HAL_FAIL;
        }

        if (ext_info->proto     & XGE_HAL_FRAME_PROTO_IP_FRAGMENTED) {
                xge_debug_ring(XGE_ERR, "IP     fragmented");
                return XGE_HAL_FAIL;
        }

        return XGE_HAL_OK;
}

/*
 * __hal_tcp_lro_capable: Finds whether tcp     is lro capable.
 * @ip: ip header.
 * @tcp: tcp header.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
__hal_tcp_lro_capable(iplro_t *ip, tcplro_t     *tcp, lro_t     *lro, int *ts_off)
{
#ifdef XGE_LL_DEBUG_DUMP_PKT
                {
                        u8 ch;
                        u16     i;

                        xge_debug_ring(XGE_TRACE, "Dump Tcp:" );
                        for     (i =0; i < 20; i++)     {
                                ch = ntohs(*((u8 *)((u8 *)tcp + i))     );
                                xge_os_printf("i:%d     %02x, ",i,ch);
                        }
                }
#endif
        if ((TCP_FAST_PATH_HDR_MASK2 != tcp->ctrl) &&
                (TCP_FAST_PATH_HDR_MASK3 !=     tcp->ctrl))
                goto _exit_fail;

        *ts_off = -1;
        if (TCP_FAST_PATH_HDR_MASK1     != tcp->doff_res) {
                u16     tcp_hdr_len     = tcp->doff_res >> 2; /* TCP header     len     */
                u16     off     = 20; /* Start of tcp options */
                int     i, diff;

                /* Does Packet can contain time stamp */
                if (tcp_hdr_len < 32) {
                        /*
                         * If the session is not opened, we     can     consider
                         * this packet for LRO
                         */
                        if (lro == NULL)
                                return XGE_HAL_OK;

                        goto _exit_fail;
                }

                /* Ignore No-operation 0x1 */
                while (((u8     *)tcp)[off]     == 0x1)
                        off++;

                /* Next option == Timestamp     */
                if (((u8 *)tcp)[off] != 0x8) {
                        /*
                         * If the session ie not opened, we     can     consider
                         * this packet for LRO
                         */
                        if (lro == NULL)
                                return XGE_HAL_OK;

                        goto _exit_fail;
                }

                *ts_off = off;
                if (lro == NULL)
                        return XGE_HAL_OK;

                /*
                 * Now the session is opened. If the LRO frame doesn't
                 * have time stamp,     we cannot consider current packet for
                 * LRO.
                 */
                if (lro->ts_off == -1) {
                        xge_debug_ring(XGE_ERR, "Pkt received with time stamp after     session opened with     no time stamp : %02x %02x", tcp->doff_res, tcp->ctrl);
                        return XGE_HAL_FAIL;
                }

                /*
                 * If the difference is greater than three,     then there are
                 * more options possible.
                 * else, there are two cases:
                 * case 1: remaining are padding bytes.
                 * case 2: remaining can contain options or     padding
                 */
                off     += ((u8 *)tcp)[off+1];
                diff = tcp_hdr_len - off;
                if (diff > 3) {
                        /*
                         * Probably     contains more options.
                         */
                        xge_debug_ring(XGE_ERR, "tcphdr not     fastpth : pkt received with     tcp     options in addition     to time stamp after     the     session is opened %02x %02x     ", tcp->doff_res,       tcp->ctrl);
                        return XGE_HAL_FAIL;
                }

                for     (i = 0; i <     diff; i++) {
                        u8 byte = ((u8 *)tcp)[off+i];

                        /* Ignore No-operation 0x1 */
                        if ((byte == 0x0) || (byte == 0x1))
                                continue;
                        xge_debug_ring(XGE_ERR, "tcphdr not     fastpth : pkt received with     tcp     options in addition     to time stamp after     the     session is opened %02x %02x     ", tcp->doff_res,       tcp->ctrl);
                        return XGE_HAL_FAIL;
                }

                /*
                 * Update the time stamp of     LRO     frame.
                 */
                xge_os_memcpy(((char *)lro->tcp_hdr     + lro->ts_off + 2),
                                (char *)((char *)tcp + (*ts_off) + 2), 8);
        }

        return XGE_HAL_OK;

_exit_fail:
        xge_debug_ring(XGE_TRACE,       "tcphdr not     fastpth %02x %02x", tcp->doff_res, tcp->ctrl);
        return XGE_HAL_FAIL;

}

/*
 * __hal_lro_capable: Finds     whether frame is lro capable.
 * @buffer:     Ethernet frame.
 * @ip: ip frame.
 * @tcp: tcp frame.
 * @ext_info: Descriptor info.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
__hal_lro_capable( u8 *buffer,
                   iplro_t **ip,
                   tcplro_t     **tcp,
       xge_hal_dtr_info_t *ext_info)
{
        u8 ip_off, ip_length;

        if (!(ext_info->proto & XGE_HAL_FRAME_PROTO_TCP)) {
                xge_debug_ring(XGE_ERR, "Cant do lro %d", ext_info->proto);
                return XGE_HAL_FAIL;
        }

  if ( !*ip )
  {
#ifdef XGE_LL_DEBUG_DUMP_PKT
                {
                        u8 ch;
                        u16     i;

                        xge_os_printf("Dump     Eth:" );
                        for     (i =0; i < 60; i++)     {
                                ch = ntohs(*((u8 *)(buffer + i)) );
                                xge_os_printf("i:%d     %02x, ",i,ch);
                        }
                }
#endif

    switch (ext_info->frame) {
    case XGE_HAL_FRAME_TYPE_DIX:
      ip_off = XGE_HAL_HEADER_ETHERNET_II_802_3_SIZE;
      break;
    case XGE_HAL_FRAME_TYPE_LLC:
      ip_off = (XGE_HAL_HEADER_ETHERNET_II_802_3_SIZE   +
                XGE_HAL_HEADER_802_2_SIZE);
      break;
    case XGE_HAL_FRAME_TYPE_SNAP:
      ip_off = (XGE_HAL_HEADER_ETHERNET_II_802_3_SIZE   +
                XGE_HAL_HEADER_SNAP_SIZE);
      break;
    default: // XGE_HAL_FRAME_TYPE_IPX, etc.
      return XGE_HAL_FAIL;
    }


    if (ext_info->proto & XGE_HAL_FRAME_PROTO_VLAN_TAGGED) {
      ip_off += XGE_HAL_HEADER_VLAN_SIZE;
    }

    /* Grab     ip,     tcp     headers */
    *ip = (iplro_t *)((char*)buffer     + ip_off);
  } /* !*ip */

        ip_length =     (u8)((*ip)->version_ihl & 0x0F);
        ip_length =     ip_length <<2;
        *tcp = (tcplro_t *)((char *)*ip + ip_length);

        xge_debug_ring(XGE_TRACE, "ip_length:%d ip:"XGE_OS_LLXFMT
                   " tcp:"XGE_OS_LLXFMT"", (int)ip_length,
                (unsigned long long)(ulong_t)*ip, (unsigned long long)(ulong_t)*tcp);

        return XGE_HAL_OK;

}


/*
 * __hal_open_lro_session: Open a new LRO session.
 * @buffer:     Ethernet frame.
 * @ip: ip header.
 * @tcp: tcp header.
 * @lro: lro pointer
 * @ext_info: Descriptor info.
 * @hldev: Hal context.
 * @ring_lro: LRO descriptor per rx ring.
 * @slot: Bucket no.
 * @tcp_seg_len: Length of tcp segment.
 * @ts_off:     time stamp offset in the packet.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
__hal_open_lro_session (u8 *buffer,     iplro_t *ip, tcplro_t *tcp,     lro_t **lro,
                        xge_hal_device_t *hldev, xge_hal_lro_desc_t     *ring_lro, int slot,
      u32 tcp_seg_len, int      ts_off)
{

        lro_t *lro_new = &ring_lro->lro_pool[slot];

        lro_new->in_use                 =       1;
        lro_new->ll_hdr                 =       buffer;
        lro_new->ip_hdr                 =       ip;
        lro_new->tcp_hdr                =       tcp;
        lro_new->tcp_next_seq_num       =       tcp_seg_len     + xge_os_ntohl(
                                                                tcp->seq);
        lro_new->tcp_seq_num            =       tcp->seq;
        lro_new->tcp_ack_num            =       tcp->ack_seq;
        lro_new->sg_num                 =       1;
        lro_new->total_length           =       xge_os_ntohs(ip->tot_len);
        lro_new->frags_len              =       0;
        lro_new->ts_off                 =       ts_off;

        hldev->stats.sw_dev_info_stats.tot_frms_lroised++;
        hldev->stats.sw_dev_info_stats.tot_lro_sessions++;

        *lro = ring_lro->lro_recent = lro_new;
        return;
}
/*
 * __hal_lro_get_free_slot:     Get     a free LRO bucket.
 * @ring_lro: LRO descriptor per ring.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
__hal_lro_get_free_slot (xge_hal_lro_desc_t     *ring_lro)
{
        int     i;

        for     (i = 0; i <     XGE_HAL_LRO_MAX_BUCKETS; i++) {
                lro_t *lro_temp = &ring_lro->lro_pool[i];

                if (!lro_temp->in_use)
                        return i;
        }
        return -1;
}

/*
 * __hal_get_lro_session: Gets matching LRO     session or creates one.
 * @eth_hdr:    Ethernet header.
 * @ip: ip header.
 * @tcp: tcp header.
 * @lro: lro pointer
 * @ext_info: Descriptor info.
 * @hldev: Hal context.
 * @ring_lro: LRO descriptor per rx ring
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
__hal_get_lro_session (u8 *eth_hdr,
                           iplro_t *ip,
                           tcplro_t     *tcp,
                           lro_t **lro,
                           xge_hal_dtr_info_t *ext_info,
                           xge_hal_device_t     *hldev,
                           xge_hal_lro_desc_t   *ring_lro,
                           lro_t **lro_end3     /* Valid only when ret=END_3 */)
{
        lro_t *lro_match;
        int     i, free_slot = -1;
        u32     tcp_seg_len;
        int     ts_off = -1;

        *lro = lro_match = NULL;
        /*
         * Compare the incoming frame with the lro session left from the
         * previous     call.  There is a good chance that this incoming frame
         * matches the lro session.
         */
        if (ring_lro->lro_recent && ring_lro->lro_recent->in_use)       {
                if (__hal_lro_check_for_session_match(ring_lro->lro_recent,
                                                          tcp, ip)
                                                        == XGE_HAL_OK)
                        lro_match =     ring_lro->lro_recent;
        }

        if (!lro_match) {
                /*
                 * Search in the pool of LROs for the session that matches
                 * the incoming frame.
                 */
                for     (i = 0; i <     XGE_HAL_LRO_MAX_BUCKETS; i++) {
                        lro_t *lro_temp = &ring_lro->lro_pool[i];

                        if (!lro_temp->in_use) {
                                if (free_slot == -1)
                                        free_slot =     i;
                                continue;
                        }

                        if (__hal_lro_check_for_session_match(lro_temp, tcp,
                                                          ip) == XGE_HAL_OK) {
                                lro_match =     lro_temp;
                                break;
                        }
                }
        }


        if (lro_match) {
                /*
                 * Matching     LRO     Session found
                 */
                *lro = lro_match;

                if (lro_match->tcp_next_seq_num != xge_os_ntohl(tcp->seq)) {
     xge_debug_ring(XGE_ERR,    "**retransmit  **"
                                                "found***");
                        hldev->stats.sw_dev_info_stats.lro_out_of_seq_pkt_cnt++;
                        return XGE_HAL_INF_LRO_END_2;
                }

                if (XGE_HAL_OK != __hal_ip_lro_capable(ip, ext_info))
    {
                        return XGE_HAL_INF_LRO_END_2;
    }

                if (XGE_HAL_OK != __hal_tcp_lro_capable(ip,     tcp, lro_match,
                                                        &ts_off)) {
                        /*
                         * Close the current session and open a new
                         * LRO session with     this packet,
                         * provided     it has tcp payload
                         */
                        tcp_seg_len     = __hal_tcp_seg_len(ip, tcp);
                        if (tcp_seg_len == 0)
      {
                                return XGE_HAL_INF_LRO_END_2;
      }

                        /* Get a free bucket  */
                        free_slot =     __hal_lro_get_free_slot(ring_lro);
                        if (free_slot == -1)
      {
                                return XGE_HAL_INF_LRO_END_2;
      }

                        /*
                         * Open a new LRO session
                         */
                        __hal_open_lro_session (eth_hdr,        ip,     tcp, lro_end3,
                                                hldev, ring_lro, free_slot, tcp_seg_len,
                                                ts_off);

                        return XGE_HAL_INF_LRO_END_3;
                }

                                /*
                 * The frame is good, in-sequence, can be LRO-ed;
                 * take its     (latest) ACK - unless it is     a dupack.
                 * Note: to     be exact need to check window size as well..
                */
                if (lro_match->tcp_ack_num == tcp->ack_seq &&
                        lro_match->tcp_seq_num == tcp->seq)     {
                        hldev->stats.sw_dev_info_stats.lro_dup_pkt_cnt++;
                        return XGE_HAL_INF_LRO_END_2;
                }

                lro_match->tcp_seq_num = tcp->seq;
                lro_match->tcp_ack_num = tcp->ack_seq;
                lro_match->frags_len += __hal_tcp_seg_len(ip, tcp);

                ring_lro->lro_recent =  lro_match;

                return XGE_HAL_INF_LRO_CONT;
        }

        /* ********** New Session ***************/
        if (free_slot == -1)
                return XGE_HAL_INF_LRO_UNCAPABLE;

        if (XGE_HAL_FAIL ==     __hal_ip_lro_capable(ip, ext_info))
                return XGE_HAL_INF_LRO_UNCAPABLE;

        if (XGE_HAL_FAIL ==     __hal_tcp_lro_capable(ip, tcp, NULL, &ts_off))
                return XGE_HAL_INF_LRO_UNCAPABLE;

        xge_debug_ring(XGE_TRACE, "Creating     lro     session.");

        /*
         * Open a LRO session, provided the     packet contains payload.
         */
        tcp_seg_len     = __hal_tcp_seg_len(ip, tcp);
        if (tcp_seg_len == 0)
                return XGE_HAL_INF_LRO_UNCAPABLE;

        __hal_open_lro_session (eth_hdr,        ip,     tcp, lro, hldev, ring_lro, free_slot,
                                tcp_seg_len, ts_off);

        return XGE_HAL_INF_LRO_BEGIN;
}

/*
 * __hal_lro_under_optimal_thresh: Finds whether combined session is optimal.
 * @ip: ip header.
 * @tcp: tcp header.
 * @lro: lro pointer
 * @hldev: Hal context.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
__hal_lro_under_optimal_thresh (iplro_t *ip,
                                        tcplro_t *tcp,
                                lro_t *lro,
                                xge_hal_device_t *hldev)
{
        if (!lro) return XGE_HAL_FAIL;

        if ((lro->total_length + __hal_tcp_seg_len(ip, tcp)     ) >
                                                hldev->config.lro_frm_len) {
                xge_debug_ring(XGE_TRACE, "Max LRO frame len exceeded:"
                 "max length %d ", hldev->config.lro_frm_len);
                hldev->stats.sw_dev_info_stats.lro_frm_len_exceed_cnt++;
                return XGE_HAL_FAIL;
        }

        if (lro->sg_num == hldev->config.lro_sg_size) {
                xge_debug_ring(XGE_TRACE, "Max sg count exceeded:"
                                 "max sg %d     ", hldev->config.lro_sg_size);
                hldev->stats.sw_dev_info_stats.lro_sg_exceed_cnt++;
                return XGE_HAL_FAIL;
        }

        return XGE_HAL_OK;
}

/*
 * __hal_collapse_ip_hdr: Collapses     ip header.
 * @ip: ip header.
 * @tcp: tcp header.
 * @lro: lro pointer
 * @hldev: Hal context.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
__hal_collapse_ip_hdr ( iplro_t *ip,
                        tcplro_t *tcp,
                        lro_t *lro,
                        xge_hal_device_t *hldev)
{

        lro->total_length += __hal_tcp_seg_len(ip, tcp);

        /* May be we have to handle     time stamps     or more options */

        return XGE_HAL_OK;

}

/*
 * __hal_collapse_tcp_hdr: Collapses tcp header.
 * @ip: ip header.
 * @tcp: tcp header.
 * @lro: lro pointer
 * @hldev: Hal context.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
__hal_collapse_tcp_hdr ( iplro_t *ip,
                         tcplro_t *tcp,
                         lro_t *lro,
                         xge_hal_device_t *hldev)
{
        lro->tcp_next_seq_num += __hal_tcp_seg_len(ip, tcp);
        return XGE_HAL_OK;

}

/*
 * __hal_append_lro: Appends new frame to existing LRO session.
 * @ip: ip header.
 * @tcp: IN     tcp     header, OUT     tcp     payload.
 * @seg_len: tcp payload length.
 * @lro: lro pointer
 * @hldev: Hal context.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
__hal_append_lro(iplro_t *ip,
                 tcplro_t **tcp,
                 u32 *seg_len,
                 lro_t *lro,
                 xge_hal_device_t *hldev)
{
        (void) __hal_collapse_ip_hdr(ip, *tcp,  lro, hldev);
        (void) __hal_collapse_tcp_hdr(ip, *tcp, lro, hldev);
        // Update mbuf chain will be done in ll driver.
        // xge_hal_accumulate_large_rx on success of appending new frame to
        // lro will     return to ll driver     tcpdata pointer, and tcp payload length.
        // along with return code lro frame     appended.

        lro->sg_num++;
        *seg_len = __hal_tcp_seg_len(ip, *tcp);
        *tcp = (tcplro_t *)((char *)*tcp        + (((*tcp)->doff_res)>>2));

        return XGE_HAL_OK;

}

/**
 * __xge_hal_accumulate_large_rx:       LRO     a given frame
 * frames
 * @ring: rx ring number
 * @eth_hdr: ethernet header.
 * @ip_hdr: ip header (optional)
 * @tcp: tcp header.
 * @seglen:     packet length.
 * @p_lro: lro pointer.
 * @ext_info: descriptor info, see xge_hal_dtr_info_t{}.
 * @hldev: HAL device.
 * @lro_end3: for lro_end3 output
 *
 * LRO the newly received frame, i.e. attach it (if     possible) to the
 * already accumulated (i.e., already LRO-ed) received frames (if any),
 * to form one super-sized frame for the subsequent     processing
 * by the stack.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
xge_hal_lro_process_rx(int ring, u8 *eth_hdr, u8 *ip_hdr, tcplro_t **tcp,
                       u32 *seglen, lro_t **p_lro,
                       xge_hal_dtr_info_t *ext_info, xge_hal_device_t *hldev,
                       lro_t **lro_end3)
{
        iplro_t *ip = (iplro_t *)ip_hdr;
        xge_hal_status_e ret;
        lro_t *lro;

        xge_debug_ring(XGE_TRACE, "Entered accumu lro. ");
        if (XGE_HAL_OK != __hal_lro_capable(eth_hdr, &ip, (tcplro_t **)tcp,
                                      ext_info))
                return XGE_HAL_INF_LRO_UNCAPABLE;

        /*
         * This function shall get matching LRO or else
         * create one and return it
         */
        ret = __hal_get_lro_session(eth_hdr, ip, (tcplro_t *)*tcp,
                              p_lro, ext_info, hldev,   &hldev->lro_desc[ring],
                              lro_end3);
        xge_debug_ring(XGE_TRACE, "ret from get_lro:%d ",ret);
        lro = *p_lro;
        if (XGE_HAL_INF_LRO_CONT == ret) {
                if (XGE_HAL_OK == __hal_lro_under_optimal_thresh(ip,
                                                (tcplro_t *)*tcp, lro, hldev)) {
                        (void) __hal_append_lro(ip,(tcplro_t **) tcp, seglen,
                                                         lro, hldev);
                        hldev->stats.sw_dev_info_stats.tot_frms_lroised++;

                        if (lro->sg_num >= hldev->config.lro_sg_size) {
                                hldev->stats.sw_dev_info_stats.lro_sg_exceed_cnt++;
                                ret = XGE_HAL_INF_LRO_END_1;
                        }

                } else ret = XGE_HAL_INF_LRO_END_2;
        }

        /*
         * Since its time to flush,
         * update ip header     so that it can be sent up
         */
        if ((ret == XGE_HAL_INF_LRO_END_1) ||
                (ret == XGE_HAL_INF_LRO_END_2) ||
                (ret == XGE_HAL_INF_LRO_END_3)) {
                lro->ip_hdr->tot_len = xge_os_htons((*p_lro)->total_length);
                lro->ip_hdr->check = xge_os_htons(0);
                lro->ip_hdr->check = XGE_LL_IP_FAST_CSUM(((u8 *)(lro->ip_hdr)),
                                        (lro->ip_hdr->version_ihl & 0x0F));
                lro->tcp_hdr->ack_seq = lro->tcp_ack_num;
        }

        return (ret);
}

/**
 * xge_hal_accumulate_large_rx: LRO     a given frame
 * frames
 * @buffer:     Ethernet frame.
 * @tcp: tcp header.
 * @seglen:     packet length.
 * @p_lro: lro pointer.
 * @ext_info: descriptor info, see xge_hal_dtr_info_t{}.
 * @hldev: HAL device.
 * @lro_end3: for lro_end3 output
 *
 * LRO the newly received frame, i.e. attach it (if     possible) to the
 * already accumulated (i.e., already LRO-ed) received frames (if any),
 * to form one super-sized frame for the subsequent     processing
 * by the stack.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
xge_hal_accumulate_large_rx(u8 *buffer, tcplro_t **tcp, u32 *seglen,
lro_t **p_lro, xge_hal_dtr_info_t *ext_info, xge_hal_device_t *hldev,
lro_t **lro_end3)
{
  int ring = 0;
  return xge_hal_lro_process_rx(ring, buffer, NULL, tcp, seglen, p_lro,
                                ext_info, hldev, lro_end3);
}

/**
 * xge_hal_lro_close_session: Close LRO session
 * @lro: LRO Session.
 * @hldev: HAL Context.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
xge_hal_lro_close_session (lro_t *lro)
{
        lro->in_use = 0;
}

/**
 * xge_hal_lro_next_session: Returns next LRO session in the list or NULL
 *                                      if none exists.
 * @hldev: HAL Context.
 * @ring: rx ring number.
 */
__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL lro_t *
xge_hal_lro_next_session (xge_hal_device_t *hldev, int ring)
{
xge_hal_lro_desc_t *ring_lro = &hldev->lro_desc[ring];
        int     i;
        int     start_idx =     ring_lro->lro_next_idx;

        for(i = start_idx; i < XGE_HAL_LRO_MAX_BUCKETS; i++) {
                lro_t *lro = &ring_lro->lro_pool[i];

                if (!lro->in_use)
                        continue;

                lro->ip_hdr->tot_len = xge_os_htons(lro->total_length);
                lro->ip_hdr->check = xge_os_htons(0);
                lro->ip_hdr->check = XGE_LL_IP_FAST_CSUM(((u8 *)(lro->ip_hdr)),
                                                                (lro->ip_hdr->version_ihl &     0x0F));
                ring_lro->lro_next_idx  = i     + 1;
                return lro;
        }

        ring_lro->lro_next_idx  = 0;
        return NULL;

}

__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL lro_t *
xge_hal_lro_get_next_session(xge_hal_device_t *hldev)
{
  int ring = 0; /* assume default ring=0 */
  return xge_hal_lro_next_session(hldev, ring);
}
#endif