mirror of
https://github.com/corundum/corundum.git
synced 2025-01-16 08:12:53 +08:00
54d0165f68
Signed-off-by: Alex Forencich <alex@alexforencich.com>
626 lines
15 KiB
C
626 lines
15 KiB
C
// SPDX-License-Identifier: BSD-2-Clause-Views
|
|
/*
|
|
* Copyright (c) 2019-2023 The Regents of the University of California
|
|
*/
|
|
|
|
#include "mqnic.h"
|
|
|
|
#include <linux/ethtool.h>
|
|
#include <linux/version.h>
|
|
|
|
#define SFF_MODULE_ID_SFP 0x03
|
|
#define SFF_MODULE_ID_QSFP 0x0c
|
|
#define SFF_MODULE_ID_QSFP_PLUS 0x0d
|
|
#define SFF_MODULE_ID_QSFP28 0x11
|
|
|
|
static void mqnic_get_drvinfo(struct net_device *ndev,
|
|
struct ethtool_drvinfo *drvinfo)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
struct mqnic_dev *mdev = priv->mdev;
|
|
|
|
strscpy(drvinfo->driver, DRIVER_NAME, sizeof(drvinfo->driver));
|
|
strscpy(drvinfo->version, DRIVER_VERSION, sizeof(drvinfo->version));
|
|
|
|
snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d.%d",
|
|
mdev->fw_ver >> 24, (mdev->fw_ver >> 16) & 0xff,
|
|
(mdev->fw_ver >> 8) & 0xff, mdev->fw_ver & 0xff);
|
|
strscpy(drvinfo->bus_info, dev_name(mdev->dev), sizeof(drvinfo->bus_info));
|
|
|
|
drvinfo->regdump_len = priv->mdev->hw_regs_size;
|
|
}
|
|
|
|
static int mqnic_get_regs_len(struct net_device *ndev)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
|
|
return priv->mdev->hw_regs_size;
|
|
}
|
|
|
|
static void mqnic_get_regs(struct net_device *ndev,
|
|
struct ethtool_regs *regs, void *p)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
u32 *out = p;
|
|
int k;
|
|
|
|
for (k = 0; k < priv->mdev->hw_regs_size/4; k ++)
|
|
out[k] = ioread32(priv->mdev->hw_addr + k*4);
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
|
|
static void mqnic_get_ringparam(struct net_device *ndev,
|
|
struct ethtool_ringparam *param,
|
|
struct kernel_ethtool_ringparam *kernel_param,
|
|
struct netlink_ext_ack *ext_ack)
|
|
#else
|
|
static void mqnic_get_ringparam(struct net_device *ndev,
|
|
struct ethtool_ringparam *param)
|
|
#endif
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
|
|
param->rx_max_pending = MQNIC_MAX_RX_RING_SZ;
|
|
param->tx_max_pending = MQNIC_MAX_TX_RING_SZ;
|
|
|
|
param->rx_pending = priv->rx_ring_size;
|
|
param->tx_pending = priv->tx_ring_size;
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
|
|
kernel_param->cqe_size = MQNIC_CPL_SIZE;
|
|
#endif
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
|
|
static int mqnic_set_ringparam(struct net_device *ndev,
|
|
struct ethtool_ringparam *param,
|
|
struct kernel_ethtool_ringparam *kernel_param,
|
|
struct netlink_ext_ack *ext_ack)
|
|
#else
|
|
static int mqnic_set_ringparam(struct net_device *ndev,
|
|
struct ethtool_ringparam *param)
|
|
#endif
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
u32 tx_ring_size, rx_ring_size;
|
|
int port_up = priv->port_up;
|
|
int ret = 0;
|
|
|
|
if (param->rx_mini_pending || param->rx_jumbo_pending)
|
|
return -EINVAL;
|
|
|
|
if (param->rx_pending < MQNIC_MIN_RX_RING_SZ)
|
|
return -EINVAL;
|
|
|
|
if (param->rx_pending > MQNIC_MAX_RX_RING_SZ)
|
|
return -EINVAL;
|
|
|
|
if (param->tx_pending < MQNIC_MIN_TX_RING_SZ)
|
|
return -EINVAL;
|
|
|
|
if (param->tx_pending > MQNIC_MAX_TX_RING_SZ)
|
|
return -EINVAL;
|
|
|
|
rx_ring_size = roundup_pow_of_two(param->rx_pending);
|
|
tx_ring_size = roundup_pow_of_two(param->tx_pending);
|
|
|
|
if (rx_ring_size == priv->rx_ring_size && tx_ring_size == priv->tx_ring_size)
|
|
return 0;
|
|
|
|
netdev_info(ndev, "New TX ring size: %d", tx_ring_size);
|
|
netdev_info(ndev, "New RX ring size: %d", rx_ring_size);
|
|
|
|
mutex_lock(&priv->mdev->state_lock);
|
|
|
|
if (port_up)
|
|
mqnic_stop_port(ndev);
|
|
|
|
priv->tx_ring_size = tx_ring_size;
|
|
priv->rx_ring_size = rx_ring_size;
|
|
|
|
if (port_up) {
|
|
ret = mqnic_start_port(ndev);
|
|
|
|
if (ret)
|
|
netdev_err(ndev, "%s: Failed to start port: %d", __func__, ret);
|
|
}
|
|
|
|
mutex_unlock(&priv->mdev->state_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void mqnic_get_pauseparam(struct net_device *ndev,
|
|
struct ethtool_pauseparam *param)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
u32 val;
|
|
|
|
param->autoneg = 0;
|
|
param->rx_pause = 0;
|
|
param->tx_pause = 0;
|
|
|
|
if (!(priv->if_features & MQNIC_IF_FEATURE_LFC))
|
|
return;
|
|
|
|
val = mqnic_port_get_lfc_ctrl(priv->port);
|
|
|
|
param->rx_pause = !!(val & MQNIC_PORT_LFC_CTRL_RX_LFC_EN);
|
|
param->tx_pause = !!(val & MQNIC_PORT_LFC_CTRL_TX_LFC_EN);
|
|
}
|
|
|
|
static int mqnic_set_pauseparam(struct net_device *ndev,
|
|
struct ethtool_pauseparam *param)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
u32 val;
|
|
|
|
if (!(priv->if_features & MQNIC_IF_FEATURE_LFC))
|
|
return -EOPNOTSUPP;
|
|
|
|
if (param->autoneg)
|
|
return -EINVAL;
|
|
|
|
val = mqnic_port_get_lfc_ctrl(priv->port);
|
|
|
|
if (param->rx_pause)
|
|
val |= MQNIC_PORT_LFC_CTRL_RX_LFC_EN;
|
|
else
|
|
val &= ~MQNIC_PORT_LFC_CTRL_RX_LFC_EN;
|
|
|
|
if (param->tx_pause)
|
|
val |= MQNIC_PORT_LFC_CTRL_TX_LFC_EN;
|
|
else
|
|
val &= ~MQNIC_PORT_LFC_CTRL_TX_LFC_EN;
|
|
|
|
mqnic_port_set_lfc_ctrl(priv->port, val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mqnic_get_rxnfc(struct net_device *ndev,
|
|
struct ethtool_rxnfc *rxnfc, u32 *rule_locs)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
|
|
switch (rxnfc->cmd) {
|
|
case ETHTOOL_GRXRINGS:
|
|
rxnfc->data = priv->rxq_count;
|
|
break;
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u32 mqnic_get_rxfh_indir_size(struct net_device *ndev)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
|
|
return priv->rx_queue_map_indir_table_size;
|
|
}
|
|
|
|
static int mqnic_get_rxfh(struct net_device *ndev, u32 *indir, u8 *key,
|
|
u8 *hfunc)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
int k;
|
|
|
|
if (hfunc)
|
|
*hfunc = ETH_RSS_HASH_TOP;
|
|
|
|
if (indir)
|
|
for (k = 0; k < priv->rx_queue_map_indir_table_size; k++)
|
|
indir[k] = priv->rx_queue_map_indir_table[k];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mqnic_set_rxfh(struct net_device *ndev, const u32 *indir,
|
|
const u8 *key, const u8 hfunc)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
int k;
|
|
|
|
if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!indir)
|
|
return 0;
|
|
|
|
if (indir) {
|
|
for (k = 0; k < priv->rx_queue_map_indir_table_size; k++) {
|
|
if (indir[k] >= priv->rxq_count)
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (k = 0; k < priv->rx_queue_map_indir_table_size; k++)
|
|
priv->rx_queue_map_indir_table[k] = indir[k];
|
|
}
|
|
|
|
return mqnic_update_indir_table(ndev);
|
|
}
|
|
|
|
static void mqnic_get_channels(struct net_device *ndev,
|
|
struct ethtool_channels *channel)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
|
|
channel->max_rx = mqnic_res_get_count(priv->interface->rxq_res);
|
|
channel->max_tx = mqnic_res_get_count(priv->interface->txq_res);
|
|
|
|
channel->rx_count = priv->rxq_count;
|
|
channel->tx_count = priv->txq_count;
|
|
}
|
|
|
|
static int mqnic_set_channels(struct net_device *ndev,
|
|
struct ethtool_channels *channel)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
u32 txq_count, rxq_count;
|
|
int port_up = priv->port_up;
|
|
int ret = 0;
|
|
int k;
|
|
|
|
if (channel->combined_count || channel->other_count)
|
|
return -EINVAL;
|
|
|
|
if (priv->rxq_count > mqnic_res_get_count(priv->interface->rxq_res))
|
|
return -EINVAL;
|
|
|
|
if (priv->txq_count > mqnic_res_get_count(priv->interface->txq_res))
|
|
return -EINVAL;
|
|
|
|
rxq_count = channel->rx_count;
|
|
txq_count = channel->tx_count;
|
|
|
|
if (rxq_count == priv->rxq_count && txq_count == priv->txq_count)
|
|
return 0;
|
|
|
|
netdev_info(ndev, "New TX channel count: %d", txq_count);
|
|
netdev_info(ndev, "New RX channel count: %d", rxq_count);
|
|
|
|
if (rxq_count != priv->rxq_count)
|
|
for (k = 0; k < priv->rx_queue_map_indir_table_size; k++)
|
|
priv->rx_queue_map_indir_table[k] = k % rxq_count;
|
|
|
|
mutex_lock(&priv->mdev->state_lock);
|
|
|
|
if (port_up)
|
|
mqnic_stop_port(ndev);
|
|
|
|
priv->txq_count = txq_count;
|
|
priv->rxq_count = rxq_count;
|
|
|
|
if (port_up) {
|
|
ret = mqnic_start_port(ndev);
|
|
|
|
if (ret)
|
|
netdev_err(ndev, "%s: Failed to start port: %d", __func__, ret);
|
|
}
|
|
|
|
mutex_unlock(&priv->mdev->state_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mqnic_get_ts_info(struct net_device *ndev,
|
|
struct ethtool_ts_info *info)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
struct mqnic_dev *mdev = priv->mdev;
|
|
|
|
ethtool_op_get_ts_info(ndev, info);
|
|
|
|
if (mdev->ptp_clock)
|
|
info->phc_index = ptp_clock_index(mdev->ptp_clock);
|
|
|
|
if (!(priv->if_features & MQNIC_IF_FEATURE_PTP_TS) || !mdev->ptp_clock)
|
|
return 0;
|
|
|
|
info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE |
|
|
SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE;
|
|
|
|
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
|
|
|
|
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mqnic_read_module_eeprom(struct net_device *ndev,
|
|
u16 offset, u16 len, u8 *data)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
|
|
if (!priv->mod_i2c_client)
|
|
return -EINVAL;
|
|
|
|
if (len > I2C_SMBUS_BLOCK_MAX)
|
|
len = I2C_SMBUS_BLOCK_MAX;
|
|
|
|
return i2c_smbus_read_i2c_block_data(priv->mod_i2c_client, offset, len, data);
|
|
}
|
|
|
|
static int mqnic_write_module_eeprom(struct net_device *ndev,
|
|
u16 offset, u16 len, u8 *data)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
|
|
if (!priv->mod_i2c_client)
|
|
return -EINVAL;
|
|
|
|
if (len > I2C_SMBUS_BLOCK_MAX)
|
|
len = I2C_SMBUS_BLOCK_MAX;
|
|
|
|
return i2c_smbus_write_i2c_block_data(priv->mod_i2c_client, offset, len, data);
|
|
}
|
|
|
|
static int mqnic_query_module_id(struct net_device *ndev)
|
|
{
|
|
int ret;
|
|
u8 data;
|
|
|
|
ret = mqnic_read_module_eeprom(ndev, 0, 1, &data);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return data;
|
|
}
|
|
|
|
static int mqnic_query_module_eeprom_by_page(struct net_device *ndev,
|
|
unsigned short i2c_addr, u16 page, u16 bank, u16 offset, u16 len, u8 *data)
|
|
{
|
|
struct mqnic_priv *priv = netdev_priv(ndev);
|
|
int module_id;
|
|
u8 d;
|
|
int ret;
|
|
unsigned short orig_i2c_addr;
|
|
|
|
module_id = mqnic_query_module_id(ndev);
|
|
|
|
if (module_id < 0) {
|
|
netdev_err(ndev, "%s: Failed to read module ID (%d)", __func__, module_id);
|
|
return module_id;
|
|
}
|
|
|
|
switch (module_id) {
|
|
case SFF_MODULE_ID_SFP:
|
|
if (page > 0 || bank > 0)
|
|
return -EINVAL;
|
|
if (i2c_addr != 0x50 && i2c_addr != 0x51)
|
|
return -EINVAL;
|
|
break;
|
|
case SFF_MODULE_ID_QSFP:
|
|
case SFF_MODULE_ID_QSFP_PLUS:
|
|
case SFF_MODULE_ID_QSFP28:
|
|
if (page > 3 || bank > 0)
|
|
return -EINVAL;
|
|
if (i2c_addr != 0x50)
|
|
return -EINVAL;
|
|
break;
|
|
default:
|
|
netdev_err(ndev, "%s: Unknown module ID (0x%x)", __func__, module_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
// set page
|
|
switch (module_id) {
|
|
case SFF_MODULE_ID_SFP:
|
|
break;
|
|
case SFF_MODULE_ID_QSFP:
|
|
case SFF_MODULE_ID_QSFP_PLUS:
|
|
case SFF_MODULE_ID_QSFP28:
|
|
if (offset+len >= 128) {
|
|
// select page
|
|
d = page;
|
|
mqnic_write_module_eeprom(ndev, 127, 1, &d);
|
|
msleep(1);
|
|
}
|
|
break;
|
|
default:
|
|
netdev_err(ndev, "%s: Unknown module ID (0x%x)", __func__, module_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
// set i2c address of the mod_i2c_client
|
|
// This code section should not be called concurrently since the IOCTL
|
|
// handler for ethtool operations is called under the RTNL lock.
|
|
// In addition the i2c_client is used only within code inside this lock.
|
|
if (!priv->mod_i2c_client)
|
|
return -EINVAL;
|
|
|
|
orig_i2c_addr = priv->mod_i2c_client->addr;
|
|
priv->mod_i2c_client->addr = i2c_addr;
|
|
|
|
// read data
|
|
ret = mqnic_read_module_eeprom(ndev, offset, len, data);
|
|
|
|
// reset i2c addr
|
|
priv->mod_i2c_client->addr = orig_i2c_addr;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mqnic_query_module_eeprom(struct net_device *ndev,
|
|
u16 offset, u16 len, u8 *data)
|
|
{
|
|
int module_id;
|
|
unsigned short i2c_addr = 0x50;
|
|
u16 page = 0;
|
|
u16 bank = 0;
|
|
|
|
module_id = mqnic_query_module_id(ndev);
|
|
|
|
if (module_id < 0) {
|
|
netdev_err(ndev, "%s: Failed to read module ID (%d)", __func__, module_id);
|
|
return module_id;
|
|
}
|
|
|
|
switch (module_id) {
|
|
case SFF_MODULE_ID_SFP:
|
|
i2c_addr = 0x50;
|
|
page = 0;
|
|
if (offset >= 256) {
|
|
offset -= 256;
|
|
i2c_addr = 0x51;
|
|
}
|
|
break;
|
|
case SFF_MODULE_ID_QSFP:
|
|
case SFF_MODULE_ID_QSFP_PLUS:
|
|
case SFF_MODULE_ID_QSFP28:
|
|
i2c_addr = 0x50;
|
|
if (offset < 256) {
|
|
page = 0;
|
|
} else {
|
|
page = 1 + ((offset - 256) / 128);
|
|
offset -= page * 128;
|
|
}
|
|
break;
|
|
default:
|
|
netdev_err(ndev, "%s: Unknown module ID (0x%x)", __func__, module_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
// clip request to end of page
|
|
if (offset + len > 256)
|
|
len = 256 - offset;
|
|
|
|
return mqnic_query_module_eeprom_by_page(ndev, i2c_addr,
|
|
page, bank, offset, len, data);
|
|
}
|
|
|
|
static int mqnic_get_module_info(struct net_device *ndev,
|
|
struct ethtool_modinfo *modinfo)
|
|
{
|
|
int read_len = 0;
|
|
u8 data[16];
|
|
|
|
// read module ID and revision
|
|
read_len = mqnic_read_module_eeprom(ndev, 0, 2, data);
|
|
|
|
if (read_len < 0)
|
|
return read_len;
|
|
|
|
if (read_len < 2)
|
|
return -EIO;
|
|
|
|
// check identifier byte at address 0
|
|
switch (data[0]) {
|
|
case SFF_MODULE_ID_SFP:
|
|
modinfo->type = ETH_MODULE_SFF_8472;
|
|
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
|
|
break;
|
|
case SFF_MODULE_ID_QSFP:
|
|
modinfo->type = ETH_MODULE_SFF_8436;
|
|
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
|
|
break;
|
|
case SFF_MODULE_ID_QSFP_PLUS:
|
|
// check revision at address 1
|
|
if (data[1] >= 0x03) {
|
|
modinfo->type = ETH_MODULE_SFF_8636;
|
|
modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
|
|
} else {
|
|
modinfo->type = ETH_MODULE_SFF_8436;
|
|
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
|
|
}
|
|
break;
|
|
case SFF_MODULE_ID_QSFP28:
|
|
modinfo->type = ETH_MODULE_SFF_8636;
|
|
modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
|
|
break;
|
|
default:
|
|
netdev_err(ndev, "%s: Unknown module ID (0x%x)", __func__, data[0]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mqnic_get_module_eeprom(struct net_device *ndev,
|
|
struct ethtool_eeprom *eeprom, u8 *data)
|
|
{
|
|
int i = 0;
|
|
int read_len;
|
|
|
|
if (eeprom->len == 0)
|
|
return -EINVAL;
|
|
|
|
memset(data, 0, eeprom->len);
|
|
|
|
while (i < eeprom->len) {
|
|
read_len = mqnic_query_module_eeprom(ndev, eeprom->offset + i,
|
|
eeprom->len - i, data + i);
|
|
|
|
if (read_len == 0)
|
|
return 0;
|
|
|
|
if (read_len < 0) {
|
|
netdev_err(ndev, "%s: Failed to read module EEPROM (%d)", __func__, read_len);
|
|
return read_len;
|
|
}
|
|
|
|
i += read_len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
|
static int mqnic_get_module_eeprom_by_page(struct net_device *ndev,
|
|
const struct ethtool_module_eeprom *eeprom,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
int i = 0;
|
|
int read_len;
|
|
|
|
if (eeprom->length == 0)
|
|
return -EINVAL;
|
|
|
|
memset(eeprom->data, 0, eeprom->length);
|
|
|
|
while (i < eeprom->length) {
|
|
read_len = mqnic_query_module_eeprom_by_page(ndev, eeprom->i2c_address,
|
|
eeprom->page, eeprom->bank, eeprom->offset + i,
|
|
eeprom->length - i, eeprom->data + i);
|
|
|
|
if (read_len == 0)
|
|
return 0;
|
|
|
|
if (read_len < 0) {
|
|
netdev_err(ndev, "%s: Failed to read module EEPROM (%d)", __func__, read_len);
|
|
return read_len;
|
|
}
|
|
|
|
i += read_len;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
#endif
|
|
|
|
const struct ethtool_ops mqnic_ethtool_ops = {
|
|
.get_drvinfo = mqnic_get_drvinfo,
|
|
.get_regs_len = mqnic_get_regs_len,
|
|
.get_regs = mqnic_get_regs,
|
|
.get_link = ethtool_op_get_link,
|
|
.get_ringparam = mqnic_get_ringparam,
|
|
.set_ringparam = mqnic_set_ringparam,
|
|
.get_pauseparam = mqnic_get_pauseparam,
|
|
.set_pauseparam = mqnic_set_pauseparam,
|
|
.get_rxnfc = mqnic_get_rxnfc,
|
|
.get_rxfh_indir_size = mqnic_get_rxfh_indir_size,
|
|
.get_rxfh = mqnic_get_rxfh,
|
|
.set_rxfh = mqnic_set_rxfh,
|
|
.get_channels = mqnic_get_channels,
|
|
.set_channels = mqnic_set_channels,
|
|
.get_ts_info = mqnic_get_ts_info,
|
|
.get_module_info = mqnic_get_module_info,
|
|
.get_module_eeprom = mqnic_get_module_eeprom,
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
|
.get_module_eeprom_by_page = mqnic_get_module_eeprom_by_page,
|
|
#endif
|
|
};
|