2011-07-21 19:43:28

by Daniel Drake

[permalink] [raw]
Subject: [PATCH] libertas: link mesh device to wiphy

The mesh device is now exposed as an interface of the wiphy.
This exposes the mesh device to the cfg80211 interface, allowing
mesh channel selection to be reimplemented, and available to
NetworkManager as it was before.

Some header tweaking was needed in order to implement lbs_mesh_activated().

Signed-off-by: Daniel Drake <[email protected]>
---
drivers/net/wireless/libertas/cfg.c | 36 +++++++++++++++-
drivers/net/wireless/libertas/dev.h | 12 +++++-
drivers/net/wireless/libertas/ethtool.c | 1 +
drivers/net/wireless/libertas/main.c | 14 ++++--
drivers/net/wireless/libertas/mesh.c | 68 ++++++++++++++++++++++---------
drivers/net/wireless/libertas/mesh.h | 27 +++++-------
drivers/net/wireless/libertas/rx.c | 1 +
drivers/net/wireless/libertas/tx.c | 1 +
8 files changed, 117 insertions(+), 43 deletions(-)

Replaces previous patch "libertas: reimplement mesh channel selection"

diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index b456a53..63009c7 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -19,6 +19,7 @@
#include "decl.h"
#include "cfg.h"
#include "cmd.h"
+#include "mesh.h"


#define CHAN2G(_channel, _freq, _flags) { \
@@ -442,13 +443,16 @@ static int lbs_cfg_set_channel(struct wiphy *wiphy,
struct lbs_private *priv = wiphy_priv(wiphy);
int ret = -ENOTSUPP;

- lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d",
- channel->center_freq, channel_type);
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d, type %d",
+ netdev_name(netdev), channel->center_freq, channel_type);

if (channel_type != NL80211_CHAN_NO_HT)
goto out;

- ret = lbs_set_channel(priv, channel->hw_value);
+ if (netdev == priv->mesh_dev)
+ ret = lbs_mesh_set_channel(priv, channel->hw_value);
+ else
+ ret = lbs_set_channel(priv, channel->hw_value);

out:
lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
@@ -1292,6 +1296,9 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
int ret = 0;
u8 preamble = RADIO_PREAMBLE_SHORT;

+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);

if (!sme->bssid) {
@@ -1408,6 +1415,9 @@ static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
struct lbs_private *priv = wiphy_priv(wiphy);
struct cmd_ds_802_11_deauthenticate cmd;

+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);

