mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-27 13:45:13 +07:00
c5691235cf
While in monitor mode the zd1211rw received only a limited set of packets. This patch forwards now all packets the device receives. Notify that while monitoring no FCS checks are done; so strange packets might appear in the network sniffer of your choice. ATTENTION: Support for multiple interfaces on a single ZD1211 device is currently broken. So this code works only on the first interface. Here is an example to put the device in monitor mode. iwconfig wlan0 mode monitor ifconfig wlan0 up iwconfig wlan0 channel 10 [dsd@gentoo.org: backport to mainline] Signed-off-by: Ulrich Kunitz <kune@deine-taler.de> Signed-off-by: Daniel Drake <dsd@gentoo.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
1384 lines
36 KiB
C
1384 lines
36 KiB
C
/* zd_mac.c
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/wireless.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/jiffies.h>
|
|
#include <net/ieee80211_radiotap.h>
|
|
|
|
#include "zd_def.h"
|
|
#include "zd_chip.h"
|
|
#include "zd_mac.h"
|
|
#include "zd_ieee80211.h"
|
|
#include "zd_netdev.h"
|
|
#include "zd_rf.h"
|
|
#include "zd_util.h"
|
|
|
|
static void ieee_init(struct ieee80211_device *ieee);
|
|
static void softmac_init(struct ieee80211softmac_device *sm);
|
|
static void set_rts_cts_work(struct work_struct *work);
|
|
static void set_basic_rates_work(struct work_struct *work);
|
|
|
|
static void housekeeping_init(struct zd_mac *mac);
|
|
static void housekeeping_enable(struct zd_mac *mac);
|
|
static void housekeeping_disable(struct zd_mac *mac);
|
|
|
|
static void set_multicast_hash_handler(struct work_struct *work);
|
|
|
|
static void do_rx(unsigned long mac_ptr);
|
|
|
|
int zd_mac_init(struct zd_mac *mac,
|
|
struct net_device *netdev,
|
|
struct usb_interface *intf)
|
|
{
|
|
struct ieee80211_device *ieee = zd_netdev_ieee80211(netdev);
|
|
|
|
memset(mac, 0, sizeof(*mac));
|
|
spin_lock_init(&mac->lock);
|
|
mac->netdev = netdev;
|
|
INIT_DELAYED_WORK(&mac->set_rts_cts_work, set_rts_cts_work);
|
|
INIT_DELAYED_WORK(&mac->set_basic_rates_work, set_basic_rates_work);
|
|
|
|
skb_queue_head_init(&mac->rx_queue);
|
|
tasklet_init(&mac->rx_tasklet, do_rx, (unsigned long)mac);
|
|
tasklet_disable(&mac->rx_tasklet);
|
|
|
|
ieee_init(ieee);
|
|
softmac_init(ieee80211_priv(netdev));
|
|
zd_chip_init(&mac->chip, netdev, intf);
|
|
housekeeping_init(mac);
|
|
INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler);
|
|
return 0;
|
|
}
|
|
|
|
static int reset_channel(struct zd_mac *mac)
|
|
{
|
|
int r;
|
|
unsigned long flags;
|
|
const struct channel_range *range;
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
range = zd_channel_range(mac->regdomain);
|
|
if (!range->start) {
|
|
r = -EINVAL;
|
|
goto out;
|
|
}
|
|
mac->requested_channel = range->start;
|
|
r = 0;
|
|
out:
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
return r;
|
|
}
|
|
|
|
int zd_mac_preinit_hw(struct zd_mac *mac)
|
|
{
|
|
int r;
|
|
u8 addr[ETH_ALEN];
|
|
|
|
r = zd_chip_read_mac_addr_fw(&mac->chip, addr);
|
|
if (r)
|
|
return r;
|
|
|
|
memcpy(mac->netdev->dev_addr, addr, ETH_ALEN);
|
|
return 0;
|
|
}
|
|
|
|
int zd_mac_init_hw(struct zd_mac *mac)
|
|
{
|
|
int r;
|
|
struct zd_chip *chip = &mac->chip;
|
|
u8 default_regdomain;
|
|
|
|
r = zd_chip_enable_int(chip);
|
|
if (r)
|
|
goto out;
|
|
r = zd_chip_init_hw(chip);
|
|
if (r)
|
|
goto disable_int;
|
|
|
|
ZD_ASSERT(!irqs_disabled());
|
|
|
|
r = zd_read_regdomain(chip, &default_regdomain);
|
|
if (r)
|
|
goto disable_int;
|
|
if (!zd_regdomain_supported(default_regdomain)) {
|
|
/* The vendor driver overrides the regulatory domain and
|
|
* allowed channel registers and unconditionally restricts
|
|
* available channels to 1-11 everywhere. Match their
|
|
* questionable behaviour only for regdomains which we don't
|
|
* recognise. */
|
|
dev_warn(zd_mac_dev(mac), "Unrecognised regulatory domain: "
|
|
"%#04x. Defaulting to FCC.\n", default_regdomain);
|
|
default_regdomain = ZD_REGDOMAIN_FCC;
|
|
}
|
|
spin_lock_irq(&mac->lock);
|
|
mac->regdomain = mac->default_regdomain = default_regdomain;
|
|
spin_unlock_irq(&mac->lock);
|
|
r = reset_channel(mac);
|
|
if (r)
|
|
goto disable_int;
|
|
|
|
/* We must inform the device that we are doing encryption/decryption in
|
|
* software at the moment. */
|
|
r = zd_set_encryption_type(chip, ENC_SNIFFER);
|
|
if (r)
|
|
goto disable_int;
|
|
|
|
r = zd_geo_init(zd_mac_to_ieee80211(mac), mac->regdomain);
|
|
if (r)
|
|
goto disable_int;
|
|
|
|
r = 0;
|
|
disable_int:
|
|
zd_chip_disable_int(chip);
|
|
out:
|
|
return r;
|
|
}
|
|
|
|
void zd_mac_clear(struct zd_mac *mac)
|
|
{
|
|
flush_workqueue(zd_workqueue);
|
|
skb_queue_purge(&mac->rx_queue);
|
|
tasklet_kill(&mac->rx_tasklet);
|
|
zd_chip_clear(&mac->chip);
|
|
ZD_ASSERT(!spin_is_locked(&mac->lock));
|
|
ZD_MEMCLEAR(mac, sizeof(struct zd_mac));
|
|
}
|
|
|
|
static int set_rx_filter(struct zd_mac *mac)
|
|
{
|
|
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
|
|
u32 filter = (ieee->iw_mode == IW_MODE_MONITOR) ? ~0 : STA_RX_FILTER;
|
|
return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter);
|
|
}
|
|
|
|
static int set_sniffer(struct zd_mac *mac)
|
|
{
|
|
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
|
|
return zd_iowrite32(&mac->chip, CR_SNIFFER_ON,
|
|
ieee->iw_mode == IW_MODE_MONITOR ? 1 : 0);
|
|
return 0;
|
|
}
|
|
|
|
static int set_mc_hash(struct zd_mac *mac)
|
|
{
|
|
struct zd_mc_hash hash;
|
|
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
|
|
|
|
zd_mc_clear(&hash);
|
|
if (ieee->iw_mode == IW_MODE_MONITOR)
|
|
zd_mc_add_all(&hash);
|
|
|
|
return zd_chip_set_multicast_hash(&mac->chip, &hash);
|
|
}
|
|
|
|
int zd_mac_open(struct net_device *netdev)
|
|
{
|
|
struct zd_mac *mac = zd_netdev_mac(netdev);
|
|
struct zd_chip *chip = &mac->chip;
|
|
struct zd_usb *usb = &chip->usb;
|
|
int r;
|
|
|
|
if (!usb->initialized) {
|
|
r = zd_usb_init_hw(usb);
|
|
if (r)
|
|
goto out;
|
|
}
|
|
|
|
tasklet_enable(&mac->rx_tasklet);
|
|
|
|
r = zd_chip_enable_int(chip);
|
|
if (r < 0)
|
|
goto out;
|
|
|
|
r = zd_write_mac_addr(chip, netdev->dev_addr);
|
|
if (r)
|
|
goto disable_int;
|
|
|
|
r = zd_chip_set_basic_rates(chip, CR_RATES_80211B | CR_RATES_80211G);
|
|
if (r < 0)
|
|
goto disable_int;
|
|
r = set_rx_filter(mac);
|
|
if (r)
|
|
goto disable_int;
|
|
r = set_sniffer(mac);
|
|
if (r)
|
|
goto disable_int;
|
|
r = set_mc_hash(mac);
|
|
if (r)
|
|
goto disable_int;
|
|
r = zd_chip_switch_radio_on(chip);
|
|
if (r < 0)
|
|
goto disable_int;
|
|
r = zd_chip_set_channel(chip, mac->requested_channel);
|
|
if (r < 0)
|
|
goto disable_radio;
|
|
r = zd_chip_enable_rx(chip);
|
|
if (r < 0)
|
|
goto disable_radio;
|
|
r = zd_chip_enable_hwint(chip);
|
|
if (r < 0)
|
|
goto disable_rx;
|
|
|
|
housekeeping_enable(mac);
|
|
ieee80211softmac_start(netdev);
|
|
return 0;
|
|
disable_rx:
|
|
zd_chip_disable_rx(chip);
|
|
disable_radio:
|
|
zd_chip_switch_radio_off(chip);
|
|
disable_int:
|
|
zd_chip_disable_int(chip);
|
|
out:
|
|
return r;
|
|
}
|
|
|
|
int zd_mac_stop(struct net_device *netdev)
|
|
{
|
|
struct zd_mac *mac = zd_netdev_mac(netdev);
|
|
struct zd_chip *chip = &mac->chip;
|
|
|
|
netif_stop_queue(netdev);
|
|
|
|
/*
|
|
* The order here deliberately is a little different from the open()
|
|
* method, since we need to make sure there is no opportunity for RX
|
|
* frames to be processed by softmac after we have stopped it.
|
|
*/
|
|
|
|
zd_chip_disable_rx(chip);
|
|
skb_queue_purge(&mac->rx_queue);
|
|
tasklet_disable(&mac->rx_tasklet);
|
|
housekeeping_disable(mac);
|
|
ieee80211softmac_stop(netdev);
|
|
|
|
/* Ensure no work items are running or queued from this point */
|
|
cancel_delayed_work(&mac->set_rts_cts_work);
|
|
cancel_delayed_work(&mac->set_basic_rates_work);
|
|
flush_workqueue(zd_workqueue);
|
|
mac->updating_rts_rate = 0;
|
|
mac->updating_basic_rates = 0;
|
|
|
|
zd_chip_disable_hwint(chip);
|
|
zd_chip_switch_radio_off(chip);
|
|
zd_chip_disable_int(chip);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zd_mac_set_mac_address(struct net_device *netdev, void *p)
|
|
{
|
|
int r;
|
|
unsigned long flags;
|
|
struct sockaddr *addr = p;
|
|
struct zd_mac *mac = zd_netdev_mac(netdev);
|
|
struct zd_chip *chip = &mac->chip;
|
|
|
|
if (!is_valid_ether_addr(addr->sa_data))
|
|
return -EADDRNOTAVAIL;
|
|
|
|
dev_dbg_f(zd_mac_dev(mac),
|
|
"Setting MAC to " MAC_FMT "\n", MAC_ARG(addr->sa_data));
|
|
|
|
if (netdev->flags & IFF_UP) {
|
|
r = zd_write_mac_addr(chip, addr->sa_data);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
memcpy(netdev->dev_addr, addr->sa_data, ETH_ALEN);
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void set_multicast_hash_handler(struct work_struct *work)
|
|
{
|
|
struct zd_mac *mac = container_of(work, struct zd_mac,
|
|
set_multicast_hash_work);
|
|
struct zd_mc_hash hash;
|
|
|
|
spin_lock_irq(&mac->lock);
|
|
hash = mac->multicast_hash;
|
|
spin_unlock_irq(&mac->lock);
|
|
|
|
zd_chip_set_multicast_hash(&mac->chip, &hash);
|
|
}
|
|
|
|
void zd_mac_set_multicast_list(struct net_device *dev)
|
|
{
|
|
struct zd_mac *mac = zd_netdev_mac(dev);
|
|
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
|
|
struct zd_mc_hash hash;
|
|
struct dev_mc_list *mc;
|
|
unsigned long flags;
|
|
|
|
if (dev->flags & (IFF_PROMISC|IFF_ALLMULTI) ||
|
|
ieee->iw_mode == IW_MODE_MONITOR) {
|
|
zd_mc_add_all(&hash);
|
|
} else {
|
|
zd_mc_clear(&hash);
|
|
for (mc = dev->mc_list; mc; mc = mc->next) {
|
|
dev_dbg_f(zd_mac_dev(mac), "mc addr " MAC_FMT "\n",
|
|
MAC_ARG(mc->dmi_addr));
|
|
zd_mc_add_addr(&hash, mc->dmi_addr);
|
|
}
|
|
}
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
mac->multicast_hash = hash;
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
queue_work(zd_workqueue, &mac->set_multicast_hash_work);
|
|
}
|
|
|
|
int zd_mac_set_regdomain(struct zd_mac *mac, u8 regdomain)
|
|
{
|
|
int r;
|
|
u8 channel;
|
|
|
|
ZD_ASSERT(!irqs_disabled());
|
|
spin_lock_irq(&mac->lock);
|
|
if (regdomain == 0) {
|
|
regdomain = mac->default_regdomain;
|
|
}
|
|
if (!zd_regdomain_supported(regdomain)) {
|
|
spin_unlock_irq(&mac->lock);
|
|
return -EINVAL;
|
|
}
|
|
mac->regdomain = regdomain;
|
|
channel = mac->requested_channel;
|
|
spin_unlock_irq(&mac->lock);
|
|
|
|
r = zd_geo_init(zd_mac_to_ieee80211(mac), regdomain);
|
|
if (r)
|
|
return r;
|
|
if (!zd_regdomain_supports_channel(regdomain, channel)) {
|
|
r = reset_channel(mac);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
u8 zd_mac_get_regdomain(struct zd_mac *mac)
|
|
{
|
|
unsigned long flags;
|
|
u8 regdomain;
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
regdomain = mac->regdomain;
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
return regdomain;
|
|
}
|
|
|
|
/* Fallback to lowest rate, if rate is unknown. */
|
|
static u8 rate_to_zd_rate(u8 rate)
|
|
{
|
|
switch (rate) {
|
|
case IEEE80211_CCK_RATE_2MB:
|
|
return ZD_CCK_RATE_2M;
|
|
case IEEE80211_CCK_RATE_5MB:
|
|
return ZD_CCK_RATE_5_5M;
|
|
case IEEE80211_CCK_RATE_11MB:
|
|
return ZD_CCK_RATE_11M;
|
|
case IEEE80211_OFDM_RATE_6MB:
|
|
return ZD_OFDM_RATE_6M;
|
|
case IEEE80211_OFDM_RATE_9MB:
|
|
return ZD_OFDM_RATE_9M;
|
|
case IEEE80211_OFDM_RATE_12MB:
|
|
return ZD_OFDM_RATE_12M;
|
|
case IEEE80211_OFDM_RATE_18MB:
|
|
return ZD_OFDM_RATE_18M;
|
|
case IEEE80211_OFDM_RATE_24MB:
|
|
return ZD_OFDM_RATE_24M;
|
|
case IEEE80211_OFDM_RATE_36MB:
|
|
return ZD_OFDM_RATE_36M;
|
|
case IEEE80211_OFDM_RATE_48MB:
|
|
return ZD_OFDM_RATE_48M;
|
|
case IEEE80211_OFDM_RATE_54MB:
|
|
return ZD_OFDM_RATE_54M;
|
|
}
|
|
return ZD_CCK_RATE_1M;
|
|
}
|
|
|
|
static u16 rate_to_cr_rate(u8 rate)
|
|
{
|
|
switch (rate) {
|
|
case IEEE80211_CCK_RATE_2MB:
|
|
return CR_RATE_1M;
|
|
case IEEE80211_CCK_RATE_5MB:
|
|
return CR_RATE_5_5M;
|
|
case IEEE80211_CCK_RATE_11MB:
|
|
return CR_RATE_11M;
|
|
case IEEE80211_OFDM_RATE_6MB:
|
|
return CR_RATE_6M;
|
|
case IEEE80211_OFDM_RATE_9MB:
|
|
return CR_RATE_9M;
|
|
case IEEE80211_OFDM_RATE_12MB:
|
|
return CR_RATE_12M;
|
|
case IEEE80211_OFDM_RATE_18MB:
|
|
return CR_RATE_18M;
|
|
case IEEE80211_OFDM_RATE_24MB:
|
|
return CR_RATE_24M;
|
|
case IEEE80211_OFDM_RATE_36MB:
|
|
return CR_RATE_36M;
|
|
case IEEE80211_OFDM_RATE_48MB:
|
|
return CR_RATE_48M;
|
|
case IEEE80211_OFDM_RATE_54MB:
|
|
return CR_RATE_54M;
|
|
}
|
|
return CR_RATE_1M;
|
|
}
|
|
|
|
static void try_enable_tx(struct zd_mac *mac)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
if (mac->updating_rts_rate == 0 && mac->updating_basic_rates == 0)
|
|
netif_wake_queue(mac->netdev);
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
}
|
|
|
|
static void set_rts_cts_work(struct work_struct *work)
|
|
{
|
|
struct zd_mac *mac =
|
|
container_of(work, struct zd_mac, set_rts_cts_work.work);
|
|
unsigned long flags;
|
|
u8 rts_rate;
|
|
unsigned int short_preamble;
|
|
|
|
mutex_lock(&mac->chip.mutex);
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
mac->updating_rts_rate = 0;
|
|
rts_rate = mac->rts_rate;
|
|
short_preamble = mac->short_preamble;
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
|
|
zd_chip_set_rts_cts_rate_locked(&mac->chip, rts_rate, short_preamble);
|
|
mutex_unlock(&mac->chip.mutex);
|
|
|
|
try_enable_tx(mac);
|
|
}
|
|
|
|
static void set_basic_rates_work(struct work_struct *work)
|
|
{
|
|
struct zd_mac *mac =
|
|
container_of(work, struct zd_mac, set_basic_rates_work.work);
|
|
unsigned long flags;
|
|
u16 basic_rates;
|
|
|
|
mutex_lock(&mac->chip.mutex);
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
mac->updating_basic_rates = 0;
|
|
basic_rates = mac->basic_rates;
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
|
|
zd_chip_set_basic_rates_locked(&mac->chip, basic_rates);
|
|
mutex_unlock(&mac->chip.mutex);
|
|
|
|
try_enable_tx(mac);
|
|
}
|
|
|
|
static void bssinfo_change(struct net_device *netdev, u32 changes)
|
|
{
|
|
struct zd_mac *mac = zd_netdev_mac(netdev);
|
|
struct ieee80211softmac_device *softmac = ieee80211_priv(netdev);
|
|
struct ieee80211softmac_bss_info *bssinfo = &softmac->bssinfo;
|
|
int need_set_rts_cts = 0;
|
|
int need_set_rates = 0;
|
|
u16 basic_rates;
|
|
unsigned long flags;
|
|
|
|
dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes);
|
|
|
|
if (changes & IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE) {
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
mac->short_preamble = bssinfo->short_preamble;
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
need_set_rts_cts = 1;
|
|
}
|
|
|
|
if (changes & IEEE80211SOFTMAC_BSSINFOCHG_RATES) {
|
|
/* Set RTS rate to highest available basic rate */
|
|
u8 hi_rate = ieee80211softmac_highest_supported_rate(softmac,
|
|
&bssinfo->supported_rates, 1);
|
|
hi_rate = rate_to_zd_rate(hi_rate);
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
if (hi_rate != mac->rts_rate) {
|
|
mac->rts_rate = hi_rate;
|
|
need_set_rts_cts = 1;
|
|
}
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
|
|
/* Set basic rates */
|
|
need_set_rates = 1;
|
|
if (bssinfo->supported_rates.count == 0) {
|
|
/* Allow the device to be flexible */
|
|
basic_rates = CR_RATES_80211B | CR_RATES_80211G;
|
|
} else {
|
|
int i = 0;
|
|
basic_rates = 0;
|
|
|
|
for (i = 0; i < bssinfo->supported_rates.count; i++) {
|
|
u16 rate = bssinfo->supported_rates.rates[i];
|
|
if ((rate & IEEE80211_BASIC_RATE_MASK) == 0)
|
|
continue;
|
|
|
|
rate &= ~IEEE80211_BASIC_RATE_MASK;
|
|
basic_rates |= rate_to_cr_rate(rate);
|
|
}
|
|
}
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
mac->basic_rates = basic_rates;
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
}
|
|
|
|
/* Schedule any changes we made above */
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
if (need_set_rts_cts && !mac->updating_rts_rate) {
|
|
mac->updating_rts_rate = 1;
|
|
netif_stop_queue(mac->netdev);
|
|
queue_delayed_work(zd_workqueue, &mac->set_rts_cts_work, 0);
|
|
}
|
|
if (need_set_rates && !mac->updating_basic_rates) {
|
|
mac->updating_basic_rates = 1;
|
|
netif_stop_queue(mac->netdev);
|
|
queue_delayed_work(zd_workqueue, &mac->set_basic_rates_work,
|
|
0);
|
|
}
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
}
|
|
|
|
static void set_channel(struct net_device *netdev, u8 channel)
|
|
{
|
|
struct zd_mac *mac = zd_netdev_mac(netdev);
|
|
|
|
dev_dbg_f(zd_mac_dev(mac), "channel %d\n", channel);
|
|
|
|
zd_chip_set_channel(&mac->chip, channel);
|
|
}
|
|
|
|
int zd_mac_request_channel(struct zd_mac *mac, u8 channel)
|
|
{
|
|
unsigned long lock_flags;
|
|
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
|
|
|
|
if (ieee->iw_mode == IW_MODE_INFRA)
|
|
return -EPERM;
|
|
|
|
spin_lock_irqsave(&mac->lock, lock_flags);
|
|
if (!zd_regdomain_supports_channel(mac->regdomain, channel)) {
|
|
spin_unlock_irqrestore(&mac->lock, lock_flags);
|
|
return -EINVAL;
|
|
}
|
|
mac->requested_channel = channel;
|
|
spin_unlock_irqrestore(&mac->lock, lock_flags);
|
|
if (netif_running(mac->netdev))
|
|
return zd_chip_set_channel(&mac->chip, channel);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
u8 zd_mac_get_channel(struct zd_mac *mac)
|
|
{
|
|
u8 channel = zd_chip_get_channel(&mac->chip);
|
|
|
|
dev_dbg_f(zd_mac_dev(mac), "channel %u\n", channel);
|
|
return channel;
|
|
}
|
|
|
|
/* If wrong rate is given, we are falling back to the slowest rate: 1MBit/s */
|
|
static u8 zd_rate_typed(u8 zd_rate)
|
|
{
|
|
static const u8 typed_rates[16] = {
|
|
[ZD_CCK_RATE_1M] = ZD_CS_CCK|ZD_CCK_RATE_1M,
|
|
[ZD_CCK_RATE_2M] = ZD_CS_CCK|ZD_CCK_RATE_2M,
|
|
[ZD_CCK_RATE_5_5M] = ZD_CS_CCK|ZD_CCK_RATE_5_5M,
|
|
[ZD_CCK_RATE_11M] = ZD_CS_CCK|ZD_CCK_RATE_11M,
|
|
[ZD_OFDM_RATE_6M] = ZD_CS_OFDM|ZD_OFDM_RATE_6M,
|
|
[ZD_OFDM_RATE_9M] = ZD_CS_OFDM|ZD_OFDM_RATE_9M,
|
|
[ZD_OFDM_RATE_12M] = ZD_CS_OFDM|ZD_OFDM_RATE_12M,
|
|
[ZD_OFDM_RATE_18M] = ZD_CS_OFDM|ZD_OFDM_RATE_18M,
|
|
[ZD_OFDM_RATE_24M] = ZD_CS_OFDM|ZD_OFDM_RATE_24M,
|
|
[ZD_OFDM_RATE_36M] = ZD_CS_OFDM|ZD_OFDM_RATE_36M,
|
|
[ZD_OFDM_RATE_48M] = ZD_CS_OFDM|ZD_OFDM_RATE_48M,
|
|
[ZD_OFDM_RATE_54M] = ZD_CS_OFDM|ZD_OFDM_RATE_54M,
|
|
};
|
|
|
|
ZD_ASSERT(ZD_CS_RATE_MASK == 0x0f);
|
|
return typed_rates[zd_rate & ZD_CS_RATE_MASK];
|
|
}
|
|
|
|
int zd_mac_set_mode(struct zd_mac *mac, u32 mode)
|
|
{
|
|
struct ieee80211_device *ieee;
|
|
|
|
switch (mode) {
|
|
case IW_MODE_AUTO:
|
|
case IW_MODE_ADHOC:
|
|
case IW_MODE_INFRA:
|
|
mac->netdev->type = ARPHRD_ETHER;
|
|
break;
|
|
case IW_MODE_MONITOR:
|
|
mac->netdev->type = ARPHRD_IEEE80211_RADIOTAP;
|
|
break;
|
|
default:
|
|
dev_dbg_f(zd_mac_dev(mac), "wrong mode %u\n", mode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ieee = zd_mac_to_ieee80211(mac);
|
|
ZD_ASSERT(!irqs_disabled());
|
|
spin_lock_irq(&ieee->lock);
|
|
ieee->iw_mode = mode;
|
|
spin_unlock_irq(&ieee->lock);
|
|
|
|
if (netif_running(mac->netdev)) {
|
|
int r = set_rx_filter(mac);
|
|
if (r)
|
|
return r;
|
|
return set_sniffer(mac);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zd_mac_get_mode(struct zd_mac *mac, u32 *mode)
|
|
{
|
|
unsigned long flags;
|
|
struct ieee80211_device *ieee;
|
|
|
|
ieee = zd_mac_to_ieee80211(mac);
|
|
spin_lock_irqsave(&ieee->lock, flags);
|
|
*mode = ieee->iw_mode;
|
|
spin_unlock_irqrestore(&ieee->lock, flags);
|
|
return 0;
|
|
}
|
|
|
|
int zd_mac_get_range(struct zd_mac *mac, struct iw_range *range)
|
|
{
|
|
int i;
|
|
const struct channel_range *channel_range;
|
|
u8 regdomain;
|
|
|
|
memset(range, 0, sizeof(*range));
|
|
|
|
/* FIXME: Not so important and depends on the mode. For 802.11g
|
|
* usually this value is used. It seems to be that Bit/s number is
|
|
* given here.
|
|
*/
|
|
range->throughput = 27 * 1000 * 1000;
|
|
|
|
range->max_qual.qual = 100;
|
|
range->max_qual.level = 100;
|
|
|
|
/* FIXME: Needs still to be tuned. */
|
|
range->avg_qual.qual = 71;
|
|
range->avg_qual.level = 80;
|
|
|
|
/* FIXME: depends on standard? */
|
|
range->min_rts = 256;
|
|
range->max_rts = 2346;
|
|
|
|
range->min_frag = MIN_FRAG_THRESHOLD;
|
|
range->max_frag = MAX_FRAG_THRESHOLD;
|
|
|
|
range->max_encoding_tokens = WEP_KEYS;
|
|
range->num_encoding_sizes = 2;
|
|
range->encoding_size[0] = 5;
|
|
range->encoding_size[1] = WEP_KEY_LEN;
|
|
|
|
range->we_version_compiled = WIRELESS_EXT;
|
|
range->we_version_source = 20;
|
|
|
|
range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
|
|
IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
|
|
|
|
ZD_ASSERT(!irqs_disabled());
|
|
spin_lock_irq(&mac->lock);
|
|
regdomain = mac->regdomain;
|
|
spin_unlock_irq(&mac->lock);
|
|
channel_range = zd_channel_range(regdomain);
|
|
|
|
range->num_channels = channel_range->end - channel_range->start;
|
|
range->old_num_channels = range->num_channels;
|
|
range->num_frequency = range->num_channels;
|
|
range->old_num_frequency = range->num_frequency;
|
|
|
|
for (i = 0; i < range->num_frequency; i++) {
|
|
struct iw_freq *freq = &range->freq[i];
|
|
freq->i = channel_range->start + i;
|
|
zd_channel_to_freq(freq, freq->i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zd_calc_tx_length_us(u8 *service, u8 zd_rate, u16 tx_length)
|
|
{
|
|
static const u8 rate_divisor[] = {
|
|
[ZD_CCK_RATE_1M] = 1,
|
|
[ZD_CCK_RATE_2M] = 2,
|
|
[ZD_CCK_RATE_5_5M] = 11, /* bits must be doubled */
|
|
[ZD_CCK_RATE_11M] = 11,
|
|
[ZD_OFDM_RATE_6M] = 6,
|
|
[ZD_OFDM_RATE_9M] = 9,
|
|
[ZD_OFDM_RATE_12M] = 12,
|
|
[ZD_OFDM_RATE_18M] = 18,
|
|
[ZD_OFDM_RATE_24M] = 24,
|
|
[ZD_OFDM_RATE_36M] = 36,
|
|
[ZD_OFDM_RATE_48M] = 48,
|
|
[ZD_OFDM_RATE_54M] = 54,
|
|
};
|
|
|
|
u32 bits = (u32)tx_length * 8;
|
|
u32 divisor;
|
|
|
|
divisor = rate_divisor[zd_rate];
|
|
if (divisor == 0)
|
|
return -EINVAL;
|
|
|
|
switch (zd_rate) {
|
|
case ZD_CCK_RATE_5_5M:
|
|
bits = (2*bits) + 10; /* round up to the next integer */
|
|
break;
|
|
case ZD_CCK_RATE_11M:
|
|
if (service) {
|
|
u32 t = bits % 11;
|
|
*service &= ~ZD_PLCP_SERVICE_LENGTH_EXTENSION;
|
|
if (0 < t && t <= 3) {
|
|
*service |= ZD_PLCP_SERVICE_LENGTH_EXTENSION;
|
|
}
|
|
}
|
|
bits += 10; /* round up to the next integer */
|
|
break;
|
|
}
|
|
|
|
return bits/divisor;
|
|
}
|
|
|
|
enum {
|
|
R2M_SHORT_PREAMBLE = 0x01,
|
|
R2M_11A = 0x02,
|
|
};
|
|
|
|
static u8 zd_rate_to_modulation(u8 zd_rate, int flags)
|
|
{
|
|
u8 modulation;
|
|
|
|
modulation = zd_rate_typed(zd_rate);
|
|
if (flags & R2M_SHORT_PREAMBLE) {
|
|
switch (ZD_CS_RATE(modulation)) {
|
|
case ZD_CCK_RATE_2M:
|
|
case ZD_CCK_RATE_5_5M:
|
|
case ZD_CCK_RATE_11M:
|
|
modulation |= ZD_CS_CCK_PREA_SHORT;
|
|
return modulation;
|
|
}
|
|
}
|
|
if (flags & R2M_11A) {
|
|
if (ZD_CS_TYPE(modulation) == ZD_CS_OFDM)
|
|
modulation |= ZD_CS_OFDM_MODE_11A;
|
|
}
|
|
return modulation;
|
|
}
|
|
|
|
static void cs_set_modulation(struct zd_mac *mac, struct zd_ctrlset *cs,
|
|
struct ieee80211_hdr_4addr *hdr)
|
|
{
|
|
struct ieee80211softmac_device *softmac = ieee80211_priv(mac->netdev);
|
|
u16 ftype = WLAN_FC_GET_TYPE(le16_to_cpu(hdr->frame_ctl));
|
|
u8 rate, zd_rate;
|
|
int is_mgt = (ftype == IEEE80211_FTYPE_MGMT) != 0;
|
|
int is_multicast = is_multicast_ether_addr(hdr->addr1);
|
|
int short_preamble = ieee80211softmac_short_preamble_ok(softmac,
|
|
is_multicast, is_mgt);
|
|
int flags = 0;
|
|
|
|
/* FIXME: 802.11a? */
|
|
rate = ieee80211softmac_suggest_txrate(softmac, is_multicast, is_mgt);
|
|
|
|
if (short_preamble)
|
|
flags |= R2M_SHORT_PREAMBLE;
|
|
|
|
zd_rate = rate_to_zd_rate(rate);
|
|
cs->modulation = zd_rate_to_modulation(zd_rate, flags);
|
|
}
|
|
|
|
static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
|
|
struct ieee80211_hdr_4addr *header)
|
|
{
|
|
struct ieee80211softmac_device *softmac = ieee80211_priv(mac->netdev);
|
|
unsigned int tx_length = le16_to_cpu(cs->tx_length);
|
|
u16 fctl = le16_to_cpu(header->frame_ctl);
|
|
u16 ftype = WLAN_FC_GET_TYPE(fctl);
|
|
u16 stype = WLAN_FC_GET_STYPE(fctl);
|
|
|
|
/*
|
|
* CONTROL TODO:
|
|
* - if backoff needed, enable bit 0
|
|
* - if burst (backoff not needed) disable bit 0
|
|
*/
|
|
|
|
cs->control = 0;
|
|
|
|
/* First fragment */
|
|
if (WLAN_GET_SEQ_FRAG(le16_to_cpu(header->seq_ctl)) == 0)
|
|
cs->control |= ZD_CS_NEED_RANDOM_BACKOFF;
|
|
|
|
/* Multicast */
|
|
if (is_multicast_ether_addr(header->addr1))
|
|
cs->control |= ZD_CS_MULTICAST;
|
|
|
|
/* PS-POLL */
|
|
if (ftype == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL)
|
|
cs->control |= ZD_CS_PS_POLL_FRAME;
|
|
|
|
/* Unicast data frames over the threshold should have RTS */
|
|
if (!is_multicast_ether_addr(header->addr1) &&
|
|
ftype != IEEE80211_FTYPE_MGMT &&
|
|
tx_length > zd_netdev_ieee80211(mac->netdev)->rts)
|
|
cs->control |= ZD_CS_RTS;
|
|
|
|
/* Use CTS-to-self protection if required */
|
|
if (ZD_CS_TYPE(cs->modulation) == ZD_CS_OFDM &&
|
|
ieee80211softmac_protection_needed(softmac)) {
|
|
/* FIXME: avoid sending RTS *and* self-CTS, is that correct? */
|
|
cs->control &= ~ZD_CS_RTS;
|
|
cs->control |= ZD_CS_SELF_CTS;
|
|
}
|
|
|
|
/* FIXME: Management frame? */
|
|
}
|
|
|
|
static int fill_ctrlset(struct zd_mac *mac,
|
|
struct ieee80211_txb *txb,
|
|
int frag_num)
|
|
{
|
|
int r;
|
|
struct sk_buff *skb = txb->fragments[frag_num];
|
|
struct ieee80211_hdr_4addr *hdr =
|
|
(struct ieee80211_hdr_4addr *) skb->data;
|
|
unsigned int frag_len = skb->len + IEEE80211_FCS_LEN;
|
|
unsigned int next_frag_len;
|
|
unsigned int packet_length;
|
|
struct zd_ctrlset *cs = (struct zd_ctrlset *)
|
|
skb_push(skb, sizeof(struct zd_ctrlset));
|
|
|
|
if (frag_num+1 < txb->nr_frags) {
|
|
next_frag_len = txb->fragments[frag_num+1]->len +
|
|
IEEE80211_FCS_LEN;
|
|
} else {
|
|
next_frag_len = 0;
|
|
}
|
|
ZD_ASSERT(frag_len <= 0xffff);
|
|
ZD_ASSERT(next_frag_len <= 0xffff);
|
|
|
|
cs_set_modulation(mac, cs, hdr);
|
|
|
|
cs->tx_length = cpu_to_le16(frag_len);
|
|
|
|
cs_set_control(mac, cs, hdr);
|
|
|
|
packet_length = frag_len + sizeof(struct zd_ctrlset) + 10;
|
|
ZD_ASSERT(packet_length <= 0xffff);
|
|
/* ZD1211B: Computing the length difference this way, gives us
|
|
* flexibility to compute the packet length.
|
|
*/
|
|
cs->packet_length = cpu_to_le16(zd_chip_is_zd1211b(&mac->chip) ?
|
|
packet_length - frag_len : packet_length);
|
|
|
|
/*
|
|
* CURRENT LENGTH:
|
|
* - transmit frame length in microseconds
|
|
* - seems to be derived from frame length
|
|
* - see Cal_Us_Service() in zdinlinef.h
|
|
* - if macp->bTxBurstEnable is enabled, then multiply by 4
|
|
* - bTxBurstEnable is never set in the vendor driver
|
|
*
|
|
* SERVICE:
|
|
* - "for PLCP configuration"
|
|
* - always 0 except in some situations at 802.11b 11M
|
|
* - see line 53 of zdinlinef.h
|
|
*/
|
|
cs->service = 0;
|
|
r = zd_calc_tx_length_us(&cs->service, ZD_CS_RATE(cs->modulation),
|
|
le16_to_cpu(cs->tx_length));
|
|
if (r < 0)
|
|
return r;
|
|
cs->current_length = cpu_to_le16(r);
|
|
|
|
if (next_frag_len == 0) {
|
|
cs->next_frame_length = 0;
|
|
} else {
|
|
r = zd_calc_tx_length_us(NULL, ZD_CS_RATE(cs->modulation),
|
|
next_frag_len);
|
|
if (r < 0)
|
|
return r;
|
|
cs->next_frame_length = cpu_to_le16(r);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zd_mac_tx(struct zd_mac *mac, struct ieee80211_txb *txb, int pri)
|
|
{
|
|
int i, r;
|
|
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
|
|
|
|
for (i = 0; i < txb->nr_frags; i++) {
|
|
struct sk_buff *skb = txb->fragments[i];
|
|
|
|
r = fill_ctrlset(mac, txb, i);
|
|
if (r) {
|
|
ieee->stats.tx_dropped++;
|
|
return r;
|
|
}
|
|
r = zd_usb_tx(&mac->chip.usb, skb->data, skb->len);
|
|
if (r) {
|
|
ieee->stats.tx_dropped++;
|
|
return r;
|
|
}
|
|
}
|
|
|
|
/* FIXME: shouldn't this be handled by the upper layers? */
|
|
mac->netdev->trans_start = jiffies;
|
|
|
|
ieee80211_txb_free(txb);
|
|
return 0;
|
|
}
|
|
|
|
struct zd_rt_hdr {
|
|
struct ieee80211_radiotap_header rt_hdr;
|
|
u8 rt_flags;
|
|
u8 rt_rate;
|
|
u16 rt_channel;
|
|
u16 rt_chbitmask;
|
|
} __attribute__((packed));
|
|
|
|
static void fill_rt_header(void *buffer, struct zd_mac *mac,
|
|
const struct ieee80211_rx_stats *stats,
|
|
const struct rx_status *status)
|
|
{
|
|
struct zd_rt_hdr *hdr = buffer;
|
|
|
|
hdr->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
|
|
hdr->rt_hdr.it_pad = 0;
|
|
hdr->rt_hdr.it_len = cpu_to_le16(sizeof(struct zd_rt_hdr));
|
|
hdr->rt_hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
|
|
(1 << IEEE80211_RADIOTAP_CHANNEL) |
|
|
(1 << IEEE80211_RADIOTAP_RATE));
|
|
|
|
hdr->rt_flags = 0;
|
|
if (status->decryption_type & (ZD_RX_WEP64|ZD_RX_WEP128|ZD_RX_WEP256))
|
|
hdr->rt_flags |= IEEE80211_RADIOTAP_F_WEP;
|
|
|
|
hdr->rt_rate = stats->rate / 5;
|
|
|
|
/* FIXME: 802.11a */
|
|
hdr->rt_channel = cpu_to_le16(ieee80211chan2mhz(
|
|
_zd_chip_get_channel(&mac->chip)));
|
|
hdr->rt_chbitmask = cpu_to_le16(IEEE80211_CHAN_2GHZ |
|
|
((status->frame_status & ZD_RX_FRAME_MODULATION_MASK) ==
|
|
ZD_RX_OFDM ? IEEE80211_CHAN_OFDM : IEEE80211_CHAN_CCK));
|
|
}
|
|
|
|
/* Returns 1 if the data packet is for us and 0 otherwise. */
|
|
static int is_data_packet_for_us(struct ieee80211_device *ieee,
|
|
struct ieee80211_hdr_4addr *hdr)
|
|
{
|
|
struct net_device *netdev = ieee->dev;
|
|
u16 fc = le16_to_cpu(hdr->frame_ctl);
|
|
|
|
ZD_ASSERT(WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA);
|
|
|
|
switch (ieee->iw_mode) {
|
|
case IW_MODE_ADHOC:
|
|
if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != 0 ||
|
|
compare_ether_addr(hdr->addr3, ieee->bssid) != 0)
|
|
return 0;
|
|
break;
|
|
case IW_MODE_AUTO:
|
|
case IW_MODE_INFRA:
|
|
if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) !=
|
|
IEEE80211_FCTL_FROMDS ||
|
|
compare_ether_addr(hdr->addr2, ieee->bssid) != 0)
|
|
return 0;
|
|
break;
|
|
default:
|
|
ZD_ASSERT(ieee->iw_mode != IW_MODE_MONITOR);
|
|
return 0;
|
|
}
|
|
|
|
return compare_ether_addr(hdr->addr1, netdev->dev_addr) == 0 ||
|
|
(is_multicast_ether_addr(hdr->addr1) &&
|
|
compare_ether_addr(hdr->addr3, netdev->dev_addr) != 0) ||
|
|
(netdev->flags & IFF_PROMISC);
|
|
}
|
|
|
|
/* Filters received packets. The function returns 1 if the packet should be
|
|
* forwarded to ieee80211_rx(). If the packet should be ignored the function
|
|
* returns 0. If an invalid packet is found the function returns -EINVAL.
|
|
*
|
|
* The function calls ieee80211_rx_mgt() directly.
|
|
*
|
|
* It has been based on ieee80211_rx_any.
|
|
*/
|
|
static int filter_rx(struct ieee80211_device *ieee,
|
|
const u8 *buffer, unsigned int length,
|
|
struct ieee80211_rx_stats *stats)
|
|
{
|
|
struct ieee80211_hdr_4addr *hdr;
|
|
u16 fc;
|
|
|
|
if (ieee->iw_mode == IW_MODE_MONITOR)
|
|
return 1;
|
|
|
|
hdr = (struct ieee80211_hdr_4addr *)buffer;
|
|
fc = le16_to_cpu(hdr->frame_ctl);
|
|
if ((fc & IEEE80211_FCTL_VERS) != 0)
|
|
return -EINVAL;
|
|
|
|
switch (WLAN_FC_GET_TYPE(fc)) {
|
|
case IEEE80211_FTYPE_MGMT:
|
|
if (length < sizeof(struct ieee80211_hdr_3addr))
|
|
return -EINVAL;
|
|
ieee80211_rx_mgt(ieee, hdr, stats);
|
|
return 0;
|
|
case IEEE80211_FTYPE_CTL:
|
|
return 0;
|
|
case IEEE80211_FTYPE_DATA:
|
|
/* Ignore invalid short buffers */
|
|
if (length < sizeof(struct ieee80211_hdr_3addr))
|
|
return -EINVAL;
|
|
return is_data_packet_for_us(ieee, hdr);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void update_qual_rssi(struct zd_mac *mac,
|
|
const u8 *buffer, unsigned int length,
|
|
u8 qual_percent, u8 rssi_percent)
|
|
{
|
|
unsigned long flags;
|
|
struct ieee80211_hdr_3addr *hdr;
|
|
int i;
|
|
|
|
hdr = (struct ieee80211_hdr_3addr *)buffer;
|
|
if (length < offsetof(struct ieee80211_hdr_3addr, addr3))
|
|
return;
|
|
if (compare_ether_addr(hdr->addr2, zd_mac_to_ieee80211(mac)->bssid) != 0)
|
|
return;
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
i = mac->stats_count % ZD_MAC_STATS_BUFFER_SIZE;
|
|
mac->qual_buffer[i] = qual_percent;
|
|
mac->rssi_buffer[i] = rssi_percent;
|
|
mac->stats_count++;
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
}
|
|
|
|
static int fill_rx_stats(struct ieee80211_rx_stats *stats,
|
|
const struct rx_status **pstatus,
|
|
struct zd_mac *mac,
|
|
const u8 *buffer, unsigned int length)
|
|
{
|
|
const struct rx_status *status;
|
|
|
|
*pstatus = status = zd_tail(buffer, length, sizeof(struct rx_status));
|
|
if (status->frame_status & ZD_RX_ERROR) {
|
|
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
|
|
ieee->stats.rx_errors++;
|
|
if (status->frame_status & ZD_RX_TIMEOUT_ERROR)
|
|
ieee->stats.rx_missed_errors++;
|
|
else if (status->frame_status & ZD_RX_FIFO_OVERRUN_ERROR)
|
|
ieee->stats.rx_fifo_errors++;
|
|
else if (status->frame_status & ZD_RX_DECRYPTION_ERROR)
|
|
ieee->ieee_stats.rx_discards_undecryptable++;
|
|
else if (status->frame_status & ZD_RX_CRC32_ERROR) {
|
|
ieee->stats.rx_crc_errors++;
|
|
ieee->ieee_stats.rx_fcs_errors++;
|
|
}
|
|
else if (status->frame_status & ZD_RX_CRC16_ERROR)
|
|
ieee->stats.rx_crc_errors++;
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(stats, 0, sizeof(struct ieee80211_rx_stats));
|
|
stats->len = length - (ZD_PLCP_HEADER_SIZE + IEEE80211_FCS_LEN +
|
|
+ sizeof(struct rx_status));
|
|
/* FIXME: 802.11a */
|
|
stats->freq = IEEE80211_24GHZ_BAND;
|
|
stats->received_channel = _zd_chip_get_channel(&mac->chip);
|
|
stats->rssi = zd_rx_strength_percent(status->signal_strength);
|
|
stats->signal = zd_rx_qual_percent(buffer,
|
|
length - sizeof(struct rx_status),
|
|
status);
|
|
stats->mask = IEEE80211_STATMASK_RSSI | IEEE80211_STATMASK_SIGNAL;
|
|
stats->rate = zd_rx_rate(buffer, status);
|
|
if (stats->rate)
|
|
stats->mask |= IEEE80211_STATMASK_RATE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void zd_mac_rx(struct zd_mac *mac, struct sk_buff *skb)
|
|
{
|
|
int r;
|
|
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
|
|
struct ieee80211_rx_stats stats;
|
|
const struct rx_status *status;
|
|
|
|
if (skb->len < ZD_PLCP_HEADER_SIZE + IEEE80211_1ADDR_LEN +
|
|
IEEE80211_FCS_LEN + sizeof(struct rx_status))
|
|
{
|
|
ieee->stats.rx_errors++;
|
|
ieee->stats.rx_length_errors++;
|
|
goto free_skb;
|
|
}
|
|
|
|
r = fill_rx_stats(&stats, &status, mac, skb->data, skb->len);
|
|
if (r) {
|
|
/* Only packets with rx errors are included here.
|
|
* The error stats have already been set in fill_rx_stats.
|
|
*/
|
|
goto free_skb;
|
|
}
|
|
|
|
__skb_pull(skb, ZD_PLCP_HEADER_SIZE);
|
|
__skb_trim(skb, skb->len -
|
|
(IEEE80211_FCS_LEN + sizeof(struct rx_status)));
|
|
|
|
update_qual_rssi(mac, skb->data, skb->len, stats.signal,
|
|
status->signal_strength);
|
|
|
|
r = filter_rx(ieee, skb->data, skb->len, &stats);
|
|
if (r <= 0) {
|
|
if (r < 0) {
|
|
ieee->stats.rx_errors++;
|
|
dev_dbg_f(zd_mac_dev(mac), "Error in packet.\n");
|
|
}
|
|
goto free_skb;
|
|
}
|
|
|
|
if (ieee->iw_mode == IW_MODE_MONITOR)
|
|
fill_rt_header(skb_push(skb, sizeof(struct zd_rt_hdr)), mac,
|
|
&stats, status);
|
|
|
|
r = ieee80211_rx(ieee, skb, &stats);
|
|
if (r)
|
|
return;
|
|
free_skb:
|
|
/* We are always in a soft irq. */
|
|
dev_kfree_skb(skb);
|
|
}
|
|
|
|
static void do_rx(unsigned long mac_ptr)
|
|
{
|
|
struct zd_mac *mac = (struct zd_mac *)mac_ptr;
|
|
struct sk_buff *skb;
|
|
|
|
while ((skb = skb_dequeue(&mac->rx_queue)) != NULL)
|
|
zd_mac_rx(mac, skb);
|
|
}
|
|
|
|
int zd_mac_rx_irq(struct zd_mac *mac, const u8 *buffer, unsigned int length)
|
|
{
|
|
struct sk_buff *skb;
|
|
|
|
skb = dev_alloc_skb(sizeof(struct zd_rt_hdr) + length);
|
|
if (!skb) {
|
|
struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac);
|
|
dev_warn(zd_mac_dev(mac), "Could not allocate skb.\n");
|
|
ieee->stats.rx_dropped++;
|
|
return -ENOMEM;
|
|
}
|
|
skb_reserve(skb, sizeof(struct zd_rt_hdr));
|
|
memcpy(__skb_put(skb, length), buffer, length);
|
|
skb_queue_tail(&mac->rx_queue, skb);
|
|
tasklet_schedule(&mac->rx_tasklet);
|
|
return 0;
|
|
}
|
|
|
|
static int netdev_tx(struct ieee80211_txb *txb, struct net_device *netdev,
|
|
int pri)
|
|
{
|
|
return zd_mac_tx(zd_netdev_mac(netdev), txb, pri);
|
|
}
|
|
|
|
static void set_security(struct net_device *netdev,
|
|
struct ieee80211_security *sec)
|
|
{
|
|
struct ieee80211_device *ieee = zd_netdev_ieee80211(netdev);
|
|
struct ieee80211_security *secinfo = &ieee->sec;
|
|
int keyidx;
|
|
|
|
dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), "\n");
|
|
|
|
for (keyidx = 0; keyidx<WEP_KEYS; keyidx++)
|
|
if (sec->flags & (1<<keyidx)) {
|
|
secinfo->encode_alg[keyidx] = sec->encode_alg[keyidx];
|
|
secinfo->key_sizes[keyidx] = sec->key_sizes[keyidx];
|
|
memcpy(secinfo->keys[keyidx], sec->keys[keyidx],
|
|
SCM_KEY_LEN);
|
|
}
|
|
|
|
if (sec->flags & SEC_ACTIVE_KEY) {
|
|
secinfo->active_key = sec->active_key;
|
|
dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
|
|
" .active_key = %d\n", sec->active_key);
|
|
}
|
|
if (sec->flags & SEC_UNICAST_GROUP) {
|
|
secinfo->unicast_uses_group = sec->unicast_uses_group;
|
|
dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
|
|
" .unicast_uses_group = %d\n",
|
|
sec->unicast_uses_group);
|
|
}
|
|
if (sec->flags & SEC_LEVEL) {
|
|
secinfo->level = sec->level;
|
|
dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
|
|
" .level = %d\n", sec->level);
|
|
}
|
|
if (sec->flags & SEC_ENABLED) {
|
|
secinfo->enabled = sec->enabled;
|
|
dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
|
|
" .enabled = %d\n", sec->enabled);
|
|
}
|
|
if (sec->flags & SEC_ENCRYPT) {
|
|
secinfo->encrypt = sec->encrypt;
|
|
dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
|
|
" .encrypt = %d\n", sec->encrypt);
|
|
}
|
|
if (sec->flags & SEC_AUTH_MODE) {
|
|
secinfo->auth_mode = sec->auth_mode;
|
|
dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)),
|
|
" .auth_mode = %d\n", sec->auth_mode);
|
|
}
|
|
}
|
|
|
|
static void ieee_init(struct ieee80211_device *ieee)
|
|
{
|
|
ieee->mode = IEEE_B | IEEE_G;
|
|
ieee->freq_band = IEEE80211_24GHZ_BAND;
|
|
ieee->modulation = IEEE80211_OFDM_MODULATION | IEEE80211_CCK_MODULATION;
|
|
ieee->tx_headroom = sizeof(struct zd_ctrlset);
|
|
ieee->set_security = set_security;
|
|
ieee->hard_start_xmit = netdev_tx;
|
|
|
|
/* Software encryption/decryption for now */
|
|
ieee->host_build_iv = 0;
|
|
ieee->host_encrypt = 1;
|
|
ieee->host_decrypt = 1;
|
|
|
|
/* FIXME: default to managed mode, until ieee80211 and zd1211rw can
|
|
* correctly support AUTO */
|
|
ieee->iw_mode = IW_MODE_INFRA;
|
|
}
|
|
|
|
static void softmac_init(struct ieee80211softmac_device *sm)
|
|
{
|
|
sm->set_channel = set_channel;
|
|
sm->bssinfo_change = bssinfo_change;
|
|
}
|
|
|
|
struct iw_statistics *zd_mac_get_wireless_stats(struct net_device *ndev)
|
|
{
|
|
struct zd_mac *mac = zd_netdev_mac(ndev);
|
|
struct iw_statistics *iw_stats = &mac->iw_stats;
|
|
unsigned int i, count, qual_total, rssi_total;
|
|
|
|
memset(iw_stats, 0, sizeof(struct iw_statistics));
|
|
/* We are not setting the status, because ieee->state is not updated
|
|
* at all and this driver doesn't track authentication state.
|
|
*/
|
|
spin_lock_irq(&mac->lock);
|
|
count = mac->stats_count < ZD_MAC_STATS_BUFFER_SIZE ?
|
|
mac->stats_count : ZD_MAC_STATS_BUFFER_SIZE;
|
|
qual_total = rssi_total = 0;
|
|
for (i = 0; i < count; i++) {
|
|
qual_total += mac->qual_buffer[i];
|
|
rssi_total += mac->rssi_buffer[i];
|
|
}
|
|
spin_unlock_irq(&mac->lock);
|
|
iw_stats->qual.updated = IW_QUAL_NOISE_INVALID;
|
|
if (count > 0) {
|
|
iw_stats->qual.qual = qual_total / count;
|
|
iw_stats->qual.level = rssi_total / count;
|
|
iw_stats->qual.updated |=
|
|
IW_QUAL_QUAL_UPDATED|IW_QUAL_LEVEL_UPDATED;
|
|
} else {
|
|
iw_stats->qual.updated |=
|
|
IW_QUAL_QUAL_INVALID|IW_QUAL_LEVEL_INVALID;
|
|
}
|
|
/* TODO: update counter */
|
|
return iw_stats;
|
|
}
|
|
|
|
#define LINK_LED_WORK_DELAY HZ
|
|
|
|
static void link_led_handler(struct work_struct *work)
|
|
{
|
|
struct zd_mac *mac =
|
|
container_of(work, struct zd_mac, housekeeping.link_led_work.work);
|
|
struct zd_chip *chip = &mac->chip;
|
|
struct ieee80211softmac_device *sm = ieee80211_priv(mac->netdev);
|
|
int is_associated;
|
|
int r;
|
|
|
|
spin_lock_irq(&mac->lock);
|
|
is_associated = sm->associnfo.associated != 0;
|
|
spin_unlock_irq(&mac->lock);
|
|
|
|
r = zd_chip_control_leds(chip,
|
|
is_associated ? LED_ASSOCIATED : LED_SCANNING);
|
|
if (r)
|
|
dev_err(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r);
|
|
|
|
queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
|
|
LINK_LED_WORK_DELAY);
|
|
}
|
|
|
|
static void housekeeping_init(struct zd_mac *mac)
|
|
{
|
|
INIT_DELAYED_WORK(&mac->housekeeping.link_led_work, link_led_handler);
|
|
}
|
|
|
|
static void housekeeping_enable(struct zd_mac *mac)
|
|
{
|
|
dev_dbg_f(zd_mac_dev(mac), "\n");
|
|
queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
|
|
0);
|
|
}
|
|
|
|
static void housekeeping_disable(struct zd_mac *mac)
|
|
{
|
|
dev_dbg_f(zd_mac_dev(mac), "\n");
|
|
cancel_rearming_delayed_workqueue(zd_workqueue,
|
|
&mac->housekeeping.link_led_work);
|
|
zd_chip_control_leds(&mac->chip, LED_OFF);
|
|
}
|