static int iwm_wext_siwpower(struct net_device *dev, struct iw_request_info *info, struct iw_param *wrq, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); u32 power_index; if (wrq->disabled) { power_index = IWM_POWER_INDEX_MIN; goto set; } else power_index = IWM_POWER_INDEX_DEFAULT; switch (wrq->flags & IW_POWER_MODE) { case IW_POWER_ON: case IW_POWER_MODE: case IW_POWER_ALL_R: break; default: return -EINVAL; } set: if (power_index == iwm->conf.power_index) return 0; iwm->conf.power_index = power_index; return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, CFG_POWER_INDEX, iwm->conf.power_index); }
static int iwm_wext_siwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { struct iwm_priv *iwm = ndev_to_iwm(dev); size_t len = data->length; int ret; if (iwm->conf.mode == UMAC_MODE_IBSS) return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); if (!test_bit(IWM_STATUS_READY, &iwm->status)) return -EIO; if (len > 0 && ssid[len - 1] == '\0') len--; if (iwm->umac_profile_active) { if (iwm->umac_profile->ssid.ssid_len == len && !memcmp(iwm->umac_profile->ssid.ssid, ssid, len)) return 0; ret = iwm_invalidate_mlme_profile(iwm); if (ret < 0) { IWM_ERR(iwm, "Couldn't invalidate profile\n"); return ret; } } iwm->umac_profile->ssid.ssid_len = len; memcpy(iwm->umac_profile->ssid.ssid, ssid, len); return iwm_send_mlme_profile(iwm); }
static int iwm_wext_giwpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); wrqu->power.disabled = (iwm->conf.power_index == IWM_POWER_INDEX_MIN); return 0; }
static int iwm_wext_giwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rate, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); rate->value = iwm->rate * 1000000; return 0; }
static struct iw_statistics *iwm_get_wireless_stats(struct net_device *dev) { struct iwm_priv *iwm = ndev_to_iwm(dev); struct iw_statistics *wstats = &iwm->wstats; if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { memset(wstats, 0, sizeof(struct iw_statistics)); wstats->qual.updated = IW_QUAL_ALL_INVALID; } return wstats; }
static int iwm_wext_giwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *key) { struct iwm_priv *iwm = ndev_to_iwm(dev); int idx, i; idx = erq->flags & IW_ENCODE_INDEX; if (idx < 1 || idx > 4) { idx = -1; if (!iwm->default_key) { erq->length = 0; erq->flags |= IW_ENCODE_NOKEY; return 0; } else for (i = 0; i < IWM_NUM_KEYS; i++) { if (iwm->default_key == &iwm->keys[i]) { idx = i; break; } } if (idx < 0) return -EINVAL; } else idx--; erq->flags = idx + 1; if (!iwm->keys[idx].in_use) { erq->length = 0; erq->flags |= IW_ENCODE_DISABLED; return 0; } memcpy(key, iwm->keys[idx].key, min_t(int, erq->length, iwm->keys[idx].key_len)); erq->length = iwm->keys[idx].key_len; erq->flags |= IW_ENCODE_ENABLED; if (iwm->umac_profile->mode == UMAC_MODE_BSS) { switch (iwm->umac_profile->sec.auth_type) { case UMAC_AUTH_TYPE_OPEN: erq->flags |= IW_ENCODE_OPEN; break; default: erq->flags |= IW_ENCODE_RESTRICTED; break; } } return 0; }
static int iwm_wext_giwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); if (iwm->conf.mode == UMAC_MODE_IBSS) return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); freq->e = 0; freq->m = iwm->channel; return 0; }
static int iwm_wext_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); if (freq->flags == IW_FREQ_AUTO) return 0; /* frequency/channel can only be set in IBSS mode */ if (iwm->conf.mode != UMAC_MODE_IBSS) return -EOPNOTSUPP; return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); }
static int iwm_wext_siwauth(struct net_device *dev, struct iw_request_info *info, struct iw_param *data, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); int ret; if ((data->flags) & (IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT | IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) { /* We need to invalidate the current profile */ if (iwm->umac_profile_active) { ret = iwm_invalidate_mlme_profile(iwm); if (ret < 0) { IWM_ERR(iwm, "Couldn't invalidate profile\n"); return ret; } } } switch (data->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: return iwm_set_wpa_version(iwm, data->value); break; case IW_AUTH_CIPHER_PAIRWISE: return iwm_set_cipher(iwm, data->value, 1); break; case IW_AUTH_CIPHER_GROUP: return iwm_set_cipher(iwm, data->value, 0); break; case IW_AUTH_KEY_MGMT: return iwm_set_key_mgt(iwm, data->value); break; case IW_AUTH_80211_AUTH_ALG: return iwm_set_auth_alg(iwm, data->value); break; default: return -ENOTSUPP; } return 0; }
static int iwm_wext_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { struct iwm_priv *iwm = ndev_to_iwm(dev); if (iwm->conf.mode == UMAC_MODE_IBSS) return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); if (!test_bit(IWM_STATUS_READY, &iwm->status)) return -EIO; data->length = iwm->umac_profile->ssid.ssid_len; if (data->length) { memcpy(ssid, iwm->umac_profile->ssid.ssid, data->length); data->flags = 1; } else data->flags = 0; return 0; }
static int iwm_wext_giwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); switch (iwm->conf.mode) { case UMAC_MODE_IBSS: return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); case UMAC_MODE_BSS: if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { ap_addr->sa_family = ARPHRD_ETHER; memcpy(&ap_addr->sa_data, iwm->bssid, ETH_ALEN); } else memset(&ap_addr->sa_data, 0, ETH_ALEN); break; default: return -EOPNOTSUPP; } return 0; }
static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); if (iwm->conf.mode == UMAC_MODE_IBSS) return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); if (!test_bit(IWM_STATUS_READY, &iwm->status)) return -EIO; if (is_zero_ether_addr(ap_addr->sa_data) || is_broadcast_ether_addr(ap_addr->sa_data)) { IWM_DBG_WEXT(iwm, DBG, "clear mandatory bssid %pM\n", iwm->umac_profile->bssid[0]); memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN); iwm->umac_profile->bss_num = 0; } else { IWM_DBG_WEXT(iwm, DBG, "add mandatory bssid %pM\n", ap_addr->sa_data); memcpy(&iwm->umac_profile->bssid[0], ap_addr->sa_data, ETH_ALEN); iwm->umac_profile->bss_num = 1; } if (iwm->umac_profile_active) { if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN)) return 0; iwm_invalidate_mlme_profile(iwm); } if (iwm->umac_profile->ssid.ssid_len) return iwm_send_mlme_profile(iwm); return 0; }
static int iwm_wext_siwencodeext(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *extra) { struct iwm_priv *iwm = ndev_to_iwm(dev); struct iwm_key *key; struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; int uninitialized_var(alg), idx, i, remove = 0; IWM_DBG_WEXT(iwm, DBG, "alg: 0x%x\n", ext->alg); IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", ext->key_len); IWM_DBG_WEXT(iwm, DBG, "ext_flags: 0x%x\n", ext->ext_flags); IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags); IWM_DBG_WEXT(iwm, DBG, "length: 0x%x\n", erq->length); switch (ext->alg) { case IW_ENCODE_ALG_NONE: remove = 1; break; case IW_ENCODE_ALG_WEP: if (ext->key_len == WLAN_KEY_LEN_WEP40) alg = UMAC_CIPHER_TYPE_WEP_40; else if (ext->key_len == WLAN_KEY_LEN_WEP104) alg = UMAC_CIPHER_TYPE_WEP_104; else { IWM_ERR(iwm, "Invalid key length: %d\n", ext->key_len); return -EINVAL; } break; case IW_ENCODE_ALG_TKIP: alg = UMAC_CIPHER_TYPE_TKIP; break; case IW_ENCODE_ALG_CCMP: alg = UMAC_CIPHER_TYPE_CCMP; break; default: return -EOPNOTSUPP; } idx = erq->flags & IW_ENCODE_INDEX; if (idx == 0) { if (iwm->default_key) for (i = 0; i < IWM_NUM_KEYS; i++) { if (iwm->default_key == &iwm->keys[i]) { idx = i; break; } } } else if (idx < 1 || idx > 4) { return -EINVAL; } else idx--; if (erq->flags & IW_ENCODE_DISABLED) remove = 1; else if ((erq->length == 0) || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) { iwm->default_key = &iwm->keys[idx]; if (iwm->umac_profile_active && ext->alg == IW_ENCODE_ALG_WEP) return iwm_set_tx_key(iwm, idx); } key = iwm_key_init(iwm, idx, !remove, ext, alg); return iwm_set_key(iwm, remove, !iwm->default_key, key); }
static int iwm_stop(struct net_device *ndev) { struct iwm_priv *iwm = ndev_to_iwm(ndev); return iwm_down(iwm); }
static int iwm_wext_siwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *key_buf) { struct iwm_priv *iwm = ndev_to_iwm(dev); struct iwm_key *uninitialized_var(key); int idx, i, uninitialized_var(alg), remove = 0, ret; IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", erq->length); IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags); if (!iwm->umac_profile) { IWM_ERR(iwm, "UMAC profile not allocated yet\n"); return -ENODEV; } if (erq->length == WLAN_KEY_LEN_WEP40) { alg = UMAC_CIPHER_TYPE_WEP_40; iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_40; iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_40; } else if (erq->length == WLAN_KEY_LEN_WEP104) { alg = UMAC_CIPHER_TYPE_WEP_104; iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_104; iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_104; } if (erq->flags & IW_ENCODE_RESTRICTED) iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; else iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN; idx = erq->flags & IW_ENCODE_INDEX; if (idx == 0) { if (iwm->default_key) for (i = 0; i < IWM_NUM_KEYS; i++) { if (iwm->default_key == &iwm->keys[i]) { idx = i; break; } } else iwm->default_key = &iwm->keys[idx]; } else if (idx < 1 || idx > 4) { return -EINVAL; } else idx--; if (erq->flags & IW_ENCODE_DISABLED) remove = 1; else if (erq->length == 0) { if (!iwm->keys[idx].in_use) return -EINVAL; iwm->default_key = &iwm->keys[idx]; } if (erq->length) { key = &iwm->keys[idx]; memset(key, 0, sizeof(struct iwm_key)); memset(key->hdr.mac, 0xff, ETH_ALEN); key->hdr.key_idx = idx; key->hdr.multicast = 1; key->in_use = !remove; key->alg = alg; key->key_len = erq->length; memcpy(key->key, key_buf, erq->length); IWM_DBG_WEXT(iwm, DBG, "Setting key %d, default: %d\n", idx, !!iwm->default_key); } if (remove) { if ((erq->flags & IW_ENCODE_NOKEY) || (erq->length == 0)) { int j; for (j = 0; j < IWM_NUM_KEYS; j++) if (iwm->keys[j].in_use) { struct iwm_key *k = &iwm->keys[j]; k->in_use = 0; ret = iwm_set_key(iwm, remove, 0, k); if (ret < 0) return ret; } iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_NONE; iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_NONE; iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN; return 0; } else { key->in_use = 0; return iwm_set_key(iwm, remove, 0, key); } } /* * If we havent set a profile yet, we cant set keys. * Keys will be pushed after we're associated. */ if (!iwm->umac_profile_active) return 0; /* * If there is a current active profile, but no * default key, it's not worth trying to associate again. */ if (!iwm->default_key) return 0; /* * Here we have an active profile, but a key setting changed. * We thus have to invalidate the current profile, and push the * new one. Keys will be pushed when association takes place. */ ret = iwm_invalidate_mlme_profile(iwm); if (ret < 0) { IWM_ERR(iwm, "Couldn't invalidate profile\n"); return ret; } return iwm_send_mlme_profile(iwm); }
int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct iwm_priv *iwm = ndev_to_iwm(netdev); struct wireless_dev *wdev = iwm_to_wdev(iwm); struct iwm_tx_info *tx_info; struct iwm_tx_queue *txq; struct iwm_sta_info *sta_info; u8 *dst_addr, sta_id; u16 queue; int ret; if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: " "not associated\n"); netif_tx_stop_all_queues(netdev); goto drop; } queue = skb_get_queue_mapping(skb); BUG_ON(queue >= IWM_TX_DATA_QUEUES); txq = &iwm->txq[queue]; if ((skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) || (skb_queue_len(&txq->stopped_queue) > IWM_TX_LIST_SIZE)) { IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue); netif_stop_subqueue(netdev, queue); return NETDEV_TX_BUSY; } ret = ieee80211_data_from_8023(skb, netdev->dev_addr, wdev->iftype, iwm->bssid, 0); if (ret) { IWM_ERR(iwm, "build wifi header failed\n"); goto drop; } dst_addr = ((struct ieee80211_hdr *)(skb->data))->addr1; for (sta_id = 0; sta_id < IWM_STA_TABLE_NUM; sta_id++) { sta_info = &iwm->sta_table[sta_id]; if (sta_info->valid && !memcmp(dst_addr, sta_info->addr, ETH_ALEN)) break; } if (sta_id == IWM_STA_TABLE_NUM) { IWM_ERR(iwm, "STA %pM not found in sta_table, Tx ignored\n", dst_addr); goto drop; } tx_info = skb_to_tx_info(skb); tx_info->sta = sta_id; tx_info->color = sta_info->color; if (sta_info->qos) tx_info->tid = skb->priority; else tx_info->tid = IWM_UMAC_MGMT_TID; spin_lock_bh(&iwm->txq[queue].lock); skb_queue_tail(&iwm->txq[queue].queue, skb); spin_unlock_bh(&iwm->txq[queue].lock); queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker); netdev->stats.tx_packets++; netdev->stats.tx_bytes += skb->len; return NETDEV_TX_OK; drop: netdev->stats.tx_dropped++; dev_kfree_skb_any(skb); return NETDEV_TX_OK; }
static int iwm_open(struct net_device *ndev) { struct iwm_priv *iwm = ndev_to_iwm(ndev); return iwm_up(iwm); }