/*
 * 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 2014 QLogic Corporation
 * The contents of this file are subject to the terms of the
 * QLogic End User License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the License at
 * http://www.qlogic.com/Resources/Documents/DriverDownloadHelp/
 * QLogic_End_User_Software_License.txt
 * See the License for the specific language governing permissions
 * and limitations under the License.
 */

/*
 * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
 */

#include "bnxe.h"

#include <sys/mac.h>
#include <sys/mac_ether.h>
#include <sys/dlpi.h>

#if !(defined(__S11) || defined(__S12))
#define mri_driver  mr_driver
#define mri_start   mr_start
#define mri_stop    mr_stop
#define mri_intr    mr_intr
#define mri_poll    mr_poll
#define mri_tx      mr_send
#define mgi_driver  mrg_driver
#define mgi_start   mrg_start
#define mgi_stop    mrg_stop
#define mgi_count   mrg_count
#define mgi_addmac  mrg_addmac
#define mgi_remmac  mrg_addmac
#define mr_gaddring mr_gadd_ring
#define mr_gremring mr_grem_ring
#endif /* not __S11 or __S12 */

/*
 * Reconfiguring the network devices parameters require net_config
 * privilege starting Solaris 10.  Only root user is allowed to
 * update device parameter in Solaris 9 and earlier version. Following
 * declaration allows single binary image to run on all OS versions.
 */
extern int secpolicy_net_config(const cred_t *, boolean_t);
extern int drv_priv(cred_t *);
#pragma weak secpolicy_net_config
#pragma weak drv_priv

#ifdef MC_SETPROP

char * bnxeLink_priv_props[] =
{
    "_adv_2500fdx_cap",
    "_en_2500fdx_cap",
    "_adv_txpause_cap",
    "_en_txpause_cap",
    "_txpause",
    "_adv_rxpause_cap",
    "_en_rxpause_cap",
    "_rxpause",
    "_autoneg_flow",
    "_checksum",
    "_num_rings",
    "_rx_descs",
    "_rx_free_reclaim",
    "_rx_copy_threshold",
    "_tx_descs",
    "_tx_free_reclaim",
    "_tx_copy_threshold",
    "_tx_ring_policy",
    "_interrupt_coalesce",
    "_rx_interrupt_coalesce_usec",
    "_tx_interrupt_coalesce_usec",
    "_disable_msix",
    "_l2_fw_flow_ctrl",
    "_autogreeen_enable",
    "_lso_enable",
    "_log_enable",
    "_fcoe_enable",
    NULL
};

#endif /* MC_SETPROP */


static int BnxeMacStats(void *     pArg,
                        uint_t     stat,
                        uint64_t * pVal)
{
    um_device_t * pUM = (um_device_t *)pArg;
    lm_device_t * pLM;
    b10_l2_chip_statistics_t b10_l2_stats;
    int idx, rc = 0;

    if ((pUM == NULL) || (pVal == NULL))
    {
        return EINVAL;
    }

    pLM = &pUM->lm_dev;

    BNXE_LOCK_ENTER_GLD(pUM);

    if (!pUM->plumbed)
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return EAGAIN;
    }

    *pVal = 0;

    switch (stat)
    {
    case MAC_STAT_IFSPEED:
        *pVal = (pUM->props.link_speed * 1000000ULL);
        break;

    case MAC_STAT_MULTIRCV:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.IfHCInMulticastPkts;
        break;

    case MAC_STAT_BRDCSTRCV:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.IfHCInBroadcastPkts;
        break;

    case MAC_STAT_MULTIXMT:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.IfHCOutMulticastPkts;
        break;

    case MAC_STAT_BRDCSTXMT:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.IfHCOutBroadcastPkts;
        break;

    case MAC_STAT_NORCVBUF:
        lm_get_stats(pLM, LM_STATS_RCV_NO_BUFFER_DROP, (u64_t *)pVal);
        break;

    case MAC_STAT_NOXMTBUF:
        *pVal = 0;
        LM_FOREACH_TSS_IDX(pLM, idx)
        {
            *pVal += pUM->txq[idx].txRecycle;
        }
        break;

    case MAC_STAT_IERRORS:
    case ETHER_STAT_MACRCV_ERRORS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.IfInErrors;
        break;

    case MAC_STAT_OERRORS:
        /* XXX not available */
        break;

    case MAC_STAT_COLLISIONS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.EtherStatsCollisions;
        break;

    case MAC_STAT_RBYTES:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.IfHCInOctets;
        break;

    case MAC_STAT_IPACKETS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.IfHCInPkts;
        break;

    case MAC_STAT_OBYTES:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.IfHCOutOctets;
        break;

    case MAC_STAT_OPACKETS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.IfHCOutPkts;
        break;

    case ETHER_STAT_ALIGN_ERRORS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.Dot3StatsAlignmentErrors;
        break;

    case ETHER_STAT_FCS_ERRORS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.Dot3StatsFCSErrors;
        break;

    case ETHER_STAT_FIRST_COLLISIONS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.Dot3StatsSingleCollisionFrames;
        break;

    case ETHER_STAT_MULTI_COLLISIONS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.Dot3StatsMultipleCollisionFrames;
        break;

    case ETHER_STAT_DEFER_XMTS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.Dot3StatsDeferredTransmissions;
        break;

    case ETHER_STAT_TX_LATE_COLLISIONS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.Dot3StatsLateCollisions;
        break;

    case ETHER_STAT_EX_COLLISIONS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.Dot3StatsExcessiveCollisions;
        break;

    case ETHER_STAT_MACXMT_ERRORS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.Dot3StatsInternalMacTransmitErrors;
        break;

    case ETHER_STAT_CARRIER_ERRORS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.Dot3StatsCarrierSenseErrors;
        break;

    case ETHER_STAT_TOOLONG_ERRORS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.EtherStatsOverrsizePkts;
        break;

#if (MAC_VERSION > 1)
    case ETHER_STAT_TOOSHORT_ERRORS:
        lm_stats_get_l2_chip_stats(pLM, &b10_l2_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_1);
        *pVal = b10_l2_stats.EtherStatsUndersizePkts;
        break;
#endif

    case ETHER_STAT_XCVR_ADDR:
        *pVal = pLM->vars.phy_addr;
        break;

    case ETHER_STAT_XCVR_ID:
        *pVal = 0;
        break;

    case ETHER_STAT_XCVR_INUSE:
        switch (pUM->props.link_speed)
        {
        case 0: /* no speed then status is down */
            *pVal = XCVR_NONE;
            break;

        case 1000:
            *pVal = XCVR_1000X;
            break;

        case 100:
            *pVal = XCVR_100X;
            break;

        case 10:
            *pVal = XCVR_10;
            break;

        default:
            /* catches 2500/10000 */
            *pVal = XCVR_UNDEFINED;
        }
        break;

#if (MAC_VERSION > 1)
    case ETHER_STAT_CAP_10GFDX:
        *pVal = 1;
        break;
#endif

    case ETHER_STAT_CAP_1000FDX:
        *pVal = 1;
        break;

#if 0
    case ETHER_STAT_CAP_1000HDX:
        //*pVal = linkconf->param_1000hdx;
        *pVal = 0;
        break;
#endif

    case ETHER_STAT_CAP_100FDX:
        //*pVal = linkconf->param_100fdx;
        *pVal = 1;
        break;

    case ETHER_STAT_CAP_100HDX:
        //*pVal = linkconf->param_100hdx;
        *pVal = 1;
        break;

    case ETHER_STAT_CAP_10FDX:
        //*pVal = linkconf->param_10fdx;
        *pVal = 1;
        break;

    case ETHER_STAT_CAP_10HDX:
        //*pVal = linkconf->param_10hdx;
        *pVal = 1;
        break;

    case ETHER_STAT_CAP_ASMPAUSE:
        *pVal = 1;
        break;

    case ETHER_STAT_CAP_PAUSE:
        *pVal = 1;
        break;

    case ETHER_STAT_CAP_AUTONEG:
        *pVal = 1;
        break;

#if (MAC_VERSION > 1)
    case ETHER_STAT_CAP_REMFAULT:
        *pVal = 1;
        break;
#endif

#if (MAC_VERSION > 1)
    case ETHER_STAT_ADV_CAP_10GFDX:
        *pVal = pUM->curcfg.lnkcfg.param_10000fdx;
        break;
#endif

    case ETHER_STAT_ADV_CAP_1000FDX:
        *pVal = pUM->curcfg.lnkcfg.param_1000fdx;
        break;

#if 0
    case ETHER_STAT_ADV_CAP_1000HDX:
        //*pVal = pUM->curcfg.lnkcfg.param_1000hdx;
        *pVal = 0;
        break;
#endif

    case ETHER_STAT_ADV_CAP_100FDX:
        *pVal = pUM->curcfg.lnkcfg.param_100fdx;
        break;

    case ETHER_STAT_ADV_CAP_100HDX:
        *pVal = pUM->curcfg.lnkcfg.param_100hdx;
        break;

    case ETHER_STAT_ADV_CAP_10FDX:
        *pVal = pUM->curcfg.lnkcfg.param_10fdx;
        break;

    case ETHER_STAT_ADV_CAP_10HDX:
        *pVal = pUM->curcfg.lnkcfg.param_10hdx;
        break;

    case ETHER_STAT_ADV_CAP_ASMPAUSE:
        *pVal = 1;
        break;

    case ETHER_STAT_ADV_CAP_PAUSE:
        *pVal = 1;
        break;

    case ETHER_STAT_ADV_CAP_AUTONEG:
        *pVal = pUM->curcfg.lnkcfg.link_autoneg;
        break;

#if (MAC_VERSION > 1)
    case ETHER_STAT_ADV_REMFAULT:
        *pVal = 1;
        break;
#endif

#if 0 /* LP caps not supported */
#if (MAC_VERSION > 1)
    case ETHER_STAT_LP_CAP_10GFDX:
        *pVal = pUM->remote.param_10000fdx;
        break;
#endif

    case ETHER_STAT_LP_CAP_1000FDX:
        *pVal = pUM->remote.param_1000fdx;
        break;

#if 0
    case ETHER_STAT_LP_CAP_1000HDX:
        //*pVal = pUM->remote.param_1000hdx;
        *pVal = 0;
        break;
#endif

    case ETHER_STAT_LP_CAP_100FDX:
        *pVal = pUM->remote.param_100fdx;
        break;

    case ETHER_STAT_LP_CAP_100HDX:
        *pVal = pUM->remote.param_100hdx;
        break;

    case ETHER_STAT_LP_CAP_10FDX:
        *pVal = pUM->remote.param_10fdx;
        break;

    case ETHER_STAT_LP_CAP_10HDX:
        *pVal = pUM->remote.param_10hdx;
        break;

#if 0
    case ETHER_STAT_LP_CAP_ASMPAUSE:
        /* XXX implement LP_ASYM_PAUSE stat */
        break;

    case ETHER_STAT_LP_CAP_PAUSE:
        /* XXX implement LP_PAUSE stat */
        break;
#endif

    case ETHER_STAT_LP_CAP_AUTONEG:
        *pVal = pUM->remote.link_autoneg;
        break;

    case ETHER_STAT_LP_REMFAULT:
        /* XXX implement LP_REMFAULT stat */
        break;
#endif /* LP caps not supported */

#if 0
    case ETHER_STAT_LINK_ASMPAUSE:
        /* XXX implement ASMPAUSE stat */
        break;

    case ETHER_STAT_LINK_PAUSE:
        /* XXX implement PAUSE stat */
        break;
#endif

    case ETHER_STAT_LINK_AUTONEG:
        *pVal = pUM->curcfg.lnkcfg.link_autoneg;
        break;

    case ETHER_STAT_LINK_DUPLEX:
        *pVal = (pUM->props.link_duplex == B_TRUE) ?
                    LINK_DUPLEX_FULL : LINK_DUPLEX_HALF;
        break;

    default:
        rc = ENOTSUP;
    }

    BNXE_LOCK_EXIT_GLD(pUM);

    return rc;
}



