| /*- |
| * GPL LICENSE SUMMARY |
| * |
| * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| * The full GNU General Public License is included in this distribution |
| * in the file called LICENSE.GPL. |
| * |
| * Contact Information: |
| * Intel Corporation |
| */ |
| |
| #include <linux/device.h> |
| #include <linux/module.h> |
| #include <linux/pci.h> |
| #include <linux/uio_driver.h> |
| #include <linux/io.h> |
| #include <linux/msi.h> |
| #include <linux/version.h> |
| #include <linux/rtnetlink.h> |
| #include <linux/netdevice.h> |
| #include <linux/etherdevice.h> |
| #include "../kni/ethtool/ixgbe/ixgbe_type.h" |
| #include "../kni/ethtool/igb/e1000_hw.h" |
| #ifdef CONFIG_XEN_DOM0 |
| #include <xen/xen.h> |
| #endif |
| #include <rte_pci_dev_features.h> |
| #include "compat.h" |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * struct to hold adapter-specific parameters |
| * it currently supports Intel 1/10 Gbps adapters |
| */ |
| enum dev_type {IXGBE, IGB}; |
| /*----------------------------------------------------------------------------*/ |
| /* list of 1 Gbps cards */ |
| static struct pci_device_id e1000_pci_tbl[] = { |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82576)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_FIBER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_QUAD_COPPER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_QUAD_COPPER_ET2)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS_SERDES)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES_QUAD)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_COPPER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_FIBER_SERDES)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82575GB_QUAD_COPPER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_FIBER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SERDES)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SGMII)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER_DUAL)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_QUAD_FIBER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_COPPER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_FIBER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_SERDES)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_SGMII)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_DA4)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER_OEM1)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER_IT)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_FIBER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SGMII)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_COPPER_FLASHLESS)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I210_SERDES_FLASHLESS)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I211_COPPER)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_1GBPS)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_2_5GBPS)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SGMII)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SERDES)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_BACKPLANE)}, |
| {PCI_VDEVICE(INTEL, E1000_DEV_ID_DH89XXCC_SFP)}, |
| /* required last entry */ |
| {0,} |
| }; |
| /*----------------------------------------------------------------------------*/ |
| static DEFINE_PCI_DEVICE_TABLE(ixgbe_pci_tbl) = { |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_BX)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_DUAL_PORT)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_SINGLE_PORT)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT2)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_SFP_LOM)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_CX4)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_CX4_DUAL_PORT)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_DA_DUAL_PORT)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_XF_LR)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4_MEZZ)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KR)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_COMBO_BACKPLANE)}, |
| {PCI_VDEVICE(INTEL, IXGBE_SUBDEV_ID_82599_KX4_KR_MEZZ)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_CX4)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP)}, |
| {PCI_VDEVICE(INTEL, IXGBE_SUBDEV_ID_82599_SFP)}, |
| {PCI_VDEVICE(INTEL, IXGBE_SUBDEV_ID_82599_560FLR)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_BACKPLANE_FCOE)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_FCOE)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_EM)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_SF2)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_QSFP_SF_QP)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599EN_SFP)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_XAUI_LOM)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_T3_LOM)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_LS)}, |
| {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540T)}, |
| /* required last entry */ |
| {0, } |
| }; |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * net adapter private struct |
| */ |
| struct net_adapter { |
| struct net_device *netdev; |
| struct pci_dev *pdev; |
| enum dev_type type; |
| union { |
| struct ixgbe_hw _ixgbe_hw; |
| struct e1000_hw _e1000_hw; |
| } hw; |
| u16 bd_number; |
| bool netdev_registered; |
| struct net_device_stats nstats; |
| }; |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * stats struct passed on from user space to the driver |
| */ |
| struct stats_struct { |
| uint64_t tx_bytes; |
| uint64_t tx_pkts; |
| uint64_t rx_bytes; |
| uint64_t rx_pkts; |
| uint8_t qid; |
| uint8_t dev; |
| }; |
| /* max qid */ |
| #define MAX_QID 16 |
| #define MAX_DEVICES 16 |
| /* ioctl# */ |
| #define SEND_STATS 0 |
| /* major number */ |
| #define MAJOR_NO 1110 |
| /* dev name */ |
| #define DEV_NAME "dpdk-iface" |
| /* sarray declaration */ |
| extern struct stats_struct sarrays[MAX_DEVICES][MAX_QID]; |
| extern struct stats_struct old_sarrays[MAX_DEVICES][MAX_QID]; |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * dummy function whenever a device is `opened' |
| */ |
| static int |
| netdev_open(struct net_device *netdev) |
| { |
| (void)netdev; |
| return 0; |
| } |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * dummy function for retrieving net stats |
| */ |
| static struct net_device_stats * |
| netdev_stats(struct net_device *netdev) |
| { |
| struct net_adapter *adapter; |
| int i, ifdx; |
| |
| adapter = netdev_priv(netdev); |
| ifdx = adapter->bd_number; |
| |
| if (ifdx >= MAX_DEVICES) |
| dev_info(&adapter->pdev->dev, "ifindex value: %d is greater than MAX_DEVICES!\n", |
| ifdx); |
| |
| adapter->nstats.rx_packets = adapter->nstats.tx_packets = 0; |
| adapter->nstats.rx_bytes = adapter->nstats.tx_bytes = 0; |
| |
| for (i = 0; i < MAX_QID; i++) { |
| adapter->nstats.rx_packets += sarrays[ifdx][i].rx_pkts + old_sarrays[ifdx][i].rx_pkts; |
| adapter->nstats.rx_bytes += sarrays[ifdx][i].rx_bytes + old_sarrays[ifdx][i].rx_bytes; |
| adapter->nstats.tx_packets += sarrays[ifdx][i].tx_pkts + old_sarrays[ifdx][i].tx_pkts; |
| adapter->nstats.tx_bytes += sarrays[ifdx][i].tx_bytes + old_sarrays[ifdx][i].tx_bytes; |
| } |
| |
| #if 0 |
| printk(KERN_ALERT "ifdx: %d, rxp: %llu, rxb: %llu, txp: %llu, txb: %llu\n", |
| ifdx, |
| (long long unsigned int)adapter->nstats.rx_packets, |
| (long long unsigned int)adapter->nstats.rx_bytes, |
| (long long unsigned int)adapter->nstats.tx_packets, |
| (long long unsigned int)adapter->nstats.tx_bytes); |
| #endif |
| return &adapter->nstats; |
| } |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * dummy function for setting features |
| */ |
| static int |
| netdev_set_features(struct net_device *netdev, netdev_features_t features) |
| { |
| (void)netdev; |
| (void)features; |
| return 0; |
| } |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * dummy function for fixing features |
| */ |
| static netdev_features_t |
| netdev_fix_features(struct net_device *netdev, netdev_features_t features) |
| { |
| (void)netdev; |
| (void)features; |
| return 0; |
| } |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * dummy function that returns void |
| */ |
| static void |
| netdev_no_ret(struct net_device *netdev) |
| { |
| (void)netdev; |
| return; |
| } |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * dummy tx function |
| */ |
| static int |
| netdev_xmit(struct sk_buff *skb, struct net_device *netdev) { |
| (void)netdev; |
| (void)skb; |
| return 0; |
| } |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * netdev_set_mac - Change the Ethernet Address of the DPDK port |
| * @netdev: network interface device structure |
| * @p: pointer to an address structure |
| * Returns 0 on success, negative on failure |
| */ |
| static int |
| netdev_set_mac(struct net_device *netdev, void *p) |
| { |
| struct sockaddr *addr = p; |
| if (!is_valid_ether_addr((unsigned char *)(addr->sa_data))) |
| return -EADDRNOTAVAIL; |
| memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); |
| return 0; |
| } |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * A naive net_device_ops struct to get the interface visible to the OS |
| */ |
| static const struct net_device_ops netdev_ops = { |
| .ndo_open = netdev_open, |
| .ndo_stop = netdev_open, |
| .ndo_start_xmit = netdev_xmit, |
| .ndo_set_rx_mode = netdev_no_ret, |
| .ndo_validate_addr = netdev_open, |
| .ndo_set_mac_address = netdev_set_mac, |
| .ndo_change_mtu = NULL, |
| .ndo_tx_timeout = netdev_no_ret, |
| .ndo_vlan_rx_add_vid = NULL, |
| .ndo_vlan_rx_kill_vid = NULL, |
| .ndo_do_ioctl = NULL, |
| .ndo_set_vf_mac = NULL, |
| .ndo_set_vf_vlan = NULL, |
| #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 15, 0) |
| .ndo_set_vf_tx_rate = NULL, |
| #else |
| .ndo_set_vf_rate = NULL, |
| #endif |
| .ndo_set_vf_spoofchk = NULL, |
| .ndo_get_vf_config = NULL, |
| .ndo_get_stats = netdev_stats, |
| .ndo_setup_tc = NULL, |
| .ndo_poll_controller = netdev_no_ret, |
| .ndo_set_features = netdev_set_features, |
| .ndo_fix_features = netdev_fix_features, |
| .ndo_fdb_add = NULL, |
| }; |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * assignment function |
| */ |
| void |
| netdev_assign_netdev_ops(struct net_device *dev) |
| { |
| dev->netdev_ops = &netdev_ops; |
| } |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * e1000_translate_register_82542 - Translate the proper register offset |
| * @reg: e1000 register to be read |
| * |
| * Registers in 82542 are located in different offsets than other adapters |
| * even though they function in the same manner. This function takes in |
| * the name of the register to read and returns the correct offset for |
| * 82542 silicon. |
| **/ |
| u32 |
| e1000_translate_register_82542(u32 reg) |
| { |
| /* |
| * Some of the 82542 registers are located at different |
| * offsets than they are in newer adapters. |
| * Despite the difference in location, the registers |
| * function in the same manner. |
| */ |
| switch (reg) { |
| case E1000_RA: |
| reg = 0x00040; |
| break; |
| case E1000_RDTR: |
| reg = 0x00108; |
| break; |
| case E1000_RDBAL(0): |
| reg = 0x00110; |
| break; |
| case E1000_RDBAH(0): |
| reg = 0x00114; |
| break; |
| case E1000_RDLEN(0): |
| reg = 0x00118; |
| break; |
| case E1000_RDH(0): |
| reg = 0x00120; |
| break; |
| case E1000_RDT(0): |
| reg = 0x00128; |
| break; |
| case E1000_RDBAL(1): |
| reg = 0x00138; |
| break; |
| case E1000_RDBAH(1): |
| reg = 0x0013C; |
| break; |
| case E1000_RDLEN(1): |
| reg = 0x00140; |
| break; |
| case E1000_RDH(1): |
| reg = 0x00148; |
| break; |
| case E1000_RDT(1): |
| reg = 0x00150; |
| break; |
| case E1000_FCRTH: |
| reg = 0x00160; |
| break; |
| case E1000_FCRTL: |
| reg = 0x00168; |
| break; |
| case E1000_MTA: |
| reg = 0x00200; |
| break; |
| case E1000_TDBAL(0): |
| reg = 0x00420; |
| break; |
| case E1000_TDBAH(0): |
| reg = 0x00424; |
| break; |
| case E1000_TDLEN(0): |
| reg = 0x00428; |
| break; |
| case E1000_TDH(0): |
| reg = 0x00430; |
| break; |
| case E1000_TDT(0): |
| reg = 0x00438; |
| break; |
| case E1000_TIDV: |
| reg = 0x00440; |
| break; |
| case E1000_VFTA: |
| reg = 0x00600; |
| break; |
| case E1000_TDFH: |
| reg = 0x08010; |
| break; |
| case E1000_TDFT: |
| reg = 0x08018; |
| break; |
| default: |
| break; |
| } |
| |
| return reg; |
| } |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * A device specific function that retrieves mac address from each NIC interface |
| */ |
| void |
| retrieve_dev_addr(struct net_device *netdev, struct net_adapter *adapter) |
| { |
| struct ixgbe_hw *hw_i; |
| struct e1000_hw *hw_e; |
| u32 rar_high; |
| u32 rar_low; |
| u16 i; |
| |
| switch (adapter->type) { |
| case IXGBE: |
| hw_i = &adapter->hw._ixgbe_hw; |
| rar_high = IXGBE_READ_REG(hw_i, IXGBE_RAH(0)); |
| rar_low = IXGBE_READ_REG(hw_i, IXGBE_RAL(0)); |
| |
| for (i = 0; i < 4; i++) |
| netdev->dev_addr[i] = (u8)(rar_low >> (i*8)); |
| |
| for (i = 0; i < 2; i++) |
| netdev->dev_addr[i+4] = (u8)(rar_high >> (i*8)); |
| break; |
| case IGB: |
| hw_e = &adapter->hw._e1000_hw; |
| rar_high = E1000_READ_REG(hw_e, E1000_RAH(0)); |
| rar_low = E1000_READ_REG(hw_e, E1000_RAL(0)); |
| |
| for (i = 0; i < E1000_RAL_MAC_ADDR_LEN; i++) |
| netdev->dev_addr[i] = (u8)(rar_low >> (i*8)); |
| |
| for (i = 0; i < E1000_RAH_MAC_ADDR_LEN; i++) |
| netdev->dev_addr[i+4] = (u8)(rar_high >> (i*8)); |
| break; |
| } |
| } |
| /*----------------------------------------------------------------------------*/ |
| /** |
| * function that extracts the device type from the registers |
| */ |
| enum dev_type |
| retrieve_dev_specs(const struct pci_device_id *id) |
| { |
| int i; |
| enum dev_type res; |
| int no_of_elements; |
| |
| res = 0xFF; |
| no_of_elements = sizeof(e1000_pci_tbl)/sizeof(struct pci_device_id); |
| for (i = 0; i < no_of_elements; i++) { |
| if (e1000_pci_tbl[i].vendor == id->vendor && |
| e1000_pci_tbl[i].device == id->device) { |
| return IGB; |
| } |
| |
| } |
| |
| no_of_elements = sizeof(ixgbe_pci_tbl)/sizeof(struct pci_device_id); |
| for (i = 0; i < no_of_elements; i++) { |
| if (ixgbe_pci_tbl[i].vendor == id->vendor && |
| ixgbe_pci_tbl[i].device == id->device) { |
| return IXGBE; |
| } |
| |
| } |
| |
| return res; |
| } |
| /*----------------------------------------------------------------------------*/ |