/* store for lbs_cfg_ret_disconnect() */
@@ -1439,6 +1449,9 @@ static int lbs_cfg_set_default_key(struct wiphy *wiphy,
{
struct lbs_private *priv = wiphy_priv(wiphy);

+ if (netdev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);

if (key_index != priv->wep_tx_key) {
@@ -1460,6 +1473,9 @@ static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev,
u16 key_type;
int ret = 0;

+ if (netdev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);

lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n",
@@ -1603,6 +1619,9 @@ static int lbs_get_survey(struct wiphy *wiphy, struct net_device *dev,
s8 signal, noise;
int ret;

+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
if (idx != 0)
ret = -ENOENT;

@@ -1636,6 +1655,9 @@ static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev,
struct lbs_private *priv = wiphy_priv(wiphy);
int ret = 0;

+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);

switch (type) {
@@ -1959,6 +1981,9 @@ static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_bss *bss;
DECLARE_SSID_BUF(ssid_buf);

+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);

if (!params->channel) {
@@ -1995,6 +2020,9 @@ static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
struct cmd_ds_802_11_ad_hoc_stop cmd;
int ret = 0;

+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
lbs_deb_enter(LBS_DEB_CFG80211);

memset(&cmd, 0, sizeof(cmd));
@@ -2117,6 +2145,8 @@ int lbs_cfg_register(struct lbs_private *priv)
BIT(NL80211_IFTYPE_ADHOC);
if (lbs_rtap_supported(priv))
wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
+ if (lbs_mesh_activated(priv))
+ wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);

wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz;

diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index adb3490..133ff1c 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -6,7 +6,6 @@
#ifndef _LBS_DEV_H_
#define _LBS_DEV_H_

-#include "mesh.h"
#include "defs.h"
#include "host.h"

@@ -22,6 +21,17 @@ struct sleep_params {
uint16_t sp_reserved;
};

+/* Mesh statistics */
+struct lbs_mesh_stats {
+ u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */
+ u32 fwd_unicast_cnt; /* Fwd: Unicast counter */
+ u32 fwd_drop_ttl; /* Fwd: TTL zero */
+ u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */
+ u32 fwd_drop_noroute; /* Fwd: No route to Destination */
+ u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */
+ u32 drop_blind; /* Rx: Dropped by blinding table */
+ u32 tx_failed_cnt; /* Tx: Failed transmissions */
+};

/* Private structure for the MV device */
struct lbs_private {
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c
index 4dfb3bf..885ddc1 100644
--- a/drivers/net/wireless/libertas/ethtool.c
+++ b/drivers/net/wireless/libertas/ethtool.c
@@ -5,6 +5,7 @@

#include "decl.h"
#include "cmd.h"
+#include "mesh.h"


static void lbs_ethtool_get_drvinfo(struct net_device *dev,
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 94652c5..ee28ae5 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -23,6 +23,7 @@
#include "cfg.h"
#include "debugfs.h"
#include "cmd.h"
+#include "mesh.h"

#define DRIVER_RELEASE_VERSION "323.p0"
const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION
@@ -950,17 +951,20 @@ int lbs_start_card(struct lbs_private *priv)
if (ret)
goto done;

+ if (!lbs_disablemesh)
+ lbs_init_mesh(priv);
+ else
+ pr_info("%s: mesh disabled\n", dev->name);
+
if (lbs_cfg_register(priv)) {
pr_err("cannot register device\n");
goto done;
}

- lbs_update_channel(priv);
+ if (lbs_mesh_activated(priv))
+ lbs_start_mesh(priv);

- if (!lbs_disablemesh)
- lbs_init_mesh(priv);
- else
- pr_info("%s: mesh disabled\n", dev->name);
+ lbs_update_channel(priv);

lbs_debugfs_init_one(priv, dev);

diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c
index be72c08..2a635d2 100644
--- a/drivers/net/wireless/libertas/mesh.c
+++ b/drivers/net/wireless/libertas/mesh.c
@@ -129,6 +129,19 @@ static int lbs_mesh_config(struct lbs_private *priv, uint16_t action,
return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
}

+int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)
+{
+ return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel);
+}
+
+static uint16_t lbs_mesh_get_channel(struct lbs_private *priv)
+{
+ struct wireless_dev *mesh_wdev = priv->mesh_dev->ieee80211_ptr;
+ if (mesh_wdev->channel)
+ return mesh_wdev->channel->hw_value;
+ else
+ return 1;
+}

/***************************************************************************
* Mesh sysfs support
@@ -812,7 +825,6 @@ static void lbs_persist_config_remove(struct net_device *dev)
*/
int lbs_init_mesh(struct lbs_private *priv)
{
- struct net_device *dev = priv->dev;
int ret = 0;

lbs_deb_enter(LBS_DEB_MESH);
@@ -837,11 +849,9 @@ int lbs_init_mesh(struct lbs_private *priv)
useful */

priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->channel)) {
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) {
priv->mesh_tlv = TLV_TYPE_MESH_ID;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->channel))
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
priv->mesh_tlv = 0;
}
} else
@@ -851,23 +861,16 @@ int lbs_init_mesh(struct lbs_private *priv)
* 0x100+37; Do not invoke command with old TLV.
*/
priv->mesh_tlv = TLV_TYPE_MESH_ID;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->channel))
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
priv->mesh_tlv = 0;
}

