/* * This function cleans up the SDIO driver. * * The following major steps are followed for cleanup - * - Resume the device if its suspended * - Disconnect the device if connected * - Shutdown the firmware * - Unregister the device from SDIO bus. */ static void mwifiex_sdio_cleanup_module(void) { struct mwifiex_adapter *adapter = g_adapter; int i; if (down_interruptible(&add_remove_card_sem)) goto exit_sem_err; if (!adapter || !adapter->priv_num) goto exit; if (adapter->is_suspended) mwifiex_sdio_resume(adapter->dev); for (i = 0; i < adapter->priv_num; i++) if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && adapter->priv[i]->media_connected) mwifiex_deauthenticate(adapter->priv[i], NULL); if (!adapter->surprise_removed) mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY), MWIFIEX_FUNC_SHUTDOWN); exit: up(&add_remove_card_sem); exit_sem_err: sdio_unregister_driver(&mwifiex_sdio); }
/* This function cleans up the USB driver. * * The following major steps are followed in .disconnect for cleanup: * - Resume the device if its suspended * - Disconnect the device if connected * - Shutdown the firmware * - Unregister the device from USB bus. */ static void mwifiex_usb_cleanup_module(void) { if (!down_interruptible(&add_remove_card_sem)) up(&add_remove_card_sem); if (usb_card) { struct mwifiex_adapter *adapter = usb_card->adapter; int i; /* In case driver is removed when asynchronous FW downloading is * in progress */ wait_for_completion(&adapter->fw_load); #ifdef CONFIG_PM if (adapter->is_suspended) mwifiex_usb_resume(usb_card->intf); #endif for (i = 0; i < adapter->priv_num; i++) if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && adapter->priv[i]->media_connected) mwifiex_deauthenticate(adapter->priv[i], NULL); mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY), MWIFIEX_FUNC_SHUTDOWN); } usb_deregister(&mwifiex_usb_driver); }
/* This function deauthenticates/disconnects from all BSS. */ void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter) { struct mwifiex_private *priv; int i; for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; if (priv) mwifiex_deauthenticate(priv, NULL); } }
/* * IOCTL request handler to change Ad-Hoc channel. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. * * The function follows the following steps to perform the change - * - Get current IBSS information * - Get current channel * - If no change is required, return * - If not connected, change channel and return * - If connected, * - Disconnect * - Change channel * - Perform specific SSID scan with same SSID * - Start/Join the IBSS */ int mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, int channel) { int ret; struct mwifiex_bss_info bss_info; struct mwifiex_ssid_bssid ssid_bssid; u16 curr_chan = 0; memset(&bss_info, 0, sizeof(bss_info)); /* Get BSS information */ if (mwifiex_get_bss_info(priv, &bss_info)) return -1; /* Get current channel */ ret = mwifiex_bss_ioctl_ibss_channel(priv, HostCmd_ACT_GEN_GET, &curr_chan); if (curr_chan == channel) { ret = 0; goto done; } dev_dbg(priv->adapter->dev, "cmd: updating channel from %d to %d\n", curr_chan, channel); if (!bss_info.media_connected) { ret = 0; goto done; } /* Do disonnect */ memset(&ssid_bssid, 0, ETH_ALEN); ret = mwifiex_deauthenticate(priv, ssid_bssid.bssid); ret = mwifiex_bss_ioctl_ibss_channel(priv, HostCmd_ACT_GEN_SET, (u16 *) &channel); /* Do specific SSID scanning */ if (mwifiex_request_scan(priv, &bss_info.ssid)) { ret = -1; goto done; } /* Start/Join Adhoc network */ memset(&ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid)); memcpy(&ssid_bssid.ssid, &bss_info.ssid, sizeof(struct mwifiex_802_11_ssid)); ret = mwifiex_bss_start(priv, &ssid_bssid); done: return ret; }
/* * 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 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; }
static void mwifiex_usb_disconnect(struct usb_interface *intf) { struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; int i; if (!card || !card->adapter) { pr_err("%s: card or card->adapter is NULL\n", __func__); return; } adapter = card->adapter; if (!adapter->priv_num) return; /* In case driver is removed when asynchronous FW downloading is * in progress */ wait_for_completion(&adapter->fw_load); if (user_rmmod) { #ifdef CONFIG_PM if (adapter->is_suspended) mwifiex_usb_resume(intf); #endif for (i = 0; i < adapter->priv_num; i++) if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && adapter->priv[i]->media_connected) mwifiex_deauthenticate(adapter->priv[i], NULL); mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY), MWIFIEX_FUNC_SHUTDOWN); } mwifiex_usb_free(card); dev_dbg(adapter->dev, "%s: removing card\n", __func__); mwifiex_remove_card(adapter, &add_remove_card_sem); usb_set_intfdata(intf, NULL); usb_put_dev(interface_to_usbdev(intf)); kfree(card); return; }
/* * Sends IOCTL request to cancel the existing Host Sleep configuration. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) { struct mwifiex_ds_hs_cfg hscfg; struct mwifiex_private *priv; int i; if (disconnect_on_suspend) { for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; if (priv) mwifiex_deauthenticate(priv, NULL); } } if (adapter->hs_activated) { dev_dbg(adapter->dev, "cmd: HS Already activated\n"); return true; } adapter->hs_activate_wait_q_woken = false; memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); hscfg.is_invoke_hostcmd = true; adapter->hs_enabling = true; mwifiex_cancel_all_pending_cmd(adapter); if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, &hscfg)) { dev_err(adapter->dev, "IOCTL request HS enable failed\n"); return false; } if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q, adapter->hs_activate_wait_q_woken, (10 * HZ)) <= 0) { dev_err(adapter->dev, "hs_activate_wait_q terminated\n"); return false; } return true; }
/* * 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; }
/* * 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; }
/* * SDIO remove. * * This function removes the interface and frees up the card structure. */ static void mwifiex_sdio_remove(struct sdio_func *func) { struct sdio_mmc_card *card; struct mwifiex_adapter *adapter; struct mwifiex_private *priv; int i; pr_debug("info: SDIO func num=%d\n", func->num); card = sdio_get_drvdata(func); if (!card) return; adapter = card->adapter; if (!adapter || !adapter->priv_num) return; /* In case driver is removed when asynchronous FW load is in progress */ wait_for_completion(&adapter->fw_load); if (user_rmmod) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) if (adapter->is_suspended) mwifiex_sdio_resume(adapter->dev); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) */ for (i = 0; i < adapter->priv_num; i++) if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && adapter->priv[i]->media_connected) mwifiex_deauthenticate(adapter->priv[i], NULL); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); mwifiex_disable_auto_ds(priv); mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); } mwifiex_remove_card(card->adapter, &add_remove_card_sem); kfree(card); }
static void mwifiex_pcie_remove(struct pci_dev *pdev) { struct pcie_service_card *card; struct mwifiex_adapter *adapter; struct mwifiex_private *priv; int i; card = pci_get_drvdata(pdev); if (!card) return; adapter = card->adapter; if (!adapter || !adapter->priv_num) return; if (user_rmmod) { #ifdef CONFIG_PM if (adapter->is_suspended) mwifiex_pcie_resume(pdev); #endif for (i = 0; i < adapter->priv_num; i++) if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && adapter->priv[i]->media_connected) mwifiex_deauthenticate(adapter->priv[i], NULL); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); mwifiex_disable_auto_ds(priv); mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); } mwifiex_remove_card(card->adapter, &add_remove_card_sem); kfree(card); }
static void mwifiex_sdio_remove(struct sdio_func *func) { struct sdio_mmc_card *card; struct mwifiex_adapter *adapter; struct mwifiex_private *priv; int i; pr_debug("info: SDIO func num=%d\n", func->num); card = sdio_get_drvdata(func); if (!card) return; adapter = card->adapter; if (!adapter || !adapter->priv_num) return; if (user_rmmod) { if (adapter->is_suspended) mwifiex_sdio_resume(adapter->dev); for (i = 0; i < adapter->priv_num; i++) if ((GET_BSS_ROLE(adapter->priv[i]) == MWIFIEX_BSS_ROLE_STA) && adapter->priv[i]->media_connected) mwifiex_deauthenticate(adapter->priv[i], NULL); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); mwifiex_disable_auto_ds(priv); mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); } mwifiex_remove_card(card->adapter, &add_remove_card_sem); kfree(card); }
/* * This function connects with a BSS. * * This function handles both Infra and Ad-Hoc modes. It also performs * validity checking on the provided parameters, disconnects from the * current BSS (if any), sets up the association/scan parameters, * including security settings, and performs specific SSID scan before * trying to connect. * * For Infra mode, the function returns failure if the specified SSID * is not found in scan table. However, for Ad-Hoc mode, it can create * the IBSS if it does not exist. On successful completion in either case, * the function notifies the CFG802.11 subsystem of the new BSS connection. */ static int mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, u8 *bssid, int mode, struct ieee80211_channel *channel, struct cfg80211_connect_params *sme, bool privacy) { struct cfg80211_ssid req_ssid; int ret, auth_type = 0; struct cfg80211_bss *bss = NULL; u8 is_scanning_required = 0; memset(&req_ssid, 0, sizeof(struct cfg80211_ssid)); req_ssid.ssid_len = ssid_len; if (ssid_len > IEEE80211_MAX_SSID_LEN) { dev_err(priv->adapter->dev, "invalid SSID - aborting\n"); return -EINVAL; } memcpy(req_ssid.ssid, ssid, ssid_len); if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { dev_err(priv->adapter->dev, "invalid SSID - aborting\n"); return -EINVAL; } /* disconnect before try to associate */ mwifiex_deauthenticate(priv, NULL); if (channel) ret = mwifiex_set_rf_channel(priv, channel, priv->adapter->channel_type); /* As this is new association, clear locally stored * keys and security related flags */ priv->sec_info.wpa_enabled = false; priv->sec_info.wpa2_enabled = false; priv->wep_key_curr_index = 0; priv->sec_info.encryption_mode = 0; priv->sec_info.is_authtype_auto = 0; ret = mwifiex_set_encode(priv, NULL, 0, 0, NULL, 1); if (mode == NL80211_IFTYPE_ADHOC) { /* "privacy" is set only for ad-hoc mode */ if (privacy) { /* * Keep WLAN_CIPHER_SUITE_WEP104 for now so that * the firmware can find a matching network from the * scan. The cfg80211 does not give us the encryption * mode at this stage so just setting it to WEP here. */ priv->sec_info.encryption_mode = WLAN_CIPHER_SUITE_WEP104; priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; } goto done; } /* Now handle infra mode. "sme" is valid for infra mode only */ if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; priv->sec_info.is_authtype_auto = 1; } else { auth_type = sme->auth_type; } if (sme->crypto.n_ciphers_pairwise) { priv->sec_info.encryption_mode = sme->crypto.ciphers_pairwise[0]; priv->sec_info.authentication_mode = auth_type; } if (sme->crypto.cipher_group) { priv->sec_info.encryption_mode = sme->crypto.cipher_group; priv->sec_info.authentication_mode = auth_type; } if (sme->ie) ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len); if (sme->key) { if (mwifiex_is_alg_wep(priv->sec_info.encryption_mode)) { dev_dbg(priv->adapter->dev, "info: setting wep encryption" " with key len %d\n", sme->key_len); priv->wep_key_curr_index = sme->key_idx; ret = mwifiex_set_encode(priv, sme->key, sme->key_len, sme->key_idx, NULL, 0); } } done: /* * Scan entries are valid for some time (15 sec). So we can save one * active scan time if we just try cfg80211_get_bss first. If it fails * then request scan and cfg80211_get_bss() again for final output. */ while (1) { if (is_scanning_required) { /* Do specific SSID scanning */ if (mwifiex_request_scan(priv, &req_ssid)) { dev_err(priv->adapter->dev, "scan error\n"); return -EFAULT; } } /* Find the BSS we want using available scan results */ if (mode == NL80211_IFTYPE_ADHOC) bss = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid, ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); else bss = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (!bss) { if (is_scanning_required) { dev_warn(priv->adapter->dev, "assoc: requested bss not found in scan results\n"); break; } is_scanning_required = 1; } else { dev_dbg(priv->adapter->dev, "info: trying to associate to '%s' bssid %pM\n", (char *) req_ssid.ssid, bss->bssid); memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN); break; } } if (mwifiex_bss_start(priv, bss, &req_ssid)) return -EFAULT; if (mode == NL80211_IFTYPE_ADHOC) { /* Inform the BSS information to kernel, otherwise * kernel will give a panic after successful assoc */ if (mwifiex_cfg80211_inform_ibss_bss(priv)) return -EFAULT; } return ret; }
/* * In Ad-Hoc mode, the IBSS is created if not found in scan list. * In both Ad-Hoc and infra mode, an deauthentication is performed * first. */ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, struct cfg80211_ssid *req_ssid) { int ret; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_bssdescriptor *bss_desc = NULL; priv->scan_block = false; if (bss) { mwifiex_process_country_ie(priv, bss); /* Allocate and fill new bss descriptor */ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); if (!bss_desc) return -ENOMEM; ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); if (ret) goto done; } if (priv->bss_mode == NL80211_IFTYPE_STATION || priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { u8 config_bands; ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; if (!bss_desc) return -1; if (mwifiex_band_to_radio_type(bss_desc->bss_band) == HostCmd_SCAN_RADIO_TYPE_BG) config_bands = BAND_B | BAND_G | BAND_GN; else config_bands = BAND_A | BAND_AN | BAND_AAC; if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) adapter->config_bands = config_bands; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (ret) goto done; if (mwifiex_11h_get_csa_closed_channel(priv) == (u8)bss_desc->channel) { dev_err(adapter->dev, "Attempt to reconnect on csa closed chan(%d)\n", bss_desc->channel); goto done; } dev_dbg(adapter->dev, "info: SSID found in scan list ... " "associating...\n"); mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); /* Clear any past association response stored for * application retrieval */ priv->assoc_rsp_size = 0; ret = mwifiex_associate(priv, bss_desc); /* If auth type is auto and association fails using open mode, * try to connect using shared mode */ if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && priv->sec_info.is_authtype_auto && priv->sec_info.wep_enabled) { priv->sec_info.authentication_mode = NL80211_AUTHTYPE_SHARED_KEY; ret = mwifiex_associate(priv, bss_desc); } if (bss) cfg80211_put_bss(priv->adapter->wiphy, bss); } else { /* Adhoc mode */ /* If the requested SSID matches current SSID, return */ if (bss_desc && bss_desc->ssid.ssid_len && (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor. ssid, &bss_desc->ssid))) { ret = 0; goto done; } /* Exit Adhoc mode first */ dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n"); ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; priv->adhoc_is_link_sensed = false; ret = mwifiex_check_network_compatibility(priv, bss_desc); mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); if (!ret) { dev_dbg(adapter->dev, "info: network found in scan" " list. Joining...\n"); ret = mwifiex_adhoc_join(priv, bss_desc); if (bss) cfg80211_put_bss(priv->adapter->wiphy, bss); } else { dev_dbg(adapter->dev, "info: Network not found in " "the list, creating adhoc with ssid = %s\n", req_ssid->ssid); ret = mwifiex_adhoc_start(priv, req_ssid); } } done: /* beacon_ie buffer was allocated in function * mwifiex_fill_new_bss_desc(). Free it now. */ if (bss_desc) kfree(bss_desc->beacon_buf); kfree(bss_desc); return ret; }
/* * In Ad-Hoc mode, the IBSS is created if not found in scan list. * In both Ad-Hoc and infra mode, an deauthentication is performed * first. */ int mwifiex_bss_start(struct mwifiex_private *priv, struct mwifiex_ssid_bssid *ssid_bssid) { int ret; struct mwifiex_adapter *adapter = priv->adapter; s32 i = -1; priv->scan_block = false; if (!ssid_bssid) return -1; if (priv->bss_mode == NL80211_IFTYPE_STATION) { /* Infra mode */ ret = mwifiex_deauthenticate(priv, NULL); if (ret) return ret; /* Search for the requested SSID in the scan table */ if (ssid_bssid->ssid.ssid_len) i = mwifiex_find_ssid_in_list(priv, &ssid_bssid->ssid, NULL, NL80211_IFTYPE_STATION); else i = mwifiex_find_bssid_in_list(priv, (u8 *) &ssid_bssid->bssid, NL80211_IFTYPE_STATION); if (i < 0) return -1; dev_dbg(adapter->dev, "info: SSID found in scan list ... associating...\n"); /* Clear any past association response stored for * application retrieval */ priv->assoc_rsp_size = 0; ret = mwifiex_associate(priv, &adapter->scan_table[i]); if (ret) return ret; } else { /* Adhoc mode */ /* If the requested SSID matches current SSID, return */ if (ssid_bssid->ssid.ssid_len && (!mwifiex_ssid_cmp (&priv->curr_bss_params.bss_descriptor.ssid, &ssid_bssid->ssid))) return 0; /* Exit Adhoc mode first */ dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n"); ret = mwifiex_deauthenticate(priv, NULL); if (ret) return ret; priv->adhoc_is_link_sensed = false; /* Search for the requested network in the scan table */ if (ssid_bssid->ssid.ssid_len) i = mwifiex_find_ssid_in_list(priv, &ssid_bssid->ssid, NULL, NL80211_IFTYPE_ADHOC); else i = mwifiex_find_bssid_in_list(priv, (u8 *)&ssid_bssid->bssid, NL80211_IFTYPE_ADHOC); if (i >= 0) { dev_dbg(adapter->dev, "info: network found in scan" " list. Joining...\n"); ret = mwifiex_adhoc_join(priv, &adapter->scan_table[i]); if (ret) return ret; } else { dev_dbg(adapter->dev, "info: Network not found in " "the list, creating adhoc with ssid = %s\n", ssid_bssid->ssid.ssid); ret = mwifiex_adhoc_start(priv, &ssid_bssid->ssid); if (ret) return ret; } } return ret; }
/* * This function connects with a BSS. * * This function handles both Infra and Ad-Hoc modes. It also performs * validity checking on the provided parameters, disconnects from the * current BSS (if any), sets up the association/scan parameters, * including security settings, and performs specific SSID scan before * trying to connect. * * For Infra mode, the function returns failure if the specified SSID * is not found in scan table. However, for Ad-Hoc mode, it can create * the IBSS if it does not exist. On successful completion in either case, * the function notifies the CFG802.11 subsystem of the new BSS connection, * otherwise the kernel will panic. */ static int mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, u8 *bssid, int mode, struct ieee80211_channel *channel, struct cfg80211_connect_params *sme, bool privacy) { struct mwifiex_802_11_ssid req_ssid; struct mwifiex_ssid_bssid ssid_bssid; int ret, auth_type = 0; memset(&req_ssid, 0, sizeof(struct mwifiex_802_11_ssid)); memset(&ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid)); req_ssid.ssid_len = ssid_len; if (ssid_len > IEEE80211_MAX_SSID_LEN) { dev_err(priv->adapter->dev, "invalid SSID - aborting\n"); return -EINVAL; } memcpy(req_ssid.ssid, ssid, ssid_len); if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { dev_err(priv->adapter->dev, "invalid SSID - aborting\n"); return -EINVAL; } /* disconnect before try to associate */ mwifiex_deauthenticate(priv, NULL); if (channel) ret = mwifiex_set_rf_channel(priv, channel, mwifiex_channels_to_cfg80211_channel_type (priv->adapter->chan_offset)); ret = mwifiex_set_encode(priv, NULL, 0, 0, 1); /* Disable keys */ if (mode == NL80211_IFTYPE_ADHOC) { /* "privacy" is set only for ad-hoc mode */ if (privacy) { /* * Keep WLAN_CIPHER_SUITE_WEP104 for now so that * the firmware can find a matching network from the * scan. The cfg80211 does not give us the encryption * mode at this stage so just setting it to WEP here. */ priv->sec_info.encryption_mode = WLAN_CIPHER_SUITE_WEP104; priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; } goto done; } /* Now handle infra mode. "sme" is valid for infra mode only */ if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC || sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) auth_type = NL80211_AUTHTYPE_SHARED_KEY; if (sme->crypto.n_ciphers_pairwise) { priv->sec_info.encryption_mode = sme->crypto.ciphers_pairwise[0]; priv->sec_info.authentication_mode = auth_type; } if (sme->crypto.cipher_group) { priv->sec_info.encryption_mode = sme->crypto.cipher_group; priv->sec_info.authentication_mode = auth_type; } if (sme->ie) ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len); if (sme->key) { if (mwifiex_is_alg_wep(priv->sec_info.encryption_mode)) { dev_dbg(priv->adapter->dev, "info: setting wep encryption" " with key len %d\n", sme->key_len); ret = mwifiex_set_encode(priv, sme->key, sme->key_len, sme->key_idx, 0); } } done: /* Do specific SSID scanning */ if (mwifiex_request_scan(priv, &req_ssid)) { dev_err(priv->adapter->dev, "scan error\n"); return -EFAULT; } memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(struct mwifiex_802_11_ssid)); if (mode != NL80211_IFTYPE_ADHOC) { if (mwifiex_find_best_bss(priv, &ssid_bssid)) return -EFAULT; /* Inform the BSS information to kernel, otherwise * kernel will give a panic after successful assoc */ if (mwifiex_inform_bss_from_scan_result(priv, &req_ssid)) return -EFAULT; } dev_dbg(priv->adapter->dev, "info: trying to associate to %s and bssid %pM\n", (char *) req_ssid.ssid, ssid_bssid.bssid); memcpy(&priv->cfg_bssid, ssid_bssid.bssid, 6); /* Connect to BSS by ESSID */ memset(&ssid_bssid.bssid, 0, ETH_ALEN); if (!netif_queue_stopped(priv->netdev)) netif_stop_queue(priv->netdev); if (mwifiex_bss_start(priv, &ssid_bssid)) return -EFAULT; if (mode == NL80211_IFTYPE_ADHOC) { /* Inform the BSS information to kernel, otherwise * kernel will give a panic after successful assoc */ if (mwifiex_cfg80211_inform_ibss_bss(priv)) return -EFAULT; } return ret; }
/* * In Ad-Hoc mode, the IBSS is created if not found in scan list. * In both Ad-Hoc and infra mode, an deauthentication is performed * first. */ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, struct cfg80211_ssid *req_ssid) { int ret; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_bssdescriptor *bss_desc = NULL; priv->scan_block = false; if (bss) { /* Allocate and fill new bss descriptor */ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); if (!bss_desc) { dev_err(priv->adapter->dev, " failed to alloc bss_desc\n"); return -ENOMEM; } ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); if (ret) goto done; } if (priv->bss_mode == NL80211_IFTYPE_STATION) { /* Infra mode */ ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (ret) goto done; dev_dbg(adapter->dev, "info: SSID found in scan list ... " "associating...\n"); if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); /* Clear any past association response stored for * application retrieval */ priv->assoc_rsp_size = 0; ret = mwifiex_associate(priv, bss_desc); /* If auth type is auto and association fails using open mode, * try to connect using shared mode */ if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && priv->sec_info.is_authtype_auto && priv->sec_info.wep_enabled) { priv->sec_info.authentication_mode = NL80211_AUTHTYPE_SHARED_KEY; ret = mwifiex_associate(priv, bss_desc); } if (bss) cfg80211_put_bss(bss); } else { /* Adhoc mode */ /* If the requested SSID matches current SSID, return */ if (bss_desc && bss_desc->ssid.ssid_len && (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor. ssid, &bss_desc->ssid))) { kfree(bss_desc); return 0; } /* Exit Adhoc mode first */ dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n"); ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; priv->adhoc_is_link_sensed = false; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); if (!ret) { dev_dbg(adapter->dev, "info: network found in scan" " list. Joining...\n"); ret = mwifiex_adhoc_join(priv, bss_desc); if (bss) cfg80211_put_bss(bss); } else { dev_dbg(adapter->dev, "info: Network not found in " "the list, creating adhoc with ssid = %s\n", req_ssid->ssid); ret = mwifiex_adhoc_start(priv, req_ssid); } } done: kfree(bss_desc); return ret; }
int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, struct cfg80211_ssid *req_ssid) { int ret; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_bssdescriptor *bss_desc = NULL; u8 *beacon_ie = NULL; priv->scan_block = false; if (bss) { bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); if (!bss_desc) { dev_err(priv->adapter->dev, " failed to alloc bss_desc\n"); return -ENOMEM; } beacon_ie = kmemdup(bss->information_elements, bss->len_beacon_ies, GFP_KERNEL); if (!beacon_ie) { kfree(bss_desc); dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); return -ENOMEM; } ret = mwifiex_fill_new_bss_desc(priv, bss->bssid, bss->signal, beacon_ie, bss->len_beacon_ies, bss->beacon_interval, bss->capability, *(u8 *)bss->priv, bss_desc); if (ret) goto done; } if (priv->bss_mode == NL80211_IFTYPE_STATION) { ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (ret) goto done; dev_dbg(adapter->dev, "info: SSID found in scan list ... " "associating...\n"); if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); priv->assoc_rsp_size = 0; ret = mwifiex_associate(priv, bss_desc); if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && priv->sec_info.is_authtype_auto && priv->sec_info.wep_enabled) { priv->sec_info.authentication_mode = NL80211_AUTHTYPE_SHARED_KEY; ret = mwifiex_associate(priv, bss_desc); } if (bss) cfg80211_put_bss(bss); } else { if (bss_desc && bss_desc->ssid.ssid_len && (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor. ssid, &bss_desc->ssid))) { kfree(bss_desc); kfree(beacon_ie); return 0; } dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n"); ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; priv->adhoc_is_link_sensed = false; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (!netif_queue_stopped(priv->netdev)) mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); if (!ret) { dev_dbg(adapter->dev, "info: network found in scan" " list. Joining...\n"); ret = mwifiex_adhoc_join(priv, bss_desc); if (bss) cfg80211_put_bss(bss); } else { dev_dbg(adapter->dev, "info: Network not found in " "the list, creating adhoc with ssid = %s\n", req_ssid->ssid); ret = mwifiex_adhoc_start(priv, req_ssid); } } done: kfree(bss_desc); kfree(beacon_ie); return ret; }
/* * 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); switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_ADHOC: switch (type) { case NL80211_IFTYPE_STATION: break; case NL80211_IFTYPE_UNSPECIFIED: wiphy_warn(wiphy, "%s: kept type as IBSS\n", dev->name); case NL80211_IFTYPE_ADHOC: /* This shouldn't happen */ return 0; case NL80211_IFTYPE_AP: default: wiphy_err(wiphy, "%s: changing to %d not supported\n", dev->name, type); return -EOPNOTSUPP; } break; case NL80211_IFTYPE_STATION: switch (type) { case NL80211_IFTYPE_ADHOC: break; case NL80211_IFTYPE_UNSPECIFIED: wiphy_warn(wiphy, "%s: kept type as STA\n", dev->name); case NL80211_IFTYPE_STATION: /* This shouldn't happen */ return 0; case NL80211_IFTYPE_AP: default: wiphy_err(wiphy, "%s: changing to %d not supported\n", dev->name, type); return -EOPNOTSUPP; } break; case NL80211_IFTYPE_AP: switch (type) { case NL80211_IFTYPE_UNSPECIFIED: wiphy_warn(wiphy, "%s: kept type as AP\n", dev->name); case NL80211_IFTYPE_AP: /* This shouldn't happen */ return 0; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: default: wiphy_err(wiphy, "%s: changing to %d not supported\n", dev->name, type); return -EOPNOTSUPP; } break; default: wiphy_err(wiphy, "%s: unknown iftype: %d\n", dev->name, dev->ieee80211_ptr->iftype); return -EOPNOTSUPP; } dev->ieee80211_ptr->iftype = type; priv->bss_mode = type; 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; }
/* * Sends IOCTL request to cancel the existing Host Sleep configuration. * * This function allocates the IOCTL request buffer, fills it * with requisite parameters and calls the IOCTL handler. */ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) { struct mwifiex_ds_hs_cfg hscfg; struct mwifiex_private *priv; int i; if (disconnect_on_suspend) { for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; if (priv) mwifiex_deauthenticate(priv, NULL); } } priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); if (priv && priv->sched_scanning) { #ifdef CONFIG_PM if (priv->wdev.wiphy->wowlan_config && !priv->wdev.wiphy->wowlan_config->nd_config) { #endif mwifiex_dbg(adapter, CMD, "aborting bgscan!\n"); mwifiex_stop_bg_scan(priv); cfg80211_sched_scan_stopped(priv->wdev.wiphy); #ifdef CONFIG_PM } #endif } if (adapter->hs_activated) { mwifiex_dbg(adapter, CMD, "cmd: HS Already activated\n"); return true; } adapter->hs_activate_wait_q_woken = false; memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); hscfg.is_invoke_hostcmd = true; adapter->hs_enabling = true; mwifiex_cancel_all_pending_cmd(adapter); if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, &hscfg)) { mwifiex_dbg(adapter, ERROR, "IOCTL request HS enable failed\n"); return false; } if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q, adapter->hs_activate_wait_q_woken, (10 * HZ)) <= 0) { mwifiex_dbg(adapter, ERROR, "hs_activate_wait_q terminated\n"); return false; } return true; }
/* * In Ad-Hoc mode, the IBSS is created if not found in scan list. * In both Ad-Hoc and infra mode, an deauthentication is performed * first. */ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, struct mwifiex_802_11_ssid *req_ssid) { int ret; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_bssdescriptor *bss_desc = NULL; u8 *beacon_ie = NULL; priv->scan_block = false; if (bss) { /* Allocate and fill new bss descriptor */ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); if (!bss_desc) { dev_err(priv->adapter->dev, " failed to alloc bss_desc\n"); return -ENOMEM; } beacon_ie = kmemdup(bss->information_elements, bss->len_beacon_ies, GFP_KERNEL); if (!beacon_ie) { kfree(bss_desc); dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); return -ENOMEM; } ret = mwifiex_fill_new_bss_desc(priv, bss->bssid, bss->signal, beacon_ie, bss->len_beacon_ies, bss->beacon_interval, bss->capability, *(u8 *)bss->priv, bss_desc); if (ret) goto done; } if (priv->bss_mode == NL80211_IFTYPE_STATION) { /* Infra mode */ ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (ret) goto done; dev_dbg(adapter->dev, "info: SSID found in scan list ... " "associating...\n"); if (!netif_queue_stopped(priv->netdev)) netif_stop_queue(priv->netdev); /* Clear any past association response stored for * application retrieval */ priv->assoc_rsp_size = 0; ret = mwifiex_associate(priv, bss_desc); if (bss) cfg80211_put_bss(bss); } else { /* Adhoc mode */ /* If the requested SSID matches current SSID, return */ if (bss_desc && bss_desc->ssid.ssid_len && (!mwifiex_ssid_cmp (&priv->curr_bss_params.bss_descriptor.ssid, &bss_desc->ssid))) { kfree(bss_desc); kfree(beacon_ie); return 0; } /* Exit Adhoc mode first */ dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n"); ret = mwifiex_deauthenticate(priv, NULL); if (ret) goto done; priv->adhoc_is_link_sensed = false; ret = mwifiex_check_network_compatibility(priv, bss_desc); if (!netif_queue_stopped(priv->netdev)) netif_stop_queue(priv->netdev); if (!ret) { dev_dbg(adapter->dev, "info: network found in scan" " list. Joining...\n"); ret = mwifiex_adhoc_join(priv, bss_desc); if (bss) cfg80211_put_bss(bss); } else { dev_dbg(adapter->dev, "info: Network not found in " "the list, creating adhoc with ssid = %s\n", req_ssid->ssid); ret = mwifiex_adhoc_start(priv, req_ssid); } } done: kfree(bss_desc); kfree(beacon_ie); return ret; }
int mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, u16 channel) { int ret; struct mwifiex_bss_info bss_info; struct mwifiex_ssid_bssid ssid_bssid; u16 curr_chan = 0; struct cfg80211_bss *bss = NULL; struct ieee80211_channel *chan; enum ieee80211_band band; memset(&bss_info, 0, sizeof(bss_info)); if (mwifiex_get_bss_info(priv, &bss_info)) return -1; ret = mwifiex_bss_ioctl_ibss_channel(priv, HostCmd_ACT_GEN_GET, &curr_chan); if (curr_chan == channel) { ret = 0; goto done; } dev_dbg(priv->adapter->dev, "cmd: updating channel from %d to %d\n", curr_chan, channel); if (!bss_info.media_connected) { ret = 0; goto done; } memset(&ssid_bssid, 0, ETH_ALEN); ret = mwifiex_deauthenticate(priv, ssid_bssid.bssid); ret = mwifiex_bss_ioctl_ibss_channel(priv, HostCmd_ACT_GEN_SET, &channel); if (mwifiex_request_scan(priv, &bss_info.ssid)) { ret = -1; goto done; } band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); chan = __ieee80211_get_channel(priv->wdev->wiphy, ieee80211_channel_to_frequency(channel, band)); bss = cfg80211_get_bss(priv->wdev->wiphy, chan, bss_info.bssid, bss_info.ssid.ssid, bss_info.ssid.ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (!bss) wiphy_warn(priv->wdev->wiphy, "assoc: bss %pM not in scan results\n", bss_info.bssid); ret = mwifiex_bss_start(priv, bss, &bss_info.ssid); done: return ret; }