/* * CFG802.11 operation handler for association request. * * This function does not work when the current mode is set to Ad-Hoc, or * when there is already an association procedure going on. The given BSS * information is used to associate. */ static int mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int ret = 0; if (priv->assoc_request) return -EBUSY; if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { wiphy_err(wiphy, "received infra assoc request " "when station is in ibss mode\n"); goto done; } priv->assoc_request = -EINPROGRESS; wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n", (char *) sme->ssid, sme->bssid); ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, priv->bss_mode, sme->channel, sme, 0); priv->assoc_request = 1; done: priv->assoc_result = ret; queue_work(priv->workqueue, &priv->cfg_workqueue); return ret; }
/* * CFG802.11 operation handler to join an IBSS. * * This function does not work in any mode other than Ad-Hoc, or if * a join operation is already in progress. */ static int mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); int ret = 0; if (priv->ibss_join_request) return -EBUSY; if (priv->bss_mode != NL80211_IFTYPE_ADHOC) { wiphy_err(wiphy, "request to join ibss received " "when station is not in ibss mode\n"); goto done; } priv->ibss_join_request = -EINPROGRESS; wiphy_dbg(wiphy, "info: trying to join to %s and bssid %pM\n", (char *) params->ssid, params->bssid); ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, params->bssid, priv->bss_mode, params->channel, NULL, params->privacy); priv->ibss_join_request = 1; done: priv->ibss_join_result = ret; queue_work(priv->workqueue, &priv->cfg_workqueue); return ret; }
/* * CFG802.11 operation handler for association request. * * This function does not work when the current mode is set to Ad-Hoc, or * when there is already an association procedure going on. The given BSS * information is used to associate. */ static int mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int ret = 0; if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { wiphy_err(wiphy, "received infra assoc request " "when station is in ibss mode\n"); goto done; } wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n", (char *) sme->ssid, sme->bssid); ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, priv->bss_mode, sme->channel, sme, 0); done: if (!ret) { cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, NULL, 0, WLAN_STATUS_SUCCESS, GFP_KERNEL); dev_dbg(priv->adapter->dev, "info: associated to bssid %pM successfully\n", priv->cfg_bssid); } else { dev_dbg(priv->adapter->dev, "info: association to bssid %pM failed\n", priv->cfg_bssid); memset(priv->cfg_bssid, 0, ETH_ALEN); } return ret; }
/* * CFG802.11 operation handler to join an IBSS. * * This function does not work in any mode other than Ad-Hoc, or if * a join operation is already in progress. */ static int mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int ret = 0; if (priv->bss_mode != NL80211_IFTYPE_ADHOC) { wiphy_err(wiphy, "request to join ibss received " "when station is not in ibss mode\n"); goto done; } wiphy_dbg(wiphy, "info: trying to join to %s and bssid %pM\n", (char *) params->ssid, params->bssid); ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, params->bssid, priv->bss_mode, params->channel, NULL, params->privacy); done: if (!ret) { cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); dev_dbg(priv->adapter->dev, "info: joined/created adhoc network with bssid" " %pM successfully\n", priv->cfg_bssid); } else { dev_dbg(priv->adapter->dev, "info: failed creating/joining adhoc network\n"); } return ret; }
static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) { int ret, i; int tmo = 2000; switch (priv->join_status) { case CW1200_JOIN_STATUS_PRE_STA: case CW1200_JOIN_STATUS_JOINING: return -EBUSY; default: break; } wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n", scan->type, scan->num_channels, scan->flags); for (i = 0; i < scan->num_channels; ++i) tmo += scan->ch[i].max_chan_time + 10; cancel_delayed_work_sync(&priv->clear_recent_scan_work); atomic_set(&priv->scan.in_progress, 1); atomic_set(&priv->recent_scan, 1); cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo)); queue_delayed_work(priv->workqueue, &priv->scan.timeout, msecs_to_jiffies(tmo)); ret = wsm_scan(priv, scan); if (ret) { atomic_set(&priv->scan.in_progress, 0); cancel_delayed_work_sync(&priv->scan.timeout); cw1200_scan_restart_delayed(priv); } return ret; }
static void cw1200_scan_restart_delayed(struct cw1200_common *priv) { if (priv->delayed_link_loss) { int tmo = priv->cqm_beacon_loss_count; if (priv->scan.direct_probe) tmo = 0; priv->delayed_link_loss = 0; /* Restart beacon loss timer and requeue BSS loss work. */ wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss in %d " "beacons.\n", tmo); spin_lock(&priv->bss_loss_lock); priv->bss_loss_status = CW1200_BSS_LOSS_NONE; spin_unlock(&priv->bss_loss_lock); cancel_delayed_work_sync(&priv->bss_loss_work); queue_delayed_work(priv->workqueue, &priv->bss_loss_work, tmo * HZ / 10); } /* FW bug: driver has to restart p2p-dev mode after scan. */ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { cw1200_enable_listening(priv); cw1200_update_filtering(priv); } if (priv->delayed_unjoin) { priv->delayed_unjoin = false; if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) wsm_unlock_tx(priv); } }
static void brcms_ops_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct brcms_info *wl = hw->priv; struct wiphy *wiphy = hw->wiphy; changed_flags &= MAC_FILTERS; *total_flags &= MAC_FILTERS; if (changed_flags & FIF_PROMISC_IN_BSS) wiphy_dbg(wiphy, "FIF_PROMISC_IN_BSS\n"); if (changed_flags & FIF_ALLMULTI) wiphy_dbg(wiphy, "FIF_ALLMULTI\n"); if (changed_flags & FIF_FCSFAIL) wiphy_dbg(wiphy, "FIF_FCSFAIL\n"); if (changed_flags & FIF_CONTROL) wiphy_dbg(wiphy, "FIF_CONTROL\n"); if (changed_flags & FIF_OTHER_BSS) wiphy_dbg(wiphy, "FIF_OTHER_BSS\n"); if (changed_flags & FIF_PSPOLL) wiphy_dbg(wiphy, "FIF_PSPOLL\n"); if (changed_flags & FIF_BCN_PRBRESP_PROMISC) wiphy_dbg(wiphy, "FIF_BCN_PRBRESP_PROMISC\n"); spin_lock_bh(&wl->lock); brcms_c_mac_promisc(wl->wlc, *total_flags); spin_unlock_bh(&wl->lock); return; }
/* * CFG802.11 operation handler to change interface type. */ static int mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { int ret; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (priv->bss_mode == type) { wiphy_warn(wiphy, "already set to required type\n"); return 0; } priv->bss_mode = type; switch (type) { case NL80211_IFTYPE_ADHOC: dev->ieee80211_ptr->iftype = NL80211_IFTYPE_ADHOC; wiphy_dbg(wiphy, "info: setting interface type to adhoc\n"); break; case NL80211_IFTYPE_STATION: dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; wiphy_dbg(wiphy, "info: setting interface type to managed\n"); break; case NL80211_IFTYPE_UNSPECIFIED: dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; wiphy_dbg(wiphy, "info: setting interface type to auto\n"); return 0; default: wiphy_err(wiphy, "unknown interface type: %d\n", type); return -EINVAL; } mwifiex_deauthenticate(priv, NULL); priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_SET_BSS_MODE, HostCmd_ACT_GEN_SET, 0, NULL); return ret; }
static int mwifiex_process_country_ie(struct mwifiex_private *priv, struct cfg80211_bss *bss) { const u8 *country_ie; u8 country_ie_len; struct mwifiex_802_11d_domain_reg *domain_info = &priv->adapter->domain_reg; rcu_read_lock(); country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); if (!country_ie) { rcu_read_unlock(); return 0; } country_ie_len = country_ie[1]; if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { rcu_read_unlock(); return 0; } if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) { rcu_read_unlock(); wiphy_dbg(priv->wdev->wiphy, "11D: skip setting domain info in FW\n"); return 0; } memcpy(priv->adapter->country_code, &country_ie[2], 2); domain_info->country_code[0] = country_ie[2]; domain_info->country_code[1] = country_ie[3]; domain_info->country_code[2] = ' '; country_ie_len -= IEEE80211_COUNTRY_STRING_LEN; domain_info->no_of_triplet = country_ie_len / sizeof(struct ieee80211_country_ie_triplet); memcpy((u8 *)domain_info->triplet, &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); rcu_read_unlock(); if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, HostCmd_ACT_GEN_SET, 0, NULL, false)) { wiphy_err(priv->adapter->wiphy, "11D: setting domain info in FW\n"); return -1; } mwifiex_dnld_txpwr_table(priv); return 0; }
static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed) { struct ieee80211_conf *conf = &hw->conf; struct brcms_info *wl = hw->priv; int err = 0; int new_int; struct wiphy *wiphy = hw->wiphy; spin_lock_bh(&wl->lock); if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { brcms_c_set_beacon_listen_interval(wl->wlc, conf->listen_interval); } if (changed & IEEE80211_CONF_CHANGE_MONITOR) wiphy_dbg(wiphy, "%s: change monitor mode: %s\n", __func__, conf->flags & IEEE80211_CONF_MONITOR ? "true" : "false"); if (changed & IEEE80211_CONF_CHANGE_PS) wiphy_err(wiphy, "%s: change power-save mode: %s (implement)\n", __func__, conf->flags & IEEE80211_CONF_PS ? "true" : "false"); if (changed & IEEE80211_CONF_CHANGE_POWER) { err = brcms_c_set_tx_power(wl->wlc, conf->power_level); if (err < 0) { wiphy_err(wiphy, "%s: Error setting power_level\n", __func__); goto config_out; } new_int = brcms_c_get_tx_power(wl->wlc); if (new_int != conf->power_level) wiphy_err(wiphy, "%s: Power level req != actual, %d %d" "\n", __func__, conf->power_level, new_int); } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { if (conf->channel_type == NL80211_CHAN_HT20 || conf->channel_type == NL80211_CHAN_NO_HT) err = brcms_c_set_channel(wl->wlc, conf->channel->hw_value); else err = -ENOTSUPP; } if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) err = brcms_c_set_rate_limit(wl->wlc, conf->short_frame_max_tx_count, conf->long_frame_max_tx_count); config_out: spin_unlock_bh(&wl->lock); return err; }
static void cw1200_scan_complete(struct cw1200_common *priv) { queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ); if (priv->scan.direct_probe) { wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n"); cw1200_scan_restart_delayed(priv); priv->scan.direct_probe = 0; up(&priv->scan.lock); wsm_unlock_tx(priv); } else { cw1200_scan_work(&priv->scan.work); } }
/* * This function sets the RF channel. * * This function creates multiple IOCTL requests, populates them accordingly * and issues them to set the band/channel and frequency. */ static int mwifiex_set_rf_channel(struct mwifiex_private *priv, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { struct mwifiex_chan_freq_power cfp; struct mwifiex_ds_band_cfg band_cfg; u32 config_bands = 0; struct wiphy *wiphy = priv->wdev->wiphy; if (chan) { memset(&band_cfg, 0, sizeof(band_cfg)); /* Set appropriate bands */ if (chan->band == IEEE80211_BAND_2GHZ) config_bands = BAND_B | BAND_G | BAND_GN; else config_bands = BAND_AN | BAND_A; if (priv->bss_mode == NL80211_IFTYPE_STATION || priv->bss_mode == NL80211_IFTYPE_UNSPECIFIED) { band_cfg.config_bands = config_bands; } else if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { band_cfg.config_bands = config_bands; band_cfg.adhoc_start_band = config_bands; } band_cfg.sec_chan_offset = mwifiex_cfg80211_channel_type_to_mwifiex_channels (channel_type); if (mwifiex_set_radio_band_cfg(priv, &band_cfg)) return -EFAULT; mwifiex_send_domain_info_cmd_fw(wiphy); } wiphy_dbg(wiphy, "info: setting band %d, channel offset %d and " "mode %d\n", config_bands, band_cfg.sec_chan_offset, priv->bss_mode); if (!chan) return 0; memset(&cfp, 0, sizeof(cfp)); cfp.freq = chan->center_freq; cfp.channel = ieee80211_frequency_to_channel(chan->center_freq); if (mwifiex_bss_set_channel(priv, &cfp)) return -EFAULT; return mwifiex_drv_change_adhoc_chan(priv, cfp.channel); }
/* * CFG802.11 operation handler to leave an IBSS. * * This function does not work if a leave operation is * already in progress. */ static int mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); wiphy_dbg(wiphy, "info: disconnecting from essid %pM\n", priv->cfg_bssid); if (mwifiex_deauthenticate(priv, NULL)) return -EFAULT; memset(priv->cfg_bssid, 0, ETH_ALEN); return 0; }
/* * CFG802.11 operation handler to delete a network key. */ static int mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool pairwise, const u8 *mac_addr) { struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); if (mwifiex_set_encode(priv, NULL, 0, key_index, 1)) { wiphy_err(wiphy, "deleting the crypto keys\n"); return -EFAULT; } wiphy_dbg(wiphy, "info: crypto keys deleted\n"); return 0; }
void __wiphy_dbg(struct wiphy *wiphy, bool print, const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, }; va_list args; va_start(args, fmt); vaf.va = &args; if (print) wiphy_dbg(wiphy, "%pV", &vaf); trace_mac80211_dbg(&vaf); va_end(args); }
/* * CFG802.11 operation handler for disconnection request. * * This function does not work when there is already a disconnection * procedure going on. */ static int mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (mwifiex_deauthenticate(priv, NULL)) return -EFAULT; wiphy_dbg(wiphy, "info: successfully disconnected from %pM:" " reason code %d\n", priv->cfg_bssid, reason_code); memset(priv->cfg_bssid, 0, ETH_ALEN); return 0; }
/* * CFG802.11 operation handler for scan request. * * This function issues a scan request to the firmware based upon * the user specified scan configuration. On successfull completion, * it also informs the results. */ static int mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); if (priv->scan_request && priv->scan_request != request) return -EBUSY; priv->scan_request = request; queue_work(priv->workqueue, &priv->cfg_workqueue); return 0; }
/* * CFG802.11 operation handler to set Power Save option. * * The timeout value, if provided, is currently ignored. */ static int mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); u32 ps_mode; if (timeout) wiphy_dbg(wiphy, "info: ignore timeout value for IEEE Power Save\n"); ps_mode = enabled; return mwifiex_drv_set_power(priv, &ps_mode); }
static void cw1200_scan_restart_delayed(struct cw1200_common *priv) { /* FW bug: driver has to restart p2p-dev mode after scan. */ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { cw1200_enable_listening(priv); cw1200_update_filtering(priv); } if (priv->delayed_unjoin) { priv->delayed_unjoin = false; if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) wsm_unlock_tx(priv); } else if (priv->delayed_link_loss) { wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n"); priv->delayed_link_loss = 0; cw1200_cqm_bssloss_sm(priv, 1, 0, 0); } }
/* * CFG802.11 operation handler to leave an IBSS. * * This function does not work if a leave operation is * already in progress. */ static int mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy); if (priv->disconnect) return -EBUSY; priv->disconnect = 1; wiphy_dbg(wiphy, "info: disconnecting from essid %pM\n", priv->cfg_bssid); if (mwifiex_deauthenticate(priv, NULL)) return -EFAULT; queue_work(priv->workqueue, &priv->cfg_workqueue); return 0; }
/* * CFG802.11 operation handler for disconnection request. * * This function does not work when there is already a disconnection * procedure going on. */ static int mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (priv->disconnect) return -EBUSY; priv->disconnect = 1; if (mwifiex_deauthenticate(priv, NULL)) return -EFAULT; wiphy_dbg(wiphy, "info: successfully disconnected from %pM:" " reason code %d\n", priv->cfg_bssid, reason_code); queue_work(priv->workqueue, &priv->cfg_workqueue); return 0; }
/* * CFG802.11 operation handler for scan request. * * This function issues a scan request to the firmware based upon * the user specified scan configuration. On successfull completion, * it also informs the results. */ static int mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int i; struct ieee80211_channel *chan; wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); priv->scan_request = request; priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); if (!priv->user_scan_cfg) { dev_err(priv->adapter->dev, "failed to alloc scan_req\n"); return -ENOMEM; } priv->user_scan_cfg->num_ssids = request->n_ssids; priv->user_scan_cfg->ssid_list = request->ssids; for (i = 0; i < request->n_channels; i++) { chan = request->channels[i]; priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value; priv->user_scan_cfg->chan_list[i].radio_type = chan->band; if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) priv->user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_PASSIVE; else priv->user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_ACTIVE; priv->user_scan_cfg->chan_list[i].scan_time = 0; } if (mwifiex_set_user_scan_ioctl(priv, priv->user_scan_cfg)) return -EFAULT; return 0; }
/* * CFG802.11 regulatory domain callback function. * * This function is called when the regulatory domain is changed due to the * following reasons - * - Set by driver * - Set by system core * - Set by user * - Set bt Country IE */ static int mwifiex_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for %c%c\n", request->alpha2[0], request->alpha2[1]); memcpy(adapter->country_code, request->alpha2, sizeof(request->alpha2)); switch (request->initiator) { case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_CORE: case NL80211_REGDOM_SET_BY_USER: break; /* Todo: apply driver specific changes in channel flags based on the request initiator if necessary. */ case NL80211_REGDOM_SET_BY_COUNTRY_IE: break; } mwifiex_send_domain_info_cmd_fw(wiphy); return 0; }
void cw1200_scan_work(struct work_struct *work) { struct cw1200_common *priv = container_of(work, struct cw1200_common, scan.work); struct ieee80211_channel **it; struct wsm_scan scan = { .type = WSM_SCAN_TYPE_FOREGROUND, .flags = WSM_SCAN_FLAG_SPLIT_METHOD, }; bool first_run = (priv->scan.begin == priv->scan.curr && priv->scan.begin != priv->scan.end); int i; if (first_run) { /* Firmware gets crazy if scan request is sent * when STA is joined but not yet associated. * Force unjoin in this case. */ if (cancel_delayed_work_sync(&priv->join_timeout) > 0) cw1200_join_timeout(&priv->join_timeout.work); } mutex_lock(&priv->conf_mutex); if (first_run) { if (priv->join_status == CW1200_JOIN_STATUS_STA && !(priv->powersave_mode.mode & WSM_PSM_PS)) { struct wsm_set_pm pm = priv->powersave_mode; pm.mode = WSM_PSM_PS; cw1200_set_pm(priv, &pm); } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { /* FW bug: driver has to restart p2p-dev mode * after scan */ cw1200_disable_listening(priv); } } if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { struct cfg80211_scan_info info = { .aborted = priv->scan.status ? 1 : 0, }; if (priv->scan.output_power != priv->output_power) wsm_set_output_power(priv, priv->output_power * 10); if (priv->join_status == CW1200_JOIN_STATUS_STA && !(priv->powersave_mode.mode & WSM_PSM_PS)) cw1200_set_pm(priv, &priv->powersave_mode); if (priv->scan.status < 0) wiphy_warn(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n", priv->scan.status); else if (priv->scan.req) wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan completed.\n"); else wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan canceled.\n"); priv->scan.req = NULL; cw1200_scan_restart_delayed(priv); wsm_unlock_tx(priv); mutex_unlock(&priv->conf_mutex); ieee80211_scan_completed(priv->hw, &info); up(&priv->scan.lock); return; } else { struct ieee80211_channel *first = *priv->scan.curr; for (it = priv->scan.curr + 1, i = 1; it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; ++it, ++i) { if ((*it)->band != first->band) break; if (((*it)->flags ^ first->flags) & IEEE80211_CHAN_NO_IR) break; if (!(first->flags & IEEE80211_CHAN_NO_IR) && (*it)->max_power != first->max_power) break; } scan.band = first->band; if (priv->scan.req->no_cck) scan.max_tx_rate = WSM_TRANSMIT_RATE_6; else scan.max_tx_rate = WSM_TRANSMIT_RATE_1; scan.num_probes = (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2; scan.num_ssids = priv->scan.n_ssids; scan.ssids = &priv->scan.ssids[0]; scan.num_channels = it - priv->scan.curr; /* TODO: Is it optimal? */ scan.probe_delay = 100; /* It is not stated in WSM specification, however * FW team says that driver may not use FG scan * when joined. */ if (priv->join_status == CW1200_JOIN_STATUS_STA) { scan.type = WSM_SCAN_TYPE_BACKGROUND; scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; } scan.ch = kcalloc(it - priv->scan.curr, sizeof(struct wsm_scan_ch), GFP_KERNEL); if (!scan.ch) { priv->scan.status = -ENOMEM; goto fail; } for (i = 0; i < scan.num_channels; ++i) { scan.ch[i].number = priv->scan.curr[i]->hw_value; if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) { scan.ch[i].min_chan_time = 50; scan.ch[i].max_chan_time = 100; } else { scan.ch[i].min_chan_time = 10; scan.ch[i].max_chan_time = 25; } } if (!(first->flags & IEEE80211_CHAN_NO_IR) && priv->scan.output_power != first->max_power) { priv->scan.output_power = first->max_power; wsm_set_output_power(priv, priv->scan.output_power * 10); } priv->scan.status = cw1200_scan_start(priv, &scan); kfree(scan.ch); if (priv->scan.status) goto fail; priv->scan.curr = it; } mutex_unlock(&priv->conf_mutex); return; fail: priv->scan.curr = priv->scan.end; mutex_unlock(&priv->conf_mutex); queue_work(priv->workqueue, &priv->scan.work); return; }
int cw1200_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_scan_request *hw_req) { struct cw1200_common *priv = hw->priv; struct cfg80211_scan_request *req = &hw_req->req; struct wsm_template_frame frame = { .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, }; int i, ret; if (!priv->vif) return -EINVAL; /* Scan when P2P_GO corrupt firmware MiniAP mode */ if (priv->join_status == CW1200_JOIN_STATUS_AP) return -EOPNOTSUPP; if (req->n_ssids == 1 && !req->ssids[0].ssid_len) req->n_ssids = 0; wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", req->n_ssids); if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) return -EINVAL; /* will be unlocked in cw1200_scan_work() */ down(&priv->scan.lock); mutex_lock(&priv->conf_mutex); frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, req->ie_len); if (!frame.skb) { mutex_unlock(&priv->conf_mutex); up(&priv->scan.lock); return -ENOMEM; } if (req->ie_len) skb_put_data(frame.skb, req->ie, req->ie_len); ret = wsm_set_template_frame(priv, &frame); if (!ret) { /* Host want to be the probe responder. */ ret = wsm_set_probe_responder(priv, true); } if (ret) { dev_kfree_skb(frame.skb); mutex_unlock(&priv->conf_mutex); up(&priv->scan.lock); return ret; } wsm_lock_tx(priv); BUG_ON(priv->scan.req); priv->scan.req = req; priv->scan.n_ssids = 0; priv->scan.status = 0; priv->scan.begin = &req->channels[0]; priv->scan.curr = priv->scan.begin; priv->scan.end = &req->channels[req->n_channels]; priv->scan.output_power = priv->output_power; for (i = 0; i < req->n_ssids; ++i) { struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids]; memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); dst->length = req->ssids[i].ssid_len; ++priv->scan.n_ssids; } if (frame.skb) dev_kfree_skb(frame.skb); mutex_unlock(&priv->conf_mutex); queue_work(priv->workqueue, &priv->scan.work); return 0; }
/* * This function sets the RF channel. * * This function creates multiple IOCTL requests, populates them accordingly * and issues them to set the band/channel and frequency. */ static int mwifiex_set_rf_channel(struct mwifiex_private *priv, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { struct mwifiex_chan_freq_power cfp; u32 config_bands = 0; struct wiphy *wiphy = priv->wdev->wiphy; struct mwifiex_adapter *adapter = priv->adapter; if (chan) { /* Set appropriate bands */ if (chan->band == IEEE80211_BAND_2GHZ) { if (channel_type == NL80211_CHAN_NO_HT) if (priv->adapter->config_bands == BAND_B || priv->adapter->config_bands == BAND_G) config_bands = priv->adapter->config_bands; else config_bands = BAND_B | BAND_G; else config_bands = BAND_B | BAND_G | BAND_GN; } else { if (channel_type == NL80211_CHAN_NO_HT) config_bands = BAND_A; else config_bands = BAND_AN | BAND_A; } if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) { adapter->config_bands = config_bands; if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { adapter->adhoc_start_band = config_bands; if ((config_bands & BAND_GN) || (config_bands & BAND_AN)) adapter->adhoc_11n_enabled = true; else adapter->adhoc_11n_enabled = false; } } adapter->sec_chan_offset = mwifiex_cfg80211_channel_type_to_sec_chan_offset (channel_type); adapter->channel_type = channel_type; mwifiex_send_domain_info_cmd_fw(wiphy); } wiphy_dbg(wiphy, "info: setting band %d, chan offset %d, mode %d\n", config_bands, adapter->sec_chan_offset, priv->bss_mode); if (!chan) return 0; memset(&cfp, 0, sizeof(cfp)); cfp.freq = chan->center_freq; cfp.channel = ieee80211_frequency_to_channel(chan->center_freq); if (mwifiex_bss_set_channel(priv, &cfp)) return -EFAULT; if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) return mwifiex_drv_change_adhoc_chan(priv, cfp.channel); else return mwifiex_uap_set_channel(priv, cfp.channel); }
void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) { struct carl9170_rsp *cmd = (void *) buf; struct ieee80211_vif *vif; if (carl9170_check_sequence(ar, cmd->hdr.seq)) return; if ((cmd->hdr.cmd & CARL9170_RSP_FLAG) != CARL9170_RSP_FLAG) { if (!(cmd->hdr.cmd & CARL9170_CMD_ASYNC_FLAG)) carl9170_cmd_callback(ar, len, buf); return; } if (unlikely(cmd->hdr.len != (len - 4))) { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "FW: received over-/under" "sized event %x (%d, but should be %d).\n", cmd->hdr.cmd, cmd->hdr.len, len - 4); print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len); } return; } /* hardware event handlers */ switch (cmd->hdr.cmd) { case CARL9170_RSP_PRETBTT: /* pre-TBTT event */ rcu_read_lock(); vif = carl9170_get_main_vif(ar); if (!vif) { rcu_read_unlock(); break; } switch (vif->type) { case NL80211_IFTYPE_STATION: carl9170_handle_ps(ar, cmd); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: carl9170_update_beacon(ar, true); break; default: break; } rcu_read_unlock(); break; case CARL9170_RSP_TXCOMP: /* TX status notification */ carl9170_tx_process_status(ar, cmd); break; case CARL9170_RSP_BEACON_CONFIG: /* * (IBSS) beacon send notification * bytes: 04 c2 XX YY B4 B3 B2 B1 * * XX always 80 * YY always 00 * B1-B4 "should" be the number of send out beacons. */ break; case CARL9170_RSP_ATIM: /* End of Atim Window */ break; case CARL9170_RSP_WATCHDOG: /* Watchdog Interrupt */ carl9170_restart(ar, CARL9170_RR_WATCHDOG); break; case CARL9170_RSP_TEXT: /* firmware debug */ carl9170_dbg_message(ar, (char *)buf + 4, len - 4); break; case CARL9170_RSP_HEXDUMP: wiphy_dbg(ar->hw->wiphy, "FW: HD %d\n", len - 4); print_hex_dump_bytes("FW:", DUMP_PREFIX_NONE, (char *)buf + 4, len - 4); break; case CARL9170_RSP_RADAR: if (!net_ratelimit()) break; wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this " "incident to [email protected] !\n"); break; case CARL9170_RSP_GPIO: #ifdef CONFIG_CARL9170_WPC if (ar->wps.pbc) { bool state = !!(cmd->gpio.gpio & cpu_to_le32( AR9170_GPIO_PORT_WPS_BUTTON_PRESSED)); if (state != ar->wps.pbc_state) { ar->wps.pbc_state = state; input_report_key(ar->wps.pbc, KEY_WPS_BUTTON, state); input_sync(ar->wps.pbc); } } #endif /* CONFIG_CARL9170_WPC */ break; case CARL9170_RSP_BOOT: complete(&ar->fw_boot_wait); break; default: wiphy_err(ar->hw->wiphy, "FW: received unhandled event %x\n", cmd->hdr.cmd); print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len); break; } }
/* * CFG802.11 operation handler for scan request. * * This function issues a scan request to the firmware based upon * the user specified scan configuration. On successfull completion, * it also informs the results. */ static int mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int i; struct ieee80211_channel *chan; wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); priv->scan_request = request; priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); if (!priv->user_scan_cfg) { dev_err(priv->adapter->dev, "failed to alloc scan_req\n"); return -ENOMEM; } priv->user_scan_cfg->num_ssids = request->n_ssids; priv->user_scan_cfg->ssid_list = request->ssids; if (request->ie && request->ie_len) { for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR) continue; priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN; memcpy(&priv->vs_ie[i].ie, request->ie, request->ie_len); break; } } for (i = 0; i < min_t(u32, request->n_channels, MWIFIEX_USER_SCAN_CHAN_MAX); i++) { chan = request->channels[i]; priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value; priv->user_scan_cfg->chan_list[i].radio_type = chan->band; if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) priv->user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_PASSIVE; else priv->user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_ACTIVE; priv->user_scan_cfg->chan_list[i].scan_time = 0; } if (mwifiex_set_user_scan_ioctl(priv, priv->user_scan_cfg)) return -EFAULT; if (request->ie && request->ie_len) { for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) { priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR; memset(&priv->vs_ie[i].ie, 0, MWIFIEX_MAX_VSIE_LEN); } } } return 0; }
void cw1200_scan_work(struct work_struct *work) { struct cw1200_common *priv = container_of(work, struct cw1200_common, scan.work); struct ieee80211_channel **it; struct wsm_scan scan = { .scanType = WSM_SCAN_TYPE_FOREGROUND, .scanFlags = WSM_SCAN_FLAG_SPLIT_METHOD, }; bool first_run = priv->scan.begin == priv->scan.curr && priv->scan.begin != priv->scan.end; int i; mutex_lock(&priv->conf_mutex); if (first_run) { if (priv->join_status == CW1200_JOIN_STATUS_STA && !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { struct wsm_set_pm pm = priv->powersave_mode; pm.pmMode = WSM_PSM_PS; cw1200_set_pm(priv, &pm); } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { /* FW bug: driver has to restart p2p-dev mode * after scan */ cw1200_disable_listening(priv); } } if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { if (priv->scan.output_power != priv->output_power) WARN_ON(wsm_set_output_power(priv, priv->output_power * 10)); if (priv->join_status == CW1200_JOIN_STATUS_STA && !(priv->powersave_mode.pmMode & WSM_PSM_PS)) cw1200_set_pm(priv, &priv->powersave_mode); if (priv->scan.status < 0) wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n", priv->scan.status); else if (priv->scan.req) wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan completed.\n"); else wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan canceled.\n"); priv->scan.req = NULL; cw1200_scan_restart_delayed(priv); wsm_unlock_tx(priv); mutex_unlock(&priv->conf_mutex); ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0); up(&priv->scan.lock); return; } else { struct ieee80211_channel *first = *priv->scan.curr; for (it = priv->scan.curr + 1, i = 1; it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; ++it, ++i) { if ((*it)->band != first->band) break; if (((*it)->flags ^ first->flags) & IEEE80211_CHAN_PASSIVE_SCAN) break; if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && (*it)->max_power != first->max_power) break; } scan.band = first->band; if (priv->scan.req->no_cck) scan.maxTransmitRate = WSM_TRANSMIT_RATE_6; else scan.maxTransmitRate = WSM_TRANSMIT_RATE_1; /* TODO: Is it optimal? */ scan.numOfProbeRequests = (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2; scan.numOfSSIDs = priv->scan.n_ssids; scan.ssids = &priv->scan.ssids[0]; scan.numOfChannels = it - priv->scan.curr; scan.probeDelay = 100; /* It is not stated in WSM specification, however * FW team says that driver may not use FG scan * when joined. */ if (priv->join_status == CW1200_JOIN_STATUS_STA) { scan.scanType = WSM_SCAN_TYPE_BACKGROUND; scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; } scan.ch = kzalloc( sizeof(struct wsm_scan_ch[it - priv->scan.curr]), GFP_KERNEL); if (!scan.ch) { priv->scan.status = -ENOMEM; goto fail; } for (i = 0; i < scan.numOfChannels; ++i) { scan.ch[i].number = priv->scan.curr[i]->hw_value; if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) { scan.ch[i].minChannelTime = 50; scan.ch[i].maxChannelTime = 100; } else { scan.ch[i].minChannelTime = 10; scan.ch[i].maxChannelTime = 25; } } if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && priv->scan.output_power != first->max_power) { priv->scan.output_power = first->max_power; WARN_ON(wsm_set_output_power(priv, priv->scan.output_power * 10)); } priv->scan.status = cw1200_scan_start(priv, &scan); kfree(scan.ch); if (priv->scan.status) goto fail; priv->scan.curr = it; } mutex_unlock(&priv->conf_mutex); return; fail: priv->scan.curr = priv->scan.end; mutex_unlock(&priv->conf_mutex); queue_work(priv->workqueue, &priv->scan.work); return; }
static int carl9170_rx_mac_status(struct ar9170 *ar, struct ar9170_rx_head *head, struct ar9170_rx_macstatus *mac, struct ieee80211_rx_status *status) { struct ieee80211_channel *chan; u8 error, decrypt; BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12); BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4); error = mac->error; if (error & AR9170_RX_ERROR_WRONG_RA) { if (!ar->sniffer_enabled) return -EINVAL; } if (error & AR9170_RX_ERROR_PLCP) { if (!(ar->filter_state & FIF_PLCPFAIL)) return -EINVAL; status->flag |= RX_FLAG_FAILED_PLCP_CRC; } if (error & AR9170_RX_ERROR_FCS) { ar->tx_fcs_errors++; if (!(ar->filter_state & FIF_FCSFAIL)) return -EINVAL; status->flag |= RX_FLAG_FAILED_FCS_CRC; } decrypt = ar9170_get_decrypt_type(mac); if (!(decrypt & AR9170_RX_ENC_SOFTWARE) && decrypt != AR9170_ENC_ALG_NONE) { if ((decrypt == AR9170_ENC_ALG_TKIP) && (error & AR9170_RX_ERROR_MMIC)) status->flag |= RX_FLAG_MMIC_ERROR; status->flag |= RX_FLAG_DECRYPTED; } if (error & AR9170_RX_ERROR_DECRYPT && !ar->sniffer_enabled) return -ENODATA; error &= ~(AR9170_RX_ERROR_MMIC | AR9170_RX_ERROR_FCS | AR9170_RX_ERROR_WRONG_RA | AR9170_RX_ERROR_DECRYPT | AR9170_RX_ERROR_PLCP); /* drop any other error frames */ if (unlikely(error)) { /* TODO: update netdevice's RX dropped/errors statistics */ if (net_ratelimit()) wiphy_dbg(ar->hw->wiphy, "received frame with " "suspicious error code (%#x).\n", error); return -EINVAL; } chan = ar->channel; if (chan) { status->band = chan->band; status->freq = chan->center_freq; } switch (mac->status & AR9170_RX_STATUS_MODULATION) { case AR9170_RX_STATUS_MODULATION_CCK: if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE) status->flag |= RX_FLAG_SHORTPRE; switch (head->plcp[0]) { case AR9170_RX_PHY_RATE_CCK_1M: status->rate_idx = 0; break; case AR9170_RX_PHY_RATE_CCK_2M: status->rate_idx = 1; break; case AR9170_RX_PHY_RATE_CCK_5M: status->rate_idx = 2; break; case AR9170_RX_PHY_RATE_CCK_11M: status->rate_idx = 3; break; default: if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "invalid plcp cck " "rate (%x).\n", head->plcp[0]); } return -EINVAL; } break; case AR9170_RX_STATUS_MODULATION_DUPOFDM: case AR9170_RX_STATUS_MODULATION_OFDM: switch (head->plcp[0] & 0xf) { case AR9170_TXRX_PHY_RATE_OFDM_6M: status->rate_idx = 0; break; case AR9170_TXRX_PHY_RATE_OFDM_9M: status->rate_idx = 1; break; case AR9170_TXRX_PHY_RATE_OFDM_12M: status->rate_idx = 2; break; case AR9170_TXRX_PHY_RATE_OFDM_18M: status->rate_idx = 3; break; case AR9170_TXRX_PHY_RATE_OFDM_24M: status->rate_idx = 4; break; case AR9170_TXRX_PHY_RATE_OFDM_36M: status->rate_idx = 5; break; case AR9170_TXRX_PHY_RATE_OFDM_48M: status->rate_idx = 6; break; case AR9170_TXRX_PHY_RATE_OFDM_54M: status->rate_idx = 7; break; default: if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "invalid plcp ofdm " "rate (%x).\n", head->plcp[0]); } return -EINVAL; } if (status->band == IEEE80211_BAND_2GHZ) status->rate_idx += 4; break; case AR9170_RX_STATUS_MODULATION_HT: if (head->plcp[3] & 0x80) status->flag |= RX_FLAG_40MHZ; if (head->plcp[6] & 0x80) status->flag |= RX_FLAG_SHORT_GI; status->rate_idx = clamp(0, 75, head->plcp[3] & 0x7f); status->flag |= RX_FLAG_HT; break; default: BUG(); return -ENOSYS; } return 0; }