/*
 * This routine is called by GLD to enable device for packet reception and
 * enable interrupts.
 */
static int BnxeMacStart(void * pArg)
{
    um_device_t * pUM = (um_device_t *)pArg;

    BNXE_LOCK_ENTER_GLD(pUM);

    if (pUM->plumbed)
    {
        /* already started */
        BNXE_LOCK_EXIT_GLD(pUM);
        return EAGAIN;
    }

    /* Always report the initial link state as unknown. */
    mac_link_update(pUM->pMac, LINK_STATE_UNKNOWN);

    if (BnxeHwStartL2(pUM))
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return EIO;
    }

    atomic_swap_32(&pUM->plumbed, B_TRUE);

    mutex_enter(&bnxeLoaderMutex);
    bnxeNumPlumbed++;
    mutex_exit(&bnxeLoaderMutex);

    BNXE_LOCK_EXIT_GLD(pUM);

    return 0;
}


/*
 * This routine stops packet reception by clearing RX MASK register.  Also
 * interrupts are disabled for this device.
 */
static void BnxeMacStop(void * pArg)
{
    um_device_t * pUM = (um_device_t *)pArg;

    BNXE_LOCK_ENTER_GLD(pUM);

    if (pUM->plumbed)
    {
        atomic_swap_32(&pUM->plumbed, B_FALSE);

        BnxeHwStopL2(pUM);

        /* Report the link state back to unknown. */
        mac_link_update(pUM->pMac, LINK_STATE_UNKNOWN);

        mutex_enter(&bnxeLoaderMutex);
        bnxeNumPlumbed--;
        mutex_exit(&bnxeLoaderMutex);
    }

    BNXE_LOCK_EXIT_GLD(pUM);
}

/* (flag) TRUE = on, FALSE = off */
static int BnxeMacPromiscuous(void *    pArg,
                              boolean_t flag)
{
    um_device_t * pUM = (um_device_t *)pArg;

    BNXE_LOCK_ENTER_GLD(pUM);

    if (!pUM->plumbed)
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return EAGAIN;
    }

    if (flag)
    {
        pUM->devParams.rx_filter_mask[LM_CLI_IDX_NDIS] |=
            LM_RX_MASK_PROMISCUOUS_MODE;
    }
    else
    {
        pUM->devParams.rx_filter_mask[LM_CLI_IDX_NDIS] &=
            ~LM_RX_MASK_PROMISCUOUS_MODE;
    }

    BNXE_LOCK_ENTER_HWINIT(pUM);

    if (BnxeRxMask(pUM, LM_CLI_IDX_NDIS,
                   pUM->devParams.rx_filter_mask[LM_CLI_IDX_NDIS]) < 0)
    {
        BNXE_LOCK_EXIT_HWINIT(pUM);
        BNXE_LOCK_EXIT_GLD(pUM);
        return ECANCELED;
    }

    BNXE_LOCK_EXIT_HWINIT(pUM);

    BNXE_LOCK_EXIT_GLD(pUM);

    return 0;
}


/*
 * This function is used to enable or disable multicast packet reception for
 * particular multicast addresses.
 * (flag) TRUE = add, FALSE = remove
 */
static int BnxeMacMulticast(void *          pArg,
                            boolean_t       flag,
                            const uint8_t * pMcastAddr)
{
    um_device_t * pUM = (um_device_t *)pArg;
    int rc;

    BNXE_LOCK_ENTER_GLD(pUM);

    if (!pUM->plumbed)
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return EAGAIN;
    }

    BNXE_LOCK_ENTER_HWINIT(pUM);
    rc = BnxeMulticast(pUM, LM_CLI_IDX_NDIS, flag, pMcastAddr, B_TRUE);
    BNXE_LOCK_EXIT_HWINIT(pUM);

    BNXE_LOCK_EXIT_GLD(pUM);

    return rc;
}


#ifdef BNXE_RINGS

#if (defined(__S11) || defined(__S12)) && !defined(ILLUMOS)
static int BnxeRxRingGroupAddMac(void *          groupHandle,
                                 const uint8_t * pMacAddr,
                                 uint64_t        flags)
#else
static int BnxeRxRingGroupAddMac(void *          groupHandle,
                                 const uint8_t * pMacAddr)
#endif
{
    RxQueueGroup * pRxQGroup = (RxQueueGroup *)groupHandle;
    um_device_t *  pUM       = (um_device_t *)pRxQGroup->pUM;
    //u32_t          idx       = pRxQGroup->idx;
    int rc;

#if (defined(__S11) || defined(__S12)) && !defined(ILLUMOS)
    _NOTE(ARGUNUSED(flags))
#endif

    BNXE_LOCK_ENTER_GLD(pUM);

    if (!pUM->plumbed)
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return ECANCELED;
    }

    /* Validate MAC address */
    if (IS_ETH_MULTICAST(pMacAddr))
    {
        BnxeLogWarn(pUM, "Cannot program a mcast/bcast address as a MAC Address.");
        BNXE_LOCK_EXIT_GLD(pUM);
        return EINVAL;
    }

    if (pUM->ucastTableLen == LM_MAX_UC_TABLE_SIZE)
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return ENOMEM;
    }

    BNXE_LOCK_ENTER_HWINIT(pUM);

    COPY_ETH_ADDRESS(pMacAddr, pUM->lm_dev.params.mac_addr);

    rc = BnxeMacAddress(pUM, LM_CLI_IDX_NDIS, B_TRUE,
                        pUM->lm_dev.params.mac_addr);

    BNXE_LOCK_EXIT_HWINIT(pUM);

    if (rc < 0)
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return ECANCELED;
    }

    pUM->ucastTableLen++;

    BNXE_LOCK_EXIT_GLD(pUM);
    return 0;
}


static int BnxeRxRingGroupRemMac(void *          groupHandle,
                                 const uint8_t * pMacAddr)
{
    RxQueueGroup * pRxQGroup = (RxQueueGroup *)groupHandle;
    um_device_t *  pUM       = (um_device_t *)pRxQGroup->pUM;
    //u32_t          idx       = pRxQGroup->idx;
    int rc;

    BNXE_LOCK_ENTER_GLD(pUM);

    if (!pUM->plumbed)
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return ECANCELED;
    }

    if (pUM->ucastTableLen == 0)
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return EINVAL;
    }

    BNXE_LOCK_ENTER_HWINIT(pUM);

    if (!IS_ETH_ADDRESS_EQUAL(pMacAddr, pUM->lm_dev.params.mac_addr))
    {
        BnxeLogWarn(pUM, "Deleting MAC address that doesn't match default");
        /* XXX */
    }

    rc = BnxeMacAddress(pUM, LM_CLI_IDX_NDIS, B_FALSE,
                        pUM->lm_dev.params.mac_addr);

    memset(pUM->lm_dev.params.mac_addr, 0, sizeof(pUM->lm_dev.params.mac_addr));

    BNXE_LOCK_EXIT_HWINIT(pUM);

    if (rc < 0)
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return ECANCELED;
    }

    pUM->ucastTableLen--;

    BNXE_LOCK_EXIT_GLD(pUM);
    return 0;
}


static mblk_t * BnxeTxRingSend(void *   ringHandle,
                               mblk_t * pMblk)
{
    TxQueue *     pTxQ  = (TxQueue *)ringHandle;
    um_device_t * pUM   = (um_device_t *)pTxQ->pUM;
    u32_t         idx   = pTxQ->idx;
    mblk_t *      pNextMblk;
    int rc;

    while (pMblk)
    {
        pNextMblk = pMblk->b_next;
        pMblk->b_next = NULL;

        rc = BnxeTxSendMblk(pUM, idx, pMblk, 0, 0);

        if (rc == BNXE_TX_GOODXMIT)
        {
            pMblk = pNextMblk;
            continue;
        }
        else if (rc == BNXE_TX_DEFERPKT)
        {
            pMblk = pNextMblk;
        }
        else
        {
            pMblk->b_next = pNextMblk;
        }

        break;
    }

    return pMblk;
}

#endif /* BNXE_RINGS */


static int BnxeMacUnicast(void *          pArg,
                          const uint8_t * pMacAddr)
{
    um_device_t * pUM = (um_device_t *)pArg;
    int rc;

    BNXE_LOCK_ENTER_GLD(pUM);

    if (!pUM->plumbed)
    {
        memcpy(pUM->gldMac, pMacAddr, ETHERNET_ADDRESS_SIZE);
        BNXE_LOCK_EXIT_GLD(pUM);
        return 0;
    }

    /* Validate MAC address */
    if (IS_ETH_MULTICAST(pMacAddr))
    {
        BnxeLogWarn(pUM, "Cannot program a mcast/bcast address as a MAC Address.");
        BNXE_LOCK_EXIT_GLD(pUM);
        return EINVAL;
    }

    BNXE_LOCK_ENTER_HWINIT(pUM);

    COPY_ETH_ADDRESS(pMacAddr, pUM->lm_dev.params.mac_addr);

    rc = BnxeMacAddress(pUM, LM_CLI_IDX_NDIS, B_TRUE,
                        pUM->lm_dev.params.mac_addr);

    BNXE_LOCK_EXIT_HWINIT(pUM);

    if (rc < 0)
    {
        BNXE_LOCK_EXIT_GLD(pUM);
        return EAGAIN;
    }

    BNXE_LOCK_EXIT_GLD(pUM);
    return 0;
}


