/* * We need to differentiate between the surprise and planned removal of the * device because of the following consideration: * * - In case of surprise removal, the hcd already frees up the pending * for the device and hence there is no need to unregister the function * driver inorder to get these requests. For planned removal, the function * driver has to explicitly unregister itself to have the hcd return all the * pending requests before the data structures for the devices are freed up. * Note that as per the current implementation, the function driver will * end up releasing all the devices since there is no API to selectively * release a particular device. * * - Certain commands issued to the target can be skipped for surprise * removal since they will anyway not go through. */ void ath6kl_destroy(struct net_device *dev, unsigned int unregister) { struct ath6kl *ar; if (!dev || !ath6kl_priv(dev)) { ath6kl_err("failed to get device structure\n"); return; } ar = ath6kl_priv(dev); destroy_workqueue(ar->ath6kl_wq); if (ar->htc_target) htc_cleanup(ar->htc_target); aggr_module_destroy(ar->aggr_cntxt); ath6kl_cookie_cleanup(ar); ath6kl_cleanup_amsdu_rxbufs(ar); ath6kl_bmi_cleanup(ar); if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) { unregister_netdev(dev); clear_bit(NETDEV_REGISTERED, &ar->flag); } free_netdev(dev); ath6kl_cfg80211_deinit(ar); }
static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) { struct ath6kl *ar = ath6kl_priv(netdev); if (test_bit(CONNECTED, &ar->flag)) return ath6kl_wmi_setpmkid_cmd(ar->wmi, ar->bssid, NULL, false); return 0; }
static int ath6kl_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_pmksa *pmksa) { struct ath6kl *ar = ath6kl_priv(netdev); return ath6kl_wmi_setpmkid_cmd(ar->wmi, pmksa->bssid, pmksa->pmkid, false); }
static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code) { struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__, reason_code); if (!ath6kl_cfg80211_ready(ar)) return -EIO; if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { ath6kl_err("busy, destroy in progress\n"); return -EBUSY; } if (down_interruptible(&ar->sem)) { ath6kl_err("busy, couldn't get access\n"); return -ERESTARTSYS; } ar->reconnect_flag = 0; ath6kl_disconnect(ar); memset(ar->ssid, 0, sizeof(ar->ssid)); ar->ssid_len = 0; if (!test_bit(SKIP_SCAN, &ar->flag)) memset(ar->req_bssid, 0, sizeof(ar->req_bssid)); up(&ar->sem); ar->sme_state = SME_DISCONNECTED; return 0; }
static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, const u8 *mac_addr) { struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); if (!ath6kl_cfg80211_ready(ar)) return -EIO; if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); return -ENOENT; } if (!ar->keys[key_index].key_len) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d is empty\n", __func__, key_index); return 0; } ar->keys[key_index].key_len = 0; return ath6kl_wmi_deletekey_cmd(ar->wmi, key_index); }
static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback) (void *cookie, struct key_params *)) { struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); struct ath6kl_key *key = NULL; struct key_params params; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); if (!ath6kl_cfg80211_ready(ar)) return -EIO; if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); return -ENOENT; } key = &ar->keys[key_index]; memset(¶ms, 0, sizeof(params)); params.cipher = key->cipher; params.key_len = key->key_len; params.seq_len = key->seq_len; params.seq = key->seq; params.key = key->key; callback(cookie, ¶ms); return key->key_len ? 0 : -ENOENT; }
static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool pmgmt, int timeout) { struct ath6kl *ar = ath6kl_priv(dev); struct wmi_power_mode_cmd mode; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: pmgmt %d, timeout %d\n", __func__, pmgmt, timeout); if (!ath6kl_cfg80211_ready(ar)) return -EIO; if (pmgmt) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: max perf\n", __func__); mode.pwr_mode = REC_POWER; } else { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: rec power\n", __func__); mode.pwr_mode = MAX_PERF_POWER; } if (ath6kl_wmi_powermode_cmd(ar->wmi, mode.pwr_mode) != 0) { ath6kl_err("wmi_powermode_cmd failed\n"); return -EIO; } return 0; }
static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, u64 cookie) { struct ath6kl *ar = ath6kl_priv(dev); if (cookie != 1) return -ENOENT; return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi); }
static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool unicast, bool multicast) { struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); struct ath6kl_key *key = NULL; int status = 0; u8 key_usage; enum crypto_type key_type = NONE_CRYPT; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index); if (!ath6kl_cfg80211_ready(ar)) return -EIO; if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); return -ENOENT; } if (!ar->keys[key_index].key_len) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: invalid key index %d\n", __func__, key_index); return -EINVAL; } ar->def_txkey_index = key_index; key = &ar->keys[ar->def_txkey_index]; key_usage = GROUP_USAGE; if (ar->prwise_crypto == WEP_CRYPT) key_usage |= TX_USAGE; if (unicast) key_type = ar->prwise_crypto; if (multicast) key_type = ar->grp_crypto; if (ar->next_mode == AP_NETWORK && !test_bit(CONNECTED, &ar->flag)) return 0; /* Delay until AP mode has been started */ status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index, key_type, key_usage, key->key_len, key->seq, key->key, KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); if (status) return -EIO; return 0; }
static int ath6kl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev); if (!ath6kl_cfg80211_ready(ar)) return -EIO; ath6kl_disconnect(ar); memset(ar->ssid, 0, sizeof(ar->ssid)); ar->ssid_len = 0; return 0; }
static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev) { struct ath6kl *ar = ath6kl_priv(dev); if (ar->nw_type != AP_NETWORK) return -EOPNOTSUPP; if (!test_bit(CONNECTED, &ar->flag)) return -ENOTCONN; ath6kl_wmi_disconnect_cmd(ar->wmi); clear_bit(CONNECTED, &ar->flag); return 0; }
static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) { struct ath6kl *ar = ath6kl_priv(dev); if (!ath6kl_cfg80211_ready(ar)) return -EIO; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n", __func__, chan->center_freq, chan->hw_value); ar->next_chan = chan->center_freq; return 0; }
static int ath6kl_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { struct ath6kl *ar = ath6kl_priv(dev); /* TODO: if already pending or ongoing remain-on-channel, * return -EBUSY */ *cookie = 1; /* only a single pending request is supported */ return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, chan->center_freq, duration); }
static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, struct net_device *dev, u16 frame_type, bool reg) { struct ath6kl *ar = ath6kl_priv(dev); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n", __func__, frame_type, reg); if (frame_type == IEEE80211_STYPE_PROBE_REQ) { /* * Note: This notification callback is not allowed to sleep, so * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we * hardcode target to report Probe Request frames all the time. */ ar->probe_req_report = reg; } }
static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_parameters *params) { struct ath6kl *ar = ath6kl_priv(dev); if (ar->nw_type != AP_NETWORK) return -EOPNOTSUPP; /* Use this only for authorizing/unauthorizing a station */ if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) return -EOPNOTSUPP; if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_AUTHORIZE, mac, 0); return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_UNAUTHORIZE, mac, 0); }
static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { struct ath6kl *ar = ath6kl_priv(ndev); struct wireless_dev *wdev = ar->wdev; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type); if (!ath6kl_cfg80211_ready(ar)) return -EIO; switch (type) { case NL80211_IFTYPE_STATION: ar->next_mode = INFRA_NETWORK; break; case NL80211_IFTYPE_ADHOC: ar->next_mode = ADHOC_NETWORK; break; case NL80211_IFTYPE_AP: ar->next_mode = AP_NETWORK; break; case NL80211_IFTYPE_P2P_CLIENT: ar->next_mode = INFRA_NETWORK; break; case NL80211_IFTYPE_P2P_GO: ar->next_mode = AP_NETWORK; break; default: ath6kl_err("invalid interface type %u\n", type); return -EOPNOTSUPP; } wdev->iftype = type; return 0; }
static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie) { struct ath6kl *ar = ath6kl_priv(dev); u32 id; const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) buf; if (buf + len >= mgmt->u.probe_resp.variable && ar->nw_type == AP_NETWORK && test_bit(CONNECTED, &ar->flag) && ieee80211_is_probe_resp(mgmt->frame_control)) { /* * Send Probe Response frame in AP mode using a separate WMI * command to allow the target to fill in the generic IEs. */ *cookie = 0; /* TX status not supported */ return ath6kl_send_go_probe_resp(ar, buf, len, chan->center_freq); } id = ar->send_action_id++; if (id == 0) { /* * 0 is a reserved value in the WMI command and shall not be * used for the command. */ id = ar->send_action_id++; } *cookie = id; return ath6kl_wmi_send_action_cmd(ar->wmi, id, chan->center_freq, wait, buf, len); }
static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *ibss_param) { struct ath6kl *ar = ath6kl_priv(dev); int status; if (!ath6kl_cfg80211_ready(ar)) return -EIO; ar->ssid_len = ibss_param->ssid_len; memcpy(ar->ssid, ibss_param->ssid, ar->ssid_len); if (ibss_param->channel) ar->ch_hint = ibss_param->channel->center_freq; if (ibss_param->channel_fixed) { /* * TODO: channel_fixed: The channel should be fixed, do not * search for IBSSs to join on other channels. Target * firmware does not support this feature, needs to be * updated. */ return -EOPNOTSUPP; } memset(ar->req_bssid, 0, sizeof(ar->req_bssid)); if (ibss_param->bssid && !is_broadcast_ether_addr(ibss_param->bssid)) memcpy(ar->req_bssid, ibss_param->bssid, sizeof(ar->req_bssid)); ath6kl_set_wpa_version(ar, 0); status = ath6kl_set_auth_type(ar, NL80211_AUTHTYPE_OPEN_SYSTEM); if (status) return status; if (ibss_param->privacy) { ath6kl_set_cipher(ar, WLAN_CIPHER_SUITE_WEP40, true); ath6kl_set_cipher(ar, WLAN_CIPHER_SUITE_WEP40, false); } else { ath6kl_set_cipher(ar, 0, true); ath6kl_set_cipher(ar, 0, false); } ar->nw_type = ar->next_mode; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: connect called with authmode %d dot11 auth %d" " PW crypto %d PW crypto len %d GRP crypto %d" " GRP crypto len %d channel hint %u\n", __func__, ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto, ar->prwise_crypto_len, ar->grp_crypto, ar->grp_crypto_len, ar->ch_hint); status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type, ar->dot11_auth_mode, ar->auth_mode, ar->prwise_crypto, ar->prwise_crypto_len, ar->grp_crypto, ar->grp_crypto_len, ar->ssid_len, ar->ssid, ar->req_bssid, ar->ch_hint, ar->connect_ctrl_flags); set_bit(CONNECT_PEND, &ar->flag); return 0; }
static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, const u8 *mac_addr, struct key_params *params) { struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); struct ath6kl_key *key = NULL; u8 key_usage; u8 key_type; int status = 0; if (!ath6kl_cfg80211_ready(ar)) return -EIO; if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: key index %d out of bounds\n", __func__, key_index); return -ENOENT; } key = &ar->keys[key_index]; memset(key, 0, sizeof(struct ath6kl_key)); if (pairwise) key_usage = PAIRWISE_USAGE; else key_usage = GROUP_USAGE; if (params) { if (params->key_len > WLAN_MAX_KEY_LEN || params->seq_len > sizeof(key->seq)) return -EINVAL; key->key_len = params->key_len; memcpy(key->key, params->key, key->key_len); key->seq_len = params->seq_len; memcpy(key->seq, params->seq, key->seq_len); key->cipher = params->cipher; } switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: key_type = WEP_CRYPT; break; case WLAN_CIPHER_SUITE_TKIP: key_type = TKIP_CRYPT; break; case WLAN_CIPHER_SUITE_CCMP: key_type = AES_CRYPT; break; default: return -ENOTSUPP; } if (((ar->auth_mode == WPA_PSK_AUTH) || (ar->auth_mode == WPA2_PSK_AUTH)) && (key_usage & GROUP_USAGE)) del_timer(&ar->disconnect_timer); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n", __func__, key_index, key->key_len, key_type, key_usage, key->seq_len); ar->def_txkey_index = key_index; if (ar->nw_type == AP_NETWORK && !pairwise && (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) { ar->ap_mode_bkey.valid = true; ar->ap_mode_bkey.key_index = key_index; ar->ap_mode_bkey.key_type = key_type; ar->ap_mode_bkey.key_len = key->key_len; memcpy(ar->ap_mode_bkey.key, key->key, key->key_len); if (!test_bit(CONNECTED, &ar->flag)) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group " "key configuration until AP mode has been " "started\n"); /* * The key will be set in ath6kl_connect_ap_mode() once * the connected event is received from the target. */ return 0; } } if (ar->next_mode == AP_NETWORK && key_type == WEP_CRYPT && !test_bit(CONNECTED, &ar->flag)) { /* * Store the key locally so that it can be re-configured after * the AP mode has properly started * (ath6kl_install_statioc_wep_keys). */ ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration " "until AP mode has been started\n"); ar->wep_key_list[key_index].key_len = key->key_len; memcpy(ar->wep_key_list[key_index].key, key->key, key->key_len); return 0; } status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index, key_type, key_usage, key->key_len, key->seq, key->key, KEY_OP_INIT_VAL, (u8 *) mac_addr, SYNC_BOTH_WMIFLAG); if (status) return -EIO; return 0; }
static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request) { struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev); s8 n_channels = 0; u16 *channels = NULL; int ret = 0; if (!ath6kl_cfg80211_ready(ar)) return -EIO; if (!ar->usr_bss_filter) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag); ret = ath6kl_wmi_bssfilter_cmd( ar->wmi, (test_bit(CONNECTED, &ar->flag) ? ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0); if (ret) { ath6kl_err("couldn't set bss filtering\n"); return ret; } } if (request->n_ssids && request->ssids[0].ssid_len) { u8 i; if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1)) request->n_ssids = MAX_PROBED_SSID_INDEX - 1; for (i = 0; i < request->n_ssids; i++) ath6kl_wmi_probedssid_cmd(ar->wmi, i + 1, SPECIFIC_SSID_FLAG, request->ssids[i].ssid_len, request->ssids[i].ssid); } if (request->ie) { ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_REQ, request->ie, request->ie_len); if (ret) { ath6kl_err("failed to set Probe Request appie for " "scan"); return ret; } } /* * Scan only the requested channels if the request specifies a set of * channels. If the list is longer than the target supports, do not * configure the list and instead, scan all available channels. */ if (request->n_channels > 0 && request->n_channels <= WMI_MAX_CHANNELS) { u8 i; n_channels = request->n_channels; channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL); if (channels == NULL) { ath6kl_warn("failed to set scan channels, " "scan all channels"); n_channels = 0; } for (i = 0; i < n_channels; i++) channels[i] = request->channels[i]->center_freq; } ret = ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0, false, 0, 0, n_channels, channels); if (ret) ath6kl_err("wmi_startscan_cmd failed\n"); else ar->scan_req = request; kfree(channels); return ret; }
static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_info *sinfo) { struct ath6kl *ar = ath6kl_priv(dev); long left; bool sgi; s32 rate; int ret; u8 mcs; if (memcmp(mac, ar->bssid, ETH_ALEN) != 0) return -ENOENT; if (down_interruptible(&ar->sem)) return -EBUSY; set_bit(STATS_UPDATE_PEND, &ar->flag); ret = ath6kl_wmi_get_stats_cmd(ar->wmi); if (ret != 0) { up(&ar->sem); return -EIO; } left = wait_event_interruptible_timeout(ar->event_wq, !test_bit(STATS_UPDATE_PEND, &ar->flag), WMI_TIMEOUT); up(&ar->sem); if (left == 0) return -ETIMEDOUT; else if (left < 0) return left; if (ar->target_stats.rx_byte) { sinfo->rx_bytes = ar->target_stats.rx_byte; sinfo->filled |= STATION_INFO_RX_BYTES; sinfo->rx_packets = ar->target_stats.rx_pkt; sinfo->filled |= STATION_INFO_RX_PACKETS; } if (ar->target_stats.tx_byte) { sinfo->tx_bytes = ar->target_stats.tx_byte; sinfo->filled |= STATION_INFO_TX_BYTES; sinfo->tx_packets = ar->target_stats.tx_pkt; sinfo->filled |= STATION_INFO_TX_PACKETS; } sinfo->signal = ar->target_stats.cs_rssi; sinfo->filled |= STATION_INFO_SIGNAL; rate = ar->target_stats.tx_ucast_rate; if (is_rate_legacy(rate)) { sinfo->txrate.legacy = rate / 100; } else if (is_rate_ht20(rate, &mcs, &sgi)) { if (sgi) { sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; sinfo->txrate.mcs = mcs - 1; } else { sinfo->txrate.mcs = mcs; } sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; } else if (is_rate_ht40(rate, &mcs, &sgi)) { if (sgi) { sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; sinfo->txrate.mcs = mcs - 1; } else { sinfo->txrate.mcs = mcs; } sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; } else { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "invalid rate from stats: %d\n", rate); ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE); return 0; } sinfo->filled |= STATION_INFO_TX_BITRATE; if (test_bit(CONNECTED, &ar->flag) && test_bit(DTIM_PERIOD_AVAIL, &ar->flag) && ar->nw_type == INFRA_NETWORK) { sinfo->filled |= STATION_INFO_BSS_PARAM; sinfo->bss_param.flags = 0; sinfo->bss_param.dtim_period = ar->assoc_bss_dtim_period; sinfo->bss_param.beacon_interval = ar->assoc_bss_beacon_int; } return 0; }
static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct ath6kl *ar = ath6kl_priv(dev); int status; ar->sme_state = SME_CONNECTING; if (!ath6kl_cfg80211_ready(ar)) return -EIO; if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { ath6kl_err("destroy in progress\n"); return -EBUSY; } if (test_bit(SKIP_SCAN, &ar->flag) && ((sme->channel && sme->channel->center_freq == 0) || (sme->bssid && is_zero_ether_addr(sme->bssid)))) { ath6kl_err("SkipScan: channel or bssid invalid\n"); return -EINVAL; } if (down_interruptible(&ar->sem)) { ath6kl_err("busy, couldn't get access\n"); return -ERESTARTSYS; } if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) { ath6kl_err("busy, destroy in progress\n"); up(&ar->sem); return -EBUSY; } if (ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)]) { /* * sleep until the command queue drains */ wait_event_interruptible_timeout(ar->event_wq, ar->tx_pending[ath6kl_wmi_get_control_ep(ar->wmi)] == 0, WMI_TIMEOUT); if (signal_pending(current)) { ath6kl_err("cmd queue drain timeout\n"); up(&ar->sem); return -EINTR; } } if (test_bit(CONNECTED, &ar->flag) && ar->ssid_len == sme->ssid_len && !memcmp(ar->ssid, sme->ssid, ar->ssid_len)) { ar->reconnect_flag = true; status = ath6kl_wmi_reconnect_cmd(ar->wmi, ar->req_bssid, ar->ch_hint); up(&ar->sem); if (status) { ath6kl_err("wmi_reconnect_cmd failed\n"); return -EIO; } return 0; } else if (ar->ssid_len == sme->ssid_len && !memcmp(ar->ssid, sme->ssid, ar->ssid_len)) { ath6kl_disconnect(ar); } memset(ar->ssid, 0, sizeof(ar->ssid)); ar->ssid_len = sme->ssid_len; memcpy(ar->ssid, sme->ssid, sme->ssid_len); if (sme->channel) ar->ch_hint = sme->channel->center_freq; memset(ar->req_bssid, 0, sizeof(ar->req_bssid)); if (sme->bssid && !is_broadcast_ether_addr(sme->bssid)) memcpy(ar->req_bssid, sme->bssid, sizeof(ar->req_bssid)); ath6kl_set_wpa_version(ar, sme->crypto.wpa_versions); status = ath6kl_set_auth_type(ar, sme->auth_type); if (status) { up(&ar->sem); return status; } if (sme->crypto.n_ciphers_pairwise) ath6kl_set_cipher(ar, sme->crypto.ciphers_pairwise[0], true); else ath6kl_set_cipher(ar, 0, true); ath6kl_set_cipher(ar, sme->crypto.cipher_group, false); if (sme->crypto.n_akm_suites) ath6kl_set_key_mgmt(ar, sme->crypto.akm_suites[0]); if ((sme->key_len) && (ar->auth_mode == NONE_AUTH) && (ar->prwise_crypto == WEP_CRYPT)) { struct ath6kl_key *key = NULL; if (sme->key_idx < WMI_MIN_KEY_INDEX || sme->key_idx > WMI_MAX_KEY_INDEX) { ath6kl_err("key index %d out of bounds\n", sme->key_idx); up(&ar->sem); return -ENOENT; } key = &ar->keys[sme->key_idx]; key->key_len = sme->key_len; memcpy(key->key, sme->key, key->key_len); key->cipher = ar->prwise_crypto; ar->def_txkey_index = sme->key_idx; ath6kl_wmi_addkey_cmd(ar->wmi, sme->key_idx, ar->prwise_crypto, GROUP_USAGE | TX_USAGE, key->key_len, NULL, key->key, KEY_OP_INIT_VAL, NULL, NO_SYNC_WMIFLAG); } if (!ar->usr_bss_filter) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag); if (ath6kl_wmi_bssfilter_cmd(ar->wmi, ALL_BSS_FILTER, 0) != 0) { ath6kl_err("couldn't set bss filtering\n"); up(&ar->sem); return -EIO; } } ar->nw_type = ar->next_mode; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: connect called with authmode %d dot11 auth %d" " PW crypto %d PW crypto len %d GRP crypto %d" " GRP crypto len %d channel hint %u\n", __func__, ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto, ar->prwise_crypto_len, ar->grp_crypto, ar->grp_crypto_len, ar->ch_hint); ar->reconnect_flag = 0; status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type, ar->dot11_auth_mode, ar->auth_mode, ar->prwise_crypto, ar->prwise_crypto_len, ar->grp_crypto, ar->grp_crypto_len, ar->ssid_len, ar->ssid, ar->req_bssid, ar->ch_hint, ar->connect_ctrl_flags); up(&ar->sem); if (status == -EINVAL) { memset(ar->ssid, 0, sizeof(ar->ssid)); ar->ssid_len = 0; ath6kl_err("invalid request\n"); return -ENOENT; } else if (status) { ath6kl_err("ath6kl_wmi_connect_cmd failed\n"); return -EIO; } if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) && ((ar->auth_mode == WPA_PSK_AUTH) || (ar->auth_mode == WPA2_PSK_AUTH))) { mod_timer(&ar->disconnect_timer, jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL)); } ar->connect_ctrl_flags &= ~CONNECT_DO_WPA_OFFLOAD; set_bit(CONNECT_PEND, &ar->flag); return 0; }
static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev, struct beacon_parameters *info, bool add) { struct ath6kl *ar = ath6kl_priv(dev); struct ieee80211_mgmt *mgmt; u8 *ies; int ies_len; struct wmi_connect_cmd p; int res; int i; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add); if (!ath6kl_cfg80211_ready(ar)) return -EIO; if (ar->next_mode != AP_NETWORK) return -EOPNOTSUPP; if (info->beacon_ies) { res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_BEACON, info->beacon_ies, info->beacon_ies_len); if (res) return res; } if (info->proberesp_ies) { res = ath6kl_set_ap_probe_resp_ies(ar, info->proberesp_ies, info->proberesp_ies_len); if (res) return res; } if (info->assocresp_ies) { res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_ASSOC_RESP, info->assocresp_ies, info->assocresp_ies_len); if (res) return res; } if (!add) return 0; ar->ap_mode_bkey.valid = false; /* TODO: * info->interval * info->dtim_period */ if (info->head == NULL) return -EINVAL; mgmt = (struct ieee80211_mgmt *) info->head; ies = mgmt->u.beacon.variable; if (ies > info->head + info->head_len) return -EINVAL; ies_len = info->head + info->head_len - ies; if (info->ssid == NULL) return -EINVAL; memcpy(ar->ssid, info->ssid, info->ssid_len); ar->ssid_len = info->ssid_len; if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE) return -EOPNOTSUPP; /* TODO */ ar->dot11_auth_mode = OPEN_AUTH; memset(&p, 0, sizeof(p)); for (i = 0; i < info->crypto.n_akm_suites; i++) { switch (info->crypto.akm_suites[i]) { case WLAN_AKM_SUITE_8021X: if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1) p.auth_mode |= WPA_AUTH; if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2) p.auth_mode |= WPA2_AUTH; break; case WLAN_AKM_SUITE_PSK: if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1) p.auth_mode |= WPA_PSK_AUTH; if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2) p.auth_mode |= WPA2_PSK_AUTH; break; } } if (p.auth_mode == 0) p.auth_mode = NONE_AUTH; ar->auth_mode = p.auth_mode; for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) { switch (info->crypto.ciphers_pairwise[i]) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: p.prwise_crypto_type |= WEP_CRYPT; break; case WLAN_CIPHER_SUITE_TKIP: p.prwise_crypto_type |= TKIP_CRYPT; break; case WLAN_CIPHER_SUITE_CCMP: p.prwise_crypto_type |= AES_CRYPT; break; } } if (p.prwise_crypto_type == 0) { p.prwise_crypto_type = NONE_CRYPT; ath6kl_set_cipher(ar, 0, true); } else if (info->crypto.n_ciphers_pairwise == 1) ath6kl_set_cipher(ar, info->crypto.ciphers_pairwise[0], true); switch (info->crypto.cipher_group) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: p.grp_crypto_type = WEP_CRYPT; break; case WLAN_CIPHER_SUITE_TKIP: p.grp_crypto_type = TKIP_CRYPT; break; case WLAN_CIPHER_SUITE_CCMP: p.grp_crypto_type = AES_CRYPT; break; default: p.grp_crypto_type = NONE_CRYPT; break; } ath6kl_set_cipher(ar, info->crypto.cipher_group, false); p.nw_type = AP_NETWORK; ar->nw_type = ar->next_mode; p.ssid_len = ar->ssid_len; memcpy(p.ssid, ar->ssid, ar->ssid_len); p.dot11_auth_mode = ar->dot11_auth_mode; p.ch = cpu_to_le16(ar->next_chan); res = ath6kl_wmi_ap_profile_commit(ar->wmi, &p); if (res < 0) return res; return 0; }
static int ath6kl_init(struct net_device *dev) { struct ath6kl *ar = ath6kl_priv(dev); int status = 0; s32 timeleft; if (!ar) return -EIO; /* Do we need to finish the BMI phase */ if (ath6kl_bmi_done(ar)) { status = -EIO; goto ath6kl_init_done; } /* Indicate that WMI is enabled (although not ready yet) */ set_bit(WMI_ENABLED, &ar->flag); ar->wmi = ath6kl_wmi_init(ar); if (!ar->wmi) { ath6kl_err("failed to initialize wmi\n"); status = -EIO; goto ath6kl_init_done; } ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); wlan_node_table_init(&ar->scan_table); /* * The reason we have to wait for the target here is that the * driver layer has to init BMI in order to set the host block * size. */ if (ath6kl_htc_wait_target(ar->htc_target)) { status = -EIO; goto err_node_cleanup; } if (ath6kl_init_service_ep(ar)) { status = -EIO; goto err_cleanup_scatter; } /* setup access class priority mappings */ ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ ar->ac_stream_pri_map[WMM_AC_BE] = 1; ar->ac_stream_pri_map[WMM_AC_VI] = 2; ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ /* give our connected endpoints some buffers */ ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); /* allocate some buffers that handle larger AMSDU frames */ ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); /* setup credit distribution */ ath6k_setup_credit_dist(ar->htc_target, &ar->credit_state_info); ath6kl_cookie_init(ar); /* start HTC */ status = ath6kl_htc_start(ar->htc_target); if (status) { ath6kl_cookie_cleanup(ar); goto err_rxbuf_cleanup; } /* Wait for Wmi event to be ready */ timeleft = wait_event_interruptible_timeout(ar->event_wq, test_bit(WMI_READY, &ar->flag), WMI_TIMEOUT); if (ar->version.abi_ver != ATH6KL_ABI_VERSION) { ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n", ATH6KL_ABI_VERSION, ar->version.abi_ver); status = -EIO; goto err_htc_stop; } if (!timeleft || signal_pending(current)) { ath6kl_err("wmi is not ready or wait was interrupted\n"); status = -EIO; goto err_htc_stop; } ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__); /* communicate the wmi protocol verision to the target */ if ((ath6kl_set_host_app_area(ar)) != 0) ath6kl_err("unable to set the host app area\n"); ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; status = ath6kl_target_config_wlan_params(ar); if (!status) goto ath6kl_init_done; err_htc_stop: ath6kl_htc_stop(ar->htc_target); err_rxbuf_cleanup: ath6kl_htc_flush_rx_buf(ar->htc_target); ath6kl_cleanup_amsdu_rxbufs(ar); err_cleanup_scatter: ath6kl_hif_cleanup_scatter(ar); err_node_cleanup: wlan_node_table_cleanup(&ar->scan_table); ath6kl_wmi_shutdown(ar->wmi); clear_bit(WMI_ENABLED, &ar->flag); ar->wmi = NULL; ath6kl_init_done: return status; }
static int _ath6kl_athtst_wioctl(struct net_device *netdev, void *data) { struct ath6kl_vif *vif = ath6kl_priv(netdev); struct athcfg_wcmd *req = NULL; unsigned long req_len = sizeof(struct athcfg_wcmd); unsigned long status = EIO; req = vmalloc(req_len); if(!req) return ENOMEM; memset(req, 0, req_len); /* XXX: Optimize only copy the amount thats valid */ if(copy_from_user(req, data, req_len)) goto done; //try to use exist WMI command to implement ATHTST command behavior //TBD // printk("%s[%d]req->iic_cmd=%d\n\r",__func__,__LINE__,req->iic_cmd); switch(req->iic_cmd) { case ATHCFG_WCMD_GET_DEV_PRODUCT_INFO: ath6kl_wmi_get_customer_product_info_cmd(vif, &req->iic_data.product_info); break; case ATHCFG_WCMD_GET_REG: ath6kl_wmi_get_customer_reg_cmd(vif, req->iic_data.reg.addr,&req->iic_data.reg.val); break; case ATHCFG_WCMD_SET_REG: ath6kl_wmi_set_customer_reg_cmd(vif, req->iic_data.reg.addr,req->iic_data.reg.val); break; case ATHCFG_WCMD_GET_TESTMODE: ath6kl_wmi_get_customer_testmode_cmd(vif, &req->iic_data.testmode); break; case ATHCFG_WCMD_SET_TESTMODE: ath6kl_wmi_set_customer_testmode_cmd(vif, &req->iic_data.testmode); break; case ATHCFG_WCMD_GET_SCANTIME: ath6kl_wmi_get_customer_scan_time_cmd(vif,&req->iic_data.scantime); break; case ATHCFG_WCMD_GET_MODE://get it from stainfo command ath6kl_wmi_get_customer_mode_cmd(vif, &req->iic_data.phymode); break; case ATHCFG_WCMD_GET_SCAN: ath6kl_wmi_get_customer_scan_cmd(vif,req->iic_data.scan); break; case ATHCFG_WCMD_SET_SCAN: ath6kl_wmi_set_customer_scan_cmd(vif); break; case ATHCFG_WCMD_GET_DEV_VERSION_INFO: ath6kl_wmi_get_customer_version_info_cmd(vif, &req->iic_data.version_info); break; case ATHCFG_WCMD_GET_TX_POWER: ath6kl_wmi_get_customer_txpow_cmd(vif, &req->iic_data.txpower); break; case ATHCFG_WCMD_GET_STAINFO: ath6kl_wmi_get_customer_stainfo_cmd(vif, &req->iic_data.station_info); break; case ATHCFG_WCMD_GET_STATS: ath6kl_wmi_get_customer_stats_cmd(vif, &req->iic_data.stats); break; default: goto done; } //status = __athtst_wioctl_sw[req->iic_cmd](sc, (athcfg_wcmd_data_t *)&req->iic_data); status = 0; /* XXX: Optimize only copy the amount thats valid */ if(copy_to_user(data, req, req_len)) status = -EIO; done: vfree(req); return status; }