]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 28 Mar 2009 01:35:03 +0000 (18:35 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 28 Mar 2009 01:35:03 +0000 (18:35 -0700)
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6: (166 commits)
  Revert "ax25: zero length frame filtering in AX25"
  Revert "netrom: zero length frame filtering in NetRom"
  cfg80211: default CONFIG_WIRELESS_OLD_REGULATORY to n
  mac80211/iwlwifi: move virtual A-MDPU queue bookkeeping to iwlwifi
  mac80211: fix aggregation to not require queue stop
  mac80211: add skb length sanity checking
  mac80211: unify and fix TX aggregation start
  mac80211: clean up __ieee80211_tx args
  mac80211: rework the pending packets code
  mac80211: fix A-MPDU queue assignment
  mac80211: rewrite fragmentation
  iwlwifi: show current driver status in user readable format
  b43: Add BCM4307 PCI-ID
  cfg80211: fix locking in nl80211_set_wiphy
  mac80211: fix RX path
  ath5k: properly drop packets from ops->tx
  ar9170: single module build
  ath9k: fix dma mapping leak of rx buffer upon rmmod
  rt2x00: New USB ID for rt73usb
  ath5k: warn and correct rate for unknown hw rate indexes
  ...

207 files changed:
Documentation/DocBook/mac80211.tmpl
Documentation/feature-removal-schedule.txt
MAINTAINERS
arch/um/drivers/net_kern.c
arch/um/include/shared/net_kern.h
drivers/net/3c503.c
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/ac3200.c
drivers/net/appletalk/cops.c
drivers/net/appletalk/ltpc.c
drivers/net/at1700.c
drivers/net/benet/be_main.c
drivers/net/cs89x0.c
drivers/net/cxgb3/adapter.h
drivers/net/cxgb3/common.h
drivers/net/cxgb3/cxgb3_main.c
drivers/net/cxgb3/sge.c
drivers/net/cxgb3/t3_hw.c
drivers/net/depca.c
drivers/net/eepro.c
drivers/net/eexpress.c
drivers/net/eth16i.c
drivers/net/ethoc.c [new file with mode: 0644]
drivers/net/ewrk3.c
drivers/net/gianfar.c
drivers/net/ibmlana.c
drivers/net/irda/donauboe.c
drivers/net/lance.c
drivers/net/lp486e.c
drivers/net/ni52.c
drivers/net/ni65.c
drivers/net/seeq8005.c
drivers/net/smc-ultra.c
drivers/net/smc-ultra32.c
drivers/net/smc9194.c
drivers/net/smsc911x.c
drivers/net/tokenring/madgemc.c
drivers/net/tokenring/proteon.c
drivers/net/tokenring/skisa.c
drivers/net/tokenring/smctr.c
drivers/net/ucc_geth.c
drivers/net/wan/sdla.c
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/ar9170/Kconfig [new file with mode: 0644]
drivers/net/wireless/ar9170/Makefile [new file with mode: 0644]
drivers/net/wireless/ar9170/ar9170.h [new file with mode: 0644]
drivers/net/wireless/ar9170/cmd.c [new file with mode: 0644]
drivers/net/wireless/ar9170/cmd.h [new file with mode: 0644]
drivers/net/wireless/ar9170/eeprom.h [new file with mode: 0644]
drivers/net/wireless/ar9170/hw.h [new file with mode: 0644]
drivers/net/wireless/ar9170/led.c [new file with mode: 0644]
drivers/net/wireless/ar9170/mac.c [new file with mode: 0644]
drivers/net/wireless/ar9170/main.c [new file with mode: 0644]
drivers/net/wireless/ar9170/phy.c [new file with mode: 0644]
drivers/net/wireless/ar9170/usb.c [new file with mode: 0644]
drivers/net/wireless/ar9170/usb.h [new file with mode: 0644]
drivers/net/wireless/arlan-main.c
drivers/net/wireless/ath5k/ath5k.h
drivers/net/wireless/ath5k/attach.c
drivers/net/wireless/ath5k/base.c
drivers/net/wireless/ath5k/base.h
drivers/net/wireless/ath5k/desc.c
drivers/net/wireless/ath5k/eeprom.c
drivers/net/wireless/ath5k/eeprom.h
drivers/net/wireless/ath5k/initvals.c
drivers/net/wireless/ath5k/led.c
drivers/net/wireless/ath5k/phy.c
drivers/net/wireless/ath5k/reg.h
drivers/net/wireless/ath5k/reset.c
drivers/net/wireless/ath9k/ahb.c
drivers/net/wireless/ath9k/ani.c
drivers/net/wireless/ath9k/ani.h
drivers/net/wireless/ath9k/ath9k.h
drivers/net/wireless/ath9k/beacon.c
drivers/net/wireless/ath9k/calib.c
drivers/net/wireless/ath9k/calib.h
drivers/net/wireless/ath9k/debug.c
drivers/net/wireless/ath9k/debug.h
drivers/net/wireless/ath9k/eeprom.c
drivers/net/wireless/ath9k/eeprom.h
drivers/net/wireless/ath9k/hw.c
drivers/net/wireless/ath9k/hw.h
drivers/net/wireless/ath9k/initvals.h
drivers/net/wireless/ath9k/mac.c
drivers/net/wireless/ath9k/mac.h
drivers/net/wireless/ath9k/main.c
drivers/net/wireless/ath9k/pci.c
drivers/net/wireless/ath9k/phy.c
drivers/net/wireless/ath9k/phy.h
drivers/net/wireless/ath9k/rc.c
drivers/net/wireless/ath9k/rc.h
drivers/net/wireless/ath9k/recv.c
drivers/net/wireless/ath9k/reg.h
drivers/net/wireless/ath9k/regd.c
drivers/net/wireless/ath9k/regd.h
drivers/net/wireless/ath9k/regd_common.h
drivers/net/wireless/ath9k/xmit.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/xmit.c
drivers/net/wireless/iwlwifi/iwl-3945-hw.h
drivers/net/wireless/iwlwifi/iwl-3945-rs.c
drivers/net/wireless/iwlwifi/iwl-3945.c
drivers/net/wireless/iwlwifi/iwl-4965.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-core.c
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/iwlwifi/iwl-debugfs.c
drivers/net/wireless/iwlwifi/iwl-dev.h
drivers/net/wireless/iwlwifi/iwl-helpers.h
drivers/net/wireless/iwlwifi/iwl-power.c
drivers/net/wireless/iwlwifi/iwl-sta.c
drivers/net/wireless/iwlwifi/iwl-tx.c
drivers/net/wireless/iwlwifi/iwl3945-base.c
drivers/net/wireless/libertas/radiotap.h
drivers/net/wireless/libertas/rx.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/p54/Kconfig
drivers/net/wireless/p54/p54common.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/wavelan.c
drivers/net/wireless/wavelan.p.h
drivers/ssb/Kconfig
drivers/ssb/b43_pci_bridge.c
include/linux/ieee80211.h
include/linux/if_frad.h
include/linux/netdevice.h
include/linux/netfilter/x_tables.h
include/linux/nl80211.h
include/linux/pci_ids.h
include/net/cfg80211.h
include/net/ethoc.h [new file with mode: 0644]
include/net/ieee80211_radiotap.h
include/net/mac80211.h
include/net/netfilter/nf_conntrack.h
include/net/netfilter/nf_conntrack_helper.h
include/net/netfilter/nf_conntrack_l3proto.h
include/net/netfilter/nf_conntrack_l4proto.h
include/net/netfilter/nf_conntrack_tuple.h
include/net/netlink.h
include/net/netns/conntrack.h
lib/nlattr.c
net/appletalk/ddp.c
net/ax25/af_ax25.c
net/core/dev.c
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/ip_tables.c
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
net/ipv4/netfilter/nf_conntrack_proto_icmp.c
net/ipv4/netfilter/nf_nat_core.c
net/ipv6/ip6_input.c
net/ipv6/netfilter/ip6_tables.c
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
net/mac80211/agg-rx.c
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/debugfs.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/pm.c
net/mac80211/rate.c
net/mac80211/rate.h
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wep.c
net/mac80211/wext.c
net/mac80211/wpa.c
net/netfilter/Kconfig
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_expect.c
net/netfilter/nf_conntrack_helper.c
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nf_conntrack_proto.c
net/netfilter/nf_conntrack_proto_dccp.c
net/netfilter/nf_conntrack_proto_gre.c
net/netfilter/nf_conntrack_proto_sctp.c
net/netfilter/nf_conntrack_proto_tcp.c
net/netfilter/nf_conntrack_proto_udp.c
net/netfilter/nf_conntrack_proto_udplite.c
net/netfilter/nf_conntrack_standalone.c
net/netfilter/xt_connlimit.c
net/netfilter/xt_physdev.c
net/netrom/af_netrom.c
net/rose/af_rose.c
net/wireless/Kconfig
net/wireless/Makefile
net/wireless/core.c
net/wireless/core.h
net/wireless/mlme.c [new file with mode: 0644]
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/reg.c
net/wireless/scan.c
net/wireless/wext-compat.c
net/x25/af_x25.c
net/xfrm/xfrm_state.c

index 8af6d9626878e0459c02052081c41bbc33c5d20d..fbeaffc1dcc39c7b25e3f36cc7923828be9cbec9 100644 (file)
@@ -227,6 +227,12 @@ usage should require reading the full document.
 !Pinclude/net/mac80211.h Powersave support
     </chapter>
 
+    <chapter id="beacon-filter">
+      <title>Beacon filter support</title>
+!Pinclude/net/mac80211.h Beacon filter support
+!Finclude/net/mac80211.h ieee80211_beacon_loss
+    </chapter>
+
     <chapter id="qos">
       <title>Multiple queues and QoS support</title>
       <para>TBD</para>
index e47c0ff8ba7aaf59d8d6fa8776ab331b9c5c25b6..02ea3773535e1fba8f7c7c931b04f7a1e1b7074d 100644 (file)
@@ -6,20 +6,47 @@ be removed from this file.
 
 ---------------------------
 
-What:  old static regulatory information and ieee80211_regdom module parameter
-When:  2.6.29
+What:  The ieee80211_regdom module parameter
+When:  March 2010 / desktop catchup
+
+Why:   This was inherited by the CONFIG_WIRELESS_OLD_REGULATORY code,
+       and currently serves as an option for users to define an
+       ISO / IEC 3166 alpha2 code for the country they are currently
+       present in. Although there are userspace API replacements for this
+       through nl80211 distributions haven't yet caught up with implementing
+       decent alternatives through standard GUIs. Although available as an
+       option through iw or wpa_supplicant its just a matter of time before
+       distributions pick up good GUI options for this. The ideal solution
+       would actually consist of intelligent designs which would do this for
+       the user automatically even when travelling through different countries.
+       Until then we leave this module parameter as a compromise.
+
+       When userspace improves with reasonable widely-available alternatives for
+       this we will no longer need this module parameter. This entry hopes that
+       by the super-futuristically looking date of "March 2010" we will have
+       such replacements widely available.
+
+Who:   Luis R. Rodriguez <lrodriguez@atheros.com>
+
+---------------------------
+
+What:  CONFIG_WIRELESS_OLD_REGULATORY - old static regulatory information
+When:  March 2010 / desktop catchup
+
 Why:   The old regulatory infrastructure has been replaced with a new one
        which does not require statically defined regulatory domains. We do
        not want to keep static regulatory domains in the kernel due to the
        the dynamic nature of regulatory law and localization. We kept around
        the old static definitions for the regulatory domains of:
+
                * US
                * JP
                * EU
+
        and used by default the US when CONFIG_WIRELESS_OLD_REGULATORY was
-       set. We also kept around the ieee80211_regdom module parameter in case
-       some applications were relying on it. Changing regulatory domains
-       can now be done instead by using nl80211, as is done with iw.
+       set. We will remove this option once the standard Linux desktop catches
+       up with the new userspace APIs we have implemented.
+
 Who:   Luis R. Rodriguez <lrodriguez@atheros.com>
 
 ---------------------------
index fa7be04b0cf0f2fb4d448b86d3dc122b39a22df6..64c89c215b01c360d29b7079c412fc8a1b19d37c 100644 (file)
@@ -765,6 +765,14 @@ L: linux-wireless@vger.kernel.org
 L:     ath9k-devel@lists.ath9k.org
 S:     Supported
 
+ATHEROS AR9170 WIRELESS DRIVER
+P:     Christian Lamparter
+M:     chunkeey@web.de
+L:     linux-wireless@vger.kernel.org
+W:     http://wireless.kernel.org/en/users/Drivers/ar9170
+S:     Maintained
+F:     drivers/net/wireless/ar9170/
+
 ATI_REMOTE2 DRIVER
 P:     Ville Syrjala
 M:     syrjala@sci.fi
@@ -3602,7 +3610,7 @@ S:        Maintained
 RALINK RT2X00 WIRELESS LAN DRIVER
 P:     rt2x00 project
 L:     linux-wireless@vger.kernel.org
-L:     rt2400-devel@lists.sourceforge.net
+L:     users@rt2x00.serialmonkey.com
 W:     http://rt2x00.serialmonkey.com/
 S:     Maintained
 T:     git kernel.org:/pub/scm/linux/kernel/git/ivd/rt2x00.git
index fde510b664d34db07ecab00ea48d285adefd2703..434224e2229ff6afde1556b5541b83200466c99e 100644 (file)
@@ -86,7 +86,7 @@ static int uml_net_rx(struct net_device *dev)
                drop_skb->dev = dev;
                /* Read a packet into drop_skb and don't do anything with it. */
                (*lp->read)(lp->fd, drop_skb, lp);
-               lp->stats.rx_dropped++;
+               dev->stats.rx_dropped++;
                return 0;
        }
 
@@ -99,8 +99,8 @@ static int uml_net_rx(struct net_device *dev)
                skb_trim(skb, pkt_len);
                skb->protocol = (*lp->protocol)(skb);
 
-               lp->stats.rx_bytes += skb->len;
-               lp->stats.rx_packets++;
+               dev->stats.rx_bytes += skb->len;
+               dev->stats.rx_packets++;
                netif_rx(skb);
                return pkt_len;
        }
@@ -224,8 +224,8 @@ static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
        len = (*lp->write)(lp->fd, skb, lp);
 
        if (len == skb->len) {
-               lp->stats.tx_packets++;
-               lp->stats.tx_bytes += skb->len;
+               dev->stats.tx_packets++;
+               dev->stats.tx_bytes += skb->len;
                dev->trans_start = jiffies;
                netif_start_queue(dev);
 
@@ -234,7 +234,7 @@ static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
        }
        else if (len == 0) {
                netif_start_queue(dev);
-               lp->stats.tx_dropped++;
+               dev->stats.tx_dropped++;
        }
        else {
                netif_start_queue(dev);
@@ -248,12 +248,6 @@ static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
        return 0;
 }
 
-static struct net_device_stats *uml_net_get_stats(struct net_device *dev)
-{
-       struct uml_net_private *lp = netdev_priv(dev);
-       return &lp->stats;
-}
-
 static void uml_net_set_multicast_list(struct net_device *dev)
 {
        return;
@@ -377,6 +371,18 @@ static void net_device_release(struct device *dev)
        free_netdev(netdev);
 }
 
+static const struct net_device_ops uml_netdev_ops = {
+       .ndo_open               = uml_net_open,
+       .ndo_stop               = uml_net_close,
+       .ndo_start_xmit         = uml_net_start_xmit,
+       .ndo_set_multicast_list = uml_net_set_multicast_list,
+       .ndo_tx_timeout         = uml_net_tx_timeout,
+       .ndo_set_mac_address    = uml_net_set_mac,
+       .ndo_change_mtu         = uml_net_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 /*
  * Ensures that platform_driver_register is called only once by
  * eth_configure.  Will be set in an initcall.
@@ -473,14 +479,7 @@ static void eth_configure(int n, void *init, char *mac,
 
        set_ether_mac(dev, device->mac);
        dev->mtu = transport->user->mtu;
-       dev->open = uml_net_open;
-       dev->hard_start_xmit = uml_net_start_xmit;
-       dev->stop = uml_net_close;
-       dev->get_stats = uml_net_get_stats;
-       dev->set_multicast_list = uml_net_set_multicast_list;
-       dev->tx_timeout = uml_net_tx_timeout;
-       dev->set_mac_address = uml_net_set_mac;
-       dev->change_mtu = uml_net_change_mtu;
+       dev->netdev_ops = &uml_netdev_ops;
        dev->ethtool_ops = &uml_net_ethtool_ops;
        dev->watchdog_timeo = (HZ >> 1);
        dev->irq = UM_ETH_IRQ;
index d843c7924a7cf2568a1ce88ff9182a52f4ccc322..5c367f22595bd5bf915b4c55e33892d4833f17e5 100644 (file)
@@ -26,7 +26,7 @@ struct uml_net_private {
        spinlock_t lock;
        struct net_device *dev;
        struct timer_list tl;
-       struct net_device_stats stats;
+
        struct work_struct work;
        int fd;
        unsigned char mac[ETH_ALEN];
index 5b91a85fe107f64d312159e80733cfefcb526ae5..4f08bd995836760285741b40110cf591819fd1e6 100644 (file)
@@ -353,9 +353,6 @@ el2_probe1(struct net_device *dev, int ioaddr)
 
     dev->netdev_ops = &el2_netdev_ops;
     dev->ethtool_ops = &netdev_ethtool_ops;
-#ifdef CONFIG_NET_POLL_CONTROLLER
-    dev->poll_controller = eip_poll;
-#endif
 
     retval = register_netdev(dev);
     if (retval)
index e5ffc1c606c19691f8fe200389747550555cece3..f062b424704eeb9a869b656451f0b4752de7ae54 100644 (file)
@@ -972,6 +972,14 @@ config ENC28J60_WRITEVERIFY
          Enable the verify after the buffer write useful for debugging purpose.
          If unsure, say N.
 
+config ETHOC
+       tristate "OpenCores 10/100 Mbps Ethernet MAC support"
+       depends on NET_ETHERNET
+       select MII
+       select PHYLIB
+       help
+         Say Y here if you want to use the OpenCores 10/100 Mbps Ethernet MAC.
+
 config SMC911X
        tristate "SMSC LAN911[5678] support"
        select CRC32
index 758ecdf4c8202d492f9ec683d696a6834e09262a..98409c9dd445c7b16d79ab9bb543a02a91a274e1 100644 (file)
@@ -230,6 +230,7 @@ obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
 pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o
 obj-$(CONFIG_MLX4_CORE) += mlx4/
 obj-$(CONFIG_ENC28J60) += enc28j60.o
+obj-$(CONFIG_ETHOC) += ethoc.o
 
 obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
 
index 071a851a2ea1d6b5a4b4db6b0c0e51625ad38222..eac73382c087523e03b38826d316338686ce3afc 100644 (file)
@@ -143,6 +143,22 @@ out:
 }
 #endif
 
+static const struct net_device_ops ac_netdev_ops = {
+       .ndo_open               = ac_open,
+       .ndo_stop               = ac_close_card,
+
+       .ndo_start_xmit         = ei_start_xmit,
+       .ndo_tx_timeout         = ei_tx_timeout,
+       .ndo_get_stats          = ei_get_stats,
+       .ndo_set_multicast_list = ei_set_multicast_list,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_change_mtu         = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = ei_poll,
+#endif
+};
+
 static int __init ac_probe1(int ioaddr, struct net_device *dev)
 {
        int i, retval;
@@ -253,11 +269,7 @@ static int __init ac_probe1(int ioaddr, struct net_device *dev)
        ei_status.block_output = &ac_block_output;
        ei_status.get_8390_hdr = &ac_get_8390_hdr;
 
-       dev->open = &ac_open;
-       dev->stop = &ac_close_card;
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       dev->poll_controller = ei_poll;
-#endif
+       dev->netdev_ops = &ac_netdev_ops;
        NS8390_init(dev, 0);
 
        retval = register_netdev(dev);
index 54819a34ba0a28a454c3e9684e8162f10ff4b977..7f8325419803f9bb62cf26b9440b881205c608c2 100644 (file)
@@ -171,7 +171,6 @@ static unsigned int cops_debug = COPS_DEBUG;
 
 struct cops_local
 {
-        struct net_device_stats stats;
         int board;                     /* Holds what board type is. */
        int nodeid;                     /* Set to 1 once have nodeid. */
         unsigned char node_acquire;    /* Node ID when acquired. */
@@ -197,7 +196,6 @@ static int  cops_send_packet (struct sk_buff *skb, struct net_device *dev);
 static void set_multicast_list (struct net_device *dev);
 static int  cops_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 static int  cops_close (struct net_device *dev);
-static struct net_device_stats *cops_get_stats (struct net_device *dev);
 
 static void cleanup_card(struct net_device *dev)
 {
@@ -260,6 +258,15 @@ out:
        return ERR_PTR(err);
 }
 
+static const struct net_device_ops cops_netdev_ops = {
+       .ndo_open               = cops_open,
+        .ndo_stop               = cops_close,
+       .ndo_start_xmit         = cops_send_packet,
+       .ndo_tx_timeout         = cops_timeout,
+        .ndo_do_ioctl           = cops_ioctl,
+       .ndo_set_multicast_list = set_multicast_list,
+};
+
 /*
  *      This is the real probe routine. Linux has a history of friendly device
  *      probes on the ISA bus. A good device probes avoids doing writes, and
@@ -333,16 +340,9 @@ static int __init cops_probe1(struct net_device *dev, int ioaddr)
        /* Copy local board variable to lp struct. */
        lp->board               = board;
 
-       dev->hard_start_xmit    = cops_send_packet;
-       dev->tx_timeout         = cops_timeout;
+       dev->netdev_ops         = &cops_netdev_ops;
        dev->watchdog_timeo     = HZ * 2;
 
-        dev->get_stats          = cops_get_stats;
-       dev->open               = cops_open;
-        dev->stop               = cops_close;
-        dev->do_ioctl           = cops_ioctl;
-       dev->set_multicast_list = set_multicast_list;
-        dev->mc_list            = NULL;
 
        /* Tell the user where the card is and what mode we're in. */
        if(board==DAYNA)
@@ -797,7 +797,7 @@ static void cops_rx(struct net_device *dev)
         {
                 printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n",
                        dev->name);
-                lp->stats.rx_dropped++;
+                dev->stats.rx_dropped++;
                 while(pkt_len--)        /* Discard packet */
                         inb(ioaddr);
                 spin_unlock_irqrestore(&lp->lock, flags);
@@ -819,7 +819,7 @@ static void cops_rx(struct net_device *dev)
         {
                printk(KERN_WARNING "%s: Bad packet length of %d bytes.\n", 
                        dev->name, pkt_len);
-                lp->stats.tx_errors++;
+                dev->stats.tx_errors++;
                 dev_kfree_skb_any(skb);
                 return;
         }
@@ -836,7 +836,7 @@ static void cops_rx(struct net_device *dev)
         if(rsp_type != LAP_RESPONSE)
         {
                 printk(KERN_WARNING "%s: Bad packet type %d.\n", dev->name, rsp_type);
-                lp->stats.tx_errors++;
+                dev->stats.tx_errors++;
                 dev_kfree_skb_any(skb);
                 return;
         }
@@ -846,8 +846,8 @@ static void cops_rx(struct net_device *dev)
         skb_reset_transport_header(skb);    /* Point to data (Skip header). */
 
         /* Update the counters. */
-        lp->stats.rx_packets++;
-        lp->stats.rx_bytes += skb->len;
+        dev->stats.rx_packets++;
+        dev->stats.rx_bytes += skb->len;
 
         /* Send packet to a higher place. */
         netif_rx(skb);
@@ -858,7 +858,7 @@ static void cops_timeout(struct net_device *dev)
         struct cops_local *lp = netdev_priv(dev);
         int ioaddr = dev->base_addr;
 
-       lp->stats.tx_errors++;
+       dev->stats.tx_errors++;
         if(lp->board==TANGENT)
         {
                if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
@@ -916,8 +916,8 @@ static int cops_send_packet(struct sk_buff *skb, struct net_device *dev)
        spin_unlock_irqrestore(&lp->lock, flags);       /* Restore interrupts. */
 
        /* Done sending packet, update counters and cleanup. */
-       lp->stats.tx_packets++;
-       lp->stats.tx_bytes += skb->len;
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += skb->len;
        dev->trans_start = jiffies;
        dev_kfree_skb (skb);
         return 0;
@@ -986,15 +986,6 @@ static int cops_close(struct net_device *dev)
         return 0;
 }
 
-/*
- *      Get the current statistics.
- *      This may be called with the card open or closed.
- */
-static struct net_device_stats *cops_get_stats(struct net_device *dev)
-{
-        struct cops_local *lp = netdev_priv(dev);
-        return &lp->stats;
-}
 
 #ifdef MODULE
 static struct net_device *cops_dev;
index dc4d49605603147acc93acb476ec9aa94cc714cb..78cc71469136350142d07ee34c72292e4678219c 100644 (file)
@@ -261,7 +261,6 @@ static unsigned char *ltdmacbuf;
 
 struct ltpc_private
 {
-       struct net_device_stats stats;
        struct atalk_addr my_addr;
 };
 
@@ -699,7 +698,6 @@ static int do_read(struct net_device *dev, void *cbuf, int cbuflen,
 static struct timer_list ltpc_timer;
 
 static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev);
-static struct net_device_stats *ltpc_get_stats(struct net_device *dev);
 
 static int read_30 ( struct net_device *dev)
 {
@@ -726,8 +724,6 @@ static int sendup_buffer (struct net_device *dev)
        int dnode, snode, llaptype, len; 
        int sklen;
        struct sk_buff *skb;
-       struct ltpc_private *ltpc_priv = netdev_priv(dev);
-       struct net_device_stats *stats = &ltpc_priv->stats;
        struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf;
 
        if (ltc->command != LT_RCVLAP) {
@@ -779,8 +775,8 @@ static int sendup_buffer (struct net_device *dev)
 
        skb_reset_transport_header(skb);
 
-       stats->rx_packets++;
-       stats->rx_bytes+=skb->len;
+       dev->stats.rx_packets++;
+       dev->stats.rx_bytes += skb->len;
 
        /* toss it onwards */
        netif_rx(skb);
@@ -904,10 +900,6 @@ static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev)
        /* in kernel 1.3.xx, on entry skb->data points to ddp header,
         * and skb->len is the length of the ddp data + ddp header
         */
-
-       struct ltpc_private *ltpc_priv = netdev_priv(dev);
-       struct net_device_stats *stats = &ltpc_priv->stats;
-
        int i;
        struct lt_sendlap cbuf;
        unsigned char *hdr;
@@ -936,20 +928,13 @@ static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev)
                printk("\n");
        }
 
-       stats->tx_packets++;
-       stats->tx_bytes+=skb->len;
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += skb->len;
 
        dev_kfree_skb(skb);
        return 0;
 }
 
-static struct net_device_stats *ltpc_get_stats(struct net_device *dev)
-{
-       struct ltpc_private *ltpc_priv = netdev_priv(dev);
-       struct net_device_stats *stats = &ltpc_priv->stats;
-       return stats;
-}
-
 /* initialization stuff */
   
 static int __init ltpc_probe_dma(int base, int dma)
@@ -1027,6 +1012,12 @@ static int __init ltpc_probe_dma(int base, int dma)
        return (want & 2) ? 3 : 1;
 }
 
+static const struct net_device_ops ltpc_netdev = {
+       .ndo_start_xmit         = ltpc_xmit,
+       .ndo_do_ioctl           = ltpc_ioctl,
+       .ndo_set_multicast_list = set_multicast_list,
+};
+
 struct net_device * __init ltpc_probe(void)
 {
        struct net_device *dev;
@@ -1133,14 +1124,7 @@ struct net_device * __init ltpc_probe(void)
        else
                printk(KERN_INFO "Apple/Farallon LocalTalk-PC card at %03x, DMA%d.  Using polled mode.\n",io,dma);
 
-       /* Fill in the fields of the device structure with ethernet-generic values. */
-       dev->hard_start_xmit = ltpc_xmit;
-       dev->get_stats = ltpc_get_stats;
-
-       /* add the ltpc-specific things */
-       dev->do_ioctl = &ltpc_ioctl;
-
-       dev->set_multicast_list = &set_multicast_list;
+       dev->netdev_ops = &ltpc_netdev;
        dev->mc_list = NULL;
        dev->base_addr = io;
        dev->irq = irq;
index ced70799b898faa6006cacf9a31cf56cff2a99c6..18b566ad4fd17cedda3deff1fa2fefc45aa262a9 100644 (file)
@@ -249,6 +249,17 @@ out:
        return ERR_PTR(err);
 }
 
+static const struct net_device_ops at1700_netdev_ops = {
+       .ndo_open               = net_open,
+       .ndo_stop               = net_close,
+       .ndo_start_xmit         = net_send_packet,
+       .ndo_set_multicast_list = set_rx_mode,
+       .ndo_tx_timeout         = net_tx_timeout,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 /* The Fujitsu datasheet suggests that the NIC be probed for by checking its
    "signature", the default bit pattern after a reset.  This *doesn't* work --
    there is no way to reset the bus interface without a complete power-cycle!
@@ -448,13 +459,7 @@ found:
        if (net_debug)
                printk(version);
 
-       memset(lp, 0, sizeof(struct net_local));
-
-       dev->open               = net_open;
-       dev->stop               = net_close;
-       dev->hard_start_xmit = net_send_packet;
-       dev->set_multicast_list = &set_rx_mode;
-       dev->tx_timeout = net_tx_timeout;
+       dev->netdev_ops = &at1700_netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
        spin_lock_init(&lp->lock);
index f901fee79a20eb38c6cf9f12fcb827aaa0150d76..9b75aa630062401886b404392ca0ecbc22d524e8 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include "be.h"
+#include <asm/div64.h>
 
 MODULE_VERSION(DRV_VER);
 MODULE_DEVICE_TABLE(pci, be_dev_ids);
@@ -290,6 +291,17 @@ static struct net_device_stats *be_get_stats(struct net_device *dev)
        return &adapter->stats.net_stats;
 }
 
+static u32 be_calc_rate(u64 bytes, unsigned long ticks)
+{
+       u64 rate = bytes;
+
+       do_div(rate, ticks / HZ);
+       rate <<= 3;                     /* bytes/sec -> bits/sec */
+       do_div(rate, 1000000ul);        /* MB/Sec */
+
+       return rate;
+}
+
 static void be_tx_rate_update(struct be_adapter *adapter)
 {
        struct be_drvr_stats *stats = drvr_stats(adapter);
@@ -303,11 +315,9 @@ static void be_tx_rate_update(struct be_adapter *adapter)
 
        /* Update tx rate once in two seconds */
        if ((now - stats->be_tx_jiffies) > 2 * HZ) {
-               u32 r;
-               r = (stats->be_tx_bytes - stats->be_tx_bytes_prev) /
-                       ((now - stats->be_tx_jiffies) / HZ);
-               r = r / 1000000;                        /* M bytes/s */
-               stats->be_tx_rate = r * 8;      /* M bits/s */
+               stats->be_tx_rate = be_calc_rate(stats->be_tx_bytes
+                                                 - stats->be_tx_bytes_prev,
+                                                now - stats->be_tx_jiffies);
                stats->be_tx_jiffies = now;
                stats->be_tx_bytes_prev = stats->be_tx_bytes;
        }
@@ -599,7 +609,6 @@ static void be_rx_rate_update(struct be_adapter *adapter)
 {
        struct be_drvr_stats *stats = drvr_stats(adapter);
        ulong now = jiffies;
-       u32 rate;
 
        /* Wrapped around */
        if (time_before(now, stats->be_rx_jiffies)) {
@@ -611,10 +620,9 @@ static void be_rx_rate_update(struct be_adapter *adapter)
        if ((now - stats->be_rx_jiffies) < 2 * HZ)
                return;
 
-       rate = (stats->be_rx_bytes - stats->be_rx_bytes_prev) /
-               ((now - stats->be_rx_jiffies) / HZ);
-       rate = rate / 1000000;  /* MB/Sec */
-       stats->be_rx_rate = rate * 8;   /* Mega Bits/Sec */
+       stats->be_rx_rate = be_calc_rate(stats->be_rx_bytes
+                                         - stats->be_rx_bytes_prev,
+                                        now - stats->be_rx_jiffies);
        stats->be_rx_jiffies = now;
        stats->be_rx_bytes_prev = stats->be_rx_bytes;
 }
index ff6497658a4538c94ceff39898c5b74f7f75da86..7433b88eed7e958f0666d02bafac62657add0bc0 100644 (file)
@@ -501,6 +501,21 @@ static void net_poll_controller(struct net_device *dev)
 }
 #endif
 
+static const struct net_device_ops net_ops = {
+       .ndo_open               = net_open,
+       .ndo_stop               = net_close,
+       .ndo_tx_timeout         = net_timeout,
+       .ndo_start_xmit         = net_send_packet,
+       .ndo_get_stats          = net_get_stats,
+       .ndo_set_multicast_list = set_multicast_list,
+       .ndo_set_mac_address    = set_mac_address,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = net_poll_controller,
+#endif
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 /* This is the real probe routine.  Linux has a history of friendly device
    probes on the ISA bus.  A good device probes avoids doing writes, and
    verifies that the correct device exists and functions.
@@ -843,17 +858,8 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
        /* print the ethernet address. */
        printk(", MAC %pM", dev->dev_addr);
 
-       dev->open               = net_open;
-       dev->stop               = net_close;
-       dev->tx_timeout         = net_timeout;
-       dev->watchdog_timeo     = HZ;
-       dev->hard_start_xmit    = net_send_packet;
-       dev->get_stats          = net_get_stats;
-       dev->set_multicast_list = set_multicast_list;
-       dev->set_mac_address    = set_mac_address;
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       dev->poll_controller    = net_poll_controller;
-#endif
+       dev->netdev_ops = &net_ops;
+       dev->watchdog_timeo = HZ;
 
        printk("\n");
        if (net_debug)
index 71eaa431371dc39c15fd3abee7723a087982bf0f..714df2b675e6b27f88009c013c54dfdedb674c7a 100644 (file)
@@ -85,6 +85,8 @@ struct fl_pg_chunk {
        struct page *page;
        void *va;
        unsigned int offset;
+       u64 *p_cnt;
+       DECLARE_PCI_UNMAP_ADDR(mapping);
 };
 
 struct rx_desc;
@@ -101,6 +103,7 @@ struct sge_fl {                     /* SGE per free-buffer list state */
        struct fl_pg_chunk pg_chunk;/* page chunk cache */
        unsigned int use_pages;     /* whether FL uses pages or sk_buffs */
        unsigned int order;         /* order of page allocations */
+       unsigned int alloc_size;    /* size of allocated buffer */
        struct rx_desc *desc;       /* address of HW Rx descriptor ring */
        struct rx_sw_desc *sdesc;   /* address of SW Rx descriptor ring */
        dma_addr_t   phys_addr;     /* physical address of HW ring start */
@@ -291,6 +294,7 @@ void t3_os_link_fault_handler(struct adapter *adapter, int port_id);
 
 void t3_sge_start(struct adapter *adap);
 void t3_sge_stop(struct adapter *adap);
+void t3_start_sge_timers(struct adapter *adap);
 void t3_stop_sge_timers(struct adapter *adap);
 void t3_free_sge_resources(struct adapter *adap);
 void t3_sge_err_intr_handler(struct adapter *adapter);
index 9ee021e750c8165264720eac31e925ced07ef0db..e508dc32f3ec620e1e7cdd6c2594711f429ed244 100644 (file)
@@ -191,7 +191,8 @@ struct mdio_ops {
 };
 
 struct adapter_info {
-       unsigned char nports;   /* # of ports */
+       unsigned char nports0;        /* # of ports on channel 0 */
+       unsigned char nports1;        /* # of ports on channel 1 */
        unsigned char phy_base_addr;    /* MDIO PHY base address */
        unsigned int gpio_out;  /* GPIO output settings */
        unsigned char gpio_intr[MAX_NPORTS]; /* GPIO PHY IRQ pins */
@@ -422,6 +423,7 @@ struct adapter_params {
        unsigned short b_wnd[NCCTRL_WIN];
 
        unsigned int nports;    /* # of ethernet ports */
+       unsigned int chan_map;  /* bitmap of in-use Tx channels */
        unsigned int stats_update_period;       /* MAC stats accumulation period */
        unsigned int linkpoll_period;   /* link poll period in 0.1s */
        unsigned int rev;       /* chip revision */
index d8be89621bf746779d2b9961201f5a6d8c504bdb..2c2aaa741450a9e57e1101be652782386dee22d6 100644 (file)
@@ -602,7 +602,6 @@ static int setup_sge_qsets(struct adapter *adap)
                                &adap->params.sge.qset[qset_idx], ntxq, dev,
                                netdev_get_tx_queue(dev, j));
                        if (err) {
-                               t3_stop_sge_timers(adap);
                                t3_free_sge_resources(adap);
                                return err;
                        }
@@ -1046,6 +1045,8 @@ static int cxgb_up(struct adapter *adap)
                setup_rss(adap);
                if (!(adap->flags & NAPI_INIT))
                        init_napi(adap);
+
+               t3_start_sge_timers(adap);
                adap->flags |= FULL_INIT_DONE;
        }
 
@@ -2870,6 +2871,9 @@ static void t3_io_resume(struct pci_dev *pdev)
 {
        struct adapter *adapter = pci_get_drvdata(pdev);
 
+       CH_ALERT(adapter, "adapter recovering, PEX ERR 0x%x\n",
+                t3_read_reg(adapter, A_PCIE_PEX_ERR));
+
        t3_resume_ports(adapter);
 }
 
@@ -3002,7 +3006,7 @@ static int __devinit init_one(struct pci_dev *pdev,
        static int version_printed;
 
        int i, err, pci_using_dac = 0;
-       unsigned long mmio_start, mmio_len;
+       resource_size_t mmio_start, mmio_len;
        const struct adapter_info *ai;
        struct adapter *adapter = NULL;
        struct port_info *pi;
@@ -3082,7 +3086,7 @@ static int __devinit init_one(struct pci_dev *pdev,
        INIT_WORK(&adapter->fatal_error_handler_task, fatal_error_task);
        INIT_DELAYED_WORK(&adapter->adap_check_task, t3_adap_check_task);
 
-       for (i = 0; i < ai->nports; ++i) {
+       for (i = 0; i < ai->nports0 + ai->nports1; ++i) {
                struct net_device *netdev;
 
                netdev = alloc_etherdev_mq(sizeof(struct port_info), SGE_QSETS);
@@ -3172,7 +3176,7 @@ static int __devinit init_one(struct pci_dev *pdev,
 
 out_free_dev:
        iounmap(adapter->regs);
-       for (i = ai->nports - 1; i >= 0; --i)
+       for (i = ai->nports0 + ai->nports1 - 1; i >= 0; --i)
                if (adapter->port[i])
                        free_netdev(adapter->port[i]);
 
index a7555cb3fa4a2aec944dc8ebf064d5b0fc1ee688..26d3587f339922696b1975ba6a8601b420719560 100644 (file)
@@ -50,6 +50,7 @@
 #define SGE_RX_COPY_THRES  256
 #define SGE_RX_PULL_LEN    128
 
+#define SGE_PG_RSVD SMP_CACHE_BYTES
 /*
  * Page chunk size for FL0 buffers if FL0 is to be populated with page chunks.
  * It must be a divisor of PAGE_SIZE.  If set to 0 FL0 will use sk_buffs
  */
 #define FL0_PG_CHUNK_SIZE  2048
 #define FL0_PG_ORDER 0
+#define FL0_PG_ALLOC_SIZE (PAGE_SIZE << FL0_PG_ORDER)
 #define FL1_PG_CHUNK_SIZE (PAGE_SIZE > 8192 ? 16384 : 8192)
 #define FL1_PG_ORDER (PAGE_SIZE > 8192 ? 0 : 1)
+#define FL1_PG_ALLOC_SIZE (PAGE_SIZE << FL1_PG_ORDER)
 
 #define SGE_RX_DROP_THRES 16
 #define RX_RECLAIM_PERIOD (HZ/4)
@@ -345,13 +348,21 @@ static inline int should_restart_tx(const struct sge_txq *q)
        return q->in_use - r < (q->size >> 1);
 }
 
-static void clear_rx_desc(const struct sge_fl *q, struct rx_sw_desc *d)
+static void clear_rx_desc(struct pci_dev *pdev, const struct sge_fl *q,
+                         struct rx_sw_desc *d)
 {
-       if (q->use_pages) {
-               if (d->pg_chunk.page)
-                       put_page(d->pg_chunk.page);
+       if (q->use_pages && d->pg_chunk.page) {
+               (*d->pg_chunk.p_cnt)--;
+               if (!*d->pg_chunk.p_cnt)
+                       pci_unmap_page(pdev,
+                                      pci_unmap_addr(&d->pg_chunk, mapping),
+                                      q->alloc_size, PCI_DMA_FROMDEVICE);
+
+               put_page(d->pg_chunk.page);
                d->pg_chunk.page = NULL;
        } else {
+               pci_unmap_single(pdev, pci_unmap_addr(d, dma_addr),
+                                q->buf_size, PCI_DMA_FROMDEVICE);
                kfree_skb(d->skb);
                d->skb = NULL;
        }
@@ -372,9 +383,8 @@ static void free_rx_bufs(struct pci_dev *pdev, struct sge_fl *q)
        while (q->credits--) {
                struct rx_sw_desc *d = &q->sdesc[cidx];
 
-               pci_unmap_single(pdev, pci_unmap_addr(d, dma_addr),
-                                q->buf_size, PCI_DMA_FROMDEVICE);
-               clear_rx_desc(q, d);
+
+               clear_rx_desc(pdev, q, d);
                if (++cidx == q->size)
                        cidx = 0;
        }
@@ -417,18 +427,39 @@ static inline int add_one_rx_buf(void *va, unsigned int len,
        return 0;
 }
 
-static int alloc_pg_chunk(struct sge_fl *q, struct rx_sw_desc *sd, gfp_t gfp,
+static inline int add_one_rx_chunk(dma_addr_t mapping, struct rx_desc *d,
+                                  unsigned int gen)
+{
+       d->addr_lo = cpu_to_be32(mapping);
+       d->addr_hi = cpu_to_be32((u64) mapping >> 32);
+       wmb();
+       d->len_gen = cpu_to_be32(V_FLD_GEN1(gen));
+       d->gen2 = cpu_to_be32(V_FLD_GEN2(gen));
+       return 0;
+}
+
+static int alloc_pg_chunk(struct adapter *adapter, struct sge_fl *q,
+                         struct rx_sw_desc *sd, gfp_t gfp,
                          unsigned int order)
 {
        if (!q->pg_chunk.page) {
+               dma_addr_t mapping;
+
                q->pg_chunk.page = alloc_pages(gfp, order);
                if (unlikely(!q->pg_chunk.page))
                        return -ENOMEM;
                q->pg_chunk.va = page_address(q->pg_chunk.page);
+               q->pg_chunk.p_cnt = q->pg_chunk.va + (PAGE_SIZE << order) -
+                                   SGE_PG_RSVD;
                q->pg_chunk.offset = 0;
+               mapping = pci_map_page(adapter->pdev, q->pg_chunk.page,
+                                      0, q->alloc_size, PCI_DMA_FROMDEVICE);
+               pci_unmap_addr_set(&q->pg_chunk, mapping, mapping);
        }
        sd->pg_chunk = q->pg_chunk;
 
+       prefetch(sd->pg_chunk.p_cnt);
+
        q->pg_chunk.offset += q->buf_size;
        if (q->pg_chunk.offset == (PAGE_SIZE << order))
                q->pg_chunk.page = NULL;
@@ -436,6 +467,12 @@ static int alloc_pg_chunk(struct sge_fl *q, struct rx_sw_desc *sd, gfp_t gfp,
                q->pg_chunk.va += q->buf_size;
                get_page(q->pg_chunk.page);
        }
+
+       if (sd->pg_chunk.offset == 0)
+               *sd->pg_chunk.p_cnt = 1;
+       else
+               *sd->pg_chunk.p_cnt += 1;
+
        return 0;
 }
 
@@ -460,35 +497,43 @@ static inline void ring_fl_db(struct adapter *adap, struct sge_fl *q)
  */
 static int refill_fl(struct adapter *adap, struct sge_fl *q, int n, gfp_t gfp)
 {
-       void *buf_start;
        struct rx_sw_desc *sd = &q->sdesc[q->pidx];
        struct rx_desc *d = &q->desc[q->pidx];
        unsigned int count = 0;
 
        while (n--) {
+               dma_addr_t mapping;
                int err;
 
                if (q->use_pages) {
-                       if (unlikely(alloc_pg_chunk(q, sd, gfp, q->order))) {
+                       if (unlikely(alloc_pg_chunk(adap, q, sd, gfp,
+                                                   q->order))) {
 nomem:                         q->alloc_failed++;
                                break;
                        }
-                       buf_start = sd->pg_chunk.va;
+                       mapping = pci_unmap_addr(&sd->pg_chunk, mapping) +
+                                                sd->pg_chunk.offset;
+                       pci_unmap_addr_set(sd, dma_addr, mapping);
+
+                       add_one_rx_chunk(mapping, d, q->gen);
+                       pci_dma_sync_single_for_device(adap->pdev, mapping,
+                                               q->buf_size - SGE_PG_RSVD,
+                                               PCI_DMA_FROMDEVICE);
                } else {
-                       struct sk_buff *skb = alloc_skb(q->buf_size, gfp);
+                       void *buf_start;
 
+                       struct sk_buff *skb = alloc_skb(q->buf_size, gfp);
                        if (!skb)
                                goto nomem;
 
                        sd->skb = skb;
                        buf_start = skb->data;
-               }
-
-               err = add_one_rx_buf(buf_start, q->buf_size, d, sd, q->gen,
-                                    adap->pdev);
-               if (unlikely(err)) {
-                       clear_rx_desc(q, sd);
-                       break;
+                       err = add_one_rx_buf(buf_start, q->buf_size, d, sd,
+                                            q->gen, adap->pdev);
+                       if (unlikely(err)) {
+                               clear_rx_desc(adap->pdev, q, sd);
+                               break;
+                       }
                }
 
                d++;
@@ -795,19 +840,19 @@ static struct sk_buff *get_packet_pg(struct adapter *adap, struct sge_fl *fl,
        struct sk_buff *newskb, *skb;
        struct rx_sw_desc *sd = &fl->sdesc[fl->cidx];
 
-       newskb = skb = q->pg_skb;
+       dma_addr_t dma_addr = pci_unmap_addr(sd, dma_addr);
 
+       newskb = skb = q->pg_skb;
        if (!skb && (len <= SGE_RX_COPY_THRES)) {
                newskb = alloc_skb(len, GFP_ATOMIC);
                if (likely(newskb != NULL)) {
                        __skb_put(newskb, len);
-                       pci_dma_sync_single_for_cpu(adap->pdev,
-                                           pci_unmap_addr(sd, dma_addr), len,
+                       pci_dma_sync_single_for_cpu(adap->pdev, dma_addr, len,
                                            PCI_DMA_FROMDEVICE);
                        memcpy(newskb->data, sd->pg_chunk.va, len);
-                       pci_dma_sync_single_for_device(adap->pdev,
-                                           pci_unmap_addr(sd, dma_addr), len,
-                                           PCI_DMA_FROMDEVICE);
+                       pci_dma_sync_single_for_device(adap->pdev, dma_addr,
+                                                      len,
+                                                      PCI_DMA_FROMDEVICE);
                } else if (!drop_thres)
                        return NULL;
 recycle:
@@ -820,16 +865,25 @@ recycle:
        if (unlikely(q->rx_recycle_buf || (!skb && fl->credits <= drop_thres)))
                goto recycle;
 
+       prefetch(sd->pg_chunk.p_cnt);
+
        if (!skb)
                newskb = alloc_skb(SGE_RX_PULL_LEN, GFP_ATOMIC);
+
        if (unlikely(!newskb)) {
                if (!drop_thres)
                        return NULL;
                goto recycle;
        }
 
-       pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr),
-                        fl->buf_size, PCI_DMA_FROMDEVICE);
+       pci_dma_sync_single_for_cpu(adap->pdev, dma_addr, len,
+                                   PCI_DMA_FROMDEVICE);
+       (*sd->pg_chunk.p_cnt)--;
+       if (!*sd->pg_chunk.p_cnt)
+               pci_unmap_page(adap->pdev,
+                              pci_unmap_addr(&sd->pg_chunk, mapping),
+                              fl->alloc_size,
+                              PCI_DMA_FROMDEVICE);
        if (!skb) {
                __skb_put(newskb, SGE_RX_PULL_LEN);
                memcpy(newskb->data, sd->pg_chunk.va, SGE_RX_PULL_LEN);
@@ -1089,7 +1143,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb,
        struct tx_desc *d = &q->desc[pidx];
        struct cpl_tx_pkt *cpl = (struct cpl_tx_pkt *)d;
 
-       cpl->len = htonl(skb->len | 0x80000000);
+       cpl->len = htonl(skb->len);
        cntrl = V_TXPKT_INTF(pi->port_id);
 
        if (vlan_tx_tag_present(skb) && pi->vlan_grp)
@@ -1958,8 +2012,8 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
        skb_pull(skb, sizeof(*p) + pad);
        skb->protocol = eth_type_trans(skb, adap->port[p->iff]);
        pi = netdev_priv(skb->dev);
-       if ((pi->rx_offload & T3_RX_CSUM) && p->csum_valid && p->csum == htons(0xffff) &&
-           !p->fragment) {
+       if ((pi->rx_offload & T3_RX_CSUM) && p->csum_valid &&
+           p->csum == htons(0xffff) && !p->fragment) {
                qs->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++;
                skb->ip_summed = CHECKSUM_UNNECESSARY;
        } else
@@ -2034,10 +2088,19 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs,
        fl->credits--;
 
        len -= offset;
-       pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr),
-                        fl->buf_size, PCI_DMA_FROMDEVICE);
+       pci_dma_sync_single_for_cpu(adap->pdev,
+                                   pci_unmap_addr(sd, dma_addr),
+                                   fl->buf_size - SGE_PG_RSVD,
+                                   PCI_DMA_FROMDEVICE);
 
-       prefetch(&qs->lro_frag_tbl);
+       (*sd->pg_chunk.p_cnt)--;
+       if (!*sd->pg_chunk.p_cnt)
+               pci_unmap_page(adap->pdev,
+                              pci_unmap_addr(&sd->pg_chunk, mapping),
+                              fl->alloc_size,
+                              PCI_DMA_FROMDEVICE);
+
+       prefetch(qs->lro_va);
 
        rx_frag += nr_frags;
        rx_frag->page = sd->pg_chunk.page;
@@ -2047,6 +2110,7 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs,
        qs->lro_frag_tbl.nr_frags++;
        qs->lro_frag_tbl.len = frag_len;
 
+
        if (!complete)
                return;
 
@@ -2236,6 +2300,8 @@ no_mem:
                        if (fl->use_pages) {
                                void *addr = fl->sdesc[fl->cidx].pg_chunk.va;
 
+                               prefetch(&qs->lro_frag_tbl);
+
                                prefetch(addr);
 #if L1_CACHE_BYTES < 128
                                prefetch(addr + L1_CACHE_BYTES);
@@ -2972,21 +3038,23 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports,
        q->fl[1].use_pages = FL1_PG_CHUNK_SIZE > 0;
        q->fl[0].order = FL0_PG_ORDER;
        q->fl[1].order = FL1_PG_ORDER;
+       q->fl[0].alloc_size = FL0_PG_ALLOC_SIZE;
+       q->fl[1].alloc_size = FL1_PG_ALLOC_SIZE;
 
        spin_lock_irq(&adapter->sge.reg_lock);
 
        /* FL threshold comparison uses < */
        ret = t3_sge_init_rspcntxt(adapter, q->rspq.cntxt_id, irq_vec_idx,
                                   q->rspq.phys_addr, q->rspq.size,
-                                  q->fl[0].buf_size, 1, 0);
+                                  q->fl[0].buf_size - SGE_PG_RSVD, 1, 0);
        if (ret)
                goto err_unlock;
 
        for (i = 0; i < SGE_RXQ_PER_SET; ++i) {
                ret = t3_sge_init_flcntxt(adapter, q->fl[i].cntxt_id, 0,
                                          q->fl[i].phys_addr, q->fl[i].size,
-                                         q->fl[i].buf_size, p->cong_thres, 1,
-                                         0);
+                                         q->fl[i].buf_size - SGE_PG_RSVD,
+                                         p->cong_thres, 1, 0);
                if (ret)
                        goto err_unlock;
        }
@@ -3044,9 +3112,6 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports,
        t3_write_reg(adapter, A_SG_GTS, V_RSPQ(q->rspq.cntxt_id) |
                     V_NEWTIMER(q->rspq.holdoff_tmr));
 
-       mod_timer(&q->tx_reclaim_timer, jiffies + TX_RECLAIM_PERIOD);
-       mod_timer(&q->rx_reclaim_timer, jiffies + RX_RECLAIM_PERIOD);
-
        return 0;
 
 err_unlock:
@@ -3056,6 +3121,27 @@ err:
        return ret;
 }
 
+/**
+ *      t3_start_sge_timers - start SGE timer call backs
+ *      @adap: the adapter
+ *
+ *      Starts each SGE queue set's timer call back
+ */
+void t3_start_sge_timers(struct adapter *adap)
+{
+       int i;
+
+       for (i = 0; i < SGE_QSETS; ++i) {
+               struct sge_qset *q = &adap->sge.qs[i];
+
+       if (q->tx_reclaim_timer.function)
+               mod_timer(&q->tx_reclaim_timer, jiffies + TX_RECLAIM_PERIOD);
+
+       if (q->rx_reclaim_timer.function)
+               mod_timer(&q->rx_reclaim_timer, jiffies + RX_RECLAIM_PERIOD);
+       }
+}
+
 /**
  *     t3_stop_sge_timers - stop SGE timer call backs
  *     @adap: the adapter
index ff262a04ded060ad227393b207a6454b80dce0bc..31ed31a3428b82e29ef29ee436e574c572272b31 100644 (file)
@@ -493,20 +493,20 @@ int t3_phy_lasi_intr_handler(struct cphy *phy)
 }
 
 static const struct adapter_info t3_adap_info[] = {
-       {2, 0,
+       {1, 1, 0,
         F_GPIO2_OEN | F_GPIO4_OEN |
         F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, { S_GPIO3, S_GPIO5 }, 0,
         &mi1_mdio_ops, "Chelsio PE9000"},
-       {2, 0,
+       {1, 1, 0,
         F_GPIO2_OEN | F_GPIO4_OEN |
         F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, { S_GPIO3, S_GPIO5 }, 0,
         &mi1_mdio_ops, "Chelsio T302"},
-       {1, 0,
+       {1, 0, 0,
         F_GPIO1_OEN | F_GPIO6_OEN | F_GPIO7_OEN | F_GPIO10_OEN |
         F_GPIO11_OEN | F_GPIO1_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL,
         { 0 }, SUPPORTED_10000baseT_Full | SUPPORTED_AUI,
         &mi1_mdio_ext_ops, "Chelsio T310"},
-       {2, 0,
+       {1, 1, 0,
         F_GPIO1_OEN | F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO5_OEN | F_GPIO6_OEN |
         F_GPIO7_OEN | F_GPIO10_OEN | F_GPIO11_OEN | F_GPIO1_OUT_VAL |
         F_GPIO5_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL,
@@ -514,7 +514,7 @@ static const struct adapter_info t3_adap_info[] = {
         &mi1_mdio_ext_ops, "Chelsio T320"},
        {},
        {},
-       {1, 0,
+       {1, 0, 0,
         F_GPIO1_OEN | F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO6_OEN | F_GPIO7_OEN |
         F_GPIO10_OEN | F_GPIO1_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL,
         { S_GPIO9 }, SUPPORTED_10000baseT_Full | SUPPORTED_AUI,
@@ -2128,16 +2128,40 @@ void t3_port_intr_clear(struct adapter *adapter, int idx)
 static int t3_sge_write_context(struct adapter *adapter, unsigned int id,
                                unsigned int type)
 {
-       t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0xffffffff);
-       t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0xffffffff);
-       t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0xffffffff);
-       t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0xffffffff);
+       if (type == F_RESPONSEQ) {
+               /*
+                * Can't write the Response Queue Context bits for
+                * Interrupt Armed or the Reserve bits after the chip
+                * has been initialized out of reset.  Writing to these
+                * bits can confuse the hardware.
+                */
+               t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0xffffffff);
+               t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0xffffffff);
+               t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0x17ffffff);
+               t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0xffffffff);
+       } else {
+               t3_write_reg(adapter, A_SG_CONTEXT_MASK0, 0xffffffff);
+               t3_write_reg(adapter, A_SG_CONTEXT_MASK1, 0xffffffff);
+               t3_write_reg(adapter, A_SG_CONTEXT_MASK2, 0xffffffff);
+               t3_write_reg(adapter, A_SG_CONTEXT_MASK3, 0xffffffff);
+       }
        t3_write_reg(adapter, A_SG_CONTEXT_CMD,
                     V_CONTEXT_CMD_OPCODE(1) | type | V_CONTEXT(id));
        return t3_wait_op_done(adapter, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY,
                               0, SG_CONTEXT_CMD_ATTEMPTS, 1);
 }
 
+/**
+ *     clear_sge_ctxt - completely clear an SGE context
+ *     @adapter: the adapter
+ *     @id: the context id
+ *     @type: the context type
+ *
+ *     Completely clear an SGE context.  Used predominantly at post-reset
+ *     initialization.  Note in particular that we don't skip writing to any
+ *     "sensitive bits" in the contexts the way that t3_sge_write_context()
+ *     does ...
+ */
 static int clear_sge_ctxt(struct adapter *adap, unsigned int id,
                          unsigned int type)
 {
@@ -2145,7 +2169,14 @@ static int clear_sge_ctxt(struct adapter *adap, unsigned int id,
        t3_write_reg(adap, A_SG_CONTEXT_DATA1, 0);
        t3_write_reg(adap, A_SG_CONTEXT_DATA2, 0);
        t3_write_reg(adap, A_SG_CONTEXT_DATA3, 0);
-       return t3_sge_write_context(adap, id, type);
+       t3_write_reg(adap, A_SG_CONTEXT_MASK0, 0xffffffff);
+       t3_write_reg(adap, A_SG_CONTEXT_MASK1, 0xffffffff);
+       t3_write_reg(adap, A_SG_CONTEXT_MASK2, 0xffffffff);
+       t3_write_reg(adap, A_SG_CONTEXT_MASK3, 0xffffffff);
+       t3_write_reg(adap, A_SG_CONTEXT_CMD,
+                    V_CONTEXT_CMD_OPCODE(1) | type | V_CONTEXT(id));
+       return t3_wait_op_done(adap, A_SG_CONTEXT_CMD, F_CONTEXT_CMD_BUSY,
+                              0, SG_CONTEXT_CMD_ATTEMPTS, 1);
 }
 
 /**
@@ -2729,10 +2760,10 @@ static void tp_config(struct adapter *adap, const struct tp_params *p)
                     F_TCPCHECKSUMOFFLOAD | V_IPTTL(64));
        t3_write_reg(adap, A_TP_TCP_OPTIONS, V_MTUDEFAULT(576) |
                     F_MTUENABLE | V_WINDOWSCALEMODE(1) |
-                    V_TIMESTAMPSMODE(0) | V_SACKMODE(1) | V_SACKRX(1));
+                    V_TIMESTAMPSMODE(1) | V_SACKMODE(1) | V_SACKRX(1));
        t3_write_reg(adap, A_TP_DACK_CONFIG, V_AUTOSTATE3(1) |
                     V_AUTOSTATE2(1) | V_AUTOSTATE1(0) |
-                    V_BYTETHRESHOLD(16384) | V_MSSTHRESHOLD(2) |
+                    V_BYTETHRESHOLD(26880) | V_MSSTHRESHOLD(2) |
                     F_AUTOCAREFUL | F_AUTOENABLE | V_DACK_MODE(1));
        t3_set_reg_field(adap, A_TP_IN_CONFIG, F_RXFBARBPRIO | F_TXFBARBPRIO,
                         F_IPV6ENABLE | F_NICMODE);
@@ -3196,20 +3227,22 @@ int t3_mps_set_active_ports(struct adapter *adap, unsigned int port_mask)
 }
 
 /*
- * Perform the bits of HW initialization that are dependent on the number
- * of available ports.
+ * Perform the bits of HW initialization that are dependent on the Tx
+ * channels being used.
  */
-static void init_hw_for_avail_ports(struct adapter *adap, int nports)
+static void chan_init_hw(struct adapter *adap, unsigned int chan_map)
 {
        int i;
 
-       if (nports == 1) {
+       if (chan_map != 3) {                                 /* one channel */
                t3_set_reg_field(adap, A_ULPRX_CTL, F_ROUND_ROBIN, 0);
                t3_set_reg_field(adap, A_ULPTX_CONFIG, F_CFG_RR_ARB, 0);
-               t3_write_reg(adap, A_MPS_CFG, F_TPRXPORTEN | F_TPTXPORT0EN |
-                            F_PORT0ACTIVE | F_ENFORCEPKT);
-               t3_write_reg(adap, A_PM1_TX_CFG, 0xffffffff);
-       } else {
+               t3_write_reg(adap, A_MPS_CFG, F_TPRXPORTEN | F_ENFORCEPKT |
+                            (chan_map == 1 ? F_TPTXPORT0EN | F_PORT0ACTIVE :
+                                             F_TPTXPORT1EN | F_PORT1ACTIVE));
+               t3_write_reg(adap, A_PM1_TX_CFG,
+                            chan_map == 1 ? 0xffffffff : 0);
+       } else {                                             /* two channels */
                t3_set_reg_field(adap, A_ULPRX_CTL, 0, F_ROUND_ROBIN);
                t3_set_reg_field(adap, A_ULPTX_CONFIG, 0, F_CFG_RR_ARB);
                t3_write_reg(adap, A_ULPTX_DMA_WEIGHT,
@@ -3517,7 +3550,7 @@ int t3_init_hw(struct adapter *adapter, u32 fw_params)
        t3_write_reg(adapter, A_PM1_RX_CFG, 0xffffffff);
        t3_write_reg(adapter, A_PM1_RX_MODE, 0);
        t3_write_reg(adapter, A_PM1_TX_MODE, 0);
-       init_hw_for_avail_ports(adapter, adapter->params.nports);
+       chan_init_hw(adapter, adapter->params.chan_map);
        t3_sge_init(adapter, &adapter->params.sge);
 
        t3_write_reg(adapter, A_T3DBG_GPIO_ACT_LOW, calc_gpio_intr(adapter));
@@ -3754,7 +3787,8 @@ int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai,
        get_pci_mode(adapter, &adapter->params.pci);
 
        adapter->params.info = ai;
-       adapter->params.nports = ai->nports;
+       adapter->params.nports = ai->nports0 + ai->nports1;
+       adapter->params.chan_map = !!ai->nports0 | (!!ai->nports1 << 1);
        adapter->params.rev = t3_read_reg(adapter, A_PL_REV);
        /*
         * We used to only run the "adapter check task" once a second if
@@ -3785,7 +3819,7 @@ int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai,
                mc7_prep(adapter, &adapter->pmtx, MC7_PMTX_BASE_ADDR, "PMTX");
                mc7_prep(adapter, &adapter->cm, MC7_CM_BASE_ADDR, "CM");
 
-               p->nchan = ai->nports;
+               p->nchan = adapter->params.chan_map == 3 ? 2 : 1;
                p->pmrx_size = t3_mc7_size(&adapter->pmrx);
                p->pmtx_size = t3_mc7_size(&adapter->pmtx);
                p->cm_size = t3_mc7_size(&adapter->cm);
index 55625dbbae5a8f8bf96415e8466f246c471f3585..357f565851edf7865c576ab4489a839c811ba369 100644 (file)
@@ -566,6 +566,18 @@ MODULE_LICENSE("GPL");
     outw(CSR0, DEPCA_ADDR);\
     outw(STOP, DEPCA_DATA)
 
+static const struct net_device_ops depca_netdev_ops = {
+       .ndo_open               = depca_open,
+       .ndo_start_xmit         = depca_start_xmit,
+       .ndo_stop               = depca_close,
+       .ndo_set_multicast_list = set_multicast_list,
+       .ndo_do_ioctl           = depca_ioctl,
+       .ndo_tx_timeout         = depca_tx_timeout,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 static int __init depca_hw_init (struct net_device *dev, struct device *device)
 {
        struct depca_private *lp;
@@ -793,12 +805,7 @@ static int __init depca_hw_init (struct net_device *dev, struct device *device)
        }
 
        /* The DEPCA-specific entries in the device structure. */
-       dev->open = &depca_open;
-       dev->hard_start_xmit = &depca_start_xmit;
-       dev->stop = &depca_close;
-       dev->set_multicast_list = &set_multicast_list;
-       dev->do_ioctl = &depca_ioctl;
-       dev->tx_timeout = depca_tx_timeout;
+       dev->netdev_ops = &depca_netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
        dev->mem_start = 0;
index e187c88ae1450329a51da2ba64024dd55da2c3e0..cc2ab6412c7382484073f405306a4a76775b59bd 100644 (file)
@@ -739,6 +739,17 @@ static void __init eepro_print_info (struct net_device *dev)
 
 static const struct ethtool_ops eepro_ethtool_ops;
 
+static const struct net_device_ops eepro_netdev_ops = {
+       .ndo_open               = eepro_open,
+       .ndo_stop               = eepro_close,
+       .ndo_start_xmit         = eepro_send_packet,
+       .ndo_set_multicast_list = set_multicast_list,
+       .ndo_tx_timeout         = eepro_tx_timeout,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 /* This is the real probe routine.  Linux has a history of friendly device
    probes on the ISA bus.  A good device probe avoids doing writes, and
    verifies that the correct device exists and functions.  */
@@ -851,11 +862,7 @@ static int __init eepro_probe1(struct net_device *dev, int autoprobe)
                }
        }
 
-       dev->open               = eepro_open;
-       dev->stop               = eepro_close;
-       dev->hard_start_xmit    = eepro_send_packet;
-       dev->set_multicast_list = &set_multicast_list;
-       dev->tx_timeout         = eepro_tx_timeout;
+       dev->netdev_ops         = &eepro_netdev_ops;
        dev->watchdog_timeo     = TX_TIMEOUT;
        dev->ethtool_ops        = &eepro_ethtool_ops;
 
index 9ff3f2f5e3829a431b1bb6f010fa172ff005374d..1686dca2874865a7d012bbc39bd9d77c63b305eb 100644 (file)
@@ -1043,6 +1043,17 @@ static void eexp_hw_tx_pio(struct net_device *dev, unsigned short *buf,
        lp->last_tx = jiffies;
 }
 
+static const struct net_device_ops eexp_netdev_ops = {
+       .ndo_open               = eexp_open,
+       .ndo_stop               = eexp_close,
+       .ndo_start_xmit         = eexp_xmit,
+       .ndo_set_multicast_list = eexp_set_multicast,
+       .ndo_tx_timeout         = eexp_timeout,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 /*
  * Sanity check the suspected EtherExpress card
  * Read hardware address, reset card, size memory and initialize buffer
@@ -1163,11 +1174,7 @@ static int __init eexp_hw_probe(struct net_device *dev, unsigned short ioaddr)
        lp->rx_buf_start = TX_BUF_START + (lp->num_tx_bufs*TX_BUF_SIZE);
        lp->width = buswidth;
 
-       dev->open = eexp_open;
-       dev->stop = eexp_close;
-       dev->hard_start_xmit = eexp_xmit;
-       dev->set_multicast_list = &eexp_set_multicast;
-       dev->tx_timeout = eexp_timeout;
+       dev->netdev_ops = &eexp_netdev_ops;
        dev->watchdog_timeo = 2*HZ;
 
        return register_netdev(dev);
index 5c048f2fd74f856bb4e3ef65a07d54376aa18eb6..0d8b6da046f244b95dc0d52703b599bd70f30354 100644 (file)
@@ -475,6 +475,17 @@ out:
 }
 #endif
 
+static const struct net_device_ops eth16i_netdev_ops = {
+       .ndo_open               = eth16i_open,
+       .ndo_stop               = eth16i_close,
+       .ndo_start_xmit         = eth16i_tx,
+       .ndo_set_multicast_list = eth16i_multicast,
+       .ndo_tx_timeout         = eth16i_timeout,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 static int __init eth16i_probe1(struct net_device *dev, int ioaddr)
 {
        struct eth16i_local *lp = netdev_priv(dev);
@@ -549,12 +560,7 @@ static int __init eth16i_probe1(struct net_device *dev, int ioaddr)
        BITCLR(ioaddr + CONFIG_REG_1, POWERUP);
 
        /* Initialize the device structure */
-       memset(lp, 0, sizeof(struct eth16i_local));
-       dev->open               = eth16i_open;
-       dev->stop               = eth16i_close;
-       dev->hard_start_xmit    = eth16i_tx;
-       dev->set_multicast_list = eth16i_multicast;
-       dev->tx_timeout         = eth16i_timeout;
+       dev->netdev_ops         = &eth16i_netdev_ops;
        dev->watchdog_timeo     = TX_TIMEOUT;
        spin_lock_init(&lp->lock);
 
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c
new file mode 100644 (file)
index 0000000..91a9b1a
--- /dev/null
@@ -0,0 +1,1112 @@
+/*
+ * linux/drivers/net/ethoc.c
+ *
+ * Copyright (C) 2007-2008 Avionic Design Development GmbH
+ * Copyright (C) 2008-2009 Avionic Design GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written by Thierry Reding <thierry.reding@avionic-design.de>
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/io.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <net/ethoc.h>
+
+/* register offsets */
+#define        MODER           0x00
+#define        INT_SOURCE      0x04
+#define        INT_MASK        0x08
+#define        IPGT            0x0c
+#define        IPGR1           0x10
+#define        IPGR2           0x14
+#define        PACKETLEN       0x18
+#define        COLLCONF        0x1c
+#define        TX_BD_NUM       0x20
+#define        CTRLMODER       0x24
+#define        MIIMODER        0x28
+#define        MIICOMMAND      0x2c
+#define        MIIADDRESS      0x30
+#define        MIITX_DATA      0x34
+#define        MIIRX_DATA      0x38
+#define        MIISTATUS       0x3c
+#define        MAC_ADDR0       0x40
+#define        MAC_ADDR1       0x44
+#define        ETH_HASH0       0x48
+#define        ETH_HASH1       0x4c
+#define        ETH_TXCTRL      0x50
+
+/* mode register */
+#define        MODER_RXEN      (1 <<  0) /* receive enable */
+#define        MODER_TXEN      (1 <<  1) /* transmit enable */
+#define        MODER_NOPRE     (1 <<  2) /* no preamble */
+#define        MODER_BRO       (1 <<  3) /* broadcast address */
+#define        MODER_IAM       (1 <<  4) /* individual address mode */
+#define        MODER_PRO       (1 <<  5) /* promiscuous mode */
+#define        MODER_IFG       (1 <<  6) /* interframe gap for incoming frames */
+#define        MODER_LOOP      (1 <<  7) /* loopback */
+#define        MODER_NBO       (1 <<  8) /* no back-off */
+#define        MODER_EDE       (1 <<  9) /* excess defer enable */
+#define        MODER_FULLD     (1 << 10) /* full duplex */
+#define        MODER_RESET     (1 << 11) /* FIXME: reset (undocumented) */
+#define        MODER_DCRC      (1 << 12) /* delayed CRC enable */
+#define        MODER_CRC       (1 << 13) /* CRC enable */
+#define        MODER_HUGE      (1 << 14) /* huge packets enable */
+#define        MODER_PAD       (1 << 15) /* padding enabled */
+#define        MODER_RSM       (1 << 16) /* receive small packets */
+
+/* interrupt source and mask registers */
+#define        INT_MASK_TXF    (1 << 0) /* transmit frame */
+#define        INT_MASK_TXE    (1 << 1) /* transmit error */
+#define        INT_MASK_RXF    (1 << 2) /* receive frame */
+#define        INT_MASK_RXE    (1 << 3) /* receive error */
+#define        INT_MASK_BUSY   (1 << 4)
+#define        INT_MASK_TXC    (1 << 5) /* transmit control frame */
+#define        INT_MASK_RXC    (1 << 6) /* receive control frame */
+
+#define        INT_MASK_TX     (INT_MASK_TXF | INT_MASK_TXE)
+#define        INT_MASK_RX     (INT_MASK_RXF | INT_MASK_RXE)
+
+#define        INT_MASK_ALL ( \
+               INT_MASK_TXF | INT_MASK_TXE | \
+               INT_MASK_RXF | INT_MASK_RXE | \
+               INT_MASK_TXC | INT_MASK_RXC | \
+               INT_MASK_BUSY \
+       )
+
+/* packet length register */
+#define        PACKETLEN_MIN(min)              (((min) & 0xffff) << 16)
+#define        PACKETLEN_MAX(max)              (((max) & 0xffff) <<  0)
+#define        PACKETLEN_MIN_MAX(min, max)     (PACKETLEN_MIN(min) | \
+                                       PACKETLEN_MAX(max))
+
+/* transmit buffer number register */
+#define        TX_BD_NUM_VAL(x)        (((x) <= 0x80) ? (x) : 0x80)
+
+/* control module mode register */
+#define        CTRLMODER_PASSALL       (1 << 0) /* pass all receive frames */
+#define        CTRLMODER_RXFLOW        (1 << 1) /* receive control flow */
+#define        CTRLMODER_TXFLOW        (1 << 2) /* transmit control flow */
+
+/* MII mode register */
+#define        MIIMODER_CLKDIV(x)      ((x) & 0xfe) /* needs to be an even number */
+#define        MIIMODER_NOPRE          (1 << 8) /* no preamble */
+
+/* MII command register */
+#define        MIICOMMAND_SCAN         (1 << 0) /* scan status */
+#define        MIICOMMAND_READ         (1 << 1) /* read status */
+#define        MIICOMMAND_WRITE        (1 << 2) /* write control data */
+
+/* MII address register */
+#define        MIIADDRESS_FIAD(x)              (((x) & 0x1f) << 0)
+#define        MIIADDRESS_RGAD(x)              (((x) & 0x1f) << 8)
+#define        MIIADDRESS_ADDR(phy, reg)       (MIIADDRESS_FIAD(phy) | \
+                                       MIIADDRESS_RGAD(reg))
+
+/* MII transmit data register */
+#define        MIITX_DATA_VAL(x)       ((x) & 0xffff)
+
+/* MII receive data register */
+#define        MIIRX_DATA_VAL(x)       ((x) & 0xffff)
+
+/* MII status register */
+#define        MIISTATUS_LINKFAIL      (1 << 0)
+#define        MIISTATUS_BUSY          (1 << 1)
+#define        MIISTATUS_INVALID       (1 << 2)
+
+/* TX buffer descriptor */
+#define        TX_BD_CS                (1 <<  0) /* carrier sense lost */
+#define        TX_BD_DF                (1 <<  1) /* defer indication */
+#define        TX_BD_LC                (1 <<  2) /* late collision */
+#define        TX_BD_RL                (1 <<  3) /* retransmission limit */
+#define        TX_BD_RETRY_MASK        (0x00f0)
+#define        TX_BD_RETRY(x)          (((x) & 0x00f0) >>  4)
+#define        TX_BD_UR                (1 <<  8) /* transmitter underrun */
+#define        TX_BD_CRC               (1 << 11) /* TX CRC enable */
+#define        TX_BD_PAD               (1 << 12) /* pad enable for short packets */
+#define        TX_BD_WRAP              (1 << 13)
+#define        TX_BD_IRQ               (1 << 14) /* interrupt request enable */
+#define        TX_BD_READY             (1 << 15) /* TX buffer ready */
+#define        TX_BD_LEN(x)            (((x) & 0xffff) << 16)
+#define        TX_BD_LEN_MASK          (0xffff << 16)
+
+#define        TX_BD_STATS             (TX_BD_CS | TX_BD_DF | TX_BD_LC | \
+                               TX_BD_RL | TX_BD_RETRY_MASK | TX_BD_UR)
+
+/* RX buffer descriptor */
+#define        RX_BD_LC        (1 <<  0) /* late collision */
+#define        RX_BD_CRC       (1 <<  1) /* RX CRC error */
+#define        RX_BD_SF        (1 <<  2) /* short frame */
+#define        RX_BD_TL        (1 <<  3) /* too long */
+#define        RX_BD_DN        (1 <<  4) /* dribble nibble */
+#define        RX_BD_IS        (1 <<  5) /* invalid symbol */
+#define        RX_BD_OR        (1 <<  6) /* receiver overrun */
+#define        RX_BD_MISS      (1 <<  7)
+#define        RX_BD_CF        (1 <<  8) /* control frame */
+#define        RX_BD_WRAP      (1 << 13)
+#define        RX_BD_IRQ       (1 << 14) /* interrupt request enable */
+#define        RX_BD_EMPTY     (1 << 15)
+#define        RX_BD_LEN(x)    (((x) & 0xffff) << 16)
+
+#define        RX_BD_STATS     (RX_BD_LC | RX_BD_CRC | RX_BD_SF | RX_BD_TL | \
+                       RX_BD_DN | RX_BD_IS | RX_BD_OR | RX_BD_MISS)
+
+#define        ETHOC_BUFSIZ            1536
+#define        ETHOC_ZLEN              64
+#define        ETHOC_BD_BASE           0x400
+#define        ETHOC_TIMEOUT           (HZ / 2)
+#define        ETHOC_MII_TIMEOUT       (1 + (HZ / 5))
+
+/**
+ * struct ethoc - driver-private device structure
+ * @iobase:    pointer to I/O memory region
+ * @membase:   pointer to buffer memory region
+ * @num_tx:    number of send buffers
+ * @cur_tx:    last send buffer written
+ * @dty_tx:    last buffer actually sent
+ * @num_rx:    number of receive buffers
+ * @cur_rx:    current receive buffer
+ * @netdev:    pointer to network device structure
+ * @napi:      NAPI structure
+ * @stats:     network device statistics
+ * @msg_enable:        device state flags
+ * @rx_lock:   receive lock
+ * @lock:      device lock
+ * @phy:       attached PHY
+ * @mdio:      MDIO bus for PHY access
+ * @phy_id:    address of attached PHY
+ */
+struct ethoc {
+       void __iomem *iobase;
+       void __iomem *membase;
+
+       unsigned int num_tx;
+       unsigned int cur_tx;
+       unsigned int dty_tx;
+
+       unsigned int num_rx;
+       unsigned int cur_rx;
+
+       struct net_device *netdev;
+       struct napi_struct napi;
+       struct net_device_stats stats;
+       u32 msg_enable;
+
+       spinlock_t rx_lock;
+       spinlock_t lock;
+
+       struct phy_device *phy;
+       struct mii_bus *mdio;
+       s8 phy_id;
+};
+
+/**
+ * struct ethoc_bd - buffer descriptor
+ * @stat:      buffer statistics
+ * @addr:      physical memory address
+ */
+struct ethoc_bd {
+       u32 stat;
+       u32 addr;
+};
+
+static u32 ethoc_read(struct ethoc *dev, loff_t offset)
+{
+       return ioread32(dev->iobase + offset);
+}
+
+static void ethoc_write(struct ethoc *dev, loff_t offset, u32 data)
+{
+       iowrite32(data, dev->iobase + offset);
+}
+
+static void ethoc_read_bd(struct ethoc *dev, int index, struct ethoc_bd *bd)
+{
+       loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd));
+       bd->stat = ethoc_read(dev, offset + 0);
+       bd->addr = ethoc_read(dev, offset + 4);
+}
+
+static void ethoc_write_bd(struct ethoc *dev, int index,
+               const struct ethoc_bd *bd)
+{
+       loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd));
+       ethoc_write(dev, offset + 0, bd->stat);
+       ethoc_write(dev, offset + 4, bd->addr);
+}
+
+static void ethoc_enable_irq(struct ethoc *dev, u32 mask)
+{
+       u32 imask = ethoc_read(dev, INT_MASK);
+       imask |= mask;
+       ethoc_write(dev, INT_MASK, imask);
+}
+
+static void ethoc_disable_irq(struct ethoc *dev, u32 mask)
+{
+       u32 imask = ethoc_read(dev, INT_MASK);
+       imask &= ~mask;
+       ethoc_write(dev, INT_MASK, imask);
+}
+
+static void ethoc_ack_irq(struct ethoc *dev, u32 mask)
+{
+       ethoc_write(dev, INT_SOURCE, mask);
+}
+
+static void ethoc_enable_rx_and_tx(struct ethoc *dev)
+{
+       u32 mode = ethoc_read(dev, MODER);
+       mode |= MODER_RXEN | MODER_TXEN;
+       ethoc_write(dev, MODER, mode);
+}
+
+static void ethoc_disable_rx_and_tx(struct ethoc *dev)
+{
+       u32 mode = ethoc_read(dev, MODER);
+       mode &= ~(MODER_RXEN | MODER_TXEN);
+       ethoc_write(dev, MODER, mode);
+}
+
+static int ethoc_init_ring(struct ethoc *dev)
+{
+       struct ethoc_bd bd;
+       int i;
+
+       dev->cur_tx = 0;
+       dev->dty_tx = 0;
+       dev->cur_rx = 0;
+
+       /* setup transmission buffers */
+       bd.addr = 0;
+       bd.stat = TX_BD_IRQ | TX_BD_CRC;
+
+       for (i = 0; i < dev->num_tx; i++) {
+               if (i == dev->num_tx - 1)
+                       bd.stat |= TX_BD_WRAP;
+
+               ethoc_write_bd(dev, i, &bd);
+               bd.addr += ETHOC_BUFSIZ;
+       }
+
+       bd.addr = dev->num_tx * ETHOC_BUFSIZ;
+       bd.stat = RX_BD_EMPTY | RX_BD_IRQ;
+
+       for (i = 0; i < dev->num_rx; i++) {
+               if (i == dev->num_rx - 1)
+                       bd.stat |= RX_BD_WRAP;
+
+               ethoc_write_bd(dev, dev->num_tx + i, &bd);
+               bd.addr += ETHOC_BUFSIZ;
+       }
+
+       return 0;
+}
+
+static int ethoc_reset(struct ethoc *dev)
+{
+       u32 mode;
+
+       /* TODO: reset controller? */
+
+       ethoc_disable_rx_and_tx(dev);
+
+       /* TODO: setup registers */
+
+       /* enable FCS generation and automatic padding */
+       mode = ethoc_read(dev, MODER);
+       mode |= MODER_CRC | MODER_PAD;
+       ethoc_write(dev, MODER, mode);
+
+       /* set full-duplex mode */
+       mode = ethoc_read(dev, MODER);
+       mode |= MODER_FULLD;
+       ethoc_write(dev, MODER, mode);
+       ethoc_write(dev, IPGT, 0x15);
+
+       ethoc_ack_irq(dev, INT_MASK_ALL);
+       ethoc_enable_irq(dev, INT_MASK_ALL);
+       ethoc_enable_rx_and_tx(dev);
+       return 0;
+}
+
+static unsigned int ethoc_update_rx_stats(struct ethoc *dev,
+               struct ethoc_bd *bd)
+{
+       struct net_device *netdev = dev->netdev;
+       unsigned int ret = 0;
+
+       if (bd->stat & RX_BD_TL) {
+               dev_err(&netdev->dev, "RX: frame too long\n");
+               dev->stats.rx_length_errors++;
+               ret++;
+       }
+
+       if (bd->stat & RX_BD_SF) {
+               dev_err(&netdev->dev, "RX: frame too short\n");
+               dev->stats.rx_length_errors++;
+               ret++;
+       }
+
+       if (bd->stat & RX_BD_DN) {
+               dev_err(&netdev->dev, "RX: dribble nibble\n");
+               dev->stats.rx_frame_errors++;
+       }
+
+       if (bd->stat & RX_BD_CRC) {
+               dev_err(&netdev->dev, "RX: wrong CRC\n");
+               dev->stats.rx_crc_errors++;
+               ret++;
+       }
+
+       if (bd->stat & RX_BD_OR) {
+               dev_err(&netdev->dev, "RX: overrun\n");
+               dev->stats.rx_over_errors++;
+               ret++;
+       }
+
+       if (bd->stat & RX_BD_MISS)
+               dev->stats.rx_missed_errors++;
+
+       if (bd->stat & RX_BD_LC) {
+               dev_err(&netdev->dev, "RX: late collision\n");
+               dev->stats.collisions++;
+               ret++;
+       }
+
+       return ret;
+}
+
+static int ethoc_rx(struct net_device *dev, int limit)
+{
+       struct ethoc *priv = netdev_priv(dev);
+       int count;
+
+       for (count = 0; count < limit; ++count) {
+               unsigned int entry;
+               struct ethoc_bd bd;
+
+               entry = priv->num_tx + (priv->cur_rx % priv->num_rx);
+               ethoc_read_bd(priv, entry, &bd);
+               if (bd.stat & RX_BD_EMPTY)
+                       break;
+
+               if (ethoc_update_rx_stats(priv, &bd) == 0) {
+                       int size = bd.stat >> 16;
+                       struct sk_buff *skb = netdev_alloc_skb(dev, size);
+                       if (likely(skb)) {
+                               void *src = priv->membase + bd.addr;
+                               memcpy_fromio(skb_put(skb, size), src, size);
+                               skb->protocol = eth_type_trans(skb, dev);
+                               dev->last_rx = jiffies;
+                               priv->stats.rx_packets++;
+                               priv->stats.rx_bytes += size;
+                               netif_receive_skb(skb);
+                       } else {
+                               if (net_ratelimit())
+                                       dev_warn(&dev->dev, "low on memory - "
+                                                       "packet dropped\n");
+
+                               priv->stats.rx_dropped++;
+                               break;
+                       }
+               }
+
+               /* clear the buffer descriptor so it can be reused */
+               bd.stat &= ~RX_BD_STATS;
+               bd.stat |=  RX_BD_EMPTY;
+               ethoc_write_bd(priv, entry, &bd);
+               priv->cur_rx++;
+       }
+
+       return count;
+}
+
+static int ethoc_update_tx_stats(struct ethoc *dev, struct ethoc_bd *bd)
+{
+       struct net_device *netdev = dev->netdev;
+
+       if (bd->stat & TX_BD_LC) {
+               dev_err(&netdev->dev, "TX: late collision\n");
+               dev->stats.tx_window_errors++;
+       }
+
+       if (bd->stat & TX_BD_RL) {
+               dev_err(&netdev->dev, "TX: retransmit limit\n");
+               dev->stats.tx_aborted_errors++;
+       }
+
+       if (bd->stat & TX_BD_UR) {
+               dev_err(&netdev->dev, "TX: underrun\n");
+               dev->stats.tx_fifo_errors++;
+       }
+
+       if (bd->stat & TX_BD_CS) {
+               dev_err(&netdev->dev, "TX: carrier sense lost\n");
+               dev->stats.tx_carrier_errors++;
+       }
+
+       if (bd->stat & TX_BD_STATS)
+               dev->stats.tx_errors++;
+
+       dev->stats.collisions += (bd->stat >> 4) & 0xf;
+       dev->stats.tx_bytes += bd->stat >> 16;
+       dev->stats.tx_packets++;
+       return 0;
+}
+
+static void ethoc_tx(struct net_device *dev)
+{
+       struct ethoc *priv = netdev_priv(dev);
+
+       spin_lock(&priv->lock);
+
+       while (priv->dty_tx != priv->cur_tx) {
+               unsigned int entry = priv->dty_tx % priv->num_tx;
+               struct ethoc_bd bd;
+
+               ethoc_read_bd(priv, entry, &bd);
+               if (bd.stat & TX_BD_READY)
+                       break;
+
+               entry = (++priv->dty_tx) % priv->num_tx;
+               (void)ethoc_update_tx_stats(priv, &bd);
+       }
+
+       if ((priv->cur_tx - priv->dty_tx) <= (priv->num_tx / 2))
+               netif_wake_queue(dev);
+
+       ethoc_ack_irq(priv, INT_MASK_TX);
+       spin_unlock(&priv->lock);
+}
+
+static irqreturn_t ethoc_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct ethoc *priv = netdev_priv(dev);
+       u32 pending;
+
+       ethoc_disable_irq(priv, INT_MASK_ALL);
+       pending = ethoc_read(priv, INT_SOURCE);
+       if (unlikely(pending == 0)) {
+               ethoc_enable_irq(priv, INT_MASK_ALL);
+               return IRQ_NONE;
+       }
+
+       ethoc_ack_irq(priv, INT_MASK_ALL);
+
+       if (pending & INT_MASK_BUSY) {
+               dev_err(&dev->dev, "packet dropped\n");
+               priv->stats.rx_dropped++;
+       }
+
+       if (pending & INT_MASK_RX) {
+               if (napi_schedule_prep(&priv->napi))
+                       __napi_schedule(&priv->napi);
+       } else {
+               ethoc_enable_irq(priv, INT_MASK_RX);
+       }
+
+       if (pending & INT_MASK_TX)
+               ethoc_tx(dev);
+
+       ethoc_enable_irq(priv, INT_MASK_ALL & ~INT_MASK_RX);
+       return IRQ_HANDLED;
+}
+
+static int ethoc_get_mac_address(struct net_device *dev, void *addr)
+{
+       struct ethoc *priv = netdev_priv(dev);
+       u8 *mac = (u8 *)addr;
+       u32 reg;
+
+       reg = ethoc_read(priv, MAC_ADDR0);
+       mac[2] = (reg >> 24) & 0xff;
+       mac[3] = (reg >> 16) & 0xff;
+       mac[4] = (reg >>  8) & 0xff;
+       mac[5] = (reg >>  0) & 0xff;
+
+       reg = ethoc_read(priv, MAC_ADDR1);
+       mac[0] = (reg >>  8) & 0xff;
+       mac[1] = (reg >>  0) & 0xff;
+
+       return 0;
+}
+
+static int ethoc_poll(struct napi_struct *napi, int budget)
+{
+       struct ethoc *priv = container_of(napi, struct ethoc, napi);
+       int work_done = 0;
+
+       work_done = ethoc_rx(priv->netdev, budget);
+       if (work_done < budget) {
+               ethoc_enable_irq(priv, INT_MASK_RX);
+               napi_complete(napi);
+       }
+
+       return work_done;
+}
+
+static int ethoc_mdio_read(struct mii_bus *bus, int phy, int reg)
+{
+       unsigned long timeout = jiffies + ETHOC_MII_TIMEOUT;
+       struct ethoc *priv = bus->priv;
+
+       ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(phy, reg));
+       ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ);
+
+       while (time_before(jiffies, timeout)) {
+               u32 status = ethoc_read(priv, MIISTATUS);
+               if (!(status & MIISTATUS_BUSY)) {
+                       u32 data = ethoc_read(priv, MIIRX_DATA);
+                       /* reset MII command register */
+                       ethoc_write(priv, MIICOMMAND, 0);
+                       return data;
+               }
+
+               schedule();
+       }
+
+       return -EBUSY;
+}
+
+static int ethoc_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
+{
+       unsigned long timeout = jiffies + ETHOC_MII_TIMEOUT;
+       struct ethoc *priv = bus->priv;
+
+       ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(phy, reg));
+       ethoc_write(priv, MIITX_DATA, val);
+       ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE);
+
+       while (time_before(jiffies, timeout)) {
+               u32 stat = ethoc_read(priv, MIISTATUS);
+               if (!(stat & MIISTATUS_BUSY))
+                       return 0;
+
+               schedule();
+       }
+
+       return -EBUSY;
+}
+
+static int ethoc_mdio_reset(struct mii_bus *bus)
+{
+       return 0;
+}
+
+static void ethoc_mdio_poll(struct net_device *dev)
+{
+}
+
+static int ethoc_mdio_probe(struct net_device *dev)
+{
+       struct ethoc *priv = netdev_priv(dev);
+       struct phy_device *phy;
+       int i;
+
+       for (i = 0; i < PHY_MAX_ADDR; i++) {
+               phy = priv->mdio->phy_map[i];
+               if (phy) {
+                       if (priv->phy_id != -1) {
+                               /* attach to specified PHY */
+                               if (priv->phy_id == phy->addr)
+                                       break;
+                       } else {
+                               /* autoselect PHY if none was specified */
+                               if (phy->addr != 0)
+                                       break;
+                       }
+               }
+       }
+
+       if (!phy) {
+               dev_err(&dev->dev, "no PHY found\n");
+               return -ENXIO;
+       }
+
+       phy = phy_connect(dev, dev_name(&phy->dev), &ethoc_mdio_poll, 0,
+                       PHY_INTERFACE_MODE_GMII);
+       if (IS_ERR(phy)) {
+               dev_err(&dev->dev, "could not attach to PHY\n");
+               return PTR_ERR(phy);
+       }
+
+       priv->phy = phy;
+       return 0;
+}
+
+static int ethoc_open(struct net_device *dev)
+{
+       struct ethoc *priv = netdev_priv(dev);
+       unsigned int min_tx = 2;
+       unsigned int num_bd;
+       int ret;
+
+       ret = request_irq(dev->irq, ethoc_interrupt, IRQF_SHARED,
+                       dev->name, dev);
+       if (ret)
+               return ret;
+
+       /* calculate the number of TX/RX buffers */
+       num_bd = (dev->mem_end - dev->mem_start + 1) / ETHOC_BUFSIZ;
+       priv->num_tx = min(min_tx, num_bd / 4);
+       priv->num_rx = num_bd - priv->num_tx;
+       ethoc_write(priv, TX_BD_NUM, priv->num_tx);
+
+       ethoc_init_ring(priv);
+       ethoc_reset(priv);
+
+       if (netif_queue_stopped(dev)) {
+               dev_dbg(&dev->dev, " resuming queue\n");
+               netif_wake_queue(dev);
+       } else {
+               dev_dbg(&dev->dev, " starting queue\n");
+               netif_start_queue(dev);
+       }
+
+       phy_start(priv->phy);
+       napi_enable(&priv->napi);
+
+       if (netif_msg_ifup(priv)) {
+               dev_info(&dev->dev, "I/O: %08lx Memory: %08lx-%08lx\n",
+                               dev->base_addr, dev->mem_start, dev->mem_end);
+       }
+
+       return 0;
+}
+
+static int ethoc_stop(struct net_device *dev)
+{
+       struct ethoc *priv = netdev_priv(dev);
+
+       napi_disable(&priv->napi);
+
+       if (priv->phy)
+               phy_stop(priv->phy);
+
+       ethoc_disable_rx_and_tx(priv);
+       free_irq(dev->irq, dev);
+
+       if (!netif_queue_stopped(dev))
+               netif_stop_queue(dev);
+
+       return 0;
+}
+
+static int ethoc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       struct ethoc *priv = netdev_priv(dev);
+       struct mii_ioctl_data *mdio = if_mii(ifr);
+       struct phy_device *phy = NULL;
+
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       if (cmd != SIOCGMIIPHY) {
+               if (mdio->phy_id >= PHY_MAX_ADDR)
+                       return -ERANGE;
+
+               phy = priv->mdio->phy_map[mdio->phy_id];
+               if (!phy)
+                       return -ENODEV;
+       } else {
+               phy = priv->phy;
+       }
+
+       return phy_mii_ioctl(phy, mdio, cmd);
+}
+
+static int ethoc_config(struct net_device *dev, struct ifmap *map)
+{
+       return -ENOSYS;
+}
+
+static int ethoc_set_mac_address(struct net_device *dev, void *addr)
+{
+       struct ethoc *priv = netdev_priv(dev);
+       u8 *mac = (u8 *)addr;
+
+       ethoc_write(priv, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) |
+                                    (mac[4] <<  8) | (mac[5] <<  0));
+       ethoc_write(priv, MAC_ADDR1, (mac[0] <<  8) | (mac[1] <<  0));
+
+       return 0;
+}
+
+static void ethoc_set_multicast_list(struct net_device *dev)
+{
+       struct ethoc *priv = netdev_priv(dev);
+       u32 mode = ethoc_read(priv, MODER);
+       struct dev_mc_list *mc = NULL;
+       u32 hash[2] = { 0, 0 };
+
+       /* set loopback mode if requested */
+       if (dev->flags & IFF_LOOPBACK)
+               mode |=  MODER_LOOP;
+       else
+               mode &= ~MODER_LOOP;
+
+       /* receive broadcast frames if requested */
+       if (dev->flags & IFF_BROADCAST)
+               mode &= ~MODER_BRO;
+       else
+               mode |=  MODER_BRO;
+
+       /* enable promiscuous mode if requested */
+       if (dev->flags & IFF_PROMISC)
+               mode |=  MODER_PRO;
+       else
+               mode &= ~MODER_PRO;
+
+       ethoc_write(priv, MODER, mode);
+
+       /* receive multicast frames */
+       if (dev->flags & IFF_ALLMULTI) {
+               hash[0] = 0xffffffff;
+               hash[1] = 0xffffffff;
+       } else {
+               for (mc = dev->mc_list; mc; mc = mc->next) {
+                       u32 crc = ether_crc(mc->dmi_addrlen, mc->dmi_addr);
+                       int bit = (crc >> 26) & 0x3f;
+                       hash[bit >> 5] |= 1 << (bit & 0x1f);
+               }
+       }
+
+       ethoc_write(priv, ETH_HASH0, hash[0]);
+       ethoc_write(priv, ETH_HASH1, hash[1]);
+}
+
+static int ethoc_change_mtu(struct net_device *dev, int new_mtu)
+{
+       return -ENOSYS;
+}
+
+static void ethoc_tx_timeout(struct net_device *dev)
+{
+       struct ethoc *priv = netdev_priv(dev);
+       u32 pending = ethoc_read(priv, INT_SOURCE);
+       if (likely(pending))
+               ethoc_interrupt(dev->irq, dev);
+}
+
+static struct net_device_stats *ethoc_stats(struct net_device *dev)
+{
+       struct ethoc *priv = netdev_priv(dev);
+       return &priv->stats;
+}
+
+static int ethoc_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ethoc *priv = netdev_priv(dev);
+       struct ethoc_bd bd;
+       unsigned int entry;
+       void *dest;
+
+       if (unlikely(skb->len > ETHOC_BUFSIZ)) {
+               priv->stats.tx_errors++;
+               return -EMSGSIZE;
+       }
+
+       entry = priv->cur_tx % priv->num_tx;
+       spin_lock_irq(&priv->lock);
+       priv->cur_tx++;
+
+       ethoc_read_bd(priv, entry, &bd);
+       if (unlikely(skb->len < ETHOC_ZLEN))
+               bd.stat |=  TX_BD_PAD;
+       else
+               bd.stat &= ~TX_BD_PAD;
+
+       dest = priv->membase + bd.addr;
+       memcpy_toio(dest, skb->data, skb->len);
+
+       bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK);
+       bd.stat |= TX_BD_LEN(skb->len);
+       ethoc_write_bd(priv, entry, &bd);
+
+       bd.stat |= TX_BD_READY;
+       ethoc_write_bd(priv, entry, &bd);
+
+       if (priv->cur_tx == (priv->dty_tx + priv->num_tx)) {
+               dev_dbg(&dev->dev, "stopping queue\n");
+               netif_stop_queue(dev);
+       }
+
+       dev->trans_start = jiffies;
+       dev_kfree_skb(skb);
+
+       spin_unlock_irq(&priv->lock);
+       return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops ethoc_netdev_ops = {
+       .ndo_open = ethoc_open,
+       .ndo_stop = ethoc_stop,
+       .ndo_do_ioctl = ethoc_ioctl,
+       .ndo_set_config = ethoc_config,
+       .ndo_set_mac_address = ethoc_set_mac_address,
+       .ndo_set_multicast_list = ethoc_set_multicast_list,
+       .ndo_change_mtu = ethoc_change_mtu,
+       .ndo_tx_timeout = ethoc_tx_timeout,
+       .ndo_get_stats = ethoc_stats,
+       .ndo_start_xmit = ethoc_start_xmit,
+};
+
+/**
+ * ethoc_probe() - initialize OpenCores ethernet MAC
+ * pdev:       platform device
+ */
+static int ethoc_probe(struct platform_device *pdev)
+{
+       struct net_device *netdev = NULL;
+       struct resource *res = NULL;
+       struct resource *mmio = NULL;
+       struct resource *mem = NULL;
+       struct ethoc *priv = NULL;
+       unsigned int phy;
+       int ret = 0;
+
+       /* allocate networking device */
+       netdev = alloc_etherdev(sizeof(struct ethoc));
+       if (!netdev) {
+               dev_err(&pdev->dev, "cannot allocate network device\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       SET_NETDEV_DEV(netdev, &pdev->dev);
+       platform_set_drvdata(pdev, netdev);
+
+       /* obtain I/O memory space */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "cannot obtain I/O memory space\n");
+               ret = -ENXIO;
+               goto free;
+       }
+
+       mmio = devm_request_mem_region(&pdev->dev, res->start,
+                       res->end - res->start + 1, res->name);
+       if (!res) {
+               dev_err(&pdev->dev, "cannot request I/O memory space\n");
+               ret = -ENXIO;
+               goto free;
+       }
+
+       netdev->base_addr = mmio->start;
+
+       /* obtain buffer memory space */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!res) {
+               dev_err(&pdev->dev, "cannot obtain memory space\n");
+               ret = -ENXIO;
+               goto free;
+       }
+
+       mem = devm_request_mem_region(&pdev->dev, res->start,
+                       res->end - res->start + 1, res->name);
+       if (!mem) {
+               dev_err(&pdev->dev, "cannot request memory space\n");
+               ret = -ENXIO;
+               goto free;
+       }
+
+       netdev->mem_start = mem->start;
+       netdev->mem_end   = mem->end;
+
+       /* obtain device IRQ number */
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "cannot obtain IRQ\n");
+               ret = -ENXIO;
+               goto free;
+       }
+
+       netdev->irq = res->start;
+
+       /* setup driver-private data */
+       priv = netdev_priv(netdev);
+       priv->netdev = netdev;
+
+       priv->iobase = devm_ioremap_nocache(&pdev->dev, netdev->base_addr,
+                       mmio->end - mmio->start + 1);
+       if (!priv->iobase) {
+               dev_err(&pdev->dev, "cannot remap I/O memory space\n");
+               ret = -ENXIO;
+               goto error;
+       }
+
+       priv->membase = devm_ioremap_nocache(&pdev->dev, netdev->mem_start,
+                       mem->end - mem->start + 1);
+       if (!priv->membase) {
+               dev_err(&pdev->dev, "cannot remap memory space\n");
+               ret = -ENXIO;
+               goto error;
+       }
+
+       /* Allow the platform setup code to pass in a MAC address. */
+       if (pdev->dev.platform_data) {
+               struct ethoc_platform_data *pdata =
+                       (struct ethoc_platform_data *)pdev->dev.platform_data;
+               memcpy(netdev->dev_addr, pdata->hwaddr, IFHWADDRLEN);
+               priv->phy_id = pdata->phy_id;
+       }
+
+       /* Check that the given MAC address is valid. If it isn't, read the
+        * current MAC from the controller. */
+       if (!is_valid_ether_addr(netdev->dev_addr))
+               ethoc_get_mac_address(netdev, netdev->dev_addr);
+
+       /* Check the MAC again for validity, if it still isn't choose and
+        * program a random one. */
+       if (!is_valid_ether_addr(netdev->dev_addr))
+               random_ether_addr(netdev->dev_addr);
+
+       ethoc_set_mac_address(netdev, netdev->dev_addr);
+
+       /* register MII bus */
+       priv->mdio = mdiobus_alloc();
+       if (!priv->mdio) {
+               ret = -ENOMEM;
+               goto free;
+       }
+
+       priv->mdio->name = "ethoc-mdio";
+       snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "%s-%d",
+                       priv->mdio->name, pdev->id);
+       priv->mdio->read = ethoc_mdio_read;
+       priv->mdio->write = ethoc_mdio_write;
+       priv->mdio->reset = ethoc_mdio_reset;
+       priv->mdio->priv = priv;
+
+       priv->mdio->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+       if (!priv->mdio->irq) {
+               ret = -ENOMEM;
+               goto free_mdio;
+       }
+
+       for (phy = 0; phy < PHY_MAX_ADDR; phy++)
+               priv->mdio->irq[phy] = PHY_POLL;
+
+       ret = mdiobus_register(priv->mdio);
+       if (ret) {
+               dev_err(&netdev->dev, "failed to register MDIO bus\n");
+               goto free_mdio;
+       }
+
+       ret = ethoc_mdio_probe(netdev);
+       if (ret) {
+               dev_err(&netdev->dev, "failed to probe MDIO bus\n");
+               goto error;
+       }
+
+       ether_setup(netdev);
+
+       /* setup the net_device structure */
+       netdev->netdev_ops = &ethoc_netdev_ops;
+       netdev->watchdog_timeo = ETHOC_TIMEOUT;
+       netdev->features |= 0;
+
+       /* setup NAPI */
+       memset(&priv->napi, 0, sizeof(priv->napi));
+       netif_napi_add(netdev, &priv->napi, ethoc_poll, 64);
+
+       spin_lock_init(&priv->rx_lock);
+       spin_lock_init(&priv->lock);
+
+       ret = register_netdev(netdev);
+       if (ret < 0) {
+               dev_err(&netdev->dev, "failed to register interface\n");
+               goto error;
+       }
+
+       goto out;
+
+error:
+       mdiobus_unregister(priv->mdio);
+free_mdio:
+       kfree(priv->mdio->irq);
+       mdiobus_free(priv->mdio);
+free:
+       free_netdev(netdev);
+out:
+       return ret;
+}
+
+/**
+ * ethoc_remove() - shutdown OpenCores ethernet MAC
+ * @pdev:      platform device
+ */
+static int ethoc_remove(struct platform_device *pdev)
+{
+       struct net_device *netdev = platform_get_drvdata(pdev);
+       struct ethoc *priv = netdev_priv(netdev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       if (netdev) {
+               phy_disconnect(priv->phy);
+               priv->phy = NULL;
+
+               if (priv->mdio) {
+                       mdiobus_unregister(priv->mdio);
+                       kfree(priv->mdio->irq);
+                       mdiobus_free(priv->mdio);
+               }
+
+               unregister_netdev(netdev);
+               free_netdev(netdev);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int ethoc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       return -ENOSYS;
+}
+
+static int ethoc_resume(struct platform_device *pdev)
+{
+       return -ENOSYS;
+}
+#else
+# define ethoc_suspend NULL
+# define ethoc_resume  NULL
+#endif
+
+static struct platform_driver ethoc_driver = {
+       .probe   = ethoc_probe,
+       .remove  = ethoc_remove,
+       .suspend = ethoc_suspend,
+       .resume  = ethoc_resume,
+       .driver  = {
+               .name = "ethoc",
+       },
+};
+
+static int __init ethoc_init(void)
+{
+       return platform_driver_register(&ethoc_driver);
+}
+
+static void __exit ethoc_exit(void)
+{
+       platform_driver_unregister(&ethoc_driver);
+}
+
+module_init(ethoc_init);
+module_exit(ethoc_exit);
+
+MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
+MODULE_DESCRIPTION("OpenCores Ethernet MAC driver");
+MODULE_LICENSE("GPL v2");
+
index b852303c9362a8a3f5c1888624a7d23edd4d2cb5..1a685a04d4b28212518c2bdc052fffc01e22553d 100644 (file)
@@ -388,6 +388,18 @@ static int __init ewrk3_probe1(struct net_device *dev, u_long iobase, int irq)
        return err;
 }
 
+static const struct net_device_ops ewrk3_netdev_ops = {
+       .ndo_open               = ewrk3_open,
+       .ndo_start_xmit         = ewrk3_queue_pkt,
+       .ndo_stop               = ewrk3_close,
+       .ndo_set_multicast_list = set_multicast_list,
+       .ndo_do_ioctl           = ewrk3_ioctl,
+       .ndo_tx_timeout         = ewrk3_timeout,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 static int __init
 ewrk3_hw_init(struct net_device *dev, u_long iobase)
 {
@@ -603,16 +615,11 @@ ewrk3_hw_init(struct net_device *dev, u_long iobase)
                printk(version);
        }
        /* The EWRK3-specific entries in the device structure. */
-       dev->open = ewrk3_open;
-       dev->hard_start_xmit = ewrk3_queue_pkt;
-       dev->stop = ewrk3_close;
-       dev->set_multicast_list = set_multicast_list;
-       dev->do_ioctl = ewrk3_ioctl;
+       dev->netdev_ops = &ewrk3_netdev_ops;
        if (lp->adapter_name[4] == '3')
                SET_ETHTOOL_OPS(dev, &ethtool_ops_203);
        else
                SET_ETHTOOL_OPS(dev, &ethtool_ops);
-       dev->tx_timeout = ewrk3_timeout;
        dev->watchdog_timeo = QUEUE_PKT_TIMEOUT;
 
        dev->mem_start = 0;
index 9d81e7a48dbab2f68850f9660a7a0d49d1f24da5..6a38800be3f109fd10d6a472517902c765f64572 100644 (file)
@@ -1239,19 +1239,9 @@ static int gfar_enet_open(struct net_device *dev)
        return err;
 }
 
-static inline struct txfcb *gfar_add_fcb(struct sk_buff **skbp)
+static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb)
 {
-       struct txfcb *fcb;
-       struct sk_buff *skb = *skbp;
-
-       if (unlikely(skb_headroom(skb) < GMAC_FCB_LEN)) {
-               struct sk_buff *old_skb = skb;
-               skb = skb_realloc_headroom(old_skb, GMAC_FCB_LEN);
-               if (!skb)
-                       return NULL;
-               dev_kfree_skb_any(old_skb);
-       }
-       fcb = (struct txfcb *)skb_push(skb, GMAC_FCB_LEN);
+       struct txfcb *fcb = (struct txfcb *)skb_push(skb, GMAC_FCB_LEN);
        cacheable_memzero(fcb, GMAC_FCB_LEN);
 
        return fcb;
@@ -1320,6 +1310,22 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        base = priv->tx_bd_base;
 
+       /* make space for additional header when fcb is needed */
+       if (((skb->ip_summed == CHECKSUM_PARTIAL) ||
+                       (priv->vlgrp && vlan_tx_tag_present(skb))) &&
+                       (skb_headroom(skb) < GMAC_FCB_LEN)) {
+               struct sk_buff *skb_new;
+
+               skb_new = skb_realloc_headroom(skb, GMAC_FCB_LEN);
+               if (!skb_new) {
+                       dev->stats.tx_errors++;
+                       kfree_skb(skb);
+                       return NETDEV_TX_OK;
+               }
+               kfree_skb(skb);
+               skb = skb_new;
+       }
+
        /* total number of fragments in the SKB */
        nr_frags = skb_shinfo(skb)->nr_frags;
 
@@ -1372,20 +1378,18 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        /* Set up checksumming */
        if (CHECKSUM_PARTIAL == skb->ip_summed) {
-               fcb = gfar_add_fcb(&skb);
-               if (likely(fcb != NULL)) {
-                       lstatus |= BD_LFLAG(TXBD_TOE);
-                       gfar_tx_checksum(skb, fcb);
-               }
+               fcb = gfar_add_fcb(skb);
+               lstatus |= BD_LFLAG(TXBD_TOE);
+               gfar_tx_checksum(skb, fcb);
        }
 
        if (priv->vlgrp && vlan_tx_tag_present(skb)) {
-               if (unlikely(NULL == fcb))
-                       fcb = gfar_add_fcb(&skb);
-               if (likely(fcb != NULL)) {
+               if (unlikely(NULL == fcb)) {
+                       fcb = gfar_add_fcb(skb);
                        lstatus |= BD_LFLAG(TXBD_TOE);
-                       gfar_tx_vlan(skb, fcb);
                }
+
+               gfar_tx_vlan(skb, fcb);
        }
 
        /* setup the TxBD length and buffer pointer for the first BD */
@@ -1433,7 +1437,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
        /* Unlock priv */
        spin_unlock_irqrestore(&priv->txlock, flags);
 
-       return 0;
+       return NETDEV_TX_OK;
 }
 
 /* Stops the kernel queue, and halts the controller */
index 5b5bf9f9861ac2f63ef8f9db6bca6cfd945fba34..c25bc0bc0b25ea2031146458ee51cb7715654646 100644 (file)
@@ -905,6 +905,17 @@ static char *ibmlana_adapter_names[] __devinitdata = {
        NULL
 };
 
+
+static const struct net_device_ops ibmlana_netdev_ops = {
+       .ndo_open               = ibmlana_open,
+       .ndo_stop               = ibmlana_close,
+       .ndo_start_xmit         = ibmlana_tx,
+       .ndo_set_multicast_list = ibmlana_set_multicast_list,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 static int __devinit ibmlana_init_one(struct device *kdev)
 {
        struct mca_device *mdev = to_mca_device(kdev);
@@ -973,11 +984,7 @@ static int __devinit ibmlana_init_one(struct device *kdev)
        mca_device_set_claim(mdev, 1);
 
        /* set methods */
-
-       dev->open = ibmlana_open;
-       dev->stop = ibmlana_close;
-       dev->hard_start_xmit = ibmlana_tx;
-       dev->set_multicast_list = ibmlana_set_multicast_list;
+       dev->netdev_ops = &ibmlana_netdev_ops;
        dev->flags |= IFF_MULTICAST;
 
        /* copy out MAC address */
index 6f3e7f71658dd058679cf9fbf79d1f22db3b2920..6b6548b9fda01717d869c7eb3e0fd7e6ceefbde2 100644 (file)
@@ -1524,6 +1524,13 @@ toshoboe_close (struct pci_dev *pci_dev)
   free_netdev(self->netdev);
 }
 
+static const struct net_device_ops toshoboe_netdev_ops = {
+       .ndo_open       = toshoboe_net_open,
+       .ndo_stop       = toshoboe_net_close,
+       .ndo_start_xmit = toshoboe_hard_xmit,
+       .ndo_do_ioctl   = toshoboe_net_ioctl,
+};
+
 static int
 toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid)
 {
@@ -1657,10 +1664,7 @@ toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid)
 #endif
 
   SET_NETDEV_DEV(dev, &pci_dev->dev);
-  dev->hard_start_xmit = toshoboe_hard_xmit;
-  dev->open = toshoboe_net_open;
-  dev->stop = toshoboe_net_close;
-  dev->do_ioctl = toshoboe_net_ioctl;
+  dev->netdev_ops = &toshoboe_netdev_ops;
 
   err = register_netdev(dev);
   if (err)
index d83d4010656d60e58727f2542bbe27129bb16a91..633808d447be66a8116c7571b46caaedd1cb0632 100644 (file)
@@ -454,6 +454,18 @@ out:
 }
 #endif
 
+static const struct net_device_ops lance_netdev_ops = {
+       .ndo_open               = lance_open,
+       .ndo_start_xmit         = lance_start_xmit,
+       .ndo_stop               = lance_close,
+       .ndo_get_stats          = lance_get_stats,
+       .ndo_set_multicast_list = set_multicast_list,
+       .ndo_tx_timeout         = lance_tx_timeout,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options)
 {
        struct lance_private *lp;
@@ -714,12 +726,7 @@ static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int
                printk(version);
 
        /* The LANCE-specific entries in the device structure. */
-       dev->open = lance_open;
-       dev->hard_start_xmit = lance_start_xmit;
-       dev->stop = lance_close;
-       dev->get_stats = lance_get_stats;
-       dev->set_multicast_list = set_multicast_list;
-       dev->tx_timeout = lance_tx_timeout;
+       dev->netdev_ops = &lance_netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
        err = register_netdev(dev);
index 4d1a059921c6413d419257327cc382207298a9e7..d44bddbee37313195c8a23d7ae905ce75192aa13 100644 (file)
@@ -952,6 +952,17 @@ static void print_eth(char *add)
                (unsigned char) add[12], (unsigned char) add[13]);
 }
 
+static const struct net_device_ops i596_netdev_ops = {
+       .ndo_open               = i596_open,
+       .ndo_stop               = i596_close,
+       .ndo_start_xmit         = i596_start_xmit,
+       .ndo_set_multicast_list = set_multicast_list,
+       .ndo_tx_timeout         = i596_tx_timeout,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 static int __init lp486e_probe(struct net_device *dev) {
        struct i596_private *lp;
        unsigned char eth_addr[6] = { 0, 0xaa, 0, 0, 0, 0 };
@@ -1014,12 +1025,8 @@ static int __init lp486e_probe(struct net_device *dev) {
        printk("\n");
 
        /* The LP486E-specific entries in the device structure. */
-       dev->open = &i596_open;
-       dev->stop = &i596_close;
-       dev->hard_start_xmit = &i596_start_xmit;
-       dev->set_multicast_list = &set_multicast_list;
+       dev->netdev_ops = &i596_netdev_ops;
        dev->watchdog_timeo = 5*HZ;
-       dev->tx_timeout = i596_tx_timeout;
 
 #if 0
        /* selftest reports 0x320925ae - don't know what that means */
index a8bcc00c3302bcb0a1b35cf9eba82a92dafb75a5..77d44a061703437b8ab95bf8138c978a6910859f 100644 (file)
@@ -441,6 +441,18 @@ out:
        return ERR_PTR(err);
 }
 
+static const struct net_device_ops ni52_netdev_ops = {
+       .ndo_open               = ni52_open,
+       .ndo_stop               = ni52_close,
+       .ndo_get_stats          = ni52_get_stats,
+       .ndo_tx_timeout         = ni52_timeout,
+       .ndo_start_xmit         = ni52_send_packet,
+       .ndo_set_multicast_list = set_multicast_list,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 static int __init ni52_probe1(struct net_device *dev, int ioaddr)
 {
        int i, size, retval;
@@ -561,15 +573,8 @@ static int __init ni52_probe1(struct net_device *dev, int ioaddr)
                printk("IRQ %d (assigned and not checked!).\n", dev->irq);
        }
 
-       dev->open               = ni52_open;
-       dev->stop               = ni52_close;
-       dev->get_stats          = ni52_get_stats;
-       dev->tx_timeout         = ni52_timeout;
+       dev->netdev_ops         = &ni52_netdev_ops;
        dev->watchdog_timeo     = HZ/20;
-       dev->hard_start_xmit    = ni52_send_packet;
-       dev->set_multicast_list = set_multicast_list;
-
-       dev->if_port            = 0;
 
        return 0;
 out:
index df5f869e8d8f1b188b4fac8e868b75da2f643966..6474f02bf78391c758445bd6f24a1f10e3f85fc0 100644 (file)
@@ -237,7 +237,7 @@ struct priv
        void *tmdbounce[TMDNUM];
        int tmdbouncenum;
        int lock,xmit_queued;
-       struct net_device_stats stats;
+
        void *self;
        int cmdr_addr;
        int cardno;
@@ -257,7 +257,6 @@ static void  ni65_timeout(struct net_device *dev);
 static int  ni65_close(struct net_device *dev);
 static int  ni65_alloc_buffer(struct net_device *dev);
 static void ni65_free_buffer(struct priv *p);
-static struct net_device_stats *ni65_get_stats(struct net_device *);
 static void set_multicast_list(struct net_device *dev);
 
 static int irqtab[] __initdata = { 9,12,15,5 }; /* irq config-translate */
@@ -401,6 +400,17 @@ out:
        return ERR_PTR(err);
 }
 
+static const struct net_device_ops ni65_netdev_ops = {
+       .ndo_open               = ni65_open,
+       .ndo_stop               = ni65_close,
+       .ndo_start_xmit         = ni65_send_packet,
+       .ndo_tx_timeout         = ni65_timeout,
+       .ndo_set_multicast_list = set_multicast_list,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 /*
  * this is the real card probe ..
  */
@@ -549,13 +559,9 @@ static int __init ni65_probe1(struct net_device *dev,int ioaddr)
        }
 
        dev->base_addr = ioaddr;
-       dev->open               = ni65_open;
-       dev->stop               = ni65_close;
-       dev->hard_start_xmit    = ni65_send_packet;
-       dev->tx_timeout         = ni65_timeout;
+       dev->netdev_ops = &ni65_netdev_ops;
        dev->watchdog_timeo     = HZ/2;
-       dev->get_stats          = ni65_get_stats;
-       dev->set_multicast_list = set_multicast_list;
+
        return 0; /* everything is OK */
 }
 
@@ -901,13 +907,13 @@ static irqreturn_t ni65_interrupt(int irq, void * dev_id)
                        if(debuglevel > 1)
                                printk(KERN_ERR "%s: general error: %04x.\n",dev->name,csr0);
                        if(csr0 & CSR0_BABL)
-                               p->stats.tx_errors++;
+                               dev->stats.tx_errors++;
                        if(csr0 & CSR0_MISS) {
                                int i;
                                for(i=0;i<RMDNUM;i++)
                                        printk("%02x ",p->rmdhead[i].u.s.status);
                                printk("\n");
-                               p->stats.rx_errors++;
+                               dev->stats.rx_errors++;
                        }
                        if(csr0 & CSR0_MERR) {
                                if(debuglevel > 1)
@@ -997,12 +1003,12 @@ static void ni65_xmit_intr(struct net_device *dev,int csr0)
 #endif
                 /* checking some errors */
                        if(tmdp->status2 & XMIT_RTRY)
-                               p->stats.tx_aborted_errors++;
+                               dev->stats.tx_aborted_errors++;
                        if(tmdp->status2 & XMIT_LCAR)
-                               p->stats.tx_carrier_errors++;
+                               dev->stats.tx_carrier_errors++;
                        if(tmdp->status2 & (XMIT_BUFF | XMIT_UFLO )) {
                /* this stops the xmitter */
-                               p->stats.tx_fifo_errors++;
+                               dev->stats.tx_fifo_errors++;
                                if(debuglevel > 0)
                                        printk(KERN_ERR "%s: Xmit FIFO/BUFF error\n",dev->name);
                                if(p->features & INIT_RING_BEFORE_START) {
@@ -1016,12 +1022,12 @@ static void ni65_xmit_intr(struct net_device *dev,int csr0)
                        if(debuglevel > 2)
                                printk(KERN_ERR "%s: xmit-error: %04x %02x-%04x\n",dev->name,csr0,(int) tmdstat,(int) tmdp->status2);
                        if(!(csr0 & CSR0_BABL)) /* don't count errors twice */
-                               p->stats.tx_errors++;
+                               dev->stats.tx_errors++;
                        tmdp->status2 = 0;
                }
                else {
-                       p->stats.tx_bytes -= (short)(tmdp->blen);
-                       p->stats.tx_packets++;
+                       dev->stats.tx_bytes -= (short)(tmdp->blen);
+                       dev->stats.tx_packets++;
                }
 
 #ifdef XMT_VIA_SKB
@@ -1057,7 +1063,7 @@ static void ni65_recv_intr(struct net_device *dev,int csr0)
                        if(!(rmdstat & RCV_ERR)) {
                                if(rmdstat & RCV_START)
                                {
-                                       p->stats.rx_length_errors++;
+                                       dev->stats.rx_length_errors++;
                                        printk(KERN_ERR "%s: recv, packet too long: %d\n",dev->name,rmdp->mlen & 0x0fff);
                                }
                        }
@@ -1066,16 +1072,16 @@ static void ni65_recv_intr(struct net_device *dev,int csr0)
                                        printk(KERN_ERR "%s: receive-error: %04x, lance-status: %04x/%04x\n",
                                                                        dev->name,(int) rmdstat,csr0,(int) inw(PORT+L_DATAREG) );
                                if(rmdstat & RCV_FRAM)
-                                       p->stats.rx_frame_errors++;
+                                       dev->stats.rx_frame_errors++;
                                if(rmdstat & RCV_OFLO)
-                                       p->stats.rx_over_errors++;
+                                       dev->stats.rx_over_errors++;
                                if(rmdstat & RCV_CRC)
-                                       p->stats.rx_crc_errors++;
+                                       dev->stats.rx_crc_errors++;
                                if(rmdstat & RCV_BUF_ERR)
-                                       p->stats.rx_fifo_errors++;
+                                       dev->stats.rx_fifo_errors++;
                        }
                        if(!(csr0 & CSR0_MISS)) /* don't count errors twice */
-                               p->stats.rx_errors++;
+                               dev->stats.rx_errors++;
                }
                else if( (len = (rmdp->mlen & 0x0fff) - 4) >= 60)
                {
@@ -1106,20 +1112,20 @@ static void ni65_recv_intr(struct net_device *dev,int csr0)
                                skb_put(skb,len);
                                skb_copy_to_linear_data(skb, (unsigned char *) p->recvbounce[p->rmdnum],len);
 #endif
-                               p->stats.rx_packets++;
-                               p->stats.rx_bytes += len;
+                               dev->stats.rx_packets++;
+                               dev->stats.rx_bytes += len;
                                skb->protocol=eth_type_trans(skb,dev);
                                netif_rx(skb);
                        }
                        else
                        {
                                printk(KERN_ERR "%s: can't alloc new sk_buff\n",dev->name);
-                               p->stats.rx_dropped++;
+                               dev->stats.rx_dropped++;
                        }
                }
                else {
                        printk(KERN_INFO "%s: received runt packet\n",dev->name);
-                       p->stats.rx_errors++;
+                       dev->stats.rx_errors++;
                }
                rmdp->blen = -(R_BUF_SIZE-8);
                rmdp->mlen = 0;
@@ -1213,23 +1219,6 @@ static int ni65_send_packet(struct sk_buff *skb, struct net_device *dev)
        return 0;
 }
 
-static struct net_device_stats *ni65_get_stats(struct net_device *dev)
-{
-
-#if 0
-       int i;
-       struct priv *p = dev->ml_priv;
-       for(i=0;i<RMDNUM;i++)
-       {
-               struct rmd *rmdp = p->rmdhead + ((p->rmdnum + i) & (RMDNUM-1));
-               printk("%02x ",rmdp->u.s.status);
-       }
-       printk("\n");
-#endif
-
-       return &((struct priv *)dev->ml_priv)->stats;
-}
-
 static void set_multicast_list(struct net_device *dev)
 {
        if(!ni65_lance_reinit(dev))
index 12a8ffffeb03d3e448c8cab167f80c7d42a3dbf9..ebbbe09725fe422c489bb7a2043d14465f68df62 100644 (file)
@@ -143,6 +143,17 @@ out:
        return ERR_PTR(err);
 }
 
+static const struct net_device_ops seeq8005_netdev_ops = {
+       .ndo_open               = seeq8005_open,
+       .ndo_stop               = seeq8005_close,
+       .ndo_start_xmit         = seeq8005_send_packet,
+       .ndo_tx_timeout         = seeq8005_timeout,
+       .ndo_set_multicast_list = set_multicast_list,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 /* This is the real probe routine.  Linux has a history of friendly device
    probes on the ISA bus.  A good device probes avoids doing writes, and
    verifies that the correct device exists and functions.  */
@@ -332,12 +343,8 @@ static int __init seeq8005_probe1(struct net_device *dev, int ioaddr)
                 }
        }
 #endif
-       dev->open               = seeq8005_open;
-       dev->stop               = seeq8005_close;
-       dev->hard_start_xmit    = seeq8005_send_packet;
-       dev->tx_timeout         = seeq8005_timeout;
+       dev->netdev_ops = &seeq8005_netdev_ops;
        dev->watchdog_timeo     = HZ/20;
-       dev->set_multicast_list = set_multicast_list;
        dev->flags &= ~IFF_MULTICAST;
 
        return 0;
index 2033fee3143a1203c87a8fb2e1e2aa924752592a..0291ea098a06ca2fe365cb90ed347f1a7ae7ebfb 100644 (file)
@@ -142,9 +142,6 @@ static int __init do_ultra_probe(struct net_device *dev)
        int base_addr = dev->base_addr;
        int irq = dev->irq;
 
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       dev->poll_controller = &ultra_poll;
-#endif
        if (base_addr > 0x1ff)          /* Check a single specified location. */
                return ultra_probe1(dev, base_addr);
        else if (base_addr != 0)        /* Don't probe at all. */
@@ -199,7 +196,7 @@ static const struct net_device_ops ultra_netdev_ops = {
        .ndo_set_mac_address    = eth_mac_addr,
        .ndo_change_mtu         = eth_change_mtu,
 #ifdef CONFIG_NET_POLL_CONTROLLER
-       .ndo_poll_controller    = ei_poll,
+       .ndo_poll_controller    = ultra_poll,
 #endif
 };
 
index cb6c097a2e0ad78e7b31d01ac18750540cc9fb7b..7a554adc70fbde2710cb51944d7acfd357ad7da0 100644 (file)
@@ -153,6 +153,22 @@ out:
        return ERR_PTR(err);
 }
 
+
+static const struct net_device_ops ultra32_netdev_ops = {
+       .ndo_open               = ultra32_open,
+       .ndo_stop               = ultra32_close,
+       .ndo_start_xmit         = ei_start_xmit,
+       .ndo_tx_timeout         = ei_tx_timeout,
+       .ndo_get_stats          = ei_get_stats,
+       .ndo_set_multicast_list = ei_set_multicast_list,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_change_mtu         = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = ei_poll,
+#endif
+};
+
 static int __init ultra32_probe1(struct net_device *dev, int ioaddr)
 {
        int i, edge, media, retval;
@@ -273,11 +289,8 @@ static int __init ultra32_probe1(struct net_device *dev, int ioaddr)
        ei_status.block_output = &ultra32_block_output;
        ei_status.get_8390_hdr = &ultra32_get_8390_hdr;
        ei_status.reset_8390 = &ultra32_reset_8390;
-       dev->open = &ultra32_open;
-       dev->stop = &ultra32_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       dev->poll_controller = ei_poll;
-#endif
+
+       dev->netdev_ops = &ultra32_netdev_ops;
        NS8390_init(dev, 0);
 
        return 0;
index 18d653bbd4e0556dfac549ff02510fa805f41fa0..9a7973a541168b0f675080cd5a44feb7a2bacbe5 100644 (file)
@@ -831,6 +831,17 @@ static int __init smc_findirq(int ioaddr)
 #endif
 }
 
+static const struct net_device_ops smc_netdev_ops = {
+       .ndo_open                = smc_open,
+       .ndo_stop               = smc_close,
+       .ndo_start_xmit         = smc_wait_to_send_packet,
+       .ndo_tx_timeout         = smc_timeout,
+       .ndo_set_multicast_list = smc_set_multicast_list,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
 /*----------------------------------------------------------------------
  . Function: smc_probe( int ioaddr )
  .
@@ -1044,12 +1055,8 @@ static int __init smc_probe(struct net_device *dev, int ioaddr)
                goto err_out;
        }
 
-       dev->open                       = smc_open;
-       dev->stop                       = smc_close;
-       dev->hard_start_xmit            = smc_wait_to_send_packet;
-       dev->tx_timeout                 = smc_timeout;
+       dev->netdev_ops                 = &smc_netdev_ops;
        dev->watchdog_timeo             = HZ/20;
-       dev->set_multicast_list         = smc_set_multicast_list;
 
        return 0;
 
index ad3cbc91a8fa6f45ee2a51cdc2d4014d8483fca6..af8f60ca0f57235f57abdb82cea9437b03d2d804 100644 (file)
@@ -1680,6 +1680,7 @@ static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
                                          u8 address, u8 data)
 {
        u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+       u32 temp;
        int ret;
 
        SMSC_TRACE(DRV, "address 0x%x, data 0x%x", address, data);
@@ -1688,6 +1689,10 @@ static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
        if (!ret) {
                op = E2P_CMD_EPC_CMD_WRITE_ | address;
                smsc911x_reg_write(pdata, E2P_DATA, (u32)data);
+
+               /* Workaround for hardware read-after-write restriction */
+               temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
                ret = smsc911x_eeprom_send_cmd(pdata, op);
        }
 
index 193308118f95a58f225e6f84a6e4ddf1ec678eb5..456f8bff40bec45f65ebffae80dfe34c326b9a3f 100644 (file)
@@ -142,7 +142,7 @@ static void madgemc_sifwritew(struct net_device *dev, unsigned short val, unsign
        return;
 }
 
-
+static struct net_device_ops madgemc_netdev_ops __read_mostly;
 
 static int __devinit madgemc_probe(struct device *device)
 {      
@@ -168,7 +168,7 @@ static int __devinit madgemc_probe(struct device *device)
                goto getout;
        }
 
-       dev->dma = 0;
+       dev->netdev_ops = &madgemc_netdev_ops;
 
        card = kmalloc(sizeof(struct card_info), GFP_KERNEL);
        if (card==NULL) {
@@ -348,9 +348,6 @@ static int __devinit madgemc_probe(struct device *device)
 
        memcpy(tp->ProductID, "Madge MCA 16/4    ", PROD_ID_SIZE + 1);
 
-       dev->open = madgemc_open;
-       dev->stop = madgemc_close;
-
        tp->tmspriv = card;
        dev_set_drvdata(device, dev);
 
@@ -758,6 +755,10 @@ static struct mca_driver madgemc_driver = {
 
 static int __init madgemc_init (void)
 {
+       madgemc_netdev_ops = tms380tr_netdev_ops;
+       madgemc_netdev_ops.ndo_open = madgemc_open;
+       madgemc_netdev_ops.ndo_stop = madgemc_close;
+
        return mca_register_driver (&madgemc_driver);
 }
 
index b8c955f6d31ae8355307b872b9c55e6abb650937..16e8783ee9cd3b1b4252975812c3d43029597663 100644 (file)
@@ -116,6 +116,8 @@ nodev:
        return -ENODEV;
 }
 
+static struct net_device_ops proteon_netdev_ops __read_mostly;
+
 static int __init setup_card(struct net_device *dev, struct device *pdev)
 {
        struct net_local *tp;
@@ -167,8 +169,7 @@ static int __init setup_card(struct net_device *dev, struct device *pdev)
 
        tp->tmspriv = NULL;
 
-       dev->open = proteon_open;
-       dev->stop = tms380tr_close;
+       dev->netdev_ops = &proteon_netdev_ops;
 
        if (dev->irq == 0)
        {
@@ -352,6 +353,10 @@ static int __init proteon_init(void)
        struct platform_device *pdev;
        int i, num = 0, err = 0;
 
+       proteon_netdev_ops = tms380tr_netdev_ops;
+       proteon_netdev_ops.ndo_open = proteon_open;
+       proteon_netdev_ops.ndo_stop = tms380tr_close;
+
        err = platform_driver_register(&proteon_driver);
        if (err)
                return err;
index c0f58f08782cd543d6df4bd3c4a883b4cc6aa269..46db5c5395b2c6c4d1a7935054690647e1b4e401 100644 (file)
@@ -133,6 +133,8 @@ static int __init sk_isa_probe1(struct net_device *dev, int ioaddr)
        return 0;
 }
 
+static struct net_device_ops sk_isa_netdev_ops __read_mostly;
+
 static int __init setup_card(struct net_device *dev, struct device *pdev)
 {
        struct net_local *tp;
@@ -184,8 +186,7 @@ static int __init setup_card(struct net_device *dev, struct device *pdev)
 
        tp->tmspriv = NULL;
 
-       dev->open = sk_isa_open;
-       dev->stop = tms380tr_close;
+       dev->netdev_ops = &sk_isa_netdev_ops;
 
        if (dev->irq == 0)
        {
@@ -362,6 +363,10 @@ static int __init sk_isa_init(void)
        struct platform_device *pdev;
        int i, num = 0, err = 0;
 
+       sk_isa_netdev_ops = tms380tr_netdev_ops;
+       sk_isa_netdev_ops.ndo_open = sk_isa_open;
+       sk_isa_netdev_ops.ndo_stop = tms380tr_close;
+
        err = platform_driver_register(&sk_isa_driver);
        if (err)
                return err;
index 9d7db2c8d6614ca2627622fcaeffdf8fc87c27fd..a91d9c55d78e94b28718c8dfdc83b62521d2a0d6 100644 (file)
@@ -124,7 +124,6 @@ static unsigned int smctr_get_num_rx_bdbs(struct net_device *dev);
 static int smctr_get_physical_drop_number(struct net_device *dev);
 static __u8 *smctr_get_rx_pointer(struct net_device *dev, short queue);
 static int smctr_get_station_id(struct net_device *dev);
-static struct net_device_stats *smctr_get_stats(struct net_device *dev);
 static FCBlock *smctr_get_tx_fcb(struct net_device *dev, __u16 queue,
         __u16 bytes_count);
 static int smctr_get_upstream_neighbor_addr(struct net_device *dev);
@@ -3633,6 +3632,14 @@ out:
        return ERR_PTR(err);
 }
 
+static const struct net_device_ops smctr_netdev_ops = {
+       .ndo_open          = smctr_open,
+       .ndo_stop          = smctr_close,
+       .ndo_start_xmit    = smctr_send_packet,
+       .ndo_tx_timeout    = smctr_timeout,
+       .ndo_get_stats     = smctr_get_stats,
+       .ndo_set_multicast_list = smctr_set_multicast_list,
+};
 
 static int __init smctr_probe1(struct net_device *dev, int ioaddr)
 {
@@ -3683,13 +3690,8 @@ static int __init smctr_probe1(struct net_device *dev, int ioaddr)
                 (unsigned int)dev->base_addr,
                 dev->irq, tp->rom_base, tp->ram_base);
 
-        dev->open               = smctr_open;
-        dev->stop               = smctr_close;
-        dev->hard_start_xmit    = smctr_send_packet;
-        dev->tx_timeout                = smctr_timeout;
+       dev->netdev_ops = &smctr_netdev_ops;
         dev->watchdog_timeo    = HZ;
-        dev->get_stats          = smctr_get_stats;
-        dev->set_multicast_list = &smctr_set_multicast_list;
         return (0);
 
 out:
index a110326dce6f8682d24206fec681a03a0084ab59..86a479f61c0c09dbceb64688f1bd2b131105031d 100644 (file)
@@ -2009,6 +2009,9 @@ static void ucc_geth_stop(struct ucc_geth_private *ugeth)
        /* Disable Rx and Tx */
        clrbits32(&ug_regs->maccfg1, MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX);
 
+       phy_disconnect(ugeth->phydev);
+       ugeth->phydev = NULL;
+
        ucc_geth_memclean(ugeth);
 }
 
@@ -3345,6 +3348,14 @@ static int ucc_geth_open(struct net_device *dev)
                return -EINVAL;
        }
 
+       err = init_phy(dev);
+       if (err) {
+               if (netif_msg_ifup(ugeth))
+                       ugeth_err("%s: Cannot initialize PHY, aborting.",
+                                 dev->name);
+               return err;
+       }
+
        err = ucc_struct_init(ugeth);
        if (err) {
                if (netif_msg_ifup(ugeth))
@@ -3381,13 +3392,6 @@ static int ucc_geth_open(struct net_device *dev)
                                   &ugeth->ug_regs->macstnaddr1,
                                   &ugeth->ug_regs->macstnaddr2);
 
-       err = init_phy(dev);
-       if (err) {
-               if (netif_msg_ifup(ugeth))
-                       ugeth_err("%s: Cannot initialize PHY, aborting.", dev->name);
-               goto out_err;
-       }
-
        phy_start(ugeth->phydev);
 
        err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
@@ -3430,9 +3434,6 @@ static int ucc_geth_close(struct net_device *dev)
 
        free_irq(ugeth->ug_info->uf_info.irq, ugeth->dev);
 
-       phy_disconnect(ugeth->phydev);
-       ugeth->phydev = NULL;
-
        netif_stop_queue(dev);
 
        return 0;
index 6a07ba9371dbd5b0cd257eb87c1cf11cecd7bdcd..1d637f407a0c850fb39c9e8eb4f5d35a391baa13 100644 (file)
@@ -714,19 +714,19 @@ static int sdla_transmit(struct sk_buff *skb, struct net_device *dev)
                switch (ret)
                {
                        case SDLA_RET_OK:
-                               flp->stats.tx_packets++;
+                               dev->stats.tx_packets++;
                                ret = DLCI_RET_OK;
                                break;
 
                        case SDLA_RET_CIR_OVERFLOW:
                        case SDLA_RET_BUF_OVERSIZE:
                        case SDLA_RET_NO_BUFS:
-                               flp->stats.tx_dropped++;
+                               dev->stats.tx_dropped++;
                                ret = DLCI_RET_DROP;
                                break;
 
                        default:
-                               flp->stats.tx_errors++;
+                               dev->stats.tx_errors++;
                                ret = DLCI_RET_ERR;
                                break;
                }
@@ -807,7 +807,7 @@ static void sdla_receive(struct net_device *dev)
                if (i == CONFIG_DLCI_MAX)
                {
                        printk(KERN_NOTICE "%s: Received packet from invalid DLCI %i, ignoring.", dev->name, dlci);
-                       flp->stats.rx_errors++;
+                       dev->stats.rx_errors++;
                        success = 0;
                }
        }
@@ -819,7 +819,7 @@ static void sdla_receive(struct net_device *dev)
                if (skb == NULL) 
                {
                        printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
-                       flp->stats.rx_dropped++; 
+                       dev->stats.rx_dropped++;
                        success = 0;
                }
                else
@@ -859,7 +859,7 @@ static void sdla_receive(struct net_device *dev)
 
        if (success)
        {
-               flp->stats.rx_packets++;
+               dev->stats.rx_packets++;
                dlp = netdev_priv(master);
                (*dlp->receive)(skb, master);
        }
@@ -1590,13 +1590,14 @@ fail:
        return err;
 }
  
-static struct net_device_stats *sdla_stats(struct net_device *dev)
-{
-       struct frad_local *flp;
-       flp = netdev_priv(dev);
-
-       return(&flp->stats);
-}
+static const struct net_device_ops sdla_netdev_ops = {
+       .ndo_open       = sdla_open,
+       .ndo_stop       = sdla_close,
+       .ndo_do_ioctl   = sdla_ioctl,
+       .ndo_set_config = sdla_set_config,
+       .ndo_start_xmit = sdla_transmit,
+       .ndo_change_mtu = sdla_change_mtu,
+};
 
 static void setup_sdla(struct net_device *dev)
 {
@@ -1604,20 +1605,13 @@ static void setup_sdla(struct net_device *dev)
 
        netdev_boot_setup_check(dev);
 
+       dev->netdev_ops         = &sdla_netdev_ops;
        dev->flags              = 0;
        dev->type               = 0xFFFF;
        dev->hard_header_len    = 0;
        dev->addr_len           = 0;
        dev->mtu                = SDLA_MAX_MTU;
 
-       dev->open               = sdla_open;
-       dev->stop               = sdla_close;
-       dev->do_ioctl           = sdla_ioctl;
-       dev->set_config         = sdla_set_config;
-       dev->get_stats          = sdla_stats;
-       dev->hard_start_xmit    = sdla_transmit;
-       dev->change_mtu         = sdla_change_mtu;
-
        flp->activate           = sdla_activate;
        flp->deactivate         = sdla_deactivate;
        flp->assoc              = sdla_assoc;
index 612fffe100a68f19274d50bf5c0526f617e2d75d..8a0823588c516f2b0eefadad0ca944571703a26c 100644 (file)
@@ -485,6 +485,7 @@ config MWL8K
 source "drivers/net/wireless/p54/Kconfig"
 source "drivers/net/wireless/ath5k/Kconfig"
 source "drivers/net/wireless/ath9k/Kconfig"
+source "drivers/net/wireless/ar9170/Kconfig"
 source "drivers/net/wireless/ipw2x00/Kconfig"
 source "drivers/net/wireless/iwlwifi/Kconfig"
 source "drivers/net/wireless/hostap/Kconfig"
index d780487c420f69126e691ebf69d3ac760b0eeedb..50e7fba7f0ea95b1922a6bcf3938420295d83ad7 100644 (file)
@@ -57,5 +57,6 @@ obj-$(CONFIG_P54_COMMON)      += p54/
 
 obj-$(CONFIG_ATH5K)    += ath5k/
 obj-$(CONFIG_ATH9K)    += ath9k/
+obj-$(CONFIG_AR9170_USB)       += ar9170/
 
 obj-$(CONFIG_MAC80211_HWSIM)   += mac80211_hwsim.o
diff --git a/drivers/net/wireless/ar9170/Kconfig b/drivers/net/wireless/ar9170/Kconfig
new file mode 100644 (file)
index 0000000..de4281f
--- /dev/null
@@ -0,0 +1,17 @@
+config AR9170_USB
+       tristate "Atheros AR9170 802.11n USB support"
+       depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL
+       select FW_LOADER
+       help
+         This is a driver for the Atheros "otus" 802.11n USB devices.
+
+         These devices require additional firmware (2 files).
+         For now, these files can be downloaded from here:
+         http://wireless.kernel.org/en/users/Drivers/ar9170
+
+         If you choose to build a module, it'll be called ar9170usb.
+
+config AR9170_LEDS
+       bool
+       depends on AR9170_USB && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = AR9170_USB)
+       default y
diff --git a/drivers/net/wireless/ar9170/Makefile b/drivers/net/wireless/ar9170/Makefile
new file mode 100644 (file)
index 0000000..8d91c7e
--- /dev/null
@@ -0,0 +1,3 @@
+ar9170usb-objs := usb.o main.o cmd.o mac.o phy.o led.o
+
+obj-$(CONFIG_AR9170_USB) += ar9170usb.o
diff --git a/drivers/net/wireless/ar9170/ar9170.h b/drivers/net/wireless/ar9170/ar9170.h
new file mode 100644 (file)
index 0000000..f4fb2e9
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Driver specific definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+#ifndef __AR9170_H
+#define __AR9170_H
+
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <net/wireless.h>
+#include <net/mac80211.h>
+#ifdef CONFIG_AR9170_LEDS
+#include <linux/leds.h>
+#endif /* CONFIG_AR9170_LEDS */
+#include "eeprom.h"
+#include "hw.h"
+
+#define PAYLOAD_MAX    (AR9170_MAX_CMD_LEN/4 - 1)
+
+enum ar9170_bw {
+       AR9170_BW_20,
+       AR9170_BW_40_BELOW,
+       AR9170_BW_40_ABOVE,
+
+       __AR9170_NUM_BW,
+};
+
+enum ar9170_rf_init_mode {
+       AR9170_RFI_NONE,
+       AR9170_RFI_WARM,
+       AR9170_RFI_COLD,
+};
+
+#define AR9170_MAX_RX_BUFFER_SIZE              8192
+
+#ifdef CONFIG_AR9170_LEDS
+struct ar9170;
+
+struct ar9170_led {
+       struct ar9170 *ar;
+       struct led_classdev l;
+       char name[32];
+       unsigned int toggled;
+       bool registered;
+};
+
+#endif /* CONFIG_AR9170_LEDS */
+
+enum ar9170_device_state {
+       AR9170_UNKNOWN_STATE,
+       AR9170_STOPPED,
+       AR9170_IDLE,
+       AR9170_STARTED,
+       AR9170_ASSOCIATED,
+};
+
+struct ar9170 {
+       struct ieee80211_hw *hw;
+       struct mutex mutex;
+       enum ar9170_device_state state;
+
+       int (*open)(struct ar9170 *);
+       void (*stop)(struct ar9170 *);
+       int (*tx)(struct ar9170 *, struct sk_buff *, bool, unsigned int);
+       int (*exec_cmd)(struct ar9170 *, enum ar9170_cmd, u32 ,
+                       void *, u32 , void *);
+       void (*callback_cmd)(struct ar9170 *, u32 , void *);
+
+       /* interface mode settings */
+       struct ieee80211_vif *vif;
+       u8 mac_addr[ETH_ALEN];
+       u8 bssid[ETH_ALEN];
+
+       /* beaconing */
+       struct sk_buff *beacon;
+       struct work_struct beacon_work;
+
+       /* cryptographic engine */
+       u64 usedkeys;
+       bool rx_software_decryption;
+       bool disable_offload;
+
+       /* filter settings */
+       struct work_struct filter_config_work;
+       u64 cur_mc_hash, want_mc_hash;
+       u32 cur_filter, want_filter;
+       unsigned int filter_changed;
+       bool sniffer_enabled;
+
+       /* PHY */
+       struct ieee80211_channel *channel;
+       int noise[4];
+
+       /* power calibration data */
+       u8 power_5G_leg[4];
+       u8 power_2G_cck[4];
+       u8 power_2G_ofdm[4];
+       u8 power_5G_ht20[8];
+       u8 power_5G_ht40[8];
+       u8 power_2G_ht20[8];
+       u8 power_2G_ht40[8];
+
+#ifdef CONFIG_AR9170_LEDS
+       struct delayed_work led_work;
+       struct ar9170_led leds[AR9170_NUM_LEDS];
+#endif /* CONFIG_AR9170_LEDS */
+
+       /* qos queue settings */
+       spinlock_t tx_stats_lock;
+       struct ieee80211_tx_queue_stats tx_stats[5];
+       struct ieee80211_tx_queue_params edcf[5];
+
+       spinlock_t cmdlock;
+       __le32 cmdbuf[PAYLOAD_MAX + 1];
+
+       /* MAC statistics */
+       struct ieee80211_low_level_stats stats;
+
+       /* EEPROM */
+       struct ar9170_eeprom eeprom;
+
+       /* global tx status for unregistered Stations. */
+       struct sk_buff_head global_tx_status;
+       struct sk_buff_head global_tx_status_waste;
+       struct delayed_work tx_status_janitor;
+};
+
+struct ar9170_sta_info {
+       struct sk_buff_head tx_status[__AR9170_NUM_TXQ];
+};
+
+#define IS_STARTED(a)          (a->state >= AR9170_STARTED)
+#define IS_ACCEPTING_CMD(a)    (a->state >= AR9170_IDLE)
+
+#define AR9170_FILTER_CHANGED_PROMISC          BIT(0)
+#define AR9170_FILTER_CHANGED_MULTICAST                BIT(1)
+#define AR9170_FILTER_CHANGED_FRAMEFILTER      BIT(2)
+
+/* exported interface */
+void *ar9170_alloc(size_t priv_size);
+int ar9170_register(struct ar9170 *ar, struct device *pdev);
+void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb);
+void ar9170_unregister(struct ar9170 *ar);
+void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+                            bool update_statistics, u16 tx_status);
+
+/* MAC */
+int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
+int ar9170_init_mac(struct ar9170 *ar);
+int ar9170_set_qos(struct ar9170 *ar);
+int ar9170_update_multicast(struct ar9170 *ar);
+int ar9170_update_frame_filter(struct ar9170 *ar);
+int ar9170_set_operating_mode(struct ar9170 *ar);
+int ar9170_set_beacon_timers(struct ar9170 *ar);
+int ar9170_set_hwretry_limit(struct ar9170 *ar, u32 max_retry);
+int ar9170_update_beacon(struct ar9170 *ar);
+void ar9170_new_beacon(struct work_struct *work);
+int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype,
+                     u8 keyidx, u8 *keydata, int keylen);
+int ar9170_disable_key(struct ar9170 *ar, u8 id);
+
+/* LEDs */
+#ifdef CONFIG_AR9170_LEDS
+int ar9170_register_leds(struct ar9170 *ar);
+void ar9170_unregister_leds(struct ar9170 *ar);
+#endif /* CONFIG_AR9170_LEDS */
+int ar9170_init_leds(struct ar9170 *ar);
+int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state);
+
+/* PHY / RF */
+int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band);
+int ar9170_init_rf(struct ar9170 *ar);
+int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
+                      enum ar9170_rf_init_mode rfi, enum ar9170_bw bw);
+
+#endif /* __AR9170_H */
diff --git a/drivers/net/wireless/ar9170/cmd.c b/drivers/net/wireless/ar9170/cmd.c
new file mode 100644 (file)
index 0000000..f57a620
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Basic HW register/memory/command access functions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+
+#include "ar9170.h"
+#include "cmd.h"
+
+int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len)
+{
+       int err;
+
+       if (unlikely(!IS_ACCEPTING_CMD(ar)))
+               return 0;
+
+       err = ar->exec_cmd(ar, AR9170_CMD_WMEM, len, (u8 *) data, 0, NULL);
+       if (err)
+               printk(KERN_DEBUG "%s: writing memory failed\n",
+                      wiphy_name(ar->hw->wiphy));
+       return err;
+}
+
+int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val)
+{
+       __le32 buf[2] = {
+               cpu_to_le32(reg),
+               cpu_to_le32(val),
+       };
+       int err;
+
+       if (unlikely(!IS_ACCEPTING_CMD(ar)))
+               return 0;
+
+       err = ar->exec_cmd(ar, AR9170_CMD_WREG, sizeof(buf),
+                          (u8 *) buf, 0, NULL);
+       if (err)
+               printk(KERN_DEBUG "%s: writing reg %#x (val %#x) failed\n",
+                      wiphy_name(ar->hw->wiphy), reg, val);
+       return err;
+}
+
+static int ar9170_read_mreg(struct ar9170 *ar, int nregs,
+                           const u32 *regs, u32 *out)
+{
+       int i, err;
+       __le32 *offs, *res;
+
+       if (unlikely(!IS_ACCEPTING_CMD(ar)))
+               return 0;
+
+       /* abuse "out" for the register offsets, must be same length */
+       offs = (__le32 *)out;
+       for (i = 0; i < nregs; i++)
+               offs[i] = cpu_to_le32(regs[i]);
+
+       /* also use the same buffer for the input */
+       res = (__le32 *)out;
+
+       err = ar->exec_cmd(ar, AR9170_CMD_RREG,
+                          4 * nregs, (u8 *)offs,
+                          4 * nregs, (u8 *)res);
+       if (err)
+               return err;
+
+       /* convert result to cpu endian */
+       for (i = 0; i < nregs; i++)
+               out[i] = le32_to_cpu(res[i]);
+
+       return 0;
+}
+
+int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val)
+{
+       return ar9170_read_mreg(ar, 1, &reg, val);
+}
+
+int ar9170_echo_test(struct ar9170 *ar, u32 v)
+{
+       __le32 echobuf = cpu_to_le32(v);
+       __le32 echores;
+       int err;
+
+       if (unlikely(!IS_ACCEPTING_CMD(ar)))
+               return -ENODEV;
+
+       err = ar->exec_cmd(ar, AR9170_CMD_ECHO,
+                          4, (u8 *)&echobuf,
+                          4, (u8 *)&echores);
+       if (err)
+               return err;
+
+       if (echobuf != echores)
+               return -EINVAL;
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ar9170/cmd.h b/drivers/net/wireless/ar9170/cmd.h
new file mode 100644 (file)
index 0000000..a4f0e50
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Basic HW register/memory/command access functions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+#ifndef __CMD_H
+#define __CMD_H
+
+#include "ar9170.h"
+
+/* basic HW access */
+int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len);
+int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val);
+int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val);
+int ar9170_echo_test(struct ar9170 *ar, u32 v);
+
+/*
+ * Macros to facilitate writing multiple registers in a single
+ * write-combining USB command. Note that when the first group
+ * fails the whole thing will fail without any others attempted,
+ * but you won't know which write in the group failed.
+ */
+#define ar9170_regwrite_begin(ar)                                      \
+do {                                                                   \
+       int __nreg = 0, __err = 0;                                      \
+       struct ar9170 *__ar = ar;
+
+#define ar9170_regwrite(r, v) do {                                     \
+       __ar->cmdbuf[2 * __nreg + 1] = cpu_to_le32(r);                  \
+       __ar->cmdbuf[2 * __nreg + 2] = cpu_to_le32(v);                  \
+       __nreg++;                                                       \
+       if ((__nreg >= PAYLOAD_MAX/2)) {                                \
+               if (IS_ACCEPTING_CMD(__ar))                             \
+                       __err = ar->exec_cmd(__ar, AR9170_CMD_WREG,     \
+                                            8 * __nreg,                \
+                                            (u8 *) &__ar->cmdbuf[1],   \
+                                            0, NULL);                  \
+               __nreg = 0;                                             \
+               if (__err)                                              \
+                       goto __regwrite_out;                            \
+       }                                                               \
+} while (0)
+
+#define ar9170_regwrite_finish()                                       \
+__regwrite_out :                                                       \
+       if (__nreg) {                                                   \
+               if (IS_ACCEPTING_CMD(__ar))                             \
+                       __err = ar->exec_cmd(__ar, AR9170_CMD_WREG,     \
+                                            8 * __nreg,                \
+                                            (u8 *) &__ar->cmdbuf[1],   \
+                                            0, NULL);                  \
+               __nreg = 0;                                             \
+       }
+
+#define ar9170_regwrite_result()                                       \
+       __err;                                                          \
+} while (0);
+
+#endif /* __CMD_H */
diff --git a/drivers/net/wireless/ar9170/eeprom.h b/drivers/net/wireless/ar9170/eeprom.h
new file mode 100644 (file)
index 0000000..d2c8cc8
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * EEPROM layout
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+#ifndef __AR9170_EEPROM_H
+#define __AR9170_EEPROM_H
+
+#define AR5416_MAX_CHAINS              2
+#define AR5416_MODAL_SPURS             5
+
+struct ar9170_eeprom_modal {
+       __le32  antCtrlChain[AR5416_MAX_CHAINS];
+       __le32  antCtrlCommon;
+       s8      antennaGainCh[AR5416_MAX_CHAINS];
+       u8      switchSettling;
+       u8      txRxAttenCh[AR5416_MAX_CHAINS];
+       u8      rxTxMarginCh[AR5416_MAX_CHAINS];
+       s8      adcDesiredSize;
+       s8      pgaDesiredSize;
+       u8      xlnaGainCh[AR5416_MAX_CHAINS];
+       u8      txEndToXpaOff;
+       u8      txEndToRxOn;
+       u8      txFrameToXpaOn;
+       u8      thresh62;
+       s8      noiseFloorThreshCh[AR5416_MAX_CHAINS];
+       u8      xpdGain;
+       u8      xpd;
+       s8      iqCalICh[AR5416_MAX_CHAINS];
+       s8      iqCalQCh[AR5416_MAX_CHAINS];
+       u8      pdGainOverlap;
+       u8      ob;
+       u8      db;
+       u8      xpaBiasLvl;
+       u8      pwrDecreaseFor2Chain;
+       u8      pwrDecreaseFor3Chain;
+       u8      txFrameToDataStart;
+       u8      txFrameToPaOn;
+       u8      ht40PowerIncForPdadc;
+       u8      bswAtten[AR5416_MAX_CHAINS];
+       u8      bswMargin[AR5416_MAX_CHAINS];
+       u8      swSettleHt40;
+       u8      reserved[22];
+       struct spur_channel {
+               __le16 spurChan;
+               u8      spurRangeLow;
+               u8      spurRangeHigh;
+       } __packed spur_channels[AR5416_MODAL_SPURS];
+} __packed;
+
+#define AR5416_NUM_PD_GAINS            4
+#define AR5416_PD_GAIN_ICEPTS          5
+
+struct ar9170_calibration_data_per_freq {
+       u8      pwr_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
+       u8      vpd_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
+} __packed;
+
+#define AR5416_NUM_5G_CAL_PIERS                8
+#define AR5416_NUM_2G_CAL_PIERS                4
+
+#define AR5416_NUM_5G_TARGET_PWRS      8
+#define AR5416_NUM_2G_CCK_TARGET_PWRS  3
+#define AR5416_NUM_2G_OFDM_TARGET_PWRS 4
+#define AR5416_MAX_NUM_TGT_PWRS                8
+
+struct ar9170_calibration_target_power_legacy {
+       u8      freq;
+       u8      power[4];
+} __packed;
+
+struct ar9170_calibration_target_power_ht {
+       u8      freq;
+       u8      power[8];
+} __packed;
+
+#define AR5416_NUM_CTLS                        24
+
+struct ar9170_calctl_edges {
+       u8      channel;
+#define AR9170_CALCTL_EDGE_FLAGS       0xC0
+       u8      power_flags;
+} __packed;
+
+#define AR5416_NUM_BAND_EDGES          8
+
+struct ar9170_calctl_data {
+       struct ar9170_calctl_edges
+               control_edges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES];
+} __packed;
+
+
+struct ar9170_eeprom {
+       __le16  length;
+       __le16  checksum;
+       __le16  version;
+       u8      operating_flags;
+#define AR9170_OPFLAG_5GHZ             1
+#define AR9170_OPFLAG_2GHZ             2
+       u8      misc;
+       __le16  reg_domain[2];
+       u8      mac_address[6];
+       u8      rx_mask;
+       u8      tx_mask;
+       __le16  rf_silent;
+       __le16  bluetooth_options;
+       __le16  device_capabilities;
+       __le32  build_number;
+       u8      deviceType;
+       u8      reserved[33];
+
+       u8      customer_data[64];
+
+       struct ar9170_eeprom_modal
+               modal_header[2];
+
+       u8      cal_freq_pier_5G[AR5416_NUM_5G_CAL_PIERS];
+       u8      cal_freq_pier_2G[AR5416_NUM_2G_CAL_PIERS];
+
+       struct ar9170_calibration_data_per_freq
+               cal_pier_data_5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS],
+               cal_pier_data_2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS];
+
+       /* power calibration data */
+       struct ar9170_calibration_target_power_legacy
+               cal_tgt_pwr_5G[AR5416_NUM_5G_TARGET_PWRS];
+       struct ar9170_calibration_target_power_ht
+               cal_tgt_pwr_5G_ht20[AR5416_NUM_5G_TARGET_PWRS],
+               cal_tgt_pwr_5G_ht40[AR5416_NUM_5G_TARGET_PWRS];
+
+       struct ar9170_calibration_target_power_legacy
+               cal_tgt_pwr_2G_cck[AR5416_NUM_2G_CCK_TARGET_PWRS],
+               cal_tgt_pwr_2G_ofdm[AR5416_NUM_2G_OFDM_TARGET_PWRS];
+       struct ar9170_calibration_target_power_ht
+               cal_tgt_pwr_2G_ht20[AR5416_NUM_2G_OFDM_TARGET_PWRS],
+               cal_tgt_pwr_2G_ht40[AR5416_NUM_2G_OFDM_TARGET_PWRS];
+
+       /* conformance testing limits */
+       u8      ctl_index[AR5416_NUM_CTLS];
+       struct ar9170_calctl_data
+               ctl_data[AR5416_NUM_CTLS];
+
+       u8      pad;
+       __le16  subsystem_id;
+} __packed;
+
+#endif /* __AR9170_EEPROM_H */
diff --git a/drivers/net/wireless/ar9170/hw.h b/drivers/net/wireless/ar9170/hw.h
new file mode 100644 (file)
index 0000000..13091bd
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Hardware-specific definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+#ifndef __AR9170_HW_H
+#define __AR9170_HW_H
+
+#define AR9170_MAX_CMD_LEN     64
+
+enum ar9170_cmd {
+       AR9170_CMD_RREG         = 0x00,
+       AR9170_CMD_WREG         = 0x01,
+       AR9170_CMD_RMEM         = 0x02,
+       AR9170_CMD_WMEM         = 0x03,
+       AR9170_CMD_BITAND       = 0x04,
+       AR9170_CMD_BITOR        = 0x05,
+       AR9170_CMD_EKEY         = 0x28,
+       AR9170_CMD_DKEY         = 0x29,
+       AR9170_CMD_FREQUENCY    = 0x30,
+       AR9170_CMD_RF_INIT      = 0x31,
+       AR9170_CMD_SYNTH        = 0x32,
+       AR9170_CMD_FREQ_START   = 0x33,
+       AR9170_CMD_ECHO         = 0x80,
+       AR9170_CMD_TALLY        = 0x81,
+       AR9170_CMD_TALLY_APD    = 0x82,
+       AR9170_CMD_CONFIG       = 0x83,
+       AR9170_CMD_RESET        = 0x90,
+       AR9170_CMD_DKRESET      = 0x91,
+       AR9170_CMD_DKTX_STATUS  = 0x92,
+       AR9170_CMD_FDC          = 0xA0,
+       AR9170_CMD_WREEPROM     = 0xB0,
+       AR9170_CMD_WFLASH       = 0xB0,
+       AR9170_CMD_FLASH_ERASE  = 0xB1,
+       AR9170_CMD_FLASH_PROG   = 0xB2,
+       AR9170_CMD_FLASH_CHKSUM = 0xB3,
+       AR9170_CMD_FLASH_READ   = 0xB4,
+       AR9170_CMD_FW_DL_INIT   = 0xB5,
+       AR9170_CMD_MEM_WREEPROM = 0xBB,
+};
+
+/* endpoints */
+#define AR9170_EP_TX                           1
+#define AR9170_EP_RX                           2
+#define AR9170_EP_IRQ                          3
+#define AR9170_EP_CMD                          4
+
+#define AR9170_EEPROM_START                    0x1600
+
+#define AR9170_GPIO_REG_BASE                   0x1d0100
+#define AR9170_GPIO_REG_PORT_TYPE              AR9170_GPIO_REG_BASE
+#define AR9170_GPIO_REG_DATA                   (AR9170_GPIO_REG_BASE + 4)
+#define AR9170_NUM_LEDS                                2
+
+
+#define AR9170_USB_REG_BASE                    0x1e1000
+#define AR9170_USB_REG_DMA_CTL                 (AR9170_USB_REG_BASE + 0x108)
+#define                AR9170_DMA_CTL_ENABLE_TO_DEVICE         0x1
+#define                AR9170_DMA_CTL_ENABLE_FROM_DEVICE       0x2
+#define                AR9170_DMA_CTL_HIGH_SPEED               0x4
+#define                AR9170_DMA_CTL_PACKET_MODE              0x8
+
+#define AR9170_USB_REG_MAX_AGG_UPLOAD          (AR9170_USB_REG_BASE + 0x110)
+#define AR9170_USB_REG_UPLOAD_TIME_CTL         (AR9170_USB_REG_BASE + 0x114)
+
+
+
+#define AR9170_MAC_REG_BASE                    0x1c3000
+
+#define AR9170_MAC_REG_TSF_L                   (AR9170_MAC_REG_BASE + 0x514)
+#define AR9170_MAC_REG_TSF_H                   (AR9170_MAC_REG_BASE + 0x518)
+
+#define AR9170_MAC_REG_ATIM_WINDOW             (AR9170_MAC_REG_BASE + 0x51C)
+#define AR9170_MAC_REG_BCN_PERIOD              (AR9170_MAC_REG_BASE + 0x520)
+#define AR9170_MAC_REG_PRETBTT                 (AR9170_MAC_REG_BASE + 0x524)
+
+#define AR9170_MAC_REG_MAC_ADDR_L              (AR9170_MAC_REG_BASE + 0x610)
+#define AR9170_MAC_REG_MAC_ADDR_H              (AR9170_MAC_REG_BASE + 0x614)
+#define AR9170_MAC_REG_BSSID_L                 (AR9170_MAC_REG_BASE + 0x618)
+#define AR9170_MAC_REG_BSSID_H                 (AR9170_MAC_REG_BASE + 0x61c)
+
+#define AR9170_MAC_REG_GROUP_HASH_TBL_L                (AR9170_MAC_REG_BASE + 0x624)
+#define AR9170_MAC_REG_GROUP_HASH_TBL_H                (AR9170_MAC_REG_BASE + 0x628)
+
+#define AR9170_MAC_REG_RX_TIMEOUT              (AR9170_MAC_REG_BASE + 0x62C)
+
+#define AR9170_MAC_REG_BASIC_RATE              (AR9170_MAC_REG_BASE + 0x630)
+#define AR9170_MAC_REG_MANDATORY_RATE          (AR9170_MAC_REG_BASE + 0x634)
+#define AR9170_MAC_REG_RTS_CTS_RATE            (AR9170_MAC_REG_BASE + 0x638)
+#define AR9170_MAC_REG_BACKOFF_PROTECT         (AR9170_MAC_REG_BASE + 0x63c)
+#define AR9170_MAC_REG_RX_THRESHOLD            (AR9170_MAC_REG_BASE + 0x640)
+#define AR9170_MAC_REG_RX_PE_DELAY             (AR9170_MAC_REG_BASE + 0x64C)
+
+#define AR9170_MAC_REG_DYNAMIC_SIFS_ACK                (AR9170_MAC_REG_BASE + 0x658)
+#define AR9170_MAC_REG_SNIFFER                 (AR9170_MAC_REG_BASE + 0x674)
+#define                AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC   BIT(0)
+#define                AR9170_MAC_REG_SNIFFER_DEFAULTS         0x02000000
+#define AR9170_MAC_REG_ENCRYPTION              (AR9170_MAC_REG_BASE + 0x678)
+#define                AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE   BIT(3)
+#define                AR9170_MAC_REG_ENCRYPTION_DEFAULTS      0x70
+
+#define AR9170_MAC_REG_MISC_680                        (AR9170_MAC_REG_BASE + 0x680)
+#define AR9170_MAC_REG_TX_UNDERRUN             (AR9170_MAC_REG_BASE + 0x688)
+
+#define AR9170_MAC_REG_FRAMETYPE_FILTER                (AR9170_MAC_REG_BASE + 0x68c)
+#define                AR9170_MAC_REG_FTF_ASSOC_REQ            BIT(0)
+#define                AR9170_MAC_REG_FTF_ASSOC_RESP           BIT(1)
+#define                AR9170_MAC_REG_FTF_REASSOC_REQ          BIT(2)
+#define                AR9170_MAC_REG_FTF_REASSOC_RESP         BIT(3)
+#define                AR9170_MAC_REG_FTF_PRB_REQ              BIT(4)
+#define                AR9170_MAC_REG_FTF_PRB_RESP             BIT(5)
+#define                AR9170_MAC_REG_FTF_BIT6                 BIT(6)
+#define                AR9170_MAC_REG_FTF_BIT7                 BIT(7)
+#define                AR9170_MAC_REG_FTF_BEACON               BIT(8)
+#define                AR9170_MAC_REG_FTF_ATIM                 BIT(9)
+#define                AR9170_MAC_REG_FTF_DEASSOC              BIT(10)
+#define                AR9170_MAC_REG_FTF_AUTH                 BIT(11)
+#define                AR9170_MAC_REG_FTF_DEAUTH               BIT(12)
+#define                AR9170_MAC_REG_FTF_BIT13                BIT(13)
+#define                AR9170_MAC_REG_FTF_BIT14                BIT(14)
+#define                AR9170_MAC_REG_FTF_BIT15                BIT(15)
+#define                AR9170_MAC_REG_FTF_BAR                  BIT(24)
+#define                AR9170_MAC_REG_FTF_BIT25                BIT(25)
+#define                AR9170_MAC_REG_FTF_PSPOLL               BIT(26)
+#define                AR9170_MAC_REG_FTF_RTS                  BIT(27)
+#define                AR9170_MAC_REG_FTF_CTS                  BIT(28)
+#define                AR9170_MAC_REG_FTF_ACK                  BIT(29)
+#define                AR9170_MAC_REG_FTF_CFE                  BIT(30)
+#define                AR9170_MAC_REG_FTF_CFE_ACK              BIT(31)
+#define                AR9170_MAC_REG_FTF_DEFAULTS             0x0500ffff
+#define                AR9170_MAC_REG_FTF_MONITOR              0xfd00ffff
+
+#define AR9170_MAC_REG_RX_TOTAL                        (AR9170_MAC_REG_BASE + 0x6A0)
+#define AR9170_MAC_REG_RX_CRC32                        (AR9170_MAC_REG_BASE + 0x6A4)
+#define AR9170_MAC_REG_RX_CRC16                        (AR9170_MAC_REG_BASE + 0x6A8)
+#define AR9170_MAC_REG_RX_ERR_DECRYPTION_UNI   (AR9170_MAC_REG_BASE + 0x6AC)
+#define AR9170_MAC_REG_RX_OVERRUN              (AR9170_MAC_REG_BASE + 0x6B0)
+#define AR9170_MAC_REG_RX_ERR_DECRYPTION_MUL   (AR9170_MAC_REG_BASE + 0x6BC)
+#define AR9170_MAC_REG_TX_RETRY                        (AR9170_MAC_REG_BASE + 0x6CC)
+#define AR9170_MAC_REG_TX_TOTAL                        (AR9170_MAC_REG_BASE + 0x6F4)
+
+
+#define AR9170_MAC_REG_ACK_EXTENSION           (AR9170_MAC_REG_BASE + 0x690)
+#define AR9170_MAC_REG_EIFS_AND_SIFS           (AR9170_MAC_REG_BASE + 0x698)
+
+#define AR9170_MAC_REG_SLOT_TIME               (AR9170_MAC_REG_BASE + 0x6F0)
+
+#define AR9170_MAC_REG_POWERMANAGEMENT         (AR9170_MAC_REG_BASE + 0x700)
+#define                AR9170_MAC_REG_POWERMGT_IBSS            0xe0
+#define                AR9170_MAC_REG_POWERMGT_AP              0xa1
+#define                AR9170_MAC_REG_POWERMGT_STA             0x2
+#define                AR9170_MAC_REG_POWERMGT_AP_WDS          0x3
+#define                AR9170_MAC_REG_POWERMGT_DEFAULTS        (0xf << 24)
+
+#define AR9170_MAC_REG_ROLL_CALL_TBL_L         (AR9170_MAC_REG_BASE + 0x704)
+#define AR9170_MAC_REG_ROLL_CALL_TBL_H         (AR9170_MAC_REG_BASE + 0x708)
+
+#define AR9170_MAC_REG_AC0_CW                  (AR9170_MAC_REG_BASE + 0xB00)
+#define AR9170_MAC_REG_AC1_CW                  (AR9170_MAC_REG_BASE + 0xB04)
+#define AR9170_MAC_REG_AC2_CW                  (AR9170_MAC_REG_BASE + 0xB08)
+#define AR9170_MAC_REG_AC3_CW                  (AR9170_MAC_REG_BASE + 0xB0C)
+#define AR9170_MAC_REG_AC4_CW                  (AR9170_MAC_REG_BASE + 0xB10)
+#define AR9170_MAC_REG_AC1_AC0_AIFS            (AR9170_MAC_REG_BASE + 0xB14)
+#define AR9170_MAC_REG_AC3_AC2_AIFS            (AR9170_MAC_REG_BASE + 0xB18)
+
+#define AR9170_MAC_REG_RETRY_MAX               (AR9170_MAC_REG_BASE + 0xB28)
+
+#define AR9170_MAC_REG_FCS_SELECT              (AR9170_MAC_REG_BASE + 0xBB0)
+#define                AR9170_MAC_FCS_SWFCS            0x1
+#define                AR9170_MAC_FCS_FIFO_PROT        0x4
+
+
+#define AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND     (AR9170_MAC_REG_BASE + 0xB30)
+
+#define AR9170_MAC_REG_AC1_AC0_TXOP            (AR9170_MAC_REG_BASE + 0xB44)
+#define AR9170_MAC_REG_AC3_AC2_TXOP            (AR9170_MAC_REG_BASE + 0xB48)
+
+#define AR9170_MAC_REG_ACK_TABLE               (AR9170_MAC_REG_BASE + 0xC00)
+#define AR9170_MAC_REG_AMPDU_RX_THRESH         (AR9170_MAC_REG_BASE + 0xC50)
+
+#define AR9170_MAC_REG_TXRX_MPI                        (AR9170_MAC_REG_BASE + 0xD7C)
+#define                AR9170_MAC_TXRX_MPI_TX_MPI_MASK 0x0000000f
+#define                AR9170_MAC_TXRX_MPI_TX_TO_MASK  0x0000fff0
+#define                AR9170_MAC_TXRX_MPI_RX_MPI_MASK 0x000f0000
+#define                AR9170_MAC_TXRX_MPI_RX_TO_MASK  0xfff00000
+
+#define AR9170_MAC_REG_BCN_ADDR                        (AR9170_MAC_REG_BASE + 0xD84)
+#define AR9170_MAC_REG_BCN_LENGTH              (AR9170_MAC_REG_BASE + 0xD88)
+#define AR9170_MAC_REG_BCN_PLCP                        (AR9170_MAC_REG_BASE + 0xD90)
+#define AR9170_MAC_REG_BCN_CTRL                        (AR9170_MAC_REG_BASE + 0xD94)
+#define AR9170_MAC_REG_BCN_HT1                 (AR9170_MAC_REG_BASE + 0xDA0)
+#define AR9170_MAC_REG_BCN_HT2                 (AR9170_MAC_REG_BASE + 0xDA4)
+
+
+#define AR9170_PWR_REG_BASE                    0x1D4000
+
+#define AR9170_PWR_REG_CLOCK_SEL               (AR9170_PWR_REG_BASE + 0x008)
+#define                AR9170_PWR_CLK_AHB_40MHZ        0
+#define                AR9170_PWR_CLK_AHB_20_22MHZ     1
+#define                AR9170_PWR_CLK_AHB_40_44MHZ     2
+#define                AR9170_PWR_CLK_AHB_80_88MHZ     3
+#define                AR9170_PWR_CLK_DAC_160_INV_DLY  0x70
+
+
+/* put beacon here in memory */
+#define AR9170_BEACON_BUFFER_ADDRESS           0x117900
+
+
+struct ar9170_tx_control {
+       __le16 length;
+       __le16 mac_control;
+       __le32 phy_control;
+       u8 frame_data[0];
+} __packed;
+
+/* these are either-or */
+#define AR9170_TX_MAC_PROT_RTS                 0x0001
+#define AR9170_TX_MAC_PROT_CTS                 0x0002
+
+#define AR9170_TX_MAC_NO_ACK                   0x0004
+/* if unset, MAC will only do SIFS space before frame */
+#define AR9170_TX_MAC_BACKOFF                  0x0008
+#define AR9170_TX_MAC_BURST                    0x0010
+#define AR9170_TX_MAC_AGGR                     0x0020
+
+/* encryption is a two-bit field */
+#define AR9170_TX_MAC_ENCR_NONE                        0x0000
+#define AR9170_TX_MAC_ENCR_RC4                 0x0040
+#define AR9170_TX_MAC_ENCR_CENC                        0x0080
+#define AR9170_TX_MAC_ENCR_AES                 0x00c0
+
+#define AR9170_TX_MAC_MMIC                     0x0100
+#define AR9170_TX_MAC_HW_DURATION              0x0200
+#define AR9170_TX_MAC_QOS_SHIFT                        10
+#define AR9170_TX_MAC_QOS_MASK                 (3 << AR9170_TX_MAC_QOS_SHIFT)
+#define AR9170_TX_MAC_AGGR_QOS_BIT1            0x0400
+#define AR9170_TX_MAC_AGGR_QOS_BIT2            0x0800
+#define AR9170_TX_MAC_DISABLE_TXOP             0x1000
+#define AR9170_TX_MAC_TXOP_RIFS                        0x2000
+#define AR9170_TX_MAC_IMM_AMPDU                        0x4000
+#define AR9170_TX_MAC_RATE_PROBE               0x8000
+
+/* either-or */
+#define AR9170_TX_PHY_MOD_CCK                  0x00000000
+#define AR9170_TX_PHY_MOD_OFDM                 0x00000001
+#define AR9170_TX_PHY_MOD_HT                   0x00000002
+
+/* depends on modulation */
+#define AR9170_TX_PHY_SHORT_PREAMBLE           0x00000004
+#define AR9170_TX_PHY_GREENFIELD               0x00000004
+
+#define AR9170_TX_PHY_BW_SHIFT                 3
+#define AR9170_TX_PHY_BW_MASK                  (3 << AR9170_TX_PHY_BW_SHIFT)
+#define AR9170_TX_PHY_BW_20MHZ                 0
+#define AR9170_TX_PHY_BW_40MHZ                 2
+#define AR9170_TX_PHY_BW_40MHZ_DUP             3
+
+#define AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT      6
+#define AR9170_TX_PHY_TX_HEAVY_CLIP_MASK       (7 << AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT)
+
+#define AR9170_TX_PHY_TX_PWR_SHIFT             9
+#define AR9170_TX_PHY_TX_PWR_MASK              (0x3f << AR9170_TX_PHY_TX_PWR_SHIFT)
+
+/* not part of the hw-spec */
+#define AR9170_TX_PHY_QOS_SHIFT                        25
+#define AR9170_TX_PHY_QOS_MASK                 (3 << AR9170_TX_PHY_QOS_SHIFT)
+
+#define AR9170_TX_PHY_TXCHAIN_SHIFT            15
+#define AR9170_TX_PHY_TXCHAIN_MASK             (7 << AR9170_TX_PHY_TXCHAIN_SHIFT)
+#define AR9170_TX_PHY_TXCHAIN_1                        1
+/* use for cck, ofdm 6/9/12/18/24 and HT if capable */
+#define AR9170_TX_PHY_TXCHAIN_2                        5
+
+#define AR9170_TX_PHY_MCS_SHIFT                        18
+#define AR9170_TX_PHY_MCS_MASK                 (0x7f << AR9170_TX_PHY_MCS_SHIFT)
+
+#define AR9170_TX_PHY_SHORT_GI                 0x80000000
+
+struct ar9170_rx_head {
+       u8 plcp[12];
+};
+
+struct ar9170_rx_tail {
+       union {
+               struct {
+                       u8 rssi_ant0, rssi_ant1, rssi_ant2,
+                          rssi_ant0x, rssi_ant1x, rssi_ant2x,
+                          rssi_combined;
+               };
+               u8 rssi[7];
+       };
+
+       u8 evm_stream0[6], evm_stream1[6];
+       u8 phy_err;
+       u8 SAidx, DAidx;
+       u8 error;
+       u8 status;
+};
+
+#define AR9170_ENC_ALG_NONE                    0x0
+#define AR9170_ENC_ALG_WEP64                   0x1
+#define AR9170_ENC_ALG_TKIP                    0x2
+#define AR9170_ENC_ALG_AESCCMP                 0x4
+#define AR9170_ENC_ALG_WEP128                  0x5
+#define AR9170_ENC_ALG_WEP256                  0x6
+#define AR9170_ENC_ALG_CENC                    0x7
+
+#define AR9170_RX_ENC_SOFTWARE                 0x8
+
+static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_tail *t)
+{
+       return (t->SAidx & 0xc0) >> 4 |
+              (t->DAidx & 0xc0) >> 6;
+}
+
+#define AR9170_RX_STATUS_MODULATION_MASK       0x03
+#define AR9170_RX_STATUS_MODULATION_CCK                0x00
+#define AR9170_RX_STATUS_MODULATION_OFDM       0x01
+#define AR9170_RX_STATUS_MODULATION_HT         0x02
+#define AR9170_RX_STATUS_MODULATION_DUPOFDM    0x03
+
+/* depends on modulation */
+#define AR9170_RX_STATUS_SHORT_PREAMBLE                0x08
+#define AR9170_RX_STATUS_GREENFIELD            0x08
+
+#define AR9170_RX_STATUS_MPDU_MASK             0x30
+#define AR9170_RX_STATUS_MPDU_SINGLE           0x00
+#define AR9170_RX_STATUS_MPDU_FIRST            0x10
+#define AR9170_RX_STATUS_MPDU_MIDDLE           0x20
+#define AR9170_RX_STATUS_MPDU_LAST             0x30
+
+
+#define AR9170_RX_ERROR_RXTO                   0x01
+#define AR9170_RX_ERROR_OVERRUN                        0x02
+#define AR9170_RX_ERROR_DECRYPT                        0x04
+#define AR9170_RX_ERROR_FCS                    0x08
+#define AR9170_RX_ERROR_WRONG_RA               0x10
+#define AR9170_RX_ERROR_PLCP                   0x20
+#define AR9170_RX_ERROR_MMIC                   0x40
+
+struct ar9170_cmd_tx_status {
+       __le16 unkn;
+       u8 dst[ETH_ALEN];
+       __le32 rate;
+       __le16 status;
+} __packed;
+
+#define AR9170_TX_STATUS_COMPLETE              0x00
+#define AR9170_TX_STATUS_RETRY                 0x01
+#define AR9170_TX_STATUS_FAILED                        0x02
+
+struct ar9170_cmd_ba_failed_count {
+       __le16 failed;
+       __le16 rate;
+} __packed;
+
+struct ar9170_cmd_response {
+       u8 flag;
+       u8 type;
+
+       union {
+               struct ar9170_cmd_tx_status             tx_status;
+               struct ar9170_cmd_ba_failed_count       ba_fail_cnt;
+               u8 data[0];
+       };
+} __packed;
+
+/* QoS */
+
+/* mac80211 queue to HW/FW map */
+static const u8 ar9170_qos_hwmap[4] = { 3, 2, 0, 1 };
+
+/* HW/FW queue to mac80211 map */
+static const u8 ar9170_qos_mac80211map[4] = { 2, 3, 1, 0 };
+
+enum ar9170_txq {
+       AR9170_TXQ_BE,
+       AR9170_TXQ_BK,
+       AR9170_TXQ_VI,
+       AR9170_TXQ_VO,
+
+       __AR9170_NUM_TXQ,
+};
+
+#endif /* __AR9170_HW_H */
diff --git a/drivers/net/wireless/ar9170/led.c b/drivers/net/wireless/ar9170/led.c
new file mode 100644 (file)
index 0000000..341cead
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * LED handling
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+
+#include "ar9170.h"
+#include "cmd.h"
+
+int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state)
+{
+       return ar9170_write_reg(ar, AR9170_GPIO_REG_DATA, led_state);
+}
+
+int ar9170_init_leds(struct ar9170 *ar)
+{
+       int err;
+
+       /* disable LEDs */
+       /* GPIO [0/1 mode: output, 2/3: input] */
+       err = ar9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3);
+       if (err)
+               goto out;
+
+       /* GPIO 0/1 value: off */
+       err = ar9170_set_leds_state(ar, 0);
+
+out:
+       return err;
+}
+
+#ifdef CONFIG_AR9170_LEDS
+static void ar9170_update_leds(struct work_struct *work)
+{
+       struct ar9170 *ar = container_of(work, struct ar9170, led_work.work);
+       int i, tmp, blink_delay = 1000;
+       u32 led_val = 0;
+       bool rerun = false;
+
+       if (unlikely(!IS_ACCEPTING_CMD(ar)))
+               return ;
+
+       mutex_lock(&ar->mutex);
+       for (i = 0; i < AR9170_NUM_LEDS; i++)
+               if (ar->leds[i].toggled) {
+                       led_val |= 1 << i;
+
+                       tmp = 70 + 200 / (ar->leds[i].toggled);
+                       if (tmp < blink_delay)
+                               blink_delay = tmp;
+
+                       if (ar->leds[i].toggled > 1)
+                               ar->leds[i].toggled = 0;
+
+                       rerun = true;
+               }
+
+       ar9170_set_leds_state(ar, led_val);
+       mutex_unlock(&ar->mutex);
+
+       if (rerun)
+               queue_delayed_work(ar->hw->workqueue, &ar->led_work,
+                                  msecs_to_jiffies(blink_delay));
+}
+
+static void ar9170_led_brightness_set(struct led_classdev *led,
+                                     enum led_brightness brightness)
+{
+       struct ar9170_led *arl = container_of(led, struct ar9170_led, l);
+       struct ar9170 *ar = arl->ar;
+
+       arl->toggled++;
+
+       if (likely(IS_ACCEPTING_CMD(ar) && brightness))
+               queue_delayed_work(ar->hw->workqueue, &ar->led_work, HZ/10);
+}
+
+static int ar9170_register_led(struct ar9170 *ar, int i, char *name,
+                              char *trigger)
+{
+       int err;
+
+       snprintf(ar->leds[i].name, sizeof(ar->leds[i].name),
+                "ar9170-%s::%s", wiphy_name(ar->hw->wiphy), name);
+
+       ar->leds[i].ar = ar;
+       ar->leds[i].l.name = ar->leds[i].name;
+       ar->leds[i].l.brightness_set = ar9170_led_brightness_set;
+       ar->leds[i].l.brightness = 0;
+       ar->leds[i].l.default_trigger = trigger;
+
+       err = led_classdev_register(wiphy_dev(ar->hw->wiphy),
+                                   &ar->leds[i].l);
+       if (err)
+               printk(KERN_ERR "%s: failed to register %s LED (%d).\n",
+                      wiphy_name(ar->hw->wiphy), ar->leds[i].name, err);
+       else
+               ar->leds[i].registered = true;
+
+       return err;
+}
+
+void ar9170_unregister_leds(struct ar9170 *ar)
+{
+       int i;
+
+       cancel_delayed_work_sync(&ar->led_work);
+
+       for (i = 0; i < AR9170_NUM_LEDS; i++)
+               if (ar->leds[i].registered) {
+                       led_classdev_unregister(&ar->leds[i].l);
+                       ar->leds[i].registered = false;
+               }
+}
+
+int ar9170_register_leds(struct ar9170 *ar)
+{
+       int err;
+
+       INIT_DELAYED_WORK(&ar->led_work, ar9170_update_leds);
+
+       err = ar9170_register_led(ar, 0, "tx",
+                                 ieee80211_get_tx_led_name(ar->hw));
+       if (err)
+               goto fail;
+
+       err = ar9170_register_led(ar, 1, "assoc",
+                                ieee80211_get_assoc_led_name(ar->hw));
+       if (err)
+               goto fail;
+
+       return 0;
+
+fail:
+       ar9170_unregister_leds(ar);
+       return err;
+}
+
+#endif /* CONFIG_AR9170_LEDS */
diff --git a/drivers/net/wireless/ar9170/mac.c b/drivers/net/wireless/ar9170/mac.c
new file mode 100644 (file)
index 0000000..c8fa307
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * MAC programming
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+#include "ar9170.h"
+#include "cmd.h"
+
+int ar9170_set_qos(struct ar9170 *ar)
+{
+       ar9170_regwrite_begin(ar);
+
+       ar9170_regwrite(AR9170_MAC_REG_AC0_CW, ar->edcf[0].cw_min |
+                       (ar->edcf[0].cw_max << 16));
+       ar9170_regwrite(AR9170_MAC_REG_AC1_CW, ar->edcf[1].cw_min |
+                       (ar->edcf[1].cw_max << 16));
+       ar9170_regwrite(AR9170_MAC_REG_AC2_CW, ar->edcf[2].cw_min |
+                       (ar->edcf[2].cw_max << 16));
+       ar9170_regwrite(AR9170_MAC_REG_AC3_CW, ar->edcf[3].cw_min |
+                       (ar->edcf[3].cw_max << 16));
+       ar9170_regwrite(AR9170_MAC_REG_AC4_CW, ar->edcf[4].cw_min |
+                       (ar->edcf[4].cw_max << 16));
+
+       ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_AIFS,
+                       ((ar->edcf[0].aifs * 9 + 10)) |
+                       ((ar->edcf[1].aifs * 9 + 10) << 12) |
+                       ((ar->edcf[2].aifs * 9 + 10) << 24));
+       ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_AIFS,
+                       ((ar->edcf[2].aifs * 9 + 10) >> 8) |
+                       ((ar->edcf[3].aifs * 9 + 10) << 4) |
+                       ((ar->edcf[4].aifs * 9 + 10) << 16));
+
+       ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP,
+                       ar->edcf[0].txop | ar->edcf[1].txop << 16);
+       ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP,
+                       ar->edcf[1].txop | ar->edcf[3].txop << 16);
+
+       ar9170_regwrite_finish();
+
+       return ar9170_regwrite_result();
+}
+
+int ar9170_init_mac(struct ar9170 *ar)
+{
+       ar9170_regwrite_begin(ar);
+
+       ar9170_regwrite(AR9170_MAC_REG_ACK_EXTENSION, 0x40);
+
+       ar9170_regwrite(AR9170_MAC_REG_RETRY_MAX, 0);
+
+       /* enable MMIC */
+       ar9170_regwrite(AR9170_MAC_REG_SNIFFER,
+                       AR9170_MAC_REG_SNIFFER_DEFAULTS);
+
+       ar9170_regwrite(AR9170_MAC_REG_RX_THRESHOLD, 0xc1f80);
+
+       ar9170_regwrite(AR9170_MAC_REG_RX_PE_DELAY, 0x70);
+       ar9170_regwrite(AR9170_MAC_REG_EIFS_AND_SIFS, 0xa144000);
+       ar9170_regwrite(AR9170_MAC_REG_SLOT_TIME, 9 << 10);
+
+       /* CF-END mode */
+       ar9170_regwrite(0x1c3b2c, 0x19000000);
+
+       /* NAV protects ACK only (in TXOP) */
+       ar9170_regwrite(0x1c3b38, 0x201);
+
+       /* Set Beacon PHY CTRL's TPC to 0x7, TA1=1 */
+       /* OTUS set AM to 0x1 */
+       ar9170_regwrite(AR9170_MAC_REG_BCN_HT1, 0x8000170);
+
+       ar9170_regwrite(AR9170_MAC_REG_BACKOFF_PROTECT, 0x105);
+
+       /* AGG test code*/
+       /* Aggregation MAX number and timeout */
+       ar9170_regwrite(0x1c3b9c, 0x10000a);
+
+       ar9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER,
+                       AR9170_MAC_REG_FTF_DEFAULTS);
+
+       /* Enable deaggregator, response in sniffer mode */
+       ar9170_regwrite(0x1c3c40, 0x1 | 1<<30);
+
+       /* rate sets */
+       ar9170_regwrite(AR9170_MAC_REG_BASIC_RATE, 0x150f);
+       ar9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, 0x150f);
+       ar9170_regwrite(AR9170_MAC_REG_RTS_CTS_RATE, 0x10b01bb);
+
+       /* MIMO response control */
+       ar9170_regwrite(0x1c3694, 0x4003C1E);/* bit 26~28  otus-AM */
+
+       /* switch MAC to OTUS interface */
+       ar9170_regwrite(0x1c3600, 0x3);
+
+       ar9170_regwrite(AR9170_MAC_REG_AMPDU_RX_THRESH, 0xffff);
+
+       /* set PHY register read timeout (??) */
+       ar9170_regwrite(AR9170_MAC_REG_MISC_680, 0xf00008);
+
+       /* Disable Rx TimeOut, workaround for BB. */
+       ar9170_regwrite(AR9170_MAC_REG_RX_TIMEOUT, 0x0);
+
+       /* Set CPU clock frequency to 88/80MHz */
+       ar9170_regwrite(AR9170_PWR_REG_CLOCK_SEL,
+                       AR9170_PWR_CLK_AHB_80_88MHZ |
+                       AR9170_PWR_CLK_DAC_160_INV_DLY);
+
+       /* Set WLAN DMA interrupt mode: generate int per packet */
+       ar9170_regwrite(AR9170_MAC_REG_TXRX_MPI, 0x110011);
+
+       ar9170_regwrite(AR9170_MAC_REG_FCS_SELECT,
+                       AR9170_MAC_FCS_FIFO_PROT);
+
+       /* Disables the CF_END frame, undocumented register */
+       ar9170_regwrite(AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND,
+                       0x141E0F48);
+
+       ar9170_regwrite_finish();
+
+       return ar9170_regwrite_result();
+}
+
+static int ar9170_set_mac_reg(struct ar9170 *ar, const u32 reg, const u8 *mac)
+{
+       static const u8 zero[ETH_ALEN] = { 0 };
+
+       if (!mac)
+               mac = zero;
+
+       ar9170_regwrite_begin(ar);
+
+       ar9170_regwrite(reg,
+                       (mac[3] << 24) | (mac[2] << 16) |
+                       (mac[1] << 8) | mac[0]);
+
+       ar9170_regwrite(reg + 4, (mac[5] << 8) | mac[4]);
+
+       ar9170_regwrite_finish();
+
+       return ar9170_regwrite_result();
+}
+
+int ar9170_update_multicast(struct ar9170 *ar)
+{
+       int err;
+
+       ar9170_regwrite_begin(ar);
+       ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H,
+               ar->want_mc_hash >> 32);
+       ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L,
+               ar->want_mc_hash);
+
+       ar9170_regwrite_finish();
+       err = ar9170_regwrite_result();
+
+       if (err)
+               return err;
+
+       ar->cur_mc_hash = ar->want_mc_hash;
+
+       return 0;
+}
+
+int ar9170_update_frame_filter(struct ar9170 *ar)
+{
+       int err;
+
+       err = ar9170_write_reg(ar, AR9170_MAC_REG_FRAMETYPE_FILTER,
+                              ar->want_filter);
+
+       if (err)
+               return err;
+
+       ar->cur_filter = ar->want_filter;
+
+       return 0;
+}
+
+static int ar9170_set_promiscouous(struct ar9170 *ar)
+{
+       u32 encr_mode, sniffer;
+       int err;
+
+       err = ar9170_read_reg(ar, AR9170_MAC_REG_SNIFFER, &sniffer);
+       if (err)
+               return err;
+
+       err = ar9170_read_reg(ar, AR9170_MAC_REG_ENCRYPTION, &encr_mode);
+       if (err)
+               return err;
+
+       if (ar->sniffer_enabled) {
+               sniffer |= AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC;
+
+               /*
+                * Rx decryption works in place.
+                *
+                * If we don't disable it, the hardware will render all
+                * encrypted frames which are encrypted with an unknown
+                * key useless.
+                */
+
+               encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
+               ar->sniffer_enabled = true;
+       } else {
+               sniffer &= ~AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC;
+
+               if (ar->rx_software_decryption)
+                       encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
+               else
+                       encr_mode &= ~AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
+       }
+
+       ar9170_regwrite_begin(ar);
+       ar9170_regwrite(AR9170_MAC_REG_ENCRYPTION, encr_mode);
+       ar9170_regwrite(AR9170_MAC_REG_SNIFFER, sniffer);
+       ar9170_regwrite_finish();
+
+       return ar9170_regwrite_result();
+}
+
+int ar9170_set_operating_mode(struct ar9170 *ar)
+{
+       u32 pm_mode = AR9170_MAC_REG_POWERMGT_DEFAULTS;
+       u8 *mac_addr, *bssid;
+       int err;
+
+       if (ar->vif) {
+               mac_addr = ar->mac_addr;
+               bssid = ar->bssid;
+
+               switch (ar->vif->type) {
+               case NL80211_IFTYPE_MESH_POINT:
+               case NL80211_IFTYPE_ADHOC:
+                       pm_mode |= AR9170_MAC_REG_POWERMGT_IBSS;
+                       break;
+/*             case NL80211_IFTYPE_AP:
+                       pm_mode |= AR9170_MAC_REG_POWERMGT_AP;
+                       break;*/
+               case NL80211_IFTYPE_WDS:
+                       pm_mode |= AR9170_MAC_REG_POWERMGT_AP_WDS;
+                       break;
+               case NL80211_IFTYPE_MONITOR:
+                       ar->sniffer_enabled = true;
+                       ar->rx_software_decryption = true;
+                       break;
+               default:
+                       pm_mode |= AR9170_MAC_REG_POWERMGT_STA;
+                       break;
+               }
+       } else {
+               mac_addr = NULL;
+               bssid = NULL;
+       }
+
+       err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_MAC_ADDR_L, mac_addr);
+       if (err)
+               return err;
+
+       err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_BSSID_L, bssid);
+       if (err)
+               return err;
+
+       err = ar9170_set_promiscouous(ar);
+       if (err)
+               return err;
+
+       ar9170_regwrite_begin(ar);
+
+       ar9170_regwrite(AR9170_MAC_REG_POWERMANAGEMENT, pm_mode);
+       ar9170_regwrite_finish();
+
+       return ar9170_regwrite_result();
+}
+
+int ar9170_set_hwretry_limit(struct ar9170 *ar, unsigned int max_retry)
+{
+       u32 tmp = min_t(u32, 0x33333, max_retry * 0x11111);
+
+       return ar9170_write_reg(ar, AR9170_MAC_REG_RETRY_MAX, tmp);
+}
+
+int ar9170_set_beacon_timers(struct ar9170 *ar)
+{
+       u32 v = 0;
+       u32 pretbtt = 0;
+
+       v |= ar->hw->conf.beacon_int;
+
+       if (ar->vif) {
+               switch (ar->vif->type) {
+               case NL80211_IFTYPE_MESH_POINT:
+               case NL80211_IFTYPE_ADHOC:
+                       v |= BIT(25);
+                       break;
+               case NL80211_IFTYPE_AP:
+                       v |= BIT(24);
+                       pretbtt = (ar->hw->conf.beacon_int - 6) << 16;
+                       break;
+               default:
+                       break;
+               }
+
+               v |= ar->vif->bss_conf.dtim_period << 16;
+       }
+
+       ar9170_regwrite_begin(ar);
+
+       ar9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt);
+       ar9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v);
+       ar9170_regwrite_finish();
+       return ar9170_regwrite_result();
+}
+
+int ar9170_update_beacon(struct ar9170 *ar)
+{
+       struct sk_buff *skb;
+       __le32 *data, *old = NULL;
+       u32 word;
+       int i;
+
+       skb = ieee80211_beacon_get(ar->hw, ar->vif);
+       if (!skb)
+               return -ENOMEM;
+
+       data = (__le32 *)skb->data;
+       if (ar->beacon)
+               old = (__le32 *)ar->beacon->data;
+
+       ar9170_regwrite_begin(ar);
+       for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
+               /*
+                * XXX: This accesses beyond skb data for up
+                *      to the last 3 bytes!!
+                */
+
+               if (old && (data[i] == old[i]))
+                       continue;
+
+               word = le32_to_cpu(data[i]);
+               ar9170_regwrite(AR9170_BEACON_BUFFER_ADDRESS + 4 * i, word);
+       }
+
+       /* XXX: use skb->cb info */
+       if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+               ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP,
+                               ((skb->len + 4) << (3+16)) + 0x0400);
+       else
+               ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP,
+                               ((skb->len + 4) << (3+16)) + 0x0400);
+
+       ar9170_regwrite(AR9170_MAC_REG_BCN_LENGTH, skb->len + 4);
+       ar9170_regwrite(AR9170_MAC_REG_BCN_ADDR, AR9170_BEACON_BUFFER_ADDRESS);
+       ar9170_regwrite(AR9170_MAC_REG_BCN_CTRL, 1);
+
+       ar9170_regwrite_finish();
+
+       dev_kfree_skb(ar->beacon);
+       ar->beacon = skb;
+
+       return ar9170_regwrite_result();
+}
+
+void ar9170_new_beacon(struct work_struct *work)
+{
+       struct ar9170 *ar = container_of(work, struct ar9170,
+                                        beacon_work);
+       struct sk_buff *skb;
+
+       if (unlikely(!IS_STARTED(ar)))
+               return ;
+
+       mutex_lock(&ar->mutex);
+
+       if (!ar->vif)
+               goto out;
+
+       ar9170_update_beacon(ar);
+
+       rcu_read_lock();
+       while ((skb = ieee80211_get_buffered_bc(ar->hw, ar->vif)))
+               ar9170_op_tx(ar->hw, skb);
+
+       rcu_read_unlock();
+
+ out:
+       mutex_unlock(&ar->mutex);
+}
+
+int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype,
+                     u8 keyidx, u8 *keydata, int keylen)
+{
+       __le32 vals[7];
+       static const u8 bcast[ETH_ALEN] =
+               { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+       u8 dummy;
+
+       mac = mac ? : bcast;
+
+       vals[0] = cpu_to_le32((keyidx << 16) + id);
+       vals[1] = cpu_to_le32(mac[1] << 24 | mac[0] << 16 | ktype);
+       vals[2] = cpu_to_le32(mac[5] << 24 | mac[4] << 16 |
+                             mac[3] << 8 | mac[2]);
+       memset(&vals[3], 0, 16);
+       if (keydata)
+               memcpy(&vals[3], keydata, keylen);
+
+       return ar->exec_cmd(ar, AR9170_CMD_EKEY,
+                           sizeof(vals), (u8 *)vals,
+                           1, &dummy);
+}
+
+int ar9170_disable_key(struct ar9170 *ar, u8 id)
+{
+       __le32 val = cpu_to_le32(id);
+       u8 dummy;
+
+       return ar->exec_cmd(ar, AR9170_CMD_EKEY,
+                           sizeof(val), (u8 *)&val,
+                           1, &dummy);
+}
diff --git a/drivers/net/wireless/ar9170/main.c b/drivers/net/wireless/ar9170/main.c
new file mode 100644 (file)
index 0000000..5996ff9
--- /dev/null
@@ -0,0 +1,1671 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * mac80211 interaction code
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, Christian Lamparter <chunkeey@web.de>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include "ar9170.h"
+#include "hw.h"
+#include "cmd.h"
+
+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+
+#define RATE(_bitrate, _hw_rate, _txpidx, _flags) {    \
+       .bitrate        = (_bitrate),                   \
+       .flags          = (_flags),                     \
+       .hw_value       = (_hw_rate) | (_txpidx) << 4,  \
+}
+
+static struct ieee80211_rate __ar9170_ratetable[] = {
+       RATE(10, 0, 0, 0),
+       RATE(20, 1, 1, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATE(55, 2, 2, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATE(110, 3, 3, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATE(60, 0xb, 0, 0),
+       RATE(90, 0xf, 0, 0),
+       RATE(120, 0xa, 0, 0),
+       RATE(180, 0xe, 0, 0),
+       RATE(240, 0x9, 0, 0),
+       RATE(360, 0xd, 1, 0),
+       RATE(480, 0x8, 2, 0),
+       RATE(540, 0xc, 3, 0),
+};
+#undef RATE
+
+#define ar9170_g_ratetable     (__ar9170_ratetable + 0)
+#define ar9170_g_ratetable_size        12
+#define ar9170_a_ratetable     (__ar9170_ratetable + 4)
+#define ar9170_a_ratetable_size        8
+
+/*
+ * NB: The hw_value is used as an index into the ar9170_phy_freq_params
+ *     array in phy.c so that we don't have to do frequency lookups!
+ */
+#define CHAN(_freq, _idx) {            \
+       .center_freq    = (_freq),      \
+       .hw_value       = (_idx),       \
+       .max_power      = 18, /* XXX */ \
+}
+
+static struct ieee80211_channel ar9170_2ghz_chantable[] = {
+       CHAN(2412,  0),
+       CHAN(2417,  1),
+       CHAN(2422,  2),
+       CHAN(2427,  3),
+       CHAN(2432,  4),
+       CHAN(2437,  5),
+       CHAN(2442,  6),
+       CHAN(2447,  7),
+       CHAN(2452,  8),
+       CHAN(2457,  9),
+       CHAN(2462, 10),
+       CHAN(2467, 11),
+       CHAN(2472, 12),
+       CHAN(2484, 13),
+};
+
+static struct ieee80211_channel ar9170_5ghz_chantable[] = {
+       CHAN(4920, 14),
+       CHAN(4940, 15),
+       CHAN(4960, 16),
+       CHAN(4980, 17),
+       CHAN(5040, 18),
+       CHAN(5060, 19),
+       CHAN(5080, 20),
+       CHAN(5180, 21),
+       CHAN(5200, 22),
+       CHAN(5220, 23),
+       CHAN(5240, 24),
+       CHAN(5260, 25),
+       CHAN(5280, 26),
+       CHAN(5300, 27),
+       CHAN(5320, 28),
+       CHAN(5500, 29),
+       CHAN(5520, 30),
+       CHAN(5540, 31),
+       CHAN(5560, 32),
+       CHAN(5580, 33),
+       CHAN(5600, 34),
+       CHAN(5620, 35),
+       CHAN(5640, 36),
+       CHAN(5660, 37),
+       CHAN(5680, 38),
+       CHAN(5700, 39),
+       CHAN(5745, 40),
+       CHAN(5765, 41),
+       CHAN(5785, 42),
+       CHAN(5805, 43),
+       CHAN(5825, 44),
+       CHAN(5170, 45),
+       CHAN(5190, 46),
+       CHAN(5210, 47),
+       CHAN(5230, 48),
+};
+#undef CHAN
+
+static struct ieee80211_supported_band ar9170_band_2GHz = {
+       .channels       = ar9170_2ghz_chantable,
+       .n_channels     = ARRAY_SIZE(ar9170_2ghz_chantable),
+       .bitrates       = ar9170_g_ratetable,
+       .n_bitrates     = ar9170_g_ratetable_size,
+};
+
+#ifdef AR9170_QUEUE_DEBUG
+/*
+ * In case some wants works with AR9170's crazy tx_status queueing techniques.
+ * He might need this rather useful probing function.
+ *
+ * NOTE: caller must hold the queue's spinlock!
+ */
+
+static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb)
+{
+       struct ar9170_tx_control *txc = (void *) skb->data;
+       struct ieee80211_hdr *hdr = (void *)txc->frame_data;
+
+       printk(KERN_DEBUG "%s: => FRAME [skb:%p, queue:%d, DA:[%pM] "
+                         "mac_control:%04x, phy_control:%08x]\n",
+              wiphy_name(ar->hw->wiphy), skb, skb_get_queue_mapping(skb),
+              ieee80211_get_DA(hdr), le16_to_cpu(txc->mac_control),
+              le32_to_cpu(txc->phy_control));
+}
+
+static void ar9170_dump_station_tx_status_queue(struct ar9170 *ar,
+                                               struct sk_buff_head *queue)
+{
+       struct sk_buff *skb;
+       int i = 0;
+
+       printk(KERN_DEBUG "---[ cut here ]---\n");
+       printk(KERN_DEBUG "%s: %d entries in tx_status queue.\n",
+              wiphy_name(ar->hw->wiphy), skb_queue_len(queue));
+
+       skb_queue_walk(queue, skb) {
+               struct ar9170_tx_control *txc = (void *) skb->data;
+               struct ieee80211_hdr *hdr = (void *)txc->frame_data;
+
+               printk(KERN_DEBUG "index:%d => \n", i);
+               ar9170_print_txheader(ar, skb);
+       }
+       printk(KERN_DEBUG "---[ end ]---\n");
+}
+#endif /* AR9170_QUEUE_DEBUG */
+
+static struct ieee80211_supported_band ar9170_band_5GHz = {
+       .channels       = ar9170_5ghz_chantable,
+       .n_channels     = ARRAY_SIZE(ar9170_5ghz_chantable),
+       .bitrates       = ar9170_a_ratetable,
+       .n_bitrates     = ar9170_a_ratetable_size,
+};
+
+void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+                            bool valid_status, u16 tx_status)
+{
+       struct ieee80211_tx_info *txinfo;
+       unsigned int retries = 0, queue = skb_get_queue_mapping(skb);
+       unsigned long flags;
+
+       spin_lock_irqsave(&ar->tx_stats_lock, flags);
+       ar->tx_stats[queue].len--;
+       if (ieee80211_queue_stopped(ar->hw, queue))
+               ieee80211_wake_queue(ar->hw, queue);
+       spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+
+       txinfo = IEEE80211_SKB_CB(skb);
+       ieee80211_tx_info_clear_status(txinfo);
+
+       switch (tx_status) {
+       case AR9170_TX_STATUS_RETRY:
+               retries = 2;
+       case AR9170_TX_STATUS_COMPLETE:
+               txinfo->flags |= IEEE80211_TX_STAT_ACK;
+               break;
+
+       case AR9170_TX_STATUS_FAILED:
+               retries = ar->hw->conf.long_frame_max_tx_count;
+               break;
+
+       default:
+               printk(KERN_ERR "%s: invalid tx_status response (%x).\n",
+                      wiphy_name(ar->hw->wiphy), tx_status);
+               break;
+       }
+
+       if (valid_status)
+               txinfo->status.rates[0].count = retries + 1;
+
+       skb_pull(skb, sizeof(struct ar9170_tx_control));
+       ieee80211_tx_status_irqsafe(ar->hw, skb);
+}
+
+static struct sk_buff *ar9170_find_skb_in_queue(struct ar9170 *ar,
+                                               const u8 *mac,
+                                               const u32 queue,
+                                               struct sk_buff_head *q)
+{
+       unsigned long flags;
+       struct sk_buff *skb;
+
+       spin_lock_irqsave(&q->lock, flags);
+       skb_queue_walk(q, skb) {
+               struct ar9170_tx_control *txc = (void *) skb->data;
+               struct ieee80211_hdr *hdr = (void *) txc->frame_data;
+               u32 txc_queue = (le32_to_cpu(txc->phy_control) &
+                               AR9170_TX_PHY_QOS_MASK) >>
+                               AR9170_TX_PHY_QOS_SHIFT;
+
+               if  ((queue != txc_queue) ||
+                    (compare_ether_addr(ieee80211_get_DA(hdr), mac)))
+                       continue;
+
+               __skb_unlink(skb, q);
+               spin_unlock_irqrestore(&q->lock, flags);
+               return skb;
+       }
+       spin_unlock_irqrestore(&q->lock, flags);
+       return NULL;
+}
+
+static struct sk_buff *ar9170_find_queued_skb(struct ar9170 *ar, const u8 *mac,
+                                             const u32 queue)
+{
+       struct ieee80211_sta *sta;
+       struct sk_buff *skb;
+
+       /*
+        * Unfortunately, the firmware does not tell to which (queued) frame
+        * this transmission status report belongs to.
+        *
+        * So we have to make risky guesses - with the scarce information
+        * the firmware provided (-> destination MAC, and phy_control) -
+        * and hope that we picked the right one...
+        */
+       rcu_read_lock();
+       sta = ieee80211_find_sta(ar->hw, mac);
+
+       if (likely(sta)) {
+               struct ar9170_sta_info *sta_priv = (void *) sta->drv_priv;
+               skb = skb_dequeue(&sta_priv->tx_status[queue]);
+               rcu_read_unlock();
+               if (likely(skb))
+                       return skb;
+       } else
+               rcu_read_unlock();
+
+       /* scan the waste queue for candidates */
+       skb = ar9170_find_skb_in_queue(ar, mac, queue,
+                                      &ar->global_tx_status_waste);
+       if (!skb) {
+               /* so it still _must_ be in the global list. */
+               skb = ar9170_find_skb_in_queue(ar, mac, queue,
+                                              &ar->global_tx_status);
+       }
+
+#ifdef AR9170_QUEUE_DEBUG
+       if (unlikely((!skb) && net_ratelimit())) {
+               printk(KERN_ERR "%s: ESS:[%pM] does not have any "
+                               "outstanding frames in this queue (%d).\n",
+                               wiphy_name(ar->hw->wiphy), mac, queue);
+       }
+#endif /* AR9170_QUEUE_DEBUG */
+       return skb;
+}
+
+/*
+ * This worker tries to keep the global tx_status queue empty.
+ * So we can guarantee that incoming tx_status reports for
+ * unregistered stations are always synced with the actual
+ * frame - which we think - belongs to.
+ */
+
+static void ar9170_tx_status_janitor(struct work_struct *work)
+{
+       struct ar9170 *ar = container_of(work, struct ar9170,
+                                        tx_status_janitor.work);
+       struct sk_buff *skb;
+
+       if (unlikely(!IS_STARTED(ar)))
+               return ;
+
+       mutex_lock(&ar->mutex);
+       /* recycle the garbage back to mac80211... one by one. */
+       while ((skb = skb_dequeue(&ar->global_tx_status_waste))) {
+#ifdef AR9170_QUEUE_DEBUG
+               printk(KERN_DEBUG "%s: dispose queued frame =>\n",
+                      wiphy_name(ar->hw->wiphy));
+               ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+               ar9170_handle_tx_status(ar, skb, false,
+                                       AR9170_TX_STATUS_FAILED);
+       }
+
+       while ((skb = skb_dequeue(&ar->global_tx_status))) {
+#ifdef AR9170_QUEUE_DEBUG
+               printk(KERN_DEBUG "%s: moving frame into waste queue =>\n",
+                      wiphy_name(ar->hw->wiphy));
+
+               ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+               skb_queue_tail(&ar->global_tx_status_waste, skb);
+       }
+
+       /* recall the janitor in 100ms - if there's garbage in the can. */
+       if (skb_queue_len(&ar->global_tx_status_waste) > 0)
+               queue_delayed_work(ar->hw->workqueue, &ar->tx_status_janitor,
+                                  msecs_to_jiffies(100));
+
+       mutex_unlock(&ar->mutex);
+}
+
+static void ar9170_handle_command_response(struct ar9170 *ar,
+                                          void *buf, u32 len)
+{
+       struct ar9170_cmd_response *cmd = (void *) buf;
+
+       if ((cmd->type & 0xc0) != 0xc0) {
+               ar->callback_cmd(ar, len, buf);
+               return;
+       }
+
+       /* hardware event handlers */
+       switch (cmd->type) {
+       case 0xc1: {
+               /*
+                * TX status notification:
+                * bytes: 0c c1 XX YY M1 M2 M3 M4 M5 M6 R4 R3 R2 R1 S2 S1
+                *
+                * XX always 81
+                * YY always 00
+                * M1-M6 is the MAC address
+                * R1-R4 is the transmit rate
+                * S1-S2 is the transmit status
+                */
+
+               struct sk_buff *skb;
+               u32 queue = (le32_to_cpu(cmd->tx_status.rate) &
+                           AR9170_TX_PHY_QOS_MASK) >> AR9170_TX_PHY_QOS_SHIFT;
+
+               skb = ar9170_find_queued_skb(ar, cmd->tx_status.dst, queue);
+               if (unlikely(!skb))
+                       return ;
+
+               ar9170_handle_tx_status(ar, skb, true,
+                                       le16_to_cpu(cmd->tx_status.status));
+               break;
+               }
+
+       case 0xc0:
+               /*
+                * pre-TBTT event
+                */
+               if (ar->vif && ar->vif->type == NL80211_IFTYPE_AP)
+                       queue_work(ar->hw->workqueue, &ar->beacon_work);
+               break;
+
+       case 0xc2:
+               /*
+                * (IBSS) beacon send notification
+                * bytes: 04 c2 XX YY B4 B3 B2 B1
+                *
+                * XX always 80
+                * YY always 00
+                * B1-B4 "should" be the number of send out beacons.
+                */
+               break;
+
+       case 0xc3:
+               /* End of Atim Window */
+               break;
+
+       case 0xc4:
+       case 0xc5:
+               /* BlockACK events */
+               break;
+
+       case 0xc6:
+               /* Watchdog Interrupt */
+               break;
+
+       case 0xc9:
+               /* retransmission issue / SIFS/EIFS collision ?! */
+               break;
+
+       default:
+               printk(KERN_INFO "received unhandled event %x\n", cmd->type);
+               print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len);
+               break;
+       }
+}
+
+/*
+ * If the frame alignment is right (or the kernel has
+ * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there
+ * is only a single MPDU in the USB frame, then we can
+ * submit to mac80211 the SKB directly. However, since
+ * there may be multiple packets in one SKB in stream
+ * mode, and we need to observe the proper ordering,
+ * this is non-trivial.
+ */
+static void ar9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
+{
+       struct sk_buff *skb;
+       struct ar9170_rx_head *head = (void *)buf;
+       struct ar9170_rx_tail *tail;
+       struct ieee80211_rx_status status;
+       int mpdu_len, i;
+       u8 error, antennas = 0, decrypt;
+       __le16 fc;
+       int reserved;
+
+       if (unlikely(!IS_STARTED(ar)))
+               return ;
+
+       /* Received MPDU */
+       mpdu_len = len;
+       mpdu_len -= sizeof(struct ar9170_rx_head);
+       mpdu_len -= sizeof(struct ar9170_rx_tail);
+       BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12);
+       BUILD_BUG_ON(sizeof(struct ar9170_rx_tail) != 24);
+
+       if (mpdu_len <= FCS_LEN)
+               return;
+
+       tail = (void *)(buf + sizeof(struct ar9170_rx_head) + mpdu_len);
+
+       for (i = 0; i < 3; i++)
+               if (tail->rssi[i] != 0x80)
+                       antennas |= BIT(i);
+
+       /* post-process RSSI */
+       for (i = 0; i < 7; i++)
+               if (tail->rssi[i] & 0x80)
+                       tail->rssi[i] = ((tail->rssi[i] & 0x7f) + 1) & 0x7f;
+
+       memset(&status, 0, sizeof(status));
+
+       status.band = ar->channel->band;
+       status.freq = ar->channel->center_freq;
+       status.signal = ar->noise[0] + tail->rssi_combined;
+       status.noise = ar->noise[0];
+       status.antenna = antennas;
+
+       switch (tail->status & AR9170_RX_STATUS_MODULATION_MASK) {
+       case AR9170_RX_STATUS_MODULATION_CCK:
+               if (tail->status & AR9170_RX_STATUS_SHORT_PREAMBLE)
+                       status.flag |= RX_FLAG_SHORTPRE;
+               switch (head->plcp[0]) {
+               case 0x0a:
+                       status.rate_idx = 0;
+                       break;
+               case 0x14:
+                       status.rate_idx = 1;
+                       break;
+               case 0x37:
+                       status.rate_idx = 2;
+                       break;
+               case 0x6e:
+                       status.rate_idx = 3;
+                       break;
+               default:
+                       if ((!ar->sniffer_enabled) && (net_ratelimit()))
+                               printk(KERN_ERR "%s: invalid plcp cck rate "
+                                      "(%x).\n", wiphy_name(ar->hw->wiphy),
+                                      head->plcp[0]);
+                       return;
+               }
+               break;
+       case AR9170_RX_STATUS_MODULATION_OFDM:
+               switch (head->plcp[0] & 0xF) {
+               case 0xB:
+                       status.rate_idx = 0;
+                       break;
+               case 0xF:
+                       status.rate_idx = 1;
+                       break;
+               case 0xA:
+                       status.rate_idx = 2;
+                       break;
+               case 0xE:
+                       status.rate_idx = 3;
+                       break;
+               case 0x9:
+                       status.rate_idx = 4;
+                       break;
+               case 0xD:
+                       status.rate_idx = 5;
+                       break;
+               case 0x8:
+                       status.rate_idx = 6;
+                       break;
+               case 0xC:
+                       status.rate_idx = 7;
+                       break;
+               default:
+                       if ((!ar->sniffer_enabled) && (net_ratelimit()))
+                               printk(KERN_ERR "%s: invalid plcp ofdm rate "
+                                      "(%x).\n", wiphy_name(ar->hw->wiphy),
+                                      head->plcp[0]);
+                       return;
+               }
+               if (status.band == IEEE80211_BAND_2GHZ)
+                       status.rate_idx += 4;
+               break;
+       case AR9170_RX_STATUS_MODULATION_HT:
+       case AR9170_RX_STATUS_MODULATION_DUPOFDM:
+               /* XXX */
+
+               if (net_ratelimit())
+                       printk(KERN_ERR "%s: invalid modulation\n",
+                              wiphy_name(ar->hw->wiphy));
+               return;
+       }
+
+       error = tail->error;
+
+       if (error & AR9170_RX_ERROR_MMIC) {
+               status.flag |= RX_FLAG_MMIC_ERROR;
+               error &= ~AR9170_RX_ERROR_MMIC;
+       }
+
+       if (error & AR9170_RX_ERROR_PLCP) {
+               status.flag |= RX_FLAG_FAILED_PLCP_CRC;
+               error &= ~AR9170_RX_ERROR_PLCP;
+       }
+
+       if (error & AR9170_RX_ERROR_FCS) {
+               status.flag |= RX_FLAG_FAILED_FCS_CRC;
+               error &= ~AR9170_RX_ERROR_FCS;
+       }
+
+       decrypt = ar9170_get_decrypt_type(tail);
+       if (!(decrypt & AR9170_RX_ENC_SOFTWARE) &&
+           decrypt != AR9170_ENC_ALG_NONE)
+               status.flag |= RX_FLAG_DECRYPTED;
+
+       /* ignore wrong RA errors */
+       error &= ~AR9170_RX_ERROR_WRONG_RA;
+
+       if (error & AR9170_RX_ERROR_DECRYPT) {
+               error &= ~AR9170_RX_ERROR_DECRYPT;
+
+               /*
+                * Rx decryption is done in place,
+                * the original data is lost anyway.
+                */
+               return ;
+       }
+
+       /* drop any other error frames */
+       if ((error) && (net_ratelimit())) {
+               printk(KERN_DEBUG "%s: errors: %#x\n",
+                      wiphy_name(ar->hw->wiphy), error);
+               return;
+       }
+
+       buf += sizeof(struct ar9170_rx_head);
+       fc = *(__le16 *)buf;
+
+       if (ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc))
+               reserved = 32 + 2;
+       else
+               reserved = 32;
+
+       skb = dev_alloc_skb(mpdu_len + reserved);
+       if (!skb)
+               return;
+
+       skb_reserve(skb, reserved);
+       memcpy(skb_put(skb, mpdu_len), buf, mpdu_len);
+       ieee80211_rx_irqsafe(ar->hw, skb, &status);
+}
+
+void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb)
+{
+       unsigned int i, tlen, resplen;
+       u8 *tbuf, *respbuf;
+
+       tbuf = skb->data;
+       tlen = skb->len;
+
+       while (tlen >= 4) {
+               int clen = tbuf[1] << 8 | tbuf[0];
+               int wlen = (clen + 3) & ~3;
+
+               /*
+                * parse stream (if any)
+                */
+               if (tbuf[2] != 0 || tbuf[3] != 0x4e) {
+                       printk(KERN_ERR "%s: missing tag!\n",
+                              wiphy_name(ar->hw->wiphy));
+                       return ;
+               }
+               if (wlen > tlen - 4) {
+                       printk(KERN_ERR "%s: invalid RX (%d, %d, %d)\n",
+                              wiphy_name(ar->hw->wiphy), clen, wlen, tlen);
+                       print_hex_dump(KERN_DEBUG, "data: ",
+                                      DUMP_PREFIX_OFFSET,
+                                      16, 1, tbuf, tlen, true);
+                       return ;
+               }
+               resplen = clen;
+               respbuf = tbuf + 4;
+               tbuf += wlen + 4;
+               tlen -= wlen + 4;
+
+               i = 0;
+
+               /* weird thing, but this is the same in the original driver */
+               while (resplen > 2 && i < 12 &&
+                      respbuf[0] == 0xff && respbuf[1] == 0xff) {
+                       i += 2;
+                       resplen -= 2;
+                       respbuf += 2;
+               }
+
+               if (resplen < 4)
+                       continue;
+
+               /* found the 6 * 0xffff marker? */
+               if (i == 12)
+                       ar9170_handle_command_response(ar, respbuf, resplen);
+               else
+                       ar9170_handle_mpdu(ar, respbuf, resplen);
+       }
+
+       if (tlen)
+               printk(KERN_ERR "%s: buffer remains!\n",
+                      wiphy_name(ar->hw->wiphy));
+}
+
+#define AR9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop)           \
+do {                                                                   \
+       queue.aifs = ai_fs;                                             \
+       queue.cw_min = cwmin;                                           \
+       queue.cw_max = cwmax;                                           \
+       queue.txop = _txop;                                             \
+} while (0)
+
+static int ar9170_op_start(struct ieee80211_hw *hw)
+{
+       struct ar9170 *ar = hw->priv;
+       int err, i;
+
+       mutex_lock(&ar->mutex);
+
+       /* reinitialize queues statistics */
+       memset(&ar->tx_stats, 0, sizeof(ar->tx_stats));
+       for (i = 0; i < ARRAY_SIZE(ar->tx_stats); i++)
+               ar->tx_stats[i].limit = 8;
+
+       /* reset QoS defaults */
+       AR9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023,  0); /* BEST EFFORT*/
+       AR9170_FILL_QUEUE(ar->edcf[1], 7, 15, 1023,  0); /* BACKGROUND */
+       AR9170_FILL_QUEUE(ar->edcf[2], 2, 7,    15, 94); /* VIDEO */
+       AR9170_FILL_QUEUE(ar->edcf[3], 2, 3,     7, 47); /* VOICE */
+       AR9170_FILL_QUEUE(ar->edcf[4], 2, 3,     7,  0); /* SPECIAL */
+
+       err = ar->open(ar);
+       if (err)
+               goto out;
+
+       err = ar9170_init_mac(ar);
+       if (err)
+               goto out;
+
+       err = ar9170_set_qos(ar);
+       if (err)
+               goto out;
+
+       err = ar9170_init_phy(ar, IEEE80211_BAND_2GHZ);
+       if (err)
+               goto out;
+
+       err = ar9170_init_rf(ar);
+       if (err)
+               goto out;
+
+       /* start DMA */
+       err = ar9170_write_reg(ar, 0x1c3d30, 0x100);
+       if (err)
+               goto out;
+
+       ar->state = AR9170_STARTED;
+
+out:
+       mutex_unlock(&ar->mutex);
+       return err;
+}
+
+static void ar9170_op_stop(struct ieee80211_hw *hw)
+{
+       struct ar9170 *ar = hw->priv;
+
+       if (IS_STARTED(ar))
+               ar->state = AR9170_IDLE;
+
+       mutex_lock(&ar->mutex);
+
+       cancel_delayed_work_sync(&ar->tx_status_janitor);
+       cancel_work_sync(&ar->filter_config_work);
+       cancel_work_sync(&ar->beacon_work);
+       skb_queue_purge(&ar->global_tx_status_waste);
+       skb_queue_purge(&ar->global_tx_status);
+
+       if (IS_ACCEPTING_CMD(ar)) {
+               ar9170_set_leds_state(ar, 0);
+
+               /* stop DMA */
+               ar9170_write_reg(ar, 0x1c3d30, 0);
+               ar->stop(ar);
+       }
+
+       mutex_unlock(&ar->mutex);
+}
+
+int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+       struct ar9170 *ar = hw->priv;
+       struct ieee80211_hdr *hdr;
+       struct ar9170_tx_control *txc;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_rate *rate = NULL;
+       struct ieee80211_tx_rate *txrate;
+       unsigned int queue = skb_get_queue_mapping(skb);
+       unsigned long flags = 0;
+       struct ar9170_sta_info *sta_info = NULL;
+       u32 power, chains;
+       u16 keytype = 0;
+       u16 len, icv = 0;
+       int err;
+       bool tx_status;
+
+       if (unlikely(!IS_STARTED(ar)))
+               goto err_free;
+
+       hdr = (void *)skb->data;
+       info = IEEE80211_SKB_CB(skb);
+       len = skb->len;
+
+       spin_lock_irqsave(&ar->tx_stats_lock, flags);
+       if (ar->tx_stats[queue].limit < ar->tx_stats[queue].len) {
+               spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+               return NETDEV_TX_OK;
+       }
+
+       ar->tx_stats[queue].len++;
+       ar->tx_stats[queue].count++;
+       if (ar->tx_stats[queue].limit == ar->tx_stats[queue].len)
+               ieee80211_stop_queue(hw, queue);
+
+       spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+
+       txc = (void *)skb_push(skb, sizeof(*txc));
+
+       tx_status = (((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) != 0) ||
+                   ((info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) != 0));
+
+       if (info->control.hw_key) {
+               icv = info->control.hw_key->icv_len;
+
+               switch (info->control.hw_key->alg) {
+               case ALG_WEP:
+                       keytype = AR9170_TX_MAC_ENCR_RC4;
+                       break;
+               case ALG_TKIP:
+                       keytype = AR9170_TX_MAC_ENCR_RC4;
+                       break;
+               case ALG_CCMP:
+                       keytype = AR9170_TX_MAC_ENCR_AES;
+                       break;
+               default:
+                       WARN_ON(1);
+                       goto err_dequeue;
+               }
+       }
+
+       /* Length */
+       txc->length = cpu_to_le16(len + icv + 4);
+
+       txc->mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION |
+                                      AR9170_TX_MAC_BACKOFF);
+       txc->mac_control |= cpu_to_le16(ar9170_qos_hwmap[queue] <<
+                                       AR9170_TX_MAC_QOS_SHIFT);
+       txc->mac_control |= cpu_to_le16(keytype);
+       txc->phy_control = cpu_to_le32(0);
+
+       if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+               txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_NO_ACK);
+
+       if (info->flags & IEEE80211_TX_CTL_AMPDU)
+               txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+
+       txrate = &info->control.rates[0];
+
+       if (txrate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
+               txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
+       else if (txrate->flags & IEEE80211_TX_RC_USE_RTS_CTS)
+               txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
+
+       if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD)
+               txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD);
+
+       if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+               txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE);
+
+       if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+               txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ);
+       /* this works because 40 MHz is 2 and dup is 3 */
+       if (txrate->flags & IEEE80211_TX_RC_DUP_DATA)
+               txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ_DUP);
+
+       if (txrate->flags & IEEE80211_TX_RC_SHORT_GI)
+               txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI);
+
+       if (txrate->flags & IEEE80211_TX_RC_MCS) {
+               u32 r = txrate->idx;
+               u8 *txpower;
+
+               r <<= AR9170_TX_PHY_MCS_SHIFT;
+               if (WARN_ON(r & ~AR9170_TX_PHY_MCS_MASK))
+                       goto err_dequeue;
+               txc->phy_control |= cpu_to_le32(r & AR9170_TX_PHY_MCS_MASK);
+               txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_MOD_HT);
+
+               if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+                       if (info->band == IEEE80211_BAND_5GHZ)
+                               txpower = ar->power_5G_ht40;
+                       else
+                               txpower = ar->power_2G_ht40;
+               } else {
+                       if (info->band == IEEE80211_BAND_5GHZ)
+                               txpower = ar->power_5G_ht20;
+                       else
+                               txpower = ar->power_2G_ht20;
+               }
+
+               power = txpower[(txrate->idx) & 7];
+       } else {
+               u8 *txpower;
+               u32 mod;
+               u32 phyrate;
+               u8 idx = txrate->idx;
+
+               if (info->band != IEEE80211_BAND_2GHZ) {
+                       idx += 4;
+                       txpower = ar->power_5G_leg;
+                       mod = AR9170_TX_PHY_MOD_OFDM;
+               } else {
+                       if (idx < 4) {
+                               txpower = ar->power_2G_cck;
+                               mod = AR9170_TX_PHY_MOD_CCK;
+                       } else {
+                               mod = AR9170_TX_PHY_MOD_OFDM;
+                               txpower = ar->power_2G_ofdm;
+                       }
+               }
+
+               rate = &__ar9170_ratetable[idx];
+
+               phyrate = rate->hw_value & 0xF;
+               power = txpower[(rate->hw_value & 0x30) >> 4];
+               phyrate <<= AR9170_TX_PHY_MCS_SHIFT;
+
+               txc->phy_control |= cpu_to_le32(mod);
+               txc->phy_control |= cpu_to_le32(phyrate);
+       }
+
+       power <<= AR9170_TX_PHY_TX_PWR_SHIFT;
+       power &= AR9170_TX_PHY_TX_PWR_MASK;
+       txc->phy_control |= cpu_to_le32(power);
+
+       /* set TX chains */
+       if (ar->eeprom.tx_mask == 1) {
+               chains = AR9170_TX_PHY_TXCHAIN_1;
+       } else {
+               chains = AR9170_TX_PHY_TXCHAIN_2;
+
+               /* >= 36M legacy OFDM - use only one chain */
+               if (rate && rate->bitrate >= 360)
+                       chains = AR9170_TX_PHY_TXCHAIN_1;
+       }
+       txc->phy_control |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_SHIFT);
+
+       if (tx_status) {
+               txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE);
+               /*
+                * WARNING:
+                * Putting the QoS queue bits into an unexplored territory is
+                * certainly not elegant.
+                *
+                * In my defense: This idea provides a reasonable way to
+                * smuggle valuable information to the tx_status callback.
+                * Also, the idea behind this bit-abuse came straight from
+                * the original driver code.
+                */
+
+               txc->phy_control |=
+                       cpu_to_le32(queue << AR9170_TX_PHY_QOS_SHIFT);
+
+               if (info->control.sta) {
+                       sta_info = (void *) info->control.sta->drv_priv;
+                       skb_queue_tail(&sta_info->tx_status[queue], skb);
+               } else {
+                       skb_queue_tail(&ar->global_tx_status, skb);
+
+                       queue_delayed_work(ar->hw->workqueue,
+                                          &ar->tx_status_janitor,
+                                          msecs_to_jiffies(100));
+               }
+       }
+
+       err = ar->tx(ar, skb, tx_status, 0);
+       if (unlikely(tx_status && err)) {
+               if (info->control.sta)
+                       skb_unlink(skb, &sta_info->tx_status[queue]);
+               else
+                       skb_unlink(skb, &ar->global_tx_status);
+       }
+
+       return NETDEV_TX_OK;
+
+err_dequeue:
+       spin_lock_irqsave(&ar->tx_stats_lock, flags);
+       ar->tx_stats[queue].len--;
+       ar->tx_stats[queue].count--;
+       spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+
+err_free:
+       dev_kfree_skb(skb);
+       return NETDEV_TX_OK;
+}
+
+static int ar9170_op_add_interface(struct ieee80211_hw *hw,
+                                  struct ieee80211_if_init_conf *conf)
+{
+       struct ar9170 *ar = hw->priv;
+       int err = 0;
+
+       mutex_lock(&ar->mutex);
+
+       if (ar->vif) {
+               err = -EBUSY;
+               goto unlock;
+       }
+
+       ar->vif = conf->vif;
+       memcpy(ar->mac_addr, conf->mac_addr, ETH_ALEN);
+
+       if (modparam_nohwcrypt || (ar->vif->type != NL80211_IFTYPE_STATION)) {
+               ar->rx_software_decryption = true;
+               ar->disable_offload = true;
+       }
+
+       ar->cur_filter = 0;
+       ar->want_filter = AR9170_MAC_REG_FTF_DEFAULTS;
+       err = ar9170_update_frame_filter(ar);
+       if (err)
+               goto unlock;
+
+       err = ar9170_set_operating_mode(ar);
+
+unlock:
+       mutex_unlock(&ar->mutex);
+       return err;
+}
+
+static void ar9170_op_remove_interface(struct ieee80211_hw *hw,
+                                      struct ieee80211_if_init_conf *conf)
+{
+       struct ar9170 *ar = hw->priv;
+
+       mutex_lock(&ar->mutex);
+       ar->vif = NULL;
+       ar->want_filter = 0;
+       ar9170_update_frame_filter(ar);
+       ar9170_set_beacon_timers(ar);
+       dev_kfree_skb(ar->beacon);
+       ar->beacon = NULL;
+       ar->sniffer_enabled = false;
+       ar->rx_software_decryption = false;
+       ar9170_set_operating_mode(ar);
+       mutex_unlock(&ar->mutex);
+}
+
+static int ar9170_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+       struct ar9170 *ar = hw->priv;
+       int err = 0;
+
+       mutex_lock(&ar->mutex);
+
+       if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) {
+               /* TODO */
+               err = 0;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
+               /* TODO */
+               err = 0;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_PS) {
+               /* TODO */
+               err = 0;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_POWER) {
+               /* TODO */
+               err = 0;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+               /*
+                * is it long_frame_max_tx_count or short_frame_max_tx_count?
+                */
+
+               err = ar9170_set_hwretry_limit(ar,
+                       ar->hw->conf.long_frame_max_tx_count);
+               if (err)
+                       goto out;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_BEACON_INTERVAL) {
+               err = ar9170_set_beacon_timers(ar);
+               if (err)
+                       goto out;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               err = ar9170_set_channel(ar, hw->conf.channel,
+                                        AR9170_RFI_NONE, AR9170_BW_20);
+               if (err)
+                       goto out;
+               /* adjust slot time for 5 GHz */
+               if (hw->conf.channel->band == IEEE80211_BAND_5GHZ)
+                       err = ar9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME,
+                                              9 << 10);
+       }
+
+out:
+       mutex_unlock(&ar->mutex);
+       return err;
+}
+
+static int ar9170_op_config_interface(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_if_conf *conf)
+{
+       struct ar9170 *ar = hw->priv;
+       int err = 0;
+
+       mutex_lock(&ar->mutex);
+
+       if (conf->changed & IEEE80211_IFCC_BSSID) {
+               memcpy(ar->bssid, conf->bssid, ETH_ALEN);
+               err = ar9170_set_operating_mode(ar);
+       }
+
+       if (conf->changed & IEEE80211_IFCC_BEACON) {
+               err = ar9170_update_beacon(ar);
+
+               if (err)
+                       goto out;
+               err = ar9170_set_beacon_timers(ar);
+       }
+
+out:
+       mutex_unlock(&ar->mutex);
+       return err;
+}
+
+static void ar9170_set_filters(struct work_struct *work)
+{
+       struct ar9170 *ar = container_of(work, struct ar9170,
+                                        filter_config_work);
+       int err;
+
+       mutex_lock(&ar->mutex);
+       if (unlikely(!IS_STARTED(ar)))
+               goto unlock;
+
+       if (ar->filter_changed & AR9170_FILTER_CHANGED_PROMISC) {
+               err = ar9170_set_operating_mode(ar);
+               if (err)
+                       goto unlock;
+       }
+
+       if (ar->filter_changed & AR9170_FILTER_CHANGED_MULTICAST) {
+               err = ar9170_update_multicast(ar);
+               if (err)
+                       goto unlock;
+       }
+
+       if (ar->filter_changed & AR9170_FILTER_CHANGED_FRAMEFILTER)
+               err = ar9170_update_frame_filter(ar);
+
+unlock:
+       mutex_unlock(&ar->mutex);
+}
+
+static void ar9170_op_configure_filter(struct ieee80211_hw *hw,
+                                      unsigned int changed_flags,
+                                      unsigned int *new_flags,
+                                      int mc_count, struct dev_mc_list *mclist)
+{
+       struct ar9170 *ar = hw->priv;
+
+       /* mask supported flags */
+       *new_flags &= FIF_ALLMULTI | FIF_CONTROL | FIF_BCN_PRBRESP_PROMISC |
+                     FIF_PROMISC_IN_BSS;
+
+       /*
+        * We can support more by setting the sniffer bit and
+        * then checking the error flags, later.
+        */
+
+       if (changed_flags & FIF_ALLMULTI) {
+               if (*new_flags & FIF_ALLMULTI) {
+                       ar->want_mc_hash = ~0ULL;
+               } else {
+                       u64 mchash;
+                       int i;
+
+                       /* always get broadcast frames */
+                       mchash = 1ULL << (0xff>>2);
+
+                       for (i = 0; i < mc_count; i++) {
+                               if (WARN_ON(!mclist))
+                                       break;
+                               mchash |= 1ULL << (mclist->dmi_addr[5] >> 2);
+                               mclist = mclist->next;
+                       }
+               ar->want_mc_hash = mchash;
+               }
+               ar->filter_changed |= AR9170_FILTER_CHANGED_MULTICAST;
+       }
+
+       if (changed_flags & FIF_CONTROL) {
+               u32 filter = AR9170_MAC_REG_FTF_PSPOLL |
+                            AR9170_MAC_REG_FTF_RTS |
+                            AR9170_MAC_REG_FTF_CTS |
+                            AR9170_MAC_REG_FTF_ACK |
+                            AR9170_MAC_REG_FTF_CFE |
+                            AR9170_MAC_REG_FTF_CFE_ACK;
+
+               if (*new_flags & FIF_CONTROL)
+                       ar->want_filter = ar->cur_filter | filter;
+               else
+                       ar->want_filter = ar->cur_filter & ~filter;
+
+               ar->filter_changed |= AR9170_FILTER_CHANGED_FRAMEFILTER;
+       }
+
+       if (changed_flags & FIF_PROMISC_IN_BSS) {
+               ar->sniffer_enabled = ((*new_flags) & FIF_PROMISC_IN_BSS) != 0;
+               ar->filter_changed |= AR9170_FILTER_CHANGED_PROMISC;
+       }
+
+       if (likely(IS_STARTED(ar)))
+               queue_work(ar->hw->workqueue, &ar->filter_config_work);
+}
+
+static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      struct ieee80211_bss_conf *bss_conf,
+                                      u32 changed)
+{
+       struct ar9170 *ar = hw->priv;
+       int err = 0;
+
+       mutex_lock(&ar->mutex);
+
+       ar9170_regwrite_begin(ar);
+
+       if (changed & BSS_CHANGED_ASSOC) {
+               ar->state = bss_conf->assoc ? AR9170_ASSOCIATED : ar->state;
+
+#ifndef CONFIG_AR9170_LEDS
+               /* enable assoc LED. */
+               err = ar9170_set_leds_state(ar, bss_conf->assoc ? 2 : 0);
+#endif /* CONFIG_AR9170_LEDS */
+       }
+
+       if (changed & BSS_CHANGED_HT) {
+               /* TODO */
+               err = 0;
+       }
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               u32 slottime = 20;
+
+               if (bss_conf->use_short_slot)
+                       slottime = 9;
+
+               ar9170_regwrite(AR9170_MAC_REG_SLOT_TIME, slottime << 10);
+       }
+
+       if (changed & BSS_CHANGED_BASIC_RATES) {
+               u32 cck, ofdm;
+
+               if (hw->conf.channel->band == IEEE80211_BAND_5GHZ) {
+                       ofdm = bss_conf->basic_rates;
+                       cck = 0;
+               } else {
+                       /* four cck rates */
+                       cck = bss_conf->basic_rates & 0xf;
+                       ofdm = bss_conf->basic_rates >> 4;
+               }
+               ar9170_regwrite(AR9170_MAC_REG_BASIC_RATE,
+                               ofdm << 8 | cck);
+       }
+
+       ar9170_regwrite_finish();
+       err = ar9170_regwrite_result();
+       mutex_unlock(&ar->mutex);
+}
+
+static u64 ar9170_op_get_tsf(struct ieee80211_hw *hw)
+{
+       struct ar9170 *ar = hw->priv;
+       int err;
+       u32 tsf_low;
+       u32 tsf_high;
+       u64 tsf;
+
+       mutex_lock(&ar->mutex);
+       err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_L, &tsf_low);
+       if (!err)
+               err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_H, &tsf_high);
+       mutex_unlock(&ar->mutex);
+
+       if (WARN_ON(err))
+               return 0;
+
+       tsf = tsf_high;
+       tsf = (tsf << 32) | tsf_low;
+       return tsf;
+}
+
+static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+                         struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+                         struct ieee80211_key_conf *key)
+{
+       struct ar9170 *ar = hw->priv;
+       int err = 0, i;
+       u8 ktype;
+
+       if ((!ar->vif) || (ar->disable_offload))
+               return -EOPNOTSUPP;
+
+       switch (key->alg) {
+       case ALG_WEP:
+               if (key->keylen == LEN_WEP40)
+                       ktype = AR9170_ENC_ALG_WEP64;
+               else
+                       ktype = AR9170_ENC_ALG_WEP128;
+               break;
+       case ALG_TKIP:
+               ktype = AR9170_ENC_ALG_TKIP;
+               break;
+       case ALG_CCMP:
+               ktype = AR9170_ENC_ALG_AESCCMP;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       mutex_lock(&ar->mutex);
+       if (cmd == SET_KEY) {
+               if (unlikely(!IS_STARTED(ar))) {
+                       err = -EOPNOTSUPP;
+                       goto out;
+               }
+
+               /* group keys need all-zeroes address */
+               if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+                       sta = NULL;
+
+               if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+                       for (i = 0; i < 64; i++)
+                               if (!(ar->usedkeys & BIT(i)))
+                                       break;
+                       if (i == 64) {
+                               ar->rx_software_decryption = true;
+                               ar9170_set_operating_mode(ar);
+                               err = -ENOSPC;
+                               goto out;
+                       }
+               } else {
+                       i = 64 + key->keyidx;
+               }
+
+               key->hw_key_idx = i;
+
+               err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL, ktype, 0,
+                                       key->key, min_t(u8, 16, key->keylen));
+               if (err)
+                       goto out;
+
+               if (key->alg == ALG_TKIP) {
+                       err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL,
+                                               ktype, 1, key->key + 16, 16);
+                       if (err)
+                               goto out;
+
+                       /*
+                        * hardware is not capable generating the MMIC
+                        * for fragmented frames!
+                        */
+                       key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+               }
+
+               if (i < 64)
+                       ar->usedkeys |= BIT(i);
+
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+       } else {
+               if (unlikely(!IS_STARTED(ar))) {
+                       /* The device is gone... together with the key ;-) */
+                       err = 0;
+                       goto out;
+               }
+
+               err = ar9170_disable_key(ar, key->hw_key_idx);
+               if (err)
+                       goto out;
+
+               if (key->hw_key_idx < 64) {
+                       ar->usedkeys &= ~BIT(key->hw_key_idx);
+               } else {
+                       err = ar9170_upload_key(ar, key->hw_key_idx, NULL,
+                                               AR9170_ENC_ALG_NONE, 0,
+                                               NULL, 0);
+                       if (err)
+                               goto out;
+
+                       if (key->alg == ALG_TKIP) {
+                               err = ar9170_upload_key(ar, key->hw_key_idx,
+                                                       NULL,
+                                                       AR9170_ENC_ALG_NONE, 1,
+                                                       NULL, 0);
+                               if (err)
+                                       goto out;
+                       }
+
+               }
+       }
+
+       ar9170_regwrite_begin(ar);
+       ar9170_regwrite(AR9170_MAC_REG_ROLL_CALL_TBL_L, ar->usedkeys);
+       ar9170_regwrite(AR9170_MAC_REG_ROLL_CALL_TBL_H, ar->usedkeys >> 32);
+       ar9170_regwrite_finish();
+       err = ar9170_regwrite_result();
+
+out:
+       mutex_unlock(&ar->mutex);
+
+       return err;
+}
+
+static void ar9170_sta_notify(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             enum sta_notify_cmd cmd,
+                             struct ieee80211_sta *sta)
+{
+       struct ar9170 *ar = hw->priv;
+       struct ar9170_sta_info *info = (void *) sta->drv_priv;
+       struct sk_buff *skb;
+       unsigned int i;
+
+       switch (cmd) {
+       case STA_NOTIFY_ADD:
+               for (i = 0; i < ar->hw->queues; i++)
+                       skb_queue_head_init(&info->tx_status[i]);
+               break;
+
+       case STA_NOTIFY_REMOVE:
+
+               /*
+                * transfer all outstanding frames that need a tx_status
+                * reports to the global tx_status queue
+                */
+
+               for (i = 0; i < ar->hw->queues; i++) {
+                       while ((skb = skb_dequeue(&info->tx_status[i]))) {
+#ifdef AR9170_QUEUE_DEBUG
+                               printk(KERN_DEBUG "%s: queueing frame in "
+                                         "global tx_status queue =>\n",
+                                      wiphy_name(ar->hw->wiphy));
+
+                               ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+                               skb_queue_tail(&ar->global_tx_status, skb);
+                       }
+               }
+               queue_delayed_work(ar->hw->workqueue, &ar->tx_status_janitor,
+                                  msecs_to_jiffies(100));
+               break;
+
+       default:
+               break;
+       }
+}
+
+static int ar9170_get_stats(struct ieee80211_hw *hw,
+                           struct ieee80211_low_level_stats *stats)
+{
+       struct ar9170 *ar = hw->priv;
+       u32 val;
+       int err;
+
+       mutex_lock(&ar->mutex);
+       err = ar9170_read_reg(ar, AR9170_MAC_REG_TX_RETRY, &val);
+       ar->stats.dot11ACKFailureCount += val;
+
+       memcpy(stats, &ar->stats, sizeof(*stats));
+       mutex_unlock(&ar->mutex);
+
+       return 0;
+}
+
+static int ar9170_get_tx_stats(struct ieee80211_hw *hw,
+                              struct ieee80211_tx_queue_stats *tx_stats)
+{
+       struct ar9170 *ar = hw->priv;
+
+       spin_lock_bh(&ar->tx_stats_lock);
+       memcpy(tx_stats, ar->tx_stats, sizeof(tx_stats[0]) * hw->queues);
+       spin_unlock_bh(&ar->tx_stats_lock);
+
+       return 0;
+}
+
+static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue,
+                         const struct ieee80211_tx_queue_params *param)
+{
+       struct ar9170 *ar = hw->priv;
+       int ret;
+
+       mutex_lock(&ar->mutex);
+       if ((param) && !(queue > ar->hw->queues)) {
+               memcpy(&ar->edcf[ar9170_qos_hwmap[queue]],
+                      param, sizeof(*param));
+
+               ret = ar9170_set_qos(ar);
+       } else
+               ret = -EINVAL;
+
+       mutex_unlock(&ar->mutex);
+       return ret;
+}
+
+static const struct ieee80211_ops ar9170_ops = {
+       .start                  = ar9170_op_start,
+       .stop                   = ar9170_op_stop,
+       .tx                     = ar9170_op_tx,
+       .add_interface          = ar9170_op_add_interface,
+       .remove_interface       = ar9170_op_remove_interface,
+       .config                 = ar9170_op_config,
+       .config_interface       = ar9170_op_config_interface,
+       .configure_filter       = ar9170_op_configure_filter,
+       .conf_tx                = ar9170_conf_tx,
+       .bss_info_changed       = ar9170_op_bss_info_changed,
+       .get_tsf                = ar9170_op_get_tsf,
+       .set_key                = ar9170_set_key,
+       .sta_notify             = ar9170_sta_notify,
+       .get_stats              = ar9170_get_stats,
+       .get_tx_stats           = ar9170_get_tx_stats,
+};
+
+void *ar9170_alloc(size_t priv_size)
+{
+       struct ieee80211_hw *hw;
+       struct ar9170 *ar;
+       int i;
+
+       hw = ieee80211_alloc_hw(priv_size, &ar9170_ops);
+       if (!hw)
+               return ERR_PTR(-ENOMEM);
+
+       ar = hw->priv;
+       ar->hw = hw;
+
+       mutex_init(&ar->mutex);
+       spin_lock_init(&ar->cmdlock);
+       spin_lock_init(&ar->tx_stats_lock);
+       skb_queue_head_init(&ar->global_tx_status);
+       skb_queue_head_init(&ar->global_tx_status_waste);
+       INIT_WORK(&ar->filter_config_work, ar9170_set_filters);
+       INIT_WORK(&ar->beacon_work, ar9170_new_beacon);
+       INIT_DELAYED_WORK(&ar->tx_status_janitor, ar9170_tx_status_janitor);
+
+       /* all hw supports 2.4 GHz, so set channel to 1 by default */
+       ar->channel = &ar9170_2ghz_chantable[0];
+
+       /* first part of wiphy init */
+       ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                        BIT(NL80211_IFTYPE_WDS) |
+                                        BIT(NL80211_IFTYPE_ADHOC);
+       ar->hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS |
+                        IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+                        IEEE80211_HW_SIGNAL_DBM |
+                        IEEE80211_HW_NOISE_DBM;
+
+       ar->hw->queues = __AR9170_NUM_TXQ;
+       ar->hw->extra_tx_headroom = 8;
+       ar->hw->sta_data_size = sizeof(struct ar9170_sta_info);
+
+       ar->hw->max_rates = 1;
+       ar->hw->max_rate_tries = 3;
+
+       for (i = 0; i < ARRAY_SIZE(ar->noise); i++)
+               ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */
+
+       return ar;
+}
+
+static int ar9170_read_eeprom(struct ar9170 *ar)
+{
+#define RW     8       /* number of words to read at once */
+#define RB     (sizeof(u32) * RW)
+       DECLARE_MAC_BUF(mbuf);
+       u8 *eeprom = (void *)&ar->eeprom;
+       u8 *addr = ar->eeprom.mac_address;
+       __le32 offsets[RW];
+       int i, j, err, bands = 0;
+
+       BUILD_BUG_ON(sizeof(ar->eeprom) & 3);
+
+       BUILD_BUG_ON(RB > AR9170_MAX_CMD_LEN - 4);
+#ifndef __CHECKER__
+       /* don't want to handle trailing remains */
+       BUILD_BUG_ON(sizeof(ar->eeprom) % RB);
+#endif
+
+       for (i = 0; i < sizeof(ar->eeprom)/RB; i++) {
+               for (j = 0; j < RW; j++)
+                       offsets[j] = cpu_to_le32(AR9170_EEPROM_START +
+                                                RB * i + 4 * j);
+
+               err = ar->exec_cmd(ar, AR9170_CMD_RREG,
+                                  RB, (u8 *) &offsets,
+                                  RB, eeprom + RB * i);
+               if (err)
+                       return err;
+       }
+
+#undef RW
+#undef RB
+
+       if (ar->eeprom.length == cpu_to_le16(0xFFFF))
+               return -ENODATA;
+
+       if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) {
+               ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &ar9170_band_2GHz;
+               bands++;
+       }
+       if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) {
+               ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &ar9170_band_5GHz;
+               bands++;
+       }
+       /*
+        * I measured this, a bandswitch takes roughly
+        * 135 ms and a frequency switch about 80.
+        *
+        * FIXME: measure these values again once EEPROM settings
+        *        are used, that will influence them!
+        */
+       if (bands == 2)
+               ar->hw->channel_change_time = 135 * 1000;
+       else
+               ar->hw->channel_change_time = 80 * 1000;
+
+       /* second part of wiphy init */
+       SET_IEEE80211_PERM_ADDR(ar->hw, addr);
+
+       return bands ? 0 : -EINVAL;
+}
+
+int ar9170_register(struct ar9170 *ar, struct device *pdev)
+{
+       int err;
+
+       /* try to read EEPROM, init MAC addr */
+       err = ar9170_read_eeprom(ar);
+       if (err)
+               goto err_out;
+
+       err = ieee80211_register_hw(ar->hw);
+       if (err)
+               goto err_out;
+
+       err = ar9170_init_leds(ar);
+       if (err)
+               goto err_unreg;
+
+#ifdef CONFIG_AR9170_LEDS
+       err = ar9170_register_leds(ar);
+       if (err)
+               goto err_unreg;
+#endif /* CONFIG_AR9170_LEDS */
+
+       dev_info(pdev, "Atheros AR9170 is registered as '%s'\n",
+                wiphy_name(ar->hw->wiphy));
+
+       return err;
+
+err_unreg:
+       ieee80211_unregister_hw(ar->hw);
+
+err_out:
+       return err;
+}
+
+void ar9170_unregister(struct ar9170 *ar)
+{
+#ifdef CONFIG_AR9170_LEDS
+       ar9170_unregister_leds(ar);
+#endif /* CONFIG_AR9170_LEDS */
+
+       ieee80211_unregister_hw(ar->hw);
+       mutex_destroy(&ar->mutex);
+}
diff --git a/drivers/net/wireless/ar9170/phy.c b/drivers/net/wireless/ar9170/phy.c
new file mode 100644 (file)
index 0000000..6ce2075
--- /dev/null
@@ -0,0 +1,1240 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * PHY and RF code
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+
+#include <linux/bitrev.h>
+#include "ar9170.h"
+#include "cmd.h"
+
+static int ar9170_init_power_cal(struct ar9170 *ar)
+{
+       ar9170_regwrite_begin(ar);
+
+       ar9170_regwrite(0x1bc000 + 0x993c, 0x7f);
+       ar9170_regwrite(0x1bc000 + 0x9934, 0x3f3f3f3f);
+       ar9170_regwrite(0x1bc000 + 0x9938, 0x3f3f3f3f);
+       ar9170_regwrite(0x1bc000 + 0xa234, 0x3f3f3f3f);
+       ar9170_regwrite(0x1bc000 + 0xa238, 0x3f3f3f3f);
+       ar9170_regwrite(0x1bc000 + 0xa38c, 0x3f3f3f3f);
+       ar9170_regwrite(0x1bc000 + 0xa390, 0x3f3f3f3f);
+       ar9170_regwrite(0x1bc000 + 0xa3cc, 0x3f3f3f3f);
+       ar9170_regwrite(0x1bc000 + 0xa3d0, 0x3f3f3f3f);
+       ar9170_regwrite(0x1bc000 + 0xa3d4, 0x3f3f3f3f);
+
+       ar9170_regwrite_finish();
+       return ar9170_regwrite_result();
+}
+
+struct ar9170_phy_init {
+       u32 reg, _5ghz_20, _5ghz_40, _2ghz_40, _2ghz_20;
+};
+
+static struct ar9170_phy_init ar5416_phy_init[] = {
+       { 0x1c5800, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+       { 0x1c5804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, },
+       { 0x1c5808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c580c, 0xad848e19, 0xad848e19, 0xad848e19, 0xad848e19, },
+       { 0x1c5810, 0x7d14e000, 0x7d14e000, 0x7d14e000, 0x7d14e000, },
+       { 0x1c5814, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, },
+       { 0x1c5818, 0x00000090, 0x00000090, 0x00000090, 0x00000090, },
+       { 0x1c581c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, },
+       { 0x1c5824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, },
+       { 0x1c5828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, },
+       { 0x1c582c, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, },
+       { 0x1c5830, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, },
+       { 0x1c5838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+       { 0x1c583c, 0x00200400, 0x00200400, 0x00200400, 0x00200400, },
+       { 0x1c5840, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e, },
+       { 0x1c5844, 0x1372161e, 0x13721c1e, 0x13721c24, 0x137216a4, },
+       { 0x1c5848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, },
+       { 0x1c584c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, },
+       { 0x1c5850, 0x6c48b4e4, 0x6c48b4e4, 0x6c48b0e4, 0x6c48b0e4, },
+       { 0x1c5854, 0x00000859, 0x00000859, 0x00000859, 0x00000859, },
+       { 0x1c5858, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, },
+       { 0x1c585c, 0x31395c5e, 0x31395c5e, 0x31395c5e, 0x31395c5e, },
+       { 0x1c5860, 0x0004dd10, 0x0004dd10, 0x0004dd20, 0x0004dd20, },
+       { 0x1c5868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, },
+       { 0x1c586c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, },
+       { 0x1c5900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5904, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5908, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c590c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, },
+       { 0x1c5918, 0x00000118, 0x00000230, 0x00000268, 0x00000134, },
+       { 0x1c591c, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, },
+       { 0x1c5920, 0x0510081c, 0x0510081c, 0x0510001c, 0x0510001c, },
+       { 0x1c5924, 0xd0058a15, 0xd0058a15, 0xd0058a15, 0xd0058a15, },
+       { 0x1c5928, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+       { 0x1c592c, 0x00000004, 0x00000004, 0x00000004, 0x00000004, },
+       { 0x1c5934, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c5938, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c593c, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, },
+       { 0x1c5944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, },
+       { 0x1c5948, 0x9280b212, 0x9280b212, 0x9280b212, 0x9280b212, },
+       { 0x1c594c, 0x00020028, 0x00020028, 0x00020028, 0x00020028, },
+       { 0x1c5954, 0x5d50e188, 0x5d50e188, 0x5d50e188, 0x5d50e188, },
+       { 0x1c5958, 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff, },
+       { 0x1c5960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+       { 0x1c5964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, },
+       { 0x1c5970, 0x190fb515, 0x190fb515, 0x190fb515, 0x190fb515, },
+       { 0x1c5974, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5978, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+       { 0x1c597c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5980, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5984, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5988, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c598c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5990, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5994, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5998, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c599c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59a4, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+       { 0x1c59a8, 0x001fff00, 0x001fff00, 0x001fff00, 0x001fff00, },
+       { 0x1c59ac, 0x006f00c4, 0x006f00c4, 0x006f00c4, 0x006f00c4, },
+       { 0x1c59b0, 0x03051000, 0x03051000, 0x03051000, 0x03051000, },
+       { 0x1c59b4, 0x00000820, 0x00000820, 0x00000820, 0x00000820, },
+       { 0x1c59c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, },
+       { 0x1c59c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, },
+       { 0x1c59c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, },
+       { 0x1c59cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, },
+       { 0x1c59d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, },
+       { 0x1c59d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59e0, 0x00000200, 0x00000200, 0x00000200, 0x00000200, },
+       { 0x1c59e4, 0x64646464, 0x64646464, 0x64646464, 0x64646464, },
+       { 0x1c59e8, 0x3c787878, 0x3c787878, 0x3c787878, 0x3c787878, },
+       { 0x1c59ec, 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa, },
+       { 0x1c59f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c59fc, 0x00001042, 0x00001042, 0x00001042, 0x00001042, },
+       { 0x1c5a00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5a04, 0x00000040, 0x00000040, 0x00000040, 0x00000040, },
+       { 0x1c5a08, 0x00000080, 0x00000080, 0x00000080, 0x00000080, },
+       { 0x1c5a0c, 0x000001a1, 0x000001a1, 0x00000141, 0x00000141, },
+       { 0x1c5a10, 0x000001e1, 0x000001e1, 0x00000181, 0x00000181, },
+       { 0x1c5a14, 0x00000021, 0x00000021, 0x000001c1, 0x000001c1, },
+       { 0x1c5a18, 0x00000061, 0x00000061, 0x00000001, 0x00000001, },
+       { 0x1c5a1c, 0x00000168, 0x00000168, 0x00000041, 0x00000041, },
+       { 0x1c5a20, 0x000001a8, 0x000001a8, 0x000001a8, 0x000001a8, },
+       { 0x1c5a24, 0x000001e8, 0x000001e8, 0x000001e8, 0x000001e8, },
+       { 0x1c5a28, 0x00000028, 0x00000028, 0x00000028, 0x00000028, },
+       { 0x1c5a2c, 0x00000068, 0x00000068, 0x00000068, 0x00000068, },
+       { 0x1c5a30, 0x00000189, 0x00000189, 0x000000a8, 0x000000a8, },
+       { 0x1c5a34, 0x000001c9, 0x000001c9, 0x00000169, 0x00000169, },
+       { 0x1c5a38, 0x00000009, 0x00000009, 0x000001a9, 0x000001a9, },
+       { 0x1c5a3c, 0x00000049, 0x00000049, 0x000001e9, 0x000001e9, },
+       { 0x1c5a40, 0x00000089, 0x00000089, 0x00000029, 0x00000029, },
+       { 0x1c5a44, 0x00000170, 0x00000170, 0x00000069, 0x00000069, },
+       { 0x1c5a48, 0x000001b0, 0x000001b0, 0x00000190, 0x00000190, },
+       { 0x1c5a4c, 0x000001f0, 0x000001f0, 0x000001d0, 0x000001d0, },
+       { 0x1c5a50, 0x00000030, 0x00000030, 0x00000010, 0x00000010, },
+       { 0x1c5a54, 0x00000070, 0x00000070, 0x00000050, 0x00000050, },
+       { 0x1c5a58, 0x00000191, 0x00000191, 0x00000090, 0x00000090, },
+       { 0x1c5a5c, 0x000001d1, 0x000001d1, 0x00000151, 0x00000151, },
+       { 0x1c5a60, 0x00000011, 0x00000011, 0x00000191, 0x00000191, },
+       { 0x1c5a64, 0x00000051, 0x00000051, 0x000001d1, 0x000001d1, },
+       { 0x1c5a68, 0x00000091, 0x00000091, 0x00000011, 0x00000011, },
+       { 0x1c5a6c, 0x000001b8, 0x000001b8, 0x00000051, 0x00000051, },
+       { 0x1c5a70, 0x000001f8, 0x000001f8, 0x00000198, 0x00000198, },
+       { 0x1c5a74, 0x00000038, 0x00000038, 0x000001d8, 0x000001d8, },
+       { 0x1c5a78, 0x00000078, 0x00000078, 0x00000018, 0x00000018, },
+       { 0x1c5a7c, 0x00000199, 0x00000199, 0x00000058, 0x00000058, },
+       { 0x1c5a80, 0x000001d9, 0x000001d9, 0x00000098, 0x00000098, },
+       { 0x1c5a84, 0x00000019, 0x00000019, 0x00000159, 0x00000159, },
+       { 0x1c5a88, 0x00000059, 0x00000059, 0x00000199, 0x00000199, },
+       { 0x1c5a8c, 0x00000099, 0x00000099, 0x000001d9, 0x000001d9, },
+       { 0x1c5a90, 0x000000d9, 0x000000d9, 0x00000019, 0x00000019, },
+       { 0x1c5a94, 0x000000f9, 0x000000f9, 0x00000059, 0x00000059, },
+       { 0x1c5a98, 0x000000f9, 0x000000f9, 0x00000099, 0x00000099, },
+       { 0x1c5a9c, 0x000000f9, 0x000000f9, 0x000000d9, 0x000000d9, },
+       { 0x1c5aa0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5aa4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5aa8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5aac, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ab0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ab4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ab8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5abc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ac0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ac4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ac8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5acc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ad0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ad4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ad8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5adc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ae0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ae4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5ae8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5aec, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5af0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5af4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5af8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5afc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+       { 0x1c5b00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5b04, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+       { 0x1c5b08, 0x00000002, 0x00000002, 0x00000002, 0x00000002, },
+       { 0x1c5b0c, 0x00000003, 0x00000003, 0x00000003, 0x00000003, },
+       { 0x1c5b10, 0x00000004, 0x00000004, 0x00000004, 0x00000004, },
+       { 0x1c5b14, 0x00000005, 0x00000005, 0x00000005, 0x00000005, },
+       { 0x1c5b18, 0x00000008, 0x00000008, 0x00000008, 0x00000008, },
+       { 0x1c5b1c, 0x00000009, 0x00000009, 0x00000009, 0x00000009, },
+       { 0x1c5b20, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000000a, },
+       { 0x1c5b24, 0x0000000b, 0x0000000b, 0x0000000b, 0x0000000b, },
+       { 0x1c5b28, 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c, },
+       { 0x1c5b2c, 0x0000000d, 0x0000000d, 0x0000000d, 0x0000000d, },
+       { 0x1c5b30, 0x00000010, 0x00000010, 0x00000010, 0x00000010, },
+       { 0x1c5b34, 0x00000011, 0x00000011, 0x00000011, 0x00000011, },
+       { 0x1c5b38, 0x00000012, 0x00000012, 0x00000012, 0x00000012, },
+       { 0x1c5b3c, 0x00000013, 0x00000013, 0x00000013, 0x00000013, },
+       { 0x1c5b40, 0x00000014, 0x00000014, 0x00000014, 0x00000014, },
+       { 0x1c5b44, 0x00000015, 0x00000015, 0x00000015, 0x00000015, },
+       { 0x1c5b48, 0x00000018, 0x00000018, 0x00000018, 0x00000018, },
+       { 0x1c5b4c, 0x00000019, 0x00000019, 0x00000019, 0x00000019, },
+       { 0x1c5b50, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, },
+       { 0x1c5b54, 0x0000001b, 0x0000001b, 0x0000001b, 0x0000001b, },
+       { 0x1c5b58, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, },
+       { 0x1c5b5c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, },
+       { 0x1c5b60, 0x00000020, 0x00000020, 0x00000020, 0x00000020, },
+       { 0x1c5b64, 0x00000021, 0x00000021, 0x00000021, 0x00000021, },
+       { 0x1c5b68, 0x00000022, 0x00000022, 0x00000022, 0x00000022, },
+       { 0x1c5b6c, 0x00000023, 0x00000023, 0x00000023, 0x00000023, },
+       { 0x1c5b70, 0x00000024, 0x00000024, 0x00000024, 0x00000024, },
+       { 0x1c5b74, 0x00000025, 0x00000025, 0x00000025, 0x00000025, },
+       { 0x1c5b78, 0x00000028, 0x00000028, 0x00000028, 0x00000028, },
+       { 0x1c5b7c, 0x00000029, 0x00000029, 0x00000029, 0x00000029, },
+       { 0x1c5b80, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a, },
+       { 0x1c5b84, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, },
+       { 0x1c5b88, 0x0000002c, 0x0000002c, 0x0000002c, 0x0000002c, },
+       { 0x1c5b8c, 0x0000002d, 0x0000002d, 0x0000002d, 0x0000002d, },
+       { 0x1c5b90, 0x00000030, 0x00000030, 0x00000030, 0x00000030, },
+       { 0x1c5b94, 0x00000031, 0x00000031, 0x00000031, 0x00000031, },
+       { 0x1c5b98, 0x00000032, 0x00000032, 0x00000032, 0x00000032, },
+       { 0x1c5b9c, 0x00000033, 0x00000033, 0x00000033, 0x00000033, },
+       { 0x1c5ba0, 0x00000034, 0x00000034, 0x00000034, 0x00000034, },
+       { 0x1c5ba4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5ba8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bac, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bb0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bb4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bb8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bbc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bc0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bc4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bc8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bcc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bd0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bd4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bd8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bdc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5be0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5be4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5be8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bec, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bf0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bf4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+       { 0x1c5bf8, 0x00000010, 0x00000010, 0x00000010, 0x00000010, },
+       { 0x1c5bfc, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, },
+       { 0x1c5c00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c14, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c18, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c1c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c28, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c2c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c30, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c34, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c38, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5c3c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5cf0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5cf4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5cf8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c5cfc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6200, 0x00000008, 0x00000008, 0x0000000e, 0x0000000e, },
+       { 0x1c6204, 0x00000440, 0x00000440, 0x00000440, 0x00000440, },
+       { 0x1c6208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, },
+       { 0x1c620c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+       { 0x1c6210, 0x40806333, 0x40806333, 0x40806333, 0x40806333, },
+       { 0x1c6214, 0x00106c10, 0x00106c10, 0x00106c10, 0x00106c10, },
+       { 0x1c6218, 0x009c4060, 0x009c4060, 0x009c4060, 0x009c4060, },
+       { 0x1c621c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, },
+       { 0x1c6220, 0x018830c6, 0x018830c6, 0x018830c6, 0x018830c6, },
+       { 0x1c6224, 0x00000400, 0x00000400, 0x00000400, 0x00000400, },
+       { 0x1c6228, 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5, },
+       { 0x1c622c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6230, 0x00000108, 0x00000210, 0x00000210, 0x00000108, },
+       { 0x1c6234, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c6238, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c623c, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, },
+       { 0x1c6240, 0x38490a20, 0x38490a20, 0x38490a20, 0x38490a20, },
+       { 0x1c6244, 0x00007bb6, 0x00007bb6, 0x00007bb6, 0x00007bb6, },
+       { 0x1c6248, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, },
+       { 0x1c624c, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+       { 0x1c6250, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, },
+       { 0x1c6254, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6258, 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380, },
+       { 0x1c625c, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, },
+       { 0x1c6260, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, },
+       { 0x1c6264, 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11, },
+       { 0x1c6268, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c626c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+       { 0x1c6274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, },
+       { 0x1c6278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+       { 0x1c627c, 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce, },
+       { 0x1c6300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, },
+       { 0x1c6304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, },
+       { 0x1c6308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, },
+       { 0x1c630c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, },
+       { 0x1c6310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, },
+       { 0x1c6314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, },
+       { 0x1c6318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, },
+       { 0x1c631c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, },
+       { 0x1c6320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, },
+       { 0x1c6324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, },
+       { 0x1c6328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, },
+       { 0x1c632c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6338, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c633c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6340, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6344, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c6348, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+       { 0x1c634c, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+       { 0x1c6350, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+       { 0x1c6354, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, },
+       { 0x1c6358, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, },
+       { 0x1c6388, 0x08000000, 0x08000000, 0x08000000, 0x08000000, },
+       { 0x1c638c, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c6390, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c6394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+       { 0x1c6398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, },
+       { 0x1c639c, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+       { 0x1c63a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63a4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63a8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63ac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63b0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63b4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63b8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63bc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63c0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63c4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63c8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63cc, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c63d0, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c63d4, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+       { 0x1c63d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+       { 0x1c63dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+       { 0x1c63e0, 0x000000c0, 0x000000c0, 0x000000c0, 0x000000c0, },
+       { 0x1c6848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, },
+       { 0x1c6920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, },
+       { 0x1c6960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+       { 0x1c720c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+       { 0x1c726c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+       { 0x1c7848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, },
+       { 0x1c7920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, },
+       { 0x1c7960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+       { 0x1c820c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+       { 0x1c826c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+/*     { 0x1c8864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, }, */
+       { 0x1c8864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, },
+       { 0x1c895c, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, },
+       { 0x1c8968, 0x000003ce, 0x000003ce, 0x000003ce, 0x000003ce, },
+       { 0x1c89bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, },
+       { 0x1c9270, 0x00820820, 0x00820820, 0x00820820, 0x00820820, },
+       { 0x1c935c, 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f, },
+       { 0x1c9360, 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207, },
+       { 0x1c9364, 0x17601685, 0x17601685, 0x17601685, 0x17601685, },
+       { 0x1c9368, 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104, },
+       { 0x1c936c, 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03, },
+       { 0x1c9370, 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883, },
+       { 0x1c9374, 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803, },
+       { 0x1c9378, 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682, },
+       { 0x1c937c, 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482, },
+       { 0x1c9380, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, },
+       { 0x1c9384, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, }
+};
+
+int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band)
+{
+       int i, err;
+       u32 val;
+       bool is_2ghz = band == IEEE80211_BAND_2GHZ;
+       bool is_40mhz = false; /* XXX: for now */
+
+       ar9170_regwrite_begin(ar);
+
+       for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) {
+               if (is_40mhz) {
+                       if (is_2ghz)
+                               val = ar5416_phy_init[i]._2ghz_40;
+                       else
+                               val = ar5416_phy_init[i]._5ghz_40;
+               } else {
+                       if (is_2ghz)
+                               val = ar5416_phy_init[i]._2ghz_20;
+                       else
+                               val = ar5416_phy_init[i]._5ghz_20;
+               }
+
+               ar9170_regwrite(ar5416_phy_init[i].reg, val);
+       }
+
+       ar9170_regwrite_finish();
+       err = ar9170_regwrite_result();
+       if (err)
+               return err;
+
+       /* XXX: use EEPROM data here! */
+
+       err = ar9170_init_power_cal(ar);
+       if (err)
+               return err;
+
+       /* XXX: remove magic! */
+       if (is_2ghz)
+               err = ar9170_write_reg(ar, 0x1d4014, 0x5163);
+       else
+               err = ar9170_write_reg(ar, 0x1d4014, 0x5143);
+
+       return err;
+}
+
+struct ar9170_rf_init {
+       u32 reg, _5ghz, _2ghz;
+};
+
+static struct ar9170_rf_init ar9170_rf_init[] = {
+     /* bank 0 */
+     { 0x1c58b0,  0x1e5795e5,  0x1e5795e5},
+     { 0x1c58e0,  0x02008020,  0x02008020},
+     /* bank 1 */
+     { 0x1c58b0,  0x02108421,  0x02108421},
+     { 0x1c58ec,  0x00000008,  0x00000008},
+     /* bank 2 */
+     { 0x1c58b0,  0x0e73ff17,  0x0e73ff17},
+     { 0x1c58e0,  0x00000420,  0x00000420},
+     /* bank 3 */
+     { 0x1c58f0,  0x01400018,  0x01c00018},
+     /* bank 4 */
+     { 0x1c58b0,  0x000001a1,  0x000001a1},
+     { 0x1c58e8,  0x00000001,  0x00000001},
+     /* bank 5 */
+     { 0x1c58b0,  0x00000013,  0x00000013},
+     { 0x1c58e4,  0x00000002,  0x00000002},
+     /* bank 6 */
+     { 0x1c58b0,  0x00000000,  0x00000000},
+     { 0x1c58b0,  0x00000000,  0x00000000},
+     { 0x1c58b0,  0x00000000,  0x00000000},
+     { 0x1c58b0,  0x00000000,  0x00000000},
+     { 0x1c58b0,  0x00000000,  0x00000000},
+     { 0x1c58b0,  0x00004000,  0x00004000},
+     { 0x1c58b0,  0x00006c00,  0x00006c00},
+     { 0x1c58b0,  0x00002c00,  0x00002c00},
+     { 0x1c58b0,  0x00004800,  0x00004800},
+     { 0x1c58b0,  0x00004000,  0x00004000},
+     { 0x1c58b0,  0x00006000,  0x00006000},
+     { 0x1c58b0,  0x00001000,  0x00001000},
+     { 0x1c58b0,  0x00004000,  0x00004000},
+     { 0x1c58b0,  0x00007c00,  0x00007c00},
+     { 0x1c58b0,  0x00007c00,  0x00007c00},
+     { 0x1c58b0,  0x00007c00,  0x00007c00},
+     { 0x1c58b0,  0x00007c00,  0x00007c00},
+     { 0x1c58b0,  0x00007c00,  0x00007c00},
+     { 0x1c58b0,  0x00087c00,  0x00087c00},
+     { 0x1c58b0,  0x00007c00,  0x00007c00},
+     { 0x1c58b0,  0x00005400,  0x00005400},
+     { 0x1c58b0,  0x00000c00,  0x00000c00},
+     { 0x1c58b0,  0x00001800,  0x00001800},
+     { 0x1c58b0,  0x00007c00,  0x00007c00},
+     { 0x1c58b0,  0x00006c00,  0x00006c00},
+     { 0x1c58b0,  0x00006c00,  0x00006c00},
+     { 0x1c58b0,  0x00007c00,  0x00007c00},
+     { 0x1c58b0,  0x00002c00,  0x00002c00},
+     { 0x1c58b0,  0x00003c00,  0x00003c00},
+     { 0x1c58b0,  0x00003800,  0x00003800},
+     { 0x1c58b0,  0x00001c00,  0x00001c00},
+     { 0x1c58b0,  0x00000800,  0x00000800},
+     { 0x1c58b0,  0x00000408,  0x00000408},
+     { 0x1c58b0,  0x00004c15,  0x00004c15},
+     { 0x1c58b0,  0x00004188,  0x00004188},
+     { 0x1c58b0,  0x0000201e,  0x0000201e},
+     { 0x1c58b0,  0x00010408,  0x00010408},
+     { 0x1c58b0,  0x00000801,  0x00000801},
+     { 0x1c58b0,  0x00000c08,  0x00000c08},
+     { 0x1c58b0,  0x0000181e,  0x0000181e},
+     { 0x1c58b0,  0x00001016,  0x00001016},
+     { 0x1c58b0,  0x00002800,  0x00002800},
+     { 0x1c58b0,  0x00004010,  0x00004010},
+     { 0x1c58b0,  0x0000081c,  0x0000081c},
+     { 0x1c58b0,  0x00000115,  0x00000115},
+     { 0x1c58b0,  0x00000015,  0x00000015},
+     { 0x1c58b0,  0x00000066,  0x00000066},
+     { 0x1c58b0,  0x0000001c,  0x0000001c},
+     { 0x1c58b0,  0x00000000,  0x00000000},
+     { 0x1c58b0,  0x00000004,  0x00000004},
+     { 0x1c58b0,  0x00000015,  0x00000015},
+     { 0x1c58b0,  0x0000001f,  0x0000001f},
+     { 0x1c58e0,  0x00000000,  0x00000400},
+     /* bank 7 */
+     { 0x1c58b0,  0x000000a0,  0x000000a0},
+     { 0x1c58b0,  0x00000000,  0x00000000},
+     { 0x1c58b0,  0x00000040,  0x00000040},
+     { 0x1c58f0,  0x0000001c,  0x0000001c},
+};
+
+static int ar9170_init_rf_banks_0_7(struct ar9170 *ar, bool band5ghz)
+{
+       int err, i;
+
+       ar9170_regwrite_begin(ar);
+
+       for (i = 0; i < ARRAY_SIZE(ar9170_rf_init); i++)
+               ar9170_regwrite(ar9170_rf_init[i].reg,
+                               band5ghz ? ar9170_rf_init[i]._5ghz
+                                        : ar9170_rf_init[i]._2ghz);
+
+       ar9170_regwrite_finish();
+       err = ar9170_regwrite_result();
+       if (err)
+               printk(KERN_ERR "%s: rf init failed\n",
+                      wiphy_name(ar->hw->wiphy));
+       return err;
+}
+
+static int ar9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz,
+                                   u32 freq, enum ar9170_bw bw)
+{
+       int err;
+       u32 d0, d1, td0, td1, fd0, fd1;
+       u8 chansel;
+       u8 refsel0 = 1, refsel1 = 0;
+       u8 lf_synth = 0;
+
+       switch (bw) {
+       case AR9170_BW_40_ABOVE:
+               freq += 10;
+               break;
+       case AR9170_BW_40_BELOW:
+               freq -= 10;
+               break;
+       case AR9170_BW_20:
+               break;
+       case __AR9170_NUM_BW:
+               BUG();
+       }
+
+       if (band5ghz) {
+               if (freq % 10) {
+                       chansel = (freq - 4800) / 5;
+               } else {
+                       chansel = ((freq - 4800) / 10) * 2;
+                       refsel0 = 0;
+                       refsel1 = 1;
+               }
+               chansel = byte_rev_table[chansel];
+       } else {
+               if (freq == 2484) {
+                       chansel = 10 + (freq - 2274) / 5;
+                       lf_synth = 1;
+               } else
+                       chansel = 16 + (freq - 2272) / 5;
+               chansel *= 4;
+               chansel = byte_rev_table[chansel];
+       }
+
+       d1 =    chansel;
+       d0 =    0x21 |
+               refsel0 << 3 |
+               refsel1 << 2 |
+               lf_synth << 1;
+       td0 =   d0 & 0x1f;
+       td1 =   d1 & 0x1f;
+       fd0 =   td1 << 5 | td0;
+
+       td0 =   (d0 >> 5) & 0x7;
+       td1 =   (d1 >> 5) & 0x7;
+       fd1 =   td1 << 5 | td0;
+
+       ar9170_regwrite_begin(ar);
+
+       ar9170_regwrite(0x1c58b0, fd0);
+       ar9170_regwrite(0x1c58e8, fd1);
+
+       ar9170_regwrite_finish();
+       err = ar9170_regwrite_result();
+       if (err)
+               return err;
+
+       msleep(10);
+
+       return 0;
+}
+
+struct ar9170_phy_freq_params {
+       u8 coeff_exp;
+       u16 coeff_man;
+       u8 coeff_exp_shgi;
+       u16 coeff_man_shgi;
+};
+
+struct ar9170_phy_freq_entry {
+       u16 freq;
+       struct ar9170_phy_freq_params params[__AR9170_NUM_BW];
+};
+
+/* NB: must be in sync with channel tables in main! */
+static const struct ar9170_phy_freq_entry ar9170_phy_freq_params[] = {
+/*
+ *     freq,
+ *             20MHz,
+ *             40MHz (below),
+ *             40Mhz (above),
+ */
+       { 2412, {
+               { 3, 21737, 3, 19563, },
+               { 3, 21827, 3, 19644, },
+               { 3, 21647, 3, 19482, },
+       } },
+       { 2417, {
+               { 3, 21692, 3, 19523, },
+               { 3, 21782, 3, 19604, },
+               { 3, 21602, 3, 19442, },
+       } },
+       { 2422, {
+               { 3, 21647, 3, 19482, },
+               { 3, 21737, 3, 19563, },
+               { 3, 21558, 3, 19402, },
+       } },
+       { 2427, {
+               { 3, 21602, 3, 19442, },
+               { 3, 21692, 3, 19523, },
+               { 3, 21514, 3, 19362, },
+       } },
+       { 2432, {
+               { 3, 21558, 3, 19402, },
+               { 3, 21647, 3, 19482, },
+               { 3, 21470, 3, 19323, },
+       } },
+       { 2437, {
+               { 3, 21514, 3, 19362, },
+               { 3, 21602, 3, 19442, },
+               { 3, 21426, 3, 19283, },
+       } },
+       { 2442, {
+               { 3, 21470, 3, 19323, },
+               { 3, 21558, 3, 19402, },
+               { 3, 21382, 3, 19244, },
+       } },
+       { 2447, {
+               { 3, 21426, 3, 19283, },
+               { 3, 21514, 3, 19362, },
+               { 3, 21339, 3, 19205, },
+       } },
+       { 2452, {
+               { 3, 21382, 3, 19244, },
+               { 3, 21470, 3, 19323, },
+               { 3, 21295, 3, 19166, },
+       } },
+       { 2457, {
+               { 3, 21339, 3, 19205, },
+               { 3, 21426, 3, 19283, },
+               { 3, 21252, 3, 19127, },
+       } },
+       { 2462, {
+               { 3, 21295, 3, 19166, },
+               { 3, 21382, 3, 19244, },
+               { 3, 21209, 3, 19088, },
+       } },
+       { 2467, {
+               { 3, 21252, 3, 19127, },
+               { 3, 21339, 3, 19205, },
+               { 3, 21166, 3, 19050, },
+       } },
+       { 2472, {
+               { 3, 21209, 3, 19088, },
+               { 3, 21295, 3, 19166, },
+               { 3, 21124, 3, 19011, },
+       } },
+       { 2484, {
+               { 3, 21107, 3, 18996, },
+               { 3, 21192, 3, 19073, },
+               { 3, 21022, 3, 18920, },
+       } },
+       { 4920, {
+               { 4, 21313, 4, 19181, },
+               { 4, 21356, 4, 19220, },
+               { 4, 21269, 4, 19142, },
+       } },
+       { 4940, {
+               { 4, 21226, 4, 19104, },
+               { 4, 21269, 4, 19142, },
+               { 4, 21183, 4, 19065, },
+       } },
+       { 4960, {
+               { 4, 21141, 4, 19027, },
+               { 4, 21183, 4, 19065, },
+               { 4, 21098, 4, 18988, },
+       } },
+       { 4980, {
+               { 4, 21056, 4, 18950, },
+               { 4, 21098, 4, 18988, },
+               { 4, 21014, 4, 18912, },
+       } },
+       { 5040, {
+               { 4, 20805, 4, 18725, },
+               { 4, 20846, 4, 18762, },
+               { 4, 20764, 4, 18687, },
+       } },
+       { 5060, {
+               { 4, 20723, 4, 18651, },
+               { 4, 20764, 4, 18687, },
+               { 4, 20682, 4, 18614, },
+       } },
+       { 5080, {
+               { 4, 20641, 4, 18577, },
+               { 4, 20682, 4, 18614, },
+               { 4, 20601, 4, 18541, },
+       } },
+       { 5180, {
+               { 4, 20243, 4, 18219, },
+               { 4, 20282, 4, 18254, },
+               { 4, 20204, 4, 18183, },
+       } },
+       { 5200, {
+               { 4, 20165, 4, 18148, },
+               { 4, 20204, 4, 18183, },
+               { 4, 20126, 4, 18114, },
+       } },
+       { 5220, {
+               { 4, 20088, 4, 18079, },
+               { 4, 20126, 4, 18114, },
+               { 4, 20049, 4, 18044, },
+       } },
+       { 5240, {
+               { 4, 20011, 4, 18010, },
+               { 4, 20049, 4, 18044, },
+               { 4, 19973, 4, 17976, },
+       } },
+       { 5260, {
+               { 4, 19935, 4, 17941, },
+               { 4, 19973, 4, 17976, },
+               { 4, 19897, 4, 17907, },
+       } },
+       { 5280, {
+               { 4, 19859, 4, 17873, },
+               { 4, 19897, 4, 17907, },
+               { 4, 19822, 4, 17840, },
+       } },
+       { 5300, {
+               { 4, 19784, 4, 17806, },
+               { 4, 19822, 4, 17840, },
+               { 4, 19747, 4, 17772, },
+       } },
+       { 5320, {
+               { 4, 19710, 4, 17739, },
+               { 4, 19747, 4, 17772, },
+               { 4, 19673, 4, 17706, },
+       } },
+       { 5500, {
+               { 4, 19065, 4, 17159, },
+               { 4, 19100, 4, 17190, },
+               { 4, 19030, 4, 17127, },
+       } },
+       { 5520, {
+               { 4, 18996, 4, 17096, },
+               { 4, 19030, 4, 17127, },
+               { 4, 18962, 4, 17065, },
+       } },
+       { 5540, {
+               { 4, 18927, 4, 17035, },
+               { 4, 18962, 4, 17065, },
+               { 4, 18893, 4, 17004, },
+       } },
+       { 5560, {
+               { 4, 18859, 4, 16973, },
+               { 4, 18893, 4, 17004, },
+               { 4, 18825, 4, 16943, },
+       } },
+       { 5580, {
+               { 4, 18792, 4, 16913, },
+               { 4, 18825, 4, 16943, },
+               { 4, 18758, 4, 16882, },
+       } },
+       { 5600, {
+               { 4, 18725, 4, 16852, },
+               { 4, 18758, 4, 16882, },
+               { 4, 18691, 4, 16822, },
+       } },
+       { 5620, {
+               { 4, 18658, 4, 16792, },
+               { 4, 18691, 4, 16822, },
+               { 4, 18625, 4, 16762, },
+       } },
+       { 5640, {
+               { 4, 18592, 4, 16733, },
+               { 4, 18625, 4, 16762, },
+               { 4, 18559, 4, 16703, },
+       } },
+       { 5660, {
+               { 4, 18526, 4, 16673, },
+               { 4, 18559, 4, 16703, },
+               { 4, 18493, 4, 16644, },
+       } },
+       { 5680, {
+               { 4, 18461, 4, 16615, },
+               { 4, 18493, 4, 16644, },
+               { 4, 18428, 4, 16586, },
+       } },
+       { 5700, {
+               { 4, 18396, 4, 16556, },
+               { 4, 18428, 4, 16586, },
+               { 4, 18364, 4, 16527, },
+       } },
+       { 5745, {
+               { 4, 18252, 4, 16427, },
+               { 4, 18284, 4, 16455, },
+               { 4, 18220, 4, 16398, },
+       } },
+       { 5765, {
+               { 4, 18189, 5, 32740, },
+               { 4, 18220, 4, 16398, },
+               { 4, 18157, 5, 32683, },
+       } },
+       { 5785, {
+               { 4, 18126, 5, 32626, },
+               { 4, 18157, 5, 32683, },
+               { 4, 18094, 5, 32570, },
+       } },
+       { 5805, {
+               { 4, 18063, 5, 32514, },
+               { 4, 18094, 5, 32570, },
+               { 4, 18032, 5, 32458, },
+       } },
+       { 5825, {
+               { 4, 18001, 5, 32402, },
+               { 4, 18032, 5, 32458, },
+               { 4, 17970, 5, 32347, },
+       } },
+       { 5170, {
+               { 4, 20282, 4, 18254, },
+               { 4, 20321, 4, 18289, },
+               { 4, 20243, 4, 18219, },
+       } },
+       { 5190, {
+               { 4, 20204, 4, 18183, },
+               { 4, 20243, 4, 18219, },
+               { 4, 20165, 4, 18148, },
+       } },
+       { 5210, {
+               { 4, 20126, 4, 18114, },
+               { 4, 20165, 4, 18148, },
+               { 4, 20088, 4, 18079, },
+       } },
+       { 5230, {
+               { 4, 20049, 4, 18044, },
+               { 4, 20088, 4, 18079, },
+               { 4, 20011, 4, 18010, },
+       } },
+};
+
+static const struct ar9170_phy_freq_params *
+ar9170_get_hw_dyn_params(struct ieee80211_channel *channel,
+                        enum ar9170_bw bw)
+{
+       unsigned int chanidx = 0;
+       u16 freq = 2412;
+
+       if (channel) {
+               chanidx = channel->hw_value;
+               freq = channel->center_freq;
+       }
+
+       BUG_ON(chanidx >= ARRAY_SIZE(ar9170_phy_freq_params));
+
+       BUILD_BUG_ON(__AR9170_NUM_BW != 3);
+
+       WARN_ON(ar9170_phy_freq_params[chanidx].freq != freq);
+
+       return &ar9170_phy_freq_params[chanidx].params[bw];
+}
+
+
+int ar9170_init_rf(struct ar9170 *ar)
+{
+       const struct ar9170_phy_freq_params *freqpar;
+       __le32 cmd[7];
+       int err;
+
+       err = ar9170_init_rf_banks_0_7(ar, false);
+       if (err)
+               return err;
+
+       err = ar9170_init_rf_bank4_pwr(ar, false, 2412, AR9170_BW_20);
+       if (err)
+               return err;
+
+       freqpar = ar9170_get_hw_dyn_params(NULL, AR9170_BW_20);
+
+       cmd[0] = cpu_to_le32(2412 * 1000);
+       cmd[1] = cpu_to_le32(0);
+       cmd[2] = cpu_to_le32(1);
+       cmd[3] = cpu_to_le32(freqpar->coeff_exp);
+       cmd[4] = cpu_to_le32(freqpar->coeff_man);
+       cmd[5] = cpu_to_le32(freqpar->coeff_exp_shgi);
+       cmd[6] = cpu_to_le32(freqpar->coeff_man_shgi);
+
+       /* RF_INIT echoes the command back to us */
+       err = ar->exec_cmd(ar, AR9170_CMD_RF_INIT,
+                          sizeof(cmd), (u8 *)cmd,
+                          sizeof(cmd), (u8 *)cmd);
+       if (err)
+               return err;
+
+       msleep(1000);
+
+       return ar9170_echo_test(ar, 0xaabbccdd);
+}
+
+static int ar9170_find_freq_idx(int nfreqs, u8 *freqs, u8 f)
+{
+       int idx = nfreqs - 2;
+
+       while (idx >= 0) {
+               if (f >= freqs[idx])
+                       return idx;
+               idx--;
+       }
+
+       return 0;
+}
+
+static s32 ar9170_interpolate_s32(s32 x, s32 x1, s32 y1, s32 x2, s32 y2)
+{
+       /* nothing to interpolate, it's horizontal */
+       if (y2 == y1)
+               return y1;
+
+       /* check if we hit one of the edges */
+       if (x == x1)
+               return y1;
+       if (x == x2)
+               return y2;
+
+       /* x1 == x2 is bad, hopefully == x */
+       if (x2 == x1)
+               return y1;
+
+       return y1 + (((y2 - y1) * (x - x1)) / (x2 - x1));
+}
+
+static u8 ar9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2)
+{
+#define SHIFT          8
+       s32 y;
+
+       y = ar9170_interpolate_s32(x << SHIFT,
+                                  x1 << SHIFT, y1 << SHIFT,
+                                  x2 << SHIFT, y2 << SHIFT);
+
+       /*
+        * XXX: unwrap this expression
+        *      Isn't it just DIV_ROUND_UP(y, 1<<SHIFT)?
+        *      Can we rely on the compiler to optimise away the div?
+        */
+       return (y >> SHIFT) + ((y & (1<<(SHIFT-1))) >> (SHIFT - 1));
+#undef SHIFT
+}
+
+static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
+{
+       struct ar9170_calibration_target_power_legacy *ctpl;
+       struct ar9170_calibration_target_power_ht *ctph;
+       u8 *ctpres;
+       int ntargets;
+       int idx, i, n;
+       u8 ackpower, ackchains, f;
+       u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS];
+
+       if (freq < 3000)
+               f = freq - 2300;
+       else
+               f = (freq - 4800)/5;
+
+       /*
+        * cycle through the various modes
+        *
+        * legacy modes first: 5G, 2G CCK, 2G OFDM
+        */
+       for (i = 0; i < 3; i++) {
+               switch (i) {
+               case 0: /* 5 GHz legacy */
+                       ctpl = &ar->eeprom.cal_tgt_pwr_5G[0];
+                       ntargets = AR5416_NUM_5G_TARGET_PWRS;
+                       ctpres = ar->power_5G_leg;
+                       break;
+               case 1: /* 2.4 GHz CCK */
+                       ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0];
+                       ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS;
+                       ctpres = ar->power_2G_cck;
+                       break;
+               case 2: /* 2.4 GHz OFDM */
+                       ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0];
+                       ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+                       ctpres = ar->power_2G_ofdm;
+                       break;
+               default:
+                       BUG();
+               }
+
+               for (n = 0; n < ntargets; n++) {
+                       if (ctpl[n].freq == 0xff)
+                               break;
+                       pwr_freqs[n] = ctpl[n].freq;
+               }
+               ntargets = n;
+               idx = ar9170_find_freq_idx(ntargets, pwr_freqs, f);
+               for (n = 0; n < 4; n++)
+                       ctpres[n] = ar9170_interpolate_u8(
+                                       f,
+                                       ctpl[idx + 0].freq,
+                                       ctpl[idx + 0].power[n],
+                                       ctpl[idx + 1].freq,
+                                       ctpl[idx + 1].power[n]);
+       }
+
+       /*
+        * HT modes now: 5G HT20, 5G HT40, 2G CCK, 2G OFDM, 2G HT20, 2G HT40
+        */
+       for (i = 0; i < 4; i++) {
+               switch (i) {
+               case 0: /* 5 GHz HT 20 */
+                       ctph = &ar->eeprom.cal_tgt_pwr_5G_ht20[0];
+                       ntargets = AR5416_NUM_5G_TARGET_PWRS;
+                       ctpres = ar->power_5G_ht20;
+                       break;
+               case 1: /* 5 GHz HT 40 */
+                       ctph = &ar->eeprom.cal_tgt_pwr_5G_ht40[0];
+                       ntargets = AR5416_NUM_5G_TARGET_PWRS;
+                       ctpres = ar->power_5G_ht40;
+                       break;
+               case 2: /* 2.4 GHz HT 20 */
+                       ctph = &ar->eeprom.cal_tgt_pwr_2G_ht20[0];
+                       ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+                       ctpres = ar->power_2G_ht20;
+                       break;
+               case 3: /* 2.4 GHz HT 40 */
+                       ctph = &ar->eeprom.cal_tgt_pwr_2G_ht40[0];
+                       ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+                       ctpres = ar->power_2G_ht40;
+                       break;
+               default:
+                       BUG();
+               }
+
+               for (n = 0; n < ntargets; n++) {
+                       if (ctph[n].freq == 0xff)
+                               break;
+                       pwr_freqs[n] = ctph[n].freq;
+               }
+               ntargets = n;
+               idx = ar9170_find_freq_idx(ntargets, pwr_freqs, f);
+               for (n = 0; n < 8; n++)
+                       ctpres[n] = ar9170_interpolate_u8(
+                                       f,
+                                       ctph[idx + 0].freq,
+                                       ctph[idx + 0].power[n],
+                                       ctph[idx + 1].freq,
+                                       ctph[idx + 1].power[n]);
+       }
+
+       /* set ACK/CTS TX power */
+       ar9170_regwrite_begin(ar);
+
+       if (ar->eeprom.tx_mask != 1)
+               ackchains = AR9170_TX_PHY_TXCHAIN_2;
+       else
+               ackchains = AR9170_TX_PHY_TXCHAIN_1;
+
+       if (freq < 3000)
+               ackpower = ar->power_2G_ofdm[0] & 0x3f;
+       else
+               ackpower = ar->power_5G_leg[0] & 0x3f;
+
+       ar9170_regwrite(0x1c3694, ackpower << 20 | ackchains << 26);
+       ar9170_regwrite(0x1c3bb4, ackpower << 5 | ackchains << 11 |
+                                 ackpower << 21 | ackchains << 27);
+
+       ar9170_regwrite_finish();
+       return ar9170_regwrite_result();
+}
+
+static int ar9170_calc_noise_dbm(u32 raw_noise)
+{
+       if (raw_noise & 0x100)
+               return ~((raw_noise & 0x0ff) >> 1);
+       else
+               return (raw_noise & 0xff) >> 1;
+}
+
+int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
+                      enum ar9170_rf_init_mode rfi, enum ar9170_bw bw)
+{
+       const struct ar9170_phy_freq_params *freqpar;
+       u32 cmd, tmp, offs;
+       __le32 vals[8];
+       int i, err;
+       bool bandswitch;
+
+       /* clear BB heavy clip enable */
+       err = ar9170_write_reg(ar, 0x1c59e0, 0x200);
+       if (err)
+               return err;
+
+       /* may be NULL at first setup */
+       if (ar->channel)
+               bandswitch = ar->channel->band != channel->band;
+       else
+               bandswitch = true;
+
+       /* HW workaround */
+       if (!ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] &&
+           channel->center_freq <= 2417)
+               bandswitch = true;
+
+       err = ar->exec_cmd(ar, AR9170_CMD_FREQ_START, 0, NULL, 0, NULL);
+       if (err)
+               return err;
+
+       if (rfi != AR9170_RFI_NONE || bandswitch) {
+               u32 val = 0x400;
+
+               if (rfi == AR9170_RFI_COLD)
+                       val = 0x800;
+
+               /* warm/cold reset BB/ADDA */
+               err = ar9170_write_reg(ar, 0x1d4004, val);
+               if (err)
+                       return err;
+
+               err = ar9170_write_reg(ar, 0x1d4004, 0x0);
+               if (err)
+                       return err;
+
+               err = ar9170_init_phy(ar, channel->band);
+               if (err)
+                       return err;
+
+               err = ar9170_init_rf_banks_0_7(ar,
+                       channel->band == IEEE80211_BAND_5GHZ);
+               if (err)
+                       return err;
+
+               cmd = AR9170_CMD_RF_INIT;
+       } else {
+               cmd = AR9170_CMD_FREQUENCY;
+       }
+
+       err = ar9170_init_rf_bank4_pwr(ar,
+               channel->band == IEEE80211_BAND_5GHZ,
+               channel->center_freq, bw);
+       if (err)
+               return err;
+
+       switch (bw) {
+       case AR9170_BW_20:
+               tmp = 0x240;
+               offs = 0;
+               break;
+       case AR9170_BW_40_BELOW:
+               tmp = 0x2c4;
+               offs = 3;
+               break;
+       case AR9170_BW_40_ABOVE:
+               tmp = 0x2d4;
+               offs = 1;
+               break;
+       default:
+               BUG();
+               return -ENOSYS;
+       }
+
+       if (0 /* 2 streams capable */)
+               tmp |= 0x100;
+
+       err = ar9170_write_reg(ar, 0x1c5804, tmp);
+       if (err)
+               return err;
+
+       err = ar9170_set_power_cal(ar, channel->center_freq, bw);
+       if (err)
+               return err;
+
+       freqpar = ar9170_get_hw_dyn_params(channel, bw);
+
+       vals[0] = cpu_to_le32(channel->center_freq * 1000);
+       vals[1] = cpu_to_le32(bw == AR9170_BW_20 ? 0 : 1);
+       vals[2] = cpu_to_le32(offs << 2 | 1);
+       vals[3] = cpu_to_le32(freqpar->coeff_exp);
+       vals[4] = cpu_to_le32(freqpar->coeff_man);
+       vals[5] = cpu_to_le32(freqpar->coeff_exp_shgi);
+       vals[6] = cpu_to_le32(freqpar->coeff_man_shgi);
+       vals[7] = cpu_to_le32(1000);
+
+       err = ar->exec_cmd(ar, cmd, sizeof(vals), (u8 *)vals,
+                          sizeof(vals), (u8 *)vals);
+       if (err)
+               return err;
+
+       for (i = 0; i < 2; i++) {
+               ar->noise[i] = ar9170_calc_noise_dbm(
+                               (le32_to_cpu(vals[2 + i]) >> 19) & 0x1ff);
+
+               ar->noise[i + 2] = ar9170_calc_noise_dbm(
+                                   (le32_to_cpu(vals[5 + i]) >> 23) & 0x1ff);
+       }
+
+       ar->channel = channel;
+       return 0;
+}
diff --git a/drivers/net/wireless/ar9170/usb.c b/drivers/net/wireless/ar9170/usb.c
new file mode 100644 (file)
index 0000000..ad29684
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * USB - frontend
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, Christian Lamparter <chunkeey@web.de>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include "ar9170.h"
+#include "cmd.h"
+#include "hw.h"
+#include "usb.h"
+
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless");
+MODULE_FIRMWARE("ar9170-1.fw");
+MODULE_FIRMWARE("ar9170-2.fw");
+
+static struct usb_device_id ar9170_usb_ids[] = {
+       /* Atheros 9170 */
+       { USB_DEVICE(0x0cf3, 0x9170) },
+       /* Atheros TG121N */
+       { USB_DEVICE(0x0cf3, 0x1001) },
+       /* D-Link DWA 160A */
+       { USB_DEVICE(0x07d1, 0x3c10) },
+       /* Netgear WNDA3100 */
+       { USB_DEVICE(0x0846, 0x9010) },
+       /* Netgear WN111 v2 */
+       { USB_DEVICE(0x0846, 0x9001) },
+       /* Zydas ZD1221 */
+       { USB_DEVICE(0x0ace, 0x1221) },
+       /* Z-Com UB81 BG */
+       { USB_DEVICE(0x0cde, 0x0023) },
+       /* Z-Com UB82 ABG */
+       { USB_DEVICE(0x0cde, 0x0026) },
+       /* Arcadyan WN7512 */
+       { USB_DEVICE(0x083a, 0xf522) },
+       /* Planex GWUS300 */
+       { USB_DEVICE(0x2019, 0x5304) },
+       /* IO-Data WNGDNUS2 */
+       { USB_DEVICE(0x04bb, 0x093f) },
+
+       /* terminate */
+       {}
+};
+MODULE_DEVICE_TABLE(usb, ar9170_usb_ids);
+
+static void ar9170_usb_tx_urb_complete_free(struct urb *urb)
+{
+       struct sk_buff *skb = urb->context;
+       struct ar9170_usb *aru = (struct ar9170_usb *)
+             usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+
+       if (!aru) {
+               dev_kfree_skb_irq(skb);
+               return ;
+       }
+
+       ar9170_handle_tx_status(&aru->common, skb, false,
+                               AR9170_TX_STATUS_COMPLETE);
+}
+
+static void ar9170_usb_tx_urb_complete(struct urb *urb)
+{
+}
+
+static void ar9170_usb_irq_completed(struct urb *urb)
+{
+       struct ar9170_usb *aru = urb->context;
+
+       switch (urb->status) {
+       /* everything is fine */
+       case 0:
+               break;
+
+       /* disconnect */
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ENODEV:
+       case -ESHUTDOWN:
+               goto free;
+
+       default:
+               goto resubmit;
+       }
+
+       print_hex_dump_bytes("ar9170 irq: ", DUMP_PREFIX_OFFSET,
+                            urb->transfer_buffer, urb->actual_length);
+
+resubmit:
+       usb_anchor_urb(urb, &aru->rx_submitted);
+       if (usb_submit_urb(urb, GFP_ATOMIC)) {
+               usb_unanchor_urb(urb);
+               goto free;
+       }
+
+       return;
+
+free:
+       usb_buffer_free(aru->udev, 64, urb->transfer_buffer, urb->transfer_dma);
+}
+
+static void ar9170_usb_rx_completed(struct urb *urb)
+{
+       struct sk_buff *skb = urb->context;
+       struct ar9170_usb *aru = (struct ar9170_usb *)
+               usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+       int err;
+
+       if (!aru)
+               goto free;
+
+       switch (urb->status) {
+       /* everything is fine */
+       case 0:
+               break;
+
+       /* disconnect */
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ENODEV:
+       case -ESHUTDOWN:
+               goto free;
+
+       default:
+               goto resubmit;
+       }
+
+       skb_put(skb, urb->actual_length);
+       ar9170_rx(&aru->common, skb);
+
+resubmit:
+       skb_reset_tail_pointer(skb);
+       skb_trim(skb, 0);
+
+       usb_anchor_urb(urb, &aru->rx_submitted);
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (err) {
+               usb_unanchor_urb(urb);
+               dev_kfree_skb_irq(skb);
+       }
+
+       return ;
+
+free:
+       dev_kfree_skb_irq(skb);
+       return;
+}
+
+static int ar9170_usb_prep_rx_urb(struct ar9170_usb *aru,
+                                 struct urb *urb, gfp_t gfp)
+{
+       struct sk_buff *skb;
+
+       skb = __dev_alloc_skb(AR9170_MAX_RX_BUFFER_SIZE + 32, gfp);
+       if (!skb)
+               return -ENOMEM;
+
+       /* reserve some space for mac80211's radiotap */
+       skb_reserve(skb, 32);
+
+       usb_fill_bulk_urb(urb, aru->udev,
+                         usb_rcvbulkpipe(aru->udev, AR9170_EP_RX),
+                         skb->data, min(skb_tailroom(skb),
+                         AR9170_MAX_RX_BUFFER_SIZE),
+                         ar9170_usb_rx_completed, skb);
+
+       return 0;
+}
+
+static int ar9170_usb_alloc_rx_irq_urb(struct ar9170_usb *aru)
+{
+       struct urb *urb = NULL;
+       void *ibuf;
+       int err = -ENOMEM;
+
+       /* initialize interrupt endpoint */
+       urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!urb)
+               goto out;
+
+       ibuf = usb_buffer_alloc(aru->udev, 64, GFP_KERNEL, &urb->transfer_dma);
+       if (!ibuf)
+               goto out;
+
+       usb_fill_int_urb(urb, aru->udev,
+                        usb_rcvintpipe(aru->udev, AR9170_EP_IRQ), ibuf,
+                        64, ar9170_usb_irq_completed, aru, 1);
+       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       usb_anchor_urb(urb, &aru->rx_submitted);
+       err = usb_submit_urb(urb, GFP_KERNEL);
+       if (err) {
+               usb_unanchor_urb(urb);
+               usb_buffer_free(aru->udev, 64, urb->transfer_buffer,
+                               urb->transfer_dma);
+       }
+
+out:
+       usb_free_urb(urb);
+       return err;
+}
+
+static int ar9170_usb_alloc_rx_bulk_urbs(struct ar9170_usb *aru)
+{
+       struct urb *urb;
+       int i;
+       int err = -EINVAL;
+
+       for (i = 0; i < AR9170_NUM_RX_URBS; i++) {
+               err = -ENOMEM;
+               urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!urb)
+                       goto err_out;
+
+               err = ar9170_usb_prep_rx_urb(aru, urb, GFP_KERNEL);
+               if (err) {
+                       usb_free_urb(urb);
+                       goto err_out;
+               }
+
+               usb_anchor_urb(urb, &aru->rx_submitted);
+               err = usb_submit_urb(urb, GFP_KERNEL);
+               if (err) {
+                       usb_unanchor_urb(urb);
+                       dev_kfree_skb_any((void *) urb->transfer_buffer);
+                       usb_free_urb(urb);
+                       goto err_out;
+               }
+               usb_free_urb(urb);
+       }
+
+       /* the device now waiting for a firmware. */
+       aru->common.state = AR9170_IDLE;
+       return 0;
+
+err_out:
+
+       usb_kill_anchored_urbs(&aru->rx_submitted);
+       return err;
+}
+
+static void ar9170_usb_cancel_urbs(struct ar9170_usb *aru)
+{
+       int ret;
+
+       aru->common.state = AR9170_UNKNOWN_STATE;
+
+       usb_unlink_anchored_urbs(&aru->tx_submitted);
+
+       /* give the LED OFF command and the deauth frame a chance to air. */
+       ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted,
+                                           msecs_to_jiffies(100));
+       if (ret == 0)
+               dev_err(&aru->udev->dev, "kill pending tx urbs.\n");
+       usb_poison_anchored_urbs(&aru->tx_submitted);
+
+       usb_poison_anchored_urbs(&aru->rx_submitted);
+}
+
+static int ar9170_usb_exec_cmd(struct ar9170 *ar, enum ar9170_cmd cmd,
+                              unsigned int plen, void *payload,
+                              unsigned int outlen, void *out)
+{
+       struct ar9170_usb *aru = (void *) ar;
+       struct urb *urb = NULL;
+       unsigned long flags;
+       int err = -ENOMEM;
+
+       if (unlikely(!IS_ACCEPTING_CMD(ar)))
+               return -EPERM;
+
+       if (WARN_ON(plen > AR9170_MAX_CMD_LEN - 4))
+               return -EINVAL;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (unlikely(!urb))
+               goto err_free;
+
+       ar->cmdbuf[0] = cpu_to_le32(plen);
+       ar->cmdbuf[0] |= cpu_to_le32(cmd << 8);
+       /* writing multiple regs fills this buffer already */
+       if (plen && payload != (u8 *)(&ar->cmdbuf[1]))
+               memcpy(&ar->cmdbuf[1], payload, plen);
+
+       spin_lock_irqsave(&aru->common.cmdlock, flags);
+       aru->readbuf = (u8 *)out;
+       aru->readlen = outlen;
+       spin_unlock_irqrestore(&aru->common.cmdlock, flags);
+
+       usb_fill_int_urb(urb, aru->udev,
+                        usb_sndbulkpipe(aru->udev, AR9170_EP_CMD),
+                        aru->common.cmdbuf, plen + 4,
+                        ar9170_usb_tx_urb_complete, NULL, 1);
+
+       usb_anchor_urb(urb, &aru->tx_submitted);
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (err) {
+               usb_unanchor_urb(urb);
+               usb_free_urb(urb);
+               goto err_unbuf;
+       }
+       usb_free_urb(urb);
+
+       err = wait_for_completion_timeout(&aru->cmd_wait, HZ);
+       if (err == 0) {
+               err = -ETIMEDOUT;
+               goto err_unbuf;
+       }
+
+       if (outlen >= 0 && aru->readlen != outlen) {
+               err = -EMSGSIZE;
+               goto err_unbuf;
+       }
+
+       return 0;
+
+err_unbuf:
+       /* Maybe the device was removed in the second we were waiting? */
+       if (IS_STARTED(ar)) {
+               dev_err(&aru->udev->dev, "no command feedback "
+                                        "received (%d).\n", err);
+
+               /* provide some maybe useful debug information */
+               print_hex_dump_bytes("ar9170 cmd: ", DUMP_PREFIX_NONE,
+                                    aru->common.cmdbuf, plen + 4);
+               dump_stack();
+       }
+
+       /* invalidate to avoid completing the next prematurely */
+       spin_lock_irqsave(&aru->common.cmdlock, flags);
+       aru->readbuf = NULL;
+       aru->readlen = 0;
+       spin_unlock_irqrestore(&aru->common.cmdlock, flags);
+
+err_free:
+
+       return err;
+}
+
+static int ar9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb,
+                        bool txstatus_needed, unsigned int extra_len)
+{
+       struct ar9170_usb *aru = (struct ar9170_usb *) ar;
+       struct urb *urb;
+       int err;
+
+       if (unlikely(!IS_STARTED(ar))) {
+               /* Seriously, what were you drink... err... thinking!? */
+               return -EPERM;
+       }
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (unlikely(!urb))
+               return -ENOMEM;
+
+       usb_fill_bulk_urb(urb, aru->udev,
+                         usb_sndbulkpipe(aru->udev, AR9170_EP_TX),
+                         skb->data, skb->len + extra_len, (txstatus_needed ?
+                         ar9170_usb_tx_urb_complete :
+                         ar9170_usb_tx_urb_complete_free), skb);
+       urb->transfer_flags |= URB_ZERO_PACKET;
+
+       usb_anchor_urb(urb, &aru->tx_submitted);
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (unlikely(err))
+               usb_unanchor_urb(urb);
+
+       usb_free_urb(urb);
+       return err;
+}
+
+static void ar9170_usb_callback_cmd(struct ar9170 *ar, u32 len , void *buffer)
+{
+       struct ar9170_usb *aru = (void *) ar;
+       unsigned long flags;
+       u32 in, out;
+
+       if (!buffer)
+               return ;
+
+       in = le32_to_cpup((__le32 *)buffer);
+       out = le32_to_cpu(ar->cmdbuf[0]);
+
+       /* mask off length byte */
+       out &= ~0xFF;
+
+       if (aru->readlen >= 0) {
+               /* add expected length */
+               out |= aru->readlen;
+       } else {
+               /* add obtained length */
+               out |= in & 0xFF;
+       }
+
+       /*
+        * Some commands (e.g: AR9170_CMD_FREQUENCY) have a variable response
+        * length and we cannot predict the correct length in advance.
+        * So we only check if we provided enough space for the data.
+        */
+       if (unlikely(out < in)) {
+               dev_warn(&aru->udev->dev, "received invalid command response "
+                                         "got %d bytes, instead of %d bytes "
+                                         "and the resp length is %d bytes\n",
+                        in, out, len);
+               print_hex_dump_bytes("ar9170 invalid resp: ",
+                                    DUMP_PREFIX_OFFSET, buffer, len);
+               /*
+                * Do not complete, then the command times out,
+                * and we get a stack trace from there.
+                */
+               return ;
+       }
+
+       spin_lock_irqsave(&aru->common.cmdlock, flags);
+       if (aru->readbuf && len > 0) {
+               memcpy(aru->readbuf, buffer + 4, len - 4);
+               aru->readbuf = NULL;
+       }
+       complete(&aru->cmd_wait);
+       spin_unlock_irqrestore(&aru->common.cmdlock, flags);
+}
+
+static int ar9170_usb_upload(struct ar9170_usb *aru, const void *data,
+                            size_t len, u32 addr, bool complete)
+{
+       int transfer, err;
+       u8 *buf = kmalloc(4096, GFP_KERNEL);
+
+       if (!buf)
+               return -ENOMEM;
+
+       while (len) {
+               transfer = min_t(int, len, 4096);
+               memcpy(buf, data, transfer);
+
+               err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0),
+                                     0x30 /* FW DL */, 0x40 | USB_DIR_OUT,
+                                     addr >> 8, 0, buf, transfer, 1000);
+
+               if (err < 0) {
+                       kfree(buf);
+                       return err;
+               }
+
+               len -= transfer;
+               data += transfer;
+               addr += transfer;
+       }
+       kfree(buf);
+
+       if (complete) {
+               err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0),
+                                     0x31 /* FW DL COMPLETE */,
+                                     0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 5000);
+       }
+
+       return 0;
+}
+
+static int ar9170_usb_request_firmware(struct ar9170_usb *aru)
+{
+       int err = 0;
+
+       err = request_firmware(&aru->init_values, "ar9170-1.fw",
+                              &aru->udev->dev);
+       if (err) {
+               dev_err(&aru->udev->dev, "file with init values not found.\n");
+               return err;
+       }
+
+       err = request_firmware(&aru->firmware, "ar9170-2.fw", &aru->udev->dev);
+       if (err) {
+               release_firmware(aru->init_values);
+               dev_err(&aru->udev->dev, "firmware file not found.\n");
+               return err;
+       }
+
+       return err;
+}
+
+static int ar9170_usb_reset(struct ar9170_usb *aru)
+{
+       int ret, lock = (aru->intf->condition != USB_INTERFACE_BINDING);
+
+       if (lock) {
+               ret = usb_lock_device_for_reset(aru->udev, aru->intf);
+               if (ret < 0) {
+                       dev_err(&aru->udev->dev, "unable to lock device "
+                               "for reset (%d).\n", ret);
+                       return ret;
+               }
+       }
+
+       ret = usb_reset_device(aru->udev);
+       if (lock)
+               usb_unlock_device(aru->udev);
+
+       /* let it rest - for a second - */
+       msleep(1000);
+
+       return ret;
+}
+
+static int ar9170_usb_upload_firmware(struct ar9170_usb *aru)
+{
+       int err;
+
+       /* First, upload initial values to device RAM */
+       err = ar9170_usb_upload(aru, aru->init_values->data,
+                               aru->init_values->size, 0x102800, false);
+       if (err) {
+               dev_err(&aru->udev->dev, "firmware part 1 "
+                       "upload failed (%d).\n", err);
+               return err;
+       }
+
+       /* Then, upload the firmware itself and start it */
+       return ar9170_usb_upload(aru, aru->firmware->data, aru->firmware->size,
+                               0x200000, true);
+}
+
+static int ar9170_usb_init_transport(struct ar9170_usb *aru)
+{
+       struct ar9170 *ar = (void *) &aru->common;
+       int err;
+
+       ar9170_regwrite_begin(ar);
+
+       /* Set USB Rx stream mode MAX packet number to 2 */
+       ar9170_regwrite(AR9170_USB_REG_MAX_AGG_UPLOAD, 0x4);
+
+       /* Set USB Rx stream mode timeout to 10us */
+       ar9170_regwrite(AR9170_USB_REG_UPLOAD_TIME_CTL, 0x80);
+
+       ar9170_regwrite_finish();
+
+       err = ar9170_regwrite_result();
+       if (err)
+               dev_err(&aru->udev->dev, "USB setup failed (%d).\n", err);
+
+       return err;
+}
+
+static void ar9170_usb_stop(struct ar9170 *ar)
+{
+       struct ar9170_usb *aru = (void *) ar;
+       int ret;
+
+       if (IS_ACCEPTING_CMD(ar))
+               aru->common.state = AR9170_STOPPED;
+
+       /* lets wait a while until the tx - queues are dried out */
+       ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted,
+                                           msecs_to_jiffies(1000));
+       if (ret == 0)
+               dev_err(&aru->udev->dev, "kill pending tx urbs.\n");
+
+       usb_poison_anchored_urbs(&aru->tx_submitted);
+
+       /*
+        * Note:
+        * So far we freed all tx urbs, but we won't dare to touch any rx urbs.
+        * Else we would end up with a unresponsive device...
+        */
+}
+
+static int ar9170_usb_open(struct ar9170 *ar)
+{
+       struct ar9170_usb *aru = (void *) ar;
+       int err;
+
+       usb_unpoison_anchored_urbs(&aru->tx_submitted);
+       err = ar9170_usb_init_transport(aru);
+       if (err) {
+               usb_poison_anchored_urbs(&aru->tx_submitted);
+               return err;
+       }
+
+       aru->common.state = AR9170_IDLE;
+       return 0;
+}
+
+static int ar9170_usb_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       struct ar9170_usb *aru;
+       struct ar9170 *ar;
+       struct usb_device *udev;
+       int err;
+
+       aru = ar9170_alloc(sizeof(*aru));
+       if (IS_ERR(aru)) {
+               err = PTR_ERR(aru);
+               goto out;
+       }
+
+       udev = interface_to_usbdev(intf);
+       usb_get_dev(udev);
+       aru->udev = udev;
+       aru->intf = intf;
+       ar = &aru->common;
+
+       usb_set_intfdata(intf, aru);
+       SET_IEEE80211_DEV(ar->hw, &udev->dev);
+
+       init_usb_anchor(&aru->rx_submitted);
+       init_usb_anchor(&aru->tx_submitted);
+       init_completion(&aru->cmd_wait);
+
+       aru->common.stop = ar9170_usb_stop;
+       aru->common.open = ar9170_usb_open;
+       aru->common.tx = ar9170_usb_tx;
+       aru->common.exec_cmd = ar9170_usb_exec_cmd;
+       aru->common.callback_cmd = ar9170_usb_callback_cmd;
+
+       err = ar9170_usb_reset(aru);
+       if (err)
+               goto err_unlock;
+
+       err = ar9170_usb_request_firmware(aru);
+       if (err)
+               goto err_unlock;
+
+       err = ar9170_usb_alloc_rx_irq_urb(aru);
+       if (err)
+               goto err_freefw;
+
+       err = ar9170_usb_alloc_rx_bulk_urbs(aru);
+       if (err)
+               goto err_unrx;
+
+       err = ar9170_usb_upload_firmware(aru);
+       if (err) {
+               err = ar9170_echo_test(&aru->common, 0x60d43110);
+               if (err) {
+                       /* force user invention, by disabling the device */
+                       err = usb_driver_set_configuration(aru->udev, -1);
+                       dev_err(&aru->udev->dev, "device is in a bad state. "
+                                                "please reconnect it!\n");
+                       goto err_unrx;
+               }
+       }
+
+       err = ar9170_usb_open(ar);
+       if (err)
+               goto err_unrx;
+
+       err = ar9170_register(ar, &udev->dev);
+
+       ar9170_usb_stop(ar);
+       if (err)
+               goto err_unrx;
+
+       return 0;
+
+err_unrx:
+       ar9170_usb_cancel_urbs(aru);
+
+err_freefw:
+       release_firmware(aru->init_values);
+       release_firmware(aru->firmware);
+
+err_unlock:
+       usb_set_intfdata(intf, NULL);
+       usb_put_dev(udev);
+       ieee80211_free_hw(ar->hw);
+out:
+       return err;
+}
+
+static void ar9170_usb_disconnect(struct usb_interface *intf)
+{
+       struct ar9170_usb *aru = usb_get_intfdata(intf);
+
+       if (!aru)
+               return;
+
+       aru->common.state = AR9170_IDLE;
+       ar9170_unregister(&aru->common);
+       ar9170_usb_cancel_urbs(aru);
+
+       release_firmware(aru->init_values);
+       release_firmware(aru->firmware);
+
+       usb_put_dev(aru->udev);
+       usb_set_intfdata(intf, NULL);
+       ieee80211_free_hw(aru->common.hw);
+}
+
+static struct usb_driver ar9170_driver = {
+       .name = "ar9170usb",
+       .probe = ar9170_usb_probe,
+       .disconnect = ar9170_usb_disconnect,
+       .id_table = ar9170_usb_ids,
+       .soft_unbind = 1,
+};
+
+static int __init ar9170_init(void)
+{
+       return usb_register(&ar9170_driver);
+}
+
+static void __exit ar9170_exit(void)
+{
+       usb_deregister(&ar9170_driver);
+}
+
+module_init(ar9170_init);
+module_exit(ar9170_exit);
diff --git a/drivers/net/wireless/ar9170/usb.h b/drivers/net/wireless/ar9170/usb.h
new file mode 100644 (file)
index 0000000..f585292
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Atheros AR9170 USB driver
+ *
+ * Driver specific definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, Christian Lamparter <chunkeey@web.de>
+ *
+ * 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; see the file COPYING.  If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *    Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ *    Permission to use, copy, modify, and/or 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.
+ */
+#ifndef __USB_H
+#define __USB_H
+
+#include <linux/usb.h>
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <linux/leds.h>
+#include <net/wireless.h>
+#include <net/mac80211.h>
+#include <linux/firmware.h>
+#include "eeprom.h"
+#include "hw.h"
+#include "ar9170.h"
+
+#define AR9170_NUM_RX_URBS                     16
+
+struct firmware;
+
+struct ar9170_usb {
+       struct ar9170 common;
+       struct usb_device *udev;
+       struct usb_interface *intf;
+
+       struct usb_anchor rx_submitted;
+       struct usb_anchor tx_submitted;
+
+       spinlock_t cmdlock;
+       struct completion cmd_wait;
+       int readlen;
+       u8 *readbuf;
+
+       const struct firmware *init_values;
+       const struct firmware *firmware;
+};
+
+#endif /* __USB_H */
index bfca15da6f0f844fa379bcb7c7eade8914139e3c..5b9f1e06ebf6b5488d7c169f99b8a35c2a9b528e 100644 (file)
@@ -1030,7 +1030,17 @@ static int arlan_mac_addr(struct net_device *dev, void *p)
        return 0;
 }
 
-
+static const struct net_device_ops arlan_netdev_ops = {
+       .ndo_open               = arlan_open,
+       .ndo_stop               = arlan_close,
+       .ndo_start_xmit         = arlan_tx,
+       .ndo_get_stats          = arlan_statistics,
+       .ndo_set_multicast_list = arlan_set_multicast,
+       .ndo_change_mtu         = arlan_change_mtu,
+       .ndo_set_mac_address    = arlan_mac_addr,
+       .ndo_tx_timeout         = arlan_tx_timeout,
+       .ndo_validate_addr      = eth_validate_addr,
+};
 
 static int __init arlan_setup_device(struct net_device *dev, int num)
 {
@@ -1042,14 +1052,7 @@ static int __init arlan_setup_device(struct net_device *dev, int num)
        ap->conf = (struct arlan_shmem *)(ap+1);
 
        dev->tx_queue_len = tx_queue_len;
-       dev->open = arlan_open;
-       dev->stop = arlan_close;
-       dev->hard_start_xmit = arlan_tx;
-       dev->get_stats = arlan_statistics;
-       dev->set_multicast_list = arlan_set_multicast;
-       dev->change_mtu = arlan_change_mtu;
-       dev->set_mac_address = arlan_mac_addr;
-       dev->tx_timeout = arlan_tx_timeout;
+       dev->netdev_ops = &arlan_netdev_ops;
        dev->watchdog_timeo = 3*HZ;
        
        ap->irq_test_done = 0;
index 0dc2c7321c8b69c6a45f79b607459f17b2ff47c7..0b616e72fe051436a59b822b51d486f834a9f343 100644 (file)
 #define AR5K_TUNE_CWMAX_11B                    1023
 #define AR5K_TUNE_CWMAX_XR                     7
 #define AR5K_TUNE_NOISE_FLOOR                  -72
-#define AR5K_TUNE_MAX_TXPOWER                  60
-#define AR5K_TUNE_DEFAULT_TXPOWER              30
-#define AR5K_TUNE_TPC_TXPOWER                  true
+#define AR5K_TUNE_MAX_TXPOWER                  63
+#define AR5K_TUNE_DEFAULT_TXPOWER              25
+#define AR5K_TUNE_TPC_TXPOWER                  false
 #define AR5K_TUNE_ANT_DIVERSITY                        true
 #define AR5K_TUNE_HWTXTRIES                    4
 
@@ -551,11 +551,11 @@ enum ath5k_pkt_type {
  */
 #define AR5K_TXPOWER_OFDM(_r, _v)      (                       \
        ((0 & 1) << ((_v) + 6)) |                               \
-       (((ah->ah_txpower.txp_rates[(_r)]) & 0x3f) << (_v))     \
+       (((ah->ah_txpower.txp_rates_power_table[(_r)]) & 0x3f) << (_v)) \
 )
 
 #define AR5K_TXPOWER_CCK(_r, _v)       (                       \
-       (ah->ah_txpower.txp_rates[(_r)] & 0x3f) << (_v) \
+       (ah->ah_txpower.txp_rates_power_table[(_r)] & 0x3f) << (_v)     \
 )
 
 /*
@@ -1085,13 +1085,25 @@ struct ath5k_hw {
        struct ath5k_gain       ah_gain;
        u8                      ah_offset[AR5K_MAX_RF_BANKS];
 
+
        struct {
-               u16             txp_pcdac[AR5K_EEPROM_POWER_TABLE_SIZE];
-               u16             txp_rates[AR5K_MAX_RATES];
-               s16             txp_min;
-               s16             txp_max;
+               /* Temporary tables used for interpolation */
+               u8              tmpL[AR5K_EEPROM_N_PD_GAINS]
+                                       [AR5K_EEPROM_POWER_TABLE_SIZE];
+               u8              tmpR[AR5K_EEPROM_N_PD_GAINS]
+                                       [AR5K_EEPROM_POWER_TABLE_SIZE];
+               u8              txp_pd_table[AR5K_EEPROM_POWER_TABLE_SIZE * 2];
+               u16             txp_rates_power_table[AR5K_MAX_RATES];
+               u8              txp_min_idx;
                bool            txp_tpc;
+               /* Values in 0.25dB units */
+               s16             txp_min_pwr;
+               s16             txp_max_pwr;
+               s16             txp_offset;
                s16             txp_ofdm;
+               /* Values in dB units */
+               s16             txp_cck_ofdm_pwr_delta;
+               s16             txp_cck_ofdm_gainf_delta;
        } ah_txpower;
 
        struct {
@@ -1161,6 +1173,7 @@ extern void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, struct ieee80211_l
 
 /* EEPROM access functions */
 extern int ath5k_eeprom_init(struct ath5k_hw *ah);
+extern void ath5k_eeprom_detach(struct ath5k_hw *ah);
 extern int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac);
 extern bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah);
 
@@ -1256,8 +1269,8 @@ extern void ath5k_hw_set_def_antenna(struct ath5k_hw *ah, unsigned int ant);
 extern unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah);
 extern int ath5k_hw_phy_disable(struct ath5k_hw *ah);
 /* TX power setup */
-extern int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, unsigned int txpower);
-extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, unsigned int power);
+extern int ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, u8 ee_mode, u8 txpower);
+extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 ee_mode, u8 txpower);
 
 /*
  * Functions used internaly
index 656cb9dc833b8690d99170b0e9ca35accb17098a..70d376c63aac4d4ba746dc899333bbcee606bff1 100644 (file)
@@ -341,6 +341,8 @@ void ath5k_hw_detach(struct ath5k_hw *ah)
        if (ah->ah_rf_banks != NULL)
                kfree(ah->ah_rf_banks);
 
+       ath5k_eeprom_detach(ah);
+
        /* assume interrupts are down */
        kfree(ah);
 }
index cad3ccf61b00b378535122f880f635d28045419b..5d57d774e466b0308b47df24b5871005dcc4fc51 100644 (file)
@@ -685,13 +685,6 @@ ath5k_pci_resume(struct pci_dev *pdev)
        if (err)
                return err;
 
-       /*
-        * Suspend/Resume resets the PCI configuration space, so we have to
-        * re-disable the RETRY_TIMEOUT register (0x41) to keep
-        * PCI Tx retries from interfering with C3 CPU state
-        */
-       pci_write_config_byte(pdev, 0x41, 0);
-
        err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
        if (err) {
                ATH5K_ERR(sc, "request_irq failed\n");
@@ -1095,9 +1088,18 @@ ath5k_mode_setup(struct ath5k_softc *sc)
 static inline int
 ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
 {
-       WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
-                       "hw_rix out of bounds: %x\n", hw_rix);
-       return sc->rate_idx[sc->curband->band][hw_rix];
+       int rix;
+
+       /* return base rate on errors */
+       if (WARN(hw_rix < 0 || hw_rix >= AR5K_MAX_RATES,
+                       "hw_rix out of bounds: %x\n", hw_rix))
+               return 0;
+
+       rix = sc->rate_idx[sc->curband->band][hw_rix];
+       if (WARN(rix < 0, "invalid hw_rix: %x\n", hw_rix))
+               rix = 0;
+
+       return rix;
 }
 
 /***************\
@@ -1216,6 +1218,9 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
 
        pktlen = skb->len;
 
+       /* 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 (info->control.hw_key) {
                keyidx = info->control.hw_key->hw_key_idx;
                pktlen += info->control.hw_key->icv_len;
@@ -2044,6 +2049,9 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
                        antenna = sc->bsent & 4 ? 2 : 1;
        }
 
+       /* 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) */
        ds->ds_data = bf->skbaddr;
        ret = ah->ah_setup_tx_desc(ah, ds, skb->len,
                        ieee80211_get_hdrlen_from_skb(skb),
@@ -2305,7 +2313,7 @@ ath5k_init(struct ath5k_softc *sc)
        sc->curband = &sc->sbands[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 | AR5K_INT_MIB;
+               AR5K_INT_FATAL | AR5K_INT_GLOBAL;
        ret = ath5k_reset(sc, false, false);
        if (ret)
                goto done;
@@ -2554,7 +2562,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                if (skb_headroom(skb) < padsize) {
                        ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough"
                                  " headroom to pad %d\n", hdrlen, padsize);
-                       return NETDEV_TX_BUSY;
+                       goto drop_packet;
                }
                skb_push(skb, padsize);
                memmove(skb->data, skb->data+padsize, hdrlen);
@@ -2565,7 +2573,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
                spin_unlock_irqrestore(&sc->txbuflock, flags);
                ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
-               return NETDEV_TX_BUSY;
+               goto drop_packet;
        }
        bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
        list_del(&bf->list);
@@ -2582,10 +2590,12 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                list_add_tail(&bf->list, &sc->txbuf);
                sc->txbuf_len++;
                spin_unlock_irqrestore(&sc->txbuflock, flags);
-               dev_kfree_skb_any(skb);
-               return NETDEV_TX_OK;
+               goto drop_packet;
        }
+       return NETDEV_TX_OK;
 
+drop_packet:
+       dev_kfree_skb_any(skb);
        return NETDEV_TX_OK;
 }
 
@@ -2608,12 +2618,6 @@ ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel)
                goto err;
        }
 
-       /*
-        * This is needed only to setup initial state
-        * but it's best done after a reset.
-        */
-       ath5k_hw_set_txpower_limit(sc->ah, 0);
-
        ret = ath5k_rx_start(sc);
        if (ret) {
                ATH5K_ERR(sc, "can't start recv logic\n");
index 20e0d14b41eca221710c9fc6fa981c239f31c5f4..822956114cd767781ec6546814d6c2cf8f0047c0 100644 (file)
@@ -112,7 +112,7 @@ struct ath5k_softc {
        struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
        struct ieee80211_channel channels[ATH_CHAN_MAX];
        struct ieee80211_rate   rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
-       u8                      rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
+       s8                      rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
        enum nl80211_iftype     opmode;
        struct ath5k_hw         *ah;            /* Atheros HW */
 
index b40a9287a39a8a3be2fd8888c2f5dd8fde484511..dc30a2b70a6b41861e9471df551dd1ba4fb62b4e 100644 (file)
@@ -194,6 +194,10 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
                return -EINVAL;
        }
 
+       tx_power += ah->ah_txpower.txp_offset;
+       if (tx_power > AR5K_TUNE_MAX_TXPOWER)
+               tx_power = AR5K_TUNE_MAX_TXPOWER;
+
        /* Clear descriptor */
        memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc));
 
index ac45ca47ca87fa483850d0295dd4a3a73b1eaa96..c0fb3b09ba45f0e28b927f008f1d74736a683717 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
- * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
- * Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -98,11 +98,6 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah)
        int ret;
        u16 val;
 
-       /* Initial TX thermal adjustment values */
-       ee->ee_tx_clip = 4;
-       ee->ee_pwd_84 = ee->ee_pwd_90 = 1;
-       ee->ee_gain_select = 1;
-
        /*
         * Read values from EEPROM and store them in the capability structure
         */
@@ -241,22 +236,22 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
        ee->ee_adc_desired_size[mode]   = (s8)((val >> 8) & 0xff);
        switch(mode) {
        case AR5K_EEPROM_MODE_11A:
-               ee->ee_ob[mode][3]              = (val >> 5) & 0x7;
-               ee->ee_db[mode][3]              = (val >> 2) & 0x7;
-               ee->ee_ob[mode][2]              = (val << 1) & 0x7;
+               ee->ee_ob[mode][3]      = (val >> 5) & 0x7;
+               ee->ee_db[mode][3]      = (val >> 2) & 0x7;
+               ee->ee_ob[mode][2]      = (val << 1) & 0x7;
 
                AR5K_EEPROM_READ(o++, val);
-               ee->ee_ob[mode][2]              |= (val >> 15) & 0x1;
-               ee->ee_db[mode][2]              = (val >> 12) & 0x7;
-               ee->ee_ob[mode][1]              = (val >> 9) & 0x7;
-               ee->ee_db[mode][1]              = (val >> 6) & 0x7;
-               ee->ee_ob[mode][0]              = (val >> 3) & 0x7;
-               ee->ee_db[mode][0]              = val & 0x7;
+               ee->ee_ob[mode][2]      |= (val >> 15) & 0x1;
+               ee->ee_db[mode][2]      = (val >> 12) & 0x7;
+               ee->ee_ob[mode][1]      = (val >> 9) & 0x7;
+               ee->ee_db[mode][1]      = (val >> 6) & 0x7;
+               ee->ee_ob[mode][0]      = (val >> 3) & 0x7;
+               ee->ee_db[mode][0]      = val & 0x7;
                break;
        case AR5K_EEPROM_MODE_11G:
        case AR5K_EEPROM_MODE_11B:
-               ee->ee_ob[mode][1]              = (val >> 4) & 0x7;
-               ee->ee_db[mode][1]              = val & 0x7;
+               ee->ee_ob[mode][1]      = (val >> 4) & 0x7;
+               ee->ee_db[mode][1]      = val & 0x7;
                break;
        }
 
@@ -504,35 +499,6 @@ ath5k_eeprom_init_modes(struct ath5k_hw *ah)
        return 0;
 }
 
-/* Used to match PCDAC steps with power values on RF5111 chips
- * (eeprom versions < 4). For RF5111 we have 10 pre-defined PCDAC
- * steps that match with the power values we read from eeprom. On
- * older eeprom versions (< 3.2) these steps are equaly spaced at
- * 10% of the pcdac curve -until the curve reaches it's maximum-
- * (10 steps from 0 to 100%) but on newer eeprom versions (>= 3.2)
- * these 10 steps are spaced in a different way. This function returns
- * the pcdac steps based on eeprom version and curve min/max so that we
- * can have  pcdac/pwr points.
- */
-static inline void
-ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
-{
-       static const u16 intercepts3[] =
-               { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 };
-       static const u16 intercepts3_2[] =
-               { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
-       const u16 *ip;
-       int i;
-
-       if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2)
-               ip = intercepts3_2;
-       else
-               ip = intercepts3;
-
-       for (i = 0; i < ARRAY_SIZE(intercepts3); i++)
-               *vp++ = (ip[i] * max + (100 - ip[i]) * min) / 100;
-}
-
 /* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff
  * frequency mask) */
 static inline int
@@ -546,26 +512,25 @@ ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max,
        int ret;
        u16 val;
 
+       ee->ee_n_piers[mode] = 0;
        while(i < max) {
                AR5K_EEPROM_READ(o++, val);
 
-               freq1 = (val >> 8) & 0xff;
-               freq2 = val & 0xff;
-
-               if (freq1) {
-                       pc[i++].freq = ath5k_eeprom_bin2freq(ee,
-                                       freq1, mode);
-                       ee->ee_n_piers[mode]++;
-               }
+               freq1 = val & 0xff;
+               if (!freq1)
+                       break;
 
-               if (freq2) {
-                       pc[i++].freq = ath5k_eeprom_bin2freq(ee,
-                                       freq2, mode);
-                       ee->ee_n_piers[mode]++;
-               }
+               pc[i++].freq = ath5k_eeprom_bin2freq(ee,
+                               freq1, mode);
+               ee->ee_n_piers[mode]++;
 
-               if (!freq1 || !freq2)
+               freq2 = (val >> 8) & 0xff;
+               if (!freq2)
                        break;
+
+               pc[i++].freq = ath5k_eeprom_bin2freq(ee,
+                               freq2, mode);
+               ee->ee_n_piers[mode]++;
        }
 
        /* return new offset */
@@ -652,13 +617,122 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
        return 0;
 }
 
-/* Read power calibration for RF5111 chips
+/*
+ * Read power calibration for RF5111 chips
+ *
  * For RF5111 we have an XPD -eXternal Power Detector- curve
- * for each calibrated channel. Each curve has PCDAC steps on
- * x axis and power on y axis and looks like a logarithmic
- * function. To recreate the curve and pass the power values
- * on the pcdac table, we read 10 points here and interpolate later.
+ * for each calibrated channel. Each curve has 0,5dB Power steps
+ * on x axis and PCDAC steps (offsets) on y axis and looks like an
+ * exponential function. To recreate the curve we read 11 points
+ * here and interpolate later.
  */
+
+/* Used to match PCDAC steps with power values on RF5111 chips
+ * (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC
+ * steps that match with the power values we read from eeprom. On
+ * older eeprom versions (< 3.2) these steps are equaly spaced at
+ * 10% of the pcdac curve -until the curve reaches it's maximum-
+ * (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2)
+ * these 11 steps are spaced in a different way. This function returns
+ * the pcdac steps based on eeprom version and curve min/max so that we
+ * can have pcdac/pwr points.
+ */
+static inline void
+ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
+{
+       const static u16 intercepts3[] =
+               { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 };
+       const static u16 intercepts3_2[] =
+               { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
+       const u16 *ip;
+       int i;
+
+       if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2)
+               ip = intercepts3_2;
+       else
+               ip = intercepts3;
+
+       for (i = 0; i < ARRAY_SIZE(intercepts3); i++)
+               vp[i] = (ip[i] * max + (100 - ip[i]) * min) / 100;
+}
+
+/* Convert RF5111 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode,
+                               struct ath5k_chan_pcal_info *chinfo)
+{
+       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+       struct ath5k_chan_pcal_info_rf5111 *pcinfo;
+       struct ath5k_pdgain_info *pd;
+       u8 pier, point, idx;
+       u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+
+       /* Fill raw data for each calibration pier */
+       for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+               pcinfo = &chinfo[pier].rf5111_info;
+
+               /* Allocate pd_curves for this cal pier */
+               chinfo[pier].pd_curves =
+                       kcalloc(AR5K_EEPROM_N_PD_CURVES,
+                               sizeof(struct ath5k_pdgain_info),
+                               GFP_KERNEL);
+
+               if (!chinfo[pier].pd_curves)
+                       return -ENOMEM;
+
+               /* Only one curve for RF5111
+                * find out which one and place
+                * in in pd_curves.
+                * Note: ee_x_gain is reversed here */
+               for (idx = 0; idx < AR5K_EEPROM_N_PD_CURVES; idx++) {
+
+                       if (!((ee->ee_x_gain[mode] >> idx) & 0x1)) {
+                               pdgain_idx[0] = idx;
+                               break;
+                       }
+               }
+
+               ee->ee_pd_gains[mode] = 1;
+
+               pd = &chinfo[pier].pd_curves[idx];
+
+               pd->pd_points = AR5K_EEPROM_N_PWR_POINTS_5111;
+
+               /* Allocate pd points for this curve */
+               pd->pd_step = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111,
+                                       sizeof(u8), GFP_KERNEL);
+               if (!pd->pd_step)
+                       return -ENOMEM;
+
+               pd->pd_pwr = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111,
+                                       sizeof(s16), GFP_KERNEL);
+               if (!pd->pd_pwr)
+                       return -ENOMEM;
+
+               /* Fill raw dataset
+                * (convert power to 0.25dB units
+                * for RF5112 combatibility) */
+               for (point = 0; point < pd->pd_points; point++) {
+
+                       /* Absolute values */
+                       pd->pd_pwr[point] = 2 * pcinfo->pwr[point];
+
+                       /* Already sorted */
+                       pd->pd_step[point] = pcinfo->pcdac[point];
+               }
+
+               /* Set min/max pwr */
+               chinfo[pier].min_pwr = pd->pd_pwr[0];
+               chinfo[pier].max_pwr = pd->pd_pwr[10];
+
+       }
+
+       return 0;
+}
+
+/* Parse EEPROM data */
 static int
 ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
 {
@@ -747,30 +821,165 @@ ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
                        cdata->pcdac_max, cdata->pcdac);
        }
 
-       return 0;
+       return ath5k_eeprom_convert_pcal_info_5111(ah, mode, pcal);
 }
 
-/* Read power calibration for RF5112 chips
+
+/*
+ * Read power calibration for RF5112 chips
+ *
  * For RF5112 we have 4 XPD -eXternal Power Detector- curves
  * for each calibrated channel on 0, -6, -12 and -18dbm but we only
- * use the higher (3) and the lower (0) curves. Each curve has PCDAC
- * steps on x axis and power on y axis and looks like a linear
- * function. To recreate the curve and pass the power values
- * on the pcdac table, we read 4 points for xpd 0 and 3 points
- * for xpd 3 here and interpolate later.
+ * use the higher (3) and the lower (0) curves. Each curve has 0.5dB
+ * power steps on x axis and PCDAC steps on y axis and looks like a
+ * linear function. To recreate the curve and pass the power values
+ * on hw, we read 4 points for xpd 0 (lower gain -> max power)
+ * and 3 points for xpd 3 (higher gain -> lower power) here and
+ * interpolate later.
  *
  * Note: Many vendors just use xpd 0 so xpd 3 is zeroed.
  */
+
+/* Convert RF5112 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_5112(struct ath5k_hw *ah, int mode,
+                               struct ath5k_chan_pcal_info *chinfo)
+{
+       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+       struct ath5k_chan_pcal_info_rf5112 *pcinfo;
+       u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+       unsigned int pier, pdg, point;
+
+       /* Fill raw data for each calibration pier */
+       for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+               pcinfo = &chinfo[pier].rf5112_info;
+
+               /* Allocate pd_curves for this cal pier */
+               chinfo[pier].pd_curves =
+                               kcalloc(AR5K_EEPROM_N_PD_CURVES,
+                                       sizeof(struct ath5k_pdgain_info),
+                                       GFP_KERNEL);
+
+               if (!chinfo[pier].pd_curves)
+                       return -ENOMEM;
+
+               /* Fill pd_curves */
+               for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+
+                       u8 idx = pdgain_idx[pdg];
+                       struct ath5k_pdgain_info *pd =
+                                       &chinfo[pier].pd_curves[idx];
+
+                       /* Lowest gain curve (max power) */
+                       if (pdg == 0) {
+                               /* One more point for better accuracy */
+                               pd->pd_points = AR5K_EEPROM_N_XPD0_POINTS;
+
+                               /* Allocate pd points for this curve */
+                               pd->pd_step = kcalloc(pd->pd_points,
+                                               sizeof(u8), GFP_KERNEL);
+
+                               if (!pd->pd_step)
+                                       return -ENOMEM;
+
+                               pd->pd_pwr = kcalloc(pd->pd_points,
+                                               sizeof(s16), GFP_KERNEL);
+
+                               if (!pd->pd_pwr)
+                                       return -ENOMEM;
+
+
+                               /* Fill raw dataset
+                                * (all power levels are in 0.25dB units) */
+                               pd->pd_step[0] = pcinfo->pcdac_x0[0];
+                               pd->pd_pwr[0] = pcinfo->pwr_x0[0];
+
+                               for (point = 1; point < pd->pd_points;
+                               point++) {
+                                       /* Absolute values */
+                                       pd->pd_pwr[point] =
+                                               pcinfo->pwr_x0[point];
+
+                                       /* Deltas */
+                                       pd->pd_step[point] =
+                                               pd->pd_step[point - 1] +
+                                               pcinfo->pcdac_x0[point];
+                               }
+
+                               /* Set min power for this frequency */
+                               chinfo[pier].min_pwr = pd->pd_pwr[0];
+
+                       /* Highest gain curve (min power) */
+                       } else if (pdg == 1) {
+
+                               pd->pd_points = AR5K_EEPROM_N_XPD3_POINTS;
+
+                               /* Allocate pd points for this curve */
+                               pd->pd_step = kcalloc(pd->pd_points,
+                                               sizeof(u8), GFP_KERNEL);
+
+                               if (!pd->pd_step)
+                                       return -ENOMEM;
+
+                               pd->pd_pwr = kcalloc(pd->pd_points,
+                                               sizeof(s16), GFP_KERNEL);
+
+                               if (!pd->pd_pwr)
+                                       return -ENOMEM;
+
+                               /* Fill raw dataset
+                                * (all power levels are in 0.25dB units) */
+                               for (point = 0; point < pd->pd_points;
+                               point++) {
+                                       /* Absolute values */
+                                       pd->pd_pwr[point] =
+                                               pcinfo->pwr_x3[point];
+
+                                       /* Fixed points */
+                                       pd->pd_step[point] =
+                                               pcinfo->pcdac_x3[point];
+                               }
+
+                               /* Since we have a higher gain curve
+                                * override min power */
+                               chinfo[pier].min_pwr = pd->pd_pwr[0];
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/* Parse EEPROM data */
 static int
 ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
 {
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
        struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info;
        struct ath5k_chan_pcal_info *gen_chan_info;
+       u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
        u32 offset;
-       unsigned int i, c;
+       u8 i, c;
        u16 val;
        int ret;
+       u8 pd_gains = 0;
+
+       /* Count how many curves we have and
+        * identify them (which one of the 4
+        * available curves we have on each count).
+        * Curves are stored from lower (x0) to
+        * higher (x3) gain */
+       for (i = 0; i < AR5K_EEPROM_N_PD_CURVES; i++) {
+               /* ee_x_gain[mode] is x gain mask */
+               if ((ee->ee_x_gain[mode] >> i) & 0x1)
+                       pdgain_idx[pd_gains++] = i;
+       }
+       ee->ee_pd_gains[mode] = pd_gains;
+
+       if (pd_gains == 0 || pd_gains > 2)
+               return -EINVAL;
 
        switch (mode) {
        case AR5K_EEPROM_MODE_11A:
@@ -808,13 +1017,13 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
        for (i = 0; i < ee->ee_n_piers[mode]; i++) {
                chan_pcal_info = &gen_chan_info[i].rf5112_info;
 
-               /* Power values in dBm * 4
+               /* Power values in quarter dB
                 * for the lower xpd gain curve
                 * (0 dBm -> higher output power) */
                for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) {
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pwr_x0[c] = (val & 0xff);
-                       chan_pcal_info->pwr_x0[++c] = ((val >> 8) & 0xff);
+                       chan_pcal_info->pwr_x0[c] = (s8) (val & 0xff);
+                       chan_pcal_info->pwr_x0[++c] = (s8) ((val >> 8) & 0xff);
                }
 
                /* PCDAC steps
@@ -825,12 +1034,12 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
                chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f);
                chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f);
 
-               /* Power values in dBm * 4
+               /* Power values in quarter dB
                 * for the higher xpd gain curve
                 * (18 dBm -> lower output power) */
                AR5K_EEPROM_READ(offset++, val);
-               chan_pcal_info->pwr_x3[0] = (val & 0xff);
-               chan_pcal_info->pwr_x3[1] = ((val >> 8) & 0xff);
+               chan_pcal_info->pwr_x3[0] = (s8) (val & 0xff);
+               chan_pcal_info->pwr_x3[1] = (s8) ((val >> 8) & 0xff);
 
                AR5K_EEPROM_READ(offset++, val);
                chan_pcal_info->pwr_x3[2] = (val & 0xff);
@@ -843,24 +1052,36 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
                chan_pcal_info->pcdac_x3[2] = 63;
 
                if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) {
-                       chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0xff);
+                       chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0x3f);
 
                        /* Last xpd0 power level is also channel maximum */
                        gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3];
                } else {
                        chan_pcal_info->pcdac_x0[0] = 1;
-                       gen_chan_info[i].max_pwr = ((val >> 8) & 0xff);
+                       gen_chan_info[i].max_pwr = (s8) ((val >> 8) & 0xff);
                }
 
-               /* Recreate pcdac_x0 table for this channel using pcdac steps */
-               chan_pcal_info->pcdac_x0[1] += chan_pcal_info->pcdac_x0[0];
-               chan_pcal_info->pcdac_x0[2] += chan_pcal_info->pcdac_x0[1];
-               chan_pcal_info->pcdac_x0[3] += chan_pcal_info->pcdac_x0[2];
        }
 
-       return 0;
+       return ath5k_eeprom_convert_pcal_info_5112(ah, mode, gen_chan_info);
 }
 
+
+/*
+ * Read power calibration for RF2413 chips
+ *
+ * For RF2413 we have a Power to PDDAC table (Power Detector)
+ * instead of a PCDAC and 4 pd gain curves for each calibrated channel.
+ * Each curve has power on x axis in 0.5 db steps and PDDADC steps on y
+ * axis and looks like an exponential function like the RF5111 curve.
+ *
+ * To recreate the curves we read here the points and interpolate
+ * later. Note that in most cases only 2 (higher and lower) curves are
+ * used (like RF5112) but vendors have the oportunity to include all
+ * 4 curves on eeprom. The final curve (higher power) has an extra
+ * point for better accuracy like RF5112.
+ */
+
 /* For RF2413 power calibration data doesn't start on a fixed location and
  * if a mode is not supported, it's section is missing -not zeroed-.
  * So we need to calculate the starting offset for each section by using
@@ -890,13 +1111,15 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
        switch(mode) {
        case AR5K_EEPROM_MODE_11G:
                if (AR5K_EEPROM_HDR_11B(ee->ee_header))
-                       offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11B) +
-                                                       AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+                       offset += ath5k_pdgains_size_2413(ee,
+                                       AR5K_EEPROM_MODE_11B) +
+                                       AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
                /* fall through */
        case AR5K_EEPROM_MODE_11B:
                if (AR5K_EEPROM_HDR_11A(ee->ee_header))
-                       offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11A) +
-                                                       AR5K_EEPROM_N_5GHZ_CHAN / 2;
+                       offset += ath5k_pdgains_size_2413(ee,
+                                       AR5K_EEPROM_MODE_11A) +
+                                       AR5K_EEPROM_N_5GHZ_CHAN / 2;
                /* fall through */
        case AR5K_EEPROM_MODE_11A:
                break;
@@ -907,37 +1130,118 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
        return offset;
 }
 
-/* Read power calibration for RF2413 chips
- * For RF2413 we have a PDDAC table (Power Detector) instead
- * of a PCDAC and 4 pd gain curves for each calibrated channel.
- * Each curve has PDDAC steps on x axis and power on y axis and
- * looks like an exponential function. To recreate the curves
- * we read here the points and interpolate later. Note that
- * in most cases only higher and lower curves are used (like
- * RF5112) but vendors have the oportunity to include all 4
- * curves on eeprom. The final curve (higher power) has an extra
- * point for better accuracy like RF5112.
- */
+/* Convert RF2413 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode,
+                               struct ath5k_chan_pcal_info *chinfo)
+{
+       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+       struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+       u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
+       unsigned int pier, pdg, point;
+
+       /* Fill raw data for each calibration pier */
+       for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+
+               pcinfo = &chinfo[pier].rf2413_info;
+
+               /* Allocate pd_curves for this cal pier */
+               chinfo[pier].pd_curves =
+                               kcalloc(AR5K_EEPROM_N_PD_CURVES,
+                                       sizeof(struct ath5k_pdgain_info),
+                                       GFP_KERNEL);
+
+               if (!chinfo[pier].pd_curves)
+                       return -ENOMEM;
+
+               /* Fill pd_curves */
+               for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+
+                       u8 idx = pdgain_idx[pdg];
+                       struct ath5k_pdgain_info *pd =
+                                       &chinfo[pier].pd_curves[idx];
+
+                       /* One more point for the highest power
+                        * curve (lowest gain) */
+                       if (pdg == ee->ee_pd_gains[mode] - 1)
+                               pd->pd_points = AR5K_EEPROM_N_PD_POINTS;
+                       else
+                               pd->pd_points = AR5K_EEPROM_N_PD_POINTS - 1;
+
+                       /* Allocate pd points for this curve */
+                       pd->pd_step = kcalloc(pd->pd_points,
+                                       sizeof(u8), GFP_KERNEL);
+
+                       if (!pd->pd_step)
+                               return -ENOMEM;
+
+                       pd->pd_pwr = kcalloc(pd->pd_points,
+                                       sizeof(s16), GFP_KERNEL);
+
+                       if (!pd->pd_pwr)
+                               return -ENOMEM;
+
+                       /* Fill raw dataset
+                        * convert all pwr levels to
+                        * quarter dB for RF5112 combatibility */
+                       pd->pd_step[0] = pcinfo->pddac_i[pdg];
+                       pd->pd_pwr[0] = 4 * pcinfo->pwr_i[pdg];
+
+                       for (point = 1; point < pd->pd_points; point++) {
+
+                               pd->pd_pwr[point] = pd->pd_pwr[point - 1] +
+                                       2 * pcinfo->pwr[pdg][point - 1];
+
+                               pd->pd_step[point] = pd->pd_step[point - 1] +
+                                               pcinfo->pddac[pdg][point - 1];
+
+                       }
+
+                       /* Highest gain curve -> min power */
+                       if (pdg == 0)
+                               chinfo[pier].min_pwr = pd->pd_pwr[0];
+
+                       /* Lowest gain curve -> max power */
+                       if (pdg == ee->ee_pd_gains[mode] - 1)
+                               chinfo[pier].max_pwr =
+                                       pd->pd_pwr[pd->pd_points - 1];
+               }
+       }
+
+       return 0;
+}
+
+/* Parse EEPROM data */
 static int
 ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
 {
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
-       struct ath5k_chan_pcal_info_rf2413 *chan_pcal_info;
-       struct ath5k_chan_pcal_info *gen_chan_info;
-       unsigned int i, c;
+       struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+       struct ath5k_chan_pcal_info *chinfo;
+       u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
        u32 offset;
-       int ret;
+       int idx, i, ret;
        u16 val;
        u8 pd_gains = 0;
 
-       if (ee->ee_x_gain[mode] & 0x1) pd_gains++;
-       if ((ee->ee_x_gain[mode] >> 1) & 0x1) pd_gains++;
-       if ((ee->ee_x_gain[mode] >> 2) & 0x1) pd_gains++;
-       if ((ee->ee_x_gain[mode] >> 3) & 0x1) pd_gains++;
+       /* Count how many curves we have and
+        * identify them (which one of the 4
+        * available curves we have on each count).
+        * Curves are stored from higher to
+        * lower gain so we go backwards */
+       for (idx = AR5K_EEPROM_N_PD_CURVES - 1; idx >= 0; idx--) {
+               /* ee_x_gain[mode] is x gain mask */
+               if ((ee->ee_x_gain[mode] >> idx) & 0x1)
+                       pdgain_idx[pd_gains++] = idx;
+
+       }
        ee->ee_pd_gains[mode] = pd_gains;
 
+       if (pd_gains == 0)
+               return -EINVAL;
+
        offset = ath5k_cal_data_offset_2413(ee, mode);
-       ee->ee_n_piers[mode] = 0;
        switch (mode) {
        case AR5K_EEPROM_MODE_11A:
                if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
@@ -945,7 +1249,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
 
                ath5k_eeprom_init_11a_pcal_freq(ah, offset);
                offset += AR5K_EEPROM_N_5GHZ_CHAN / 2;
-               gen_chan_info = ee->ee_pwr_cal_a;
+               chinfo = ee->ee_pwr_cal_a;
                break;
        case AR5K_EEPROM_MODE_11B:
                if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
@@ -953,7 +1257,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
 
                ath5k_eeprom_init_11bg_2413(ah, mode, offset);
                offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
-               gen_chan_info = ee->ee_pwr_cal_b;
+               chinfo = ee->ee_pwr_cal_b;
                break;
        case AR5K_EEPROM_MODE_11G:
                if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
@@ -961,41 +1265,35 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
 
                ath5k_eeprom_init_11bg_2413(ah, mode, offset);
                offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
-               gen_chan_info = ee->ee_pwr_cal_g;
+               chinfo = ee->ee_pwr_cal_g;
                break;
        default:
                return -EINVAL;
        }
 
-       if (pd_gains == 0)
-               return 0;
-
        for (i = 0; i < ee->ee_n_piers[mode]; i++) {
-               chan_pcal_info = &gen_chan_info[i].rf2413_info;
+               pcinfo = &chinfo[i].rf2413_info;
 
                /*
                 * Read pwr_i, pddac_i and the first
                 * 2 pd points (pwr, pddac)
                 */
                AR5K_EEPROM_READ(offset++, val);
-               chan_pcal_info->pwr_i[0] = val & 0x1f;
-               chan_pcal_info->pddac_i[0] = (val >> 5) & 0x7f;
-               chan_pcal_info->pwr[0][0] =
-                                       (val >> 12) & 0xf;
+               pcinfo->pwr_i[0] = val & 0x1f;
+               pcinfo->pddac_i[0] = (val >> 5) & 0x7f;
+               pcinfo->pwr[0][0] = (val >> 12) & 0xf;
 
                AR5K_EEPROM_READ(offset++, val);
-               chan_pcal_info->pddac[0][0] = val & 0x3f;
-               chan_pcal_info->pwr[0][1] = (val >> 6) & 0xf;
-               chan_pcal_info->pddac[0][1] =
-                                       (val >> 10) & 0x3f;
+               pcinfo->pddac[0][0] = val & 0x3f;
+               pcinfo->pwr[0][1] = (val >> 6) & 0xf;
+               pcinfo->pddac[0][1] = (val >> 10) & 0x3f;
 
                AR5K_EEPROM_READ(offset++, val);
-               chan_pcal_info->pwr[0][2] = val & 0xf;
-               chan_pcal_info->pddac[0][2] =
-                                       (val >> 4) & 0x3f;
+               pcinfo->pwr[0][2] = val & 0xf;
+               pcinfo->pddac[0][2] = (val >> 4) & 0x3f;
 
-               chan_pcal_info->pwr[0][3] = 0;
-               chan_pcal_info->pddac[0][3] = 0;
+               pcinfo->pwr[0][3] = 0;
+               pcinfo->pddac[0][3] = 0;
 
                if (pd_gains > 1) {
                        /*
@@ -1003,44 +1301,36 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
                         * so it only has 2 pd points.
                         * Continue wih pd gain 1.
                         */
-                       chan_pcal_info->pwr_i[1] = (val >> 10) & 0x1f;
+                       pcinfo->pwr_i[1] = (val >> 10) & 0x1f;
 
-                       chan_pcal_info->pddac_i[1] = (val >> 15) & 0x1;
+                       pcinfo->pddac_i[1] = (val >> 15) & 0x1;
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pddac_i[1] |= (val & 0x3F) << 1;
+                       pcinfo->pddac_i[1] |= (val & 0x3F) << 1;
 
-                       chan_pcal_info->pwr[1][0] = (val >> 6) & 0xf;
-                       chan_pcal_info->pddac[1][0] =
-                                               (val >> 10) & 0x3f;
+                       pcinfo->pwr[1][0] = (val >> 6) & 0xf;
+                       pcinfo->pddac[1][0] = (val >> 10) & 0x3f;
 
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pwr[1][1] = val & 0xf;
-                       chan_pcal_info->pddac[1][1] =
-                                               (val >> 4) & 0x3f;
-                       chan_pcal_info->pwr[1][2] =
-                                               (val >> 10) & 0xf;
-
-                       chan_pcal_info->pddac[1][2] =
-                                               (val >> 14) & 0x3;
+                       pcinfo->pwr[1][1] = val & 0xf;
+                       pcinfo->pddac[1][1] = (val >> 4) & 0x3f;
+                       pcinfo->pwr[1][2] = (val >> 10) & 0xf;
+
+                       pcinfo->pddac[1][2] = (val >> 14) & 0x3;
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pddac[1][2] |=
-                                               (val & 0xF) << 2;
+                       pcinfo->pddac[1][2] |= (val & 0xF) << 2;
 
-                       chan_pcal_info->pwr[1][3] = 0;
-                       chan_pcal_info->pddac[1][3] = 0;
+                       pcinfo->pwr[1][3] = 0;
+                       pcinfo->pddac[1][3] = 0;
                } else if (pd_gains == 1) {
                        /*
                         * Pd gain 0 is the last one so
                         * read the extra point.
                         */
-                       chan_pcal_info->pwr[0][3] =
-                                               (val >> 10) & 0xf;
+                       pcinfo->pwr[0][3] = (val >> 10) & 0xf;
 
-                       chan_pcal_info->pddac[0][3] =
-                                               (val >> 14) & 0x3;
+                       pcinfo->pddac[0][3] = (val >> 14) & 0x3;
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pddac[0][3] |=
-                                               (val & 0xF) << 2;
+                       pcinfo->pddac[0][3] |= (val & 0xF) << 2;
                }
 
                /*
@@ -1048,105 +1338,65 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
                 * as above.
                 */
                if (pd_gains > 2) {
-                       chan_pcal_info->pwr_i[2] = (val >> 4) & 0x1f;
-                       chan_pcal_info->pddac_i[2] = (val >> 9) & 0x7f;
+                       pcinfo->pwr_i[2] = (val >> 4) & 0x1f;
+                       pcinfo->pddac_i[2] = (val >> 9) & 0x7f;
 
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pwr[2][0] =
-                                               (val >> 0) & 0xf;
-                       chan_pcal_info->pddac[2][0] =
-                                               (val >> 4) & 0x3f;
-                       chan_pcal_info->pwr[2][1] =
-                                               (val >> 10) & 0xf;
-
-                       chan_pcal_info->pddac[2][1] =
-                                               (val >> 14) & 0x3;
+                       pcinfo->pwr[2][0] = (val >> 0) & 0xf;
+                       pcinfo->pddac[2][0] = (val >> 4) & 0x3f;
+                       pcinfo->pwr[2][1] = (val >> 10) & 0xf;
+
+                       pcinfo->pddac[2][1] = (val >> 14) & 0x3;
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pddac[2][1] |=
-                                               (val & 0xF) << 2;
+                       pcinfo->pddac[2][1] |= (val & 0xF) << 2;
 
-                       chan_pcal_info->pwr[2][2] =
-                                               (val >> 4) & 0xf;
-                       chan_pcal_info->pddac[2][2] =
-                                               (val >> 8) & 0x3f;
+                       pcinfo->pwr[2][2] = (val >> 4) & 0xf;
+                       pcinfo->pddac[2][2] = (val >> 8) & 0x3f;
 
-                       chan_pcal_info->pwr[2][3] = 0;
-                       chan_pcal_info->pddac[2][3] = 0;
+                       pcinfo->pwr[2][3] = 0;
+                       pcinfo->pddac[2][3] = 0;
                } else if (pd_gains == 2) {
-                       chan_pcal_info->pwr[1][3] =
-                                               (val >> 4) & 0xf;
-                       chan_pcal_info->pddac[1][3] =
-                                               (val >> 8) & 0x3f;
+                       pcinfo->pwr[1][3] = (val >> 4) & 0xf;
+                       pcinfo->pddac[1][3] = (val >> 8) & 0x3f;
                }
 
                if (pd_gains > 3) {
-                       chan_pcal_info->pwr_i[3] = (val >> 14) & 0x3;
+                       pcinfo->pwr_i[3] = (val >> 14) & 0x3;
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pwr_i[3] |= ((val >> 0) & 0x7) << 2;
+                       pcinfo->pwr_i[3] |= ((val >> 0) & 0x7) << 2;
 
-                       chan_pcal_info->pddac_i[3] = (val >> 3) & 0x7f;
-                       chan_pcal_info->pwr[3][0] =
-                                               (val >> 10) & 0xf;
-                       chan_pcal_info->pddac[3][0] =
-                                               (val >> 14) & 0x3;
+                       pcinfo->pddac_i[3] = (val >> 3) & 0x7f;
+                       pcinfo->pwr[3][0] = (val >> 10) & 0xf;
+                       pcinfo->pddac[3][0] = (val >> 14) & 0x3;
 
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pddac[3][0] |=
-                                               (val & 0xF) << 2;
-                       chan_pcal_info->pwr[3][1] =
-                                               (val >> 4) & 0xf;
-                       chan_pcal_info->pddac[3][1] =
-                                               (val >> 8) & 0x3f;
-
-                       chan_pcal_info->pwr[3][2] =
-                                               (val >> 14) & 0x3;
+                       pcinfo->pddac[3][0] |= (val & 0xF) << 2;
+                       pcinfo->pwr[3][1] = (val >> 4) & 0xf;
+                       pcinfo->pddac[3][1] = (val >> 8) & 0x3f;
+
+                       pcinfo->pwr[3][2] = (val >> 14) & 0x3;
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pwr[3][2] |=
-                                               ((val >> 0) & 0x3) << 2;
+                       pcinfo->pwr[3][2] |= ((val >> 0) & 0x3) << 2;
 
-                       chan_pcal_info->pddac[3][2] =
-                                               (val >> 2) & 0x3f;
-                       chan_pcal_info->pwr[3][3] =
-                                               (val >> 8) & 0xf;
+                       pcinfo->pddac[3][2] = (val >> 2) & 0x3f;
+                       pcinfo->pwr[3][3] = (val >> 8) & 0xf;
 
-                       chan_pcal_info->pddac[3][3] =
-                                               (val >> 12) & 0xF;
+                       pcinfo->pddac[3][3] = (val >> 12) & 0xF;
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pddac[3][3] |=
-                                               ((val >> 0) & 0x3) << 4;
+                       pcinfo->pddac[3][3] |= ((val >> 0) & 0x3) << 4;
                } else if (pd_gains == 3) {
-                       chan_pcal_info->pwr[2][3] =
-                                               (val >> 14) & 0x3;
+                       pcinfo->pwr[2][3] = (val >> 14) & 0x3;
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pwr[2][3] |=
-                                               ((val >> 0) & 0x3) << 2;
-
-                       chan_pcal_info->pddac[2][3] =
-                                               (val >> 2) & 0x3f;
-               }
+                       pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2;
 
-               for (c = 0; c < pd_gains; c++) {
-                       /* Recreate pwr table for this channel using pwr steps */
-                       chan_pcal_info->pwr[c][0] += chan_pcal_info->pwr_i[c] * 2;
-                       chan_pcal_info->pwr[c][1] += chan_pcal_info->pwr[c][0];
-                       chan_pcal_info->pwr[c][2] += chan_pcal_info->pwr[c][1];
-                       chan_pcal_info->pwr[c][3] += chan_pcal_info->pwr[c][2];
-                       if (chan_pcal_info->pwr[c][3] == chan_pcal_info->pwr[c][2])
-                               chan_pcal_info->pwr[c][3] = 0;
-
-                       /* Recreate pddac table for this channel using pddac steps */
-                       chan_pcal_info->pddac[c][0] += chan_pcal_info->pddac_i[c];
-                       chan_pcal_info->pddac[c][1] += chan_pcal_info->pddac[c][0];
-                       chan_pcal_info->pddac[c][2] += chan_pcal_info->pddac[c][1];
-                       chan_pcal_info->pddac[c][3] += chan_pcal_info->pddac[c][2];
-                       if (chan_pcal_info->pddac[c][3] == chan_pcal_info->pddac[c][2])
-                               chan_pcal_info->pddac[c][3] = 0;
+                       pcinfo->pddac[2][3] = (val >> 2) & 0x3f;
                }
        }
 
-       return 0;
+       return ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo);
 }
 
+
 /*
  * Read per rate target power (this is the maximum tx power
  * supported by the card). This info is used when setting
@@ -1154,11 +1404,12 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
  *
  * This also works for v5 EEPROMs.
  */
-static int ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
+static int
+ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
 {
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
        struct ath5k_rate_pcal_info *rate_pcal_info;
-       u16 *rate_target_pwr_num;
+       u8 *rate_target_pwr_num;
        u32 offset;
        u16 val;
        int ret, i;
@@ -1264,7 +1515,9 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
        else
                read_pcal = ath5k_eeprom_read_pcal_info_5111;
 
-       for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) {
+
+       for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G;
+       mode++) {
                err = read_pcal(ah, mode);
                if (err)
                        return err;
@@ -1277,6 +1530,62 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
        return 0;
 }
 
+static int
+ath5k_eeprom_free_pcal_info(struct ath5k_hw *ah, int mode)
+{
+       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+       struct ath5k_chan_pcal_info *chinfo;
+       u8 pier, pdg;
+
+       switch (mode) {
+       case AR5K_EEPROM_MODE_11A:
+               if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+                       return 0;
+               chinfo = ee->ee_pwr_cal_a;
+               break;
+       case AR5K_EEPROM_MODE_11B:
+               if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
+                       return 0;
+               chinfo = ee->ee_pwr_cal_b;
+               break;
+       case AR5K_EEPROM_MODE_11G:
+               if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+                       return 0;
+               chinfo = ee->ee_pwr_cal_g;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
+               if (!chinfo[pier].pd_curves)
+                       continue;
+
+               for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
+                       struct ath5k_pdgain_info *pd =
+                                       &chinfo[pier].pd_curves[pdg];
+
+                       if (pd != NULL) {
+                               kfree(pd->pd_step);
+                               kfree(pd->pd_pwr);
+                       }
+               }
+
+               kfree(chinfo[pier].pd_curves);
+       }
+
+       return 0;
+}
+
+void
+ath5k_eeprom_detach(struct ath5k_hw *ah)
+{
+       u8 mode;
+
+       for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++)
+               ath5k_eeprom_free_pcal_info(ah, mode);
+}
+
 /* Read conformance test limits used for regulatory control */
 static int
 ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah)
@@ -1457,3 +1766,4 @@ bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah)
        else
                return false;
 }
+
index 1deebc0257d40d63d44346d5dbb19b31cbb518af..b0c0606dea0be78b785376390f6f98b504a54110 100644 (file)
 #define AR5K_EEPROM_N_5GHZ_CHAN                10
 #define AR5K_EEPROM_N_2GHZ_CHAN                3
 #define AR5K_EEPROM_N_2GHZ_CHAN_2413   4
+#define        AR5K_EEPROM_N_2GHZ_CHAN_MAX     4
 #define AR5K_EEPROM_MAX_CHAN           10
 #define AR5K_EEPROM_N_PWR_POINTS_5111  11
 #define AR5K_EEPROM_N_PCDAC            11
 #define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10)
 #define AR5K_EEPROM_N_CTLS(_v)         AR5K_EEPROM_OFF(_v, 16, 32)
 #define AR5K_EEPROM_MAX_CTLS           32
-#define AR5K_EEPROM_N_XPD_PER_CHANNEL  4
+#define AR5K_EEPROM_N_PD_CURVES                4
 #define AR5K_EEPROM_N_XPD0_POINTS      4
 #define AR5K_EEPROM_N_XPD3_POINTS      3
 #define AR5K_EEPROM_N_PD_GAINS         4
@@ -232,7 +233,7 @@ enum ath5k_ctl_mode {
        AR5K_CTL_11B = 1,
        AR5K_CTL_11G = 2,
        AR5K_CTL_TURBO = 3,
-       AR5K_CTL_108G = 4,
+       AR5K_CTL_TURBOG = 4,
        AR5K_CTL_2GHT20 = 5,
        AR5K_CTL_5GHT20 = 6,
        AR5K_CTL_2GHT40 = 7,
@@ -240,65 +241,114 @@ enum ath5k_ctl_mode {
        AR5K_CTL_MODE_M = 15,
 };
 
+/* Default CTL ids for the 3 main reg domains.
+ * Atheros only uses these by default but vendors
+ * can have up to 32 different CTLs for different
+ * scenarios. Note that theese values are ORed with
+ * the mode id (above) so we can have up to 24 CTL
+ * datasets out of these 3 main regdomains. That leaves
+ * 8 ids that can be used by vendors and since 0x20 is
+ * missing from HAL sources i guess this is the set of
+ * custom CTLs vendors can use. */
+#define        AR5K_CTL_FCC    0x10
+#define        AR5K_CTL_CUSTOM 0x20
+#define        AR5K_CTL_ETSI   0x30
+#define        AR5K_CTL_MKK    0x40
+
+/* Indicates a CTL with only mode set and
+ * no reg domain mapping, such CTLs are used
+ * for world roaming domains or simply when
+ * a reg domain is not set */
+#define        AR5K_CTL_NO_REGDOMAIN   0xf0
+
+/* Indicates an empty (invalid) CTL */
+#define AR5K_CTL_NO_CTL                0xff
+
 /* Per channel calibration data, used for power table setup */
 struct ath5k_chan_pcal_info_rf5111 {
        /* Power levels in half dbm units
         * for one power curve. */
-       u8              pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
+       u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
        /* PCDAC table steps
         * for the above values */
-       u8              pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
+       u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
        /* Starting PCDAC step */
-       u8              pcdac_min;
+       u8 pcdac_min;
        /* Final PCDAC step */
-       u8              pcdac_max;
+       u8 pcdac_max;
 };
 
 struct ath5k_chan_pcal_info_rf5112 {
        /* Power levels in quarter dBm units
         * for lower (0) and higher (3)
-        * level curves */
-       s8              pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
-       s8              pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
+        * level curves in 0.25dB units */
+       s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
+       s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
        /* PCDAC table steps
         * for the above values */
-       u8      pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
-       u8      pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
+       u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
+       u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
 };
 
 struct ath5k_chan_pcal_info_rf2413 {
        /* Starting pwr/pddac values */
-       s8              pwr_i[AR5K_EEPROM_N_PD_GAINS];
-       u8      pddac_i[AR5K_EEPROM_N_PD_GAINS];
-       /* (pwr,pddac) points */
-       s8              pwr[AR5K_EEPROM_N_PD_GAINS]
-                               [AR5K_EEPROM_N_PD_POINTS];
-       u8      pddac[AR5K_EEPROM_N_PD_GAINS]
-                               [AR5K_EEPROM_N_PD_POINTS];
+       s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
+       u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
+       /* (pwr,pddac) points
+        * power levels in 0.5dB units */
+       s8 pwr[AR5K_EEPROM_N_PD_GAINS]
+               [AR5K_EEPROM_N_PD_POINTS];
+       u8 pddac[AR5K_EEPROM_N_PD_GAINS]
+               [AR5K_EEPROM_N_PD_POINTS];
+};
+
+enum ath5k_powertable_type {
+       AR5K_PWRTABLE_PWR_TO_PCDAC = 0,
+       AR5K_PWRTABLE_LINEAR_PCDAC = 1,
+       AR5K_PWRTABLE_PWR_TO_PDADC = 2,
+};
+
+struct ath5k_pdgain_info {
+       u8 pd_points;
+       u8 *pd_step;
+       /* Power values are in
+        * 0.25dB units */
+       s16 *pd_pwr;
 };
 
 struct ath5k_chan_pcal_info {
        /* Frequency */
        u16     freq;
-       /* Max available power */
-       s8              max_pwr;
+       /* Tx power boundaries */
+       s16     max_pwr;
+       s16     min_pwr;
        union {
                struct ath5k_chan_pcal_info_rf5111 rf5111_info;
                struct ath5k_chan_pcal_info_rf5112 rf5112_info;
                struct ath5k_chan_pcal_info_rf2413 rf2413_info;
        };
+       /* Raw values used by phy code
+        * Curves are stored in order from lower
+        * gain to higher gain (max txpower -> min txpower) */
+       struct ath5k_pdgain_info *pd_curves;
 };
 
-/* Per rate calibration data for each mode, used for power table setup */
+/* Per rate calibration data for each mode,
+ * used for rate power table setup.
+ * Note: Values in 0.5dB units */
 struct ath5k_rate_pcal_info {
        u16     freq; /* Frequency */
-       /* Power level for 6-24Mbit/s rates */
+       /* Power level for 6-24Mbit/s rates or
+        * 1Mb rate */
        u16     target_power_6to24;
-       /* Power level for 36Mbit rate */
+       /* Power level for 36Mbit rate or
+        * 2Mb rate */
        u16     target_power_36;
-       /* Power level for 48Mbit rate */
+       /* Power level for 48Mbit rate or
+        * 5.5Mbit rate */
        u16     target_power_48;
-       /* Power level for 54Mbit rate */
+       /* Power level for 54Mbit rate or
+        * 11Mbit rate */
        u16     target_power_54;
 };
 
@@ -330,12 +380,6 @@ struct ath5k_eeprom_info {
        u16     ee_cck_ofdm_power_delta;
        u16     ee_scaled_cck_delta;
 
-       /* Used for tx thermal adjustment (eeprom_init, rfregs) */
-       u16     ee_tx_clip;
-       u16     ee_pwd_84;
-       u16     ee_pwd_90;
-       u16     ee_gain_select;
-
        /* RF Calibration settings (reset, rfregs) */
        u16     ee_i_cal[AR5K_EEPROM_N_MODES];
        u16     ee_q_cal[AR5K_EEPROM_N_MODES];
@@ -363,23 +407,25 @@ struct ath5k_eeprom_info {
        /* Power calibration data */
        u16     ee_false_detect[AR5K_EEPROM_N_MODES];
 
-       /* Number of pd gain curves per mode (RF2413) */
-       u8 ee_pd_gains[AR5K_EEPROM_N_MODES];
+       /* Number of pd gain curves per mode */
+       u8      ee_pd_gains[AR5K_EEPROM_N_MODES];
+       /* Back mapping pdcurve number -> pdcurve index in pd->pd_curves */
+       u8      ee_pdc_to_idx[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PD_GAINS];
 
-       u8 ee_n_piers[AR5K_EEPROM_N_MODES];
+       u8      ee_n_piers[AR5K_EEPROM_N_MODES];
        struct ath5k_chan_pcal_info     ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN];
-       struct ath5k_chan_pcal_info     ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN];
-       struct ath5k_chan_pcal_info     ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN];
+       struct ath5k_chan_pcal_info     ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+       struct ath5k_chan_pcal_info     ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
 
        /* Per rate target power levels */
-       u16     ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
+       u     ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
        struct ath5k_rate_pcal_info     ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN];
-       struct ath5k_rate_pcal_info     ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN];
-       struct ath5k_rate_pcal_info     ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN];
+       struct ath5k_rate_pcal_info     ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
+       struct ath5k_rate_pcal_info     ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
 
        /* Conformance test limits (Unused) */
-       u16     ee_ctls;
-       u16     ee_ctl[AR5K_EEPROM_MAX_CTLS];
+       u     ee_ctls;
+       u     ee_ctl[AR5K_EEPROM_MAX_CTLS];
        struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS];
 
        /* Noise Floor Calibration settings */
index 44886434187bc702293f668533851b78e1ece1d0..61fb621ed20d2ad86fecc1f702815532274b332b 100644 (file)
@@ -1510,8 +1510,8 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel)
                                        rf2425_ini_mode_end, mode);
 
                        ath5k_hw_ini_registers(ah,
-                                       ARRAY_SIZE(rf2413_ini_common_end),
-                                       rf2413_ini_common_end, change_channel);
+                                       ARRAY_SIZE(rf2425_ini_common_end),
+                                       rf2425_ini_common_end, change_channel);
 
                        ath5k_hw_ini_registers(ah,
                                        ARRAY_SIZE(rf5112_ini_bbgain),
index 0686e12738b3b3daea2fe20568788bb88269f8ae..19555fb79c9b09a31c1f1dcc5fb366a7dc1b20b6 100644 (file)
@@ -65,6 +65,8 @@ static const struct pci_device_id ath5k_led_devices[] = {
        { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) },
        /* E-machines E510 (tuliom@gmail.com) */
        { ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0428), ATH_LED(3, 0) },
+       /* Acer Extensa 5620z (nekoreeve@gmail.com) */
+       { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0105), ATH_LED(3, 0) },
        { }
 };
 
index 81f5bebc48b13d2a40074664af148e4f162ef1d2..9e2faae5ae942c81d5a857d2b9c528138370cbfa 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
  * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
  * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
+ * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -183,7 +184,9 @@ static void ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah)
        if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE)
                return;
 
-       ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_max,
+       /* Send the packet with 2dB below max power as
+        * patent doc suggest */
+       ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_max_pwr - 4,
                        AR5K_PHY_PAPD_PROBE_TXPOWER) |
                        AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE);
 
@@ -1433,93 +1436,1120 @@ unsigned int ath5k_hw_get_def_antenna(struct ath5k_hw *ah)
        return false; /*XXX: What do we return for 5210 ?*/
 }
 
+
+/****************\
+* TX power setup *
+\****************/
+
+/*
+ * Helper functions
+ */
+
+/*
+ * Do linear interpolation between two given (x, y) points
+ */
+static s16
+ath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right,
+                                       s16 y_left, s16 y_right)
+{
+       s16 ratio, result;
+
+       /* Avoid divide by zero and skip interpolation
+        * if we have the same point */
+       if ((x_left == x_right) || (y_left == y_right))
+               return y_left;
+
+       /*
+        * Since we use ints and not fps, we need to scale up in
+        * order to get a sane ratio value (or else we 'll eg. get
+        * always 1 instead of 1.25, 1.75 etc). We scale up by 100
+        * to have some accuracy both for 0.5 and 0.25 steps.
+        */
+       ratio = ((100 * y_right - 100 * y_left)/(x_right - x_left));
+
+       /* Now scale down to be in range */
+       result = y_left + (ratio * (target - x_left) / 100);
+
+       return result;
+}
+
+/*
+ * Find vertical boundary (min pwr) for the linear PCDAC curve.
+ *
+ * Since we have the top of the curve and we draw the line below
+ * until we reach 1 (1 pcdac step) we need to know which point
+ * (x value) that is so that we don't go below y axis and have negative
+ * pcdac values when creating the curve, or fill the table with zeroes.
+ */
+static s16
+ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR,
+                               const s16 *pwrL, const s16 *pwrR)
+{
+       s8 tmp;
+       s16 min_pwrL, min_pwrR;
+       s16 pwr_i = pwrL[0];
+
+       do {
+               pwr_i--;
+               tmp = (s8) ath5k_get_interpolated_value(pwr_i,
+                                               pwrL[0], pwrL[1],
+                                               stepL[0], stepL[1]);
+
+       } while (tmp > 1);
+
+       min_pwrL = pwr_i;
+
+       pwr_i = pwrR[0];
+       do {
+               pwr_i--;
+               tmp = (s8) ath5k_get_interpolated_value(pwr_i,
+                                               pwrR[0], pwrR[1],
+                                               stepR[0], stepR[1]);
+
+       } while (tmp > 1);
+
+       min_pwrR = pwr_i;
+
+       /* Keep the right boundary so that it works for both curves */
+       return max(min_pwrL, min_pwrR);
+}
+
+/*
+ * Interpolate (pwr,vpd) points to create a Power to PDADC or a
+ * Power to PCDAC curve.
+ *
+ * Each curve has power on x axis (in 0.5dB units) and PCDAC/PDADC
+ * steps (offsets) on y axis. Power can go up to 31.5dB and max
+ * PCDAC/PDADC step for each curve is 64 but we can write more than
+ * one curves on hw so we can go up to 128 (which is the max step we
+ * can write on the final table).
+ *
+ * We write y values (PCDAC/PDADC steps) on hw.
+ */
+static void
+ath5k_create_power_curve(s16 pmin, s16 pmax,
+                       const s16 *pwr, const u8 *vpd,
+                       u8 num_points,
+                       u8 *vpd_table, u8 type)
+{
+       u8 idx[2] = { 0, 1 };
+       s16 pwr_i = 2*pmin;
+       int i;
+
+       if (num_points < 2)
+               return;
+
+       /* We want the whole line, so adjust boundaries
+        * to cover the entire power range. Note that
+        * power values are already 0.25dB so no need
+        * to multiply pwr_i by 2 */
+       if (type == AR5K_PWRTABLE_LINEAR_PCDAC) {
+               pwr_i = pmin;
+               pmin = 0;
+               pmax = 63;
+       }
+
+       /* Find surrounding turning points (TPs)
+        * and interpolate between them */
+       for (i = 0; (i <= (u16) (pmax - pmin)) &&
+       (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
+
+               /* We passed the right TP, move to the next set of TPs
+                * if we pass the last TP, extrapolate above using the last
+                * two TPs for ratio */
+               if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) {
+                       idx[0]++;
+                       idx[1]++;
+               }
+
+               vpd_table[i] = (u8) ath5k_get_interpolated_value(pwr_i,
+                                               pwr[idx[0]], pwr[idx[1]],
+                                               vpd[idx[0]], vpd[idx[1]]);
+
+               /* Increase by 0.5dB
+                * (0.25 dB units) */
+               pwr_i += 2;
+       }
+}
+
+/*
+ * Get the surrounding per-channel power calibration piers
+ * for a given frequency so that we can interpolate between
+ * them and come up with an apropriate dataset for our current
+ * channel.
+ */
+static void
+ath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah,
+                       struct ieee80211_channel *channel,
+                       struct ath5k_chan_pcal_info **pcinfo_l,
+                       struct ath5k_chan_pcal_info **pcinfo_r)
+{
+       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+       struct ath5k_chan_pcal_info *pcinfo;
+       u8 idx_l, idx_r;
+       u8 mode, max, i;
+       u32 target = channel->center_freq;
+
+       idx_l = 0;
+       idx_r = 0;
+
+       if (!(channel->hw_value & CHANNEL_OFDM)) {
+               pcinfo = ee->ee_pwr_cal_b;
+               mode = AR5K_EEPROM_MODE_11B;
+       } else if (channel->hw_value & CHANNEL_2GHZ) {
+               pcinfo = ee->ee_pwr_cal_g;
+               mode = AR5K_EEPROM_MODE_11G;
+       } else {
+               pcinfo = ee->ee_pwr_cal_a;
+               mode = AR5K_EEPROM_MODE_11A;
+       }
+       max = ee->ee_n_piers[mode] - 1;
+
+       /* Frequency is below our calibrated
+        * range. Use the lowest power curve
+        * we have */
+       if (target < pcinfo[0].freq) {
+               idx_l = idx_r = 0;
+               goto done;
+       }
+
+       /* Frequency is above our calibrated
+        * range. Use the highest power curve
+        * we have */
+       if (target > pcinfo[max].freq) {
+               idx_l = idx_r = max;
+               goto done;
+       }
+
+       /* Frequency is inside our calibrated
+        * channel range. Pick the surrounding
+        * calibration piers so that we can
+        * interpolate */
+       for (i = 0; i <= max; i++) {
+
+               /* Frequency matches one of our calibration
+                * piers, no need to interpolate, just use
+                * that calibration pier */
+               if (pcinfo[i].freq == target) {
+                       idx_l = idx_r = i;
+                       goto done;
+               }
+
+               /* We found a calibration pier that's above
+                * frequency, use this pier and the previous
+                * one to interpolate */
+               if (target < pcinfo[i].freq) {
+                       idx_r = i;
+                       idx_l = idx_r - 1;
+                       goto done;
+               }
+       }
+
+done:
+       *pcinfo_l = &pcinfo[idx_l];
+       *pcinfo_r = &pcinfo[idx_r];
+
+       return;
+}
+
+/*
+ * Get the surrounding per-rate power calibration data
+ * for a given frequency and interpolate between power
+ * values to set max target power supported by hw for
+ * each rate.
+ */
+static void
+ath5k_get_rate_pcal_data(struct ath5k_hw *ah,
+                       struct ieee80211_channel *channel,
+                       struct ath5k_rate_pcal_info *rates)
+{
+       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+       struct ath5k_rate_pcal_info *rpinfo;
+       u8 idx_l, idx_r;
+       u8 mode, max, i;
+       u32 target = channel->center_freq;
+
+       idx_l = 0;
+       idx_r = 0;
+
+       if (!(channel->hw_value & CHANNEL_OFDM)) {
+               rpinfo = ee->ee_rate_tpwr_b;
+               mode = AR5K_EEPROM_MODE_11B;
+       } else if (channel->hw_value & CHANNEL_2GHZ) {
+               rpinfo = ee->ee_rate_tpwr_g;
+               mode = AR5K_EEPROM_MODE_11G;
+       } else {
+               rpinfo = ee->ee_rate_tpwr_a;
+               mode = AR5K_EEPROM_MODE_11A;
+       }
+       max = ee->ee_rate_target_pwr_num[mode] - 1;
+
+       /* Get the surrounding calibration
+        * piers - same as above */
+       if (target < rpinfo[0].freq) {
+               idx_l = idx_r = 0;
+               goto done;
+       }
+
+       if (target > rpinfo[max].freq) {
+               idx_l = idx_r = max;
+               goto done;
+       }
+
+       for (i = 0; i <= max; i++) {
+
+               if (rpinfo[i].freq == target) {
+                       idx_l = idx_r = i;
+                       goto done;
+               }
+
+               if (target < rpinfo[i].freq) {
+                       idx_r = i;
+                       idx_l = idx_r - 1;
+                       goto done;
+               }
+       }
+
+done:
+       /* Now interpolate power value, based on the frequency */
+       rates->freq = target;
+
+       rates->target_power_6to24 =
+               ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+                                       rpinfo[idx_r].freq,
+                                       rpinfo[idx_l].target_power_6to24,
+                                       rpinfo[idx_r].target_power_6to24);
+
+       rates->target_power_36 =
+               ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+                                       rpinfo[idx_r].freq,
+                                       rpinfo[idx_l].target_power_36,
+                                       rpinfo[idx_r].target_power_36);
+
+       rates->target_power_48 =
+               ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+                                       rpinfo[idx_r].freq,
+                                       rpinfo[idx_l].target_power_48,
+                                       rpinfo[idx_r].target_power_48);
+
+       rates->target_power_54 =
+               ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
+                                       rpinfo[idx_r].freq,
+                                       rpinfo[idx_l].target_power_54,
+                                       rpinfo[idx_r].target_power_54);
+}
+
+/*
+ * Get the max edge power for this channel if
+ * we have such data from EEPROM's Conformance Test
+ * Limits (CTL), and limit max power if needed.
+ *
+ * FIXME: Only works for world regulatory domains
+ */
+static void
+ath5k_get_max_ctl_power(struct ath5k_hw *ah,
+                       struct ieee80211_channel *channel)
+{
+       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+       struct ath5k_edge_power *rep = ee->ee_ctl_pwr;
+       u8 *ctl_val = ee->ee_ctl;
+       s16 max_chan_pwr = ah->ah_txpower.txp_max_pwr / 4;
+       s16 edge_pwr = 0;
+       u8 rep_idx;
+       u8 i, ctl_mode;
+       u8 ctl_idx = 0xFF;
+       u32 target = channel->center_freq;
+
+       /* Find out a CTL for our mode that's not mapped
+        * on a specific reg domain.
+        *
+        * TODO: Map our current reg domain to one of the 3 available
+        * reg domain ids so that we can support more CTLs. */
+       switch (channel->hw_value & CHANNEL_MODES) {
+       case CHANNEL_A:
+               ctl_mode = AR5K_CTL_11A | AR5K_CTL_NO_REGDOMAIN;
+               break;
+       case CHANNEL_G:
+               ctl_mode = AR5K_CTL_11G | AR5K_CTL_NO_REGDOMAIN;
+               break;
+       case CHANNEL_B:
+               ctl_mode = AR5K_CTL_11B | AR5K_CTL_NO_REGDOMAIN;
+               break;
+       case CHANNEL_T:
+               ctl_mode = AR5K_CTL_TURBO | AR5K_CTL_NO_REGDOMAIN;
+               break;
+       case CHANNEL_TG:
+               ctl_mode = AR5K_CTL_TURBOG | AR5K_CTL_NO_REGDOMAIN;
+               break;
+       case CHANNEL_XR:
+               /* Fall through */
+       default:
+               return;
+       }
+
+       for (i = 0; i < ee->ee_ctls; i++) {
+               if (ctl_val[i] == ctl_mode) {
+                       ctl_idx = i;
+                       break;
+               }
+       }
+
+       /* If we have a CTL dataset available grab it and find the
+        * edge power for our frequency */
+       if (ctl_idx == 0xFF)
+               return;
+
+       /* Edge powers are sorted by frequency from lower
+        * to higher. Each CTL corresponds to 8 edge power
+        * measurements. */
+       rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES;
+
+       /* Don't do boundaries check because we
+        * might have more that one bands defined
+        * for this mode */
+
+       /* Get the edge power that's closer to our
+        * frequency */
+       for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) {
+               rep_idx += i;
+               if (target <= rep[rep_idx].freq)
+                       edge_pwr = (s16) rep[rep_idx].edge;
+       }
+
+       if (edge_pwr)
+               ah->ah_txpower.txp_max_pwr = 4*min(edge_pwr, max_chan_pwr);
+}
+
+
+/*
+ * Power to PCDAC table functions
+ */
+
 /*
- * TX power setup
+ * Fill Power to PCDAC table on RF5111
+ *
+ * No further processing is needed for RF5111, the only thing we have to
+ * do is fill the values below and above calibration range since eeprom data
+ * may not cover the entire PCDAC table.
  */
+static void
+ath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min,
+                                                       s16 *table_max)
+{
+       u8      *pcdac_out = ah->ah_txpower.txp_pd_table;
+       u8      *pcdac_tmp = ah->ah_txpower.tmpL[0];
+       u8      pcdac_0, pcdac_n, pcdac_i, pwr_idx, i;
+       s16     min_pwr, max_pwr;
+
+       /* Get table boundaries */
+       min_pwr = table_min[0];
+       pcdac_0 = pcdac_tmp[0];
+
+       max_pwr = table_max[0];
+       pcdac_n = pcdac_tmp[table_max[0] - table_min[0]];
+
+       /* Extrapolate below minimum using pcdac_0 */
+       pcdac_i = 0;
+       for (i = 0; i < min_pwr; i++)
+               pcdac_out[pcdac_i++] = pcdac_0;
+
+       /* Copy values from pcdac_tmp */
+       pwr_idx = min_pwr;
+       for (i = 0 ; pwr_idx <= max_pwr &&
+       pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) {
+               pcdac_out[pcdac_i++] = pcdac_tmp[i];
+               pwr_idx++;
+       }
+
+       /* Extrapolate above maximum */
+       while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE)
+               pcdac_out[pcdac_i++] = pcdac_n;
+
+}
 
 /*
- * Initialize the tx power table (not fully implemented)
+ * Combine available XPD Curves and fill Linear Power to PCDAC table
+ * on RF5112
+ *
+ * RFX112 can have up to 2 curves (one for low txpower range and one for
+ * higher txpower range). We need to put them both on pcdac_out and place
+ * them in the correct location. In case we only have one curve available
+ * just fit it on pcdac_out (it's supposed to cover the entire range of
+ * available pwr levels since it's always the higher power curve). Extrapolate
+ * below and above final table if needed.
  */
-static void ath5k_txpower_table(struct ath5k_hw *ah,
-               struct ieee80211_channel *channel, s16 max_power)
+static void
+ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
+                                               s16 *table_max, u8 pdcurves)
 {
-       unsigned int i, min, max, n;
-       u16 txpower, *rates;
-
-       rates = ah->ah_txpower.txp_rates;
-
-       txpower = AR5K_TUNE_DEFAULT_TXPOWER * 2;
-       if (max_power > txpower)
-               txpower = max_power > AR5K_TUNE_MAX_TXPOWER ?
-                   AR5K_TUNE_MAX_TXPOWER : max_power;
-
-       for (i = 0; i < AR5K_MAX_RATES; i++)
-               rates[i] = txpower;
-
-       /* XXX setup target powers by rate */
-
-       ah->ah_txpower.txp_min = rates[7];
-       ah->ah_txpower.txp_max = rates[0];
-       ah->ah_txpower.txp_ofdm = rates[0];
-
-       /* Calculate the power table */
-       n = ARRAY_SIZE(ah->ah_txpower.txp_pcdac);
-       min = AR5K_EEPROM_PCDAC_START;
-       max = AR5K_EEPROM_PCDAC_STOP;
-       for (i = 0; i < n; i += AR5K_EEPROM_PCDAC_STEP)
-               ah->ah_txpower.txp_pcdac[i] =
-#ifdef notyet
-               min + ((i * (max - min)) / n);
-#else
-               min;
+       u8      *pcdac_out = ah->ah_txpower.txp_pd_table;
+       u8      *pcdac_low_pwr;
+       u8      *pcdac_high_pwr;
+       u8      *pcdac_tmp;
+       u8      pwr;
+       s16     max_pwr_idx;
+       s16     min_pwr_idx;
+       s16     mid_pwr_idx = 0;
+       /* Edge flag turs on the 7nth bit on the PCDAC
+        * to delcare the higher power curve (force values
+        * to be greater than 64). If we only have one curve
+        * we don't need to set this, if we have 2 curves and
+        * fill the table backwards this can also be used to
+        * switch from higher power curve to lower power curve */
+       u8      edge_flag;
+       int     i;
+
+       /* When we have only one curve available
+        * that's the higher power curve. If we have
+        * two curves the first is the high power curve
+        * and the next is the low power curve. */
+       if (pdcurves > 1) {
+               pcdac_low_pwr = ah->ah_txpower.tmpL[1];
+               pcdac_high_pwr = ah->ah_txpower.tmpL[0];
+               mid_pwr_idx = table_max[1] - table_min[1] - 1;
+               max_pwr_idx = (table_max[0] - table_min[0]) / 2;
+
+               /* If table size goes beyond 31.5dB, keep the
+                * upper 31.5dB range when setting tx power.
+                * Note: 126 = 31.5 dB in quarter dB steps */
+               if (table_max[0] - table_min[1] > 126)
+                       min_pwr_idx = table_max[0] - 126;
+               else
+                       min_pwr_idx = table_min[1];
+
+               /* Since we fill table backwards
+                * start from high power curve */
+               pcdac_tmp = pcdac_high_pwr;
+
+               edge_flag = 0x40;
+#if 0
+               /* If both min and max power limits are in lower
+                * power curve's range, only use the low power curve.
+                * TODO: min/max levels are related to target
+                * power values requested from driver/user
+                * XXX: Is this really needed ? */
+               if (min_pwr < table_max[1] &&
+               max_pwr < table_max[1]) {
+                       edge_flag = 0;
+                       pcdac_tmp = pcdac_low_pwr;
+                       max_pwr_idx = (table_max[1] - table_min[1])/2;
+               }
 #endif
+       } else {
+               pcdac_low_pwr = ah->ah_txpower.tmpL[1]; /* Zeroed */
+               pcdac_high_pwr = ah->ah_txpower.tmpL[0];
+               min_pwr_idx = table_min[0];
+               max_pwr_idx = (table_max[0] - table_min[0]) / 2;
+               pcdac_tmp = pcdac_high_pwr;
+               edge_flag = 0;
+       }
+
+       /* This is used when setting tx power*/
+       ah->ah_txpower.txp_min_idx = min_pwr_idx/2;
+
+       /* Fill Power to PCDAC table backwards */
+       pwr = max_pwr_idx;
+       for (i = 63; i >= 0; i--) {
+               /* Entering lower power range, reset
+                * edge flag and set pcdac_tmp to lower
+                * power curve.*/
+               if (edge_flag == 0x40 &&
+               (2*pwr <= (table_max[1] - table_min[0]) || pwr == 0)) {
+                       edge_flag = 0x00;
+                       pcdac_tmp = pcdac_low_pwr;
+                       pwr = mid_pwr_idx/2;
+               }
+
+               /* Don't go below 1, extrapolate below if we have
+                * already swithced to the lower power curve -or
+                * we only have one curve and edge_flag is zero
+                * anyway */
+               if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) {
+                       while (i >= 0) {
+                               pcdac_out[i] = pcdac_out[i + 1];
+                               i--;
+                       }
+                       break;
+               }
+
+               pcdac_out[i] = pcdac_tmp[pwr] | edge_flag;
+
+               /* Extrapolate above if pcdac is greater than
+                * 126 -this can happen because we OR pcdac_out
+                * value with edge_flag on high power curve */
+               if (pcdac_out[i] > 126)
+                       pcdac_out[i] = 126;
+
+               /* Decrease by a 0.5dB step */
+               pwr--;
+       }
 }
 
+/* Write PCDAC values on hw */
+static void
+ath5k_setup_pcdac_table(struct ath5k_hw *ah)
+{
+       u8      *pcdac_out = ah->ah_txpower.txp_pd_table;
+       int     i;
+
+       /*
+        * Write TX power values
+        */
+       for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
+               ath5k_hw_reg_write(ah,
+                       (((pcdac_out[2*i + 0] << 8 | 0xff) & 0xffff) << 0) |
+                       (((pcdac_out[2*i + 1] << 8 | 0xff) & 0xffff) << 16),
+                       AR5K_PHY_PCDAC_TXPOWER(i));
+       }
+}
+
+
 /*
- * Set transmition power
+ * Power to PDADC table functions
  */
-int /*O.K. - txpower_table is unimplemented so this doesn't work*/
-ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
-               unsigned int txpower)
+
+/*
+ * Set the gain boundaries and create final Power to PDADC table
+ *
+ * We can have up to 4 pd curves, we need to do a simmilar process
+ * as we do for RF5112. This time we don't have an edge_flag but we
+ * set the gain boundaries on a separate register.
+ */
+static void
+ath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah,
+                       s16 *pwr_min, s16 *pwr_max, u8 pdcurves)
 {
-       bool tpc = ah->ah_txpower.txp_tpc;
-       unsigned int i;
+       u8 gain_boundaries[AR5K_EEPROM_N_PD_GAINS];
+       u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
+       u8 *pdadc_tmp;
+       s16 pdadc_0;
+       u8 pdadc_i, pdadc_n, pwr_step, pdg, max_idx, table_size;
+       u8 pd_gain_overlap;
+
+       /* Note: Register value is initialized on initvals
+        * there is no feedback from hw.
+        * XXX: What about pd_gain_overlap from EEPROM ? */
+       pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) &
+               AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP;
+
+       /* Create final PDADC table */
+       for (pdg = 0, pdadc_i = 0; pdg < pdcurves; pdg++) {
+               pdadc_tmp = ah->ah_txpower.tmpL[pdg];
+
+               if (pdg == pdcurves - 1)
+                       /* 2 dB boundary stretch for last
+                        * (higher power) curve */
+                       gain_boundaries[pdg] = pwr_max[pdg] + 4;
+               else
+                       /* Set gain boundary in the middle
+                        * between this curve and the next one */
+                       gain_boundaries[pdg] =
+                               (pwr_max[pdg] + pwr_min[pdg + 1]) / 2;
+
+               /* Sanity check in case our 2 db stretch got out of
+                * range. */
+               if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER)
+                       gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER;
+
+               /* For the first curve (lower power)
+                * start from 0 dB */
+               if (pdg == 0)
+                       pdadc_0 = 0;
+               else
+                       /* For the other curves use the gain overlap */
+                       pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) -
+                                                       pd_gain_overlap;
 
-       ATH5K_TRACE(ah->ah_sc);
-       if (txpower > AR5K_TUNE_MAX_TXPOWER) {
-               ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower);
-               return -EINVAL;
+               /* Force each power step to be at least 0.5 dB */
+               if ((pdadc_tmp[1] - pdadc_tmp[0]) > 1)
+                       pwr_step = pdadc_tmp[1] - pdadc_tmp[0];
+               else
+                       pwr_step = 1;
+
+               /* If pdadc_0 is negative, we need to extrapolate
+                * below this pdgain by a number of pwr_steps */
+               while ((pdadc_0 < 0) && (pdadc_i < 128)) {
+                       s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step;
+                       pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp;
+                       pdadc_0++;
+               }
+
+               /* Set last pwr level, using gain boundaries */
+               pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg];
+               /* Limit it to be inside pwr range */
+               table_size = pwr_max[pdg] - pwr_min[pdg];
+               max_idx = (pdadc_n < table_size) ? pdadc_n : table_size;
+
+               /* Fill pdadc_out table */
+               while (pdadc_0 < max_idx)
+                       pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++];
+
+               /* Need to extrapolate above this pdgain? */
+               if (pdadc_n <= max_idx)
+                       continue;
+
+               /* Force each power step to be at least 0.5 dB */
+               if ((pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]) > 1)
+                       pwr_step = pdadc_tmp[table_size - 1] -
+                                               pdadc_tmp[table_size - 2];
+               else
+                       pwr_step = 1;
+
+               /* Extrapolate above */
+               while ((pdadc_0 < (s16) pdadc_n) &&
+               (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2)) {
+                       s16 tmp = pdadc_tmp[table_size - 1] +
+                                       (pdadc_0 - max_idx) * pwr_step;
+                       pdadc_out[pdadc_i++] = (tmp > 127) ? 127 : (u8) tmp;
+                       pdadc_0++;
+               }
        }
 
+       while (pdg < AR5K_EEPROM_N_PD_GAINS) {
+               gain_boundaries[pdg] = gain_boundaries[pdg - 1];
+               pdg++;
+       }
+
+       while (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2) {
+               pdadc_out[pdadc_i] = pdadc_out[pdadc_i - 1];
+               pdadc_i++;
+       }
+
+       /* Set gain boundaries */
+       ath5k_hw_reg_write(ah,
+               AR5K_REG_SM(pd_gain_overlap,
+                       AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) |
+               AR5K_REG_SM(gain_boundaries[0],
+                       AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) |
+               AR5K_REG_SM(gain_boundaries[1],
+                       AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) |
+               AR5K_REG_SM(gain_boundaries[2],
+                       AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) |
+               AR5K_REG_SM(gain_boundaries[3],
+                       AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4),
+               AR5K_PHY_TPC_RG5);
+
+       /* Used for setting rate power table */
+       ah->ah_txpower.txp_min_idx = pwr_min[0];
+
+}
+
+/* Write PDADC values on hw */
+static void
+ath5k_setup_pwr_to_pdadc_table(struct ath5k_hw *ah,
+                       u8 pdcurves, u8 *pdg_to_idx)
+{
+       u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
+       u32 reg;
+       u8 i;
+
+       /* Select the right pdgain curves */
+
+       /* Clear current settings */
+       reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1);
+       reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 |
+               AR5K_PHY_TPC_RG1_PDGAIN_2 |
+               AR5K_PHY_TPC_RG1_PDGAIN_3 |
+               AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
+
        /*
-        * RF2413 for some reason can't
-        * transmit anything if we call
-        * this funtion, so we skip it
-        * until we fix txpower.
+        * Use pd_gains curve from eeprom
         *
-        * XXX: Assume same for RF2425
-        * to be safe.
+        * This overrides the default setting from initvals
+        * in case some vendors (e.g. Zcomax) don't use the default
+        * curves. If we don't honor their settings we 'll get a
+        * 5dB (1 * gain overlap ?) drop.
         */
-       if ((ah->ah_radio == AR5K_RF2413) || (ah->ah_radio == AR5K_RF2425))
-               return 0;
+       reg |= AR5K_REG_SM(pdcurves, AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
 
-       /* Reset TX power values */
-       memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
-       ah->ah_txpower.txp_tpc = tpc;
-
-       /* Initialize TX power table */
-       ath5k_txpower_table(ah, channel, txpower);
+       switch (pdcurves) {
+       case 3:
+               reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3);
+               /* Fall through */
+       case 2:
+               reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2);
+               /* Fall through */
+       case 1:
+               reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1);
+               break;
+       }
+       ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1);
 
        /*
         * Write TX power values
         */
        for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
                ath5k_hw_reg_write(ah,
-                       ((((ah->ah_txpower.txp_pcdac[(i << 1) + 1] << 8) | 0xff) & 0xffff) << 16) |
-                       (((ah->ah_txpower.txp_pcdac[(i << 1)    ] << 8) | 0xff) & 0xffff),
-                       AR5K_PHY_PCDAC_TXPOWER(i));
+                       ((pdadc_out[4*i + 0] & 0xff) << 0) |
+                       ((pdadc_out[4*i + 1] & 0xff) << 8) |
+                       ((pdadc_out[4*i + 2] & 0xff) << 16) |
+                       ((pdadc_out[4*i + 3] & 0xff) << 24),
+                       AR5K_PHY_PDADC_TXPOWER(i));
+       }
+}
+
+
+/*
+ * Common code for PCDAC/PDADC tables
+ */
+
+/*
+ * This is the main function that uses all of the above
+ * to set PCDAC/PDADC table on hw for the current channel.
+ * This table is used for tx power calibration on the basband,
+ * without it we get weird tx power levels and in some cases
+ * distorted spectral mask
+ */
+static int
+ath5k_setup_channel_powertable(struct ath5k_hw *ah,
+                       struct ieee80211_channel *channel,
+                       u8 ee_mode, u8 type)
+{
+       struct ath5k_pdgain_info *pdg_L, *pdg_R;
+       struct ath5k_chan_pcal_info *pcinfo_L;
+       struct ath5k_chan_pcal_info *pcinfo_R;
+       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+       u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode];
+       s16 table_min[AR5K_EEPROM_N_PD_GAINS];
+       s16 table_max[AR5K_EEPROM_N_PD_GAINS];
+       u8 *tmpL;
+       u8 *tmpR;
+       u32 target = channel->center_freq;
+       int pdg, i;
+
+       /* Get surounding freq piers for this channel */
+       ath5k_get_chan_pcal_surrounding_piers(ah, channel,
+                                               &pcinfo_L,
+                                               &pcinfo_R);
+
+       /* Loop over pd gain curves on
+        * surounding freq piers by index */
+       for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) {
+
+               /* Fill curves in reverse order
+                * from lower power (max gain)
+                * to higher power. Use curve -> idx
+                * backmaping we did on eeprom init */
+               u8 idx = pdg_curve_to_idx[pdg];
+
+               /* Grab the needed curves by index */
+               pdg_L = &pcinfo_L->pd_curves[idx];
+               pdg_R = &pcinfo_R->pd_curves[idx];
+
+               /* Initialize the temp tables */
+               tmpL = ah->ah_txpower.tmpL[pdg];
+               tmpR = ah->ah_txpower.tmpR[pdg];
+
+               /* Set curve's x boundaries and create
+                * curves so that they cover the same
+                * range (if we don't do that one table
+                * will have values on some range and the
+                * other one won't have any so interpolation
+                * will fail) */
+               table_min[pdg] = min(pdg_L->pd_pwr[0],
+                                       pdg_R->pd_pwr[0]) / 2;
+
+               table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
+                               pdg_R->pd_pwr[pdg_R->pd_points - 1]) / 2;
+
+               /* Now create the curves on surrounding channels
+                * and interpolate if needed to get the final
+                * curve for this gain on this channel */
+               switch (type) {
+               case AR5K_PWRTABLE_LINEAR_PCDAC:
+                       /* Override min/max so that we don't loose
+                        * accuracy (don't divide by 2) */
+                       table_min[pdg] = min(pdg_L->pd_pwr[0],
+                                               pdg_R->pd_pwr[0]);
+
+                       table_max[pdg] =
+                               max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
+                                       pdg_R->pd_pwr[pdg_R->pd_points - 1]);
+
+                       /* Override minimum so that we don't get
+                        * out of bounds while extrapolating
+                        * below. Don't do this when we have 2
+                        * curves and we are on the high power curve
+                        * because table_min is ok in this case */
+                       if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) {
+
+                               table_min[pdg] =
+                                       ath5k_get_linear_pcdac_min(pdg_L->pd_step,
+                                                               pdg_R->pd_step,
+                                                               pdg_L->pd_pwr,
+                                                               pdg_R->pd_pwr);
+
+                               /* Don't go too low because we will
+                                * miss the upper part of the curve.
+                                * Note: 126 = 31.5dB (max power supported)
+                                * in 0.25dB units */
+                               if (table_max[pdg] - table_min[pdg] > 126)
+                                       table_min[pdg] = table_max[pdg] - 126;
+                       }
+
+                       /* Fall through */
+               case AR5K_PWRTABLE_PWR_TO_PCDAC:
+               case AR5K_PWRTABLE_PWR_TO_PDADC:
+
+                       ath5k_create_power_curve(table_min[pdg],
+                                               table_max[pdg],
+                                               pdg_L->pd_pwr,
+                                               pdg_L->pd_step,
+                                               pdg_L->pd_points, tmpL, type);
+
+                       /* We are in a calibration
+                        * pier, no need to interpolate
+                        * between freq piers */
+                       if (pcinfo_L == pcinfo_R)
+                               continue;
+
+                       ath5k_create_power_curve(table_min[pdg],
+                                               table_max[pdg],
+                                               pdg_R->pd_pwr,
+                                               pdg_R->pd_step,
+                                               pdg_R->pd_points, tmpR, type);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               /* Interpolate between curves
+                * of surounding freq piers to
+                * get the final curve for this
+                * pd gain. Re-use tmpL for interpolation
+                * output */
+               for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) &&
+               (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
+                       tmpL[i] = (u8) ath5k_get_interpolated_value(target,
+                                                       (s16) pcinfo_L->freq,
+                                                       (s16) pcinfo_R->freq,
+                                                       (s16) tmpL[i],
+                                                       (s16) tmpR[i]);
+               }
        }
 
+       /* Now we have a set of curves for this
+        * channel on tmpL (x range is table_max - table_min
+        * and y values are tmpL[pdg][]) sorted in the same
+        * order as EEPROM (because we've used the backmaping).
+        * So for RF5112 it's from higher power to lower power
+        * and for RF2413 it's from lower power to higher power.
+        * For RF5111 we only have one curve. */
+
+       /* Fill min and max power levels for this
+        * channel by interpolating the values on
+        * surounding channels to complete the dataset */
+       ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target,
+                                       (s16) pcinfo_L->freq,
+                                       (s16) pcinfo_R->freq,
+                                       pcinfo_L->min_pwr, pcinfo_R->min_pwr);
+
+       ah->ah_txpower.txp_max_pwr = ath5k_get_interpolated_value(target,
+                                       (s16) pcinfo_L->freq,
+                                       (s16) pcinfo_R->freq,
+                                       pcinfo_L->max_pwr, pcinfo_R->max_pwr);
+
+       /* We are ready to go, fill PCDAC/PDADC
+        * table and write settings on hardware */
+       switch (type) {
+       case AR5K_PWRTABLE_LINEAR_PCDAC:
+               /* For RF5112 we can have one or two curves
+                * and each curve covers a certain power lvl
+                * range so we need to do some more processing */
+               ath5k_combine_linear_pcdac_curves(ah, table_min, table_max,
+                                               ee->ee_pd_gains[ee_mode]);
+
+               /* Set txp.offset so that we can
+                * match max power value with max
+                * table index */
+               ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2);
+
+               /* Write settings on hw */
+               ath5k_setup_pcdac_table(ah);
+               break;
+       case AR5K_PWRTABLE_PWR_TO_PCDAC:
+               /* We are done for RF5111 since it has only
+                * one curve, just fit the curve on the table */
+               ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max);
+
+               /* No rate powertable adjustment for RF5111 */
+               ah->ah_txpower.txp_min_idx = 0;
+               ah->ah_txpower.txp_offset = 0;
+
+               /* Write settings on hw */
+               ath5k_setup_pcdac_table(ah);
+               break;
+       case AR5K_PWRTABLE_PWR_TO_PDADC:
+               /* Set PDADC boundaries and fill
+                * final PDADC table */
+               ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max,
+                                               ee->ee_pd_gains[ee_mode]);
+
+               /* Write settings on hw */
+               ath5k_setup_pwr_to_pdadc_table(ah, pdg, pdg_curve_to_idx);
+
+               /* Set txp.offset, note that table_min
+                * can be negative */
+               ah->ah_txpower.txp_offset = table_min[0];
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+
+/*
+ * Per-rate tx power setting
+ *
+ * This is the code that sets the desired tx power (below
+ * maximum) on hw for each rate (we also have TPC that sets
+ * power per packet). We do that by providing an index on the
+ * PCDAC/PDADC table we set up.
+ */
+
+/*
+ * Set rate power table
+ *
+ * For now we only limit txpower based on maximum tx power
+ * supported by hw (what's inside rate_info). We need to limit
+ * this even more, based on regulatory domain etc.
+ *
+ * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps)
+ * and is indexed as follows:
+ * rates[0] - rates[7] -> OFDM rates
+ * rates[8] - rates[14] -> CCK rates
+ * rates[15] -> XR rates (they all have the same power)
+ */
+static void
+ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr,
+                       struct ath5k_rate_pcal_info *rate_info,
+                       u8 ee_mode)
+{
+       unsigned int i;
+       u16 *rates;
+
+       /* max_pwr is power level we got from driver/user in 0.5dB
+        * units, switch to 0.25dB units so we can compare */
+       max_pwr *= 2;
+       max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2;
+
+       /* apply rate limits */
+       rates = ah->ah_txpower.txp_rates_power_table;
+
+       /* OFDM rates 6 to 24Mb/s */
+       for (i = 0; i < 5; i++)
+               rates[i] = min(max_pwr, rate_info->target_power_6to24);
+
+       /* Rest OFDM rates */
+       rates[5] = min(rates[0], rate_info->target_power_36);
+       rates[6] = min(rates[0], rate_info->target_power_48);
+       rates[7] = min(rates[0], rate_info->target_power_54);
+
+       /* CCK rates */
+       /* 1L */
+       rates[8] = min(rates[0], rate_info->target_power_6to24);
+       /* 2L */
+       rates[9] = min(rates[0], rate_info->target_power_36);
+       /* 2S */
+       rates[10] = min(rates[0], rate_info->target_power_36);
+       /* 5L */
+       rates[11] = min(rates[0], rate_info->target_power_48);
+       /* 5S */
+       rates[12] = min(rates[0], rate_info->target_power_48);
+       /* 11L */
+       rates[13] = min(rates[0], rate_info->target_power_54);
+       /* 11S */
+       rates[14] = min(rates[0], rate_info->target_power_54);
+
+       /* XR rates */
+       rates[15] = min(rates[0], rate_info->target_power_6to24);
+
+       /* CCK rates have different peak to average ratio
+        * so we have to tweak their power so that gainf
+        * correction works ok. For this we use OFDM to
+        * CCK delta from eeprom */
+       if ((ee_mode == AR5K_EEPROM_MODE_11G) &&
+       (ah->ah_phy_revision < AR5K_SREV_PHY_5212A))
+               for (i = 8; i <= 15; i++)
+                       rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta;
+
+       ah->ah_txpower.txp_min_pwr = rates[7];
+       ah->ah_txpower.txp_max_pwr = rates[0];
+       ah->ah_txpower.txp_ofdm = rates[7];
+}
+
+
+/*
+ * Set transmition power
+ */
+int
+ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
+               u8 ee_mode, u8 txpower)
+{
+       struct ath5k_rate_pcal_info rate_info;
+       u8 type;
+       int ret;
+
+       ATH5K_TRACE(ah->ah_sc);
+       if (txpower > AR5K_TUNE_MAX_TXPOWER) {
+               ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower);
+               return -EINVAL;
+       }
+       if (txpower == 0)
+               txpower = AR5K_TUNE_DEFAULT_TXPOWER;
+
+       /* Reset TX power values */
+       memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
+       ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
+       ah->ah_txpower.txp_min_pwr = 0;
+       ah->ah_txpower.txp_max_pwr = AR5K_TUNE_MAX_TXPOWER;
+
+       /* Initialize TX power table */
+       switch (ah->ah_radio) {
+       case AR5K_RF5111:
+               type = AR5K_PWRTABLE_PWR_TO_PCDAC;
+               break;
+       case AR5K_RF5112:
+               type = AR5K_PWRTABLE_LINEAR_PCDAC;
+               break;
+       case AR5K_RF2413:
+       case AR5K_RF5413:
+       case AR5K_RF2316:
+       case AR5K_RF2317:
+       case AR5K_RF2425:
+               type = AR5K_PWRTABLE_PWR_TO_PDADC;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* FIXME: Only on channel/mode change */
+       ret = ath5k_setup_channel_powertable(ah, channel, ee_mode, type);
+       if (ret)
+               return ret;
+
+       /* Limit max power if we have a CTL available */
+       ath5k_get_max_ctl_power(ah, channel);
+
+       /* FIXME: Tx power limit for this regdomain
+        * XXX: Mac80211/CRDA will do that anyway ? */
+
+       /* FIXME: Antenna reduction stuff */
+
+       /* FIXME: Limit power on turbo modes */
+
+       /* FIXME: TPC scale reduction */
+
+       /* Get surounding channels for per-rate power table
+        * calibration */
+       ath5k_get_rate_pcal_data(ah, channel, &rate_info);
+
+       /* Setup rate power table */
+       ath5k_setup_rate_powertable(ah, txpower, &rate_info, ee_mode);
+
+       /* Write rate power table on hw */
        ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) |
                AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) |
                AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1);
@@ -1536,26 +2566,34 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
                AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) |
                AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4);
 
-       if (ah->ah_txpower.txp_tpc)
+       /* FIXME: TPC support */
+       if (ah->ah_txpower.txp_tpc) {
                ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE |
                        AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
-       else
+
+               ath5k_hw_reg_write(ah,
+                       AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) |
+                       AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) |
+                       AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
+                       AR5K_TPC);
+       } else {
                ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX |
                        AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
+       }
 
        return 0;
 }
 
-int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, unsigned int power)
+int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 mode, u8 txpower)
 {
        /*Just a try M.F.*/
        struct ieee80211_channel *channel = &ah->ah_current_channel;
 
        ATH5K_TRACE(ah->ah_sc);
        ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_TXPOWER,
-               "changing txpower to %d\n", power);
+               "changing txpower to %d\n", txpower);
 
-       return ath5k_hw_txpower(ah, channel, power);
+       return ath5k_hw_txpower(ah, channel, mode, txpower);
 }
 
 #undef _ATH5K_PHY
index 2dc008e102268baf945fe2e8b064c99c5e7e7233..7070d1543cdc54359107b72ffe7032dbe16cf327 100644 (file)
 
 /*===5212 Specific PCU registers===*/
 
+/*
+ * Transmit power control register
+ */
+#define AR5K_TPC                       0x80e8
+#define AR5K_TPC_ACK                   0x0000003f      /* ack frames */
+#define AR5K_TPC_ACK_S                 0
+#define AR5K_TPC_CTS                   0x00003f00      /* cts frames */
+#define AR5K_TPC_CTS_S                 8
+#define AR5K_TPC_CHIRP                 0x003f0000      /* chirp frames */
+#define AR5K_TPC_CHIRP_S               16
+#define AR5K_TPC_DOPPLER               0x0f000000      /* doppler chirp span */
+#define AR5K_TPC_DOPPLER_S             24
+
 /*
  * XR (eXtended Range) mode register
  */
 #define        AR5K_PHY_TPC_RG1                0xa258
 #define        AR5K_PHY_TPC_RG1_NUM_PD_GAIN    0x0000c000
 #define        AR5K_PHY_TPC_RG1_NUM_PD_GAIN_S  14
+#define AR5K_PHY_TPC_RG1_PDGAIN_1      0x00030000
+#define AR5K_PHY_TPC_RG1_PDGAIN_1_S    16
+#define AR5K_PHY_TPC_RG1_PDGAIN_2      0x000c0000
+#define AR5K_PHY_TPC_RG1_PDGAIN_2_S    18
+#define AR5K_PHY_TPC_RG1_PDGAIN_3      0x00300000
+#define AR5K_PHY_TPC_RG1_PDGAIN_3_S    20
 
 #define        AR5K_PHY_TPC_RG5                        0xa26C
 #define        AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP        0x0000000F
index 685dc213edae01e8772b7f2fad6d7d612e3546ca..7a17d31b2fd9f762a2bd8870639e7e0ed8b3723c 100644 (file)
@@ -664,29 +664,35 @@ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
                struct ieee80211_channel *channel, u8 *ant, u8 ee_mode)
 {
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+       s16 cck_ofdm_pwr_delta;
 
-       /* Set CCK to OFDM power delta */
-       if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
-               int16_t cck_ofdm_pwr_delta;
-
-               /* Adjust power delta for channel 14 */
-               if (channel->center_freq == 2484)
-                       cck_ofdm_pwr_delta =
-                               ((ee->ee_cck_ofdm_power_delta -
-                               ee->ee_scaled_cck_delta) * 2) / 10;
-               else
-                       cck_ofdm_pwr_delta =
-                               (ee->ee_cck_ofdm_power_delta * 2) / 10;
+       /* Adjust power delta for channel 14 */
+       if (channel->center_freq == 2484)
+               cck_ofdm_pwr_delta =
+                       ((ee->ee_cck_ofdm_power_delta -
+                       ee->ee_scaled_cck_delta) * 2) / 10;
+       else
+               cck_ofdm_pwr_delta =
+                       (ee->ee_cck_ofdm_power_delta * 2) / 10;
 
+       /* Set CCK to OFDM power delta on tx power
+        * adjustment register */
+       if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
                if (channel->hw_value == CHANNEL_G)
                        ath5k_hw_reg_write(ah,
-                       AR5K_REG_SM((ee->ee_cck_ofdm_power_delta * -1),
+                       AR5K_REG_SM((ee->ee_cck_ofdm_gain_delta * -1),
                                AR5K_PHY_TX_PWR_ADJ_CCK_GAIN_DELTA) |
                        AR5K_REG_SM((cck_ofdm_pwr_delta * -1),
                                AR5K_PHY_TX_PWR_ADJ_CCK_PCDAC_INDEX),
                                AR5K_PHY_TX_PWR_ADJ);
                else
                        ath5k_hw_reg_write(ah, 0, AR5K_PHY_TX_PWR_ADJ);
+       } else {
+               /* For older revs we scale power on sw during tx power
+                * setup */
+               ah->ah_txpower.txp_cck_ofdm_pwr_delta = cck_ofdm_pwr_delta;
+               ah->ah_txpower.txp_cck_ofdm_gainf_delta =
+                                               ee->ee_cck_ofdm_gain_delta;
        }
 
        /* Set antenna idle switch table */
@@ -994,7 +1000,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
                /*
                 * Set TX power (FIXME)
                 */
-               ret = ath5k_hw_txpower(ah, channel, AR5K_TUNE_DEFAULT_TXPOWER);
+               ret = ath5k_hw_txpower(ah, channel, ee_mode,
+                                       AR5K_TUNE_DEFAULT_TXPOWER);
                if (ret)
                        return ret;
 
index 00cc7bb01f2ee17cb28620df66d75124b116fe69..0e65c51ba1769d8466f925c40959f9bbdd71accc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
  * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
  *
index a39eb760cbb7e0979ef8730d3b1c260c1368a208..6c5e887d50d7d79c1a7d61e65d5d36995e4e3f32 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 7315761f6d74483917302b32d37d8538a0bfe4b0..08b4e7ed5ff0bb847ee887833fd62c3cb276ebdf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index b64be8e9a69029ba067b241de1a771f8a2060803..2689a08a2844e045d97b67d2fbcf1f658bfe3e37 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -295,13 +295,9 @@ struct ath_tx_control {
        enum ath9k_internal_frame_type frame_type;
 };
 
-struct ath_xmit_status {
-       int retries;
-       int flags;
 #define ATH_TX_ERROR        0x01
 #define ATH_TX_XRETRY       0x02
 #define ATH_TX_BAR          0x04
-};
 
 /* All RSSI values are noise floor adjusted */
 struct ath_tx_stat {
@@ -390,6 +386,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid
 
 struct ath_vif {
        int av_bslot;
+       __le64 tsf_adjust; /* TSF adjustment for staggered beacons */
        enum nl80211_iftype av_opmode;
        struct ath_buf *av_bcbuf;
        struct ath_tx_control av_btxctl;
@@ -406,7 +403,7 @@ struct ath_vif {
  * number of beacon intervals, the game's up.
  */
 #define BSTUCK_THRESH                  (9 * ATH_BCBUF)
-#define        ATH_BCBUF                       1
+#define        ATH_BCBUF                       4
 #define ATH_DEFAULT_BINTVAL            100 /* TU */
 #define ATH_DEFAULT_BMISS_LIMIT        10
 #define IEEE80211_MS_TO_TU(x)           (((x) * 1000) / 1024)
index 039c78136c50e2b2de7b884412bb8ac7701803f7..ec995730632ddeacb4f9e2a0b4eb7337abcad2f9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -70,7 +70,8 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
        ds = bf->bf_desc;
        flags = ATH9K_TXDESC_NOACK;
 
-       if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC &&
+       if (((sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
+            (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) &&
            (ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
                ds->ds_link = bf->bf_daddr; /* self-linked */
                flags |= ATH9K_TXDESC_VEOL;
@@ -153,6 +154,8 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
        bf->bf_mpdu = skb;
        if (skb == NULL)
                return NULL;
+       ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
+               avp->tsf_adjust;
 
        info = IEEE80211_SKB_CB(skb);
        if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
@@ -253,7 +256,6 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
 {
        struct ath_softc *sc = aphy->sc;
        struct ath_vif *avp;
-       struct ieee80211_hdr *hdr;
        struct ath_buf *bf;
        struct sk_buff *skb;
        __le64 tstamp;
@@ -316,42 +318,33 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
 
        tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
        sc->beacon.bc_tstamp = le64_to_cpu(tstamp);
-
-       /*
-        * Calculate a TSF adjustment factor required for
-        * staggered beacons.  Note that we assume the format
-        * of the beacon frame leaves the tstamp field immediately
-        * following the header.
-        */
+       /* Calculate a TSF adjustment factor required for staggered beacons. */
        if (avp->av_bslot > 0) {
                u64 tsfadjust;
-               __le64 val;
                int intval;
 
                intval = sc->hw->conf.beacon_int ?
                        sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL;
 
                /*
-                * The beacon interval is in TU's; the TSF in usecs.
-                * We figure out how many TU's to add to align the
-                * timestamp then convert to TSF units and handle
-                * byte swapping before writing it in the frame.
-                * The hardware will then add this each time a beacon
-                * frame is sent.  Note that we align vif's 1..N
-                * and leave vif 0 untouched.  This means vap 0
-                * has a timestamp in one beacon interval while the
-                * others get a timestamp aligned to the next interval.
+                * Calculate the TSF offset for this beacon slot, i.e., the
+                * number of usecs that need to be added to the timestamp field
+                * in Beacon and Probe Response frames. Beacon slot 0 is
+                * processed at the correct offset, so it does not require TSF
+                * adjustment. Other slots are adjusted to get the timestamp
+                * close to the TBTT for the BSS.
                 */
-               tsfadjust = (intval * (ATH_BCBUF - avp->av_bslot)) / ATH_BCBUF;
-               val = cpu_to_le64(tsfadjust << 10);     /* TU->TSF */
+               tsfadjust = intval * avp->av_bslot / ATH_BCBUF;
+               avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
 
                DPRINTF(sc, ATH_DBG_BEACON,
                        "stagger beacons, bslot %d intval %u tsfadjust %llu\n",
                        avp->av_bslot, intval, (unsigned long long)tsfadjust);
 
-               hdr = (struct ieee80211_hdr *)skb->data;
-               memcpy(&hdr[1], &val, sizeof(val));
-       }
+               ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
+                       avp->tsf_adjust;
+       } else
+               avp->tsf_adjust = cpu_to_le64(0);
 
        bf->bf_mpdu = skb;
        bf->bf_buf_addr = bf->bf_dmacontext =
@@ -447,8 +440,16 @@ void ath_beacon_tasklet(unsigned long data)
        tsf = ath9k_hw_gettsf64(ah);
        tsftu = TSF_TO_TU(tsf>>32, tsf);
        slot = ((tsftu % intval) * ATH_BCBUF) / intval;
-       vif = sc->beacon.bslot[(slot + 1) % ATH_BCBUF];
-       aphy = sc->beacon.bslot_aphy[(slot + 1) % ATH_BCBUF];
+       /*
+        * Reverse the slot order to get slot 0 on the TBTT offset that does
+        * not require TSF adjustment and other slots adding
+        * slot/ATH_BCBUF * beacon_int to timestamp. For example, with
+        * ATH_BCBUF = 4, we process beacon slots as follows: 3 2 1 0 3 2 1 ..
+        * and slot 0 is at correct offset to TBTT.
+        */
+       slot = ATH_BCBUF - slot - 1;
+       vif = sc->beacon.bslot[slot];
+       aphy = sc->beacon.bslot_aphy[slot];
 
        DPRINTF(sc, ATH_DBG_BEACON,
                "slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
@@ -728,6 +729,7 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
                        ath_beacon_config_ap(sc, &conf, avp);
                        break;
                case NL80211_IFTYPE_ADHOC:
+               case NL80211_IFTYPE_MESH_POINT:
                        ath_beacon_config_adhoc(sc, &conf, avp, vif);
                        break;
                case NL80211_IFTYPE_STATION:
index c9446fb6b15318b5b1548ceb4aca5075ce4eed89..e2d62e97131c2d3f5453bae6ccea9eaf5660e3ba 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 32589e0c501810f41de30340418f2e9215a834e3..1c74bd50700d68dc283b430c3194b509e5492337 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 82573cadb1abf34b28a2c603792474f9f020f0c9..fdf9528fa49b7282adff4acfdc6874a75fce513e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 065268b8568f218266e68e7ee1e5fe70d7264859..7b0e5419d2bcbdb50e6bae0be98a85c358511f1d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 183c949bcca1b44612e10236db2d6d093875c50a..ffc36b0361c79c6e400fe9b8f3bf4ed3a84cd30a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -342,8 +342,7 @@ static int ath9k_hw_4k_get_eeprom_rev(struct ath_hw *ah)
 static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah)
 {
 #define SIZE_EEPROM_4K (sizeof(struct ar5416_eeprom_4k) / sizeof(u16))
-       struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
-       u16 *eep_data;
+       u16 *eep_data = (u16 *)&ah->eeprom.map4k;
        int addr, eep_start_loc = 0;
 
        eep_start_loc = 64;
@@ -353,8 +352,6 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah)
                        "Reading from EEPROM, not flash\n");
        }
 
-       eep_data = (u16 *)eep;
-
        for (addr = 0; addr < SIZE_EEPROM_4K; addr++) {
                if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) {
                        DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
@@ -363,6 +360,7 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah)
                }
                eep_data++;
        }
+
        return true;
 #undef SIZE_EEPROM_4K
 }
@@ -379,16 +377,15 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
 
 
        if (!ath9k_hw_use_flash(ah)) {
-
                if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
                                         &magic)) {
-                       DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                       DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
                                "Reading Magic # failed\n");
                        return false;
                }
 
                DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
-                               "Read Magic = 0x%04X\n", magic);
+                       "Read Magic = 0x%04X\n", magic);
 
                if (magic != AR5416_EEPROM_MAGIC) {
                        magic2 = swab16(magic);
@@ -401,16 +398,9 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
                                        temp = swab16(*eepdata);
                                        *eepdata = temp;
                                        eepdata++;
-
-                                       DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
-                                               "0x%04X  ", *eepdata);
-
-                                       if (((addr + 1) % 6) == 0)
-                                               DPRINTF(ah->ah_sc,
-                                                       ATH_DBG_EEPROM, "\n");
                                }
                        } else {
-                               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                               DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
                                        "Invalid EEPROM Magic. "
                                        "endianness mismatch.\n");
                                return -EINVAL;
@@ -426,7 +416,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
        else
                el = ah->eeprom.map4k.baseEepHeader.length;
 
-       if (el > sizeof(struct ar5416_eeprom_def))
+       if (el > sizeof(struct ar5416_eeprom_4k))
                el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16);
        else
                el = el / sizeof(u16);
@@ -441,7 +431,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
                u16 word;
 
                DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
-                       "EEPROM Endianness is not native.. Changing \n");
+                       "EEPROM Endianness is not native.. Changing\n");
 
                word = swab16(eep->baseEepHeader.length);
                eep->baseEepHeader.length = word;
@@ -483,7 +473,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
 
        if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER ||
            ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
-               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+               DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
                        "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
                        sum, ah->eep_ops->get_eeprom_ver(ah));
                return -EINVAL;
@@ -1203,57 +1193,63 @@ static void ath9k_hw_4k_set_addac(struct ath_hw *ah,
        }
 }
 
-static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah,
-                                        struct ath9k_channel *chan)
+static void ath9k_hw_4k_set_gain(struct ath_hw *ah,
+                                struct modal_eep_4k_header *pModal,
+                                struct ar5416_eeprom_4k *eep,
+                                u8 txRxAttenLocal, int regChainOffset)
 {
-       struct modal_eep_4k_header *pModal;
-       struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
-       int regChainOffset;
-       u8 txRxAttenLocal;
-       u8 ob[5], db1[5], db2[5];
-       u8 ant_div_control1, ant_div_control2;
-       u32 regVal;
-
-
-       pModal = &eep->modalHeader;
-
-       txRxAttenLocal = 23;
-
-       REG_WRITE(ah, AR_PHY_SWITCH_COM,
-                 ah->eep_ops->get_eeprom_antenna_cfg(ah, chan));
-
-       regChainOffset = 0;
        REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset,
                  pModal->antCtrlChain[0]);
 
        REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset,
-                (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) &
-                ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
-                AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
-                SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
-                SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
+                 (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) &
+                  ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
+                    AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
+                 SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
+                 SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
 
        if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
-                       AR5416_EEP_MINOR_VER_3) {
+           AR5416_EEP_MINOR_VER_3) {
                txRxAttenLocal = pModal->txRxAttenCh[0];
+
                REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
-                       AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]);
+                             AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]);
                REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
-                       AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]);
+                             AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]);
                REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
-                       AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
-                       pModal->xatten2Margin[0]);
+                             AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
+                             pModal->xatten2Margin[0]);
                REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
-                       AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]);
+                             AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]);
        }
 
        REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset,
-                       AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
+                     AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
        REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset,
-                       AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
+                     AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
 
        if (AR_SREV_9285_11(ah))
                REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14));
+}
+
+static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
+                                        struct ath9k_channel *chan)
+{
+       struct modal_eep_4k_header *pModal;
+       struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
+       u8 txRxAttenLocal;
+       u8 ob[5], db1[5], db2[5];
+       u8 ant_div_control1, ant_div_control2;
+       u32 regVal;
+
+       pModal = &eep->modalHeader;
+       txRxAttenLocal = 23;
+
+       REG_WRITE(ah, AR_PHY_SWITCH_COM,
+                 ah->eep_ops->get_eeprom_antenna_cfg(ah, chan));
+
+       /* Single chain for 4K EEPROM*/
+       ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal, 0);
 
        /* Initialize Ant Diversity settings from EEPROM */
        if (pModal->version == 3) {
@@ -1295,9 +1291,6 @@ static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah,
                db2[4] = ((pModal->db2_234 >> 8) & 0xf);
 
        } else if (pModal->version == 1) {
-
-               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
-                       "EEPROM Model version is set to 1 \n");
                ob[0] = (pModal->ob_01 & 0xf);
                ob[1] = ob[2] = ob[3] = ob[4] = (pModal->ob_01 >> 4) & 0xf;
                db1[0] = (pModal->db1_01 & 0xf);
@@ -1385,8 +1378,6 @@ static bool ath9k_hw_4k_set_board_values(struct ath_hw *ah,
                                      AR_PHY_SETTLING_SWITCH,
                                      pModal->swSettleHt40);
        }
-
-       return true;
 }
 
 static u16 ath9k_hw_4k_get_eeprom_antenna_cfg(struct ath_hw *ah,
@@ -1464,16 +1455,13 @@ static int ath9k_hw_def_get_eeprom_rev(struct ath_hw *ah)
 static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah)
 {
 #define SIZE_EEPROM_DEF (sizeof(struct ar5416_eeprom_def) / sizeof(u16))
-       struct ar5416_eeprom_def *eep = &ah->eeprom.def;
-       u16 *eep_data;
+       u16 *eep_data = (u16 *)&ah->eeprom.def;
        int addr, ar5416_eep_start_loc = 0x100;
 
-       eep_data = (u16 *)eep;
-
        for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) {
                if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc,
                                         eep_data)) {
-                       DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                       DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
                                "Unable to read eeprom region\n");
                        return false;
                }
@@ -1492,17 +1480,14 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
        bool need_swap = false;
        int i, addr, size;
 
-       if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
-                                &magic)) {
-               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
-                       "Reading Magic # failed\n");
+       if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
+               DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Reading Magic # failed\n");
                return false;
        }
 
        if (!ath9k_hw_use_flash(ah)) {
-
                DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
-                               "Read Magic = 0x%04X\n", magic);
+                       "Read Magic = 0x%04X\n", magic);
 
                if (magic != AR5416_EEPROM_MAGIC) {
                        magic2 = swab16(magic);
@@ -1516,18 +1501,11 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
                                        temp = swab16(*eepdata);
                                        *eepdata = temp;
                                        eepdata++;
-
-                                       DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
-                                               "0x%04X  ", *eepdata);
-
-                                       if (((addr + 1) % 6) == 0)
-                                               DPRINTF(ah->ah_sc,
-                                                       ATH_DBG_EEPROM, "\n");
                                }
                        } else {
-                               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                               DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
                                        "Invalid EEPROM Magic. "
-                                       "endianness mismatch.\n");
+                                       "Endianness mismatch.\n");
                                return -EINVAL;
                        }
                }
@@ -1556,7 +1534,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
                u16 word;
 
                DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
-                       "EEPROM Endianness is not native.. Changing \n");
+                       "EEPROM Endianness is not native.. Changing.\n");
 
                word = swab16(eep->baseEepHeader.length);
                eep->baseEepHeader.length = word;
@@ -1602,7 +1580,7 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
 
        if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER ||
            ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
-               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+               DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
                        "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
                        sum, ah->eep_ops->get_eeprom_ver(ah));
                return -EINVAL;
@@ -1614,7 +1592,6 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
 static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
                                   enum eeprom_param param)
 {
-#define AR5416_VER_MASK (pBase->version & AR5416_EEP_VER_MINOR_MASK)
        struct ar5416_eeprom_def *eep = &ah->eeprom.def;
        struct modal_eep_header *pModal = eep->modalHeader;
        struct base_eep_header *pBase = &eep->baseEepHeader;
@@ -1681,21 +1658,73 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
        default:
                return 0;
        }
-#undef AR5416_VER_MASK
 }
 
-/* XXX: Clean me up, make me more legible */
-static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
+static void ath9k_hw_def_set_gain(struct ath_hw *ah,
+                                 struct modal_eep_header *pModal,
+                                 struct ar5416_eeprom_def *eep,
+                                 u8 txRxAttenLocal, int regChainOffset, int i)
+{
+       if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) {
+               txRxAttenLocal = pModal->txRxAttenCh[i];
+
+               if (AR_SREV_9280_10_OR_LATER(ah)) {
+                       REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+                             AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN,
+                             pModal->bswMargin[i]);
+                       REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+                             AR_PHY_GAIN_2GHZ_XATTEN1_DB,
+                             pModal->bswAtten[i]);
+                       REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+                             AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
+                             pModal->xatten2Margin[i]);
+                       REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+                             AR_PHY_GAIN_2GHZ_XATTEN2_DB,
+                             pModal->xatten2Db[i]);
+               } else {
+                       REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+                         (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
+                          ~AR_PHY_GAIN_2GHZ_BSW_MARGIN)
+                         | SM(pModal-> bswMargin[i],
+                              AR_PHY_GAIN_2GHZ_BSW_MARGIN));
+                       REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+                         (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
+                          ~AR_PHY_GAIN_2GHZ_BSW_ATTEN)
+                         | SM(pModal->bswAtten[i],
+                              AR_PHY_GAIN_2GHZ_BSW_ATTEN));
+               }
+       }
+
+       if (AR_SREV_9280_10_OR_LATER(ah)) {
+               REG_RMW_FIELD(ah,
+                     AR_PHY_RXGAIN + regChainOffset,
+                     AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
+               REG_RMW_FIELD(ah,
+                     AR_PHY_RXGAIN + regChainOffset,
+                     AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]);
+       } else {
+               REG_WRITE(ah,
+                         AR_PHY_RXGAIN + regChainOffset,
+                         (REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) &
+                          ~AR_PHY_RXGAIN_TXRX_ATTEN)
+                         | SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN));
+               REG_WRITE(ah,
+                         AR_PHY_GAIN_2GHZ + regChainOffset,
+                         (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
+                          ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) |
+                         SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN));
+       }
+}
+
+static void ath9k_hw_def_set_board_values(struct ath_hw *ah,
                                          struct ath9k_channel *chan)
 {
-#define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK)
        struct modal_eep_header *pModal;
        struct ar5416_eeprom_def *eep = &ah->eeprom.def;
        int i, regChainOffset;
        u8 txRxAttenLocal;
 
        pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
-
        txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44;
 
        REG_WRITE(ah, AR_PHY_SWITCH_COM,
@@ -1708,8 +1737,7 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
                }
 
                if (AR_SREV_5416_20_OR_LATER(ah) &&
-                   (ah->rxchainmask == 5 || ah->txchainmask == 5)
-                   && (i != 0))
+                   (ah->rxchainmask == 5 || ah->txchainmask == 5) && (i != 0))
                        regChainOffset = (i == 1) ? 0x2000 : 0x1000;
                else
                        regChainOffset = i * 0x1000;
@@ -1718,9 +1746,7 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
                          pModal->antCtrlChain[i]);
 
                REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset,
-                         (REG_READ(ah,
-                                   AR_PHY_TIMING_CTRL4(0) +
-                                   regChainOffset) &
+                         (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) &
                           ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
                             AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
                          SM(pModal->iqCalICh[i],
@@ -1728,87 +1754,9 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
                          SM(pModal->iqCalQCh[i],
                             AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
 
-               if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) {
-                       if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) {
-                               txRxAttenLocal = pModal->txRxAttenCh[i];
-                               if (AR_SREV_9280_10_OR_LATER(ah)) {
-                                       REG_RMW_FIELD(ah,
-                                               AR_PHY_GAIN_2GHZ +
-                                               regChainOffset,
-                                               AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN,
-                                               pModal->
-                                               bswMargin[i]);
-                                       REG_RMW_FIELD(ah,
-                                               AR_PHY_GAIN_2GHZ +
-                                               regChainOffset,
-                                               AR_PHY_GAIN_2GHZ_XATTEN1_DB,
-                                               pModal->
-                                               bswAtten[i]);
-                                       REG_RMW_FIELD(ah,
-                                               AR_PHY_GAIN_2GHZ +
-                                               regChainOffset,
-                                               AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
-                                               pModal->
-                                               xatten2Margin[i]);
-                                       REG_RMW_FIELD(ah,
-                                               AR_PHY_GAIN_2GHZ +
-                                               regChainOffset,
-                                               AR_PHY_GAIN_2GHZ_XATTEN2_DB,
-                                               pModal->
-                                               xatten2Db[i]);
-                               } else {
-                                       REG_WRITE(ah,
-                                                 AR_PHY_GAIN_2GHZ +
-                                                 regChainOffset,
-                                                 (REG_READ(ah,
-                                                           AR_PHY_GAIN_2GHZ +
-                                                           regChainOffset) &
-                                                  ~AR_PHY_GAIN_2GHZ_BSW_MARGIN)
-                                                 | SM(pModal->
-                                                 bswMargin[i],
-                                                 AR_PHY_GAIN_2GHZ_BSW_MARGIN));
-                                       REG_WRITE(ah,
-                                                 AR_PHY_GAIN_2GHZ +
-                                                 regChainOffset,
-                                                 (REG_READ(ah,
-                                                           AR_PHY_GAIN_2GHZ +
-                                                           regChainOffset) &
-                                                  ~AR_PHY_GAIN_2GHZ_BSW_ATTEN)
-                                                 | SM(pModal->bswAtten[i],
-                                                 AR_PHY_GAIN_2GHZ_BSW_ATTEN));
-                               }
-                       }
-                       if (AR_SREV_9280_10_OR_LATER(ah)) {
-                               REG_RMW_FIELD(ah,
-                                             AR_PHY_RXGAIN +
-                                             regChainOffset,
-                                             AR9280_PHY_RXGAIN_TXRX_ATTEN,
-                                             txRxAttenLocal);
-                               REG_RMW_FIELD(ah,
-                                             AR_PHY_RXGAIN +
-                                             regChainOffset,
-                                             AR9280_PHY_RXGAIN_TXRX_MARGIN,
-                                             pModal->rxTxMarginCh[i]);
-                       } else {
-                               REG_WRITE(ah,
-                                         AR_PHY_RXGAIN + regChainOffset,
-                                         (REG_READ(ah,
-                                                   AR_PHY_RXGAIN +
-                                                   regChainOffset) &
-                                          ~AR_PHY_RXGAIN_TXRX_ATTEN) |
-                                         SM(txRxAttenLocal,
-                                            AR_PHY_RXGAIN_TXRX_ATTEN));
-                               REG_WRITE(ah,
-                                         AR_PHY_GAIN_2GHZ +
-                                         regChainOffset,
-                                         (REG_READ(ah,
-                                                   AR_PHY_GAIN_2GHZ +
-                                                   regChainOffset) &
-                                          ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) |
-                                         SM(pModal->rxTxMarginCh[i],
-                                            AR_PHY_GAIN_2GHZ_RXTX_MARGIN));
-                       }
-               }
+               if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah))
+                       ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal,
+                                             regChainOffset, i);
        }
 
        if (AR_SREV_9280_10_OR_LATER(ah)) {
@@ -1855,8 +1803,6 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
                                          AR_AN_TOP2_LOCALBIAS,
                                          AR_AN_TOP2_LOCALBIAS_S,
                                          pModal->local_bias);
-               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "ForceXPAon: %d\n",
-                       pModal->force_xpaon);
                REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG,
                              pModal->force_xpaon);
        }
@@ -1882,6 +1828,7 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
 
        REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON,
                      pModal->txEndToRxOn);
+
        if (AR_SREV_9280_10_OR_LATER(ah)) {
                REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62,
                              pModal->thresh62);
@@ -1912,10 +1859,10 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
        }
 
        if (AR_SREV_9280_20_OR_LATER(ah) &&
-                       AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19)
+           AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19)
                REG_RMW_FIELD(ah, AR_PHY_CCK_TX_CTRL,
-                               AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK,
-                               pModal->miscBits);
+                             AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK,
+                             pModal->miscBits);
 
 
        if (AR_SREV_9280_20(ah) && AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) {
@@ -1926,18 +1873,15 @@ static bool ath9k_hw_def_set_board_values(struct ath_hw *ah,
                        REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, 0);
                else
                        REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE,
-                                       eep->baseEepHeader.dacLpMode);
+                                     eep->baseEepHeader.dacLpMode);
 
                REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP,
-                               pModal->miscBits >> 2);
+                             pModal->miscBits >> 2);
 
                REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL9,
-                               AR_PHY_TX_DESIRED_SCALE_CCK,
-                               eep->baseEepHeader.desiredScaleCCK);
+                             AR_PHY_TX_DESIRED_SCALE_CCK,
+                             eep->baseEepHeader.desiredScaleCCK);
        }
-
-       return true;
-#undef AR5416_VER_MASK
 }
 
 static void ath9k_hw_def_set_addac(struct ath_hw *ah,
index d6f6108f63c7c7e2d6e6b60a17fa11d4aa731ee8..25b68c881ff12c14b75250401b2ff4ea768e4615 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -95,6 +95,7 @@
 #define FREQ2FBIN(x, y)                ((y) ? ((x) - 2300) : (((x) - 4800) / 5))
 #define ath9k_hw_use_flash(_ah)        (!(_ah->ah_flags & AH_USE_EEPROM))
 
+#define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK)
 #define OLC_FOR_AR9280_20_LATER (AR_SREV_9280_20_OR_LATER(ah) && \
                                 ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL))
 
@@ -489,7 +490,7 @@ struct eeprom_ops {
        u8 (*get_num_ant_config)(struct ath_hw *hw, enum ieee80211_band band);
        u16 (*get_eeprom_antenna_cfg)(struct ath_hw *hw,
                                      struct ath9k_channel *chan);
-       bool (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan);
+       void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan);
        void (*set_addac)(struct ath_hw *hw, struct ath9k_channel *chan);
        int (*set_txpower)(struct ath_hw *hw, struct ath9k_channel *chan,
                           u16 cfgCtl, u8 twiceAntennaReduction,
index d494e98ba971b3160fcd3830deb1d882fc65152a..b15eaf8417ff0d50a1ec3e86d8d0d3c6e1065218 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -588,6 +588,10 @@ static int ath9k_hw_post_attach(struct ath_hw *ah)
        ecode = ath9k_hw_eeprom_attach(ah);
        if (ecode != 0)
                return ecode;
+
+       DPRINTF(ah->ah_sc, ATH_DBG_CONFIG, "Eeprom VER: %d, REV: %d\n",
+               ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah));
+
        ecode = ath9k_hw_rfattach(ah);
        if (ecode != 0)
                return ecode;
@@ -1444,6 +1448,7 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode)
                REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
                break;
        case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
                REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC
                          | AR_STA_ID1_KSRCH_MODE);
                REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
@@ -2273,11 +2278,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        else
                ath9k_hw_spur_mitigate(ah, chan);
 
-       if (!ah->eep_ops->set_board_values(ah, chan)) {
-               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
-                       "error setting board options\n");
-               return -EIO;
-       }
+       ah->eep_ops->set_board_values(ah, chan);
 
        ath9k_hw_decrease_chain_power(ah, chan);
 
@@ -3149,6 +3150,7 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
                flags |= AR_TBTT_TIMER_EN;
                break;
        case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
                REG_SET_BIT(ah, AR_TXCFG,
                            AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
                REG_WRITE(ah, AR_NEXT_NDP_TIMER,
index dc681f011fdfa1b3be21686c6b1ce0805b7405b7..0b594e0ee26055407877202c56297652081a816e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 1d60c3706f1c204dfac5c0d32fc125717312bba1..e2f0a34b79a1439fe35ab700d0fb98c1ec9f402d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index f757bc7eec68da37e68889a85385712a5fa390e3..e0a6dee4583949eeb2b3e6679fb3f7fbef690c0c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index a75f65dae1d7b5a8a818fa059664599bc9b0d844..1176bce8b76c69d94d970fd57d7a7441f494ea8b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 8db75f6de53ed441c2c2549a6c09410972961a1c..13d4e6756c9973e57eeb6243ad8f757a07d463a3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -940,18 +940,25 @@ static void ath_led_blink_work(struct work_struct *work)
 
        if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED))
                return;
-       ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN,
-                         (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0);
+
+       if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
+           (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
+               ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0);
+       else
+               ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN,
+                                 (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0);
 
        queue_delayed_work(sc->hw->workqueue, &sc->ath_led_blink_work,
                           (sc->sc_flags & SC_OP_LED_ON) ?
                           msecs_to_jiffies(sc->led_off_duration) :
                           msecs_to_jiffies(sc->led_on_duration));
 
-       sc->led_on_duration =
-                       max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25);
-       sc->led_off_duration =
-                       max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10);
+       sc->led_on_duration = sc->led_on_cnt ?
+                       max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) :
+                       ATH_LED_ON_DURATION_IDLE;
+       sc->led_off_duration = sc->led_off_cnt ?
+                       max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) :
+                       ATH_LED_OFF_DURATION_IDLE;
        sc->led_on_cnt = sc->led_off_cnt = 0;
        if (sc->sc_flags & SC_OP_LED_ON)
                sc->sc_flags &= ~SC_OP_LED_ON;
@@ -1592,7 +1599,8 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_AP) |
                BIT(NL80211_IFTYPE_STATION) |
-               BIT(NL80211_IFTYPE_ADHOC);
+               BIT(NL80211_IFTYPE_ADHOC) |
+               BIT(NL80211_IFTYPE_MESH_POINT);
 
        hw->wiphy->reg_notifier = ath9k_reg_notifier;
        hw->wiphy->strict_regulatory = true;
@@ -2200,18 +2208,13 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
                ic_opmode = NL80211_IFTYPE_STATION;
                break;
        case NL80211_IFTYPE_ADHOC:
-               if (sc->nbcnvifs >= ATH_BCBUF) {
-                       ret = -ENOBUFS;
-                       goto out;
-               }
-               ic_opmode = NL80211_IFTYPE_ADHOC;
-               break;
        case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_MESH_POINT:
                if (sc->nbcnvifs >= ATH_BCBUF) {
                        ret = -ENOBUFS;
                        goto out;
                }
-               ic_opmode = NL80211_IFTYPE_AP;
+               ic_opmode = conf->type;
                break;
        default:
                DPRINTF(sc, ATH_DBG_FATAL,
@@ -2247,7 +2250,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
         * Note we only do this (at the moment) for station mode.
         */
        if ((conf->type == NL80211_IFTYPE_STATION) ||
-           (conf->type == NL80211_IFTYPE_ADHOC)) {
+           (conf->type == NL80211_IFTYPE_ADHOC) ||
+           (conf->type == NL80211_IFTYPE_MESH_POINT)) {
                if (ath9k_hw_phycounters(sc->sc_ah))
                        sc->imask |= ATH9K_INT_MIB;
                sc->imask |= ATH9K_INT_TSFOOR;
@@ -2294,8 +2298,9 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
        del_timer_sync(&sc->ani.timer);
 
        /* Reclaim beacon resources */
-       if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
-           sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) {
+       if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
+           (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
+           (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) {
                ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
                ath_beacon_return(sc, avp);
        }
@@ -2428,6 +2433,7 @@ static int ath9k_config_interface(struct ieee80211_hw *hw,
                switch (vif->type) {
                case NL80211_IFTYPE_STATION:
                case NL80211_IFTYPE_ADHOC:
+               case NL80211_IFTYPE_MESH_POINT:
                        /* Set BSSID */
                        memcpy(sc->curbssid, conf->bssid, ETH_ALEN);
                        memcpy(avp->bssid, conf->bssid, ETH_ALEN);
@@ -2451,7 +2457,8 @@ static int ath9k_config_interface(struct ieee80211_hw *hw,
        }
 
        if ((vif->type == NL80211_IFTYPE_ADHOC) ||
-           (vif->type == NL80211_IFTYPE_AP)) {
+           (vif->type == NL80211_IFTYPE_AP) ||
+           (vif->type == NL80211_IFTYPE_MESH_POINT)) {
                if ((conf->changed & IEEE80211_IFCC_BEACON) ||
                    (conf->changed & IEEE80211_IFCC_BEACON_ENABLED &&
                     conf->enable_beacon)) {
@@ -2723,7 +2730,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
 
                ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid);
                break;
-       case IEEE80211_AMPDU_TX_RESUME:
+       case IEEE80211_AMPDU_TX_OPERATIONAL:
                ath_tx_aggr_resume(sc, sta, tid);
                break;
        default:
index 9a58baabb9cab5c387cf216cd5873fbefa1ad547..6dbc58580abbc1d5df9223aa60a11925370c71aa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -87,7 +87,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        struct ath_softc *sc;
        struct ieee80211_hw *hw;
        u8 csz;
-       u32 val;
        int ret = 0;
        struct ath_hw *ah;
 
@@ -134,14 +133,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        pci_set_master(pdev);
 
-       /*
-        * Disable the RETRY_TIMEOUT register (0x41) to keep
-        * PCI Tx retries from interfering with C3 CPU state.
-        */
-       pci_read_config_dword(pdev, 0x40, &val);
-       if ((val & 0x0000ff00) != 0)
-               pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-
        ret = pci_request_region(pdev, 0, "ath9k");
        if (ret) {
                dev_err(&pdev->dev, "PCI memory region reserve error\n");
@@ -253,21 +244,12 @@ static int ath_pci_resume(struct pci_dev *pdev)
        struct ieee80211_hw *hw = pci_get_drvdata(pdev);
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
-       u32 val;
        int err;
 
        err = pci_enable_device(pdev);
        if (err)
                return err;
        pci_restore_state(pdev);
-       /*
-        * Suspend/Resume resets the PCI configuration space, so we have to
-        * re-disable the RETRY_TIMEOUT register (0x41) to keep
-        * PCI Tx retries from interfering with C3 CPU state
-        */
-       pci_read_config_dword(pdev, 0x40, &val);
-       if ((val & 0x0000ff00) != 0)
-               pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
 
        /* Enable LED */
        ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
index e1494bae0f9fdc7d3f6d08f341c101c2a03f0a90..8bcba906929ae95b4839f345068a211f62054456 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 1eac8c707342647da61bfebbd41da6f06f72692a..0f7f8e0c9c95faeb73bb89c7c18f6eaeac0b53a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 832735677a465740c6173208b0a1026e2df68c74..824ccbb8b7b83249791cde9b962720efb7953e4c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2004-2008 Atheros Communications, Inc.
+ * Copyright (c) 2004-2009 Atheros Communications, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -864,6 +864,8 @@ static void ath_rc_ratefind(struct ath_softc *sc,
                                          rate_table, nrix, 1, 0);
                ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
                                       try_per_rate, nrix, 0);
+
+               tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
        } else {
                try_per_rate = (ATH_11N_TXMAXTRY/4);
                /* Set the choosen rate. No RTS for first series entry. */
@@ -1468,16 +1470,18 @@ static void ath_rc_init(struct ath_softc *sc,
                ath_rc_priv->ht_cap);
 }
 
-static u8 ath_rc_build_ht_caps(struct ath_softc *sc, bool is_ht, bool is_cw40,
-                              bool is_sgi40)
+static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta,
+                              bool is_cw40, bool is_sgi40)
 {
        u8 caps = 0;
 
-       if (is_ht) {
+       if (sta->ht_cap.ht_supported) {
                caps = WLAN_RC_HT_FLAG;
                if (sc->sc_ah->caps.tx_chainmask != 1 &&
-                   ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_DS, 0, NULL))
-                       caps |= WLAN_RC_DS_FLAG;
+                   ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_DS, 0, NULL)) {
+                       if (sta->ht_cap.mcs.rx_mask[1])
+                               caps |= WLAN_RC_DS_FLAG;
+               }
                if (is_cw40)
                        caps |= WLAN_RC_40_FLAG;
                if (is_sgi40)
@@ -1615,6 +1619,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
        /* Choose rate table first */
 
        if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) ||
+           (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) ||
            (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)) {
                rate_table = ath_choose_rate_table(sc, sband->band,
                                                   sta->ht_cap.ht_supported,
@@ -1624,8 +1629,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
                rate_table = sc->cur_rate_table;
        }
 
-       ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta->ht_cap.ht_supported,
-                                                  is_cw40, is_sgi40);
+       ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi40);
        ath_rc_init(sc, priv_sta, sband, sta, rate_table);
 }
 
@@ -1659,8 +1663,7 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
                        rate_table = ath_choose_rate_table(sc, sband->band,
                                                   sta->ht_cap.ht_supported,
                                                   oper_cw40);
-                       ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc,
-                                                  sta->ht_cap.ht_supported,
+                       ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta,
                                                   oper_cw40, oper_sgi40);
                        ath_rc_init(sc, priv_sta, sband, sta, rate_table);
 
index db9b0b9a343100fdca3e35a03257157892fa075a..199a3ce57d64df520541a6ce0bac2df166e7dce8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2004 Sam Leffler, Errno Consulting
  * Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 0bba17662a1f040a2517a3e4d0478d1cc1c69c22..71cb18d6757dea78bb829ef94c84f88479c23c29 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -344,8 +344,13 @@ void ath_rx_cleanup(struct ath_softc *sc)
 
        list_for_each_entry(bf, &sc->rx.rxbuf, list) {
                skb = bf->bf_mpdu;
-               if (skb)
+               if (skb) {
+                       dma_unmap_single(sc->dev,
+                                        bf->bf_buf_addr,
+                                        sc->rx.bufsize,
+                                        DMA_FROM_DEVICE);
                        dev_kfree_skb(skb);
+               }
        }
 
        if (sc->rx.rxdma.dd_desc_len != 0)
index d86e90e38173592aee2765a92a6957495981e34a..52605246679fa93eb2a11f0c1015fb04b03f15fd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index b8f9b6d6bec442908f48d2360d168e1f9a412e28..4ca625102291178f9656e96a71a71619d30d3443 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 8f885f3bc8dff67f100330bb9e71d00cbeff68b3..9f5fbd4eea7a2fd1d89fbd378b8caa3cf79f21e6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index b41d0002f3fef4bad462be9ac2aa1544216d42e0..4d0e298cd1c75728e8e734e173234c5badf0523b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index e3f376611f85459ac4b0bb446e072316418e7763..689bdbf78808bc4f32c2a3c316c28ef1c9481c87 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -64,6 +64,10 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
 static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
                             struct list_head *head);
 static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf);
+static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf,
+                             int txok);
+static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds,
+                            int nbad, int txok, bool update_rc);
 
 /*********************/
 /* Aggregation logic */
@@ -274,9 +278,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
        struct ath_buf *bf_next, *bf_last = bf->bf_lastbf;
        struct ath_desc *ds = bf_last->bf_desc;
        struct list_head bf_head, bf_pending;
-       u16 seq_st = 0;
+       u16 seq_st = 0, acked_cnt = 0, txfail_cnt = 0;
        u32 ba[WME_BA_BMP_SIZE >> 5];
-       int isaggr, txfail, txpending, sendbar = 0, needreset = 0;
+       int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0;
+       bool rc_update = true;
 
        skb = (struct sk_buff *)bf->bf_mpdu;
        hdr = (struct ieee80211_hdr *)skb->data;
@@ -316,6 +321,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
        INIT_LIST_HEAD(&bf_pending);
        INIT_LIST_HEAD(&bf_head);
 
+       nbad = ath_tx_num_badfrms(sc, bf, txok);
        while (bf) {
                txfail = txpending = 0;
                bf_next = bf->bf_next;
@@ -323,8 +329,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, bf->bf_seqno))) {
                        /* transmit completion, subframe is
                         * acked by block ack */
+                       acked_cnt++;
                } else if (!isaggr && txok) {
                        /* transmit completion */
+                       acked_cnt++;
                } else {
                        if (!(tid->state & AGGR_CLEANUP) &&
                            ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) {
@@ -335,6 +343,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                                        bf->bf_state.bf_type |= BUF_XRETRY;
                                        txfail = 1;
                                        sendbar = 1;
+                                       txfail_cnt++;
                                }
                        } else {
                                /*
@@ -361,6 +370,13 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                        ath_tx_update_baw(sc, tid, bf->bf_seqno);
                        spin_unlock_bh(&txq->axq_lock);
 
+                       if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) {
+                               ath_tx_rc_status(bf, ds, nbad, txok, true);
+                               rc_update = false;
+                       } else {
+                               ath_tx_rc_status(bf, ds, nbad, txok, false);
+                       }
+
                        ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar);
                } else {
                        /* retry the un-acked ones */
@@ -1734,7 +1750,7 @@ exit:
 /*****************/
 
 static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
-                           struct ath_xmit_status *tx_status)
+                           int tx_flags)
 {
        struct ieee80211_hw *hw = sc->hw;
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
@@ -1755,18 +1771,14 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
                tx_info->rate_driver_data[0] = NULL;
        }
 
-       if (tx_status->flags & ATH_TX_BAR) {
+       if (tx_flags & ATH_TX_BAR)
                tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
-               tx_status->flags &= ~ATH_TX_BAR;
-       }
 
-       if (!(tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY))) {
+       if (!(tx_flags & (ATH_TX_ERROR | ATH_TX_XRETRY))) {
                /* Frame was ACKed */
                tx_info->flags |= IEEE80211_TX_STAT_ACK;
        }
 
-       tx_info->status.rates[0].count = tx_status->retries + 1;
-
        hdrlen = ieee80211_get_hdrlen_from_skb(skb);
        padsize = hdrlen & 3;
        if (padsize && hdrlen >= 24) {
@@ -1789,29 +1801,22 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
                                int txok, int sendbar)
 {
        struct sk_buff *skb = bf->bf_mpdu;
-       struct ath_xmit_status tx_status;
        unsigned long flags;
+       int tx_flags = 0;
 
-       /*
-        * Set retry information.
-        * NB: Don't use the information in the descriptor, because the frame
-        * could be software retried.
-        */
-       tx_status.retries = bf->bf_retries;
-       tx_status.flags = 0;
 
        if (sendbar)
-               tx_status.flags = ATH_TX_BAR;
+               tx_flags = ATH_TX_BAR;
 
        if (!txok) {
-               tx_status.flags |= ATH_TX_ERROR;
+               tx_flags |= ATH_TX_ERROR;
 
                if (bf_isxretried(bf))
-                       tx_status.flags |= ATH_TX_XRETRY;
+                       tx_flags |= ATH_TX_XRETRY;
        }
 
        dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE);
-       ath_tx_complete(sc, skb, &tx_status);
+       ath_tx_complete(sc, skb, tx_flags);
 
        /*
         * Return the list of ath_buf of this mpdu to free queue
@@ -1852,27 +1857,40 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf,
        return nbad;
 }
 
-static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, int nbad)
+static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds,
+                            int nbad, int txok, bool update_rc)
 {
        struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
+       struct ieee80211_hw *hw = tx_info_priv->aphy->hw;
+       u8 i, tx_rateindex;
+
+       if (txok)
+               tx_info->status.ack_signal = ds->ds_txstat.ts_rssi;
 
-       tx_info_priv->update_rc = false;
+       tx_rateindex = ds->ds_txstat.ts_rateindex;
+       WARN_ON(tx_rateindex >= hw->max_rates);
+
+       tx_info_priv->update_rc = update_rc;
        if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT)
                tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
 
        if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 &&
-           (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) {
+           (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) {
                if (ieee80211_is_data(hdr->frame_control)) {
                        memcpy(&tx_info_priv->tx, &ds->ds_txstat,
                               sizeof(tx_info_priv->tx));
                        tx_info_priv->n_frames = bf->bf_nframes;
                        tx_info_priv->n_bad_frames = nbad;
-                       tx_info_priv->update_rc = true;
                }
        }
+
+       for (i = tx_rateindex + 1; i < hw->max_rates; i++)
+               tx_info->status.rates[i].count = 0;
+
+       tx_info->status.rates[tx_rateindex].count = bf->bf_retries + 1;
 }
 
 static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq)
@@ -1897,7 +1915,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
        struct ath_buf *bf, *lastbf, *bf_held = NULL;
        struct list_head bf_head;
        struct ath_desc *ds;
-       int txok, nbad = 0;
+       int txok;
        int status;
 
        DPRINTF(sc, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n",
@@ -1991,13 +2009,9 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                        bf->bf_retries = ds->ds_txstat.ts_longretry;
                        if (ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY)
                                bf->bf_state.bf_type |= BUF_XRETRY;
-                       nbad = 0;
-               } else {
-                       nbad = ath_tx_num_badfrms(sc, bf, txok);
+                       ath_tx_rc_status(bf, ds, 0, txok, true);
                }
 
-               ath_tx_rc_status(bf, ds, nbad);
-
                if (bf_isampdu(bf))
                        ath_tx_complete_aggr(sc, txq, bf, &bf_head, txok);
                else
index b72ef3fd315a41833ed1cfb4b863de56443c99e0..4896e083111458b17b66601f85c4b6cd3f1afea0 100644 (file)
@@ -3993,6 +3993,8 @@ static void setup_struct_wldev_for_init(struct b43_wldev *dev)
        dev->irq_reason = 0;
        memset(dev->dma_reason, 0, sizeof(dev->dma_reason));
        dev->irq_savedstate = B43_IRQ_MASKTEMPLATE;
+       if (b43_modparam_verbose < B43_VERBOSITY_DEBUG)
+               dev->irq_savedstate &= ~B43_IRQ_PHY_TXERR;
 
        dev->mac_suspended = 1;
 
index 0f53c7e5e01e99d1377993acb772958cb2f6117e..a63d88841df86034cf011f93c85a2acce02e3448 100644 (file)
@@ -50,7 +50,7 @@ static int b43_plcp_get_bitrate_idx_cck(struct b43_plcp_hdr6 *plcp)
 }
 
 /* Extract the bitrate index out of an OFDM PLCP header. */
-static u8 b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool aphy)
+static int b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool aphy)
 {
        int base = aphy ? 0 : 4;
 
index 205603d082aa2bf75598249f0d1f3df3a7b4d94c..73f93a0ff2df820d3a3f4b2eb9557150e2146a25 100644 (file)
@@ -233,7 +233,7 @@ struct iwl3945_eeprom {
 #define PCI_CFG_REV_ID_BIT_RTP                      (0x80)     /* bit 7    */
 
 #define TFD_QUEUE_MIN           0
-#define TFD_QUEUE_MAX           6
+#define TFD_QUEUE_MAX           5      /* 4 DATA + 1 CMD */
 
 #define IWL_NUM_SCAN_RATES         (2)
 
index f65c308a67148a2294ccb2c7d329ee617c0236a6..af6b9d4447782512a6fc020bf65c45a8a1ac5c7e 100644 (file)
@@ -124,7 +124,7 @@ static struct iwl3945_tpt_entry iwl3945_tpt_table_g[] = {
 #define IWL39_RATE_HIGH_TH          11520
 #define IWL_SUCCESS_UP_TH         8960
 #define IWL_SUCCESS_DOWN_TH      10880
-#define IWL_RATE_MIN_FAILURE_TH       8
+#define IWL_RATE_MIN_FAILURE_TH       6
 #define IWL_RATE_MIN_SUCCESS_TH       8
 #define IWL_RATE_DECREASE_TH       1920
 #define IWL_RATE_RETRY_TH           15
@@ -488,7 +488,7 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband
 
        IWL_DEBUG_RATE(priv, "enter\n");
 
-       retries = info->status.rates[0].count - 1;
+       retries = info->status.rates[0].count;
        /* Sanity Check for retries */
        if (retries > IWL_RATE_RETRY_TH)
                retries = IWL_RATE_RETRY_TH;
@@ -791,16 +791,15 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
        if ((window->success_ratio < IWL_RATE_DECREASE_TH) || !current_tpt) {
                IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n");
                scale_action = -1;
-
        /* No throughput measured yet for adjacent rates,
         * try increase */
        } else if ((low_tpt == IWL_INVALID_VALUE) &&
                   (high_tpt == IWL_INVALID_VALUE)) {
 
-               if (high != IWL_RATE_INVALID && window->success_counter >= IWL_RATE_INCREASE_TH)
+               if (high != IWL_RATE_INVALID && window->success_ratio >= IWL_RATE_INCREASE_TH)
                        scale_action = 1;
                else if (low != IWL_RATE_INVALID)
-                       scale_action = -1;
+                       scale_action = 0;
 
        /* Both adjacent throughputs are measured, but neither one has
         * better throughput; we're using the best rate, don't change
@@ -826,14 +825,14 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
                        else {
                                IWL_DEBUG_RATE(priv,
                                    "decrease rate because of high tpt\n");
-                               scale_action = -1;
+                               scale_action = 0;
                        }
                } else if (low_tpt != IWL_INVALID_VALUE) {
                        if (low_tpt > current_tpt) {
                                IWL_DEBUG_RATE(priv,
                                    "decrease rate because of low tpt\n");
                                scale_action = -1;
-                       } else if (window->success_counter >= IWL_RATE_INCREASE_TH) {
+                       } else if (window->success_ratio >= IWL_RATE_INCREASE_TH) {
                                /* Lower rate has better
                                 * throughput,decrease rate */
                                scale_action = 1;
index ba7e720e73c1638a22b07d01c93a18349e332b37..2399328e8de79d4b827d837445eef56d135a9173 100644 (file)
@@ -293,7 +293,7 @@ static void iwl3945_tx_queue_reclaim(struct iwl_priv *priv,
        if (iwl_queue_space(q) > q->low_mark && (txq_id >= 0) &&
                        (txq_id != IWL_CMD_QUEUE_NUM) &&
                        priv->mac80211_registered)
-               ieee80211_wake_queue(priv->hw, txq_id);
+               iwl_wake_queue(priv, txq_id);
 }
 
 /**
@@ -747,11 +747,6 @@ void iwl3945_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
        int i;
        int counter;
 
-       /* classify bd */
-       if (txq->q.id == IWL_CMD_QUEUE_NUM)
-               /* nothing to cleanup after for host commands */
-               return;
-
        /* sanity check */
        counter = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags));
        if (counter > NUM_TFD_CHUNKS) {
@@ -1046,7 +1041,7 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv)
                goto error;
 
        /* Tx queue(s) */
-       for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++) {
+       for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) {
                slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ?
                                TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
                rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num,
@@ -1184,7 +1179,7 @@ int iwl3945_hw_nic_init(struct iwl_priv *priv)
        IWL_DEBUG_INFO(priv, "HW Revision ID = 0x%X\n", rev_id);
 
        rc = priv->cfg->ops->lib->apm_ops.set_pwr_src(priv, IWL_PWR_SRC_VMAIN);
-       if(rc)
+       if (rc)
                return rc;
 
        priv->cfg->ops->lib->apm_ops.config(priv);
@@ -1239,8 +1234,12 @@ void iwl3945_hw_txq_ctx_free(struct iwl_priv *priv)
        int txq_id;
 
        /* Tx queues */
-       for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++)
-               iwl_tx_queue_free(priv, txq_id);
+       for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++)
+               if (txq_id == IWL_CMD_QUEUE_NUM)
+                       iwl_cmd_queue_free(priv);
+               else
+                       iwl_tx_queue_free(priv, txq_id);
+
 }
 
 void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv)
@@ -1259,7 +1258,7 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv)
        iwl_write_prph(priv, ALM_SCD_MODE_REG, 0);
 
        /* reset TFD queues */
-       for (txq_id = 0; txq_id < TFD_QUEUE_MAX; txq_id++) {
+       for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) {
                iwl_write_direct32(priv, FH39_TCSR_CONFIG(txq_id), 0x0);
                iwl_poll_direct_bit(priv, FH39_TSSR_TX_STATUS,
                                FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id),
@@ -2488,6 +2487,9 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv)
                return -ENOMEM;
        }
 
+       /* Assign number of Usable TX queues */
+       priv->hw_params.max_txq_num = TFD_QUEUE_MAX;
+
        priv->hw_params.tfd_size = sizeof(struct iwl3945_tfd);
        priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_3K;
        priv->hw_params.max_pkt_size = 2342;
index bd0140be774e4320435850b2095dcbc78a7c462b..847a6220c5e6b03c74c8693bade699a97056ba9d 100644 (file)
@@ -2178,10 +2178,9 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
                            (iwl_queue_space(&txq->q) > txq->q.low_mark) &&
                            (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) {
                                if (agg->state == IWL_AGG_OFF)
-                                       ieee80211_wake_queue(priv->hw, txq_id);
+                                       iwl_wake_queue(priv, txq_id);
                                else
-                                       ieee80211_wake_queue(priv->hw,
-                                                            txq->swq_id);
+                                       iwl_wake_queue(priv, txq->swq_id);
                        }
                }
        } else {
@@ -2205,7 +2204,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
 
                if (priv->mac80211_registered &&
                    (iwl_queue_space(&txq->q) > txq->q.low_mark))
-                       ieee80211_wake_queue(priv->hw, txq_id);
+                       iwl_wake_queue(priv, txq_id);
        }
 
        if (qc && likely(sta_id != IWL_INVALID_STATION))
index 08c19bea71e35cb0e028e3990b1796756bfc5abf..e5ca2511a81a2b5949a1373ff87b329f77c161e2 100644 (file)
@@ -1077,7 +1077,7 @@ static int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
 
        if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
            (IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES <= txq_id)) {
-               IWL_WARN(priv,
+               IWL_ERR(priv,
                        "queue number out of range: %d, must be %d to %d\n",
                        txq_id, IWL50_FIRST_AMPDU_QUEUE,
                        IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES - 1);
@@ -1295,10 +1295,9 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
                            (iwl_queue_space(&txq->q) > txq->q.low_mark) &&
                            (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) {
                                if (agg->state == IWL_AGG_OFF)
-                                       ieee80211_wake_queue(priv->hw, txq_id);
+                                       iwl_wake_queue(priv, txq_id);
                                else
-                                       ieee80211_wake_queue(priv->hw,
-                                                            txq->swq_id);
+                                       iwl_wake_queue(priv, txq->swq_id);
                        }
                }
        } else {
@@ -1324,7 +1323,7 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
 
                if (priv->mac80211_registered &&
                    (iwl_queue_space(&txq->q) > txq->q.low_mark))
-                       ieee80211_wake_queue(priv->hw, txq_id);
+                       iwl_wake_queue(priv, txq_id);
        }
 
        if (ieee80211_is_data_qos(tx_resp->frame_ctrl))
index 0db3bc011ac2b76a63696995f10af5279f3bf0db..663dc83be501d22c742759feadbf3d9ce8570a79 100644 (file)
@@ -1567,9 +1567,8 @@ static void iwl_alive_start(struct iwl_priv *priv)
        if (iwl_is_associated(priv)) {
                struct iwl_rxon_cmd *active_rxon =
                                (struct iwl_rxon_cmd *)&priv->active_rxon;
-
-               memcpy(&priv->staging_rxon, &priv->active_rxon,
-                      sizeof(priv->staging_rxon));
+               /* apply any changes in staging */
+               priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
                active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
        } else {
                /* Initialize our rx_config data */
@@ -2184,110 +2183,112 @@ static int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
        struct iwl_priv *priv = hw->priv;
        const struct iwl_channel_info *ch_info;
        struct ieee80211_conf *conf = &hw->conf;
-       unsigned long flags;
+       unsigned long flags = 0;
        int ret = 0;
-       u16 channel;
+       u16 ch;
+       int scan_active = 0;
 
        mutex_lock(&priv->mutex);
-       IWL_DEBUG_MAC80211(priv, "enter to channel %d\n", conf->channel->hw_value);
+       IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n",
+                                       conf->channel->hw_value, changed);
 
-       priv->current_ht_config.is_ht = conf_is_ht(conf);
-
-       if (conf->radio_enabled && iwl_radio_kill_sw_enable_radio(priv)) {
-               IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - waiting for uCode\n");
-               goto out;
+       if (unlikely(!priv->cfg->mod_params->disable_hw_scan &&
+                       test_bit(STATUS_SCANNING, &priv->status))) {
+               scan_active = 1;
+               IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
        }
 
-       if (!conf->radio_enabled)
-               iwl_radio_kill_sw_disable_radio(priv);
 
-       if (!iwl_is_ready(priv)) {
-               IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
-               ret = -EIO;
-               goto out;
-       }
+       /* during scanning mac80211 will delay channel setting until
+        * scan finish with changed = 0
+        */
+       if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
+               if (scan_active)
+                       goto set_ch_out;
+
+               ch = ieee80211_frequency_to_channel(conf->channel->center_freq);
+               ch_info = iwl_get_channel_info(priv, conf->channel->band, ch);
+               if (!is_channel_valid(ch_info)) {
+                       IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
+                       ret = -EINVAL;
+                       goto set_ch_out;
+               }
 
-       if (unlikely(!priv->cfg->mod_params->disable_hw_scan &&
-                    test_bit(STATUS_SCANNING, &priv->status))) {
-               IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
-               mutex_unlock(&priv->mutex);
-               return 0;
-       }
+               if (priv->iw_mode == NL80211_IFTYPE_ADHOC &&
+                       !is_channel_ibss(ch_info)) {
+                       IWL_ERR(priv, "channel %d in band %d not "
+                               "IBSS channel\n",
+                               conf->channel->hw_value, conf->channel->band);
+                       ret = -EINVAL;
+                       goto set_ch_out;
+               }
 
-       channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
-       ch_info = iwl_get_channel_info(priv, conf->channel->band, channel);
-       if (!is_channel_valid(ch_info)) {
-               IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
-               ret = -EINVAL;
-               goto out;
-       }
+               priv->current_ht_config.is_ht = conf_is_ht(conf);
 
-       if (priv->iw_mode == NL80211_IFTYPE_ADHOC &&
-           !is_channel_ibss(ch_info)) {
-               IWL_ERR(priv, "channel %d in band %d not IBSS channel\n",
-                       conf->channel->hw_value, conf->channel->band);
-               ret = -EINVAL;
-               goto out;
-       }
+               spin_lock_irqsave(&priv->lock, flags);
 
-       spin_lock_irqsave(&priv->lock, flags);
 
+               /* if we are switching from ht to 2.4 clear flags
+                * from any ht related info since 2.4 does not
+                * support ht */
+               if ((le16_to_cpu(priv->staging_rxon.channel) != ch))
+                       priv->staging_rxon.flags = 0;
 
-       /* if we are switching from ht to 2.4 clear flags
-        * from any ht related info since 2.4 does not
-        * support ht */
-       if ((le16_to_cpu(priv->staging_rxon.channel) != channel)
-#ifdef IEEE80211_CONF_CHANNEL_SWITCH
-           && !(conf->flags & IEEE80211_CONF_CHANNEL_SWITCH)
-#endif
-       )
-               priv->staging_rxon.flags = 0;
+               iwl_set_rxon_channel(priv, conf->channel);
 
-       iwl_set_rxon_channel(priv, conf->channel);
+               iwl_set_flags_for_band(priv, conf->channel->band);
+               spin_unlock_irqrestore(&priv->lock, flags);
+ set_ch_out:
+               /* The list of supported rates and rate mask can be different
+                * for each band; since the band may have changed, reset
+                * the rate mask to what mac80211 lists */
+               iwl_set_rate(priv);
+       }
 
-       iwl_set_flags_for_band(priv, conf->channel->band);
+       if (changed & IEEE80211_CONF_CHANGE_PS) {
+               if (conf->flags & IEEE80211_CONF_PS)
+                       ret = iwl_power_set_user_mode(priv, IWL_POWER_INDEX_3);
+               else
+                       ret = iwl_power_set_user_mode(priv, IWL_POWER_MODE_CAM);
+               if (ret)
+                       IWL_DEBUG_MAC80211(priv, "Error setting power level\n");
 
-       /* The list of supported rates and rate mask can be different
-        * for each band; since the band may have changed, reset
-        * the rate mask to what mac80211 lists */
-       iwl_set_rate(priv);
+       }
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       if (changed & IEEE80211_CONF_CHANGE_POWER) {
+               IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
+                       priv->tx_power_user_lmt, conf->power_level);
 
-#ifdef IEEE80211_CONF_CHANNEL_SWITCH
-       if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) {
-               iwl_hw_channel_switch(priv, conf->channel);
-               goto out;
+               iwl_set_tx_power(priv, conf->power_level, false);
+       }
+
+       /* call to ensure that 4965 rx_chain is set properly in monitor mode */
+       iwl_set_rxon_chain(priv);
+
+       if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) {
+               if (conf->radio_enabled &&
+                       iwl_radio_kill_sw_enable_radio(priv)) {
+                       IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - "
+                                               "waiting for uCode\n");
+                       goto out;
+               }
+
+               if (!conf->radio_enabled)
+                       iwl_radio_kill_sw_disable_radio(priv);
        }
-#endif
 
        if (!conf->radio_enabled) {
                IWL_DEBUG_MAC80211(priv, "leave - radio disabled\n");
                goto out;
        }
 
-       if (iwl_is_rfkill(priv)) {
-               IWL_DEBUG_MAC80211(priv, "leave - RF kill\n");
-               ret = -EIO;
+       if (!iwl_is_ready(priv)) {
+               IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
                goto out;
        }
 
-       if (conf->flags & IEEE80211_CONF_PS)
-               ret = iwl_power_set_user_mode(priv, IWL_POWER_INDEX_3);
-       else
-               ret = iwl_power_set_user_mode(priv, IWL_POWER_MODE_CAM);
-       if (ret)
-               IWL_DEBUG_MAC80211(priv, "Error setting power level\n");
-
-       IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
-                          priv->tx_power_user_lmt, conf->power_level);
-
-       iwl_set_tx_power(priv, conf->power_level, false);
-
-       iwl_set_rate(priv);
-
-       /* call to ensure that 4965 rx_chain is set properly in monitor mode */
-       iwl_set_rxon_chain(priv);
+       if (scan_active)
+               goto out;
 
        if (memcmp(&priv->active_rxon,
                   &priv->staging_rxon, sizeof(priv->staging_rxon)))
@@ -2295,9 +2296,9 @@ static int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
        else
                IWL_DEBUG_INFO(priv, "No re-sending same RXON configuration.\n");
 
-       IWL_DEBUG_MAC80211(priv, "leave\n");
 
 out:
+       IWL_DEBUG_MAC80211(priv, "leave\n");
        mutex_unlock(&priv->mutex);
        return ret;
 }
@@ -2682,6 +2683,7 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
                             struct ieee80211_sta *sta, u16 tid, u16 *ssn)
 {
        struct iwl_priv *priv = hw->priv;
+       int ret;
 
        IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n",
                     sta->addr, tid);
@@ -2695,13 +2697,21 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
                return iwl_sta_rx_agg_start(priv, sta->addr, tid, *ssn);
        case IEEE80211_AMPDU_RX_STOP:
                IWL_DEBUG_HT(priv, "stop Rx\n");
-               return iwl_sta_rx_agg_stop(priv, sta->addr, tid);
+               ret = iwl_sta_rx_agg_stop(priv, sta->addr, tid);
+               if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+                       return 0;
+               else
+                       return ret;
        case IEEE80211_AMPDU_TX_START:
                IWL_DEBUG_HT(priv, "start Tx\n");
                return iwl_tx_agg_start(priv, sta->addr, tid, ssn);
        case IEEE80211_AMPDU_TX_STOP:
                IWL_DEBUG_HT(priv, "stop Tx\n");
-               return iwl_tx_agg_stop(priv, sta->addr, tid);
+               ret = iwl_tx_agg_stop(priv, sta->addr, tid);
+               if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+                       return 0;
+               else
+                       return ret;
        default:
                IWL_DEBUG_HT(priv, "unknown\n");
                return -EINVAL;
@@ -3083,11 +3093,6 @@ static ssize_t store_power_level(struct device *d,
 
        mutex_lock(&priv->mutex);
 
-       if (!iwl_is_ready(priv)) {
-               ret = -EAGAIN;
-               goto out;
-       }
-
        ret = strict_strtoul(buf, 10, &mode);
        if (ret)
                goto out;
index 085e9cf1cac99f0be0613063adcfd782a05cccf2..c54fb93e9d720e7c63d29692b5f16364b2666807 100644 (file)
@@ -1298,6 +1298,7 @@ int iwl_setup_mac(struct iwl_priv *priv)
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_NOISE_DBM |
                    IEEE80211_HW_AMPDU_AGGREGATION |
+                   IEEE80211_HW_SPECTRUM_MGMT |
                    IEEE80211_HW_SUPPORTS_PS;
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
@@ -1308,9 +1309,6 @@ int iwl_setup_mac(struct iwl_priv *priv)
 
        /* Default value; 4 EDCA QOS priorities */
        hw->queues = 4;
-       /* queues to support 11n aggregation */
-       if (priv->cfg->sku & IWL_SKU_N)
-               hw->ampdu_queues = priv->cfg->mod_params->num_of_ampdu_queues;
 
        hw->conf.beacon_int = 100;
        hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
@@ -1437,6 +1435,10 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
 
        priv->tx_power_user_lmt = tx_power;
 
+       /* if nic is not up don't send command */
+       if (!iwl_is_ready_rf(priv))
+               return ret;
+
        if (force && priv->cfg->ops->lib->send_tx_power)
                ret = priv->cfg->ops->lib->send_tx_power(priv);
 
index 27310fec2e43bd5f5498596721b3403b69b0cd35..a8eac8c3c1fa8e89c68b6f19f996cc2f2b63444a 100644 (file)
@@ -264,6 +264,7 @@ void iwl_rx_reply_error(struct iwl_priv *priv,
 * RX
 ******************************************************/
 void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
+void iwl_cmd_queue_free(struct iwl_priv *priv);
 int iwl_rx_queue_alloc(struct iwl_priv *priv);
 void iwl_rx_handle(struct iwl_priv *priv);
 int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv,
index 36cfeccfafbcde17933cba2a1e6261a7c6023d04..64eb585f1578a2d4e320d39ce9ba0fadfcb621f6 100644 (file)
@@ -425,6 +425,56 @@ static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf,
        return ret;
 }
 
+static ssize_t iwl_dbgfs_status_read(struct file *file,
+                                               char __user *user_buf,
+                                               size_t count, loff_t *ppos) {
+
+       struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+       char buf[512];
+       int pos = 0;
+       const size_t bufsz = sizeof(buf);
+
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_ACTIVE:\t %d\n",
+               test_bit(STATUS_HCMD_ACTIVE, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_SYNC_ACTIVE: %d\n",
+               test_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INT_ENABLED:\t %d\n",
+               test_bit(STATUS_INT_ENABLED, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n",
+               test_bit(STATUS_RF_KILL_HW, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_SW:\t %d\n",
+               test_bit(STATUS_RF_KILL_SW, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INIT:\t\t %d\n",
+               test_bit(STATUS_INIT, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n",
+               test_bit(STATUS_ALIVE, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n",
+               test_bit(STATUS_READY, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_TEMPERATURE:\t %d\n",
+               test_bit(STATUS_TEMPERATURE, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_GEO_CONFIGURED:\t %d\n",
+               test_bit(STATUS_GEO_CONFIGURED, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n",
+               test_bit(STATUS_EXIT_PENDING, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_IN_SUSPEND:\t %d\n",
+               test_bit(STATUS_IN_SUSPEND, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n",
+               test_bit(STATUS_STATISTICS, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n",
+               test_bit(STATUS_SCANNING, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n",
+               test_bit(STATUS_SCAN_ABORTING, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n",
+               test_bit(STATUS_SCAN_HW, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n",
+               test_bit(STATUS_POWER_PMI, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n",
+               test_bit(STATUS_FW_ERROR, &priv->status));
+       pos += scnprintf(buf + pos, bufsz - pos, "STATUS_MODE_PENDING:\t %d\n",
+               test_bit(STATUS_MODE_PENDING, &priv->status));
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
 DEBUGFS_READ_WRITE_FILE_OPS(sram);
 DEBUGFS_WRITE_FILE_OPS(log_event);
 DEBUGFS_READ_FILE_OPS(eeprom);
@@ -432,6 +482,7 @@ DEBUGFS_READ_FILE_OPS(stations);
 DEBUGFS_READ_FILE_OPS(rx_statistics);
 DEBUGFS_READ_FILE_OPS(tx_statistics);
 DEBUGFS_READ_FILE_OPS(channels);
+DEBUGFS_READ_FILE_OPS(status);
 
 /*
  * Create the debugfs files and directories
@@ -466,7 +517,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
        DEBUGFS_ADD_FILE(rx_statistics, data);
        DEBUGFS_ADD_FILE(tx_statistics, data);
        DEBUGFS_ADD_FILE(channels, data);
-       DEBUGFS_ADD_X32(status, data, (u32 *)&priv->status);
+       DEBUGFS_ADD_FILE(status, data);
        DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal);
        DEBUGFS_ADD_BOOL(disable_chain_noise, rf,
                         &priv->disable_chain_noise_cal);
@@ -496,6 +547,7 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv)
        DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_log_event);
        DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_stations);
        DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_channels);
+       DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_status);
        DEBUGFS_REMOVE(priv->dbgfs->dir_data);
        DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_sensitivity);
        DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_chain_noise);
index 0baae80228246f9d72c6705c0e1d02339dae8a01..ec9a13846edde70eece54a1039d3ad948675254c 100644 (file)
@@ -996,6 +996,12 @@ struct iwl_priv {
        u8 key_mapping_key;
        unsigned long ucode_key_table;
 
+       /* queue refcounts */
+#define IWL_MAX_HW_QUEUES      32
+       unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
+       /* for each AC */
+       atomic_t queue_stop_count[4];
+
        /* Indication if ieee80211_ops->open has been called */
        u8 is_open;
 
index fb64d297dd4eb6e2cefdc68f90bdb4a216495c8f..a1328c3c81ae1b80de3a75ca01381a331f3f7a6d 100644 (file)
@@ -93,4 +93,56 @@ static inline int iwl_alloc_fw_desc(struct pci_dev *pci_dev,
        return (desc->v_addr != NULL) ? 0 : -ENOMEM;
 }
 
+/*
+ * we have 8 bits used like this:
+ *
+ * 7 6 5 4 3 2 1 0
+ * | | | | | | | |
+ * | | | | | | +-+-------- AC queue (0-3)
+ * | | | | | |
+ * | +-+-+-+-+------------ HW A-MPDU queue
+ * |
+ * +---------------------- indicates agg queue
+ */
+static inline u8 iwl_virtual_agg_queue_num(u8 ac, u8 hwq)
+{
+       BUG_ON(ac > 3);   /* only have 2 bits */
+       BUG_ON(hwq > 31); /* only have 5 bits */
+
+       return 0x80 | (hwq << 2) | ac;
+}
+
+static inline void iwl_wake_queue(struct iwl_priv *priv, u8 queue)
+{
+       u8 ac = queue;
+       u8 hwq = queue;
+
+       if (queue & 0x80) {
+               ac = queue & 3;
+               hwq = (queue >> 2) & 0x1f;
+       }
+
+       if (test_and_clear_bit(hwq, priv->queue_stopped))
+               if (atomic_dec_return(&priv->queue_stop_count[ac]) <= 0)
+                       ieee80211_wake_queue(priv->hw, ac);
+}
+
+static inline void iwl_stop_queue(struct iwl_priv *priv, u8 queue)
+{
+       u8 ac = queue;
+       u8 hwq = queue;
+
+       if (queue & 0x80) {
+               ac = queue & 3;
+               hwq = (queue >> 2) & 0x1f;
+       }
+
+       if (!test_and_set_bit(hwq, priv->queue_stopped))
+               if (atomic_inc_return(&priv->queue_stop_count[ac]) > 0)
+                       ieee80211_stop_queue(priv->hw, ac);
+}
+
+#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue
+#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue
+
 #endif                         /* __iwl_helpers_h__ */
index 18b7e4195ea1c77de594b4528eac72159088001c..47c894530eb583e978e17c2f1002a0871620def2 100644 (file)
@@ -273,7 +273,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
        if (priv->iw_mode != NL80211_IFTYPE_STATION)
                final_mode = IWL_POWER_MODE_CAM;
 
-       if (!iwl_is_rfkill(priv) && !setting->power_disabled &&
+       if (iwl_is_ready_rf(priv) && !setting->power_disabled &&
            ((setting->power_mode != final_mode) || force)) {
                struct iwl_powertable_cmd cmd;
 
index 1684490d93c00f474da32cedb27e6cc0b7649350..5798fe49c771d97d45d8e12e07fac4ad5bf6f492 100644 (file)
@@ -1138,8 +1138,10 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid)
        int sta_id;
 
        sta_id = iwl_find_station(priv, addr);
-       if (sta_id == IWL_INVALID_STATION)
+       if (sta_id == IWL_INVALID_STATION) {
+               IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
                return -ENXIO;
+       }
 
        spin_lock_irqsave(&priv->sta_lock, flags);
        priv->stations[sta_id].sta.station_flags_msk = 0;
index dff60fb70214938d5acb1b1ed9270a6261c45581..1f117a49c569422ae31a0d6f69a0fa2ea11a8f9b 100644 (file)
@@ -174,7 +174,7 @@ EXPORT_SYMBOL(iwl_tx_queue_free);
  * Free all buffers.
  * 0-fill, but do not free "txq" descriptor structure.
  */
-static void iwl_cmd_queue_free(struct iwl_priv *priv)
+void iwl_cmd_queue_free(struct iwl_priv *priv)
 {
        struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM];
        struct iwl_queue *q = &txq->q;
@@ -193,12 +193,14 @@ static void iwl_cmd_queue_free(struct iwl_priv *priv)
 
        /* De-alloc circular buffer of TFDs */
        if (txq->q.n_bd)
-               pci_free_consistent(dev, sizeof(struct iwl_tfd) *
+               pci_free_consistent(dev, priv->hw_params.tfd_size *
                                    txq->q.n_bd, txq->tfds, txq->q.dma_addr);
 
        /* 0-fill queue descriptor structure */
        memset(txq, 0, sizeof(*txq));
 }
+EXPORT_SYMBOL(iwl_cmd_queue_free);
+
 /*************** DMA-QUEUE-GENERAL-FUNCTIONS  *****
  * DMA services
  *
@@ -761,8 +763,10 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
                hdr->seq_ctrl |= cpu_to_le16(seq_number);
                seq_number += 0x10;
                /* aggregation is on for this <sta,tid> */
-               if (info->flags & IEEE80211_TX_CTL_AMPDU)
+               if (info->flags & IEEE80211_TX_CTL_AMPDU) {
                        txq_id = priv->stations[sta_id].tid[tid].agg.txq_id;
+                       swq_id = iwl_virtual_agg_queue_num(swq_id, txq_id);
+               }
                priv->stations[sta_id].tid[tid].tfds_in_queue++;
        }
 
@@ -893,7 +897,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
                        iwl_txq_update_write_ptr(priv, txq);
                        spin_unlock_irqrestore(&priv->lock, flags);
                } else {
-                       ieee80211_stop_queue(priv->hw, txq->swq_id);
+                       iwl_stop_queue(priv, txq->swq_id);
                }
        }
 
@@ -1221,8 +1225,10 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid)
 
        sta_id = iwl_find_station(priv, ra);
 
-       if (sta_id == IWL_INVALID_STATION)
+       if (sta_id == IWL_INVALID_STATION) {
+               IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid);
                return -ENXIO;
+       }
 
        if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_ON)
                IWL_WARN(priv, "Stopping AGG while state not IWL_AGG_ON\n");
@@ -1429,7 +1435,7 @@ void iwl_rx_reply_compressed_ba(struct iwl_priv *priv,
                if ((iwl_queue_space(&txq->q) > txq->q.low_mark) &&
                    priv->mac80211_registered &&
                    (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA))
-                       ieee80211_wake_queue(priv->hw, txq->swq_id);
+                       iwl_wake_queue(priv, txq->swq_id);
 
                iwl_txq_check_empty(priv, sta_id, tid, scd_flow);
        }
index 4465320f27350d2c59b12d48afafacb50e3dfb78..a71b08ca7c7116f096bf9e097048058055f454c3 100644 (file)
@@ -485,14 +485,14 @@ static int iwl3945_set_ccmp_dynamic_key_info(struct iwl_priv *priv,
        memcpy(priv->stations_39[sta_id].sta.key.key, keyconf->key,
               keyconf->keylen);
 
-       if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK)
+       if ((priv->stations_39[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK)
                        == STA_KEY_FLG_NO_ENC)
-               priv->stations[sta_id].sta.key.key_offset =
+               priv->stations_39[sta_id].sta.key.key_offset =
                                 iwl_get_free_ucode_key_index(priv);
        /* else, we are overriding an existing key => no need to allocated room
        * in uCode. */
 
-       WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
+       WARN(priv->stations_39[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET,
                "no space for a new key");
 
        priv->stations_39[sta_id].sta.key.key_flags = key_flags;
@@ -560,7 +560,7 @@ static int iwl3945_set_dynamic_key(struct iwl_priv *priv,
                ret = iwl3945_set_wep_dynamic_key_info(priv, keyconf, sta_id);
                break;
        default:
-               IWL_ERR(priv,"Unknown alg: %s alg = %d\n", __func__, keyconf->alg);
+               IWL_ERR(priv, "Unknown alg: %s alg = %d\n", __func__, keyconf->alg);
                ret = -EINVAL;
        }
 
@@ -1168,7 +1168,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
                        spin_unlock_irqrestore(&priv->lock, flags);
                }
 
-               ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb));
+               iwl_stop_queue(priv, skb_get_queue_mapping(skb));
        }
 
        return 0;
@@ -3773,15 +3773,19 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, u32 changed)
        }
 #endif
 
-       if (conf->radio_enabled && iwl_radio_kill_sw_enable_radio(priv)) {
-               IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - waiting for uCode\n");
-               goto out;
-       }
+       if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) {
+               if (conf->radio_enabled &&
+                   iwl_radio_kill_sw_enable_radio(priv)) {
+                       IWL_DEBUG_MAC80211(priv, "leave - RF-KILL - "
+                                                "waiting for uCode\n");
+                       goto out;
+               }
 
-       if (!conf->radio_enabled) {
-               iwl_radio_kill_sw_disable_radio(priv);
-               IWL_DEBUG_MAC80211(priv, "leave - radio disabled\n");
-               goto out;
+               if (!conf->radio_enabled) {
+                       iwl_radio_kill_sw_disable_radio(priv);
+                       IWL_DEBUG_MAC80211(priv, "leave - radio disabled\n");
+                       goto out;
+               }
        }
 
        if (iwl_is_rfkill(priv)) {
@@ -4546,11 +4550,6 @@ static ssize_t store_power_level(struct device *d,
 
        mutex_lock(&priv->mutex);
 
-       if (!iwl_is_ready(priv)) {
-               ret = -EAGAIN;
-               goto out;
-       }
-
        ret = strict_strtoul(buf, 10, &mode);
        if (ret)
                goto out;
@@ -4905,7 +4904,8 @@ static int iwl3945_setup_mac(struct iwl_priv *priv)
 
        /* Tell mac80211 our characteristics */
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
-                   IEEE80211_HW_NOISE_DBM;
+                   IEEE80211_HW_NOISE_DBM |
+                   IEEE80211_HW_SPECTRUM_MGMT;
 
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
index f8eb9097ff0a013b06aa61d76033c622488f6a1b..d16b26416e82097f8ba4cd4464eb9f4a7fd81145 100644 (file)
@@ -33,22 +33,12 @@ struct rx_radiotap_hdr {
        struct ieee80211_radiotap_header hdr;
        u8 flags;
        u8 rate;
-       u16 chan_freq;
-       u16 chan_flags;
-       u8 antenna;
        u8 antsignal;
-       u16 rx_flags;
-#if 0
-       u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18];
-#endif
 } __attribute__ ((packed));
 
 #define RX_RADIOTAP_PRESENT (                  \
        (1 << IEEE80211_RADIOTAP_FLAGS) |       \
        (1 << IEEE80211_RADIOTAP_RATE) |        \
-       (1 << IEEE80211_RADIOTAP_CHANNEL) |     \
-       (1 << IEEE80211_RADIOTAP_ANTENNA) |     \
        (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\
-       (1 << IEEE80211_RADIOTAP_RX_FLAGS) |    \
        0)
 
index 4f60948dde9c65d5051ea86b70aefb0da024b7e5..63d7e19ce9bd46f6753c15d7bafc2f2b6b48abe4 100644 (file)
@@ -351,19 +351,11 @@ static int process_rxed_802_11_packet(struct lbs_private *priv,
        radiotap_hdr.hdr.it_pad = 0;
        radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr));
        radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT);
-       /* unknown values */
-       radiotap_hdr.flags = 0;
-       radiotap_hdr.chan_freq = 0;
-       radiotap_hdr.chan_flags = 0;
-       radiotap_hdr.antenna = 0;
-       /* known values */
+       if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
+               radiotap_hdr.flags |= IEEE80211_RADIOTAP_F_BADFCS;
        radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate);
        /* XXX must check no carryout */
        radiotap_hdr.antsignal = prxpd->snr + prxpd->nf;
-       radiotap_hdr.rx_flags = 0;
-       if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
-               radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS;
-       //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18);
 
        /* chop the rxpd */
        skb_pull(skb, sizeof(struct rxpd));
index 2368b7f825a233f26f3b1406474f836bdb74208d..d4fdc8b7d7d8c7ff3c0321c9e4d60ba3d35f7147 100644 (file)
@@ -933,7 +933,6 @@ static int __init init_mac80211_hwsim(void)
                        BIT(NL80211_IFTYPE_STATION) |
                        BIT(NL80211_IFTYPE_AP) |
                        BIT(NL80211_IFTYPE_MESH_POINT);
-               hw->ampdu_queues = 1;
 
                hw->flags = IEEE80211_HW_MFP_CAPABLE;
 
@@ -1041,6 +1040,9 @@ static int __init init_mac80211_hwsim(void)
                        break;
                }
 
+               /* give the regulatory workqueue a chance to run */
+               if (regtest)
+                       schedule_timeout_interruptible(1);
                err = ieee80211_register_hw(hw);
                if (err < 0) {
                        printk(KERN_DEBUG "mac80211_hwsim: "
index cfc5f41aa1361e31a5ec5bfefe131eb41bdd713a..b45d6a4ed1e8ac1a2f6812a7a63a58fc951f0cc3 100644 (file)
@@ -1,9 +1,10 @@
 config P54_COMMON
        tristate "Softmac Prism54 support"
-       depends on MAC80211 && WLAN_80211 && FW_LOADER && EXPERIMENTAL
+       depends on MAC80211 && WLAN_80211 && EXPERIMENTAL
+       select FW_LOADER
        ---help---
-         This is common code for isl38xx based cards.
-         This module does nothing by itself - the USB/PCI frontends
+         This is common code for isl38xx/stlc45xx based modules.
+         This module does nothing by itself - the USB/PCI/SPI front-ends
          also need to be enabled in order to support any devices.
 
          These devices require softmac firmware which can be found at
@@ -17,31 +18,6 @@ config P54_USB
        select CRC32
        ---help---
          This driver is for USB isl38xx based wireless cards.
-         These are USB based adapters found in devices such as:
-
-         3COM 3CRWE254G72
-         SMC 2862W-G
-         Accton 802.11g WN4501 USB
-         Siemens Gigaset USB
-         Netgear WG121
-         Netgear WG111
-         Medion 40900, Roper Europe
-         Shuttle PN15, Airvast WM168g, IOGear GWU513
-         Linksys WUSB54G
-         Linksys WUSB54G Portable
-         DLink DWL-G120 Spinnaker
-         DLink DWL-G122
-         Belkin F5D7050 ver 1000
-         Cohiba Proto board
-         SMC 2862W-G version 2
-         U.S. Robotics U5 802.11g Adapter
-         FUJITSU E-5400 USB D1700
-         Sagem XG703A
-         DLink DWL-G120 Cohiba
-         Spinnaker Proto board
-         Linksys WUSB54AG
-         Inventel UR054G
-         Spinnaker DUT
 
          These devices require softmac firmware which can be found at
          http://prism54.org/
@@ -64,10 +40,15 @@ config P54_PCI
 
 config P54_SPI
        tristate "Prism54 SPI (stlc45xx) support"
-       depends on P54_COMMON && SPI_MASTER
+       depends on P54_COMMON && SPI_MASTER && GENERIC_HARDIRQS
        ---help---
          This driver is for stlc4550 or stlc4560 based wireless chips.
          This driver is experimental, untested and will probably only work on
          Nokia's N800/N810 Portable Internet Tablet.
 
          If you choose to build a module, it'll be called p54spi.
+
+config P54_LEDS
+       bool
+       depends on P54_COMMON && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = P54_COMMON)
+       default y
index 0a989834b70d38fec2bc5cbe1be70190f7f56b23..0c1b0577d4eed013a552e8325f8b23d4b37087c9 100644 (file)
@@ -21,9 +21,9 @@
 #include <linux/etherdevice.h>
 
 #include <net/mac80211.h>
-#ifdef CONFIG_MAC80211_LEDS
+#ifdef CONFIG_P54_LEDS
 #include <linux/leds.h>
-#endif /* CONFIG_MAC80211_LEDS */
+#endif /* CONFIG_P54_LEDS */
 
 #include "p54.h"
 #include "p54common.h"
@@ -2420,7 +2420,7 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
        return 0;
 }
 
-#ifdef CONFIG_MAC80211_LEDS
+#ifdef CONFIG_P54_LEDS
 static void p54_led_brightness_set(struct led_classdev *led_dev,
                                   enum led_brightness brightness)
 {
@@ -2508,7 +2508,7 @@ static void p54_unregister_leds(struct ieee80211_hw *dev)
        if (priv->assoc_led.registered)
                led_classdev_unregister(&priv->assoc_led.led_dev);
 }
-#endif /* CONFIG_MAC80211_LEDS */
+#endif /* CONFIG_P54_LEDS */
 
 static const struct ieee80211_ops p54_ops = {
        .tx                     = p54_tx,
@@ -2592,11 +2592,11 @@ int p54_register_common(struct ieee80211_hw *dev, struct device *pdev)
                return err;
        }
 
-       #ifdef CONFIG_MAC80211_LEDS
+#ifdef CONFIG_P54_LEDS
        err = p54_init_leds(dev);
        if (err)
                return err;
-       #endif /* CONFIG_MAC80211_LEDS */
+#endif /* CONFIG_P54_LEDS */
 
        dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy));
        return 0;
@@ -2610,9 +2610,9 @@ void p54_free_common(struct ieee80211_hw *dev)
        kfree(priv->output_limit);
        kfree(priv->curve_data);
 
-       #ifdef CONFIG_MAC80211_LEDS
+#ifdef CONFIG_P54_LEDS
        p54_unregister_leds(dev);
-       #endif /* CONFIG_MAC80211_LEDS */
+#endif /* CONFIG_P54_LEDS */
 }
 EXPORT_SYMBOL_GPL(p54_free_common);
 
index 24fdfdfee3df4efd28c8a09cd60f8b5c4aa6af2e..420fff42c0dd888e5ee9c997626809c1ec99e61f 100644 (file)
@@ -2425,6 +2425,8 @@ static struct usb_device_id rt73usb_device_table[] = {
        { USB_DEVICE(0x0df6, 0x9712), USB_DEVICE_DATA(&rt73usb_ops) },
        /* Surecom */
        { USB_DEVICE(0x0769, 0x31f3), USB_DEVICE_DATA(&rt73usb_ops) },
+       /* Tilgin */
+       { USB_DEVICE(0x6933, 0x5001), USB_DEVICE_DATA(&rt73usb_ops) },
        /* Philips */
        { USB_DEVICE(0x0471, 0x200a), USB_DEVICE_DATA(&rt73usb_ops) },
        /* Planex */
index b728541f2fb538c116367fb0241e1247b0859d64..3ab3eb95718928c53425b29dcc3b1c5eccb43b1a 100644 (file)
@@ -735,9 +735,9 @@ if (lp->tx_n_in_use > 0)
                if (tx_status & AC_SFLD_OK) {
                        int ncollisions;
 
-                       lp->stats.tx_packets++;
+                       dev->stats.tx_packets++;
                        ncollisions = tx_status & AC_SFLD_MAXCOL;
-                       lp->stats.collisions += ncollisions;
+                       dev->stats.collisions += ncollisions;
 #ifdef DEBUG_TX_INFO
                        if (ncollisions > 0)
                                printk(KERN_DEBUG
@@ -745,9 +745,9 @@ if (lp->tx_n_in_use > 0)
                                       dev->name, ncollisions);
 #endif
                } else {
-                       lp->stats.tx_errors++;
+                       dev->stats.tx_errors++;
                        if (tx_status & AC_SFLD_S10) {
-                               lp->stats.tx_carrier_errors++;
+                               dev->stats.tx_carrier_errors++;
 #ifdef DEBUG_TX_FAIL
                                printk(KERN_DEBUG
                                       "%s: wv_complete(): tx error: no CS.\n",
@@ -755,7 +755,7 @@ if (lp->tx_n_in_use > 0)
 #endif
                        }
                        if (tx_status & AC_SFLD_S9) {
-                               lp->stats.tx_carrier_errors++;
+                               dev->stats.tx_carrier_errors++;
 #ifdef DEBUG_TX_FAIL
                                printk(KERN_DEBUG
                                       "%s: wv_complete(): tx error: lost CTS.\n",
@@ -763,7 +763,7 @@ if (lp->tx_n_in_use > 0)
 #endif
                        }
                        if (tx_status & AC_SFLD_S8) {
-                               lp->stats.tx_fifo_errors++;
+                               dev->stats.tx_fifo_errors++;
 #ifdef DEBUG_TX_FAIL
                                printk(KERN_DEBUG
                                       "%s: wv_complete(): tx error: slow DMA.\n",
@@ -771,7 +771,7 @@ if (lp->tx_n_in_use > 0)
 #endif
                        }
                        if (tx_status & AC_SFLD_S6) {
-                               lp->stats.tx_heartbeat_errors++;
+                               dev->stats.tx_heartbeat_errors++;
 #ifdef DEBUG_TX_FAIL
                                printk(KERN_DEBUG
                                       "%s: wv_complete(): tx error: heart beat.\n",
@@ -779,7 +779,7 @@ if (lp->tx_n_in_use > 0)
 #endif
                        }
                        if (tx_status & AC_SFLD_S5) {
-                               lp->stats.tx_aborted_errors++;
+                               dev->stats.tx_aborted_errors++;
 #ifdef DEBUG_TX_FAIL
                                printk(KERN_DEBUG
                                       "%s: wv_complete(): tx error: too many collisions.\n",
@@ -1346,20 +1346,6 @@ static void wv_init_info(struct net_device * dev)
  * or wireless extensions
  */
 
-/*------------------------------------------------------------------*/
-/*
- * Get the current Ethernet statistics. This may be called with the
- * card open or closed.
- * Used when the user read /proc/net/dev
- */
-static en_stats *wavelan_get_stats(struct net_device * dev)
-{
-#ifdef DEBUG_IOCTL_TRACE
-       printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
-#endif
-
-       return &((net_local *)netdev_priv(dev))->stats;
-}
 
 /*------------------------------------------------------------------*/
 /*
@@ -2466,7 +2452,7 @@ wv_packet_read(struct net_device * dev, u16 buf_off, int sksize)
                       "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n",
                       dev->name, sksize);
 #endif
-               lp->stats.rx_dropped++;
+               dev->stats.rx_dropped++;
                return;
        }
 
@@ -2526,8 +2512,8 @@ wv_packet_read(struct net_device * dev, u16 buf_off, int sksize)
        netif_rx(skb);
 
        /* Keep statistics up to date */
-       lp->stats.rx_packets++;
-       lp->stats.rx_bytes += sksize;
+       dev->stats.rx_packets++;
+       dev->stats.rx_bytes += sksize;
 
 #ifdef DEBUG_RX_TRACE
        printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
@@ -2608,7 +2594,7 @@ static void wv_receive(struct net_device * dev)
 #endif
                } else {        /* If reception was no successful */
 
-                       lp->stats.rx_errors++;
+                       dev->stats.rx_errors++;
 
 #ifdef DEBUG_RX_INFO
                        printk(KERN_DEBUG
@@ -2624,7 +2610,7 @@ static void wv_receive(struct net_device * dev)
 #endif
 
                        if ((fd.fd_status & FD_STATUS_S7) != 0) {
-                               lp->stats.rx_length_errors++;
+                               dev->stats.rx_length_errors++;
 #ifdef DEBUG_RX_FAIL
                                printk(KERN_DEBUG
                                       "%s: wv_receive(): frame too short.\n",
@@ -2633,7 +2619,7 @@ static void wv_receive(struct net_device * dev)
                        }
 
                        if ((fd.fd_status & FD_STATUS_S8) != 0) {
-                               lp->stats.rx_over_errors++;
+                               dev->stats.rx_over_errors++;
 #ifdef DEBUG_RX_FAIL
                                printk(KERN_DEBUG
                                       "%s: wv_receive(): rx DMA overrun.\n",
@@ -2642,7 +2628,7 @@ static void wv_receive(struct net_device * dev)
                        }
 
                        if ((fd.fd_status & FD_STATUS_S9) != 0) {
-                               lp->stats.rx_fifo_errors++;
+                               dev->stats.rx_fifo_errors++;
 #ifdef DEBUG_RX_FAIL
                                printk(KERN_DEBUG
                                       "%s: wv_receive(): ran out of resources.\n",
@@ -2651,7 +2637,7 @@ static void wv_receive(struct net_device * dev)
                        }
 
                        if ((fd.fd_status & FD_STATUS_S10) != 0) {
-                               lp->stats.rx_frame_errors++;
+                               dev->stats.rx_frame_errors++;
 #ifdef DEBUG_RX_FAIL
                                printk(KERN_DEBUG
                                       "%s: wv_receive(): alignment error.\n",
@@ -2660,7 +2646,7 @@ static void wv_receive(struct net_device * dev)
                        }
 
                        if ((fd.fd_status & FD_STATUS_S11) != 0) {
-                               lp->stats.rx_crc_errors++;
+                               dev->stats.rx_crc_errors++;
 #ifdef DEBUG_RX_FAIL
                                printk(KERN_DEBUG
                                       "%s: wv_receive(): CRC error.\n",
@@ -2826,7 +2812,7 @@ static int wv_packet_write(struct net_device * dev, void *buf, short length)
        dev->trans_start = jiffies;
 
        /* Keep stats up to date. */
-       lp->stats.tx_bytes += length;
+       dev->stats.tx_bytes += length;
 
        if (lp->tx_first_in_use == I82586NULL)
                lp->tx_first_in_use = txblock;
@@ -4038,6 +4024,22 @@ static int wavelan_close(struct net_device * dev)
        return 0;
 }
 
+static const struct net_device_ops wavelan_netdev_ops = {
+       .ndo_open               = wavelan_open,
+       .ndo_stop               = wavelan_close,
+       .ndo_start_xmit         = wavelan_packet_xmit,
+       .ndo_set_multicast_list = wavelan_set_multicast_list,
+        .ndo_tx_timeout                = wavelan_watchdog,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+#ifdef SET_MAC_ADDRESS
+       .ndo_set_mac_address    = wavelan_set_mac_address
+#else
+       .ndo_set_mac_address    = eth_mac_addr,
+#endif
+};
+
+
 /*------------------------------------------------------------------*/
 /*
  * Probe an I/O address, and if the WaveLAN is there configure the
@@ -4130,17 +4132,8 @@ static int __init wavelan_config(struct net_device *dev, unsigned short ioaddr)
        /* Init spinlock */
        spin_lock_init(&lp->spinlock);
 
-       dev->open = wavelan_open;
-       dev->stop = wavelan_close;
-       dev->hard_start_xmit = wavelan_packet_xmit;
-       dev->get_stats = wavelan_get_stats;
-       dev->set_multicast_list = &wavelan_set_multicast_list;
-        dev->tx_timeout                = &wavelan_watchdog;
-        dev->watchdog_timeo    = WATCHDOG_JIFFIES;
-#ifdef SET_MAC_ADDRESS
-       dev->set_mac_address = &wavelan_set_mac_address;
-#endif                         /* SET_MAC_ADDRESS */
-
+       dev->netdev_ops = &wavelan_netdev_ops;
+       dev->watchdog_timeo = WATCHDOG_JIFFIES;
        dev->wireless_handlers = &wavelan_handler_def;
        lp->wireless_data.spy_data = &lp->spy_data;
        dev->wireless_data = &lp->wireless_data;
index 44d31bbf39e49d57409b5f41a3e487040974d1e6..2daa0210d7898b02f7feae6aa6a5a76f72827839 100644 (file)
@@ -459,11 +459,9 @@ static const char  *version        = "wavelan.c : v24 (SMP + wireless extensions) 11/12/
 /****************************** TYPES ******************************/
 
 /* Shortcuts */
-typedef struct net_device_stats        en_stats;
 typedef struct iw_statistics   iw_stats;
 typedef struct iw_quality      iw_qual;
-typedef struct iw_freq         iw_freq;
-typedef struct net_local       net_local;
+typedef struct iw_freq         iw_freq;typedef struct net_local        net_local;
 typedef struct timer_list      timer_list;
 
 /* Basic types */
@@ -475,15 +473,12 @@ typedef u_char            mac_addr[WAVELAN_ADDR_SIZE];    /* Hardware address */
  * For each network interface, Linux keeps data in two structures:  "device"
  * keeps the generic data (same format for everybody) and "net_local" keeps
  * additional specific data.
- * Note that some of this specific data is in fact generic (en_stats, for
- * example).
  */
 struct net_local
 {
   net_local *  next;           /* linked list of the devices */
   struct net_device *  dev;            /* reverse link */
   spinlock_t   spinlock;       /* Serialize access to the hardware (SMP) */
-  en_stats     stats;          /* Ethernet interface statistics */
   int          nresets;        /* number of hardware resets */
   u_char       reconfig_82586; /* We need to reconfigure the controller. */
   u_char       promiscuous;    /* promiscuous mode */
@@ -601,8 +596,6 @@ static void
 static inline void
        wv_init_info(struct net_device *);      /* display startup info */
 /* ------------------- IOCTL, STATS & RECONFIG ------------------- */
-static en_stats        *
-       wavelan_get_stats(struct net_device *); /* Give stats /proc/net/dev */
 static iw_stats *
        wavelan_get_wireless_stats(struct net_device *);
 static void
index b1b947edcf0157b409279de2d6ae235875de21e8..540a2948596cd007284667f933b600cbf1f1f46d 100644 (file)
@@ -53,11 +53,11 @@ config SSB_B43_PCI_BRIDGE
 
 config SSB_PCMCIAHOST_POSSIBLE
        bool
-       depends on SSB && (PCMCIA = y || PCMCIA = SSB) && EXPERIMENTAL
+       depends on SSB && (PCMCIA = y || PCMCIA = SSB)
        default y
 
 config SSB_PCMCIAHOST
-       bool "Support for SSB on PCMCIA-bus host (EXPERIMENTAL)"
+       bool "Support for SSB on PCMCIA-bus host"
        depends on SSB_PCMCIAHOST_POSSIBLE
        select SSB_SPROM
        help
@@ -107,14 +107,14 @@ config SSB_DRIVER_PCICORE
          If unsure, say Y
 
 config SSB_PCICORE_HOSTMODE
-       bool "Hostmode support for SSB PCI core (EXPERIMENTAL)"
-       depends on SSB_DRIVER_PCICORE && SSB_DRIVER_MIPS && EXPERIMENTAL
+       bool "Hostmode support for SSB PCI core"
+       depends on SSB_DRIVER_PCICORE && SSB_DRIVER_MIPS
        help
          PCIcore hostmode operation (external PCI bus).
 
 config SSB_DRIVER_MIPS
-       bool "SSB Broadcom MIPS core driver (EXPERIMENTAL)"
-       depends on SSB && MIPS && EXPERIMENTAL
+       bool "SSB Broadcom MIPS core driver"
+       depends on SSB && MIPS
        select SSB_SERIAL
        help
          Driver for the Sonics Silicon Backplane attached
@@ -129,8 +129,8 @@ config SSB_EMBEDDED
        default y
 
 config SSB_DRIVER_EXTIF
-       bool "SSB Broadcom EXTIF core driver (EXPERIMENTAL)"
-       depends on SSB_DRIVER_MIPS && EXPERIMENTAL
+       bool "SSB Broadcom EXTIF core driver"
+       depends on SSB_DRIVER_MIPS
        help
          Driver for the Sonics Silicon Backplane attached
          Broadcom EXTIF core.
index 27a677584a4cb50f6be679628303c17a700defb0..ef9c6a04ad8f01af0d402f9f0f03a08175c04524 100644 (file)
@@ -18,6 +18,7 @@
 
 static const struct pci_device_id b43_pci_bridge_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4301) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4306) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4307) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4311) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4312) },
index b1bb817d1427875a4ddc2923084fb9dfebc65b57..4b501b48ce86efdb43dffd84d62ed8116d53d7ce 100644 (file)
 #include <linux/types.h>
 #include <asm/byteorder.h>
 
+/*
+ * DS bit usage
+ *
+ * TA = transmitter address
+ * RA = receiver address
+ * DA = destination address
+ * SA = source address
+ *
+ * ToDS    FromDS  A1(RA)  A2(TA)  A3      A4      Use
+ * -----------------------------------------------------------------
+ *  0       0       DA      SA      BSSID   -       IBSS/DLS
+ *  0       1       DA      BSSID   SA      -       AP -> STA
+ *  1       0       BSSID   SA      DA      -       AP <- STA
+ *  1       1       RA      TA      DA      SA      unspecified (WDS)
+ */
+
 #define FCS_LEN 4
 
 #define IEEE80211_FCTL_VERS            0x0003
@@ -851,6 +867,7 @@ struct ieee80211_ht_info {
 /* Authentication algorithms */
 #define WLAN_AUTH_OPEN 0
 #define WLAN_AUTH_SHARED_KEY 1
+#define WLAN_AUTH_FT 2
 #define WLAN_AUTH_LEAP 128
 
 #define WLAN_AUTH_CHALLENGE_LEN 128
index 60e16a551dd652c2b5fe351b0e18a4876b54f16d..673f2209453d9e2237a15d848e29ff9ae2381f21 100644 (file)
@@ -153,7 +153,6 @@ struct frhdr
 
 struct dlci_local
 {
-   struct net_device_stats stats;
    struct net_device      *master;
    struct net_device      *slave;
    struct dlci_conf       config;
index be3ebd7e8ce58d62d6c59c2a0b489017a4dc774e..1b55952a17f6f1d12d30c6fbcbcb52f5849855a6 100644 (file)
@@ -32,6 +32,7 @@
 #ifdef __KERNEL__
 #include <linux/timer.h>
 #include <linux/delay.h>
+#include <linux/mm.h>
 #include <asm/atomic.h>
 #include <asm/cache.h>
 #include <asm/byteorder.h>
index adbc50a20ec21714434ebf491f7d1e87f358c808..7b1a652066c08a205a255223cc754169de1905ae 100644 (file)
@@ -437,6 +437,29 @@ extern void xt_free_table_info(struct xt_table_info *info);
 extern void xt_table_entry_swap_rcu(struct xt_table_info *old,
                                    struct xt_table_info *new);
 
+/*
+ * This helper is performance critical and must be inlined
+ */
+static inline unsigned long ifname_compare_aligned(const char *_a,
+                                                  const char *_b,
+                                                  const char *_mask)
+{
+       const unsigned long *a = (const unsigned long *)_a;
+       const unsigned long *b = (const unsigned long *)_b;
+       const unsigned long *mask = (const unsigned long *)_mask;
+       unsigned long ret;
+
+       ret = (a[0] ^ b[0]) & mask[0];
+       if (IFNAMSIZ > sizeof(unsigned long))
+               ret |= (a[1] ^ b[1]) & mask[1];
+       if (IFNAMSIZ > 2 * sizeof(unsigned long))
+               ret |= (a[2] ^ b[2]) & mask[2];
+       if (IFNAMSIZ > 3 * sizeof(unsigned long))
+               ret |= (a[3] ^ b[3]) & mask[3];
+       BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
+       return ret;
+}
+
 #ifdef CONFIG_COMPAT
 #include <net/compat.h>
 
index f33aa08dd9b37d6a7b8e1414ec99022ada1673a1..cbe8ce3bf486121ca2cbaa49d9a1b022a766ff28 100644 (file)
  *     %NL80211_ATTR_IE. If the command succeeds, the requested data will be
  *     added to all specified management frames generated by
  *     kernel/firmware/driver.
+ *     Note: This command has been removed and it is only reserved at this
+ *     point to avoid re-using existing command number. The functionality this
+ *     command was planned for has been provided with cleaner design with the
+ *     option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN,
+ *     NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE,
+ *     NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE.
  *
  * @NL80211_CMD_GET_SCAN: get scan results
  * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
  *     %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
  *     to (%NL80211_ATTR_REG_ALPHA2).
  *
+ * @NL80211_CMD_AUTHENTICATE: authentication request and notification.
+ *     This command is used both as a command (request to authenticate) and
+ *     as an event on the "mlme" multicast group indicating completion of the
+ *     authentication process.
+ *     When used as a command, %NL80211_ATTR_IFINDEX is used to identify the
+ *     interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and
+ *     BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
+ *     the SSID (mainly for association, but is included in authentication
+ *     request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used
+ *     to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE
+ *     is used to specify the authentication type. %NL80211_ATTR_IE is used to
+ *     define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs)
+ *     to be added to the frame.
+ *     When used as an event, this reports reception of an Authentication
+ *     frame in station and IBSS modes when the local MLME processed the
+ *     frame, i.e., it was for the local STA and was received in correct
+ *     state. This is similar to MLME-AUTHENTICATE.confirm primitive in the
+ *     MLME SAP interface (kernel providing MLME, userspace SME). The
+ *     included NL80211_ATTR_FRAME attribute contains the management frame
+ *     (including both the header and frame body, but not FCS).
+ * @NL80211_CMD_ASSOCIATE: association request and notification; like
+ *     NL80211_CMD_AUTHENTICATE but for Association and Reassociation
+ *     (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request,
+ *     MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives).
+ * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like
+ *     NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to
+ *     MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication
+ *     primitives).
+ * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like
+ *     NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to
+ *     MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives).
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -206,7 +244,7 @@ enum nl80211_commands {
        NL80211_CMD_GET_MESH_PARAMS,
        NL80211_CMD_SET_MESH_PARAMS,
 
-       NL80211_CMD_SET_MGMT_EXTRA_IE,
+       NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */,
 
        NL80211_CMD_GET_REG,
 
@@ -217,6 +255,11 @@ enum nl80211_commands {
 
        NL80211_CMD_REG_CHANGE,
 
+       NL80211_CMD_AUTHENTICATE,
+       NL80211_CMD_ASSOCIATE,
+       NL80211_CMD_DEAUTHENTICATE,
+       NL80211_CMD_DISASSOCIATE,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -230,8 +273,11 @@ enum nl80211_commands {
  */
 #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS
 #define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE
-
 #define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE
+#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE
+#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE
+#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE
+#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
 
 /**
  * enum nl80211_attrs - nl80211 netlink attributes
@@ -349,6 +395,19 @@ enum nl80211_commands {
  * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently
  *     set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
  *
+ * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies
+ *     an array of command numbers (i.e. a mapping index to command number)
+ *     that the driver for the given wiphy supports.
+ *
+ * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header
+ *     and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and
+ *     NL80211_CMD_ASSOCIATE events
+ * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets)
+ * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type,
+ *     represented as a u32
+ * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and
+ *     %NL80211_CMD_DISASSOCIATE, u16
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -426,6 +485,13 @@ enum nl80211_attrs {
        NL80211_ATTR_REG_INITIATOR,
        NL80211_ATTR_REG_TYPE,
 
+       NL80211_ATTR_SUPPORTED_COMMANDS,
+
+       NL80211_ATTR_FRAME,
+       NL80211_ATTR_SSID,
+       NL80211_ATTR_AUTH_TYPE,
+       NL80211_ATTR_REASON_CODE,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -445,6 +511,10 @@ enum nl80211_attrs {
 #define NL80211_ATTR_IE NL80211_ATTR_IE
 #define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR
 #define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE
+#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME
+#define NL80211_ATTR_SSID NL80211_ATTR_SSID
+#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE
+#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE
 
 #define NL80211_MAX_SUPP_RATES                 32
 #define NL80211_MAX_SUPP_REG_RULES             32
@@ -978,4 +1048,18 @@ enum nl80211_bss {
        NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1
 };
 
+/**
+ * enum nl80211_auth_type - AuthenticationType
+ *
+ * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication
+ * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only)
+ * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r)
+ * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP)
+ */
+enum nl80211_auth_type {
+       NL80211_AUTHTYPE_OPEN_SYSTEM,
+       NL80211_AUTHTYPE_SHARED_KEY,
+       NL80211_AUTHTYPE_FT,
+       NL80211_AUTHTYPE_NETWORK_EAP,
+};
 #endif /* __LINUX_NL80211_H */
index 097f410edefa78b3672c499b8c97d65e8463ff95..05dfa7c4fb6432817b5dd7b3f5d1ccb7293bb2ea 100644 (file)
 #define PCI_DEVICE_ID_KORENIX_JETCARDF0        0x1600
 #define PCI_DEVICE_ID_KORENIX_JETCARDF1        0x16ff
 
+#define PCI_VENDOR_ID_QMI              0x1a32
+
 #define PCI_VENDOR_ID_TEKRAM           0x1de1
 #define PCI_DEVICE_ID_TEKRAM_DC290     0xdc29
 
index 50f3fd9ff52430c7bc196bdddfc3dd17df3abb35..5389afdc129756abcef4c088b2c7adfd84165670 100644 (file)
@@ -471,26 +471,6 @@ struct ieee80211_txq_params {
        u8 aifs;
 };
 
-/**
- * struct mgmt_extra_ie_params - Extra management frame IE parameters
- *
- * Used to add extra IE(s) into management frames. If the driver cannot add the
- * requested data into all management frames of the specified subtype that are
- * generated in kernel or firmware/hardware, it must reject the configuration
- * call. The IE data buffer is added to the end of the specified management
- * frame body after all other IEs. This addition is not applied to frames that
- * are injected through a monitor interface.
- *
- * @subtype: Management frame subtype
- * @ies: IE data buffer or %NULL to remove previous data
- * @ies_len: Length of @ies in octets
- */
-struct mgmt_extra_ie_params {
-       u8 subtype;
-       u8 *ies;
-       int ies_len;
-};
-
 /* from net/wireless.h */
 struct wiphy;
 
@@ -559,6 +539,7 @@ enum cfg80211_signal_type {
  *     is no guarantee that these are well-formed!)
  * @len_information_elements: total length of the information elements
  * @signal: signal strength value (type depends on the wiphy's signal_type)
+ * @hold: BSS should not expire
  * @free_priv: function pointer to free private data
  * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
  */
@@ -578,6 +559,105 @@ struct cfg80211_bss {
        u8 priv[0] __attribute__((__aligned__(sizeof(void *))));
 };
 
+/**
+ * struct cfg80211_auth_request - Authentication request data
+ *
+ * This structure provides information needed to complete IEEE 802.11
+ * authentication.
+ * NOTE: This structure will likely change when more code from mac80211 is
+ * moved into cfg80211 so that non-mac80211 drivers can benefit from it, too.
+ * Before using this in a driver that does not use mac80211, it would be better
+ * to check the status of that work and better yet, volunteer to work on it.
+ *
+ * @chan: The channel to use or %NULL if not specified (auto-select based on
+ *     scan results)
+ * @peer_addr: The address of the peer STA (AP BSSID in infrastructure case);
+ *     this field is required to be present; if the driver wants to help with
+ *     BSS selection, it should use (yet to be added) MLME event to allow user
+ *     space SME to be notified of roaming candidate, so that the SME can then
+ *     use the authentication request with the recommended BSSID and whatever
+ *     other data may be needed for authentication/association
+ * @ssid: SSID or %NULL if not yet available
+ * @ssid_len: Length of ssid in octets
+ * @auth_type: Authentication type (algorithm)
+ * @ie: Extra IEs to add to Authentication frame or %NULL
+ * @ie_len: Length of ie buffer in octets
+ */
+struct cfg80211_auth_request {
+       struct ieee80211_channel *chan;
+       u8 *peer_addr;
+       const u8 *ssid;
+       size_t ssid_len;
+       enum nl80211_auth_type auth_type;
+       const u8 *ie;
+       size_t ie_len;
+};
+
+/**
+ * struct cfg80211_assoc_request - (Re)Association request data
+ *
+ * This structure provides information needed to complete IEEE 802.11
+ * (re)association.
+ * NOTE: This structure will likely change when more code from mac80211 is
+ * moved into cfg80211 so that non-mac80211 drivers can benefit from it, too.
+ * Before using this in a driver that does not use mac80211, it would be better
+ * to check the status of that work and better yet, volunteer to work on it.
+ *
+ * @chan: The channel to use or %NULL if not specified (auto-select based on
+ *     scan results)
+ * @peer_addr: The address of the peer STA (AP BSSID); this field is required
+ *     to be present and the STA must be in State 2 (authenticated) with the
+ *     peer STA
+ * @ssid: SSID
+ * @ssid_len: Length of ssid in octets
+ * @ie: Extra IEs to add to (Re)Association Request frame or %NULL
+ * @ie_len: Length of ie buffer in octets
+ */
+struct cfg80211_assoc_request {
+       struct ieee80211_channel *chan;
+       u8 *peer_addr;
+       const u8 *ssid;
+       size_t ssid_len;
+       const u8 *ie;
+       size_t ie_len;
+};
+
+/**
+ * struct cfg80211_deauth_request - Deauthentication request data
+ *
+ * This structure provides information needed to complete IEEE 802.11
+ * deauthentication.
+ *
+ * @peer_addr: The address of the peer STA (AP BSSID); this field is required
+ *     to be present and the STA must be authenticated with the peer STA
+ * @ie: Extra IEs to add to Deauthentication frame or %NULL
+ * @ie_len: Length of ie buffer in octets
+ */
+struct cfg80211_deauth_request {
+       u8 *peer_addr;
+       u16 reason_code;
+       const u8 *ie;
+       size_t ie_len;
+};
+
+/**
+ * struct cfg80211_disassoc_request - Disassociation request data
+ *
+ * This structure provides information needed to complete IEEE 802.11
+ * disassocation.
+ *
+ * @peer_addr: The address of the peer STA (AP BSSID); this field is required
+ *     to be present and the STA must be associated with the peer STA
+ * @ie: Extra IEs to add to Disassociation frame or %NULL
+ * @ie_len: Length of ie buffer in octets
+ */
+struct cfg80211_disassoc_request {
+       u8 *peer_addr;
+       u16 reason_code;
+       const u8 *ie;
+       size_t ie_len;
+};
+
 /**
  * struct cfg80211_ops - backend description for wireless configuration
  *
@@ -644,12 +724,15 @@ struct cfg80211_bss {
  *
  * @set_channel: Set channel
  *
- * @set_mgmt_extra_ie: Set extra IE data for management frames
- *
  * @scan: Request to do a scan. If returning zero, the scan request is given
  *     the driver, and will be valid until passed to cfg80211_scan_done().
  *     For scan results, call cfg80211_inform_bss(); you can call this outside
  *     the scan/scan_done bracket too.
+ *
+ * @auth: Request to authenticate with the specified peer
+ * @assoc: Request to (re)associate with the specified peer
+ * @deauth: Request to deauthenticate from the specified peer
+ * @disassoc: Request to disassociate from the specified peer
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy);
@@ -724,12 +807,17 @@ struct cfg80211_ops {
                               struct ieee80211_channel *chan,
                               enum nl80211_channel_type channel_type);
 
-       int     (*set_mgmt_extra_ie)(struct wiphy *wiphy,
-                                    struct net_device *dev,
-                                    struct mgmt_extra_ie_params *params);
-
        int     (*scan)(struct wiphy *wiphy, struct net_device *dev,
                        struct cfg80211_scan_request *request);
+
+       int     (*auth)(struct wiphy *wiphy, struct net_device *dev,
+                       struct cfg80211_auth_request *req);
+       int     (*assoc)(struct wiphy *wiphy, struct net_device *dev,
+                        struct cfg80211_assoc_request *req);
+       int     (*deauth)(struct wiphy *wiphy, struct net_device *dev,
+                         struct cfg80211_deauth_request *req);
+       int     (*disassoc)(struct wiphy *wiphy, struct net_device *dev,
+                           struct cfg80211_disassoc_request *req);
 };
 
 /* temporary wext handlers */
@@ -807,4 +895,67 @@ void cfg80211_put_bss(struct cfg80211_bss *bss);
  */
 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 
+/**
+ * cfg80211_send_rx_auth - notification of processed authentication
+ * @dev: network device
+ * @buf: authentication frame (header + body)
+ * @len: length of the frame data
+ *
+ * This function is called whenever an authentication has been processed in
+ * station mode.
+ */
+void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
+
+/**
+ * cfg80211_send_rx_assoc - notification of processed association
+ * @dev: network device
+ * @buf: (re)association response frame (header + body)
+ * @len: length of the frame data
+ *
+ * This function is called whenever a (re)association response has been
+ * processed in station mode.
+ */
+void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len);
+
+/**
+ * cfg80211_send_rx_deauth - notification of processed deauthentication
+ * @dev: network device
+ * @buf: deauthentication frame (header + body)
+ * @len: length of the frame data
+ *
+ * This function is called whenever deauthentication has been processed in
+ * station mode.
+ */
+void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf,
+                            size_t len);
+
+/**
+ * cfg80211_send_rx_disassoc - notification of processed disassociation
+ * @dev: network device
+ * @buf: disassociation response frame (header + body)
+ * @len: length of the frame data
+ *
+ * This function is called whenever disassociation has been processed in
+ * station mode.
+ */
+void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf,
+                              size_t len);
+
+/**
+ * cfg80211_hold_bss - exclude bss from expiration
+ * @bss: bss which should not expire
+ *
+ * In a case when the BSS is not updated but it shouldn't expire this
+ * function can be used to mark the BSS to be excluded from expiration.
+ */
+void cfg80211_hold_bss(struct cfg80211_bss *bss);
+
+/**
+ * cfg80211_unhold_bss - remove expiration exception from the BSS
+ * @bss: bss which can expire again
+ *
+ * This function marks the BSS to be expirable again.
+ */
+void cfg80211_unhold_bss(struct cfg80211_bss *bss);
+
 #endif /* __NET_CFG80211_H */
diff --git a/include/net/ethoc.h b/include/net/ethoc.h
new file mode 100644 (file)
index 0000000..96f3789
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * linux/include/net/ethoc.h
+ *
+ * Copyright (C) 2008-2009 Avionic Design GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Written by Thierry Reding <thierry.reding@avionic-design.de>
+ */
+
+#ifndef LINUX_NET_ETHOC_H
+#define LINUX_NET_ETHOC_H 1
+
+struct ethoc_platform_data {
+       u8 hwaddr[IFHWADDRLEN];
+       s8 phy_id;
+};
+
+#endif /* !LINUX_NET_ETHOC_H */
+
index 384698cb773a81b2667a4e7dea8c2c8e763de4d5..23c3f3d97779e8682943b2ac3321c39bce8ff5a0 100644 (file)
@@ -230,8 +230,10 @@ enum ieee80211_radiotap_type {
                                                 * 802.11 header and payload
                                                 * (to 32-bit boundary)
                                                 */
+#define IEEE80211_RADIOTAP_F_BADFCS    0x40    /* bad FCS */
+
 /* For IEEE80211_RADIOTAP_RX_FLAGS */
-#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001  /* frame failed crc check */
+#define IEEE80211_RADIOTAP_F_RX_BADPLCP        0x0002  /* frame has bad PLCP */
 
 /* For IEEE80211_RADIOTAP_TX_FLAGS */
 #define IEEE80211_RADIOTAP_F_TX_FAIL   0x0001  /* failed due to excessive
index 12a52efcd0d1d8a864224347f3ccf3601c942c25..3b83a80e3fe0d69ec3e0c2a1fa6a48f71297374b 100644 (file)
@@ -93,12 +93,9 @@ struct ieee80211_ht_bss_info {
  * enum ieee80211_max_queues - maximum number of queues
  *
  * @IEEE80211_MAX_QUEUES: Maximum number of regular device queues.
- * @IEEE80211_MAX_AMPDU_QUEUES: Maximum number of queues usable
- *     for A-MPDU operation.
  */
 enum ieee80211_max_queues {
-       IEEE80211_MAX_QUEUES =          16,
-       IEEE80211_MAX_AMPDU_QUEUES =    16,
+       IEEE80211_MAX_QUEUES =          4,
 };
 
 /**
@@ -245,6 +242,12 @@ struct ieee80211_bss_conf {
  * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be
  *     set by rate control algorithms to indicate probe rate, will
  *     be cleared for fragmented frames (except on the last fragment)
+ * @IEEE80211_TX_INTFL_RCALGO: mac80211 internal flag, do not test or
+ *     set this flag in the driver; indicates that the rate control
+ *     algorithm was used and should be notified of TX status
+ * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211,
+ *     used to indicate that a pending frame requires TX processing before
+ *     it can be sent out.
  */
 enum mac80211_tx_control_flags {
        IEEE80211_TX_CTL_REQ_TX_STATUS          = BIT(0),
@@ -260,6 +263,8 @@ enum mac80211_tx_control_flags {
        IEEE80211_TX_STAT_AMPDU                 = BIT(10),
        IEEE80211_TX_STAT_AMPDU_NO_BACK         = BIT(11),
        IEEE80211_TX_CTL_RATE_CTRL_PROBE        = BIT(12),
+       IEEE80211_TX_INTFL_RCALGO               = BIT(13),
+       IEEE80211_TX_INTFL_NEED_TXPROCESSING    = BIT(14),
 };
 
 /**
@@ -520,12 +525,6 @@ enum ieee80211_conf_flags {
        IEEE80211_CONF_PS               = (1<<1),
 };
 
-/* XXX: remove all this once drivers stop trying to use it */
-static inline int __deprecated __IEEE80211_CONF_SHORT_SLOT_TIME(void)
-{
-       return 0;
-}
-#define IEEE80211_CONF_SHORT_SLOT_TIME (__IEEE80211_CONF_SHORT_SLOT_TIME())
 
 /**
  * enum ieee80211_conf_changed - denotes which configuration changed
@@ -888,6 +887,10 @@ enum ieee80211_tkip_key_type {
  *
  * @IEEE80211_HW_MFP_CAPABLE:
  *     Hardware supports management frame protection (MFP, IEEE 802.11w).
+ *
+ * @IEEE80211_HW_BEACON_FILTER:
+ *     Hardware supports dropping of irrelevant beacon frames to
+ *     avoid waking up cpu.
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_RX_INCLUDES_FCS                    = 1<<1,
@@ -903,6 +906,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_PS_NULLFUNC_STACK                  = 1<<11,
        IEEE80211_HW_SUPPORTS_DYNAMIC_PS                = 1<<12,
        IEEE80211_HW_MFP_CAPABLE                        = 1<<13,
+       IEEE80211_HW_BEACON_FILTER                      = 1<<14,
 };
 
 /**
@@ -945,12 +949,6 @@ enum ieee80211_hw_flags {
  *     data packets. WMM/QoS requires at least four, these
  *     queues need to have configurable access parameters.
  *
- * @ampdu_queues: number of available hardware transmit queues
- *     for A-MPDU packets, these have no access parameters
- *     because they're used only for A-MPDU frames. Note that
- *     mac80211 will not currently use any of the regular queues
- *     for aggregation.
- *
  * @rate_control_algorithm: rate control algorithm for this hardware.
  *     If unset (NULL), the default algorithm will be used. Must be
  *     set before calling ieee80211_register_hw().
@@ -975,7 +973,6 @@ struct ieee80211_hw {
        int vif_data_size;
        int sta_data_size;
        u16 queues;
-       u16 ampdu_queues;
        u16 max_listen_interval;
        s8 max_signal;
        u8 max_rates;
@@ -1017,11 +1014,6 @@ static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, u8 *addr)
        memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN);
 }
 
-static inline int ieee80211_num_regular_queues(struct ieee80211_hw *hw)
-{
-       return hw->queues;
-}
-
 static inline struct ieee80211_rate *
 ieee80211_get_tx_rate(const struct ieee80211_hw *hw,
                      const struct ieee80211_tx_info *c)
@@ -1131,6 +1123,24 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
  * value, or by the stack if all nullfunc handling is in the stack.
  */
 
+/**
+ * DOC: Beacon filter support
+ *
+ * Some hardware have beacon filter support to reduce host cpu wakeups
+ * which will reduce system power consumption. It usuallly works so that
+ * the firmware creates a checksum of the beacon but omits all constantly
+ * changing elements (TSF, TIM etc). Whenever the checksum changes the
+ * beacon is forwarded to the host, otherwise it will be just dropped. That
+ * way the host will only receive beacons where some relevant information
+ * (for example ERP protection or WMM settings) have changed.
+ *
+ * Beacon filter support is informed with %IEEE80211_HW_BEACON_FILTER flag.
+ * The driver needs to enable beacon filter support whenever power save is
+ * enabled, that is %IEEE80211_CONF_PS is set. When power save is enabled,
+ * the stack will not check for beacon miss at all and the driver needs to
+ * notify about complete loss of beacons with ieee80211_beacon_loss().
+ */
+
 /**
  * DOC: Frame filtering
  *
@@ -1220,14 +1230,14 @@ enum ieee80211_filter_flags {
  * @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation
  * @IEEE80211_AMPDU_TX_START: start Tx aggregation
  * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation
- * @IEEE80211_AMPDU_TX_RESUME: resume TX aggregation
+ * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational
  */
 enum ieee80211_ampdu_mlme_action {
        IEEE80211_AMPDU_RX_START,
        IEEE80211_AMPDU_RX_STOP,
        IEEE80211_AMPDU_TX_START,
        IEEE80211_AMPDU_TX_STOP,
-       IEEE80211_AMPDU_TX_RESUME,
+       IEEE80211_AMPDU_TX_OPERATIONAL,
 };
 
 /**
@@ -1318,11 +1328,13 @@ enum ieee80211_ampdu_mlme_action {
  *
  * @hw_scan: Ask the hardware to service the scan request, no need to start
  *     the scan state machine in stack. The scan must honour the channel
- *     configuration done by the regulatory agent in the wiphy's registered
- *     bands. When the scan finishes, ieee80211_scan_completed() must be
- *     called; note that it also must be called when the scan cannot finish
- *     because the hardware is turned off! Anything else is a bug!
- *     Returns a negative error code which will be seen in userspace.
+ *     configuration done by the regulatory agent in the wiphy's
+ *     registered bands. The hardware (or the driver) needs to make sure
+ *     that power save is disabled. When the scan finishes,
+ *     ieee80211_scan_completed() must be called; note that it also must
+ *     be called when the scan cannot finish because the hardware is
+ *     turned off! Anything else is a bug! Returns a negative error code
+ *     which will be seen in userspace.
  *
  * @sw_scan_start: Notifier function that is called just before a software scan
  *     is started. Can be NULL, if the driver doesn't need this notification.
@@ -1350,8 +1362,8 @@ enum ieee80211_ampdu_mlme_action {
  * @get_tx_stats: Get statistics of the current TX queue status. This is used
  *     to get number of currently queued packets (queue length), maximum queue
  *     size (limit), and total number of packets sent using each TX queue
- *     (count). The 'stats' pointer points to an array that has hw->queues +
- *     hw->ampdu_queues items.
+ *     (count). The 'stats' pointer points to an array that has hw->queues
+ *     items.
  *
  * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently,
  *     this is only used for IBSS mode BSSID merging and debugging. Is not a
@@ -1979,6 +1991,16 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, const u8 *ra,
 struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
                                         const u8 *addr);
 
+/**
+ * ieee80211_beacon_loss - inform hardware does not receive beacons
+ *
+ * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf.
+ *
+ * When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and
+ * IEEE80211_CONF_PS is set, the driver needs to inform whenever the
+ * hardware is not receiving beacons with this function.
+ */
+void ieee80211_beacon_loss(struct ieee80211_vif *vif);
 
 /* Rate control API */
 
index 4dfb793c3f1560185106b6b0f671d5631bc0d083..6c3f964de9e1d652841cfe9510e895db3519cec9 100644 (file)
@@ -91,8 +91,7 @@ struct nf_conn_help {
 #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
 #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
 
-struct nf_conn
-{
+struct nf_conn {
        /* Usage count in here is 1 for hash table/destruct timer, 1 per skb,
            plus 1 for any connection(s) we are `master' for */
        struct nf_conntrack ct_general;
@@ -126,7 +125,6 @@ struct nf_conn
 #ifdef CONFIG_NET_NS
        struct net *ct_net;
 #endif
-       struct rcu_head rcu;
 };
 
 static inline struct nf_conn *
@@ -190,9 +188,13 @@ static inline void nf_ct_put(struct nf_conn *ct)
 extern int nf_ct_l3proto_try_module_get(unsigned short l3proto);
 extern void nf_ct_l3proto_module_put(unsigned short l3proto);
 
-extern struct hlist_head *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced);
-extern void nf_ct_free_hashtable(struct hlist_head *hash, int vmalloced,
-                                unsigned int size);
+/*
+ * Allocate a hashtable of hlist_head (if nulls == 0),
+ * or hlist_nulls_head (if nulls == 1)
+ */
+extern void *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced, int nulls);
+
+extern void nf_ct_free_hashtable(void *hash, int vmalloced, unsigned int size);
 
 extern struct nf_conntrack_tuple_hash *
 __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple);
index 66d65a7caa394164776c8559d3c5bd41c55bc8d8..ee2a4b369a0466ac884af3f282dec210e41c2a56 100644 (file)
@@ -14,6 +14,8 @@
 
 struct module;
 
+#define NF_CT_HELPER_NAME_LEN  16
+
 struct nf_conntrack_helper
 {
        struct hlist_node hnode;        /* Internal use. */
index 0378676c3dd816ba97a172359240a59153612e84..9f99d36d5de902b6e6209da5a4e53befaa0e24d0 100644 (file)
@@ -53,10 +53,17 @@ struct nf_conntrack_l3proto
        int (*tuple_to_nlattr)(struct sk_buff *skb,
                               const struct nf_conntrack_tuple *t);
 
+       /*
+        * Calculate size of tuple nlattr
+        */
+       int (*nlattr_tuple_size)(void);
+
        int (*nlattr_to_tuple)(struct nlattr *tb[],
                               struct nf_conntrack_tuple *t);
        const struct nla_policy *nla_policy;
 
+       size_t nla_size;
+
 #ifdef CONFIG_SYSCTL
        struct ctl_table_header *ctl_table_header;
        struct ctl_path         *ctl_table_path;
index b01070bf2f8445cc57962ea3b2f8d702a5f13e40..ba32ed7bdabe75c0655e728fbdcebbffae492151 100644 (file)
@@ -64,16 +64,22 @@ struct nf_conntrack_l4proto
        /* convert protoinfo to nfnetink attributes */
        int (*to_nlattr)(struct sk_buff *skb, struct nlattr *nla,
                         const struct nf_conn *ct);
+       /* Calculate protoinfo nlattr size */
+       int (*nlattr_size)(void);
 
        /* convert nfnetlink attributes to protoinfo */
        int (*from_nlattr)(struct nlattr *tb[], struct nf_conn *ct);
 
        int (*tuple_to_nlattr)(struct sk_buff *skb,
                               const struct nf_conntrack_tuple *t);
+       /* Calculate tuple nlattr size */
+       int (*nlattr_tuple_size)(void);
        int (*nlattr_to_tuple)(struct nlattr *tb[],
                               struct nf_conntrack_tuple *t);
        const struct nla_policy *nla_policy;
 
+       size_t nla_size;
+
 #ifdef CONFIG_SYSCTL
        struct ctl_table_header **ctl_table_header;
        struct ctl_table        *ctl_table;
@@ -107,6 +113,7 @@ extern int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb,
                                      const struct nf_conntrack_tuple *tuple);
 extern int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[],
                                      struct nf_conntrack_tuple *t);
+extern int nf_ct_port_nlattr_tuple_size(void);
 extern const struct nla_policy nf_ct_port_nla_policy[];
 
 #ifdef CONFIG_SYSCTL
index f2f6aa73dc10fd3ec65d3428118c486c2b51b20a..2628c154d40e1ac9f381915fa0936c0e82c483e4 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/nf_conntrack_tuple_common.h>
+#include <linux/list_nulls.h>
 
 /* A `tuple' is a structure containing the information to uniquely
   identify a connection.  ie. if two packets have the same tuple, they
@@ -146,9 +147,8 @@ static inline void nf_ct_dump_tuple(const struct nf_conntrack_tuple *t)
        ((enum ip_conntrack_dir)(h)->tuple.dst.dir)
 
 /* Connections have two entries in the hash table: one for each way */
-struct nf_conntrack_tuple_hash
-{
-       struct hlist_node hnode;
+struct nf_conntrack_tuple_hash {
+       struct hlist_nulls_node hnnode;
        struct nf_conntrack_tuple tuple;
 };
 
index 8a6150a3f4c74a5a6a150947c5e1ff6ec494eca9..eddb50289d6d12b8cb8a234c373f8574a0cb5562 100644 (file)
@@ -230,6 +230,7 @@ extern int          nla_validate(struct nlattr *head, int len, int maxtype,
 extern int             nla_parse(struct nlattr *tb[], int maxtype,
                                  struct nlattr *head, int len,
                                  const struct nla_policy *policy);
+extern int             nla_policy_len(const struct nla_policy *, int);
 extern struct nlattr * nla_find(struct nlattr *head, int len, int attrtype);
 extern size_t          nla_strlcpy(char *dst, const struct nlattr *nla,
                                    size_t dstsize);
index f4498a62881b0e60d8aace59b99f9a55f70fd0a4..9dc58402bc09e4b5d1c210cf081313cfbccd968d 100644 (file)
@@ -2,6 +2,7 @@
 #define __NETNS_CONNTRACK_H
 
 #include <linux/list.h>
+#include <linux/list_nulls.h>
 #include <asm/atomic.h>
 
 struct ctl_table_header;
@@ -10,9 +11,9 @@ struct nf_conntrack_ecache;
 struct netns_ct {
        atomic_t                count;
        unsigned int            expect_count;
-       struct hlist_head       *hash;
+       struct hlist_nulls_head *hash;
        struct hlist_head       *expect_hash;
-       struct hlist_head       unconfirmed;
+       struct hlist_nulls_head unconfirmed;
        struct ip_conntrack_stat *stat;
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
        struct nf_conntrack_ecache *ecache;
index 80009a24e21dd553a6204310b083ccd5e8bf6cf3..c4706eb98d3dfc922ec19fdbb0c23a36fab47a58 100644 (file)
@@ -132,6 +132,32 @@ errout:
        return err;
 }
 
+/**
+ * nla_policy_len - Determin the max. length of a policy
+ * @policy: policy to use
+ * @n: number of policies
+ *
+ * Determines the max. length of the policy.  It is currently used
+ * to allocated Netlink buffers roughly the size of the actual
+ * message.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+nla_policy_len(const struct nla_policy *p, int n)
+{
+       int i, len = 0;
+
+       for (i = 0; i < n; i++) {
+               if (p->len)
+                       len += nla_total_size(p->len);
+               else if (nla_attr_minlen[p->type])
+                       len += nla_total_size(nla_attr_minlen[p->type]);
+       }
+
+       return len;
+}
+
 /**
  * nla_parse - Parse a stream of attributes into a tb buffer
  * @tb: destination array with maxtype+1 elements
@@ -467,6 +493,7 @@ EXPORT_SYMBOL(nla_append);
 #endif
 
 EXPORT_SYMBOL(nla_validate);
+EXPORT_SYMBOL(nla_policy_len);
 EXPORT_SYMBOL(nla_parse);
 EXPORT_SYMBOL(nla_find);
 EXPORT_SYMBOL(nla_strlcpy);
index 3e0671df3a3f4e0acca576ac223fd5983c8cd63c..d6a9243641afaa82cbf9894935a0ddf9acb7a288 100644 (file)
@@ -1571,14 +1571,10 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                    usat->sat_family != AF_APPLETALK)
                        return -EINVAL;
 
-               /* netatalk doesn't implement this check */
+               /* netatalk didn't implement this check */
                if (usat->sat_addr.s_node == ATADDR_BCAST &&
                    !sock_flag(sk, SOCK_BROADCAST)) {
-                       printk(KERN_INFO "SO_BROADCAST: Fix your netatalk as "
-                                        "it will break before 2.2\n");
-#if 0
                        return -EPERM;
-#endif
                }
        } else {
                if (sk->sk_state != TCP_ESTABLISHED)
index 7da5ebb84e976c2689cb4fd338dd6c43586bdbf4..fd9d06f291dc31f4bb0d108f85d3eff51ead0e3f 100644 (file)
@@ -1435,11 +1435,6 @@ static int ax25_sendmsg(struct kiocb *iocb, struct socket *sock,
        size_t size;
        int lv, err, addr_len = msg->msg_namelen;
 
-       /* AX.25 empty data frame has no meaning : don't send */
-       if (len == 0) {
-               return (0);
-       }
-
        if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_CMSG_COMPAT))
                return -EINVAL;
 
@@ -1639,13 +1634,6 @@ static int ax25_recvmsg(struct kiocb *iocb, struct socket *sock,
        skb_reset_transport_header(skb);
        copied = skb->len;
 
-       /* AX.25 empty data frame has no meaning : ignore it */
-       if (copied == 0) {
-               err = copied;
-               skb_free_datagram(sk, skb);
-               goto out;
-       }
-
        if (copied > size) {
                copied = size;
                msg->msg_flags |= MSG_TRUNC;
index 052dd478d3e112b769b66ab83db5b82996d88061..63ec4bf89b2914a5a3a271c14b16d4833c7d53ce 100644 (file)
@@ -2627,18 +2627,15 @@ static int process_backlog(struct napi_struct *napi, int quota)
                local_irq_disable();
                skb = __skb_dequeue(&queue->input_pkt_queue);
                if (!skb) {
+                       __napi_complete(napi);
                        local_irq_enable();
-                       napi_complete(napi);
-                       goto out;
+                       break;
                }
                local_irq_enable();
 
-               napi_gro_receive(napi, skb);
+               netif_receive_skb(skb);
        } while (++work < quota && jiffies == start_time);
 
-       napi_gro_flush(napi);
-
-out:
        return work;
 }
 
index 84b9c179df5145fb998e54963496fe7dfbc62573..35c5f6a5cb7c8ee4dad9ad56eba517727ffefbc5 100644 (file)
@@ -81,19 +81,7 @@ static inline int arp_devaddr_compare(const struct arpt_devaddr_info *ap,
 static unsigned long ifname_compare(const char *_a, const char *_b, const char *_mask)
 {
 #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
-       const unsigned long *a = (const unsigned long *)_a;
-       const unsigned long *b = (const unsigned long *)_b;
-       const unsigned long *mask = (const unsigned long *)_mask;
-       unsigned long ret;
-
-       ret = (a[0] ^ b[0]) & mask[0];
-       if (IFNAMSIZ > sizeof(unsigned long))
-               ret |= (a[1] ^ b[1]) & mask[1];
-       if (IFNAMSIZ > 2 * sizeof(unsigned long))
-               ret |= (a[2] ^ b[2]) & mask[2];
-       if (IFNAMSIZ > 3 * sizeof(unsigned long))
-               ret |= (a[3] ^ b[3]) & mask[3];
-       BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
+       unsigned long ret = ifname_compare_aligned(_a, _b, _mask);
 #else
        unsigned long ret = 0;
        const u16 *a = (const u16 *)_a;
@@ -404,7 +392,9 @@ static int mark_source_chains(struct xt_table_info *newinfo,
                            && unconditional(&e->arp)) || visited) {
                                unsigned int oldpos, size;
 
-                               if (t->verdict < -NF_MAX_VERDICT - 1) {
+                               if ((strcmp(t->target.u.user.name,
+                                           ARPT_STANDARD_TARGET) == 0) &&
+                                   t->verdict < -NF_MAX_VERDICT - 1) {
                                        duprintf("mark_source_chains: bad "
                                                "negative verdict (%i)\n",
                                                                t->verdict);
index e5294aec967d45762cf15e1aaed577b83455a8c5..82ee7c9049ff032d0f652bc114b0b237e6d374d1 100644 (file)
@@ -74,25 +74,6 @@ do {                                                         \
 
    Hence the start of any table is given by get_table() below.  */
 
-static unsigned long ifname_compare(const char *_a, const char *_b,
-                                   const unsigned char *_mask)
-{
-       const unsigned long *a = (const unsigned long *)_a;
-       const unsigned long *b = (const unsigned long *)_b;
-       const unsigned long *mask = (const unsigned long *)_mask;
-       unsigned long ret;
-
-       ret = (a[0] ^ b[0]) & mask[0];
-       if (IFNAMSIZ > sizeof(unsigned long))
-               ret |= (a[1] ^ b[1]) & mask[1];
-       if (IFNAMSIZ > 2 * sizeof(unsigned long))
-               ret |= (a[2] ^ b[2]) & mask[2];
-       if (IFNAMSIZ > 3 * sizeof(unsigned long))
-               ret |= (a[3] ^ b[3]) & mask[3];
-       BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
-       return ret;
-}
-
 /* Returns whether matches rule or not. */
 /* Performance critical - called for every packet */
 static inline bool
@@ -121,7 +102,7 @@ ip_packet_match(const struct iphdr *ip,
                return false;
        }
 
-       ret = ifname_compare(indev, ipinfo->iniface, ipinfo->iniface_mask);
+       ret = ifname_compare_aligned(indev, ipinfo->iniface, ipinfo->iniface_mask);
 
        if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
                dprintf("VIA in mismatch (%s vs %s).%s\n",
@@ -130,7 +111,7 @@ ip_packet_match(const struct iphdr *ip,
                return false;
        }
 
-       ret = ifname_compare(outdev, ipinfo->outiface, ipinfo->outiface_mask);
+       ret = ifname_compare_aligned(outdev, ipinfo->outiface, ipinfo->outiface_mask);
 
        if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
                dprintf("VIA out mismatch (%s vs %s).%s\n",
@@ -507,7 +488,9 @@ mark_source_chains(struct xt_table_info *newinfo,
                            && unconditional(&e->ip)) || visited) {
                                unsigned int oldpos, size;
 
-                               if (t->verdict < -NF_MAX_VERDICT - 1) {
+                               if ((strcmp(t->target.u.user.name,
+                                           IPT_STANDARD_TARGET) == 0) &&
+                                   t->verdict < -NF_MAX_VERDICT - 1) {
                                        duprintf("mark_source_chains: bad "
                                                "negative verdict (%i)\n",
                                                                t->verdict);
index 8b681f24e271c7600b05fb1c4e9e724853246919..7d2ead7228ac700265a35b295767f58bc4fd2952 100644 (file)
@@ -328,6 +328,11 @@ static int ipv4_nlattr_to_tuple(struct nlattr *tb[],
 
        return 0;
 }
+
+static int ipv4_nlattr_tuple_size(void)
+{
+       return nla_policy_len(ipv4_nla_policy, CTA_IP_MAX + 1);
+}
 #endif
 
 static struct nf_sockopt_ops so_getorigdst = {
@@ -347,6 +352,7 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {
        .get_l4proto     = ipv4_get_l4proto,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .tuple_to_nlattr = ipv4_tuple_to_nlattr,
+       .nlattr_tuple_size = ipv4_nlattr_tuple_size,
        .nlattr_to_tuple = ipv4_nlattr_to_tuple,
        .nla_policy      = ipv4_nla_policy,
 #endif
index 6ba5c557690c439f55f44013da1f6e50a985db07..8668a3defda6bd212170ff0b26b400bb60274f9d 100644 (file)
@@ -25,40 +25,42 @@ struct ct_iter_state {
        unsigned int bucket;
 };
 
-static struct hlist_node *ct_get_first(struct seq_file *seq)
+static struct hlist_nulls_node *ct_get_first(struct seq_file *seq)
 {
        struct net *net = seq_file_net(seq);
        struct ct_iter_state *st = seq->private;
-       struct hlist_node *n;
+       struct hlist_nulls_node *n;
 
        for (st->bucket = 0;
             st->bucket < nf_conntrack_htable_size;
             st->bucket++) {
                n = rcu_dereference(net->ct.hash[st->bucket].first);
-               if (n)
+               if (!is_a_nulls(n))
                        return n;
        }
        return NULL;
 }
 
-static struct hlist_node *ct_get_next(struct seq_file *seq,
-                                     struct hlist_node *head)
+static struct hlist_nulls_node *ct_get_next(struct seq_file *seq,
+                                     struct hlist_nulls_node *head)
 {
        struct net *net = seq_file_net(seq);
        struct ct_iter_state *st = seq->private;
 
        head = rcu_dereference(head->next);
-       while (head == NULL) {
-               if (++st->bucket >= nf_conntrack_htable_size)
-                       return NULL;
+       while (is_a_nulls(head)) {
+               if (likely(get_nulls_value(head) == st->bucket)) {
+                       if (++st->bucket >= nf_conntrack_htable_size)
+                               return NULL;
+               }
                head = rcu_dereference(net->ct.hash[st->bucket].first);
        }
        return head;
 }
 
-static struct hlist_node *ct_get_idx(struct seq_file *seq, loff_t pos)
+static struct hlist_nulls_node *ct_get_idx(struct seq_file *seq, loff_t pos)
 {
-       struct hlist_node *head = ct_get_first(seq);
+       struct hlist_nulls_node *head = ct_get_first(seq);
 
        if (head)
                while (pos && (head = ct_get_next(seq, head)))
@@ -87,69 +89,76 @@ static void ct_seq_stop(struct seq_file *s, void *v)
 
 static int ct_seq_show(struct seq_file *s, void *v)
 {
-       const struct nf_conntrack_tuple_hash *hash = v;
-       const struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash);
+       struct nf_conntrack_tuple_hash *hash = v;
+       struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash);
        const struct nf_conntrack_l3proto *l3proto;
        const struct nf_conntrack_l4proto *l4proto;
+       int ret = 0;
 
        NF_CT_ASSERT(ct);
+       if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use)))
+               return 0;
+
 
        /* we only want to print DIR_ORIGINAL */
        if (NF_CT_DIRECTION(hash))
-               return 0;
+               goto release;
        if (nf_ct_l3num(ct) != AF_INET)
-               return 0;
+               goto release;
 
        l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct));
        NF_CT_ASSERT(l3proto);
        l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
        NF_CT_ASSERT(l4proto);
 
+       ret = -ENOSPC;
        if (seq_printf(s, "%-8s %u %ld ",
                      l4proto->name, nf_ct_protonum(ct),
                      timer_pending(&ct->timeout)
                      ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0)
-               return -ENOSPC;
+               goto release;
 
        if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct))
-               return -ENOSPC;
+               goto release;
 
        if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
                        l3proto, l4proto))
-               return -ENOSPC;
+               goto release;
 
        if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL))
-               return -ENOSPC;
+               goto release;
 
        if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status)))
                if (seq_printf(s, "[UNREPLIED] "))
-                       return -ENOSPC;
+                       goto release;
 
        if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple,
                        l3proto, l4proto))
-               return -ENOSPC;
+               goto release;
 
        if (seq_print_acct(s, ct, IP_CT_DIR_REPLY))
-               return -ENOSPC;
+               goto release;
 
        if (test_bit(IPS_ASSURED_BIT, &ct->status))
                if (seq_printf(s, "[ASSURED] "))
-                       return -ENOSPC;
+                       goto release;
 
 #ifdef CONFIG_NF_CONNTRACK_MARK
        if (seq_printf(s, "mark=%u ", ct->mark))
-               return -ENOSPC;
+               goto release;
 #endif
 
 #ifdef CONFIG_NF_CONNTRACK_SECMARK
        if (seq_printf(s, "secmark=%u ", ct->secmark))
-               return -ENOSPC;
+               goto release;
 #endif
 
        if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)))
-               return -ENOSPC;
-
-       return 0;
+               goto release;
+       ret = 0;
+release:
+       nf_ct_put(ct);
+       return ret;
 }
 
 static const struct seq_operations ct_seq_ops = {
index 2a8bee26f43d9dcb58079a291a69264455342b22..23b2c2ee869a85c4f92a5a45bce4f731e4815e13 100644 (file)
@@ -262,6 +262,11 @@ static int icmp_nlattr_to_tuple(struct nlattr *tb[],
 
        return 0;
 }
+
+static int icmp_nlattr_tuple_size(void)
+{
+       return nla_policy_len(icmp_nla_policy, CTA_PROTO_MAX + 1);
+}
 #endif
 
 #ifdef CONFIG_SYSCTL
@@ -309,6 +314,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly =
        .me                     = NULL,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .tuple_to_nlattr        = icmp_tuple_to_nlattr,
+       .nlattr_tuple_size      = icmp_nlattr_tuple_size,
        .nlattr_to_tuple        = icmp_nlattr_to_tuple,
        .nla_policy             = icmp_nla_policy,
 #endif
index a65cf692359fbc803032a2d9a18dead804747bc0..fe65187810f065aecdf12987e905ee2695192b75 100644 (file)
@@ -679,7 +679,7 @@ nfnetlink_parse_nat_setup(struct nf_conn *ct,
 static int __net_init nf_nat_net_init(struct net *net)
 {
        net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&nf_nat_htable_size,
-                                                     &net->ipv4.nat_vmalloced);
+                                                     &net->ipv4.nat_vmalloced, 0);
        if (!net->ipv4.nat_bysource)
                return -ENOMEM;
        return 0;
index f171e8dbac9124ae43c94f5e169fdfa6571c5fca..8f04bd9da274f444a35df267efe6a29a37186434 100644 (file)
@@ -75,8 +75,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
        if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL ||
            !idev || unlikely(idev->cnf.disable_ipv6)) {
                IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INDISCARDS);
-               rcu_read_unlock();
-               goto out;
+               goto drop;
        }
 
        memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
@@ -147,7 +146,6 @@ err:
 drop:
        rcu_read_unlock();
        kfree_skb(skb);
-out:
        return 0;
 }
 
index 34af7bb8df5f8d6a3821288f0243f0b591bf3658..e89cfa3a8f254650948f60ada09982b9862c745d 100644 (file)
@@ -89,25 +89,6 @@ ip6t_ext_hdr(u8 nexthdr)
                 (nexthdr == IPPROTO_DSTOPTS) );
 }
 
-static unsigned long ifname_compare(const char *_a, const char *_b,
-                                   const unsigned char *_mask)
-{
-       const unsigned long *a = (const unsigned long *)_a;
-       const unsigned long *b = (const unsigned long *)_b;
-       const unsigned long *mask = (const unsigned long *)_mask;
-       unsigned long ret;
-
-       ret = (a[0] ^ b[0]) & mask[0];
-       if (IFNAMSIZ > sizeof(unsigned long))
-               ret |= (a[1] ^ b[1]) & mask[1];
-       if (IFNAMSIZ > 2 * sizeof(unsigned long))
-               ret |= (a[2] ^ b[2]) & mask[2];
-       if (IFNAMSIZ > 3 * sizeof(unsigned long))
-               ret |= (a[3] ^ b[3]) & mask[3];
-       BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
-       return ret;
-}
-
 /* Returns whether matches rule or not. */
 /* Performance critical - called for every packet */
 static inline bool
@@ -138,7 +119,7 @@ ip6_packet_match(const struct sk_buff *skb,
                return false;
        }
 
-       ret = ifname_compare(indev, ip6info->iniface, ip6info->iniface_mask);
+       ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask);
 
        if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
                dprintf("VIA in mismatch (%s vs %s).%s\n",
@@ -147,7 +128,7 @@ ip6_packet_match(const struct sk_buff *skb,
                return false;
        }
 
-       ret = ifname_compare(outdev, ip6info->outiface, ip6info->outiface_mask);
+       ret = ifname_compare_aligned(outdev, ip6info->outiface, ip6info->outiface_mask);
 
        if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
                dprintf("VIA out mismatch (%s vs %s).%s\n",
@@ -536,7 +517,9 @@ mark_source_chains(struct xt_table_info *newinfo,
                            && unconditional(&e->ipv6)) || visited) {
                                unsigned int oldpos, size;
 
-                               if (t->verdict < -NF_MAX_VERDICT - 1) {
+                               if ((strcmp(t->target.u.user.name,
+                                           IP6T_STANDARD_TARGET) == 0) &&
+                                   t->verdict < -NF_MAX_VERDICT - 1) {
                                        duprintf("mark_source_chains: bad "
                                                "negative verdict (%i)\n",
                                                                t->verdict);
index e6852f617217893f35edf544ec213f12c88246f2..2a15c2d66c69dc23ff63f7ba81ebacf337134a84 100644 (file)
@@ -342,6 +342,11 @@ static int ipv6_nlattr_to_tuple(struct nlattr *tb[],
 
        return 0;
 }
+
+static int ipv6_nlattr_tuple_size(void)
+{
+       return nla_policy_len(ipv6_nla_policy, CTA_IP_MAX + 1);
+}
 #endif
 
 struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
@@ -353,6 +358,7 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
        .get_l4proto            = ipv6_get_l4proto,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .tuple_to_nlattr        = ipv6_tuple_to_nlattr,
+       .nlattr_tuple_size      = ipv6_nlattr_tuple_size,
        .nlattr_to_tuple        = ipv6_nlattr_to_tuple,
        .nla_policy             = ipv6_nla_policy,
 #endif
index 41b8a956e1becaa1ef2261d5af1d7397291c764d..9903227bf37c9b2966a127f503d5b52aa97fdec3 100644 (file)
@@ -269,6 +269,11 @@ static int icmpv6_nlattr_to_tuple(struct nlattr *tb[],
 
        return 0;
 }
+
+static int icmpv6_nlattr_tuple_size(void)
+{
+       return nla_policy_len(icmpv6_nla_policy, CTA_PROTO_MAX + 1);
+}
 #endif
 
 #ifdef CONFIG_SYSCTL
@@ -300,6 +305,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly =
        .error                  = icmpv6_error,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .tuple_to_nlattr        = icmpv6_tuple_to_nlattr,
+       .nlattr_tuple_size      = icmpv6_nlattr_tuple_size,
        .nlattr_to_tuple        = icmpv6_nlattr_to_tuple,
        .nla_policy             = icmpv6_nla_policy,
 #endif
index a95affc94629254ea98a75b5c41966d059acb54d..07656d830bc4d7cec8a9152b9e28d1495dbe1809 100644 (file)
@@ -197,6 +197,14 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
 
        status = WLAN_STATUS_REQUEST_DECLINED;
 
+       if (test_sta_flags(sta, WLAN_STA_SUSPEND)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "Suspend in progress. "
+                      "Denying ADDBA request\n");
+#endif
+               goto end_no_lock;
+       }
+
        /* sanity check for incoming parameters:
         * check if configuration can support the BA policy
         * and if buffer size does not exceeds max value */
index 1df116d4d6e786498b8cad51511161a9418045f5..947aaaad35d26b03f355299e15d26a7f2514367e 100644 (file)
@@ -131,24 +131,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
 
        state = &sta->ampdu_mlme.tid_state_tx[tid];
 
-       if (local->hw.ampdu_queues) {
-               if (initiator) {
-                       /*
-                        * Stop the AC queue to avoid issues where we send
-                        * unaggregated frames already before the delba.
-                        */
-                       ieee80211_stop_queue_by_reason(&local->hw,
-                               local->hw.queues + sta->tid_to_tx_q[tid],
-                               IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-               }
-
-               /*
-                * Pretend the driver woke the queue, just in case
-                * it disabled it before the session was stopped.
-                */
-               ieee80211_wake_queue(
-                       &local->hw, local->hw.queues + sta->tid_to_tx_q[tid]);
-       }
        *state = HT_AGG_STATE_REQ_STOP_BA_MSK |
                (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
 
@@ -158,6 +140,10 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
        /* HW shall not deny going back to legacy */
        if (WARN_ON(ret)) {
                *state = HT_AGG_STATE_OPERATIONAL;
+               /*
+                * We may have pending packets get stuck in this case...
+                * Not bothering with a workaround for now.
+                */
        }
 
        return ret;
@@ -212,7 +198,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
        struct sta_info *sta;
        struct ieee80211_sub_if_data *sdata;
        u8 *state;
-       int i, qn = -1, ret = 0;
+       int ret = 0;
        u16 start_seq_num;
 
        if (WARN_ON(!local->ops->ampdu_action))
@@ -226,13 +212,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
               ra, tid);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
-       if (hw->ampdu_queues && ieee80211_ac_from_tid(tid) == 0) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "rejecting on voice AC\n");
-#endif
-               return -EINVAL;
-       }
-
        rcu_read_lock();
 
        sta = sta_info_get(local, ra);
@@ -257,7 +236,17 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
                goto unlock;
        }
 
+       if (test_sta_flags(sta, WLAN_STA_SUSPEND)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+               printk(KERN_DEBUG "Suspend in progress. "
+                      "Denying BA session request\n");
+#endif
+               ret = -EINVAL;
+               goto unlock;
+       }
+
        spin_lock_bh(&sta->lock);
+       spin_lock(&local->ampdu_lock);
 
        sdata = sta->sdata;
 
@@ -278,41 +267,16 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
                goto err_unlock_sta;
        }
 
-       if (hw->ampdu_queues) {
-               spin_lock(&local->queue_stop_reason_lock);
-               /* reserve a new queue for this session */
-               for (i = 0; i < local->hw.ampdu_queues; i++) {
-                       if (local->ampdu_ac_queue[i] < 0) {
-                               qn = i;
-                               local->ampdu_ac_queue[qn] =
-                                       ieee80211_ac_from_tid(tid);
-                               break;
-                       }
-               }
-               spin_unlock(&local->queue_stop_reason_lock);
-
-               if (qn < 0) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-                       printk(KERN_DEBUG "BA request denied - "
-                              "queue unavailable for tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-                       ret = -ENOSPC;
-                       goto err_unlock_sta;
-               }
-
-               /*
-                * If we successfully allocate the session, we can't have
-                * anything going on on the queue this TID maps into, so
-                * stop it for now. This is a "virtual" stop using the same
-                * mechanism that drivers will use.
-                *
-                * XXX: queue up frames for this session in the sta_info
-                *      struct instead to avoid hitting all other STAs.
-                */
-               ieee80211_stop_queue_by_reason(
-                       &local->hw, hw->queues + qn,
-                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-       }
+       /*
+        * While we're asking the driver about the aggregation,
+        * stop the AC queue so that we don't have to worry
+        * about frames that came in while we were doing that,
+        * which would require us to put them to the AC pending
+        * afterwards which just makes the code more complex.
+        */
+       ieee80211_stop_queue_by_reason(
+               &local->hw, ieee80211_ac_from_tid(tid),
+               IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
 
        /* prepare A-MPDU MLME for Tx aggregation */
        sta->ampdu_mlme.tid_tx[tid] =
@@ -324,9 +288,11 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
                                        tid);
 #endif
                ret = -ENOMEM;
-               goto err_return_queue;
+               goto err_wake_queue;
        }
 
+       skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending);
+
        /* Tx timer */
        sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
                        sta_addba_resp_timer_expired;
@@ -351,8 +317,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
                *state = HT_AGG_STATE_IDLE;
                goto err_free;
        }
-       sta->tid_to_tx_q[tid] = qn;
 
+       /* Driver vetoed or OKed, but we can take packets again now */
+       ieee80211_wake_queue_by_reason(
+               &local->hw, ieee80211_ac_from_tid(tid),
+               IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+
+       spin_unlock(&local->ampdu_lock);
        spin_unlock_bh(&sta->lock);
 
        /* send an addBA request */
@@ -377,17 +348,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
  err_free:
        kfree(sta->ampdu_mlme.tid_tx[tid]);
        sta->ampdu_mlme.tid_tx[tid] = NULL;
- err_return_queue:
-       if (qn >= 0) {
-               /* We failed, so start queue again right away. */
-               ieee80211_wake_queue_by_reason(hw, hw->queues + qn,
-                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-               /* give queue back to pool */
-               spin_lock(&local->queue_stop_reason_lock);
-               local->ampdu_ac_queue[qn] = -1;
-               spin_unlock(&local->queue_stop_reason_lock);
-       }
+ err_wake_queue:
+       ieee80211_wake_queue_by_reason(
+               &local->hw, ieee80211_ac_from_tid(tid),
+               IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
  err_unlock_sta:
+       spin_unlock(&local->ampdu_lock);
        spin_unlock_bh(&sta->lock);
  unlock:
        rcu_read_unlock();
@@ -395,6 +361,67 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 }
 EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
 
+/*
+ * splice packets from the STA's pending to the local pending,
+ * requires a call to ieee80211_agg_splice_finish and holding
+ * local->ampdu_lock across both calls.
+ */
+static void ieee80211_agg_splice_packets(struct ieee80211_local *local,
+                                        struct sta_info *sta, u16 tid)
+{
+       unsigned long flags;
+       u16 queue = ieee80211_ac_from_tid(tid);
+
+       ieee80211_stop_queue_by_reason(
+               &local->hw, queue,
+               IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+
+       if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) {
+               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+               /* mark queue as pending, it is stopped already */
+               __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING,
+                         &local->queue_stop_reasons[queue]);
+               /* copy over remaining packets */
+               skb_queue_splice_tail_init(
+                       &sta->ampdu_mlme.tid_tx[tid]->pending,
+                       &local->pending[queue]);
+               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+       }
+}
+
+static void ieee80211_agg_splice_finish(struct ieee80211_local *local,
+                                       struct sta_info *sta, u16 tid)
+{
+       u16 queue = ieee80211_ac_from_tid(tid);
+
+       ieee80211_wake_queue_by_reason(
+               &local->hw, queue,
+               IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+}
+
+/* caller must hold sta->lock */
+static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
+                                        struct sta_info *sta, u16 tid)
+{
+#ifdef CONFIG_MAC80211_HT_DEBUG
+       printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
+#endif
+
+       spin_lock(&local->ampdu_lock);
+       ieee80211_agg_splice_packets(local, sta, tid);
+       /*
+        * NB: we rely on sta->lock being taken in the TX
+        * processing here when adding to the pending queue,
+        * otherwise we could only change the state of the
+        * session to OPERATIONAL _here_.
+        */
+       ieee80211_agg_splice_finish(local, sta, tid);
+       spin_unlock(&local->ampdu_lock);
+
+       local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL,
+                                &sta->sta, tid, NULL);
+}
+
 void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 {
        struct ieee80211_local *local = hw_to_local(hw);
@@ -437,20 +464,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 
        *state |= HT_ADDBA_DRV_READY_MSK;
 
-       if (*state == HT_AGG_STATE_OPERATIONAL) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
-#endif
-               if (hw->ampdu_queues) {
-                       /*
-                        * Wake up this queue, we stopped it earlier,
-                        * this will in turn wake the entire AC.
-                        */
-                       ieee80211_wake_queue_by_reason(hw,
-                               hw->queues + sta->tid_to_tx_q[tid],
-                               IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-               }
-       }
+       if (*state == HT_AGG_STATE_OPERATIONAL)
+               ieee80211_agg_tx_operational(local, sta, tid);
 
  out:
        spin_unlock_bh(&sta->lock);
@@ -584,22 +599,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
                        WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
 
        spin_lock_bh(&sta->lock);
+       spin_lock(&local->ampdu_lock);
 
-       if (*state & HT_AGG_STATE_INITIATOR_MSK &&
-           hw->ampdu_queues) {
-               /*
-                * Wake up this queue, we stopped it earlier,
-                * this will in turn wake the entire AC.
-                */
-               ieee80211_wake_queue_by_reason(hw,
-                       hw->queues + sta->tid_to_tx_q[tid],
-                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-       }
+       ieee80211_agg_splice_packets(local, sta, tid);
 
        *state = HT_AGG_STATE_IDLE;
+       /* from now on packets are no longer put onto sta->pending */
        sta->ampdu_mlme.addba_req_num[tid] = 0;
        kfree(sta->ampdu_mlme.tid_tx[tid]);
        sta->ampdu_mlme.tid_tx[tid] = NULL;
+
+       ieee80211_agg_splice_finish(local, sta, tid);
+
+       spin_unlock(&local->ampdu_lock);
        spin_unlock_bh(&sta->lock);
 
        rcu_read_unlock();
@@ -637,9 +649,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
                                  struct ieee80211_mgmt *mgmt,
                                  size_t len)
 {
-       struct ieee80211_hw *hw = &local->hw;
-       u16 capab;
-       u16 tid, start_seq_num;
+       u16 capab, tid;
        u8 *state;
 
        capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
@@ -673,26 +683,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
 
                *state |= HT_ADDBA_RECEIVED_MSK;
 
-               if (hw->ampdu_queues && *state != curstate &&
-                   *state == HT_AGG_STATE_OPERATIONAL) {
-                       /*
-                        * Wake up this queue, we stopped it earlier,
-                        * this will in turn wake the entire AC.
-                        */
-                       ieee80211_wake_queue_by_reason(hw,
-                               hw->queues + sta->tid_to_tx_q[tid],
-                               IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-               }
-               sta->ampdu_mlme.addba_req_num[tid] = 0;
+               if (*state != curstate && *state == HT_AGG_STATE_OPERATIONAL)
+                       ieee80211_agg_tx_operational(local, sta, tid);
 
-               if (local->ops->ampdu_action) {
-                       (void)local->ops->ampdu_action(hw,
-                                              IEEE80211_AMPDU_TX_RESUME,
-                                              &sta->sta, tid, &start_seq_num);
-               }
-#ifdef CONFIG_MAC80211_HT_DEBUG
-               printk(KERN_DEBUG "Resuming TX aggregation for tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
+               sta->ampdu_mlme.addba_req_num[tid] = 0;
        } else {
                sta->ampdu_mlme.addba_req_num[tid]++;
                ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR);
index 58693e52d458c3d8431ceae4f35ae8bd099a5ab7..e677b751d46857db80d163aa58bdbff04a116348 100644 (file)
@@ -540,9 +540,6 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_AP)
-               return -EINVAL;
-
        old = sdata->u.ap.beacon;
 
        if (old)
@@ -559,9 +556,6 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_AP)
-               return -EINVAL;
-
        old = sdata->u.ap.beacon;
 
        if (!old)
@@ -577,9 +571,6 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_AP)
-               return -EINVAL;
-
        old = sdata->u.ap.beacon;
 
        if (!old)
@@ -728,10 +719,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        int err;
        int layer2_update;
 
-       /* Prevent a race with changing the rate control algorithm */
-       if (!netif_running(dev))
-               return -ENETDOWN;
-
        if (params->vlan) {
                sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
@@ -860,14 +847,8 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
        struct sta_info *sta;
        int err;
 
-       if (!netif_running(dev))
-               return -ENETDOWN;
-
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
-               return -ENOTSUPP;
-
        rcu_read_lock();
        sta = sta_info_get(local, next_hop);
        if (!sta) {
@@ -913,14 +894,8 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
        struct mesh_path *mpath;
        struct sta_info *sta;
 
-       if (!netif_running(dev))
-               return -ENETDOWN;
-
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
-               return -ENOTSUPP;
-
        rcu_read_lock();
 
        sta = sta_info_get(local, next_hop);
@@ -989,9 +964,6 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
-               return -ENOTSUPP;
-
        rcu_read_lock();
        mpath = mesh_path_lookup(dst, sdata);
        if (!mpath) {
@@ -1013,9 +985,6 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
-               return -ENOTSUPP;
-
        rcu_read_lock();
        mpath = mesh_path_lookup_by_idx(idx, sdata);
        if (!mpath) {
@@ -1035,8 +1004,6 @@ static int ieee80211_get_mesh_params(struct wiphy *wiphy,
        struct ieee80211_sub_if_data *sdata;
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
-               return -ENOTSUPP;
        memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config));
        return 0;
 }
@@ -1054,9 +1021,6 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy,
        struct ieee80211_sub_if_data *sdata;
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
-               return -ENOTSUPP;
-
        /* Set the config options which we are interested in setting */
        conf = &(sdata->u.mesh.mshcfg);
        if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask))
@@ -1104,9 +1068,6 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->vif.type != NL80211_IFTYPE_AP)
-               return -EINVAL;
-
        if (params->use_cts_prot >= 0) {
                sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
                changed |= BSS_CHANGED_ERP_CTS_PROT;
@@ -1181,91 +1142,6 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
        return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 }
 
-static int set_mgmt_extra_ie_sta(struct ieee80211_sub_if_data *sdata,
-                                u8 subtype, u8 *ies, size_t ies_len)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
-       switch (subtype) {
-       case IEEE80211_STYPE_PROBE_REQ >> 4:
-               if (local->ops->hw_scan)
-                       break;
-               kfree(ifmgd->ie_probereq);
-               ifmgd->ie_probereq = ies;
-               ifmgd->ie_probereq_len = ies_len;
-               return 0;
-       case IEEE80211_STYPE_PROBE_RESP >> 4:
-               kfree(ifmgd->ie_proberesp);
-               ifmgd->ie_proberesp = ies;
-               ifmgd->ie_proberesp_len = ies_len;
-               return 0;
-       case IEEE80211_STYPE_AUTH >> 4:
-               kfree(ifmgd->ie_auth);
-               ifmgd->ie_auth = ies;
-               ifmgd->ie_auth_len = ies_len;
-               return 0;
-       case IEEE80211_STYPE_ASSOC_REQ >> 4:
-               kfree(ifmgd->ie_assocreq);
-               ifmgd->ie_assocreq = ies;
-               ifmgd->ie_assocreq_len = ies_len;
-               return 0;
-       case IEEE80211_STYPE_REASSOC_REQ >> 4:
-               kfree(ifmgd->ie_reassocreq);
-               ifmgd->ie_reassocreq = ies;
-               ifmgd->ie_reassocreq_len = ies_len;
-               return 0;
-       case IEEE80211_STYPE_DEAUTH >> 4:
-               kfree(ifmgd->ie_deauth);
-               ifmgd->ie_deauth = ies;
-               ifmgd->ie_deauth_len = ies_len;
-               return 0;
-       case IEEE80211_STYPE_DISASSOC >> 4:
-               kfree(ifmgd->ie_disassoc);
-               ifmgd->ie_disassoc = ies;
-               ifmgd->ie_disassoc_len = ies_len;
-               return 0;
-       }
-
-       return -EOPNOTSUPP;
-}
-
-static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy,
-                                      struct net_device *dev,
-                                      struct mgmt_extra_ie_params *params)
-{
-       struct ieee80211_sub_if_data *sdata;
-       u8 *ies;
-       size_t ies_len;
-       int ret = -EOPNOTSUPP;
-
-       if (params->ies) {
-               ies = kmemdup(params->ies, params->ies_len, GFP_KERNEL);
-               if (ies == NULL)
-                       return -ENOMEM;
-               ies_len = params->ies_len;
-       } else {
-               ies = NULL;
-               ies_len = 0;
-       }
-
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-       switch (sdata->vif.type) {
-       case NL80211_IFTYPE_STATION:
-               ret = set_mgmt_extra_ie_sta(sdata, params->subtype,
-                                           ies, ies_len);
-               break;
-       default:
-               ret = -EOPNOTSUPP;
-               break;
-       }
-
-       if (ret)
-               kfree(ies);
-       return ret;
-}
-
 #ifdef CONFIG_PM
 static int ieee80211_suspend(struct wiphy *wiphy)
 {
@@ -1287,9 +1163,6 @@ static int ieee80211_scan(struct wiphy *wiphy,
 {
        struct ieee80211_sub_if_data *sdata;
 
-       if (!netif_running(dev))
-               return -ENETDOWN;
-
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
        if (sdata->vif.type != NL80211_IFTYPE_STATION &&
@@ -1300,6 +1173,119 @@ static int ieee80211_scan(struct wiphy *wiphy,
        return ieee80211_request_scan(sdata, req);
 }
 
+static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
+                         struct cfg80211_auth_request *req)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       switch (req->auth_type) {
+       case NL80211_AUTHTYPE_OPEN_SYSTEM:
+               sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_OPEN;
+               break;
+       case NL80211_AUTHTYPE_SHARED_KEY:
+               sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_SHARED_KEY;
+               break;
+       case NL80211_AUTHTYPE_FT:
+               sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_FT;
+               break;
+       case NL80211_AUTHTYPE_NETWORK_EAP:
+               sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_LEAP;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       memcpy(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN);
+       sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
+       sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET;
+
+       /* TODO: req->chan */
+       sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL;
+
+       if (req->ssid) {
+               sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET;
+               memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len);
+               sdata->u.mgd.ssid_len = req->ssid_len;
+               sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
+       }
+
+       kfree(sdata->u.mgd.sme_auth_ie);
+       sdata->u.mgd.sme_auth_ie = NULL;
+       sdata->u.mgd.sme_auth_ie_len = 0;
+       if (req->ie) {
+               sdata->u.mgd.sme_auth_ie = kmalloc(req->ie_len, GFP_KERNEL);
+               if (sdata->u.mgd.sme_auth_ie == NULL)
+                       return -ENOMEM;
+               memcpy(sdata->u.mgd.sme_auth_ie, req->ie, req->ie_len);
+               sdata->u.mgd.sme_auth_ie_len = req->ie_len;
+       }
+
+       sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME;
+       sdata->u.mgd.state = IEEE80211_STA_MLME_DIRECT_PROBE;
+       ieee80211_sta_req_auth(sdata);
+       return 0;
+}
+
+static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
+                          struct cfg80211_assoc_request *req)
+{
+       struct ieee80211_sub_if_data *sdata;
+       int ret;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 ||
+           !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED))
+               return -ENOLINK; /* not authenticated */
+
+       sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
+       sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET;
+
+       /* TODO: req->chan */
+       sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL;
+
+       if (req->ssid) {
+               sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET;
+               memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len);
+               sdata->u.mgd.ssid_len = req->ssid_len;
+               sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
+       } else
+               sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL;
+
+       ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len);
+       if (ret)
+               return ret;
+
+       sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME;
+       sdata->u.mgd.state = IEEE80211_STA_MLME_ASSOCIATE;
+       ieee80211_sta_req_auth(sdata);
+       return 0;
+}
+
+static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev,
+                           struct cfg80211_deauth_request *req)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       /* TODO: req->ie */
+       return ieee80211_sta_deauthenticate(sdata, req->reason_code);
+}
+
+static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
+                             struct cfg80211_disassoc_request *req)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       /* TODO: req->ie */
+       return ieee80211_sta_disassociate(sdata, req->reason_code);
+}
+
 struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -1329,8 +1315,11 @@ struct cfg80211_ops mac80211_config_ops = {
        .change_bss = ieee80211_change_bss,
        .set_txq_params = ieee80211_set_txq_params,
        .set_channel = ieee80211_set_channel,
-       .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie,
        .suspend = ieee80211_suspend,
        .resume = ieee80211_resume,
        .scan = ieee80211_scan,
+       .auth = ieee80211_auth,
+       .assoc = ieee80211_assoc,
+       .deauth = ieee80211_deauth,
+       .disassoc = ieee80211_disassoc,
 };
index e37f557de3f33e5178ccaeb9fdc0f01689b8fc29..210b9b6fecd2a3676c8fe23393c22d87700228b4 100644 (file)
@@ -40,6 +40,10 @@ static const struct file_operations name## _ops = {                  \
        local->debugfs.name = debugfs_create_file(#name, 0400, phyd,    \
                                                  local, &name## _ops);
 
+#define DEBUGFS_ADD_MODE(name, mode)                                   \
+       local->debugfs.name = debugfs_create_file(#name, mode, phyd,    \
+                                                 local, &name## _ops);
+
 #define DEBUGFS_DEL(name)                                              \
        debugfs_remove(local->debugfs.name);                            \
        local->debugfs.name = NULL;
@@ -113,6 +117,24 @@ static const struct file_operations tsf_ops = {
        .open = mac80211_open_file_generic
 };
 
+static ssize_t reset_write(struct file *file, const char __user *user_buf,
+                          size_t count, loff_t *ppos)
+{
+       struct ieee80211_local *local = file->private_data;
+
+       rtnl_lock();
+       __ieee80211_suspend(&local->hw);
+       __ieee80211_resume(&local->hw);
+       rtnl_unlock();
+
+       return count;
+}
+
+static const struct file_operations reset_ops = {
+       .write = reset_write,
+       .open = mac80211_open_file_generic,
+};
+
 /* statistics stuff */
 
 #define DEBUGFS_STATS_FILE(name, buflen, fmt, value...)                        \
@@ -254,6 +276,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
        DEBUGFS_ADD(total_ps_buffered);
        DEBUGFS_ADD(wep_iv);
        DEBUGFS_ADD(tsf);
+       DEBUGFS_ADD_MODE(reset, 0200);
 
        statsd = debugfs_create_dir("statistics", phyd);
        local->debugfs.statistics = statsd;
@@ -308,6 +331,7 @@ void debugfs_hw_del(struct ieee80211_local *local)
        DEBUGFS_DEL(total_ps_buffered);
        DEBUGFS_DEL(wep_iv);
        DEBUGFS_DEL(tsf);
+       DEBUGFS_DEL(reset);
 
        DEBUGFS_STATS_DEL(transmitted_fragment_count);
        DEBUGFS_STATS_DEL(multicast_transmitted_frame_count);
index f4becc12904eb3c830649292660a89067b27c4b5..3201e1f96365649a5c1ad56f898afa20af9ddf0a 100644 (file)
@@ -812,8 +812,9 @@ int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata)
 
        ifibss->ibss_join_req = jiffies;
        ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
+       set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
 
-       return ieee80211_sta_find_ibss(sdata);
+       return 0;
 }
 
 int ieee80211_ibss_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
index fbb91f1aebb2a9775a5093937bfde9091c27c078..e6ed78cb16b39c8dde63a8e895c2c03cff2e6e91 100644 (file)
@@ -149,11 +149,6 @@ struct ieee80211_tx_data {
 
        struct ieee80211_channel *channel;
 
-       /* Extra fragments (in addition to the first fragment
-        * in skb) */
-       struct sk_buff **extra_frag;
-       int num_extra_frag;
-
        u16 ethertype;
        unsigned int flags;
 };
@@ -189,12 +184,6 @@ struct ieee80211_rx_data {
        u16 tkip_iv16;
 };
 
-struct ieee80211_tx_stored_packet {
-       struct sk_buff *skb;
-       struct sk_buff **extra_frag;
-       int num_extra_frag;
-};
-
 struct beacon_data {
        u8 *head, *tail;
        int head_len, tail_len;
@@ -247,8 +236,9 @@ struct mesh_preq_queue {
 #define IEEE80211_STA_ASSOCIATED       BIT(4)
 #define IEEE80211_STA_PROBEREQ_POLL    BIT(5)
 #define IEEE80211_STA_CREATE_IBSS      BIT(6)
-#define IEEE80211_STA_MIXED_CELL       BIT(7)
+/* hole at 7, please re-use */
 #define IEEE80211_STA_WMM_ENABLED      BIT(8)
+/* hole at 9, please re-use */
 #define IEEE80211_STA_AUTO_SSID_SEL    BIT(10)
 #define IEEE80211_STA_AUTO_BSSID_SEL   BIT(11)
 #define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12)
@@ -256,6 +246,7 @@ struct mesh_preq_queue {
 #define IEEE80211_STA_TKIP_WEP_USED    BIT(14)
 #define IEEE80211_STA_CSA_RECEIVED     BIT(15)
 #define IEEE80211_STA_MFP_ENABLED      BIT(16)
+#define IEEE80211_STA_EXT_SME          BIT(17)
 /* flags for MLME request */
 #define IEEE80211_STA_REQ_SCAN 0
 #define IEEE80211_STA_REQ_DIRECT_PROBE 1
@@ -266,12 +257,14 @@ struct mesh_preq_queue {
 #define IEEE80211_AUTH_ALG_OPEN BIT(0)
 #define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1)
 #define IEEE80211_AUTH_ALG_LEAP BIT(2)
+#define IEEE80211_AUTH_ALG_FT BIT(3)
 
 struct ieee80211_if_managed {
        struct timer_list timer;
        struct timer_list chswitch_timer;
        struct work_struct work;
        struct work_struct chswitch_work;
+       struct work_struct beacon_loss_work;
 
        u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
 
@@ -305,6 +298,7 @@ struct ieee80211_if_managed {
        unsigned long request;
 
        unsigned long last_probe;
+       unsigned long last_beacon;
 
        unsigned int flags;
 
@@ -321,20 +315,8 @@ struct ieee80211_if_managed {
        int wmm_last_param_set;
 
        /* Extra IE data for management frames */
-       u8 *ie_probereq;
-       size_t ie_probereq_len;
-       u8 *ie_proberesp;
-       size_t ie_proberesp_len;
-       u8 *ie_auth;
-       size_t ie_auth_len;
-       u8 *ie_assocreq;
-       size_t ie_assocreq_len;
-       u8 *ie_reassocreq;
-       size_t ie_reassocreq_len;
-       u8 *ie_deauth;
-       size_t ie_deauth_len;
-       u8 *ie_disassoc;
-       size_t ie_disassoc_len;
+       u8 *sme_auth_ie;
+       size_t sme_auth_ie_len;
 };
 
 enum ieee80211_ibss_flags {
@@ -421,7 +403,6 @@ struct ieee80211_if_mesh {
  *
  * @IEEE80211_SDATA_ALLMULTI: interface wants all multicast packets
  * @IEEE80211_SDATA_PROMISC: interface is promisc
- * @IEEE80211_SDATA_USERSPACE_MLME: userspace MLME is active
  * @IEEE80211_SDATA_OPERATING_GMODE: operating in G-only mode
  * @IEEE80211_SDATA_DONT_BRIDGE_PACKETS: bridge packets between
  *     associated stations and deliver multicast frames both
@@ -430,9 +411,8 @@ struct ieee80211_if_mesh {
 enum ieee80211_sub_if_data_flags {
        IEEE80211_SDATA_ALLMULTI                = BIT(0),
        IEEE80211_SDATA_PROMISC                 = BIT(1),
-       IEEE80211_SDATA_USERSPACE_MLME          = BIT(2),
-       IEEE80211_SDATA_OPERATING_GMODE         = BIT(3),
-       IEEE80211_SDATA_DONT_BRIDGE_PACKETS     = BIT(4),
+       IEEE80211_SDATA_OPERATING_GMODE         = BIT(2),
+       IEEE80211_SDATA_DONT_BRIDGE_PACKETS     = BIT(3),
 };
 
 struct ieee80211_sub_if_data {
@@ -598,6 +578,8 @@ enum queue_stop_reason {
        IEEE80211_QUEUE_STOP_REASON_PS,
        IEEE80211_QUEUE_STOP_REASON_CSA,
        IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
+       IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+       IEEE80211_QUEUE_STOP_REASON_PENDING,
 };
 
 struct ieee80211_master_priv {
@@ -612,12 +594,7 @@ struct ieee80211_local {
 
        const struct ieee80211_ops *ops;
 
-       /* AC queue corresponding to each AMPDU queue */
-       s8 ampdu_ac_queue[IEEE80211_MAX_AMPDU_QUEUES];
-       unsigned int amdpu_ac_stop_refcnt[IEEE80211_MAX_AMPDU_QUEUES];
-
-       unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES +
-                                        IEEE80211_MAX_AMPDU_QUEUES];
+       unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
        /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */
        spinlock_t queue_stop_reason_lock;
 
@@ -654,11 +631,17 @@ struct ieee80211_local {
        struct sta_info *sta_hash[STA_HASH_SIZE];
        struct timer_list sta_cleanup;
 
-       unsigned long queues_pending[BITS_TO_LONGS(IEEE80211_MAX_QUEUES)];
-       unsigned long queues_pending_run[BITS_TO_LONGS(IEEE80211_MAX_QUEUES)];
-       struct ieee80211_tx_stored_packet pending_packet[IEEE80211_MAX_QUEUES];
+       struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
        struct tasklet_struct tx_pending_tasklet;
 
+       /*
+        * This lock is used to prevent concurrent A-MPDU
+        * session start/stop processing, this thus also
+        * synchronises the ->ampdu_action() callback to
+        * drivers and limits it to one at a time.
+        */
+       spinlock_t ampdu_lock;
+
        /* number of interfaces with corresponding IFF_ flags */
        atomic_t iff_allmultis, iff_promiscs;
 
@@ -774,6 +757,7 @@ struct ieee80211_local {
                struct dentry *total_ps_buffered;
                struct dentry *wep_iv;
                struct dentry *tsf;
+               struct dentry *reset;
                struct dentry *statistics;
                struct local_debugfsdentries_statsdentries {
                        struct dentry *transmitted_fragment_count;
@@ -969,7 +953,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
                  struct sk_buff *skb,
                  struct ieee80211_rx_status *rx_status);
 int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
-                              char *ie, size_t len);
+                              const char *ie, size_t len);
 
 void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);
 void ieee80211_scan_failed(struct ieee80211_local *local);
@@ -1053,8 +1037,19 @@ void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
                                 u8 pwr_constr_elem_len);
 
 /* Suspend/resume */
+#ifdef CONFIG_PM
 int __ieee80211_suspend(struct ieee80211_hw *hw);
 int __ieee80211_resume(struct ieee80211_hw *hw);
+#else
+static inline int __ieee80211_suspend(struct ieee80211_hw *hw)
+{
+       return 0;
+}
+static inline int __ieee80211_resume(struct ieee80211_hw *hw)
+{
+       return 0;
+}
+#endif
 
 /* utility functions/constants */
 extern void *mac80211_wiphy_privid; /* for wiphy privid */
@@ -1081,6 +1076,9 @@ void ieee80211_dynamic_ps_timer(unsigned long data);
 void ieee80211_send_nullfunc(struct ieee80211_local *local,
                             struct ieee80211_sub_if_data *sdata,
                             int powersave);
+void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
+                            struct ieee80211_hdr *hdr);
+void ieee80211_beacon_loss_work(struct work_struct *work);
 
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
                                     enum queue_stop_reason reason);
index f9f27b9cadbe0e8ea79a2a18648395de1d622dd8..91e8e1bacaaa1a141ed5396ee54bce2d744250fe 100644 (file)
@@ -261,8 +261,7 @@ static int ieee80211_open(struct net_device *dev)
                ieee80211_bss_info_change_notify(sdata, changed);
                ieee80211_enable_keys(sdata);
 
-               if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-                   !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
+               if (sdata->vif.type == NL80211_IFTYPE_STATION)
                        netif_carrier_off(dev);
                else
                        netif_carrier_on(dev);
@@ -478,6 +477,9 @@ static int ieee80211_stop(struct net_device *dev)
                 */
                cancel_work_sync(&sdata->u.mgd.work);
                cancel_work_sync(&sdata->u.mgd.chswitch_work);
+
+               cancel_work_sync(&sdata->u.mgd.beacon_loss_work);
+
                /*
                 * When we get here, the interface is marked down.
                 * Call synchronize_rcu() to wait for the RX path
@@ -653,13 +655,7 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
                kfree(sdata->u.mgd.extra_ie);
                kfree(sdata->u.mgd.assocreq_ies);
                kfree(sdata->u.mgd.assocresp_ies);
-               kfree(sdata->u.mgd.ie_probereq);
-               kfree(sdata->u.mgd.ie_proberesp);
-               kfree(sdata->u.mgd.ie_auth);
-               kfree(sdata->u.mgd.ie_assocreq);
-               kfree(sdata->u.mgd.ie_reassocreq);
-               kfree(sdata->u.mgd.ie_deauth);
-               kfree(sdata->u.mgd.ie_disassoc);
+               kfree(sdata->u.mgd.sme_auth_ie);
                break;
        case NL80211_IFTYPE_WDS:
        case NL80211_IFTYPE_AP_VLAN:
index f38db4d37e5d6bd93c93ceff48652ce78f6bb0bf..a6f1d8a869bcca64a9abe1824182f10eeaf26cf8 100644 (file)
@@ -161,12 +161,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
        if (WARN_ON(!netif_running(sdata->dev)))
                return 0;
 
-       if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN))
-               return -EINVAL;
-
-       if (!local->ops->config_interface)
-               return 0;
-
        memset(&conf, 0, sizeof(conf));
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION)
@@ -183,6 +177,9 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
                return -EINVAL;
        }
 
+       if (!local->ops->config_interface)
+               return 0;
+
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_ADHOC:
@@ -224,9 +221,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
                }
        }
 
-       if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID)))
-               return -EINVAL;
-
        conf.changed = changed;
 
        return local->ops->config_interface(local_to_hw(local),
@@ -780,13 +774,10 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
        setup_timer(&local->dynamic_ps_timer,
                    ieee80211_dynamic_ps_timer, (unsigned long) local);
 
-       for (i = 0; i < IEEE80211_MAX_AMPDU_QUEUES; i++)
-               local->ampdu_ac_queue[i] = -1;
-       /* using an s8 won't work with more than that */
-       BUILD_BUG_ON(IEEE80211_MAX_AMPDU_QUEUES > 127);
-
        sta_info_init(local);
 
+       for (i = 0; i < IEEE80211_MAX_QUEUES; i++)
+               skb_queue_head_init(&local->pending[i]);
        tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
                     (unsigned long)local);
        tasklet_disable(&local->tx_pending_tasklet);
@@ -799,6 +790,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
        skb_queue_head_init(&local->skb_queue);
        skb_queue_head_init(&local->skb_queue_unreliable);
 
+       spin_lock_init(&local->ampdu_lock);
+
        return local_to_hw(local);
 }
 EXPORT_SYMBOL(ieee80211_alloc_hw);
@@ -876,10 +869,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
         */
        if (hw->queues > IEEE80211_MAX_QUEUES)
                hw->queues = IEEE80211_MAX_QUEUES;
-       if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
-               hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
-       if (hw->queues < 4)
-               hw->ampdu_queues = 0;
 
        mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv),
                               "wmaster%d", ieee80211_master_setup,
index 841b8450b3de787823a79b7ffe6ce6e437247d75..7ecda9d59d8a8e0d2eabefde24472a5538b9de3d 100644 (file)
@@ -30,7 +30,7 @@
 #define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
 #define IEEE80211_ASSOC_MAX_TRIES 3
 #define IEEE80211_MONITORING_INTERVAL (2 * HZ)
-#define IEEE80211_PROBE_INTERVAL (60 * HZ)
+#define IEEE80211_PROBE_IDLE_TIME (60 * HZ)
 #define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
 
 /* utils */
@@ -82,38 +82,23 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
 
 /* frame sending functions */
 
-static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len)
-{
-       if (ies)
-               memcpy(skb_put(skb, ies_len), ies, ies_len);
-}
-
 static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       u8 *pos, *ies, *ht_ie, *e_ies;
+       u8 *pos, *ies, *ht_ie;
        int i, len, count, rates_len, supp_rates_len;
        u16 capab;
        struct ieee80211_bss *bss;
        int wmm = 0;
        struct ieee80211_supported_band *sband;
        u32 rates = 0;
-       size_t e_ies_len;
-
-       if (ifmgd->flags & IEEE80211_IBSS_PREV_BSSID_SET) {
-               e_ies = sdata->u.mgd.ie_reassocreq;
-               e_ies_len = sdata->u.mgd.ie_reassocreq_len;
-       } else {
-               e_ies = sdata->u.mgd.ie_assocreq;
-               e_ies_len = sdata->u.mgd.ie_assocreq_len;
-       }
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom +
                            sizeof(*mgmt) + 200 + ifmgd->extra_ie_len +
-                           ifmgd->ssid_len + e_ies_len);
+                           ifmgd->ssid_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
                       "frame\n", sdata->dev->name);
@@ -304,8 +289,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
        }
 
-       add_extra_ies(skb, e_ies, e_ies_len);
-
        kfree(ifmgd->assocreq_ies);
        ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies;
        ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL);
@@ -323,19 +306,8 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       u8 *ies;
-       size_t ies_len;
-
-       if (stype == IEEE80211_STYPE_DEAUTH) {
-               ies = sdata->u.mgd.ie_deauth;
-               ies_len = sdata->u.mgd.ie_deauth_len;
-       } else {
-               ies = sdata->u.mgd.ie_disassoc;
-               ies_len = sdata->u.mgd.ie_disassoc_len;
-       }
 
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) +
-                           ies_len);
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for "
                       "deauth/disassoc frame\n", sdata->dev->name);
@@ -353,8 +325,6 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
        /* u.deauth.reason_code == u.disassoc.reason_code */
        mgmt->u.deauth.reason_code = cpu_to_le16(reason);
 
-       add_extra_ies(skb, ies, ies_len);
-
        ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED);
 }
 
@@ -640,6 +610,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
                bss_info_changed |= ieee80211_handle_bss_capability(sdata,
                        bss->cbss.capability, bss->has_erp_value, bss->erp_value);
 
+               cfg80211_hold_bss(&bss->cbss);
+
                ieee80211_rx_bss_put(local, bss);
        }
 
@@ -682,6 +654,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_local *local = sdata->local;
 
        ifmgd->direct_probe_tries++;
        if (ifmgd->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
@@ -697,6 +670,13 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
                ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
                                sdata->local->hw.conf.channel->center_freq,
                                ifmgd->ssid, ifmgd->ssid_len);
+
+               /*
+                * We might have a pending scan which had no chance to run yet
+                * due to state == IEEE80211_STA_MLME_DIRECT_PROBE.
+                * Hence, queue the STAs work again
+                */
+               queue_work(local->hw.workqueue, &ifmgd->work);
                return;
        }
 
@@ -721,6 +701,9 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
 static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_local *local = sdata->local;
+       u8 *ies;
+       size_t ies_len;
 
        ifmgd->auth_tries++;
        if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
@@ -732,6 +715,13 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
                ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
                                sdata->local->hw.conf.channel->center_freq,
                                ifmgd->ssid, ifmgd->ssid_len);
+
+               /*
+                * We might have a pending scan which had no chance to run yet
+                * due to state == IEEE80211_STA_MLME_AUTHENTICATE.
+                * Hence, queue the STAs work again
+                */
+               queue_work(local->hw.workqueue, &ifmgd->work);
                return;
        }
 
@@ -739,7 +729,14 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
        printk(KERN_DEBUG "%s: authenticate with AP %pM\n",
               sdata->dev->name, ifmgd->bssid);
 
-       ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, NULL, 0,
+       if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
+               ies = ifmgd->sme_auth_ie;
+               ies_len = ifmgd->sme_auth_ie_len;
+       } else {
+               ies = NULL;
+               ies_len = 0;
+       }
+       ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ies, ies_len,
                            ifmgd->bssid, 0);
        ifmgd->auth_transaction = 2;
 
@@ -756,6 +753,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_conf *conf = &local_to_hw(local)->conf;
+       struct ieee80211_bss *bss;
        struct sta_info *sta;
        u32 changed = 0, config_changed = 0;
 
@@ -779,6 +778,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_sta_tear_down_BA_sessions(sta);
 
+       bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
+                                  conf->channel->center_freq,
+                                  ifmgd->ssid, ifmgd->ssid_len);
+
+       if (bss) {
+               cfg80211_unhold_bss(&bss->cbss);
+               ieee80211_rx_bss_put(local, bss);
+       }
+
        if (self_disconnected) {
                if (deauth)
                        ieee80211_send_deauth_disassoc(sdata,
@@ -854,7 +862,7 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata)
        int wep_privacy;
        int privacy_invoked;
 
-       if (!ifmgd || (ifmgd->flags & IEEE80211_STA_MIXED_CELL))
+       if (!ifmgd || (ifmgd->flags & IEEE80211_STA_EXT_SME))
                return 0;
 
        bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
@@ -878,6 +886,7 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata)
 static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_local *local = sdata->local;
 
        ifmgd->assoc_tries++;
        if (ifmgd->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
@@ -889,6 +898,12 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
                ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
                                sdata->local->hw.conf.channel->center_freq,
                                ifmgd->ssid, ifmgd->ssid_len);
+               /*
+                * We might have a pending scan which had no chance to run yet
+                * due to state == IEEE80211_STA_MLME_ASSOCIATE.
+                * Hence, queue the STAs work again
+                */
+               queue_work(local->hw.workqueue, &ifmgd->work);
                return;
        }
 
@@ -907,13 +922,55 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
        mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
 }
 
+void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
+                            struct ieee80211_hdr *hdr)
+{
+       /*
+        * We can postpone the mgd.timer whenever receiving unicast frames
+        * from AP because we know that the connection is working both ways
+        * at that time. But multicast frames (and hence also beacons) must
+        * be ignored here, because we need to trigger the timer during
+        * data idle periods for sending the periodical probe request to
+        * the AP.
+        */
+       if (!is_multicast_ether_addr(hdr->addr1))
+               mod_timer(&sdata->u.mgd.timer,
+                         jiffies + IEEE80211_MONITORING_INTERVAL);
+}
+
+void ieee80211_beacon_loss_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            u.mgd.beacon_loss_work);
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+       printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM "
+              "- sending probe request\n", sdata->dev->name,
+              sdata->u.mgd.bssid);
+
+       ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
+       ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
+                                ifmgd->ssid_len, NULL, 0);
+
+       mod_timer(&ifmgd->timer, jiffies + IEEE80211_MONITORING_INTERVAL);
+}
+
+void ieee80211_beacon_loss(struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       queue_work(sdata->local->hw.workqueue,
+                  &sdata->u.mgd.beacon_loss_work);
+}
+EXPORT_SYMBOL(ieee80211_beacon_loss);
 
 static void ieee80211_associated(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
-       int disassoc;
+       bool disassoc = false;
 
        /* TODO: start monitoring current AP signal quality and number of
         * missed beacons. Scan other channels every now and then and search
@@ -928,36 +985,45 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata)
        if (!sta) {
                printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n",
                       sdata->dev->name, ifmgd->bssid);
-               disassoc = 1;
-       } else {
-               disassoc = 0;
-               if (time_after(jiffies,
-                              sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
-                       if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) {
-                               printk(KERN_DEBUG "%s: No ProbeResp from "
-                                      "current AP %pM - assume out of "
-                                      "range\n",
-                                      sdata->dev->name, ifmgd->bssid);
-                               disassoc = 1;
-                       } else
-                               ieee80211_send_probe_req(sdata, ifmgd->bssid,
-                                                        ifmgd->ssid,
-                                                        ifmgd->ssid_len,
-                                                        NULL, 0);
-                       ifmgd->flags ^= IEEE80211_STA_PROBEREQ_POLL;
-               } else {
-                       ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
-                       if (time_after(jiffies, ifmgd->last_probe +
-                                      IEEE80211_PROBE_INTERVAL)) {
-                               ifmgd->last_probe = jiffies;
-                               ieee80211_send_probe_req(sdata, ifmgd->bssid,
-                                                        ifmgd->ssid,
-                                                        ifmgd->ssid_len,
-                                                        NULL, 0);
-                       }
-               }
+               disassoc = true;
+               goto unlock;
        }
 
+       if ((ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) &&
+           time_after(jiffies, sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
+               printk(KERN_DEBUG "%s: no probe response from AP %pM "
+                      "- disassociating\n",
+                      sdata->dev->name, ifmgd->bssid);
+               disassoc = true;
+               ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
+               goto unlock;
+       }
+
+       /*
+        * Beacon filtering is only enabled with power save and then the
+        * stack should not check for beacon loss.
+        */
+       if (!((local->hw.flags & IEEE80211_HW_BEACON_FILTER) &&
+             (local->hw.conf.flags & IEEE80211_CONF_PS)) &&
+           time_after(jiffies,
+                      ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) {
+               printk(KERN_DEBUG "%s: beacon loss from AP %pM "
+                      "- sending probe request\n",
+                      sdata->dev->name, ifmgd->bssid);
+               ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
+               ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
+                                        ifmgd->ssid_len, NULL, 0);
+               goto unlock;
+
+       }
+
+       if (time_after(jiffies, sta->last_rx + IEEE80211_PROBE_IDLE_TIME)) {
+               ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
+               ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
+                                        ifmgd->ssid_len, NULL, 0);
+       }
+
+ unlock:
        rcu_read_unlock();
 
        if (disassoc)
@@ -975,7 +1041,11 @@ static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata)
 
        printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
        ifmgd->flags |= IEEE80211_STA_AUTHENTICATED;
-       ieee80211_associate(sdata);
+       if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
+               /* Wait for SME to request association */
+               ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+       } else
+               ieee80211_associate(sdata);
 }
 
 
@@ -1061,12 +1131,15 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
        switch (ifmgd->auth_alg) {
        case WLAN_AUTH_OPEN:
        case WLAN_AUTH_LEAP:
+       case WLAN_AUTH_FT:
                ieee80211_auth_completed(sdata);
+               cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len);
                break;
        case WLAN_AUTH_SHARED_KEY:
-               if (ifmgd->auth_transaction == 4)
+               if (ifmgd->auth_transaction == 4) {
                        ieee80211_auth_completed(sdata);
-               else
+                       cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len);
+               } else
                        ieee80211_auth_challenge(sdata, mgmt, len);
                break;
        }
@@ -1092,9 +1165,10 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
                printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n",
                                sdata->dev->name, reason_code);
 
-       if (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE ||
-           ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE ||
-           ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) {
+       if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
+           (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE ||
+            ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE ||
+            ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)) {
                ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
                mod_timer(&ifmgd->timer, jiffies +
                                      IEEE80211_RETRY_AUTH_INTERVAL);
@@ -1102,6 +1176,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_set_disassoc(sdata, true, false, 0);
        ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED;
+       cfg80211_send_rx_deauth(sdata->dev, (u8 *) mgmt, len);
 }
 
 
@@ -1124,13 +1199,15 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
                printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
                                sdata->dev->name, reason_code);
 
-       if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) {
+       if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
+           ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) {
                ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE;
                mod_timer(&ifmgd->timer, jiffies +
                                      IEEE80211_RETRY_AUTH_INTERVAL);
        }
 
        ieee80211_set_disassoc(sdata, false, false, reason_code);
+       cfg80211_send_rx_disassoc(sdata->dev, (u8 *) mgmt, len);
 }
 
 
@@ -1346,7 +1423,14 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        bss_conf->assoc_capability = capab_info;
        ieee80211_set_associated(sdata, changed);
 
+       /*
+        * initialise the time of last beacon to be the association time,
+        * otherwise beacon loss check will trigger immediately
+        */
+       ifmgd->last_beacon = jiffies;
+
        ieee80211_associated(sdata);
+       cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len);
 }
 
 
@@ -1393,9 +1477,12 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
                                         size_t len,
                                         struct ieee80211_rx_status *rx_status)
 {
+       struct ieee80211_if_managed *ifmgd;
        size_t baselen;
        struct ieee802_11_elems elems;
 
+       ifmgd = &sdata->u.mgd;
+
        if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
                return; /* ignore ProbeResp to foreign address */
 
@@ -1410,11 +1497,14 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
 
        /* direct probe may be part of the association flow */
        if (test_and_clear_bit(IEEE80211_STA_REQ_DIRECT_PROBE,
-           &sdata->u.mgd.request)) {
+                              &ifmgd->request)) {
                printk(KERN_DEBUG "%s direct probe responded\n",
                       sdata->dev->name);
                ieee80211_authenticate(sdata);
        }
+
+       if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL)
+               ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
 }
 
 static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
@@ -1636,6 +1726,8 @@ static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata)
                ifmgd->auth_alg = WLAN_AUTH_SHARED_KEY;
        else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP)
                ifmgd->auth_alg = WLAN_AUTH_LEAP;
+       else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_FT)
+               ifmgd->auth_alg = WLAN_AUTH_FT;
        else
                ifmgd->auth_alg = WLAN_AUTH_OPEN;
        ifmgd->auth_transaction = -1;
@@ -1659,7 +1751,8 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata)
        u16 capa_val = WLAN_CAPABILITY_ESS;
        struct ieee80211_channel *chan = local->oper_channel;
 
-       if (ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL |
+       if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
+           ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL |
                            IEEE80211_STA_AUTO_BSSID_SEL |
                            IEEE80211_STA_AUTO_CHANNEL_SEL)) {
                capa_mask |= WLAN_CAPABILITY_PRIVACY;
@@ -1822,6 +1915,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        ifmgd = &sdata->u.mgd;
        INIT_WORK(&ifmgd->work, ieee80211_sta_work);
        INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
+       INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
@@ -1834,7 +1928,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        ifmgd->flags |= IEEE80211_STA_CREATE_IBSS |
                IEEE80211_STA_AUTO_BSSID_SEL |
                IEEE80211_STA_AUTO_CHANNEL_SEL;
-       if (ieee80211_num_regular_queues(&sdata->local->hw) >= 4)
+       if (sdata->local->hw.queues >= 4)
                ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
 }
 
@@ -1856,7 +1950,11 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata)
                        ieee80211_set_disassoc(sdata, true, true,
                                               WLAN_REASON_DEAUTH_LEAVING);
 
-               set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request);
+               if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) ||
+                   ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE)
+                       set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request);
+               else if (ifmgd->flags & IEEE80211_STA_EXT_SME)
+                       set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
                queue_work(local->hw.workqueue, &ifmgd->work);
        }
 }
@@ -1865,8 +1963,6 @@ int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-       ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
-
        if (ifmgd->ssid_len)
                ifmgd->flags |= IEEE80211_STA_SSID_SET;
        else
@@ -1885,6 +1981,10 @@ int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size
        ifmgd = &sdata->u.mgd;
 
        if (ifmgd->ssid_len != len || memcmp(ifmgd->ssid, ssid, len) != 0) {
+               /*
+                * Do not use reassociation if SSID is changed (different ESS).
+                */
+               ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
                memset(ifmgd->ssid, 0, sizeof(ifmgd->ssid));
                memcpy(ifmgd->ssid, ssid, len);
                ifmgd->ssid_len = len;
@@ -1923,7 +2023,8 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
        return ieee80211_sta_commit(sdata);
 }
 
-int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, char *ie, size_t len)
+int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
+                              const char *ie, size_t len)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
index 44525f51707733f47ed10704828387690e023ca9..027302326498a681645e981db118077ecff3707e 100644 (file)
@@ -10,6 +10,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_if_init_conf conf;
        struct sta_info *sta;
+       unsigned long flags;
+
+       ieee80211_stop_queues_by_reason(hw,
+                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
        flush_workqueue(local->hw.workqueue);
 
@@ -17,10 +21,23 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
        list_for_each_entry(sdata, &local->interfaces, list)
                ieee80211_disable_keys(sdata);
 
-       /* remove STAs */
-       list_for_each_entry(sta, &local->sta_list, list) {
+       /* Tear down aggregation sessions */
+
+       rcu_read_lock();
+
+       if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+               list_for_each_entry_rcu(sta, &local->sta_list, list) {
+                       set_sta_flags(sta, WLAN_STA_SUSPEND);
+                       ieee80211_sta_tear_down_BA_sessions(sta);
+               }
+       }
 
-               if (local->ops->sta_notify) {
+       rcu_read_unlock();
+
+       /* remove STAs */
+       if (local->ops->sta_notify) {
+               spin_lock_irqsave(&local->sta_lock, flags);
+               list_for_each_entry(sta, &local->sta_list, list) {
                        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                                sdata = container_of(sdata->bss,
                                             struct ieee80211_sub_if_data,
@@ -29,11 +46,11 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
                        local->ops->sta_notify(hw, &sdata->vif,
                                STA_NOTIFY_REMOVE, &sta->sta);
                }
+               spin_unlock_irqrestore(&local->sta_lock, flags);
        }
 
        /* remove all interfaces */
        list_for_each_entry(sdata, &local->interfaces, list) {
-
                if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
                    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
                    netif_running(sdata->dev)) {
@@ -61,6 +78,7 @@ int __ieee80211_resume(struct ieee80211_hw *hw)
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_if_init_conf conf;
        struct sta_info *sta;
+       unsigned long flags;
        int res;
 
        /* restart hardware */
@@ -72,7 +90,6 @@ int __ieee80211_resume(struct ieee80211_hw *hw)
 
        /* add interfaces */
        list_for_each_entry(sdata, &local->interfaces, list) {
-
                if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
                    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
                    netif_running(sdata->dev)) {
@@ -84,9 +101,9 @@ int __ieee80211_resume(struct ieee80211_hw *hw)
        }
 
        /* add STAs back */
-       list_for_each_entry(sta, &local->sta_list, list) {
-
-               if (local->ops->sta_notify) {
+       if (local->ops->sta_notify) {
+               spin_lock_irqsave(&local->sta_lock, flags);
+               list_for_each_entry(sta, &local->sta_list, list) {
                        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                                sdata = container_of(sdata->bss,
                                             struct ieee80211_sub_if_data,
@@ -95,8 +112,21 @@ int __ieee80211_resume(struct ieee80211_hw *hw)
                        local->ops->sta_notify(hw, &sdata->vif,
                                STA_NOTIFY_ADD, &sta->sta);
                }
+               spin_unlock_irqrestore(&local->sta_lock, flags);
        }
 
+       /* Clear Suspend state so that ADDBA requests can be processed */
+
+       rcu_read_lock();
+
+       if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+               list_for_each_entry_rcu(sta, &local->sta_list, list) {
+                       clear_sta_flags(sta, WLAN_STA_SUSPEND);
+               }
+       }
+
+       rcu_read_unlock();
+
        /* add back keys */
        list_for_each_entry(sdata, &local->interfaces, list)
                if (netif_running(sdata->dev))
@@ -113,5 +143,37 @@ int __ieee80211_resume(struct ieee80211_hw *hw)
        ieee80211_configure_filter(local);
        netif_addr_unlock_bh(local->mdev);
 
+       /* Finally also reconfigure all the BSS information */
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               u32 changed = ~0;
+               if (!netif_running(sdata->dev))
+                       continue;
+               switch (sdata->vif.type) {
+               case NL80211_IFTYPE_STATION:
+                       /* disable beacon change bits */
+                       changed &= ~IEEE80211_IFCC_BEACON;
+                       /* fall through */
+               case NL80211_IFTYPE_ADHOC:
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_MESH_POINT:
+                       WARN_ON(ieee80211_if_config(sdata, changed));
+                       ieee80211_bss_info_change_notify(sdata, ~0);
+                       break;
+               case NL80211_IFTYPE_WDS:
+                       break;
+               case NL80211_IFTYPE_AP_VLAN:
+               case NL80211_IFTYPE_MONITOR:
+                       /* ignore virtual */
+                       break;
+               case NL80211_IFTYPE_UNSPECIFIED:
+               case __NL80211_IFTYPE_AFTER_LAST:
+                       WARN_ON(1);
+                       break;
+               }
+       }
+
+       ieee80211_wake_queues_by_reason(hw,
+                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+
        return 0;
 }
index 3fa7ab285066a46c5200d49c4ca06e4c56ff5456..4641f00a1e5cbfd005d4719d4925ac5a61fb58e0 100644 (file)
@@ -219,10 +219,12 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
                info->control.rates[i].count = 1;
        }
 
-       if (sta && sdata->force_unicast_rateidx > -1)
+       if (sta && sdata->force_unicast_rateidx > -1) {
                info->control.rates[0].idx = sdata->force_unicast_rateidx;
-       else
+       } else {
                ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
+               info->flags |= IEEE80211_TX_INTFL_RCALGO;
+       }
 
        /*
         * try to enforce the maximum rate the user wanted
index b9164c9a9563750ebd1f604931d0548b3242be07..2ab5ad9e71ce206a7c93efce93c80a4ce75c42b5 100644 (file)
@@ -44,8 +44,10 @@ static inline void rate_control_tx_status(struct ieee80211_local *local,
        struct rate_control_ref *ref = local->rate_ctrl;
        struct ieee80211_sta *ista = &sta->sta;
        void *priv_sta = sta->rate_ctrl_priv;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 
-       ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
+       if (likely(info->flags & IEEE80211_TX_INTFL_RCALGO))
+               ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
 }
 
 
index 66f7ecf51b924078a8ea52e6f88e85c3ff1f07d4..64ebe664effcec8b9de8f5c558be5fcbd33c7fec 100644 (file)
@@ -142,6 +142,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
        /* IEEE80211_RADIOTAP_FLAGS */
        if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
                *pos |= IEEE80211_RADIOTAP_F_FCS;
+       if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
+               *pos |= IEEE80211_RADIOTAP_F_BADFCS;
        if (status->flag & RX_FLAG_SHORTPRE)
                *pos |= IEEE80211_RADIOTAP_F_SHORTPRE;
        pos++;
@@ -204,9 +206,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
        /* ensure 2 byte alignment for the 2 byte field as required */
        if ((pos - (unsigned char *)rthdr) & 1)
                pos++;
-       /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */
-       if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
-               *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS);
+       if (status->flag & RX_FLAG_FAILED_PLCP_CRC)
+               *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADPLCP);
        pos += 2;
 }
 
@@ -849,12 +850,19 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
                 * Mesh beacons will update last_rx when if they are found to
                 * match the current local configuration when processed.
                 */
-               sta->last_rx = jiffies;
+               if (rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
+                   ieee80211_is_beacon(hdr->frame_control)) {
+                       rx->sdata->u.mgd.last_beacon = jiffies;
+               } else
+                       sta->last_rx = jiffies;
        }
 
        if (!(rx->flags & IEEE80211_RX_RA_MATCH))
                return RX_CONTINUE;
 
+       if (rx->sdata->vif.type == NL80211_IFTYPE_STATION)
+               ieee80211_sta_rx_notify(rx->sdata, hdr);
+
        sta->rx_fragments++;
        sta->rx_bytes += rx->skb->len;
        sta->last_signal = rx->status->signal;
@@ -1876,18 +1884,13 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
        if (ieee80211_vif_is_mesh(&sdata->vif))
                return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status);
 
-       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
-           sdata->vif.type != NL80211_IFTYPE_ADHOC)
-               return RX_DROP_MONITOR;
-
+       if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+               return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status);
 
-       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
-                       return RX_DROP_MONITOR;
+       if (sdata->vif.type == NL80211_IFTYPE_STATION)
                return ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
-       }
 
-       return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status);
+       return RX_DROP_MONITOR;
 }
 
 static void ieee80211_rx_michael_mic_report(struct net_device *dev,
index 5030a3c87509a28fe8957d10cc4312e4f9ee99f9..3bf9839f59169cda1ded90dd8263fc3e6597097d 100644 (file)
@@ -214,6 +214,66 @@ void ieee80211_scan_failed(struct ieee80211_local *local)
        local->scan_req = NULL;
 }
 
+/*
+ * inform AP that we will go to sleep so that it will buffer the frames
+ * while we scan
+ */
+static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       bool ps = false;
+
+       /* FIXME: what to do when local->pspolling is true? */
+
+       del_timer_sync(&local->dynamic_ps_timer);
+       cancel_work_sync(&local->dynamic_ps_enable_work);
+
+       if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+               ps = true;
+               local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+       }
+
+       if (!ps || !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
+               /*
+                * If power save was enabled, no need to send a nullfunc
+                * frame because AP knows that we are sleeping. But if the
+                * hardware is creating the nullfunc frame for power save
+                * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not
+                * enabled) and power save was enabled, the firmware just
+                * sent a null frame with power save disabled. So we need
+                * to send a new nullfunc frame to inform the AP that we
+                * are again sleeping.
+                */
+               ieee80211_send_nullfunc(local, sdata, 1);
+}
+
+/* inform AP that we are awake again, unless power save is enabled */
+static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+
+       if (!local->powersave)
+               ieee80211_send_nullfunc(local, sdata, 0);
+       else {
+               /*
+                * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
+                * will send a nullfunc frame with the powersave bit set
+                * even though the AP already knows that we are sleeping.
+                * This could be avoided by sending a null frame with power
+                * save bit disabled before enabling the power save, but
+                * this doesn't gain anything.
+                *
+                * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need
+                * to send a nullfunc frame because AP already knows that
+                * we are sleeping, let's just enable power save mode in
+                * hardware.
+                */
+               local->hw.conf.flags |= IEEE80211_CONF_PS;
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+       }
+}
+
 void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
@@ -268,7 +328,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
                /* Tell AP we're back */
                if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                        if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) {
-                               ieee80211_send_nullfunc(local, sdata, 0);
+                               ieee80211_scan_ps_disable(sdata);
                                netif_tx_wake_all_queues(sdata->dev);
                        }
                } else
@@ -409,6 +469,19 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
                return 0;
        }
 
+       /*
+        * Hardware/driver doesn't support hw_scan, so use software
+        * scanning instead. First send a nullfunc frame with power save
+        * bit on so that AP will buffer the frames for us while we are not
+        * listening, then send probe requests to each channel and wait for
+        * the responses. After all channels are scanned, tune back to the
+        * original channel and send a nullfunc frame with power save bit
+        * off to trigger the AP to send us all the buffered frames.
+        *
+        * Note that while local->sw_scanning is true everything else but
+        * nullfunc frames and probe requests will be dropped in
+        * ieee80211_tx_h_check_assoc().
+        */
        local->sw_scanning = true;
        if (local->ops->sw_scan_start)
                local->ops->sw_scan_start(local_to_hw(local));
@@ -428,7 +501,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
                if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                        if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) {
                                netif_tx_stop_all_queues(sdata->dev);
-                               ieee80211_send_nullfunc(local, sdata, 1);
+                               ieee80211_scan_ps_enable(sdata);
                        }
                } else
                        netif_tx_stop_all_queues(sdata->dev);
index 4ba3c540fcf3adedf2402833b67bf2cedc9d6b8a..c5f14e6bbde20ab9128f69d28923cf8a3c84b459 100644 (file)
@@ -203,17 +203,6 @@ void sta_info_destroy(struct sta_info *sta)
                if (tid_rx)
                        tid_rx->shutdown = true;
 
-               /*
-                * The stop callback cannot find this station any more, but
-                * it didn't complete its work -- start the queue if necessary
-                */
-               if (sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_INITIATOR_MSK &&
-                   sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_REQ_STOP_BA_MSK &&
-                   local->hw.ampdu_queues)
-                       ieee80211_wake_queue_by_reason(&local->hw,
-                               local->hw.queues + sta->tid_to_tx_q[i],
-                               IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-
                spin_unlock_bh(&sta->lock);
 
                /*
@@ -239,6 +228,11 @@ void sta_info_destroy(struct sta_info *sta)
                tid_tx = sta->ampdu_mlme.tid_tx[i];
                if (tid_tx) {
                        del_timer_sync(&tid_tx->addba_resp_timer);
+                       /*
+                        * STA removed while aggregation session being
+                        * started? Bit odd, but purge frames anyway.
+                        */
+                       skb_queue_purge(&tid_tx->pending);
                        kfree(tid_tx);
                }
        }
@@ -287,7 +281,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                 * enable session_timer's data differentiation. refer to
                 * sta_rx_agg_session_timer_expired for useage */
                sta->timer_to_tid[i] = i;
-               sta->tid_to_tx_q[i] = -1;
                /* rx */
                sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE;
                sta->ampdu_mlme.tid_rx[i] = NULL;
index 1f45573c580c4a837857f714290c8128faf2567e..5534d489f50678826f751d7cf04fbcd2af50f45f 100644 (file)
@@ -35,6 +35,8 @@
  *     IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next
  *     frame to this station is transmitted.
  * @WLAN_STA_MFP: Management frame protection is used with this STA.
+ * @WLAN_STA_SUSPEND: Set/cleared during a suspend/resume cycle.
+ *     Used to deny ADDBA requests (both TX and RX).
  */
 enum ieee80211_sta_info_flags {
        WLAN_STA_AUTH           = 1<<0,
@@ -48,6 +50,7 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_PSPOLL         = 1<<8,
        WLAN_STA_CLEAR_PS_FILT  = 1<<9,
        WLAN_STA_MFP            = 1<<10,
+       WLAN_STA_SUSPEND        = 1<<11
 };
 
 #define STA_TID_NUM 16
@@ -70,11 +73,13 @@ enum ieee80211_sta_info_flags {
  * struct tid_ampdu_tx - TID aggregation information (Tx).
  *
  * @addba_resp_timer: timer for peer's response to addba request
+ * @pending: pending frames queue -- use sta's spinlock to protect
  * @ssn: Starting Sequence Number expected to be aggregated.
  * @dialog_token: dialog token for aggregation session
  */
 struct tid_ampdu_tx {
        struct timer_list addba_resp_timer;
+       struct sk_buff_head pending;
        u16 ssn;
        u8 dialog_token;
 };
@@ -201,7 +206,6 @@ struct sta_ampdu_mlme {
  * @tid_seq: per-TID sequence numbers for sending to this STA
  * @ampdu_mlme: A-MPDU state machine state
  * @timer_to_tid: identity mapping to ID timers
- * @tid_to_tx_q: map tid to tx queue (invalid == negative values)
  * @llid: Local link ID
  * @plid: Peer link ID
  * @reason: Cancel reason on PLINK_HOLDING state
@@ -276,7 +280,6 @@ struct sta_info {
         */
        struct sta_ampdu_mlme ampdu_mlme;
        u8 timer_to_tid[STA_TID_NUM];
-       s8 tid_to_tx_q[STA_TID_NUM];
 
 #ifdef CONFIG_MAC80211_MESH
        /*
index 457238a2f3fc392a087944c7edb5808b2b004c37..3fb04a86444d959d980f5e7d3e0ce1a2c779a63a 100644 (file)
@@ -34,8 +34,7 @@
 
 #define IEEE80211_TX_OK                0
 #define IEEE80211_TX_AGAIN     1
-#define IEEE80211_TX_FRAG_AGAIN        2
-#define IEEE80211_TX_PENDING   3
+#define IEEE80211_TX_PENDING   2
 
 /* misc utils */
 
@@ -193,7 +192,19 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
                return TX_CONTINUE;
 
        if (unlikely(tx->local->sw_scanning) &&
-           !ieee80211_is_probe_req(hdr->frame_control))
+           !ieee80211_is_probe_req(hdr->frame_control) &&
+           !ieee80211_is_nullfunc(hdr->frame_control))
+               /*
+                * When software scanning only nullfunc frames (to notify
+                * the sleep state to the AP) and probe requests (for the
+                * active scan) are allowed, all other frames should not be
+                * sent and we should not get here, but if we do
+                * nonetheless, drop them to avoid sending them
+                * off-channel. See the link below and
+                * ieee80211_start_scan() for more.
+                *
+                * http://article.gmane.org/gmane.linux.kernel.wireless.general/30089
+                */
                return TX_DROP;
 
        if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
@@ -690,17 +701,62 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
        return TX_CONTINUE;
 }
 
+static int ieee80211_fragment(struct ieee80211_local *local,
+                             struct sk_buff *skb, int hdrlen,
+                             int frag_threshold)
+{
+       struct sk_buff *tail = skb, *tmp;
+       int per_fragm = frag_threshold - hdrlen - FCS_LEN;
+       int pos = hdrlen + per_fragm;
+       int rem = skb->len - hdrlen - per_fragm;
+
+       if (WARN_ON(rem < 0))
+               return -EINVAL;
+
+       while (rem) {
+               int fraglen = per_fragm;
+
+               if (fraglen > rem)
+                       fraglen = rem;
+               rem -= fraglen;
+               tmp = dev_alloc_skb(local->tx_headroom +
+                                   frag_threshold +
+                                   IEEE80211_ENCRYPT_HEADROOM +
+                                   IEEE80211_ENCRYPT_TAILROOM);
+               if (!tmp)
+                       return -ENOMEM;
+               tail->next = tmp;
+               tail = tmp;
+               skb_reserve(tmp, local->tx_headroom +
+                                IEEE80211_ENCRYPT_HEADROOM);
+               /* copy control information */
+               memcpy(tmp->cb, skb->cb, sizeof(tmp->cb));
+               skb_copy_queue_mapping(tmp, skb);
+               tmp->priority = skb->priority;
+               tmp->do_not_encrypt = skb->do_not_encrypt;
+               tmp->dev = skb->dev;
+               tmp->iif = skb->iif;
+
+               /* copy header and data */
+               memcpy(skb_put(tmp, hdrlen), skb->data, hdrlen);
+               memcpy(skb_put(tmp, fraglen), skb->data + pos, fraglen);
+
+               pos += fraglen;
+       }
+
+       skb->len = hdrlen + per_fragm;
+       return 0;
+}
+
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
 {
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
-       size_t hdrlen, per_fragm, num_fragm, payload_len, left;
-       struct sk_buff **frags, *first, *frag;
-       int i;
-       u16 seq;
-       u8 *pos;
+       struct sk_buff *skb = tx->skb;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (void *)skb->data;
        int frag_threshold = tx->local->fragmentation_threshold;
+       int hdrlen;
+       int fragnum;
 
        if (!(tx->flags & IEEE80211_TX_FRAGMENTED))
                return TX_CONTINUE;
@@ -713,58 +769,35 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
        if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
                return TX_DROP;
 
-       first = tx->skb;
-
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
-       payload_len = first->len - hdrlen;
-       per_fragm = frag_threshold - hdrlen - FCS_LEN;
-       num_fragm = DIV_ROUND_UP(payload_len, per_fragm);
-
-       frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC);
-       if (!frags)
-               goto fail;
-
-       hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
-       seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ;
-       pos = first->data + hdrlen + per_fragm;
-       left = payload_len - per_fragm;
-       for (i = 0; i < num_fragm - 1; i++) {
-               struct ieee80211_hdr *fhdr;
-               size_t copylen;
-
-               if (left <= 0)
-                       goto fail;
 
-               /* reserve enough extra head and tail room for possible
-                * encryption */
-               frag = frags[i] =
-                       dev_alloc_skb(tx->local->tx_headroom +
-                                     frag_threshold +
-                                     IEEE80211_ENCRYPT_HEADROOM +
-                                     IEEE80211_ENCRYPT_TAILROOM);
-               if (!frag)
-                       goto fail;
-
-               /* Make sure that all fragments use the same priority so
-                * that they end up using the same TX queue */
-               frag->priority = first->priority;
+       /* internal error, why is TX_FRAGMENTED set? */
+       if (WARN_ON(skb->len <= frag_threshold))
+               return TX_DROP;
 
-               skb_reserve(frag, tx->local->tx_headroom +
-                                 IEEE80211_ENCRYPT_HEADROOM);
+       /*
+        * Now fragment the frame. This will allocate all the fragments and
+        * chain them (using skb as the first fragment) to skb->next.
+        * During transmission, we will remove the successfully transmitted
+        * fragments from this list. When the low-level driver rejects one
+        * of the fragments then we will simply pretend to accept the skb
+        * but store it away as pending.
+        */
+       if (ieee80211_fragment(tx->local, skb, hdrlen, frag_threshold))
+               return TX_DROP;
 
-               /* copy TX information */
-               info = IEEE80211_SKB_CB(frag);
-               memcpy(info, first->cb, sizeof(frag->cb));
+       /* update duration/seq/flags of fragments */
+       fragnum = 0;
+       do {
+               int next_len;
+               const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
 
-               /* copy/fill in 802.11 header */
-               fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
-               memcpy(fhdr, first->data, hdrlen);
-               fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
+               hdr = (void *)skb->data;
+               info = IEEE80211_SKB_CB(skb);
 
-               if (i == num_fragm - 2) {
-                       /* clear MOREFRAGS bit for the last fragment */
-                       fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
-               } else {
+               if (skb->next) {
+                       hdr->frame_control |= morefrags;
+                       next_len = skb->next->len;
                        /*
                         * No multi-rate retries for fragmented frames, that
                         * would completely throw off the NAV at other STAs.
@@ -775,37 +808,16 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
                        info->control.rates[4].idx = -1;
                        BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5);
                        info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
+               } else {
+                       hdr->frame_control &= ~morefrags;
+                       next_len = 0;
                }
-
-               /* copy data */
-               copylen = left > per_fragm ? per_fragm : left;
-               memcpy(skb_put(frag, copylen), pos, copylen);
-
-               skb_copy_queue_mapping(frag, first);
-
-               frag->do_not_encrypt = first->do_not_encrypt;
-               frag->dev = first->dev;
-               frag->iif = first->iif;
-
-               pos += copylen;
-               left -= copylen;
-       }
-       skb_trim(first, hdrlen + per_fragm);
-
-       tx->num_extra_frag = num_fragm - 1;
-       tx->extra_frag = frags;
+               hdr->duration_id = ieee80211_duration(tx, 0, next_len);
+               hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG);
+               fragnum++;
+       } while ((skb = skb->next));
 
        return TX_CONTINUE;
-
- fail:
-       if (frags) {
-               for (i = 0; i < num_fragm - 1; i++)
-                       if (frags[i])
-                               dev_kfree_skb(frags[i]);
-               kfree(frags);
-       }
-       I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment);
-       return TX_DROP;
 }
 
 static ieee80211_tx_result debug_noinline
@@ -833,27 +845,19 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
 {
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
-       int next_len, i;
-       int group_addr = is_multicast_ether_addr(hdr->addr1);
-
-       if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) {
-               hdr->duration_id = ieee80211_duration(tx, group_addr, 0);
-               return TX_CONTINUE;
-       }
+       struct sk_buff *skb = tx->skb;
+       struct ieee80211_hdr *hdr;
+       int next_len;
+       bool group_addr;
 
-       hdr->duration_id = ieee80211_duration(tx, group_addr,
-                                             tx->extra_frag[0]->len);
+       do {
+               hdr = (void *) skb->data;
+               next_len = skb->next ? skb->next->len : 0;
+               group_addr = is_multicast_ether_addr(hdr->addr1);
 
-       for (i = 0; i < tx->num_extra_frag; i++) {
-               if (i + 1 < tx->num_extra_frag)
-                       next_len = tx->extra_frag[i + 1]->len;
-               else
-                       next_len = 0;
-
-               hdr = (struct ieee80211_hdr *)tx->extra_frag[i]->data;
-               hdr->duration_id = ieee80211_duration(tx, 0, next_len);
-       }
+               hdr->duration_id =
+                       ieee80211_duration(tx, group_addr, next_len);
+       } while ((skb = skb->next));
 
        return TX_CONTINUE;
 }
@@ -861,19 +865,16 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
 {
-       int i;
+       struct sk_buff *skb = tx->skb;
 
        if (!tx->sta)
                return TX_CONTINUE;
 
        tx->sta->tx_packets++;
-       tx->sta->tx_fragments++;
-       tx->sta->tx_bytes += tx->skb->len;
-       if (tx->extra_frag) {
-               tx->sta->tx_fragments += tx->num_extra_frag;
-               for (i = 0; i < tx->num_extra_frag; i++)
-                       tx->sta->tx_bytes += tx->extra_frag[i]->len;
-       }
+       do {
+               tx->sta->tx_fragments++;
+               tx->sta->tx_bytes += skb->len;
+       } while ((skb = skb->next));
 
        return TX_CONTINUE;
 }
@@ -983,9 +984,9 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
        struct ieee80211_hdr *hdr;
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
        int hdrlen, tid;
        u8 *qc, *state;
+       bool queued = false;
 
        memset(tx, 0, sizeof(*tx));
        tx->skb = skb;
@@ -1012,25 +1013,53 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
                 */
        }
 
+       /*
+        * If this flag is set to true anywhere, and we get here,
+        * we are doing the needed processing, so remove the flag
+        * now.
+        */
+       info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+
        hdr = (struct ieee80211_hdr *) skb->data;
 
        tx->sta = sta_info_get(local, hdr->addr1);
 
-       if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+       if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
+           (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) {
                unsigned long flags;
+               struct tid_ampdu_tx *tid_tx;
+
                qc = ieee80211_get_qos_ctl(hdr);
                tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
 
                spin_lock_irqsave(&tx->sta->lock, flags);
+               /*
+                * XXX: This spinlock could be fairly expensive, but see the
+                *      comment in agg-tx.c:ieee80211_agg_tx_operational().
+                *      One way to solve this would be to do something RCU-like
+                *      for managing the tid_tx struct and using atomic bitops
+                *      for the actual state -- by introducing an actual
+                *      'operational' bit that would be possible. It would
+                *      require changing ieee80211_agg_tx_operational() to
+                *      set that bit, and changing the way tid_tx is managed
+                *      everywhere, including races between that bit and
+                *      tid_tx going away (tid_tx being added can be easily
+                *      committed to memory before the 'operational' bit).
+                */
+               tid_tx = tx->sta->ampdu_mlme.tid_tx[tid];
                state = &tx->sta->ampdu_mlme.tid_state_tx[tid];
                if (*state == HT_AGG_STATE_OPERATIONAL) {
                        info->flags |= IEEE80211_TX_CTL_AMPDU;
-                       if (local->hw.ampdu_queues)
-                               skb_set_queue_mapping(
-                                       skb, tx->local->hw.queues +
-                                            tx->sta->tid_to_tx_q[tid]);
+               } else if (*state != HT_AGG_STATE_IDLE) {
+                       /* in progress */
+                       queued = true;
+                       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+                       __skb_queue_tail(&tid_tx->pending, skb);
                }
                spin_unlock_irqrestore(&tx->sta->lock, flags);
+
+               if (unlikely(queued))
+                       return TX_QUEUED;
        }
 
        if (is_multicast_ether_addr(hdr->addr1)) {
@@ -1081,51 +1110,55 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local,
        }
        if (unlikely(!dev))
                return -ENODEV;
-       /* initialises tx with control */
+       /*
+        * initialises tx with control
+        *
+        * return value is safe to ignore here because this function
+        * can only be invoked for multicast frames
+        *
+        * XXX: clean up
+        */
        __ieee80211_tx_prepare(tx, skb, dev);
        dev_put(dev);
        return 0;
 }
 
-static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
-                         struct ieee80211_tx_data *tx)
+static int __ieee80211_tx(struct ieee80211_local *local,
+                         struct sk_buff **skbp,
+                         struct sta_info *sta)
 {
+       struct sk_buff *skb = *skbp, *next;
        struct ieee80211_tx_info *info;
-       int ret, i;
+       int ret, len;
+       bool fragm = false;
 
-       if (skb) {
+       local->mdev->trans_start = jiffies;
+
+       while (skb) {
                if (ieee80211_queue_stopped(&local->hw,
                                            skb_get_queue_mapping(skb)))
                        return IEEE80211_TX_PENDING;
 
-               ret = local->ops->tx(local_to_hw(local), skb);
-               if (ret)
-                       return IEEE80211_TX_AGAIN;
-               local->mdev->trans_start = jiffies;
-               ieee80211_led_tx(local, 1);
-       }
-       if (tx->extra_frag) {
-               for (i = 0; i < tx->num_extra_frag; i++) {
-                       if (!tx->extra_frag[i])
-                               continue;
-                       info = IEEE80211_SKB_CB(tx->extra_frag[i]);
+               info = IEEE80211_SKB_CB(skb);
+
+               if (fragm)
                        info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
                                         IEEE80211_TX_CTL_FIRST_FRAGMENT);
-                       if (ieee80211_queue_stopped(&local->hw,
-                                       skb_get_queue_mapping(tx->extra_frag[i])))
-                               return IEEE80211_TX_FRAG_AGAIN;
-
-                       ret = local->ops->tx(local_to_hw(local),
-                                           tx->extra_frag[i]);
-                       if (ret)
-                               return IEEE80211_TX_FRAG_AGAIN;
-                       local->mdev->trans_start = jiffies;
-                       ieee80211_led_tx(local, 1);
-                       tx->extra_frag[i] = NULL;
+
+               next = skb->next;
+               len = skb->len;
+               ret = local->ops->tx(local_to_hw(local), skb);
+               if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
+                       dev_kfree_skb(skb);
+                       ret = NETDEV_TX_OK;
                }
-               kfree(tx->extra_frag);
-               tx->extra_frag = NULL;
+               if (ret != NETDEV_TX_OK)
+                       return IEEE80211_TX_AGAIN;
+               *skbp = skb = next;
+               ieee80211_led_tx(local, 1);
+               fragm = true;
        }
+
        return IEEE80211_TX_OK;
 }
 
@@ -1137,7 +1170,6 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
 {
        struct sk_buff *skb = tx->skb;
        ieee80211_tx_result res = TX_DROP;
-       int i;
 
 #define CALL_TXH(txh)          \
        res = txh(tx);          \
@@ -1161,11 +1193,13 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
  txh_done:
        if (unlikely(res == TX_DROP)) {
                I802_DEBUG_INC(tx->local->tx_handlers_drop);
-               dev_kfree_skb(skb);
-               for (i = 0; i < tx->num_extra_frag; i++)
-                       if (tx->extra_frag[i])
-                               dev_kfree_skb(tx->extra_frag[i]);
-               kfree(tx->extra_frag);
+               while (skb) {
+                       struct sk_buff *next;
+
+                       next = skb->next;
+                       dev_kfree_skb(skb);
+                       skb = next;
+               }
                return -1;
        } else if (unlikely(res == TX_QUEUED)) {
                I802_DEBUG_INC(tx->local->tx_handlers_queued);
@@ -1175,23 +1209,26 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
        return 0;
 }
 
-static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
+static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
+                        bool txpending)
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct sta_info *sta;
        struct ieee80211_tx_data tx;
        ieee80211_tx_result res_prepare;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       int ret, i;
+       struct sk_buff *next;
+       unsigned long flags;
+       int ret, retries;
        u16 queue;
 
        queue = skb_get_queue_mapping(skb);
 
-       WARN_ON(test_bit(queue, local->queues_pending));
+       WARN_ON(!txpending && !skb_queue_empty(&local->pending[queue]));
 
        if (unlikely(skb->len < 10)) {
                dev_kfree_skb(skb);
-               return 0;
+               return;
        }
 
        rcu_read_lock();
@@ -1199,10 +1236,13 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
        /* initialises tx */
        res_prepare = __ieee80211_tx_prepare(&tx, skb, dev);
 
-       if (res_prepare == TX_DROP) {
+       if (unlikely(res_prepare == TX_DROP)) {
                dev_kfree_skb(skb);
                rcu_read_unlock();
-               return 0;
+               return;
+       } else if (unlikely(res_prepare == TX_QUEUED)) {
+               rcu_read_unlock();
+               return;
        }
 
        sta = tx.sta;
@@ -1212,59 +1252,71 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
        if (invoke_tx_handlers(&tx))
                goto out;
 
-retry:
-       ret = __ieee80211_tx(local, skb, &tx);
-       if (ret) {
-               struct ieee80211_tx_stored_packet *store;
-
+       retries = 0;
+ retry:
+       ret = __ieee80211_tx(local, &tx.skb, tx.sta);
+       switch (ret) {
+       case IEEE80211_TX_OK:
+               break;
+       case IEEE80211_TX_AGAIN:
                /*
                 * Since there are no fragmented frames on A-MPDU
                 * queues, there's no reason for a driver to reject
                 * a frame there, warn and drop it.
                 */
-               if (ret != IEEE80211_TX_PENDING)
-                       if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
-                               goto drop;
+               if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
+                       goto drop;
+               /* fall through */
+       case IEEE80211_TX_PENDING:
+               skb = tx.skb;
+
+               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
+               if (__netif_subqueue_stopped(local->mdev, queue)) {
+                       do {
+                               next = skb->next;
+                               skb->next = NULL;
+                               if (unlikely(txpending))
+                                       skb_queue_head(&local->pending[queue],
+                                                      skb);
+                               else
+                                       skb_queue_tail(&local->pending[queue],
+                                                      skb);
+                       } while ((skb = next));
 
-               store = &local->pending_packet[queue];
+                       /*
+                        * Make sure nobody will enable the queue on us
+                        * (without going through the tasklet) nor disable the
+                        * netdev queue underneath the pending handling code.
+                        */
+                       __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING,
+                                 &local->queue_stop_reasons[queue]);
 
-               if (ret == IEEE80211_TX_FRAG_AGAIN)
-                       skb = NULL;
+                       spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+                                              flags);
+               } else {
+                       spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+                                              flags);
 
-               set_bit(queue, local->queues_pending);
-               smp_mb();
-               /*
-                * When the driver gets out of buffers during sending of
-                * fragments and calls ieee80211_stop_queue, the netif
-                * subqueue is stopped. There is, however, a small window
-                * in which the PENDING bit is not yet set. If a buffer
-                * gets available in that window (i.e. driver calls
-                * ieee80211_wake_queue), we would end up with ieee80211_tx
-                * called with the PENDING bit still set. Prevent this by
-                * continuing transmitting here when that situation is
-                * possible to have happened.
-                */
-               if (!__netif_subqueue_stopped(local->mdev, queue)) {
-                       clear_bit(queue, local->queues_pending);
+                       retries++;
+                       if (WARN(retries > 10, "tx refused but queue active"))
+                               goto drop;
                        goto retry;
                }
-               store->skb = skb;
-               store->extra_frag = tx.extra_frag;
-               store->num_extra_frag = tx.num_extra_frag;
        }
  out:
        rcu_read_unlock();
-       return 0;
+       return;
 
  drop:
-       if (skb)
-               dev_kfree_skb(skb);
-       for (i = 0; i < tx.num_extra_frag; i++)
-               if (tx.extra_frag[i])
-                       dev_kfree_skb(tx.extra_frag[i]);
-       kfree(tx.extra_frag);
        rcu_read_unlock();
-       return 0;
+
+       skb = tx.skb;
+       while (skb) {
+               next = skb->next;
+               dev_kfree_skb(skb);
+               skb = next;
+       }
 }
 
 /* device xmit handlers */
@@ -1323,7 +1375,6 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
                FOUND_SDATA,
                UNKNOWN_ADDRESS,
        } monitor_iface = NOT_MONITOR;
-       int ret;
 
        if (skb->iif)
                odev = dev_get_by_index(&init_net, skb->iif);
@@ -1337,7 +1388,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
                       "originating device\n", dev->name);
 #endif
                dev_kfree_skb(skb);
-               return 0;
+               return NETDEV_TX_OK;
        }
 
        if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
@@ -1366,7 +1417,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
                else
                        if (mesh_nexthop_lookup(skb, osdata)) {
                                dev_put(odev);
-                               return 0;
+                               return NETDEV_TX_OK;
                        }
                if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0)
                        IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh,
@@ -1428,7 +1479,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (ieee80211_skb_resize(osdata->local, skb, headroom, may_encrypt)) {
                dev_kfree_skb(skb);
                dev_put(odev);
-               return 0;
+               return NETDEV_TX_OK;
        }
 
        if (osdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -1437,10 +1488,11 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                      u.ap);
        if (likely(monitor_iface != UNKNOWN_ADDRESS))
                info->control.vif = &osdata->vif;
-       ret = ieee80211_tx(odev, skb);
+
+       ieee80211_tx(odev, skb, false);
        dev_put(odev);
 
-       return ret;
+       return NETDEV_TX_OK;
 }
 
 int ieee80211_monitor_start_xmit(struct sk_buff *skb,
@@ -1666,8 +1718,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
        }
 
        /* receiver and we are QoS enabled, use a QoS type frame */
-       if (sta_flags & WLAN_STA_WME &&
-           ieee80211_num_regular_queues(&local->hw) >= 4) {
+       if ((sta_flags & WLAN_STA_WME) && local->hw.queues >= 4) {
                fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
                hdrlen += 2;
        }
@@ -1799,19 +1850,58 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
  */
 void ieee80211_clear_tx_pending(struct ieee80211_local *local)
 {
-       int i, j;
-       struct ieee80211_tx_stored_packet *store;
+       int i;
 
-       for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) {
-               if (!test_bit(i, local->queues_pending))
-                       continue;
-               store = &local->pending_packet[i];
-               kfree_skb(store->skb);
-               for (j = 0; j < store->num_extra_frag; j++)
-                       kfree_skb(store->extra_frag[j]);
-               kfree(store->extra_frag);
-               clear_bit(i, local->queues_pending);
+       for (i = 0; i < local->hw.queues; i++)
+               skb_queue_purge(&local->pending[i]);
+}
+
+static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
+                                    struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_sub_if_data *sdata;
+       struct sta_info *sta;
+       struct ieee80211_hdr *hdr;
+       struct net_device *dev;
+       int ret;
+       bool result = true;
+
+       /* does interface still exist? */
+       dev = dev_get_by_index(&init_net, skb->iif);
+       if (!dev) {
+               dev_kfree_skb(skb);
+               return true;
        }
+
+       /* validate info->control.vif against skb->iif */
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+               sdata = container_of(sdata->bss,
+                                    struct ieee80211_sub_if_data,
+                                    u.ap);
+
+       if (unlikely(info->control.vif && info->control.vif != &sdata->vif)) {
+               dev_kfree_skb(skb);
+               result = true;
+               goto out;
+       }
+
+       if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
+               ieee80211_tx(dev, skb, true);
+       } else {
+               hdr = (struct ieee80211_hdr *)skb->data;
+               sta = sta_info_get(local, hdr->addr1);
+
+               ret = __ieee80211_tx(local, &skb, sta);
+               if (ret != IEEE80211_TX_OK)
+                       result = false;
+       }
+
+ out:
+       dev_put(dev);
+
+       return result;
 }
 
 /*
@@ -1822,40 +1912,53 @@ void ieee80211_tx_pending(unsigned long data)
 {
        struct ieee80211_local *local = (struct ieee80211_local *)data;
        struct net_device *dev = local->mdev;
-       struct ieee80211_tx_stored_packet *store;
-       struct ieee80211_tx_data tx;
-       int i, ret;
+       unsigned long flags;
+       int i;
+       bool next;
 
+       rcu_read_lock();
        netif_tx_lock_bh(dev);
-       for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) {
-               /* Check that this queue is ok */
-               if (__netif_subqueue_stopped(local->mdev, i) &&
-                   !test_bit(i, local->queues_pending_run))
-                       continue;
 
-               if (!test_bit(i, local->queues_pending)) {
-                       clear_bit(i, local->queues_pending_run);
-                       ieee80211_wake_queue(&local->hw, i);
+       for (i = 0; i < local->hw.queues; i++) {
+               /*
+                * If queue is stopped by something other than due to pending
+                * frames, or we have no pending frames, proceed to next queue.
+                */
+               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+               next = false;
+               if (local->queue_stop_reasons[i] !=
+                       BIT(IEEE80211_QUEUE_STOP_REASON_PENDING) ||
+                   skb_queue_empty(&local->pending[i]))
+                       next = true;
+               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+               if (next)
                        continue;
-               }
 
-               clear_bit(i, local->queues_pending_run);
+               /*
+                * start the queue now to allow processing our packets,
+                * we're under the tx lock here anyway so nothing will
+                * happen as a result of this
+                */
                netif_start_subqueue(local->mdev, i);
 
-               store = &local->pending_packet[i];
-               tx.extra_frag = store->extra_frag;
-               tx.num_extra_frag = store->num_extra_frag;
-               tx.flags = 0;
-               ret = __ieee80211_tx(local, store->skb, &tx);
-               if (ret) {
-                       if (ret == IEEE80211_TX_FRAG_AGAIN)
-                               store->skb = NULL;
-               } else {
-                       clear_bit(i, local->queues_pending);
-                       ieee80211_wake_queue(&local->hw, i);
+               while (!skb_queue_empty(&local->pending[i])) {
+                       struct sk_buff *skb = skb_dequeue(&local->pending[i]);
+
+                       if (!ieee80211_tx_pending_skb(local, skb)) {
+                               skb_queue_head(&local->pending[i], skb);
+                               break;
+                       }
                }
+
+               /* Start regular packet processing again. */
+               if (skb_queue_empty(&local->pending[i]))
+                       ieee80211_wake_queue_by_reason(&local->hw, i,
+                                       IEEE80211_QUEUE_STOP_REASON_PENDING);
        }
+
        netif_tx_unlock_bh(dev);
+       rcu_read_unlock();
 }
 
 /* functions for drivers to get certain frames */
index e0431a1d218b1a299c092560c6d07b4f6c181780..fdf432f14554d018b3cca371288d5d96d01aafbf 100644 (file)
@@ -166,18 +166,13 @@ int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
 
 void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
 {
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
-
-       hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-       if (tx->extra_frag) {
-               struct ieee80211_hdr *fhdr;
-               int i;
-               for (i = 0; i < tx->num_extra_frag; i++) {
-                       fhdr = (struct ieee80211_hdr *)
-                               tx->extra_frag[i]->data;
-                       fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-               }
-       }
+       struct sk_buff *skb = tx->skb;
+       struct ieee80211_hdr *hdr;
+
+       do {
+               hdr = (struct ieee80211_hdr *) skb->data;
+               hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+       } while ((skb = skb->next));
 }
 
 int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
@@ -344,42 +339,21 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
-       if (queue >= hw->queues) {
-               if (local->ampdu_ac_queue[queue - hw->queues] < 0)
-                       return;
-
-               /*
-                * for virtual aggregation queues, we need to refcount the
-                * internal mac80211 disable (multiple times!), keep track of
-                * driver disable _and_ make sure the regular queue is
-                * actually enabled.
-                */
-               if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
-                       local->amdpu_ac_stop_refcnt[queue - hw->queues]--;
-               else
-                       __clear_bit(reason, &local->queue_stop_reasons[queue]);
-
-               if (local->queue_stop_reasons[queue] ||
-                   local->amdpu_ac_stop_refcnt[queue - hw->queues])
-                       return;
-
-               /* now go on to treat the corresponding regular queue */
-               queue = local->ampdu_ac_queue[queue - hw->queues];
-               reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
-       }
+       if (WARN_ON(queue >= hw->queues))
+               return;
 
        __clear_bit(reason, &local->queue_stop_reasons[queue]);
 
+       if (!skb_queue_empty(&local->pending[queue]) &&
+           local->queue_stop_reasons[queue] ==
+                               BIT(IEEE80211_QUEUE_STOP_REASON_PENDING))
+               tasklet_schedule(&local->tx_pending_tasklet);
+
        if (local->queue_stop_reasons[queue] != 0)
                /* someone still has this queue stopped */
                return;
 
-       if (test_bit(queue, local->queues_pending)) {
-               set_bit(queue, local->queues_pending_run);
-               tasklet_schedule(&local->tx_pending_tasklet);
-       } else {
-               netif_wake_subqueue(local->mdev, queue);
-       }
+       netif_wake_subqueue(local->mdev, queue);
 }
 
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -405,29 +379,18 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
-       if (queue >= hw->queues) {
-               if (local->ampdu_ac_queue[queue - hw->queues] < 0)
-                       return;
-
-               /*
-                * for virtual aggregation queues, we need to refcount the
-                * internal mac80211 disable (multiple times!), keep track of
-                * driver disable _and_ make sure the regular queue is
-                * actually enabled.
-                */
-               if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
-                       local->amdpu_ac_stop_refcnt[queue - hw->queues]++;
-               else
-                       __set_bit(reason, &local->queue_stop_reasons[queue]);
+       if (WARN_ON(queue >= hw->queues))
+               return;
 
-               /* now go on to treat the corresponding regular queue */
-               queue = local->ampdu_ac_queue[queue - hw->queues];
-               reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
-       }
+       /*
+        * Only stop if it was previously running, this is necessary
+        * for correct pending packets handling because there we may
+        * start (but not wake) the queue and rely on that.
+        */
+       if (!local->queue_stop_reasons[queue])
+               netif_stop_subqueue(local->mdev, queue);
 
        __set_bit(reason, &local->queue_stop_reasons[queue]);
-
-       netif_stop_subqueue(local->mdev, queue);
 }
 
 void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -473,15 +436,9 @@ EXPORT_SYMBOL(ieee80211_stop_queues);
 int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       unsigned long flags;
 
-       if (queue >= hw->queues) {
-               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-               queue = local->ampdu_ac_queue[queue - hw->queues];
-               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
-               if (queue < 0)
-                       return true;
-       }
+       if (WARN_ON(queue >= hw->queues))
+               return true;
 
        return __netif_subqueue_stopped(local->mdev, queue);
 }
@@ -496,7 +453,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
-       for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
+       for (i = 0; i < hw->queues; i++)
                __ieee80211_wake_queue(hw, i, reason);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -846,16 +803,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       const u8 *ie_auth = NULL;
-       int ie_auth_len = 0;
-
-       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               ie_auth_len = sdata->u.mgd.ie_auth_len;
-               ie_auth = sdata->u.mgd.ie_auth;
-       }
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                           sizeof(*mgmt) + 6 + extra_len + ie_auth_len);
+                           sizeof(*mgmt) + 6 + extra_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
                       "frame\n", sdata->dev->name);
@@ -877,8 +827,6 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        mgmt->u.auth.status_code = cpu_to_le16(0);
        if (extra)
                memcpy(skb_put(skb, extra_len), extra, extra_len);
-       if (ie_auth)
-               memcpy(skb_put(skb, ie_auth_len), ie_auth, ie_auth_len);
 
        ieee80211_tx_skb(sdata, skb, encrypt);
 }
@@ -891,20 +839,11 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
        struct ieee80211_supported_band *sband;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       u8 *pos, *supp_rates, *esupp_rates = NULL, *extra_preq_ie = NULL;
-       int i, extra_preq_ie_len = 0;
-
-       switch (sdata->vif.type) {
-       case NL80211_IFTYPE_STATION:
-               extra_preq_ie_len = sdata->u.mgd.ie_probereq_len;
-               extra_preq_ie = sdata->u.mgd.ie_probereq;
-               break;
-       default:
-               break;
-       }
+       u8 *pos, *supp_rates, *esupp_rates = NULL;
+       int i;
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 +
-                           ie_len + extra_preq_ie_len);
+                           ie_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
                       "request\n", sdata->dev->name);
@@ -953,9 +892,6 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
 
        if (ie)
                memcpy(skb_put(skb, ie_len), ie, ie_len);
-       if (extra_preq_ie)
-               memcpy(skb_put(skb, extra_preq_ie_len), extra_preq_ie,
-                      extra_preq_ie_len);
 
        ieee80211_tx_skb(sdata, skb, 0);
 }
index 7043ddc75498f0f952586c58d4f4371d13984007..ef73105b306134ba5fff1cbb8624b6a02100f21d 100644 (file)
@@ -329,24 +329,17 @@ static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
 ieee80211_tx_result
 ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
 {
-       int i;
+       struct sk_buff *skb;
 
        ieee80211_tx_set_protected(tx);
 
-       if (wep_encrypt_skb(tx, tx->skb) < 0) {
-               I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
-               return TX_DROP;
-       }
-
-       if (tx->extra_frag) {
-               for (i = 0; i < tx->num_extra_frag; i++) {
-                       if (wep_encrypt_skb(tx, tx->extra_frag[i])) {
-                               I802_DEBUG_INC(tx->local->
-                                              tx_handlers_drop_wep);
-                               return TX_DROP;
-                       }
+       skb = tx->skb;
+       do {
+               if (wep_encrypt_skb(tx, skb) < 0) {
+                       I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
+                       return TX_DROP;
                }
-       }
+       } while ((skb = skb->next));
 
        return TX_CONTINUE;
 }
index 935c63ed3dfae554e52cc2563a13588aec4fccff..deb4ecec122a5f97be85cdf41629b4bc5f7a10f3 100644 (file)
@@ -129,14 +129,12 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
-               return -EOPNOTSUPP;
-
        if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length);
                if (ret)
                        return ret;
                sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
+               sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
                ieee80211_sta_req_auth(sdata);
                return 0;
        }
@@ -207,14 +205,6 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
-                       if (len > IEEE80211_MAX_SSID_LEN)
-                               return -EINVAL;
-                       memcpy(sdata->u.mgd.ssid, ssid, len);
-                       sdata->u.mgd.ssid_len = len;
-                       return 0;
-               }
-
                if (data->flags)
                        sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
                else
@@ -224,6 +214,7 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
                if (ret)
                        return ret;
 
+               sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
                ieee80211_sta_req_auth(sdata);
                return 0;
        } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
@@ -272,11 +263,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                int ret;
-               if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
-                       memcpy(sdata->u.mgd.bssid, (u8 *) &ap_addr->sa_data,
-                              ETH_ALEN);
-                       return 0;
-               }
+
                if (is_zero_ether_addr((u8 *) &ap_addr->sa_data))
                        sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL |
                                IEEE80211_STA_AUTO_CHANNEL_SEL;
@@ -287,6 +274,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
                ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data);
                if (ret)
                        return ret;
+               sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
                ieee80211_sta_req_auth(sdata);
                return 0;
        } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
@@ -630,7 +618,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
        struct ieee80211_sub_if_data *sdata;
        int idx, i, alg = ALG_WEP;
        u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-       int remove = 0;
+       int remove = 0, ret;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -656,11 +644,20 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
                return 0;
        }
 
-       return ieee80211_set_encryption(
+       ret = ieee80211_set_encryption(
                sdata, bcaddr,
                idx, alg, remove,
                !sdata->default_key,
                keybuf, erq->length);
+
+       if (!ret) {
+               if (remove)
+                       sdata->u.mgd.flags &= ~IEEE80211_STA_TKIP_WEP_USED;
+               else
+                       sdata->u.mgd.flags |= IEEE80211_STA_TKIP_WEP_USED;
+       }
+
+       return ret;
 }
 
 
index 9101b48ec2ae69e3a6c0e00e823876f230818e81..4f8bfea278f25b37ab6cc2a711744f57f368b3c0 100644 (file)
@@ -196,19 +196,13 @@ ieee80211_tx_result
 ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
 {
        struct sk_buff *skb = tx->skb;
-       int i;
 
        ieee80211_tx_set_protected(tx);
 
-       if (tkip_encrypt_skb(tx, skb) < 0)
-               return TX_DROP;
-
-       if (tx->extra_frag) {
-               for (i = 0; i < tx->num_extra_frag; i++) {
-                       if (tkip_encrypt_skb(tx, tx->extra_frag[i]))
-                               return TX_DROP;
-               }
-       }
+       do {
+               if (tkip_encrypt_skb(tx, skb) < 0)
+                       return TX_DROP;
+       } while ((skb = skb->next));
 
        return TX_CONTINUE;
 }
@@ -428,19 +422,13 @@ ieee80211_tx_result
 ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
 {
        struct sk_buff *skb = tx->skb;
-       int i;
 
        ieee80211_tx_set_protected(tx);
 
-       if (ccmp_encrypt_skb(tx, skb) < 0)
-               return TX_DROP;
-
-       if (tx->extra_frag) {
-               for (i = 0; i < tx->num_extra_frag; i++) {
-                       if (ccmp_encrypt_skb(tx, tx->extra_frag[i]))
-                               return TX_DROP;
-               }
-       }
+       do {
+               if (ccmp_encrypt_skb(tx, skb) < 0)
+                       return TX_DROP;
+       } while ((skb = skb->next));
 
        return TX_CONTINUE;
 }
index 2562d05dbaf541eedd74365fb37a8f1c03722ce0..2c967e4f706c9c0de0c6990befa1b3444053149a 100644 (file)
@@ -374,7 +374,7 @@ config NETFILTER_XT_TARGET_HL
 
 config NETFILTER_XT_TARGET_LED
        tristate '"LED" target support'
-       depends on LEDS_CLASS
+       depends on LEDS_CLASS && LED_TRIGGERS
        depends on NETFILTER_ADVANCED
        help
          This option adds a `LED' target, which allows you to blink LEDs in
index dfb447b584da889d50dcd5b383be31f475a1b11b..8020db6274b86471149533b40eecd89c496c24d5 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/netdevice.h>
 #include <linux/socket.h>
 #include <linux/mm.h>
+#include <linux/rculist_nulls.h>
 
 #include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_l3proto.h>
@@ -163,8 +164,8 @@ static void
 clean_from_lists(struct nf_conn *ct)
 {
        pr_debug("clean_from_lists(%p)\n", ct);
-       hlist_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode);
-       hlist_del_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnode);
+       hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
+       hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode);
 
        /* Destroy all pending expectations */
        nf_ct_remove_expectations(ct);
@@ -204,8 +205,8 @@ destroy_conntrack(struct nf_conntrack *nfct)
 
        /* We overload first tuple to link into unconfirmed list. */
        if (!nf_ct_is_confirmed(ct)) {
-               BUG_ON(hlist_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode));
-               hlist_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode);
+               BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode));
+               hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
        }
 
        NF_CT_STAT_INC(net, delete);
@@ -242,18 +243,26 @@ static void death_by_timeout(unsigned long ul_conntrack)
        nf_ct_put(ct);
 }
 
+/*
+ * Warning :
+ * - Caller must take a reference on returned object
+ *   and recheck nf_ct_tuple_equal(tuple, &h->tuple)
+ * OR
+ * - Caller must lock nf_conntrack_lock before calling this function
+ */
 struct nf_conntrack_tuple_hash *
 __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple)
 {
        struct nf_conntrack_tuple_hash *h;
-       struct hlist_node *n;
+       struct hlist_nulls_node *n;
        unsigned int hash = hash_conntrack(tuple);
 
        /* Disable BHs the entire time since we normally need to disable them
         * at least once for the stats anyway.
         */
        local_bh_disable();
-       hlist_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnode) {
+begin:
+       hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnnode) {
                if (nf_ct_tuple_equal(tuple, &h->tuple)) {
                        NF_CT_STAT_INC(net, found);
                        local_bh_enable();
@@ -261,6 +270,13 @@ __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple)
                }
                NF_CT_STAT_INC(net, searched);
        }
+       /*
+        * if the nulls value we got at the end of this lookup is
+        * not the expected one, we must restart lookup.
+        * We probably met an item that was moved to another chain.
+        */
+       if (get_nulls_value(n) != hash)
+               goto begin;
        local_bh_enable();
 
        return NULL;
@@ -275,11 +291,18 @@ nf_conntrack_find_get(struct net *net, const struct nf_conntrack_tuple *tuple)
        struct nf_conn *ct;
 
        rcu_read_lock();
+begin:
        h = __nf_conntrack_find(net, tuple);
        if (h) {
                ct = nf_ct_tuplehash_to_ctrack(h);
                if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use)))
                        h = NULL;
+               else {
+                       if (unlikely(!nf_ct_tuple_equal(tuple, &h->tuple))) {
+                               nf_ct_put(ct);
+                               goto begin;
+                       }
+               }
        }
        rcu_read_unlock();
 
@@ -293,9 +316,9 @@ static void __nf_conntrack_hash_insert(struct nf_conn *ct,
 {
        struct net *net = nf_ct_net(ct);
 
-       hlist_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode,
+       hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
                           &net->ct.hash[hash]);
-       hlist_add_head_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnode,
+       hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode,
                           &net->ct.hash[repl_hash]);
 }
 
@@ -318,7 +341,7 @@ __nf_conntrack_confirm(struct sk_buff *skb)
        struct nf_conntrack_tuple_hash *h;
        struct nf_conn *ct;
        struct nf_conn_help *help;
-       struct hlist_node *n;
+       struct hlist_nulls_node *n;
        enum ip_conntrack_info ctinfo;
        struct net *net;
 
@@ -350,17 +373,17 @@ __nf_conntrack_confirm(struct sk_buff *skb)
        /* See if there's one in the list already, including reverse:
           NAT could have grabbed it without realizing, since we're
           not in the hash.  If there is, we lost race. */
-       hlist_for_each_entry(h, n, &net->ct.hash[hash], hnode)
+       hlist_nulls_for_each_entry(h, n, &net->ct.hash[hash], hnnode)
                if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
                                      &h->tuple))
                        goto out;
-       hlist_for_each_entry(h, n, &net->ct.hash[repl_hash], hnode)
+       hlist_nulls_for_each_entry(h, n, &net->ct.hash[repl_hash], hnnode)
                if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple,
                                      &h->tuple))
                        goto out;
 
        /* Remove from unconfirmed list */
-       hlist_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode);
+       hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
 
        __nf_conntrack_hash_insert(ct, hash, repl_hash);
        /* Timer relative to confirmation time, not original
@@ -399,14 +422,14 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple,
 {
        struct net *net = nf_ct_net(ignored_conntrack);
        struct nf_conntrack_tuple_hash *h;
-       struct hlist_node *n;
+       struct hlist_nulls_node *n;
        unsigned int hash = hash_conntrack(tuple);
 
        /* Disable BHs the entire time since we need to disable them at
         * least once for the stats anyway.
         */
        rcu_read_lock_bh();
-       hlist_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnode) {
+       hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash], hnnode) {
                if (nf_ct_tuplehash_to_ctrack(h) != ignored_conntrack &&
                    nf_ct_tuple_equal(tuple, &h->tuple)) {
                        NF_CT_STAT_INC(net, found);
@@ -430,14 +453,14 @@ static noinline int early_drop(struct net *net, unsigned int hash)
        /* Use oldest entry, which is roughly LRU */
        struct nf_conntrack_tuple_hash *h;
        struct nf_conn *ct = NULL, *tmp;
-       struct hlist_node *n;
+       struct hlist_nulls_node *n;
        unsigned int i, cnt = 0;
        int dropped = 0;
 
        rcu_read_lock();
        for (i = 0; i < nf_conntrack_htable_size; i++) {
-               hlist_for_each_entry_rcu(h, n, &net->ct.hash[hash],
-                                        hnode) {
+               hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[hash],
+                                        hnnode) {
                        tmp = nf_ct_tuplehash_to_ctrack(h);
                        if (!test_bit(IPS_ASSURED_BIT, &tmp->status))
                                ct = tmp;
@@ -508,27 +531,19 @@ struct nf_conn *nf_conntrack_alloc(struct net *net,
 #ifdef CONFIG_NET_NS
        ct->ct_net = net;
 #endif
-       INIT_RCU_HEAD(&ct->rcu);
 
        return ct;
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_alloc);
 
-static void nf_conntrack_free_rcu(struct rcu_head *head)
-{
-       struct nf_conn *ct = container_of(head, struct nf_conn, rcu);
-
-       nf_ct_ext_free(ct);
-       kmem_cache_free(nf_conntrack_cachep, ct);
-}
-
 void nf_conntrack_free(struct nf_conn *ct)
 {
        struct net *net = nf_ct_net(ct);
 
        nf_ct_ext_destroy(ct);
        atomic_dec(&net->ct.count);
-       call_rcu(&ct->rcu, nf_conntrack_free_rcu);
+       nf_ct_ext_free(ct);
+       kmem_cache_free(nf_conntrack_cachep, ct);
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_free);
 
@@ -594,7 +609,7 @@ init_conntrack(struct net *net,
        }
 
        /* Overload tuple linked list to put us in unconfirmed list. */
-       hlist_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode,
+       hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
                       &net->ct.unconfirmed);
 
        spin_unlock_bh(&nf_conntrack_lock);
@@ -906,6 +921,12 @@ int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[],
        return 0;
 }
 EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_to_tuple);
+
+int nf_ct_port_nlattr_tuple_size(void)
+{
+       return nla_policy_len(nf_ct_port_nla_policy, CTA_PROTO_MAX + 1);
+}
+EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_tuple_size);
 #endif
 
 /* Used by ipt_REJECT and ip6t_REJECT. */
@@ -934,17 +955,17 @@ get_next_corpse(struct net *net, int (*iter)(struct nf_conn *i, void *data),
 {
        struct nf_conntrack_tuple_hash *h;
        struct nf_conn *ct;
-       struct hlist_node *n;
+       struct hlist_nulls_node *n;
 
        spin_lock_bh(&nf_conntrack_lock);
        for (; *bucket < nf_conntrack_htable_size; (*bucket)++) {
-               hlist_for_each_entry(h, n, &net->ct.hash[*bucket], hnode) {
+               hlist_nulls_for_each_entry(h, n, &net->ct.hash[*bucket], hnnode) {
                        ct = nf_ct_tuplehash_to_ctrack(h);
                        if (iter(ct, data))
                                goto found;
                }
        }
-       hlist_for_each_entry(h, n, &net->ct.unconfirmed, hnode) {
+       hlist_nulls_for_each_entry(h, n, &net->ct.unconfirmed, hnnode) {
                ct = nf_ct_tuplehash_to_ctrack(h);
                if (iter(ct, data))
                        set_bit(IPS_DYING_BIT, &ct->status);
@@ -992,7 +1013,7 @@ static int kill_all(struct nf_conn *i, void *data)
        return 1;
 }
 
-void nf_ct_free_hashtable(struct hlist_head *hash, int vmalloced, unsigned int size)
+void nf_ct_free_hashtable(void *hash, int vmalloced, unsigned int size)
 {
        if (vmalloced)
                vfree(hash);
@@ -1060,26 +1081,28 @@ void nf_conntrack_cleanup(struct net *net)
        }
 }
 
-struct hlist_head *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced)
+void *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced, int nulls)
 {
-       struct hlist_head *hash;
-       unsigned int size, i;
+       struct hlist_nulls_head *hash;
+       unsigned int nr_slots, i;
+       size_t sz;
 
        *vmalloced = 0;
 
-       size = *sizep = roundup(*sizep, PAGE_SIZE / sizeof(struct hlist_head));
-       hash = (void*)__get_free_pages(GFP_KERNEL|__GFP_NOWARN,
-                                      get_order(sizeof(struct hlist_head)
-                                                * size));
+       BUILD_BUG_ON(sizeof(struct hlist_nulls_head) != sizeof(struct hlist_head));
+       nr_slots = *sizep = roundup(*sizep, PAGE_SIZE / sizeof(struct hlist_nulls_head));
+       sz = nr_slots * sizeof(struct hlist_nulls_head);
+       hash = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO,
+                                       get_order(sz));
        if (!hash) {
                *vmalloced = 1;
                printk(KERN_WARNING "nf_conntrack: falling back to vmalloc.\n");
-               hash = vmalloc(sizeof(struct hlist_head) * size);
+               hash = __vmalloc(sz, GFP_KERNEL | __GFP_ZERO, PAGE_KERNEL);
        }
 
-       if (hash)
-               for (i = 0; i < size; i++)
-                       INIT_HLIST_HEAD(&hash[i]);
+       if (hash && nulls)
+               for (i = 0; i < nr_slots; i++)
+                       INIT_HLIST_NULLS_HEAD(&hash[i], i);
 
        return hash;
 }
@@ -1090,7 +1113,7 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
        int i, bucket, vmalloced, old_vmalloced;
        unsigned int hashsize, old_size;
        int rnd;
-       struct hlist_head *hash, *old_hash;
+       struct hlist_nulls_head *hash, *old_hash;
        struct nf_conntrack_tuple_hash *h;
 
        /* On boot, we can set this without any fancy locking. */
@@ -1101,7 +1124,7 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
        if (!hashsize)
                return -EINVAL;
 
-       hash = nf_ct_alloc_hashtable(&hashsize, &vmalloced);
+       hash = nf_ct_alloc_hashtable(&hashsize, &vmalloced, 1);
        if (!hash)
                return -ENOMEM;
 
@@ -1116,12 +1139,12 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
         */
        spin_lock_bh(&nf_conntrack_lock);
        for (i = 0; i < nf_conntrack_htable_size; i++) {
-               while (!hlist_empty(&init_net.ct.hash[i])) {
-                       h = hlist_entry(init_net.ct.hash[i].first,
-                                       struct nf_conntrack_tuple_hash, hnode);
-                       hlist_del_rcu(&h->hnode);
+               while (!hlist_nulls_empty(&init_net.ct.hash[i])) {
+                       h = hlist_nulls_entry(init_net.ct.hash[i].first,
+                                       struct nf_conntrack_tuple_hash, hnnode);
+                       hlist_nulls_del_rcu(&h->hnnode);
                        bucket = __hash_conntrack(&h->tuple, hashsize, rnd);
-                       hlist_add_head(&h->hnode, &hash[bucket]);
+                       hlist_nulls_add_head_rcu(&h->hnnode, &hash[bucket]);
                }
        }
        old_size = nf_conntrack_htable_size;
@@ -1172,7 +1195,7 @@ static int nf_conntrack_init_init_net(void)
 
        nf_conntrack_cachep = kmem_cache_create("nf_conntrack",
                                                sizeof(struct nf_conn),
-                                               0, 0, NULL);
+                                               0, SLAB_DESTROY_BY_RCU, NULL);
        if (!nf_conntrack_cachep) {
                printk(KERN_ERR "Unable to create nf_conn slab cache\n");
                ret = -ENOMEM;
@@ -1202,7 +1225,7 @@ static int nf_conntrack_init_net(struct net *net)
        int ret;
 
        atomic_set(&net->ct.count, 0);
-       INIT_HLIST_HEAD(&net->ct.unconfirmed);
+       INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, 0);
        net->ct.stat = alloc_percpu(struct ip_conntrack_stat);
        if (!net->ct.stat) {
                ret = -ENOMEM;
@@ -1212,7 +1235,7 @@ static int nf_conntrack_init_net(struct net *net)
        if (ret < 0)
                goto err_ecache;
        net->ct.hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size,
-                                                 &net->ct.hash_vmalloc);
+                                            &net->ct.hash_vmalloc, 1);
        if (!net->ct.hash) {
                ret = -ENOMEM;
                printk(KERN_ERR "Unable to create nf_conntrack_hash\n");
index 357ba39d4c8d72544d22e7fc0a051abfc913b681..3940f996a2e4ac180b8ed135e9f7c423f670b66d 100644 (file)
@@ -604,7 +604,7 @@ int nf_conntrack_expect_init(struct net *net)
 
        net->ct.expect_count = 0;
        net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize,
-                                                 &net->ct.expect_vmalloc);
+                                                 &net->ct.expect_vmalloc, 0);
        if (net->ct.expect_hash == NULL)
                goto err1;
 
index a51bdac9f3a0682a442d833d44a82c813cd19eff..30b8e9009f99a2ea3d7c366c9f7fc6a551d03776 100644 (file)
@@ -142,6 +142,7 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
 
        BUG_ON(me->expect_policy == NULL);
        BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
+       BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1);
 
        mutex_lock(&nf_ct_helper_mutex);
        hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
@@ -158,6 +159,7 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me,
        struct nf_conntrack_tuple_hash *h;
        struct nf_conntrack_expect *exp;
        const struct hlist_node *n, *next;
+       const struct hlist_nulls_node *nn;
        unsigned int i;
 
        /* Get rid of expectations */
@@ -174,10 +176,10 @@ static void __nf_conntrack_helper_unregister(struct nf_conntrack_helper *me,
        }
 
        /* Get rid of expecteds, set helpers to NULL. */
-       hlist_for_each_entry(h, n, &net->ct.unconfirmed, hnode)
+       hlist_for_each_entry(h, nn, &net->ct.unconfirmed, hnnode)
                unhelp(h, me);
        for (i = 0; i < nf_conntrack_htable_size; i++) {
-               hlist_for_each_entry(h, n, &net->ct.hash[i], hnode)
+               hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
                        unhelp(h, me);
        }
 }
@@ -217,7 +219,7 @@ int nf_conntrack_helper_init(void)
 
        nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
        nf_ct_helper_hash = nf_ct_alloc_hashtable(&nf_ct_helper_hsize,
-                                                 &nf_ct_helper_vmalloc);
+                                                 &nf_ct_helper_vmalloc, 0);
        if (!nf_ct_helper_hash)
                return -ENOMEM;
 
index 7a16bd462f82386bb1f7be34935ccf754bcbd72f..c6439c77953c49cf705ac07fe758764d1cc82a9d 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/rculist.h>
+#include <linux/rculist_nulls.h>
 #include <linux/types.h>
 #include <linux/timer.h>
 #include <linux/skbuff.h>
@@ -404,6 +405,78 @@ nla_put_failure:
 }
 
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
+/*
+ * The general structure of a ctnetlink event is
+ *
+ *  CTA_TUPLE_ORIG
+ *    <l3/l4-proto-attributes>
+ *  CTA_TUPLE_REPLY
+ *    <l3/l4-proto-attributes>
+ *  CTA_ID
+ *  ...
+ *  CTA_PROTOINFO
+ *    <l4-proto-attributes>
+ *  CTA_TUPLE_MASTER
+ *    <l3/l4-proto-attributes>
+ *
+ * Therefore the formular is
+ *
+ *   size = sizeof(headers) + sizeof(generic_nlas) + 3 * sizeof(tuple_nlas)
+ *             + sizeof(protoinfo_nlas)
+ */
+static struct sk_buff *
+ctnetlink_alloc_skb(const struct nf_conntrack_tuple *tuple, gfp_t gfp)
+{
+       struct nf_conntrack_l3proto *l3proto;
+       struct nf_conntrack_l4proto *l4proto;
+       int len;
+
+#define NLA_TYPE_SIZE(type)            nla_total_size(sizeof(type))
+
+       /* proto independant part */
+       len = NLMSG_SPACE(sizeof(struct nfgenmsg))
+               + 3 * nla_total_size(0)         /* CTA_TUPLE_ORIG|REPL|MASTER */
+               + 3 * nla_total_size(0)         /* CTA_TUPLE_IP */
+               + 3 * nla_total_size(0)         /* CTA_TUPLE_PROTO */
+               + 3 * NLA_TYPE_SIZE(u_int8_t)   /* CTA_PROTO_NUM */
+               + NLA_TYPE_SIZE(u_int32_t)      /* CTA_ID */
+               + NLA_TYPE_SIZE(u_int32_t)      /* CTA_STATUS */
+#ifdef CONFIG_NF_CT_ACCT
+               + 2 * nla_total_size(0)         /* CTA_COUNTERS_ORIG|REPL */
+               + 2 * NLA_TYPE_SIZE(uint64_t)   /* CTA_COUNTERS_PACKETS */
+               + 2 * NLA_TYPE_SIZE(uint64_t)   /* CTA_COUNTERS_BYTES */
+#endif
+               + NLA_TYPE_SIZE(u_int32_t)      /* CTA_TIMEOUT */
+               + nla_total_size(0)             /* CTA_PROTOINFO */
+               + nla_total_size(0)             /* CTA_HELP */
+               + nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+               + NLA_TYPE_SIZE(u_int32_t)      /* CTA_SECMARK */
+#endif
+#ifdef CONFIG_NF_NAT_NEEDED
+               + 2 * nla_total_size(0)         /* CTA_NAT_SEQ_ADJ_ORIG|REPL */
+               + 2 * NLA_TYPE_SIZE(u_int32_t)  /* CTA_NAT_SEQ_CORRECTION_POS */
+               + 2 * NLA_TYPE_SIZE(u_int32_t)  /* CTA_NAT_SEQ_CORRECTION_BEFORE */
+               + 2 * NLA_TYPE_SIZE(u_int32_t)  /* CTA_NAT_SEQ_CORRECTION_AFTER */
+#endif
+#ifdef CONFIG_NF_CONNTRACK_MARK
+               + NLA_TYPE_SIZE(u_int32_t)      /* CTA_MARK */
+#endif
+               ;
+
+#undef NLA_TYPE_SIZE
+
+       rcu_read_lock();
+       l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
+       len += l3proto->nla_size;
+
+       l4proto = __nf_ct_l4proto_find(tuple->src.l3num, tuple->dst.protonum);
+       len += l4proto->nla_size;
+       rcu_read_unlock();
+
+       return alloc_skb(len, gfp);
+}
+
 static int ctnetlink_conntrack_event(struct notifier_block *this,
                                     unsigned long events, void *ptr)
 {
@@ -437,7 +510,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
        if (!item->report && !nfnetlink_has_listeners(group))
                return NOTIFY_DONE;
 
-       skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
+       skb = ctnetlink_alloc_skb(tuple(ct, IP_CT_DIR_ORIGINAL), GFP_ATOMIC);
        if (!skb)
                return NOTIFY_DONE;
 
@@ -536,7 +609,7 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct nf_conn *ct, *last;
        struct nf_conntrack_tuple_hash *h;
-       struct hlist_node *n;
+       struct hlist_nulls_node *n;
        struct nfgenmsg *nfmsg = NLMSG_DATA(cb->nlh);
        u_int8_t l3proto = nfmsg->nfgen_family;
 
@@ -544,27 +617,27 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
        last = (struct nf_conn *)cb->args[1];
        for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) {
 restart:
-               hlist_for_each_entry_rcu(h, n, &init_net.ct.hash[cb->args[0]],
-                                        hnode) {
+               hlist_nulls_for_each_entry_rcu(h, n, &init_net.ct.hash[cb->args[0]],
+                                        hnnode) {
                        if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
                                continue;
                        ct = nf_ct_tuplehash_to_ctrack(h);
+                       if (!atomic_inc_not_zero(&ct->ct_general.use))
+                               continue;
                        /* Dump entries of a given L3 protocol number.
                         * If it is not specified, ie. l3proto == 0,
                         * then dump everything. */
                        if (l3proto && nf_ct_l3num(ct) != l3proto)
-                               continue;
+                               goto releasect;
                        if (cb->args[1]) {
                                if (ct != last)
-                                       continue;
+                                       goto releasect;
                                cb->args[1] = 0;
                        }
                        if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
                                                cb->nlh->nlmsg_seq,
                                                IPCTNL_MSG_CT_NEW,
                                                1, ct) < 0) {
-                               if (!atomic_inc_not_zero(&ct->ct_general.use))
-                                       continue;
                                cb->args[1] = (unsigned long)ct;
                                goto out;
                        }
@@ -577,6 +650,8 @@ restart:
                                if (acct)
                                        memset(acct, 0, sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]));
                        }
+releasect:
+               nf_ct_put(ct);
                }
                if (cb->args[1]) {
                        cb->args[1] = 0;
@@ -1242,13 +1317,12 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
                if (err < 0)
                        goto err2;
 
-               master_h = __nf_conntrack_find(&init_net, &master);
+               master_h = nf_conntrack_find_get(&init_net, &master);
                if (master_h == NULL) {
                        err = -ENOENT;
                        goto err2;
                }
                master_ct = nf_ct_tuplehash_to_ctrack(master_h);
-               nf_conntrack_get(&master_ct->ct_general);
                __set_bit(IPS_EXPECTED_BIT, &ct->status);
                ct->master = master_ct;
        }
index 9a62b4efa0e1d64516ac0c5312484330b2562da5..1a4568bf7ea515ee790b700dfe85a31c38b8bdc6 100644 (file)
@@ -167,6 +167,9 @@ int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
        if (proto->l3proto >= AF_MAX)
                return -EBUSY;
 
+       if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size)
+               return -EINVAL;
+
        mutex_lock(&nf_ct_proto_mutex);
        if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) {
                ret = -EBUSY;
@@ -177,6 +180,9 @@ int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
        if (ret < 0)
                goto out_unlock;
 
+       if (proto->nlattr_tuple_size)
+               proto->nla_size = 3 * proto->nlattr_tuple_size();
+
        rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
 
 out_unlock:
@@ -263,6 +269,10 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto)
        if (l4proto->l3proto >= PF_MAX)
                return -EBUSY;
 
+       if ((l4proto->to_nlattr && !l4proto->nlattr_size)
+               || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size))
+               return -EINVAL;
+
        mutex_lock(&nf_ct_proto_mutex);
        if (!nf_ct_protos[l4proto->l3proto]) {
                /* l3proto may be loaded latter. */
@@ -290,6 +300,12 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto)
        if (ret < 0)
                goto out_unlock;
 
+       l4proto->nla_size = 0;
+       if (l4proto->nlattr_size)
+               l4proto->nla_size += l4proto->nlattr_size();
+       if (l4proto->nlattr_tuple_size)
+               l4proto->nla_size += 3 * l4proto->nlattr_tuple_size();
+
        rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
                           l4proto);
 
index d3d5a7fd73ce7c3fc79d4009cf4f0c1671ca2caf..50dac8dbe7d86278ae3f7a0bb3500dc132b169e1 100644 (file)
@@ -669,6 +669,12 @@ static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct)
        write_unlock_bh(&dccp_lock);
        return 0;
 }
+
+static int dccp_nlattr_size(void)
+{
+       return nla_total_size(0)        /* CTA_PROTOINFO_DCCP */
+               + nla_policy_len(dccp_nla_policy, CTA_PROTOINFO_DCCP_MAX + 1);
+}
 #endif
 
 #ifdef CONFIG_SYSCTL
@@ -749,8 +755,10 @@ static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = {
        .print_conntrack        = dccp_print_conntrack,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .to_nlattr              = dccp_to_nlattr,
+       .nlattr_size            = dccp_nlattr_size,
        .from_nlattr            = nlattr_to_dccp,
        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
+       .nlattr_tuple_size      = nf_ct_port_nlattr_tuple_size,
        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
        .nla_policy             = nf_ct_port_nla_policy,
 #endif
@@ -771,6 +779,7 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = {
        .to_nlattr              = dccp_to_nlattr,
        .from_nlattr            = nlattr_to_dccp,
        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
+       .nlattr_tuple_size      = nf_ct_port_nlattr_tuple_size,
        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
        .nla_policy             = nf_ct_port_nla_policy,
 #endif
index 1b279f9d6bf3a81d90205b5dd06cb158fdbf4829..117b80112fcbd08f6c8effc4590d64f637a9bdbe 100644 (file)
@@ -293,6 +293,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = {
        .me              = THIS_MODULE,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
+       .nlattr_tuple_size = nf_ct_port_nlattr_tuple_size,
        .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
        .nla_policy      = nf_ct_port_nla_policy,
 #endif
index 74e03790119989df87cd1a95e8a80280f1c85764..101b4ad9e817bc421393503d3b59e1a8d98f7918 100644 (file)
@@ -537,6 +537,12 @@ static int nlattr_to_sctp(struct nlattr *cda[], struct nf_conn *ct)
 
        return 0;
 }
+
+static int sctp_nlattr_size(void)
+{
+       return nla_total_size(0)        /* CTA_PROTOINFO_SCTP */
+               + nla_policy_len(sctp_nla_policy, CTA_PROTOINFO_SCTP_MAX + 1);
+}
 #endif
 
 #ifdef CONFIG_SYSCTL
@@ -668,8 +674,10 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = {
        .me                     = THIS_MODULE,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .to_nlattr              = sctp_to_nlattr,
+       .nlattr_size            = sctp_nlattr_size,
        .from_nlattr            = nlattr_to_sctp,
        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
+       .nlattr_tuple_size      = nf_ct_port_nlattr_tuple_size,
        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
        .nla_policy             = nf_ct_port_nla_policy,
 #endif
@@ -696,8 +704,10 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = {
        .me                     = THIS_MODULE,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .to_nlattr              = sctp_to_nlattr,
+       .nlattr_size            = sctp_nlattr_size,
        .from_nlattr            = nlattr_to_sctp,
        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
+       .nlattr_tuple_size      = nf_ct_port_nlattr_tuple_size,
        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
        .nla_policy             = nf_ct_port_nla_policy,
 #endif
index 0aeb8b09a1f7eaa6c7aa4bc659a97bdf4f2f6110..b5ccf2b4b2e729d6ba31c959cc34c43efbe784df 100644 (file)
@@ -1184,6 +1184,17 @@ static int nlattr_to_tcp(struct nlattr *cda[], struct nf_conn *ct)
 
        return 0;
 }
+
+static int tcp_nlattr_size(void)
+{
+       return nla_total_size(0)           /* CTA_PROTOINFO_TCP */
+               + nla_policy_len(tcp_nla_policy, CTA_PROTOINFO_TCP_MAX + 1);
+}
+
+static int tcp_nlattr_tuple_size(void)
+{
+       return nla_policy_len(nf_ct_port_nla_policy, CTA_PROTO_MAX + 1);
+}
 #endif
 
 #ifdef CONFIG_SYSCTL
@@ -1399,9 +1410,11 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly =
        .error                  = tcp_error,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .to_nlattr              = tcp_to_nlattr,
+       .nlattr_size            = tcp_nlattr_size,
        .from_nlattr            = nlattr_to_tcp,
        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
+       .nlattr_tuple_size      = tcp_nlattr_tuple_size,
        .nla_policy             = nf_ct_port_nla_policy,
 #endif
 #ifdef CONFIG_SYSCTL
@@ -1429,9 +1442,11 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly =
        .error                  = tcp_error,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .to_nlattr              = tcp_to_nlattr,
+       .nlattr_size            = tcp_nlattr_size,
        .from_nlattr            = nlattr_to_tcp,
        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
+       .nlattr_tuple_size      = tcp_nlattr_tuple_size,
        .nla_policy             = nf_ct_port_nla_policy,
 #endif
 #ifdef CONFIG_SYSCTL
index d4021179e24ea9d3a1d9e5e1d6552bb013788528..70809d117b91e9f4a0efbfdf7f16a9739bb91e23 100644 (file)
@@ -195,6 +195,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly =
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
+       .nlattr_tuple_size      = nf_ct_port_nlattr_tuple_size,
        .nla_policy             = nf_ct_port_nla_policy,
 #endif
 #ifdef CONFIG_SYSCTL
@@ -222,6 +223,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly =
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
+       .nlattr_tuple_size      = nf_ct_port_nlattr_tuple_size,
        .nla_policy             = nf_ct_port_nla_policy,
 #endif
 #ifdef CONFIG_SYSCTL
index 4579d8de13b15d75f69181c7e4bd36ed3d7e39db..4614696c1b88f400f43a1bdb33f10c7708519e34 100644 (file)
@@ -180,6 +180,7 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly =
        .error                  = udplite_error,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
+       .nlattr_tuple_size      = nf_ct_port_nlattr_tuple_size,
        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
        .nla_policy             = nf_ct_port_nla_policy,
 #endif
index 4da54b0b92339b61b242b51fdcab4bc1db32233c..193515381970f0a996a960efffe5d75dd2a65af0 100644 (file)
@@ -44,40 +44,42 @@ struct ct_iter_state {
        unsigned int bucket;
 };
 
-static struct hlist_node *ct_get_first(struct seq_file *seq)
+static struct hlist_nulls_node *ct_get_first(struct seq_file *seq)
 {
        struct net *net = seq_file_net(seq);
        struct ct_iter_state *st = seq->private;
-       struct hlist_node *n;
+       struct hlist_nulls_node *n;
 
        for (st->bucket = 0;
             st->bucket < nf_conntrack_htable_size;
             st->bucket++) {
                n = rcu_dereference(net->ct.hash[st->bucket].first);
-               if (n)
+               if (!is_a_nulls(n))
                        return n;
        }
        return NULL;
 }
 
-static struct hlist_node *ct_get_next(struct seq_file *seq,
-                                     struct hlist_node *head)
+static struct hlist_nulls_node *ct_get_next(struct seq_file *seq,
+                                     struct hlist_nulls_node *head)
 {
        struct net *net = seq_file_net(seq);
        struct ct_iter_state *st = seq->private;
 
        head = rcu_dereference(head->next);
-       while (head == NULL) {
-               if (++st->bucket >= nf_conntrack_htable_size)
-                       return NULL;
+       while (is_a_nulls(head)) {
+               if (likely(get_nulls_value(head) == st->bucket)) {
+                       if (++st->bucket >= nf_conntrack_htable_size)
+                               return NULL;
+               }
                head = rcu_dereference(net->ct.hash[st->bucket].first);
        }
        return head;
 }
 
-static struct hlist_node *ct_get_idx(struct seq_file *seq, loff_t pos)
+static struct hlist_nulls_node *ct_get_idx(struct seq_file *seq, loff_t pos)
 {
-       struct hlist_node *head = ct_get_first(seq);
+       struct hlist_nulls_node *head = ct_get_first(seq);
 
        if (head)
                while (pos && (head = ct_get_next(seq, head)))
@@ -107,67 +109,74 @@ static void ct_seq_stop(struct seq_file *s, void *v)
 /* return 0 on success, 1 in case of error */
 static int ct_seq_show(struct seq_file *s, void *v)
 {
-       const struct nf_conntrack_tuple_hash *hash = v;
-       const struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash);
+       struct nf_conntrack_tuple_hash *hash = v;
+       struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash);
        const struct nf_conntrack_l3proto *l3proto;
        const struct nf_conntrack_l4proto *l4proto;
+       int ret = 0;
 
        NF_CT_ASSERT(ct);
+       if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use)))
+               return 0;
 
        /* we only want to print DIR_ORIGINAL */
        if (NF_CT_DIRECTION(hash))
-               return 0;
+               goto release;
 
        l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct));
        NF_CT_ASSERT(l3proto);
        l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
        NF_CT_ASSERT(l4proto);
 
+       ret = -ENOSPC;
        if (seq_printf(s, "%-8s %u %-8s %u %ld ",
                       l3proto->name, nf_ct_l3num(ct),
                       l4proto->name, nf_ct_protonum(ct),
                       timer_pending(&ct->timeout)
                       ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0)
-               return -ENOSPC;
+               goto release;
 
        if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct))
-               return -ENOSPC;
+               goto release;
 
        if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
                        l3proto, l4proto))
-               return -ENOSPC;
+               goto release;
 
        if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL))
-               return -ENOSPC;
+               goto release;
 
        if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status)))
                if (seq_printf(s, "[UNREPLIED] "))
-                       return -ENOSPC;
+                       goto release;
 
        if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple,
                        l3proto, l4proto))
-               return -ENOSPC;
+               goto release;
 
        if (seq_print_acct(s, ct, IP_CT_DIR_REPLY))
-               return -ENOSPC;
+               goto release;
 
        if (test_bit(IPS_ASSURED_BIT, &ct->status))
                if (seq_printf(s, "[ASSURED] "))
-                       return -ENOSPC;
+                       goto release;
 
 #if defined(CONFIG_NF_CONNTRACK_MARK)
        if (seq_printf(s, "mark=%u ", ct->mark))
-               return -ENOSPC;
+               goto release;
 #endif
 
 #ifdef CONFIG_NF_CONNTRACK_SECMARK
        if (seq_printf(s, "secmark=%u ", ct->secmark))
-               return -ENOSPC;
+               goto release;
 #endif
 
        if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)))
-               return -ENOSPC;
+               goto release;
 
+       ret = 0;
+release:
+       nf_ct_put(ct);
        return 0;
 }
 
index 7f404cc64c830e9bc9d7de65c5e02b04006072ba..6809809543951a635ad3f53ad625dd6287e20220 100644 (file)
@@ -108,7 +108,7 @@ static int count_them(struct xt_connlimit_data *data,
        const struct nf_conntrack_tuple_hash *found;
        struct xt_connlimit_conn *conn;
        struct xt_connlimit_conn *tmp;
-       const struct nf_conn *found_ct;
+       struct nf_conn *found_ct;
        struct list_head *hash;
        bool addit = true;
        int matches = 0;
@@ -123,7 +123,7 @@ static int count_them(struct xt_connlimit_data *data,
 
        /* check the saved connections */
        list_for_each_entry_safe(conn, tmp, hash, list) {
-               found    = __nf_conntrack_find(&init_net, &conn->tuple);
+               found    = nf_conntrack_find_get(&init_net, &conn->tuple);
                found_ct = NULL;
 
                if (found != NULL)
@@ -151,6 +151,7 @@ static int count_them(struct xt_connlimit_data *data,
                         * we do not care about connections which are
                         * closed already -> ditch it
                         */
+                       nf_ct_put(found_ct);
                        list_del(&conn->list);
                        kfree(conn);
                        continue;
@@ -160,6 +161,7 @@ static int count_them(struct xt_connlimit_data *data,
                    match->family))
                        /* same source network -> be counted! */
                        ++matches;
+               nf_ct_put(found_ct);
        }
 
        rcu_read_unlock();
index 44a234ef44396ee5ab7d0d1bb043845297caf8d1..8d28ca5848bc6a57d6363023683d8c3d6daaddc3 100644 (file)
@@ -20,23 +20,6 @@ MODULE_DESCRIPTION("Xtables: Bridge physical device match");
 MODULE_ALIAS("ipt_physdev");
 MODULE_ALIAS("ip6t_physdev");
 
-static unsigned long ifname_compare(const char *_a, const char *_b, const char *_mask)
-{
-       const unsigned long *a = (const unsigned long *)_a;
-       const unsigned long *b = (const unsigned long *)_b;
-       const unsigned long *mask = (const unsigned long *)_mask;
-       unsigned long ret;
-
-       ret = (a[0] ^ b[0]) & mask[0];
-       if (IFNAMSIZ > sizeof(unsigned long))
-               ret |= (a[1] ^ b[1]) & mask[1];
-       if (IFNAMSIZ > 2 * sizeof(unsigned long))
-               ret |= (a[2] ^ b[2]) & mask[2];
-       if (IFNAMSIZ > 3 * sizeof(unsigned long))
-               ret |= (a[3] ^ b[3]) & mask[3];
-       BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
-       return ret;
-}
 
 static bool
 physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par)
@@ -85,7 +68,7 @@ physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par)
        if (!(info->bitmask & XT_PHYSDEV_OP_IN))
                goto match_outdev;
        indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
-       ret = ifname_compare(indev, info->physindev, info->in_mask);
+       ret = ifname_compare_aligned(indev, info->physindev, info->in_mask);
 
        if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN))
                return false;
@@ -95,7 +78,7 @@ match_outdev:
                return true;
        outdev = nf_bridge->physoutdev ?
                 nf_bridge->physoutdev->name : nulldevname;
-       ret = ifname_compare(outdev, info->physoutdev, info->out_mask);
+       ret = ifname_compare_aligned(outdev, info->physoutdev, info->out_mask);
 
        return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT));
 }
index 6d9c58ec56ac70193adb3cddcb2ef0968ee1906c..4e705f87969f332c30939dc8acabd4f8d4d74352 100644 (file)
@@ -1037,10 +1037,6 @@ static int nr_sendmsg(struct kiocb *iocb, struct socket *sock,
        unsigned char *asmptr;
        int size;
 
-       /* Netrom empty data frame has no meaning : don't send */
-       if (len == 0)
-               return 0;
-
        if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_CMSG_COMPAT))
                return -EINVAL;
 
@@ -1086,7 +1082,11 @@ static int nr_sendmsg(struct kiocb *iocb, struct socket *sock,
 
        SOCK_DEBUG(sk, "NET/ROM: sendto: Addresses built.\n");
 
-       /* Build a packet */
+       /* Build a packet - the conventional user limit is 236 bytes. We can
+          do ludicrously large NetROM frames but must not overflow */
+       if (len > 65536)
+               return -EMSGSIZE;
+
        SOCK_DEBUG(sk, "NET/ROM: sendto: building packet.\n");
        size = len + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
 
@@ -1171,11 +1171,6 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock,
        skb_reset_transport_header(skb);
        copied     = skb->len;
 
-       /* NetRom empty data frame has no meaning : ignore it */
-       if (copied == 0) {
-               goto out;
-       }
-
        if (copied > size) {
                copied = size;
                msg->msg_flags |= MSG_TRUNC;
@@ -1191,7 +1186,7 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock,
 
        msg->msg_namelen = sizeof(*sax);
 
-out:   skb_free_datagram(sk, skb);
+       skb_free_datagram(sk, skb);
 
        release_sock(sk);
        return copied;
index 6501396265819eec021f89eade0f4f7e1551bdfe..0f36e8d59b29c563bfae1f6421c9d5d2a5ddeb44 100644 (file)
@@ -1124,6 +1124,10 @@ static int rose_sendmsg(struct kiocb *iocb, struct socket *sock,
 
        /* Build a packet */
        SOCK_DEBUG(sk, "ROSE: sendto: building packet.\n");
+       /* Sanity check the packet size */
+       if (len > 65535)
+               return -EMSGSIZE;
+
        size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
 
        if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
index 092ae6faccca896a272b24d7d87ee59f07d332cf..3c3bc9e579edad5f7719577fc96ecea9f136250f 100644 (file)
@@ -10,51 +10,19 @@ config CFG80211_REG_DEBUG
 
          If unsure, say N.
 
-config NL80211
-       bool "nl80211 new netlink interface support"
-       depends on CFG80211
-       default y
-       ---help---
-         This option turns on the new netlink interface
-         (nl80211) support in cfg80211.
-
-         If =n, drivers using mac80211 will be configured via
-         wireless extension support provided by that subsystem.
-
-         If unsure, say Y.
-
 config WIRELESS_OLD_REGULATORY
        bool "Old wireless static regulatory definitions"
-       default y
+       default n
        ---help---
          This option enables the old static regulatory information
-         and uses it within the new framework. This is available
-         temporarily as an option to help prevent immediate issues
-         due to the switch to the new regulatory framework which
-         does require a new userspace application which has the
-         database of regulatory information (CRDA) and another for
-         setting regulatory domains (iw).
-
-         For more information see:
-
-         http://wireless.kernel.org/en/developers/Regulatory/CRDA
-         http://wireless.kernel.org/en/users/Documentation/iw
-
-         It is important to note though that if you *do* have CRDA present
-         and if this option is enabled CRDA *will* be called to update the
-         regulatory domain (for US and JP only). Support for letting the user
-         set the regulatory domain through iw is also supported. This option
-         mainly exists to leave around for a kernel release some old static
-         regulatory domains that were defined and to keep around the old
-         ieee80211_regdom module parameter. This is being phased out and you
-         should stop using them ASAP.
-
-         Note: You will need CRDA if you want 802.11d support
-
-         Say Y unless you have installed a new userspace application.
-         Also say Y if have one currently depending on the ieee80211_regdom
-         module parameter and cannot port it to use the new userspace
-         interfaces.
+         and uses it within the new framework. This option is available
+         for historical reasons and it is advised to leave it off.
+
+         For details see:
+
+         http://wireless.kernel.org/en/developers/Regulatory
+
+         Say N and if you say Y, please tell us why. The default is N.
 
 config WIRELESS_EXT
        bool "Wireless extensions"
index dad43c24f695283b1faed4e579a5f0cb2abfd07f..6d1e7b27b752a8d99a404502d94d2322f49940df 100644 (file)
@@ -5,8 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
 obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
 obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
 
-cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o
+cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o
 cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
-cfg80211-$(CONFIG_NL80211) += nl80211.o
 
 ccflags-y += -D__CHECK_ENDIAN__
index 17fe39049740ddd6bb9914884be34b44c963b6b6..d1f556535f6d784b27a1d4773fc128d2d15eb737 100644 (file)
@@ -87,7 +87,7 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
 }
 
 /* requires cfg80211_mutex to be held! */
-static struct cfg80211_registered_device *
+struct cfg80211_registered_device *
 __cfg80211_drv_from_info(struct genl_info *info)
 {
        int ifindex;
@@ -176,13 +176,14 @@ void cfg80211_put_dev(struct cfg80211_registered_device *drv)
        mutex_unlock(&drv->mtx);
 }
 
+/* requires cfg80211_mutex to be held */
 int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                        char *newname)
 {
        struct cfg80211_registered_device *drv;
        int wiphy_idx, taken = -1, result, digits;
 
-       mutex_lock(&cfg80211_mutex);
+       assert_cfg80211_lock();
 
        /* prohibit calling the thing phy%d when %d is not its number */
        sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
@@ -195,30 +196,23 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                 * deny the name if it is phy<idx> where <idx> is printed
                 * without leading zeroes. taken == strlen(newname) here
                 */
-               result = -EINVAL;
                if (taken == strlen(PHY_NAME) + digits)
-                       goto out_unlock;
+                       return -EINVAL;
        }
 
 
        /* Ignore nop renames */
-       result = 0;
        if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0)
-               goto out_unlock;
+               return 0;
 
        /* Ensure another device does not already have this name. */
-       list_for_each_entry(drv, &cfg80211_drv_list, list) {
-               result = -EINVAL;
+       list_for_each_entry(drv, &cfg80211_drv_list, list)
                if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0)
-                       goto out_unlock;
-       }
+                       return -EINVAL;
 
-       /* this will only check for collisions in sysfs
-        * which is not even always compiled in.
-        */
        result = device_rename(&rdev->wiphy.dev, newname);
        if (result)
-               goto out_unlock;
+               return result;
 
        if (rdev->wiphy.debugfsdir &&
            !debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
@@ -228,13 +222,9 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n",
                       newname);
 
-       result = 0;
-out_unlock:
-       mutex_unlock(&cfg80211_mutex);
-       if (result == 0)
-               nl80211_notify_dev_rename(rdev);
+       nl80211_notify_dev_rename(rdev);
 
-       return result;
+       return 0;
 }
 
 /* exported functions */
index 6acd483a61f838f7359aa946dbf488aeff50c1d0..d43daa236ef9894f0839f053451cea8f9d8485fa 100644 (file)
@@ -90,6 +90,8 @@ struct cfg80211_internal_bss {
        struct rb_node rbn;
        unsigned long ts;
        struct kref ref;
+       bool hold;
+
        /* must be last because of priv member */
        struct cfg80211_bss pub;
 };
@@ -97,6 +99,9 @@ struct cfg80211_internal_bss {
 struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx);
 int get_wiphy_idx(struct wiphy *wiphy);
 
+struct cfg80211_registered_device *
+__cfg80211_drv_from_info(struct genl_info *info);
+
 /*
  * This function returns a pointer to the driver
  * that the genl_info item that is passed refers to.
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
new file mode 100644 (file)
index 0000000..bec5721
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * cfg80211 MLME SAP interface
+ *
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/nl80211.h>
+#include <net/cfg80211.h>
+#include "core.h"
+#include "nl80211.h"
+
+void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
+{
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       nl80211_send_rx_auth(rdev, dev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_send_rx_auth);
+
+void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len)
+{
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       nl80211_send_rx_assoc(rdev, dev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_send_rx_assoc);
+
+void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf, size_t len)
+{
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       nl80211_send_rx_deauth(rdev, dev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_send_rx_deauth);
+
+void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf,
+                              size_t len)
+{
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       nl80211_send_rx_disassoc(rdev, dev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_send_rx_disassoc);
index ab9d8f14e15103a4d762c08fdd132e1565c15df3..353e1a4ece836423dd503e55becc7cbffd4a2812 100644 (file)
@@ -111,6 +111,11 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
                              .len = IEEE80211_MAX_DATA_LEN },
        [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
        [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
+
+       [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
+                               .len = IEEE80211_MAX_SSID_LEN },
+       [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
+       [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
 };
 
 /* message building helper */
@@ -131,6 +136,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        struct nlattr *nl_freqs, *nl_freq;
        struct nlattr *nl_rates, *nl_rate;
        struct nlattr *nl_modes;
+       struct nlattr *nl_cmds;
        enum ieee80211_band band;
        struct ieee80211_channel *chan;
        struct ieee80211_rate *rate;
@@ -242,6 +248,35 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        }
        nla_nest_end(msg, nl_bands);
 
+       nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
+       if (!nl_cmds)
+               goto nla_put_failure;
+
+       i = 0;
+#define CMD(op, n)                                             \
+        do {                                                   \
+               if (dev->ops->op) {                             \
+                       i++;                                    \
+                       NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
+               }                                               \
+       } while (0)
+
+       CMD(add_virtual_intf, NEW_INTERFACE);
+       CMD(change_virtual_intf, SET_INTERFACE);
+       CMD(add_key, NEW_KEY);
+       CMD(add_beacon, NEW_BEACON);
+       CMD(add_station, NEW_STATION);
+       CMD(add_mpath, NEW_MPATH);
+       CMD(set_mesh_params, SET_MESH_PARAMS);
+       CMD(change_bss, SET_BSS);
+       CMD(auth, AUTHENTICATE);
+       CMD(assoc, ASSOCIATE);
+       CMD(deauth, DEAUTHENTICATE);
+       CMD(disassoc, DISASSOCIATE);
+
+#undef CMD
+       nla_nest_end(msg, nl_cmds);
+
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -331,16 +366,26 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        int result = 0, rem_txq_params = 0;
        struct nlattr *nl_txq_params;
 
-       rdev = cfg80211_get_dev_from_info(info);
-       if (IS_ERR(rdev))
-               return PTR_ERR(rdev);
+       rtnl_lock();
+
+       mutex_lock(&cfg80211_mutex);
+
+       rdev = __cfg80211_drv_from_info(info);
+       if (IS_ERR(rdev)) {
+               result = PTR_ERR(rdev);
+               goto unlock;
+       }
+
+       mutex_lock(&rdev->mtx);
 
-       if (info->attrs[NL80211_ATTR_WIPHY_NAME]) {
+       if (info->attrs[NL80211_ATTR_WIPHY_NAME])
                result = cfg80211_dev_rename(
                        rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
-               if (result)
-                       goto bad_res;
-       }
+
+       mutex_unlock(&cfg80211_mutex);
+
+       if (result)
+               goto bad_res;
 
        if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
                struct ieee80211_txq_params txq_params;
@@ -436,7 +481,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
 
  bad_res:
-       cfg80211_put_dev(rdev);
+       mutex_unlock(&rdev->mtx);
+ unlock:
+       rtnl_unlock();
        return result;
 }
 
@@ -572,21 +619,31 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
        enum nl80211_iftype type;
        struct net_device *dev;
        u32 _flags, *flags = NULL;
+       bool change = false;
 
        memset(&params, 0, sizeof(params));
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
+
        ifindex = dev->ifindex;
        type = dev->ieee80211_ptr->iftype;
        dev_put(dev);
 
-       err = -EINVAL;
        if (info->attrs[NL80211_ATTR_IFTYPE]) {
-               type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
-               if (type > NL80211_IFTYPE_MAX)
+               enum nl80211_iftype ntype;
+
+               ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+               if (type != ntype)
+                       change = true;
+               type = ntype;
+               if (type > NL80211_IFTYPE_MAX) {
+                       err = -EINVAL;
                        goto unlock;
+               }
        }
 
        if (!drv->ops->change_virtual_intf ||
@@ -602,6 +659,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                }
                params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
                params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
+               change = true;
        }
 
        if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
@@ -611,20 +669,26 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                }
                err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
                                          &_flags);
-               if (!err)
-                       flags = &_flags;
+               if (err)
+                       goto unlock;
+
+               flags = &_flags;
+               change = true;
        }
-       rtnl_lock();
-       err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
-                                           type, flags, &params);
+
+       if (change)
+               err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
+                                                   type, flags, &params);
+       else
+               err = 0;
 
        dev = __dev_get_by_index(&init_net, ifindex);
        WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type));
 
-       rtnl_unlock();
-
  unlock:
        cfg80211_put_dev(drv);
+ unlock_rtnl:
+       rtnl_unlock();
        return err;
 }
 
@@ -647,9 +711,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
+       rtnl_lock();
+
        drv = cfg80211_get_dev_from_info(info);
-       if (IS_ERR(drv))
-               return PTR_ERR(drv);
+       if (IS_ERR(drv)) {
+               err = PTR_ERR(drv);
+               goto unlock_rtnl;
+       }
 
        if (!drv->ops->add_virtual_intf ||
            !(drv->wiphy.interface_modes & (1 << type))) {
@@ -663,18 +731,17 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
        }
 
-       rtnl_lock();
        err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
                                  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
                                  &flags);
        err = drv->ops->add_virtual_intf(&drv->wiphy,
                nla_data(info->attrs[NL80211_ATTR_IFNAME]),
                type, err ? NULL : &flags, &params);
-       rtnl_unlock();
-
 
  unlock:
        cfg80211_put_dev(drv);
+ unlock_rtnl:
+       rtnl_unlock();
        return err;
 }
 
@@ -684,9 +751,11 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
        int ifindex, err;
        struct net_device *dev;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
        ifindex = dev->ifindex;
        dev_put(dev);
 
@@ -695,12 +764,12 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
+ unlock_rtnl:
+       rtnl_unlock();
        return err;
 }
 
@@ -752,9 +821,11 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_MAC])
                mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
 
        if (!drv->ops->get_key) {
                err = -EOPNOTSUPP;
@@ -782,10 +853,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
        if (mac_addr)
                NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
 
-       rtnl_lock();
        err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
                                &cookie, get_key_callback);
-       rtnl_unlock();
 
        if (err)
                goto out;
@@ -803,6 +872,9 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -831,9 +903,11 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
            !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
                return -EINVAL;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
 
        if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
                func = drv->ops->set_default_key;
@@ -845,13 +919,15 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
        err = func(&drv->wiphy, dev, key_idx);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -921,22 +997,25 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
        }
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
 
        if (!drv->ops->add_key) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -957,22 +1036,26 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_MAC])
                mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
 
        if (!drv->ops->del_key) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -986,9 +1069,16 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
        struct beacon_parameters params;
        int haveinfo = 0;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
 
        switch (info->genlhdr->cmd) {
        case NL80211_CMD_NEW_BEACON:
@@ -1049,13 +1139,14 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
        err = call(&drv->wiphy, dev, &params);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1065,22 +1156,29 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
        int err;
        struct net_device *dev;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
 
        if (!drv->ops->del_beacon) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
        err = drv->ops->del_beacon(&drv->wiphy, dev);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1246,30 +1344,32 @@ static int nl80211_dump_station(struct sk_buff *skb,
                        return -EINVAL;
        }
 
-       netdev = dev_get_by_index(&init_net, ifidx);
-       if (!netdev)
-               return -ENODEV;
+       rtnl_lock();
+
+       netdev = __dev_get_by_index(&init_net, ifidx);
+       if (!netdev) {
+               err = -ENODEV;
+               goto out_rtnl;
+       }
 
        dev = cfg80211_get_dev_from_ifindex(ifidx);
        if (IS_ERR(dev)) {
                err = PTR_ERR(dev);
-               goto out_put_netdev;
+               goto out_rtnl;
        }
 
        if (!dev->ops->dump_station) {
-               err = -ENOSYS;
+               err = -EOPNOTSUPP;
                goto out_err;
        }
 
-       rtnl_lock();
-
        while (1) {
                err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
                                             mac_addr, &sinfo);
                if (err == -ENOENT)
                        break;
                if (err)
-                       goto out_err_rtnl;
+                       goto out_err;
 
                if (nl80211_send_station(skb,
                                NETLINK_CB(cb->skb).pid,
@@ -1285,12 +1385,10 @@ static int nl80211_dump_station(struct sk_buff *skb,
  out:
        cb->args[1] = sta_idx;
        err = skb->len;
- out_err_rtnl:
-       rtnl_unlock();
  out_err:
        cfg80211_put_dev(dev);
- out_put_netdev:
-       dev_put(netdev);
+ out_rtnl:
+       rtnl_unlock();
 
        return err;
 }
@@ -1311,19 +1409,18 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 
        mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->get_station) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
-       rtnl_unlock();
-
        if (err)
                goto out;
 
@@ -1340,10 +1437,12 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 
  out_free:
        nlmsg_free(msg);
-
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1411,9 +1510,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                params.plink_action =
                    nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
        if (err)
@@ -1424,15 +1525,16 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
-       rtnl_unlock();
 
  out:
        if (params.vlan)
                dev_put(params.vlan);
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1474,9 +1576,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                                &params.station_flags))
                return -EINVAL;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
        if (err)
@@ -1487,15 +1591,21 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
        err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
-       rtnl_unlock();
 
  out:
        if (params.vlan)
                dev_put(params.vlan);
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1509,22 +1619,25 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_MAC])
                mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->del_station) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1605,22 +1718,29 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
                        return -EINVAL;
        }
 
-       netdev = dev_get_by_index(&init_net, ifidx);
-       if (!netdev)
-               return -ENODEV;
+       rtnl_lock();
+
+       netdev = __dev_get_by_index(&init_net, ifidx);
+       if (!netdev) {
+               err = -ENODEV;
+               goto out_rtnl;
+       }
 
        dev = cfg80211_get_dev_from_ifindex(ifidx);
        if (IS_ERR(dev)) {
                err = PTR_ERR(dev);
-               goto out_put_netdev;
+               goto out_rtnl;
        }
 
        if (!dev->ops->dump_mpath) {
-               err = -ENOSYS;
+               err = -EOPNOTSUPP;
                goto out_err;
        }
 
-       rtnl_lock();
+       if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
 
        while (1) {
                err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
@@ -1628,7 +1748,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
                if (err == -ENOENT)
                        break;
                if (err)
-                       goto out_err_rtnl;
+                       goto out_err;
 
                if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
                                       cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -1643,12 +1763,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
  out:
        cb->args[1] = path_idx;
        err = skb->len;
- out_err_rtnl:
-       rtnl_unlock();
  out_err:
        cfg80211_put_dev(dev);
- out_put_netdev:
-       dev_put(netdev);
+ out_rtnl:
+       rtnl_unlock();
 
        return err;
 }
@@ -1670,19 +1788,23 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
 
        dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->get_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
-       err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
-       rtnl_unlock();
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
 
+       err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
        if (err)
                goto out;
 
@@ -1699,10 +1821,12 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
 
  out_free:
        nlmsg_free(msg);
-
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1723,22 +1847,35 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
        dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
        next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->change_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
        err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
@@ -1758,22 +1895,35 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
        dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
        next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->add_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
        err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1787,22 +1937,25 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_MAC])
                dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->del_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1835,22 +1988,30 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
                        nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
        }
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->change_bss) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
        err = drv->ops->change_bss(&drv->wiphy, dev, &params);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1945,10 +2106,12 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
        struct nlattr *pinfoattr;
        struct sk_buff *msg;
 
+       rtnl_lock();
+
        /* Look up our device */
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->get_mesh_params) {
                err = -EOPNOTSUPP;
@@ -1956,9 +2119,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
        }
 
        /* Get the mesh params */
-       rtnl_lock();
        err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
-       rtnl_unlock();
        if (err)
                goto out;
 
@@ -2007,13 +2168,16 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
        err = genlmsg_unicast(msg, info->snd_pid);
        goto out;
 
-nla_put_failure:
+ nla_put_failure:
        genlmsg_cancel(msg, hdr);
        err = -EMSGSIZE;
-out:
+ out:
        /* Cleanup */
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -2060,9 +2224,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
                        parent_attr, nl80211_meshconf_params_policy))
                return -EINVAL;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->set_mesh_params) {
                err = -EOPNOTSUPP;
@@ -2109,14 +2275,15 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
                        nla_get_u16);
 
        /* Apply changes */
-       rtnl_lock();
        err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
-       rtnl_unlock();
 
  out:
        /* cleanup */
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -2262,59 +2429,24 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        return -EINVAL;
 }
 
-static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb,
-                                    struct genl_info *info)
+static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *drv;
-       int err;
        struct net_device *dev;
-       struct mgmt_extra_ie_params params;
-
-       memset(&params, 0, sizeof(params));
+       struct cfg80211_scan_request *request;
+       struct cfg80211_ssid *ssid;
+       struct ieee80211_channel *channel;
+       struct nlattr *attr;
+       struct wiphy *wiphy;
+       int err, tmp, n_ssids = 0, n_channels = 0, i;
+       enum ieee80211_band band;
+       size_t ie_len;
 
-       if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE])
-               return -EINVAL;
-       params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
-       if (params.subtype > 15)
-               return -EINVAL; /* FC Subtype field is 4 bits (0..15) */
-
-       if (info->attrs[NL80211_ATTR_IE]) {
-               params.ies = nla_data(info->attrs[NL80211_ATTR_IE]);
-               params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
-       }
+       rtnl_lock();
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
-
-       if (drv->ops->set_mgmt_extra_ie) {
-               rtnl_lock();
-               err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, &params);
-               rtnl_unlock();
-       } else
-               err = -EOPNOTSUPP;
-
-       cfg80211_put_dev(drv);
-       dev_put(dev);
-       return err;
-}
-
-static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
-{
-       struct cfg80211_registered_device *drv;
-       struct net_device *dev;
-       struct cfg80211_scan_request *request;
-       struct cfg80211_ssid *ssid;
-       struct ieee80211_channel *channel;
-       struct nlattr *attr;
-       struct wiphy *wiphy;
-       int err, tmp, n_ssids = 0, n_channels = 0, i;
-       enum ieee80211_band band;
-       size_t ie_len;
-
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
-       if (err)
-               return err;
+               goto out_rtnl;
 
        wiphy = &drv->wiphy;
 
@@ -2323,11 +2455,14 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
 
        if (drv->scan_req) {
                err = -EBUSY;
-               goto out_unlock;
+               goto out;
        }
 
        if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
@@ -2335,7 +2470,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                        n_channels++;
                if (!n_channels) {
                        err = -EINVAL;
-                       goto out_unlock;
+                       goto out;
                }
        } else {
                for (band = 0; band < IEEE80211_NUM_BANDS; band++)
@@ -2349,7 +2484,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 
        if (n_ssids > wiphy->max_scan_ssids) {
                err = -EINVAL;
-               goto out_unlock;
+               goto out;
        }
 
        if (info->attrs[NL80211_ATTR_IE])
@@ -2363,7 +2498,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                        + ie_len, GFP_KERNEL);
        if (!request) {
                err = -ENOMEM;
-               goto out_unlock;
+               goto out;
        }
 
        request->channels = (void *)((char *)request + sizeof(*request));
@@ -2434,11 +2569,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                drv->scan_req = NULL;
                kfree(request);
        }
- out_unlock:
-       rtnl_unlock();
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -2558,6 +2694,288 @@ static int nl80211_dump_scan(struct sk_buff *skb,
        return err;
 }
 
+static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
+{
+       return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
+               auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
+               auth_type == NL80211_AUTHTYPE_FT ||
+               auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
+}
+
+static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       struct cfg80211_auth_request req;
+       struct wiphy *wiphy;
+       int err;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!drv->ops->auth) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       if (!info->attrs[NL80211_ATTR_MAC]) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       wiphy = &drv->wiphy;
+       memset(&req, 0, sizeof(req));
+
+       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               req.chan = ieee80211_get_channel(
+                       wiphy,
+                       nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+               if (!req.chan) {
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+       if (info->attrs[NL80211_ATTR_SSID]) {
+               req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+               req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+       }
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
+               req.auth_type =
+                       nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+               if (!nl80211_valid_auth_type(req.auth_type)) {
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+       err = drv->ops->auth(&drv->wiphy, dev, &req);
+
+out:
+       cfg80211_put_dev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       struct cfg80211_assoc_request req;
+       struct wiphy *wiphy;
+       int err;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!drv->ops->assoc) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       if (!info->attrs[NL80211_ATTR_MAC] ||
+           !info->attrs[NL80211_ATTR_SSID]) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       wiphy = &drv->wiphy;
+       memset(&req, 0, sizeof(req));
+
+       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               req.chan = ieee80211_get_channel(
+                       wiphy,
+                       nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+               if (!req.chan) {
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+       req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+       req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       err = drv->ops->assoc(&drv->wiphy, dev, &req);
+
+out:
+       cfg80211_put_dev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       struct cfg80211_deauth_request req;
+       struct wiphy *wiphy;
+       int err;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!drv->ops->deauth) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       if (!info->attrs[NL80211_ATTR_MAC]) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       wiphy = &drv->wiphy;
+       memset(&req, 0, sizeof(req));
+
+       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       if (info->attrs[NL80211_ATTR_REASON_CODE]) {
+               req.reason_code =
+                       nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+               if (req.reason_code == 0) {
+                       /* Reason Code 0 is reserved */
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       err = drv->ops->deauth(&drv->wiphy, dev, &req);
+
+out:
+       cfg80211_put_dev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       struct cfg80211_disassoc_request req;
+       struct wiphy *wiphy;
+       int err;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!drv->ops->disassoc) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       if (!info->attrs[NL80211_ATTR_MAC]) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       wiphy = &drv->wiphy;
+       memset(&req, 0, sizeof(req));
+
+       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       if (info->attrs[NL80211_ATTR_REASON_CODE]) {
+               req.reason_code =
+                       nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+               if (req.reason_code == 0) {
+                       /* Reason Code 0 is reserved */
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       err = drv->ops->disassoc(&drv->wiphy, dev, &req);
+
+out:
+       cfg80211_put_dev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
 static struct genl_ops nl80211_ops[] = {
        {
                .cmd = NL80211_CMD_GET_WIPHY,
@@ -2724,12 +3142,6 @@ static struct genl_ops nl80211_ops[] = {
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
-       {
-               .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE,
-               .doit = nl80211_set_mgmt_extra_ie,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
        {
                .cmd = NL80211_CMD_TRIGGER_SCAN,
                .doit = nl80211_trigger_scan,
@@ -2741,6 +3153,33 @@ static struct genl_ops nl80211_ops[] = {
                .policy = nl80211_policy,
                .dumpit = nl80211_dump_scan,
        },
+       {
+               .cmd = NL80211_CMD_AUTHENTICATE,
+               .doit = nl80211_authenticate,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_ASSOCIATE,
+               .doit = nl80211_associate,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_DEAUTHENTICATE,
+               .doit = nl80211_deauthenticate,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_DISASSOCIATE,
+               .doit = nl80211_disassociate,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+};
+static struct genl_multicast_group nl80211_mlme_mcgrp = {
+       .name = "mlme",
 };
 
 /* multicast groups */
@@ -2887,6 +3326,71 @@ nla_put_failure:
        nlmsg_free(msg);
 }
 
+static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
+                                   struct net_device *netdev,
+                                   const u8 *buf, size_t len,
+                                   enum nl80211_commands cmd)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+
+void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
+                         struct net_device *netdev, const u8 *buf, size_t len)
+{
+       nl80211_send_mlme_event(rdev, netdev, buf, len,
+                               NL80211_CMD_AUTHENTICATE);
+}
+
+void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
+                          struct net_device *netdev, const u8 *buf,
+                          size_t len)
+{
+       nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
+}
+
+void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev,
+                           struct net_device *netdev, const u8 *buf,
+                           size_t len)
+{
+       nl80211_send_mlme_event(rdev, netdev, buf, len,
+                               NL80211_CMD_DEAUTHENTICATE);
+}
+
+void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev,
+                             struct net_device *netdev, const u8 *buf,
+                             size_t len)
+{
+       nl80211_send_mlme_event(rdev, netdev, buf, len,
+                               NL80211_CMD_DISASSOCIATE);
+}
+
 /* initialisation/exit functions */
 
 int nl80211_init(void)
@@ -2915,6 +3419,10 @@ int nl80211_init(void)
        if (err)
                goto err_out;
 
+       err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
+       if (err)
+               goto err_out;
+
        return 0;
  err_out:
        genl_unregister_family(&nl80211_fam);
index e65a3c38c52fa2ec30545407efeb23c9e73b83d6..b77af4ab80becebd45800e554660a8a9fe0ef56b 100644 (file)
@@ -3,7 +3,6 @@
 
 #include "core.h"
 
-#ifdef CONFIG_NL80211
 extern int nl80211_init(void);
 extern void nl80211_exit(void);
 extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev);
@@ -12,30 +11,17 @@ extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
 extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
                                      struct net_device *netdev);
 extern void nl80211_send_reg_change_event(struct regulatory_request *request);
-#else
-static inline int nl80211_init(void)
-{
-       return 0;
-}
-static inline void nl80211_exit(void)
-{
-}
-static inline void nl80211_notify_dev_rename(
-       struct cfg80211_registered_device *rdev)
-{
-}
-static inline void
-nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
-                      struct net_device *netdev)
-{}
-static inline void nl80211_send_scan_aborted(
-                                       struct cfg80211_registered_device *rdev,
-                                       struct net_device *netdev)
-{}
-static inline void
-nl80211_send_reg_change_event(struct regulatory_request *request)
-{
-}
-#endif /* CONFIG_NL80211 */
+extern void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
+                                struct net_device *netdev,
+                                const u8 *buf, size_t len);
+extern void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
+                                 struct net_device *netdev,
+                                 const u8 *buf, size_t len);
+extern void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev,
+                                  struct net_device *netdev,
+                                  const u8 *buf, size_t len);
+extern void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev,
+                                    struct net_device *netdev,
+                                    const u8 *buf, size_t len);
 
 #endif /* __NET_WIRELESS_NL80211_H */
index eb8b8ed161554e869148285e5a164496a596686c..6327e1617acbac5e7c34cc3ca9a038dfbed57fa4 100644 (file)
@@ -122,9 +122,14 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
 
 #ifdef CONFIG_WIRELESS_OLD_REGULATORY
 static char *ieee80211_regdom = "US";
+#else
+static char *ieee80211_regdom = "00";
+#endif
+
 module_param(ieee80211_regdom, charp, 0444);
 MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
 
+#ifdef CONFIG_WIRELESS_OLD_REGULATORY
 /*
  * We assume 40 MHz bandwidth for the old regulatory work.
  * We make emphasis we are using the exact same frequencies
@@ -1415,16 +1420,6 @@ new_request:
                return r;
        }
 
-       /*
-        * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled
-        * AND if CRDA is NOT present nothing will happen, if someone
-        * wants to bother with 11d with OLD_REG you can add a timer.
-        * If after x amount of time nothing happens you can call:
-        *
-        * return set_regdom(country_ie_regdomain);
-        *
-        * to intersect with the static rd
-        */
        return call_crda(last_request->alpha2);
 }
 
@@ -1601,6 +1596,10 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy,
 
        assert_cfg80211_lock();
 
+       if (unlikely(last_request->initiator !=
+           NL80211_REGDOM_SET_BY_COUNTRY_IE))
+               return false;
+
        request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
 
        if (!request_wiphy)
@@ -1663,7 +1662,9 @@ void regulatory_hint_11d(struct wiphy *wiphy,
         * we optimize an early check to exit out early if we don't have to
         * do anything
         */
-       if (likely(wiphy_idx_valid(last_request->wiphy_idx))) {
+       if (likely(last_request->initiator ==
+           NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+           wiphy_idx_valid(last_request->wiphy_idx))) {
                struct cfg80211_registered_device *drv_last_ie;
 
                drv_last_ie =
@@ -2022,28 +2023,21 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
         */
 
        BUG_ON(!country_ie_regdomain);
+       BUG_ON(rd == country_ie_regdomain);
 
-       if (rd != country_ie_regdomain) {
-               /*
-                * Intersect what CRDA returned and our what we
-                * had built from the Country IE received
-                */
+       /*
+        * Intersect what CRDA returned and our what we
+        * had built from the Country IE received
+        */
 
-               intersected_rd = regdom_intersect(rd, country_ie_regdomain);
+       intersected_rd = regdom_intersect(rd, country_ie_regdomain);
 
-               reg_country_ie_process_debug(rd, country_ie_regdomain,
-                       intersected_rd);
+       reg_country_ie_process_debug(rd,
+                                    country_ie_regdomain,
+                                    intersected_rd);
 
-               kfree(country_ie_regdomain);
-               country_ie_regdomain = NULL;
-       } else {
-               /*
-                * This would happen when CRDA was not present and
-                * OLD_REGULATORY was enabled. We intersect our Country
-                * IE rd and what was set on cfg80211 originally
-                */
-               intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
-       }
+       kfree(country_ie_regdomain);
+       country_ie_regdomain = NULL;
 
        if (!intersected_rd)
                return -EINVAL;
@@ -2135,15 +2129,18 @@ int regulatory_init(void)
        /*
         * The old code still requests for a new regdomain and if
         * you have CRDA you get it updated, otherwise you get
-        * stuck with the static values. We ignore "EU" code as
-        * that is not a valid ISO / IEC 3166 alpha2
+        * stuck with the static values. Since "EU" is not a valid
+        * ISO / IEC 3166 alpha2 code we can't expect userpace to
+        * give us a regulatory domain for it. We need last_request
+        * iniitalized though so lets just send a request which we
+        * know will be ignored... this crap will be removed once
+        * OLD_REG dies.
         */
-       if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U')
-               err = regulatory_hint_core(ieee80211_regdom);
+       err = regulatory_hint_core(ieee80211_regdom);
 #else
        cfg80211_regdomain = cfg80211_world_regdom;
 
-       err = regulatory_hint_core("00");
+       err = regulatory_hint_core(ieee80211_regdom);
 #endif
        if (err) {
                if (err == -ENOMEM)
index 280dbcd02c1563e7e56df72dd2e7a771fc8563c9..2a00e362f5fe1f0eca4c503fa92faf6d13450deb 100644 (file)
@@ -80,7 +80,8 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
        bool expired = false;
 
        list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
-               if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
+               if (bss->hold ||
+                   !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
                        continue;
                list_del(&bss->list);
                rb_erase(&bss->rbn, &dev->bss_tree);
@@ -471,6 +472,30 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 }
 EXPORT_SYMBOL(cfg80211_unlink_bss);
 
+void cfg80211_hold_bss(struct cfg80211_bss *pub)
+{
+       struct cfg80211_internal_bss *bss;
+
+       if (!pub)
+               return;
+
+       bss = container_of(pub, struct cfg80211_internal_bss, pub);
+       bss->hold = true;
+}
+EXPORT_SYMBOL(cfg80211_hold_bss);
+
+void cfg80211_unhold_bss(struct cfg80211_bss *pub)
+{
+       struct cfg80211_internal_bss *bss;
+
+       if (!pub)
+               return;
+
+       bss = container_of(pub, struct cfg80211_internal_bss, pub);
+       bss->hold = false;
+}
+EXPORT_SYMBOL(cfg80211_unhold_bss);
+
 #ifdef CONFIG_WIRELESS_EXT
 int cfg80211_wext_siwscan(struct net_device *dev,
                          struct iw_request_info *info,
index b84a9b4fe96a79409dc84caf693669c82d3315d0..0fd1db6e95bbe26ef13a9d3524a42ff5765d97c9 100644 (file)
@@ -66,6 +66,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
        struct cfg80211_registered_device *rdev;
        struct vif_params vifparams;
        enum nl80211_iftype type;
+       int ret;
 
        if (!wdev)
                return -EOPNOTSUPP;
@@ -96,10 +97,16 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
                return -EINVAL;
        }
 
+       if (type == wdev->iftype)
+               return 0;
+
        memset(&vifparams, 0, sizeof(vifparams));
 
-       return rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type,
-                                             NULL, &vifparams);
+       ret = rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type,
+                                            NULL, &vifparams);
+       WARN_ON(!ret && wdev->iftype != type);
+
+       return ret;
 }
 EXPORT_SYMBOL(cfg80211_wext_siwmode);
 
index 9ca17b1ce52e8191f04b401e2f0a1e021062badf..ed80af8ca5fb86f018c2421467d497d084fe73ba 100644 (file)
@@ -1035,6 +1035,12 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
                sx25.sx25_addr   = x25->dest_addr;
        }
 
+       /* Sanity check the packet size */
+       if (len > 65535) {
+               rc = -EMSGSIZE;
+               goto out;
+       }
+
        SOCK_DEBUG(sk, "x25_sendmsg: sendto: Addresses built.\n");
 
        /* Build a packet */
index 62a5425cc6aa1c83a386f2123281d4ccc07c9cdc..82271720d970f25005015cc73728eda7fc5bf448 100644 (file)
@@ -1615,7 +1615,7 @@ void xfrm_state_walk_done(struct xfrm_state_walk *walk)
 
        spin_lock_bh(&xfrm_state_lock);
        list_del(&walk->all);
-       spin_lock_bh(&xfrm_state_lock);
+       spin_unlock_bh(&xfrm_state_lock);
 }
 EXPORT_SYMBOL(xfrm_state_walk_done);