static mblk_t * BnxeMacTx(void *   pArg,
                          mblk_t * pMblk)
{
    um_device_t * pUM = (um_device_t *)pArg;
    mblk_t *      pNextMblk;
    int ring, rc;

    BNXE_LOCK_ENTER_GLDTX(pUM, RW_READER);

    if (!pUM->plumbed)
    {
        freemsgchain(pMblk);
        BNXE_LOCK_EXIT_GLDTX(pUM);

        return NULL;
    }

    while (pMblk)
    {
        ring = BnxeRouteTxRing(pUM, pMblk);

        pNextMblk = pMblk->b_next;
        pMblk->b_next = NULL;

        //rc = BnxeTxSendMblk(pUM, NDIS_CID(&pUM->lm_dev), pMblk, 0, 0);
        rc = BnxeTxSendMblk(pUM, ring, pMblk, 0, 0);

        if (rc == BNXE_TX_GOODXMIT)
        {
            pMblk = pNextMblk;
            continue;
        }
        else if (rc == BNXE_TX_DEFERPKT)
        {
            pMblk = pNextMblk;
        }
        else
        {
            pMblk->b_next = pNextMblk;
        }

        break;
    }

    BNXE_LOCK_EXIT_GLDTX(pUM);

    return pMblk;
}


#ifdef MC_RESOURCES

static void BnxeBlank(void * pArg,
                      time_t tick_cnt,
                      uint_t pkt_cnt)
{
    um_device_t * pUM = (um_device_t *)pArg;

    if (!pUM->plumbed)
    {
        return;
    }

    /* XXX
     * Need to dynamically reconfigure the hw with new interrupt
     * coalescing params...
     */
}


static void BnxeMacResources(void * pArg)
{
    um_device_t * pUM = (um_device_t *)pArg;
    mac_rx_fifo_t mrf;
    int idx;

    mrf.mrf_type              = MAC_RX_FIFO;
    mrf.mrf_blank             = BnxeBlank;
    mrf.mrf_arg               = (void *)pUM;
    mrf.mrf_normal_blank_time = 25;
    mrf.mrf_normal_pkt_count  = 8;

    LM_FOREACH_RSS_IDX(&pUM->lm_dev, idx)
    {
        pUM->macRxResourceHandles[idx] =
            mac_resource_add(pUM->pMac, (mac_resource_t *)&mrf);
    }
}

#endif /* MC_RESOURCES */


static boolean_t BnxeReadReg(um_device_t *          pUM,
                             struct bnxe_reg_data * pData)
{
    if (pData->offset & 0x3)
    {
        BnxeLogWarn(pUM, "Invalid register offset for GIOCBNXEREG ioctl");
        return B_FALSE;
    }

    LM_BAR_RD32_OFFSET(&pUM->lm_dev, 0, pData->offset, &pData->value);

    return B_TRUE;
}


static boolean_t BnxeWriteReg(um_device_t *          pUM,
                              struct bnxe_reg_data * pData)
{
    if (pData->offset & 0x3)
    {
        BnxeLogWarn(pUM, "Invalid register offset for SIOCBNXEREG ioctl");
        return B_FALSE;
    }

    LM_BAR_WR32_OFFSET(&pUM->lm_dev, 0, pData->offset, pData->value);

    return B_TRUE;
}


static boolean_t BnxeReadNvm(um_device_t *            pUM,
                             struct bnxe_nvram_data * pData)
{
    if (pData->offset & 0x3)
    {
        BnxeLogWarn(pUM, "Invalid register offset for GIOCBNXENVRM ioctl");
        return B_FALSE;
    }

    if (lm_nvram_read(&pUM->lm_dev,
                      pData->offset,
                      pData->value,
                      (pData->num_of_u32 * sizeof(u32_t))) !=
        LM_STATUS_SUCCESS)
    {
        return B_FALSE;
    }

    return B_TRUE;
}


static boolean_t BnxeWriteNvm(um_device_t *            pUM,
                              struct bnxe_nvram_data * pData)
{
    if (pData->offset & 0x3)
    {
        BnxeLogWarn(pUM, "Invalid register offset for SIOCBNXENVRM ioctl");
        return B_FALSE;
    }

    if (lm_nvram_write(&pUM->lm_dev,
                       pData->offset,
                       pData->value,
                       (pData->num_of_u32 * sizeof(u32_t))) !=
        LM_STATUS_SUCCESS)
    {
        return B_FALSE;
    }

    return B_TRUE;
}


static boolean_t BnxeReadPciCfg(um_device_t *          pUM,
                                struct bnxe_reg_data * pData)
{
    pData->value = pci_config_get32(pUM->pPciCfg, (off_t)pData->offset);
    return B_TRUE;
}

typedef enum {
    STATS_SHOW_TYPE_NUM,
    STATS_SHOW_TYPE_STR,
    STATS_SHOW_TYPE_CNT,
    STATS_SHOW_TYPE_MAX
} stats_show_type_t;

typedef union _b10_stats_show_data_t
{
    u32_t op; /* ioctl sub-commond */

    struct
    {
        u32_t num; /* return number of stats */
        u32_t len; /* length of each string item */
    } desc;

    /* variable length... */
    char str[1]; /* holds names of desc.num stats, each desc.len in length */

    struct
    {
        b10_l2_chip_statistics_v2_t l2_chip_stats;
        b10_l4_chip_statistics_t    l4_chip_stats;
        b10_l2_driver_statistics_t  l2_drv_stats;
        b10_l4_driver_statistics_t  l4_drv_stats;
    } cnt;
} b10_stats_show_data_t;


static boolean_t BnxeStatsShow(um_device_t *           pUM,
                               b10_stats_show_data_t * pStats,
                               u32_t                   statsLen)
{
    stats_show_type_t op;
    const size_t stats_size = sizeof(pStats->cnt);

    /*
     * All stats names MUST conform to STATS_STR_LEN length!!!
     */

    #define STATS_STR_LEN 39

    /* XXX
     * Note: these strings must be updated whenever any of
     * b10_l2_chip_statistics_t, b10_l4_chip_statistics_t,
     * b10_l2_driver_statistics_t or b10_l4_driver_statistics_t
     * are changed, or additional statistics are required.
     */

    const char p_stat_str[] =

        // b10_l2_chip_statistics_t

        "l2_chip_stats_ver_num\0                 "
        "IfHCInOctets\0                          "
        "IfHCInBadOctets\0                       "
        "IfHCOutOctets\0                         "
        "IfHCOutBadOctets\0                      "
        "IfHCOutPkts\0                           "
        "IfHCInPkts\0                            "
        "IfHCInUcastPkts\0                       "
        "IfHCInMulticastPkts\0                   "
        "IfHCInBroadcastPkts\0                   "
        "IfHCOutUcastPkts\0                      "
        "IfHCOutMulticastPkts\0                  "
        "IfHCOutBroadcastPkts\0                  "
        "IfHCInUcastOctets\0                     "
        "IfHCInMulticastOctets\0                 "
        "IfHCInBroadcastOctets\0                 "
        "IfHCOutUcastOctets\0                    "
        "IfHCOutMulticastOctets\0                "
        "IfHCOutBroadcastOctets\0                "
        "IfHCOutDiscards\0                       "
        "IfHCInFalseCarrierErrors\0              "
        "Dot3StatsInternalMacTransmitErrors\0    "
        "Dot3StatsCarrierSenseErrors\0           "
        "Dot3StatsFCSErrors\0                    "
        "Dot3StatsAlignmentErrors\0              "
        "Dot3StatsSingleCollisionFrames\0        "
        "Dot3StatsMultipleCollisionFrames\0      "
        "Dot3StatsDeferredTransmissions\0        "
        "Dot3StatsExcessiveCollisions\0          "
        "Dot3StatsLateCollisions\0               "
        "EtherStatsCollisions\0                  "
        "EtherStatsFragments\0                   "
        "EtherStatsJabbers\0                     "
        "EtherStatsUndersizePkts\0               "
        "EtherStatsOverrsizePkts\0               "
        "EtherStatsPktsTx64Octets\0              "
        "EtherStatsPktsTx65Octetsto127Octets\0   "
        "EtherStatsPktsTx128Octetsto255Octets\0  "
        "EtherStatsPktsTx256Octetsto511Octets\0  "
        "EtherStatsPktsTx512Octetsto1023Octets\0 "
        "EtherStatsPktsTx1024Octetsto1522Octets\0"
        "EtherStatsPktsTxOver1522Octets\0        "
        "XonPauseFramesReceived\0                "
        "XoffPauseFramesReceived\0               "
        "OutXonSent\0                            "
        "OutXoffSent\0                           "
        "FlowControlDone\0                       "
        "MacControlFramesReceived\0              "
        "XoffStateEntered\0                      "
        "IfInFramesL2FilterDiscards\0            "
        "IfInTTL0Discards\0                      "
        "IfInxxOverflowDiscards\0                "
        "IfInMBUFDiscards\0                      "
        "IfInErrors\0                            "
        "IfInErrorsOctets\0                      "
        "IfInNoBrbBuffer\0                       "

        "Nig_brb_packet\0                        "
        "Nig_brb_truncate\0                      "
        "Nig_flow_ctrl_discard\0                 "
        "Nig_flow_ctrl_octets\0                  "
        "Nig_flow_ctrl_packet\0                  "
        "Nig_mng_discard\0                       "
        "Nig_mng_octet_inp\0                     "
        "Nig_mng_octet_out\0                     "
        "Nig_mng_packet_inp\0                    "
        "Nig_mng_packet_out\0                    "
        "Nig_pbf_octets\0                        "
        "Nig_pbf_packet\0                        "
        "Nig_safc_inp\0                          "

        "Tx_Lpi_Count\0                          "        // This counter counts the number of timers the debounced version of EEE link idle is asserted

        // b10_l4_chip_statistics_t

        "l4_chip_stats_ver_num\0                 "
        "NoTxCqes\0                              "
        "InTCP4Segments\0                        "
        "OutTCP4Segments\0                       "
        "RetransmittedTCP4Segments\0             "
        "InTCP4Errors\0                          "
        "InIP4Receives\0                         "
        "InIP4HeaderErrors\0                     "
        "InIP4Discards\0                         "
        "InIP4Delivers\0                         "
        "InIP4Octets\0                           "
        "OutIP4Octets\0                          "
        "InIP4TruncatedPackets\0                 "
        "InTCP6Segments\0                        "
        "OutTCP6Segments\0                       "
        "RetransmittedTCP6Segments\0             "
        "InTCP6Errors\0                          "
        "InIP6Receives\0                         "
        "InIP6HeaderErrors\0                     "
        "InIP6Discards\0                         "
        "InIP6Delivers\0                         "
        "InIP6Octets\0                           "
        "OutIP6Octets\0                          "
        "InIP6TruncatedPackets\0                 "

        // b10_l2_driver_statistics_t

        "l2_driver_stats_ver_num\0               "
        "RxIPv4FragCount\0                       "
        "RxIpCsErrorCount\0                      "
        "RxTcpCsErrorCount\0                     "
        "RxLlcSnapCount\0                        "
        "RxPhyErrorCount\0                       "
        "RxIpv6ExtCount\0                        "
        "TxNoL2Bd\0                              "
        "TxNoSqWqe\0                             "
        "TxL2AssemblyBufUse\0                    "

        // b10_l4_driver_statistics_t

        "l4_driver_stats_ver_num\0               "
        "CurrentlyIpv4Established\0              "
        "OutIpv4Resets\0                         "
        "OutIpv4Fin\0                            "
        "InIpv4Reset\0                           "
        "InIpv4Fin\0                             "
        "CurrentlyIpv6Established\0              "
        "OutIpv6Resets\0                         "
        "OutIpv6Fin\0                            "
        "InIpv6Reset\0                           "
        "InIpv6Fin\0                             "
        "RxIndicateReturnPendingCnt\0            "
        "RxIndicateReturnDoneCnt\0               "
        "RxActiveGenBufCnt\0                     "
        "TxNoL4Bd\0                              "
        "TxL4AssemblyBufUse\0                   "

        ;

    ASSERT_STATIC((sizeof(p_stat_str) / STATS_STR_LEN) ==
                  (stats_size / sizeof(u64_t)));

    op = *((stats_show_type_t *)pStats);

    switch (op)
    {
    case STATS_SHOW_TYPE_NUM:

        if (statsLen < sizeof(pStats->desc))
        {
            return B_FALSE;
        }

        pStats->desc.num = (stats_size / sizeof(u64_t));
        pStats->desc.len = STATS_STR_LEN;

        return B_TRUE;

    case STATS_SHOW_TYPE_STR:

        if (statsLen != sizeof(p_stat_str))
        {
            return B_FALSE;
        }

        memcpy(pStats->str, p_stat_str, sizeof(p_stat_str));

        return B_TRUE;

    case STATS_SHOW_TYPE_CNT:

        if (statsLen != stats_size)
        {
            return B_FALSE;
        }

        lm_stats_get_l2_chip_stats(&pUM->lm_dev,
                                   &pStats->cnt.l2_chip_stats,
                                   L2_CHIP_STATISTICS_VER_NUM_2);

        lm_stats_get_l4_chip_stats(&pUM->lm_dev,
                                   &pStats->cnt.l4_chip_stats);

        lm_stats_get_l2_driver_stats(&pUM->lm_dev
                                     ,&pStats->cnt.l2_drv_stats);

        lm_stats_get_l4_driver_stats(&pUM->lm_dev,
                                     &pStats->cnt.l4_drv_stats);

        return B_TRUE;

    default:

        return B_FALSE;
    }
}