/* Stop meshing until interface is brought up */
- lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, priv->channel);
+ lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1);

if (priv->mesh_tlv) {
sprintf(priv->mesh_ssid, "mesh");
priv->mesh_ssid_len = 4;
-
- lbs_add_mesh(priv);
-
- if (device_create_file(&dev->dev, &dev_attr_lbs_mesh))
- netdev_err(dev, "cannot register lbs_mesh attribute\n");
-
ret = 1;
}

@@ -875,6 +878,13 @@ int lbs_init_mesh(struct lbs_private *priv)
return ret;
}

+void lbs_start_mesh(struct lbs_private *priv)
+{
+ lbs_add_mesh(priv);
+
+ if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh))
+ netdev_err(priv->dev, "cannot register lbs_mesh attribute\n");
+}

int lbs_deinit_mesh(struct lbs_private *priv)
{
@@ -904,7 +914,8 @@ static int lbs_mesh_stop(struct net_device *dev)
struct lbs_private *priv = dev->ml_priv;

lbs_deb_enter(LBS_DEB_MESH);
- lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, priv->channel);
+ lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
+ lbs_mesh_get_channel(priv));

spin_lock_irq(&priv->driver_lock);

@@ -947,7 +958,8 @@ static int lbs_mesh_dev_open(struct net_device *dev)

spin_unlock_irq(&priv->driver_lock);

- ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, priv->channel);
+ ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
+ lbs_mesh_get_channel(priv));

