Author: bschmidt
Date: Thu May 10 17:41:16 2012
New Revision: 235233
URL: http://svn.freebsd.org/changeset/base/235233

Log:
  Add support for Ralink RT2800/RT3000 chipsets.
  
  Thanks to ray@, Sevan and Sergey Dyatko for feedback and testing!
  
  Obtained from:        OpenBSD
  MFC after:    3 weeks

Added:
  head/sys/dev/ral/rt2860.c   (contents, props changed)
  head/sys/dev/ral/rt2860reg.h   (contents, props changed)
  head/sys/dev/ral/rt2860var.h   (contents, props changed)
Modified:
  head/sys/conf/files
  head/sys/dev/ral/if_ral_pci.c
  head/sys/modules/ral/Makefile

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files Thu May 10 15:23:20 2012        (r235232)
+++ head/sys/conf/files Thu May 10 17:41:16 2012        (r235233)
@@ -1759,6 +1759,7 @@ dev/puc/pucdata.c         optional puc pci
 dev/quicc/quicc_core.c         optional quicc
 dev/ral/rt2560.c               optional ral
 dev/ral/rt2661.c               optional ral
+dev/ral/rt2860.c               optional ral
 dev/ral/if_ral_pci.c           optional ral pci
 rt2561fw.c                     optional rt2561fw | ralfw               \
        compile-with    "${AWK} -f $S/tools/fw_stub.awk rt2561.fw:rt2561fw 
-mrt2561 -c${.TARGET}" \

Modified: head/sys/dev/ral/if_ral_pci.c
==============================================================================
--- head/sys/dev/ral/if_ral_pci.c       Thu May 10 15:23:20 2012        
(r235232)
+++ head/sys/dev/ral/if_ral_pci.c       Thu May 10 17:41:16 2012        
(r235233)
@@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/ral/rt2560var.h>
 #include <dev/ral/rt2661var.h>
+#include <dev/ral/rt2860var.h>
 
 MODULE_DEPEND(ral, pci, 1, 1, 1);
 MODULE_DEPEND(ral, firmware, 1, 1, 1);
@@ -70,11 +71,37 @@ struct ral_pci_ident {
 };
 
 static const struct ral_pci_ident ral_pci_ids[] = {
+       { 0x1432, 0x7708, "Edimax RT2860" },
+       { 0x1432, 0x7711, "Edimax RT3591" },
+       { 0x1432, 0x7722, "Edimax RT3591" },
+       { 0x1432, 0x7727, "Edimax RT2860" },
+       { 0x1432, 0x7728, "Edimax RT2860" },
+       { 0x1432, 0x7738, "Edimax RT2860" },
+       { 0x1432, 0x7748, "Edimax RT2860" },
+       { 0x1432, 0x7758, "Edimax RT2860" },
+       { 0x1432, 0x7768, "Edimax RT2860" },
+       { 0x1462, 0x891a, "MSI RT3090" },
        { 0x1814, 0x0201, "Ralink Technology RT2560" },
        { 0x1814, 0x0301, "Ralink Technology RT2561S" },
        { 0x1814, 0x0302, "Ralink Technology RT2561" },
        { 0x1814, 0x0401, "Ralink Technology RT2661" },
-
+       { 0x1814, 0x0601, "Ralink Technology RT2860" },
+       { 0x1814, 0x0681, "Ralink Technology RT2890" },
+       { 0x1814, 0x0701, "Ralink Technology RT2760" },
+       { 0x1814, 0x0781, "Ralink Technology RT2790" },
+       { 0x1814, 0x3060, "Ralink Technology RT3060" },
+       { 0x1814, 0x3062, "Ralink Technology RT3062" },
+       { 0x1814, 0x3090, "Ralink Technology RT3090" },
+       { 0x1814, 0x3091, "Ralink Technology RT3091" },
+       { 0x1814, 0x3092, "Ralink Technology RT3092" },
+       { 0x1814, 0x3390, "Ralink Technology RT3390" },
+       { 0x1814, 0x3562, "Ralink Technology RT3562" },
+       { 0x1814, 0x3592, "Ralink Technology RT3592" },
+       { 0x1814, 0x3593, "Ralink Technology RT3593" },
+       { 0x1814, 0x5390, "Ralink Technology RT5390" },
+       { 0x1814, 0x539a, "Ralink Technology RT5390" },
+       { 0x1814, 0x539f, "Ralink Technology RT5390" },
+       { 0x1a3b, 0x1059, "AWT RT2890" },
        { 0, 0, NULL }
 };
 
