2019-05-16 06:04:09 -06:00
|
|
|
// SPDX-License-Identifier: ISC
|
2011-10-05 05:19:03 -06:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2010 Broadcom Corporation
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/etherdevice.h>
|
2011-10-12 13:35:07 -06:00
|
|
|
#include <linux/module.h>
|
2016-01-02 01:41:36 -07:00
|
|
|
#include <linux/inetdevice.h>
|
2011-10-05 05:19:03 -06:00
|
|
|
#include <net/cfg80211.h>
|
|
|
|
#include <net/rtnetlink.h>
|
2016-02-17 03:26:55 -07:00
|
|
|
#include <net/addrconf.h>
|
2018-06-24 13:44:37 -06:00
|
|
|
#include <net/ieee80211_radiotap.h>
|
2016-02-17 03:26:55 -07:00
|
|
|
#include <net/ipv6.h>
|
2011-10-05 05:19:03 -06:00
|
|
|
#include <brcmu_utils.h>
|
|
|
|
#include <brcmu_wifi.h>
|
|
|
|
|
2014-10-28 07:56:18 -06:00
|
|
|
#include "core.h"
|
2014-10-28 07:56:14 -06:00
|
|
|
#include "bus.h"
|
2014-10-28 07:56:13 -06:00
|
|
|
#include "debug.h"
|
2013-02-08 07:53:44 -07:00
|
|
|
#include "fwil_types.h"
|
2013-02-08 07:53:36 -07:00
|
|
|
#include "p2p.h"
|
2017-06-09 05:19:20 -06:00
|
|
|
#include "pno.h"
|
2014-10-28 07:56:16 -06:00
|
|
|
#include "cfg80211.h"
|
2012-11-05 17:22:14 -07:00
|
|
|
#include "fwil.h"
|
2014-07-12 00:49:39 -06:00
|
|
|
#include "feature.h"
|
2013-11-29 04:25:16 -07:00
|
|
|
#include "proto.h"
|
2014-07-30 05:20:04 -06:00
|
|
|
#include "pcie.h"
|
2015-10-08 12:33:19 -06:00
|
|
|
#include "common.h"
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2016-02-17 03:27:01 -07:00
|
|
|
#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(950)
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2015-09-18 14:08:07 -06:00
|
|
|
#define BRCMF_BSSIDX_INVALID -1
|
|
|
|
|
2019-02-07 23:42:30 -07:00
|
|
|
#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;
|
|
|
|
|
2015-10-29 13:33:12 -06:00
|
|
|
char *brcmf_ifname(struct brcmf_if *ifp)
|
2011-10-05 05:19:03 -06:00
|
|
|
{
|
2015-10-29 13:33:12 -06:00
|
|
|
if (!ifp)
|
2011-10-05 05:19:03 -06:00
|
|
|
return "<if_null>";
|
|
|
|
|
2015-10-29 13:33:12 -06:00
|
|
|
if (ifp->ndev)
|
|
|
|
return ifp->ndev->name;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
|
|
|
return "<if_none>";
|
|
|
|
}
|
|
|
|
|
2015-08-26 14:14:53 -06:00
|
|
|
struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
|
|
|
|
{
|
2015-08-26 14:15:00 -06:00
|
|
|
struct brcmf_if *ifp;
|
2015-10-29 13:33:17 -06:00
|
|
|
s32 bsscfgidx;
|
2015-08-26 14:15:00 -06:00
|
|
|
|
2015-08-26 14:14:53 -06:00
|
|
|
if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "ifidx %d out of range\n", ifidx);
|
2015-08-26 14:14:54 -06:00
|
|
|
return NULL;
|
2015-08-26 14:14:53 -06:00
|
|
|
}
|
|
|
|
|
2015-08-26 14:15:00 -06:00
|
|
|
ifp = NULL;
|
2015-10-29 13:33:17 -06:00
|
|
|
bsscfgidx = drvr->if2bss[ifidx];
|
|
|
|
if (bsscfgidx >= 0)
|
|
|
|
ifp = drvr->iflist[bsscfgidx];
|
2015-08-26 14:14:53 -06:00
|
|
|
|
2015-08-26 14:15:00 -06:00
|
|
|
return ifp;
|
2015-08-26 14:14:53 -06:00
|
|
|
}
|
|
|
|
|
2017-11-08 06:36:32 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-05 05:19:03 -06:00
|
|
|
static void _brcmf_set_multicast_list(struct work_struct *work)
|
|
|
|
{
|
2019-02-19 15:42:19 -07:00
|
|
|
struct brcmf_if *ifp = container_of(work, struct brcmf_if,
|
|
|
|
multicast_work);
|
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
2011-10-05 05:19:03 -06:00
|
|
|
struct net_device *ndev;
|
|
|
|
struct netdev_hw_addr *ha;
|
2012-11-05 17:22:14 -07:00
|
|
|
u32 cmd_value, cnt;
|
2011-10-05 05:19:03 -06:00
|
|
|
__le32 cnt_le;
|
|
|
|
char *buf, *bufp;
|
2012-11-05 17:22:14 -07:00
|
|
|
u32 buflen;
|
|
|
|
s32 err;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
|
2013-02-06 10:40:42 -07:00
|
|
|
|
2012-11-05 17:22:14 -07:00
|
|
|
ndev = ifp->ndev;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
|
|
|
/* Determine initial value of allmulti flag */
|
2012-11-05 17:22:14 -07:00
|
|
|
cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
|
|
|
/* Send down the multicast list first. */
|
2012-11-05 17:22:14 -07:00
|
|
|
cnt = netdev_mc_count(ndev);
|
|
|
|
buflen = sizeof(cnt) + (cnt * ETH_ALEN);
|
|
|
|
buf = kmalloc(buflen, GFP_ATOMIC);
|
|
|
|
if (!buf)
|
2011-10-05 05:19:03 -06:00
|
|
|
return;
|
2012-11-05 17:22:14 -07:00
|
|
|
bufp = buf;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
|
|
|
cnt_le = cpu_to_le32(cnt);
|
2012-11-05 17:22:14 -07:00
|
|
|
memcpy(bufp, &cnt_le, sizeof(cnt_le));
|
2011-10-05 05:19:03 -06:00
|
|
|
bufp += sizeof(cnt_le);
|
|
|
|
|
|
|
|
netdev_for_each_mc_addr(ha, ndev) {
|
|
|
|
if (!cnt)
|
|
|
|
break;
|
|
|
|
memcpy(bufp, ha->addr, ETH_ALEN);
|
|
|
|
bufp += ETH_ALEN;
|
|
|
|
cnt--;
|
|
|
|
}
|
|
|
|
|
2012-11-05 17:22:14 -07:00
|
|
|
err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen);
|
|
|
|
if (err < 0) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "Setting mcast_list failed, %d\n", err);
|
2012-11-05 17:22:14 -07:00
|
|
|
cmd_value = cnt ? true : cmd_value;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
kfree(buf);
|
|
|
|
|
2012-11-05 17:22:14 -07:00
|
|
|
/*
|
|
|
|
* Now send the allmulti setting. This is based on the setting in the
|
2011-10-05 05:19:03 -06:00
|
|
|
* net_device flags, but might be modified above to be turned on if we
|
|
|
|
* were trying to set some addresses and dongle rejected it...
|
|
|
|
*/
|
2012-11-05 17:22:14 -07:00
|
|
|
err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value);
|
|
|
|
if (err < 0)
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "Setting allmulti failed, %d\n", err);
|
2012-11-05 17:22:14 -07:00
|
|
|
|
|
|
|
/*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)
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "Setting BRCMF_C_SET_PROMISC failed, %d\n",
|
|
|
|
err);
|
2017-11-08 06:36:32 -07:00
|
|
|
brcmf_configure_arp_nd_offload(ifp, !cmd_value);
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2016-02-17 03:26:55 -07:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
static void _brcmf_update_ndtable(struct work_struct *work)
|
|
|
|
{
|
2019-02-19 15:42:19 -07:00
|
|
|
struct brcmf_if *ifp = container_of(work, struct brcmf_if,
|
|
|
|
ndoffload_work);
|
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
2016-02-17 03:26:55 -07:00
|
|
|
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)
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "add nd ip err %d\n", ret);
|
2016-02-17 03:26:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static void _brcmf_update_ndtable(struct work_struct *work)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-10-05 05:19:03 -06:00
|
|
|
static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
|
|
|
|
{
|
2011-10-21 08:16:33 -06:00
|
|
|
struct brcmf_if *ifp = netdev_priv(ndev);
|
2011-10-05 05:19:03 -06:00
|
|
|
struct sockaddr *sa = (struct sockaddr *)addr;
|
2019-02-19 15:42:19 -07:00
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
2016-09-19 05:09:55 -06:00
|
|
|
int err;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2016-09-19 05:09:55 -06:00
|
|
|
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) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "Setting cur_etheraddr failed, %d\n", err);
|
2016-09-19 05:09:55 -06:00
|
|
|
} 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;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
|
|
|
|
{
|
2011-10-21 08:16:33 -06:00
|
|
|
struct brcmf_if *ifp = netdev_priv(ndev);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2012-11-14 19:46:09 -07:00
|
|
|
schedule_work(&ifp->multicast_work);
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2018-03-15 01:29:09 -06:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
}
|
|
|
|
|
2013-01-02 07:22:49 -07:00
|
|
|
static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
|
|
|
|
struct net_device *ndev)
|
2011-10-05 05:19:03 -06:00
|
|
|
{
|
|
|
|
int ret;
|
2011-10-21 08:16:33 -06:00
|
|
|
struct brcmf_if *ifp = netdev_priv(ndev);
|
2011-12-16 19:36:51 -07:00
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
2017-04-24 05:40:50 -06:00
|
|
|
struct ethhdr *eh;
|
2017-06-22 04:01:03 -06:00
|
|
|
int head_delta;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2013-01-02 07:22:49 -07:00
|
|
|
/* Can the device send data? */
|
2015-01-25 12:31:35 -07:00
|
|
|
if (drvr->bus_if->state != BRCMF_BUS_UP) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "xmit rejected state=%d\n", drvr->bus_if->state);
|
2011-10-05 05:19:03 -06:00
|
|
|
netif_stop_queue(ndev);
|
2013-01-02 07:22:49 -07:00
|
|
|
dev_kfree_skb(skb);
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto done;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2018-03-15 01:29:09 -06:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2017-06-22 04:01:03 -06:00
|
|
|
/* Make sure there's enough writeable headroom */
|
|
|
|
if (skb_headroom(skb) < drvr->hdrlen || skb_header_cloned(skb)) {
|
2017-07-26 05:24:10 -06:00
|
|
|
head_delta = max_t(int, drvr->hdrlen - skb_headroom(skb), 0);
|
2017-06-22 04:01:03 -06:00
|
|
|
|
|
|
|
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) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "%s: failed to expand headroom\n",
|
|
|
|
brcmf_ifname(ifp));
|
2017-06-22 04:01:03 -06:00
|
|
|
atomic_inc(&drvr->bus_if->stats.pktcow_failed);
|
|
|
|
goto done;
|
|
|
|
}
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2013-01-02 07:22:46 -07:00
|
|
|
/* validate length for ether packet */
|
|
|
|
if (skb->len < sizeof(*eh)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
goto done;
|
2012-09-13 13:11:57 -06:00
|
|
|
}
|
|
|
|
|
2017-04-24 05:40:50 -06:00
|
|
|
eh = (struct ethhdr *)(skb->data);
|
|
|
|
|
2014-05-12 02:47:29 -06:00
|
|
|
if (eh->h_proto == htons(ETH_P_PAE))
|
|
|
|
atomic_inc(&ifp->pend_8021x_cnt);
|
|
|
|
|
2016-09-26 15:51:44 -06:00
|
|
|
/* 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);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
|
|
|
done:
|
2013-02-06 10:40:41 -07:00
|
|
|
if (ret) {
|
2017-02-13 03:14:09 -07:00
|
|
|
ndev->stats.tx_dropped++;
|
2013-02-06 10:40:41 -07:00
|
|
|
} else {
|
2017-02-13 03:14:09 -07:00
|
|
|
ndev->stats.tx_packets++;
|
|
|
|
ndev->stats.tx_bytes += skb->len;
|
2013-02-06 10:40:41 -07:00
|
|
|
}
|
2011-10-05 05:19:03 -06:00
|
|
|
|
|
|
|
/* Return ok: we always eat the packet */
|
2013-01-02 07:22:49 -07:00
|
|
|
return NETDEV_TX_OK;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2013-04-03 04:40:34 -06:00
|
|
|
void brcmf_txflowblock_if(struct brcmf_if *ifp,
|
|
|
|
enum brcmf_netif_stop_reason reason, bool state)
|
|
|
|
{
|
2013-06-06 05:17:48 -06:00
|
|
|
unsigned long flags;
|
|
|
|
|
2013-07-22 04:46:24 -06:00
|
|
|
if (!ifp || !ifp->ndev)
|
2013-04-03 04:40:34 -06:00
|
|
|
return;
|
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(TRACE, "enter: bsscfgidx=%d stop=0x%X reason=%d state=%d\n",
|
|
|
|
ifp->bsscfgidx, ifp->netif_stop, reason, state);
|
2013-06-06 05:17:48 -06:00
|
|
|
|
|
|
|
spin_lock_irqsave(&ifp->netif_stop_lock, flags);
|
2013-04-03 04:40:34 -06:00
|
|
|
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);
|
|
|
|
}
|
2013-06-06 05:17:48 -06:00
|
|
|
spin_unlock_irqrestore(&ifp->netif_stop_lock, flags);
|
2013-04-03 04:40:34 -06:00
|
|
|
}
|
|
|
|
|
2016-04-11 03:35:27 -06:00
|
|
|
void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
|
2013-08-10 04:27:21 -06:00
|
|
|
{
|
2018-03-15 01:29:09 -06:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2013-08-10 04:27:21 -06:00
|
|
|
if (skb->pkt_type == PACKET_MULTICAST)
|
2017-02-13 03:14:09 -07:00
|
|
|
ifp->ndev->stats.multicast++;
|
2013-08-10 04:27:21 -06:00
|
|
|
|
|
|
|
if (!(ifp->ndev->flags & IFF_UP)) {
|
|
|
|
brcmu_pkt_buf_free_skb(skb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-13 03:14:09 -07:00
|
|
|
ifp->ndev->stats.rx_bytes += skb->len;
|
|
|
|
ifp->ndev->stats.rx_packets++;
|
2013-08-10 04:27:21 -06:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-06-24 13:44:37 -06:00
|
|
|
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 */
|
2019-02-07 23:42:30 -07:00
|
|
|
} 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;
|
2018-06-24 13:44:37 -06:00
|
|
|
} 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);
|
|
|
|
}
|
|
|
|
|
2016-04-11 03:35:28 -06:00
|
|
|
static int brcmf_rx_hdrpull(struct brcmf_pub *drvr, struct sk_buff *skb,
|
|
|
|
struct brcmf_if **ifp)
|
2011-10-05 05:19:03 -06:00
|
|
|
{
|
2013-01-02 07:22:43 -07:00
|
|
|
int ret;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2013-09-25 05:05:46 -06:00
|
|
|
/* process and remove protocol-specific header */
|
2016-04-11 03:35:28 -06:00
|
|
|
ret = brcmf_proto_hdrpull(drvr, true, skb, ifp);
|
2013-01-02 07:22:43 -07:00
|
|
|
|
2016-04-11 03:35:28 -06:00
|
|
|
if (ret || !(*ifp) || !(*ifp)->ndev) {
|
2016-04-19 08:25:43 -06:00
|
|
|
if (ret != -ENODATA && *ifp)
|
2017-02-13 03:14:09 -07:00
|
|
|
(*ifp)->ndev->stats.rx_errors++;
|
2013-09-25 05:05:46 -06:00
|
|
|
brcmu_pkt_buf_free_skb(skb);
|
2016-04-11 03:35:28 -06:00
|
|
|
return -ENODATA;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
2013-09-25 05:05:46 -06:00
|
|
|
|
2016-04-11 03:35:28 -06:00
|
|
|
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;
|
2016-04-11 03:35:27 -06:00
|
|
|
|
|
|
|
if (brcmf_proto_is_reorder_skb(skb)) {
|
2016-04-11 03:35:26 -06:00
|
|
|
brcmf_proto_rxreorder(ifp, skb);
|
2016-04-11 03:35:27 -06:00
|
|
|
} else {
|
|
|
|
/* Process special event packets */
|
|
|
|
if (handle_event)
|
2019-02-14 05:43:48 -07:00
|
|
|
brcmf_fweh_process_skb(ifp->drvr, skb,
|
|
|
|
BCMILCP_SUBTYPE_VENDOR_LONG);
|
2016-04-11 03:35:27 -06:00
|
|
|
|
|
|
|
brcmf_netif_rx(ifp, skb);
|
|
|
|
}
|
2016-04-11 03:35:25 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2016-04-11 03:35:28 -06:00
|
|
|
if (brcmf_rx_hdrpull(drvr, skb, &ifp))
|
2016-04-11 03:35:25 -06:00
|
|
|
return;
|
|
|
|
|
2019-02-14 05:43:48 -07:00
|
|
|
brcmf_fweh_process_skb(ifp->drvr, skb, 0);
|
2016-04-11 03:35:25 -06:00
|
|
|
brcmu_pkt_buf_free_skb(skb);
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2015-08-26 14:14:59 -06:00
|
|
|
void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
|
2011-10-05 05:19:03 -06:00
|
|
|
{
|
|
|
|
struct ethhdr *eh;
|
|
|
|
u16 type;
|
|
|
|
|
2018-11-21 02:16:55 -07:00
|
|
|
if (!ifp) {
|
|
|
|
brcmu_pkt_buf_free_skb(txp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-05-12 02:47:28 -06:00
|
|
|
eh = (struct ethhdr *)(txp->data);
|
|
|
|
type = ntohs(eh->h_proto);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2014-05-12 02:47:28 -06:00
|
|
|
if (type == ETH_P_PAE) {
|
|
|
|
atomic_dec(&ifp->pend_8021x_cnt);
|
|
|
|
if (waitqueue_active(&ifp->pend_8021x_wait))
|
|
|
|
wake_up(&ifp->pend_8021x_wait);
|
2012-11-05 17:22:16 -07:00
|
|
|
}
|
2014-05-12 02:47:28 -06:00
|
|
|
|
2013-02-06 10:40:41 -07:00
|
|
|
if (!success)
|
2017-02-13 03:14:09 -07:00
|
|
|
ifp->ndev->stats.tx_errors++;
|
2015-08-26 14:14:59 -06:00
|
|
|
|
2013-04-03 04:40:40 -06:00
|
|
|
brcmu_pkt_buf_free_skb(txp);
|
2013-04-03 04:40:39 -06:00
|
|
|
}
|
2013-03-03 04:45:29 -07:00
|
|
|
|
2011-10-05 05:19:03 -06:00
|
|
|
static void brcmf_ethtool_get_drvinfo(struct net_device *ndev,
|
|
|
|
struct ethtool_drvinfo *info)
|
|
|
|
{
|
2011-10-21 08:16:33 -06:00
|
|
|
struct brcmf_if *ifp = netdev_priv(ndev);
|
2011-12-16 19:36:51 -07:00
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
2015-01-25 12:31:42 -07:00
|
|
|
char drev[BRCMU_DOTREV_LEN] = "n/a";
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2015-01-25 12:31:42 -07:00
|
|
|
if (drvr->revinfo.result == 0)
|
|
|
|
brcmu_dotrev_str(drvr->revinfo.driverrev, drev);
|
2013-01-05 17:44:26 -07:00
|
|
|
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
|
2015-01-25 12:31:42 -07:00
|
|
|
strlcpy(info->version, drev, sizeof(info->version));
|
2013-11-29 04:25:17 -07:00
|
|
|
strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version));
|
2013-01-05 17:44:26 -07:00
|
|
|
strlcpy(info->bus_info, dev_name(drvr->bus_if->dev),
|
|
|
|
sizeof(info->bus_info));
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2012-01-04 15:52:51 -07:00
|
|
|
static const struct ethtool_ops brcmf_ethtool_ops = {
|
|
|
|
.get_drvinfo = brcmf_ethtool_get_drvinfo,
|
2011-10-05 05:19:03 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
static int brcmf_netdev_stop(struct net_device *ndev)
|
|
|
|
{
|
2011-10-21 08:16:33 -06:00
|
|
|
struct brcmf_if *ifp = netdev_priv(ndev);
|
2012-11-05 17:22:30 -07:00
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2012-11-14 19:46:09 -07:00
|
|
|
brcmf_cfg80211_down(ndev);
|
2012-11-05 17:22:30 -07:00
|
|
|
|
2019-07-11 03:05:08 -06:00
|
|
|
if (ifp->drvr->bus_if->state == BRCMF_BUS_UP)
|
|
|
|
brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", NULL, 0);
|
2016-01-02 01:41:36 -07:00
|
|
|
|
2015-10-08 12:33:21 -06:00
|
|
|
brcmf_net_setcarrier(ifp, false);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int brcmf_netdev_open(struct net_device *ndev)
|
|
|
|
{
|
2011-10-21 08:16:33 -06:00
|
|
|
struct brcmf_if *ifp = netdev_priv(ndev);
|
2011-12-16 19:36:51 -07:00
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
2012-02-09 13:09:05 -07:00
|
|
|
struct brcmf_bus *bus_if = drvr->bus_if;
|
2011-10-05 05:19:03 -06:00
|
|
|
u32 toe_ol;
|
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2012-11-14 19:46:09 -07:00
|
|
|
/* If bus is not ready, can't continue */
|
2015-01-25 12:31:35 -07:00
|
|
|
if (bus_if->state != BRCMF_BUS_UP) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "failed bus is not ready\n");
|
2012-11-14 19:46:09 -07:00
|
|
|
return -EAGAIN;
|
|
|
|
}
|
2012-02-09 13:09:05 -07:00
|
|
|
|
2013-02-06 10:40:39 -07:00
|
|
|
atomic_set(&ifp->pend_8021x_cnt, 0);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2012-11-14 19:46:09 -07:00
|
|
|
/* 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)
|
2013-01-02 07:22:51 -07:00
|
|
|
ndev->features |= NETIF_F_IP_CSUM;
|
2012-11-14 19:46:09 -07:00
|
|
|
else
|
2013-01-02 07:22:51 -07:00
|
|
|
ndev->features &= ~NETIF_F_IP_CSUM;
|
2012-04-11 03:52:47 -06:00
|
|
|
|
2012-11-14 19:46:09 -07:00
|
|
|
if (brcmf_cfg80211_up(ndev)) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "failed to bring up cfg80211\n");
|
2013-11-29 03:48:16 -07:00
|
|
|
return -EIO;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2015-10-08 12:33:21 -06:00
|
|
|
/* Clear, carrier, set when connected or AP mode. */
|
|
|
|
netif_carrier_off(ndev);
|
2013-11-29 03:48:16 -07:00
|
|
|
return 0;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2011-10-21 08:16:20 -06:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2013-02-08 07:53:59 -07:00
|
|
|
int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
|
2012-04-11 03:52:43 -06:00
|
|
|
{
|
2012-04-11 03:52:44 -06:00
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
2012-04-11 03:52:43 -06:00
|
|
|
struct net_device *ndev;
|
2013-02-08 07:53:59 -07:00
|
|
|
s32 err;
|
2012-04-11 03:52:43 -06:00
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx,
|
2013-02-06 10:40:42 -07:00
|
|
|
ifp->mac_addr);
|
2012-11-14 19:46:09 -07:00
|
|
|
ndev = ifp->ndev;
|
2012-04-11 03:52:43 -06:00
|
|
|
|
2012-11-14 19:46:09 -07:00
|
|
|
/* set appropriate operations */
|
2013-02-08 07:53:52 -07:00
|
|
|
ndev->netdev_ops = &brcmf_netdev_ops_pri;
|
2012-04-11 03:52:43 -06:00
|
|
|
|
2016-06-03 15:31:09 -06:00
|
|
|
ndev->needed_headroom += drvr->hdrlen;
|
2012-04-11 03:52:43 -06:00
|
|
|
ndev->ethtool_ops = &brcmf_ethtool_ops;
|
|
|
|
|
2017-03-28 04:43:26 -06:00
|
|
|
/* set the mac address & netns */
|
2012-11-14 19:46:12 -07:00
|
|
|
memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
|
2017-03-28 04:43:26 -06:00
|
|
|
dev_net_set(ndev, wiphy_net(cfg_to_wiphy(drvr->config)));
|
2012-04-11 03:52:43 -06:00
|
|
|
|
2013-02-08 07:53:52 -07:00
|
|
|
INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
|
2016-02-17 03:26:55 -07:00
|
|
|
INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable);
|
2013-02-08 07:53:52 -07:00
|
|
|
|
2013-02-08 07:53:59 -07:00
|
|
|
if (rtnl_locked)
|
|
|
|
err = register_netdevice(ndev);
|
|
|
|
else
|
|
|
|
err = register_netdev(ndev);
|
|
|
|
if (err != 0) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "couldn't register the net device\n");
|
2012-04-11 03:52:43 -06:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2017-06-24 15:08:27 -06:00
|
|
|
ndev->priv_destructor = brcmf_cfg80211_free_netdev;
|
2012-04-11 03:52:43 -06:00
|
|
|
brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2015-10-29 13:33:17 -06:00
|
|
|
drvr->iflist[ifp->bsscfgidx] = NULL;
|
2012-04-11 03:52:43 -06:00
|
|
|
ndev->netdev_ops = NULL;
|
|
|
|
return -EBADE;
|
|
|
|
}
|
|
|
|
|
2016-06-17 04:29:21 -06:00
|
|
|
static void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked)
|
2015-08-26 14:15:04 -06:00
|
|
|
{
|
2016-06-17 04:29:21 -06:00
|
|
|
if (ndev->reg_state == NETREG_REGISTERED) {
|
|
|
|
if (rtnl_locked)
|
|
|
|
unregister_netdevice(ndev);
|
|
|
|
else
|
|
|
|
unregister_netdev(ndev);
|
|
|
|
} else {
|
2015-08-26 14:15:04 -06:00
|
|
|
brcmf_cfg80211_free_netdev(ndev);
|
2017-06-24 15:08:27 -06:00
|
|
|
free_netdev(ndev);
|
2016-06-17 04:29:21 -06:00
|
|
|
}
|
2015-08-26 14:15:04 -06:00
|
|
|
}
|
|
|
|
|
2015-10-08 12:33:21 -06:00
|
|
|
void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on)
|
|
|
|
{
|
|
|
|
struct net_device *ndev;
|
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d carrier=%d\n", ifp->bsscfgidx,
|
|
|
|
on);
|
2015-10-08 12:33:21 -06:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-08 07:53:52 -07:00
|
|
|
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)
|
|
|
|
{
|
2019-02-19 15:42:19 -07:00
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
2013-02-08 07:53:52 -07:00
|
|
|
struct net_device *ndev;
|
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx,
|
2013-02-08 07:53:52 -07:00
|
|
|
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) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "couldn't register the p2p net device\n");
|
2013-02-08 07:53:52 -07:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2015-10-29 13:33:17 -06:00
|
|
|
ifp->drvr->iflist[ifp->bsscfgidx] = NULL;
|
2013-05-27 13:09:56 -06:00
|
|
|
ndev->netdev_ops = NULL;
|
2013-02-08 07:53:52 -07:00
|
|
|
return -EBADE;
|
|
|
|
}
|
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
|
2016-06-17 04:48:44 -06:00
|
|
|
bool is_p2pdev, const char *name, u8 *mac_addr)
|
2011-10-05 05:19:03 -06:00
|
|
|
{
|
|
|
|
struct brcmf_if *ifp;
|
2011-10-21 08:16:33 -06:00
|
|
|
struct net_device *ndev;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx, ifidx);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
ifp = drvr->iflist[bsscfgidx];
|
2011-10-21 08:16:33 -06:00
|
|
|
/*
|
|
|
|
* Delete the existing interface before overwriting it
|
|
|
|
* in case we missed the BRCMF_E_IF_DEL event.
|
|
|
|
*/
|
|
|
|
if (ifp) {
|
2012-11-14 19:46:15 -07:00
|
|
|
if (ifidx) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "ERROR: netdev:%s already exists\n",
|
|
|
|
ifp->ndev->name);
|
2012-11-14 19:46:15 -07:00
|
|
|
netif_stop_queue(ifp->ndev);
|
2016-06-17 04:29:21 -06:00
|
|
|
brcmf_net_detach(ifp->ndev, false);
|
2015-10-29 13:33:17 -06:00
|
|
|
drvr->iflist[bsscfgidx] = NULL;
|
2012-11-14 19:46:15 -07:00
|
|
|
} else {
|
2015-11-25 03:32:39 -07:00
|
|
|
brcmf_dbg(INFO, "netdev:%s ignore IF event\n",
|
|
|
|
ifp->ndev->name);
|
2012-11-14 19:46:15 -07:00
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
}
|
2011-10-21 08:16:32 -06:00
|
|
|
}
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2016-01-02 01:41:41 -07:00
|
|
|
if (!drvr->settings->p2p_enable && is_p2pdev) {
|
2013-04-05 02:57:51 -06:00
|
|
|
/* this is P2P_DEVICE interface */
|
|
|
|
brcmf_dbg(INFO, "allocate non-netdev interface\n");
|
|
|
|
ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
|
2013-04-17 13:25:50 -06:00
|
|
|
if (!ifp)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2013-04-05 02:57:51 -06:00
|
|
|
} else {
|
|
|
|
brcmf_dbg(INFO, "allocate netdev interface\n");
|
|
|
|
/* Allocate netdev, including space for private structure */
|
2015-10-08 12:33:15 -06:00
|
|
|
ndev = alloc_netdev(sizeof(*ifp), is_p2pdev ? "p2p%d" : name,
|
|
|
|
NET_NAME_UNKNOWN, ether_setup);
|
2013-04-17 13:25:51 -06:00
|
|
|
if (!ndev)
|
2013-04-05 02:57:51 -06:00
|
|
|
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;
|
2013-04-05 02:57:51 -06:00
|
|
|
ifp = netdev_priv(ndev);
|
|
|
|
ifp->ndev = ndev;
|
2015-10-29 13:33:17 -06:00
|
|
|
/* store mapping ifidx to bsscfgidx */
|
2015-09-18 14:08:07 -06:00
|
|
|
if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID)
|
2015-10-29 13:33:17 -06:00
|
|
|
drvr->if2bss[ifidx] = bsscfgidx;
|
2011-10-21 08:16:32 -06:00
|
|
|
}
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2011-12-16 19:36:51 -07:00
|
|
|
ifp->drvr = drvr;
|
2015-10-29 13:33:17 -06:00
|
|
|
drvr->iflist[bsscfgidx] = ifp;
|
2013-02-08 04:06:31 -07:00
|
|
|
ifp->ifidx = ifidx;
|
2015-10-29 13:33:17 -06:00
|
|
|
ifp->bsscfgidx = bsscfgidx;
|
2012-11-14 19:46:09 -07:00
|
|
|
|
2013-02-06 10:40:39 -07:00
|
|
|
init_waitqueue_head(&ifp->pend_8021x_wait);
|
2013-06-06 05:17:48 -06:00
|
|
|
spin_lock_init(&ifp->netif_stop_lock);
|
2013-02-06 10:40:39 -07:00
|
|
|
|
2013-02-08 04:06:31 -07:00
|
|
|
if (mac_addr != NULL)
|
|
|
|
memcpy(ifp->mac_addr, mac_addr, ETH_ALEN);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2012-11-14 19:46:11 -07:00
|
|
|
brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n",
|
2013-04-05 02:57:51 -06:00
|
|
|
current->pid, name, ifp->mac_addr);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2012-10-22 11:36:20 -06:00
|
|
|
return ifp;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2016-06-17 04:29:21 -06:00
|
|
|
static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx,
|
|
|
|
bool rtnl_locked)
|
2011-10-05 05:19:03 -06:00
|
|
|
{
|
|
|
|
struct brcmf_if *ifp;
|
2019-03-13 03:52:01 -06:00
|
|
|
int ifidx;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2015-10-29 13:33:17 -06:00
|
|
|
ifp = drvr->iflist[bsscfgidx];
|
2011-10-05 05:19:03 -06:00
|
|
|
if (!ifp) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "Null interface, bsscfgidx=%d\n", bsscfgidx);
|
2011-10-05 05:19:03 -06:00
|
|
|
return;
|
|
|
|
}
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx,
|
|
|
|
ifp->ifidx);
|
2019-03-13 03:52:01 -06:00
|
|
|
ifidx = ifp->ifidx;
|
|
|
|
|
2011-10-21 08:16:20 -06:00
|
|
|
if (ifp->ndev) {
|
2015-10-29 13:33:17 -06:00
|
|
|
if (bsscfgidx == 0) {
|
2011-10-21 08:16:20 -06:00
|
|
|
if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
|
|
|
|
rtnl_lock();
|
|
|
|
brcmf_netdev_stop(ifp->ndev);
|
|
|
|
rtnl_unlock();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
netif_stop_queue(ifp->ndev);
|
|
|
|
}
|
|
|
|
|
2013-02-08 07:53:52 -07:00
|
|
|
if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
|
|
|
|
cancel_work_sync(&ifp->multicast_work);
|
2016-02-17 03:26:55 -07:00
|
|
|
cancel_work_sync(&ifp->ndoffload_work);
|
2013-02-08 07:53:52 -07:00
|
|
|
}
|
2016-06-17 04:29:21 -06:00
|
|
|
brcmf_net_detach(ifp->ndev, rtnl_locked);
|
2015-09-18 14:08:12 -06:00
|
|
|
} 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.
|
|
|
|
*/
|
2016-08-15 03:40:57 -06:00
|
|
|
brcmf_p2p_ifp_removed(ifp, rtnl_locked);
|
2015-09-18 14:08:12 -06:00
|
|
|
kfree(ifp);
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
2019-03-13 03:52:01 -06:00
|
|
|
|
|
|
|
drvr->iflist[bsscfgidx] = NULL;
|
|
|
|
if (drvr->if2bss[ifidx] == bsscfgidx)
|
|
|
|
drvr->if2bss[ifidx] = BRCMF_BSSIDX_INVALID;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2016-06-17 04:29:21 -06:00
|
|
|
void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked)
|
2014-12-03 13:05:33 -07:00
|
|
|
{
|
2015-10-29 13:33:17 -06:00
|
|
|
if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bsscfgidx] != ifp))
|
2015-08-26 14:14:55 -06:00
|
|
|
return;
|
2015-10-29 13:33:17 -06:00
|
|
|
brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", ifp->bsscfgidx,
|
2015-09-18 14:08:12 -06:00
|
|
|
ifp->ifidx);
|
2017-03-10 14:17:05 -07:00
|
|
|
brcmf_proto_del_if(ifp->drvr, ifp);
|
2016-06-17 04:29:21 -06:00
|
|
|
brcmf_del_if(ifp->drvr, ifp->bsscfgidx, rtnl_locked);
|
2014-12-03 13:05:33 -07:00
|
|
|
}
|
|
|
|
|
2017-02-24 09:32:46 -07:00
|
|
|
static int brcmf_psm_watchdog_notify(struct brcmf_if *ifp,
|
|
|
|
const struct brcmf_event_msg *evtmsg,
|
|
|
|
void *data)
|
|
|
|
{
|
2019-02-19 15:42:19 -07:00
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
2017-02-24 09:32:46 -07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
|
|
|
|
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "PSM's watchdog has fired!\n");
|
2017-02-24 09:32:46 -07:00
|
|
|
|
|
|
|
err = brcmf_debug_create_memdump(ifp->drvr->bus_if, data,
|
|
|
|
evtmsg->datalen);
|
|
|
|
if (err)
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "Failed to get memory dump, %d\n", err);
|
2017-02-24 09:32:46 -07:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-01-02 01:41:36 -07:00
|
|
|
#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) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "fail to get arp ip table err:%d\n", ret);
|
2016-01-02 01:41:36 -07:00
|
|
|
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)
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "add arp ip err %d\n", ret);
|
2016-01-02 01:41:36 -07:00
|
|
|
}
|
|
|
|
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) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "fail to clear arp ip table err:%d\n",
|
|
|
|
ret);
|
2016-01-02 01:41:36 -07:00
|
|
|
return NOTIFY_OK;
|
|
|
|
}
|
|
|
|
for (i = 0; i < ARPOL_MAX_ENTRIES; i++) {
|
2016-02-17 03:26:56 -07:00
|
|
|
if (addr_table[i] == 0)
|
|
|
|
continue;
|
|
|
|
ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip",
|
|
|
|
&addr_table[i],
|
|
|
|
sizeof(addr_table[i]));
|
|
|
|
if (ret)
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "add arp ip err %d\n",
|
|
|
|
ret);
|
2016-01-02 01:41:36 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_OK;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-02-17 03:26:55 -07:00
|
|
|
#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:
|
2016-09-19 05:09:57 -06:00
|
|
|
if (i < NDOL_MAX_ENTRIES) {
|
|
|
|
for (; i < ifp->ipv6addr_idx - 1; i++)
|
2016-02-17 03:26:55 -07:00
|
|
|
table[i] = table[i + 1];
|
2016-09-19 05:09:57 -06:00
|
|
|
memset(&table[i], 0, sizeof(table[i]));
|
|
|
|
ifp->ipv6addr_idx--;
|
|
|
|
}
|
2016-02-17 03:26:55 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
schedule_work(&ifp->ndoffload_work);
|
|
|
|
|
|
|
|
return NOTIFY_OK;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-06-16 02:41:00 -06:00
|
|
|
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);
|
|
|
|
|
2015-02-06 10:36:44 -07:00
|
|
|
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));
|
2018-03-22 14:28:21 -06:00
|
|
|
seq_printf(s, "chip: %s\n", ri->chipname);
|
2015-02-06 10:36:44 -07:00
|
|
|
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);
|
|
|
|
|
2017-11-10 02:27:15 -07:00
|
|
|
seq_printf(s, "clmver: %s\n", bus_if->drvr->clmver);
|
|
|
|
|
2015-02-06 10:36:44 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-02-26 06:11:19 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-09-01 05:34:36 -06:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2018-03-22 14:28:23 -06:00
|
|
|
static int brcmf_bus_started(struct brcmf_pub *drvr, struct cfg80211_ops *ops)
|
2011-10-05 05:19:03 -06:00
|
|
|
{
|
|
|
|
int ret = -1;
|
2018-02-19 16:14:23 -07:00
|
|
|
struct brcmf_bus *bus_if = drvr->bus_if;
|
2012-10-22 11:36:20 -06:00
|
|
|
struct brcmf_if *ifp;
|
2013-02-08 07:53:52 -07:00
|
|
|
struct brcmf_if *p2p_ifp;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
|
|
|
brcmf_dbg(TRACE, "\n");
|
|
|
|
|
2012-10-22 11:36:20 -06:00
|
|
|
/* add primary networking interface */
|
2015-08-26 14:14:57 -06:00
|
|
|
ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL);
|
2012-10-22 11:36:20 -06:00
|
|
|
if (IS_ERR(ifp))
|
|
|
|
return PTR_ERR(ifp);
|
|
|
|
|
2015-10-08 12:33:15 -06:00
|
|
|
p2p_ifp = NULL;
|
2013-02-08 07:53:52 -07:00
|
|
|
|
2012-10-22 11:36:25 -06:00
|
|
|
/* signal bus ready */
|
2015-01-25 12:31:35 -07:00
|
|
|
brcmf_bus_change_state(bus_if, BRCMF_BUS_UP);
|
2012-10-22 11:36:25 -06:00
|
|
|
|
2018-02-19 16:14:18 -07:00
|
|
|
/* do bus specific preinit here */
|
2018-02-19 16:14:23 -07:00
|
|
|
ret = brcmf_bus_preinit(bus_if);
|
2018-02-19 16:14:18 -07:00
|
|
|
if (ret < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2012-10-22 11:36:25 -06:00
|
|
|
/* Bus is ready, do any initialization */
|
|
|
|
ret = brcmf_c_preinit_dcmds(ifp);
|
2011-10-05 05:19:03 -06:00
|
|
|
if (ret < 0)
|
2012-11-05 17:22:15 -07:00
|
|
|
goto fail;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2014-07-12 00:49:39 -06:00
|
|
|
brcmf_feat_attach(drvr);
|
|
|
|
|
2017-03-28 04:43:24 -06:00
|
|
|
ret = brcmf_proto_init_done(drvr);
|
2013-04-23 04:53:14 -06:00
|
|
|
if (ret < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2017-03-10 14:17:04 -07:00
|
|
|
brcmf_proto_add_if(drvr, ifp);
|
2013-03-03 04:45:28 -07:00
|
|
|
|
2018-03-22 14:28:23 -06:00
|
|
|
drvr->config = brcmf_cfg80211_attach(drvr, ops,
|
2016-01-02 01:41:41 -07:00
|
|
|
drvr->settings->p2p_enable);
|
2012-11-05 17:22:15 -07:00
|
|
|
if (drvr->config == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
2012-10-22 11:36:20 -06:00
|
|
|
|
2013-02-08 07:53:59 -07:00
|
|
|
ret = brcmf_net_attach(ifp, false);
|
2015-10-08 12:33:15 -06:00
|
|
|
|
2016-01-02 01:41:41 -07:00
|
|
|
if ((!ret) && (drvr->settings->p2p_enable)) {
|
2015-10-08 12:33:15 -06:00
|
|
|
p2p_ifp = drvr->iflist[1];
|
|
|
|
if (p2p_ifp)
|
|
|
|
ret = brcmf_net_p2p_attach(p2p_ifp);
|
|
|
|
}
|
2016-01-02 01:41:36 -07:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
#ifdef CONFIG_INET
|
|
|
|
drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed;
|
|
|
|
ret = register_inetaddr_notifier(&drvr->inetaddr_notifier);
|
2016-02-17 03:26:55 -07:00
|
|
|
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;
|
|
|
|
}
|
2016-01-02 01:41:36 -07:00
|
|
|
#endif
|
2016-02-17 03:26:55 -07:00
|
|
|
#endif /* CONFIG_INET */
|
|
|
|
|
2019-02-26 06:11:19 -07:00
|
|
|
INIT_WORK(&drvr->bus_reset, brcmf_core_bus_reset);
|
|
|
|
|
2018-03-22 14:28:24 -06:00
|
|
|
/* populate debugfs */
|
|
|
|
brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read);
|
2019-09-01 05:34:36 -06:00
|
|
|
debugfs_create_file("reset", 0600, brcmf_debugfs_get_devdir(drvr), drvr,
|
|
|
|
&bus_reset_fops);
|
2018-03-22 14:28:24 -06:00
|
|
|
brcmf_feat_debugfs_create(drvr);
|
|
|
|
brcmf_proto_debugfs_create(drvr);
|
2019-02-14 05:43:49 -07:00
|
|
|
brcmf_bus_debugfs_create(bus_if);
|
2018-03-22 14:28:24 -06:00
|
|
|
|
2016-02-17 03:26:55 -07:00
|
|
|
return 0;
|
2016-01-02 01:41:36 -07:00
|
|
|
|
2012-11-05 17:22:15 -07:00
|
|
|
fail:
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "failed: %d\n", ret);
|
2016-02-17 03:26:55 -07:00
|
|
|
if (drvr->config) {
|
|
|
|
brcmf_cfg80211_detach(drvr->config);
|
|
|
|
drvr->config = NULL;
|
2012-10-22 11:36:20 -06:00
|
|
|
}
|
2016-09-19 05:09:56 -06:00
|
|
|
brcmf_net_detach(ifp->ndev, false);
|
2016-02-17 03:26:55 -07:00
|
|
|
if (p2p_ifp)
|
2016-06-17 04:29:21 -06:00
|
|
|
brcmf_net_detach(p2p_ifp->ndev, false);
|
2016-02-17 03:26:55 -07:00
|
|
|
drvr->iflist[0] = NULL;
|
|
|
|
drvr->iflist[1] = NULL;
|
2016-02-17 03:27:08 -07:00
|
|
|
if (drvr->settings->ignore_probe_fail)
|
2016-02-17 03:26:55 -07:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
return ret;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2019-09-02 22:29:27 -06:00
|
|
|
int brcmf_alloc(struct device *dev, struct brcmf_mp_device *settings)
|
2018-02-19 16:14:22 -07:00
|
|
|
{
|
2018-03-22 14:28:23 -06:00
|
|
|
struct wiphy *wiphy;
|
|
|
|
struct cfg80211_ops *ops;
|
2018-02-19 16:14:22 -07:00
|
|
|
struct brcmf_pub *drvr = NULL;
|
|
|
|
|
|
|
|
brcmf_dbg(TRACE, "Enter\n");
|
|
|
|
|
2018-12-04 11:29:05 -07:00
|
|
|
ops = brcmf_cfg80211_get_ops(settings);
|
2018-03-22 14:28:23 -06:00
|
|
|
if (!ops)
|
2018-02-19 16:14:22 -07:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-03-22 14:28:23 -06:00
|
|
|
wiphy = wiphy_new(ops, sizeof(*drvr));
|
2019-09-02 22:29:26 -06:00
|
|
|
if (!wiphy) {
|
|
|
|
kfree(ops);
|
2018-03-22 14:28:23 -06:00
|
|
|
return -ENOMEM;
|
2019-09-02 22:29:26 -06:00
|
|
|
}
|
2018-03-22 14:28:23 -06:00
|
|
|
|
|
|
|
set_wiphy_dev(wiphy, dev);
|
|
|
|
drvr = wiphy_priv(wiphy);
|
|
|
|
drvr->wiphy = wiphy;
|
2019-09-02 22:29:26 -06:00
|
|
|
drvr->ops = ops;
|
2019-09-02 22:29:27 -06:00
|
|
|
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");
|
2018-03-22 14:28:23 -06:00
|
|
|
|
2018-02-19 16:14:22 -07:00
|
|
|
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) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "brcmf_prot_attach failed\n");
|
2018-02-19 16:14:22 -07:00
|
|
|
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);
|
|
|
|
|
2019-09-02 22:29:27 -06:00
|
|
|
ret = brcmf_bus_started(drvr, drvr->ops);
|
2018-02-19 16:14:23 -07:00
|
|
|
if (ret != 0) {
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "dongle is not responding: err=%d\n", ret);
|
2018-02-19 16:14:23 -07:00
|
|
|
goto fail;
|
|
|
|
}
|
2018-03-22 14:28:23 -06:00
|
|
|
|
2021-06-16 02:41:00 -06:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2018-02-19 16:14:23 -07:00
|
|
|
return 0;
|
2018-02-19 16:14:22 -07:00
|
|
|
|
|
|
|
fail:
|
|
|
|
brcmf_detach(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-11-29 03:48:14 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-02 07:22:40 -07:00
|
|
|
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;
|
|
|
|
|
2013-02-08 04:06:32 -07:00
|
|
|
if (drvr->iflist[0])
|
|
|
|
brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1);
|
2013-01-02 07:22:40 -07:00
|
|
|
}
|
|
|
|
|
2018-05-16 06:11:59 -06:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2019-02-26 06:11:18 -07:00
|
|
|
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);
|
2019-02-26 06:11:19 -07:00
|
|
|
|
|
|
|
schedule_work(&drvr->bus_reset);
|
2019-02-26 06:11:18 -07:00
|
|
|
}
|
|
|
|
|
2011-12-16 19:36:56 -07:00
|
|
|
void brcmf_detach(struct device *dev)
|
2011-10-05 05:19:03 -06:00
|
|
|
{
|
2013-02-08 04:06:31 -07:00
|
|
|
s32 i;
|
2011-12-16 19:36:56 -07:00
|
|
|
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
|
|
|
|
struct brcmf_pub *drvr = bus_if->drvr;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
|
|
|
brcmf_dbg(TRACE, "Enter\n");
|
|
|
|
|
2012-11-05 17:22:30 -07:00
|
|
|
if (drvr == NULL)
|
|
|
|
return;
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2021-06-16 02:41:00 -06:00
|
|
|
device_remove_file(&drvr->wiphy->dev, &dev_attr_chipname);
|
|
|
|
|
2016-01-02 01:41:36 -07:00
|
|
|
#ifdef CONFIG_INET
|
|
|
|
unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
|
|
|
|
#endif
|
|
|
|
|
2016-02-17 03:26:55 -07:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
unregister_inet6addr_notifier(&drvr->inet6addr_notifier);
|
|
|
|
#endif
|
|
|
|
|
2014-01-13 14:20:29 -07:00
|
|
|
brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
|
2017-01-18 03:48:53 -07:00
|
|
|
brcmf_bus_stop(drvr->bus_if);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2019-07-11 03:05:07 -06:00
|
|
|
brcmf_fweh_detach(drvr);
|
2019-07-11 03:05:06 -06:00
|
|
|
brcmf_proto_detach(drvr);
|
2011-10-05 05:19:03 -06:00
|
|
|
|
2019-11-18 05:38:55 -07:00
|
|
|
if (drvr->mon_if) {
|
|
|
|
brcmf_net_detach(drvr->mon_if->ndev, false);
|
|
|
|
drvr->mon_if = NULL;
|
|
|
|
}
|
|
|
|
|
2019-07-11 03:05:07 -06:00
|
|
|
/* 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;
|
|
|
|
}
|
2019-09-02 22:29:27 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2019-07-11 03:05:07 -06:00
|
|
|
|
2011-12-16 19:36:56 -07:00
|
|
|
bus_if->drvr = NULL;
|
2019-07-11 03:05:07 -06:00
|
|
|
|
2019-09-02 22:29:26 -06:00
|
|
|
kfree(drvr->ops);
|
|
|
|
|
2018-03-22 14:28:23 -06:00
|
|
|
wiphy_free(drvr->wiphy);
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2013-11-29 03:48:15 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-02-06 10:40:39 -07:00
|
|
|
static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp)
|
2011-10-05 05:19:03 -06:00
|
|
|
{
|
2013-02-06 10:40:39 -07:00
|
|
|
return atomic_read(&ifp->pend_8021x_cnt);
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2014-12-21 04:43:49 -07:00
|
|
|
int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp)
|
2011-10-05 05:19:03 -06:00
|
|
|
{
|
2019-02-19 15:42:19 -07:00
|
|
|
struct brcmf_pub *drvr = ifp->drvr;
|
2012-11-05 17:22:16 -07:00
|
|
|
int err;
|
|
|
|
|
2013-02-06 10:40:39 -07:00
|
|
|
err = wait_event_timeout(ifp->pend_8021x_wait,
|
|
|
|
!brcmf_get_pend_8021x_cnt(ifp),
|
2016-01-05 03:05:48 -07:00
|
|
|
MAX_WAIT_FOR_8021X_TX);
|
2012-11-05 17:22:16 -07:00
|
|
|
|
2016-09-27 04:12:24 -06:00
|
|
|
if (!err)
|
2019-02-19 15:42:19 -07:00
|
|
|
bphy_err(drvr, "Timed out waiting for no pending 802.1x packets\n");
|
2012-11-05 17:22:16 -07:00
|
|
|
|
|
|
|
return !err;
|
2011-10-05 05:19:03 -06:00
|
|
|
}
|
|
|
|
|
2015-01-25 12:31:36 -07:00
|
|
|
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);
|
2018-02-19 16:14:20 -07:00
|
|
|
|
|
|
|
if (!drvr) {
|
|
|
|
brcmf_dbg(INFO, "ignoring transition, bus not attached yet\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-25 12:31:36 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-25 04:11:01 -06:00
|
|
|
static void brcmf_driver_register(struct work_struct *work)
|
2012-02-09 13:09:03 -07:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_BRCMFMAC_SDIO
|
2013-09-25 04:11:01 -06:00
|
|
|
brcmf_sdio_register();
|
2012-02-09 13:09:03 -07:00
|
|
|
#endif
|
2012-02-09 13:09:08 -07:00
|
|
|
#ifdef CONFIG_BRCMFMAC_USB
|
2013-09-25 04:11:01 -06:00
|
|
|
brcmf_usb_register();
|
2012-02-09 13:09:08 -07:00
|
|
|
#endif
|
2014-07-30 05:20:04 -06:00
|
|
|
#ifdef CONFIG_BRCMFMAC_PCIE
|
|
|
|
brcmf_pcie_register();
|
|
|
|
#endif
|
2012-03-02 14:55:49 -07:00
|
|
|
}
|
2013-09-25 04:11:01 -06:00
|
|
|
static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register);
|
2012-03-02 14:55:49 -07:00
|
|
|
|
2016-02-17 03:27:02 -07:00
|
|
|
int __init brcmf_core_init(void)
|
2012-03-02 14:55:49 -07:00
|
|
|
{
|
|
|
|
if (!schedule_work(&brcmf_driver_work))
|
|
|
|
return -EBUSY;
|
|
|
|
|
2012-03-02 14:55:48 -07:00
|
|
|
return 0;
|
2012-02-09 13:09:03 -07:00
|
|
|
}
|
|
|
|
|
2016-02-17 03:27:02 -07:00
|
|
|
void __exit brcmf_core_exit(void)
|
2012-02-09 13:09:03 -07:00
|
|
|
{
|
2012-03-02 14:55:49 -07:00
|
|
|
cancel_work_sync(&brcmf_driver_work);
|
|
|
|
|
2012-02-09 13:09:03 -07:00
|
|
|
#ifdef CONFIG_BRCMFMAC_SDIO
|
|
|
|
brcmf_sdio_exit();
|
|
|
|
#endif
|
2012-02-09 13:09:08 -07:00
|
|
|
#ifdef CONFIG_BRCMFMAC_USB
|
|
|
|
brcmf_usb_exit();
|
2014-07-30 05:20:04 -06:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_BRCMFMAC_PCIE
|
|
|
|
brcmf_pcie_exit();
|
2012-02-09 13:09:08 -07:00
|
|
|
#endif
|
2012-02-09 13:09:03 -07:00
|
|
|
}
|
|
|
|
|
2018-02-06 04:07:05 -07:00
|
|
|
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;
|
|
|
|
}
|