rt2x00core.c
linux_drivers/rt2x00-2.0.0-b3/rt2x00core.c
/*
Copyright (C) 2004 - 2005 rt2x00 SourceForge Project
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the
Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
Module: rt2x00core
Abstract: rt2x00 core routines.
Supported chipsets: RT2460, RT2560 & RT2570.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include "rt2x00.h"
#include "rt2x00core.h"
#ifdef DRV_NAME
#undef DRV_NAME
#define DRV_NAME "rt2x00core"
#endif /* DRV_NAME */
static int rt2x00_radio_on(struct _rt2x00_device *device);
static int rt2x00_radio_off(struct _rt2x00_device *device);
struct _rt2x00_device*
rt2x00_device(struct net_device *net_dev)
{
return ieee80211_priv(net_dev);
}
EXPORT_SYMBOL_GPL(rt2x00_device);
static void
rt2x00_wireless_event(struct _rt2x00_device *device, unsigned int event)
{
struct _rt2x00_core *core = rt2x00_core(device);
union iwreq_data wrqu;
switch(event){
case SIOCGIWSCAN:
wrqu.data.length = 0;
wrqu.data.flags = 0;
break;
case SIOCGIWAP:
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(&wrqu.ap_addr.sa_data, &core->config.active.bssid, ETH_ALEN);
break;
}
wireless_send_event(device->net_dev, event, &wrqu, NULL);
}
static void
rt2x00_update_config(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
u16 update_flags = 0x0000;
if(!test_bit(DEVICE_ENABLED, &device->flags)
&& !test_bit(DEVICE_RADIO_ON, &device->flags))
return;
if(test_and_set_bit(DEVICE_CONFIG_UPDATE, &device->flags))
return;
down_write(&device->rt2x00_sem);
update_flags = core->config.update_flags;
core->config.update_flags = 0;
downgrade_write(&device->rt2x00_sem);
if(likely(update_flags))
device->handler->dev_update_config(device, &core->config, update_flags);
up_read(&device->rt2x00_sem);
clear_bit(DEVICE_CONFIG_UPDATE, &device->flags);
}
/*
* Management frames handlers
*/
static struct sk_buff*
rt2x00_create_mgmt_frame(struct _rt2x00_device *device, unsigned int length, u16 type, u8 *addr)
{
struct sk_buff *skb = NULL;
struct ieee80211_hdr_3addr *ieee80211hdr = NULL;
skb = dev_alloc_skb(MGMT_FRAME_SIZE);
if(!skb){
ERROR("couldn't create mgmt frame.\n");
return NULL;
}
skb_reserve(skb, NET_IP_ALIGN);
if(unlikely((skb->tail + length) > skb->end)){
dev_kfree_skb_any(skb);
ERROR("padding of mgmt frame failed.\n");
return NULL;
}
ieee80211hdr = (struct ieee80211_hdr_3addr*)skb_put(skb, length);
ieee80211hdr->frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | type);
ieee80211hdr->duration_id = cpu_to_le16(0);
memcpy(&ieee80211hdr->addr1, addr, ETH_ALEN);
memcpy(&ieee80211hdr->addr2, &device->net_dev->dev_addr, ETH_ALEN);
memcpy(&ieee80211hdr->addr3, addr, ETH_ALEN);
ieee80211hdr->seq_ctl = cpu_to_le16(rt2x00_get_sequence(device));
return skb;
}
static int
rt2x00_encrypt_mgmt_frame(struct _rt2x00_device *device, struct sk_buff *skb)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct ieee80211_crypt_data *crypt = NULL;
spin_lock(&core->ieee80211->lock);
crypt = core->ieee80211->crypt[core->ieee80211->tx_keyidx];
atomic_inc(&crypt->refcnt);
if(pskb_expand_head(skb,
crypt->ops->extra_msdu_prefix_len + crypt->ops->extra_mpdu_prefix_len,
crypt->ops->extra_msdu_postfix_len + crypt->ops->extra_mpdu_postfix_len,
GFP_KERNEL)){
INFO("Failed to expand buffer for encryption.\n");
goto exit_fail;
}
if(crypt->ops->encrypt_msdu
&& crypt->ops->encrypt_msdu(skb, IEEE80211_3ADDR_LEN, crypt->priv) < 0){
INFO("Encryption (msdu) of mgmt frame failed.\n");
goto exit_fail;
}
if(crypt->ops->encrypt_mpdu
&& crypt->ops->encrypt_mpdu(skb, IEEE80211_3ADDR_LEN, crypt->priv) < 0){
INFO("Encryption (mpdu) of mgmt frame failed.\n");
goto exit_fail;
}
if(unlikely((skb->tail + 4) > skb->end)){
ERROR("padding of mgmt frame failed.\n");
goto exit_fail;
}
skb_put(skb, 4);
atomic_dec(&crypt->refcnt);
spin_unlock(&core->ieee80211->lock);
return 0;
exit_fail:
atomic_dec(&crypt->refcnt);
core->ieee80211->ieee_stats.tx_discards++;
spin_unlock(&core->ieee80211->lock);
return -EFAULT;
}
static int
rt2x00_put_mgmt_buffer(struct sk_buff *skb, struct ieee80211_info_element *element, void *data)
{
u8 *buffer = NULL;
if(unlikely((skb->tail + sizeof(*element) + element->len) > skb->end)){
dev_kfree_skb_any(skb);
ERROR("padding of mgmt buffer failed.\n");
return -ENOMEM;
}
buffer = skb_put(skb, sizeof(*element) + element->len);
memcpy(buffer, element, sizeof(*element));
buffer += sizeof(*element);
memcpy(buffer, data, element->len);
return 0;
}
static int
rt2x00_create_mgmt_buffer(struct _rt2x00_device *device, struct sk_buff *skb, u8 flags)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct ieee80211_info_element element;
u8 index = 0x00;
if(flags & MGMT_BUFFER_ESSID){
element.id = MFIE_TYPE_SSID;
element.len = strlen(&core->config.active.essid[0]);
if(rt2x00_put_mgmt_buffer(skb, &element, &core->config.active.essid))
return -ENOMEM;
}
if(flags & MGMT_BUFFER_RATES){
element.id = MFIE_TYPE_RATES;
if(core->config.active.network == IEEE_B){
element.len = 4;
index = 0;
}else if(core->config.active.network & IEEE_B){
element.len = 12;
index = 0;
}else{
element.len = 8;
index = 4;
}
if(rt2x00_put_mgmt_buffer(skb, &element, &capabilities.bitrate[index]))
return -ENOMEM;
}
if(flags & MGMT_BUFFER_CHANNEL){
element.id = MFIE_TYPE_DS_SET;
element.len = sizeof(core->config.active.channel);
if(rt2x00_put_mgmt_buffer(skb, &element, &core->config.active.channel))
return -ENOMEM;
}
if(core->ieee80211->sec.level >= SEC_LEVEL_2
|| !(flags & MGMT_BUFFER_CHALLENGE) || !core->ieee80211->wpa_ie)
return 0;
memcpy(&element, core->ieee80211->wpa_ie, sizeof(element));
if(((core->config.active.wpa_version == IW_AUTH_WPA_VERSION_WPA) && (element.id == MFIE_TYPE_GENERIC))
|| ((core->config.active.wpa_version == IW_AUTH_WPA_VERSION_WPA2) && (element.id == MFIE_TYPE_RSN)))
return rt2x00_put_mgmt_buffer(skb, &element, core->ieee80211->wpa_ie);
WARNING("Required WPA IE not found.\n");
return 0;
}
static u16
rt2x00_create_mgmt_capability(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
u16 capability = 0x0000;
if(core->config.active.iw_mode == IW_MODE_INFRA)
capability |= WLAN_CAPABILITY_ESS;
if(core->config.active.iw_mode == IW_MODE_ADHOC)
capability |= WLAN_CAPABILITY_IBSS;
if(core->ieee80211->sec.encrypt)
capability |= WLAN_CAPABILITY_PRIVACY;
if(core->config.active.flags & CONFIG_ENABLED_SHORT_PREAMBLE)
capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
if(core->config.slot_time == 9)
capability |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
return capability;
}
static void
rt2x00_mgmt_snd_beacon(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct sk_buff *skb = NULL;
struct ieee80211_beacon *beacon = NULL;
u8 addr[ETH_ALEN];
memset(&addr, 0xff, sizeof(addr));
skb = rt2x00_create_mgmt_frame(device, sizeof(*beacon), IEEE80211_STYPE_BEACON, &addr[0]);
if(!skb)
return;
down_read(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
beacon = (struct ieee80211_beacon*)skb->data;
beacon->time_stamp[0] = jiffies;
beacon->beacon_interval = cpu_to_le16(core->config.beacon);
beacon->capability = cpu_to_le16(rt2x00_create_mgmt_capability(device));
if(rt2x00_create_mgmt_buffer(device, skb, MGMT_BUFFER_ALL)){
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
return;
}
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
spin_lock(&core->beacon.lock);
skb_queue_purge(&core->beacon.queue);
spin_unlock(&core->beacon.lock);
rt2x00_queue_ring(&core->beacon, skb);
}
static void
rt2x00_mgmt_snd_probe_req(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct sk_buff *skb = NULL;
struct ieee80211_info_element element;
u8 index = 0x00;
memset(&element, 0x00, sizeof(element));
skb = rt2x00_create_mgmt_frame(device, sizeof(struct ieee80211_probe_request), IEEE80211_STYPE_PROBE_REQ, &core->scan_req->bssid.sa_data[0]);
if(!skb)
return;
element.id = MFIE_TYPE_SSID;
element.len = core->scan_req->essid_len;
if(rt2x00_put_mgmt_buffer(skb, &element, &core->scan_req->essid)){
ERROR("could not create mgmt buffer.\n");
return;
}
element.id = MFIE_TYPE_RATES;
down_read(&device->rt2x00_sem);
if(core->config.active.network == IEEE_B){
element.len = 4;
index = 0;
}else if(core->config.active.network & IEEE_B){
element.len = 12;
index = 0;
}else{
element.len = 8;
index = 4;
}
up_read(&device->rt2x00_sem);
if(rt2x00_put_mgmt_buffer(skb, &element, &capabilities.bitrate[index])){
ERROR("could not create mgmt buffer.\n");
return;
}
rt2x00_queue_ring(&core->prio, skb);
}
static void
rt2x00_mgmt_snd_probe_resp(struct _rt2x00_device *device, u8 *addr)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct sk_buff *skb = NULL;
struct ieee80211_probe_response *probe = NULL;
skb = rt2x00_create_mgmt_frame(device, sizeof(*probe), IEEE80211_STYPE_PROBE_RESP, addr);
if(!skb)
return;
down_read(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
probe = (struct ieee80211_probe_response*)skb->data;
probe->time_stamp[0] = jiffies;
probe->beacon_interval = cpu_to_le16(core->config.beacon);
probe->capability = cpu_to_le16(rt2x00_create_mgmt_capability(device));
if(rt2x00_create_mgmt_buffer(device, skb, MGMT_BUFFER_ALL)){
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
return;
}
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
rt2x00_queue_ring(&core->prio, skb);
INFO("Probe response sent to " MAC_FMT ".\n", MAC_ARG(probe->header.addr1));
}
static void
rt2x00_mgmt_snd_assoc(struct _rt2x00_device *device, u8 *addr)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct sk_buff *skb = NULL;
struct ieee80211_assoc_request *assoc = NULL;
skb = rt2x00_create_mgmt_frame(device, sizeof(*assoc), IEEE80211_STYPE_ASSOC_REQ, addr);
if(!skb)
return;
down_read(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
assoc = (struct ieee80211_assoc_request*)skb->data;
assoc->capability = cpu_to_le16(rt2x00_create_mgmt_capability(device));
assoc->listen_interval = cpu_to_le16(3);
if(rt2x00_create_mgmt_buffer(device, skb, MGMT_BUFFER_ESSID | MGMT_BUFFER_RATES)){
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
return;
}
rt2x00_set_connect(core, CONNECTION_ASSOCIATE);
core->ieee80211->state = IEEE80211_ASSOCIATING;
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
INFO("Association request sent to " MAC_FMT ".\n", MAC_ARG(assoc->header.addr1));
rt2x00_queue_ring(&core->prio, skb);
}
static void
rt2x00_mgmt_snd_assoc_resp(struct _rt2x00_device *device, u16 request, u8 *addr, u16 reason)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct sk_buff *skb = NULL;
struct ieee80211_assoc_response *assoc = NULL;
if(request == IEEE80211_STYPE_ASSOC_REQ)
skb = rt2x00_create_mgmt_frame(device, sizeof(*assoc), IEEE80211_STYPE_ASSOC_RESP, addr);
else
skb = rt2x00_create_mgmt_frame(device, sizeof(*assoc), IEEE80211_STYPE_REASSOC_RESP, addr);
if(!skb)
return;
down_read(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
assoc = (struct ieee80211_assoc_response*)skb->data;
assoc->capability = cpu_to_le16(rt2x00_create_mgmt_capability(device));
assoc->status = cpu_to_le16(reason);
if(rt2x00_create_mgmt_buffer(device, skb, MGMT_BUFFER_ESSID | MGMT_BUFFER_RATES)){
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
return;
}
rt2x00_set_connect(core, CONNECTION_ASSOCIATE);
core->ieee80211->state = IEEE80211_ASSOCIATING;
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
INFO("Association response sent to " MAC_FMT ".\n", MAC_ARG(assoc->header.addr1));
rt2x00_queue_ring(&core->prio, skb);
}
static void
rt2x00_mgmt_snd_disassoc(struct _rt2x00_device *device, u8 *addr, u16 reason)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct sk_buff *skb = NULL;
struct ieee80211_disassoc *disassoc = NULL;
skb = rt2x00_create_mgmt_frame(device, sizeof(*disassoc), IEEE80211_STYPE_DISASSOC, addr);
if(!skb)
return;
down_read(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
disassoc = (struct ieee80211_disassoc*)skb->data;
disassoc->reason = cpu_to_le16(reason);
rt2x00_set_disconnect(core);
core->ieee80211->state = IEEE80211_AUTHENTICATED;
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
INFO("Disassociation request sent to " MAC_FMT ".\n", MAC_ARG(disassoc->header.addr1));
rt2x00_queue_ring(&core->prio, skb);
}
static void
rt2x00_mgmt_snd_auth(struct _rt2x00_device *device, u16 direction, u8 *addr, u16 reason)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct sk_buff *skb = NULL;
struct ieee80211_auth *auth = NULL;
skb = rt2x00_create_mgmt_frame(device, sizeof(*auth), IEEE80211_STYPE_AUTH, addr);
if(!skb)
return;
down_read(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
auth = (struct ieee80211_auth*)skb->data;
auth->algorithm = cpu_to_le16(core->ieee80211->sec.auth_mode);
auth->transaction = cpu_to_le16((direction == CONNECTION_TO_NODE) ? 1 : 2);
auth->status = cpu_to_le16(reason);
if(rt2x00_create_mgmt_buffer(device, skb, MGMT_BUFFER_CHALLENGE)){
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
return;
}
rt2x00_init_connect(core, CONNECTION_AUTHENTICATE, direction);
core->ieee80211->state = IEEE80211_AUTHENTICATING;
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
INFO("Authentication request sent to " MAC_FMT ".\n", MAC_ARG(auth->header.addr1));
rt2x00_queue_ring(&core->prio, skb);
}
static void
rt2x00_mgmt_snd_auth_shake(struct _rt2x00_device *device, u8 *addr, u16 reason)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct sk_buff *skb = NULL;
struct ieee80211_auth *auth = NULL;
skb = rt2x00_create_mgmt_frame(device, sizeof(*auth), IEEE80211_STYPE_AUTH, addr);
if(!skb)
return;
down_read(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
auth = (struct ieee80211_auth*)skb->data;
auth->algorithm = cpu_to_le16(core->ieee80211->sec.auth_mode);
auth->transaction = cpu_to_le16((CONNECT_DIR(core) == CONNECTION_TO_NODE) ? 3 : 4);
auth->status = cpu_to_le16(reason);
if(rt2x00_create_mgmt_buffer(device, skb, MGMT_BUFFER_CHALLENGE))
goto exit_fail;
if(rt2x00_encrypt_mgmt_frame(device, skb))
goto exit_fail;
rt2x00_set_connect(core, CONNECTION_HANDSHAKE);
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
INFO("Authentication handshake request sent to " MAC_FMT ".\n", MAC_ARG(auth->header.addr1));
rt2x00_queue_ring(&core->prio, skb);
return;
exit_fail:
dev_kfree_skb_any(skb);
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
}
static void
rt2x00_mgmt_snd_deauth(struct _rt2x00_device *device, u8 *addr, u16 reason)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct sk_buff *skb = NULL;
struct ieee80211_deauth *auth = NULL;
skb = rt2x00_create_mgmt_frame(device, sizeof(*auth), IEEE80211_STYPE_DEAUTH, addr);
if(!skb)
return;
down_read(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
auth = (struct ieee80211_deauth*)skb->data;
auth->reason = cpu_to_le16(reason);
rt2x00_set_disconnect(core);
core->ieee80211->state = IEEE80211_INITIALIZED;
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
INFO("Deauthentication request sent to " MAC_FMT ".\n", MAC_ARG(auth->header.addr1));
rt2x00_queue_ring(&core->prio, skb);
}
static int
rt2x00_handle_auth(struct net_device *net_dev, struct ieee80211_auth *auth)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
u16 status = le16_to_cpu(auth->status);
down_read(&device->rt2x00_sem);
if(!core->network
|| (core->config.active.flags & CONFIG_FIX_BSSID
&& compare_ether_addr(&auth->header.addr2[0], &core->config.active.bssid[0]))){
up_read(&device->rt2x00_sem);
return 0;
}
if(!AUTHENTICATING(core)){
up_read(&device->rt2x00_sem);
INFO("Authentication request received from " MAC_FMT ".\n", MAC_ARG(auth->header.addr2));
rt2x00_mgmt_snd_auth(device, CONNECTION_FROM_NODE, &auth->header.addr2[0], WLAN_STATUS_SUCCESS);
return 0;
}
if(status != WLAN_STATUS_SUCCESS){
up_read(&device->rt2x00_sem);
INFO("Authentication request failed for " MAC_FMT ", status: %d.\n", MAC_ARG(auth->header.addr2), status);
return 0;
}
spin_lock(&core->ieee80211->lock);
if(!core->ieee80211->ieee802_1x
&& core->ieee80211->sec.encrypt
&& core->ieee80211->sec.level >= SEC_LEVEL_2){
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
rt2x00_mgmt_snd_auth_shake(device, &auth->header.addr2[0], WLAN_STATUS_SUCCESS);
return 0;
}
core->ieee80211->state = IEEE80211_AUTHENTICATED;
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
INFO("Authentication request succeeded for " MAC_FMT ".\n", MAC_ARG(auth->header.addr2));
rt2x00_mgmt_snd_assoc(device, &auth->header.addr2[0]);
return 0;
}
static int
rt2x00_handle_deauth(struct net_device *net_dev, struct ieee80211_auth *auth)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
rt2x00_mgmt_snd_deauth(device, &auth->header.addr2[0], auth->transaction);
return 0;
}
static int
rt2x00_handle_probe_request(struct net_device *net_dev, struct ieee80211_probe_request *req, struct ieee80211_rx_stats *rx_stats)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
if(core->config.active.iw_mode == IW_MODE_MONITOR
|| core->config.active.iw_mode == IW_MODE_INFRA){
up_read(&device->rt2x00_sem);
return 0;
}
up_read(&device->rt2x00_sem);
rt2x00_mgmt_snd_probe_resp(device, &req->header.addr2[0]);
return 0;
}
static void
rt2x00_start_connection(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
u8 mode = 0x00;
down_write(&device->rt2x00_sem);
/*
* Start net interface tx /rx.
*/
netif_carrier_on(device->net_dev);
netif_start_queue(device->net_dev);
mode = core->config.active.iw_mode;
core->config.led_status = 1;
core->config.update_flags |= UPDATE_LED_STATUS;
up_write(&device->rt2x00_sem);
if(mode != IW_MODE_MONITOR){
rt2x00_mgmt_snd_beacon(device);
rt2x00_wireless_event(device, SIOCGIWAP);
}
rt2x00_update_config(device);
}
static void
rt2x00_handle_assoc(struct _rt2x00_device *device, struct ieee80211_hdr_4addr *ieee80211hdr)
{
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
if(!core->network
|| (core->config.active.flags & CONFIG_FIX_BSSID
&& compare_ether_addr(&ieee80211hdr->addr2[0], &core->config.active.bssid[0]))){
up_read(&device->rt2x00_sem);
return;
}
spin_lock(&core->ieee80211->lock);
rt2x00_set_connect(core, CONNECTION_ESTABLISHED);
core->ieee80211->state = IEEE80211_ASSOCIATED;
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
INFO("Association request received from " MAC_FMT ".\n", MAC_ARG(ieee80211hdr->addr2));
if(WLAN_FC_GET_STYPE(le16_to_cpu(ieee80211hdr->frame_ctl)) == IEEE80211_STYPE_ASSOC_REQ)
rt2x00_mgmt_snd_assoc_resp(device, IEEE80211_STYPE_ASSOC_REQ, &ieee80211hdr->addr2[0], WLAN_STATUS_SUCCESS);
else
rt2x00_mgmt_snd_assoc_resp(device, IEEE80211_STYPE_REASSOC_REQ, &ieee80211hdr->addr2[0], WLAN_STATUS_SUCCESS);
rt2x00_start_connection(device);
return;
}
static int
rt2x00_handle_assoc_resp(struct net_device *net_dev, struct ieee80211_assoc_response *assoc, struct ieee80211_network *network)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
u16 status = le16_to_cpu(assoc->status);
down_read(&device->rt2x00_sem);
if(!ASSOCIATING(core)
|| !core->network || compare_ether_addr(&assoc->header.addr2[0], &core->network->bssid[0])){
up_read(&device->rt2x00_sem);
return 0;
}
if(status != WLAN_STATUS_SUCCESS){
up_read(&device->rt2x00_sem);
INFO("Association request failed for " MAC_FMT ", reason: %d.\n", MAC_ARG(assoc->header.addr2), status);
return 0;
}
core->network->last_associate = jiffies;
core->network->last_scanned = jiffies;
spin_lock(&core->ieee80211->lock);
rt2x00_set_connect(core, CONNECTION_ESTABLISHED);
core->ieee80211->state = IEEE80211_ASSOCIATED;
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
INFO("Association request succeeded for " MAC_FMT ".\n", MAC_ARG(assoc->header.addr2));
rt2x00_start_connection(device);
return 0;
}
static int
rt2x00_handle_disassoc(struct net_device *net_dev, struct ieee80211_disassoc *disassoc)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
if(!core->network || compare_ether_addr(&disassoc->header.addr2[0], &core->network->bssid[0]))
return 0;
INFO("Disassociation request received from " MAC_FMT ".\n", MAC_ARG(disassoc->header.addr2));
rt2x00_mgmt_snd_disassoc(device, &disassoc->header.addr2[0], disassoc->reason);
return 0;
}
static int
rt2x00_handle_beacon(struct net_device *net_dev, struct ieee80211_beacon *beacon, struct ieee80211_network *network)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
network->last_scanned = jiffies;
if(core->network && !compare_ether_addr(&network->bssid[0], &core->network->bssid[0]))
memcpy(core->network, network, sizeof(*network));
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_handle_probe_resp(struct net_device *net_dev, struct ieee80211_probe_response *resp, struct ieee80211_network *network)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
network->last_scanned = jiffies;
if(core->network && !compare_ether_addr(&network->bssid[0], &core->network->bssid[0]))
memcpy(core->network, network, sizeof(*network));
up_read(&device->rt2x00_sem);
return 0;
}
/*
* Scanning routines.
*/
#define SCAN_TIME ( HZ / 5 )
#define BEACON_EXPIRE ( HZ * 2 )
static void
rt2x00_start_scan(struct _rt2x00_device *device, struct iw_scan_req *scan_req, const u16 flags)
{
struct _rt2x00_core *core = rt2x00_core(device);
const struct ieee80211_geo *geo = ieee80211_get_geo(core->ieee80211);
u8 counter = 0x00;
if(test_and_set_bit(DEVICE_SCANNING, &device->flags)){
NOTICE("Device already busy scanning.\n");
return;
}
down(&core->scan_sem);
core->scan_flags = flags;
core->scan_req = kmalloc(sizeof(*core->scan_req), GFP_KERNEL);
if(!core->scan_req){
up(&core->scan_sem);
WARNING("not enough memory to allocate scan request.\n");
return;
}
if(scan_req)
memcpy(core->scan_req, scan_req, sizeof(*core->scan_req));
else
memset(core->scan_req, 0x00, sizeof(*core->scan_req));
if(core->scan_flags == IW_SCAN_DEFAULT)
memset(&core->scan_req->bssid.sa_data, 0xff, sizeof(core->scan_req->bssid.sa_data));
if(!(core->scan_flags & IW_SCAN_THIS_ESSID)){
memset(&core->scan_req->essid, '\0', sizeof(core->scan_req->essid));
core->scan_req->essid_len = 0;
}
if(core->scan_flags & IW_SCAN_THIS_FREQ){
down_read(&device->rt2x00_sem);
core->scan_req->channel_list[0].m = core->config.active.channel;
core->scan_req->num_channels = 1;
up_read(&device->rt2x00_sem);
}else if(!core->scan_req->num_channels){
spin_lock(&core->ieee80211->lock);
if(core->ieee80211->freq_band & IEEE80211_24GHZ_BAND){
for(counter = 0; counter < geo->bg_channels; counter++)
core->scan_req->channel_list[counter].m = geo->bg[counter].channel;
core->scan_req->num_channels += geo->bg_channels;
}
if(core->ieee80211->freq_band & IEEE80211_52GHZ_BAND){
for(counter = 0; counter < geo->a_channels; counter++)
core->scan_req->channel_list[core->scan_req->num_channels + counter].m = geo->a[counter].channel;
core->scan_req->num_channels += geo->a_channels;
}
spin_unlock(&core->ieee80211->lock);
}else{
for(counter = 0; counter < core->scan_req->num_channels; counter++){
if(core->scan_req->channel_list[counter].m > 1000)
core->scan_req->channel_list[counter].m =
rt2x00_freq_to_channel(core->scan_req->channel_list[counter].m / 100000);
}
}
if(core->scan_flags & IW_SCAN_THIS_FREQ){
INFO("Start scanning current channel...\n");
}else{
INFO("Start scanning all channels...\n");
down_write(&device->rt2x00_sem);
core->config.active.channel = core->scan_req->channel_list[0].m;
core->config.update_flags |= UPDATE_CHANNEL;
up_write(&device->rt2x00_sem);
}
rt2x00_update_config(device);
if(core->scan_req->scan_type == IW_SCAN_TYPE_ACTIVE)
rt2x00_mgmt_snd_probe_req(device);
queue_delayed_work(core->workqueue, &core->scan_tuner, SCAN_TIME);
}
static void
rt2x00_stop_scan(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
cancel_delayed_work(&core->scan_tuner);
if(!test_and_clear_bit(DEVICE_SCANNING, &device->flags))
return;
if(!(core->scan_flags & IW_SCAN_THIS_FREQ)){
down_write(&device->rt2x00_sem);
core->config.active.channel = core->config.user.channel;
core->config.update_flags |= UPDATE_CHANNEL;
up_write(&device->rt2x00_sem);
rt2x00_update_config(device);
}
if(core->scan_req)
kfree(core->scan_req);
core->scan_req = NULL;
if(!test_bit(DEVICE_ENABLED, &device->flags)
&& test_bit(DEVICE_RADIO_ON, &device->flags))
rt2x00_radio_off(device);
rt2x00_wireless_event(device, SIOCGIWSCAN);
up(&core->scan_sem);
INFO("...Scan ended.\n");
}
static void
rt2x00_scan_periodic(void *data)
{
struct _rt2x00_device *device = data;
struct _rt2x00_core *core = rt2x00_core(device);
u8 counter = 0x00;
u8 channel = 0x00;
u8 new_channel = 0x00;
down_read(&device->rt2x00_sem);
channel = core->config.active.channel;
up_read(&device->rt2x00_sem);
if(!test_bit(DEVICE_RADIO_ON, &device->flags))
return;
if(!test_bit(DEVICE_SCANNING, &device->flags)){
WARNING("Scan update failed, not busy scanning.\n");
return;
}
if(core->scan_flags & IW_SCAN_THIS_FREQ || core->scan_req->num_channels == 1){
rt2x00_stop_scan(device);
return;
}
for(counter = 0; counter < (core->scan_req->num_channels - 1); counter++){
if(channel == core->scan_req->channel_list[counter].m)
new_channel = core->scan_req->channel_list[counter + 1].m;
}
if(!new_channel){
rt2x00_stop_scan(device);
return;
}
down_write(&device->rt2x00_sem);
core->config.active.channel = new_channel;
core->config.update_flags |= UPDATE_CHANNEL;
up_write(&device->rt2x00_sem);
rt2x00_update_config(device);
if(core->scan_req->scan_type == IW_SCAN_TYPE_ACTIVE)
rt2x00_mgmt_snd_probe_req(device);
queue_delayed_work(core->workqueue, &core->scan_tuner, SCAN_TIME);
}
/*
* rt2x00_validate_scan_entry should be called with rt2x00_sem and ieee80211->lock held!.
*/
static int
rt2x00_validate_scan_entry(struct ieee80211_device *ieee80211, struct _rt2x00_config *config, struct ieee80211_network *entry)
{
if(time_after(jiffies, (entry->last_scanned + BEACON_EXPIRE)))
return -EINVAL;
if(config->active.iw_mode != IW_MODE_AUTO){
if(!(entry->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)))
return -EINVAL;
if((config->active.iw_mode == IW_MODE_INFRA)
&& !(entry->capability & WLAN_CAPABILITY_ESS))
return -EINVAL;
if(config->active.iw_mode == IW_MODE_ADHOC
&& (entry->capability & WLAN_CAPABILITY_ESS))
return -EINVAL;
}
if(config->active.flags & CONFIG_FIX_BSSID
&& compare_ether_addr(&config->active.bssid[0], &entry->bssid[0]))
return -EINVAL;
if(config->active.flags & CONFIG_FIX_ESSID
&& !ieee80211_is_empty_essid(&entry->ssid[0], entry->ssid_len)){
if(entry->ssid_len != strlen(&config->active.essid[0])
|| memcmp(&config->active.essid, &entry->ssid, entry->ssid_len))
return -EINVAL;
}
if(config->active.flags & CONFIG_FIX_CHANNEL
&& !(entry->channel == config->active.channel))
return -EINVAL;
if(!(entry->flags & NETWORK_HAS_CCK && ieee80211->modulation & IEEE80211_CCK_MODULATION)
&& !(entry->flags & NETWORK_HAS_OFDM && ieee80211->modulation & IEEE80211_OFDM_MODULATION))
return -EINVAL;
if((entry->capability & WLAN_CAPABILITY_PRIVACY) ^ ieee80211->sec.enabled){
if((ieee80211->sec.level != SEC_LEVEL_1)
|| (!(entry->capability & WLAN_CAPABILITY_PRIVACY)
&& (ieee80211->sec.auth_mode != WLAN_AUTH_OPEN)))
return -EINVAL;
}
return 0;
}
/*
* rt2x00_search_scan should be called with rt2x00_sem and ieee80211->lock held!.
*/
static struct ieee80211_network*
rt2x00_search_scan(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct ieee80211_network *result = NULL;
struct ieee80211_network *entry = NULL;
if(test_bit(DEVICE_SCANNING, &device->flags))
return NULL;
list_for_each_entry(entry, &core->ieee80211->network_list, list){
if(!rt2x00_validate_scan_entry(core->ieee80211, &core->config, entry))
if(!result || entry->stats.rssi > result->stats.rssi)
result = entry;
}
return result;
}
/*
* Configuration validation routines.
* The validation functions must be called with the rt2x00_sem held.
*/
static int
rt2x00_validate_config_mode(struct _rt2x00_device *device, struct _rt2x00_config_request *config, u8 iw_mode)
{
if(!config->adhoc_ofdm && iw_mode == IW_MODE_ADHOC && ieee80211_is_ofdm_rate(config->bitrate)){
NOTICE("OFDM rates are not allowed on an adhoc network.\n");
return -EINVAL;
}
return 0;
}
static int
rt2x00_validate_config_channel(struct _rt2x00_device *device, struct _rt2x00_config_request *config, u8 channel)
{
if((!(config->network & IEEE_A) && rt2x00_is_52ghz_channel(channel))
|| (config->network == IEEE_A && rt2x00_is_24ghz_channel(channel))){
NOTICE("Channel not supported for current network type.\n");
return -EINVAL;
}
if(rt2x00_is_52ghz_channel(channel) && ieee80211_is_cck_rate(config->bitrate)){
NOTICE("CCK rates are not allowed on a 5.2GHz channel.\n");
return -EINVAL;
}
if(channel == 14 && ieee80211_is_ofdm_rate(config->bitrate)){
NOTICE("OFDM rates are not allowed on channel 14.\n");
return -EINVAL;
}
return 0;
}
static int
rt2x00_validate_config_bitrate(struct _rt2x00_device *device, struct _rt2x00_config_request *config, u8 bitrate)
{
struct _rt2x00_core *core = rt2x00_core(device);
u8 cck_rate = ieee80211_is_cck_rate(bitrate);
u8 ofdm_rate = ieee80211_is_ofdm_rate(bitrate);
u8 counter = 0x00;
if(!(cck_rate ^ ofdm_rate)){
NOTICE("Rate %d is not CCK and not OFDM.\n", bitrate);
return -EINVAL;
}
if((!(config->network & IEEE_B) && cck_rate)
|| (config->network == IEEE_B && ofdm_rate)){
NOTICE("Rate not supported for current network type.\n");
return -EINVAL;
}
if(rt2x00_is_52ghz_channel(config->channel) && cck_rate){
NOTICE("CCK rates are not allowed on a 5.2GHz channel.\n");
return -EINVAL;
}
if(config->channel == 14 && ofdm_rate){
NOTICE("OFDM rates are not allowed on channel 14.\n");
return -EINVAL;
}
if(!config->adhoc_ofdm && config->iw_mode == IW_MODE_ADHOC && ofdm_rate){
NOTICE("OFDM rates are not allowed on an adhoc network.\n");
return -EINVAL;
}
if(!core->network)
return 0;
for(counter = 0; counter < MAX_RATES_EX_LENGTH; counter++){
if(counter < core->network->rates_len){
if(bitrate == (core->network->rates[counter]^IEEE80211_BASIC_RATE_MASK))
return 0;
}
if(counter < core->network->rates_ex_len){
if(bitrate == (core->network->rates_ex[counter]^IEEE80211_BASIC_RATE_MASK))
return 0;
}
}
return -EINVAL;
}
static int
rt2x00_activate_config_network(struct _rt2x00_device *device, u8 network)
{
struct _rt2x00_core *core = rt2x00_core(device);
spin_lock(&core->ieee80211->lock);
core->ieee80211->mode = network;
core->ieee80211->abg_true = network;
if(network == IEEE_A){ /* 802.11a */
core->ieee80211->freq_band &= ~IEEE80211_24GHZ_BAND;
core->ieee80211->freq_band |= IEEE80211_52GHZ_BAND;
core->ieee80211->modulation &= ~IEEE80211_CCK_MODULATION;
core->ieee80211->modulation |= IEEE80211_OFDM_MODULATION;
}else if(network & IEEE_A){ /* 802.11abg */
core->ieee80211->freq_band |= IEEE80211_24GHZ_BAND;
core->ieee80211->freq_band |= IEEE80211_52GHZ_BAND;
core->ieee80211->modulation |= IEEE80211_CCK_MODULATION;
core->ieee80211->modulation |= IEEE80211_OFDM_MODULATION;
}else if(network & IEEE_G){ /* 802.11bg */
core->ieee80211->freq_band |= IEEE80211_24GHZ_BAND;
core->ieee80211->freq_band &= ~IEEE80211_52GHZ_BAND;
core->ieee80211->modulation |= IEEE80211_CCK_MODULATION;
core->ieee80211->modulation |= IEEE80211_OFDM_MODULATION;
}else{ /* 802.11b */
core->ieee80211->freq_band |= IEEE80211_24GHZ_BAND;
core->ieee80211->freq_band &= ~IEEE80211_52GHZ_BAND;
core->ieee80211->modulation |= IEEE80211_CCK_MODULATION;
core->ieee80211->modulation &= ~IEEE80211_OFDM_MODULATION;
}
core->config.active.network = network;
spin_unlock(&core->ieee80211->lock);
return 0;
}
static int
rt2x00_activate_config_geography(struct _rt2x00_device *device, u8 geography)
{
struct _rt2x00_core *core = rt2x00_core(device);
struct ieee80211_geo *geo = NULL;
u8 counter = 0x00;
u8 channel = 0x00;
u8 channel_max = 0x00;
u8 a_mask = 0x00;
u16 frequency = 0x0000;
geo = kmalloc(sizeof(*geo), GFP_KERNEL);
if(!geo)
return -ENOMEM;
memset(geo, 0x00, sizeof(geo));
if(geography == IOCTL_GEOGRAPHY_USA){
strcpy(geo->name, "US");
channel = 1;
channel_max = 11;
a_mask = CHANNEL_UNII_LOW | CHANNEL_UNII_HIGH;
}else if(geography == IOCTL_GEOGRAPHY_CANADA){
strcpy(geo->name, "CA");
channel = 1;
channel_max = 11;
a_mask = CHANNEL_UNII_LOW | CHANNEL_UNII_HIGH;
}else if(geography == IOCTL_GEOGRAPHY_EUROPE){
strcpy(geo->name, "EU");
channel = 1;
channel_max = 13;
a_mask = CHANNEL_HIPERLAN2;
}else if(geography == IOCTL_GEOGRAPHY_SPAIN){
strcpy(geo->name, "ES");
channel = 10;
channel_max = 11;
a_mask = CHANNEL_HIPERLAN2;
}else if(geography == IOCTL_GEOGRAPHY_FRANCE){
strcpy(geo->name, "FR");
channel = 10;
channel_max = 13;
a_mask = CHANNEL_HIPERLAN2;
}else if(geography == IOCTL_GEOGRAPHY_JAPAN){
strcpy(geo->name, "JP");
channel = 10;
channel_max = 11;
}else if(geography == IOCTL_GEOGRAPHY_JAPAN1){
strcpy(geo->name, "JP1");
channel = 1;
channel_max = 13;
}else if(geography == IOCTL_GEOGRAPHY_ISRAEL){
strcpy(geo->name, "IL");
channel = 3;
channel_max = 9;
}else if(geography == IOCTL_GEOGRAPHY_INVAL){
strcpy(geo->name, "??");
channel = 1;
channel_max = 11;
}
frequency = 2412;
for(counter = 0; channel <= channel_max; channel++){
geo->bg[counter].channel = channel;
geo->bg[counter].freq = frequency;
frequency += 5;
counter++;
}
if(geography == IOCTL_GEOGRAPHY_JAPAN){
geo->bg_channels += 1;
geo->bg[counter].channel = 14;
geo->bg[counter].freq = 2484;
counter++;
}
geo->bg_channels = counter;
counter = 0;
if(a_mask & CHANNEL_UNII_LOW){
frequency = 5180;
for(channel = CHANNEL_UNII_LOW_MIN; channel <= CHANNEL_UNII_LOW_MAX; channel += 4){
geo->a[counter].channel = channel;
geo->a[counter].freq = frequency;
frequency += 20;
counter++;
}
geo->a_channels = counter;
}
if(a_mask & CHANNEL_HIPERLAN2){
frequency = 5500;
for(channel = CHANNEL_HIPERLAN2_MIN; channel <= CHANNEL_HIPERLAN2_MAX; channel += 4){
geo->a[counter].channel = channel;
geo->a[counter].freq = frequency;
frequency += 20;
counter++;
}
}
if(a_mask & CHANNEL_UNII_HIGH){
frequency = 5745;
for(channel = CHANNEL_UNII_HIGH_MIN; channel <= CHANNEL_UNII_HIGH_MAX; channel += 4){
geo->a[counter].channel = channel;
geo->a[counter].freq = frequency;
frequency += 20;
counter++;
}
}
geo->a_channels = counter;
spin_lock(&core->ieee80211->lock);
ieee80211_set_geo(core->ieee80211, geo);
spin_unlock(&core->ieee80211->lock);
kfree(geo);
return 0;
}
static void
rt2x00_activate_config(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
const struct ieee80211_geo *geo = ieee80211_get_geo(core->ieee80211);
u32 commit = 0x00000000;
INFO("Activating configuration...\n");
down_write(&device->rt2x00_sem);
commit = core->config.user_commit;
core->config.user_commit = 0;
if(commit & COMMIT_NETWORK){
if(!core->config.user.network){
if(test_bit(DEVICE_CAP_802_11A, &device->flags))
core->config.user.network |= IEEE_A;
if(test_bit(DEVICE_CAP_802_11B, &device->flags))
core->config.user.network |= IEEE_B;
if(test_bit(DEVICE_CAP_802_11G, &device->flags))
core->config.user.network |= IEEE_G;
}
if(rt2x00_activate_config_network(device, core->config.user.network)){
core->config.user_commit |= COMMIT_NETWORK;
}else{
core->config.active.network = core->config.user.network;
core->config.user_commit |= COMMIT_BITRATE | COMMIT_CHANNEL;
}
}
if(commit & COMMIT_GEOGRAPHY){
if(core->config.user.geography >= IOCTL_GEOGRAPHY_INVAL)
core->config.user.geography = IOCTL_GEOGRAPHY_INVAL;
if(rt2x00_activate_config_geography(device, core->config.user.geography)){
core->config.user_commit |= COMMIT_GEOGRAPHY;
}else{
core->config.active.geography = core->config.user.geography;
core->config.user_commit |= COMMIT_CHANNEL;
}
}
if(commit & COMMIT_ADHOC_OFDM){
core->config.active.adhoc_ofdm = core->config.user.adhoc_ofdm;
}
if(commit & COMMIT_MODE){
if(rt2x00_validate_config_mode(device, &core->config.active, core->config.user.iw_mode)) {
core->config.user.bitrate = IEEE80211_CCK_RATE_11MB;
core->config.user_commit |= COMMIT_BITRATE;
}
core->config.active.iw_mode = core->config.user.iw_mode;
}
if(commit & COMMIT_CHANNEL){
if(!core->config.user.channel){
if(core->config.active.network & IEEE_B)
core->config.user.channel = geo->bg[0].channel;
else
core->config.user.channel = geo->a[0].channel;
}
if(!rt2x00_validate_config_channel(device, &core->config.active, core->config.user.channel)
|| ieee80211_is_valid_channel(core->ieee80211, core->config.user.channel))
core->config.active.channel = core->config.user.channel;
else if(core->config.active.network & IEEE_B)
core->config.active.channel = geo->bg[0].channel;
else
core->config.active.channel = geo->a[0].channel;
if(rt2x00_is_52ghz_channel(core->config.active.channel))
core->config.slot_time = 9;
else
core->config.slot_time = 20;
core->config.update_flags |= UPDATE_CHANNEL | UPDATE_DURATION;
}
if(commit & COMMIT_BITRATE){
if(!core->config.user.bitrate){
if(core->config.active.network & IEEE_G)
core->config.user.bitrate = IEEE80211_OFDM_RATE_54MB;
else
core->config.user.bitrate = IEEE80211_CCK_RATE_11MB;
}
if(!rt2x00_validate_config_bitrate(device, &core->config.active, core->config.user.bitrate))
core->config.active.bitrate = core->config.user.bitrate;
else if(core->config.active.network & IEEE_B)
core->config.active.bitrate = IEEE80211_CCK_RATE_11MB;
else
core->config.active.bitrate = IEEE80211_OFDM_RATE_54MB;
core->config.update_flags |= UPDATE_BITRATE;
}
if(commit & COMMIT_ESSID){
if(core->config.user.flags & CONFIG_FIX_ESSID)
memcpy(&core->config.active.essid, &core->config.user.essid, sizeof(core->config.active.essid));
}
if(commit & COMMIT_BSSID){
if(core->config.user.flags & CONFIG_FIX_BSSID)
memcpy(&core->config.active.bssid, &core->config.user.bssid, sizeof(core->config.active.bssid));
core->config.update_flags |= UPDATE_BSSID;
}
if(commit & COMMIT_RETRY){
core->config.active.long_retry = core->config.user.long_retry;
core->config.active.short_retry = core->config.user.short_retry;
core->config.update_flags |= UPDATE_RETRY;
}
if(commit & COMMIT_TXPOWER){
core->config.active.txpower = core->config.user.txpower;
core->config.update_flags |= UPDATE_TXPOWER;
}
if(commit & COMMIT_PREAMBLE){
core->config.update_flags |= UPDATE_PREAMBLE;
}
if(commit & COMMIT_ANTENNA){
core->config.active.antenna_flags = core->config.user.antenna_flags;
core->config.update_flags |= UPDATE_ANTENNA;
}
if(commit & COMMIT_SENSITIVITY){
core->config.active.sensitivity = core->config.user.sensitivity;
core->config.update_flags |= UPDATE_SENSITIVITY;
}
if(commit & COMMIT_RTS_THRESHOLD){
core->config.active.rts_threshold = core->config.user.rts_threshold;
spin_lock(&core->ieee80211->lock);
core->ieee80211->rts = core->config.active.rts_threshold;
spin_unlock(&core->ieee80211->lock);
}
if(commit & COMMIT_FRAGMENTATION){
core->config.active.fragmentation = core->config.user.fragmentation;
spin_lock(&core->ieee80211->lock);
core->ieee80211->fts = core->config.active.fragmentation;
spin_unlock(&core->ieee80211->lock);
}
if(commit & COMMIT_PACKET_FILTER){
core->config.update_flags |= UPDATE_PACKET_FILTER;
}
if(commit & COMMIT_AUTHENTICATION){
core->config.active.wpa_version |= core->config.user.wpa_version;
}
core->config.active.flags = core->config.user.flags;
up_write(&device->rt2x00_sem);
}
/*
* Link up/down routines.
*/
#define LINK_TIME ( HZ )
static void
rt2x00_link_up_config(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
s8 counter = 0x00;
u8 rate = 0x00;
u8 bitrate_min = 0x00;
u8 bitrate_max = 0x00;
down_write(&device->rt2x00_sem);
bitrate_min = (core->config.active.network & IEEE_B) ? 0 : 4;
bitrate_max = (core->config.active.network == IEEE_B) ? 4 : 12;
if(core->config.active.iw_mode == IW_MODE_MONITOR){
memset(&core->config.active.essid, '\0', sizeof(core->config.active.essid));
memset(&core->config.active.bssid, 0x00, sizeof(core->config.active.bssid));
core->config.active.channel = core->config.user.channel;
core->config.update_flags |= UPDATE_BSSID | UPDATE_CHANNEL;
up_write(&device->rt2x00_sem);
return;
}
if(!core->network){
up_write(&device->rt2x00_sem);
return;
}
if(!ieee80211_is_empty_essid(&core->network->ssid[0], core->network->ssid_len))
memcpy(&core->config.active.essid, &core->network->ssid, core->network->ssid_len);
else
memcpy(&core->config.active.essid, "<hidden>", sizeof("<hidden>"));
memcpy(&core->config.active.bssid, &core->network->bssid, sizeof(core->config.active.bssid));
core->config.active.channel = core->network->channel;
if(rt2x00_is_52ghz_channel(core->config.active.channel))
core->config.slot_time = 9;
else
core->config.slot_time = 20;
for(counter = (bitrate_max - 1); counter >= bitrate_min; counter--){
rate = capabilities.bitrate[counter];
if(rate <= core->config.user.bitrate
&& !rt2x00_validate_config_bitrate(device, &core->config.active, rate)){
core->config.active.bitrate = rate;
break;
}
}
core->config.update_flags |= UPDATE_BSSID | UPDATE_CHANNEL | UPDATE_DURATION;
up_write(&device->rt2x00_sem);
}
static void
rt2x00_link_down_config(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
down_write(&device->rt2x00_sem);
if(!(core->config.active.flags & CONFIG_FIX_ESSID))
memset(&core->config.active.essid, '\0', sizeof(core->config.active.essid));
if(!(core->config.active.flags & CONFIG_FIX_BSSID))
memset(&core->config.active.bssid, 0x00, sizeof(core->config.active.bssid));
up_write(&device->rt2x00_sem);
}
static void
rt2x00_link_periodic_config(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
u8 status = 0x00;
down_write(&device->rt2x00_sem);
if(device->net_dev->flags & IFF_PROMISC){
if(!(core->config.user.flags & CONFIG_ACCEPT_PROMISC))
core->config.user_commit |= COMMIT_PACKET_FILTER;
core->config.user.flags |= CONFIG_ACCEPT_PROMISC;
}else{
if(core->config.user.flags & CONFIG_ACCEPT_PROMISC)
core->config.user_commit |= COMMIT_PACKET_FILTER;
core->config.user.flags &= ~CONFIG_ACCEPT_PROMISC;
}
status = core->config.user_commit != 0;
device->handler->dev_update_stats(device, &core->rt2x00_stats);
up_write(&device->rt2x00_sem);
if(status)
rt2x00_activate_config(device);
}
static int
rt2x00_connect(struct _rt2x00_device *device, struct ieee80211_network *network, u8 mode)
{
struct _rt2x00_core *core = rt2x00_core(device);
if(network){
core->network = kmalloc(sizeof(struct ieee80211_network), GFP_KERNEL);
if(!core->network){
ERROR("could not allocate ieee80211 struct.\n");
return -ENOMEM;
}
memcpy(core->network, network, sizeof(*network));
}
down_write(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
core->ieee80211->iw_mode = mode;
core->config.active.iw_mode = mode;
if(core->network)
memcpy(&core->ieee80211->bssid, &core->network->bssid, sizeof(core->network->bssid));
else
core->connection_status = CONNECTION_ESTABLISHED;
spin_unlock(&core->ieee80211->lock);
up_write(&device->rt2x00_sem);
rt2x00_link_up_config(device);
rt2x00_link_periodic_config(device);
rt2x00_update_config(device);
if(core->network)
rt2x00_mgmt_snd_auth(device, CONNECTION_TO_NODE, &core->network->bssid[0], WLAN_STATUS_SUCCESS);
if(mode == IW_MODE_MONITOR)
rt2x00_start_connection(device);
return 0;
}
static void
rt2x00_disconnect(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
enum ieee80211_state state = IEEE80211_UNINITIALIZED;
down_write(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
/*
* Stop net interface tx /rx.
*/
netif_stop_queue(device->net_dev);
netif_carrier_off(device->net_dev);
/*
* Reset working mode.
*/
core->ieee80211->iw_mode = 0;
core->config.active.iw_mode = core->config.user.iw_mode;
memset(&core->ieee80211->bssid, 0x00, sizeof(core->ieee80211->bssid));
state = core->ieee80211->state;
core->config.led_status = 0;
core->config.update_flags |= UPDATE_LED_STATUS;
spin_unlock(&core->ieee80211->lock);
up_write(&device->rt2x00_sem);
if(core->network){
if(state == IEEE80211_ASSOCIATED)
rt2x00_mgmt_snd_disassoc(device, &core->network->bssid[0], WLAN_REASON_DISASSOC_STA_HAS_LEFT);
else if(state == IEEE80211_AUTHENTICATED)
rt2x00_mgmt_snd_deauth(device, &core->network->bssid[0], WLAN_REASON_DEAUTH_LEAVING);
kfree(core->network);
core->network = NULL;
}
rt2x00_link_down_config(device);
rt2x00_update_config(device);
rt2x00_wireless_event(device, SIOCGIWAP);
}
static void
rt2x00_link_up(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
u8 mode = 0x00;
u8 channel_scan = 0x00;
NOTICE("Link up.\n");
down_read(&device->rt2x00_sem);
mode = core->config.active.iw_mode;
channel_scan = (core->config.active.flags & CONFIG_FIX_CHANNEL) ? IW_SCAN_THIS_FREQ : IW_SCAN_DEFAULT;
up_read(&device->rt2x00_sem);
if(mode == IW_MODE_AUTO || mode == IW_MODE_ADHOC || mode == IW_MODE_INFRA){
rt2x00_start_scan(device, NULL, channel_scan);
}else if(mode == IW_MODE_MONITOR){
NOTICE("Enabling monitor mode.\n");
down_write(&device->rt2x00_sem);
device->net_dev->type = ARPHRD_IEEE80211;
dev_set_promiscuity(device->net_dev, 1);
up_write(&device->rt2x00_sem);
rt2x00_connect(device, NULL, IW_MODE_MONITOR);
}
queue_delayed_work(core->workqueue, &core->link_tuner, LINK_TIME);
}
static void
rt2x00_link_down(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
cancel_delayed_work(&core->link_tuner);
down_read(&device->rt2x00_sem);
if(core->config.active.iw_mode == IW_MODE_MONITOR){
device->net_dev->type = ARPHRD_ETHER;
dev_set_promiscuity(device->net_dev, -1);
}
up_write(&device->rt2x00_sem);
rt2x00_disconnect(device);
NOTICE("Link down.\n");
}
static void
rt2x00_link_periodic(void *data)
{
struct _rt2x00_device *device = data;
struct _rt2x00_core *core = rt2x00_core(device);
struct ieee80211_network *network = NULL;
int status = 0x00000000;
u8 channel_scan = 0x00;
if(!test_bit(DEVICE_RADIO_ON, &device->flags))
return;
if(test_bit(DEVICE_SCANNING, &device->flags))
goto exit;
down_read(&device->rt2x00_sem);
if(core->config.active.iw_mode == IW_MODE_MONITOR){
up_read(&device->rt2x00_sem);
rt2x00_link_periodic_config(device);
rt2x00_update_config(device);
goto exit;
}
channel_scan = (core->config.active.flags & CONFIG_FIX_CHANNEL) ? IW_SCAN_THIS_FREQ : IW_SCAN_DEFAULT;
if(CONNECTED(core)){
up_read(&device->rt2x00_sem);
rt2x00_link_periodic_config(device);
rt2x00_update_config(device);
goto exit;
}else if(!core->network){
spin_lock(&core->ieee80211->lock);
network = rt2x00_search_scan(device);
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
if(!network
&& (core->config.active.iw_mode == IW_MODE_AUTO
|| core->config.active.iw_mode == IW_MODE_ADHOC))
rt2x00_connect(device, NULL, IW_MODE_ADHOC);
else if(!network && core->config.active.iw_mode == IW_MODE_INFRA)
rt2x00_start_scan(device, NULL, channel_scan);
else if(network && network->capability & WLAN_CAPABILITY_ESS)
rt2x00_connect(device, network, IW_MODE_INFRA);
else if(network && network->capability & WLAN_CAPABILITY_IBSS)
rt2x00_connect(device, network, IW_MODE_ADHOC);
goto exit;
}else{
spin_lock(&core->ieee80211->lock);
status = rt2x00_validate_scan_entry(core->ieee80211, &core->config, core->network);
spin_unlock(&core->ieee80211->lock);
if(status){
up_read(&device->rt2x00_sem);
NOTICE("Beacon lost, rescanning.\n");
rt2x00_disconnect(device);
rt2x00_start_scan(device, NULL, channel_scan);
goto exit;
}
status = rt2x00_connection_status(core);
if(status == -EAGAIN && AUTHENTICATING(core)){
up_read(&device->rt2x00_sem);
if(HANDSHAKING(core))
rt2x00_mgmt_snd_auth_shake(device, &core->network->bssid[0], WLAN_STATUS_SUCCESS);
else
rt2x00_mgmt_snd_auth(device, CONNECTION_TO_NODE, &core->network->bssid[0], WLAN_STATUS_SUCCESS);
}else if(status == -EAGAIN && ASSOCIATING(core)){
up_read(&device->rt2x00_sem);
rt2x00_mgmt_snd_assoc(device, &core->network->bssid[0]);
}else{
up_read(&device->rt2x00_sem);
NOTICE("Connection attempt failed, rescanning.\n");
rt2x00_disconnect(device);
rt2x00_start_scan(device, NULL, channel_scan);
}
goto exit;
}
up_read(&device->rt2x00_sem);
exit:
queue_delayed_work(core->workqueue, &core->link_tuner, LINK_TIME);
}
/*
* Radio control.
*/
static int
rt2x00_radio_on(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
int status = 0x00000000;
if(test_bit(DEVICE_RADIO_ON, &device->flags)){
WARNING("Radio already on.\n");
return -ENOTCONN;
}
if(!try_module_get(device->handler->dev_module)){
ERROR("failure to acquire module.\n");
return -ENODEV;
}
rt2x00_enable_ring(&core->rx);
rt2x00_enable_ring(&core->tx);
rt2x00_enable_ring(&core->atim);
rt2x00_enable_ring(&core->prio);
rt2x00_enable_ring(&core->beacon);
status = device->handler->dev_radio_on(device);
if(status){
module_put(device->handler->dev_module);
return status;
}
set_bit(DEVICE_RADIO_ON, &device->flags);
return 0;
}
static int
rt2x00_radio_off(struct _rt2x00_device *device)
{
struct _rt2x00_core *core = rt2x00_core(device);
if(!test_and_clear_bit(DEVICE_RADIO_ON, &device->flags)){
WARNING("Radio already off.\n");
return -ENOTCONN;
}
/*
* Shut down all rings.
*/
rt2x00_disable_ring(&core->rx);
rt2x00_disable_ring(&core->tx);
rt2x00_disable_ring(&core->atim);
rt2x00_disable_ring(&core->prio);
rt2x00_disable_ring(&core->beacon);
/*
* Halt all periodic handlers.
*/
rt2x00_stop_scan(device);
rt2x00_link_down(device);
/*
* Wait untill all running tasks have been halted.
*/
flush_workqueue(core->workqueue);
/*
* Deactivate device.
*/
device->handler->dev_radio_off(device);
module_put(device->handler->dev_module);
return 0;
}
/*
* IOCTL routines.
*/
static int
rt2x00_ioctl_commit(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
int status = 0x00000000;
u8 commit = 0x00;
u8 mode = 0x00;
down_read(&device->rt2x00_sem);
commit = core->config.user_commit;
mode = core->config.active.iw_mode;
up_read(&device->rt2x00_sem);
if(test_bit(DEVICE_RADIO_ON, &device->flags) && commit & COMMIT_RESET_REQUIRED && mode != IW_MODE_MONITOR){
INFO("SIOCSIWCOMMIT: Connection will be reset.\n");
rt2x00_radio_off(device);
status = rt2x00_radio_on(device);
if(status)
return status;
rt2x00_activate_config(device);
rt2x00_link_up(device);
}else{
INFO("SIOCSIWCOMMIT: Pending configuration will be loaded.\n");
rt2x00_activate_config(device);
rt2x00_update_config(device);
}
return 0;
}
static int
rt2x00_ioctl_get_name(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
char *ieee80211_modes[] = {"?", "a", "b", "ab", "g", "ag", "bg", "abg"};
down_read(&device->rt2x00_sem);
snprintf(wrqu->name, sizeof(wrqu->name), "IEEE 802.11%s", ieee80211_modes[core->config.active.network]);
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_frequency(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
u16 channel = 0x00;
if((wrqu->freq.m >= 0) && (wrqu->freq.m <= 1000))
channel = wrqu->freq.m;
else
channel = rt2x00_freq_to_channel(wrqu->freq.m / 100000);
down_write(&device->rt2x00_sem);
if(!channel || rt2x00_validate_config_channel(device, &core->config.user, channel)){
up_write(&device->rt2x00_sem);
return -EINVAL;
}
core->config.user.channel = channel;
if(wrqu->freq.flags == IW_FREQ_FIXED)
core->config.user.flags |= CONFIG_FIX_CHANNEL;
else
core->config.user.flags &= ~CONFIG_FIX_CHANNEL;
core->config.user_commit |= COMMIT_CHANNEL;
up_write(&device->rt2x00_sem);
INFO("SIOCSIWFREQ: Channel = %d.\n", channel);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_frequency(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
u8 channel = 0x00;
down_read(&device->rt2x00_sem);
if(test_bit(DEVICE_SCANNING, &device->flags))
channel = core->config.user.channel;
else
channel = core->config.active.channel;
up_read(&device->rt2x00_sem);
wrqu->freq.m = rt2x00_channel_to_freq(channel) * 100000;
wrqu->freq.e = 1;
wrqu->freq.i = channel;
wrqu->freq.flags = (core->config.active.flags == CONFIG_FIX_CHANNEL) ? IW_FREQ_FIXED : IW_FREQ_AUTO;
return 0;
}
static int
rt2x00_ioctl_set_mode(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
if(wrqu->mode == IW_MODE_REPEAT
|| wrqu->mode == IW_MODE_MASTER
|| wrqu->mode == IW_MODE_SECOND)
return -ENOTSUPP;
down_write(&device->rt2x00_sem);
core->config.user.iw_mode = wrqu->mode;
core->config.user_commit |= COMMIT_MODE;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_mode(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
if(core->config.active.iw_mode)
wrqu->mode = core->config.active.iw_mode;
else
wrqu->mode = core->config.user.iw_mode;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_sens(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_write(&device->rt2x00_sem);
if(wrqu->sens.value < 0) /* dBm. */
core->config.user.sensitivity = wrqu->sens.value + 120;
else /* RSSI. */
core->config.user.sensitivity = wrqu->sens.value;
core->config.user_commit |= COMMIT_SENSITIVITY;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_sens(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
wrqu->sens.value = core->config.active.sensitivity;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_get_range(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
struct iw_range *range = (struct iw_range *)extra;
const struct ieee80211_geo *geo = ieee80211_get_geo(core->ieee80211);
u8 bitrate_min = 0x00;
u8 bitrate_max = 0x00;
u8 network = 0x00;
u8 counter = 0x00;
memset(range, 0x00, sizeof(*range));
down_read(&device->rt2x00_sem);
bitrate_min = (core->config.active.network & IEEE_B) ? 0 : 4;
bitrate_max = (core->config.active.network == IEEE_B) ? 4 : 12;
network = core->config.active.network;
up_read(&device->rt2x00_sem);
range->throughput = capabilities.bitrate[bitrate_max - 1] * 500000;
IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSPY);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY);
range->num_bitrates = bitrate_max;
for(counter = 0x00; counter < bitrate_max; counter++)
range->bitrate[counter] = capabilities.bitrate[counter] * 500000;
range->min_rts = 0;
range->max_rts = MAX_FRAG_THRESHOLD;
range->min_frag = MIN_FRAG_THRESHOLD;
range->max_frag = MAX_FRAG_THRESHOLD;
range->encoding_size[0] = 5; /* 64bit WEP. */
range->encoding_size[1] = 13; /* 128bit WEP. */
range->num_encoding_sizes = 2;
range->max_encoding_tokens = 4;
range->txpower_capa = IW_TXPOW_RELATIVE;
range->num_txpower = ARRAY_SIZE(capabilities.txpower);
for(counter = 0x00; counter < range->num_txpower; counter++)
range->txpower[counter] = capabilities.txpower[counter];
range->we_version_compiled = WIRELESS_EXT;
range->we_version_source = 18;
range->retry_capa = IW_RETRY_LIMIT;
range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MIN | IW_RETRY_MAX;
range->min_retry = 0;
range->max_retry = 255;
spin_lock(&core->ieee80211->lock);
range->num_channels = 0;
if((network & IEEE_B))
range->num_channels += geo->bg_channels;
if(network & IEEE_A)
range->num_channels += geo->a_channels;
if(range->num_channels > IW_MAX_FREQUENCIES)
range->num_channels = IW_MAX_FREQUENCIES;
range->num_frequency = range->num_channels;
for(counter = 0x00; counter < range->num_frequency; counter++) {
if((network & IEEE_B) && counter < geo->bg_channels){
range->freq[counter].i = geo->bg[counter].channel;
range->freq[counter].m = geo->bg[counter].freq * 100000;
range->freq[counter].e = 1;
}else if((network & IEEE_A) && (counter - geo->bg_channels) < geo->a_channels){
range->freq[counter].i = geo->a[counter - geo->bg_channels].channel;
range->freq[counter].m = geo->a[counter - geo->bg_channels].freq * 100000;
range->freq[counter].e = 1;
}
}
spin_unlock(&core->ieee80211->lock);
range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
wrqu->data.length = sizeof(struct iw_range);
return 0;
}
static int
rt2x00_ioctl_set_wap(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
if(wrqu->ap_addr.sa_family != ARPHRD_ETHER)
return -EINVAL;
down_write(&device->rt2x00_sem);
/*
* When the address is not valid, because it is, for example, a broadcast address,
* we must set the essid to 'any' instead.
*/
if(is_valid_ether_addr(&wrqu->ap_addr.sa_data[0])){
memcpy(&core->config.user.bssid, &wrqu->ap_addr.sa_data, ETH_ALEN);
core->config.user_commit |= COMMIT_BSSID;
core->config.user.flags |= CONFIG_FIX_BSSID;
} else {
core->config.user_commit |= COMMIT_BSSID;
core->config.user.flags &= ~CONFIG_FIX_BSSID;
}
INFO("SIOCSIWAP: Bssid = " MAC_FMT ".\n", MAC_ARG(core->config.user.bssid));
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_wap(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
wrqu->ap_addr.sa_family = ARPHRD_ETHER;
if(CONNECTED(core)
&& (core->config.active.iw_mode == IW_MODE_ADHOC
|| core->config.active.iw_mode == IW_MODE_INFRA))
memcpy(&wrqu->ap_addr.sa_data, &core->config.active.bssid, sizeof(wrqu->ap_addr.sa_data));
else
memset(&wrqu->ap_addr.sa_data, 0x00, sizeof(wrqu->ap_addr.sa_data));
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_mlme(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct iw_mlme *mlme = (struct iw_mlme*)wrqu->data.pointer;
if(!test_bit(DEVICE_RADIO_ON, &device->flags))
return -ENOTCONN;
if(mlme->cmd == IW_MLME_DEAUTH)
rt2x00_mgmt_snd_deauth(device, &mlme->addr.sa_data[0], mlme->reason_code);
if(mlme->cmd == IW_MLME_DISASSOC)
rt2x00_mgmt_snd_disassoc(device, &mlme->addr.sa_data[0], mlme->reason_code);
return 0;
}
static int
rt2x00_ioctl_set_scan(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = rt2x00_device(net_dev);
int status = 0x00000000;
if(test_bit(DEVICE_SCANNING, &device->flags)){
NOTICE("Device already busy scanning, aborting current scan...\n");
rt2x00_stop_scan(device);
}
if(!test_bit(DEVICE_RADIO_ON, &device->flags)){
status = rt2x00_radio_on(device);
if(status)
return status;
rt2x00_activate_config(device);
}
if(wrqu->param.flags)
rt2x00_start_scan(device, (struct iw_scan_req*)extra, wrqu->param.flags);
else
rt2x00_start_scan(device, NULL, IW_SCAN_DEFAULT);
return 0;
}
static int
rt2x00_ioctl_get_scan(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = rt2x00_device(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
if(down_interruptible(&core->scan_sem))
return -EINTR;
up(&core->scan_sem);
return ieee80211_wx_get_scan(core->ieee80211, info, wrqu, extra);
}
static int
rt2x00_ioctl_set_essid(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_write(&device->rt2x00_sem);
if(wrqu->essid.flags){
if(wrqu->essid.length > IW_ESSID_MAX_SIZE) {
up_write(&device->rt2x00_sem);
return -E2BIG;
}
if(wrqu->essid.length > 0){
memcpy(&core->config.user.essid, extra, wrqu->essid.length);
memset(&core->config.user.essid[wrqu->essid.length], '\0', IW_ESSID_MAX_SIZE - wrqu->essid.length);
}
core->config.user.flags |= CONFIG_FIX_ESSID;
}else
core->config.user.flags &= ~CONFIG_FIX_ESSID;
core->config.user_commit |= COMMIT_ESSID;
up_write(&device->rt2x00_sem);
if (wrqu->essid.flags){
INFO("SIOCSIWESSID: Essid = %s length = %d.\n", extra, wrqu->essid.length);}
else{
INFO("SIOCSIWESSID: Essid = ANY\n");}
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_essid(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
wrqu->essid.length = strnlen(core->config.active.essid, IW_ESSID_MAX_SIZE);
if(wrqu->essid.length){
memcpy(extra, &core->config.active.essid, wrqu->essid.length);
wrqu->essid.flags = 1;
}else
wrqu->essid.flags = 0;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_nickname(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
if (wrqu->data.length > IW_ESSID_MAX_SIZE)
return -E2BIG;
down_write(&device->rt2x00_sem);
memcpy(&core->config.nickn, extra, wrqu->data.length);
memset(&core->config.nickn[wrqu->data.length], '\0', IW_ESSID_MAX_SIZE - wrqu->data.length);
up_write(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_get_nickname(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
wrqu->data.length = strnlen(core->config.nickn, IW_ESSID_MAX_SIZE);
memcpy(extra, &core->config.nickn, wrqu->data.length);
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_bitrate(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
u8 rate = wrqu->bitrate.value / 500000;
down_write(&device->rt2x00_sem);
if(wrqu->bitrate.value == -1)
rate = 0;
else{
if(rt2x00_validate_config_bitrate(device, &core->config.user, rate)){
up_write(&device->rt2x00_sem);
return -EINVAL;
}
}
core->config.user.bitrate = rate;
if(wrqu->bitrate.fixed)
core->config.user.flags |= CONFIG_FIX_BITRATE;
else
core->config.user.flags &= ~CONFIG_FIX_BITRATE;
core->config.user_commit |= COMMIT_BITRATE;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_bitrate(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
wrqu->bitrate.value = core->config.active.bitrate * 500000;
wrqu->bitrate.fixed = core->config.active.flags & CONFIG_FIX_BITRATE;
wrqu->bitrate.disabled = 0;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_rts_threshold(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
if(wrqu->rts.value != -1 && wrqu->rts.value > MAX_FRAG_THRESHOLD)
return -EINVAL;
down_write(&device->rt2x00_sem);
if(wrqu->rts.disabled){
core->config.user.flags &= ~CONFIG_ENABLED_RTS;
core->config.user.rts_threshold = MAX_FRAG_THRESHOLD;
}else{
core->config.user.flags |= CONFIG_ENABLED_RTS;
core->config.user.rts_threshold = wrqu->rts.value;
}
if(wrqu->rts.fixed)
core->config.user.flags |= CONFIG_FIX_RTS;
else
core->config.user.flags &= ~CONFIG_FIX_RTS;
core->config.user_commit |= COMMIT_RTS_THRESHOLD;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_rts_threshold(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
wrqu->rts.value = core->config.active.rts_threshold;
wrqu->rts.disabled = !(core->config.active.flags & CONFIG_ENABLED_RTS);
wrqu->rts.fixed = core->config.active.flags & CONFIG_FIX_RTS;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_fragmentation(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
if(wrqu->frag.value != -1
&& (MIN_FRAG_THRESHOLD > wrqu->frag.value || wrqu->frag.value > MAX_FRAG_THRESHOLD))
return -EINVAL;
down_write(&device->rt2x00_sem);
if(wrqu->frag.disabled){
core->config.user.flags &= ~CONFIG_ENABLED_FRAG;
core->config.user.fragmentation = MAX_FRAG_THRESHOLD;
}else{
core->config.user.flags |= CONFIG_ENABLED_FRAG;
core->config.user.fragmentation = wrqu->frag.value;
}
if(wrqu->frag.fixed)
core->config.user.flags |= CONFIG_FIX_FRAG;
else
core->config.user.flags &= ~CONFIG_FIX_FRAG;
core->config.user_commit |= COMMIT_FRAGMENTATION;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_fragmentation(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
wrqu->frag.value = core->config.active.fragmentation;
wrqu->frag.disabled = !(core->config.active.flags & CONFIG_ENABLED_FRAG);
wrqu->frag.fixed = core->config.active.flags & CONFIG_FIX_FRAG;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_txpower(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
int status = 0x00000000;
u8 txpower = 0x00;
u8 counter = 0x00;
u8 max = 0x00;
if(test_bit(DEVICE_ENABLED, &device->flags)
&& test_bit(DEVICE_RADIO_ON, &device->flags)
&& (wrqu->txpower.disabled || wrqu->txpower.value == 0)){
INFO("SIOCSIWTXPOW: Radio off.\n");
return rt2x00_radio_off(device);
}
if(wrqu->txpower.flags & IW_TXPOW_DBM
|| wrqu->txpower.flags & IW_TXPOW_MWATT)
return -ENOTSUPP;
max = ARRAY_SIZE(capabilities.txpower);
for(; counter < max; counter++){
txpower = capabilities.txpower[counter];
if(txpower >= wrqu->txpower.value)
break;
}
down_write(&device->rt2x00_sem);
core->config.user.txpower = txpower;
if(wrqu->txpower.fixed)
core->config.user.flags |= CONFIG_FIX_TXPOWER;
else
core->config.user.flags &= ~CONFIG_FIX_TXPOWER;
core->config.user_commit |= COMMIT_TXPOWER;
up_write(&device->rt2x00_sem);
if(test_bit(DEVICE_ENABLED, &device->flags)
&& !test_bit(DEVICE_RADIO_ON, &device->flags)){
INFO("SIOCSIWTXPOW: Radio on.\n");
status = rt2x00_radio_on(device);
if(status)
return status;
rt2x00_activate_config(device);
rt2x00_link_up(device);
return 0;
}
INFO("SIOCSIWTXPOW: Tx power = %d%s.\n", txpower, "%");
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_txpower(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
wrqu->txpower.value = core->config.active.txpower;
wrqu->txpower.disabled = !test_bit(DEVICE_RADIO_ON, &device->flags);
wrqu->txpower.fixed = core->config.active.flags & CONFIG_FIX_TXPOWER;
wrqu->txpower.flags |= IW_TXPOW_RELATIVE;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_retry(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
if(!(wrqu->retry.flags & IW_RETRY_LIMIT))
return -ENOTSUPP;
down_write(&device->rt2x00_sem);
if(wrqu->retry.flags & IW_RETRY_MAX){
core->config.user.long_retry = wrqu->retry.value;
core->config.user.flags |= CONFIG_ENABLED_LONG_RETRY;
}else if(wrqu->retry.flags & IW_RETRY_MIN){
core->config.user.short_retry = wrqu->retry.value;
core->config.user.flags &= ~CONFIG_ENABLED_LONG_RETRY;
}else{
core->config.user.short_retry = wrqu->retry.value;
core->config.user.long_retry = wrqu->retry.value;
core->config.user.flags |= CONFIG_ENABLED_LONG_RETRY;
}
core->config.user_commit |= COMMIT_RETRY;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_retry(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
if(!(wrqu->retry.flags & IW_RETRY_LIMIT))
return -ENOTSUPP;
down_read(&device->rt2x00_sem);
if(wrqu->retry.flags & IW_RETRY_MAX)
wrqu->retry.value = core->config.active.long_retry;
else if(wrqu->retry.flags & IW_RETRY_MIN)
wrqu->retry.value = core->config.active.short_retry;
else if(core->config.active.flags & CONFIG_ENABLED_LONG_RETRY){
wrqu->retry.value = core->config.active.long_retry;
wrqu->retry.flags |= IW_RETRY_MAX;
}else{
wrqu->retry.value = core->config.active.short_retry;
wrqu->retry.flags |= IW_RETRY_MIN;
}
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_encode(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
int status = 0x00000000;
status = ieee80211_wx_set_encode(netdev_priv(net_dev), info, wrqu, extra);
if(status)
return status;
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_encode(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
return ieee80211_wx_get_encode(netdev_priv(net_dev), info, wrqu, extra);
}
static int
rt2x00_ioctl_set_genie(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
void *data = NULL;
if(wrqu->data.length > MAX_WPA_IE_LEN)
return -E2BIG;
if(wrqu->data.length){
data = kmalloc(wrqu->data.length, GFP_KERNEL);
if(!data)
return -ENOMEM;
memcpy(data, wrqu->data.pointer, wrqu->data.length);
}
spin_lock(&core->ieee80211->lock);
if(core->ieee80211->wpa_ie)
kfree(core->ieee80211->wpa_ie);
core->ieee80211->wpa_ie = data;
core->ieee80211->wpa_ie_len = wrqu->data.length;
spin_unlock(&core->ieee80211->lock);
down_write(&device->rt2x00_sem);
core->config.user_commit |= COMMIT_ENCRYPTION;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_genie(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
spin_lock(&core->ieee80211->lock);
if(core->ieee80211->wpa_ie == NULL || core->ieee80211->wpa_ie_len == 0){
spin_unlock(&core->ieee80211->lock);
return -ENOTSUPP;
}
memcpy(wrqu->data.pointer, core->ieee80211->wpa_ie, core->ieee80211->wpa_ie_len);
wrqu->data.length = core->ieee80211->wpa_ie_len;
spin_unlock(&core->ieee80211->lock);
return 0;
}
static int
rt2x00_ioctl_set_auth(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
int status = -EIWCOMMIT;
down_write(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
switch(wrqu->param.flags & IW_AUTH_INDEX){
case IW_AUTH_WPA_VERSION:
core->config.user.wpa_version = wrqu->param.value;
break;
case IW_AUTH_DROP_UNENCRYPTED:
core->ieee80211->drop_unencrypted = wrqu->param.value;
break;
case IW_AUTH_80211_AUTH_ALG:
if(wrqu->param.value & IW_AUTH_ALG_SHARED_KEY){
core->ieee80211->sec.auth_mode = WLAN_AUTH_SHARED_KEY;
core->ieee80211->open_wep = 0;
}else if(wrqu->param.value & IW_AUTH_ALG_OPEN_SYSTEM){
core->ieee80211->sec.auth_mode = WLAN_AUTH_OPEN;
core->ieee80211->open_wep = 1;
}else
status = -ENOTSUPP;
break;
case IW_AUTH_WPA_ENABLED:
core->ieee80211->wpa_enabled = wrqu->param.value;
break;
case IW_AUTH_PRIVACY_INVOKED:
core->ieee80211->privacy_invoked = wrqu->param.value;
break;
case IW_AUTH_RX_UNENCRYPTED_EAPOL:
core->ieee80211->ieee802_1x = wrqu->param.value;
break;
default:
status = -ENOTSUPP;
break;
}
spin_unlock(&core->ieee80211->lock);
if(status == -EIWCOMMIT)
core->config.user_commit |= COMMIT_AUTHENTICATION;
else if(status == -ENOTSUPP)
ERROR("SIOCSIWAUTH: unknown or unsupported flag flag %d.\n", wrqu->param.flags);
up_write(&device->rt2x00_sem);
return status;
}
static int
rt2x00_ioctl_get_auth(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
int status = 0x00000000;
down_read(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
switch(wrqu->param.flags & IW_AUTH_INDEX){
case IW_AUTH_WPA_VERSION:
wrqu->param.value = core->config.user.wpa_version;
break;
case IW_AUTH_DROP_UNENCRYPTED:
wrqu->param.value = core->ieee80211->drop_unencrypted;
break;
case IW_AUTH_80211_AUTH_ALG:
if(core->ieee80211->sec.auth_mode & WLAN_AUTH_SHARED_KEY)
wrqu->param.value = IW_AUTH_ALG_SHARED_KEY;
else
wrqu->param.value = IW_AUTH_ALG_OPEN_SYSTEM;
break;
case IW_AUTH_WPA_ENABLED:
wrqu->param.value = core->ieee80211->wpa_enabled;
break;
case IW_AUTH_PRIVACY_INVOKED:
wrqu->param.value = core->ieee80211->privacy_invoked;
break;
case IW_AUTH_RX_UNENCRYPTED_EAPOL:
wrqu->param.value = core->ieee80211->ieee802_1x;
break;
default:
status = -ENOTSUPP;
break;
}
spin_unlock(&core->ieee80211->lock);
up_read(&device->rt2x00_sem);
if(status == -ENOTSUPP)
ERROR("SIOCGIWAUTH: unknown or unsupported flag flag %d.\n", wrqu->param.flags);
return status;
}
static int
rt2x00_ioctl_set_encodeext(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
int res = 0x00000000;
res = ieee80211_wx_set_encodeext(netdev_priv(net_dev), info, wrqu, extra);
if(res)
return res;
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_encodeext(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
return ieee80211_wx_get_encodeext(netdev_priv(net_dev), info, wrqu, extra);
}
static int
rt2x00_ioctl_set_network(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
u8 network = *extra;
if((network & IEEE_A) && !test_bit(DEVICE_CAP_802_11A, &device->flags)){
DEBUG("SIOCSIWPRIVNETWORK: Device does not support 802.11a.\n");
return 0;
}
if((network & IEEE_G) && !test_bit(DEVICE_CAP_802_11G, &device->flags)){
DEBUG("SIOCSIWPRIVNETWORK: Device does not support 802.11g.\n");
return 0;
}
if(!(network & IEEE_MODE_MASK))
network = 0;
if(network & IEEE_G)
network |= IEEE_B;
if(network & IEEE_A && network & IEEE_B)
network |= IEEE_G;
down_write(&device->rt2x00_sem);
core->config.user.network = network;
core->config.user_commit |= COMMIT_NETWORK;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_network(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
*extra = core->config.active.network;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_preamble(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_write(&device->rt2x00_sem);
if(*extra & IOCTL_PREAMBLE_SHORT)
core->config.user.flags |= CONFIG_ENABLED_SHORT_PREAMBLE;
else
core->config.user.flags &= ~CONFIG_ENABLED_SHORT_PREAMBLE;
core->config.user_commit |= COMMIT_PREAMBLE;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_preamble(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
if(core->config.active.flags & CONFIG_ENABLED_SHORT_PREAMBLE)
*extra = IOCTL_PREAMBLE_SHORT;
else
*extra = IOCTL_PREAMBLE_LONG;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_antenna(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
u8 antenna = *extra;
down_write(&device->rt2x00_sem);
if(antenna & IOCTL_ANTENNA_RX_MASK){
core->config.user.antenna_flags &= ~ANTENNA_RX;
if(antenna & IOCTL_ANTENNA_RX_DIV)
core->config.user.antenna_flags |= ANTENNA_RX_DIV;
else if(antenna & IOCTL_ANTENNA_RX_A)
core->config.user.antenna_flags |= ANTENNA_RX_A;
else if(antenna & IOCTL_ANTENNA_RX_B)
core->config.user.antenna_flags |= ANTENNA_RX_B;
}
if(antenna & IOCTL_ANTENNA_TX_MASK){
core->config.user.antenna_flags &= ~ANTENNA_TX;
if(antenna & IOCTL_ANTENNA_TX_DIV)
core->config.user.antenna_flags |= ANTENNA_TX_DIV;
else if(antenna & IOCTL_ANTENNA_TX_A)
core->config.user.antenna_flags |= ANTENNA_TX_A;
else if(antenna & IOCTL_ANTENNA_TX_B)
core->config.user.antenna_flags |= ANTENNA_TX_B;
}
core->config.user_commit |= COMMIT_ANTENNA;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_antenna(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
if((core->config.active.antenna_flags & ANTENNA_RX) == ANTENNA_RX_DIV)
*extra |= IOCTL_ANTENNA_RX_DIV;
else if((core->config.active.antenna_flags & ANTENNA_RX) == ANTENNA_RX_A)
*extra |= IOCTL_ANTENNA_RX_A;
else if((core->config.active.antenna_flags & ANTENNA_RX) == ANTENNA_RX_B)
*extra |= IOCTL_ANTENNA_RX_B;
if((core->config.active.antenna_flags & ANTENNA_TX) == ANTENNA_TX_DIV)
*extra |= IOCTL_ANTENNA_TX_DIV;
else if((core->config.active.antenna_flags & ANTENNA_TX) == ANTENNA_TX_A)
*extra |= IOCTL_ANTENNA_TX_A;
else if((core->config.active.antenna_flags & ANTENNA_TX) == ANTENNA_TX_B)
*extra |= IOCTL_ANTENNA_TX_B;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_geography(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
u8 geography = *extra;
if(geography >= IOCTL_GEOGRAPHY_INVAL)
return -EINVAL;
down_write(&device->rt2x00_sem);
core->config.user.geography = geography;
core->config.user_commit |= COMMIT_GEOGRAPHY;
up_write(&device->rt2x00_sem);
return -EIWCOMMIT;
}
static int
rt2x00_ioctl_get_geography(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
*extra = core->config.active.geography;
up_read(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_set_adhoc_ofdm(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_write(&device->rt2x00_sem);
core->config.user.adhoc_ofdm = *extra;
core->config.user_commit |= COMMIT_ADHOC_OFDM;
up_write(&device->rt2x00_sem);
return 0;
}
static int
rt2x00_ioctl_get_adhoc_ofdm(
struct net_device *net_dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
down_read(&device->rt2x00_sem);
*extra = core->config.active.adhoc_ofdm;
up_read(&device->rt2x00_sem);
return 0;
}
static struct iw_statistics*
rt2x00_get_wireless_stats(struct net_device *net_dev)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
return &core->wireless_stats;
}
static const iw_handler rt2x00_ioctl_handler[] = {
(iw_handler) rt2x00_ioctl_commit, /* SIOCSIWCOMMIT */
(iw_handler) rt2x00_ioctl_get_name, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
(iw_handler) rt2x00_ioctl_set_frequency, /* SIOCSIWFREQ */
(iw_handler) rt2x00_ioctl_get_frequency, /* SIOCGIWFREQ */
(iw_handler) rt2x00_ioctl_set_mode, /* SIOCSIWMODE */
(iw_handler) rt2x00_ioctl_get_mode, /* SIOCGIWMODE */
(iw_handler) rt2x00_ioctl_set_sens, /* SIOCSIWSENS */
(iw_handler) rt2x00_ioctl_get_sens, /* SIOCGIWSENS */
(iw_handler) NULL, /* Unused */ /* SIOCSIWRANGE */
(iw_handler) rt2x00_ioctl_get_range, /* SIOCGIWRANGE */
(iw_handler) NULL, /* Unused */ /* SIOCSIWPRIV */
(iw_handler) NULL, /* Provided by kernel */ /* SIOCGIWPRIV */
(iw_handler) NULL, /* Unused */ /* SIOCSIWSTATS */
(iw_handler) NULL, /* Provided by kernel */ /* SIOCGIWSTATS */
(iw_handler) iw_handler_set_spy, /* SIOCSIWSPY */
(iw_handler) iw_handler_get_spy, /* SIOCGIWSPY */
(iw_handler) iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
(iw_handler) iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
(iw_handler) rt2x00_ioctl_set_wap, /* SIOCSIWAP */
(iw_handler) rt2x00_ioctl_get_wap, /* SIOCGIWAP */
(iw_handler) rt2x00_ioctl_set_mlme, /* SIOCSIWMLME */
(iw_handler) NULL, /* Deprecated */ /* SIOCGIWAPLIST */
(iw_handler) rt2x00_ioctl_set_scan, /* SIOCSIWSCAN */
(iw_handler) rt2x00_ioctl_get_scan, /* SIOCGIWSCAN */
(iw_handler) rt2x00_ioctl_set_essid, /* SIOCSIWESSID */
(iw_handler) rt2x00_ioctl_get_essid, /* SIOCGIWESSID */
(iw_handler) rt2x00_ioctl_set_nickname, /* SIOCSIWNICKN */
(iw_handler) rt2x00_ioctl_get_nickname, /* SIOCGIWNICKN */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) rt2x00_ioctl_set_bitrate, /* SIOCSIWRATE */
(iw_handler) rt2x00_ioctl_get_bitrate, /* SIOCGIWRATE */
(iw_handler) rt2x00_ioctl_set_rts_threshold, /* SIOCSIWRTS */
(iw_handler) rt2x00_ioctl_get_rts_threshold, /* SIOCGIWRTS */
(iw_handler) rt2x00_ioctl_set_fragmentation, /* SIOCSIWFRAG */
(iw_handler) rt2x00_ioctl_get_fragmentation, /* SIOCGIWFRAG */
(iw_handler) rt2x00_ioctl_set_txpower, /* SIOCSIWTXPOW */
(iw_handler) rt2x00_ioctl_get_txpower, /* SIOCGIWTXPOW */
(iw_handler) rt2x00_ioctl_set_retry, /* SIOCSIWRETRY */
(iw_handler) rt2x00_ioctl_get_retry, /* SIOCGIWRETRY */
(iw_handler) rt2x00_ioctl_set_encode, /* SIOCSIWENCODE */
(iw_handler) rt2x00_ioctl_get_encode, /* SIOCGIWENCODE */
(iw_handler) NULL, /* SIOCSIWPOWER */
(iw_handler) NULL, /* SIOCGIWPOWER */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) rt2x00_ioctl_set_genie, /* SIOCSIWGENIE */
(iw_handler) rt2x00_ioctl_get_genie, /* SIOCGIWGENIE */
(iw_handler) rt2x00_ioctl_set_auth, /* SIOCSIWAUTH */
(iw_handler) rt2x00_ioctl_get_auth, /* SIOCGIWAUTH */
(iw_handler) rt2x00_ioctl_set_encodeext, /* SIOCSIWENCODEEXT */
(iw_handler) rt2x00_ioctl_get_encodeext, /* SIOCGIWENCODEEXT */
(iw_handler) NULL, /* SIOCSIWPMKSA */
};
static const iw_handler rt2x00_ioctl_priv_handler[] = {
(iw_handler) rt2x00_ioctl_set_network, /* SIOCSIWPRIVNETWORK */
(iw_handler) rt2x00_ioctl_get_network, /* SIOCGIWPRIVNETWORK */
(iw_handler) rt2x00_ioctl_set_preamble, /* SIOCSIWPRIVPREAMBLE */
(iw_handler) rt2x00_ioctl_get_preamble, /* SIOCGIWPRIVPREAMBLE */
(iw_handler) rt2x00_ioctl_set_antenna, /* SIOCSIWPRIVANTENNA */
(iw_handler) rt2x00_ioctl_get_antenna, /* SIOCGIWPRIVANTENNA */
(iw_handler) rt2x00_ioctl_set_geography, /* SIOCSIWPRIVGEOGRAPHY */
(iw_handler) rt2x00_ioctl_get_geography, /* SIOCGIWPRIVGEOGRAPHY */
(iw_handler) rt2x00_ioctl_set_adhoc_ofdm, /* SIOCSIWPRIVADHOCOFDM */
(iw_handler) rt2x00_ioctl_get_adhoc_ofdm, /* SIOCGIWPRIVADHOCOFDM */
};
static const struct iw_priv_args rt2x00_ioctl_priv_args[] = {
{ SIOCSIWPRIVNETWORK, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "set_network" },
{ SIOCGIWPRIVNETWORK, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "get_network" },
{ SIOCSIWPRIVPREAMBLE, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble" },
{ SIOCGIWPRIVPREAMBLE, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "get_preamble" },
{ SIOCSIWPRIVANTENNA, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "set_antenna" },
{ SIOCGIWPRIVANTENNA, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "get_antenna" },
{ SIOCSIWPRIVGEOGRAPHY, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "set_geography" },
{ SIOCGIWPRIVGEOGRAPHY, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "get_geography" },
{ SIOCSIWPRIVADHOCOFDM, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "set_adhoc_ofdm" },
{ SIOCGIWPRIVADHOCOFDM, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "get_adhoc_ofdm" },
};
static const struct iw_handler_def rt2x00_ioctl_handler_def = {
.num_standard = ARRAY_SIZE(rt2x00_ioctl_handler),
.num_private = ARRAY_SIZE(rt2x00_ioctl_priv_handler),
.num_private_args = ARRAY_SIZE(rt2x00_ioctl_priv_args),
.standard = rt2x00_ioctl_handler,
.private = rt2x00_ioctl_priv_handler,
.private_args = rt2x00_ioctl_priv_args,
.get_wireless_stats = rt2x00_get_wireless_stats,
};
static void
rt2x00_set_security(struct net_device *net_dev, struct ieee80211_security *sec)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
u8 counter = 0x00;
down_write(&device->rt2x00_sem);
spin_lock(&core->ieee80211->lock);
for(counter = 0; counter < WEP_KEYS; counter++){
if(sec->flags & (1 << counter)) {
core->ieee80211->sec.encode_alg[counter] = sec->encode_alg[counter];
core->ieee80211->sec.key_sizes[counter] = sec->key_sizes[counter];
if(sec->key_sizes[counter] != 0)
memcpy(core->ieee80211->sec.keys[counter], sec->keys[counter], sec->key_sizes[counter]);
core->ieee80211->sec.flags |= (1 << counter);
}else if(sec->level != SEC_LEVEL_1)
core->ieee80211->sec.flags &= ~(1 << counter);
}
if(sec->flags & SEC_ACTIVE_KEY){
if(sec->active_key <= 3){
core->ieee80211->sec.active_key = sec->active_key;
core->ieee80211->sec.flags |= SEC_ACTIVE_KEY;
}else
core->ieee80211->sec.flags &= ~SEC_ACTIVE_KEY;
}else
core->ieee80211->sec.flags &= ~SEC_ACTIVE_KEY;
if((sec->flags & SEC_AUTH_MODE) && (core->ieee80211->sec.auth_mode != sec->auth_mode)){
core->ieee80211->sec.auth_mode = sec->auth_mode;
core->ieee80211->sec.flags |= SEC_AUTH_MODE;
}
if(sec->flags & SEC_ENABLED && core->ieee80211->sec.enabled != sec->enabled){
core->ieee80211->sec.flags |= SEC_ENABLED;
core->ieee80211->sec.enabled = sec->enabled;
}
if(sec->flags & SEC_ENCRYPT)
core->ieee80211->sec.encrypt = sec->encrypt;
if(sec->flags & SEC_LEVEL && core->ieee80211->sec.level != sec->level){
core->ieee80211->sec.level = sec->level;
core->ieee80211->sec.flags |= SEC_LEVEL;
}
spin_unlock(&core->ieee80211->lock);
core->config.user_commit |= COMMIT_ENCRYPTION;
up_write(&device->rt2x00_sem);
}
/*
* TX/RX related routines.
*/
static int
rt2x00_start_xmit(struct ieee80211_txb *txb, struct net_device *net_dev, int pri)
{
struct _rt2x00_device *device = rt2x00_device(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
struct sk_buff *skb = NULL;
u16 frame = 0x0000;
u8 counter = 0x00;
if(!test_bit(DEVICE_RADIO_ON, &device->flags))
return -ENOTCONN;
frame = rt2x00_get_sequence(device);
if(txb->nr_frags > 1)
frame += 0x0001;
spin_lock(&core->tx.lock);
for(counter= 0x00; counter < txb->nr_frags; counter++){
skb = txb->fragments[counter];
txb->fragments[counter] = NULL;
if(unlikely(!skb))
break;
skb->priority = pri;
((struct ieee80211_hdr_3addr*)skb->data)->seq_ctl = cpu_to_le16(frame);
((struct _skb_cb*)&skb->cb)->id = frame;
((struct _skb_cb*)&skb->cb)->packets = txb->nr_frags;
skb_queue_tail(&core->tx.queue, skb);
frame += 0x0001;
}
if(core->tx.ready)
queue_work(core->workqueue, &core->tx.work);
spin_unlock(&core->tx.lock);
ieee80211_txb_free(txb);
return 0;
}
void
rt2x00_rx(struct _rt2x00_device *device, struct sk_buff *skb)
{
rt2x00_queue_ring(&((struct _rt2x00_core*)rt2x00_core(device))->rx, skb);
}
EXPORT_SYMBOL_GPL(rt2x00_rx);
void
rt2x00_tx(struct _rt2x00_device *device, u8 ring_type)
{
struct _rt2x00_core *core = rt2x00_core(device);
if(!test_bit(DEVICE_RADIO_ON, &device->flags))
return;
if(ring_type & RING_TX)
rt2x00_kick_ring(&core->tx);
if(ring_type & RING_PRIO)
rt2x00_kick_ring(&core->prio);
if(ring_type & RING_ATIM)
rt2x00_kick_ring(&core->atim);
if(ring_type & RING_BEACON)
rt2x00_kick_ring(&core->beacon);
}
EXPORT_SYMBOL_GPL(rt2x00_tx);
void
rt2x00_update_stats(struct _rt2x00_device *device, u8 flags, u8 value)
{
struct _rt2x00_core *core = rt2x00_core(device);
if(flags & STATS_RX_CRC || flags & STATS_RX_PHYSICAL || flags & STATS_RX_QUALITY)
core->ieee80211->stats.rx_crc_errors += value;
if(flags & STATS_RX_DROP)
core->ieee80211->stats.rx_dropped += value;
if(flags & STATS_TX_RETRY_COUNT)
core->ieee80211->stats.collisions += value;
if(flags & STATS_TX_RESULT){
switch(value){
case TX_FAIL_RETRY:
core->wireless_stats.discard.retries++;
core->ieee80211->stats.tx_errors++;
break;
case TX_FAIL_INVALID:
case TX_FAIL_OTHER:
core->wireless_stats.discard.misc++;
core->ieee80211->stats.tx_errors++;
break;
}
}
}
EXPORT_SYMBOL_GPL(rt2x00_update_stats);
/*
* Workqueue handlers.
*/
static void
rt2x00_delayed_rx(void *data)
{
struct _packet_ring *ring = data;
struct _rt2x00_core *core = ring->core;
struct ieee80211_hdr_4addr *ieee80211hdr = NULL;
struct ieee80211_rx_stats rx_stats;
struct sk_buff *skb = NULL;
u16 frame = 0x00;
if(!ring->ready
|| !test_bit(DEVICE_RADIO_ON, &core->device->flags))
return;
memset(&rx_stats, 0x00, sizeof(rx_stats));
down_read(&core->device->rt2x00_sem);
rx_stats.rate = core->config.active.bitrate * 5000; /* in 100 kbps */
rx_stats.received_channel = core->config.active.channel;
if(rt2x00_is_24ghz_channel(core->config.active.channel))
rx_stats.freq = IEEE80211_24GHZ_BAND;
else
rx_stats.freq = IEEE80211_52GHZ_BAND;
up_read(&core->device->rt2x00_sem);
spin_lock(&ring->lock);
while(!skb_queue_empty(&ring->queue)){
skb = skb_dequeue(&ring->queue);
if(!skb){
ERROR("failed to dequeue packet.\n");
break;
}
spin_unlock(&ring->lock);
rx_stats.mask = IEEE80211_STATMASK_RATE;
rx_stats.rssi = ((struct _skb_cb*)&skb->cb)->rssi;
if(rx_stats.rssi)
rx_stats.mask |= IEEE80211_STATMASK_RSSI;
rx_stats.noise = core->rt2x00_stats.noise;
if(rx_stats.noise)
rx_stats.mask |= IEEE80211_STATMASK_NOISE;
rx_stats.signal = core->rt2x00_stats.signal;
if(rx_stats.signal)
rx_stats.mask |= IEEE80211_STATMASK_SIGNAL;
rx_stats.len = skb->len;
ieee80211hdr = (struct ieee80211_hdr_4addr*)skb->data;
frame = le16_to_cpu(ieee80211hdr->frame_ctl);
if(WLAN_FC_GET_TYPE(frame) == IEEE80211_FTYPE_MGMT
&& core->config.active.iw_mode != IW_MODE_MONITOR){
if(WLAN_FC_GET_STYPE(frame) == IEEE80211_STYPE_ASSOC_REQ
|| WLAN_FC_GET_STYPE(frame) == IEEE80211_STYPE_REASSOC_REQ)
rt2x00_handle_assoc(core->device, ieee80211hdr);
else
ieee80211_rx_mgt(core->ieee80211, ieee80211hdr, &rx_stats);
dev_kfree_skb_any(skb);
}else if(!ieee80211_rx(core->ieee80211, skb, &rx_stats))
dev_kfree_skb_any(skb);
spin_lock(&ring->lock);
}
spin_unlock(&ring->lock);
core->device->net_dev->last_rx = jiffies;
}
static int
rt2x00_xmit_packet(struct _rt2x00_core *core, struct _packet_ring *ring, u8 rate, u16 xmit_flags)
{
struct sk_buff *skb = NULL;
struct ieee80211_hdr_3addr *ieee80211hdr = NULL;
u16 duration = 0x0000;
u8 frame_gap = 0x00;
if(!ring->ready)
return -EBUSY;
skb = skb_dequeue(&ring->queue);
if(!skb){
ERROR("failed to dequeue packet.\n");
return -ENODATA;
}
ieee80211hdr = (struct ieee80211_hdr_3addr*)skb->data;
if(ring->type == RING_BEACON)
ieee80211hdr->seq_ctl = cpu_to_le16(rt2x00_get_sequence(core->device));
duration = core->config.sifs + get_preamble(&core->config) + get_duration(ACK_SIZE, IEEE80211_CCK_RATE_2MB);
if(ring->type == RING_TX){
duration *= 2;
duration += core->config.sifs + get_preamble(&core->config) + get_duration(skb->len, rate);
}
ieee80211hdr->duration_id = cpu_to_le16(duration);
if(WLAN_GET_SEQ_FRAG(ieee80211hdr->seq_ctl) <= 1){
frame_gap = XMIT_IFS_BACKOFF;
xmit_flags |= XMIT_NEW_SEQUENCE;
}else
frame_gap = XMIT_IFS_SIFS;
if(WLAN_FC_GET_TYPE(ieee80211hdr->frame_ctl)==IEEE80211_STYPE_RTS){
xmit_flags |= XMIT_RTS;
frame_gap = XMIT_IFS_SIFS;
}
xmit_flags |= frame_gap;
if(is_valid_ether_addr(&ieee80211hdr->addr1[0])
&& WLAN_FC_GET_TYPE(ieee80211hdr->frame_ctl)==IEEE80211_STYPE_ACK)
xmit_flags |= XMIT_ACK;
if(WLAN_FC_GET_TYPE(ieee80211hdr->frame_ctl)==IEEE80211_STYPE_PROBE_RESP)
xmit_flags |= XMIT_TIMESTAMP;
if(ieee80211_is_ofdm_rate(rate))
xmit_flags |= XMIT_OFDM;
if(core->device->handler->dev_xmit_packet(core->device, skb, ring->type, rate, xmit_flags)){
ring->ready = 0;
skb_queue_head(&ring->queue, skb);
return -ENOMEM;
}
if(ring->type != RING_BEACON)
dev_kfree_skb_any(skb);
else{
ring->ready = 0;
skb_queue_head(&ring->queue, skb);
}
return 0;
}
static void
rt2x00_delayed_tx(void *data)
{
struct _packet_ring *ring = data;
struct _rt2x00_device *device = ring->core->device;
struct sk_buff *skb = NULL;
u16 packet_id = 0x0000;
u16 xmit_flags = 0x0000;
u8 rate = 0x00;
if(!test_bit(DEVICE_RADIO_ON, &device->flags))
return;
if(test_bit(DEVICE_SCANNING, &device->flags))
return;
down_read(&device->rt2x00_sem);
rate = ring->core->config.active.bitrate;
if(ring->core->config.active.flags & CONFIG_ENABLED_SHORT_PREAMBLE)
xmit_flags |= XMIT_SHORT_PREAMBLE;
if(ring->core->config.active.flags & CONFIG_ENABLED_LONG_RETRY)
xmit_flags |= XMIT_LONG_RETRY;
up_read(&device->rt2x00_sem);
spin_lock(&ring->lock);
while(!skb_queue_empty(&ring->queue)){
skb = skb_peek(&ring->queue);
if(!skb)
goto exit_kick_tx;
packet_id = ((struct _skb_cb*)&skb->cb)->id;
if(device->handler->dev_test_tx(device, ((struct _skb_cb*)&skb->cb)->packets)){
NOTICE("TX ring full");
goto exit_kick_tx;
}
do{
if(rt2x00_xmit_packet(ring->core, ring, rate, xmit_flags)){
NOTICE("TX ring full");
goto exit_kick_tx;
}
skb = skb_peek(&ring->queue);
if(!skb)
goto exit_kick_tx;
}while(((struct _skb_cb*)&skb->cb)->id == packet_id);
}
exit_kick_tx:
if(device->handler->dev_xmit_packet(device, NULL, ring->type, 0, XMIT_START))
ERROR("failed to kick TX queue.\n");
spin_unlock(&ring->lock);
device->net_dev->trans_start = jiffies;
}
static void
rt2x00_delayed_mgmt(void *data)
{
struct _packet_ring *ring = data;
struct _rt2x00_device *device = ring->core->device;
u16 xmit_flags = 0x0000;
u8 rate = 0x00;
if(!test_bit(DEVICE_RADIO_ON, &device->flags))
return;
if(ring->type != RING_PRIO && test_bit(DEVICE_SCANNING, &device->flags))
return;
down_read(&ring->core->device->rt2x00_sem);
rate = ring->core->config.active.bitrate;
if(ring->core->config.active.flags & CONFIG_ENABLED_SHORT_PREAMBLE)
xmit_flags |= XMIT_SHORT_PREAMBLE;
if(ring->core->config.active.flags & CONFIG_ENABLED_LONG_RETRY)
xmit_flags |= XMIT_LONG_RETRY;
if(ring->type != RING_BEACON)
xmit_flags |= XMIT_START;
up_read(&ring->core->device->rt2x00_sem);
spin_lock(&ring->lock);
while(!skb_queue_empty(&ring->queue)){
if(rt2x00_xmit_packet(ring->core, ring, IEEE80211_CCK_RATE_2MB, xmit_flags)
|| ring->type == RING_BEACON)
break;
}
spin_unlock(&ring->lock);
}
/*
* net_device handlers.
*/
static int
rt2x00_open(struct net_device *net_dev)
{
struct _rt2x00_device *device = rt2x00_device(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
int status = 0x00000000;
if(test_and_set_bit(DEVICE_ENABLED, &device->flags)){
ERROR("device already enabled.\n");
return -EBUSY;
}
if(test_bit(DEVICE_SCANNING, &device->flags)){
queue_delayed_work(core->workqueue, &core->link_tuner, LINK_TIME);
return 0;
}
status = rt2x00_radio_on(device);
if(status){
clear_bit(DEVICE_ENABLED, &device->flags);
return status;
}
rt2x00_activate_config(device);
rt2x00_link_up(device);
return 0;
}
static int
rt2x00_stop(struct net_device *net_dev)
{
struct _rt2x00_device *device = rt2x00_device(net_dev);
if(!test_and_clear_bit(DEVICE_ENABLED, &device->flags)){
ERROR("device already disabled.\n");
return -EBUSY;
}
rt2x00_radio_off(device);
return 0;
}
static int
rt2x00_change_mtu(struct net_device *net_dev, int new_mtu)
{
if((new_mtu < 256) || (new_mtu > DATA_FRAME_SIZE) || (new_mtu > MAX_FRAG_THRESHOLD))
return -EINVAL;
net_dev->mtu = new_mtu;
return 0;
}
static void
rt2x00_tx_timeout(struct net_device *net_dev)
{
struct _rt2x00_device *device = ieee80211_priv(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
WARNING("Tx timed out. Start kicking all packet rings.\n");
device->handler->dev_xmit_packet(device, NULL, RING_TX, 0, XMIT_START);
device->handler->dev_xmit_packet(device, NULL, RING_PRIO, 0, XMIT_START);
device->handler->dev_xmit_packet(device, NULL, RING_ATIM, 0, XMIT_START);
rt2x00_kick_ring(&core->tx);
rt2x00_kick_ring(&core->prio);
rt2x00_kick_ring(&core->atim);
}
static struct net_device_stats*
rt2x00_get_stats(struct net_device *net_dev)
{
return &((struct ieee80211_device*)netdev_priv(net_dev))->stats;
}
/*
* Initialization handlers.
*/
static void
rt2x00_init_config(struct _rt2x00_core *core)
{
memset(&core->config.nickn, '\0', sizeof(core->config.nickn));
memset(&core->config.user.essid, '\0', sizeof(core->config.user.essid));
memset(&core->config.user.bssid, 0x00, sizeof(core->config.user.bssid));
core->config.user.network = 0;
core->config.user.iw_mode = IW_MODE_AUTO;
core->config.user.geography = IOCTL_GEOGRAPHY_INVAL;
core->config.user.channel = 0;
core->config.user.bitrate = 0;
core->config.user.flags |= CONFIG_ACCEPT_UNICAST | CONFIG_ACCEPT_MULTICAST;
core->config.user.short_retry = 4;
core->config.user.long_retry = 7;
core->config.user.rts_threshold = MAX_FRAG_THRESHOLD;
core->config.user.fragmentation = MAX_FRAG_THRESHOLD;
core->config.user.txpower = 100;
core->config.user.antenna_flags |= ANTENNA_TX_DIV | ANTENNA_RX_DIV;
core->config.user.wpa_version = IW_AUTH_WPA_VERSION_DISABLED;
core->config.user.adhoc_ofdm = 0;
core->config.beacon = 100;
core->config.plcp = 48;
core->config.sifs = 10;
core->config.sequence = 0;
core->config.user.sensitivity = 10;
core->config.user_commit = COMMIT_ALL_CONFIG;
}
/*
* Module handlers.
*/
struct net_device*
rt2x00_core_probe(struct _rt2x00_dev_handler *handler, void *priv, u32 sizeof_dev, struct device *dev)
{
struct net_device *net_dev = NULL;
struct _rt2x00_device *device = NULL;
struct _rt2x00_core *core = NULL;
if(unlikely(handler->dev_radio_on == NULL)
|| unlikely(handler->dev_radio_off == NULL)
|| unlikely(handler->dev_update_config == NULL)
|| unlikely(handler->dev_update_stats == NULL)
|| unlikely(handler->dev_test_tx == NULL)
|| unlikely(handler->dev_xmit_packet == NULL)){
ERROR("not enough handlers provided by device.\n");
goto exit;
}
net_dev = alloc_ieee80211(sizeof_dev + sizeof(*device) + sizeof(*core));
if(!net_dev){
ERROR("not enough memory to allocate net_device.\n");
goto exit;
}
device = ieee80211_priv(net_dev);
memset(device, 0x00, sizeof(*device));
core = (void*)device + sizeof(*device);
core->ieee80211 = netdev_priv(net_dev);
core->device = device;
core->network = NULL;
core->scan_req = NULL;
core->scan_flags = 0;
core->connection_attempt = 0;
core->connection_status = CONNECTION_INITIALIZED;
memset(&core->rt2x00_stats, 0x00, sizeof(core->rt2x00_stats));
device->priv = (void*)core + sizeof(*core);
device->owner = core;
device->handler = handler;
device->net_dev = net_dev;
init_rwsem(&device->rt2x00_sem);
init_MUTEX(&core->scan_sem);
core->workqueue = create_singlethread_workqueue("rt2x00");
if (!core->workqueue)
goto exit_free_ieee80211;
INIT_WORK(&core->scan_tuner, rt2x00_scan_periodic, device);
INIT_WORK(&core->link_tuner, rt2x00_link_periodic, device);
rt2x00_init_ring(device, &core->rx, RING_RX, rt2x00_delayed_rx);
rt2x00_init_ring(device, &core->tx, RING_TX, rt2x00_delayed_tx);
rt2x00_init_ring(device, &core->atim, RING_ATIM, rt2x00_delayed_mgmt);
rt2x00_init_ring(device, &core->prio, RING_PRIO, rt2x00_delayed_mgmt);
rt2x00_init_ring(device, &core->beacon, RING_BEACON, rt2x00_delayed_mgmt);
rt2x00_init_config(core);
if(device->handler->dev_probe
&& device->handler->dev_probe(device, &core->config, priv)){
ERROR("device probe failed.\n");
goto exit_free_workqueue;
}
INFO("Device " MAC_FMT " detected.\n", MAC_ARG(device->net_dev->dev_addr));
core->ieee80211->set_security = rt2x00_set_security;
core->ieee80211->hard_start_xmit = rt2x00_start_xmit;
core->ieee80211->handle_auth = rt2x00_handle_auth;
core->ieee80211->handle_deauth = rt2x00_handle_deauth;
core->ieee80211->handle_disassoc = rt2x00_handle_disassoc;
core->ieee80211->handle_probe_request = rt2x00_handle_probe_request;
core->ieee80211->handle_assoc_response = rt2x00_handle_assoc_resp;
core->ieee80211->handle_beacon = rt2x00_handle_beacon;
core->ieee80211->handle_probe_response = rt2x00_handle_probe_resp;
core->ieee80211->config |= CFG_IEEE80211_RTS;
core->ieee80211->state = IEEE80211_INITIALIZED;
core->ieee80211->wpa_ie = NULL;
core->ieee80211->sec.enabled = 0;
core->ieee80211->sec.encrypt = 0;
core->ieee80211->sec.auth_mode = WLAN_AUTH_OPEN;
net_dev->open = rt2x00_open;
net_dev->stop = rt2x00_stop;
net_dev->change_mtu = rt2x00_change_mtu;
net_dev->tx_timeout = rt2x00_tx_timeout;
net_dev->watchdog_timeo = 2 * HZ;
net_dev->wireless_handlers = &rt2x00_ioctl_handler_def;
net_dev->wireless_data = &core->wireless_data;
net_dev->get_stats = rt2x00_get_stats;
SET_NETDEV_DEV(net_dev,dev);
if(register_netdev(net_dev)){
ERROR("net_device registration failed.\n");
goto exit_dev_remove;
}
set_bit(DEVICE_AWAKE, &device->flags);
/*
* Activate current configuration.
*/
rt2x00_activate_config(device);
return net_dev;
exit_dev_remove:
if(device->handler->dev_remove)
device->handler->dev_remove(device);
exit_free_workqueue:
destroy_workqueue(core->workqueue);
exit_free_ieee80211:
free_ieee80211(net_dev);
exit:
return NULL;
}
EXPORT_SYMBOL_GPL(rt2x00_core_probe);
void
rt2x00_core_remove(struct net_device *net_dev)
{
struct _rt2x00_device *device = rt2x00_device(net_dev);
struct _rt2x00_core *core = rt2x00_core(device);
unregister_netdev(net_dev);
destroy_workqueue(core->workqueue);
down_write(&device->rt2x00_sem);
if(device->handler->dev_remove)
device->handler->dev_remove(device);
if(core->ieee80211->wpa_ie)
kfree(core->ieee80211->wpa_ie);
core->ieee80211->wpa_ie = NULL;
free_ieee80211(net_dev);
up_write(&device->rt2x00_sem);
}
EXPORT_SYMBOL_GPL(rt2x00_core_remove);
int
rt2x00_suspend(struct _rt2x00_device *device)
{
if(test_bit(DEVICE_ENABLED, &device->flags)){
set_bit(DEVICE_RESTORE_LINK, &device->flags);
if(dev_close(device->net_dev))
return -EBUSY;
}
if(test_bit(DEVICE_RADIO_ON, &device->flags)){
rt2x00_radio_off(device);
}
return 0;
}
EXPORT_SYMBOL_GPL(rt2x00_suspend);
int
rt2x00_resume(struct _rt2x00_device *device)
{
if(test_bit(DEVICE_RESTORE_LINK, &device->flags))
return dev_open(device->net_dev);
return 0;
}
EXPORT_SYMBOL_GPL(rt2x00_resume);
/*
* RT2x00 core module information.
*/
static char version[] = DRV_NAME " - " DRV_VERSION " (" DRV_RELDATE ") by " DRV_PROJECT;
int rt2x00_debug_level = 0x00000000;
EXPORT_SYMBOL_GPL(rt2x00_debug_level);
MODULE_AUTHOR(DRV_PROJECT);
MODULE_VERSION(DRV_VERSION);
MODULE_DESCRIPTION("Ralink RT2400/RT2500 PCI, PCMCIA & USB Wireless LAN driver.");
MODULE_SUPPORTED_DEVICE("Ralink RT2460, RT2560 & RT2570 PCI, PCMCIA & USB chipset based cards");
MODULE_LICENSE("GPL");
static int __init rt2x00_core_init(void)
{
printk(KERN_INFO "Loading module: %s\n", version);
return 0;
}
static void __exit rt2x00_core_exit(void)
{
printk(KERN_INFO "Unloading module: %s\n", version);
}
module_init(rt2x00_core_init);
module_exit(rt2x00_core_exit);
Artigos relacionados
HW2.cpp
HW2.cpp — cpp source code from the linux drivers learning materials (linux_drivers/HW2.cpp).
Ler artigo →cycle_link.cpp
cycle_link.cpp — cpp source code from the linux drivers learning materials (linux_drivers/cycle_link.cpp).
Ler artigo →ieee80211_crypt.c
ieee80211_crypt.c — c source code from the linux drivers learning materials (linux_drivers/rt2x00-2.0.0-b3/ieee80211/ieee80211_crypt.c).
Ler artigo →ieee80211_crypt_ccmp.c
ieee80211_crypt_ccmp.c — c source code from the linux drivers learning materials (linux_drivers/rt2x00-2.0.0-b3/ieee80211/ieee80211_crypt_ccmp.c).
Ler artigo →ieee80211_crypt_tkip.c
ieee80211_crypt_tkip.c — c source code from the linux drivers learning materials (linux_drivers/rt2x00-2.0.0-b3/ieee80211/ieee80211_crypt_tkip.c).
Ler artigo →ieee80211_crypt_wep.c
ieee80211_crypt_wep.c — c source code from the linux drivers learning materials (linux_drivers/rt2x00-2.0.0-b3/ieee80211/ieee80211_crypt_wep.c).
Ler artigo →