android_kernel_motorola_sm6225/net/ncsi/ncsi-rsp.c
Gavin Shan e6f44ed6d0 net/ncsi: Package and channel management
This manages NCSI packages and channels:

 * The available packages and channels are enumerated in the first
   time of calling ncsi_start_dev(). The channels' capabilities are
   probed in the meanwhile. The NCSI network topology won't change
   until the NCSI device is destroyed.
 * There in a queue in every NCSI device. The element in the queue,
   channel, is waiting for configuration (bringup) or suspending
   (teardown). The channel's state (inactive/active) indicates the
   futher action (configuration or suspending) will be applied on the
   channel. Another channel's state (invisible) means the requested
   action is being applied.
 * The hardware arbitration will be enabled if all available packages
   and channels support it. All available channels try to provide
   service when hardware arbitration is enabled. Otherwise, one channel
   is selected as the active one at once.
 * When channel is in active state, meaning it's providing service, a
   timer started to retrieve the channe's link status. If the channel's
   link status fails to be updated in the determined period, the channel
   is going to be reconfigured. It's the error handling implementation
   as defined in NCSI spec.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-07-19 20:49:17 -07:00

1031 lines
27 KiB
C

/*
* Copyright Gavin Shan, IBM Corporation 2016.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/ncsi.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include "internal.h"
#include "ncsi-pkt.h"
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)
return -EINVAL;
if (ntohs(h->common.length) != payload)
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)
return -EINVAL;
/* Validate checksum, which might be zeroes if the
* sender doesn't support checksum according to NCSI
* specification.
*/
pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
if (ntohl(*pchecksum) == 0)
return 0;
checksum = ncsi_calculate_checksum((unsigned char *)h,
sizeof(*h) + payload - 4);
if (*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 -EBUSY;
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 -EBUSY;
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 -EBUSY;
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 -EBUSY;
ncm->enable = 1;
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 -EBUSY;
/* 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->driven)
return 0;
/* Reset the channel monitor if it has been enabled */
spin_lock_irqsave(&nc->lock, flags);
nc->timeout = 0;
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_filter *ncf;
unsigned short vlan;
int 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;
cmd = (struct ncsi_cmd_svf_pkt *)skb_network_header(nr->cmd);
ncf = nc->filters[NCSI_FILTER_VLAN];
if (!ncf)
return -ENOENT;
if (cmd->index >= ncf->total)
return -ERANGE;
/* Add or remove the VLAN filter */
if (!(cmd->enable & 0x1)) {
ret = ncsi_remove_filter(nc, NCSI_FILTER_VLAN, cmd->index);
} else {
vlan = ntohs(cmd->vlan);
ret = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan);
}
return ret;
}
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 -EBUSY;
/* Update to VLAN mode */
cmd = (struct ncsi_cmd_ev_pkt *)skb_network_header(nr->cmd);
ncm->enable = 1;
ncm->data[0] = ntohl(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 -EBUSY;
/* 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_filter *ncf;
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;
/* 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);
switch (cmd->at_e >> 5) {
case 0x0: /* UC address */
ncf = nc->filters[NCSI_FILTER_UC];
break;
case 0x1: /* MC address */
ncf = nc->filters[NCSI_FILTER_MC];
break;
default:
return -EINVAL;
}
/* Sanity check on the filter */
if (!ncf)
return -ENOENT;
else if (cmd->index >= ncf->total)
return -ERANGE;
bitmap = &ncf->bitmap;
if (cmd->at_e & 0x1) {
if (test_and_set_bit(cmd->index, bitmap))
return -EBUSY;
memcpy(ncf->data + 6 * cmd->index, cmd->mac, 6);
} else {
if (!test_and_clear_bit(cmd->index, bitmap))
return -EBUSY;
memset(ncf->data + 6 * cmd->index, 0, 6);
}
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 -EBUSY;
/* 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 -EBUSY;
/* 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 -EBUSY;
/* 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 -EBUSY;
/* 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 -EBUSY;
/* 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;
}
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 to channel's version info */
ncv = &nc->version;
ncv->version = ntohl(rsp->ncsi_version);
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_channel_filter *ncf;
size_t size, entry_size;
int cnt, i;
/* Find the channel */
rsp = (struct ncsi_rsp_gc_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 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;
/* Build filters */
for (i = 0; i < NCSI_FILTER_MAX; i++) {
switch (i) {
case NCSI_FILTER_VLAN:
cnt = rsp->vlan_cnt;
entry_size = 2;
break;
case NCSI_FILTER_MIXED:
cnt = rsp->mixed_cnt;
entry_size = 6;
break;
case NCSI_FILTER_MC:
cnt = rsp->mc_cnt;
entry_size = 6;
break;
case NCSI_FILTER_UC:
cnt = rsp->uc_cnt;
entry_size = 6;
break;
default:
continue;
}
if (!cnt || nc->filters[i])
continue;
size = sizeof(*ncf) + cnt * entry_size;
ncf = kzalloc(size, GFP_ATOMIC);
if (!ncf) {
pr_warn("%s: Cannot alloc filter table (%d)\n",
__func__, i);
return -ENOMEM;
}
ncf->index = i;
ncf->total = cnt;
ncf->bitmap = 0x0ul;
nc->filters[i] = ncf;
}
return 0;
}
static int ncsi_rsp_handler_gp(struct ncsi_request *nr)
{
struct ncsi_rsp_gp_pkt *rsp;
struct ncsi_dev_priv *ndp = nr->ndp;
struct ncsi_channel *nc;
unsigned short enable, vlan;
unsigned char *pdata;
int table, 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;
for (i = 0; i < rsp->mac_cnt; i++, pdata += 6) {
if (i >= (nc->filters[NCSI_FILTER_UC]->total +
nc->filters[NCSI_FILTER_MC]->total))
table = NCSI_FILTER_MIXED;
else if (i >= nc->filters[NCSI_FILTER_UC]->total)
table = NCSI_FILTER_MC;
else
table = NCSI_FILTER_UC;
if (!(enable & (0x1 << i)))
continue;
if (ncsi_find_filter(nc, table, pdata) >= 0)
continue;
ncsi_add_filter(nc, table, pdata);
}
/* VLAN filter table */
enable = ntohs(rsp->vlan_enable);
for (i = 0; i < rsp->vlan_cnt; i++, pdata += 2) {
if (!(enable & (0x1 << i)))
continue;
vlan = ntohs(*(__be16 *)pdata);
if (ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan) >= 0)
continue;
ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan);
}
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 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, 36, 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, 172, ncsi_rsp_handler_gcps },
{ NCSI_PKT_RSP_GNS, 172, ncsi_rsp_handler_gns },
{ NCSI_PKT_RSP_GNPTS, 172, ncsi_rsp_handler_gnpts },
{ NCSI_PKT_RSP_GPS, 8, ncsi_rsp_handler_gps },
{ NCSI_PKT_RSP_OEM, 0, NULL },
{ NCSI_PKT_RSP_PLDM, 0, NULL },
{ NCSI_PKT_RSP_GPUUID, 20, ncsi_rsp_handler_gpuuid }
};
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(dev);
ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
if (!ndp)
return -ENODEV;
/* Find the handler */
hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
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)
goto out;
/* Process the packet */
ret = nrh->handler(nr);
out:
ncsi_free_request(nr);
return ret;
}