static void BnxeMacIoctl(void *    pArg,
                         queue_t * pQ,
                         mblk_t *  pMblk)
{
    um_device_t * pUM = (um_device_t *)pArg;
    struct iocblk * pIoctl;
    int rc;

    if ((pQ == NULL) || (pMblk == NULL))
    {
        return;
    }

    if (pMblk->b_datap->db_type != M_IOCTL)
    {
        miocnak(pQ, pMblk, 0, EINVAL);
        return;
    }

    pIoctl = (struct iocblk *)pMblk->b_rptr;

    BNXE_LOCK_ENTER_GLD(pUM);

    switch (pIoctl->ioc_cmd)
    {
    case GIOCBNXELLDP:

        if ((pIoctl->ioc_count != sizeof(b10_lldp_params_get_t)) ||
            (pMblk->b_cont == NULL) || (pMblk->b_cont->b_rptr == NULL) ||
            (miocpullup(pMblk, sizeof(b10_lldp_params_get_t)) < 0))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
            break;
        }

        if (((b10_lldp_params_get_t *)pMblk->b_cont->b_rptr)->ver_num !=
            LLDP_PARAMS_VER_NUM)
        {
            miocnak(pQ, pMblk, 0, EINVAL);
            break;
        }

        if (lm_dcbx_lldp_read_params(&pUM->lm_dev,
                         (b10_lldp_params_get_t *)pMblk->b_cont->b_rptr) !=
            LM_STATUS_SUCCESS)
        {
            miocnak(pQ, pMblk, 0,
                    (!IS_DCB_ENABLED(&pUM->lm_dev)) ? ENOTSUP : EINVAL);
            break;
        }

        miocack(pQ, pMblk, pIoctl->ioc_count, 0);
        break;

    case GIOCBNXEDCBX:

        if ((pIoctl->ioc_count != sizeof(b10_dcbx_params_get_t)) ||
            (pMblk->b_cont == NULL) || (pMblk->b_cont->b_rptr == NULL) ||
            (miocpullup(pMblk, sizeof(b10_dcbx_params_get_t)) < 0))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
            break;
        }

        if (((b10_dcbx_params_get_t *)pMblk->b_cont->b_rptr)->ver_num !=
            DCBX_PARAMS_VER_NUM)
        {
            miocnak(pQ, pMblk, 0, EINVAL);
            break;
        }

        if (lm_dcbx_read_params(&pUM->lm_dev,
                         (b10_dcbx_params_get_t *)pMblk->b_cont->b_rptr) !=
            LM_STATUS_SUCCESS)
        {
            miocnak(pQ, pMblk, 0,
                    (!IS_DCB_ENABLED(&pUM->lm_dev)) ? ENOTSUP : EINVAL);
            break;
        }

        miocack(pQ, pMblk, pIoctl->ioc_count, 0);
        break;

    case SIOCBNXEDCBX:

        /* XXX */
        miocnak(pQ, pMblk, 0, EINVAL);
        break;

    case GIOCBNXEREG:

        if ((pIoctl->ioc_count != sizeof(struct bnxe_reg_data)) ||
            (pMblk->b_cont == NULL) || (pMblk->b_cont->b_rptr == NULL) ||
            (miocpullup(pMblk, sizeof(struct bnxe_reg_data)) < 0))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
            break;
        }

        if (!BnxeReadReg(pUM, (struct bnxe_reg_data *)pMblk->b_cont->b_rptr))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
        }
        else
        {
            miocack(pQ, pMblk, pIoctl->ioc_count, 0);
        }

        break;

    case SIOCBNXEREG:

        if ((pIoctl->ioc_count != sizeof(struct bnxe_reg_data)) ||
            (pMblk->b_cont == NULL) || (pMblk->b_cont->b_rptr == NULL) ||
            (miocpullup(pMblk, sizeof(struct bnxe_reg_data)) < 0))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
            break;
        }

        if (!BnxeWriteReg(pUM, (struct bnxe_reg_data *)pMblk->b_cont->b_rptr))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
        }
        else
        {
            miocack(pQ, pMblk, pIoctl->ioc_count, 0);
        }

        break;

    case GIOCBNXENVRM:

        if ((pIoctl->ioc_count < sizeof(struct bnxe_nvram_data)) ||
            (pMblk->b_cont == NULL) || (pMblk->b_cont->b_rptr == NULL) ||
            (miocpullup(pMblk, pIoctl->ioc_count) < 0))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
            break;
        }

        if (!BnxeReadNvm(pUM, (struct bnxe_nvram_data *)pMblk->b_cont->b_rptr))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
        }
        else
        {
            miocack(pQ, pMblk, pIoctl->ioc_count, 0);
        }

        break;

    case SIOCBNXENVRM:

        if ((pIoctl->ioc_count < sizeof(struct bnxe_nvram_data)) ||
            (pMblk->b_cont == NULL) || (pMblk->b_cont->b_rptr == NULL) ||
            (miocpullup(pMblk, pIoctl->ioc_count) < 0))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
            break;
        }

        if (!BnxeWriteNvm(pUM, (struct bnxe_nvram_data *)pMblk->b_cont->b_rptr))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
        }
        else
        {
            miocack(pQ, pMblk, pIoctl->ioc_count, 0);
        }

        break;

    case GIOCBNXEPCI:

        if ((pIoctl->ioc_count != sizeof(struct bnxe_reg_data)) ||
            (pMblk->b_cont == NULL) || (pMblk->b_cont->b_rptr == NULL) ||
            (miocpullup(pMblk, sizeof(struct bnxe_reg_data)) < 0))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
            break;
        }

        if (!BnxeReadPciCfg(pUM, (struct bnxe_reg_data *)pMblk->b_cont->b_rptr))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
        }
        else
        {
            miocack(pQ, pMblk, pIoctl->ioc_count, 0);
        }

        break;

    case GIOCBNXESTATS:

        /* min size = sizeof(op) in b10_stats_show_data_t */
        if ((pIoctl->ioc_count < sizeof(u32_t)) ||
            (pMblk->b_cont == NULL) || (pMblk->b_cont->b_rptr == NULL) ||
            (miocpullup(pMblk, pIoctl->ioc_count) < 0))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
            break;
        }

        if (!BnxeStatsShow(pUM,
                           (b10_stats_show_data_t *)pMblk->b_cont->b_rptr,
                           pIoctl->ioc_count))
        {
            miocnak(pQ, pMblk, 0, EINVAL);
        }
        else
        {
            miocack(pQ, pMblk, pIoctl->ioc_count, 0);
        }

        break;

    default:

        miocnak(pQ, pMblk, 0, EINVAL);
        break;
    }

    BNXE_LOCK_EXIT_GLD(pUM);
}


#ifdef BNXE_RINGS

#if (defined(__S11) || defined(__S12)) && !defined(ILLUMOS)
static mblk_t * BnxeRxRingPoll(void * ringHandle,
                               int    numBytes,
                               int    numPkts)
#else
static mblk_t * BnxeRxRingPoll(void * ringHandle,
                               int    numBytes)
#endif
{
    RxQueue *     pRxQ  = (RxQueue *)ringHandle;
    um_device_t * pUM   = (um_device_t *)pRxQ->pUM;
    u32_t         idx   = pRxQ->idx;
    mblk_t *      pMblk = NULL;
    boolean_t     pktsRxed = 0;
    boolean_t     pktsTxed = 0;

#if (defined(__S11) || defined(__S12)) && !defined(ILLUMOS)
    _NOTE(ARGUNUSED(numPkts))
#endif

    if (numBytes <= 0)
    {
        return NULL;
    }

    if (pRxQ->inPollMode == B_FALSE)
    {
        BnxeLogWarn(pUM, "Polling on ring %d when NOT in poll mode!", idx);
        return NULL;
    }

    BNXE_LOCK_ENTER_INTR(pUM, idx);

    pRxQ->pollCnt++;

    BnxePollRxRing(pUM, idx, &pktsRxed, &pktsTxed);

    if (pktsTxed) BnxeTxRingProcess(pUM, idx);
    if (pktsRxed) pMblk = BnxeRxRingProcess(pUM, idx, TRUE, numBytes);

    /*
     * This is here for the off chance that all rings are in polling
     * mode and the default interrupt hasn't fired recently to handle
     * the sq.
     */
    lm_sq_post_pending(&pUM->lm_dev);

    BNXE_LOCK_EXIT_INTR(pUM, idx);

    return pMblk;
}


