1
0
Fork 0
remarkable-linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c

1677 lines
41 KiB
C
Raw Normal View History

// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2010 Broadcom Corporation
*/
#include <linux/kernel.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <linux/inetdevice.h>
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
#include <net/addrconf.h>
#include <net/ieee80211_radiotap.h>
#include <net/ipv6.h>
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
#include "core.h"
#include "bus.h"
#include "debug.h"
#include "fwil_types.h"
#include "p2p.h"
#include "pno.h"
#include "cfg80211.h"
#include "fwil.h"
#include "feature.h"
#include "proto.h"
#include "pcie.h"
#include "common.h"
#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(950)
#define BRCMF_BSSIDX_INVALID -1
#define RXS_PBPRES BIT(2)
#define D11_PHY_HDR_LEN 6
struct d11rxhdr_le {
__le16 RxFrameSize;
u16 PAD;
__le16 PhyRxStatus_0;
__le16 PhyRxStatus_1;
__le16 PhyRxStatus_2;
__le16 PhyRxStatus_3;
__le16 PhyRxStatus_4;
__le16 PhyRxStatus_5;
__le16 RxStatus1;
__le16 RxStatus2;
__le16 RxTSFTime;
__le16 RxChan;
u8 unknown[12];
} __packed;
struct wlc_d11rxhdr {
struct d11rxhdr_le rxhdr;
__le32 tsf_l;
s8 rssi;
s8 rxpwr0;
s8 rxpwr1;
s8 do_rssi_ma;
s8 rxpwr[4];
} __packed;
char *brcmf_ifname(struct brcmf_if *ifp)
{
if (!ifp)
return "<if_null>";
if (ifp->ndev)
return ifp->ndev->name;
return "<if_none>";
}
struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
{
struct brcmf_if *ifp;
s32 bsscfgidx;
if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
bphy_err(drvr, "ifidx %d out of range\n", ifidx);
return NULL;
}
ifp = NULL;
bsscfgidx = drvr->if2bss[ifidx];
if (bsscfgidx >= 0)
ifp = drvr->iflist[bsscfgidx];
return ifp;
}
void brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable)
{
s32 err;
u32 mode;
if (enable)
mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
else
mode = 0;
/* Try to set and enable ARP offload feature, this may fail, then it */
/* is simply not supported and err 0 will be returned */
err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
if (err) {
brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
mode, err);
} else {
err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
if (err) {
brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
enable, err);
} else {
brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
enable, mode);
}
}
err = brcmf_fil_iovar_int_set(ifp, "ndoe", enable);
if (err) {
brcmf_dbg(TRACE, "failed to configure (%d) ND offload err = %d\n",
enable, err);
} else {
brcmf_dbg(TRACE, "successfully configured (%d) ND offload to 0x%x\n",
enable, mode);
}
}
static void _brcmf_set_multicast_list(struct work_struct *work)
{
struct brcmf_if *ifp = container_of(work, struct brcmf_if,
multicast_work);
struct brcmf_pub *drvr = ifp->drvr;
struct net_device *ndev;
struct netdev_hw_addr *ha;
u32 cmd_value, cnt;
__le32 cnt_le;
char *buf, *bufp;
u32 buflen;
s32 err;
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
ndev = ifp->ndev;
/* Determine initial value of allmulti flag */
cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false;
/* Send down the multicast list first. */
cnt = netdev_mc_count(ndev);
buflen = sizeof(cnt) + (cnt * ETH_ALEN);
buf = kmalloc(buflen, GFP_ATOMIC);
if (!buf)
return;
bufp = buf;
cnt_le = cpu_to_le32(cnt);
memcpy(bufp, &cnt_le, sizeof(cnt_le));
bufp += sizeof(cnt_le);
netdev_for_each_mc_addr(ha, ndev) {
if (!cnt)
break;
memcpy(bufp, ha->addr, ETH_ALEN);
bufp += ETH_ALEN;
cnt--;
}
err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen);
if (err < 0) {
bphy_err(drvr, "Setting mcast_list failed, %d\n", err);
cmd_value = cnt ? true : cmd_value;
}
kfree(buf);
/*
* Now send the allmulti setting. This is based on the setting in the
* net_device flags, but might be modified above to be turned on if we
* were trying to set some addresses and dongle rejected it...
*/
err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value);
if (err < 0)
bphy_err(drvr, "Setting allmulti failed, %d\n", err);
/*Finally, pick up the PROMISC flag */
cmd_value = (ndev->flags & IFF_PROMISC) ? true : false;
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value);
if (err < 0)
bphy_err(drvr, "Setting BRCMF_C_SET_PROMISC failed, %d\n",
err);
brcmf_configure_arp_nd_offload(ifp, !cmd_value);
}
#if IS_ENABLED(CONFIG_IPV6)
static void _brcmf_update_ndtable(struct work_struct *work)
{
struct brcmf_if *ifp = container_of(work, struct brcmf_if,
ndoffload_work);
struct brcmf_pub *drvr = ifp->drvr;
int i, ret;
/* clear the table in firmware */
ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip_clear", NULL, 0);
if (ret) {
brcmf_dbg(TRACE, "fail to clear nd ip table err:%d\n", ret);
return;
}
for (i = 0; i < ifp->ipv6addr_idx; i++) {
ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip",
&ifp->ipv6_addr_tbl[i],
sizeof(struct in6_addr));
if (ret)
bphy_err(drvr, "add nd ip err %d\n", ret);
}
}
#else
static void _brcmf_update_ndtable(struct work_struct *work)
{
}
#endif
static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
{
struct brcmf_if *ifp = netdev_priv(ndev);
struct sockaddr *sa = (struct sockaddr *)addr;
struct brcmf_pub *drvr = ifp->drvr;
int err;
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", sa->sa_data,
ETH_ALEN);
if (err < 0) {
bphy_err(drvr, "Setting cur_etheraddr failed, %d\n", err);
} else {
brcmf_dbg(TRACE, "updated to %pM\n", sa->sa_data);
memcpy(ifp->mac_addr, sa->sa_data, ETH_ALEN);
memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
}
return err;
}
static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
{
struct brcmf_if *ifp = netdev_priv(ndev);
schedule_work(&ifp->multicast_work);
}
/**
* brcmf_skb_is_iapp - checks if skb is an IAPP packet
*
* @skb: skb to check
*/
static bool brcmf_skb_is_iapp(struct sk_buff *skb)
{
static const u8 iapp_l2_update_packet[6] __aligned(2) = {
0x00, 0x01, 0xaf, 0x81, 0x01, 0x00,
};
unsigned char *eth_data;
#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
const u16 *a, *b;
#endif
if (skb->len - skb->mac_len != 6 ||
!is_multicast_ether_addr(eth_hdr(skb)->h_dest))
return false;
eth_data = skb_mac_header(skb) + ETH_HLEN;
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
return !(((*(const u32 *)eth_data) ^ (*(const u32 *)iapp_l2_update_packet)) |
((*(const u16 *)(eth_data + 4)) ^ (*(const u16 *)(iapp_l2_update_packet + 4))));
#else
a = (const u16 *)eth_data;
b = (const u16 *)iapp_l2_update_packet;
return !((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]));
#endif
}
static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
int ret;
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_pub *drvr = ifp->drvr;
struct ethhdr *eh;
int head_delta;
brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
/* Can the device send data? */
if (drvr->bus_if->state != BRCMF_BUS_UP) {
bphy_err(drvr, "xmit rejected state=%d\n", drvr->bus_if->state);
netif_stop_queue(ndev);
dev_kfree_skb(skb);
ret = -ENODEV;
goto done;
}
/* Some recent Broadcom's firmwares disassociate STA when they receive
* an 802.11f ADD frame. This behavior can lead to a local DoS security
* issue. Attacker may trigger disassociation of any STA by sending a
* proper Ethernet frame to the wireless interface.
*
* Moreover this feature may break AP interfaces in some specific
* setups. This applies e.g. to the bridge with hairpin mode enabled and
* IFLA_BRPORT_MCAST_TO_UCAST set. IAPP packet generated by a firmware
* will get passed back to the wireless interface and cause immediate
* disassociation of a just-connected STA.
*/
if (!drvr->settings->iapp && brcmf_skb_is_iapp(skb)) {
dev_kfree_skb(skb);
ret = -EINVAL;
goto done;
}
/* Make sure there's enough writeable headroom */
if (skb_headroom(skb) < drvr->hdrlen || skb_header_cloned(skb)) {
head_delta = max_t(int, drvr->hdrlen - skb_headroom(skb), 0);
brcmf_dbg(INFO, "%s: insufficient headroom (%d)\n",
brcmf_ifname(ifp), head_delta);
atomic_inc(&drvr->bus_if->stats.pktcowed);
ret = pskb_expand_head(skb, ALIGN(head_delta, NET_SKB_PAD), 0,
GFP_ATOMIC);
if (ret < 0) {
bphy_err(drvr, "%s: failed to expand headroom\n",
brcmf_ifname(ifp));
atomic_inc(&drvr->bus_if->stats.pktcow_failed);
goto done;
}
}
/* validate length for ether packet */
if (skb->len < sizeof(*eh)) {
ret = -EINVAL;
dev_kfree_skb(skb);
goto done;
}
eh = (struct ethhdr *)(skb->data);
if (eh->h_proto == htons(ETH_P_PAE))
atomic_inc(&ifp->pend_8021x_cnt);
/* determine the priority */
if ((skb->priority == 0) || (skb->priority > 7))
skb->priority = cfg80211_classify8021d(skb, NULL);
ret = brcmf_proto_tx_queue_data(drvr, ifp->ifidx, skb);
if (ret < 0)
brcmf_txfinalize(ifp, skb, false);
done:
if (ret) {
ndev->stats.tx_dropped++;
} else {
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += skb->len;
}
/* Return ok: we always eat the packet */
return NETDEV_TX_OK;
}
void brcmf_txflowblock_if(struct brcmf_if *ifp,
enum brcmf_netif_stop_reason reason, bool state)
{
unsigned long flags;
if (!ifp || !ifp->ndev)
return;
brcmf_dbg(TRACE, "enter: bsscfgidx=%d stop=0x%X reason=%d state=%d\n",
ifp->bsscfgidx, ifp->netif_stop, reason, state);
spin_lock_irqsave(&ifp->netif_stop_lock, flags);
if (state) {
if (!ifp->netif_stop)
netif_stop_queue(ifp->ndev);
ifp->netif_stop |= reason;
} else {
ifp->netif_stop &= ~reason;
if (!ifp->netif_stop)
netif_wake_queue(ifp->ndev);
}
spin_unlock_irqrestore(&ifp->netif_stop_lock, flags);
}
void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
{
/* Most of Broadcom's firmwares send 802.11f ADD frame every time a new
* STA connects to the AP interface. This is an obsoleted standard most
* users don't use, so don't pass these frames up unless requested.
*/
if (!ifp->drvr->settings->iapp && brcmf_skb_is_iapp(skb)) {
brcmu_pkt_buf_free_skb(skb);
return;
}
if (skb->pkt_type == PACKET_MULTICAST)
ifp->ndev->stats.multicast++;
if (!(ifp->ndev->flags & IFF_UP)) {
brcmu_pkt_buf_free_skb(skb);
return;
}
ifp->ndev->stats.rx_bytes += skb->len;
ifp->ndev->stats.rx_packets++;
brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol));
if (in_interrupt())
netif_rx(skb);
else
/* If the receive is not processed inside an ISR,
* the softirqd must be woken explicitly to service
* the NET_RX_SOFTIRQ. This is handled by netif_rx_ni().
*/
netif_rx_ni(skb);
}
void brcmf_netif_mon_rx(struct brcmf_if *ifp, struct sk_buff *skb)
{
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MONITOR_FMT_RADIOTAP)) {
/* Do nothing */
} else if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MONITOR_FMT_HW_RX_HDR)) {
struct wlc_d11rxhdr *wlc_rxhdr = (struct wlc_d11rxhdr *)skb->data;
struct ieee80211_radiotap_header *radiotap;
unsigned int offset;
u16 RxStatus1;
RxStatus1 = le16_to_cpu(wlc_rxhdr->rxhdr.RxStatus1);
offset = sizeof(struct wlc_d11rxhdr);
/* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU
* subframes
*/
if (RxStatus1 & RXS_PBPRES)
offset += 2;
offset += D11_PHY_HDR_LEN;
skb_pull(skb, offset);
/* TODO: use RX header to fill some radiotap data */
radiotap = skb_push(skb, sizeof(*radiotap));
memset(radiotap, 0, sizeof(*radiotap));
radiotap->it_len = cpu_to_le16(sizeof(*radiotap));
/* TODO: 4 bytes with receive status? */
skb->len -= 4;
} else {
struct ieee80211_radiotap_header *radiotap;
/* TODO: use RX status to fill some radiotap data */
radiotap = skb_push(skb, sizeof(*radiotap));
memset(radiotap, 0, sizeof(*radiotap));
radiotap->it_len = cpu_to_le16(sizeof(*radiotap));
/* TODO: 4 bytes with receive status? */
skb->len -= 4;
}
skb->dev = ifp->ndev;
skb_reset_mac_header(skb);
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
brcmf_netif_rx(ifp, skb);
}
static int brcmf_rx_hdrpull(struct brcmf_pub *drvr, struct sk_buff *skb,
struct brcmf_if **ifp)
{
int ret;
/* process and remove protocol-specific header */
ret = brcmf_proto_hdrpull(drvr, true, skb, ifp);
if (ret || !(*ifp) || !(*ifp)->ndev) {
if (ret != -ENODATA && *ifp)
(*ifp)->ndev->stats.rx_errors++;
brcmu_pkt_buf_free_skb(skb);
return -ENODATA;
}
skb->protocol = eth_type_trans(skb, (*ifp)->ndev);
return 0;
}
void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event)
{
struct brcmf_if *ifp;
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
if (brcmf_rx_hdrpull(drvr, skb, &ifp))
return;
if (brcmf_proto_is_reorder_skb(skb)) {
brcmf_proto_rxreorder(ifp, skb);
} else {
/* Process special event packets */
if (handle_event)
brcmf_fweh_process_skb(ifp->drvr, skb,
BCMILCP_SUBTYPE_VENDOR_LONG);
brcmf_netif_rx(ifp, skb);
}
}
void brcmf_rx_event(struct device *dev, struct sk_buff *skb)
{
struct brcmf_if *ifp;
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
brcmf_dbg(EVENT, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
if (brcmf_rx_hdrpull(drvr, skb, &ifp))
return;
brcmf_fweh_process_skb(ifp->drvr, skb, 0);
brcmu_pkt_buf_free_skb(skb);
}
void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
{
struct ethhdr *eh;
u16 type;
if (!ifp) {
brcmu_pkt_buf_free_skb(txp);
return;
}
eh = (struct ethhdr *)(txp->data);
type = ntohs(eh->h_proto);
if (type == ETH_P_PAE) {
atomic_dec(&ifp->pend_8021x_cnt);
if (waitqueue_active(&ifp->pend_8021x_wait))
wake_up(&ifp->pend_8021x_wait);
}
if (!success)
ifp->ndev->stats.tx_errors++;
brcmu_pkt_buf_free_skb(txp);
}
static void brcmf_ethtool_get_drvinfo(struct net_device *ndev,
struct ethtool_drvinfo *info)
{
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_pub *drvr = ifp->drvr;
char drev[BRCMU_DOTREV_LEN] = "n/a";
if (drvr->revinfo.result == 0)
brcmu_dotrev_str(drvr->revinfo.driverrev, drev);
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
strlcpy(info->version, drev, sizeof(info->version));
strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version));
strlcpy(info->bus_info, dev_name(drvr->bus_if->dev),
sizeof(info->bus_info));
}
static const struct ethtool_ops brcmf_ethtool_ops = {
.get_drvinfo = brcmf_ethtool_get_drvinfo,
};
static int brcmf_netdev_stop(struct net_device *ndev)
{
struct brcmf_if *ifp = netdev_priv(ndev);
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
brcmf_cfg80211_down(ndev);
if (ifp->drvr->bus_if->state == BRCMF_BUS_UP)
brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", NULL, 0);
brcmf_net_setcarrier(ifp, false);
return 0;
}
static int brcmf_netdev_open(struct net_device *ndev)
{
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_bus *bus_if = drvr->bus_if;
u32 toe_ol;
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
/* If bus is not ready, can't continue */
if (bus_if->state != BRCMF_BUS_UP) {
bphy_err(drvr, "failed bus is not ready\n");
return -EAGAIN;
}
atomic_set(&ifp->pend_8021x_cnt, 0);
/* Get current TOE mode from dongle */
if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0
&& (toe_ol & TOE_TX_CSUM_OL) != 0)
ndev->features |= NETIF_F_IP_CSUM;
else
ndev->features &= ~NETIF_F_IP_CSUM;
if (brcmf_cfg80211_up(ndev)) {
bphy_err(drvr, "failed to bring up cfg80211\n");
return -EIO;
}
/* Clear, carrier, set when connected or AP mode. */
netif_carrier_off(ndev);
return 0;
}
static const struct net_device_ops brcmf_netdev_ops_pri = {
.ndo_open = brcmf_netdev_open,
.ndo_stop = brcmf_netdev_stop,
.ndo_start_xmit = brcmf_netdev_start_xmit,
.ndo_set_mac_address = brcmf_netdev_set_mac_address,
.ndo_set_rx_mode = brcmf_netdev_set_multicast_list
};
int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
{
struct brcmf_pub *drvr = ifp->drvr;
struct net_device *ndev;
s32 err;
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx,
ifp->mac_addr);
ndev = ifp->ndev;
/* set appropriate operations */
ndev->netdev_ops = &brcmf_netdev_ops_pri;
ndev->needed_headroom += drvr->hdrlen;
ndev->ethtool_ops = &brcmf_ethtool_ops;
/* set the mac address & netns */
memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
dev_net_set(ndev, wiphy_net(cfg_to_wiphy(drvr->config)));
INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable);
if (rtnl_locked)
err = register_netdevice(ndev);
else
err = register_netdev(ndev);
if (err != 0) {
bphy_err(drvr, "couldn't register the net device\n");
goto fail;
}
ndev->priv_destructor = brcmf_cfg80211_free_netdev;
brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
return 0;
fail:
drvr->iflist[ifp->bsscfgidx] = NULL;
ndev->netdev_ops = NULL;
return -EBADE;
}
static void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked)
{
if (ndev->reg_state == NETREG_REGISTERED) {
if (rtnl_locked)
unregister_netdevice(ndev);
else
unregister_netdev(ndev);
} else {
brcmf_cfg80211_free_netdev(ndev);
free_netdev(ndev);
}
}
void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on)
{
struct net_device *ndev;
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d carrier=%d\n", ifp->bsscfgidx,
on);
ndev = ifp->ndev;
brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_DISCONNECTED, !on);
if (on) {
if (!netif_carrier_ok(ndev))
netif_carrier_on(ndev);
} else {
if (netif_carrier_ok(ndev))
netif_carrier_off(ndev);
}
}
static int brcmf_net_p2p_open(struct net_device *ndev)
{
brcmf_dbg(TRACE, "Enter\n");
return brcmf_cfg80211_up(ndev);
}
static int brcmf_net_p2p_stop(struct net_device *ndev)
{
brcmf_dbg(TRACE, "Enter\n");
return brcmf_cfg80211_down(ndev);
}
static netdev_tx_t brcmf_net_p2p_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
if (skb)
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
static const struct net_device_ops brcmf_netdev_ops_p2p = {
.ndo_open = brcmf_net_p2p_open,
.ndo_stop = brcmf_net_p2p_stop,
.ndo_start_xmit = brcmf_net_p2p_start_xmit
};
static int brcmf_net_p2p_attach(struct brcmf_if *ifp)
{
struct brcmf_pub *drvr = ifp->drvr;
struct net_device *ndev;
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx,
ifp->mac_addr);
ndev = ifp->ndev;
ndev->netdev_ops = &brcmf_netdev_ops_p2p;
/* set the mac address */
memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
if (register_netdev(ndev) != 0) {
bphy_err(drvr, "couldn't register the p2p net device\n");
goto fail;
}
brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
return 0;
fail:
ifp->drvr->iflist[ifp->bsscfgidx] = NULL;
ndev->netdev_ops = NULL;
return -EBADE;
}
struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
bool is_p2pdev, const char *name, u8 *mac_addr)
{
struct brcmf_if *ifp;
struct net_device *ndev;
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx, ifidx);
ifp = drvr->iflist[bsscfgidx];
/*
* Delete the existing interface before overwriting it
* in case we missed the BRCMF_E_IF_DEL event.
*/
if (ifp) {
if (ifidx) {
bphy_err(drvr, "ERROR: netdev:%s already exists\n",
ifp->ndev->name);
netif_stop_queue(ifp->ndev);
brcmf_net_detach(ifp->ndev, false);
drvr->iflist[bsscfgidx] = NULL;
} else {
brcmf_dbg(INFO, "netdev:%s ignore IF event\n",
ifp->ndev->name);
return ERR_PTR(-EINVAL);
}
}
if (!drvr->settings->p2p_enable && is_p2pdev) {
/* this is P2P_DEVICE interface */
brcmf_dbg(INFO, "allocate non-netdev interface\n");
ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
if (!ifp)
return ERR_PTR(-ENOMEM);
} else {
brcmf_dbg(INFO, "allocate netdev interface\n");
/* Allocate netdev, including space for private structure */
ndev = alloc_netdev(sizeof(*ifp), is_p2pdev ? "p2p%d" : name,
NET_NAME_UNKNOWN, ether_setup);
if (!ndev)
return ERR_PTR(-ENOMEM);
net: Fix inconsistent teardown and release of private netdev state. Network devices can allocate reasources and private memory using netdev_ops->ndo_init(). However, the release of these resources can occur in one of two different places. Either netdev_ops->ndo_uninit() or netdev->destructor(). The decision of which operation frees the resources depends upon whether it is necessary for all netdev refs to be released before it is safe to perform the freeing. netdev_ops->ndo_uninit() presumably can occur right after the NETDEV_UNREGISTER notifier completes and the unicast and multicast address lists are flushed. netdev->destructor(), on the other hand, does not run until the netdev references all go away. Further complicating the situation is that netdev->destructor() almost universally does also a free_netdev(). This creates a problem for the logic in register_netdevice(). Because all callers of register_netdevice() manage the freeing of the netdev, and invoke free_netdev(dev) if register_netdevice() fails. If netdev_ops->ndo_init() succeeds, but something else fails inside of register_netdevice(), it does call ndo_ops->ndo_uninit(). But it is not able to invoke netdev->destructor(). This is because netdev->destructor() will do a free_netdev() and then the caller of register_netdevice() will do the same. However, this means that the resources that would normally be released by netdev->destructor() will not be. Over the years drivers have added local hacks to deal with this, by invoking their destructor parts by hand when register_netdevice() fails. Many drivers do not try to deal with this, and instead we have leaks. Let's close this hole by formalizing the distinction between what private things need to be freed up by netdev->destructor() and whether the driver needs unregister_netdevice() to perform the free_netdev(). netdev->priv_destructor() performs all actions to free up the private resources that used to be freed by netdev->destructor(), except for free_netdev(). netdev->needs_free_netdev is a boolean that indicates whether free_netdev() should be done at the end of unregister_netdevice(). Now, register_netdevice() can sanely release all resources after ndo_ops->ndo_init() succeeds, by invoking both ndo_ops->ndo_uninit() and netdev->priv_destructor(). And at the end of unregister_netdevice(), we invoke netdev->priv_destructor() and optionally call free_netdev(). Signed-off-by: David S. Miller <davem@davemloft.net>
2017-05-08 10:52:56 -06:00
ndev->needs_free_netdev = true;
ifp = netdev_priv(ndev);
ifp->ndev = ndev;
/* store mapping ifidx to bsscfgidx */
if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID)
drvr->if2bss[ifidx] = bsscfgidx;
}
ifp->drvr = drvr;
drvr->iflist[bsscfgidx] = ifp;
ifp->ifidx = ifidx;
ifp->bsscfgidx = bsscfgidx;
init_waitqueue_head(&ifp->pend_8021x_wait);
spin_lock_init(&ifp->netif_stop_lock);
if (mac_addr != NULL)
memcpy(ifp->mac_addr, mac_addr, ETH_ALEN);
brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n",
current->pid, name, ifp->mac_addr);
return ifp;
}
static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx,
bool rtnl_locked)
{
struct brcmf_if *ifp;
brcmfmac: fix Oops when bringing up interface during USB disconnect Fix a race which leads to an Oops with NULL pointer dereference. The dereference is in brcmf_config_dongle() when cfg_to_ndev() attempts to get net_device structure of interface with index 0 via if2bss mapping. This shouldn't fail because of check for bus being ready in brcmf_netdev_open(), but it's not synchronised with USB disconnect and there is a race: after the check the bus can be marked down and the mapping for interface 0 may be gone. Solve this by modifying disconnect handling so that the removal of mapping of ifidx to brcmf_if structure happens after netdev removal (which is synchronous with brcmf_netdev_open() thanks to rtln being locked in devinet_ioctl()). This assures brcmf_netdev_open() returns before the mapping is removed during disconnect. Unable to handle kernel NULL pointer dereference at virtual address 00000008 pgd = bcae2612 [00000008] *pgd=8be73831 Internal error: Oops: 17 [#1] PREEMPT SMP ARM Modules linked in: brcmfmac brcmutil nf_log_ipv4 nf_log_common xt_LOG xt_limit iptable_mangle xt_connmark xt_tcpudp xt_conntrack nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 iptable_filter ip_tables x_tables usb_f_mass_storage usb_f_rndis u_ether usb_serial_simple usbserial cdc_acm smsc95xx usbnet ci_hdrc_imx ci_hdrc usbmisc_imx ulpi 8250_exar 8250_pci 8250 8250_base libcomposite configfs udc_core [last unloaded: brcmutil] CPU: 2 PID: 24478 Comm: ifconfig Not tainted 4.19.23-00078-ga62866d-dirty #115 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) PC is at brcmf_cfg80211_up+0x94/0x29c [brcmfmac] LR is at brcmf_cfg80211_up+0x8c/0x29c [brcmfmac] pc : [<7f26a91c>] lr : [<7f26a914>] psr: a0070013 sp : eca99d28 ip : 00000000 fp : ee9c6c00 r10: 00000036 r9 : 00000000 r8 : ece4002c r7 : edb5b800 r6 : 00000000 r5 : 80f08448 r4 : edb5b968 r3 : ffffffff r2 : 00000000 r1 : 00000002 r0 : 00000000 Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none Control: 10c5387d Table: 7ca0c04a DAC: 00000051 Process ifconfig (pid: 24478, stack limit = 0xd9e85a0e) Stack: (0xeca99d28 to 0xeca9a000) 9d20: 00000000 80f873b0 0000000d 80f08448 eca99d68 50d45f32 9d40: 7f27de94 ece40000 80f08448 80f08448 7f27de94 ece4002c 00000000 00000036 9d60: ee9c6c00 7f27262c 00001002 50d45f32 ece40000 00000000 80f08448 80772008 9d80: 00000001 00001043 00001002 ece40000 00000000 50d45f32 ece40000 00000001 9da0: 80f08448 00001043 00001002 807723d0 00000000 50d45f32 80f08448 eca99e58 9dc0: 80f87113 50d45f32 80f08448 ece40000 ece40138 00001002 80f08448 00000000 9de0: 00000000 80772434 edbd5380 eca99e58 edbd5380 80f08448 ee9c6c0c 80805f70 9e00: 00000000 ede08e00 00008914 ece40000 00000014 ee9c6c0c 600c0013 00001043 9e20: 0208a8c0 ffffffff 00000000 50d45f32 eca98000 80f08448 7ee9fc38 00008914 9e40: 80f68e40 00000051 eca98000 00000036 00000003 80808b9c 6e616c77 00000030 9e60: 00000000 00000000 00001043 0208a8c0 ffffffff 00000000 80f08448 00000000 9e80: 00000000 816d8b20 600c0013 00000001 ede09320 801763d4 00000000 50d45f32 9ea0: eca98000 80f08448 7ee9fc38 50d45f32 00008914 80f08448 7ee9fc38 80f68e40 9ec0: ed531540 8074721c 00000800 00000001 00000000 6e616c77 00000030 00000000 9ee0: 00000000 00001002 0208a8c0 ffffffff 00000000 50d45f32 80f08448 7ee9fc38 9f00: ed531560 ec8fc900 80285a6c 80285138 edb910c0 00000000 ecd91008 ede08e00 9f20: 80f08448 00000000 00000000 816d8b20 600c0013 00000001 ede09320 801763d4 9f40: 00000000 50d45f32 00021000 edb91118 edb910c0 80f08448 01b29000 edb91118 9f60: eca99f7c 50d45f32 00021000 ec8fc900 00000003 ec8fc900 00008914 7ee9fc38 9f80: eca98000 00000036 00000003 80285a6c 00086364 7ee9fe1c 000000c3 00000036 9fa0: 801011c4 80101000 00086364 7ee9fe1c 00000003 00008914 7ee9fc38 00086364 9fc0: 00086364 7ee9fe1c 000000c3 00000036 0008630c 7ee9fe1c 7ee9fc38 00000003 9fe0: 000a42b8 7ee9fbd4 00019914 76e09acc 600c0010 00000003 00000000 00000000 [<7f26a91c>] (brcmf_cfg80211_up [brcmfmac]) from [<7f27262c>] (brcmf_netdev_open+0x74/0xe8 [brcmfmac]) [<7f27262c>] (brcmf_netdev_open [brcmfmac]) from [<80772008>] (__dev_open+0xcc/0x150) [<80772008>] (__dev_open) from [<807723d0>] (__dev_change_flags+0x168/0x1b4) [<807723d0>] (__dev_change_flags) from [<80772434>] (dev_change_flags+0x18/0x48) [<80772434>] (dev_change_flags) from [<80805f70>] (devinet_ioctl+0x67c/0x79c) [<80805f70>] (devinet_ioctl) from [<80808b9c>] (inet_ioctl+0x210/0x3d4) [<80808b9c>] (inet_ioctl) from [<8074721c>] (sock_ioctl+0x350/0x524) [<8074721c>] (sock_ioctl) from [<80285138>] (do_vfs_ioctl+0xb0/0x9b0) [<80285138>] (do_vfs_ioctl) from [<80285a6c>] (ksys_ioctl+0x34/0x5c) [<80285a6c>] (ksys_ioctl) from [<80101000>] (ret_fast_syscall+0x0/0x28) Exception stack(0xeca99fa8 to 0xeca99ff0) 9fa0: 00086364 7ee9fe1c 00000003 00008914 7ee9fc38 00086364 9fc0: 00086364 7ee9fe1c 000000c3 00000036 0008630c 7ee9fe1c 7ee9fc38 00000003 9fe0: 000a42b8 7ee9fbd4 00019914 76e09acc Code: e5970328 eb002021 e1a02006 e3a01002 (e5909008) ---[ end trace 5cbac2333f3ac5df ]--- Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-03-13 03:52:01 -06:00
int ifidx;
ifp = drvr->iflist[bsscfgidx];
if (!ifp) {
bphy_err(drvr, "Null interface, bsscfgidx=%d\n", bsscfgidx);
return;
}
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx,
ifp->ifidx);
brcmfmac: fix Oops when bringing up interface during USB disconnect Fix a race which leads to an Oops with NULL pointer dereference. The dereference is in brcmf_config_dongle() when cfg_to_ndev() attempts to get net_device structure of interface with index 0 via if2bss mapping. This shouldn't fail because of check for bus being ready in brcmf_netdev_open(), but it's not synchronised with USB disconnect and there is a race: after the check the bus can be marked down and the mapping for interface 0 may be gone. Solve this by modifying disconnect handling so that the removal of mapping of ifidx to brcmf_if structure happens after netdev removal (which is synchronous with brcmf_netdev_open() thanks to rtln being locked in devinet_ioctl()). This assures brcmf_netdev_open() returns before the mapping is removed during disconnect. Unable to handle kernel NULL pointer dereference at virtual address 00000008 pgd = bcae2612 [00000008] *pgd=8be73831 Internal error: Oops: 17 [#1] PREEMPT SMP ARM Modules linked in: brcmfmac brcmutil nf_log_ipv4 nf_log_common xt_LOG xt_limit iptable_mangle xt_connmark xt_tcpudp xt_conntrack nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 iptable_filter ip_tables x_tables usb_f_mass_storage usb_f_rndis u_ether usb_serial_simple usbserial cdc_acm smsc95xx usbnet ci_hdrc_imx ci_hdrc usbmisc_imx ulpi 8250_exar 8250_pci 8250 8250_base libcomposite configfs udc_core [last unloaded: brcmutil] CPU: 2 PID: 24478 Comm: ifconfig Not tainted 4.19.23-00078-ga62866d-dirty #115 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) PC is at brcmf_cfg80211_up+0x94/0x29c [brcmfmac] LR is at brcmf_cfg80211_up+0x8c/0x29c [brcmfmac] pc : [<7f26a91c>] lr : [<7f26a914>] psr: a0070013 sp : eca99d28 ip : 00000000 fp : ee9c6c00 r10: 00000036 r9 : 00000000 r8 : ece4002c r7 : edb5b800 r6 : 00000000 r5 : 80f08448 r4 : edb5b968 r3 : ffffffff r2 : 00000000 r1 : 00000002 r0 : 00000000 Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none Control: 10c5387d Table: 7ca0c04a DAC: 00000051 Process ifconfig (pid: 24478, stack limit = 0xd9e85a0e) Stack: (0xeca99d28 to 0xeca9a000) 9d20: 00000000 80f873b0 0000000d 80f08448 eca99d68 50d45f32 9d40: 7f27de94 ece40000 80f08448 80f08448 7f27de94 ece4002c 00000000 00000036 9d60: ee9c6c00 7f27262c 00001002 50d45f32 ece40000 00000000 80f08448 80772008 9d80: 00000001 00001043 00001002 ece40000 00000000 50d45f32 ece40000 00000001 9da0: 80f08448 00001043 00001002 807723d0 00000000 50d45f32 80f08448 eca99e58 9dc0: 80f87113 50d45f32 80f08448 ece40000 ece40138 00001002 80f08448 00000000 9de0: 00000000 80772434 edbd5380 eca99e58 edbd5380 80f08448 ee9c6c0c 80805f70 9e00: 00000000 ede08e00 00008914 ece40000 00000014 ee9c6c0c 600c0013 00001043 9e20: 0208a8c0 ffffffff 00000000 50d45f32 eca98000 80f08448 7ee9fc38 00008914 9e40: 80f68e40 00000051 eca98000 00000036 00000003 80808b9c 6e616c77 00000030 9e60: 00000000 00000000 00001043 0208a8c0 ffffffff 00000000 80f08448 00000000 9e80: 00000000 816d8b20 600c0013 00000001 ede09320 801763d4 00000000 50d45f32 9ea0: eca98000 80f08448 7ee9fc38 50d45f32 00008914 80f08448 7ee9fc38 80f68e40 9ec0: ed531540 8074721c 00000800 00000001 00000000 6e616c77 00000030 00000000 9ee0: 00000000 00001002 0208a8c0 ffffffff 00000000 50d45f32 80f08448 7ee9fc38 9f00: ed531560 ec8fc900 80285a6c 80285138 edb910c0 00000000 ecd91008 ede08e00 9f20: 80f08448 00000000 00000000 816d8b20 600c0013 00000001 ede09320 801763d4 9f40: 00000000 50d45f32 00021000 edb91118 edb910c0 80f08448 01b29000 edb91118 9f60: eca99f7c 50d45f32 00021000 ec8fc900 00000003 ec8fc900 00008914 7ee9fc38 9f80: eca98000 00000036 00000003 80285a6c 00086364 7ee9fe1c 000000c3 00000036 9fa0: 801011c4 80101000 00086364 7ee9fe1c 00000003 00008914 7ee9fc38 00086364 9fc0: 00086364 7ee9fe1c 000000c3 00000036 0008630c 7ee9fe1c 7ee9fc38 00000003 9fe0: 000a42b8 7ee9fbd4 00019914 76e09acc 600c0010 00000003 00000000 00000000 [<7f26a91c>] (brcmf_cfg80211_up [brcmfmac]) from [<7f27262c>] (brcmf_netdev_open+0x74/0xe8 [brcmfmac]) [<7f27262c>] (brcmf_netdev_open [brcmfmac]) from [<80772008>] (__dev_open+0xcc/0x150) [<80772008>] (__dev_open) from [<807723d0>] (__dev_change_flags+0x168/0x1b4) [<807723d0>] (__dev_change_flags) from [<80772434>] (dev_change_flags+0x18/0x48) [<80772434>] (dev_change_flags) from [<80805f70>] (devinet_ioctl+0x67c/0x79c) [<80805f70>] (devinet_ioctl) from [<80808b9c>] (inet_ioctl+0x210/0x3d4) [<80808b9c>] (inet_ioctl) from [<8074721c>] (sock_ioctl+0x350/0x524) [<8074721c>] (sock_ioctl) from [<80285138>] (do_vfs_ioctl+0xb0/0x9b0) [<80285138>] (do_vfs_ioctl) from [<80285a6c>] (ksys_ioctl+0x34/0x5c) [<80285a6c>] (ksys_ioctl) from [<80101000>] (ret_fast_syscall+0x0/0x28) Exception stack(0xeca99fa8 to 0xeca99ff0) 9fa0: 00086364 7ee9fe1c 00000003 00008914 7ee9fc38 00086364 9fc0: 00086364 7ee9fe1c 000000c3 00000036 0008630c 7ee9fe1c 7ee9fc38 00000003 9fe0: 000a42b8 7ee9fbd4 00019914 76e09acc Code: e5970328 eb002021 e1a02006 e3a01002 (e5909008) ---[ end trace 5cbac2333f3ac5df ]--- Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-03-13 03:52:01 -06:00
ifidx = ifp->ifidx;
if (ifp->ndev) {
if (bsscfgidx == 0) {
if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
rtnl_lock();
brcmf_netdev_stop(ifp->ndev);
rtnl_unlock();
}
} else {
netif_stop_queue(ifp->ndev);
}
if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
cancel_work_sync(&ifp->multicast_work);
cancel_work_sync(&ifp->ndoffload_work);
}
brcmf_net_detach(ifp->ndev, rtnl_locked);
} else {
/* Only p2p device interfaces which get dynamically created
* end up here. In this case the p2p module should be informed
* about the removal of the interface within the firmware. If
* not then p2p commands towards the firmware will cause some
* serious troublesome side effects. The p2p module will clean
* up the ifp if needed.
*/
brcmfmac: Check rtnl_lock is locked when removing interface Check rtnl_lock is locked in brcmf_p2p_ifp_removed() by passing rtnl_locked flag. Actually the caller brcmf_del_if() checks whether the rtnl_lock is locked, but doesn't pass it to brcmf_p2p_ifp_removed(). Without this fix, wpa_supplicant goes softlockup with rtnl_lock holding (this means all other process using netlink are locked up too) e.g. [ 4495.876627] INFO: task wpa_supplicant:7307 blocked for more than 10 seconds. [ 4495.876632] Tainted: G W 4.8.0-rc1+ #8 [ 4495.876635] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 4495.876638] wpa_supplicant D ffff974c647b39a0 0 7307 1 0x00000000 [ 4495.876644] ffff974c647b39a0 0000000000000000 ffff974c00000000 ffff974c7dc59c58 [ 4495.876651] ffff974c6b7417c0 ffff974c645017c0 ffff974c647b4000 ffffffff86f16c08 [ 4495.876657] ffff974c645017c0 0000000000000246 00000000ffffffff ffff974c647b39b8 [ 4495.876664] Call Trace: [ 4495.876671] [<ffffffff868aeccc>] schedule+0x3c/0x90 [ 4495.876676] [<ffffffff868af065>] schedule_preempt_disabled+0x15/0x20 [ 4495.876682] [<ffffffff868b0996>] mutex_lock_nested+0x176/0x3b0 [ 4495.876686] [<ffffffff867a2067>] ? rtnl_lock+0x17/0x20 [ 4495.876690] [<ffffffff867a2067>] rtnl_lock+0x17/0x20 [ 4495.876720] [<ffffffffc0ae9a5d>] brcmf_p2p_ifp_removed+0x4d/0x70 [brcmfmac] [ 4495.876741] [<ffffffffc0aebde6>] brcmf_remove_interface+0x196/0x1b0 [brcmfmac] [ 4495.876760] [<ffffffffc0ae9901>] brcmf_p2p_del_vif+0x111/0x220 [brcmfmac] [ 4495.876777] [<ffffffffc0adefab>] brcmf_cfg80211_del_iface+0x21b/0x270 [brcmfmac] [ 4495.876820] [<ffffffffc097b39e>] nl80211_del_interface+0xfe/0x3a0 [cfg80211] [ 4495.876825] [<ffffffff867ca335>] genl_family_rcv_msg+0x1b5/0x370 [ 4495.876832] [<ffffffff860e5d8d>] ? trace_hardirqs_on+0xd/0x10 [ 4495.876836] [<ffffffff867ca56d>] genl_rcv_msg+0x7d/0xb0 [ 4495.876839] [<ffffffff867ca4f0>] ? genl_family_rcv_msg+0x370/0x370 [ 4495.876846] [<ffffffff867c9a47>] netlink_rcv_skb+0x97/0xb0 [ 4495.876849] [<ffffffff867ca168>] genl_rcv+0x28/0x40 [ 4495.876854] [<ffffffff867c93c3>] netlink_unicast+0x1d3/0x2f0 [ 4495.876860] [<ffffffff867c933b>] ? netlink_unicast+0x14b/0x2f0 [ 4495.876866] [<ffffffff867c97cb>] netlink_sendmsg+0x2eb/0x3a0 [ 4495.876870] [<ffffffff8676dad8>] sock_sendmsg+0x38/0x50 [ 4495.876874] [<ffffffff8676e4df>] ___sys_sendmsg+0x27f/0x290 [ 4495.876882] [<ffffffff8628b935>] ? mntput_no_expire+0x5/0x3f0 [ 4495.876888] [<ffffffff8628b9be>] ? mntput_no_expire+0x8e/0x3f0 [ 4495.876894] [<ffffffff8628b935>] ? mntput_no_expire+0x5/0x3f0 [ 4495.876899] [<ffffffff8628bd44>] ? mntput+0x24/0x40 [ 4495.876904] [<ffffffff86267830>] ? __fput+0x190/0x200 [ 4495.876909] [<ffffffff8676f125>] __sys_sendmsg+0x45/0x80 [ 4495.876914] [<ffffffff8676f172>] SyS_sendmsg+0x12/0x20 [ 4495.876918] [<ffffffff868b5680>] entry_SYSCALL_64_fastpath+0x23/0xc1 [ 4495.876924] [<ffffffff860e2b8f>] ? trace_hardirqs_off_caller+0x1f/0xc0 Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Acked-by: Rafał Miłecki <rafal@milecki.pl> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2016-08-15 03:40:57 -06:00
brcmf_p2p_ifp_removed(ifp, rtnl_locked);
kfree(ifp);
}
brcmfmac: fix Oops when bringing up interface during USB disconnect Fix a race which leads to an Oops with NULL pointer dereference. The dereference is in brcmf_config_dongle() when cfg_to_ndev() attempts to get net_device structure of interface with index 0 via if2bss mapping. This shouldn't fail because of check for bus being ready in brcmf_netdev_open(), but it's not synchronised with USB disconnect and there is a race: after the check the bus can be marked down and the mapping for interface 0 may be gone. Solve this by modifying disconnect handling so that the removal of mapping of ifidx to brcmf_if structure happens after netdev removal (which is synchronous with brcmf_netdev_open() thanks to rtln being locked in devinet_ioctl()). This assures brcmf_netdev_open() returns before the mapping is removed during disconnect. Unable to handle kernel NULL pointer dereference at virtual address 00000008 pgd = bcae2612 [00000008] *pgd=8be73831 Internal error: Oops: 17 [#1] PREEMPT SMP ARM Modules linked in: brcmfmac brcmutil nf_log_ipv4 nf_log_common xt_LOG xt_limit iptable_mangle xt_connmark xt_tcpudp xt_conntrack nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 iptable_filter ip_tables x_tables usb_f_mass_storage usb_f_rndis u_ether usb_serial_simple usbserial cdc_acm smsc95xx usbnet ci_hdrc_imx ci_hdrc usbmisc_imx ulpi 8250_exar 8250_pci 8250 8250_base libcomposite configfs udc_core [last unloaded: brcmutil] CPU: 2 PID: 24478 Comm: ifconfig Not tainted 4.19.23-00078-ga62866d-dirty #115 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) PC is at brcmf_cfg80211_up+0x94/0x29c [brcmfmac] LR is at brcmf_cfg80211_up+0x8c/0x29c [brcmfmac] pc : [<7f26a91c>] lr : [<7f26a914>] psr: a0070013 sp : eca99d28 ip : 00000000 fp : ee9c6c00 r10: 00000036 r9 : 00000000 r8 : ece4002c r7 : edb5b800 r6 : 00000000 r5 : 80f08448 r4 : edb5b968 r3 : ffffffff r2 : 00000000 r1 : 00000002 r0 : 00000000 Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none Control: 10c5387d Table: 7ca0c04a DAC: 00000051 Process ifconfig (pid: 24478, stack limit = 0xd9e85a0e) Stack: (0xeca99d28 to 0xeca9a000) 9d20: 00000000 80f873b0 0000000d 80f08448 eca99d68 50d45f32 9d40: 7f27de94 ece40000 80f08448 80f08448 7f27de94 ece4002c 00000000 00000036 9d60: ee9c6c00 7f27262c 00001002 50d45f32 ece40000 00000000 80f08448 80772008 9d80: 00000001 00001043 00001002 ece40000 00000000 50d45f32 ece40000 00000001 9da0: 80f08448 00001043 00001002 807723d0 00000000 50d45f32 80f08448 eca99e58 9dc0: 80f87113 50d45f32 80f08448 ece40000 ece40138 00001002 80f08448 00000000 9de0: 00000000 80772434 edbd5380 eca99e58 edbd5380 80f08448 ee9c6c0c 80805f70 9e00: 00000000 ede08e00 00008914 ece40000 00000014 ee9c6c0c 600c0013 00001043 9e20: 0208a8c0 ffffffff 00000000 50d45f32 eca98000 80f08448 7ee9fc38 00008914 9e40: 80f68e40 00000051 eca98000 00000036 00000003 80808b9c 6e616c77 00000030 9e60: 00000000 00000000 00001043 0208a8c0 ffffffff 00000000 80f08448 00000000 9e80: 00000000 816d8b20 600c0013 00000001 ede09320 801763d4 00000000 50d45f32 9ea0: eca98000 80f08448 7ee9fc38 50d45f32 00008914 80f08448 7ee9fc38 80f68e40 9ec0: ed531540 8074721c 00000800 00000001 00000000 6e616c77 00000030 00000000 9ee0: 00000000 00001002 0208a8c0 ffffffff 00000000 50d45f32 80f08448 7ee9fc38 9f00: ed531560 ec8fc900 80285a6c 80285138 edb910c0 00000000 ecd91008 ede08e00 9f20: 80f08448 00000000 00000000 816d8b20 600c0013 00000001 ede09320 801763d4 9f40: 00000000 50d45f32 00021000 edb91118 edb910c0 80f08448 01b29000 edb91118 9f60: eca99f7c 50d45f32 00021000 ec8fc900 00000003 ec8fc900 00008914 7ee9fc38 9f80: eca98000 00000036 00000003 80285a6c 00086364 7ee9fe1c 000000c3 00000036 9fa0: 801011c4 80101000 00086364 7ee9fe1c 00000003 00008914 7ee9fc38 00086364 9fc0: 00086364 7ee9fe1c 000000c3 00000036 0008630c 7ee9fe1c 7ee9fc38 00000003 9fe0: 000a42b8 7ee9fbd4 00019914 76e09acc 600c0010 00000003 00000000 00000000 [<7f26a91c>] (brcmf_cfg80211_up [brcmfmac]) from [<7f27262c>] (brcmf_netdev_open+0x74/0xe8 [brcmfmac]) [<7f27262c>] (brcmf_netdev_open [brcmfmac]) from [<80772008>] (__dev_open+0xcc/0x150) [<80772008>] (__dev_open) from [<807723d0>] (__dev_change_flags+0x168/0x1b4) [<807723d0>] (__dev_change_flags) from [<80772434>] (dev_change_flags+0x18/0x48) [<80772434>] (dev_change_flags) from [<80805f70>] (devinet_ioctl+0x67c/0x79c) [<80805f70>] (devinet_ioctl) from [<80808b9c>] (inet_ioctl+0x210/0x3d4) [<80808b9c>] (inet_ioctl) from [<8074721c>] (sock_ioctl+0x350/0x524) [<8074721c>] (sock_ioctl) from [<80285138>] (do_vfs_ioctl+0xb0/0x9b0) [<80285138>] (do_vfs_ioctl) from [<80285a6c>] (ksys_ioctl+0x34/0x5c) [<80285a6c>] (ksys_ioctl) from [<80101000>] (ret_fast_syscall+0x0/0x28) Exception stack(0xeca99fa8 to 0xeca99ff0) 9fa0: 00086364 7ee9fe1c 00000003 00008914 7ee9fc38 00086364 9fc0: 00086364 7ee9fe1c 000000c3 00000036 0008630c 7ee9fe1c 7ee9fc38 00000003 9fe0: 000a42b8 7ee9fbd4 00019914 76e09acc Code: e5970328 eb002021 e1a02006 e3a01002 (e5909008) ---[ end trace 5cbac2333f3ac5df ]--- Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2019-03-13 03:52:01 -06:00
drvr->iflist[bsscfgidx] = NULL;
if (drvr->if2bss[ifidx] == bsscfgidx)
drvr->if2bss[ifidx] = BRCMF_BSSIDX_INVALID;
}
void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked)
{
if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bsscfgidx] != ifp))
return;
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", ifp->bsscfgidx,
ifp->ifidx);
brcmf_proto_del_if(ifp->drvr, ifp);
brcmf_del_if(ifp->drvr, ifp->bsscfgidx, rtnl_locked);
}
static int brcmf_psm_watchdog_notify(struct brcmf_if *ifp,
const struct brcmf_event_msg *evtmsg,
void *data)
{
struct brcmf_pub *drvr = ifp->drvr;
int err;
brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
bphy_err(drvr, "PSM's watchdog has fired!\n");
err = brcmf_debug_create_memdump(ifp->drvr->bus_if, data,
evtmsg->datalen);
if (err)
bphy_err(drvr, "Failed to get memory dump, %d\n", err);
return err;
}
#ifdef CONFIG_INET
#define ARPOL_MAX_ENTRIES 8
static int brcmf_inetaddr_changed(struct notifier_block *nb,
unsigned long action, void *data)
{
struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
inetaddr_notifier);
struct in_ifaddr *ifa = data;
struct net_device *ndev = ifa->ifa_dev->dev;
struct brcmf_if *ifp;
int idx, i, ret;
u32 val;
__be32 addr_table[ARPOL_MAX_ENTRIES] = {0};
/* Find out if the notification is meant for us */
for (idx = 0; idx < BRCMF_MAX_IFS; idx++) {
ifp = drvr->iflist[idx];
if (ifp && ifp->ndev == ndev)
break;
if (idx == BRCMF_MAX_IFS - 1)
return NOTIFY_DONE;
}
/* check if arp offload is supported */
ret = brcmf_fil_iovar_int_get(ifp, "arpoe", &val);
if (ret)
return NOTIFY_OK;
/* old version only support primary index */
ret = brcmf_fil_iovar_int_get(ifp, "arp_version", &val);
if (ret)
val = 1;
if (val == 1)
ifp = drvr->iflist[0];
/* retrieve the table from firmware */
ret = brcmf_fil_iovar_data_get(ifp, "arp_hostip", addr_table,
sizeof(addr_table));
if (ret) {
bphy_err(drvr, "fail to get arp ip table err:%d\n", ret);
return NOTIFY_OK;
}
for (i = 0; i < ARPOL_MAX_ENTRIES; i++)
if (ifa->ifa_address == addr_table[i])
break;
switch (action) {
case NETDEV_UP:
if (i == ARPOL_MAX_ENTRIES) {
brcmf_dbg(TRACE, "add %pI4 to arp table\n",
&ifa->ifa_address);
/* set it directly */
ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip",
&ifa->ifa_address, sizeof(ifa->ifa_address));
if (ret)
bphy_err(drvr, "add arp ip err %d\n", ret);
}
break;
case NETDEV_DOWN:
if (i < ARPOL_MAX_ENTRIES) {
addr_table[i] = 0;
brcmf_dbg(TRACE, "remove %pI4 from arp table\n",
&ifa->ifa_address);
/* clear the table in firmware */
ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear",
NULL, 0);
if (ret) {
bphy_err(drvr, "fail to clear arp ip table err:%d\n",
ret);
return NOTIFY_OK;
}
for (i = 0; i < ARPOL_MAX_ENTRIES; i++) {
if (addr_table[i] == 0)
continue;
ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip",
&addr_table[i],
sizeof(addr_table[i]));
if (ret)
bphy_err(drvr, "add arp ip err %d\n",
ret);
}
}
break;
default:
break;
}
return NOTIFY_OK;
}
#endif
#if IS_ENABLED(CONFIG_IPV6)
static int brcmf_inet6addr_changed(struct notifier_block *nb,
unsigned long action, void *data)
{
struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
inet6addr_notifier);
struct inet6_ifaddr *ifa = data;
struct brcmf_if *ifp;
int i;
struct in6_addr *table;
/* Only handle primary interface */
ifp = drvr->iflist[0];
if (!ifp)
return NOTIFY_DONE;
if (ifp->ndev != ifa->idev->dev)
return NOTIFY_DONE;
table = ifp->ipv6_addr_tbl;
for (i = 0; i < NDOL_MAX_ENTRIES; i++)
if (ipv6_addr_equal(&ifa->addr, &table[i]))
break;
switch (action) {
case NETDEV_UP:
if (i == NDOL_MAX_ENTRIES) {
if (ifp->ipv6addr_idx < NDOL_MAX_ENTRIES) {
table[ifp->ipv6addr_idx++] = ifa->addr;
} else {
for (i = 0; i < NDOL_MAX_ENTRIES - 1; i++)
table[i] = table[i + 1];
table[NDOL_MAX_ENTRIES - 1] = ifa->addr;
}
}
break;
case NETDEV_DOWN:
if (i < NDOL_MAX_ENTRIES) {
for (; i < ifp->ipv6addr_idx - 1; i++)
table[i] = table[i + 1];
memset(&table[i], 0, sizeof(table[i]));
ifp->ipv6addr_idx--;
}
break;
default:
break;
}
schedule_work(&ifp->ndoffload_work);
return NOTIFY_OK;
}
#endif
static ssize_t chipname_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev->parent);
struct brcmf_pub *drvr = bus_if->drvr;
struct brcmf_rev_info *ri = &drvr->revinfo;
return snprintf(buf, PAGE_SIZE, "%s\n", ri->chipname);
}
static DEVICE_ATTR_RO(chipname);
static int brcmf_revinfo_read(struct seq_file *s, void *data)
{
struct brcmf_bus *bus_if = dev_get_drvdata(s->private);
struct brcmf_rev_info *ri = &bus_if->drvr->revinfo;
char drev[BRCMU_DOTREV_LEN];
char brev[BRCMU_BOARDREV_LEN];
seq_printf(s, "vendorid: 0x%04x\n", ri->vendorid);
seq_printf(s, "deviceid: 0x%04x\n", ri->deviceid);
seq_printf(s, "radiorev: %s\n", brcmu_dotrev_str(ri->radiorev, drev));
seq_printf(s, "chip: %s\n", ri->chipname);
seq_printf(s, "chippkg: %u\n", ri->chippkg);
seq_printf(s, "corerev: %u\n", ri->corerev);
seq_printf(s, "boardid: 0x%04x\n", ri->boardid);
seq_printf(s, "boardvendor: 0x%04x\n", ri->boardvendor);
seq_printf(s, "boardrev: %s\n", brcmu_boardrev_str(ri->boardrev, brev));
seq_printf(s, "driverrev: %s\n", brcmu_dotrev_str(ri->driverrev, drev));
seq_printf(s, "ucoderev: %u\n", ri->ucoderev);
seq_printf(s, "bus: %u\n", ri->bus);
seq_printf(s, "phytype: %u\n", ri->phytype);
seq_printf(s, "phyrev: %u\n", ri->phyrev);
seq_printf(s, "anarev: %u\n", ri->anarev);
seq_printf(s, "nvramrev: %08x\n", ri->nvramrev);
seq_printf(s, "clmver: %s\n", bus_if->drvr->clmver);
return 0;
}
static void brcmf_core_bus_reset(struct work_struct *work)
{
struct brcmf_pub *drvr = container_of(work, struct brcmf_pub,
bus_reset);
brcmf_bus_reset(drvr->bus_if);
}
static ssize_t bus_reset_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct brcmf_pub *drvr = file->private_data;
u8 value;
if (kstrtou8_from_user(user_buf, count, 0, &value))
return -EINVAL;
if (value != 1)
return -EINVAL;
schedule_work(&drvr->bus_reset);
return count;
}
static const struct file_operations bus_reset_fops = {
.open = simple_open,
.llseek = no_llseek,
.write = bus_reset_write,
};
static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops)
{
int ret = -1;
struct brcmf_bus *bus_if = drvr->bus_if;
struct brcmf_if *ifp;
struct brcmf_if *p2p_ifp;
brcmf_dbg(TRACE, "\n");
/* add primary networking interface */
ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL);
if (IS_ERR(ifp))
return PTR_ERR(ifp);
p2p_ifp = NULL;
/* signal bus ready */
brcmf_bus_change_state(bus_if, BRCMF_BUS_UP);
/* do bus specific preinit here */
ret = brcmf_bus_preinit(bus_if);
if (ret < 0)
goto fail;
/* Bus is ready, do any initialization */
ret = brcmf_c_preinit_dcmds(ifp);
if (ret < 0)
goto fail;
brcmf_feat_attach(drvr);
ret = brcmf_proto_init_done(drvr);
if (ret < 0)
goto fail;
brcmf_proto_add_if(drvr, ifp);
drvr->config = brcmf_cfg80211_attach(drvr, ops,
drvr->settings->p2p_enable);
if (drvr->config == NULL) {
ret = -ENOMEM;
goto fail;
}
ret = brcmf_net_attach(ifp, false);
if ((!ret) && (drvr->settings->p2p_enable)) {
p2p_ifp = drvr->iflist[1];
if (p2p_ifp)
ret = brcmf_net_p2p_attach(p2p_ifp);
}
if (ret)
goto fail;
#ifdef CONFIG_INET
drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed;
ret = register_inetaddr_notifier(&drvr->inetaddr_notifier);
if (ret)
goto fail;
#if IS_ENABLED(CONFIG_IPV6)
drvr->inet6addr_notifier.notifier_call = brcmf_inet6addr_changed;
ret = register_inet6addr_notifier(&drvr->inet6addr_notifier);
if (ret) {
unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
goto fail;
}
#endif
#endif /* CONFIG_INET */
INIT_WORK(&drvr->bus_reset, brcmf_core_bus_reset);
/* populate debugfs */
brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read);
debugfs_create_file("reset", 0600, brcmf_debugfs_get_devdir(drvr), drvr,
&bus_reset_fops);
brcmf_feat_debugfs_create(drvr);
brcmf_proto_debugfs_create(drvr);
brcmf_bus_debugfs_create(bus_if);
return 0;
fail:
bphy_err(drvr, "failed: %d\n", ret);
if (drvr->config) {
brcmf_cfg80211_detach(drvr->config);
drvr->config = NULL;
}
brcmf_net_detach(ifp->ndev, false);
if (p2p_ifp)
brcmf_net_detach(p2p_ifp->ndev, false);
drvr->iflist[0] = NULL;
drvr->iflist[1] = NULL;
if (drvr->settings->ignore_probe_fail)
ret = 0;
return ret;
}
int brcmf_alloc(struct device *dev, struct brcmf_mp_device *settings)
{
struct wiphy *wiphy;
struct cfg80211_ops *ops;
struct brcmf_pub *drvr = NULL;
brcmf_dbg(TRACE, "Enter\n");
ops = brcmf_cfg80211_get_ops(settings);
if (!ops)
return -ENOMEM;
wiphy = wiphy_new(ops, sizeof(*drvr));
if (!wiphy) {
kfree(ops);
return -ENOMEM;
}
set_wiphy_dev(wiphy, dev);
drvr = wiphy_priv(wiphy);
drvr->wiphy = wiphy;
drvr->ops = ops;
drvr->bus_if = dev_get_drvdata(dev);
drvr->bus_if->drvr = drvr;
drvr->settings = settings;
return 0;
}
int brcmf_attach(struct device *dev)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
int ret = 0;
int i;
brcmf_dbg(TRACE, "Enter\n");
for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++)
drvr->if2bss[i] = BRCMF_BSSIDX_INVALID;
mutex_init(&drvr->proto_block);
/* Link to bus module */
drvr->hdrlen = 0;
/* Attach and link in the protocol */
ret = brcmf_proto_attach(drvr);
if (ret != 0) {
bphy_err(drvr, "brcmf_prot_attach failed\n");
goto fail;
}
/* Attach to events important for core code */
brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG,
brcmf_psm_watchdog_notify);
/* attach firmware event handler */
brcmf_fweh_attach(drvr);
ret = brcmf_bus_started(drvr, drvr->ops);
if (ret != 0) {
bphy_err(drvr, "dongle is not responding: err=%d\n", ret);
goto fail;
}
/* Create sysfs file to get chip name */
ret = device_create_file(&drvr->wiphy->dev, &dev_attr_chipname);
if (ret) {
brcmf_err("failed to create sysfs file chipname");
goto fail;
}
return 0;
fail:
brcmf_detach(dev);
return ret;
}
void brcmf_bus_add_txhdrlen(struct device *dev, uint len)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
if (drvr) {
drvr->hdrlen += len;
}
}
void brcmf_dev_reset(struct device *dev)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
if (drvr == NULL)
return;
if (drvr->iflist[0])
brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1);
}
void brcmf_dev_coredump(struct device *dev)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
if (brcmf_debug_create_memdump(bus_if, NULL, 0) < 0)
brcmf_dbg(TRACE, "failed to create coredump\n");
}
void brcmf_fw_crashed(struct device *dev)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
bphy_err(drvr, "Firmware has halted or crashed\n");
brcmf_dev_coredump(dev);
schedule_work(&drvr->bus_reset);
}
void brcmf_detach(struct device *dev)
{
s32 i;
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
brcmf_dbg(TRACE, "Enter\n");
if (drvr == NULL)
return;
device_remove_file(&drvr->wiphy->dev, &dev_attr_chipname);
#ifdef CONFIG_INET
unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
#endif
#if IS_ENABLED(CONFIG_IPV6)
unregister_inet6addr_notifier(&drvr->inet6addr_notifier);
#endif
brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
brcmf_bus_stop(drvr->bus_if);
brcmf_fweh_detach(drvr);
brcmf_proto_detach(drvr);
if (drvr->mon_if) {
brcmf_net_detach(drvr->mon_if->ndev, false);
drvr->mon_if = NULL;
}
/* make sure primary interface removed last */
for (i = BRCMF_MAX_IFS - 1; i > -1; i--) {
if (drvr->iflist[i])
brcmf_del_if(drvr, drvr->iflist[i]->bsscfgidx, false);
}
if (drvr->config) {
brcmf_p2p_detach(&drvr->config->p2p);
brcmf_cfg80211_detach(drvr->config);
drvr->config = NULL;
}
}
void brcmf_free(struct device *dev)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
if (!drvr)
return;
bus_if->drvr = NULL;
kfree(drvr->ops);
wiphy_free(drvr->wiphy);
}
s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_if *ifp = bus_if->drvr->iflist[0];
return brcmf_fil_iovar_data_set(ifp, name, data, len);
}
static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp)
{
return atomic_read(&ifp->pend_8021x_cnt);
}
int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp)
{
struct brcmf_pub *drvr = ifp->drvr;
int err;
err = wait_event_timeout(ifp->pend_8021x_wait,
!brcmf_get_pend_8021x_cnt(ifp),
MAX_WAIT_FOR_8021X_TX);
if (!err)
bphy_err(drvr, "Timed out waiting for no pending 802.1x packets\n");
return !err;
}
void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state)
{
struct brcmf_pub *drvr = bus->drvr;
struct net_device *ndev;
int ifidx;
brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state);
if (!drvr) {
brcmf_dbg(INFO, "ignoring transition, bus not attached yet\n");
return;
}
bus->state = state;
if (state == BRCMF_BUS_UP) {
for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
if ((drvr->iflist[ifidx]) &&
(drvr->iflist[ifidx]->ndev)) {
ndev = drvr->iflist[ifidx]->ndev;
if (netif_queue_stopped(ndev))
netif_wake_queue(ndev);
}
}
}
}
brcmfmac: obtain platform data upon module initialization The driver uses platform_driver_probe() to obtain platform data if any. However, that function is placed in the .init section so it must be called upon driver module initialization. The problem was reported by Fenguang Wu resulting in a kernel oops because the .init section was already freed. [ 48.966342] Switched to clocksource tsc [ 48.970002] kernel tried to execute NX-protected page - exploit attempt? (uid: 0) [ 48.970851] BUG: unable to handle kernel paging request at ffffffff82196446 [ 48.970957] IP: [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] PGD 1e76067 PUD 1e77063 PMD f388063 PTE 8000000002196163 [ 48.970957] Oops: 0011 [#1] [ 48.970957] CPU: 0 PID: 17 Comm: kworker/0:1 Not tainted 3.11.0-rc7-00444-gc52dd7f #23 [ 48.970957] Workqueue: events brcmf_driver_init [ 48.970957] task: ffff8800001d2000 ti: ffff8800001d4000 task.ti: ffff8800001d4000 [ 48.970957] RIP: 0010:[<ffffffff82196446>] [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] RSP: 0000:ffff8800001d5d40 EFLAGS: 00000286 [ 48.970957] RAX: 0000000000000001 RBX: ffffffff820c5620 RCX: 0000000000000000 [ 48.970957] RDX: 0000000000000001 RSI: ffffffff816f7380 RDI: ffffffff820c56c0 [ 48.970957] RBP: ffff8800001d5d50 R08: ffff8800001d2508 R09: 0000000000000002 [ 48.970957] R10: 0000000000000000 R11: 0001f7ce298c5620 R12: ffff8800001c76b0 [ 48.970957] R13: ffffffff81e91d40 R14: 0000000000000000 R15: ffff88000e0ce300 [ 48.970957] FS: 0000000000000000(0000) GS:ffffffff81e84000(0000) knlGS:0000000000000000 [ 48.970957] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 48.970957] CR2: ffffffff82196446 CR3: 0000000001e75000 CR4: 00000000000006b0 [ 48.970957] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 48.970957] DR3: 0000000000000000 DR6: 0000000000000000 DR7: 0000000000000000 [ 48.970957] Stack: [ 48.970957] ffffffff816f7df8 ffffffff820c5620 ffff8800001d5d60 ffffffff816eeec9 [ 48.970957] ffff8800001d5de0 ffffffff81073dc5 ffffffff81073d68 ffff8800001d5db8 [ 48.970957] 0000000000000086 ffffffff820c5620 ffffffff824f7fd0 0000000000000000 [ 48.970957] Call Trace: [ 48.970957] [<ffffffff816f7df8>] ? brcmf_sdio_init+0x18/0x70 [ 48.970957] [<ffffffff816eeec9>] brcmf_driver_init+0x9/0x10 [ 48.970957] [<ffffffff81073dc5>] process_one_work+0x1d5/0x480 [ 48.970957] [<ffffffff81073d68>] ? process_one_work+0x178/0x480 [ 48.970957] [<ffffffff81074188>] worker_thread+0x118/0x3a0 [ 48.970957] [<ffffffff81074070>] ? process_one_work+0x480/0x480 [ 48.970957] [<ffffffff8107aa17>] kthread+0xe7/0xf0 [ 48.970957] [<ffffffff810829f7>] ? finish_task_switch.constprop.57+0x37/0xd0 [ 48.970957] [<ffffffff8107a930>] ? __kthread_parkme+0x80/0x80 [ 48.970957] [<ffffffff81a6923a>] ret_from_fork+0x7a/0xb0 [ 48.970957] [<ffffffff8107a930>] ? __kthread_parkme+0x80/0x80 [ 48.970957] Code: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc <cc> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc [ 48.970957] RIP [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] RSP <ffff8800001d5d40> [ 48.970957] CR2: ffffffff82196446 [ 48.970957] ---[ end trace 62980817cd525f14 ]--- Cc: <stable@vger.kernel.org> # 3.10.x, 3.11.x Reported-by: Fengguang Wu <fengguang.wu@intel.com> Reviewed-by: Hante Meuleman <meuleman@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Tested-by: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2013-09-25 04:11:01 -06:00
static void brcmf_driver_register(struct work_struct *work)
{
#ifdef CONFIG_BRCMFMAC_SDIO
brcmfmac: obtain platform data upon module initialization The driver uses platform_driver_probe() to obtain platform data if any. However, that function is placed in the .init section so it must be called upon driver module initialization. The problem was reported by Fenguang Wu resulting in a kernel oops because the .init section was already freed. [ 48.966342] Switched to clocksource tsc [ 48.970002] kernel tried to execute NX-protected page - exploit attempt? (uid: 0) [ 48.970851] BUG: unable to handle kernel paging request at ffffffff82196446 [ 48.970957] IP: [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] PGD 1e76067 PUD 1e77063 PMD f388063 PTE 8000000002196163 [ 48.970957] Oops: 0011 [#1] [ 48.970957] CPU: 0 PID: 17 Comm: kworker/0:1 Not tainted 3.11.0-rc7-00444-gc52dd7f #23 [ 48.970957] Workqueue: events brcmf_driver_init [ 48.970957] task: ffff8800001d2000 ti: ffff8800001d4000 task.ti: ffff8800001d4000 [ 48.970957] RIP: 0010:[<ffffffff82196446>] [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] RSP: 0000:ffff8800001d5d40 EFLAGS: 00000286 [ 48.970957] RAX: 0000000000000001 RBX: ffffffff820c5620 RCX: 0000000000000000 [ 48.970957] RDX: 0000000000000001 RSI: ffffffff816f7380 RDI: ffffffff820c56c0 [ 48.970957] RBP: ffff8800001d5d50 R08: ffff8800001d2508 R09: 0000000000000002 [ 48.970957] R10: 0000000000000000 R11: 0001f7ce298c5620 R12: ffff8800001c76b0 [ 48.970957] R13: ffffffff81e91d40 R14: 0000000000000000 R15: ffff88000e0ce300 [ 48.970957] FS: 0000000000000000(0000) GS:ffffffff81e84000(0000) knlGS:0000000000000000 [ 48.970957] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 48.970957] CR2: ffffffff82196446 CR3: 0000000001e75000 CR4: 00000000000006b0 [ 48.970957] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 48.970957] DR3: 0000000000000000 DR6: 0000000000000000 DR7: 0000000000000000 [ 48.970957] Stack: [ 48.970957] ffffffff816f7df8 ffffffff820c5620 ffff8800001d5d60 ffffffff816eeec9 [ 48.970957] ffff8800001d5de0 ffffffff81073dc5 ffffffff81073d68 ffff8800001d5db8 [ 48.970957] 0000000000000086 ffffffff820c5620 ffffffff824f7fd0 0000000000000000 [ 48.970957] Call Trace: [ 48.970957] [<ffffffff816f7df8>] ? brcmf_sdio_init+0x18/0x70 [ 48.970957] [<ffffffff816eeec9>] brcmf_driver_init+0x9/0x10 [ 48.970957] [<ffffffff81073dc5>] process_one_work+0x1d5/0x480 [ 48.970957] [<ffffffff81073d68>] ? process_one_work+0x178/0x480 [ 48.970957] [<ffffffff81074188>] worker_thread+0x118/0x3a0 [ 48.970957] [<ffffffff81074070>] ? process_one_work+0x480/0x480 [ 48.970957] [<ffffffff8107aa17>] kthread+0xe7/0xf0 [ 48.970957] [<ffffffff810829f7>] ? finish_task_switch.constprop.57+0x37/0xd0 [ 48.970957] [<ffffffff8107a930>] ? __kthread_parkme+0x80/0x80 [ 48.970957] [<ffffffff81a6923a>] ret_from_fork+0x7a/0xb0 [ 48.970957] [<ffffffff8107a930>] ? __kthread_parkme+0x80/0x80 [ 48.970957] Code: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc <cc> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc [ 48.970957] RIP [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] RSP <ffff8800001d5d40> [ 48.970957] CR2: ffffffff82196446 [ 48.970957] ---[ end trace 62980817cd525f14 ]--- Cc: <stable@vger.kernel.org> # 3.10.x, 3.11.x Reported-by: Fengguang Wu <fengguang.wu@intel.com> Reviewed-by: Hante Meuleman <meuleman@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Tested-by: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2013-09-25 04:11:01 -06:00
brcmf_sdio_register();
#endif
#ifdef CONFIG_BRCMFMAC_USB
brcmfmac: obtain platform data upon module initialization The driver uses platform_driver_probe() to obtain platform data if any. However, that function is placed in the .init section so it must be called upon driver module initialization. The problem was reported by Fenguang Wu resulting in a kernel oops because the .init section was already freed. [ 48.966342] Switched to clocksource tsc [ 48.970002] kernel tried to execute NX-protected page - exploit attempt? (uid: 0) [ 48.970851] BUG: unable to handle kernel paging request at ffffffff82196446 [ 48.970957] IP: [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] PGD 1e76067 PUD 1e77063 PMD f388063 PTE 8000000002196163 [ 48.970957] Oops: 0011 [#1] [ 48.970957] CPU: 0 PID: 17 Comm: kworker/0:1 Not tainted 3.11.0-rc7-00444-gc52dd7f #23 [ 48.970957] Workqueue: events brcmf_driver_init [ 48.970957] task: ffff8800001d2000 ti: ffff8800001d4000 task.ti: ffff8800001d4000 [ 48.970957] RIP: 0010:[<ffffffff82196446>] [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] RSP: 0000:ffff8800001d5d40 EFLAGS: 00000286 [ 48.970957] RAX: 0000000000000001 RBX: ffffffff820c5620 RCX: 0000000000000000 [ 48.970957] RDX: 0000000000000001 RSI: ffffffff816f7380 RDI: ffffffff820c56c0 [ 48.970957] RBP: ffff8800001d5d50 R08: ffff8800001d2508 R09: 0000000000000002 [ 48.970957] R10: 0000000000000000 R11: 0001f7ce298c5620 R12: ffff8800001c76b0 [ 48.970957] R13: ffffffff81e91d40 R14: 0000000000000000 R15: ffff88000e0ce300 [ 48.970957] FS: 0000000000000000(0000) GS:ffffffff81e84000(0000) knlGS:0000000000000000 [ 48.970957] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 48.970957] CR2: ffffffff82196446 CR3: 0000000001e75000 CR4: 00000000000006b0 [ 48.970957] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 48.970957] DR3: 0000000000000000 DR6: 0000000000000000 DR7: 0000000000000000 [ 48.970957] Stack: [ 48.970957] ffffffff816f7df8 ffffffff820c5620 ffff8800001d5d60 ffffffff816eeec9 [ 48.970957] ffff8800001d5de0 ffffffff81073dc5 ffffffff81073d68 ffff8800001d5db8 [ 48.970957] 0000000000000086 ffffffff820c5620 ffffffff824f7fd0 0000000000000000 [ 48.970957] Call Trace: [ 48.970957] [<ffffffff816f7df8>] ? brcmf_sdio_init+0x18/0x70 [ 48.970957] [<ffffffff816eeec9>] brcmf_driver_init+0x9/0x10 [ 48.970957] [<ffffffff81073dc5>] process_one_work+0x1d5/0x480 [ 48.970957] [<ffffffff81073d68>] ? process_one_work+0x178/0x480 [ 48.970957] [<ffffffff81074188>] worker_thread+0x118/0x3a0 [ 48.970957] [<ffffffff81074070>] ? process_one_work+0x480/0x480 [ 48.970957] [<ffffffff8107aa17>] kthread+0xe7/0xf0 [ 48.970957] [<ffffffff810829f7>] ? finish_task_switch.constprop.57+0x37/0xd0 [ 48.970957] [<ffffffff8107a930>] ? __kthread_parkme+0x80/0x80 [ 48.970957] [<ffffffff81a6923a>] ret_from_fork+0x7a/0xb0 [ 48.970957] [<ffffffff8107a930>] ? __kthread_parkme+0x80/0x80 [ 48.970957] Code: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc <cc> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc [ 48.970957] RIP [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] RSP <ffff8800001d5d40> [ 48.970957] CR2: ffffffff82196446 [ 48.970957] ---[ end trace 62980817cd525f14 ]--- Cc: <stable@vger.kernel.org> # 3.10.x, 3.11.x Reported-by: Fengguang Wu <fengguang.wu@intel.com> Reviewed-by: Hante Meuleman <meuleman@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Tested-by: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2013-09-25 04:11:01 -06:00
brcmf_usb_register();
#endif
#ifdef CONFIG_BRCMFMAC_PCIE
brcmf_pcie_register();
#endif
}
brcmfmac: obtain platform data upon module initialization The driver uses platform_driver_probe() to obtain platform data if any. However, that function is placed in the .init section so it must be called upon driver module initialization. The problem was reported by Fenguang Wu resulting in a kernel oops because the .init section was already freed. [ 48.966342] Switched to clocksource tsc [ 48.970002] kernel tried to execute NX-protected page - exploit attempt? (uid: 0) [ 48.970851] BUG: unable to handle kernel paging request at ffffffff82196446 [ 48.970957] IP: [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] PGD 1e76067 PUD 1e77063 PMD f388063 PTE 8000000002196163 [ 48.970957] Oops: 0011 [#1] [ 48.970957] CPU: 0 PID: 17 Comm: kworker/0:1 Not tainted 3.11.0-rc7-00444-gc52dd7f #23 [ 48.970957] Workqueue: events brcmf_driver_init [ 48.970957] task: ffff8800001d2000 ti: ffff8800001d4000 task.ti: ffff8800001d4000 [ 48.970957] RIP: 0010:[<ffffffff82196446>] [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] RSP: 0000:ffff8800001d5d40 EFLAGS: 00000286 [ 48.970957] RAX: 0000000000000001 RBX: ffffffff820c5620 RCX: 0000000000000000 [ 48.970957] RDX: 0000000000000001 RSI: ffffffff816f7380 RDI: ffffffff820c56c0 [ 48.970957] RBP: ffff8800001d5d50 R08: ffff8800001d2508 R09: 0000000000000002 [ 48.970957] R10: 0000000000000000 R11: 0001f7ce298c5620 R12: ffff8800001c76b0 [ 48.970957] R13: ffffffff81e91d40 R14: 0000000000000000 R15: ffff88000e0ce300 [ 48.970957] FS: 0000000000000000(0000) GS:ffffffff81e84000(0000) knlGS:0000000000000000 [ 48.970957] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 48.970957] CR2: ffffffff82196446 CR3: 0000000001e75000 CR4: 00000000000006b0 [ 48.970957] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 48.970957] DR3: 0000000000000000 DR6: 0000000000000000 DR7: 0000000000000000 [ 48.970957] Stack: [ 48.970957] ffffffff816f7df8 ffffffff820c5620 ffff8800001d5d60 ffffffff816eeec9 [ 48.970957] ffff8800001d5de0 ffffffff81073dc5 ffffffff81073d68 ffff8800001d5db8 [ 48.970957] 0000000000000086 ffffffff820c5620 ffffffff824f7fd0 0000000000000000 [ 48.970957] Call Trace: [ 48.970957] [<ffffffff816f7df8>] ? brcmf_sdio_init+0x18/0x70 [ 48.970957] [<ffffffff816eeec9>] brcmf_driver_init+0x9/0x10 [ 48.970957] [<ffffffff81073dc5>] process_one_work+0x1d5/0x480 [ 48.970957] [<ffffffff81073d68>] ? process_one_work+0x178/0x480 [ 48.970957] [<ffffffff81074188>] worker_thread+0x118/0x3a0 [ 48.970957] [<ffffffff81074070>] ? process_one_work+0x480/0x480 [ 48.970957] [<ffffffff8107aa17>] kthread+0xe7/0xf0 [ 48.970957] [<ffffffff810829f7>] ? finish_task_switch.constprop.57+0x37/0xd0 [ 48.970957] [<ffffffff8107a930>] ? __kthread_parkme+0x80/0x80 [ 48.970957] [<ffffffff81a6923a>] ret_from_fork+0x7a/0xb0 [ 48.970957] [<ffffffff8107a930>] ? __kthread_parkme+0x80/0x80 [ 48.970957] Code: cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc <cc> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc [ 48.970957] RIP [<ffffffff82196446>] classes_init+0x26/0x26 [ 48.970957] RSP <ffff8800001d5d40> [ 48.970957] CR2: ffffffff82196446 [ 48.970957] ---[ end trace 62980817cd525f14 ]--- Cc: <stable@vger.kernel.org> # 3.10.x, 3.11.x Reported-by: Fengguang Wu <fengguang.wu@intel.com> Reviewed-by: Hante Meuleman <meuleman@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Tested-by: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
2013-09-25 04:11:01 -06:00
static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register);
int __init brcmf_core_init(void)
{
if (!schedule_work(&brcmf_driver_work))
return -EBUSY;
return 0;
}
void __exit brcmf_core_exit(void)
{
cancel_work_sync(&brcmf_driver_work);
#ifdef CONFIG_BRCMFMAC_SDIO
brcmf_sdio_exit();
#endif
#ifdef CONFIG_BRCMFMAC_USB
brcmf_usb_exit();
#endif
#ifdef CONFIG_BRCMFMAC_PCIE
brcmf_pcie_exit();
#endif
}
int
brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num, bool add)
{
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_pkt_filter_le *pkt_filter;
int filter_fixed_len = offsetof(struct brcmf_pkt_filter_le, u);
int pattern_fixed_len = offsetof(struct brcmf_pkt_filter_pattern_le,
mask_and_pattern);
u16 mask_and_pattern[MAX_PKTFILTER_PATTERN_SIZE];
int buflen = 0;
int ret = 0;
brcmf_dbg(INFO, "%s packet filter number %d\n",
(add ? "add" : "remove"), filter_num);
pkt_filter = kzalloc(sizeof(*pkt_filter) +
(MAX_PKTFILTER_PATTERN_SIZE * 2), GFP_ATOMIC);
if (!pkt_filter)
return -ENOMEM;
switch (filter_num) {
case BRCMF_UNICAST_FILTER_NUM:
pkt_filter->id = 100;
pkt_filter->type = 0;
pkt_filter->negate_match = 0;
pkt_filter->u.pattern.offset = 0;
pkt_filter->u.pattern.size_bytes = 1;
mask_and_pattern[0] = 0x0001;
break;
case BRCMF_BROADCAST_FILTER_NUM:
//filter_pattern = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF";
pkt_filter->id = 101;
pkt_filter->type = 0;
pkt_filter->negate_match = 0;
pkt_filter->u.pattern.offset = 0;
pkt_filter->u.pattern.size_bytes = 6;
mask_and_pattern[0] = 0xFFFF;
mask_and_pattern[1] = 0xFFFF;
mask_and_pattern[2] = 0xFFFF;
mask_and_pattern[3] = 0xFFFF;
mask_and_pattern[4] = 0xFFFF;
mask_and_pattern[5] = 0xFFFF;
break;
case BRCMF_MULTICAST4_FILTER_NUM:
//filter_pattern = "102 0 0 0 0xFFFFFF 0x01005E";
pkt_filter->id = 102;
pkt_filter->type = 0;
pkt_filter->negate_match = 0;
pkt_filter->u.pattern.offset = 0;
pkt_filter->u.pattern.size_bytes = 3;
mask_and_pattern[0] = 0xFFFF;
mask_and_pattern[1] = 0x01FF;
mask_and_pattern[2] = 0x5E00;
break;
case BRCMF_MULTICAST6_FILTER_NUM:
//filter_pattern = "103 0 0 0 0xFFFF 0x3333";
pkt_filter->id = 103;
pkt_filter->type = 0;
pkt_filter->negate_match = 0;
pkt_filter->u.pattern.offset = 0;
pkt_filter->u.pattern.size_bytes = 2;
mask_and_pattern[0] = 0xFFFF;
mask_and_pattern[1] = 0x3333;
break;
case BRCMF_MDNS_FILTER_NUM:
//filter_pattern = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB";
pkt_filter->id = 104;
pkt_filter->type = 0;
pkt_filter->negate_match = 0;
pkt_filter->u.pattern.offset = 0;
pkt_filter->u.pattern.size_bytes = 6;
mask_and_pattern[0] = 0xFFFF;
mask_and_pattern[1] = 0xFFFF;
mask_and_pattern[2] = 0xFFFF;
mask_and_pattern[3] = 0x0001;
mask_and_pattern[4] = 0x005E;
mask_and_pattern[5] = 0xFB00;
break;
case BRCMF_ARP_FILTER_NUM:
//filter_pattern = "105 0 0 12 0xFFFF 0x0806";
pkt_filter->id = 105;
pkt_filter->type = 0;
pkt_filter->negate_match = 0;
pkt_filter->u.pattern.offset = 12;
pkt_filter->u.pattern.size_bytes = 2;
mask_and_pattern[0] = 0xFFFF;
mask_and_pattern[1] = 0x0608;
break;
case BRCMF_BROADCAST_ARP_FILTER_NUM:
//filter_pattern = "106 0 0 0
//0xFFFFFFFFFFFF0000000000000806
//0xFFFFFFFFFFFF0000000000000806";
pkt_filter->id = 106;
pkt_filter->type = 0;
pkt_filter->negate_match = 0;
pkt_filter->u.pattern.offset = 0;
pkt_filter->u.pattern.size_bytes = 14;
mask_and_pattern[0] = 0xFFFF;
mask_and_pattern[1] = 0xFFFF;
mask_and_pattern[2] = 0xFFFF;
mask_and_pattern[3] = 0x0000;
mask_and_pattern[4] = 0x0000;
mask_and_pattern[5] = 0x0000;
mask_and_pattern[6] = 0x0608;
mask_and_pattern[7] = 0xFFFF;
mask_and_pattern[8] = 0xFFFF;
mask_and_pattern[9] = 0xFFFF;
mask_and_pattern[10] = 0x0000;
mask_and_pattern[11] = 0x0000;
mask_and_pattern[12] = 0x0000;
mask_and_pattern[13] = 0x0608;
break;
default:
ret = -EINVAL;
goto failed;
}
memcpy(pkt_filter->u.pattern.mask_and_pattern, mask_and_pattern,
pkt_filter->u.pattern.size_bytes * 2);
buflen = filter_fixed_len + pattern_fixed_len +
pkt_filter->u.pattern.size_bytes * 2;
if (add) {
/* Add filter */
ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add",
pkt_filter, buflen);
if (ret)
goto failed;
drvr->pkt_filter[filter_num].id = pkt_filter->id;
drvr->pkt_filter[filter_num].enable = 0;
} else {
/* Delete filter */
ret = brcmf_fil_iovar_int_set(ifp, "pkt_filter_delete",
pkt_filter->id);
if (ret == -ENOENT)
ret = 0;
if (ret)
goto failed;
drvr->pkt_filter[filter_num].id = 0;
drvr->pkt_filter[filter_num].enable = 0;
}
failed:
if (ret)
brcmf_err("%s packet filter failed, ret=%d\n",
(add ? "add" : "remove"), ret);
kfree(pkt_filter);
return ret;
}
int brcmf_pktfilter_enable(struct net_device *ndev, bool enable)
{
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_pub *drvr = ifp->drvr;
int ret = 0;
int idx = 0;
for (idx = 0; idx < MAX_PKT_FILTER_COUNT; ++idx) {
if (drvr->pkt_filter[idx].id != 0) {
drvr->pkt_filter[idx].enable = enable;
ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable",
&drvr->pkt_filter[idx],
sizeof(struct brcmf_pkt_filter_enable_le));
if (ret) {
brcmf_err("%s packet filter id(%d) failed, ret=%d\n",
(enable ? "enable" : "disable"),
drvr->pkt_filter[idx].id, ret);
}
}
}
return ret;
}