This allows for virtual function acceleration in virtualized
          environments.
 
+config QLCNIC_DCB
+       bool "QLOGIC QLCNIC 82XX and 83XX family DCB Support"
+       depends on QLCNIC && DCB
+       default y
+       ---help---
+         This configuration parameter enables DCB support in QLE83XX
+         and QLE82XX Converged Ethernet devices. This allows for DCB
+         get operations support through rtNetlink interface. Only CEE
+         mode of DCB is supported. PG and PFC values are related only
+         to Tx.
+
 config QLGE
        tristate "QLogic QLGE 10Gb Ethernet Driver Support"
        depends on PCI
 
        qlcnic_minidump.o qlcnic_sriov_common.o
 
 qlcnic-$(CONFIG_QLCNIC_SRIOV) += qlcnic_sriov_pf.o
+
+qlcnic-$(CONFIG_QLCNIC_DCB) += qlcnic_dcb.o
 
 #include "qlcnic_hdr.h"
 #include "qlcnic_hw.h"
 #include "qlcnic_83xx_hw.h"
+#include "qlcnic_dcb.h"
 
 #define _QLCNIC_LINUX_MAJOR 5
 #define _QLCNIC_LINUX_MINOR 3
 #define __QLCNIC_SRIOV_CAPABLE         11
 #define __QLCNIC_MBX_POLL_ENABLE       12
 #define __QLCNIC_DIAG_MODE             13
+#define __QLCNIC_DCB_STATE             14
 
 #define QLCNIC_INTERRUPT_TEST          1
 #define QLCNIC_LOOPBACK_TEST           2
        struct delayed_work fw_work;
        struct delayed_work idc_aen_work;
        struct delayed_work mbx_poll_work;
+       struct qlcnic_dcb *dcb;
 
        struct qlcnic_filter_hash fhash;
        struct qlcnic_filter_hash rx_fhash;
 
        return status;
 }
+
+static inline int qlcnic_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_dcb *dcb = adapter->dcb;
+
+       if (dcb && dcb->ops->get_hw_capability)
+               return dcb->ops->get_hw_capability(adapter);
+
+       return 0;
+}
+
+static inline void qlcnic_dcb_free(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_dcb *dcb = adapter->dcb;
+
+       if (dcb && dcb->ops->free)
+               dcb->ops->free(adapter);
+}
+
+static inline int qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_dcb *dcb = adapter->dcb;
+
+       if (dcb && dcb->ops->attach)
+               return dcb->ops->attach(adapter);
+
+       return 0;
+}
+
+static inline int
+qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter, char *buf)
+{
+       struct qlcnic_dcb *dcb = adapter->dcb;
+
+       if (dcb && dcb->ops->query_hw_capability)
+               return dcb->ops->query_hw_capability(adapter, buf);
+
+       return 0;
+}
+
+static inline void qlcnic_dcb_get_info(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_dcb *dcb = adapter->dcb;
+
+       if (dcb && dcb->ops->get_info)
+               dcb->ops->get_info(adapter);
+}
 #endif                         /* __QLCNIC_H_ */
 
        {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26},
        {QLCNIC_CMD_CONFIG_VPORT, 4, 4},
        {QLCNIC_CMD_BC_EVENT_SETUP, 2, 1},
+       {QLCNIC_CMD_DCB_QUERY_CAP, 1, 2},
 };
 
 const u32 qlcnic_83xx_ext_reg_tbl[] = {
 
 
        if (adapter->portnum == 0)
                qlcnic_set_drv_version(adapter);
+
+       qlcnic_dcb_get_info(adapter);
        qlcnic_83xx_idc_attach_driver(adapter);
 
        return 0;
        if (err)
                goto disable_mbx_intr;
 
+       if (adapter->dcb && qlcnic_dcb_attach(adapter))
+               qlcnic_clear_dcb_ops(adapter);
+
        /* Periodically monitor device status */
        qlcnic_83xx_idc_poll_dev_state(&adapter->fw_work.work);
        return 0;
 
        {QLCNIC_CMD_82XX_SET_DRV_VER, 4, 1},
        {QLCNIC_CMD_GET_LED_STATUS, 4, 2},
        {QLCNIC_CMD_MQ_TX_CONFIG_INTR, 2, 3},
+       {QLCNIC_CMD_DCB_QUERY_CAP, 1, 2},
 };
 
 static inline u32 qlcnic_get_cmd_signature(struct qlcnic_hardware_context *ahw)
 