static int BnxeRxRingStart(mac_ring_driver_t ringHandle
#if defined(__S11) || defined(__S12)
                           , uint64_t          genNumber
#endif
                           )
{
    RxQueue *     pRxQ = (RxQueue *)ringHandle;
    um_device_t * pUM  = (um_device_t *)pRxQ->pUM;
    u32_t         idx  = pRxQ->idx;

    BnxeLogDbg(pUM, "Starting Rx Ring %d", idx);

    BNXE_LOCK_ENTER_RX(pUM, idx);
#if defined(__S11) || defined(__S12)
    pRxQ->genNumber      = genNumber;
#endif
    pRxQ->inPollMode     = B_FALSE;
    pRxQ->intrDisableCnt = 0;
    pRxQ->intrEnableCnt  = 0;
    pRxQ->pollCnt        = 0;
    BNXE_LOCK_EXIT_RX(pUM, idx);

    return 0;
}


#if defined(__S11) || defined(__S12)

static int BnxeRingStat(mac_ring_driver_t ringHandle,
                        uint_t            stat,
                        uint64_t *        val)
{
    RxQueue *     pRxQ = (RxQueue *)ringHandle;
    um_device_t * pUM  = (um_device_t *)pRxQ->pUM;

    switch (stat)
    {
    case MAC_STAT_OERRORS:
    case MAC_STAT_OBYTES:
    case MAC_STAT_OPACKETS:
    case MAC_STAT_IERRORS:
    case MAC_STAT_RBYTES: /* MAC_STAT_IBYTES */
    case MAC_STAT_IPACKETS:
    default:
        return ENOTSUP;
    }

    return 0;
}

#endif /* __S11 or __S12 */


#if defined(__S11) || defined(__S12)
static int BnxeRxRingIntrEnable(mac_ring_driver_t ringHandle)
#else
static int BnxeRxRingIntrEnable(mac_intr_handle_t ringHandle)
#endif
{
    RxQueue *     pRxQ = (RxQueue *)ringHandle;
    um_device_t * pUM  = (um_device_t *)pRxQ->pUM;

    BnxeLogDbg(pUM, "Enabling Interrupt for Rx Ring %d", pRxQ->idx);

    /* polling not allowed on LM_NON_RSS_SB when overlapped with FCoE */
    if ((pRxQ->idx == LM_NON_RSS_SB(&pUM->lm_dev)) &&
        CLIENT_BOUND(pUM, LM_CLI_IDX_FCOE) &&
        (pUM->rssIntr.intrCount == LM_MAX_RSS_CHAINS(&pUM->lm_dev)))
    {
        return 0; /* ok, already enabled */
    }

    BnxeIntrIguSbEnable(pUM, pRxQ->idx, B_FALSE);

    return 0;
}


#if defined(__S11) || defined(__S12)
static int BnxeRxRingIntrDisable(mac_ring_driver_t ringHandle)
#else
static int BnxeRxRingIntrDisable(mac_intr_handle_t ringHandle)
#endif
{
    RxQueue *     pRxQ = (RxQueue *)ringHandle;
    um_device_t * pUM  = (um_device_t *)pRxQ->pUM;

    BnxeLogDbg(pUM, "Disabling Interrupt for Rx Ring %d", pRxQ->idx);

    /* polling not allowed on LM_NON_RSS_SB when overlapped with FCoE */
    if ((pRxQ->idx == LM_NON_RSS_SB(&pUM->lm_dev)) &&
        CLIENT_BOUND(pUM, LM_CLI_IDX_FCOE) &&
        (pUM->rssIntr.intrCount == LM_MAX_RSS_CHAINS(&pUM->lm_dev)))
    {
        return -1; /* NO, keep enabled! */
    }

    BnxeIntrIguSbDisable(pUM, pRxQ->idx, B_FALSE);

    return 0;
}


/* callback function for MAC layer to register rings */
static void BnxeFillRing(void *            arg,
                         mac_ring_type_t   ringType,
                         const int         ringGroupIndex,
                         const int         ringIndex,
                         mac_ring_info_t * pRingInfo,
                         mac_ring_handle_t ringHandle)
{
    um_device_t * pUM = (um_device_t *)arg;
    RxQueue *     pRxQ;
    TxQueue *     pTxQ;

    switch (ringType)
    {
    case MAC_RING_TYPE_RX:

        BnxeLogInfo(pUM, "Initializing Rx Ring %d (Ring Group %d)",
                    ringIndex, ringGroupIndex);

        ASSERT(ringGroupIndex == 0);
        ASSERT(ringIndex < pUM->devParams.numRings);

        pRxQ = &pUM->rxq[ringIndex];
        pRxQ->ringHandle = ringHandle;

        pRingInfo->mri_driver = (mac_ring_driver_t)pRxQ;
        pRingInfo->mri_start  = BnxeRxRingStart;
        pRingInfo->mri_stop   = NULL;
#if defined(__S11) || defined(__S12)
        pRingInfo->mri_stat   = BnxeRingStat;
#endif
        pRingInfo->mri_poll   = BnxeRxRingPoll;

#if !(defined(__S11) || defined(__S12))
        pRingInfo->mri_intr.mi_handle  = (mac_intr_handle_t)pRxQ;
#endif
        pRingInfo->mri_intr.mi_enable  = (mac_intr_enable_t)BnxeRxRingIntrEnable;
        pRingInfo->mri_intr.mi_disable = (mac_intr_disable_t)BnxeRxRingIntrDisable;

        break;

    case MAC_RING_TYPE_TX:

        BnxeLogInfo(pUM, "Initializing Tx Ring %d (Ring Group %d)",
                    ringIndex, ringGroupIndex);

        ASSERT(ringGroupIndex == 0);
        ASSERT(ringIndex < pUM->devParams.numRings);

        pTxQ = &pUM->txq[ringIndex];
        pTxQ->ringHandle = ringHandle;

        pRingInfo->mri_driver = (mac_ring_driver_t)pTxQ;
        pRingInfo->mri_start  = NULL;
        pRingInfo->mri_stop   = NULL;
#if defined(__S11) || defined(__S12)
        pRingInfo->mri_stat   = BnxeRingStat;
#endif
        pRingInfo->mri_tx     = (mac_ring_send_t)BnxeTxRingSend;

        break;

    default:
        break;
    }
}


/* callback function for MAC layer to register groups */
static void BnxeFillGroup(void *             arg,
                          mac_ring_type_t    ringType,
                          const int          ringGroupIndex,
                          mac_group_info_t * pGroupInfo,
                          mac_group_handle_t groupHandle)
{
    um_device_t *  pUM = (um_device_t *)arg;
    RxQueueGroup * pRxQGroup;

    switch (ringType)
    {
    case MAC_RING_TYPE_RX:

        BnxeLogInfo(pUM, "Initializing Rx Group %d", ringGroupIndex);

        pRxQGroup = &pUM->rxqGroup[ringGroupIndex];
        pRxQGroup->groupHandle = groupHandle;

        pGroupInfo->mgi_driver = (mac_group_driver_t)pRxQGroup;
        pGroupInfo->mgi_start  = NULL;
        pGroupInfo->mgi_stop   = NULL;
        pGroupInfo->mgi_addmac = BnxeRxRingGroupAddMac;
        pGroupInfo->mgi_remmac = BnxeRxRingGroupRemMac;
        pGroupInfo->mgi_count  = (pUM->devParams.numRings /
                                  USER_OPTION_RX_RING_GROUPS_DEFAULT);
#if (defined(__S11) || defined(__S12)) && !defined(ILLUMOS)
        pGroupInfo->mgi_flags  = MAC_GROUP_DEFAULT;
#endif

        break;

    case MAC_RING_TYPE_TX:
    default:
        break;
    }
}

#endif /* BNXE_RINGS */


static boolean_t BnxeMacGetCapability(void *      pArg,
                                      mac_capab_t capability,
                                      void *      pCapabilityData)
{
    um_device_t * pUM = (um_device_t *)pArg;
    mac_capab_lso_t *   pCapLSO;
    mac_capab_rings_t * pCapRings;

    switch (capability)
    {
    case MAC_CAPAB_HCKSUM:

        *((u32_t *)pCapabilityData) = 0;

        if (pUM->devParams.enabled_oflds &
            (LM_OFFLOAD_TX_IP_CKSUM | LM_OFFLOAD_RX_IP_CKSUM))
        {
            *((u32_t *)pCapabilityData) |= HCKSUM_IPHDRCKSUM;
        }

        if (pUM->devParams.enabled_oflds &
            (LM_OFFLOAD_TX_TCP_CKSUM | LM_OFFLOAD_TX_UDP_CKSUM |
             LM_OFFLOAD_RX_TCP_CKSUM | LM_OFFLOAD_RX_UDP_CKSUM))
        {
            *((u32_t *)pCapabilityData) |= HCKSUM_INET_PARTIAL;
        }

        break;

    case MAC_CAPAB_LSO:

        pCapLSO = (mac_capab_lso_t *)pCapabilityData;

        if (pUM->devParams.lsoEnable)
        {
            pCapLSO->lso_flags                  = LSO_TX_BASIC_TCP_IPV4;
            pCapLSO->lso_basic_tcp_ipv4.lso_max = BNXE_LSO_MAXLEN;
            break;
        }

        return B_FALSE;

#ifdef BNXE_RINGS

    case MAC_CAPAB_RINGS:

        if (!pUM->devParams.numRings)
        {
            return B_FALSE;
        }

        pCapRings = (mac_capab_rings_t *)pCapabilityData;

#if (defined(__S11) || defined(__S12)) && !defined(ILLUMOS)
        pCapRings->mr_version    = MAC_RINGS_VERSION_1;
        pCapRings->mr_flags      = MAC_RINGS_FLAGS_NONE;
#endif
        pCapRings->mr_group_type = MAC_GROUP_TYPE_STATIC;
        pCapRings->mr_rnum       = pUM->devParams.numRings;
        pCapRings->mr_rget       = BnxeFillRing;
        pCapRings->mr_gaddring   = NULL;
        pCapRings->mr_gremring   = NULL;
#if (defined(__S11) || defined(__S12)) && !defined(ILLUMOS)
        pCapRings->mr_ggetringtc = NULL;
#endif

        switch (pCapRings->mr_type)
        {
        case MAC_RING_TYPE_RX:

            pCapRings->mr_gnum = USER_OPTION_RX_RING_GROUPS_DEFAULT;
            pCapRings->mr_gget = BnxeFillGroup;
            break;

        case MAC_RING_TYPE_TX:

#if (defined(__S11) || defined(__S12)) && !defined(ILLUMOS)
            pCapRings->mr_gnum = 1;
#else
            pCapRings->mr_gnum = 0;
#endif
            pCapRings->mr_gget = NULL;
            break;

        default:

            return B_FALSE;
        }

        break;

#endif /* BNXE_RINGS */

#if !(defined(__S11) || defined(__S12))

    case MAC_CAPAB_POLL:

        /*
         * There's nothing for us to fill in, simply returning B_TRUE stating
         * that we support polling is sufficient.
         */
        break;

#endif /* not __S11 or __S12 */

    default:

        return B_FALSE;
    }

    return B_TRUE;
}


