From dc0b369ea1bb98a9835cfac7ff3899797e65227e Mon Sep 17 00:00:00 2001 From: nbd Date: Wed, 28 Dec 2005 13:22:51 +0000 Subject: add devicescape 802.11 stack git-svn-id: svn://svn.openwrt.org/openwrt/trunk@2791 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../package/ieee80211-dscape/src/ieee80211_scan.c | 352 +++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 openwrt/target/linux/package/ieee80211-dscape/src/ieee80211_scan.c (limited to 'openwrt/target/linux/package/ieee80211-dscape/src/ieee80211_scan.c') diff --git a/openwrt/target/linux/package/ieee80211-dscape/src/ieee80211_scan.c b/openwrt/target/linux/package/ieee80211-dscape/src/ieee80211_scan.c new file mode 100644 index 000000000..7d4effe82 --- /dev/null +++ b/openwrt/target/linux/package/ieee80211-dscape/src/ieee80211_scan.c @@ -0,0 +1,352 @@ +/* + * Copyright 2002-2004, Instant802 Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ieee80211_i.h" +#include "rate_control.h" + + +/* Maximum number of seconds to wait for the traffic load to get below + * threshold before forcing a passive scan. */ +#define MAX_SCAN_WAIT 60 +/* Threshold (pkts/sec TX or RX) for delaying passive scan */ +#define SCAN_TXRX_THRESHOLD 75 + +static void get_channel_params(struct ieee80211_local *local, int channel, + struct ieee80211_hw_modes **mode, + struct ieee80211_channel **chan) +{ + int m; + + for (m = 0; m < local->hw->num_modes; m++) { + *mode = &local->hw->modes[m]; + if ((*mode)->mode == local->conf.phymode) + break; + } + local->scan.mode_idx = m; + local->scan.chan_idx = 0; + do { + *chan = &(*mode)->channels[local->scan.chan_idx]; + if ((*chan)->chan == channel) { + return; + } + local->scan.chan_idx++; + } while (local->scan.chan_idx < (*mode)->num_channels); + *chan = NULL; +} + + +static void next_chan_same_mode(struct ieee80211_local *local, + struct ieee80211_hw_modes **mode, + struct ieee80211_channel **chan) +{ + int m, prev; + + for (m = 0; m < local->hw->num_modes; m++) { + *mode = &local->hw->modes[m]; + if ((*mode)->mode == local->conf.phymode) + break; + } + local->scan.mode_idx = m; + + /* Select next channel - scan only channels marked with W_SCAN flag */ + prev = local->scan.chan_idx; + do { + local->scan.chan_idx++; + if (local->scan.chan_idx >= (*mode)->num_channels) + local->scan.chan_idx = 0; + *chan = &(*mode)->channels[local->scan.chan_idx]; + if ((*chan)->flag & IEEE80211_CHAN_W_SCAN) + break; + } while (local->scan.chan_idx != prev); +} + + +static void next_chan_all_modes(struct ieee80211_local *local, + struct ieee80211_hw_modes **mode, + struct ieee80211_channel **chan) +{ + int prev, prev_m; + + if (local->scan.mode_idx >= local->hw->num_modes) { + local->scan.mode_idx = 0; + local->scan.chan_idx = 0; + } + + /* Select next channel - scan only channels marked with W_SCAN flag */ + prev = local->scan.chan_idx; + prev_m = local->scan.mode_idx; + do { + *mode = &local->hw->modes[local->scan.mode_idx]; + local->scan.chan_idx++; + if (local->scan.chan_idx >= (*mode)->num_channels) { + local->scan.chan_idx = 0; + local->scan.mode_idx++; + if (local->scan.mode_idx >= local->hw->num_modes) + local->scan.mode_idx = 0; + *mode = &local->hw->modes[local->scan.mode_idx]; + } + *chan = &(*mode)->channels[local->scan.chan_idx]; + if ((*chan)->flag & IEEE80211_CHAN_W_SCAN) + break; + } while (local->scan.chan_idx != prev || + local->scan.mode_idx != prev_m); +} + + +static void ieee80211_scan_start(struct net_device *dev, + struct ieee80211_scan_conf *conf) +{ + struct ieee80211_local *local = dev->priv; + int old_mode_idx = local->scan.mode_idx; + int old_chan_idx = local->scan.chan_idx; + struct ieee80211_hw_modes *mode = NULL; + struct ieee80211_channel *chan = NULL; + int ret; + + if (local->hw->passive_scan == 0) { + printk(KERN_DEBUG "%s: Scan handler called, yet the hardware " + "does not support passive scanning. Disabled.\n", + dev->name); + return; + } + + if ((local->scan.tries < MAX_SCAN_WAIT && + local->scan.txrx_count > SCAN_TXRX_THRESHOLD)) { + local->scan.tries++; + /* Count TX/RX packets during one second interval and allow + * scan to start only if the number of packets is below the + * threshold. */ + local->scan.txrx_count = 0; + local->scan.timer.expires = jiffies + HZ; + add_timer(&local->scan.timer); + return; + } + + if (local->scan.skb == NULL) { + printk(KERN_DEBUG "%s: Scan start called even though scan.skb " + "is not set\n", dev->name); + } + + if (local->scan.our_mode_only) { + if (local->scan.channel > 0) { + get_channel_params(local, local->scan.channel, &mode, + &chan); + } else + next_chan_same_mode(local, &mode, &chan); + } + else + next_chan_all_modes(local, &mode, &chan); + + conf->scan_channel = chan->chan; + conf->scan_freq = chan->freq; + conf->scan_channel_val = chan->val; + conf->scan_phymode = mode->mode; + conf->scan_power_level = chan->power_level; + conf->scan_antenna_max = chan->antenna_max; + conf->scan_time = 2 * local->hw->channel_change_time + + local->scan.time; /* 10ms scan time+hardware changes */ + conf->skb = local->scan.skb ? + skb_clone(local->scan.skb, GFP_ATOMIC) : NULL; + conf->tx_control = &local->scan.tx_control; +#if 0 + printk(KERN_DEBUG "%s: Doing scan on mode: %d freq: %d chan: %d " + "for %d ms\n", + dev->name, conf->scan_phymode, conf->scan_freq, + conf->scan_channel, conf->scan_time); +#endif + local->scan.rx_packets = 0; + local->scan.rx_beacon = 0; + local->scan.freq = chan->freq; + local->scan.in_scan = 1; + + ieee80211_netif_oper(dev, NETIF_STOP); + + ret = local->hw->passive_scan(dev, IEEE80211_SCAN_START, conf); + + if (ret == 0) { + long usec = local->hw->channel_change_time + + local->scan.time; + usec += 1000000L / HZ - 1; + usec /= 1000000L / HZ; + local->scan.timer.expires = jiffies + usec; + } else { + local->scan.in_scan = 0; + if (conf->skb) + dev_kfree_skb(conf->skb); + ieee80211_netif_oper(dev, NETIF_WAKE); + if (ret == -EAGAIN) { + local->scan.timer.expires = jiffies + + (local->scan.interval * HZ / 100); + local->scan.mode_idx = old_mode_idx; + local->scan.chan_idx = old_chan_idx; + } else { + printk(KERN_DEBUG "%s: Got unknown error from " + "passive_scan %d\n", dev->name, ret); + local->scan.timer.expires = jiffies + + (local->scan.interval * HZ); + } + local->scan.in_scan = 0; + } + + add_timer(&local->scan.timer); +} + + +static void ieee80211_scan_stop(struct net_device *dev, + struct ieee80211_scan_conf *conf) +{ + struct ieee80211_local *local = dev->priv; + struct ieee80211_hw_modes *mode; + struct ieee80211_channel *chan; + int wait; + + if (local->hw->passive_scan == NULL) + return; + + if (local->scan.mode_idx >= local->hw->num_modes) { + local->scan.mode_idx = 0; + local->scan.chan_idx = 0; + } + + mode = &local->hw->modes[local->scan.mode_idx]; + + if (local->scan.chan_idx >= mode->num_channels) { + local->scan.chan_idx = 0; + } + + chan = &mode->channels[local->scan.chan_idx]; + + local->hw->passive_scan(dev, IEEE80211_SCAN_END, conf); + +#ifdef CONFIG_IEEE80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: Did scan on mode: %d freq: %d chan: %d " + "GOT: %d Beacon: %d (%d)\n", + dev->name, + mode->mode, chan->freq, chan->chan, + local->scan.rx_packets, local->scan.rx_beacon, + local->scan.tries); +#endif /* CONFIG_IEEE80211_VERBOSE_DEBUG */ + local->scan.num_scans++; + + local->scan.in_scan = 0; + ieee80211_netif_oper(dev, NETIF_WAKE); + + local->scan.tries = 0; + /* Use random interval of scan.interval .. 2 * scan.interval */ + wait = (local->scan.interval * HZ * ((net_random() & 127) + 128)) / + 128; + local->scan.timer.expires = jiffies + wait; + + add_timer(&local->scan.timer); +} + + +static void ieee80211_scan_handler(unsigned long uldev) +{ + struct net_device *dev = (struct net_device *) uldev; + struct ieee80211_local *local = dev->priv; + struct ieee80211_scan_conf conf; + + if (local->scan.interval == 0 && !local->scan.in_scan) { + /* Passive scanning is disabled - keep the timer always + * running to make code cleaner. */ + local->scan.timer.expires = jiffies + 10 * HZ; + add_timer(&local->scan.timer); + return; + } + + memset(&conf, 0, sizeof(struct ieee80211_scan_conf)); + conf.running_freq = local->conf.freq; + conf.running_channel = local->conf.channel; + conf.running_phymode = local->conf.phymode; + conf.running_channel_val = local->conf.channel_val; + conf.running_power_level = local->conf.power_level; + conf.running_antenna_max = local->conf.antenna_max; + + if (local->scan.in_scan == 0) + ieee80211_scan_start(dev, &conf); + else + ieee80211_scan_stop(dev, &conf); +} + + +void ieee80211_init_scan(struct net_device *dev) +{ + struct ieee80211_local *local = dev->priv; + struct ieee80211_hdr hdr; + u16 fc; + int len = 10; + struct rate_control_extra extra; + + /* Only initialize passive scanning if the hardware supports it */ + if (!local->hw->passive_scan) { + local->scan.skb = NULL; + memset(&local->scan.tx_control, 0, + sizeof(local->scan.tx_control)); + printk(KERN_DEBUG "%s: Does not support passive scan, " + "disabled\n", dev->name); + return; + } + + local->scan.interval = 0; + local->scan.our_mode_only = 1; + local->scan.time = 10000; + local->scan.timer.function = ieee80211_scan_handler; + local->scan.timer.data = (unsigned long) dev; + local->scan.timer.expires = jiffies + local->scan.interval * HZ; + add_timer(&local->scan.timer); + + /* Create a CTS from for broadcasting before + * the low level changes channels */ + local->scan.skb = alloc_skb(len, GFP_KERNEL); + if (local->scan.skb == NULL) { + printk(KERN_WARNING "%s: Failed to allocate CTS packet for " + "passive scan\n", dev->name); + return; + } + + fc = (WLAN_FC_TYPE_CTRL << 2) | (WLAN_FC_STYPE_CTS << 4); + hdr.frame_control = cpu_to_le16(fc); + hdr.duration_id = + cpu_to_le16(2 * local->hw->channel_change_time + + local->scan.time); + memcpy(hdr.addr1, dev->dev_addr, ETH_ALEN); /* DA */ + hdr.seq_ctrl = 0; + + memcpy(skb_put(local->scan.skb, len), &hdr, len); + + memset(&local->scan.tx_control, 0, sizeof(local->scan.tx_control)); + local->scan.tx_control.key_idx = HW_KEY_IDX_INVALID; + local->scan.tx_control.do_not_encrypt = 1; + memset(&extra, 0, sizeof(extra)); + extra.endidx = local->num_curr_rates; + local->scan.tx_control.tx_rate = + rate_control_get_rate(dev, local->scan.skb, &extra)->val; + local->scan.tx_control.no_ack = 1; +} + + +void ieee80211_stop_scan(struct net_device *dev) +{ + struct ieee80211_local *local = dev->priv; + + if (local->hw->passive_scan != 0) { + del_timer_sync(&local->scan.timer); + dev_kfree_skb(local->scan.skb); + local->scan.skb = NULL; + } +} -- cgit v1.2.3