mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
e85e271dec
Currently NCSI driver will send several NCSI commands back to back without
waiting the response of previous NCSI command or timeout in some state
when NIC have multi channel. This operation against the single thread
manner defined by NCSI SPEC(section 6.3.2.3 in DSP0222_1.1.1)
According to NCSI SPEC(section 6.2.13.1 in DSP0222_1.1.1), we should probe
one channel at a time by sending NCSI commands (Clear initial state, Get
version ID, Get capabilities...), than repeat this steps until the max
number of channels which we got from NCSI command (Get capabilities) has
been probed.
Fixes: e6f44ed6d0
("net/ncsi: Package and channel management")
Signed-off-by: DelphineCCChiu <delphine_cc_chiu@wiwynn.com>
Link: https://lore.kernel.org/r/20240529065856.825241-1-delphine_cc_chiu@wiwynn.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1274 lines
34 KiB
C
1274 lines
34 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright Gavin Shan, IBM Corporation 2016.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <net/ncsi.h>
|
|
#include <net/net_namespace.h>
|
|
#include <net/sock.h>
|
|
#include <net/genetlink.h>
|
|
|
|
#include "internal.h"
|
|
#include "ncsi-pkt.h"
|
|
#include "ncsi-netlink.h"
|
|
|
|
/* Nibbles within [0xA, 0xF] add zero "0" to the returned value.
|
|
* Optional fields (encoded as 0xFF) will default to zero.
|
|
*/
|
|
static u8 decode_bcd_u8(u8 x)
|
|
{
|
|
int lo = x & 0xF;
|
|
int hi = x >> 4;
|
|
|
|
lo = lo < 0xA ? lo : 0;
|
|
hi = hi < 0xA ? hi : 0;
|
|
return lo + hi * 10;
|
|
}
|
|
|
|
static int ncsi_validate_rsp_pkt(struct ncsi_request *nr,
|
|
unsigned short payload)
|
|
{
|
|
struct ncsi_rsp_pkt_hdr *h;
|
|
u32 checksum;
|
|
__be32 *pchecksum;
|
|
|
|
/* Check NCSI packet header. We don't need validate
|
|
* the packet type, which should have been checked
|
|
* before calling this function.
|
|
*/
|
|
h = (struct ncsi_rsp_pkt_hdr *)skb_network_header(nr->rsp);
|
|
|
|
if (h->common.revision != NCSI_PKT_REVISION) {
|
|
netdev_dbg(nr->ndp->ndev.dev,
|
|
"NCSI: unsupported header revision\n");
|
|
return -EINVAL;
|
|
}
|
|
if (ntohs(h->common.length) != payload) {
|
|
netdev_dbg(nr->ndp->ndev.dev,
|
|
"NCSI: payload length mismatched\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check on code and reason */
|
|
if (ntohs(h->code) != NCSI_PKT_RSP_C_COMPLETED ||
|
|
ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR) {
|
|
netdev_dbg(nr->ndp->ndev.dev,
|
|
"NCSI: non zero response/reason code %04xh, %04xh\n",
|
|
ntohs(h->code), ntohs(h->reason));
|
|
return -EPERM;
|
|
}
|
|
|
|
/* Validate checksum, which might be zeroes if the
|
|
* sender doesn't support checksum according to NCSI
|
|
* specification.
|
|
*/
|
|
pchecksum = (__be32 *)((void *)(h + 1) + ALIGN(payload, 4) - 4);
|
|
if (ntohl(*pchecksum) == 0)
|
|
return 0;
|
|
|
|
checksum = ncsi_calculate_checksum((unsigned char *)h,
|
|
sizeof(*h) + payload - 4);
|
|
|
|
if (*pchecksum != htonl(checksum)) {
|
|
netdev_dbg(nr->ndp->ndev.dev,
|
|
"NCSI: checksum mismatched; recd: %08x calc: %08x\n",
|
|
*pchecksum, htonl(checksum));
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_cis(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_package *np;
|
|
struct ncsi_channel *nc;
|
|
unsigned char id;
|
|
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, &np, &nc);
|
|
if (!nc) {
|
|
if (ndp->flags & NCSI_DEV_PROBED)
|
|
return -ENXIO;
|
|
|
|
id = NCSI_CHANNEL_INDEX(rsp->rsp.common.channel);
|
|
nc = ncsi_add_channel(np, id);
|
|
}
|
|
|
|
return nc ? 0 : -ENODEV;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_sp(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_package *np;
|
|
unsigned char id;
|
|
|
|
/* Add the package if it's not existing. Otherwise,
|
|
* to change the state of its child channels.
|
|
*/
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
&np, NULL);
|
|
if (!np) {
|
|
if (ndp->flags & NCSI_DEV_PROBED)
|
|
return -ENXIO;
|
|
|
|
id = NCSI_PACKAGE_INDEX(rsp->rsp.common.channel);
|
|
np = ncsi_add_package(ndp, id);
|
|
if (!np)
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_dp(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_package *np;
|
|
struct ncsi_channel *nc;
|
|
unsigned long flags;
|
|
|
|
/* Find the package */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
&np, NULL);
|
|
if (!np)
|
|
return -ENODEV;
|
|
|
|
/* Change state of all channels attached to the package */
|
|
NCSI_FOR_EACH_CHANNEL(np, nc) {
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_ec(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
ncm = &nc->modes[NCSI_MODE_ENABLE];
|
|
if (ncm->enable)
|
|
return 0;
|
|
|
|
ncm->enable = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_dc(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
int ret;
|
|
|
|
ret = ncsi_validate_rsp_pkt(nr, 4);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
ncm = &nc->modes[NCSI_MODE_ENABLE];
|
|
if (!ncm->enable)
|
|
return 0;
|
|
|
|
ncm->enable = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_rc(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
unsigned long flags;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Update state for the specified channel */
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
nc->state = NCSI_CHANNEL_INACTIVE;
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_ecnt(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
|
|
if (ncm->enable)
|
|
return 0;
|
|
|
|
ncm->enable = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_dcnt(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
|
|
if (!ncm->enable)
|
|
return 0;
|
|
|
|
ncm->enable = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_ae(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_cmd_ae_pkt *cmd;
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Check if the AEN has been enabled */
|
|
ncm = &nc->modes[NCSI_MODE_AEN];
|
|
if (ncm->enable)
|
|
return 0;
|
|
|
|
/* Update to AEN configuration */
|
|
cmd = (struct ncsi_cmd_ae_pkt *)skb_network_header(nr->cmd);
|
|
ncm->enable = 1;
|
|
ncm->data[0] = cmd->mc_id;
|
|
ncm->data[1] = ntohl(cmd->mode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_sl(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_cmd_sl_pkt *cmd;
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
cmd = (struct ncsi_cmd_sl_pkt *)skb_network_header(nr->cmd);
|
|
ncm = &nc->modes[NCSI_MODE_LINK];
|
|
ncm->data[0] = ntohl(cmd->mode);
|
|
ncm->data[1] = ntohl(cmd->oem_mode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_gls(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_gls_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
unsigned long flags;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_gls_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
ncm = &nc->modes[NCSI_MODE_LINK];
|
|
ncm->data[2] = ntohl(rsp->status);
|
|
ncm->data[3] = ntohl(rsp->other);
|
|
ncm->data[4] = ntohl(rsp->oem_status);
|
|
|
|
if (nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN)
|
|
return 0;
|
|
|
|
/* Reset the channel monitor if it has been enabled */
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
nc->monitor.state = NCSI_CHANNEL_MONITOR_START;
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_svf(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_cmd_svf_pkt *cmd;
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_vlan_filter *ncf;
|
|
unsigned long flags;
|
|
void *bitmap;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
cmd = (struct ncsi_cmd_svf_pkt *)skb_network_header(nr->cmd);
|
|
ncf = &nc->vlan_filter;
|
|
if (cmd->index == 0 || cmd->index > ncf->n_vids)
|
|
return -ERANGE;
|
|
|
|
/* Add or remove the VLAN filter. Remember HW indexes from 1 */
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
bitmap = &ncf->bitmap;
|
|
if (!(cmd->enable & 0x1)) {
|
|
if (test_and_clear_bit(cmd->index - 1, bitmap))
|
|
ncf->vids[cmd->index - 1] = 0;
|
|
} else {
|
|
set_bit(cmd->index - 1, bitmap);
|
|
ncf->vids[cmd->index - 1] = ntohs(cmd->vlan);
|
|
}
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_ev(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_cmd_ev_pkt *cmd;
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Check if VLAN mode has been enabled */
|
|
ncm = &nc->modes[NCSI_MODE_VLAN];
|
|
if (ncm->enable)
|
|
return 0;
|
|
|
|
/* Update to VLAN mode */
|
|
cmd = (struct ncsi_cmd_ev_pkt *)skb_network_header(nr->cmd);
|
|
ncm->enable = 1;
|
|
ncm->data[0] = ntohl((__force __be32)cmd->mode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_dv(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Check if VLAN mode has been enabled */
|
|
ncm = &nc->modes[NCSI_MODE_VLAN];
|
|
if (!ncm->enable)
|
|
return 0;
|
|
|
|
/* Update to VLAN mode */
|
|
ncm->enable = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_sma(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_cmd_sma_pkt *cmd;
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mac_filter *ncf;
|
|
unsigned long flags;
|
|
void *bitmap;
|
|
bool enabled;
|
|
int index;
|
|
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* According to NCSI spec 1.01, the mixed filter table
|
|
* isn't supported yet.
|
|
*/
|
|
cmd = (struct ncsi_cmd_sma_pkt *)skb_network_header(nr->cmd);
|
|
enabled = cmd->at_e & 0x1;
|
|
ncf = &nc->mac_filter;
|
|
bitmap = &ncf->bitmap;
|
|
|
|
if (cmd->index == 0 ||
|
|
cmd->index > ncf->n_uc + ncf->n_mc + ncf->n_mixed)
|
|
return -ERANGE;
|
|
|
|
index = (cmd->index - 1) * ETH_ALEN;
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
if (enabled) {
|
|
set_bit(cmd->index - 1, bitmap);
|
|
memcpy(&ncf->addrs[index], cmd->mac, ETH_ALEN);
|
|
} else {
|
|
clear_bit(cmd->index - 1, bitmap);
|
|
eth_zero_addr(&ncf->addrs[index]);
|
|
}
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_ebf(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_cmd_ebf_pkt *cmd;
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
/* Find the package and channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Check if broadcast filter has been enabled */
|
|
ncm = &nc->modes[NCSI_MODE_BC];
|
|
if (ncm->enable)
|
|
return 0;
|
|
|
|
/* Update to broadcast filter mode */
|
|
cmd = (struct ncsi_cmd_ebf_pkt *)skb_network_header(nr->cmd);
|
|
ncm->enable = 1;
|
|
ncm->data[0] = ntohl(cmd->mode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_dbf(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Check if broadcast filter isn't enabled */
|
|
ncm = &nc->modes[NCSI_MODE_BC];
|
|
if (!ncm->enable)
|
|
return 0;
|
|
|
|
/* Update to broadcast filter mode */
|
|
ncm->enable = 0;
|
|
ncm->data[0] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_egmf(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_cmd_egmf_pkt *cmd;
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
/* Find the channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Check if multicast filter has been enabled */
|
|
ncm = &nc->modes[NCSI_MODE_MC];
|
|
if (ncm->enable)
|
|
return 0;
|
|
|
|
/* Update to multicast filter mode */
|
|
cmd = (struct ncsi_cmd_egmf_pkt *)skb_network_header(nr->cmd);
|
|
ncm->enable = 1;
|
|
ncm->data[0] = ntohl(cmd->mode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_dgmf(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Check if multicast filter has been enabled */
|
|
ncm = &nc->modes[NCSI_MODE_MC];
|
|
if (!ncm->enable)
|
|
return 0;
|
|
|
|
/* Update to multicast filter mode */
|
|
ncm->enable = 0;
|
|
ncm->data[0] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_snfc(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_cmd_snfc_pkt *cmd;
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_mode *ncm;
|
|
|
|
/* Find the channel */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Check if flow control has been enabled */
|
|
ncm = &nc->modes[NCSI_MODE_FC];
|
|
if (ncm->enable)
|
|
return 0;
|
|
|
|
/* Update to flow control mode */
|
|
cmd = (struct ncsi_cmd_snfc_pkt *)skb_network_header(nr->cmd);
|
|
ncm->enable = 1;
|
|
ncm->data[0] = cmd->mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Response handler for Get Mac Address command */
|
|
static int ncsi_rsp_handler_oem_gma(struct ncsi_request *nr, int mfr_id)
|
|
{
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct net_device *ndev = ndp->ndev.dev;
|
|
struct ncsi_rsp_oem_pkt *rsp;
|
|
struct sockaddr saddr;
|
|
u32 mac_addr_off = 0;
|
|
int ret = 0;
|
|
|
|
/* Get the response header */
|
|
rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp);
|
|
|
|
saddr.sa_family = ndev->type;
|
|
ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
|
|
if (mfr_id == NCSI_OEM_MFR_BCM_ID)
|
|
mac_addr_off = BCM_MAC_ADDR_OFFSET;
|
|
else if (mfr_id == NCSI_OEM_MFR_MLX_ID)
|
|
mac_addr_off = MLX_MAC_ADDR_OFFSET;
|
|
else if (mfr_id == NCSI_OEM_MFR_INTEL_ID)
|
|
mac_addr_off = INTEL_MAC_ADDR_OFFSET;
|
|
|
|
memcpy(saddr.sa_data, &rsp->data[mac_addr_off], ETH_ALEN);
|
|
if (mfr_id == NCSI_OEM_MFR_BCM_ID || mfr_id == NCSI_OEM_MFR_INTEL_ID)
|
|
eth_addr_inc((u8 *)saddr.sa_data);
|
|
if (!is_valid_ether_addr((const u8 *)saddr.sa_data))
|
|
return -ENXIO;
|
|
|
|
/* Set the flag for GMA command which should only be called once */
|
|
ndp->gma_flag = 1;
|
|
|
|
rtnl_lock();
|
|
ret = dev_set_mac_address(ndev, &saddr, NULL);
|
|
rtnl_unlock();
|
|
if (ret < 0)
|
|
netdev_warn(ndev, "NCSI: 'Writing mac address to device failed\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Response handler for Mellanox card */
|
|
static int ncsi_rsp_handler_oem_mlx(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_oem_mlx_pkt *mlx;
|
|
struct ncsi_rsp_oem_pkt *rsp;
|
|
|
|
/* Get the response header */
|
|
rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp);
|
|
mlx = (struct ncsi_rsp_oem_mlx_pkt *)(rsp->data);
|
|
|
|
if (mlx->cmd == NCSI_OEM_MLX_CMD_GMA &&
|
|
mlx->param == NCSI_OEM_MLX_CMD_GMA_PARAM)
|
|
return ncsi_rsp_handler_oem_gma(nr, NCSI_OEM_MFR_MLX_ID);
|
|
return 0;
|
|
}
|
|
|
|
/* Response handler for Broadcom card */
|
|
static int ncsi_rsp_handler_oem_bcm(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_oem_bcm_pkt *bcm;
|
|
struct ncsi_rsp_oem_pkt *rsp;
|
|
|
|
/* Get the response header */
|
|
rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp);
|
|
bcm = (struct ncsi_rsp_oem_bcm_pkt *)(rsp->data);
|
|
|
|
if (bcm->type == NCSI_OEM_BCM_CMD_GMA)
|
|
return ncsi_rsp_handler_oem_gma(nr, NCSI_OEM_MFR_BCM_ID);
|
|
return 0;
|
|
}
|
|
|
|
/* Response handler for Intel card */
|
|
static int ncsi_rsp_handler_oem_intel(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_oem_intel_pkt *intel;
|
|
struct ncsi_rsp_oem_pkt *rsp;
|
|
|
|
/* Get the response header */
|
|
rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp);
|
|
intel = (struct ncsi_rsp_oem_intel_pkt *)(rsp->data);
|
|
|
|
if (intel->cmd == NCSI_OEM_INTEL_CMD_GMA)
|
|
return ncsi_rsp_handler_oem_gma(nr, NCSI_OEM_MFR_INTEL_ID);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct ncsi_rsp_oem_handler {
|
|
unsigned int mfr_id;
|
|
int (*handler)(struct ncsi_request *nr);
|
|
} ncsi_rsp_oem_handlers[] = {
|
|
{ NCSI_OEM_MFR_MLX_ID, ncsi_rsp_handler_oem_mlx },
|
|
{ NCSI_OEM_MFR_BCM_ID, ncsi_rsp_handler_oem_bcm },
|
|
{ NCSI_OEM_MFR_INTEL_ID, ncsi_rsp_handler_oem_intel }
|
|
};
|
|
|
|
/* Response handler for OEM command */
|
|
static int ncsi_rsp_handler_oem(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_oem_handler *nrh = NULL;
|
|
struct ncsi_rsp_oem_pkt *rsp;
|
|
unsigned int mfr_id, i;
|
|
|
|
/* Get the response header */
|
|
rsp = (struct ncsi_rsp_oem_pkt *)skb_network_header(nr->rsp);
|
|
mfr_id = ntohl(rsp->mfr_id);
|
|
|
|
/* Check for manufacturer id and Find the handler */
|
|
for (i = 0; i < ARRAY_SIZE(ncsi_rsp_oem_handlers); i++) {
|
|
if (ncsi_rsp_oem_handlers[i].mfr_id == mfr_id) {
|
|
if (ncsi_rsp_oem_handlers[i].handler)
|
|
nrh = &ncsi_rsp_oem_handlers[i];
|
|
else
|
|
nrh = NULL;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!nrh) {
|
|
netdev_err(nr->ndp->ndev.dev, "Received unrecognized OEM packet with MFR-ID (0x%x)\n",
|
|
mfr_id);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Process the packet */
|
|
return nrh->handler(nr);
|
|
}
|
|
|
|
static int ncsi_rsp_handler_gvi(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_gvi_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_version *ncv;
|
|
int i;
|
|
|
|
/* Find the channel */
|
|
rsp = (struct ncsi_rsp_gvi_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Update channel's version info
|
|
*
|
|
* Major, minor, and update fields are supposed to be
|
|
* unsigned integers encoded as packed BCD.
|
|
*
|
|
* Alpha1 and alpha2 are ISO/IEC 8859-1 characters.
|
|
*/
|
|
ncv = &nc->version;
|
|
ncv->major = decode_bcd_u8(rsp->major);
|
|
ncv->minor = decode_bcd_u8(rsp->minor);
|
|
ncv->update = decode_bcd_u8(rsp->update);
|
|
ncv->alpha1 = rsp->alpha1;
|
|
ncv->alpha2 = rsp->alpha2;
|
|
memcpy(ncv->fw_name, rsp->fw_name, 12);
|
|
ncv->fw_version = ntohl(rsp->fw_version);
|
|
for (i = 0; i < ARRAY_SIZE(ncv->pci_ids); i++)
|
|
ncv->pci_ids[i] = ntohs(rsp->pci_ids[i]);
|
|
ncv->mf_id = ntohl(rsp->mf_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_gc(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_gc_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_package *np;
|
|
size_t size;
|
|
|
|
/* Find the channel */
|
|
rsp = (struct ncsi_rsp_gc_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
&np, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Update channel's capabilities */
|
|
nc->caps[NCSI_CAP_GENERIC].cap = ntohl(rsp->cap) &
|
|
NCSI_CAP_GENERIC_MASK;
|
|
nc->caps[NCSI_CAP_BC].cap = ntohl(rsp->bc_cap) &
|
|
NCSI_CAP_BC_MASK;
|
|
nc->caps[NCSI_CAP_MC].cap = ntohl(rsp->mc_cap) &
|
|
NCSI_CAP_MC_MASK;
|
|
nc->caps[NCSI_CAP_BUFFER].cap = ntohl(rsp->buf_cap);
|
|
nc->caps[NCSI_CAP_AEN].cap = ntohl(rsp->aen_cap) &
|
|
NCSI_CAP_AEN_MASK;
|
|
nc->caps[NCSI_CAP_VLAN].cap = rsp->vlan_mode &
|
|
NCSI_CAP_VLAN_MASK;
|
|
|
|
size = (rsp->uc_cnt + rsp->mc_cnt + rsp->mixed_cnt) * ETH_ALEN;
|
|
nc->mac_filter.addrs = kzalloc(size, GFP_ATOMIC);
|
|
if (!nc->mac_filter.addrs)
|
|
return -ENOMEM;
|
|
nc->mac_filter.n_uc = rsp->uc_cnt;
|
|
nc->mac_filter.n_mc = rsp->mc_cnt;
|
|
nc->mac_filter.n_mixed = rsp->mixed_cnt;
|
|
|
|
nc->vlan_filter.vids = kcalloc(rsp->vlan_cnt,
|
|
sizeof(*nc->vlan_filter.vids),
|
|
GFP_ATOMIC);
|
|
if (!nc->vlan_filter.vids)
|
|
return -ENOMEM;
|
|
/* Set VLAN filters active so they are cleared in the first
|
|
* configuration state
|
|
*/
|
|
nc->vlan_filter.bitmap = U64_MAX;
|
|
nc->vlan_filter.n_vids = rsp->vlan_cnt;
|
|
np->ndp->channel_count = rsp->channel_cnt;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_gp(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_channel_vlan_filter *ncvf;
|
|
struct ncsi_channel_mac_filter *ncmf;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_rsp_gp_pkt *rsp;
|
|
struct ncsi_channel *nc;
|
|
unsigned short enable;
|
|
unsigned char *pdata;
|
|
unsigned long flags;
|
|
void *bitmap;
|
|
int i;
|
|
|
|
/* Find the channel */
|
|
rsp = (struct ncsi_rsp_gp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Modes with explicit enabled indications */
|
|
if (ntohl(rsp->valid_modes) & 0x1) { /* BC filter mode */
|
|
nc->modes[NCSI_MODE_BC].enable = 1;
|
|
nc->modes[NCSI_MODE_BC].data[0] = ntohl(rsp->bc_mode);
|
|
}
|
|
if (ntohl(rsp->valid_modes) & 0x2) /* Channel enabled */
|
|
nc->modes[NCSI_MODE_ENABLE].enable = 1;
|
|
if (ntohl(rsp->valid_modes) & 0x4) /* Channel Tx enabled */
|
|
nc->modes[NCSI_MODE_TX_ENABLE].enable = 1;
|
|
if (ntohl(rsp->valid_modes) & 0x8) /* MC filter mode */
|
|
nc->modes[NCSI_MODE_MC].enable = 1;
|
|
|
|
/* Modes without explicit enabled indications */
|
|
nc->modes[NCSI_MODE_LINK].enable = 1;
|
|
nc->modes[NCSI_MODE_LINK].data[0] = ntohl(rsp->link_mode);
|
|
nc->modes[NCSI_MODE_VLAN].enable = 1;
|
|
nc->modes[NCSI_MODE_VLAN].data[0] = rsp->vlan_mode;
|
|
nc->modes[NCSI_MODE_FC].enable = 1;
|
|
nc->modes[NCSI_MODE_FC].data[0] = rsp->fc_mode;
|
|
nc->modes[NCSI_MODE_AEN].enable = 1;
|
|
nc->modes[NCSI_MODE_AEN].data[0] = ntohl(rsp->aen_mode);
|
|
|
|
/* MAC addresses filter table */
|
|
pdata = (unsigned char *)rsp + 48;
|
|
enable = rsp->mac_enable;
|
|
ncmf = &nc->mac_filter;
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
bitmap = &ncmf->bitmap;
|
|
for (i = 0; i < rsp->mac_cnt; i++, pdata += 6) {
|
|
if (!(enable & (0x1 << i)))
|
|
clear_bit(i, bitmap);
|
|
else
|
|
set_bit(i, bitmap);
|
|
|
|
memcpy(&ncmf->addrs[i * ETH_ALEN], pdata, ETH_ALEN);
|
|
}
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
/* VLAN filter table */
|
|
enable = ntohs(rsp->vlan_enable);
|
|
ncvf = &nc->vlan_filter;
|
|
bitmap = &ncvf->bitmap;
|
|
spin_lock_irqsave(&nc->lock, flags);
|
|
for (i = 0; i < rsp->vlan_cnt; i++, pdata += 2) {
|
|
if (!(enable & (0x1 << i)))
|
|
clear_bit(i, bitmap);
|
|
else
|
|
set_bit(i, bitmap);
|
|
|
|
ncvf->vids[i] = ntohs(*(__be16 *)pdata);
|
|
}
|
|
spin_unlock_irqrestore(&nc->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_gcps(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_gcps_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_stats *ncs;
|
|
|
|
/* Find the channel */
|
|
rsp = (struct ncsi_rsp_gcps_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Update HNC's statistics */
|
|
ncs = &nc->stats;
|
|
ncs->hnc_cnt_hi = ntohl(rsp->cnt_hi);
|
|
ncs->hnc_cnt_lo = ntohl(rsp->cnt_lo);
|
|
ncs->hnc_rx_bytes = ntohl(rsp->rx_bytes);
|
|
ncs->hnc_tx_bytes = ntohl(rsp->tx_bytes);
|
|
ncs->hnc_rx_uc_pkts = ntohl(rsp->rx_uc_pkts);
|
|
ncs->hnc_rx_mc_pkts = ntohl(rsp->rx_mc_pkts);
|
|
ncs->hnc_rx_bc_pkts = ntohl(rsp->rx_bc_pkts);
|
|
ncs->hnc_tx_uc_pkts = ntohl(rsp->tx_uc_pkts);
|
|
ncs->hnc_tx_mc_pkts = ntohl(rsp->tx_mc_pkts);
|
|
ncs->hnc_tx_bc_pkts = ntohl(rsp->tx_bc_pkts);
|
|
ncs->hnc_fcs_err = ntohl(rsp->fcs_err);
|
|
ncs->hnc_align_err = ntohl(rsp->align_err);
|
|
ncs->hnc_false_carrier = ntohl(rsp->false_carrier);
|
|
ncs->hnc_runt_pkts = ntohl(rsp->runt_pkts);
|
|
ncs->hnc_jabber_pkts = ntohl(rsp->jabber_pkts);
|
|
ncs->hnc_rx_pause_xon = ntohl(rsp->rx_pause_xon);
|
|
ncs->hnc_rx_pause_xoff = ntohl(rsp->rx_pause_xoff);
|
|
ncs->hnc_tx_pause_xon = ntohl(rsp->tx_pause_xon);
|
|
ncs->hnc_tx_pause_xoff = ntohl(rsp->tx_pause_xoff);
|
|
ncs->hnc_tx_s_collision = ntohl(rsp->tx_s_collision);
|
|
ncs->hnc_tx_m_collision = ntohl(rsp->tx_m_collision);
|
|
ncs->hnc_l_collision = ntohl(rsp->l_collision);
|
|
ncs->hnc_e_collision = ntohl(rsp->e_collision);
|
|
ncs->hnc_rx_ctl_frames = ntohl(rsp->rx_ctl_frames);
|
|
ncs->hnc_rx_64_frames = ntohl(rsp->rx_64_frames);
|
|
ncs->hnc_rx_127_frames = ntohl(rsp->rx_127_frames);
|
|
ncs->hnc_rx_255_frames = ntohl(rsp->rx_255_frames);
|
|
ncs->hnc_rx_511_frames = ntohl(rsp->rx_511_frames);
|
|
ncs->hnc_rx_1023_frames = ntohl(rsp->rx_1023_frames);
|
|
ncs->hnc_rx_1522_frames = ntohl(rsp->rx_1522_frames);
|
|
ncs->hnc_rx_9022_frames = ntohl(rsp->rx_9022_frames);
|
|
ncs->hnc_tx_64_frames = ntohl(rsp->tx_64_frames);
|
|
ncs->hnc_tx_127_frames = ntohl(rsp->tx_127_frames);
|
|
ncs->hnc_tx_255_frames = ntohl(rsp->tx_255_frames);
|
|
ncs->hnc_tx_511_frames = ntohl(rsp->tx_511_frames);
|
|
ncs->hnc_tx_1023_frames = ntohl(rsp->tx_1023_frames);
|
|
ncs->hnc_tx_1522_frames = ntohl(rsp->tx_1522_frames);
|
|
ncs->hnc_tx_9022_frames = ntohl(rsp->tx_9022_frames);
|
|
ncs->hnc_rx_valid_bytes = ntohl(rsp->rx_valid_bytes);
|
|
ncs->hnc_rx_runt_pkts = ntohl(rsp->rx_runt_pkts);
|
|
ncs->hnc_rx_jabber_pkts = ntohl(rsp->rx_jabber_pkts);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_gns(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_gns_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_stats *ncs;
|
|
|
|
/* Find the channel */
|
|
rsp = (struct ncsi_rsp_gns_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Update HNC's statistics */
|
|
ncs = &nc->stats;
|
|
ncs->ncsi_rx_cmds = ntohl(rsp->rx_cmds);
|
|
ncs->ncsi_dropped_cmds = ntohl(rsp->dropped_cmds);
|
|
ncs->ncsi_cmd_type_errs = ntohl(rsp->cmd_type_errs);
|
|
ncs->ncsi_cmd_csum_errs = ntohl(rsp->cmd_csum_errs);
|
|
ncs->ncsi_rx_pkts = ntohl(rsp->rx_pkts);
|
|
ncs->ncsi_tx_pkts = ntohl(rsp->tx_pkts);
|
|
ncs->ncsi_tx_aen_pkts = ntohl(rsp->tx_aen_pkts);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_gnpts(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_gnpts_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_channel *nc;
|
|
struct ncsi_channel_stats *ncs;
|
|
|
|
/* Find the channel */
|
|
rsp = (struct ncsi_rsp_gnpts_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
NULL, &nc);
|
|
if (!nc)
|
|
return -ENODEV;
|
|
|
|
/* Update HNC's statistics */
|
|
ncs = &nc->stats;
|
|
ncs->pt_tx_pkts = ntohl(rsp->tx_pkts);
|
|
ncs->pt_tx_dropped = ntohl(rsp->tx_dropped);
|
|
ncs->pt_tx_channel_err = ntohl(rsp->tx_channel_err);
|
|
ncs->pt_tx_us_err = ntohl(rsp->tx_us_err);
|
|
ncs->pt_rx_pkts = ntohl(rsp->rx_pkts);
|
|
ncs->pt_rx_dropped = ntohl(rsp->rx_dropped);
|
|
ncs->pt_rx_channel_err = ntohl(rsp->rx_channel_err);
|
|
ncs->pt_rx_us_err = ntohl(rsp->rx_us_err);
|
|
ncs->pt_rx_os_err = ntohl(rsp->rx_os_err);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_gps(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_gps_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_package *np;
|
|
|
|
/* Find the package */
|
|
rsp = (struct ncsi_rsp_gps_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
&np, NULL);
|
|
if (!np)
|
|
return -ENODEV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_gpuuid(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_rsp_gpuuid_pkt *rsp;
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_package *np;
|
|
|
|
/* Find the package */
|
|
rsp = (struct ncsi_rsp_gpuuid_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
&np, NULL);
|
|
if (!np)
|
|
return -ENODEV;
|
|
|
|
memcpy(np->uuid, rsp->uuid, sizeof(rsp->uuid));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_pldm(struct ncsi_request *nr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_netlink(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct ncsi_rsp_pkt *rsp;
|
|
struct ncsi_package *np;
|
|
struct ncsi_channel *nc;
|
|
int ret;
|
|
|
|
/* Find the package */
|
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
|
&np, &nc);
|
|
if (!np)
|
|
return -ENODEV;
|
|
|
|
ret = ncsi_send_netlink_rsp(nr, np, nc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ncsi_rsp_handler_gmcma(struct ncsi_request *nr)
|
|
{
|
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
|
struct net_device *ndev = ndp->ndev.dev;
|
|
struct ncsi_rsp_gmcma_pkt *rsp;
|
|
struct sockaddr saddr;
|
|
int ret = -1;
|
|
int i;
|
|
|
|
rsp = (struct ncsi_rsp_gmcma_pkt *)skb_network_header(nr->rsp);
|
|
saddr.sa_family = ndev->type;
|
|
ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
|
|
|
|
netdev_info(ndev, "NCSI: Received %d provisioned MAC addresses\n",
|
|
rsp->address_count);
|
|
for (i = 0; i < rsp->address_count; i++) {
|
|
netdev_info(ndev, "NCSI: MAC address %d: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
i, rsp->addresses[i][0], rsp->addresses[i][1],
|
|
rsp->addresses[i][2], rsp->addresses[i][3],
|
|
rsp->addresses[i][4], rsp->addresses[i][5]);
|
|
}
|
|
|
|
for (i = 0; i < rsp->address_count; i++) {
|
|
memcpy(saddr.sa_data, &rsp->addresses[i], ETH_ALEN);
|
|
ret = ndev->netdev_ops->ndo_set_mac_address(ndev, &saddr);
|
|
if (ret < 0) {
|
|
netdev_warn(ndev, "NCSI: Unable to assign %pM to device\n",
|
|
saddr.sa_data);
|
|
continue;
|
|
}
|
|
netdev_warn(ndev, "NCSI: Set MAC address to %pM\n", saddr.sa_data);
|
|
break;
|
|
}
|
|
|
|
ndp->gma_flag = ret == 0;
|
|
return ret;
|
|
}
|
|
|
|
static struct ncsi_rsp_handler {
|
|
unsigned char type;
|
|
int payload;
|
|
int (*handler)(struct ncsi_request *nr);
|
|
} ncsi_rsp_handlers[] = {
|
|
{ NCSI_PKT_RSP_CIS, 4, ncsi_rsp_handler_cis },
|
|
{ NCSI_PKT_RSP_SP, 4, ncsi_rsp_handler_sp },
|
|
{ NCSI_PKT_RSP_DP, 4, ncsi_rsp_handler_dp },
|
|
{ NCSI_PKT_RSP_EC, 4, ncsi_rsp_handler_ec },
|
|
{ NCSI_PKT_RSP_DC, 4, ncsi_rsp_handler_dc },
|
|
{ NCSI_PKT_RSP_RC, 4, ncsi_rsp_handler_rc },
|
|
{ NCSI_PKT_RSP_ECNT, 4, ncsi_rsp_handler_ecnt },
|
|
{ NCSI_PKT_RSP_DCNT, 4, ncsi_rsp_handler_dcnt },
|
|
{ NCSI_PKT_RSP_AE, 4, ncsi_rsp_handler_ae },
|
|
{ NCSI_PKT_RSP_SL, 4, ncsi_rsp_handler_sl },
|
|
{ NCSI_PKT_RSP_GLS, 16, ncsi_rsp_handler_gls },
|
|
{ NCSI_PKT_RSP_SVF, 4, ncsi_rsp_handler_svf },
|
|
{ NCSI_PKT_RSP_EV, 4, ncsi_rsp_handler_ev },
|
|
{ NCSI_PKT_RSP_DV, 4, ncsi_rsp_handler_dv },
|
|
{ NCSI_PKT_RSP_SMA, 4, ncsi_rsp_handler_sma },
|
|
{ NCSI_PKT_RSP_EBF, 4, ncsi_rsp_handler_ebf },
|
|
{ NCSI_PKT_RSP_DBF, 4, ncsi_rsp_handler_dbf },
|
|
{ NCSI_PKT_RSP_EGMF, 4, ncsi_rsp_handler_egmf },
|
|
{ NCSI_PKT_RSP_DGMF, 4, ncsi_rsp_handler_dgmf },
|
|
{ NCSI_PKT_RSP_SNFC, 4, ncsi_rsp_handler_snfc },
|
|
{ NCSI_PKT_RSP_GVI, 40, ncsi_rsp_handler_gvi },
|
|
{ NCSI_PKT_RSP_GC, 32, ncsi_rsp_handler_gc },
|
|
{ NCSI_PKT_RSP_GP, -1, ncsi_rsp_handler_gp },
|
|
{ NCSI_PKT_RSP_GCPS, 204, ncsi_rsp_handler_gcps },
|
|
{ NCSI_PKT_RSP_GNS, 32, ncsi_rsp_handler_gns },
|
|
{ NCSI_PKT_RSP_GNPTS, 48, ncsi_rsp_handler_gnpts },
|
|
{ NCSI_PKT_RSP_GPS, 8, ncsi_rsp_handler_gps },
|
|
{ NCSI_PKT_RSP_OEM, -1, ncsi_rsp_handler_oem },
|
|
{ NCSI_PKT_RSP_PLDM, -1, ncsi_rsp_handler_pldm },
|
|
{ NCSI_PKT_RSP_GPUUID, 20, ncsi_rsp_handler_gpuuid },
|
|
{ NCSI_PKT_RSP_QPNPR, -1, ncsi_rsp_handler_pldm },
|
|
{ NCSI_PKT_RSP_SNPR, -1, ncsi_rsp_handler_pldm },
|
|
{ NCSI_PKT_RSP_GMCMA, -1, ncsi_rsp_handler_gmcma },
|
|
};
|
|
|
|
int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
|
|
struct packet_type *pt, struct net_device *orig_dev)
|
|
{
|
|
struct ncsi_rsp_handler *nrh = NULL;
|
|
struct ncsi_dev *nd;
|
|
struct ncsi_dev_priv *ndp;
|
|
struct ncsi_request *nr;
|
|
struct ncsi_pkt_hdr *hdr;
|
|
unsigned long flags;
|
|
int payload, i, ret;
|
|
|
|
/* Find the NCSI device */
|
|
nd = ncsi_find_dev(orig_dev);
|
|
ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
|
|
if (!ndp)
|
|
return -ENODEV;
|
|
|
|
/* Check if it is AEN packet */
|
|
hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
|
|
if (hdr->type == NCSI_PKT_AEN)
|
|
return ncsi_aen_handler(ndp, skb);
|
|
|
|
/* Find the handler */
|
|
for (i = 0; i < ARRAY_SIZE(ncsi_rsp_handlers); i++) {
|
|
if (ncsi_rsp_handlers[i].type == hdr->type) {
|
|
if (ncsi_rsp_handlers[i].handler)
|
|
nrh = &ncsi_rsp_handlers[i];
|
|
else
|
|
nrh = NULL;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!nrh) {
|
|
netdev_err(nd->dev, "Received unrecognized packet (0x%x)\n",
|
|
hdr->type);
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Associate with the request */
|
|
spin_lock_irqsave(&ndp->lock, flags);
|
|
nr = &ndp->requests[hdr->id];
|
|
if (!nr->used) {
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
return -ENODEV;
|
|
}
|
|
|
|
nr->rsp = skb;
|
|
if (!nr->enabled) {
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
ret = -ENOENT;
|
|
goto out;
|
|
}
|
|
|
|
/* Validate the packet */
|
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
|
payload = nrh->payload;
|
|
if (payload < 0)
|
|
payload = ntohs(hdr->length);
|
|
ret = ncsi_validate_rsp_pkt(nr, payload);
|
|
if (ret) {
|
|
netdev_warn(ndp->ndev.dev,
|
|
"NCSI: 'bad' packet ignored for type 0x%x\n",
|
|
hdr->type);
|
|
|
|
if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
|
|
if (ret == -EPERM)
|
|
goto out_netlink;
|
|
else
|
|
ncsi_send_netlink_err(ndp->ndev.dev,
|
|
nr->snd_seq,
|
|
nr->snd_portid,
|
|
&nr->nlhdr,
|
|
ret);
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
/* Process the packet */
|
|
ret = nrh->handler(nr);
|
|
if (ret)
|
|
netdev_err(ndp->ndev.dev,
|
|
"NCSI: Handler for packet type 0x%x returned %d\n",
|
|
hdr->type, ret);
|
|
|
|
out_netlink:
|
|
if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
|
|
ret = ncsi_rsp_handler_netlink(nr);
|
|
if (ret) {
|
|
netdev_err(ndp->ndev.dev,
|
|
"NCSI: Netlink handler for packet type 0x%x returned %d\n",
|
|
hdr->type, ret);
|
|
}
|
|
}
|
|
|
|
out:
|
|
ncsi_free_request(nr);
|
|
return ret;
|
|
}
|