📄 zd_mac.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"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; DECLARE_MAC_BUF(mac2); if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; dev_dbg_f(zd_mac_dev(mac), "Setting MAC to %s\n", print_mac(mac2, 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; DECLARE_MAC_BUF(mac2); 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 %s\n", print_mac(mac2, 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -