Skip to content

Commit

Permalink
brcmfmac: add support for creating P2P client/GO interface
Browse files Browse the repository at this point in the history
This patch allow the creation of P2P client and group owner
virtual interfaces in the driver.

Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Arend van Spriel authored and John W. Linville committed Feb 8, 2013
1 parent 9f440b7 commit d3c0b63
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 15 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/dhd.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
#define BRCMF_C_SET_WSEC 134
#define BRCMF_C_GET_PHY_NOISE 135
#define BRCMF_C_GET_BSS_INFO 136
#define BRCMF_C_SET_SCB_TIMEOUT 158
#define BRCMF_C_GET_PHYLIST 180
#define BRCMF_C_SET_SCAN_CHANNEL_TIME 185
#define BRCMF_C_SET_SCAN_UNASSOC_TIME 187
Expand Down
42 changes: 42 additions & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2012 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.
*/


#ifndef FWIL_TYPES_H_
#define FWIL_TYPES_H_

#include <linux/if_ether.h>

enum brcmf_fil_p2p_if_types {
BRCMF_FIL_P2P_IF_CLIENT,
BRCMF_FIL_P2P_IF_GO,
BRCMF_FIL_P2P_IF_DYNBCN_GO,
BRCMF_FIL_P2P_IF_DEV,
};

struct brcmf_fil_p2p_if_le {
u8 addr[ETH_ALEN];
enum brcmf_fil_p2p_if_types type;
__le16 chspec;
};

struct brcmf_fil_chan_info_le {
__le32 hw_channel;
__le32 target_channel;
__le32 scan_channel;
};

#endif /* FWIL_TYPES_H_ */
112 changes: 107 additions & 5 deletions drivers/net/wireless/brcm80211/brcmfmac/p2p.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <dhd.h>
#include <dhd_dbg.h>
#include "fwil.h"
#include "fwil_types.h"
#include "p2p.h"
#include "wl_cfg80211.h"

Expand All @@ -45,6 +46,8 @@
#define SOCIAL_CHAN_CNT 3
#define AF_PEER_SEARCH_CNT 2

#define BRCMF_SCB_TIMEOUT_VALUE 20

/**
* struct brcmf_p2p_disc_st_le - set discovery state in firmware.
*
Expand Down Expand Up @@ -285,7 +288,7 @@ static s32 brcmf_p2p_init_discovery(struct brcmf_p2p_info *p2p)
}

/* create a vif for it */
vif = brcmf_alloc_vif(p2p->cfg, NULL, NL80211_IFTYPE_P2P_DEVICE, false);
vif = brcmf_alloc_vif(p2p->cfg, NL80211_IFTYPE_P2P_DEVICE, false);
if (IS_ERR(vif)) {
brcmf_err("could not create discovery vif\n");
kfree(ifp);
Expand Down Expand Up @@ -691,6 +694,43 @@ void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
memset(p2p, 0, sizeof(*p2p));
}

static int brcmf_p2p_request_p2p_if(struct brcmf_if *ifp, u8 ea[ETH_ALEN],
enum brcmf_fil_p2p_if_types iftype)
{
struct brcmf_fil_p2p_if_le if_request;
struct brcmf_fil_chan_info_le ci;
u16 chanspec = 11 & WL_CHANSPEC_CHAN_MASK;
int err;

/* we need a default channel */
err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_CHANNEL, &ci, sizeof(ci));
if (!err) {
chanspec = le32_to_cpu(ci.hw_channel) & WL_CHANSPEC_CHAN_MASK;
if (chanspec < CH_MAX_2G_CHANNEL)
chanspec |= WL_CHANSPEC_BAND_2G;
else
chanspec |= WL_CHANSPEC_BAND_5G;
}
chanspec |= WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;

/* fill the firmware request */
memcpy(if_request.addr, ea, ETH_ALEN);
if_request.type = iftype;
if_request.chspec = cpu_to_le16(chanspec);

err = brcmf_fil_iovar_data_set(ifp, "p2p_ifadd", &if_request,
sizeof(if_request));
if (err)
return err;

if (iftype == BRCMF_FIL_P2P_IF_GO) {
/* set station timeout for p2p */
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCB_TIMEOUT,
BRCMF_SCB_TIMEOUT_VALUE);
}
return err;
}

/**
* brcmf_p2p_add_vif() - create a new P2P virtual interface.
*
Expand All @@ -699,16 +739,67 @@ void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
* @type: nl80211 interface type.
* @flags: TBD
* @params: TBD
*
* TODO: not yet supported.
*/
struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{
brcmf_err("enter - not supported yet\n");
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
struct brcmf_cfg80211_vif *vif;
enum brcmf_fil_p2p_if_types iftype;
enum wl_mode mode;
int err;

if (brcmf_cfg80211_vif_event_armed(cfg))
return ERR_PTR(-EBUSY);

brcmf_dbg(INFO, "adding vif \"%s\" (type=%d)\n", name, type);
return ERR_PTR(-EOPNOTSUPP);

switch (type) {
case NL80211_IFTYPE_P2P_CLIENT:
iftype = BRCMF_FIL_P2P_IF_CLIENT;
mode = WL_MODE_BSS;
break;
case NL80211_IFTYPE_P2P_GO:
iftype = BRCMF_FIL_P2P_IF_GO;
mode = WL_MODE_AP;
break;
default:
return ERR_PTR(-EOPNOTSUPP);
}

vif = brcmf_alloc_vif(cfg, type, false);
brcmf_cfg80211_arm_vif_event(cfg, vif);

err = brcmf_p2p_request_p2p_if(ifp, cfg->p2p.int_addr, iftype);
if (err)
goto fail;

/* wait for firmware event */
err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
msecs_to_jiffies(1500));
brcmf_cfg80211_arm_vif_event(cfg, NULL);
if (!err) {
brcmf_err("timeout occurred\n");
err = -EIO;
goto fail;
}

