Skip to content

Commit

Permalink
staging: brcm80211: Fixed ibss join/leave functionality in brcmfmac d…
Browse files Browse the repository at this point in the history
…river

IBSS functionality is broken in fullmac driver, which is fixed with this patch

Cc: devel@linuxdriverproject.org
Cc: linux-wireless@vger.kernel.org
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Brett Rudley <brudley@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Sukesh Srikakula authored and Greg Kroah-Hartman committed May 17, 2011
1 parent e494632 commit 69274f0
Showing 1 changed file with 206 additions and 73 deletions.
279 changes: 206 additions & 73 deletions drivers/staging/brcm80211/brcmfmac/wl_cfg80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -598,10 +598,10 @@ wl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
struct wl_priv *wl = wiphy_to_wl(wiphy);
struct wireless_dev *wdev;
s32 infra = 0;
s32 ap = 0;
s32 err = 0;

CHECK_SYS_UP();

switch (type) {
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_WDS:
Expand All @@ -610,32 +610,32 @@ wl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
return -EOPNOTSUPP;
case NL80211_IFTYPE_ADHOC:
wl->conf->mode = WL_MODE_IBSS;
infra = 0;
break;
case NL80211_IFTYPE_STATION:
wl->conf->mode = WL_MODE_BSS;
infra = 1;
break;
default:
return -EINVAL;
err = -EINVAL;
goto done;
}

infra = cpu_to_le32(infra);
ap = cpu_to_le32(ap);
wdev = ndev->ieee80211_ptr;
wdev->iftype = type;
WL_DBG("%s : ap (%d), infra (%d)\n", ndev->name, ap, infra);
err = wl_dev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra));
if (unlikely(err)) {
WL_ERR("WLC_SET_INFRA error (%d)\n", err);
return err;
}
err = wl_dev_ioctl(ndev, WLC_SET_AP, &ap, sizeof(ap));
if (unlikely(err)) {
WL_ERR("WLC_SET_AP error (%d)\n", err);
return err;
err = -EAGAIN;
} else {
wdev = ndev->ieee80211_ptr;
wdev->iftype = type;
}

/* -EINPROGRESS: Call commit handler */
return -EINPROGRESS;
WL_INFO("IF Type = %s\n",
(wl->conf->mode == WL_MODE_IBSS) ? "Adhoc" : "Infra");

done:
return err;
}