out:
lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
@@ -971,18 +983,32 @@ static const struct net_device_ops mesh_netdev_ops = {
static int lbs_add_mesh(struct lbs_private *priv)
{
struct net_device *mesh_dev = NULL;
+ struct wireless_dev *mesh_wdev;
int ret = 0;

lbs_deb_enter(LBS_DEB_MESH);

/* Allocate a virtual mesh device */
+ mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ if (!mesh_wdev) {
+ lbs_deb_mesh("init mshX wireless device failed\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+
mesh_dev = alloc_netdev(0, "msh%d", ether_setup);
if (!mesh_dev) {
lbs_deb_mesh("init mshX device failed\n");
ret = -ENOMEM;
- goto done;
+ goto err_free_wdev;
}
+
+ mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT;
+ mesh_wdev->wiphy = priv->wdev->wiphy;
+ mesh_wdev->netdev = mesh_dev;
+
mesh_dev->ml_priv = priv;
+ mesh_dev->ieee80211_ptr = mesh_wdev;
priv->mesh_dev = mesh_dev;

mesh_dev->netdev_ops = &mesh_netdev_ops;
@@ -996,7 +1022,7 @@ static int lbs_add_mesh(struct lbs_private *priv)
ret = register_netdev(mesh_dev);
if (ret) {
pr_err("cannot register mshX virtual interface\n");
- goto err_free;
+ goto err_free_netdev;
}

ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
@@ -1012,9 +1038,12 @@ static int lbs_add_mesh(struct lbs_private *priv)
err_unregister:
unregister_netdev(mesh_dev);

-err_free:
+err_free_netdev:
free_netdev(mesh_dev);

+err_free_wdev:
+ kfree(mesh_wdev);
+
done:
lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
return ret;
@@ -1035,6 +1064,7 @@ void lbs_remove_mesh(struct lbs_private *priv)
lbs_persist_config_remove(mesh_dev);
unregister_netdev(mesh_dev);
priv->mesh_dev = NULL;
+ kfree(mesh_dev->ieee80211_ptr);
free_netdev(mesh_dev);
lbs_deb_leave(LBS_DEB_MESH);
}
diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/libertas/mesh.h
index 5014491..6603f34 100644
--- a/drivers/net/wireless/libertas/mesh.h
+++ b/drivers/net/wireless/libertas/mesh.h
@@ -9,30 +9,25 @@
#include <net/lib80211.h>

#include "host.h"
+#include "dev.h"

#ifdef CONFIG_LIBERTAS_MESH

-/* Mesh statistics */
-struct lbs_mesh_stats {
- u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */
- u32 fwd_unicast_cnt; /* Fwd: Unicast counter */
- u32 fwd_drop_ttl; /* Fwd: TTL zero */
- u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */
- u32 fwd_drop_noroute; /* Fwd: No route to Destination */
- u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */
- u32 drop_blind; /* Rx: Dropped by blinding table */
- u32 tx_failed_cnt; /* Tx: Failed transmissions */
-};
-
-
struct net_device;
-struct lbs_private;

int lbs_init_mesh(struct lbs_private *priv);
+void lbs_start_mesh(struct lbs_private *priv);
int lbs_deinit_mesh(struct lbs_private *priv);

void lbs_remove_mesh(struct lbs_private *priv);

+static inline bool lbs_mesh_activated(struct lbs_private *priv)
+{
+ /* Mesh SSID is only programmed after successful init */
+ return priv->mesh_ssid_len != 0;
+}
+
+int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel);

/* Sending / Receiving */

@@ -67,11 +62,13 @@ void lbs_mesh_ethtool_get_strings(struct net_device *dev,

#define lbs_init_mesh(priv)
#define lbs_deinit_mesh(priv)
+#define lbs_start_mesh(priv)
#define lbs_add_mesh(priv)
#define lbs_remove_mesh(priv)
#define lbs_mesh_set_dev(priv, dev, rxpd) (dev)
#define lbs_mesh_set_txpd(priv, dev, txpd)
-#define lbs_mesh_config(priv, enable, chan)
+#define lbs_mesh_set_channel(priv, channel) (0)
+#define lbs_mesh_activated(priv) (false)

#endif

diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c
index bfb8898..62e10ee 100644
--- a/drivers/net/wireless/libertas/rx.c
+++ b/drivers/net/wireless/libertas/rx.c
@@ -15,6 +15,7 @@
#include "radiotap.h"
#include "decl.h"
#include "dev.h"
+#include "mesh.h"

struct eth803hdr {
u8 dest_addr[6];
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c
index a6e8513..8f12752 100644
--- a/drivers/net/wireless/libertas/tx.c
+++ b/drivers/net/wireless/libertas/tx.c
@@ -12,6 +12,7 @@
#include "decl.h"
#include "defs.h"
#include "dev.h"
+#include "mesh.h"

/**
* convert_radiotap_rate_to_mv - converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE
--
1.7.6



2011-07-27 19:11:15

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH] libertas: link mesh device to wiphy

On Thu, 2011-07-21 at 20:43 +0100, Daniel Drake wrote:
> The mesh device is now exposed as an interface of the wiphy.
> This exposes the mesh device to the cfg80211 interface, allowing
> mesh channel selection to be reimplemented, and available to
> NetworkManager as it was before.
>
> Some header tweaking was needed in order to implement lbs_mesh_activated().
>
> Signed-off-by: Daniel Drake <[email protected]>

Acked-by: Dan Williams <[email protected]>

> ---
> drivers/net/wireless/libertas/cfg.c | 36 +++++++++++++++-
> drivers/net/wireless/libertas/dev.h | 12 +++++-
> drivers/net/wireless/libertas/ethtool.c | 1 +
> drivers/net/wireless/libertas/main.c | 14 ++++--
> drivers/net/wireless/libertas/mesh.c | 68 ++++++++++++++++++++++---------
> drivers/net/wireless/libertas/mesh.h | 27 +++++-------
> drivers/net/wireless/libertas/rx.c | 1 +
> drivers/net/wireless/libertas/tx.c | 1 +
> 8 files changed, 117 insertions(+), 43 deletions(-)
>
> Replaces previous patch "libertas: reimplement mesh channel selection"
>
> diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
> index b456a53..63009c7 100644
> --- a/drivers/net/wireless/libertas/cfg.c
> +++ b/drivers/net/wireless/libertas/cfg.c
> @@ -19,6 +19,7 @@
> #include "decl.h"
> #include "cfg.h"
> #include "cmd.h"
> +#include "mesh.h"
>
>
> #define CHAN2G(_channel, _freq, _flags) { \
> @@ -442,13 +443,16 @@ static int lbs_cfg_set_channel(struct wiphy *wiphy,
> struct lbs_private *priv = wiphy_priv(wiphy);
> int ret = -ENOTSUPP;
>
> - lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d",
> - channel->center_freq, channel_type);
> + lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d, type %d",
> + netdev_name(netdev), channel->center_freq, channel_type);
>
> if (channel_type != NL80211_CHAN_NO_HT)
> goto out;
>
> - ret = lbs_set_channel(priv, channel->hw_value);
> + if (netdev == priv->mesh_dev)
> + ret = lbs_mesh_set_channel(priv, channel->hw_value);
> + else
> + ret = lbs_set_channel(priv, channel->hw_value);
>
> out:
> lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
> @@ -1292,6 +1296,9 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
> int ret = 0;
> u8 preamble = RADIO_PREAMBLE_SHORT;
>
> + if (dev == priv->mesh_dev)
> + return -EOPNOTSUPP;
> +
> lbs_deb_enter(LBS_DEB_CFG80211);
>
> if (!sme->bssid) {
> @@ -1408,6 +1415,9 @@ static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
> struct lbs_private *priv = wiphy_priv(wiphy);
> struct cmd_ds_802_11_deauthenticate cmd;
>
> + if (dev == priv->mesh_dev)
> + return -EOPNOTSUPP;
> +
> lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);
>
> /* store for lbs_cfg_ret_disconnect() */
> @@ -1439,6 +1449,9 @@ static int lbs_cfg_set_default_key(struct wiphy *wiphy,
> {
> struct lbs_private *priv = wiphy_priv(wiphy);
>
> + if (netdev == priv->mesh_dev)
> + return -EOPNOTSUPP;
> +
> lbs_deb_enter(LBS_DEB_CFG80211);
>
> if (key_index != priv->wep_tx_key) {
> @@ -1460,6 +1473,9 @@ static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev,
> u16 key_type;
> int ret = 0;
>
> + if (netdev == priv->mesh_dev)
> + return -EOPNOTSUPP;
> +
> lbs_deb_enter(LBS_DEB_CFG80211);
>
> lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n",
> @@ -1603,6 +1619,9 @@ static int lbs_get_survey(struct wiphy *wiphy, struct net_device *dev,
> s8 signal, noise;
> int ret;
>
> + if (dev == priv->mesh_dev)
> + return -EOPNOTSUPP;
> +
> if (idx != 0)
> ret = -ENOENT;
>
> @@ -1636,6 +1655,9 @@ static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev,
> struct lbs_private *priv = wiphy_priv(wiphy);
> int ret = 0;
>
> + if (dev == priv->mesh_dev)
> + return -EOPNOTSUPP;
> +
> lbs_deb_enter(LBS_DEB_CFG80211);
>
> switch (type) {
> @@ -1959,6 +1981,9 @@ static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev,
> struct cfg80211_bss *bss;
> DECLARE_SSID_BUF(ssid_buf);
>
> + if (dev == priv->mesh_dev)
> + return -EOPNOTSUPP;
> +
> lbs_deb_enter(LBS_DEB_CFG80211);
>
> if (!params->channel) {
> @@ -1995,6 +2020,9 @@ static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
> struct cmd_ds_802_11_ad_hoc_stop cmd;
> int ret = 0;
>
> + if (dev == priv->mesh_dev)
> + return -EOPNOTSUPP;
> +
> lbs_deb_enter(LBS_DEB_CFG80211);
>
> memset(&cmd, 0, sizeof(cmd));
> @@ -2117,6 +2145,8 @@ int lbs_cfg_register(struct lbs_private *priv)
> BIT(NL80211_IFTYPE_ADHOC);
> if (lbs_rtap_supported(priv))
> wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
> + if (lbs_mesh_activated(priv))
> + wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
>
> wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz;
>
> diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
> index adb3490..133ff1c 100644
> --- a/drivers/net/wireless/libertas/dev.h
> +++ b/drivers/net/wireless/libertas/dev.h
> @@ -6,7 +6,6 @@
> #ifndef _LBS_DEV_H_
> #define _LBS_DEV_H_
>
> -#include "mesh.h"
> #include "defs.h"
> #include "host.h"
>
> @@ -22,6 +21,17 @@ struct sleep_params {
> uint16_t sp_reserved;
> };
>
> +/* Mesh statistics */
> +struct lbs_mesh_stats {
> + u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */
> + u32 fwd_unicast_cnt; /* Fwd: Unicast counter */
> + u32 fwd_drop_ttl; /* Fwd: TTL zero */
> + u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */
> + u32 fwd_drop_noroute; /* Fwd: No route to Destination */
> + u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */
> + u32 drop_blind; /* Rx: Dropped by blinding table */
> + u32 tx_failed_cnt; /* Tx: Failed transmissions */
> +};
>
> /* Private structure for the MV device */
> struct lbs_private {
> diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c
> index 4dfb3bf..885ddc1 100644
> --- a/drivers/net/wireless/libertas/ethtool.c
> +++ b/drivers/net/wireless/libertas/ethtool.c
> @@ -5,6 +5,7 @@
>
> #include "decl.h"
> #include "cmd.h"
> +#include "mesh.h"
>
>
> static void lbs_ethtool_get_drvinfo(struct net_device *dev,
> diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
> index 94652c5..ee28ae5 100644
> --- a/drivers/net/wireless/libertas/main.c
> +++ b/drivers/net/wireless/libertas/main.c
> @@ -23,6 +23,7 @@
> #include "cfg.h"
> #include "debugfs.h"
> #include "cmd.h"
> +#include "mesh.h"
>
> #define DRIVER_RELEASE_VERSION "323.p0"
> const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION
> @@ -950,17 +951,20 @@ int lbs_start_card(struct lbs_private *priv)
> if (ret)
> goto done;
>
> + if (!lbs_disablemesh)
> + lbs_init_mesh(priv);
> + else
> + pr_info("%s: mesh disabled\n", dev->name);
> +
> if (lbs_cfg_register(priv)) {
> pr_err("cannot register device\n");
> goto done;
> }
>
> - lbs_update_channel(priv);
> + if (lbs_mesh_activated(priv))
> + lbs_start_mesh(priv);
>
> - if (!lbs_disablemesh)
> - lbs_init_mesh(priv);
> - else
> - pr_info("%s: mesh disabled\n", dev->name);
> + lbs_update_channel(priv);
>
> lbs_debugfs_init_one(priv, dev);
>
> diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c
> index be72c08..2a635d2 100644
> --- a/drivers/net/wireless/libertas/mesh.c
> +++ b/drivers/net/wireless/libertas/mesh.c
> @@ -129,6 +129,19 @@ static int lbs_mesh_config(struct lbs_private *priv, uint16_t action,
> return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
> }
>
> +int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)
> +{
> + return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel);
> +}
> +
> +static uint16_t lbs_mesh_get_channel(struct lbs_private *priv)
> +{
> + struct wireless_dev *mesh_wdev = priv->mesh_dev->ieee80211_ptr;
> + if (mesh_wdev->channel)
> + return mesh_wdev->channel->hw_value;
> + else
> + return 1;
> +}
>
> /***************************************************************************
> * Mesh sysfs support
> @@ -812,7 +825,6 @@ static void lbs_persist_config_remove(struct net_device *dev)
> */
> int lbs_init_mesh(struct lbs_private *priv)
> {
> - struct net_device *dev = priv->dev;
> int ret = 0;
>
> lbs_deb_enter(LBS_DEB_MESH);
> @@ -837,11 +849,9 @@ int lbs_init_mesh(struct lbs_private *priv)
> useful */
>
> priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
> - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
> - priv->channel)) {
> + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) {
> priv->mesh_tlv = TLV_TYPE_MESH_ID;
> - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
> - priv->channel))
> + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
> priv->mesh_tlv = 0;
> }
> } else
> @@ -851,23 +861,16 @@ int lbs_init_mesh(struct lbs_private *priv)
> * 0x100+37; Do not invoke command with old TLV.
> */
> priv->mesh_tlv = TLV_TYPE_MESH_ID;
> - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
> - priv->channel))
> + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
> priv->mesh_tlv = 0;
> }
>
> /* Stop meshing until interface is brought up */
> - lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, priv->channel);
> + lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1);
>
> if (priv->mesh_tlv) {
> sprintf(priv->mesh_ssid, "mesh");
> priv->mesh_ssid_len = 4;
> -
> - lbs_add_mesh(priv);
> -
> - if (device_create_file(&dev->dev, &dev_attr_lbs_mesh))
> - netdev_err(dev, "cannot register lbs_mesh attribute\n");
> -
> ret = 1;
> }
>
> @@ -875,6 +878,13 @@ int lbs_init_mesh(struct lbs_private *priv)
> return ret;
> }
>
> +void lbs_start_mesh(struct lbs_private *priv)
> +{
> + lbs_add_mesh(priv);
> +
> + if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh))
> + netdev_err(priv->dev, "cannot register lbs_mesh attribute\n");
> +}
>
> int lbs_deinit_mesh(struct lbs_private *priv)
> {
> @@ -904,7 +914,8 @@ static int lbs_mesh_stop(struct net_device *dev)
> struct lbs_private *priv = dev->ml_priv;
>
> lbs_deb_enter(LBS_DEB_MESH);
> - lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, priv->channel);
> + lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
> + lbs_mesh_get_channel(priv));
>
> spin_lock_irq(&priv->driver_lock);
>
> @@ -947,7 +958,8 @@ static int lbs_mesh_dev_open(struct net_device *dev)
>
> spin_unlock_irq(&priv->driver_lock);
>
> - ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, priv->channel);
> + ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
> + lbs_mesh_get_channel(priv));
>
> out:
> lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
> @@ -971,18 +983,32 @@ static const struct net_device_ops mesh_netdev_ops = {
> static int lbs_add_mesh(struct lbs_private *priv)
> {
> struct net_device *mesh_dev = NULL;
> + struct wireless_dev *mesh_wdev;
> int ret = 0;
>
> lbs_deb_enter(LBS_DEB_MESH);
>
> /* Allocate a virtual mesh device */
> + mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
> + if (!mesh_wdev) {
> + lbs_deb_mesh("init mshX wireless device failed\n");
> + ret = -ENOMEM;
> + goto done;
> + }
> +
> mesh_dev = alloc_netdev(0, "msh%d", ether_setup);
> if (!mesh_dev) {
> lbs_deb_mesh("init mshX device failed\n");
> ret = -ENOMEM;
> - goto done;
> + goto err_free_wdev;
> }
> +
> + mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT;
> + mesh_wdev->wiphy = priv->wdev->wiphy;
> + mesh_wdev->netdev = mesh_dev;
> +
> mesh_dev->ml_priv = priv;
> + mesh_dev->ieee80211_ptr = mesh_wdev;
> priv->mesh_dev = mesh_dev;
>
> mesh_dev->netdev_ops = &mesh_netdev_ops;
> @@ -996,7 +1022,7 @@ static int lbs_add_mesh(struct lbs_private *priv)
> ret = register_netdev(mesh_dev);
> if (ret) {
> pr_err("cannot register mshX virtual interface\n");
> - goto err_free;
> + goto err_free_netdev;
> }
>
> ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
> @@ -1012,9 +1038,12 @@ static int lbs_add_mesh(struct lbs_private *priv)
> err_unregister:
> unregister_netdev(mesh_dev);
>
> -err_free:
> +err_free_netdev:
> free_netdev(mesh_dev);
>
> +err_free_wdev:
> + kfree(mesh_wdev);
> +
> done:
> lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
> return ret;
> @@ -1035,6 +1064,7 @@ void lbs_remove_mesh(struct lbs_private *priv)
> lbs_persist_config_remove(mesh_dev);
> unregister_netdev(mesh_dev);
> priv->mesh_dev = NULL;
> + kfree(mesh_dev->ieee80211_ptr);
> free_netdev(mesh_dev);
> lbs_deb_leave(LBS_DEB_MESH);
> }
> diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/libertas/mesh.h
> index 5014491..6603f34 100644
> --- a/drivers/net/wireless/libertas/mesh.h
> +++ b/drivers/net/wireless/libertas/mesh.h
> @@ -9,30 +9,25 @@
> #include <net/lib80211.h>
>
> #include "host.h"
> +#include "dev.h"
>
> #ifdef CONFIG_LIBERTAS_MESH
>
> -/* Mesh statistics */
> -struct lbs_mesh_stats {
> - u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */
> - u32 fwd_unicast_cnt; /* Fwd: Unicast counter */
> - u32 fwd_drop_ttl; /* Fwd: TTL zero */
> - u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */
> - u32 fwd_drop_noroute; /* Fwd: No route to Destination */
> - u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */
> - u32 drop_blind; /* Rx: Dropped by blinding table */
> - u32 tx_failed_cnt; /* Tx: Failed transmissions */
> -};
> -
> -
> struct net_device;
> -struct lbs_private;
>
> int lbs_init_mesh(struct lbs_private *priv);
> +void lbs_start_mesh(struct lbs_private *priv);
> int lbs_deinit_mesh(struct lbs_private *priv);
>
> void lbs_remove_mesh(struct lbs_private *priv);
>
> +static inline bool lbs_mesh_activated(struct lbs_private *priv)
> +{
> + /* Mesh SSID is only programmed after successful init */
> + return priv->mesh_ssid_len != 0;
> +}
> +
> +int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel);
>
> /* Sending / Receiving */
>
> @@ -67,11 +62,13 @@ void lbs_mesh_ethtool_get_strings(struct net_device *dev,
>
> #define lbs_init_mesh(priv)
> #define lbs_deinit_mesh(priv)
> +#define lbs_start_mesh(priv)
> #define lbs_add_mesh(priv)
> #define lbs_remove_mesh(priv)
> #define lbs_mesh_set_dev(priv, dev, rxpd) (dev)
> #define lbs_mesh_set_txpd(priv, dev, txpd)
> -#define lbs_mesh_config(priv, enable, chan)
> +#define lbs_mesh_set_channel(priv, channel) (0)
> +#define lbs_mesh_activated(priv) (false)
>
> #endif
>
> diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c
> index bfb8898..62e10ee 100644
> --- a/drivers/net/wireless/libertas/rx.c
> +++ b/drivers/net/wireless/libertas/rx.c
> @@ -15,6 +15,7 @@
> #include "radiotap.h"
> #include "decl.h"
> #include "dev.h"
> +#include "mesh.h"
>
> struct eth803hdr {
> u8 dest_addr[6];
> diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c
> index a6e8513..8f12752 100644
> --- a/drivers/net/wireless/libertas/tx.c
> +++ b/drivers/net/wireless/libertas/tx.c
> @@ -12,6 +12,7 @@
> #include "decl.h"
> #include "defs.h"
> #include "dev.h"
> +#include "mesh.h"
>
> /**
> * convert_radiotap_rate_to_mv - converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE