blob: d7b3553c70d0cef730292173d468a2e17c4215a0 [file] [log] [blame]
/*
* Linux cfg80211 driver - Dongle Host Driver (DHD) related
*
* Copyright 1999-2016, Broadcom Corporation
* All rights reserved,
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* This software is provided by the copyright holder "as is" and any express or
* implied warranties, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose are disclaimed. In no event
* shall copyright holder be liable for any direct, indirect, incidental, special,
* exemplary, or consequential damages (including, but not limited to, procurement
* of substitute goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether in
* contract, strict liability, or tort (including negligence or otherwise) arising
* in any way out of the use of this software, even if advised of the possibility
* of such damage
*
*
* <<Broadcom-WL-IPTag/Open:>>
*
* $Id: dhd_cfg80211.c 591285 2015-10-07 11:56:29Z $
*/
#include <linux/vmalloc.h>
#include <net/rtnetlink.h>
#include <bcmutils.h>
#include <wldev_common.h>
#ifdef WL_CFG80211_V1
#include <wl_cfg80211_v1.h>
#else
#include <wl_cfg80211.h>
#endif /* WL_CFG80211_V1 */
#include <dhd_cfg80211.h>
#ifdef PKT_FILTER_SUPPORT
#include <dngl_stats.h>
#include <dhd.h>
#endif
#ifdef WL_CFG80211_V1
extern struct bcm_cfg80211 *g_bcm_cfg;
#endif /* WL_CFG80211_V1 */
#ifdef PKT_FILTER_SUPPORT
extern uint dhd_pkt_filter_enable;
extern uint dhd_master_mode;
extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
#endif
static int dhd_dongle_up = FALSE;
#include <dngl_stats.h>
#include <dhd.h>
#include <dhdioctl.h>
#include <wlioctl.h>
#include <brcm_nl80211.h>
#include <dhd_cfg80211.h>
static s32 wl_dongle_up(struct net_device *ndev);
static s32 wl_dongle_down(struct net_device *ndev);
/**
* Function implementations
*/
s32 dhd_cfg80211_init(struct bcm_cfg80211 *cfg)
{
dhd_dongle_up = FALSE;
return 0;
}
s32 dhd_cfg80211_deinit(struct bcm_cfg80211 *cfg)
{
dhd_dongle_up = FALSE;
return 0;
}
s32 dhd_cfg80211_down(struct bcm_cfg80211 *cfg)
{
struct net_device *ndev;
s32 err = 0;
WL_TRACE(("In\n"));
if (!dhd_dongle_up) {
WL_ERR(("Dongle is already down\n"));
return err;
}
ndev = bcmcfg_to_prmry_ndev(cfg);
wl_dongle_down(ndev);
dhd_dongle_up = FALSE;
return 0;
}
s32 dhd_cfg80211_set_p2p_info(struct bcm_cfg80211 *cfg, int val)
{
dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
dhd->op_mode |= val;
WL_ERR(("Set : op_mode=0x%04x\n", dhd->op_mode));
#ifdef ARP_OFFLOAD_SUPPORT
if (dhd->arp_version == 1) {
/* IF P2P is enabled, disable arpoe */
dhd_arp_offload_set(dhd, 0);
dhd_arp_offload_enable(dhd, false);
}
#endif /* ARP_OFFLOAD_SUPPORT */
return 0;
}
s32 dhd_cfg80211_clean_p2p_info(struct bcm_cfg80211 *cfg)
{
dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
dhd->op_mode &= ~(DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE);
WL_ERR(("Clean : op_mode=0x%04x\n", dhd->op_mode));
#ifdef ARP_OFFLOAD_SUPPORT
if (dhd->arp_version == 1) {
/* IF P2P is disabled, enable arpoe back for STA mode. */
dhd_arp_offload_set(dhd, dhd_arp_mode);
dhd_arp_offload_enable(dhd, true);
}
#endif /* ARP_OFFLOAD_SUPPORT */
return 0;
}
#ifdef WL_CFG80211_V1
struct net_device* wl_cfg80211_allocate_if(struct bcm_cfg80211 *cfg, int ifidx, char *name,
uint8 *mac, uint8 bssidx, char *dngl_name)
#else
struct net_device* wl_cfg80211_allocate_if(struct bcm_cfg80211 *cfg, int ifidx, const char *name,
uint8 *mac, uint8 bssidx, const char *dngl_name)
#endif /* WL_CFG80211_V1 */
{
return dhd_allocate_if(cfg->pub, ifidx, name, mac, bssidx, FALSE, dngl_name);
}
#ifdef WL_CFG80211_V1
int wl_cfg80211_register_if(struct bcm_cfg80211 *cfg, int ifidx, struct net_device* ndev)
{
return dhd_register_if(cfg->pub, ifidx, FALSE);
}
int wl_cfg80211_remove_if(struct bcm_cfg80211 *cfg, int ifidx, struct net_device* ndev)
{
return dhd_remove_if(cfg->pub, ifidx, FALSE);
}
#else
int wl_cfg80211_register_if(struct bcm_cfg80211 *cfg,
int ifidx, struct net_device* ndev, bool rtnl_lock_reqd)
{
return dhd_register_if(cfg->pub, ifidx, rtnl_lock_reqd);
}
int wl_cfg80211_remove_if(struct bcm_cfg80211 *cfg,
int ifidx, struct net_device* ndev, bool rtnl_lock_reqd)
{
return dhd_remove_if(cfg->pub, ifidx, rtnl_lock_reqd);
}
#endif /* WL_CFG80211_V1 */
struct net_device * dhd_cfg80211_netdev_free(struct net_device *ndev)
{
if (ndev) {
if (ndev->ieee80211_ptr) {
kfree(ndev->ieee80211_ptr);
ndev->ieee80211_ptr = NULL;
}
free_netdev(ndev);
return NULL;
}
return ndev;
}
void dhd_netdev_free(struct net_device *ndev)
{
#ifdef WL_CFG80211
ndev = dhd_cfg80211_netdev_free(ndev);
#endif
if (ndev)
free_netdev(ndev);
}
static s32
wl_dongle_up(struct net_device *ndev)
{
s32 err = 0;
u32 up = 0;
err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true);
if (unlikely(err)) {
WL_ERR(("WLC_UP error (%d)\n", err));
}
return err;
}
static s32
wl_dongle_down(struct net_device *ndev)
{
s32 err = 0;
u32 down = 0;
err = wldev_ioctl(ndev, WLC_DOWN, &down, sizeof(down), true);
if (unlikely(err)) {
WL_ERR(("WLC_DOWN error (%d)\n", err));
}
return err;
}
s32 dhd_config_dongle(struct bcm_cfg80211 *cfg)
{
#ifndef DHD_SDALIGN
#define DHD_SDALIGN 32
#endif
struct net_device *ndev;
s32 err = 0;
WL_TRACE(("In\n"));
if (dhd_dongle_up) {
WL_ERR(("Dongle is already up\n"));
return err;
}
ndev = bcmcfg_to_prmry_ndev(cfg);
err = wl_dongle_up(ndev);
if (unlikely(err)) {
WL_ERR(("wl_dongle_up failed\n"));
goto default_conf_out;
}
dhd_dongle_up = true;
default_conf_out:
return err;
}
int dhd_cfgvendor_priv_string_handler(struct bcm_cfg80211 *cfg, struct wireless_dev *wdev,
const struct bcm_nlmsg_hdr *nlioc, void *buf, int ifindex)
{
dhd_pub_t *dhd;
dhd_ioctl_t ioc = { 0 };
int ret = 0, err = 0;
int8 index;
struct net_device *ndev;
WL_TRACE(("entry: cmd = %d\n", nlioc->cmd));
dhd = cfg->pub;
DHD_OS_WAKE_LOCK(dhd);
/* send to dongle only if we are not waiting for reload already */
if (dhd->hang_was_sent) {
WL_ERR(("HANG was sent up earlier\n"));
DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhd, DHD_EVENT_TIMEOUT_MS);
DHD_OS_WAKE_UNLOCK(dhd);
return OSL_ERROR(BCME_DONGLE_DOWN);
}
index = dhd_ifindex2idx(dhd->info, ifindex);
if (index == DHD_BAD_IF) {
ndev = wdev_to_wlc_ndev(wdev, cfg);
index = dhd_net2idx(dhd->info, ndev);
if (index == DHD_BAD_IF) {
WL_ERR(("Bad ifidx %d, wdev:%p\n", ifindex, wdev));
ret = BCME_ERROR;
goto done;
}
}
ioc.cmd = nlioc->cmd;
ioc.len = nlioc->len;
ioc.set = nlioc->set;
ioc.driver = nlioc->magic;
err = dhd_ioctl_process(dhd, index, &ioc, buf);
if (err) {
ret = OSL_ERROR(err);
ndev = dhd_idx2net(dhd, index);
if(ndev && (ndev->flags & IFF_UP))
WL_DBG(("cmd %d return err %d on %s, ret %d\n",
nlioc->cmd, err, ndev->name, ret));
goto done;
}
done:
DHD_OS_WAKE_UNLOCK(dhd);
return ret;
}