#ifdef MC_SETPROP

static int BnxeSetPrivateProperty(um_device_t * pUM,
                                  const char *  pr_name,
                                  uint_t        pr_valsize,
                                  const void *  pr_val)
{
    int err = 0;
    long result;

    if (strcmp(pr_name, "_en_2500fdx_cap") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->hwinit.lnkcfg.param_2500fdx = (uint32_t)result;
        pUM->curcfg.lnkcfg.param_2500fdx = (uint32_t)result;
        if (pUM->plumbed) BnxeUpdatePhy(pUM);
    }
    else if (strcmp(pr_name, "_en_txpause_cap") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->hwinit.lnkcfg.param_txpause = (uint32_t)result;
        pUM->curcfg.lnkcfg.param_txpause = (uint32_t)result;
        if (pUM->plumbed) BnxeUpdatePhy(pUM);
    }
    else if (strcmp(pr_name, "_en_rxpause_cap") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->hwinit.lnkcfg.param_rxpause = (uint32_t)result;
        pUM->curcfg.lnkcfg.param_rxpause = (uint32_t)result;
        if (pUM->plumbed) BnxeUpdatePhy(pUM);
    }
    else if (strcmp(pr_name, "_autoneg_flow") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->hwinit.flow_autoneg = (uint32_t)result;
        pUM->curcfg.flow_autoneg = (uint32_t)result;
        if (pUM->plumbed) BnxeUpdatePhy(pUM);
    }
    else if (strcmp(pr_name, "_checksum") == 0)
    {
        if (pUM->plumbed)
        {
            return EBUSY;
        }

        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        switch (result)
        {
        case USER_OPTION_CKSUM_NONE:

            pUM->devParams.enabled_oflds = LM_OFFLOAD_NONE;
            break;

        case USER_OPTION_CKSUM_L3:

            pUM->devParams.enabled_oflds = (LM_OFFLOAD_TX_IP_CKSUM |
                                            LM_OFFLOAD_RX_IP_CKSUM);
            break;

        case USER_OPTION_CKSUM_L3_L4:

            pUM->devParams.enabled_oflds = (LM_OFFLOAD_TX_IP_CKSUM  |
                                            LM_OFFLOAD_RX_IP_CKSUM  |
                                            LM_OFFLOAD_TX_TCP_CKSUM |
                                            LM_OFFLOAD_RX_TCP_CKSUM |
                                            LM_OFFLOAD_TX_UDP_CKSUM |
                                            LM_OFFLOAD_RX_UDP_CKSUM);
            break;

        default:

            return EINVAL;
        }

        pUM->devParams.checksum = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_tx_ring_policy") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        switch (result)
        {
        case BNXE_ROUTE_RING_NONE:
        case BNXE_ROUTE_RING_TCPUDP:
        case BNXE_ROUTE_RING_DEST_MAC:
        case BNXE_ROUTE_RING_MSG_PRIO:

            break;

        default:

            return EINVAL;
        }

        pUM->devParams.routeTxRingPolicy = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_num_rings") == 0)
    {
        if (pUM->plumbed)
        {
            return EBUSY;
        }

        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result < USER_OPTION_NUM_RINGS_MIN) ||
            (result > USER_OPTION_NUM_RINGS_MAX))
        {
            return EINVAL;
        }

        pUM->devParams.numRings = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_rx_descs") == 0)
    {
        if (pUM->plumbed)
        {
            return EBUSY;
        }

        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result < USER_OPTION_BDS_MIN) || (result > USER_OPTION_BDS_MAX))
        {
            return EINVAL;
        }

        pUM->devParams.numRxDesc[LM_CLI_IDX_NDIS] = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_rx_free_reclaim") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result < USER_OPTION_BDS_MIN) || (result > USER_OPTION_BDS_MAX))
        {
            return EINVAL;
        }

        pUM->devParams.maxRxFree = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_tx_descs") == 0)
    {
        if (pUM->plumbed)
        {
            return EBUSY;
        }

        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result < USER_OPTION_BDS_MIN) || (result > USER_OPTION_BDS_MAX))
        {
            return EINVAL;
        }

        pUM->devParams.numTxDesc[LM_CLI_IDX_NDIS] = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_tx_free_reclaim") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result < USER_OPTION_BDS_MIN) || (result > USER_OPTION_BDS_MAX))
        {
            return EINVAL;
        }

        pUM->devParams.maxTxFree = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_rx_copy_threshold") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        pUM->devParams.rxCopyThreshold = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_tx_copy_threshold") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        pUM->devParams.txCopyThreshold = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_interrupt_coalesce") == 0)
    {
        if (pUM->plumbed)
        {
            return EBUSY;
        }

        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->devParams.intrCoalesce = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_rx_interrupt_coalesce_usec") == 0)
    {
        if (pUM->plumbed)
        {
            return EBUSY;
        }

        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result < USER_OPTION_INTR_COALESCE_MIN) ||
            (result < USER_OPTION_INTR_COALESCE_MAX))
        {
            return EINVAL;
        }

        pUM->devParams.intrRxPerSec = (uint32_t)(1000000 / result);
    }
    else if (strcmp(pr_name, "_tx_interrupt_coalesce_usec") == 0)
    {
        if (pUM->plumbed)
        {
            return EBUSY;
        }

        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result < USER_OPTION_INTR_COALESCE_MIN) ||
            (result < USER_OPTION_INTR_COALESCE_MAX))
        {
            return EINVAL;
        }

        pUM->devParams.intrTxPerSec = (uint32_t)(1000000 / result);
    }
    else if (strcmp(pr_name, "_disable_msix") == 0)
    {
        if (pUM->plumbed)
        {
            return EBUSY;
        }

        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->devParams.disableMsix = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_l2_fw_flow_ctrl") == 0)
    {
        if (pUM->plumbed)
        {
            return EBUSY;
        }

        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->devParams.l2_fw_flow_ctrl = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_autogreeen_enable") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->devParams.autogreeenEnable = (uint32_t)result;
        if (pUM->plumbed) BnxeUpdatePhy(pUM);
    }
    else if (strcmp(pr_name, "_lso_enable") == 0)
    {
        if (pUM->plumbed)
        {
            return EBUSY;
        }

        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->devParams.lsoEnable = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_log_enable") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->devParams.logEnable = (uint32_t)result;
    }
    else if (strcmp(pr_name, "_fcoe_enable") == 0)
    {
        if (ddi_strtol(pr_val, (char **)NULL, 0, &result))
        {
            return EINVAL;
        }

        if ((result > 1) || (result < 0))
        {
            return EINVAL;
        }

        pUM->devParams.fcoeEnable = (uint32_t)result;

        if (BNXE_FCOE(pUM))
        {
            BnxeFcoeStartStop(pUM);
        }
    }
    else
    {
        err = ENOTSUP;
    }

    return err;
}


static int BnxeMacSetProperty(void *        barg,
                              const char *  pr_name,
                              mac_prop_id_t pr_num,
                              uint_t        pr_valsize,
                              const void *  pr_val)
{
    um_device_t *   pUM = barg;
    boolean_t       reprogram = B_FALSE;
    boolean_t       rxpause;
    boolean_t       txpause;
    uint32_t        mtu;
    link_flowctrl_t fl;
    int err = 0;

    BNXE_LOCK_ENTER_GLD(pUM);

    switch (pr_num)
    {
    /* read-only props */
    case MAC_PROP_STATUS:
    case MAC_PROP_SPEED:
    case MAC_PROP_DUPLEX:

    case MAC_PROP_ADV_10GFDX_CAP:
    case MAC_PROP_ADV_1000FDX_CAP:
    case MAC_PROP_ADV_1000HDX_CAP:
    case MAC_PROP_ADV_100FDX_CAP:
    case MAC_PROP_ADV_100HDX_CAP:
    case MAC_PROP_ADV_10FDX_CAP:
    case MAC_PROP_ADV_10HDX_CAP:
    case MAC_PROP_ADV_100T4_CAP:

    case MAC_PROP_EN_1000HDX_CAP:
    case MAC_PROP_EN_100T4_CAP:

    default:

        err = ENOTSUP;
        break;

    case MAC_PROP_EN_10GFDX_CAP:

        pUM->hwinit.lnkcfg.param_10000fdx = *(uint8_t *)pr_val;
        pUM->curcfg.lnkcfg.param_10000fdx = *(uint8_t *)pr_val;
        reprogram = B_TRUE;
        break;

    case MAC_PROP_EN_1000FDX_CAP:

        pUM->hwinit.lnkcfg.param_1000fdx = *(uint8_t *)pr_val;
        pUM->curcfg.lnkcfg.param_1000fdx = *(uint8_t *)pr_val;
        reprogram = B_TRUE;
        break;

    case MAC_PROP_EN_100FDX_CAP:

        pUM->hwinit.lnkcfg.param_100fdx = *(uint8_t *)pr_val;
        pUM->curcfg.lnkcfg.param_100fdx = *(uint8_t *)pr_val;
        reprogram = B_TRUE;
        break;

    case MAC_PROP_EN_100HDX_CAP:

        pUM->hwinit.lnkcfg.param_100hdx = *(uint8_t *)pr_val;
        pUM->curcfg.lnkcfg.param_100hdx = *(uint8_t *)pr_val;
        reprogram = B_TRUE;
        break;

    case MAC_PROP_EN_10FDX_CAP:

        pUM->hwinit.lnkcfg.param_10fdx = *(uint8_t *)pr_val;
        pUM->curcfg.lnkcfg.param_10fdx = *(uint8_t *)pr_val;
        reprogram = B_TRUE;
        break;

    case MAC_PROP_EN_10HDX_CAP:

        pUM->hwinit.lnkcfg.param_10hdx = *(uint8_t *)pr_val;
        pUM->curcfg.lnkcfg.param_10hdx = *(uint8_t *)pr_val;
        reprogram = B_TRUE;
        break;

    case MAC_PROP_AUTONEG:

        pUM->hwinit.lnkcfg.link_autoneg = *(uint8_t *)pr_val;
        pUM->curcfg.lnkcfg.link_autoneg = *(uint8_t *)pr_val;
        reprogram = B_TRUE;
        break;

    case MAC_PROP_FLOWCTRL:

        bcopy(pr_val, &fl, sizeof(fl));

        switch (fl)
        {
        case LINK_FLOWCTRL_NONE:

            rxpause = B_FALSE;
            txpause = B_FALSE;
            break;

        case LINK_FLOWCTRL_RX:

            rxpause = B_TRUE;
            txpause = B_FALSE;
            break;

        case LINK_FLOWCTRL_TX:

            rxpause = B_FALSE;
            txpause = B_TRUE;
            break;

        case LINK_FLOWCTRL_BI:

            rxpause = B_TRUE;
            txpause = B_TRUE;
            break;

        default:

            err = ENOTSUP;
            break;
        }

        if (err == 0)
        {
            pUM->hwinit.lnkcfg.param_rxpause = rxpause;
            pUM->hwinit.lnkcfg.param_txpause = txpause;
            pUM->curcfg.lnkcfg.param_rxpause = rxpause;
            pUM->curcfg.lnkcfg.param_txpause = txpause;
            reprogram = B_TRUE;
        }

        break;

    case MAC_PROP_MTU:

        if (pUM->plumbed)
        {
            err = EBUSY;
            break;
        }

        bcopy(pr_val, &mtu, sizeof (mtu));

        if ((mtu < USER_OPTION_MTU_MIN) || (mtu > USER_OPTION_MTU_MAX))
        {
            err = EINVAL;
            break;
        }

        if (pUM->devParams.mtu[LM_CLI_IDX_NDIS] == mtu)
        {
            break;
        }

        pUM->devParams.mtu[LM_CLI_IDX_NDIS] = mtu;
        err = mac_maxsdu_update(pUM->pMac, pUM->devParams.mtu[LM_CLI_IDX_NDIS]);
        pUM->lm_dev.params.mtu[LM_CLI_IDX_NDIS] = pUM->devParams.mtu[LM_CLI_IDX_NDIS];
        break;

    case MAC_PROP_PRIVATE:

        err = BnxeSetPrivateProperty(pUM, pr_name, pr_valsize, pr_val);
        break;
    }

    if (!err && reprogram)
    {
        if (pUM->plumbed) BnxeUpdatePhy(pUM);
    }

    BNXE_LOCK_EXIT_GLD(pUM);
    return err;
}

