From ca67b09c015d4af3ae3cce12aa72e60941dbb8b5 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:29:52 +0200 Subject: Adding debian version 2.06-13+deb12u1. Signed-off-by: Daniel Baumann --- .../disabled/gpxe/src/drivers/net/ath5k/ath5k.c | 1694 ++++++++++++++++++++ 1 file changed, 1694 insertions(+) create mode 100644 debian/grub-extras/disabled/gpxe/src/drivers/net/ath5k/ath5k.c (limited to 'debian/grub-extras/disabled/gpxe/src/drivers/net/ath5k/ath5k.c') diff --git a/debian/grub-extras/disabled/gpxe/src/drivers/net/ath5k/ath5k.c b/debian/grub-extras/disabled/gpxe/src/drivers/net/ath5k/ath5k.c new file mode 100644 index 0000000..5101a54 --- /dev/null +++ b/debian/grub-extras/disabled/gpxe/src/drivers/net/ath5k/ath5k.c @@ -0,0 +1,1694 @@ +/* + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2005 Atheros Communications, Inc. + * Copyright (c) 2006 Devicescape Software, Inc. + * Copyright (c) 2007 Jiri Slaby + * Copyright (c) 2007 Luis R. Rodriguez + * + * Modified for gPXE, July 2009, by Joshua Oreman + * Original from Linux kernel 2.6.30. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +FILE_LICENCE ( BSD3 ); + +#include +#include +#include +#include +#include +#include + +#include "base.h" +#include "reg.h" + +#define ATH5K_CALIB_INTERVAL 10 /* Calibrate PHY every 10 seconds */ +#define ATH5K_RETRIES 4 /* Number of times to retry packet sends */ +#define ATH5K_DESC_ALIGN 16 /* Alignment for TX/RX descriptors */ + +/******************\ +* Internal defines * +\******************/ + +/* Known PCI ids */ +static struct pci_device_id ath5k_nics[] = { + PCI_ROM(0x168c, 0x0207, "ath5210e", "Atheros 5210 early", AR5K_AR5210), + PCI_ROM(0x168c, 0x0007, "ath5210", "Atheros 5210", AR5K_AR5210), + PCI_ROM(0x168c, 0x0011, "ath5311", "Atheros 5311 (AHB)", AR5K_AR5211), + PCI_ROM(0x168c, 0x0012, "ath5211", "Atheros 5211", AR5K_AR5211), + PCI_ROM(0x168c, 0x0013, "ath5212", "Atheros 5212", AR5K_AR5212), + PCI_ROM(0xa727, 0x0013, "ath5212c","3com Ath 5212", AR5K_AR5212), + PCI_ROM(0x10b7, 0x0013, "rdag675", "3com 3CRDAG675", AR5K_AR5212), + PCI_ROM(0x168c, 0x1014, "ath5212m", "Ath 5212 miniPCI", AR5K_AR5212), + PCI_ROM(0x168c, 0x0014, "ath5212x14", "Atheros 5212 x14", AR5K_AR5212), + PCI_ROM(0x168c, 0x0015, "ath5212x15", "Atheros 5212 x15", AR5K_AR5212), + PCI_ROM(0x168c, 0x0016, "ath5212x16", "Atheros 5212 x16", AR5K_AR5212), + PCI_ROM(0x168c, 0x0017, "ath5212x17", "Atheros 5212 x17", AR5K_AR5212), + PCI_ROM(0x168c, 0x0018, "ath5212x18", "Atheros 5212 x18", AR5K_AR5212), + PCI_ROM(0x168c, 0x0019, "ath5212x19", "Atheros 5212 x19", AR5K_AR5212), + PCI_ROM(0x168c, 0x001a, "ath2413", "Atheros 2413 Griffin", AR5K_AR5212), + PCI_ROM(0x168c, 0x001b, "ath5413", "Atheros 5413 Eagle", AR5K_AR5212), + PCI_ROM(0x168c, 0x001c, "ath5212e", "Atheros 5212 PCI-E", AR5K_AR5212), + PCI_ROM(0x168c, 0x001d, "ath2417", "Atheros 2417 Nala", AR5K_AR5212), +}; + +/* Known SREVs */ +static const struct ath5k_srev_name srev_names[] = { + { "5210", AR5K_VERSION_MAC, AR5K_SREV_AR5210 }, + { "5311", AR5K_VERSION_MAC, AR5K_SREV_AR5311 }, + { "5311A", AR5K_VERSION_MAC, AR5K_SREV_AR5311A }, + { "5311B", AR5K_VERSION_MAC, AR5K_SREV_AR5311B }, + { "5211", AR5K_VERSION_MAC, AR5K_SREV_AR5211 }, + { "5212", AR5K_VERSION_MAC, AR5K_SREV_AR5212 }, + { "5213", AR5K_VERSION_MAC, AR5K_SREV_AR5213 }, + { "5213A", AR5K_VERSION_MAC, AR5K_SREV_AR5213A }, + { "2413", AR5K_VERSION_MAC, AR5K_SREV_AR2413 }, + { "2414", AR5K_VERSION_MAC, AR5K_SREV_AR2414 }, + { "5424", AR5K_VERSION_MAC, AR5K_SREV_AR5424 }, + { "5413", AR5K_VERSION_MAC, AR5K_SREV_AR5413 }, + { "5414", AR5K_VERSION_MAC, AR5K_SREV_AR5414 }, + { "2415", AR5K_VERSION_MAC, AR5K_SREV_AR2415 }, + { "5416", AR5K_VERSION_MAC, AR5K_SREV_AR5416 }, + { "5418", AR5K_VERSION_MAC, AR5K_SREV_AR5418 }, + { "2425", AR5K_VERSION_MAC, AR5K_SREV_AR2425 }, + { "2417", AR5K_VERSION_MAC, AR5K_SREV_AR2417 }, + { "xxxxx", AR5K_VERSION_MAC, AR5K_SREV_UNKNOWN }, + { "5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110 }, + { "5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111 }, + { "5111A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111A }, + { "2111", AR5K_VERSION_RAD, AR5K_SREV_RAD_2111 }, + { "5112", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112 }, + { "5112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A }, + { "5112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112B }, + { "2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112 }, + { "2112A", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A }, + { "2112B", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112B }, + { "2413", AR5K_VERSION_RAD, AR5K_SREV_RAD_2413 }, + { "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 }, + { "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 }, + { "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 }, + { "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 }, + { "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 }, + { "xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN }, +}; + +#define ATH5K_SPMBL_NO 1 +#define ATH5K_SPMBL_YES 2 +#define ATH5K_SPMBL_BOTH 3 + +static const struct { + u16 bitrate; + u8 short_pmbl; + u8 hw_code; +} ath5k_rates[] = { + { 10, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_1M }, + { 20, ATH5K_SPMBL_NO, ATH5K_RATE_CODE_2M }, + { 55, ATH5K_SPMBL_NO, ATH5K_RATE_CODE_5_5M }, + { 110, ATH5K_SPMBL_NO, ATH5K_RATE_CODE_11M }, + { 60, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_6M }, + { 90, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_9M }, + { 120, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_12M }, + { 180, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_18M }, + { 240, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_24M }, + { 360, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_36M }, + { 480, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_48M }, + { 540, ATH5K_SPMBL_BOTH, ATH5K_RATE_CODE_54M }, + { 20, ATH5K_SPMBL_YES, ATH5K_RATE_CODE_2M | AR5K_SET_SHORT_PREAMBLE }, + { 55, ATH5K_SPMBL_YES, ATH5K_RATE_CODE_5_5M | AR5K_SET_SHORT_PREAMBLE }, + { 110, ATH5K_SPMBL_YES, ATH5K_RATE_CODE_11M | AR5K_SET_SHORT_PREAMBLE }, + { 0, 0, 0 }, +}; + +#define ATH5K_NR_RATES 15 + +/* + * Prototypes - PCI stack related functions + */ +static int ath5k_probe(struct pci_device *pdev, + const struct pci_device_id *id); +static void ath5k_remove(struct pci_device *pdev); + +struct pci_driver ath5k_pci_driver __pci_driver = { + .ids = ath5k_nics, + .id_count = sizeof(ath5k_nics) / sizeof(ath5k_nics[0]), + .probe = ath5k_probe, + .remove = ath5k_remove, +}; + + + +/* + * Prototypes - MAC 802.11 stack related functions + */ +static int ath5k_tx(struct net80211_device *dev, struct io_buffer *skb); +static int ath5k_reset(struct ath5k_softc *sc, struct net80211_channel *chan); +static int ath5k_reset_wake(struct ath5k_softc *sc); +static int ath5k_start(struct net80211_device *dev); +static void ath5k_stop(struct net80211_device *dev); +static int ath5k_config(struct net80211_device *dev, int changed); +static void ath5k_poll(struct net80211_device *dev); +static void ath5k_irq(struct net80211_device *dev, int enable); + +static struct net80211_device_operations ath5k_ops = { + .open = ath5k_start, + .close = ath5k_stop, + .transmit = ath5k_tx, + .poll = ath5k_poll, + .irq = ath5k_irq, + .config = ath5k_config, +}; + +/* + * Prototypes - Internal functions + */ +/* Attach detach */ +static int ath5k_attach(struct net80211_device *dev); +static void ath5k_detach(struct net80211_device *dev); +/* Channel/mode setup */ +static unsigned int ath5k_copy_channels(struct ath5k_hw *ah, + struct net80211_channel *channels, + unsigned int mode, + unsigned int max); +static int ath5k_setup_bands(struct net80211_device *dev); +static int ath5k_chan_set(struct ath5k_softc *sc, + struct net80211_channel *chan); +static void ath5k_setcurmode(struct ath5k_softc *sc, + unsigned int mode); +static void ath5k_mode_setup(struct ath5k_softc *sc); + +/* Descriptor setup */ +static int ath5k_desc_alloc(struct ath5k_softc *sc); +static void ath5k_desc_free(struct ath5k_softc *sc); +/* Buffers setup */ +static int ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf); +static int ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf); + +static inline void ath5k_txbuf_free(struct ath5k_softc *sc, + struct ath5k_buf *bf) +{ + if (!bf->iob) + return; + + net80211_tx_complete(sc->dev, bf->iob, 0, ECANCELED); + bf->iob = NULL; +} + +static inline void ath5k_rxbuf_free(struct ath5k_softc *sc __unused, + struct ath5k_buf *bf) +{ + free_iob(bf->iob); + bf->iob = NULL; +} + +/* Queues setup */ +static int ath5k_txq_setup(struct ath5k_softc *sc, + int qtype, int subtype); +static void ath5k_txq_drainq(struct ath5k_softc *sc, + struct ath5k_txq *txq); +static void ath5k_txq_cleanup(struct ath5k_softc *sc); +static void ath5k_txq_release(struct ath5k_softc *sc); +/* Rx handling */ +static int ath5k_rx_start(struct ath5k_softc *sc); +static void ath5k_rx_stop(struct ath5k_softc *sc); +/* Tx handling */ +static void ath5k_tx_processq(struct ath5k_softc *sc, + struct ath5k_txq *txq); + +/* Interrupt handling */ +static int ath5k_init(struct ath5k_softc *sc); +static int ath5k_stop_hw(struct ath5k_softc *sc); + +static void ath5k_calibrate(struct ath5k_softc *sc); + +/* Filter */ +static void ath5k_configure_filter(struct ath5k_softc *sc); + +/********************\ +* PCI Initialization * +\********************/ + +#if DBGLVL_MAX +static const char * +ath5k_chip_name(enum ath5k_srev_type type, u16 val) +{ + const char *name = "xxxxx"; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(srev_names); i++) { + if (srev_names[i].sr_type != type) + continue; + + if ((val & 0xf0) == srev_names[i].sr_val) + name = srev_names[i].sr_name; + + if ((val & 0xff) == srev_names[i].sr_val) { + name = srev_names[i].sr_name; + break; + } + } + + return name; +} +#endif + +static int ath5k_probe(struct pci_device *pdev, + const struct pci_device_id *id) +{ + void *mem; + struct ath5k_softc *sc; + struct net80211_device *dev; + int ret; + u8 csz; + + adjust_pci_device(pdev); + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); + if (csz == 0) { + /* + * We must have this setup properly for rx buffer + * DMA to work so force a reasonable value here if it + * comes up zero. + */ + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 16); + } + /* + * The default setting of latency timer yields poor results, + * set it to the value used by other systems. It may be worth + * tweaking this setting more. + */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); + + /* + * Disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_write_config_byte(pdev, 0x41, 0); + + mem = ioremap(pdev->membase, 0x10000); + if (!mem) { + DBG("ath5k: cannot remap PCI memory region\n"); + ret = -EIO; + goto err; + } + + /* + * Allocate dev (net80211 main struct) + * and dev->priv (driver private data) + */ + dev = net80211_alloc(sizeof(*sc)); + if (!dev) { + DBG("ath5k: cannot allocate 802.11 device\n"); + ret = -ENOMEM; + goto err_map; + } + + /* Initialize driver private data */ + sc = dev->priv; + sc->dev = dev; + sc->pdev = pdev; + + sc->hwinfo = zalloc(sizeof(*sc->hwinfo)); + if (!sc->hwinfo) { + DBG("ath5k: cannot allocate 802.11 hardware info structure\n"); + ret = -ENOMEM; + goto err_free; + } + + sc->hwinfo->flags = NET80211_HW_RX_HAS_FCS; + sc->hwinfo->signal_type = NET80211_SIGNAL_DB; + sc->hwinfo->signal_max = 40; /* 35dB should give perfect 54Mbps */ + sc->hwinfo->channel_change_time = 5000; + + /* Avoid working with the device until setup is complete */ + sc->status |= ATH_STAT_INVALID; + + sc->iobase = mem; + sc->cachelsz = csz * 4; /* convert to bytes */ + + DBG("ath5k: register base at %p (%08lx)\n", sc->iobase, pdev->membase); + DBG("ath5k: cache line size %d\n", sc->cachelsz); + + /* Set private data */ + pci_set_drvdata(pdev, dev); + dev->netdev->dev = (struct device *)pdev; + + /* Initialize device */ + ret = ath5k_hw_attach(sc, id->driver_data, &sc->ah); + if (ret) + goto err_free_hwinfo; + + /* Finish private driver data initialization */ + ret = ath5k_attach(dev); + if (ret) + goto err_ah; + +#if DBGLVL_MAX + DBG("Atheros AR%s chip found (MAC: 0x%x, PHY: 0x%x)\n", + ath5k_chip_name(AR5K_VERSION_MAC, sc->ah->ah_mac_srev), + sc->ah->ah_mac_srev, sc->ah->ah_phy_revision); + + if (!sc->ah->ah_single_chip) { + /* Single chip radio (!RF5111) */ + if (sc->ah->ah_radio_5ghz_revision && + !sc->ah->ah_radio_2ghz_revision) { + /* No 5GHz support -> report 2GHz radio */ + if (!(sc->ah->ah_capabilities.cap_mode & AR5K_MODE_BIT_11A)) { + DBG("RF%s 2GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + /* No 2GHz support (5110 and some + * 5Ghz only cards) -> report 5Ghz radio */ + } else if (!(sc->ah->ah_capabilities.cap_mode & AR5K_MODE_BIT_11B)) { + DBG("RF%s 5GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + /* Multiband radio */ + } else { + DBG("RF%s multiband radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + } + } + /* Multi chip radio (RF5111 - RF2111) -> + * report both 2GHz/5GHz radios */ + else if (sc->ah->ah_radio_5ghz_revision && + sc->ah->ah_radio_2ghz_revision) { + DBG("RF%s 5GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_5ghz_revision), + sc->ah->ah_radio_5ghz_revision); + DBG("RF%s 2GHz radio found (0x%x)\n", + ath5k_chip_name(AR5K_VERSION_RAD, + sc->ah->ah_radio_2ghz_revision), + sc->ah->ah_radio_2ghz_revision); + } + } +#endif + + /* Ready to go */ + sc->status &= ~ATH_STAT_INVALID; + + return 0; +err_ah: + ath5k_hw_detach(sc->ah); +err_free_hwinfo: + free(sc->hwinfo); +err_free: + net80211_free(dev); +err_map: + iounmap(mem); +err: + return ret; +} + +static void ath5k_remove(struct pci_device *pdev) +{ + struct net80211_device *dev = pci_get_drvdata(pdev); + struct ath5k_softc *sc = dev->priv; + + ath5k_detach(dev); + ath5k_hw_detach(sc->ah); + iounmap(sc->iobase); + free(sc->hwinfo); + net80211_free(dev); +} + + +/***********************\ +* Driver Initialization * +\***********************/ + +static int +ath5k_attach(struct net80211_device *dev) +{ + struct ath5k_softc *sc = dev->priv; + struct ath5k_hw *ah = sc->ah; + int ret; + + /* + * Collect the channel list. The 802.11 layer + * is resposible for filtering this list based + * on settings like the phy mode and regulatory + * domain restrictions. + */ + ret = ath5k_setup_bands(dev); + if (ret) { + DBG("ath5k: can't get channels\n"); + goto err; + } + + /* NB: setup here so ath5k_rate_update is happy */ + if (ah->ah_modes & AR5K_MODE_BIT_11A) + ath5k_setcurmode(sc, AR5K_MODE_11A); + else + ath5k_setcurmode(sc, AR5K_MODE_11B); + + /* + * Allocate tx+rx descriptors and populate the lists. + */ + ret = ath5k_desc_alloc(sc); + if (ret) { + DBG("ath5k: can't allocate descriptors\n"); + goto err; + } + + /* + * Allocate hardware transmit queues. Note that hw functions + * handle reseting these queues at the needed time. + */ + ret = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BE); + if (ret) { + DBG("ath5k: can't setup xmit queue\n"); + goto err_desc; + } + + sc->last_calib_ticks = currticks(); + + ret = ath5k_eeprom_read_mac(ah, sc->hwinfo->hwaddr); + if (ret) { + DBG("ath5k: unable to read address from EEPROM: 0x%04x\n", + sc->pdev->device); + goto err_queues; + } + + memset(sc->bssidmask, 0xff, ETH_ALEN); + ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); + + ret = net80211_register(sc->dev, &ath5k_ops, sc->hwinfo); + if (ret) { + DBG("ath5k: can't register ieee80211 hw\n"); + goto err_queues; + } + + return 0; +err_queues: + ath5k_txq_release(sc); +err_desc: + ath5k_desc_free(sc); +err: + return ret; +} + +static void +ath5k_detach(struct net80211_device *dev) +{ + struct ath5k_softc *sc = dev->priv; + + net80211_unregister(dev); + ath5k_desc_free(sc); + ath5k_txq_release(sc); +} + + + + +/********************\ +* Channel/mode setup * +\********************/ + +/* + * Convert IEEE channel number to MHz frequency. + */ +static inline short +ath5k_ieee2mhz(short chan) +{ + if (chan < 14) + return 2407 + 5 * chan; + if (chan == 14) + return 2484; + if (chan < 27) + return 2212 + 20 * chan; + return 5000 + 5 * chan; +} + +static unsigned int +ath5k_copy_channels(struct ath5k_hw *ah, + struct net80211_channel *channels, + unsigned int mode, unsigned int max) +{ + unsigned int i, count, size, chfreq, freq, ch; + + if (!(ah->ah_modes & (1 << mode))) + return 0; + + switch (mode) { + case AR5K_MODE_11A: + case AR5K_MODE_11A_TURBO: + /* 1..220, but 2GHz frequencies are filtered by check_channel */ + size = 220; + chfreq = CHANNEL_5GHZ; + break; + case AR5K_MODE_11B: + case AR5K_MODE_11G: + case AR5K_MODE_11G_TURBO: + size = 26; + chfreq = CHANNEL_2GHZ; + break; + default: + return 0; + } + + for (i = 0, count = 0; i < size && max > 0; i++) { + ch = i + 1 ; + freq = ath5k_ieee2mhz(ch); + + /* Check if channel is supported by the chipset */ + if (!ath5k_channel_ok(ah, freq, chfreq)) + continue; + + /* Write channel info and increment counter */ + channels[count].center_freq = freq; + channels[count].maxpower = 0; /* use regulatory */ + channels[count].band = (chfreq == CHANNEL_2GHZ) ? + NET80211_BAND_2GHZ : NET80211_BAND_5GHZ; + switch (mode) { + case AR5K_MODE_11A: + case AR5K_MODE_11G: + channels[count].hw_value = chfreq | CHANNEL_OFDM; + break; + case AR5K_MODE_11A_TURBO: + case AR5K_MODE_11G_TURBO: + channels[count].hw_value = chfreq | + CHANNEL_OFDM | CHANNEL_TURBO; + break; + case AR5K_MODE_11B: + channels[count].hw_value = CHANNEL_B; + } + + count++; + max--; + } + + return count; +} + +static int +ath5k_setup_bands(struct net80211_device *dev) +{ + struct ath5k_softc *sc = dev->priv; + struct ath5k_hw *ah = sc->ah; + int max_c, count_c = 0; + int i; + int band; + + max_c = sizeof(sc->hwinfo->channels) / sizeof(sc->hwinfo->channels[0]); + + /* 2GHz band */ + if (sc->ah->ah_capabilities.cap_mode & AR5K_MODE_BIT_11G) { + /* G mode */ + band = NET80211_BAND_2GHZ; + sc->hwinfo->bands = NET80211_BAND_BIT_2GHZ; + sc->hwinfo->modes = (NET80211_MODE_G | NET80211_MODE_B); + + for (i = 0; i < 12; i++) + sc->hwinfo->rates[band][i] = ath5k_rates[i].bitrate; + sc->hwinfo->nr_rates[band] = 12; + + sc->hwinfo->nr_channels = + ath5k_copy_channels(ah, sc->hwinfo->channels, + AR5K_MODE_11G, max_c); + count_c = sc->hwinfo->nr_channels; + max_c -= count_c; + } else if (sc->ah->ah_capabilities.cap_mode & AR5K_MODE_BIT_11B) { + /* B mode */ + band = NET80211_BAND_2GHZ; + sc->hwinfo->bands = NET80211_BAND_BIT_2GHZ; + sc->hwinfo->modes = NET80211_MODE_B; + + for (i = 0; i < 4; i++) + sc->hwinfo->rates[band][i] = ath5k_rates[i].bitrate; + sc->hwinfo->nr_rates[band] = 4; + + sc->hwinfo->nr_channels = + ath5k_copy_channels(ah, sc->hwinfo->channels, + AR5K_MODE_11B, max_c); + count_c = sc->hwinfo->nr_channels; + max_c -= count_c; + } + + /* 5GHz band, A mode */ + if (sc->ah->ah_capabilities.cap_mode & AR5K_MODE_BIT_11A) { + band = NET80211_BAND_5GHZ; + sc->hwinfo->bands |= NET80211_BAND_BIT_5GHZ; + sc->hwinfo->modes |= NET80211_MODE_A; + + for (i = 0; i < 8; i++) + sc->hwinfo->rates[band][i] = ath5k_rates[i+4].bitrate; + sc->hwinfo->nr_rates[band] = 8; + + sc->hwinfo->nr_channels = + ath5k_copy_channels(ah, sc->hwinfo->channels, + AR5K_MODE_11B, max_c); + count_c = sc->hwinfo->nr_channels; + max_c -= count_c; + } + + return 0; +} + +/* + * Set/change channels. If the channel is really being changed, + * it's done by reseting the chip. To accomplish this we must + * first cleanup any pending DMA, then restart stuff after a la + * ath5k_init. + */ +static int +ath5k_chan_set(struct ath5k_softc *sc, struct net80211_channel *chan) +{ + if (chan->center_freq != sc->curchan->center_freq || + chan->hw_value != sc->curchan->hw_value) { + /* + * To switch channels clear any pending DMA operations; + * wait long enough for the RX fifo to drain, reset the + * hardware at the new frequency, and then re-enable + * the relevant bits of the h/w. + */ + DBG2("ath5k: resetting for channel change (%d -> %d MHz)\n", + sc->curchan->center_freq, chan->center_freq); + return ath5k_reset(sc, chan); + } + + return 0; +} + +static void +ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode) +{ + sc->curmode = mode; + + if (mode == AR5K_MODE_11A) { + sc->curband = NET80211_BAND_5GHZ; + } else { + sc->curband = NET80211_BAND_2GHZ; + } +} + +static void +ath5k_mode_setup(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + u32 rfilt; + + /* configure rx filter */ + rfilt = sc->filter_flags; + ath5k_hw_set_rx_filter(ah, rfilt); + + if (ath5k_hw_hasbssidmask(ah)) + ath5k_hw_set_bssid_mask(ah, sc->bssidmask); + + /* configure operational mode */ + ath5k_hw_set_opmode(ah); + + ath5k_hw_set_mcast_filter(ah, 0, 0); +} + +static inline int +ath5k_hw_rix_to_bitrate(int hw_rix) +{ + int i; + + for (i = 0; i < ATH5K_NR_RATES; i++) { + if (ath5k_rates[i].hw_code == hw_rix) + return ath5k_rates[i].bitrate; + } + + DBG("ath5k: invalid rix %02x\n", hw_rix); + return 10; /* use lowest rate */ +} + +int ath5k_bitrate_to_hw_rix(int bitrate) +{ + int i; + + for (i = 0; i < ATH5K_NR_RATES; i++) { + if (ath5k_rates[i].bitrate == bitrate) + return ath5k_rates[i].hw_code; + } + + DBG("ath5k: invalid bitrate %d\n", bitrate); + return ATH5K_RATE_CODE_1M; /* use lowest rate */ +} + +/***************\ +* Buffers setup * +\***************/ + +static struct io_buffer * +ath5k_rx_iob_alloc(struct ath5k_softc *sc, u32 *iob_addr) +{ + struct io_buffer *iob; + unsigned int off; + + /* + * Allocate buffer with headroom_needed space for the + * fake physical layer header at the start. + */ + iob = alloc_iob(sc->rxbufsize + sc->cachelsz - 1); + + if (!iob) { + DBG("ath5k: can't alloc iobuf of size %d\n", + sc->rxbufsize + sc->cachelsz - 1); + return NULL; + } + + *iob_addr = virt_to_bus(iob->data); + + /* + * Cache-line-align. This is important (for the + * 5210 at least) as not doing so causes bogus data + * in rx'd frames. + */ + off = *iob_addr % sc->cachelsz; + if (off != 0) { + iob_reserve(iob, sc->cachelsz - off); + *iob_addr += sc->cachelsz - off; + } + + return iob; +} + +static int +ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) +{ + struct ath5k_hw *ah = sc->ah; + struct io_buffer *iob = bf->iob; + struct ath5k_desc *ds; + + if (!iob) { + iob = ath5k_rx_iob_alloc(sc, &bf->iobaddr); + if (!iob) + return -ENOMEM; + bf->iob = iob; + } + + /* + * Setup descriptors. For receive we always terminate + * the descriptor list with a self-linked entry so we'll + * not get overrun under high load (as can happen with a + * 5212 when ANI processing enables PHY error frames). + * + * To insure the last descriptor is self-linked we create + * each descriptor as self-linked and add it to the end. As + * each additional descriptor is added the previous self-linked + * entry is ``fixed'' naturally. This should be safe even + * if DMA is happening. When processing RX interrupts we + * never remove/process the last, self-linked, entry on the + * descriptor list. This insures the hardware always has + * someplace to write a new frame. + */ + ds = bf->desc; + ds->ds_link = bf->daddr; /* link to self */ + ds->ds_data = bf->iobaddr; + if (ah->ah_setup_rx_desc(ah, ds, + iob_tailroom(iob), /* buffer size */ + 0) != 0) { + DBG("ath5k: error setting up RX descriptor for %d bytes\n", iob_tailroom(iob)); + return -EINVAL; + } + + if (sc->rxlink != NULL) + *sc->rxlink = bf->daddr; + sc->rxlink = &ds->ds_link; + return 0; +} + +static int +ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) +{ + struct ath5k_hw *ah = sc->ah; + struct ath5k_txq *txq = &sc->txq; + struct ath5k_desc *ds = bf->desc; + struct io_buffer *iob = bf->iob; + unsigned int pktlen, flags; + int ret; + u16 duration = 0; + u16 cts_rate = 0; + + flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK; + bf->iobaddr = virt_to_bus(iob->data); + pktlen = iob_len(iob); + + /* FIXME: If we are in g mode and rate is a CCK rate + * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta + * from tx power (value is in dB units already) */ + if (sc->dev->phy_flags & NET80211_PHY_USE_PROTECTION) { + struct net80211_device *dev = sc->dev; + + flags |= AR5K_TXDESC_CTSENA; + cts_rate = sc->hw_rtscts_rate; + duration = net80211_cts_duration(dev, pktlen); + } + ret = ah->ah_setup_tx_desc(ah, ds, pktlen, + IEEE80211_TYP_FRAME_HEADER_LEN, + AR5K_PKT_TYPE_NORMAL, sc->power_level * 2, + sc->hw_rate, ATH5K_RETRIES, + AR5K_TXKEYIX_INVALID, 0, flags, + cts_rate, duration); + if (ret) + return ret; + + ds->ds_link = 0; + ds->ds_data = bf->iobaddr; + + list_add_tail(&bf->list, &txq->q); + if (txq->link == NULL) /* is this first packet? */ + ath5k_hw_set_txdp(ah, txq->qnum, bf->daddr); + else /* no, so only link it */ + *txq->link = bf->daddr; + + txq->link = &ds->ds_link; + ath5k_hw_start_tx_dma(ah, txq->qnum); + mb(); + + return 0; +} + +/*******************\ +* Descriptors setup * +\*******************/ + +static int +ath5k_desc_alloc(struct ath5k_softc *sc) +{ + struct ath5k_desc *ds; + struct ath5k_buf *bf; + u32 da; + unsigned int i; + int ret; + + /* allocate descriptors */ + sc->desc_len = sizeof(struct ath5k_desc) * (ATH_TXBUF + ATH_RXBUF + 1); + sc->desc = malloc_dma(sc->desc_len, ATH5K_DESC_ALIGN); + if (sc->desc == NULL) { + DBG("ath5k: can't allocate descriptors\n"); + ret = -ENOMEM; + goto err; + } + memset(sc->desc, 0, sc->desc_len); + sc->desc_daddr = virt_to_bus(sc->desc); + + ds = sc->desc; + da = sc->desc_daddr; + + bf = calloc(ATH_TXBUF + ATH_RXBUF + 1, sizeof(struct ath5k_buf)); + if (bf == NULL) { + DBG("ath5k: can't allocate buffer pointers\n"); + ret = -ENOMEM; + goto err_free; + } + sc->bufptr = bf; + + INIT_LIST_HEAD(&sc->rxbuf); + for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) { + bf->desc = ds; + bf->daddr = da; + list_add_tail(&bf->list, &sc->rxbuf); + } + + INIT_LIST_HEAD(&sc->txbuf); + sc->txbuf_len = ATH_TXBUF; + for (i = 0; i < ATH_TXBUF; i++, bf++, ds++, da += sizeof(*ds)) { + bf->desc = ds; + bf->daddr = da; + list_add_tail(&bf->list, &sc->txbuf); + } + + return 0; + +err_free: + free_dma(sc->desc, sc->desc_len); +err: + sc->desc = NULL; + return ret; +} + +static void +ath5k_desc_free(struct ath5k_softc *sc) +{ + struct ath5k_buf *bf; + + list_for_each_entry(bf, &sc->txbuf, list) + ath5k_txbuf_free(sc, bf); + list_for_each_entry(bf, &sc->rxbuf, list) + ath5k_rxbuf_free(sc, bf); + + /* Free memory associated with all descriptors */ + free_dma(sc->desc, sc->desc_len); + + free(sc->bufptr); + sc->bufptr = NULL; +} + + + + + +/**************\ +* Queues setup * +\**************/ + +static int +ath5k_txq_setup(struct ath5k_softc *sc, int qtype, int subtype) +{ + struct ath5k_hw *ah = sc->ah; + struct ath5k_txq *txq; + struct ath5k_txq_info qi = { + .tqi_subtype = subtype, + .tqi_aifs = AR5K_TXQ_USEDEFAULT, + .tqi_cw_min = AR5K_TXQ_USEDEFAULT, + .tqi_cw_max = AR5K_TXQ_USEDEFAULT + }; + int qnum; + + /* + * Enable interrupts only for EOL and DESC conditions. + * We mark tx descriptors to receive a DESC interrupt + * when a tx queue gets deep; otherwise waiting for the + * EOL to reap descriptors. Note that this is done to + * reduce interrupt load and this only defers reaping + * descriptors, never transmitting frames. Aside from + * reducing interrupts this also permits more concurrency. + * The only potential downside is if the tx queue backs + * up in which case the top half of the kernel may backup + * due to a lack of tx descriptors. + */ + qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE | + AR5K_TXQ_FLAG_TXDESCINT_ENABLE; + qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi); + if (qnum < 0) { + DBG("ath5k: can't set up a TX queue\n"); + return -EIO; + } + + txq = &sc->txq; + if (!txq->setup) { + txq->qnum = qnum; + txq->link = NULL; + INIT_LIST_HEAD(&txq->q); + txq->setup = 1; + } + return 0; +} + +static void +ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq) +{ + struct ath5k_buf *bf, *bf0; + + list_for_each_entry_safe(bf, bf0, &txq->q, list) { + ath5k_txbuf_free(sc, bf); + + list_del(&bf->list); + list_add_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + } + txq->link = NULL; +} + +/* + * Drain the transmit queues and reclaim resources. + */ +static void +ath5k_txq_cleanup(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + + if (!(sc->status & ATH_STAT_INVALID)) { + /* don't touch the hardware if marked invalid */ + if (sc->txq.setup) { + ath5k_hw_stop_tx_dma(ah, sc->txq.qnum); + DBG("ath5k: txq [%d] %x, link %p\n", + sc->txq.qnum, + ath5k_hw_get_txdp(ah, sc->txq.qnum), + sc->txq.link); + } + } + + if (sc->txq.setup) + ath5k_txq_drainq(sc, &sc->txq); +} + +static void +ath5k_txq_release(struct ath5k_softc *sc) +{ + if (sc->txq.setup) { + ath5k_hw_release_tx_queue(sc->ah); + sc->txq.setup = 0; + } +} + + + + +/*************\ +* RX Handling * +\*************/ + +/* + * Enable the receive h/w following a reset. + */ +static int +ath5k_rx_start(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + struct ath5k_buf *bf; + int ret; + + sc->rxbufsize = IEEE80211_MAX_LEN; + if (sc->rxbufsize % sc->cachelsz != 0) + sc->rxbufsize += sc->cachelsz - (sc->rxbufsize % sc->cachelsz); + + sc->rxlink = NULL; + + list_for_each_entry(bf, &sc->rxbuf, list) { + ret = ath5k_rxbuf_setup(sc, bf); + if (ret != 0) + return ret; + } + + bf = list_entry(sc->rxbuf.next, struct ath5k_buf, list); + + ath5k_hw_set_rxdp(ah, bf->daddr); + ath5k_hw_start_rx_dma(ah); /* enable recv descriptors */ + ath5k_mode_setup(sc); /* set filters, etc. */ + ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */ + + return 0; +} + +/* + * Disable the receive h/w in preparation for a reset. + */ +static void +ath5k_rx_stop(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + + ath5k_hw_stop_rx_pcu(ah); /* disable PCU */ + ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */ + ath5k_hw_stop_rx_dma(ah); /* disable DMA engine */ + + sc->rxlink = NULL; /* just in case */ +} + +static void +ath5k_handle_rx(struct ath5k_softc *sc) +{ + struct ath5k_rx_status rs; + struct io_buffer *iob, *next_iob; + u32 next_iob_addr; + struct ath5k_buf *bf, *bf_last; + struct ath5k_desc *ds; + int ret; + + memset(&rs, 0, sizeof(rs)); + + if (list_empty(&sc->rxbuf)) { + DBG("ath5k: empty rx buf pool\n"); + return; + } + + bf_last = list_entry(sc->rxbuf.prev, struct ath5k_buf, list); + + do { + bf = list_entry(sc->rxbuf.next, struct ath5k_buf, list); + assert(bf->iob != NULL); + iob = bf->iob; + ds = bf->desc; + + /* + * last buffer must not be freed to ensure proper hardware + * function. When the hardware finishes also a packet next to + * it, we are sure, it doesn't use it anymore and we can go on. + */ + if (bf_last == bf) + bf->flags |= 1; + if (bf->flags) { + struct ath5k_buf *bf_next = list_entry(bf->list.next, + struct ath5k_buf, list); + ret = sc->ah->ah_proc_rx_desc(sc->ah, bf_next->desc, + &rs); + if (ret) + break; + bf->flags &= ~1; + /* skip the overwritten one (even status is martian) */ + goto next; + } + + ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs); + if (ret) { + if (ret != -EINPROGRESS) { + DBG("ath5k: error in processing rx desc: %s\n", + strerror(ret)); + net80211_rx_err(sc->dev, NULL, -ret); + } else { + /* normal return, reached end of + available descriptors */ + } + return; + } + + if (rs.rs_more) { + DBG("ath5k: unsupported fragmented rx\n"); + goto next; + } + + if (rs.rs_status) { + if (rs.rs_status & AR5K_RXERR_PHY) { + DBG("ath5k: rx PHY error\n"); + goto next; + } + if (rs.rs_status & AR5K_RXERR_CRC) { + net80211_rx_err(sc->dev, NULL, EIO); + goto next; + } + if (rs.rs_status & AR5K_RXERR_DECRYPT) { + /* + * Decrypt error. If the error occurred + * because there was no hardware key, then + * let the frame through so the upper layers + * can process it. This is necessary for 5210 + * parts which have no way to setup a ``clear'' + * key cache entry. + * + * XXX do key cache faulting + */ + if (rs.rs_keyix == AR5K_RXKEYIX_INVALID && + !(rs.rs_status & AR5K_RXERR_CRC)) + goto accept; + } + + /* any other error, unhandled */ + DBG("ath5k: packet rx status %x\n", rs.rs_status); + goto next; + } +accept: + next_iob = ath5k_rx_iob_alloc(sc, &next_iob_addr); + + /* + * If we can't replace bf->iob with a new iob under memory + * pressure, just skip this packet + */ + if (!next_iob) { + DBG("ath5k: dropping packet under memory pressure\n"); + goto next; + } + + iob_put(iob, rs.rs_datalen); + + /* The MAC header is padded to have 32-bit boundary if the + * packet payload is non-zero. However, gPXE only + * supports standard 802.11 packets with 24-byte + * header, so no padding correction should be needed. + */ + + DBG2("ath5k: rx %d bytes, signal %d\n", rs.rs_datalen, + rs.rs_rssi); + + net80211_rx(sc->dev, iob, rs.rs_rssi, + ath5k_hw_rix_to_bitrate(rs.rs_rate)); + + bf->iob = next_iob; + bf->iobaddr = next_iob_addr; +next: + list_del(&bf->list); + list_add_tail(&bf->list, &sc->rxbuf); + } while (ath5k_rxbuf_setup(sc, bf) == 0); +} + + + + +/*************\ +* TX Handling * +\*************/ + +static void +ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) +{ + struct ath5k_tx_status ts; + struct ath5k_buf *bf, *bf0; + struct ath5k_desc *ds; + struct io_buffer *iob; + int ret; + + memset(&ts, 0, sizeof(ts)); + + list_for_each_entry_safe(bf, bf0, &txq->q, list) { + ds = bf->desc; + + ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts); + if (ret) { + if (ret != -EINPROGRESS) { + DBG("ath5k: error in processing tx desc: %s\n", + strerror(ret)); + } else { + /* normal return, reached end of tx completions */ + } + break; + } + + iob = bf->iob; + bf->iob = NULL; + + DBG2("ath5k: tx %d bytes complete, %d retries\n", + iob_len(iob), ts.ts_retry[0]); + + net80211_tx_complete(sc->dev, iob, ts.ts_retry[0], + ts.ts_status ? EIO : 0); + + list_del(&bf->list); + list_add_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + } + + if (list_empty(&txq->q)) + txq->link = NULL; +} + +static void +ath5k_handle_tx(struct ath5k_softc *sc) +{ + ath5k_tx_processq(sc, &sc->txq); +} + + +/********************\ +* Interrupt handling * +\********************/ + +static void +ath5k_irq(struct net80211_device *dev, int enable) +{ + struct ath5k_softc *sc = dev->priv; + struct ath5k_hw *ah = sc->ah; + + sc->irq_ena = enable; + ah->ah_ier = enable ? AR5K_IER_ENABLE : AR5K_IER_DISABLE; + + ath5k_hw_reg_write(ah, ah->ah_ier, AR5K_IER); + ath5k_hw_set_imr(ah, sc->imask); +} + +static int +ath5k_init(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + int ret, i; + + /* + * Stop anything previously setup. This is safe + * no matter this is the first time through or not. + */ + ath5k_stop_hw(sc); + + /* + * The basic interface to setting the hardware in a good + * state is ``reset''. On return the hardware is known to + * be powered up and with interrupts disabled. This must + * be followed by initialization of the appropriate bits + * and then setup of the interrupt mask. + */ + sc->curchan = sc->dev->channels + sc->dev->channel; + sc->curband = sc->curchan->band; + sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | + AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | + AR5K_INT_FATAL | AR5K_INT_GLOBAL; + ret = ath5k_reset(sc, NULL); + if (ret) + goto done; + + /* + * Reset the key cache since some parts do not reset the + * contents on initial power up or resume from suspend. + */ + for (i = 0; i < AR5K_KEYTABLE_SIZE; i++) + ath5k_hw_reset_key(ah, i); + + /* Set ack to be sent at low bit-rates */ + ath5k_hw_set_ack_bitrate_high(ah, 0); + + ret = 0; +done: + mb(); + return ret; +} + +static int +ath5k_stop_hw(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + + /* + * Shutdown the hardware and driver: + * stop output from above + * disable interrupts + * turn off timers + * turn off the radio + * clear transmit machinery + * clear receive machinery + * drain and release tx queues + * reclaim beacon resources + * power down hardware + * + * Note that some of this work is not possible if the + * hardware is gone (invalid). + */ + + if (!(sc->status & ATH_STAT_INVALID)) { + ath5k_hw_set_imr(ah, 0); + } + ath5k_txq_cleanup(sc); + if (!(sc->status & ATH_STAT_INVALID)) { + ath5k_rx_stop(sc); + ath5k_hw_phy_disable(ah); + } else + sc->rxlink = NULL; + + return 0; +} + +static void +ath5k_poll(struct net80211_device *dev) +{ + struct ath5k_softc *sc = dev->priv; + struct ath5k_hw *ah = sc->ah; + enum ath5k_int status; + unsigned int counter = 1000; + + if (currticks() - sc->last_calib_ticks > + ATH5K_CALIB_INTERVAL * ticks_per_sec()) { + ath5k_calibrate(sc); + sc->last_calib_ticks = currticks(); + } + + if ((sc->status & ATH_STAT_INVALID) || + (sc->irq_ena && !ath5k_hw_is_intr_pending(ah))) + return; + + do { + ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */ + DBGP("ath5k: status %#x/%#x\n", status, sc->imask); + if (status & AR5K_INT_FATAL) { + /* + * Fatal errors are unrecoverable. + * Typically these are caused by DMA errors. + */ + DBG("ath5k: fatal error, resetting\n"); + ath5k_reset_wake(sc); + } else if (status & AR5K_INT_RXORN) { + DBG("ath5k: rx overrun, resetting\n"); + ath5k_reset_wake(sc); + } else { + if (status & AR5K_INT_RXEOL) { + /* + * NB: the hardware should re-read the link when + * RXE bit is written, but it doesn't work at + * least on older hardware revs. + */ + DBG("ath5k: rx EOL\n"); + sc->rxlink = NULL; + } + if (status & AR5K_INT_TXURN) { + /* bump tx trigger level */ + DBG("ath5k: tx underrun\n"); + ath5k_hw_update_tx_triglevel(ah, 1); + } + if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR)) + ath5k_handle_rx(sc); + if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC + | AR5K_INT_TXERR | AR5K_INT_TXEOL)) + ath5k_handle_tx(sc); + } + } while (ath5k_hw_is_intr_pending(ah) && counter-- > 0); + + if (!counter) + DBG("ath5k: too many interrupts, giving up for now\n"); +} + +/* + * Periodically recalibrate the PHY to account + * for temperature/environment changes. + */ +static void +ath5k_calibrate(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + + if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) { + /* + * Rfgain is out of bounds, reset the chip + * to load new gain values. + */ + DBG("ath5k: resetting for calibration\n"); + ath5k_reset_wake(sc); + } + if (ath5k_hw_phy_calibrate(ah, sc->curchan)) + DBG("ath5k: calibration of channel %d failed\n", + sc->curchan->channel_nr); +} + + +/********************\ +* Net80211 functions * +\********************/ + +static int +ath5k_tx(struct net80211_device *dev, struct io_buffer *iob) +{ + struct ath5k_softc *sc = dev->priv; + struct ath5k_buf *bf; + int rc; + + /* + * The hardware expects the header padded to 4 byte boundaries. + * gPXE only ever sends 24-byte headers, so no action necessary. + */ + + if (list_empty(&sc->txbuf)) { + DBG("ath5k: dropping packet because no tx bufs available\n"); + return -ENOBUFS; + } + + bf = list_entry(sc->txbuf.next, struct ath5k_buf, list); + list_del(&bf->list); + sc->txbuf_len--; + + bf->iob = iob; + + if ((rc = ath5k_txbuf_setup(sc, bf)) != 0) { + bf->iob = NULL; + list_add_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + return rc; + } + return 0; +} + +/* + * Reset the hardware. If chan is not NULL, then also pause rx/tx + * and change to the given channel. + */ +static int +ath5k_reset(struct ath5k_softc *sc, struct net80211_channel *chan) +{ + struct ath5k_hw *ah = sc->ah; + int ret; + + if (chan) { + ath5k_hw_set_imr(ah, 0); + ath5k_txq_cleanup(sc); + ath5k_rx_stop(sc); + + sc->curchan = chan; + sc->curband = chan->band; + } + + ret = ath5k_hw_reset(ah, sc->curchan, 1); + if (ret) { + DBG("ath5k: can't reset hardware: %s\n", strerror(ret)); + return ret; + } + + ret = ath5k_rx_start(sc); + if (ret) { + DBG("ath5k: can't start rx logic: %s\n", strerror(ret)); + return ret; + } + + /* + * Change channels and update the h/w rate map if we're switching; + * e.g. 11a to 11b/g. + * + * We may be doing a reset in response to an ioctl that changes the + * channel so update any state that might change as a result. + * + * XXX needed? + */ +/* ath5k_chan_change(sc, c); */ + + /* Reenable interrupts if necessary */ + ath5k_irq(sc->dev, sc->irq_ena); + + return 0; +} + +static int ath5k_reset_wake(struct ath5k_softc *sc) +{ + return ath5k_reset(sc, sc->curchan); +} + +static int ath5k_start(struct net80211_device *dev) +{ + struct ath5k_softc *sc = dev->priv; + int ret; + + if ((ret = ath5k_init(sc)) != 0) + return ret; + + sc->assoc = 0; + ath5k_configure_filter(sc); + ath5k_hw_set_lladdr(sc->ah, dev->netdev->ll_addr); + + return 0; +} + +static void ath5k_stop(struct net80211_device *dev) +{ + struct ath5k_softc *sc = dev->priv; + u8 mac[ETH_ALEN] = {}; + + ath5k_hw_set_lladdr(sc->ah, mac); + + ath5k_stop_hw(sc); +} + +static int +ath5k_config(struct net80211_device *dev, int changed) +{ + struct ath5k_softc *sc = dev->priv; + struct ath5k_hw *ah = sc->ah; + struct net80211_channel *chan = &dev->channels[dev->channel]; + int ret; + + if (changed & NET80211_CFG_CHANNEL) { + sc->power_level = chan->maxpower; + if ((ret = ath5k_chan_set(sc, chan)) != 0) + return ret; + } + + if ((changed & NET80211_CFG_RATE) || + (changed & NET80211_CFG_PHY_PARAMS)) { + int spmbl = ATH5K_SPMBL_NO; + u16 rate = dev->rates[dev->rate]; + u16 slowrate = dev->rates[dev->rtscts_rate]; + int i; + + if (dev->phy_flags & NET80211_PHY_USE_SHORT_PREAMBLE) + spmbl = ATH5K_SPMBL_YES; + + for (i = 0; i < ATH5K_NR_RATES; i++) { + if (ath5k_rates[i].bitrate == rate && + (ath5k_rates[i].short_pmbl & spmbl)) + sc->hw_rate = ath5k_rates[i].hw_code; + + if (ath5k_rates[i].bitrate == slowrate && + (ath5k_rates[i].short_pmbl & spmbl)) + sc->hw_rtscts_rate = ath5k_rates[i].hw_code; + } + } + + if (changed & NET80211_CFG_ASSOC) { + sc->assoc = !!(dev->state & NET80211_ASSOCIATED); + if (sc->assoc) { + memcpy(ah->ah_bssid, dev->bssid, ETH_ALEN); + } else { + memset(ah->ah_bssid, 0xff, ETH_ALEN); + } + ath5k_hw_set_associd(ah, ah->ah_bssid, 0); + } + + return 0; +} + +/* + * o always accept unicast, broadcast, and multicast traffic + * o multicast traffic for all BSSIDs will be enabled if mac80211 + * says it should be + * o maintain current state of phy ofdm or phy cck error reception. + * If the hardware detects any of these type of errors then + * ath5k_hw_get_rx_filter() will pass to us the respective + * hardware filters to be able to receive these type of frames. + * o probe request frames are accepted only when operating in + * hostap, adhoc, or monitor modes + * o enable promiscuous mode according to the interface state + * o accept beacons: + * - when operating in adhoc mode so the 802.11 layer creates + * node table entries for peers, + * - when operating in station mode for collecting rssi data when + * the station is otherwise quiet, or + * - when scanning + */ +static void ath5k_configure_filter(struct ath5k_softc *sc) +{ + struct ath5k_hw *ah = sc->ah; + u32 mfilt[2], rfilt; + + /* Enable all multicast */ + mfilt[0] = ~0; + mfilt[1] = ~0; + + /* Enable data frames and beacons */ + rfilt = (AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST | + AR5K_RX_FILTER_MCAST | AR5K_RX_FILTER_BEACON); + + /* Set filters */ + ath5k_hw_set_rx_filter(ah, rfilt); + + /* Set multicast bits */ + ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]); + + /* Set the cached hw filter flags, this will alter actually + * be set in HW */ + sc->filter_flags = rfilt; +} -- cgit v1.2.3