--- /dev/null
+/*
+ * QLogic qlcnic NIC Driver
+ * Copyright (c)  2009-2013 QLogic Corporation
+ *
+ * See LICENSE.qlcnic for copyright and licensing details.
+ */
+
+#include "qlcnic.h"
+
+#define QLC_DCB_MAX_TC                 0x8
+
+#define QLC_DCB_TSA_SUPPORT(V)         (V & 0x1)
+#define QLC_DCB_ETS_SUPPORT(V)         ((V >> 1) & 0x1)
+#define QLC_DCB_VERSION_SUPPORT(V)     ((V >> 2) & 0xf)
+#define QLC_DCB_MAX_NUM_TC(V)          ((V >> 20) & 0xf)
+#define QLC_DCB_MAX_NUM_ETS_TC(V)      ((V >> 24) & 0xf)
+#define QLC_DCB_MAX_NUM_PFC_TC(V)      ((V >> 28) & 0xf)
+
+static void __qlcnic_dcb_free(struct qlcnic_adapter *);
+static int __qlcnic_dcb_attach(struct qlcnic_adapter *);
+static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *, char *);
+static void __qlcnic_dcb_get_info(struct qlcnic_adapter *);
+
+static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *);
+
+static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *);
+
+struct qlcnic_dcb_capability {
+       bool    tsa_capability;
+       bool    ets_capability;
+       u8      max_num_tc;
+       u8      max_ets_tc;
+       u8      max_pfc_tc;
+       u8      dcb_capability;
+};
+
+struct qlcnic_dcb_cfg {
+       struct qlcnic_dcb_capability capability;
+       u32 version;
+};
+
+static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
+       .free                   = __qlcnic_dcb_free,
+       .attach                 = __qlcnic_dcb_attach,
+       .query_hw_capability    = __qlcnic_dcb_query_hw_capability,
+       .get_info               = __qlcnic_dcb_get_info,
+
+       .get_hw_capability      = qlcnic_83xx_dcb_get_hw_capability,
+};
+
+static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
+       .free                   = __qlcnic_dcb_free,
+       .attach                 = __qlcnic_dcb_attach,
+       .query_hw_capability    = __qlcnic_dcb_query_hw_capability,
+       .get_info               = __qlcnic_dcb_get_info,
+
+       .get_hw_capability      = qlcnic_82xx_dcb_get_hw_capability,
+};
+
+void qlcnic_set_dcb_ops(struct qlcnic_adapter *adapter)
+{
+       if (qlcnic_82xx_check(adapter))
+               adapter->dcb->ops = &qlcnic_82xx_dcb_ops;
+       else if (qlcnic_83xx_check(adapter))
+               adapter->dcb->ops = &qlcnic_83xx_dcb_ops;
+}
+
+int __qlcnic_register_dcb(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_dcb *dcb;
+
+       dcb = kzalloc(sizeof(struct qlcnic_dcb), GFP_ATOMIC);
+       if (!dcb)
+               return -ENOMEM;
+
+       adapter->dcb = dcb;
+       qlcnic_set_dcb_ops(adapter);
+
+       return 0;
+}
+
+static void __qlcnic_dcb_free(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_dcb *dcb = adapter->dcb;
+
+       if (!dcb)
+               return;
+
+       kfree(dcb->cfg);
+       dcb->cfg = NULL;
+       kfree(dcb);
+       adapter->dcb = NULL;
+}
+
+static void __qlcnic_dcb_get_info(struct qlcnic_adapter *adapter)
+{
+       qlcnic_dcb_get_hw_capability(adapter);
+}
+
+static int __qlcnic_dcb_attach(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_dcb *dcb = adapter->dcb;
+
+       dcb->cfg = kzalloc(sizeof(struct qlcnic_dcb_cfg), GFP_ATOMIC);
+       if (!dcb->cfg)
+               return -ENOMEM;
+
+       qlcnic_dcb_get_info(adapter);
+
+       return 0;
+}
+
+static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter,
+                                           char *buf)
+{
+       struct qlcnic_cmd_args cmd;
+       u32 mbx_out;
+       int err;
+
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DCB_QUERY_CAP);
+       if (err)
+               return err;
+
+       err = qlcnic_issue_cmd(adapter, &cmd);
+       if (err) {
+               dev_err(&adapter->pdev->dev,
+                       "Failed to query DCBX capability, err %d\n", err);
+       } else {
+               mbx_out = cmd.rsp.arg[1];
+               if (buf)
+                       memcpy(buf, &mbx_out, sizeof(u32));
+       }
+
+       qlcnic_free_mbx_args(&cmd);
+
+       return err;
+}
+
+static int __qlcnic_dcb_get_capability(struct qlcnic_adapter *adapter, u32 *val)
+{
+       struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability;
+       u32 mbx_out;
+       int err;
+
+       memset(cap, 0, sizeof(struct qlcnic_dcb_capability));
+
+       err = qlcnic_dcb_query_hw_capability(adapter, (char *)val);
+       if (err)
+               return err;
+
+       mbx_out = *val;
+       if (QLC_DCB_TSA_SUPPORT(mbx_out))
+               cap->tsa_capability = true;
+
+       if (QLC_DCB_ETS_SUPPORT(mbx_out))
+               cap->ets_capability = true;
+
+       cap->max_num_tc = QLC_DCB_MAX_NUM_TC(mbx_out);
+       cap->max_ets_tc = QLC_DCB_MAX_NUM_ETS_TC(mbx_out);
+       cap->max_pfc_tc = QLC_DCB_MAX_NUM_PFC_TC(mbx_out);
+
+       if (cap->max_num_tc > QLC_DCB_MAX_TC ||
+           cap->max_ets_tc > cap->max_num_tc ||
+           cap->max_pfc_tc > cap->max_num_tc) {
+               dev_err(&adapter->pdev->dev, "Invalid DCB configuration\n");
+               return -EINVAL;
+       }
+
+       return err;
+}
+
+static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg;
+       struct qlcnic_dcb_capability *cap;
+       u32 mbx_out;
+       int err;
+
+       err = __qlcnic_dcb_get_capability(adapter, &mbx_out);
+       if (err)
+               return err;
+
+       cap = &cfg->capability;
+       cap->dcb_capability = DCB_CAP_DCBX_VER_CEE | DCB_CAP_DCBX_LLD_MANAGED;
+
+       if (cap->dcb_capability && cap->tsa_capability && cap->ets_capability)
+               set_bit(__QLCNIC_DCB_STATE, &adapter->state);
+
+       return err;
+}
+
+static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability;
+       u32 mbx_out;
+       int err;
+
+       err = __qlcnic_dcb_get_capability(adapter, &mbx_out);
+       if (err)
+               return err;
+
+       if (mbx_out & BIT_2)
+               cap->dcb_capability = DCB_CAP_DCBX_VER_CEE;
+       if (mbx_out & BIT_3)
+               cap->dcb_capability |= DCB_CAP_DCBX_VER_IEEE;
+       if (cap->dcb_capability)
+               cap->dcb_capability |= DCB_CAP_DCBX_LLD_MANAGED;
+
+       if (cap->dcb_capability && cap->tsa_capability && cap->ets_capability)
+               set_bit(__QLCNIC_DCB_STATE, &adapter->state);
+
+       return err;
+}
 
