diff options
Diffstat (limited to 'drivers/net/wireless/libertas')
23 files changed, 1667 insertions, 1531 deletions
diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile index f7d01bfa2e4..eac72f7bd34 100644 --- a/drivers/net/wireless/libertas/Makefile +++ b/drivers/net/wireless/libertas/Makefile @@ -6,6 +6,7 @@ libertas-y += ethtool.o libertas-y += main.o libertas-y += rx.o libertas-y += tx.o +libertas-y += firmware.o libertas-$(CONFIG_LIBERTAS_MESH) += mesh.o usb8xxx-objs += if_usb.o diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README index 1453eec82a9..1a554a685e9 100644 --- a/drivers/net/wireless/libertas/README +++ b/drivers/net/wireless/libertas/README @@ -8,9 +8,8 @@ Ltd. under the terms of the GNU General Public License Version 2, June 1991 (the "License"). You may use, redistribute and/or modify this File in accordance with the terms and conditions of the License, a copy of which - is available along with the File in the license.txt file or by writing to - the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + is available along with the File in the license.txt file or on the worldwide + web at http://www.gnu.org/licenses/gpl.txt. THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE @@ -238,28 +237,3 @@ hostsleep echo "1" > hostsleep : enable host sleep. echo "0" > hostsleep : disable host sleep -======================== -IWCONFIG COMMANDS -======================== -power period - - This command is used to configure the station in deep sleep mode / - auto deep sleep mode. - - The timer is implemented to monitor the activities (command, event, - etc.). When an activity is detected station will exit from deep - sleep mode automatically and restart the timer. At timer expiry - (no activity for defined time period) the deep sleep mode is entered - automatically. - - Note: this command is for SDIO interface only. - - Usage: - To enable deep sleep mode do: - iwconfig wlan0 power period 0 - To enable auto deep sleep mode with idle time period 5 seconds do: - iwconfig wlan0 power period 5 - To disable deep sleep/auto deep sleep mode do: - iwconfig wlan0 power period -1 - -============================================================================== diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 5d637af2d7c..47a998d8f99 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/hardirq.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/slab.h> @@ -18,6 +19,7 @@ #include "decl.h" #include "cfg.h" #include "cmd.h" +#include "mesh.h" #define CHAN2G(_channel, _freq, _flags) { \ @@ -101,7 +103,7 @@ static const u32 cipher_suites[] = { * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1 * in the firmware spec */ -static u8 lbs_auth_to_authtype(enum nl80211_auth_type auth_type) +static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type) { int ret = -ENOTSUPP; @@ -296,6 +298,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) const u8 *rates_eid, *ext_rates_eid; int n = 0; + rcu_read_lock(); rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); @@ -323,6 +326,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) *tlv++ = 0x96; n = 4; } + rcu_read_unlock(); rate_tlv->header.len = cpu_to_le16(n); return sizeof(rate_tlv->header) + n; @@ -433,21 +437,40 @@ static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) * Set Channel */ -static int lbs_cfg_set_channel(struct wiphy *wiphy, - struct net_device *netdev, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type) +static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) { 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); + chandef->chan->center_freq, + cfg80211_get_chandef_type(chandef)); - if (channel_type != NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) goto out; - ret = lbs_set_channel(priv, channel->hw_value); + ret = lbs_set_channel(priv, chandef->chan->hw_value); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy, + struct net_device *netdev, + struct ieee80211_channel *channel) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = -ENOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d", + netdev_name(netdev), channel->center_freq); + + if (netdev != priv->mesh_dev) + goto out; + + ret = lbs_mesh_set_channel(priv, channel->hw_value); out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); @@ -480,6 +503,7 @@ static int lbs_cfg_set_channel(struct wiphy *wiphy, static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, struct cmd_header *resp) { + struct cfg80211_bss *bss; struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp; int bsssize; const u8 *pos; @@ -597,7 +621,7 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, id = *pos++; elen = *pos++; left -= 2; - if (elen > left || elen == 0) { + if (elen > left) { lbs_deb_scan("scan response: invalid IE fmt\n"); goto done; } @@ -627,12 +651,14 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, LBS_SCAN_RSSI_TO_MBM(rssi)/100); if (channel && - !(channel->flags & IEEE80211_CHAN_DISABLED)) - cfg80211_inform_bss(wiphy, channel, - bssid, le64_to_cpu(*(__le64 *)tsfdesc), + !(channel->flags & IEEE80211_CHAN_DISABLED)) { + bss = cfg80211_inform_bss(wiphy, channel, + bssid, get_unaligned_le64(tsfdesc), capa, intvl, ie, ielen, LBS_SCAN_RSSI_TO_MBM(rssi), GFP_KERNEL); + cfg80211_put_bss(wiphy, bss); + } } else lbs_deb_scan("scan response: missing BSS channel IE\n"); @@ -690,7 +716,7 @@ static void lbs_scan_worker(struct work_struct *work) tlv = scan_cmd->tlvbuffer; /* add SSID TLV */ - if (priv->scan_req->n_ssids) + if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0) tlv += lbs_add_ssid_tlv(tlv, priv->scan_req->ssids[0].ssid, priv->scan_req->ssids[0].ssid_len); @@ -707,7 +733,7 @@ static void lbs_scan_worker(struct work_struct *work) if (priv->scan_channel < priv->scan_req->n_channels) { cancel_delayed_work(&priv->scan_work); - if (!priv->stopping) + if (netif_running(priv->dev)) queue_delayed_work(priv->work_thread, &priv->scan_work, msecs_to_jiffies(300)); } @@ -725,13 +751,8 @@ static void lbs_scan_worker(struct work_struct *work) if (priv->scan_channel >= priv->scan_req->n_channels) { /* Mark scan done */ - if (priv->internal_scan) - kfree(priv->scan_req); - else - cfg80211_scan_done(priv->scan_req, false); - - priv->scan_req = NULL; - priv->last_scan = jiffies; + cancel_delayed_work(&priv->scan_work); + lbs_scan_done(priv); } /* Restart network */ @@ -761,17 +782,31 @@ static void _internal_start_scan(struct lbs_private *priv, bool internal, request->n_ssids, request->n_channels, request->ie_len); priv->scan_channel = 0; - queue_delayed_work(priv->work_thread, &priv->scan_work, - msecs_to_jiffies(50)); - priv->scan_req = request; priv->internal_scan = internal; + queue_delayed_work(priv->work_thread, &priv->scan_work, + msecs_to_jiffies(50)); + lbs_deb_leave(LBS_DEB_CFG80211); } +/* + * Clean up priv->scan_req. Should be used to handle the allocation details. + */ +void lbs_scan_done(struct lbs_private *priv) +{ + WARN_ON(!priv->scan_req); + + if (priv->internal_scan) + kfree(priv->scan_req); + else + cfg80211_scan_done(priv->scan_req, false); + + priv->scan_req = NULL; +} + static int lbs_cfg_scan(struct wiphy *wiphy, - struct net_device *dev, struct cfg80211_scan_request *request) { struct lbs_private *priv = wiphy_priv(wiphy); @@ -971,9 +1006,8 @@ struct cmd_key_material { } __packed; static int lbs_set_key_material(struct lbs_private *priv, - int key_type, - int key_info, - u8 *key, u16 key_len) + int key_type, int key_info, + const u8 *key, u16 key_len) { struct cmd_key_material cmd; int ret; @@ -1107,11 +1141,13 @@ static int lbs_associate(struct lbs_private *priv, cmd->capability = cpu_to_le16(bss->capability); /* add SSID TLV */ + rcu_read_lock(); ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); if (ssid_eid) pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); else lbs_deb_assoc("no SSID\n"); + rcu_read_unlock(); /* add DS param TLV */ if (bss->channel) @@ -1221,6 +1257,7 @@ static int lbs_associate(struct lbs_private *priv, netif_tx_wake_all_queues(priv->dev); } + kfree(cmd); done: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; @@ -1230,14 +1267,9 @@ static struct cfg80211_scan_request * _new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme) { struct cfg80211_scan_request *creq = NULL; - int i, n_channels = 0; + int i, n_channels = ieee80211_get_num_supported_channels(wiphy); enum ieee80211_band band; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } - creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + n_channels * sizeof(void *), GFP_ATOMIC); @@ -1291,27 +1323,32 @@ 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) { - /* Run a scan if one isn't in-progress already and if the last - * scan was done more than 2 seconds ago. - */ - if (priv->scan_req == NULL && - time_after(jiffies, priv->last_scan + (2 * HZ))) { - struct cfg80211_scan_request *creq; + struct cfg80211_scan_request *creq; - creq = _new_connect_scan_req(wiphy, sme); - if (!creq) { - ret = -EINVAL; - goto done; - } + /* + * Scan for the requested network after waiting for existing + * scans to finish. + */ + lbs_deb_assoc("assoc: waiting for existing scans\n"); + wait_event_interruptible_timeout(priv->scan_q, + (priv->scan_req == NULL), + (15 * HZ)); - lbs_deb_assoc("assoc: scanning for compatible AP\n"); - _internal_start_scan(priv, true, creq); + creq = _new_connect_scan_req(wiphy, sme); + if (!creq) { + ret = -EINVAL; + goto done; } - /* Wait for any in-progress scan to complete */ + lbs_deb_assoc("assoc: scanning for compatible AP\n"); + _internal_start_scan(priv, true, creq); + lbs_deb_assoc("assoc: waiting for scan to complete\n"); wait_event_interruptible_timeout(priv->scan_q, (priv->scan_req == NULL), @@ -1388,7 +1425,12 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, goto done; } - lbs_set_authtype(priv, sme); + ret = lbs_set_authtype(priv, sme); + if (ret == -ENOTSUPP) { + wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type); + goto done; + } + lbs_set_radio(priv, preamble, 1); /* Do the actual association */ @@ -1396,33 +1438,28 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, done: if (bss) - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } -static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, - u16 reason_code) +int lbs_disconnect(struct lbs_private *priv, u16 reason) { - struct lbs_private *priv = wiphy_priv(wiphy); struct cmd_ds_802_11_deauthenticate cmd; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code); - - /* store for lbs_cfg_ret_disconnect() */ - priv->disassoc_reason = reason_code; + int ret; memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); /* Mildly ugly to use a locally store my own BSSID ... */ memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN); - cmd.reasoncode = cpu_to_le16(reason_code); + cmd.reasoncode = cpu_to_le16(reason); - if (lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd)) - return -EFAULT; + ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd); + if (ret) + return ret; cfg80211_disconnected(priv->dev, - priv->disassoc_reason, + reason, NULL, 0, GFP_KERNEL); priv->connect_status = LBS_DISCONNECTED; @@ -1430,6 +1467,21 @@ static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + 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() */ + priv->disassoc_reason = reason_code; + + return lbs_disconnect(priv, reason_code); +} static int lbs_cfg_set_default_key(struct wiphy *wiphy, struct net_device *netdev, @@ -1438,6 +1490,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) { @@ -1459,6 +1514,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", @@ -1551,7 +1609,7 @@ static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, */ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct lbs_private *priv = wiphy_priv(wiphy); s8 signal, noise; @@ -1592,39 +1650,6 @@ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, /* - * "Site survey", here just current channel and noise level - */ - -static int lbs_get_survey(struct wiphy *wiphy, struct net_device *dev, - int idx, struct survey_info *survey) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - s8 signal, noise; - int ret; - - if (idx != 0) - ret = -ENOENT; - - lbs_deb_enter(LBS_DEB_CFG80211); - - survey->channel = ieee80211_get_channel(wiphy, - ieee80211_channel_to_frequency(priv->channel, - IEEE80211_BAND_2GHZ)); - - ret = lbs_get_rssi(priv, &signal, &noise); - if (ret == 0) { - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = noise; - } - - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - - -/* * Change interface */ @@ -1635,28 +1660,23 @@ static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev, struct lbs_private *priv = wiphy_priv(wiphy); int ret = 0; - lbs_deb_enter(LBS_DEB_CFG80211); + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; switch (type) { case NL80211_IFTYPE_MONITOR: - ret = lbs_set_monitor_mode(priv, 1); - break; case NL80211_IFTYPE_STATION: - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) - ret = lbs_set_monitor_mode(priv, 0); - if (!ret) - ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1); - break; case NL80211_IFTYPE_ADHOC: - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) - ret = lbs_set_monitor_mode(priv, 0); - if (!ret) - ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2); break; default: - ret = -ENOTSUPP; + return -EOPNOTSUPP; } + lbs_deb_enter(LBS_DEB_CFG80211); + + if (priv->iface_running) + ret = lbs_set_iface_type(priv, type); + if (!ret) priv->wdev->iftype = type; @@ -1688,6 +1708,7 @@ static void lbs_join_post(struct lbs_private *priv, 2 + 2 + /* atim */ 2 + 8]; /* extended rates */ u8 *fake = fake_ie; + struct cfg80211_bss *bss; lbs_deb_enter(LBS_DEB_CFG80211); @@ -1711,7 +1732,7 @@ static void lbs_join_post(struct lbs_private *priv, /* Fake DS channel IE */ *fake++ = WLAN_EID_DS_PARAMS; *fake++ = 1; - *fake++ = params->channel->hw_value; + *fake++ = params->chandef.chan->hw_value; /* Fake IBSS params IE */ *fake++ = WLAN_EID_IBSS_PARAMS; *fake++ = 2; @@ -1731,19 +1752,21 @@ static void lbs_join_post(struct lbs_private *priv, *fake++ = 0x6c; lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie); - cfg80211_inform_bss(priv->wdev->wiphy, - params->channel, - bssid, - 0, - capability, - params->beacon_interval, - fake_ie, fake - fake_ie, - 0, GFP_KERNEL); + bss = cfg80211_inform_bss(priv->wdev->wiphy, + params->chandef.chan, + bssid, + 0, + capability, + params->beacon_interval, + fake_ie, fake - fake_ie, + 0, GFP_KERNEL); + cfg80211_put_bss(priv->wdev->wiphy, bss); memcpy(priv->wdev->ssid, params->ssid, params->ssid_len); priv->wdev->ssid_len = params->ssid_len; - cfg80211_ibss_joined(priv->dev, bssid, GFP_KERNEL); + cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan, + GFP_KERNEL); /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */ priv->connect_status = LBS_CONNECTED; @@ -1758,7 +1781,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, struct cfg80211_ibss_params *params, struct cfg80211_bss *bss) { - const u8 *rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); + const u8 *rates_eid; struct cmd_ds_802_11_ad_hoc_join cmd; u8 preamble = RADIO_PREAMBLE_SHORT; int ret = 0; @@ -1809,7 +1832,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval); cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS; cmd.bss.ds.header.len = 1; - cmd.bss.ds.channel = params->channel->hw_value; + cmd.bss.ds.channel = params->chandef.chan->hw_value; cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS; cmd.bss.ibss.header.len = 2; cmd.bss.ibss.atimwindow = 0; @@ -1817,6 +1840,8 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, /* set rates to the intersection of our rates and the rates in the bss */ + rcu_read_lock(); + rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); if (!rates_eid) { lbs_add_rates(cmd.bss.rates); } else { @@ -1836,6 +1861,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, } } } + rcu_read_unlock(); /* Only v8 and below support setting this */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { @@ -1918,7 +1944,7 @@ static int lbs_ibss_start_new(struct lbs_private *priv, cmd.ibss.atimwindow = 0; cmd.ds.header.id = WLAN_EID_DS_PARAMS; cmd.ds.header.len = 1; - cmd.ds.channel = params->channel->hw_value; + cmd.ds.channel = params->chandef.chan->hw_value; /* Only v8 and below support setting probe delay */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); @@ -1958,26 +1984,29 @@ 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) { + if (!params->chandef.chan) { ret = -ENOTSUPP; goto out; } - ret = lbs_set_channel(priv, params->channel->hw_value); + ret = lbs_set_channel(priv, params->chandef.chan->hw_value); if (ret) goto out; /* Search if someone is beaconing. This assumes that the * bss list is populated already */ - bss = cfg80211_get_bss(wiphy, params->channel, params->bssid, + bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid, params->ssid, params->ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); if (bss) { ret = lbs_ibss_join_existing(priv, params, bss); - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); } else ret = lbs_ibss_start_new(priv, params); @@ -1994,6 +2023,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)); @@ -2015,7 +2047,8 @@ static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev) */ static struct cfg80211_ops lbs_cfg80211_ops = { - .set_channel = lbs_cfg_set_channel, + .set_monitor_channel = lbs_cfg_set_monitor_channel, + .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel, .scan = lbs_cfg_scan, .connect = lbs_cfg_connect, .disconnect = lbs_cfg_disconnect, @@ -2023,7 +2056,6 @@ static struct cfg80211_ops lbs_cfg80211_ops = { .del_key = lbs_cfg_del_key, .set_default_key = lbs_cfg_set_default_key, .get_station = lbs_cfg_get_station, - .dump_survey = lbs_get_survey, .change_virtual_intf = lbs_change_intf, .join_ibss = lbs_join_ibss, .leave_ibss = lbs_leave_ibss, @@ -2044,10 +2076,8 @@ struct wireless_dev *lbs_cfg_alloc(struct device *dev) lbs_deb_enter(LBS_DEB_CFG80211); wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); - if (!wdev) { - dev_err(dev, "cannot allocate wireless device\n"); + if (!wdev) return ERR_PTR(-ENOMEM); - } wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private)); if (!wdev->wiphy) { @@ -2095,6 +2125,21 @@ static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv) lbs_deb_leave(LBS_DEB_CFG80211); } +static void lbs_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain " + "callback for domain %c%c\n", request->alpha2[0], + request->alpha2[1]); + + memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); + if (lbs_iface_active(priv)) + lbs_set_11d_domain_info(priv); + + lbs_deb_leave(LBS_DEB_CFG80211); +} /* * This function get's called after lbs_setup_firmware() determined the @@ -2116,6 +2161,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; @@ -2145,22 +2192,6 @@ int lbs_cfg_register(struct lbs_private *priv) return ret; } -int lbs_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain " - "callback for domain %c%c\n", request->alpha2[0], - request->alpha2[1]); - - ret = lbs_set_11d_domain_info(priv, request, wiphy->bands); - - lbs_deb_leave(LBS_DEB_CFG80211); - return ret; -} - void lbs_scan_deinit(struct lbs_private *priv) { lbs_deb_enter(LBS_DEB_CFG80211); diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h index 4f46bb744be..10995f59fe3 100644 --- a/drivers/net/wireless/libertas/cfg.h +++ b/drivers/net/wireless/libertas/cfg.h @@ -10,12 +10,11 @@ struct wireless_dev *lbs_cfg_alloc(struct device *dev); int lbs_cfg_register(struct lbs_private *priv); void lbs_cfg_free(struct lbs_private *priv); -int lbs_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request); - void lbs_send_disconnect_notification(struct lbs_private *priv); void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event); +void lbs_scan_done(struct lbs_private *priv); void lbs_scan_deinit(struct lbs_private *priv); +int lbs_disconnect(struct lbs_private *priv, u16 reason); #endif diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 71c8f3fccfa..aaa297315c4 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -3,10 +3,12 @@ * It prepares command and sends it to firmware when it is ready. */ +#include <linux/hardirq.h> #include <linux/kfifo.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/if_arp.h> +#include <linux/export.h> #include "decl.h" #include "cfg.h" @@ -731,15 +733,13 @@ int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf) * to the firmware * * @priv: pointer to &struct lbs_private - * @request: cfg80211 regulatory request structure - * @bands: the device's supported bands and channels * * returns: 0 on success, error code on failure */ -int lbs_set_11d_domain_info(struct lbs_private *priv, - struct regulatory_request *request, - struct ieee80211_supported_band **bands) +int lbs_set_11d_domain_info(struct lbs_private *priv) { + struct wiphy *wiphy = priv->wdev->wiphy; + struct ieee80211_supported_band **bands = wiphy->bands; struct cmd_ds_802_11d_domain_info cmd; struct mrvl_ie_domain_param_set *domain = &cmd.domain; struct ieee80211_country_ie_triplet *t; @@ -750,21 +750,23 @@ int lbs_set_11d_domain_info(struct lbs_private *priv, u8 first_channel = 0, next_chan = 0, max_pwr = 0; u8 i, flag = 0; size_t triplet_size; - int ret; + int ret = 0; lbs_deb_enter(LBS_DEB_11D); + if (!priv->country_code[0]) + goto out; memset(&cmd, 0, sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); lbs_deb_11d("Setting country code '%c%c'\n", - request->alpha2[0], request->alpha2[1]); + priv->country_code[0], priv->country_code[1]); domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); /* Set country code */ - domain->country_code[0] = request->alpha2[0]; - domain->country_code[1] = request->alpha2[1]; + domain->country_code[0] = priv->country_code[0]; + domain->country_code[1] = priv->country_code[1]; domain->country_code[2] = ' '; /* Now set up the channel triplets; firmware is somewhat picky here @@ -846,6 +848,7 @@ int lbs_set_11d_domain_info(struct lbs_private *priv, ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd); +out: lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); return ret; } @@ -873,6 +876,7 @@ int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_GET); + cmd.offset = cpu_to_le16(offset); if (reg != CMD_MAC_REG_ACCESS && reg != CMD_BBP_REG_ACCESS && @@ -882,7 +886,7 @@ int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) } ret = lbs_cmd_with_response(priv, reg, &cmd); - if (ret) { + if (!ret) { if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) *value = cmd.value.bbp_rf; else if (reg == CMD_MAC_REG_ACCESS) @@ -915,6 +919,7 @@ int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value) memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.offset = cpu_to_le16(offset); if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) cmd.value.bbp_rf = (u8) (value & 0xFF); @@ -1015,9 +1020,9 @@ static void lbs_submit_command(struct lbs_private *priv, if (ret) { netdev_info(priv->dev, "DNLD_CMD: hw_host_to_card failed: %d\n", ret); - /* Let the timer kick in and retry, and potentially reset - the whole thing if the condition persists */ - timeo = HZ/4; + /* Reset dnld state machine, report failure */ + priv->dnld_sent = DNLD_RES_RECEIVED; + lbs_complete_command(priv, cmdnode, ret); } if (command == CMD_802_11_DEEP_SLEEP) { @@ -1067,16 +1072,34 @@ static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv, spin_unlock_irqrestore(&priv->driver_lock, flags); } -void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result) +void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result) { + /* + * Normally, commands are removed from cmdpendingq before being + * submitted. However, we can arrive here on alternative codepaths + * where the command is still pending. Make sure the command really + * isn't part of a list at this point. + */ + list_del_init(&cmd->list); + cmd->result = result; cmd->cmdwaitqwoken = 1; - wake_up_interruptible(&cmd->cmdwait_q); + wake_up(&cmd->cmdwait_q); if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) __lbs_cleanup_and_insert_cmd(priv, cmd); priv->cur_cmd = NULL; + wake_up(&priv->waitq); +} + +void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result) +{ + unsigned long flags; + spin_lock_irqsave(&priv->driver_lock, flags); + __lbs_complete_command(priv, cmd, result); + spin_unlock_irqrestore(&priv->driver_lock, flags); } int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) @@ -1136,6 +1159,22 @@ void lbs_set_mac_control(struct lbs_private *priv) lbs_deb_leave(LBS_DEB_CMD); } +int lbs_set_mac_control_sync(struct lbs_private *priv) +{ + struct cmd_ds_mac_control cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(priv->mac_control); + cmd.reserved = 0; + ret = lbs_cmd_with_response(priv, CMD_MAC_CONTROL, &cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + /** * lbs_allocate_cmd_buffer - allocates the command buffer and links * it to command free queue @@ -1248,7 +1287,7 @@ static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv) if (!list_empty(&priv->cmdfreeq)) { tempnode = list_first_entry(&priv->cmdfreeq, struct cmd_ctrl_node, list); - list_del(&tempnode->list); + list_del_init(&tempnode->list); } else { lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); tempnode = NULL; @@ -1356,10 +1395,7 @@ int lbs_execute_next_command(struct lbs_private *priv) cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { lbs_deb_host( "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); - spin_lock_irqsave(&priv->driver_lock, flags); - list_del(&cmdnode->list); lbs_complete_command(priv, cmdnode, 0); - spin_unlock_irqrestore(&priv->driver_lock, flags); ret = 0; goto done; @@ -1369,10 +1405,7 @@ int lbs_execute_next_command(struct lbs_private *priv) (priv->psstate == PS_STATE_PRE_SLEEP)) { lbs_deb_host( "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); - spin_lock_irqsave(&priv->driver_lock, flags); - list_del(&cmdnode->list); lbs_complete_command(priv, cmdnode, 0); - spin_unlock_irqrestore(&priv->driver_lock, flags); priv->needtowakeup = 1; ret = 0; @@ -1384,7 +1417,7 @@ int lbs_execute_next_command(struct lbs_private *priv) } } spin_lock_irqsave(&priv->driver_lock, flags); - list_del(&cmdnode->list); + list_del_init(&cmdnode->list); spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", le16_to_cpu(cmd->command)); @@ -1612,7 +1645,7 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); /* Wake up main thread to execute next command */ - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); cmdnode = ERR_PTR(-ENOBUFS); goto done; } @@ -1632,7 +1665,7 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, cmdnode->cmdwaitqwoken = 0; lbs_queue_cmd(priv, cmdnode); - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); done: lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode); @@ -1667,7 +1700,13 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command, } might_sleep(); - wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); + + /* + * Be careful with signals here. A signal may be received as the system + * goes into suspend or resume. We do not want this to interrupt the + * command, so we perform an uninterruptible sleep. + */ + wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); spin_lock_irqsave(&priv->driver_lock, flags); ret = cmdnode->result; diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h index 7109d6b717e..4279e8ab95f 100644 --- a/drivers/net/wireless/libertas/cmd.h +++ b/drivers/net/wireless/libertas/cmd.h @@ -59,6 +59,8 @@ int lbs_allocate_cmd_buffer(struct lbs_private *priv); int lbs_free_cmd_buffer(struct lbs_private *priv); int lbs_execute_next_command(struct lbs_private *priv); +void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result); void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, int result); int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); @@ -94,6 +96,7 @@ void lbs_ps_confirm_sleep(struct lbs_private *priv); int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on); void lbs_set_mac_control(struct lbs_private *priv); +int lbs_set_mac_control_sync(struct lbs_private *priv); int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, s16 *maxlevel); @@ -126,9 +129,7 @@ int lbs_set_monitor_mode(struct lbs_private *priv, int enable); int lbs_get_rssi(struct lbs_private *priv, s8 *snr, s8 *nf); -int lbs_set_11d_domain_info(struct lbs_private *priv, - struct regulatory_request *request, - struct ieee80211_supported_band **bands); +int lbs_set_11d_domain_info(struct lbs_private *priv); int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value); diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c index 207fc361db8..65f18f1e869 100644 --- a/drivers/net/wireless/libertas/cmdresp.c +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -3,6 +3,7 @@ * responses as well as events generated by firmware. */ +#include <linux/hardirq.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/sched.h> @@ -165,7 +166,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); } - lbs_complete_command(priv, priv->cur_cmd, result); + __lbs_complete_command(priv, priv->cur_cmd, result); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = 0; @@ -186,7 +187,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) break; } - lbs_complete_command(priv, priv->cur_cmd, result); + __lbs_complete_command(priv, priv->cur_cmd, result); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; @@ -204,7 +205,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) if (priv->cur_cmd) { /* Clean up and Put current command back to cmdfreeq */ - lbs_complete_command(priv, priv->cur_cmd, result); + __lbs_complete_command(priv, priv->cur_cmd, result); } spin_unlock_irqrestore(&priv->driver_lock, flags); @@ -247,7 +248,7 @@ int lbs_process_event(struct lbs_private *priv, u32 event) /* handle unexpected PS SLEEP event */ if (priv->psstate == PS_STATE_FULL_POWER) { lbs_deb_cmd( - "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n"); + "EVENT: in FULL POWER mode, ignoring PS_SLEEP\n"); break; } priv->psstate = PS_STATE_PRE_SLEEP; diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c index 23250f62176..cc6a0a586f0 100644 --- a/drivers/net/wireless/libertas/debugfs.c +++ b/drivers/net/wireless/libertas/debugfs.c @@ -1,9 +1,11 @@ #include <linux/dcache.h> #include <linux/debugfs.h> #include <linux/delay.h> +#include <linux/hardirq.h> #include <linux/mm.h> #include <linux/string.h> #include <linux/slab.h> +#include <linux/export.h> #include "decl.h" #include "cmd.h" @@ -19,12 +21,6 @@ static char *szStates[] = { static void lbs_debug_init(struct lbs_private *priv); #endif -static int open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - static ssize_t write_file_dummy(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { @@ -487,7 +483,7 @@ static ssize_t lbs_rdmac_write(struct file *file, res = -EFAULT; goto out_unlock; } - priv->mac_offset = simple_strtoul((char *)buf, NULL, 16); + priv->mac_offset = simple_strtoul(buf, NULL, 16); res = count; out_unlock: free_page(addr); @@ -569,7 +565,7 @@ static ssize_t lbs_rdbbp_write(struct file *file, res = -EFAULT; goto out_unlock; } - priv->bbp_offset = simple_strtoul((char *)buf, NULL, 16); + priv->bbp_offset = simple_strtoul(buf, NULL, 16); res = count; out_unlock: free_page(addr); @@ -694,7 +690,7 @@ out_unlock: #define FOPS(fread, fwrite) { \ .owner = THIS_MODULE, \ - .open = open_file_generic, \ + .open = simple_open, \ .read = (fread), \ .write = (fwrite), \ .llseek = generic_file_llseek, \ @@ -702,7 +698,7 @@ out_unlock: struct lbs_debugfs_files { const char *name; - int perm; + umode_t perm; struct file_operations fops; }; @@ -917,7 +913,10 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, char *p2; struct debug_data *d = f->private_data; - pdata = kmalloc(cnt, GFP_KERNEL); + if (cnt == 0) + return 0; + + pdata = kmalloc(cnt + 1, GFP_KERNEL); if (pdata == NULL) return 0; @@ -926,6 +925,7 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, kfree(pdata); return 0; } + pdata[cnt] = '\0'; p0 = pdata; for (i = 0; i < num_of_items; i++) { @@ -960,7 +960,7 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, static const struct file_operations lbs_debug_fops = { .owner = THIS_MODULE, - .open = open_file_generic, + .open = simple_open, .write = lbs_debugfs_write, .read = lbs_debugfs_read, .llseek = default_llseek, diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h index da0b05bb89f..84a3aa7ac57 100644 --- a/drivers/net/wireless/libertas/decl.h +++ b/drivers/net/wireless/libertas/decl.h @@ -9,6 +9,7 @@ #include <linux/netdevice.h> #include <linux/firmware.h> +#include <linux/nl80211.h> /* Should be terminated by a NULL entry */ struct lbs_fw_table { @@ -18,6 +19,10 @@ struct lbs_fw_table { }; struct lbs_private; +typedef void (*lbs_fw_cb)(struct lbs_private *priv, int ret, + const struct firmware *helper, const struct firmware *mainfw); + +struct lbs_private; struct sk_buff; struct net_device; struct cmd_ds_command; @@ -43,10 +48,15 @@ int lbs_start_card(struct lbs_private *priv); void lbs_stop_card(struct lbs_private *priv); void lbs_host_to_card_done(struct lbs_private *priv); +int lbs_start_iface(struct lbs_private *priv); +int lbs_stop_iface(struct lbs_private *priv); +int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type); + int lbs_rtap_supported(struct lbs_private *priv); int lbs_set_mac_address(struct net_device *dev, void *addr); void lbs_set_multicast_list(struct net_device *dev); +void lbs_update_mcast(struct lbs_private *priv); int lbs_suspend(struct lbs_private *priv); int lbs_resume(struct lbs_private *priv); @@ -60,10 +70,13 @@ int lbs_exit_auto_deep_sleep(struct lbs_private *priv); u32 lbs_fw_index_to_data_rate(u8 index); u8 lbs_data_rate_to_fw_index(u32 rate); -int lbs_get_firmware(struct device *dev, const char *user_helper, - const char *user_mainfw, u32 card_model, +int lbs_get_firmware(struct device *dev, u32 card_model, const struct lbs_fw_table *fw_table, const struct firmware **helper, const struct firmware **mainfw); +int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, + u32 card_model, const struct lbs_fw_table *fw_table, + lbs_fw_cb callback); +void lbs_wait_for_firmware_load(struct lbs_private *priv); #endif diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index ab966f08024..407784aca62 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -90,7 +90,8 @@ do { if ((lbs_debug & (grp)) == (grp)) \ #define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args) #ifdef DEBUG -static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len) +static inline void lbs_deb_hex(unsigned int grp, const char *prompt, + const u8 *buf, int len) { int i = 0; diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index 76d018beebf..6bd1608992b 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -6,8 +6,8 @@ #ifndef _LBS_DEV_H_ #define _LBS_DEV_H_ -#include "mesh.h" #include "defs.h" +#include "decl.h" #include "host.h" #include <linux/kfifo.h> @@ -22,6 +22,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 { @@ -36,20 +47,19 @@ struct lbs_private { /* CFG80211 */ struct wireless_dev *wdev; bool wiphy_registered; - bool stopping; struct cfg80211_scan_request *scan_req; u8 assoc_bss[ETH_ALEN]; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; u8 disassoc_reason; /* Mesh */ struct net_device *mesh_dev; /* Virtual device */ #ifdef CONFIG_LIBERTAS_MESH - u32 mesh_connect_status; struct lbs_mesh_stats mstats; - int mesh_open; uint16_t mesh_tlv; u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1]; u8 mesh_ssid_len; + u8 mesh_channel; #endif /* Debugfs */ @@ -88,11 +98,14 @@ struct lbs_private { /* Hardware access */ void *card; + bool iface_running; u8 fw_ready; u8 surpriseremoved; u8 setup_fw_on_resume; int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb); void (*reset_card) (struct lbs_private *priv); + int (*power_save) (struct lbs_private *priv); + int (*power_restore) (struct lbs_private *priv); int (*enter_deep_sleep) (struct lbs_private *priv); int (*exit_deep_sleep) (struct lbs_private *priv); int (*reset_deep_sleep_wakeup) (struct lbs_private *priv); @@ -148,6 +161,7 @@ struct lbs_private { /* protected by hard_start_xmit serialization */ u8 txretrycount; struct sk_buff *currenttxskb; + struct timer_list tx_lockup_timer; /* Locks */ struct mutex lock; @@ -169,9 +183,29 @@ struct lbs_private { wait_queue_head_t scan_q; /* Whether the scan was initiated internally and not by cfg80211 */ bool internal_scan; - unsigned long last_scan; + + /* Firmware load */ + u32 fw_model; + wait_queue_head_t fw_waitq; + struct device *fw_device; + const struct firmware *helper_fw; + const struct lbs_fw_table *fw_table; + const struct lbs_fw_table *fw_iter; + lbs_fw_cb fw_callback; }; extern struct cmd_confirm_sleep confirm_sleep; +/* Check if there is an interface active. */ +static inline int lbs_iface_active(struct lbs_private *priv) +{ + int r; + + r = netif_running(priv->dev); + if (priv->mesh_dev) + r |= netif_running(priv->mesh_dev); + + return r; +} + #endif diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c index 29dbce4a9f8..f955b2d66ed 100644 --- a/drivers/net/wireless/libertas/ethtool.c +++ b/drivers/net/wireless/libertas/ethtool.c @@ -1,9 +1,11 @@ +#include <linux/hardirq.h> #include <linux/netdevice.h> #include <linux/ethtool.h> #include <linux/delay.h> #include "decl.h" #include "cmd.h" +#include "mesh.h" static void lbs_ethtool_get_drvinfo(struct net_device *dev, @@ -11,13 +13,14 @@ static void lbs_ethtool_get_drvinfo(struct net_device *dev, { struct lbs_private *priv = dev->ml_priv; - snprintf(info->fw_version, 32, "%u.%u.%u.p%u", + snprintf(info->fw_version, sizeof(info->fw_version), + "%u.%u.%u.p%u", priv->fwrelease >> 24 & 0xff, priv->fwrelease >> 16 & 0xff, priv->fwrelease >> 8 & 0xff, priv->fwrelease & 0xff); - strcpy(info->driver, "libertas"); - strcpy(info->version, lbs_driver_version); + strlcpy(info->driver, "libertas", sizeof(info->driver)); + strlcpy(info->version, lbs_driver_version, sizeof(info->version)); } /* diff --git a/drivers/net/wireless/libertas/firmware.c b/drivers/net/wireless/libertas/firmware.c new file mode 100644 index 00000000000..51b92b5df11 --- /dev/null +++ b/drivers/net/wireless/libertas/firmware.c @@ -0,0 +1,227 @@ +/* + * Firmware loading and handling functions. + */ + +#include <linux/sched.h> +#include <linux/firmware.h> +#include <linux/module.h> + +#include "dev.h" +#include "decl.h" + +static void load_next_firmware_from_table(struct lbs_private *private); + +static void lbs_fw_loaded(struct lbs_private *priv, int ret, + const struct firmware *helper, const struct firmware *mainfw) +{ + unsigned long flags; + + lbs_deb_fw("firmware load complete, code %d\n", ret); + + /* User must free helper/mainfw */ + priv->fw_callback(priv, ret, helper, mainfw); + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->fw_callback = NULL; + wake_up(&priv->fw_waitq); + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +static void do_load_firmware(struct lbs_private *priv, const char *name, + void (*cb)(const struct firmware *fw, void *context)) +{ + int ret; + + lbs_deb_fw("Requesting %s\n", name); + ret = request_firmware_nowait(THIS_MODULE, true, name, + priv->fw_device, GFP_KERNEL, priv, cb); + if (ret) { + lbs_deb_fw("request_firmware_nowait error %d\n", ret); + lbs_fw_loaded(priv, ret, NULL, NULL); + } +} + +static void main_firmware_cb(const struct firmware *firmware, void *context) +{ + struct lbs_private *priv = context; + + if (!firmware) { + /* Failed to find firmware: try next table entry */ + load_next_firmware_from_table(priv); + return; + } + + /* Firmware found! */ + lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); + if (priv->helper_fw) { + release_firmware (priv->helper_fw); + priv->helper_fw = NULL; + } + release_firmware (firmware); +} + +static void helper_firmware_cb(const struct firmware *firmware, void *context) +{ + struct lbs_private *priv = context; + + if (!firmware) { + /* Failed to find firmware: try next table entry */ + load_next_firmware_from_table(priv); + return; + } + + /* Firmware found! */ + if (priv->fw_iter->fwname) { + priv->helper_fw = firmware; + do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb); + } else { + /* No main firmware needed for this helper --> success! */ + lbs_fw_loaded(priv, 0, firmware, NULL); + } +} + +static void load_next_firmware_from_table(struct lbs_private *priv) +{ + const struct lbs_fw_table *iter; + + if (!priv->fw_iter) + iter = priv->fw_table; + else + iter = ++priv->fw_iter; + + if (priv->helper_fw) { + release_firmware(priv->helper_fw); + priv->helper_fw = NULL; + } + +next: + if (!iter->helper) { + /* End of table hit. */ + lbs_fw_loaded(priv, -ENOENT, NULL, NULL); + return; + } + + if (iter->model != priv->fw_model) { + iter++; + goto next; + } + + priv->fw_iter = iter; + do_load_firmware(priv, iter->helper, helper_firmware_cb); +} + +void lbs_wait_for_firmware_load(struct lbs_private *priv) +{ + wait_event(priv->fw_waitq, priv->fw_callback == NULL); +} + +/** + * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load + * either a helper firmware and a main firmware (2-stage), or just the helper. + * + * @priv: Pointer to lbs_private instance + * @dev: A pointer to &device structure + * @card_model: Bus-specific card model ID used to filter firmware table + * elements + * @fw_table: Table of firmware file names and device model numbers + * terminated by an entry with a NULL helper name + * @callback: User callback to invoke when firmware load succeeds or fails. + */ +int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, + u32 card_model, const struct lbs_fw_table *fw_table, + lbs_fw_cb callback) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->driver_lock, flags); + if (priv->fw_callback) { + lbs_deb_fw("firmware load already in progress\n"); + spin_unlock_irqrestore(&priv->driver_lock, flags); + return -EBUSY; + } + + priv->fw_device = device; + priv->fw_callback = callback; + priv->fw_table = fw_table; + priv->fw_iter = NULL; + priv->fw_model = card_model; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbs_deb_fw("Starting async firmware load\n"); + load_next_firmware_from_table(priv); + return 0; +} +EXPORT_SYMBOL_GPL(lbs_get_firmware_async); + +/** + * lbs_get_firmware - Retrieves two-stage firmware + * + * @dev: A pointer to &device structure + * @card_model: Bus-specific card model ID used to filter firmware table + * elements + * @fw_table: Table of firmware file names and device model numbers + * terminated by an entry with a NULL helper name + * @helper: On success, the helper firmware; caller must free + * @mainfw: On success, the main firmware; caller must free + * + * Deprecated: use lbs_get_firmware_async() instead. + * + * returns: 0 on success, non-zero on failure + */ +int lbs_get_firmware(struct device *dev, u32 card_model, + const struct lbs_fw_table *fw_table, + const struct firmware **helper, + const struct firmware **mainfw) +{ + const struct lbs_fw_table *iter; + int ret; + + BUG_ON(helper == NULL); + BUG_ON(mainfw == NULL); + + /* Search for firmware to use from the table. */ + iter = fw_table; + while (iter && iter->helper) { + if (iter->model != card_model) + goto next; + + if (*helper == NULL) { + ret = request_firmware(helper, iter->helper, dev); + if (ret) + goto next; + + /* If the device has one-stage firmware (ie cf8305) and + * we've got it then we don't need to bother with the + * main firmware. + */ + if (iter->fwname == NULL) + return 0; + } + + if (*mainfw == NULL) { + ret = request_firmware(mainfw, iter->fwname, dev); + if (ret) { + /* Clear the helper to ensure we don't have + * mismatched firmware pairs. + */ + release_firmware(*helper); + *helper = NULL; + } + } + + if (*helper && *mainfw) + return 0; + + next: + iter++; + } + + /* Failed */ + release_firmware(*helper); + *helper = NULL; + release_firmware(*mainfw); + *mainfw = NULL; + + return -ENOENT; +} +EXPORT_SYMBOL_GPL(lbs_get_firmware); diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h index 2e2dbfa2ee5..96726f79a1d 100644 --- a/drivers/net/wireless/libertas/host.h +++ b/drivers/net/wireless/libertas/host.h @@ -68,7 +68,6 @@ #define CMD_802_11_BEACON_STOP 0x0049 #define CMD_802_11_MAC_ADDRESS 0x004d #define CMD_802_11_LED_GPIO_CTRL 0x004e -#define CMD_802_11_EEPROM_ACCESS 0x0059 #define CMD_802_11_BAND_CONFIG 0x0058 #define CMD_GSPI_BUS_CONFIG 0x005a #define CMD_802_11D_DOMAIN_INFO 0x005b diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index e2693517986..f499efc6abc 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -738,6 +738,46 @@ done: return ret; } +static void if_cs_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *helper, + const struct firmware *mainfw) +{ + struct if_cs_card *card = priv->card; + + if (ret) { + pr_err("failed to find firmware (%d)\n", ret); + return; + } + + /* Load the firmware */ + ret = if_cs_prog_helper(card, helper); + if (ret == 0 && (card->model != MODEL_8305)) + ret = if_cs_prog_real(card, mainfw); + if (ret) + return; + + /* Now actually get the IRQ */ + ret = request_irq(card->p_dev->irq, if_cs_interrupt, + IRQF_SHARED, DRV_NAME, card); + if (ret) { + pr_err("error in request_irq\n"); + return; + } + + /* + * Clear any interrupt cause that happened while sending + * firmware/initializing card + */ + if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK); + if_cs_enable_ints(card); + + /* And finally bring the card up */ + priv->fw_ready = 1; + if (lbs_start_card(priv) != 0) { + pr_err("could not activate card\n"); + free_irq(card->p_dev->irq, card); + } +} /********************************************************************/ @@ -809,16 +849,13 @@ static int if_cs_probe(struct pcmcia_device *p_dev) unsigned int prod_id; struct lbs_private *priv; struct if_cs_card *card; - const struct firmware *helper = NULL; - const struct firmware *mainfw = NULL; lbs_deb_enter(LBS_DEB_CS); card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL); - if (!card) { - pr_err("error in kzalloc\n"); + if (!card) goto out; - } + card->p_dev = p_dev; p_dev->priv = card; @@ -859,19 +896,20 @@ static int if_cs_probe(struct pcmcia_device *p_dev) * Most of the libertas cards can do unaligned register access, but some * weird ones cannot. That's especially true for the CF8305 card. */ - card->align_regs = 0; + card->align_regs = false; card->model = get_model(p_dev->manf_id, p_dev->card_id); if (card->model == MODEL_UNKNOWN) { pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n", p_dev->manf_id, p_dev->card_id); + ret = -ENODEV; goto out2; } /* Check if we have a current silicon */ prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID); if (card->model == MODEL_8305) { - card->align_regs = 1; + card->align_regs = true; if (prod_id < IF_CS_CF8305_B1_REV) { pr_err("8305 rev B0 and older are not supported\n"); ret = -ENODEV; @@ -891,20 +929,6 @@ static int if_cs_probe(struct pcmcia_device *p_dev) goto out2; } - ret = lbs_get_firmware(&p_dev->dev, NULL, NULL, card->model, - &fw_table[0], &helper, &mainfw); - if (ret) { - pr_err("failed to find firmware (%d)\n", ret); - goto out2; - } - - /* Load the firmware early, before calling into libertas.ko */ - ret = if_cs_prog_helper(card, helper); - if (ret == 0 && (card->model != MODEL_8305)) - ret = if_cs_prog_real(card, mainfw); - if (ret) - goto out2; - /* Make this card known to the libertas driver */ priv = lbs_add_card(card, &p_dev->dev); if (!priv) { @@ -912,37 +936,22 @@ static int if_cs_probe(struct pcmcia_device *p_dev) goto out2; } - /* Finish setting up fields in lbs_private */ + /* Set up fields in lbs_private */ card->priv = priv; priv->card = card; priv->hw_host_to_card = if_cs_host_to_card; priv->enter_deep_sleep = NULL; priv->exit_deep_sleep = NULL; priv->reset_deep_sleep_wakeup = NULL; - priv->fw_ready = 1; - /* Now actually get the IRQ */ - ret = request_irq(p_dev->irq, if_cs_interrupt, - IRQF_SHARED, DRV_NAME, card); + /* Get firmware */ + ret = lbs_get_firmware_async(priv, &p_dev->dev, card->model, fw_table, + if_cs_prog_firmware); if (ret) { - pr_err("error in request_irq\n"); - goto out3; - } - - /* - * Clear any interrupt cause that happened while sending - * firmware/initializing card - */ - if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK); - if_cs_enable_ints(card); - - /* And finally bring the card up */ - if (lbs_start_card(priv) != 0) { - pr_err("could not activate card\n"); + pr_err("failed to find firmware (%d)\n", ret); goto out3; } - ret = 0; goto out; out3: @@ -952,11 +961,6 @@ out2: out1: pcmcia_disable_device(p_dev); out: - if (helper) - release_firmware(helper); - if (mainfw) - release_firmware(mainfw); - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; } @@ -992,7 +996,6 @@ static const struct pcmcia_device_id if_cs_ids[] = { }; MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); - static struct pcmcia_driver lbs_driver = { .owner = THIS_MODULE, .name = DRV_NAME, @@ -1000,26 +1003,4 @@ static struct pcmcia_driver lbs_driver = { .remove = if_cs_detach, .id_table = if_cs_ids, }; - - -static int __init if_cs_init(void) -{ - int ret; - - lbs_deb_enter(LBS_DEB_CS); - ret = pcmcia_register_driver(&lbs_driver); - lbs_deb_leave(LBS_DEB_CS); - return ret; -} - - -static void __exit if_cs_exit(void) -{ - lbs_deb_enter(LBS_DEB_CS); - pcmcia_unregister_driver(&lbs_driver); - lbs_deb_leave(LBS_DEB_CS); -} - - -module_init(if_cs_init); -module_exit(if_cs_exit); +module_pcmcia_driver(lbs_driver); diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index a7b5cb0c275..33ceda296c9 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -29,7 +29,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/firmware.h> #include <linux/netdevice.h> @@ -39,6 +39,7 @@ #include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio.h> #include <linux/mmc/host.h> +#include <linux/pm_runtime.h> #include "host.h" #include "decl.h" @@ -47,6 +48,8 @@ #include "cmd.h" #include "if_sdio.h" +static void if_sdio_interrupt(struct sdio_func *func); + /* The if_sdio_remove() callback function is called when * user removes this module from kernel space or ejects * the card from the slot. The driver handles these 2 cases @@ -62,12 +65,6 @@ */ static u8 user_rmmod; -static char *lbs_helper_name = NULL; -module_param_named(helper_name, lbs_helper_name, charp, 0644); - -static char *lbs_fw_name = NULL; -module_param_named(fw_name, lbs_fw_name, charp, 0644); - static const struct sdio_device_id if_sdio_ids[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_LIBERTAS) }, @@ -120,11 +117,8 @@ struct if_sdio_card { int model; unsigned long ioport; unsigned int scratch_reg; - - const char *helper; - const char *firmware; - bool helper_allocated; - bool firmware_allocated; + bool started; + wait_queue_head_t pwron_waitq; u8 buffer[65536] __attribute__((aligned(4))); @@ -137,6 +131,9 @@ struct if_sdio_card { u8 rx_unit; }; +static void if_sdio_finish_power_on(struct if_sdio_card *card); +static int if_sdio_power_off(struct if_sdio_card *card); + /********************************************************************/ /* I/O */ /********************************************************************/ @@ -501,7 +498,7 @@ static int if_sdio_prog_helper(struct if_sdio_card *card, */ mdelay(2); - chunk_size = min(size, (size_t)60); + chunk_size = min_t(size_t, size, 60); *((__le32*)chunk_buffer) = cpu_to_le32(chunk_size); memcpy(chunk_buffer + 4, firmware, chunk_size); @@ -591,17 +588,38 @@ static int if_sdio_prog_real(struct if_sdio_card *card, size = fw->size; while (size) { - ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); - if (ret) - goto release; + timeout = jiffies + HZ; + while (1) { + ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); + if (ret) + goto release; - req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); - if (ret) - goto release; + req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, + &ret); + if (ret) + goto release; + + req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, + &ret) << 8; + if (ret) + goto release; + + /* + * For SD8688 wait until the length is not 0, 1 or 2 + * before downloading the first FW block, + * since BOOT code writes the register to indicate the + * helper/FW download winner, + * the value could be 1 or 2 (Func1 or Func2). + */ + if ((size != fw->size) || (req_size > 2)) + break; + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + goto release; + } + mdelay(1); + } - req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; - if (ret) - goto release; /* lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size); */ @@ -621,7 +639,7 @@ static int if_sdio_prog_real(struct if_sdio_card *card, req_size = size; while (req_size) { - chunk_size = min(req_size, (size_t)512); + chunk_size = min_t(size_t, req_size, 512); memcpy(chunk_buffer, firmware, chunk_size); /* @@ -677,12 +695,35 @@ out: return ret; } +static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *helper, + const struct firmware *mainfw) +{ + struct if_sdio_card *card = priv->card; + + if (ret) { + pr_err("failed to find firmware (%d)\n", ret); + return; + } + + ret = if_sdio_prog_helper(card, helper); + if (ret) + return; + + lbs_deb_sdio("Helper firmware loaded\n"); + + ret = if_sdio_prog_real(card, mainfw); + if (ret) + return; + + lbs_deb_sdio("Firmware loaded\n"); + if_sdio_finish_power_on(card); +} + static int if_sdio_prog_firmware(struct if_sdio_card *card) { int ret; u16 scratch; - const struct firmware *helper = NULL; - const struct firmware *mainfw = NULL; lbs_deb_enter(LBS_DEB_SDIO); @@ -716,47 +757,184 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card) */ if (scratch == IF_SDIO_FIRMWARE_OK) { lbs_deb_sdio("firmware already loaded\n"); - goto success; + if_sdio_finish_power_on(card); + return 0; } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) { lbs_deb_sdio("firmware may be running\n"); - goto success; + if_sdio_finish_power_on(card); + return 0; } - ret = lbs_get_firmware(&card->func->dev, lbs_helper_name, lbs_fw_name, - card->model, &fw_table[0], &helper, &mainfw); - if (ret) { - pr_err("failed to find firmware (%d)\n", ret); - goto out; + ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model, + fw_table, if_sdio_do_prog_firmware); + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +/********************************************************************/ +/* Power management */ +/********************************************************************/ + +/* Finish power on sequence (after firmware is loaded) */ +static void if_sdio_finish_power_on(struct if_sdio_card *card) +{ + struct sdio_func *func = card->func; + struct lbs_private *priv = card->priv; + int ret; + + sdio_claim_host(func); + sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); + + /* + * Get rx_unit if the chip is SD8688 or newer. + * SD8385 & SD8686 do not have rx_unit. + */ + if ((card->model != MODEL_8385) + && (card->model != MODEL_8686)) + card->rx_unit = if_sdio_read_rx_unit(card); + else + card->rx_unit = 0; + + /* + * Set up the interrupt handler late. + * + * If we set it up earlier, the (buggy) hardware generates a spurious + * interrupt, even before the interrupt has been enabled, with + * CCCR_INTx = 0. + * + * We register the interrupt handler late so that we can handle any + * spurious interrupts, and also to avoid generation of that known + * spurious interrupt in the first place. + */ + ret = sdio_claim_irq(func, if_sdio_interrupt); + if (ret) + goto release; + + /* + * Enable interrupts now that everything is set up + */ + sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); + if (ret) + goto release_irq; + + sdio_release_host(func); + + /* Set fw_ready before queuing any commands so that + * lbs_thread won't block from sending them to firmware. + */ + priv->fw_ready = 1; + + /* + * FUNC_INIT is required for SD8688 WLAN/BT multiple functions + */ + if (card->model == MODEL_8688) { + struct cmd_header cmd; + + memset(&cmd, 0, sizeof(cmd)); + + lbs_deb_sdio("send function INIT command\n"); + if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd), + lbs_cmd_copyback, (unsigned long) &cmd)) + netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n"); } - ret = if_sdio_prog_helper(card, helper); + wake_up(&card->pwron_waitq); + + if (!card->started) { + ret = lbs_start_card(priv); + if_sdio_power_off(card); + if (ret == 0) { + card->started = true; + /* Tell PM core that we don't need the card to be + * powered now */ + pm_runtime_put(&func->dev); + } + } + + return; + +release_irq: + sdio_release_irq(func); +release: + sdio_release_host(func); +} + +static int if_sdio_power_on(struct if_sdio_card *card) +{ + struct sdio_func *func = card->func; + struct mmc_host *host = func->card->host; + int ret; + + sdio_claim_host(func); + + ret = sdio_enable_func(func); if (ret) - goto out; + goto release; - lbs_deb_sdio("Helper firmware loaded\n"); + /* For 1-bit transfers to the 8686 model, we need to enable the + * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 + * bit to allow access to non-vendor registers. */ + if ((card->model == MODEL_8686) && + (host->caps & MMC_CAP_SDIO_IRQ) && + (host->ios.bus_width == MMC_BUS_WIDTH_1)) { + u8 reg; - ret = if_sdio_prog_real(card, mainfw); + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret); + if (ret) + goto disable; + + reg |= SDIO_BUS_ECSI; + sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret); + if (ret) + goto disable; + } + + card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); if (ret) - goto out; + goto disable; - lbs_deb_sdio("Firmware loaded\n"); + card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; + if (ret) + goto disable; -success: - sdio_claim_host(card->func); - sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); - sdio_release_host(card->func); - ret = 0; + card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; + if (ret) + goto disable; -out: - if (helper) - release_firmware(helper); - if (mainfw) - release_firmware(mainfw); + sdio_release_host(func); + ret = if_sdio_prog_firmware(card); + if (ret) { + sdio_claim_host(func); + goto disable; + } - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return 0; + +disable: + sdio_disable_func(func); +release: + sdio_release_host(func); return ret; } +static int if_sdio_power_off(struct if_sdio_card *card) +{ + struct sdio_func *func = card->func; + struct lbs_private *priv = card->priv; + + priv->fw_ready = 0; + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_disable_func(func); + sdio_release_host(func); + return 0; +} + + /*******************************************************************/ /* Libertas callbacks */ /*******************************************************************/ @@ -892,6 +1070,69 @@ static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv) } +static struct mmc_host *reset_host; + +static void if_sdio_reset_card_worker(struct work_struct *work) +{ + /* + * The actual reset operation must be run outside of lbs_thread. This + * is because mmc_remove_host() will cause the device to be instantly + * destroyed, and the libertas driver then needs to end lbs_thread, + * leading to a deadlock. + * + * We run it in a workqueue totally independent from the if_sdio_card + * instance for that reason. + */ + + pr_info("Resetting card..."); + mmc_remove_host(reset_host); + mmc_add_host(reset_host); +} +static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker); + +static void if_sdio_reset_card(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + + if (work_pending(&card_reset_work)) + return; + + reset_host = card->func->card->host; + schedule_work(&card_reset_work); +} + +static int if_sdio_power_save(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int ret; + + flush_workqueue(card->workqueue); + + ret = if_sdio_power_off(card); + + /* Let runtime PM know the card is powered off */ + pm_runtime_put_sync(&card->func->dev); + + return ret; +} + +static int if_sdio_power_restore(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int r; + + /* Make sure the card will not be powered off by runtime PM */ + pm_runtime_get_sync(&card->func->dev); + + r = if_sdio_power_on(card); + if (r) + return r; + + wait_event(card->pwron_waitq, priv->fw_ready); + return 0; +} + + /*******************************************************************/ /* SDIO callbacks */ /*******************************************************************/ @@ -907,7 +1148,7 @@ static void if_sdio_interrupt(struct sdio_func *func) card = sdio_get_drvdata(func); cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret); - if (ret) + if (ret || !cause) goto out; lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause); @@ -945,7 +1186,6 @@ static int if_sdio_probe(struct sdio_func *func, int ret, i; unsigned int model; struct if_sdio_packet *packet; - struct mmc_host *host = func->card->host; lbs_deb_enter(LBS_DEB_SDIO); @@ -990,6 +1230,7 @@ static int if_sdio_probe(struct sdio_func *func, spin_lock_init(&card->lock); card->workqueue = create_workqueue("libertas_sdio"); INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); + init_waitqueue_head(&card->pwron_waitq); /* Check if we support this card */ for (i = 0; i < ARRAY_SIZE(fw_table); i++) { @@ -1002,49 +1243,6 @@ static int if_sdio_probe(struct sdio_func *func, goto free; } - sdio_claim_host(func); - - ret = sdio_enable_func(func); - if (ret) - goto release; - - ret = sdio_claim_irq(func, if_sdio_interrupt); - if (ret) - goto disable; - - /* For 1-bit transfers to the 8686 model, we need to enable the - * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 - * bit to allow access to non-vendor registers. */ - if ((card->model == MODEL_8686) && - (host->caps & MMC_CAP_SDIO_IRQ) && - (host->ios.bus_width == MMC_BUS_WIDTH_1)) { - u8 reg; - - func->card->quirks |= MMC_QUIRK_LENIENT_FN0; - reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret); - if (ret) - goto release_int; - - reg |= SDIO_BUS_ECSI; - sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret); - if (ret) - goto release_int; - } - - card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); - if (ret) - goto release_int; - - card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; - if (ret) - goto release_int; - - card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; - if (ret) - goto release_int; - - sdio_release_host(func); - sdio_set_drvdata(func, card); lbs_deb_sdio("class = 0x%X, vendor = 0x%X, " @@ -1052,14 +1250,11 @@ static int if_sdio_probe(struct sdio_func *func, func->class, func->vendor, func->device, model, (unsigned)card->ioport); - ret = if_sdio_prog_firmware(card); - if (ret) - goto reclaim; priv = lbs_add_card(card, &func->dev); if (!priv) { ret = -ENOMEM; - goto reclaim; + goto free; } card->priv = priv; @@ -1069,44 +1264,11 @@ static int if_sdio_probe(struct sdio_func *func, priv->enter_deep_sleep = if_sdio_enter_deep_sleep; priv->exit_deep_sleep = if_sdio_exit_deep_sleep; priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; + priv->reset_card = if_sdio_reset_card; + priv->power_save = if_sdio_power_save; + priv->power_restore = if_sdio_power_restore; - sdio_claim_host(func); - - /* - * Get rx_unit if the chip is SD8688 or newer. - * SD8385 & SD8686 do not have rx_unit. - */ - if ((card->model != MODEL_8385) - && (card->model != MODEL_8686)) - card->rx_unit = if_sdio_read_rx_unit(card); - else - card->rx_unit = 0; - - /* - * Enable interrupts now that everything is set up - */ - sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); - sdio_release_host(func); - if (ret) - goto reclaim; - - priv->fw_ready = 1; - - /* - * FUNC_INIT is required for SD8688 WLAN/BT multiple functions - */ - if (card->model == MODEL_8688) { - struct cmd_header cmd; - - memset(&cmd, 0, sizeof(cmd)); - - lbs_deb_sdio("send function INIT command\n"); - if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd), - lbs_cmd_copyback, (unsigned long) &cmd)) - netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n"); - } - - ret = lbs_start_card(priv); + ret = if_sdio_power_on(card); if (ret) goto err_activate_card; @@ -1118,14 +1280,6 @@ out: err_activate_card: flush_workqueue(card->workqueue); lbs_remove_card(priv); -reclaim: - sdio_claim_host(func); -release_int: - sdio_release_irq(func); -disable: - sdio_disable_func(func); -release: - sdio_release_host(func); free: destroy_workqueue(card->workqueue); while (card->packets) { @@ -1134,10 +1288,6 @@ free: kfree(packet); } - if (card->helper_allocated) - kfree(card->helper); - if (card->firmware_allocated) - kfree(card->firmware); kfree(card); goto out; @@ -1152,6 +1302,9 @@ static void if_sdio_remove(struct sdio_func *func) card = sdio_get_drvdata(func); + /* Undo decrement done above in if_sdio_probe */ + pm_runtime_get_noresume(&func->dev); + if (user_rmmod && (card->model == MODEL_8688)) { /* * FUNC_SHUTDOWN is required for SD8688 WLAN/BT @@ -1176,23 +1329,13 @@ static void if_sdio_remove(struct sdio_func *func) flush_workqueue(card->workqueue); destroy_workqueue(card->workqueue); - sdio_claim_host(func); - sdio_release_irq(func); - sdio_disable_func(func); - sdio_release_host(func); - while (card->packets) { packet = card->packets; card->packets = card->packets->next; kfree(packet); } - if (card->helper_allocated) - kfree(card->helper); - if (card->firmware_allocated) - kfree(card->firmware); kfree(card); - lbs_deb_leave(LBS_DEB_SDIO); } @@ -1204,6 +1347,11 @@ static int if_sdio_suspend(struct device *dev) mmc_pm_flag_t flags = sdio_get_host_pm_caps(func); + /* If we're powered off anyway, just let the mmc layer remove the + * card. */ + if (!lbs_iface_active(card->priv)) + return -ENOSYS; + dev_info(dev, "%s: suspend: PM flags = 0x%x\n", sdio_func_id(func), flags); @@ -1290,6 +1438,8 @@ static void __exit if_sdio_exit_module(void) /* Set the flag as user is removing this module. */ user_rmmod = 1; + cancel_work_sync(&card_reset_work); + sdio_unregister_driver(&if_sdio_driver); lbs_deb_leave(LBS_DEB_SDIO); diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index 463352c890d..f11728a866f 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -19,7 +19,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/moduleparam.h> +#include <linux/hardirq.h> +#include <linux/interrupt.h> +#include <linux/module.h> #include <linux/firmware.h> #include <linux/jiffies.h> #include <linux/list.h> @@ -91,7 +93,6 @@ static void free_if_spi_card(struct if_spi_card *card) list_del(&packet->list); kfree(packet); } - spi_set_drvdata(card->spi, NULL); kfree(card); } @@ -529,10 +530,6 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card, goto out; err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_CMD_DOWNLOAD_OVER); - goto out; - - lbs_deb_spi("waiting for helper to boot...\n"); - out: if (err) pr_err("failed to load helper firmware (err=%d)\n", err); @@ -997,6 +994,7 @@ static int if_spi_host_to_card(struct lbs_private *priv, spin_unlock_irqrestore(&card->buffer_lock, flags); break; default: + kfree(packet); netdev_err(priv->dev, "can't transfer buffer of type %d\n", type); err = -EINVAL; @@ -1032,7 +1030,6 @@ static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id) static int if_spi_init_card(struct if_spi_card *card) { struct lbs_private *priv = card->priv; - struct spi_device *spi = card->spi; int err, i; u32 scratch; const struct firmware *helper = NULL; @@ -1066,9 +1063,8 @@ static int if_spi_init_card(struct if_spi_card *card) goto out; } - err = lbs_get_firmware(&card->spi->dev, NULL, NULL, - card->card_id, &fw_table[0], &helper, - &mainfw); + err = lbs_get_firmware(&card->spi->dev, card->card_id, + &fw_table[0], &helper, &mainfw); if (err) { netdev_err(priv->dev, "failed to find firmware (%d)\n", err); @@ -1080,8 +1076,9 @@ static int if_spi_init_card(struct if_spi_card *card) "attached to SPI bus_num %d, chip_select %d. " "spi->max_speed_hz=%d\n", card->card_id, card->card_rev, - spi->master->bus_num, spi->chip_select, - spi->max_speed_hz); + card->spi->master->bus_num, + card->spi->chip_select, + card->spi->max_speed_hz); err = if_spi_prog_helper_firmware(card, helper); if (err) goto out; @@ -1096,13 +1093,7 @@ static int if_spi_init_card(struct if_spi_card *card) goto out; out: - if (helper) - release_firmware(helper); - if (mainfw) - release_firmware(mainfw); - lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); - return err; } @@ -1128,11 +1119,11 @@ static void if_spi_resume_worker(struct work_struct *work) } } -static int __devinit if_spi_probe(struct spi_device *spi) +static int if_spi_probe(struct spi_device *spi) { struct if_spi_card *card; struct lbs_private *priv = NULL; - struct libertas_spi_platform_data *pdata = spi->dev.platform_data; + struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev); int err = 0; lbs_deb_enter(LBS_DEB_SPI); @@ -1230,7 +1221,7 @@ out: return err; } -static int __devexit libertas_spi_remove(struct spi_device *spi) +static int libertas_spi_remove(struct spi_device *spi) { struct if_spi_card *card = spi_get_drvdata(spi); struct lbs_private *priv = card->priv; @@ -1289,10 +1280,9 @@ static const struct dev_pm_ops if_spi_pm_ops = { static struct spi_driver libertas_spi_driver = { .probe = if_spi_probe, - .remove = __devexit_p(libertas_spi_remove), + .remove = libertas_spi_remove, .driver = { .name = "libertas_spi", - .bus = &spi_bus_type, .owner = THIS_MODULE, .pm = &if_spi_pm_ops, }, diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index b5acc393a65..dff08a2896a 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -5,11 +5,12 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/delay.h> -#include <linux/moduleparam.h> +#include <linux/module.h> #include <linux/firmware.h> #include <linux/netdevice.h> #include <linux/slab.h> #include <linux/usb.h> +#include <linux/olpc-ec.h> #ifdef CONFIG_OLPC #include <asm/olpc.h> @@ -29,9 +30,6 @@ #define MESSAGE_HEADER_LEN 4 -static char *lbs_fw_name = NULL; -module_param_named(fw_name, lbs_fw_name, charp, 0644); - MODULE_FIRMWARE("libertas/usb8388_v9.bin"); MODULE_FIRMWARE("libertas/usb8388_v5.bin"); MODULE_FIRMWARE("libertas/usb8388.bin"); @@ -44,6 +42,16 @@ enum { MODEL_8682 = 0x2 }; +/* table of firmware file names */ +static const struct lbs_fw_table fw_table[] = { + { MODEL_8388, "libertas/usb8388_olpc.bin", NULL }, + { MODEL_8388, "libertas/usb8388_v9.bin", NULL }, + { MODEL_8388, "libertas/usb8388_v5.bin", NULL }, + { MODEL_8388, "libertas/usb8388.bin", NULL }, + { MODEL_8388, "usb8388.bin", NULL }, + { MODEL_8682, "libertas/usb8682.bin", NULL } +}; + static struct usb_device_id if_usb_table[] = { /* Enter the device signature inside */ { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 }, @@ -55,10 +63,9 @@ MODULE_DEVICE_TABLE(usb, if_usb_table); static void if_usb_receive(struct urb *urb); static void if_usb_receive_fwload(struct urb *urb); -static int __if_usb_prog_firmware(struct if_usb_card *cardp, - const char *fwname, int cmd); -static int if_usb_prog_firmware(struct if_usb_card *cardp, - const char *fwname, int cmd); +static void if_usb_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *fw, + const struct firmware *unused); static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, uint8_t *payload, uint16_t nb); static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, @@ -67,69 +74,6 @@ static void if_usb_free(struct if_usb_card *cardp); static int if_usb_submit_rx_urb(struct if_usb_card *cardp); static int if_usb_reset_device(struct if_usb_card *cardp); -/* sysfs hooks */ - -/* - * Set function to write firmware to device's persistent memory - */ -static ssize_t if_usb_firmware_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct if_usb_card *cardp = priv->card; - int ret; - - BUG_ON(buf == NULL); - - ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_FW); - if (ret == 0) - return count; - - return ret; -} - -/* - * lbs_flash_fw attribute to be exported per ethX interface through sysfs - * (/sys/class/net/ethX/lbs_flash_fw). Use this like so to write firmware to - * the device's persistent memory: - * echo usb8388-5.126.0.p5.bin > /sys/class/net/ethX/lbs_flash_fw - */ -static DEVICE_ATTR(lbs_flash_fw, 0200, NULL, if_usb_firmware_set); - -/** - * if_usb_boot2_set - write firmware to device's persistent memory - * - * @dev: target device - * @attr: device attributes - * @buf: firmware buffer to write - * @count: number of bytes to write - * - * returns: number of bytes written or negative error code - */ -static ssize_t if_usb_boot2_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct if_usb_card *cardp = priv->card; - int ret; - - BUG_ON(buf == NULL); - - ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_BOOT2); - if (ret == 0) - return count; - - return ret; -} - -/* - * lbs_flash_boot2 attribute to be exported per ethX interface through sysfs - * (/sys/class/net/ethX/lbs_flash_boot2). Use this like so to write firmware - * to the device's persistent memory: - * echo usb8388-5.126.0.p5.bin > /sys/class/net/ethX/lbs_flash_boot2 - */ -static DEVICE_ATTR(lbs_flash_boot2, 0200, NULL, if_usb_boot2_set); - /** * if_usb_write_bulk_callback - callback function to handle the status * of the URB @@ -256,15 +200,14 @@ static int if_usb_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *endpoint; struct lbs_private *priv; struct if_usb_card *cardp; + int r = -ENOMEM; int i; udev = interface_to_usbdev(intf); cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); - if (!cardp) { - pr_err("Out of memory allocating private data\n"); + if (!cardp) goto error; - } setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); init_waitqueue_head(&cardp->fw_wq); @@ -315,20 +258,10 @@ static int if_usb_probe(struct usb_interface *intf, goto dealloc; } - /* Upload firmware */ - kparam_block_sysfs_write(fw_name); - if (__if_usb_prog_firmware(cardp, lbs_fw_name, BOOT_CMD_FW_BY_USB)) { - kparam_unblock_sysfs_write(fw_name); - lbs_deb_usbd(&udev->dev, "FW upload failed\n"); - goto err_prog_firmware; - } - kparam_unblock_sysfs_write(fw_name); - - if (!(priv = lbs_add_card(cardp, &udev->dev))) - goto err_prog_firmware; + if (!(priv = lbs_add_card(cardp, &intf->dev))) + goto err_add_card; cardp->priv = priv; - cardp->priv->fw_ready = 1; priv->hw_host_to_card = if_usb_host_to_card; priv->enter_deep_sleep = NULL; @@ -341,42 +274,25 @@ static int if_usb_probe(struct usb_interface *intf, cardp->boot2_version = udev->descriptor.bcdDevice; - if_usb_submit_rx_urb(cardp); - - if (lbs_start_card(priv)) - goto err_start_card; - - if_usb_setup_firmware(priv); - usb_get_dev(udev); usb_set_intfdata(intf, cardp); - if (device_create_file(&priv->dev->dev, &dev_attr_lbs_flash_fw)) - netdev_err(priv->dev, - "cannot register lbs_flash_fw attribute\n"); - - if (device_create_file(&priv->dev->dev, &dev_attr_lbs_flash_boot2)) - netdev_err(priv->dev, - "cannot register lbs_flash_boot2 attribute\n"); - - /* - * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware. - */ - priv->wol_criteria = EHS_REMOVE_WAKEUP; - if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) - priv->ehs_remove_supported = false; + r = lbs_get_firmware_async(priv, &udev->dev, cardp->model, + fw_table, if_usb_prog_firmware); + if (r) + goto err_get_fw; return 0; -err_start_card: +err_get_fw: lbs_remove_card(priv); -err_prog_firmware: +err_add_card: if_usb_reset_device(cardp); dealloc: if_usb_free(cardp); error: - return -ENOMEM; + return r; } /** @@ -387,17 +303,13 @@ error: static void if_usb_disconnect(struct usb_interface *intf) { struct if_usb_card *cardp = usb_get_intfdata(intf); - struct lbs_private *priv = (struct lbs_private *) cardp->priv; + struct lbs_private *priv = cardp->priv; lbs_deb_enter(LBS_DEB_MAIN); - device_remove_file(&priv->dev->dev, &dev_attr_lbs_flash_boot2); - device_remove_file(&priv->dev->dev, &dev_attr_lbs_flash_fw); - cardp->surprise_removed = 1; if (priv) { - priv->surpriseremoved = 1; lbs_stop_card(priv); lbs_remove_card(priv); } @@ -914,103 +826,25 @@ static int check_fwfile_format(const uint8_t *data, uint32_t totlen) return ret; } - -/** -* if_usb_prog_firmware - programs the firmware subject to cmd -* -* @cardp: the if_usb_card descriptor -* @fwname: firmware or boot2 image file name -* @cmd: either BOOT_CMD_FW_BY_USB, BOOT_CMD_UPDATE_FW, -* or BOOT_CMD_UPDATE_BOOT2. -* returns: 0 or error code -*/ -static int if_usb_prog_firmware(struct if_usb_card *cardp, - const char *fwname, int cmd) -{ - struct lbs_private *priv = cardp->priv; - unsigned long flags, caps; - int ret; - - caps = priv->fwcapinfo; - if (((cmd == BOOT_CMD_UPDATE_FW) && !(caps & FW_CAPINFO_FIRMWARE_UPGRADE)) || - ((cmd == BOOT_CMD_UPDATE_BOOT2) && !(caps & FW_CAPINFO_BOOT2_UPGRADE))) - return -EOPNOTSUPP; - - /* Ensure main thread is idle. */ - spin_lock_irqsave(&priv->driver_lock, flags); - while (priv->cur_cmd != NULL || priv->dnld_sent != DNLD_RES_RECEIVED) { - spin_unlock_irqrestore(&priv->driver_lock, flags); - if (wait_event_interruptible(priv->waitq, - (priv->cur_cmd == NULL && - priv->dnld_sent == DNLD_RES_RECEIVED))) { - return -ERESTARTSYS; - } - spin_lock_irqsave(&priv->driver_lock, flags); - } - priv->dnld_sent = DNLD_BOOTCMD_SENT; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - ret = __if_usb_prog_firmware(cardp, fwname, cmd); - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->dnld_sent = DNLD_RES_RECEIVED; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - wake_up_interruptible(&priv->waitq); - - return ret; -} - -/* table of firmware file names */ -static const struct { - u32 model; - const char *fwname; -} fw_table[] = { - { MODEL_8388, "libertas/usb8388_v9.bin" }, - { MODEL_8388, "libertas/usb8388_v5.bin" }, - { MODEL_8388, "libertas/usb8388.bin" }, - { MODEL_8388, "usb8388.bin" }, - { MODEL_8682, "libertas/usb8682.bin" } -}; - -static int get_fw(struct if_usb_card *cardp, const char *fwname) -{ - int i; - - /* Try user-specified firmware first */ - if (fwname) - return request_firmware(&cardp->fw, fwname, &cardp->udev->dev); - - /* Otherwise search for firmware to use */ - for (i = 0; i < ARRAY_SIZE(fw_table); i++) { - if (fw_table[i].model != cardp->model) - continue; - if (request_firmware(&cardp->fw, fw_table[i].fwname, - &cardp->udev->dev) == 0) - return 0; - } - - return -ENOENT; -} - -static int __if_usb_prog_firmware(struct if_usb_card *cardp, - const char *fwname, int cmd) +static void if_usb_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *fw, + const struct firmware *unused) { + struct if_usb_card *cardp = priv->card; int i = 0; static int reset_count = 10; - int ret = 0; lbs_deb_enter(LBS_DEB_USB); - ret = get_fw(cardp, fwname); if (ret) { pr_err("failed to find firmware (%d)\n", ret); goto done; } + cardp->fw = fw; if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { ret = -EINVAL; - goto release_fw; + goto done; } /* Cancel any pending usb business */ @@ -1027,14 +861,14 @@ restart: if (if_usb_submit_rx_urb_fwload(cardp) < 0) { lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); ret = -EIO; - goto release_fw; + goto done; } cardp->bootcmdresp = 0; do { int j = 0; i++; - if_usb_issue_boot_command(cardp, cmd); + if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); /* wait for command response */ do { j++; @@ -1049,14 +883,14 @@ restart: usb_kill_urb(cardp->tx_urb); if (if_usb_submit_rx_urb(cardp) < 0) ret = -EIO; - goto release_fw; + goto done; } else if (cardp->bootcmdresp <= 0) { if (--reset_count >= 0) { if_usb_reset_device(cardp); goto restart; } ret = -EIO; - goto release_fw; + goto done; } i = 0; @@ -1087,16 +921,27 @@ restart: pr_info("FW download failure, time = %d ms\n", i * 100); ret = -EIO; - goto release_fw; + goto done; } - release_fw: - release_firmware(cardp->fw); - cardp->fw = NULL; + cardp->priv->fw_ready = 1; + if_usb_submit_rx_urb(cardp); + + if (lbs_start_card(priv)) + goto done; + + if_usb_setup_firmware(priv); + + /* + * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware. + */ + priv->wol_criteria = EHS_REMOVE_WAKEUP; + if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) + priv->ehs_remove_supported = false; done: - lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret); - return ret; + cardp->fw = NULL; + lbs_deb_leave(LBS_DEB_USB); } @@ -1109,8 +954,19 @@ static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) lbs_deb_enter(LBS_DEB_USB); - if (priv->psstate != PS_STATE_FULL_POWER) - return -1; + if (priv->psstate != PS_STATE_FULL_POWER) { + ret = -1; + goto out; + } + +#ifdef CONFIG_OLPC + if (machine_is_olpc()) { + if (priv->wol_criteria == EHS_REMOVE_WAKEUP) + olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN); + else + olpc_ec_wakeup_set(EC_SCI_SRC_WLAN); + } +#endif ret = lbs_suspend(priv); if (ret) @@ -1152,31 +1008,10 @@ static struct usb_driver if_usb_driver = { .suspend = if_usb_suspend, .resume = if_usb_resume, .reset_resume = if_usb_resume, + .disable_hub_initiated_lpm = 1, }; -static int __init if_usb_init_module(void) -{ - int ret = 0; - - lbs_deb_enter(LBS_DEB_MAIN); - - ret = usb_register(&if_usb_driver); - - lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); - return ret; -} - -static void __exit if_usb_exit_module(void) -{ - lbs_deb_enter(LBS_DEB_MAIN); - - usb_deregister(&if_usb_driver); - - lbs_deb_leave(LBS_DEB_MAIN); -} - -module_init(if_usb_init_module); -module_exit(if_usb_exit_module); +module_usb_driver(if_usb_driver); MODULE_DESCRIPTION("8388 USB WLAN Driver"); MODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc."); diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 8c40949cb07..0c02f0483d1 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -6,9 +6,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/moduleparam.h> +#include <linux/module.h> #include <linux/delay.h> #include <linux/etherdevice.h> +#include <linux/hardirq.h> #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/kthread.h> @@ -22,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 @@ -97,6 +99,75 @@ u8 lbs_data_rate_to_fw_index(u32 rate) return 0; } +int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type) +{ + int ret = 0; + + switch (type) { + case NL80211_IFTYPE_MONITOR: + ret = lbs_set_monitor_mode(priv, 1); + break; + case NL80211_IFTYPE_STATION: + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) + ret = lbs_set_monitor_mode(priv, 0); + if (!ret) + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1); + break; + case NL80211_IFTYPE_ADHOC: + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) + ret = lbs_set_monitor_mode(priv, 0); + if (!ret) + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2); + break; + default: + ret = -ENOTSUPP; + } + return ret; +} + +int lbs_start_iface(struct lbs_private *priv) +{ + struct cmd_ds_802_11_mac_address cmd; + int ret; + + if (priv->power_restore) { + ret = priv->power_restore(priv); + if (ret) + return ret; + } + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + memcpy(cmd.macadd, priv->current_addr, ETH_ALEN); + + ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); + if (ret) { + lbs_deb_net("set MAC address failed\n"); + goto err; + } + + ret = lbs_set_iface_type(priv, priv->wdev->iftype); + if (ret) { + lbs_deb_net("set iface type failed\n"); + goto err; + } + + ret = lbs_set_11d_domain_info(priv); + if (ret) { + lbs_deb_net("set 11d domain info failed\n"); + goto err; + } + + lbs_update_channel(priv); + + priv->iface_running = true; + return 0; + +err: + if (priv->power_save) + priv->power_save(priv); + return ret; +} /** * lbs_dev_open - open the ethX interface @@ -110,23 +181,65 @@ static int lbs_dev_open(struct net_device *dev) int ret = 0; lbs_deb_enter(LBS_DEB_NET); + if (!priv->iface_running) { + ret = lbs_start_iface(priv); + if (ret) + goto out; + } spin_lock_irq(&priv->driver_lock); - priv->stopping = false; - if (priv->connect_status == LBS_CONNECTED) - netif_carrier_on(dev); - else - netif_carrier_off(dev); + netif_carrier_off(dev); if (!priv->tx_pending_len) netif_wake_queue(dev); spin_unlock_irq(&priv->driver_lock); + +out: lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); return ret; } +static bool lbs_command_queue_empty(struct lbs_private *priv) +{ + unsigned long flags; + bool ret; + spin_lock_irqsave(&priv->driver_lock, flags); + ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq); + spin_unlock_irqrestore(&priv->driver_lock, flags); + return ret; +} + +int lbs_stop_iface(struct lbs_private *priv) +{ + unsigned long flags; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MAIN); + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->iface_running = false; + kfree_skb(priv->currenttxskb); + priv->currenttxskb = NULL; + priv->tx_pending_len = 0; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + cancel_work_sync(&priv->mcast_work); + del_timer_sync(&priv->tx_lockup_timer); + + /* Disable command processing, and wait for all commands to complete */ + lbs_deb_main("waiting for commands to complete\n"); + wait_event(priv->waitq, lbs_command_queue_empty(priv)); + lbs_deb_main("all commands completed\n"); + + if (priv->power_save) + ret = priv->power_save(priv); + + lbs_deb_leave(LBS_DEB_MAIN); + return ret; +} + /** * lbs_eth_stop - close the ethX interface * @@ -139,17 +252,22 @@ static int lbs_eth_stop(struct net_device *dev) lbs_deb_enter(LBS_DEB_NET); + if (priv->connect_status == LBS_CONNECTED) + lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING); + spin_lock_irq(&priv->driver_lock); - priv->stopping = true; netif_stop_queue(dev); spin_unlock_irq(&priv->driver_lock); - schedule_work(&priv->mcast_work); + lbs_update_mcast(priv); cancel_delayed_work_sync(&priv->scan_work); - if (priv->scan_req) { - cfg80211_scan_done(priv->scan_req, false); - priv->scan_req = NULL; - } + if (priv->scan_req) + lbs_scan_done(priv); + + netif_carrier_off(priv->dev); + + if (!lbs_iface_active(priv)) + lbs_stop_iface(priv); lbs_deb_leave(LBS_DEB_NET); return 0; @@ -162,13 +280,14 @@ void lbs_host_to_card_done(struct lbs_private *priv) lbs_deb_enter(LBS_DEB_THREAD); spin_lock_irqsave(&priv->driver_lock, flags); + del_timer(&priv->tx_lockup_timer); priv->dnld_sent = DNLD_RES_RECEIVED; /* Wake main thread if commands are pending */ if (!priv->cur_cmd || priv->tx_pending_len > 0) { if (!priv->wakeup_dev_required) - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); } spin_unlock_irqrestore(&priv->driver_lock, flags); @@ -181,29 +300,24 @@ int lbs_set_mac_address(struct net_device *dev, void *addr) int ret = 0; struct lbs_private *priv = dev->ml_priv; struct sockaddr *phwaddr = addr; - struct cmd_ds_802_11_mac_address cmd; lbs_deb_enter(LBS_DEB_NET); + /* + * Can only set MAC address when all interfaces are down, to be written + * to the hardware when one of them is brought up. + */ + if (lbs_iface_active(priv)) + return -EBUSY; + /* In case it was called from the mesh device */ dev = priv->dev; - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - memcpy(cmd.macadd, phwaddr->sa_data, ETH_ALEN); - - ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); - if (ret) { - lbs_deb_net("set MAC address failed\n"); - goto done; - } - memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN); if (priv->mesh_dev) memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN); -done: lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); return ret; } @@ -257,18 +371,18 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, return i; } -static void lbs_set_mcast_worker(struct work_struct *work) +void lbs_update_mcast(struct lbs_private *priv) { - struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); struct cmd_ds_mac_multicast_adr mcast_cmd; - int dev_flags; + int dev_flags = 0; int nr_addrs; int old_mac_control = priv->mac_control; lbs_deb_enter(LBS_DEB_NET); - dev_flags = priv->dev->flags; - if (priv->mesh_dev) + if (netif_running(priv->dev)) + dev_flags |= priv->dev->flags; + if (priv->mesh_dev && netif_running(priv->mesh_dev)) dev_flags |= priv->mesh_dev->flags; if (dev_flags & IFF_PROMISC) { @@ -314,6 +428,12 @@ static void lbs_set_mcast_worker(struct work_struct *work) lbs_deb_leave(LBS_DEB_NET); } +static void lbs_set_mcast_worker(struct work_struct *work) +{ + struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); + lbs_update_mcast(priv); +} + void lbs_set_multicast_list(struct net_device *dev) { struct lbs_private *priv = dev->ml_priv; @@ -451,7 +571,10 @@ static int lbs_thread(void *data) netdev_info(dev, "Timeout submitting command 0x%04x\n", le16_to_cpu(cmdnode->cmdbuf->command)); lbs_complete_command(priv, cmdnode, -ETIMEDOUT); - if (priv->reset_card) + + /* Reset card, but only when it isn't in the process + * of being shutdown anyway. */ + if (!dev->dismantle && priv->reset_card) priv->reset_card(priv); } priv->cmd_timed_out = 0; @@ -503,6 +626,9 @@ static int lbs_thread(void *data) if (ret) { lbs_deb_tx("host_to_card failed %d\n", ret); priv->dnld_sent = DNLD_RES_RECEIVED; + } else { + mod_timer(&priv->tx_lockup_timer, + jiffies + (HZ * 5)); } priv->tx_pending_len = 0; if (!priv->currenttxskb) { @@ -511,7 +637,7 @@ static int lbs_thread(void *data) if (priv->connect_status == LBS_CONNECTED) netif_wake_queue(priv->dev); if (priv->mesh_dev && - lbs_mesh_connected(priv)) + netif_running(priv->mesh_dev)) netif_wake_queue(priv->mesh_dev); } } @@ -519,6 +645,7 @@ static int lbs_thread(void *data) } del_timer(&priv->command_timer); + del_timer(&priv->tx_lockup_timer); del_timer(&priv->auto_deepsleep_timer); lbs_deb_leave(LBS_DEB_THREAD); @@ -555,8 +682,10 @@ static int lbs_setup_firmware(struct lbs_private *priv) /* Send cmd to FW to enable 11D function */ ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); + if (ret) + goto done; - lbs_set_mac_control(priv); + ret = lbs_set_mac_control_sync(priv); done: lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); return ret; @@ -638,13 +767,47 @@ static void lbs_cmd_timeout_handler(unsigned long data) le16_to_cpu(priv->cur_cmd->cmdbuf->command)); priv->cmd_timed_out = 1; - wake_up_interruptible(&priv->waitq); + + /* + * If the device didn't even acknowledge the command, reset the state + * so that we don't block all future commands due to this one timeout. + */ + if (priv->dnld_sent == DNLD_CMD_SENT) + priv->dnld_sent = DNLD_RES_RECEIVED; + + wake_up(&priv->waitq); out: spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_CMD); } /** + * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames + * to the hardware. This is known to frequently happen with SD8686 when + * waking up after a Wake-on-WLAN-triggered resume. + * + * @data: &struct lbs_private pointer + */ +static void lbs_tx_lockup_handler(unsigned long data) +{ + struct lbs_private *priv = (struct lbs_private *)data; + unsigned long flags; + + lbs_deb_enter(LBS_DEB_TX); + spin_lock_irqsave(&priv->driver_lock, flags); + + netdev_info(priv->dev, "TX lockup detected\n"); + if (priv->reset_card) + priv->reset_card(priv); + + priv->dnld_sent = DNLD_RES_RECEIVED; + wake_up_interruptible(&priv->waitq); + + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_TX); +} + +/** * auto_deepsleep_timer_fn - put the device back to deep sleep mode when * timer expires and no activity (command, event, data etc.) is detected. * @data: &struct lbs_private pointer @@ -726,10 +889,13 @@ static int lbs_init_adapter(struct lbs_private *priv) priv->is_host_sleep_configured = 0; priv->is_host_sleep_activated = 0; init_waitqueue_head(&priv->host_sleep_q); + init_waitqueue_head(&priv->fw_waitq); mutex_init(&priv->lock); setup_timer(&priv->command_timer, lbs_cmd_timeout_handler, (unsigned long)priv); + setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler, + (unsigned long)priv); setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, (unsigned long)priv); @@ -767,6 +933,7 @@ static void lbs_free_adapter(struct lbs_private *priv) lbs_free_cmd_buffer(priv); kfifo_free(&priv->event_fifo); del_timer(&priv->command_timer); + del_timer(&priv->tx_lockup_timer); del_timer(&priv->auto_deepsleep_timer); lbs_deb_leave(LBS_DEB_MAIN); @@ -777,7 +944,7 @@ static const struct net_device_ops lbs_netdev_ops = { .ndo_stop = lbs_eth_stop, .ndo_start_xmit = lbs_hard_start_xmit, .ndo_set_mac_address = lbs_set_mac_address, - .ndo_set_multicast_list = lbs_set_multicast_list, + .ndo_set_rx_mode = lbs_set_multicast_list, .ndo_change_mtu = eth_change_mtu, .ndo_validate_addr = eth_validate_addr, }; @@ -878,11 +1045,11 @@ void lbs_remove_card(struct lbs_private *priv) lbs_deb_enter(LBS_DEB_MAIN); lbs_remove_mesh(priv); - lbs_scan_deinit(priv); - dev = priv->dev; + if (priv->wiphy_registered) + lbs_scan_deinit(priv); - cancel_work_sync(&priv->mcast_work); + lbs_wait_for_firmware_load(priv); /* worker thread destruction blocks on the in-flight command which * should have been cleared already in lbs_stop_card(). @@ -941,17 +1108,18 @@ 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_disablemesh) - lbs_init_mesh(priv); - else - pr_info("%s: mesh disabled\n", dev->name); + if (lbs_mesh_activated(priv)) + lbs_start_mesh(priv); lbs_debugfs_init_one(priv, dev); @@ -969,8 +1137,6 @@ EXPORT_SYMBOL_GPL(lbs_start_card); void lbs_stop_card(struct lbs_private *priv) { struct net_device *dev; - struct cmd_ctrl_node *cmdnode; - unsigned long flags; lbs_deb_enter(LBS_DEB_MAIN); @@ -978,35 +1144,16 @@ void lbs_stop_card(struct lbs_private *priv) goto out; dev = priv->dev; + /* If the netdev isn't registered, it means that lbs_start_card() was + * never called so we have nothing to do here. */ + if (dev->reg_state != NETREG_REGISTERED) + goto out; + netif_stop_queue(dev); netif_carrier_off(dev); lbs_debugfs_remove_one(priv); lbs_deinit_mesh(priv); - - /* Delete the timeout of the currently processing command */ - del_timer_sync(&priv->command_timer); - del_timer_sync(&priv->auto_deepsleep_timer); - - /* Flush pending command nodes */ - spin_lock_irqsave(&priv->driver_lock, flags); - lbs_deb_main("clearing pending commands\n"); - list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { - cmdnode->result = -ENOENT; - cmdnode->cmdwaitqwoken = 1; - wake_up_interruptible(&cmdnode->cmdwait_q); - } - - /* Flush the command the card is currently processing */ - if (priv->cur_cmd) { - lbs_deb_main("clearing current command\n"); - priv->cur_cmd->result = -ENOENT; - priv->cur_cmd->cmdwaitqwoken = 1; - wake_up_interruptible(&priv->cur_cmd->cmdwait_q); - } - lbs_deb_main("done clearing commands\n"); - spin_unlock_irqrestore(&priv->driver_lock, flags); - unregister_netdev(dev); out: @@ -1027,7 +1174,7 @@ void lbs_queue_event(struct lbs_private *priv, u32 event) kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32)); - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_leave(LBS_DEB_THREAD); @@ -1045,117 +1192,12 @@ void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) BUG_ON(resp_idx > 1); priv->resp_idx = resp_idx; - wake_up_interruptible(&priv->waitq); + wake_up(&priv->waitq); lbs_deb_leave(LBS_DEB_THREAD); } EXPORT_SYMBOL_GPL(lbs_notify_command_response); -/** - * lbs_get_firmware - Retrieves two-stage firmware - * - * @dev: A pointer to &device structure - * @user_helper: User-defined helper firmware file - * @user_mainfw: User-defined main firmware file - * @card_model: Bus-specific card model ID used to filter firmware table - * elements - * @fw_table: Table of firmware file names and device model numbers - * terminated by an entry with a NULL helper name - * @helper: On success, the helper firmware; caller must free - * @mainfw: On success, the main firmware; caller must free - * - * returns: 0 on success, non-zero on failure - */ -int lbs_get_firmware(struct device *dev, const char *user_helper, - const char *user_mainfw, u32 card_model, - const struct lbs_fw_table *fw_table, - const struct firmware **helper, - const struct firmware **mainfw) -{ - const struct lbs_fw_table *iter; - int ret; - - BUG_ON(helper == NULL); - BUG_ON(mainfw == NULL); - - /* Try user-specified firmware first */ - if (user_helper) { - ret = request_firmware(helper, user_helper, dev); - if (ret) { - dev_err(dev, "couldn't find helper firmware %s\n", - user_helper); - goto fail; - } - } - if (user_mainfw) { - ret = request_firmware(mainfw, user_mainfw, dev); - if (ret) { - dev_err(dev, "couldn't find main firmware %s\n", - user_mainfw); - goto fail; - } - } - - if (*helper && *mainfw) - return 0; - - /* Otherwise search for firmware to use. If neither the helper or - * the main firmware were specified by the user, then we need to - * make sure that found helper & main are from the same entry in - * fw_table. - */ - iter = fw_table; - while (iter && iter->helper) { - if (iter->model != card_model) - goto next; - - if (*helper == NULL) { - ret = request_firmware(helper, iter->helper, dev); - if (ret) - goto next; - - /* If the device has one-stage firmware (ie cf8305) and - * we've got it then we don't need to bother with the - * main firmware. - */ - if (iter->fwname == NULL) - return 0; - } - - if (*mainfw == NULL) { - ret = request_firmware(mainfw, iter->fwname, dev); - if (ret && !user_helper) { - /* Clear the helper if it wasn't user-specified - * and the main firmware load failed, to ensure - * we don't have mismatched firmware pairs. - */ - release_firmware(*helper); - *helper = NULL; - } - } - - if (*helper && *mainfw) - return 0; - - next: - iter++; - } - - fail: - /* Failed */ - if (*helper) { - release_firmware(*helper); - *helper = NULL; - } - if (*mainfw) { - release_firmware(*mainfw); - *mainfw = NULL; - } - - return -ENOENT; -} -EXPORT_SYMBOL_GPL(lbs_get_firmware); - static int __init lbs_init_module(void) { lbs_deb_enter(LBS_DEB_MAIN); diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index 24cf06680c6..6fef746345b 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -2,6 +2,7 @@ #include <linux/delay.h> #include <linux/etherdevice.h> +#include <linux/hardirq.h> #include <linux/netdevice.h> #include <linux/if_ether.h> #include <linux/if_arp.h> @@ -14,6 +15,131 @@ #include "cmd.h" +static int lbs_add_mesh(struct lbs_private *priv); + +/*************************************************************************** + * Mesh command handling + */ + +static int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, + struct cmd_ds_mesh_access *cmd) +{ + int ret; + + lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); + + cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS); + cmd->hdr.size = cpu_to_le16(sizeof(*cmd)); + cmd->hdr.result = 0; + + cmd->action = cpu_to_le16(cmd_action); + + ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +static int __lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type) +{ + int ret; + u16 command = CMD_MESH_CONFIG_OLD; + + lbs_deb_enter(LBS_DEB_CMD); + + /* + * Command id is 0xac for v10 FW along with mesh interface + * id in bits 14-13-12. + */ + if (priv->mesh_tlv == TLV_TYPE_MESH_ID) + command = CMD_MESH_CONFIG | + (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET); + + cmd->hdr.command = cpu_to_le16(command); + cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config)); + cmd->hdr.result = 0; + + cmd->type = cpu_to_le16(type); + cmd->action = cpu_to_le16(action); + + ret = lbs_cmd_with_response(priv, command, cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +static int lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type) +{ + int ret; + + if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) + return -EOPNOTSUPP; + + ret = __lbs_mesh_config_send(priv, cmd, action, type); + return ret; +} + +/* This function is the CMD_MESH_CONFIG legacy function. It only handles the + * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG + * are all handled by preparing a struct cmd_ds_mesh_config and passing it to + * lbs_mesh_config_send. + */ +static int lbs_mesh_config(struct lbs_private *priv, uint16_t action, + uint16_t chan) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_meshie *ie; + DECLARE_SSID_BUF(ssid); + + memset(&cmd, 0, sizeof(cmd)); + cmd.channel = cpu_to_le16(chan); + ie = (struct mrvl_meshie *)cmd.data; + + switch (action) { + case CMD_ACT_MESH_CONFIG_START: + ie->id = WLAN_EID_VENDOR_SPECIFIC; + ie->val.oui[0] = 0x00; + ie->val.oui[1] = 0x50; + ie->val.oui[2] = 0x43; + ie->val.type = MARVELL_MESH_IE_TYPE; + ie->val.subtype = MARVELL_MESH_IE_SUBTYPE; + ie->val.version = MARVELL_MESH_IE_VERSION; + ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP; + ie->val.active_metric_id = MARVELL_MESH_METRIC_ID; + ie->val.mesh_capability = MARVELL_MESH_CAPABILITY; + ie->val.mesh_id_len = priv->mesh_ssid_len; + memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len); + ie->len = sizeof(struct mrvl_meshie_val) - + IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len; + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val)); + break; + case CMD_ACT_MESH_CONFIG_STOP: + break; + default: + return -1; + } + lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n", + action, priv->mesh_tlv, chan, + print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len)); + + return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); +} + +int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel) +{ + priv->mesh_channel = channel; + return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel); +} + +static uint16_t lbs_mesh_get_channel(struct lbs_private *priv) +{ + return priv->mesh_channel ?: 1; +} + /*************************************************************************** * Mesh sysfs support */ @@ -114,7 +240,7 @@ static ssize_t lbs_prb_rsp_limit_set(struct device *dev, memset(&mesh_access, 0, sizeof(mesh_access)); mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET); - if (!strict_strtoul(buf, 10, &retry_limit)) + if (!kstrtoul(buf, 10, &retry_limit)) return -ENOTSUPP; if (retry_limit > 15) return -ENOTSUPP; @@ -154,17 +280,11 @@ static ssize_t lbs_mesh_set(struct device *dev, { struct lbs_private *priv = to_net_dev(dev)->ml_priv; int enable; - int ret, action = CMD_ACT_MESH_CONFIG_STOP; sscanf(buf, "%x", &enable); enable = !!enable; if (enable == !!priv->mesh_dev) return count; - if (enable) - action = CMD_ACT_MESH_CONFIG_START; - ret = lbs_mesh_config(priv, action, priv->channel); - if (ret) - return ret; if (enable) lbs_add_mesh(priv); @@ -199,582 +319,11 @@ static struct attribute *lbs_mesh_sysfs_entries[] = { NULL, }; -static struct attribute_group lbs_mesh_attr_group = { +static const struct attribute_group lbs_mesh_attr_group = { .attrs = lbs_mesh_sysfs_entries, }; - -/*************************************************************************** - * Initializing and starting, stopping mesh - */ - -/* - * Check mesh FW version and appropriately send the mesh start - * command - */ -int lbs_init_mesh(struct lbs_private *priv) -{ - struct net_device *dev = priv->dev; - int ret = 0; - - lbs_deb_enter(LBS_DEB_MESH); - - priv->mesh_connect_status = LBS_DISCONNECTED; - - /* Determine mesh_fw_ver from fwrelease and fwcapinfo */ - /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */ - /* 5.110.22 have mesh command with 0xa3 command id */ - /* 10.0.0.p0 FW brings in mesh config command with different id */ - /* Check FW version MSB and initialize mesh_fw_ver */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) { - /* Enable mesh, if supported, and work out which TLV it uses. - 0x100 + 291 is an unofficial value used in 5.110.20.pXX - 0x100 + 37 is the official value used in 5.110.21.pXX - but we check them in that order because 20.pXX doesn't - give an error -- it just silently fails. */ - - /* 5.110.20.pXX firmware will fail the command if the channel - doesn't match the existing channel. But only if the TLV - is correct. If the channel is wrong, _BOTH_ versions will - give an error to 0x100+291, and allow 0x100+37 to succeed. - It's just that 5.110.20.pXX will not have done anything - useful */ - - priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, - priv->channel)) { - priv->mesh_tlv = TLV_TYPE_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, - priv->channel)) - priv->mesh_tlv = 0; - } - } else - if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && - (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) { - /* 10.0.0.pXX new firmwares should succeed with TLV - * 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)) - priv->mesh_tlv = 0; - } - - - 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; - } - - lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); - return ret; -} - - -int lbs_deinit_mesh(struct lbs_private *priv) -{ - struct net_device *dev = priv->dev; - int ret = 0; - - lbs_deb_enter(LBS_DEB_MESH); - - if (priv->mesh_tlv) { - device_remove_file(&dev->dev, &dev_attr_lbs_mesh); - ret = 1; - } - - lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); - return ret; -} - - -/** - * lbs_mesh_stop - close the mshX interface - * - * @dev: A pointer to &net_device structure - * returns: 0 - */ -static int lbs_mesh_stop(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - - lbs_deb_enter(LBS_DEB_MESH); - spin_lock_irq(&priv->driver_lock); - - priv->mesh_open = 0; - priv->mesh_connect_status = LBS_DISCONNECTED; - - netif_stop_queue(dev); - netif_carrier_off(dev); - - spin_unlock_irq(&priv->driver_lock); - - schedule_work(&priv->mcast_work); - - lbs_deb_leave(LBS_DEB_MESH); - return 0; -} - -/** - * lbs_mesh_dev_open - open the mshX interface - * - * @dev: A pointer to &net_device structure - * returns: 0 or -EBUSY if monitor mode active - */ -static int lbs_mesh_dev_open(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - int ret = 0; - - lbs_deb_enter(LBS_DEB_NET); - - spin_lock_irq(&priv->driver_lock); - - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { - ret = -EBUSY; - goto out; - } - - priv->mesh_open = 1; - priv->mesh_connect_status = LBS_CONNECTED; - netif_carrier_on(dev); - - if (!priv->tx_pending_len) - netif_wake_queue(dev); - out: - - spin_unlock_irq(&priv->driver_lock); - lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); - return ret; -} - -static const struct net_device_ops mesh_netdev_ops = { - .ndo_open = lbs_mesh_dev_open, - .ndo_stop = lbs_mesh_stop, - .ndo_start_xmit = lbs_hard_start_xmit, - .ndo_set_mac_address = lbs_set_mac_address, - .ndo_set_multicast_list = lbs_set_multicast_list, -}; - -/** - * lbs_add_mesh - add mshX interface - * - * @priv: A pointer to the &struct lbs_private structure - * returns: 0 if successful, -X otherwise - */ -int lbs_add_mesh(struct lbs_private *priv) -{ - struct net_device *mesh_dev = NULL; - int ret = 0; - - lbs_deb_enter(LBS_DEB_MESH); - - /* Allocate a virtual mesh device */ - mesh_dev = alloc_netdev(0, "msh%d", ether_setup); - if (!mesh_dev) { - lbs_deb_mesh("init mshX device failed\n"); - ret = -ENOMEM; - goto done; - } - mesh_dev->ml_priv = priv; - priv->mesh_dev = mesh_dev; - - mesh_dev->netdev_ops = &mesh_netdev_ops; - mesh_dev->ethtool_ops = &lbs_ethtool_ops; - memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, ETH_ALEN); - - SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); - - mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - /* Register virtual mesh interface */ - ret = register_netdev(mesh_dev); - if (ret) { - pr_err("cannot register mshX virtual interface\n"); - goto err_free; - } - - ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); - if (ret) - goto err_unregister; - - lbs_persist_config_init(mesh_dev); - - /* Everything successful */ - ret = 0; - goto done; - -err_unregister: - unregister_netdev(mesh_dev); - -err_free: - free_netdev(mesh_dev); - -done: - lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); - return ret; -} - -void lbs_remove_mesh(struct lbs_private *priv) -{ - struct net_device *mesh_dev; - - mesh_dev = priv->mesh_dev; - if (!mesh_dev) - return; - - lbs_deb_enter(LBS_DEB_MESH); - netif_stop_queue(mesh_dev); - netif_carrier_off(mesh_dev); - sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); - lbs_persist_config_remove(mesh_dev); - unregister_netdev(mesh_dev); - priv->mesh_dev = NULL; - free_netdev(mesh_dev); - lbs_deb_leave(LBS_DEB_MESH); -} - - - -/*************************************************************************** - * Sending and receiving - */ -struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, - struct net_device *dev, struct rxpd *rxpd) -{ - if (priv->mesh_dev) { - if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) { - if (rxpd->rx_control & RxPD_MESH_FRAME) - dev = priv->mesh_dev; - } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) { - if (rxpd->u.bss.bss_num == MESH_IFACE_ID) - dev = priv->mesh_dev; - } - } - return dev; -} - - -void lbs_mesh_set_txpd(struct lbs_private *priv, - struct net_device *dev, struct txpd *txpd) -{ - if (dev == priv->mesh_dev) { - if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) - txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME); - else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) - txpd->u.bss.bss_num = MESH_IFACE_ID; - } -} - - -/*************************************************************************** - * Mesh command handling - */ - -/** - * lbs_mesh_bt_add_del - Add or delete Mesh Blinding Table entries - * - * @priv: A pointer to &struct lbs_private structure - * @add: TRUE to add the entry, FALSE to delete it - * @addr1: Destination address to blind or unblind - * - * returns: 0 on success, error on failure - */ -int lbs_mesh_bt_add_del(struct lbs_private *priv, bool add, u8 *addr1) -{ - struct cmd_ds_bt_access cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - BUG_ON(addr1 == NULL); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - memcpy(cmd.addr1, addr1, ETH_ALEN); - if (add) { - cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_ADD); - lbs_deb_hex(LBS_DEB_MESH, "BT_ADD: blinded MAC addr", - addr1, ETH_ALEN); - } else { - cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_DEL); - lbs_deb_hex(LBS_DEB_MESH, "BT_DEL: blinded MAC addr", - addr1, ETH_ALEN); - } - - ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd); - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_mesh_bt_reset - Reset/clear the mesh blinding table - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: 0 on success, error on failure - */ -int lbs_mesh_bt_reset(struct lbs_private *priv) -{ - struct cmd_ds_bt_access cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_RESET); - - ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd); - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_mesh_bt_get_inverted - Gets the inverted status of the mesh - * blinding table - * - * Normally the firmware "blinds" or ignores traffic from mesh nodes in the - * table, but an inverted table allows *only* traffic from nodes listed in - * the table. - * - * @priv: A pointer to &struct lbs_private structure - * @inverted: On success, TRUE if the blinding table is inverted, - * FALSE if it is not inverted - * - * returns: 0 on success, error on failure - */ -int lbs_mesh_bt_get_inverted(struct lbs_private *priv, bool *inverted) -{ - struct cmd_ds_bt_access cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - BUG_ON(inverted == NULL); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_GET_INVERT); - - ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd); - if (ret == 0) - *inverted = !!cmd.id; - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_mesh_bt_set_inverted - Sets the inverted status of the mesh - * blinding table - * - * Normally the firmware "blinds" or ignores traffic from mesh nodes in the - * table, but an inverted table allows *only* traffic from nodes listed in - * the table. - * - * @priv: A pointer to &struct lbs_private structure - * @inverted: TRUE to invert the blinding table (only traffic from - * listed nodes allowed), FALSE to return it - * to normal state (listed nodes ignored) - * - * returns: 0 on success, error on failure - */ -int lbs_mesh_bt_set_inverted(struct lbs_private *priv, bool inverted) -{ - struct cmd_ds_bt_access cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_SET_INVERT); - cmd.id = cpu_to_le32(!!inverted); - - ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd); - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_mesh_bt_get_entry - List an entry in the mesh blinding table - * - * @priv: A pointer to &struct lbs_private structure - * @id: The ID of the entry to list - * @addr1: MAC address associated with the table entry - * - * returns: 0 on success, error on failure - */ -int lbs_mesh_bt_get_entry(struct lbs_private *priv, u32 id, u8 *addr1) -{ - struct cmd_ds_bt_access cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - BUG_ON(addr1 == NULL); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_SET_INVERT); - cmd.id = cpu_to_le32(id); - - ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd); - if (ret == 0) - memcpy(addr1, cmd.addr1, sizeof(cmd.addr1)); - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_cmd_fwt_access - Access the mesh forwarding table - * - * @priv: A pointer to &struct lbs_private structure - * @cmd_action: The forwarding table action to perform - * @cmd: The pre-filled FWT_ACCESS command - * - * returns: 0 on success and 'cmd' will be filled with the - * firmware's response - */ -int lbs_cmd_fwt_access(struct lbs_private *priv, u16 cmd_action, - struct cmd_ds_fwt_access *cmd) -{ - int ret; - - lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); - - cmd->hdr.command = cpu_to_le16(CMD_FWT_ACCESS); - cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access)); - cmd->hdr.result = 0; - cmd->action = cpu_to_le16(cmd_action); - - ret = lbs_cmd_with_response(priv, CMD_FWT_ACCESS, cmd); - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return 0; -} - -int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, - struct cmd_ds_mesh_access *cmd) -{ - int ret; - - lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); - - cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS); - cmd->hdr.size = cpu_to_le16(sizeof(*cmd)); - cmd->hdr.result = 0; - - cmd->action = cpu_to_le16(cmd_action); - - ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -static int __lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type) -{ - int ret; - u16 command = CMD_MESH_CONFIG_OLD; - - lbs_deb_enter(LBS_DEB_CMD); - - /* - * Command id is 0xac for v10 FW along with mesh interface - * id in bits 14-13-12. - */ - if (priv->mesh_tlv == TLV_TYPE_MESH_ID) - command = CMD_MESH_CONFIG | - (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET); - - cmd->hdr.command = cpu_to_le16(command); - cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config)); - cmd->hdr.result = 0; - - cmd->type = cpu_to_le16(type); - cmd->action = cpu_to_le16(action); - - ret = lbs_cmd_with_response(priv, command, cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -int lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type) -{ - int ret; - - if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) - return -EOPNOTSUPP; - - ret = __lbs_mesh_config_send(priv, cmd, action, type); - return ret; -} - -/* This function is the CMD_MESH_CONFIG legacy function. It only handles the - * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG - * are all handled by preparing a struct cmd_ds_mesh_config and passing it to - * lbs_mesh_config_send. - */ -int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_meshie *ie; - DECLARE_SSID_BUF(ssid); - - memset(&cmd, 0, sizeof(cmd)); - cmd.channel = cpu_to_le16(chan); - ie = (struct mrvl_meshie *)cmd.data; - - switch (action) { - case CMD_ACT_MESH_CONFIG_START: - ie->id = WLAN_EID_GENERIC; - ie->val.oui[0] = 0x00; - ie->val.oui[1] = 0x50; - ie->val.oui[2] = 0x43; - ie->val.type = MARVELL_MESH_IE_TYPE; - ie->val.subtype = MARVELL_MESH_IE_SUBTYPE; - ie->val.version = MARVELL_MESH_IE_VERSION; - ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP; - ie->val.active_metric_id = MARVELL_MESH_METRIC_ID; - ie->val.mesh_capability = MARVELL_MESH_CAPABILITY; - ie->val.mesh_id_len = priv->mesh_ssid_len; - memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len); - ie->len = sizeof(struct mrvl_meshie_val) - - IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len; - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val)); - break; - case CMD_ACT_MESH_CONFIG_STOP: - break; - default: - return -1; - } - lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n", - action, priv->mesh_tlv, chan, - print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len)); - - return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); -} - - - /*************************************************************************** * Persistent configuration support */ @@ -1231,7 +780,7 @@ static struct attribute *boot_opts_attrs[] = { NULL }; -static struct attribute_group boot_opts_group = { +static const struct attribute_group boot_opts_group = { .name = "boot_options", .attrs = boot_opts_attrs, }; @@ -1244,31 +793,323 @@ static struct attribute *mesh_ie_attrs[] = { NULL }; -static struct attribute_group mesh_ie_group = { +static const struct attribute_group mesh_ie_group = { .name = "mesh_ie", .attrs = mesh_ie_attrs, }; -void lbs_persist_config_init(struct net_device *dev) +static void lbs_persist_config_init(struct net_device *dev) { int ret; ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group); ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group); } -void lbs_persist_config_remove(struct net_device *dev) +static void lbs_persist_config_remove(struct net_device *dev) { sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group); sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group); } +/*************************************************************************** + * Initializing and starting, stopping mesh + */ + +/* + * Check mesh FW version and appropriately send the mesh start + * command + */ +int lbs_init_mesh(struct lbs_private *priv) +{ + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + /* Determine mesh_fw_ver from fwrelease and fwcapinfo */ + /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */ + /* 5.110.22 have mesh command with 0xa3 command id */ + /* 10.0.0.p0 FW brings in mesh config command with different id */ + /* Check FW version MSB and initialize mesh_fw_ver */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) { + /* Enable mesh, if supported, and work out which TLV it uses. + 0x100 + 291 is an unofficial value used in 5.110.20.pXX + 0x100 + 37 is the official value used in 5.110.21.pXX + but we check them in that order because 20.pXX doesn't + give an error -- it just silently fails. */ + + /* 5.110.20.pXX firmware will fail the command if the channel + doesn't match the existing channel. But only if the TLV + is correct. If the channel is wrong, _BOTH_ versions will + give an error to 0x100+291, and allow 0x100+37 to succeed. + It's just that 5.110.20.pXX will not have done anything + useful */ + + priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID; + 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, 1)) + priv->mesh_tlv = 0; + } + } else + if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && + (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) { + /* 10.0.0.pXX new firmwares should succeed with TLV + * 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, 1)) + priv->mesh_tlv = 0; + } + + /* Stop meshing until interface is brought up */ + lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1); + + if (priv->mesh_tlv) { + sprintf(priv->mesh_ssid, "mesh"); + priv->mesh_ssid_len = 4; + ret = 1; + } + + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + 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) +{ + struct net_device *dev = priv->dev; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + if (priv->mesh_tlv) { + device_remove_file(&dev->dev, &dev_attr_lbs_mesh); + ret = 1; + } + + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + return ret; +} + + +/** + * lbs_mesh_stop - close the mshX interface + * + * @dev: A pointer to &net_device structure + * returns: 0 + */ +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, + lbs_mesh_get_channel(priv)); + + spin_lock_irq(&priv->driver_lock); + + netif_stop_queue(dev); + netif_carrier_off(dev); + + spin_unlock_irq(&priv->driver_lock); + + lbs_update_mcast(priv); + if (!lbs_iface_active(priv)) + lbs_stop_iface(priv); + + lbs_deb_leave(LBS_DEB_MESH); + return 0; +} + +/** + * lbs_mesh_dev_open - open the mshX interface + * + * @dev: A pointer to &net_device structure + * returns: 0 or -EBUSY if monitor mode active + */ +static int lbs_mesh_dev_open(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + int ret = 0; + + lbs_deb_enter(LBS_DEB_NET); + if (!priv->iface_running) { + ret = lbs_start_iface(priv); + if (ret) + goto out; + } + + spin_lock_irq(&priv->driver_lock); + + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + ret = -EBUSY; + spin_unlock_irq(&priv->driver_lock); + goto out; + } + + netif_carrier_on(dev); + + if (!priv->tx_pending_len) + netif_wake_queue(dev); + + spin_unlock_irq(&priv->driver_lock); + + 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); + return ret; +} + +static const struct net_device_ops mesh_netdev_ops = { + .ndo_open = lbs_mesh_dev_open, + .ndo_stop = lbs_mesh_stop, + .ndo_start_xmit = lbs_hard_start_xmit, + .ndo_set_mac_address = lbs_set_mac_address, + .ndo_set_rx_mode = lbs_set_multicast_list, +}; + +/** + * lbs_add_mesh - add mshX interface + * + * @priv: A pointer to the &struct lbs_private structure + * returns: 0 if successful, -X otherwise + */ +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 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; + mesh_dev->ethtool_ops = &lbs_ethtool_ops; + eth_hw_addr_inherit(mesh_dev, priv->dev); + + SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); + + mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + /* Register virtual mesh interface */ + ret = register_netdev(mesh_dev); + if (ret) { + pr_err("cannot register mshX virtual interface\n"); + goto err_free_netdev; + } + + ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); + if (ret) + goto err_unregister; + + lbs_persist_config_init(mesh_dev); + + /* Everything successful */ + ret = 0; + goto done; + +err_unregister: + unregister_netdev(mesh_dev); + +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; +} + +void lbs_remove_mesh(struct lbs_private *priv) +{ + struct net_device *mesh_dev; + + mesh_dev = priv->mesh_dev; + if (!mesh_dev) + return; + + lbs_deb_enter(LBS_DEB_MESH); + netif_stop_queue(mesh_dev); + netif_carrier_off(mesh_dev); + sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); + 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); +} + + +/*************************************************************************** + * Sending and receiving + */ +struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, + struct net_device *dev, struct rxpd *rxpd) +{ + if (priv->mesh_dev) { + if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) { + if (rxpd->rx_control & RxPD_MESH_FRAME) + dev = priv->mesh_dev; + } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) { + if (rxpd->u.bss.bss_num == MESH_IFACE_ID) + dev = priv->mesh_dev; + } + } + return dev; +} + + +void lbs_mesh_set_txpd(struct lbs_private *priv, + struct net_device *dev, struct txpd *txpd) +{ + if (dev == priv->mesh_dev) { + if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) + txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME); + else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) + txpd->u.bss.bss_num = MESH_IFACE_ID; + } +} + /*************************************************************************** * Ethtool related */ -static const char *mesh_stat_strings[] = { +static const char * const mesh_stat_strings[] = { "drop_duplicate_bcast", "drop_ttl_zero", "drop_no_fwd_route", diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/libertas/mesh.h index ee95c73ed5f..6603f341c87 100644 --- a/drivers/net/wireless/libertas/mesh.h +++ b/drivers/net/wireless/libertas/mesh.h @@ -9,31 +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); -int lbs_add_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 */ @@ -52,29 +46,6 @@ struct cmd_ds_command; struct cmd_ds_mesh_access; struct cmd_ds_mesh_config; -int lbs_mesh_bt_add_del(struct lbs_private *priv, bool add, u8 *addr1); -int lbs_mesh_bt_reset(struct lbs_private *priv); -int lbs_mesh_bt_get_inverted(struct lbs_private *priv, bool *inverted); -int lbs_mesh_bt_set_inverted(struct lbs_private *priv, bool inverted); -int lbs_mesh_bt_get_entry(struct lbs_private *priv, u32 id, u8 *addr1); - -int lbs_cmd_fwt_access(struct lbs_private *priv, u16 cmd_action, - struct cmd_ds_fwt_access *cmd); - -int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, - struct cmd_ds_mesh_access *cmd); -int lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type); -int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan); - - - -/* Persistent configuration */ - -void lbs_persist_config_init(struct net_device *net); -void lbs_persist_config_remove(struct net_device *net); - /* Ethtool statistics */ @@ -87,22 +58,17 @@ void lbs_mesh_ethtool_get_strings(struct net_device *dev, uint32_t stringset, uint8_t *s); -/* Accessors */ - -#define lbs_mesh_open(priv) (priv->mesh_open) -#define lbs_mesh_connected(priv) (priv->mesh_connect_status == LBS_CONNECTED) - #else #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_open(priv) (0) -#define lbs_mesh_connected(priv) (0) +#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 fdb0448301a..e446fed7b34 100644 --- a/drivers/net/wireless/libertas/rx.c +++ b/drivers/net/wireless/libertas/rx.c @@ -5,8 +5,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/etherdevice.h> +#include <linux/hardirq.h> #include <linux/slab.h> #include <linux/types.h> +#include <linux/export.h> #include <net/cfg80211.h> #include "defs.h" @@ -14,6 +16,7 @@ #include "radiotap.h" #include "decl.h" #include "dev.h" +#include "mesh.h" struct eth803hdr { u8 dest_addr[6]; @@ -68,8 +71,10 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) skb->ip_summed = CHECKSUM_NONE; - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) - return process_rxed_802_11_packet(priv, skb); + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + ret = process_rxed_802_11_packet(priv, skb); + goto done; + } p_rx_pd = (struct rxpd *) skb->data; p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd + @@ -83,7 +88,7 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { lbs_deb_rx("rx err: frame received with bad length\n"); dev->stats.rx_length_errors++; - ret = 0; + ret = -EINVAL; dev_kfree_skb(skb); goto done; } diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c index bbb95f88dc0..c025f9c1828 100644 --- a/drivers/net/wireless/libertas/tx.c +++ b/drivers/net/wireless/libertas/tx.c @@ -1,9 +1,11 @@ /* * This file contains the handling of TX in wlan driver. */ +#include <linux/hardirq.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/sched.h> +#include <linux/export.h> #include <net/cfg80211.h> #include "host.h" @@ -11,6 +13,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 @@ -198,7 +201,7 @@ void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count) if (priv->connect_status == LBS_CONNECTED) netif_wake_queue(priv->dev); - if (priv->mesh_dev && lbs_mesh_connected(priv)) + if (priv->mesh_dev && netif_running(priv->mesh_dev)) netif_wake_queue(priv->mesh_dev); } EXPORT_SYMBOL_GPL(lbs_send_tx_feedback); |