static void wl_iscan_prep(struct wl_scan_params *params, struct wlc_ssid *ssid)
Expand Down Expand Up @@ -983,68 +983,137 @@ wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ibss_params *params)
{
struct wl_priv *wl = wiphy_to_wl(wiphy);
struct cfg80211_bss *bss;
struct ieee80211_channel *chan;
struct wl_join_params join_params;
struct cfg80211_ssid ssid;
s32 scan_retry = 0;
size_t join_params_size = 0;
s32 err = 0;
s32 wsec = 0;
s32 bcnprd;

CHECK_SYS_UP();
if (params->bssid) {
WL_ERR("Invalid bssid\n");

if (params->ssid)
WL_DBG("SSID: %s\n", params->ssid);
else {
WL_DBG("SSID: NULL, Not supported\n");
return -EOPNOTSUPP;
}
bss = cfg80211_get_ibss(wiphy, NULL, params->ssid, params->ssid_len);
if (!bss) {
memcpy(ssid.ssid, params->ssid, params->ssid_len);
ssid.ssid_len = params->ssid_len;
do {
if (unlikely
(__wl_cfg80211_scan(wiphy, dev, NULL, &ssid) ==
-EBUSY)) {
wl_delay(150);
} else {
break;
}
} while (++scan_retry < WL_SCAN_RETRY_MAX);
rtnl_unlock(); /* to allow scan_inform to paropagate
to cfg80211 plane */
schedule_timeout_interruptible(4 * HZ); /* wait 4 secons
till scan done.... */
rtnl_lock();
bss = cfg80211_get_ibss(wiphy, NULL,
params->ssid, params->ssid_len);

if (params->bssid)
WL_DBG("BSSID: %02X %02X %02X %02X %02X %02X\n",
params->bssid[0], params->bssid[1], params->bssid[2],
params->bssid[3], params->bssid[4], params->bssid[5]);
else
WL_DBG("No BSSID specified\n");

if (params->channel)
WL_DBG("channel: %d\n", params->channel->center_freq);
else
WL_DBG("no channel specified\n");

if (params->channel_fixed)
WL_DBG("fixed channel required\n");
else
WL_DBG("no fixed channel required\n");

if (params->ie && params->ie_len)
WL_DBG("ie len: %d\n", params->ie_len);
else
WL_DBG("no ie specified\n");

if (params->beacon_interval)
WL_DBG("beacon interval: %d\n", params->beacon_interval);
else
WL_DBG("no beacon interval specified\n");

if (params->basic_rates)
WL_DBG("basic rates: %08X\n", params->basic_rates);
else
WL_DBG("no basic rates specified\n");

if (params->privacy)
WL_DBG("privacy required\n");
else
WL_DBG("no privacy required\n");

/* Configure Privacy for starter */
if (params->privacy)
wsec |= WEP_ENABLED;

err = wl_dev_intvar_set(dev, "wsec", wsec);
if (unlikely(err)) {
WL_ERR("wsec failed (%d)\n", err);
goto done;
}

/* Configure Beacon Interval for starter */
if (params->beacon_interval)
bcnprd = cpu_to_le32(params->beacon_interval);
else
bcnprd = cpu_to_le32(100);

err = wl_dev_ioctl(dev, WLC_SET_BCNPRD, &bcnprd, sizeof(bcnprd));
if (unlikely(err)) {
WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err);
goto done;
}
if (bss) {
wl->ibss_starter = false;
WL_DBG("Found IBSS\n");

/* Configure required join parameter */
memset(&join_params, 0, sizeof(wl_join_params_t));

/* SSID */
join_params.ssid.SSID_len =
(params->ssid_len > 32) ? 32 : params->ssid_len;
memcpy(join_params.ssid.SSID, params->ssid, join_params.ssid.SSID_len);
join_params.ssid.SSID_len = cpu_to_le32(join_params.ssid.SSID_len);
join_params_size = sizeof(join_params.ssid);
wl_update_prof(wl, NULL, &join_params.ssid, WL_PROF_SSID);

/* BSSID */
if (params->bssid) {
memcpy(join_params.params.bssid, params->bssid, ETH_ALEN);
join_params_size =
sizeof(join_params.ssid) + WL_ASSOC_PARAMS_FIXED_SIZE;
} else {
wl->ibss_starter = true;
memcpy(join_params.params.bssid, ether_bcast, ETH_ALEN);
}
chan = params->channel;
if (chan)
wl->channel = ieee80211_frequency_to_channel(chan->center_freq);
/*
** Join with specific BSSID and cached SSID
** If SSID is zero join based on BSSID only
*/
memset(&join_params, 0, sizeof(join_params));
memcpy((void *)join_params.ssid.SSID, (void *)params->ssid,
params->ssid_len);
join_params.ssid.SSID_len = cpu_to_le32(params->ssid_len);
if (params->bssid)
memcpy(&join_params.params.bssid, params->bssid,
ETH_ALEN);
else
memset(&join_params.params.bssid, 0, ETH_ALEN);
wl_update_prof(wl, NULL, &join_params.params.bssid, WL_PROF_BSSID);

/* Channel */
if (params->channel) {
u32 target_channel;

wl->channel =
ieee80211_frequency_to_channel(
params->channel->center_freq);
if (params->channel_fixed) {
/* adding chanspec */
wl_ch_to_chanspec(wl->channel,
&join_params, &join_params_size);
}

err = wl_dev_ioctl(dev, WLC_SET_SSID, &join_params,
sizeof(join_params));
/* set channel for starter */
target_channel = cpu_to_le32(wl->channel);
err = wl_dev_ioctl(dev, WLC_SET_CHANNEL,
&target_channel, sizeof(target_channel));
if (unlikely(err)) {
WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err);
goto done;
}
} else
wl->channel = 0;

wl->ibss_starter = false;


err = wl_dev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size);
if (unlikely(err)) {
WL_ERR("Error (%d)\n", err);
return err;
WL_ERR("WLC_SET_SSID failed (%d)\n", err);
goto done;
}

set_bit(WL_STATUS_CONNECTING, &wl->status);

done:
return err;
}

Expand Down Expand Up @@ -2367,6 +2436,76 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi)
return err;
}

static s32
wl_inform_ibss(struct wl_priv *wl, struct net_device *dev, const u8 *bssid)
{
struct wiphy *wiphy = wl_to_wiphy(wl);
struct ieee80211_channel *notify_channel;
struct wl_bss_info *bi = NULL;
struct ieee80211_supported_band *band;
u8 *buf = NULL;
s32 err = 0;
u16 channel;
u32 freq;
u64 notify_timestamp;
u16 notify_capability;
u16 notify_interval;
u8 *notify_ie;
size_t notify_ielen;
s32 notify_signal;

buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
if (buf == NULL) {
WL_ERR("kzalloc() failed\n");
err = -ENOMEM;
goto CleanUp;
}

*(u32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);

err = wl_dev_ioctl(dev, WLC_GET_BSS_INFO, buf, WL_BSS_INFO_MAX);
if (unlikely(err)) {
WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err);
goto CleanUp;
}

bi = (wl_bss_info_t *)(buf + 4);

channel = bi->ctl_ch ? bi->ctl_ch :
CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));

if (channel <= CH_MAX_2G_CHANNEL)
band = wiphy->bands[IEEE80211_BAND_2GHZ];
else
band = wiphy->bands[IEEE80211_BAND_5GHZ];

freq = ieee80211_channel_to_frequency(channel, band->band);
notify_channel = ieee80211_get_channel(wiphy, freq);

notify_timestamp = jiffies_to_msecs(jiffies)*1000; /* uSec */
notify_capability = le16_to_cpu(bi->capability);
notify_interval = le16_to_cpu(bi->beacon_period);
notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
notify_ielen = le16_to_cpu(bi->ie_length);
notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;

WL_DBG("channel: %d(%d)\n", channel, freq);
WL_DBG("capability: %X\n", notify_capability);
WL_DBG("beacon interval: %d\n", notify_interval);
WL_DBG("signal: %d\n", notify_signal);
WL_DBG("notify_timestamp: %#018llx\n", notify_timestamp);

cfg80211_inform_bss(wiphy, notify_channel, bssid,
notify_timestamp, notify_capability, notify_interval,
notify_ie, notify_ielen, notify_signal, GFP_KERNEL);

CleanUp:

kfree(buf);

return err;
}

static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e)
{
u32 event = be32_to_cpu(e->event_type);
Expand Down Expand Up @@ -2424,6 +2563,7 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev,
if (wl_is_ibssmode(wl)) {
wl_update_prof(wl, NULL, (void *)e->addr,
WL_PROF_BSSID);
wl_inform_ibss(wl, ndev, e->addr);
cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
clear_bit(WL_STATUS_CONNECTING, &wl->status);
set_bit(WL_STATUS_CONNECTED, &wl->status);
Expand Down Expand Up @@ -3405,7 +3545,6 @@ struct sdio_func *wl_cfg80211_get_sdio_func(void)
static s32 wl_dongle_mode(struct net_device *ndev, s32 iftype)
{
s32 infra = 0;
s32 ap = 0;
s32 err = 0;

switch (iftype) {
Expand All @@ -3416,6 +3555,7 @@ static s32 wl_dongle_mode(struct net_device *ndev, s32 iftype)
err = -EINVAL;
return err;
case NL80211_IFTYPE_ADHOC:
infra = 0;
break;
case NL80211_IFTYPE_STATION:
infra = 1;
Expand All @@ -3426,20 +3566,13 @@ static s32 wl_dongle_mode(struct net_device *ndev, s32 iftype)
return err;
}
infra = cpu_to_le32(infra);
ap = cpu_to_le32(ap);
WL_DBG("%s ap (%d), infra (%d)\n", ndev->name, ap, infra);
err = wl_dev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra));
if (unlikely(err)) {
WL_ERR("WLC_SET_INFRA error (%d)\n", err);
return err;
}
err = wl_dev_ioctl(ndev, WLC_SET_AP, &ap, sizeof(ap));
if (unlikely(err)) {
WL_ERR("WLC_SET_AP error (%d)\n", err);
return err;
}

return -EINPROGRESS;
return 0;
}

#ifndef EMBEDDED_PLATFORM
Expand Down

0 comments on commit 69274f0

Please sign in to comment.