--- /dev/null
+/*
+ * QLogic qlcnic NIC Driver
+ * Copyright (c)  2009-2013 QLogic Corporation
+ *
+ * See LICENSE.qlcnic for copyright and licensing details.
+ */
+
+#ifndef __QLCNIC_DCBX_H
+#define __QLCNIC_DCBX_H
+
+void qlcnic_clear_dcb_ops(struct qlcnic_adapter *);
+
+#ifdef CONFIG_QLCNIC_DCB
+int __qlcnic_register_dcb(struct qlcnic_adapter *);
+#else
+static inline int __qlcnic_register_dcb(struct qlcnic_adapter *adapter)
+{ return 0; }
+#endif
+
+struct qlcnic_dcb_ops {
+       void (*free) (struct qlcnic_adapter *);
+       int (*attach) (struct qlcnic_adapter *);
+       int (*query_hw_capability) (struct qlcnic_adapter *, char *);
+       int (*get_hw_capability) (struct qlcnic_adapter *);
+       void (*get_info) (struct qlcnic_adapter *);
+};
+
+struct qlcnic_dcb {
+       struct qlcnic_dcb_ops   *ops;
+       struct qlcnic_dcb_cfg   *cfg;
+};
+#endif
 
 #define QLCNIC_CMD_GET_TEMP_HDR                        0x30
 #define QLCNIC_CMD_BC_EVENT_SETUP              0x31
 #define        QLCNIC_CMD_CONFIG_VPORT                 0x32
