p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
if (np)
*np = p; if (nc)
*nc = c;
}
/* For two consecutive NCSI commands, the packet IDs shouldn't *besame.Otherwise,thebogusresponsemightbereplied.So *theavailableIDsareallocatedinround-robinfashion.
*/ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, unsignedint req_flags)
{ struct ncsi_request *nr = NULL; int i, limit = ARRAY_SIZE(ndp->requests); unsignedlong flags;
/* Check if there is one available request until the ceiling */
spin_lock_irqsave(&ndp->lock, flags); for (i = ndp->request_id; i < limit; i++) { if (ndp->requests[i].used) continue;
nr = &ndp->requests[i];
nr->used = true;
nr->flags = req_flags;
ndp->request_id = i + 1; goto found;
}
/* Fail back to check from the starting cursor */ for (i = NCSI_REQ_START_IDX; i < ndp->request_id; i++) { if (ndp->requests[i].used) continue;
nr = &ndp->requests[i];
nr->used = true;
nr->flags = req_flags;
ndp->request_id = i + 1; goto found;
}
/* To retrieve the last link states of channels in current *packagewhencurrentactivechannelneedsfailoverto *anotherone.Itmeanswewillpossiblyselectanother *channelasnextactiveone.Thelinkstatesofchannels *aremostimportantfactoroftheselection.Soweneed *accuratelinkstates.Unfortunately,thelinkstateson *inactivechannelscan'tbeupdatedwithLSCAENintime.
*/ if (ndp->flags & NCSI_DEV_RESHUFFLE)
nd->state = ncsi_dev_state_suspend_gls; else
nd->state = ncsi_dev_state_suspend_dcnt;
ret = ncsi_xmit_cmd(&nca); if (ret) goto error;
break; case ncsi_dev_state_suspend_gls:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_GLS;
nca.package = np->id;
nca.channel = ndp->channel_probe_id;
ret = ncsi_xmit_cmd(&nca); if (ret) goto error;
ndp->channel_probe_id++;
nd->state = ncsi_dev_state_suspend_deselect;
ret = ncsi_xmit_cmd(&nca); if (ret) goto error;
NCSI_FOR_EACH_CHANNEL(np, tmp) { /* If there is another channel active on this package *donotdeselectthepackage.
*/ if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) {
nd->state = ncsi_dev_state_suspend_done; break;
}
} break; case ncsi_dev_state_suspend_deselect:
ndp->pending_req_num = 1;
nca->type = NCSI_PKT_CMD_SVF;
nca->words[1] = vid; /* HW filter index starts at 1 */
nca->bytes[6] = index + 1;
nca->bytes[7] = 0x00; return0;
}
/* Find an outstanding VLAN tag and construct a "Set VLAN Filter - Enable" *packet.
*/ staticint set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc, struct ncsi_cmd_arg *nca)
{ struct ncsi_channel_vlan_filter *ncf; struct vlan_vid *vlan = NULL; unsignedlong flags; int i, index; void *bitmap;
u16 vid;
if (list_empty(&ndp->vlan_vids)) return -1;
ncf = &nc->vlan_filter;
bitmap = &ncf->bitmap;
spin_lock_irqsave(&nc->lock, flags);
rcu_read_lock();
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
vid = vlan->vid; for (i = 0; i < ncf->n_vids; i++) if (ncf->vids[i] == vid) {
vid = 0; break;
} if (vid) break;
}
rcu_read_unlock();
if (!vid) { /* No VLAN ID is not set */
spin_unlock_irqrestore(&nc->lock, flags); return -1;
}
index = find_first_zero_bit(bitmap, ncf->n_vids); if (index < 0 || index >= ncf->n_vids) {
netdev_err(ndp->ndev.dev, "Channel %u already has all VLAN filters set\n",
nc->id);
spin_unlock_irqrestore(&nc->lock, flags); return -1;
}
/* This function should only be called once, return if flag set */ if (nca->ndp->gma_flag == 1) return -1;
/* Find gma handler for given manufacturer id */ for (i = 0; i < ARRAY_SIZE(ncsi_oem_gma_handlers); i++) { if (ncsi_oem_gma_handlers[i].mfr_id == mf_id) { if (ncsi_oem_gma_handlers[i].handler)
nch = &ncsi_oem_gma_handlers[i]; break;
}
}
if (!nch) {
netdev_err(nca->ndp->ndev.dev, "NCSI: No GMA handler available for MFR-ID (0x%x)\n",
mf_id); return -1;
}
/* Get Mac address from NCSI device */ return nch->handler(nca);
}
/* Determine if a given channel from the channel_queue should be used for Tx */ staticbool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc)
{ struct ncsi_channel_mode *ncm; struct ncsi_channel *channel; struct ncsi_package *np;
/* Check if any other channel has Tx enabled; a channel may have already *beenconfiguredandremovedfromthechannelqueue.
*/
NCSI_FOR_EACH_PACKAGE(ndp, np) { if (!ndp->multi_package && np != nc->package) continue;
NCSI_FOR_EACH_CHANNEL(np, channel) {
ncm = &channel->modes[NCSI_MODE_TX_ENABLE]; if (ncm->enable) returnfalse;
}
}
/* This channel is the preferred channel and has link */
list_for_each_entry_rcu(channel, &ndp->channel_queue, link) {
np = channel->package; if (np->preferred_channel &&
ncsi_channel_has_link(np->preferred_channel)) { return np->preferred_channel == nc;
}
}
/* This channel has link */ if (ncsi_channel_has_link(nc)) returntrue;
list_for_each_entry_rcu(channel, &ndp->channel_queue, link) if (ncsi_channel_has_link(channel)) returnfalse;
/* No other channel has link; default to this one */ returntrue;
}
/* Change the active Tx channel in a multi-channel setup */ int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp, struct ncsi_package *package, struct ncsi_channel *disable, struct ncsi_channel *enable)
{ struct ncsi_cmd_arg nca; struct ncsi_channel *nc; struct ncsi_package *np; int ret = 0;
if (!package->multi_channel && !ndp->multi_package)
netdev_warn(ndp->ndev.dev, "NCSI: Trying to update Tx channel in single-channel mode\n");
nca.ndp = ndp;
nca.req_flags = 0;
/* Find current channel with Tx enabled */
NCSI_FOR_EACH_PACKAGE(ndp, np) { if (disable) break; if (!ndp->multi_package && np != package) continue;
break; case ncsi_dev_state_config_apply_mac:
rtnl_lock();
ret = dev_set_mac_address(dev, &ndp->pending_mac, NULL);
rtnl_unlock(); if (ret < 0)
netdev_warn(dev, "NCSI: 'Writing MAC address to device failed\n");
nd->state = ncsi_dev_state_config_clear_vids;
fallthrough; case ncsi_dev_state_config_clear_vids: case ncsi_dev_state_config_svf: case ncsi_dev_state_config_ev: case ncsi_dev_state_config_sma: case ncsi_dev_state_config_ebf: case ncsi_dev_state_config_dgmf: case ncsi_dev_state_config_ecnt: case ncsi_dev_state_config_ec: case ncsi_dev_state_config_ae: case ncsi_dev_state_config_gls:
ndp->pending_req_num = 1;
nca.package = np->id;
nca.channel = nc->id;
/* Clear any active filters on the channel before setting */ if (nd->state == ncsi_dev_state_config_clear_vids) {
ret = clear_one_vid(ndp, nc, &nca); if (ret) {
nd->state = ncsi_dev_state_config_svf;
schedule_work(&ndp->work); break;
} /* Repeat */
nd->state = ncsi_dev_state_config_clear_vids; /* Add known VLAN tags to the filter */
} elseif (nd->state == ncsi_dev_state_config_svf) {
ret = set_one_vid(ndp, nc, &nca); if (ret) {
nd->state = ncsi_dev_state_config_ev;
schedule_work(&ndp->work); break;
} /* Repeat */
nd->state = ncsi_dev_state_config_svf; /* Enable/Disable the VLAN filter */
} elseif (nd->state == ncsi_dev_state_config_ev) { if (list_empty(&ndp->vlan_vids)) {
nca.type = NCSI_PKT_CMD_DV;
} else {
nca.type = NCSI_PKT_CMD_EV;
nca.bytes[3] = NCSI_CAP_VLAN_NO;
}
nd->state = ncsi_dev_state_config_sma;
} elseif (nd->state == ncsi_dev_state_config_sma) { /* Use first entry in unicast filter table. Note that *theMACfiltertablestartsfromentry1insteadof *0.
*/
nca.type = NCSI_PKT_CMD_SMA; for (index = 0; index < 6; index++)
nca.bytes[index] = dev->dev_addr[index];
nca.bytes[6] = 0x1;
nca.bytes[7] = 0x1;
nd->state = ncsi_dev_state_config_ebf;
} elseif (nd->state == ncsi_dev_state_config_ebf) {
nca.type = NCSI_PKT_CMD_EBF;
nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap; /* if multicast global filtering is supported then *disableitsothatallmulticastpacketwillbe *forwardedtomanagementcontroller
*/ if (nc->caps[NCSI_CAP_GENERIC].cap &
NCSI_CAP_GENERIC_MC)
nd->state = ncsi_dev_state_config_dgmf; elseif (ncsi_channel_is_tx(ndp, nc))
nd->state = ncsi_dev_state_config_ecnt; else
nd->state = ncsi_dev_state_config_ec;
} elseif (nd->state == ncsi_dev_state_config_dgmf) {
nca.type = NCSI_PKT_CMD_DGMF; if (ncsi_channel_is_tx(ndp, nc))
nd->state = ncsi_dev_state_config_ecnt; else
nd->state = ncsi_dev_state_config_ec;
} elseif (nd->state == ncsi_dev_state_config_ecnt) { if (np->preferred_channel &&
nc != np->preferred_channel)
netdev_info(ndp->ndev.dev, "NCSI: Tx failed over to channel %u\n",
nc->id);
nca.type = NCSI_PKT_CMD_ECNT;
nd->state = ncsi_dev_state_config_ec;
} elseif (nd->state == ncsi_dev_state_config_ec) { /* Enable AEN if it's supported */
nca.type = NCSI_PKT_CMD_EC;
nd->state = ncsi_dev_state_config_ae; if (!(nc->caps[NCSI_CAP_AEN].cap & NCSI_CAP_AEN_MASK))
nd->state = ncsi_dev_state_config_gls;
} elseif (nd->state == ncsi_dev_state_config_ae) {
nca.type = NCSI_PKT_CMD_AE;
nca.bytes[0] = 0;
nca.dwords[1] = nc->caps[NCSI_CAP_AEN].cap;
nd->state = ncsi_dev_state_config_gls;
} elseif (nd->state == ncsi_dev_state_config_gls) {
nca.type = NCSI_PKT_CMD_GLS;
nd->state = ncsi_dev_state_config_done;
}
ret = ncsi_xmit_cmd(&nca); if (ret) {
netdev_err(ndp->ndev.dev, "NCSI: Failed to transmit CMD %x\n",
nca.type); goto error;
} break; case ncsi_dev_state_config_done:
netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n",
nc->id);
spin_lock_irqsave(&nc->lock, flags);
nc->state = NCSI_CHANNEL_ACTIVE;
if (ndp->flags & NCSI_DEV_RESET) { /* A reset event happened during config, start it now */
nc->reconfigure_needed = false;
spin_unlock_irqrestore(&nc->lock, flags);
ncsi_reset_dev(nd); break;
}
if (nc->reconfigure_needed) { /* This channel's configuration has been updated *part-wayduringtheconfigstate-startthe *channelconfigurationover
*/
nc->reconfigure_needed = false;
nc->state = NCSI_CHANNEL_INACTIVE;
spin_unlock_irqrestore(&nc->lock, flags);
/* By default the search is done once an inactive channel with up *linkisfound,unlessapreferredchannelisset. *Ifmulti_packageormulti_channelareconfiguredallchannelsinthe *whitelistareaddedtothechannelqueue.
*/
found = NULL;
with_link = false;
NCSI_FOR_EACH_PACKAGE(ndp, np) { if (!(ndp->package_whitelist & (0x1 << np->id))) continue;
NCSI_FOR_EACH_CHANNEL(np, nc) { if (!(np->channel_whitelist & (0x1 << nc->id))) continue;
/* The hardware arbitration is disabled if any one channel *doesn'tsupportexplicitly.
*/
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
has_channel = true;
nca.ndp = ndp;
nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN; switch (nd->state) { case ncsi_dev_state_probe:
nd->state = ncsi_dev_state_probe_deselect;
fallthrough; case ncsi_dev_state_probe_deselect:
ndp->pending_req_num = 8;
/* Deselect all possible packages */
nca.type = NCSI_PKT_CMD_DP;
nca.channel = NCSI_RESERVED_CHANNEL; for (index = 0; index < 8; index++) {
nca.package = index;
ret = ncsi_xmit_cmd(&nca); if (ret) goto error;
}
nd->state = ncsi_dev_state_probe_package; break; case ncsi_dev_state_probe_package: if (ndp->package_probe_id >= 8) { /* Last package probed, finishing */
ndp->flags |= NCSI_DEV_PROBED; break;
}
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_SP;
nca.bytes[0] = 1;
nca.package = ndp->package_probe_id;
nca.channel = NCSI_RESERVED_CHANNEL;
ret = ncsi_xmit_cmd(&nca); if (ret) goto error;
nd->state = ncsi_dev_state_probe_channel; break; case ncsi_dev_state_probe_channel:
ndp->active_package = ncsi_find_package(ndp,
ndp->package_probe_id); if (!ndp->active_package) { /* No response */
nd->state = ncsi_dev_state_probe_dp;
schedule_work(&ndp->work); break;
}
nd->state = ncsi_dev_state_probe_cis; if (IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC) &&
ndp->mlx_multi_host)
nd->state = ncsi_dev_state_probe_mlx_gma;
schedule_work(&ndp->work); break; case ncsi_dev_state_probe_mlx_gma:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_OEM;
nca.package = ndp->active_package->id;
nca.channel = 0;
ret = ncsi_oem_gma_handler_mlx(&nca); if (ret) goto error;
nd->state = ncsi_dev_state_probe_mlx_smaf; break; case ncsi_dev_state_probe_mlx_smaf:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_OEM;
nca.package = ndp->active_package->id;
nca.channel = 0;
ret = ncsi_oem_smaf_mlx(&nca); if (ret) goto error;
nd->state = ncsi_dev_state_probe_cis; break; case ncsi_dev_state_probe_keep_phy:
ndp->pending_req_num = 1;
nca.type = NCSI_PKT_CMD_OEM;
nca.package = ndp->active_package->id;
nca.channel = 0;
ret = ncsi_oem_keep_phy_intel(&nca); if (ret) goto error;
nd->state = ncsi_dev_state_probe_gvi; break; case ncsi_dev_state_probe_cis: case ncsi_dev_state_probe_gvi: case ncsi_dev_state_probe_gc: case ncsi_dev_state_probe_gls:
np = ndp->active_package;
ndp->pending_req_num = 1;
/* Clear initial state Retrieve version, capability or link status */ if (nd->state == ncsi_dev_state_probe_cis)
nca.type = NCSI_PKT_CMD_CIS; elseif (nd->state == ncsi_dev_state_probe_gvi)
nca.type = NCSI_PKT_CMD_GVI; elseif (nd->state == ncsi_dev_state_probe_gc)
nca.type = NCSI_PKT_CMD_GC; else
nca.type = NCSI_PKT_CMD_GLS;
int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
{ struct ncsi_dev_priv *ndp; unsignedint n_vids = 0; struct vlan_vid *vlan; struct ncsi_dev *nd; bool found = false;
if (vid == 0) return0;
nd = ncsi_find_dev(dev); if (!nd) {
netdev_warn(dev, "NCSI: No net_device?\n"); return0;
}
ndp = TO_NCSI_DEV_PRIV(nd);
/* Add the VLAN id to our internal list */
list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
n_vids++; if (vlan->vid == vid) {
netdev_dbg(dev, "NCSI: vid %u already registered\n",
vid); return0;
}
} if (n_vids >= NCSI_MAX_VLAN_VIDS) {
netdev_warn(dev, "tried to add vlan id %u but NCSI max already registered (%u)\n",
vid, NCSI_MAX_VLAN_VIDS); return -ENOSPC;
}
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL); if (!vlan) return -ENOMEM;
return found ? ncsi_process_next_channel(ndp) : 0;
}
EXPORT_SYMBOL_GPL(ncsi_vlan_rx_add_vid);
int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
{ struct vlan_vid *vlan, *tmp; struct ncsi_dev_priv *ndp; struct ncsi_dev *nd; bool found = false;
if (vid == 0) return0;
nd = ncsi_find_dev(dev); if (!nd) {
netdev_warn(dev, "NCSI: no net_device?\n"); return0;
}
ndp = TO_NCSI_DEV_PRIV(nd);
/* Remove the VLAN id from our internal list */
list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list) if (vlan->vid == vid) {
netdev_dbg(dev, "NCSI: vid %u found, removing\n", vid);
list_del_rcu(&vlan->list);
found = true;
kfree(vlan);
}
if (!found) {
netdev_err(dev, "NCSI: vid %u wasn't registered!\n", vid); return -EINVAL;
}
found = ncsi_kick_channels(ndp) != 0;
return found ? ncsi_process_next_channel(ndp) : 0;
}
EXPORT_SYMBOL_GPL(ncsi_vlan_rx_kill_vid);
/* Stop the channel monitor on any active channels. Don't reset the *channelstatesoweknowwhichwereactivewhenncsi_start_dev() *isnextcalled.
*/
NCSI_FOR_EACH_PACKAGE(ndp, np) {
NCSI_FOR_EACH_CHANNEL(np, nc) {
ncsi_stop_channel_monitor(nc);
if (!(ndp->flags & NCSI_DEV_RESET)) { /* Haven't been called yet, check states */ switch (nd->state & ncsi_dev_state_major) { case ncsi_dev_state_registered: case ncsi_dev_state_probe: /* Not even probed yet - do nothing */
spin_unlock_irqrestore(&ndp->lock, flags); return0; case ncsi_dev_state_suspend: case ncsi_dev_state_config: /* Wait for the channel to finish its suspend/config *operation;onceitfinishesitwillcheckfor *NCSI_DEV_RESETandresetthestate.
*/
ndp->flags |= NCSI_DEV_RESET;
spin_unlock_irqrestore(&ndp->lock, flags); return0;
}
} else { switch (nd->state) { case ncsi_dev_state_suspend_done: case ncsi_dev_state_config_done: case ncsi_dev_state_functional: /* Ok */ break; default: /* Current reset operation happening */
spin_unlock_irqrestore(&ndp->lock, flags); return0;
}
}
if (!list_empty(&ndp->channel_queue)) { /* Clear any channel queue we may have interrupted */
list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link)
list_del_init(&nc->link);
}
spin_unlock_irqrestore(&ndp->lock, flags);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.