#endif /* MC_SETPROP */


#ifdef MC_GETPROP

static int BnxeGetPrivateProperty(um_device_t * pUM,
                                  const char *  pr_name,
                                  uint_t        pr_valsize,
                                  void *        pr_val)
{
    BnxeLinkCfg * lnk_cfg = &pUM->curcfg.lnkcfg;
    BnxeLinkCfg * hw_cfg  = &pUM->hwinit.lnkcfg;
    int value;
    int err = 0;

    if (strcmp(pr_name, "_adv_2500fdx_cap") == 0)
    {
        value = lnk_cfg->param_2500fdx;
    }
    else if (strcmp(pr_name, "_en_2500fdx_cap") == 0)
    {
        value = hw_cfg->param_2500fdx;
    }
    else if (strcmp(pr_name, "_adv_txpause_cap") == 0)
    {
        value = lnk_cfg->param_txpause;
    }
    else if (strcmp(pr_name, "_en_txpause_cap") == 0)
    {
        value = hw_cfg->param_txpause;
    }
    else if (strcmp(pr_name, "_txpause") == 0)
    {
        value = pUM->props.link_txpause;
    }
    else if (strcmp(pr_name, "_adv_rxpause_cap") == 0)
    {
        value = lnk_cfg->param_rxpause;
    }
    else if (strcmp(pr_name, "_en_rxpause_cap") == 0)
    {
        value = hw_cfg->param_rxpause;
    }
    else if (strcmp(pr_name, "_rxpause") == 0)
    {
        value = pUM->props.link_rxpause;
    }
    else if (strcmp(pr_name, "_autoneg_flow") == 0)
    {
        value = pUM->hwinit.flow_autoneg;
    }
    else if (strcmp(pr_name, "_checksum") == 0)
    {
        value = pUM->devParams.checksum;
    }
    else if (strcmp(pr_name, "_tx_ring_policy") == 0)
    {
        value = pUM->devParams.routeTxRingPolicy;
    }
    else if (strcmp(pr_name, "_num_rings") == 0)
    {
        value = pUM->devParams.numRings;
    }
    else if (strcmp(pr_name, "_rx_descs") == 0)
    {
        value = pUM->devParams.numRxDesc[LM_CLI_IDX_NDIS];
    }
    else if (strcmp(pr_name, "_rx_free_reclaim") == 0)
    {
        value = pUM->devParams.maxRxFree;
    }
    else if (strcmp(pr_name, "_tx_descs") == 0)
    {
        value = pUM->devParams.numTxDesc[LM_CLI_IDX_NDIS];
    }
    else if (strcmp(pr_name, "_tx_free_reclaim") == 0)
    {
        value = pUM->devParams.maxTxFree;
    }
    else if (strcmp(pr_name, "_rx_copy_threshold") == 0)
    {
        value = pUM->devParams.rxCopyThreshold;
    }
    else if (strcmp(pr_name, "_tx_copy_threshold") == 0)
    {
        value = pUM->devParams.txCopyThreshold;
    }
    else if (strcmp(pr_name, "_interrupt_coalesce") == 0)
    {
        value = pUM->devParams.intrCoalesce;
    }
    else if (strcmp(pr_name, "_rx_interrupt_coalesce_usec") == 0)
    {
        value = pUM->devParams.intrRxPerSec;
    }
    else if (strcmp(pr_name, "_tx_interrupt_coalesce_usec") == 0)
    {
        value = pUM->devParams.intrTxPerSec;
    }
    else if (strcmp(pr_name, "_disable_msix") == 0)
    {
        value = pUM->devParams.disableMsix;
    }
    else if (strcmp(pr_name, "_l2_fw_flow_ctrl") == 0)
    {
        value = pUM->devParams.l2_fw_flow_ctrl;
    }
    else if (strcmp(pr_name, "_autogreeen_enable") == 0)
    {
        value = pUM->devParams.autogreeenEnable;
    }
    else if (strcmp(pr_name, "_lso_enable") == 0)
    {
        value = pUM->devParams.lsoEnable;
    }
    else if (strcmp(pr_name, "_log_enable") == 0)
    {
        value = pUM->devParams.logEnable;
    }
    else if (strcmp(pr_name, "_fcoe_enable") == 0)
    {
        value = pUM->devParams.fcoeEnable;
    }
    else
    {
        err = ENOTSUP;
    }

    if (!err)
    {
        (void)snprintf(pr_val, pr_valsize, "%d", value);
    }

    return err;
}


static int BnxeMacGetProperty(void *        barg,
                              const char *  pr_name,
                              mac_prop_id_t pr_num,
                              uint_t        pr_valsize,
                              void *        pr_val)
{
    um_device_t *   pUM = barg;
    link_flowctrl_t link_flowctrl;
    link_state_t    link_state;
    link_duplex_t   link_duplex;
    uint64_t        link_speed;
    BnxeLinkCfg * lnk_cfg = &pUM->curcfg.lnkcfg;
    BnxeLinkCfg * hw_cfg  = &pUM->hwinit.lnkcfg;

    switch (pr_num)
    {
    case MAC_PROP_MTU:

        ASSERT(pr_valsize >= sizeof(u32_t));

        bcopy(&pUM->devParams.mtu[LM_CLI_IDX_NDIS], pr_val, sizeof(u32_t));
        break;

    case MAC_PROP_DUPLEX:

        ASSERT(pr_valsize >= sizeof(link_duplex_t));

        link_duplex = pUM->props.link_duplex ?
                          LINK_DUPLEX_FULL : LINK_DUPLEX_HALF;
        bcopy(&link_duplex, pr_val, sizeof(link_duplex_t));
        break;

    case MAC_PROP_SPEED:

        ASSERT(pr_valsize >= sizeof(link_speed));

        link_speed = (pUM->props.link_speed * 1000000ULL);
        bcopy(&link_speed, pr_val, sizeof(link_speed));
        break;

    case MAC_PROP_STATUS:

        ASSERT(pr_valsize >= sizeof(link_state_t));

        link_state = pUM->props.link_speed ?
                         LINK_STATE_UP : LINK_STATE_DOWN;
        bcopy(&link_state, pr_val, sizeof(link_state_t));
        break;

    case MAC_PROP_AUTONEG:

        *(uint8_t *)pr_val = lnk_cfg->link_autoneg;
        break;

    case MAC_PROP_FLOWCTRL:

        ASSERT(pr_valsize >= sizeof(link_flowctrl_t));

        if (!lnk_cfg->param_rxpause && !lnk_cfg->param_txpause)
        {
            link_flowctrl = LINK_FLOWCTRL_NONE;
        }
        if (lnk_cfg->param_rxpause && !lnk_cfg->param_txpause)
        {
            link_flowctrl = LINK_FLOWCTRL_RX;
        }
        if (!lnk_cfg->param_rxpause && lnk_cfg->param_txpause)
        {
            link_flowctrl = LINK_FLOWCTRL_TX;
        }
        if (lnk_cfg->param_rxpause && lnk_cfg->param_txpause)
        {
            link_flowctrl = LINK_FLOWCTRL_BI;
        }

        bcopy(&link_flowctrl, pr_val, sizeof(link_flowctrl_t));
        break;

    case MAC_PROP_ADV_10GFDX_CAP:

        *(uint8_t *)pr_val = lnk_cfg->param_10000fdx;
        break;

    case MAC_PROP_EN_10GFDX_CAP:

        *(uint8_t *)pr_val = hw_cfg->param_10000fdx;
        break;

    case MAC_PROP_ADV_1000FDX_CAP:

        *(uint8_t *)pr_val = lnk_cfg->param_1000fdx;
        break;

    case MAC_PROP_EN_1000FDX_CAP:

        *(uint8_t *)pr_val = hw_cfg->param_1000fdx;
        break;

    case MAC_PROP_ADV_1000HDX_CAP:
    case MAC_PROP_EN_1000HDX_CAP:

        *(uint8_t *)pr_val = 0;
        break;

    case MAC_PROP_ADV_100FDX_CAP:

        *(uint8_t *)pr_val = lnk_cfg->param_100fdx;
        break;

    case MAC_PROP_EN_100FDX_CAP:

        *(uint8_t *)pr_val = hw_cfg->param_100fdx;
        break;

    case MAC_PROP_ADV_100HDX_CAP:

        *(uint8_t *)pr_val = lnk_cfg->param_100hdx;
        break;

    case MAC_PROP_EN_100HDX_CAP:

        *(uint8_t *)pr_val = hw_cfg->param_100hdx;
        break;

    case MAC_PROP_ADV_100T4_CAP:
    case MAC_PROP_EN_100T4_CAP:

        *(uint8_t *)pr_val = 0;
        break;

    case MAC_PROP_ADV_10FDX_CAP:

        *(uint8_t *)pr_val = lnk_cfg->param_10fdx;
        break;

    case MAC_PROP_EN_10FDX_CAP:

        *(uint8_t *)pr_val = hw_cfg->param_10fdx;
        break;

    case MAC_PROP_ADV_10HDX_CAP:

        *(uint8_t *)pr_val = lnk_cfg->param_10hdx;
        break;

    case MAC_PROP_EN_10HDX_CAP:

        *(uint8_t *)pr_val = hw_cfg->param_10hdx;
        break;

    case MAC_PROP_PRIVATE:

        return BnxeGetPrivateProperty(pUM,
                                      pr_name,
                                      pr_valsize,
                                      pr_val);

    default:

        return ENOTSUP;
    }

    return 0;
}

