linux/drivers/net/wireless/rt2x00/rt2x00config.c
<<
>>
Prefs
   1/*
   2        Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
   3        <http://rt2x00.serialmonkey.com>
   4
   5        This program is free software; you can redistribute it and/or modify
   6        it under the terms of the GNU General Public License as published by
   7        the Free Software Foundation; either version 2 of the License, or
   8        (at your option) any later version.
   9
  10        This program is distributed in the hope that it will be useful,
  11        but WITHOUT ANY WARRANTY; without even the implied warranty of
  12        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13        GNU General Public License for more details.
  14
  15        You should have received a copy of the GNU General Public License
  16        along with this program; if not, write to the
  17        Free Software Foundation, Inc.,
  18        59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19 */
  20
  21/*
  22        Module: rt2x00lib
  23        Abstract: rt2x00 generic configuration routines.
  24 */
  25
  26#include <linux/kernel.h>
  27#include <linux/module.h>
  28
  29#include "rt2x00.h"
  30#include "rt2x00lib.h"
  31
  32void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev,
  33                           struct rt2x00_intf *intf,
  34                           enum nl80211_iftype type,
  35                           const u8 *mac, const u8 *bssid)
  36{
  37        struct rt2x00intf_conf conf;
  38        unsigned int flags = 0;
  39
  40        conf.type = type;
  41
  42        switch (type) {
  43        case NL80211_IFTYPE_ADHOC:
  44                conf.sync = TSF_SYNC_ADHOC;
  45                break;
  46        case NL80211_IFTYPE_AP:
  47        case NL80211_IFTYPE_MESH_POINT:
  48        case NL80211_IFTYPE_WDS:
  49                conf.sync = TSF_SYNC_AP_NONE;
  50                break;
  51        case NL80211_IFTYPE_STATION:
  52                conf.sync = TSF_SYNC_INFRA;
  53                break;
  54        default:
  55                conf.sync = TSF_SYNC_NONE;
  56                break;
  57        }
  58
  59        /*
  60         * Note that when NULL is passed as address we will send
  61         * 00:00:00:00:00 to the device to clear the address.
  62         * This will prevent the device being confused when it wants
  63         * to ACK frames or considers itself associated.
  64         */
  65        memset(conf.mac, 0, sizeof(conf.mac));
  66        if (mac)
  67                memcpy(conf.mac, mac, ETH_ALEN);
  68
  69        memset(conf.bssid, 0, sizeof(conf.bssid));
  70        if (bssid)
  71                memcpy(conf.bssid, bssid, ETH_ALEN);
  72
  73        flags |= CONFIG_UPDATE_TYPE;
  74        if (mac || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count))
  75                flags |= CONFIG_UPDATE_MAC;
  76        if (bssid || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count))
  77                flags |= CONFIG_UPDATE_BSSID;
  78
  79        rt2x00dev->ops->lib->config_intf(rt2x00dev, intf, &conf, flags);
  80}
  81
  82void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
  83                          struct rt2x00_intf *intf,
  84                          struct ieee80211_bss_conf *bss_conf,
  85                          u32 changed)
  86{
  87        struct rt2x00lib_erp erp;
  88
  89        memset(&erp, 0, sizeof(erp));
  90
  91        erp.short_preamble = bss_conf->use_short_preamble;
  92        erp.cts_protection = bss_conf->use_cts_prot;
  93
  94        erp.slot_time = bss_conf->use_short_slot ? SHORT_SLOT_TIME : SLOT_TIME;
  95        erp.sifs = SIFS;
  96        erp.pifs = bss_conf->use_short_slot ? SHORT_PIFS : PIFS;
  97        erp.difs = bss_conf->use_short_slot ? SHORT_DIFS : DIFS;
  98        erp.eifs = bss_conf->use_short_slot ? SHORT_EIFS : EIFS;
  99
 100        erp.basic_rates = bss_conf->basic_rates;
 101        erp.beacon_int = bss_conf->beacon_int;
 102
 103        /* Update the AID, this is needed for dynamic PS support */
 104        rt2x00dev->aid = bss_conf->assoc ? bss_conf->aid : 0;
 105        rt2x00dev->last_beacon = bss_conf->sync_tsf;
 106
 107        /* Update global beacon interval time, this is needed for PS support */
 108        rt2x00dev->beacon_int = bss_conf->beacon_int;
 109
 110        if (changed & BSS_CHANGED_HT)
 111                erp.ht_opmode = bss_conf->ht_operation_mode;
 112
 113        rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed);
 114}
 115
 116void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
 117                              struct antenna_setup config)
 118{
 119        struct link_ant *ant = &rt2x00dev->link.ant;
 120        struct antenna_setup *def = &rt2x00dev->default_ant;
 121        struct antenna_setup *active = &rt2x00dev->link.ant.active;
 122
 123        /*
 124         * When the caller tries to send the SW diversity,
 125         * we must update the ANTENNA_RX_DIVERSITY flag to
 126         * enable the antenna diversity in the link tuner.
 127         *
 128         * Secondly, we must guarentee we never send the
 129         * software antenna diversity command to the driver.
 130         */
 131        if (!(ant->flags & ANTENNA_RX_DIVERSITY)) {
 132                if (config.rx == ANTENNA_SW_DIVERSITY) {
 133                        ant->flags |= ANTENNA_RX_DIVERSITY;
 134
 135                        if (def->rx == ANTENNA_SW_DIVERSITY)
 136                                config.rx = ANTENNA_B;
 137                        else
 138                                config.rx = def->rx;
 139                }
 140        } else if (config.rx == ANTENNA_SW_DIVERSITY)
 141                config.rx = active->rx;
 142
 143        if (!(ant->flags & ANTENNA_TX_DIVERSITY)) {
 144                if (config.tx == ANTENNA_SW_DIVERSITY) {
 145                        ant->flags |= ANTENNA_TX_DIVERSITY;
 146
 147                        if (def->tx == ANTENNA_SW_DIVERSITY)
 148                                config.tx = ANTENNA_B;
 149                        else
 150                                config.tx = def->tx;
 151                }
 152        } else if (config.tx == ANTENNA_SW_DIVERSITY)
 153                config.tx = active->tx;
 154
 155        /*
 156         * Antenna setup changes require the RX to be disabled,
 157         * else the changes will be ignored by the device.
 158         */
 159        if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
 160                rt2x00queue_stop_queue(rt2x00dev->rx);
 161
 162        /*
 163         * Write new antenna setup to device and reset the link tuner.
 164         * The latter is required since we need to recalibrate the
 165         * noise-sensitivity ratio for the new setup.
 166         */
 167        rt2x00dev->ops->lib->config_ant(rt2x00dev, &config);
 168
 169        rt2x00link_reset_tuner(rt2x00dev, true);
 170
 171        memcpy(active, &config, sizeof(config));
 172
 173        if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
 174                rt2x00queue_start_queue(rt2x00dev->rx);
 175}
 176
 177static u16 rt2x00ht_center_channel(struct rt2x00_dev *rt2x00dev,
 178                                   struct ieee80211_conf *conf)
 179{
 180        struct hw_mode_spec *spec = &rt2x00dev->spec;
 181        int center_channel;
 182        u16 i;
 183
 184        /*
 185         * Initialize center channel to current channel.
 186         */
 187        center_channel = spec->channels[conf->chandef.chan->hw_value].channel;
 188
 189        /*
 190         * Adjust center channel to HT40+ and HT40- operation.
 191         */
 192        if (conf_is_ht40_plus(conf))
 193                center_channel += 2;
 194        else if (conf_is_ht40_minus(conf))
 195                center_channel -= (center_channel == 14) ? 1 : 2;
 196
 197        for (i = 0; i < spec->num_channels; i++)
 198                if (spec->channels[i].channel == center_channel)
 199                        return i;
 200
 201        WARN_ON(1);
 202        return conf->chandef.chan->hw_value;
 203}
 204
 205void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
 206                      struct ieee80211_conf *conf,
 207                      unsigned int ieee80211_flags)
 208{
 209        struct rt2x00lib_conf libconf;
 210        u16 hw_value;
 211        u16 autowake_timeout;
 212        u16 beacon_int;
 213        u16 beacon_diff;
 214
 215        memset(&libconf, 0, sizeof(libconf));
 216
 217        libconf.conf = conf;
 218
 219        if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) {
 220                if (!conf_is_ht(conf))
 221                        set_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags);
 222                else
 223                        clear_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags);
 224
 225                if (conf_is_ht40(conf)) {
 226                        set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);
 227                        hw_value = rt2x00ht_center_channel(rt2x00dev, conf);
 228                } else {
 229                        clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);
 230                        hw_value = conf->chandef.chan->hw_value;
 231                }
 232
 233                memcpy(&libconf.rf,
 234                       &rt2x00dev->spec.channels[hw_value],
 235                       sizeof(libconf.rf));
 236
 237                memcpy(&libconf.channel,
 238                       &rt2x00dev->spec.channels_info[hw_value],
 239                       sizeof(libconf.channel));
 240
 241                /* Used for VCO periodic calibration */
 242                rt2x00dev->rf_channel = libconf.rf.channel;
 243        }
 244
 245        if (test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) &&
 246            (ieee80211_flags & IEEE80211_CONF_CHANGE_PS))
 247                cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
 248
 249        /*
 250         * Start configuration.
 251         */
 252        rt2x00dev->ops->lib->config(rt2x00dev, &libconf, ieee80211_flags);
 253
 254        /*
 255         * Some configuration changes affect the link quality
 256         * which means we need to reset the link tuner.
 257         */
 258        if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL)
 259                rt2x00link_reset_tuner(rt2x00dev, false);
 260
 261        if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
 262            test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) &&
 263            (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) &&
 264            (conf->flags & IEEE80211_CONF_PS)) {
 265                beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon;
 266                beacon_int = msecs_to_jiffies(rt2x00dev->beacon_int);
 267
 268                if (beacon_diff > beacon_int)
 269                        beacon_diff = 0;
 270
 271                autowake_timeout = (conf->max_sleep_period * beacon_int) - beacon_diff;
 272                queue_delayed_work(rt2x00dev->workqueue,
 273                                   &rt2x00dev->autowakeup_work,
 274                                   autowake_timeout - 15);
 275        }
 276
 277        if (conf->flags & IEEE80211_CONF_PS)
 278                set_bit(CONFIG_POWERSAVING, &rt2x00dev->flags);
 279        else
 280                clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags);
 281
 282        rt2x00dev->curr_band = conf->chandef.chan->band;
 283        rt2x00dev->curr_freq = conf->chandef.chan->center_freq;
 284        rt2x00dev->tx_power = conf->power_level;
 285        rt2x00dev->short_retry = conf->short_frame_max_tx_count;
 286        rt2x00dev->long_retry = conf->long_frame_max_tx_count;
 287}
 288