/*
* Copyright (c) 2010 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
#include "dhd.h"
#include "dhd_bus.h"
#include "dhd_proto.h"
#include "dhd_dbg.h"
#include "wl_cfg80211.h"
#include "fwil.h"
MODULE_AUTHOR("Broadcom Corporation");
MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
MODULE_SUPPORTED_DEVICE("Broadcom 802.11 WLAN fullmac cards");
MODULE_LICENSE("Dual BSD/GPL");
#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */
/* Error bits */
int brcmf_msg_level;
module_param(brcmf_msg_level, int, 0);
char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
{
if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
brcmf_err("ifidx %d out of range\n", ifidx);
return "<if_bad>";
}
if (drvr->iflist[ifidx] == NULL) {
brcmf_err("null i/f %d\n", ifidx);
return "<if_null>";
}
if (drvr->iflist[ifidx]->ndev)
return drvr->iflist[ifidx]->ndev->name;
return "<if_none>";
}
static void _brcmf_set_multicast_list(struct work_struct *work)
{
struct brcmf_if *ifp;
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\n");
ifp = container_of(work, struct brcmf_if, multicast_work);
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) {
brcmf_err("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)
brcmf_err("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)
brcmf_err("Setting BRCMF_C_SET_PROMISC failed, %d\n",
err);
}
static void
_brcmf_set_mac_address(struct work_struct *work)
{
struct brcmf_if *ifp;
s32 err;
brcmf_dbg(TRACE, "enter\n");
ifp = container_of(work, struct brcmf_if, setmacaddr_work);
err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr,
ETH_ALEN);
if (err < 0) {
brcmf_err("Setting cur_etheraddr failed, %d\n", err);
} else {
brcmf_dbg(TRACE, "MAC address updated to %pM\n",
ifp->mac_addr);
memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
}
}
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;
memcpy(&ifp->mac_addr, sa->sa_data, ETH_ALEN);
schedule_work(