#endif /* MC_GETPROP */


#ifdef MC_PROPINFO

static void BnxeMacPrivatePropertyInfo(um_device_t *          pUM,
                                       const char *           pr_name,
                                       mac_prop_info_handle_t prh)
{
    char valstr[64];
    BnxeLinkCfg * default_cfg = &bnxeLinkCfg;
    int default_val;

    bzero(valstr, sizeof (valstr));

    if ((strcmp(pr_name, "_adv_2500fdx_cap")            == 0) ||
        (strcmp(pr_name, "_adv_txpause_cap")            == 0) ||
        (strcmp(pr_name, "_txpause")                    == 0) ||
        (strcmp(pr_name, "_adv_rxpause_cap")            == 0) ||
        (strcmp(pr_name, "_rxpause")                    == 0) ||
        (strcmp(pr_name, "_checksum")                   == 0) ||
        (strcmp(pr_name, "_num_rings")                  == 0) ||
        (strcmp(pr_name, "_rx_descs")                   == 0) ||
        (strcmp(pr_name, "_tx_descs")                   == 0) ||
        (strcmp(pr_name, "_interrupt_coalesce")         == 0) ||
        (strcmp(pr_name, "_rx_interrupt_coalesce_usec") == 0) ||
        (strcmp(pr_name, "_tx_interrupt_coalesce_usec") == 0) ||
        (strcmp(pr_name, "_disable_msix")               == 0) ||
        (strcmp(pr_name, "_l2_fw_flow_ctrl")            == 0) ||
        (strcmp(pr_name, "_lso_enable")                 == 0))
    {
        mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
        return;
    }

    if (strcmp(pr_name, "_autoneg_flow") == 0)
    {
        default_val = B_TRUE;
    }
    else if (strcmp(pr_name, "_tx_ring_policy") == 0)
    {
        default_val = BNXE_ROUTE_RING_TCPUDP;
    }
    else if (strcmp(pr_name, "_rx_free_reclaim") == 0)
    {
        default_val = USER_OPTION_RX_MAX_FREE_DEFAULT;
    }
    else if (strcmp(pr_name, "_tx_free_reclaim") == 0)
    {
        default_val = USER_OPTION_TX_MAX_FREE_DEFAULT;
    }
    else if (strcmp(pr_name, "_rx_copy_threshold") == 0)
    {
        default_val = USER_OPTION_RX_DCOPY_THRESH_DEFAULT;
    }
    else if (strcmp(pr_name, "_tx_copy_threshold") == 0)
    {
        default_val = USER_OPTION_TX_DCOPY_THRESH_DEFAULT;
    }
    else if (strcmp(pr_name, "_autogreeen_enable") == 0)
    {
        default_val = B_TRUE;
    }
    else if (strcmp(pr_name, "_log_enable") == 0)
    {
        default_val = B_TRUE;
    }
    else if (strcmp(pr_name, "_fcoe_enable") == 0)
    {
        default_val = B_TRUE;
    }
    else
    {
        return;
    }

    snprintf(valstr, sizeof (valstr), "%d", default_val);
    mac_prop_info_set_default_str(prh, valstr);
}


static void BnxeMacPropertyInfo(void *                 barg,
                                const char *           pr_name,
                                mac_prop_id_t          pr_num, 
                                mac_prop_info_handle_t prh)
{
    um_device_t * pUM = barg;
    link_flowctrl_t link_flowctrl;
    BnxeLinkCfg * default_cfg = &bnxeLinkCfg;

    switch (pr_num)
    {
    case MAC_PROP_STATUS:
    case MAC_PROP_SPEED:
    case MAC_PROP_DUPLEX:

    case MAC_PROP_ADV_10GFDX_CAP:
    case MAC_PROP_ADV_1000FDX_CAP:
    case MAC_PROP_ADV_1000HDX_CAP:
    case MAC_PROP_ADV_100FDX_CAP:
    case MAC_PROP_ADV_100HDX_CAP:
    case MAC_PROP_ADV_100T4_CAP:
    case MAC_PROP_ADV_10FDX_CAP:
    case MAC_PROP_ADV_10HDX_CAP:

    case MAC_PROP_EN_1000HDX_CAP:
    case MAC_PROP_EN_100T4_CAP:

        mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
        break;

    case MAC_PROP_EN_10GFDX_CAP:

        mac_prop_info_set_default_uint8(prh, default_cfg->param_10000fdx);
        break;

    case MAC_PROP_EN_1000FDX_CAP:

        mac_prop_info_set_default_uint8(prh, default_cfg->param_1000fdx);
        break;

    case MAC_PROP_EN_100FDX_CAP:

        mac_prop_info_set_default_uint8(prh, default_cfg->param_100fdx);
        break;

    case MAC_PROP_EN_100HDX_CAP:

        mac_prop_info_set_default_uint8(prh, default_cfg->param_100hdx);
        break;

    case MAC_PROP_EN_10FDX_CAP:

        mac_prop_info_set_default_uint8(prh, default_cfg->param_10fdx);
        break;

    case MAC_PROP_EN_10HDX_CAP:

        mac_prop_info_set_default_uint8(prh, default_cfg->param_10hdx);
        break;

    case MAC_PROP_MTU:

        mac_prop_info_set_range_uint32(prh,
                                       USER_OPTION_MTU_MIN,
                                       USER_OPTION_MTU_MAX);
        break;

    case MAC_PROP_AUTONEG:

        mac_prop_info_set_default_uint8(prh, default_cfg->link_autoneg);
        break;

    case MAC_PROP_FLOWCTRL:

        if (!default_cfg->param_rxpause && !default_cfg->param_txpause)
        {
            link_flowctrl = LINK_FLOWCTRL_NONE;
        }

        if (default_cfg->param_rxpause && !default_cfg->param_txpause)
        {
            link_flowctrl = LINK_FLOWCTRL_RX;
        }

        if (!default_cfg->param_rxpause && default_cfg->param_txpause)
        {
            link_flowctrl = LINK_FLOWCTRL_TX;
        }

        if (default_cfg->param_rxpause && default_cfg->param_txpause)
        {
            link_flowctrl = LINK_FLOWCTRL_BI;
        }

        mac_prop_info_set_default_link_flowctrl(prh, link_flowctrl);
        break;

    case MAC_PROP_PRIVATE:

        BnxeMacPrivatePropertyInfo(pUM, pr_name, prh);
        break;
    }
}

#endif /* MC_PROPINFO */


static mac_callbacks_t bnxe_callbacks =
{
    (
      MC_IOCTL
#ifdef MC_RESOURCES
    | MC_RESOURCES
#endif
#ifdef MC_SETPROP
    | MC_SETPROP
#endif
#ifdef MC_GETPROP
    | MC_GETPROP
#endif
#ifdef MC_PROPINFO
    | MC_PROPINFO
#endif
    | MC_GETCAPAB
    ),
    BnxeMacStats,
    BnxeMacStart,
    BnxeMacStop,
    BnxeMacPromiscuous,
    BnxeMacMulticast,
    NULL,
    BnxeMacTx,
#ifdef MC_RESOURCES
    BnxeMacResources,
#else
    NULL,
#endif
    BnxeMacIoctl,
    BnxeMacGetCapability,
#ifdef MC_OPEN
    NULL,
    NULL,
#endif
#ifdef MC_SETPROP
    BnxeMacSetProperty,
#endif
#ifdef MC_GETPROP
    BnxeMacGetProperty,
#endif
#ifdef MC_PROPINFO
    BnxeMacPropertyInfo
#endif
};


boolean_t BnxeGldInit(um_device_t * pUM)
{
    mac_register_t * pMac;
    int rc;

    atomic_swap_32(&pUM->plumbed, B_FALSE);

    if ((pMac = mac_alloc(MAC_VERSION)) == NULL)
    {
        BnxeLogWarn(pUM, "Failed to allocate GLD MAC memory");
        return B_FALSE;
    }

    pMac->m_driver     = pUM;
    pMac->m_dip        = pUM->pDev;
    pMac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
    pMac->m_callbacks  = &bnxe_callbacks;
    pMac->m_min_sdu    = 0;
    pMac->m_max_sdu    = pUM->devParams.mtu[LM_CLI_IDX_NDIS];
    pMac->m_src_addr   = &(pUM->lm_dev.params.mac_addr[0]);

#ifdef MC_OPEN
    pMac->m_margin = VLAN_TAGSZ;
#endif

#ifdef MC_SETPROP
    pMac->m_priv_props = bnxeLink_priv_props;
#endif

#if (defined(__S11) || defined(__S12)) && !defined(ILLUMOS)
    bnxe_callbacks.mc_unicst =
        (!pUM->devParams.numRings) ? BnxeMacUnicast : NULL;
#else
    bnxe_callbacks.mc_unicst = BnxeMacUnicast;
#endif

    rc = mac_register(pMac, &pUM->pMac);

    mac_free(pMac);

    if (rc != 0)
    {
        BnxeLogWarn(pUM, "Failed to register with GLD (%d)", rc);
        return B_FALSE;
    }

    /* Always report the initial link state as unknown. */
    mac_link_update(pUM->pMac, LINK_STATE_UNKNOWN);

    return B_TRUE;
}


boolean_t BnxeGldFini(um_device_t * pUM)
{
    int cnt;

    if (pUM->plumbed)
    {
        BnxeLogWarn(pUM, "Detaching device from GLD that is started!");
        return B_FALSE;
    }

    /* We must not detach until all packets held by stack are retrieved. */
    if (!BnxeWaitForPacketsFromClient(pUM, LM_CLI_IDX_NDIS))
    {
        return B_FALSE;
    }

    if (pUM->pMac)
    {
        if (mac_unregister(pUM->pMac))
        {
            BnxeLogWarn(pUM, "Failed to unregister with the GLD");
            return B_FALSE;
        }
    }

    return B_TRUE;
}


void BnxeGldLink(um_device_t * pUM,
                 link_state_t  state)
{
    mac_link_update(pUM->pMac, state);
}