+#define        QLCNIC_CMD_DCB_QUERY_CAP                0x34
 #define QLCNIC_CMD_GET_MAC_STATS               0x37
 #define QLCNIC_CMD_82XX_SET_DRV_VER            0x38
 #define QLCNIC_CMD_MQ_TX_CONFIG_INTR           0x39
 
                qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd);
 }
 
+static int qlcnic_register_dcb(struct qlcnic_adapter *adapter)
+{
+       return __qlcnic_register_dcb(adapter);
+}
+
+void qlcnic_clear_dcb_ops(struct qlcnic_adapter *adapter)
+{
+       kfree(adapter->dcb);
+       adapter->dcb = NULL;
+}
+
 static int
 qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 
        INIT_LIST_HEAD(&adapter->mac_list);
 
+       qlcnic_register_dcb(adapter);
+
        if (qlcnic_82xx_check(adapter)) {
                qlcnic_check_vf(adapter, ent);
                adapter->portnum = adapter->ahw->pci_func;
                        goto err_out_free_hw;
 
                adapter->flags |= QLCNIC_NEED_FLR;
+
+               if (adapter->dcb && qlcnic_dcb_attach(adapter))
+                       qlcnic_clear_dcb_ops(adapter);
+
        } else if (qlcnic_83xx_check(adapter)) {
                adapter->max_drv_tx_rings = 1;
                qlcnic_83xx_check_vf(adapter, ent);
        qlcnic_cancel_idc_work(adapter);
        ahw = adapter->ahw;
 
+       qlcnic_dcb_free(adapter);
+
        unregister_netdev(netdev);
        qlcnic_sriov_cleanup(adapter);
 
                destroy_workqueue(adapter->qlcnic_wq);
                adapter->qlcnic_wq = NULL;
        }
+
        qlcnic_free_adapter_resources(adapter);
        kfree(ahw);
        free_netdev(netdev);
                return;
        }
 attach:
+       qlcnic_dcb_get_info(adapter);
+
        if (netif_running(netdev)) {
                if (qlcnic_up(adapter, netdev))
                        goto done;
 
        if (err)
                goto err_out_send_channel_term;
 
+       if (adapter->dcb && qlcnic_dcb_attach(adapter))
+               qlcnic_clear_dcb_ops(adapter);
+
        err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac);
        if (err)
                goto err_out_send_channel_term;
        pci_set_drvdata(adapter->pdev, adapter);
        dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n",
                 adapter->netdev->name);
+
        qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state,
                             adapter->ahw->idc.delay);
        return 0;
        if (err)
                goto err_out_term_channel;
 
+       qlcnic_dcb_get_info(adapter);
+
        return 0;
 
 err_out_term_channel:
 
        QLCNIC_CMD_GET_STATISTICS,
        QLCNIC_CMD_GET_PORT_CONFIG,
        QLCNIC_CMD_GET_LINK_STATUS,
+       QLCNIC_CMD_DCB_QUERY_CAP,
 };
 
 static const struct qlcnic_sriov_cmd_handler qlcnic_pf_bc_cmd_hdlr[] = {