@@ -101,12 +128,20 @@ static struct ral_opns {
        rt2661_suspend,
        rt2661_resume,
        rt2661_intr
+}, ral_rt2860_opns = {
+       rt2860_attach,
+       rt2860_detach,
+       rt2860_shutdown,
+       rt2860_suspend,
+       rt2860_resume,
+       rt2860_intr
 };
 
 struct ral_pci_softc {
        union {
                struct rt2560_softc sc_rt2560;
                struct rt2661_softc sc_rt2661;
+               struct rt2860_softc sc_rt2860;
        } u;
 
        struct ral_opns         *sc_opns;
@@ -180,8 +215,19 @@ ral_pci_attach(device_t dev)
        /* enable bus-mastering */
        pci_enable_busmaster(dev);
 
-       psc->sc_opns = (pci_get_device(dev) == 0x0201) ? &ral_rt2560_opns :
-           &ral_rt2661_opns;
+       switch (pci_get_device(dev)) {
+       case 0x0201:
+               psc->sc_opns = &ral_rt2560_opns;
+               break;
+       case 0x0301:
+       case 0x0302:
+       case 0x0401:
+               psc->sc_opns = &ral_rt2661_opns;
+               break;
+       default:
+               psc->sc_opns = &ral_rt2860_opns;
+               break;
+       }
 
        psc->mem_rid = RAL_PCI_BAR0;
        psc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &psc->mem_rid,

Added: head/sys/dev/ral/rt2860.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/ral/rt2860.c   Thu May 10 17:41:16 2012        (r235233)
@@ -0,0 +1,4103 @@
+/*-
+ * Copyright (c) 2007-2010 Damien Bergamini <damien.bergam...@free.fr>
+ * Copyright (c) 2012 Bernhard Schmidt <bschm...@freebsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $OpenBSD: rt2860.c,v 1.65 2010/10/23 14:24:54 damien Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Ralink Technology RT2860/RT3090/RT3390/RT3562 chipset driver
+ * http://www.ralinktech.com/
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/firmware.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_ratectl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+
+#include <dev/ral/rt2860reg.h>
+#include <dev/ral/rt2860var.h>
+
+#define RAL_DEBUG
+#ifdef RAL_DEBUG
+#define DPRINTF(x)     do { if (sc->sc_debug > 0) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (sc->sc_debug >= (n)) printf x; } while (0)
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n, x)
+#endif
+
+static struct ieee80211vap *rt2860_vap_create(struct ieee80211com *,
+                           const char [IFNAMSIZ], int, enum ieee80211_opmode,
+                           int, const uint8_t [IEEE80211_ADDR_LEN],
+                           const uint8_t [IEEE80211_ADDR_LEN]);
+static void    rt2860_vap_delete(struct ieee80211vap *);
+static void    rt2860_dma_map_addr(void *, bus_dma_segment_t *, int, int);
+static int     rt2860_alloc_tx_ring(struct rt2860_softc *,
+                   struct rt2860_tx_ring *);
+static void    rt2860_reset_tx_ring(struct rt2860_softc *,
+                   struct rt2860_tx_ring *);
+static void    rt2860_free_tx_ring(struct rt2860_softc *,
+                   struct rt2860_tx_ring *);
+static int     rt2860_alloc_tx_pool(struct rt2860_softc *);
+static void    rt2860_free_tx_pool(struct rt2860_softc *);
+static int     rt2860_alloc_rx_ring(struct rt2860_softc *,
+                   struct rt2860_rx_ring *);
+static void    rt2860_reset_rx_ring(struct rt2860_softc *,
+                   struct rt2860_rx_ring *);
+static void    rt2860_free_rx_ring(struct rt2860_softc *,
+                   struct rt2860_rx_ring *);
+static void    rt2860_updatestats(struct rt2860_softc *);
+static void    rt2860_newassoc(struct ieee80211_node *, int);
+static void    rt2860_node_free(struct ieee80211_node *);
+#ifdef IEEE80211_HT
+static int     rt2860_ampdu_rx_start(struct ieee80211com *,
+                   struct ieee80211_node *, uint8_t);
+static void    rt2860_ampdu_rx_stop(struct ieee80211com *,
+                   struct ieee80211_node *, uint8_t);
+#endif
+static int     rt2860_newstate(struct ieee80211vap *, enum ieee80211_state,
+                   int);
+static uint16_t        rt3090_efuse_read_2(struct rt2860_softc *, uint16_t);
+static uint16_t        rt2860_eeprom_read_2(struct rt2860_softc *, uint16_t);
+static void    rt2860_intr_coherent(struct rt2860_softc *);
+static void    rt2860_drain_stats_fifo(struct rt2860_softc *);
+static void    rt2860_tx_intr(struct rt2860_softc *, int);
+static void    rt2860_rx_intr(struct rt2860_softc *);
+static void    rt2860_tbtt_intr(struct rt2860_softc *);
+static void    rt2860_gp_intr(struct rt2860_softc *);
+static int     rt2860_tx(struct rt2860_softc *, struct mbuf *,
+                   struct ieee80211_node *);
+static int     rt2860_raw_xmit(struct ieee80211_node *, struct mbuf *,
+                   const struct ieee80211_bpf_params *);
+static int     rt2860_tx_raw(struct rt2860_softc *, struct mbuf *,
+                   struct ieee80211_node *,
+                   const struct ieee80211_bpf_params *params);
+static void    rt2860_start(struct ifnet *);
+static void    rt2860_start_locked(struct ifnet *);
+static void    rt2860_watchdog(void *);
+static int     rt2860_ioctl(struct ifnet *, u_long, caddr_t);
+static void    rt2860_mcu_bbp_write(struct rt2860_softc *, uint8_t, uint8_t);
+static uint8_t rt2860_mcu_bbp_read(struct rt2860_softc *, uint8_t);
+static void    rt2860_rf_write(struct rt2860_softc *, uint8_t, uint32_t);
+static uint8_t rt3090_rf_read(struct rt2860_softc *, uint8_t);
+static void    rt3090_rf_write(struct rt2860_softc *, uint8_t, uint8_t);
+static int     rt2860_mcu_cmd(struct rt2860_softc *, uint8_t, uint16_t, int);
+static void    rt2860_enable_mrr(struct rt2860_softc *);
+static void    rt2860_set_txpreamble(struct rt2860_softc *);
+static void    rt2860_set_basicrates(struct rt2860_softc *,
+                   const struct ieee80211_rateset *);
+static void    rt2860_scan_start(struct ieee80211com *);
+static void    rt2860_scan_end(struct ieee80211com *);
+static void    rt2860_set_channel(struct ieee80211com *);
+static void    rt2860_select_chan_group(struct rt2860_softc *, int);
+static void    rt2860_set_chan(struct rt2860_softc *, u_int);
+static void    rt3090_set_chan(struct rt2860_softc *, u_int);
+static int     rt3090_rf_init(struct rt2860_softc *);
+static void    rt3090_rf_wakeup(struct rt2860_softc *);
+static int     rt3090_filter_calib(struct rt2860_softc *, uint8_t, uint8_t,
+                   uint8_t *);
+static void    rt3090_rf_setup(struct rt2860_softc *);
+static void    rt2860_set_leds(struct rt2860_softc *, uint16_t);
+static void    rt2860_set_gp_timer(struct rt2860_softc *, int);
+static void    rt2860_set_bssid(struct rt2860_softc *, const uint8_t *);
+static void    rt2860_set_macaddr(struct rt2860_softc *, const uint8_t *);
+static void    rt2860_update_promisc(struct ifnet *);
+static void    rt2860_updateslot(struct ifnet *);
+static void    rt2860_updateprot(struct ifnet *);
+static int     rt2860_updateedca(struct ieee80211com *);
+#ifdef HW_CRYPTO
+static int     rt2860_set_key(struct ieee80211com *, struct ieee80211_node *,
+                   struct ieee80211_key *);
+static void    rt2860_delete_key(struct ieee80211com *,
+                   struct ieee80211_node *, struct ieee80211_key *);
+#endif
+static int8_t  rt2860_rssi2dbm(struct rt2860_softc *, uint8_t, uint8_t);
+static const char *rt2860_get_rf(uint8_t);
+static int     rt2860_read_eeprom(struct rt2860_softc *,
+                   uint8_t macaddr[IEEE80211_ADDR_LEN]);
+static int     rt2860_bbp_init(struct rt2860_softc *);
+static int     rt2860_txrx_enable(struct rt2860_softc *);
+static void    rt2860_init(void *);
+static void    rt2860_init_locked(struct rt2860_softc *);
+static void    rt2860_stop(void *);
+static void    rt2860_stop_locked(struct rt2860_softc *);
+static int     rt2860_load_microcode(struct rt2860_softc *);
+#ifdef NOT_YET
+static void    rt2860_calib(struct rt2860_softc *);
+#endif
+static void    rt3090_set_rx_antenna(struct rt2860_softc *, int);
+static void    rt2860_switch_chan(struct rt2860_softc *,
+                   struct ieee80211_channel *);
+static int     rt2860_setup_beacon(struct rt2860_softc *,
+                   struct ieee80211vap *);
+static void    rt2860_enable_tsf_sync(struct rt2860_softc *);
+
+static const struct {
+       uint32_t        reg;
+       uint32_t        val;
+} rt2860_def_mac[] = {
+       RT2860_DEF_MAC
+};
+
+static const struct {
+       uint8_t reg;
+       uint8_t val;
+} rt2860_def_bbp[] = {
+       RT2860_DEF_BBP
+};
+
+static const struct rfprog {
+       uint8_t         chan;
+       uint32_t        r1, r2, r3, r4;
+} rt2860_rf2850[] = {
+       RT2860_RF2850
+};
+
+struct {
+       uint8_t n, r, k;
+} rt3090_freqs[] = {
+       RT3070_RF3052
+};
+
+static const struct {
+       uint8_t reg;
+       uint8_t val;
+}  rt3090_def_rf[] = {
+       RT3070_DEF_RF
+};
+
+int
+rt2860_attach(device_t dev, int id)
+{
+       struct rt2860_softc *sc = device_get_softc(dev);
+       struct ieee80211com *ic;
+       struct ifnet *ifp;
+       uint32_t tmp;
+       int error, ntries, qid;
+       uint8_t bands;
+       uint8_t macaddr[IEEE80211_ADDR_LEN];
+
+       sc->sc_dev = dev;
+       sc->sc_debug = 0;
+
+       ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+       if (ifp == NULL) {
+               device_printf(sc->sc_dev, "can not if_alloc()\n");
+               return ENOMEM;
+       }
+       ic = ifp->if_l2com;
+
+       mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+           MTX_DEF | MTX_RECURSE);
+
+       callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0);
+
+       /* wait for NIC to initialize */
+       for (ntries = 0; ntries < 100; ntries++) {
+               tmp = RAL_READ(sc, RT2860_ASIC_VER_ID);
+               if (tmp != 0 && tmp != 0xffffffff)
+                       break;
+               DELAY(10);
+       }
+       if (ntries == 100) {
+               device_printf(sc->sc_dev,
+                   "timeout waiting for NIC to initialize\n");
+               error = EIO;
+               goto fail1;
+       }
+       sc->mac_ver = tmp >> 16;
+       sc->mac_rev = tmp & 0xffff;
+
+       if (sc->mac_ver != 0x2860 &&
+           (id == 0x0681 || id == 0x0781 || id == 0x1059))
+               sc->sc_flags |= RT2860_ADVANCED_PS;
+
+       /* retrieve RF rev. no and various other things from EEPROM */
+       rt2860_read_eeprom(sc, macaddr);
+       if (bootverbose) {
+               device_printf(sc->sc_dev, "MAC/BBP RT%X (rev 0x%04X), "
+                   "RF %s (MIMO %dT%dR), address %6D\n",
+                   sc->mac_ver, sc->mac_rev, rt2860_get_rf(sc->rf_rev),
+                   sc->ntxchains, sc->nrxchains, macaddr, ":");
+       }
+
+       /*
+        * Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings.
+        */
+       for (qid = 0; qid < 6; qid++) {
+               if ((error = rt2860_alloc_tx_ring(sc, &sc->txq[qid])) != 0) {
+                       device_printf(sc->sc_dev,
+                           "could not allocate Tx ring %d\n", qid);
+                       goto fail2;
+               }
+       }
+
+       if ((error = rt2860_alloc_rx_ring(sc, &sc->rxq)) != 0) {
+               device_printf(sc->sc_dev, "could not allocate Rx ring\n");
+               goto fail2;
+       }
+
+       if ((error = rt2860_alloc_tx_pool(sc)) != 0) {
+               device_printf(sc->sc_dev, "could not allocate Tx pool\n");
+               goto fail3;
+       }
+
+       /* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */
+       sc->mgtqid = (sc->mac_ver == 0x2860 && sc->mac_rev == 0x0100) ?
+           WME_AC_VO : 5;
+
+       ifp->if_softc = sc;
+       if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+       ifp->if_init = rt2860_init;
+       ifp->if_ioctl = rt2860_ioctl;
+       ifp->if_start = rt2860_start;
+       IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+       ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
+       IFQ_SET_READY(&ifp->if_snd);
+
+       ic->ic_ifp = ifp;
+       ic->ic_opmode = IEEE80211_M_STA;
+       ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+
+       /* set device capabilities */
+       ic->ic_caps =
+                 IEEE80211_C_STA               /* station mode */
+               | IEEE80211_C_IBSS              /* ibss, nee adhoc, mode */
+               | IEEE80211_C_HOSTAP            /* hostap mode */
+               | IEEE80211_C_MONITOR           /* monitor mode */
+               | IEEE80211_C_AHDEMO            /* adhoc demo mode */
+               | IEEE80211_C_WDS               /* 4-address traffic works */
+               | IEEE80211_C_MBSS              /* mesh point link mode */
+               | IEEE80211_C_SHPREAMBLE        /* short preamble supported */
+               | IEEE80211_C_SHSLOT            /* short slot time supported */
+               | IEEE80211_C_WPA               /* capable of WPA1+WPA2 */
+#if 0
+               | IEEE80211_C_BGSCAN            /* capable of bg scanning */
+#endif
+               | IEEE80211_C_WME               /* 802.11e */
+               ;
+
+       bands = 0;
+       setbit(&bands, IEEE80211_MODE_11B);
+       setbit(&bands, IEEE80211_MODE_11G);
+       if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850)
+               setbit(&bands, IEEE80211_MODE_11A);
+       ieee80211_init_channels(ic, NULL, &bands);
+
+       ieee80211_ifattach(ic, macaddr);
+
+       ic->ic_wme.wme_update = rt2860_updateedca;
+       ic->ic_scan_start = rt2860_scan_start;
+       ic->ic_scan_end = rt2860_scan_end;
+       ic->ic_set_channel = rt2860_set_channel;
+       ic->ic_updateslot = rt2860_updateslot;
+       ic->ic_update_promisc = rt2860_update_promisc;
+       ic->ic_raw_xmit = rt2860_raw_xmit;
+       sc->sc_node_free = ic->ic_node_free;
+       ic->ic_node_free = rt2860_node_free;
+       ic->ic_newassoc = rt2860_newassoc;
+
+       ic->ic_vap_create = rt2860_vap_create;
+       ic->ic_vap_delete = rt2860_vap_delete;
+
+       ieee80211_radiotap_attach(ic,
+           &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
+               RT2860_TX_RADIOTAP_PRESENT,
+           &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
+               RT2860_RX_RADIOTAP_PRESENT);
+
+#ifdef RAL_DEBUG
+       SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+           SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+           "debug", CTLFLAG_RW, &sc->sc_debug, 0, "debug msgs");
+#endif
+       if (bootverbose)
+               ieee80211_announce(ic);
+
+       return 0;
+
+fail3: rt2860_free_rx_ring(sc, &sc->rxq);
+fail2: while (--qid >= 0)
+               rt2860_free_tx_ring(sc, &sc->txq[qid]);
+fail1: mtx_destroy(&sc->sc_mtx);
+       if_free(ifp);
+       return error;
+}
+
+int
+rt2860_detach(void *xsc)
+{
+       struct rt2860_softc *sc = xsc;
+       struct ifnet *ifp =  sc->sc_ifp;
+       struct ieee80211com *ic = ifp->if_l2com;
+       int qid;
+
+       RAL_LOCK(sc);
+       rt2860_stop_locked(sc);
+       RAL_UNLOCK(sc);
+
+       ieee80211_ifdetach(ic);
+
+       for (qid = 0; qid < 6; qid++)
+               rt2860_free_tx_ring(sc, &sc->txq[qid]);
+       rt2860_free_rx_ring(sc, &sc->rxq);
+       rt2860_free_tx_pool(sc);
+
+       if_free(ifp);
+
+       mtx_destroy(&sc->sc_mtx);
+
+       return 0;
+}
+
+void
+rt2860_shutdown(void *xsc)
+{
+       struct rt2860_softc *sc = xsc;
+
+       rt2860_stop(sc);
+}
+
+void
+rt2860_suspend(void *xsc)
+{
+       struct rt2860_softc *sc = xsc;
+
+       rt2860_stop(sc);
+}
+
+void
+rt2860_resume(void *xsc)
+{
+       struct rt2860_softc *sc = xsc;
+       struct ifnet *ifp = sc->sc_ifp;
+
+       if (ifp->if_flags & IFF_UP)
+               rt2860_init(sc);
+}
+
+static struct ieee80211vap *
+rt2860_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
+    enum ieee80211_opmode opmode, int flags,
+    const uint8_t bssid[IEEE80211_ADDR_LEN],
+    const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+       struct ifnet *ifp = ic->ic_ifp;
+       struct rt2860_vap *rvp;
+       struct ieee80211vap *vap;
+
+       switch (opmode) {
+       case IEEE80211_M_STA:
+       case IEEE80211_M_IBSS:
+       case IEEE80211_M_AHDEMO:
+       case IEEE80211_M_MONITOR:
+       case IEEE80211_M_HOSTAP:
+       case IEEE80211_M_MBSS:
+               /* XXXRP: TBD */
+               if (!TAILQ_EMPTY(&ic->ic_vaps)) {
+                       if_printf(ifp, "only 1 vap supported\n");
+                       return NULL;
+               }
+               if (opmode == IEEE80211_M_STA)
+                       flags |= IEEE80211_CLONE_NOBEACONS;
+               break;
+       case IEEE80211_M_WDS:
+               if (TAILQ_EMPTY(&ic->ic_vaps) ||
+                   ic->ic_opmode != IEEE80211_M_HOSTAP) {
+                       if_printf(ifp, "wds only supported in ap mode\n");
+                       return NULL;
+               }
+               /*
+                * Silently remove any request for a unique
+                * bssid; WDS vap's always share the local
+                * mac address.
+                */
+               flags &= ~IEEE80211_CLONE_BSSID;
+               break;
+       default:
+               if_printf(ifp, "unknown opmode %d\n", opmode);
+               return NULL;
+       }
+       rvp = malloc(sizeof(struct rt2860_vap), M_80211_VAP, M_NOWAIT | M_ZERO);
+       if (rvp == NULL)
+               return NULL;
+       vap = &rvp->ral_vap;
+       ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
+
+       /* override state transition machine */
+       rvp->ral_newstate = vap->iv_newstate;
+       vap->iv_newstate = rt2860_newstate;
+#if 0
+       vap->iv_update_beacon = rt2860_beacon_update;
+#endif
+
+       /* HW supports up to 255 STAs (0-254) in HostAP and IBSS modes */
+       vap->iv_max_aid = min(IEEE80211_AID_MAX, RT2860_WCID_MAX);
+
+       ieee80211_ratectl_init(vap);
+       /* complete setup */
+       ieee80211_vap_attach(vap, ieee80211_media_change, 
ieee80211_media_status);
+       if (TAILQ_FIRST(&ic->ic_vaps) == vap)
+               ic->ic_opmode = opmode;
+       return vap;
+}
+
+static void
+rt2860_vap_delete(struct ieee80211vap *vap)
+{
+       struct rt2860_vap *rvp = RT2860_VAP(vap);
+
+       ieee80211_ratectl_deinit(vap);
+       ieee80211_vap_detach(vap);
+       free(rvp, M_80211_VAP);
+}
+
+static void
+rt2860_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+       if (error != 0)
+               return;
+
+       KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
+
+       *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
+
+static int
+rt2860_alloc_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring)
+{
+       int size, error;
+
+       size = RT2860_TX_RING_COUNT * sizeof (struct rt2860_txd);
+
+       error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 16, 0,
+           BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+           size, 1, size, 0, NULL, NULL, &ring->desc_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create desc DMA map\n");
+               goto fail;
+       }
+
+       error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->txd,
+           BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate DMA memory\n");
+               goto fail;
+       }
+
+       error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->txd,
+           size, rt2860_dma_map_addr, &ring->paddr, 0);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not load desc DMA map\n");
+               goto fail;
+       }
+
+       bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE);
+
+       return 0;
+
+fail:  rt2860_free_tx_ring(sc, ring);
+       return error;
+}
+
+void
+rt2860_reset_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring)
+{
+       struct rt2860_tx_data *data;
+       int i;
+
+       for (i = 0; i < RT2860_TX_RING_COUNT; i++) {
+               if ((data = ring->data[i]) == NULL)
+                       continue;       /* nothing mapped in this slot */
+
+               if (data->m != NULL) {
+                       bus_dmamap_sync(sc->txwi_dmat, data->map,
+                           BUS_DMASYNC_POSTWRITE);
+                       bus_dmamap_unload(sc->txwi_dmat, data->map);
+                       m_freem(data->m);
+                       data->m = NULL;
+               }
+               if (data->ni != NULL) {
+                       ieee80211_free_node(data->ni);
+                       data->ni = NULL;
+               }
+
+               SLIST_INSERT_HEAD(&sc->data_pool, data, next);
+               ring->data[i] = NULL;
+       }
+
+       ring->queued = 0;
+       ring->cur = ring->next = 0;
+}
+
+void
+rt2860_free_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring)
+{
+       struct rt2860_tx_data *data;
+       int i;
+
+       if (ring->txd != NULL) {
+               bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
+               bus_dmamem_free(ring->desc_dmat, ring->txd, ring->desc_map);
+       }
+       if (ring->desc_dmat != NULL)
+               bus_dma_tag_destroy(ring->desc_dmat);
+
+       for (i = 0; i < RT2860_TX_RING_COUNT; i++) {
+               if ((data = ring->data[i]) == NULL)
+                       continue;       /* nothing mapped in this slot */
+
+               if (data->m != NULL) {
+                       bus_dmamap_sync(sc->txwi_dmat, data->map,
+                           BUS_DMASYNC_POSTWRITE);
+                       bus_dmamap_unload(sc->txwi_dmat, data->map);
+                       m_freem(data->m);
+               }
+               if (data->ni != NULL)
+                       ieee80211_free_node(data->ni);
+
+               SLIST_INSERT_HEAD(&sc->data_pool, data, next);
+       }
+}
+
+/*
+ * Allocate a pool of TX Wireless Information blocks.
+ */
+int
+rt2860_alloc_tx_pool(struct rt2860_softc *sc)
+{
+       caddr_t vaddr;
+       bus_addr_t paddr;
+       int i, size, error;
+
+       size = RT2860_TX_POOL_COUNT * RT2860_TXWI_DMASZ;
+
+       /* init data_pool early in case of failure.. */
+       SLIST_INIT(&sc->data_pool);
+
+       error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
+           BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+           size, 1, size, 0, NULL, NULL, &sc->txwi_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create txwi DMA tag\n");
+               goto fail;
+       }
+
+       error = bus_dmamem_alloc(sc->txwi_dmat, (void **)&sc->txwi_vaddr,
+           BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->txwi_map);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate DMA memory\n");
+               goto fail;
+       }
+
+       error = bus_dmamap_load(sc->txwi_dmat, sc->txwi_map,
+           sc->txwi_vaddr, size, rt2860_dma_map_addr, &paddr, 0);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not load txwi DMA map\n");
+               goto fail;
+       }
+
+       bus_dmamap_sync(sc->txwi_dmat, sc->txwi_map, BUS_DMASYNC_PREWRITE);
+
+       vaddr = sc->txwi_vaddr;
+       for (i = 0; i < RT2860_TX_POOL_COUNT; i++) {
+               struct rt2860_tx_data *data = &sc->data[i];
+
+               error = bus_dmamap_create(sc->txwi_dmat, 0, &data->map);
+               if (error != 0) {
+                       device_printf(sc->sc_dev, "could not create DMA map\n");
+                       goto fail;
+               }
+               data->txwi = (struct rt2860_txwi *)vaddr;
+               data->paddr = paddr;
+               vaddr += RT2860_TXWI_DMASZ;
+               paddr += RT2860_TXWI_DMASZ;
+
+               SLIST_INSERT_HEAD(&sc->data_pool, data, next);
+       }
+
+       return 0;
+
+fail:  rt2860_free_tx_pool(sc);
+       return error;
+}
+
+void
+rt2860_free_tx_pool(struct rt2860_softc *sc)
+{
+       if (sc->txwi_vaddr != NULL) {
+               bus_dmamap_sync(sc->txwi_dmat, sc->txwi_map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(sc->txwi_dmat, sc->txwi_map);
+               bus_dmamem_free(sc->txwi_dmat, sc->txwi_vaddr, sc->txwi_map);
+       }
+       if (sc->txwi_dmat != NULL)
+               bus_dma_tag_destroy(sc->txwi_dmat);
+
+       while (!SLIST_EMPTY(&sc->data_pool)) {
+               struct rt2860_tx_data *data;
+               data = SLIST_FIRST(&sc->data_pool);
+               bus_dmamap_destroy(sc->txwi_dmat, data->map);
+               SLIST_REMOVE_HEAD(&sc->data_pool, next);
+       }
+}
+
+int
+rt2860_alloc_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring)
+{
+       bus_addr_t physaddr;
+       int i, size, error;
+
+       size = RT2860_RX_RING_COUNT * sizeof (struct rt2860_rxd);
+
+       error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 16, 0,
+           BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+           size, 1, size, 0, NULL, NULL, &ring->desc_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create desc DMA tag\n");
+               goto fail;
+       }
+
+       error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->rxd,
+           BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate DMA memory\n");
+               goto fail;
+       }
+
+       error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->rxd,
+           size, rt2860_dma_map_addr, &ring->paddr, 0);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not load desc DMA map\n");
+               goto fail;
+       }
+
+       error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
+           BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
+           1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create data DMA tag\n");
+               goto fail;
+       }
+
+       for (i = 0; i < RT2860_RX_RING_COUNT; i++) {
+               struct rt2860_rx_data *data = &ring->data[i];
+               struct rt2860_rxd *rxd = &ring->rxd[i];
+
+               error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
+               if (error != 0) {
+                       device_printf(sc->sc_dev, "could not create DMA map\n");
+                       goto fail;
+               }
+
+               data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+               if (data->m == NULL) {
+                       device_printf(sc->sc_dev,
+                           "could not allocate rx mbuf\n");
+                       error = ENOMEM;
+                       goto fail;
+               }
+
+               error = bus_dmamap_load(ring->data_dmat, data->map,
+                   mtod(data->m, void *), MCLBYTES, rt2860_dma_map_addr,
+                   &physaddr, 0);
+               if (error != 0) {
+                       device_printf(sc->sc_dev,
+                           "could not load rx buf DMA map");
+                       goto fail;
+               }
+
+               rxd->sdp0 = htole32(physaddr);
+               rxd->sdl0 = htole16(MCLBYTES);
+       }
+
+       bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE);
+
+       return 0;
+
+fail:  rt2860_free_rx_ring(sc, ring);
+       return error;
+}
+
+void
+rt2860_reset_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring)
+{
+       int i;
+
+       for (i = 0; i < RT2860_RX_RING_COUNT; i++)
+               ring->rxd[i].sdl0 &= ~htole16(RT2860_RX_DDONE);
+
+       bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE);
+
+       ring->cur = 0;
+}
+
+void
+rt2860_free_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring)
+{
+       int i;
+
+       if (ring->rxd != NULL) {
+               bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
+               bus_dmamem_free(ring->desc_dmat, ring->rxd, ring->desc_map);
+       }
+       if (ring->desc_dmat != NULL)
+               bus_dma_tag_destroy(ring->desc_dmat);
+
+       for (i = 0; i < RT2860_RX_RING_COUNT; i++) {
+               struct rt2860_rx_data *data = &ring->data[i];
+
+               if (data->m != NULL) {
+                       bus_dmamap_sync(ring->data_dmat, data->map,
+                           BUS_DMASYNC_POSTREAD);
+                       bus_dmamap_unload(ring->data_dmat, data->map);
+                       m_freem(data->m);
+               }
+               if (data->map != NULL)
+                       bus_dmamap_destroy(ring->data_dmat, data->map);
+       }
+       if (ring->data_dmat != NULL)
+               bus_dma_tag_destroy(ring->data_dmat);
+}
+
+static void
+rt2860_updatestats(struct rt2860_softc *sc)
+{
+       struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+
+       /*
+        * In IBSS or HostAP modes (when the hardware sends beacons), the
+        * MAC can run into a livelock and start sending CTS-to-self frames
+        * like crazy if protection is enabled.  Fortunately, we can detect
+        * when such a situation occurs and reset the MAC.
+        */
+       if (ic->ic_curmode != IEEE80211_M_STA) {
+               /* check if we're in a livelock situation.. */
+               uint32_t tmp = RAL_READ(sc, RT2860_DEBUG);
+               if ((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))) {
+                       /* ..and reset MAC/BBP for a while.. */
+                       DPRINTF(("CTS-to-self livelock detected\n"));
+                       RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_SRST);
+                       RAL_BARRIER_WRITE(sc);
+                       DELAY(1);
+                       RAL_WRITE(sc, RT2860_MAC_SYS_CTRL,
+                           RT2860_MAC_RX_EN | RT2860_MAC_TX_EN);
+               }
+       }
+}
+
+static void
+rt2860_newassoc(struct ieee80211_node *ni, int isnew)
+{
+       struct ieee80211com *ic = ni->ni_ic;
+       struct rt2860_softc *sc = ic->ic_ifp->if_softc;
+       uint8_t wcid;
+
+       wcid = IEEE80211_AID(ni->ni_associd);
+       if (isnew && ni->ni_associd != 0) {
+               sc->wcid2ni[wcid] = ni;
+
+               /* init WCID table entry */
+               RAL_WRITE_REGION_1(sc, RT2860_WCID_ENTRY(wcid),
+                   ni->ni_macaddr, IEEE80211_ADDR_LEN);
+       }
+       DPRINTF(("new assoc isnew=%d addr=%s WCID=%d\n",
+           isnew, ether_sprintf(ni->ni_macaddr), wcid));
+}
+
+static void
+rt2860_node_free(struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = ni->ni_ic;
+       struct rt2860_softc *sc = ic->ic_ifp->if_softc;
+       uint8_t wcid;
+
+       if (ni->ni_associd != 0) {
+               wcid = IEEE80211_AID(ni->ni_associd);
+
+               /* clear Rx WCID search table entry */
+               RAL_SET_REGION_4(sc, RT2860_WCID_ENTRY(wcid), 0, 2);
+       }
+       sc->sc_node_free(ni);
+}
+
+#ifdef IEEE80211_HT
+static int
+rt2860_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
+    uint8_t tid)
+{
+       struct rt2860_softc *sc = ic->ic_softc;
+       uint8_t wcid = ((struct rt2860_node *)ni)->wcid;
+       uint32_t tmp;
+
+       /* update BA session mask */
+       tmp = RAL_READ(sc, RT2860_WCID_ENTRY(wcid) + 4);
+       tmp |= (1 << tid) << 16;
+       RAL_WRITE(sc, RT2860_WCID_ENTRY(wcid) + 4, tmp);
+       return 0;
+}
+
+static void

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to