/* interface created in firmware */
ifp = vif->ifp;
if (!ifp) {
brcmf_err("no if pointer provided\n");
err = -ENOENT;
}

strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
brcmf_cfg80211_vif_complete(cfg);
return &ifp->vif->wdev;

fail:
brcmf_free_vif(vif);
return ERR_PTR(err);
}

/**
Expand All @@ -721,9 +812,20 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
*/
int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
{
struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
struct brcmf_cfg80211_vif *vif;
int err;

vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);

if (brcmf_cfg80211_vif_event_armed(cfg))
return -EBUSY;

brcmf_cfg80211_arm_vif_event(cfg, vif);
/* wait for firmware event */
err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL,
msecs_to_jiffies(1500));
brcmf_cfg80211_arm_vif_event(cfg, NULL);
if (wdev->netdev)
brcmf_dbg(INFO, "deleting vif \"%s\"\n", wdev->netdev->name);
else
Expand Down
123 changes: 114 additions & 9 deletions drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -3812,7 +3812,6 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
}

struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
struct net_device *netdev,
enum nl80211_iftype type,
bool pm_block)
{
Expand All @@ -3828,15 +3827,8 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
return ERR_PTR(-ENOMEM);

vif->wdev.wiphy = cfg->wiphy;
vif->wdev.netdev = netdev;
vif->wdev.iftype = type;

if (netdev) {
vif->ifp = netdev_priv(netdev);
netdev->ieee80211_ptr = &vif->wdev;
SET_NETDEV_DEV(netdev, wiphy_dev(cfg->wiphy));
}

vif->mode = brcmf_nl80211_iftype_to_mode(type);
vif->pm_block = pm_block;
vif->roam_off = -1;
Expand Down Expand Up @@ -4195,6 +4187,57 @@ brcmf_notify_mic_status(struct brcmf_if *ifp,
return 0;
}

static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
const struct brcmf_event_msg *e, void *data)
{
struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
struct brcmf_cfg80211_vif *vif;

brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
ifevent->action, ifevent->flags, ifevent->ifidx,
ifevent->bssidx);


mutex_lock(&event->vif_event_lock);
event->action = ifevent->action;
vif = event->vif;

switch (ifevent->action) {
case BRCMF_E_IF_ADD:
/* waiting process may have timed out */
if (!cfg->vif_event.vif)
return -EBADF;

ifp->vif = vif;
vif->ifp = ifp;
vif->wdev.netdev = ifp->ndev;
ifp->ndev->ieee80211_ptr = &vif->wdev;
SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
mutex_unlock(&event->vif_event_lock);
wake_up(&event->vif_wq);

/* waiting process need to set the netdev name */
wait_for_completion(&event->vif_complete);
return brcmf_net_attach(ifp);

case BRCMF_E_IF_DEL:
ifp->vif = NULL;
brcmf_free_vif(vif);
mutex_unlock(&event->vif_event_lock);
/* event may not be upon user request */
if (brcmf_cfg80211_vif_event_armed(cfg))
wake_up(&event->vif_wq);
return 0;

default:
mutex_unlock(&event->vif_event_lock);
break;
}
return -EINVAL;
}

static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
{
conf->frag_threshold = (u32)-1;
Expand Down Expand Up @@ -4226,6 +4269,8 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
brcmf_notify_connect_status);
brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
brcmf_notify_sched_scan_results);
brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
brcmf_notify_vif_event);
}

static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Expand Down Expand Up @@ -4292,6 +4337,13 @@ static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
brcmf_deinit_priv_mem(cfg);
}

static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
{
init_waitqueue_head(&event->vif_wq);
init_completion(&event->vif_complete);
mutex_init(&event->vif_event_lock);
}

struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
struct device *busdev)
{
Expand All @@ -4315,14 +4367,20 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
cfg = wiphy_priv(wiphy);
cfg->wiphy = wiphy;
cfg->pub = drvr;
init_vif_event(&cfg->vif_event);
INIT_LIST_HEAD(&cfg->vif_list);

vif = brcmf_alloc_vif(cfg, ndev, NL80211_IFTYPE_STATION, false);
vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
if (IS_ERR(vif)) {
wiphy_free(wiphy);
return NULL;
}

vif->ifp = ifp;
vif->wdev.netdev = ndev;
ndev->ieee80211_ptr = &vif->wdev;
SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));

err = wl_init_priv(cfg);
if (err) {
brcmf_err("Failed to init iwm_priv (%d)\n", err);
Expand Down Expand Up @@ -4585,3 +4643,50 @@ u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state)
}
return result;
}

static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
u8 action)
{
u8 evt_action;

mutex_lock(&event->vif_event_lock);
evt_action = event->action;
mutex_unlock(&event->vif_event_lock);
return evt_action == action;
}

void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
struct brcmf_cfg80211_vif *vif)
{
struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;

mutex_lock(&event->vif_event_lock);
event->vif = vif;
event->action = 0;
mutex_unlock(&event->vif_event_lock);
}

bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
{
struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
bool armed;

mutex_lock(&event->vif_event_lock);
armed = event->vif != NULL;
mutex_unlock(&event->vif_event_lock);

return armed;
}
int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
u8 action, ulong timeout)
{
struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;

return wait_event_timeout(event->vif_wq,
vif_event_equals(event, action), timeout);
}

void brcmf_cfg80211_vif_complete(struct brcmf_cfg80211_info *cfg)
{
complete(&cfg->vif_event.vif_complete);
}
Loading

0 comments on commit d3c0b63

Please sign in to comment.