浏览代码

Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/linville/wireless-2.6.26

David S. Miller 17 年之前
父节点
当前提交
ba73d4c84a
共有 100 个文件被更改,包括 10511 次插入3477 次删除
  1. 2 1
      Documentation/DocBook/Makefile
  2. 335 0
      Documentation/DocBook/mac80211.tmpl
  3. 27 26
      drivers/net/wireless/ath5k/ath5k.h
  4. 94 53
      drivers/net/wireless/ath5k/base.c
  5. 24 13
      drivers/net/wireless/ath5k/debug.c
  6. 2 4
      drivers/net/wireless/ath5k/debug.h
  7. 250 191
      drivers/net/wireless/ath5k/hw.c
  8. 89 61
      drivers/net/wireless/ath5k/hw.h
  9. 231 2
      drivers/net/wireless/ath5k/initvals.c
  10. 170 4
      drivers/net/wireless/ath5k/phy.c
  11. 3 1
      drivers/net/wireless/ath5k/reg.h
  12. 44 9
      drivers/net/wireless/b43/b43.h
  13. 178 208
      drivers/net/wireless/b43/dma.c
  14. 10 1
      drivers/net/wireless/b43/dma.h
  15. 191 5
      drivers/net/wireless/b43/main.c
  16. 4 0
      drivers/net/wireless/b43/main.h
  17. 0 27
      drivers/net/wireless/b43/xmit.c
  18. 0 12
      drivers/net/wireless/b43/xmit.h
  19. 5 0
      drivers/net/wireless/iwlwifi/Kconfig
  20. 3 0
      drivers/net/wireless/iwlwifi/Makefile
  21. 20 20
      drivers/net/wireless/iwlwifi/iwl-3945-commands.h
  22. 80 0
      drivers/net/wireless/iwlwifi/iwl-3945-core.h
  23. 16 1
      drivers/net/wireless/iwlwifi/iwl-3945-debug.h
  24. 0 174
      drivers/net/wireless/iwlwifi/iwl-3945-hw.h
  25. 39 37
      drivers/net/wireless/iwlwifi/iwl-3945-rs.c
  26. 3 3
      drivers/net/wireless/iwlwifi/iwl-3945-rs.h
  27. 339 48
      drivers/net/wireless/iwlwifi/iwl-3945.c
  28. 12 23
      drivers/net/wireless/iwlwifi/iwl-3945.h
  29. 20 20
      drivers/net/wireless/iwlwifi/iwl-4965-commands.h
  30. 16 0
      drivers/net/wireless/iwlwifi/iwl-4965-debug.h
  31. 0 175
      drivers/net/wireless/iwlwifi/iwl-4965-hw.h
  32. 50 48
      drivers/net/wireless/iwlwifi/iwl-4965-rs.c
  33. 1 1
      drivers/net/wireless/iwlwifi/iwl-4965-rs.h
  34. 286 66
      drivers/net/wireless/iwlwifi/iwl-4965.c
  35. 16 17
      drivers/net/wireless/iwlwifi/iwl-4965.h
  36. 47 0
      drivers/net/wireless/iwlwifi/iwl-core.c
  37. 84 0
      drivers/net/wireless/iwlwifi/iwl-core.h
  38. 259 0
      drivers/net/wireless/iwlwifi/iwl-csr.h
  39. 205 0
      drivers/net/wireless/iwlwifi/iwl-eeprom.c
  40. 399 0
      drivers/net/wireless/iwlwifi/iwl-eeprom.h
  41. 20 0
      drivers/net/wireless/iwlwifi/iwl-helpers.h
  42. 94 437
      drivers/net/wireless/iwlwifi/iwl3945-base.c
  43. 79 272
      drivers/net/wireless/iwlwifi/iwl4965-base.c
  44. 2 10
      drivers/net/wireless/libertas/assoc.c
  45. 75 56
      drivers/net/wireless/libertas/cmd.c
  46. 2 0
      drivers/net/wireless/libertas/cmd.h
  47. 0 63
      drivers/net/wireless/libertas/cmdresp.c
  48. 11 8
      drivers/net/wireless/libertas/hostcmd.h
  49. 221 313
      drivers/net/wireless/libertas/scan.c
  50. 3 51
      drivers/net/wireless/libertas/scan.h
  51. 13 0
      drivers/net/wireless/libertas/types.h
  52. 1 0
      drivers/net/wireless/zd1211rw/zd_chip.c
  53. 8 0
      drivers/net/wireless/zd1211rw/zd_chip.h
  54. 73 2
      drivers/net/wireless/zd1211rw/zd_mac.c
  55. 3 0
      drivers/net/wireless/zd1211rw/zd_mac.h
  56. 9 2
      drivers/net/wireless/zd1211rw/zd_usb.c
  57. 9 0
      drivers/ssb/Kconfig
  58. 1 0
      drivers/ssb/Makefile
  59. 294 0
      drivers/ssb/driver_gige.c
  60. 1 0
      drivers/ssb/driver_mipscore.c
  61. 89 71
      drivers/ssb/driver_pcicore.c
  62. 90 0
      drivers/ssb/embedded.c
  63. 29 1
      drivers/ssb/main.c
  64. 2 0
      drivers/ssb/ssb_private.h
  65. 35 0
      include/linux/ieee80211.h
  66. 101 18
      include/linux/nl80211.h
  67. 7 0
      include/linux/ssb/ssb.h
  68. 174 0
      include/linux/ssb/ssb_driver_gige.h
  69. 19 0
      include/linux/ssb/ssb_driver_pci.h
  70. 1 0
      include/linux/wireless.h
  71. 122 17
      include/net/cfg80211.h
  72. 72 34
      include/net/mac80211.h
  73. 16 0
      net/mac80211/Kconfig
  74. 6 0
      net/mac80211/Makefile
  75. 312 48
      net/mac80211/cfg.c
  76. 197 0
      net/mac80211/debugfs_netdev.c
  77. 3 3
      net/mac80211/debugfs_sta.c
  78. 2 0
      net/mac80211/debugfs_sta.h
  79. 97 60
      net/mac80211/ieee80211.c
  80. 267 45
      net/mac80211/ieee80211_i.h
  81. 22 14
      net/mac80211/ieee80211_iface.c
  82. 30 31
      net/mac80211/ieee80211_ioctl.c
  83. 5 3
      net/mac80211/ieee80211_rate.c
  84. 1 0
      net/mac80211/ieee80211_rate.h
  85. 420 148
      net/mac80211/ieee80211_sta.c
  86. 12 6
      net/mac80211/key.c
  87. 449 0
      net/mac80211/mesh.c
  88. 290 0
      net/mac80211/mesh.h
  89. 857 0
      net/mac80211/mesh_hwmp.c
  90. 516 0
      net/mac80211/mesh_pathtbl.c
  91. 761 0
      net/mac80211/mesh_plink.c
  92. 25 11
      net/mac80211/rc80211_pid_algo.c
  93. 11 7
      net/mac80211/rc80211_simple.c
  94. 210 99
      net/mac80211/rx.c
  95. 320 144
      net/mac80211/sta_info.c
  96. 170 62
      net/mac80211/sta_info.h
  97. 260 172
      net/mac80211/tx.c
  98. 24 4
      net/mac80211/util.c
  99. 12 12
      net/mac80211/wep.c
  100. 2 2
      net/mac80211/wep.h

+ 2 - 1
Documentation/DocBook/Makefile

@@ -11,7 +11,8 @@ DOCBOOKS := wanbook.xml z8530book.xml mcabook.xml videobook.xml \
 	    procfs-guide.xml writing_usb_driver.xml networking.xml \
 	    procfs-guide.xml writing_usb_driver.xml networking.xml \
 	    kernel-api.xml filesystems.xml lsm.xml usb.xml \
 	    kernel-api.xml filesystems.xml lsm.xml usb.xml \
 	    gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
 	    gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
-	    genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml
+	    genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
+	    mac80211.xml
 
 
 ###
 ###
 # The build process is as follows (targets):
 # The build process is as follows (targets):

+ 335 - 0
Documentation/DocBook/mac80211.tmpl

@@ -0,0 +1,335 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+	"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="mac80211-developers-guide">
+  <bookinfo>
+    <title>The mac80211 subsystem for kernel developers</title>
+
+    <authorgroup>
+      <author>
+        <firstname>Johannes</firstname>
+        <surname>Berg</surname>
+        <affiliation>
+          <address><email>johannes@sipsolutions.net</email></address>
+        </affiliation>
+      </author>
+    </authorgroup>
+
+    <copyright>
+      <year>2007</year>
+      <year>2008</year>
+      <holder>Johannes Berg</holder>
+    </copyright>
+
+    <legalnotice>
+      <para>
+        This documentation 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.
+      </para>
+
+      <para>
+        This documentation 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.
+      </para>
+
+      <para>
+        You should have received a copy of the GNU General Public
+        License along with this documentation; if not, write to the Free
+        Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+        MA 02111-1307 USA
+      </para>
+
+      <para>
+        For more details see the file COPYING in the source
+        distribution of Linux.
+      </para>
+    </legalnotice>
+
+    <abstract>
+!Pinclude/net/mac80211.h Introduction
+!Pinclude/net/mac80211.h Warning
+    </abstract>
+  </bookinfo>
+
+  <toc></toc>
+
+<!--
+Generally, this document shall be ordered by increasing complexity.
+It is important to note that readers should be able to read only
+the first few sections to get a working driver and only advanced
+usage should require reading the full document.
+-->
+
+  <part>
+    <title>The basic mac80211 driver interface</title>
+    <partintro>
+      <para>
+        You should read and understand the information contained
+        within this part of the book while implementing a driver.
+        In some chapters, advanced usage is noted, that may be
+        skipped at first.
+      </para>
+      <para>
+        This part of the book only covers station and monitor mode
+        functionality, additional information required to implement
+        the other modes is covered in the second part of the book.
+      </para>
+    </partintro>
+
+    <chapter id="basics">
+      <title>Basic hardware handling</title>
+      <para>TBD</para>
+      <para>
+        This chapter shall contain information on getting a hw
+        struct allocated and registered with mac80211.
+      </para>
+      <para>
+        Since it is required to allocate rates/modes before registering
+        a hw struct, this chapter shall also contain information on setting
+        up the rate/mode structs.
+      </para>
+      <para>
+        Additionally, some discussion about the callbacks and
+        the general programming model should be in here, including
+        the definition of ieee80211_ops which will be referred to
+        a lot.
+      </para>
+      <para>
+        Finally, a discussion of hardware capabilities should be done
+        with references to other parts of the book.
+      </para>
+<!-- intentionally multiple !F lines to get proper order -->
+!Finclude/net/mac80211.h ieee80211_hw
+!Finclude/net/mac80211.h ieee80211_hw_flags
+!Finclude/net/mac80211.h SET_IEEE80211_DEV
+!Finclude/net/mac80211.h SET_IEEE80211_PERM_ADDR
+!Finclude/net/mac80211.h ieee80211_ops
+!Finclude/net/mac80211.h ieee80211_alloc_hw
+!Finclude/net/mac80211.h ieee80211_register_hw
+!Finclude/net/mac80211.h ieee80211_get_tx_led_name
+!Finclude/net/mac80211.h ieee80211_get_rx_led_name
+!Finclude/net/mac80211.h ieee80211_get_assoc_led_name
+!Finclude/net/mac80211.h ieee80211_get_radio_led_name
+!Finclude/net/mac80211.h ieee80211_unregister_hw
+!Finclude/net/mac80211.h ieee80211_free_hw
+    </chapter>
+
+    <chapter id="phy-handling">
+      <title>PHY configuration</title>
+      <para>TBD</para>
+      <para>
+        This chapter should describe PHY handling including
+        start/stop callbacks and the various structures used.
+      </para>
+!Finclude/net/mac80211.h ieee80211_conf
+!Finclude/net/mac80211.h ieee80211_conf_flags
+    </chapter>
+
+    <chapter id="iface-handling">
+      <title>Virtual interfaces</title>
+      <para>TBD</para>
+      <para>
+        This chapter should describe virtual interface basics
+        that are relevant to the driver (VLANs, MGMT etc are not.)
+        It should explain the use of the add_iface/remove_iface
+        callbacks as well as the interface configuration callbacks.
+      </para>
+      <para>Things related to AP mode should be discussed there.</para>
+      <para>
+        Things related to supporting multiple interfaces should be
+        in the appropriate chapter, a BIG FAT note should be here about
+        this though and the recommendation to allow only a single
+        interface in STA mode at first!
+      </para>
+!Finclude/net/mac80211.h ieee80211_if_types
+!Finclude/net/mac80211.h ieee80211_if_init_conf
+!Finclude/net/mac80211.h ieee80211_if_conf
+    </chapter>
+
+    <chapter id="rx-tx">
+      <title>Receive and transmit processing</title>
+      <sect1>
+        <title>what should be here</title>
+        <para>TBD</para>
+        <para>
+          This should describe the receive and transmit
+          paths in mac80211/the drivers as well as
+          transmit status handling.
+        </para>
+      </sect1>
+      <sect1>
+        <title>Frame format</title>
+!Pinclude/net/mac80211.h Frame format
+      </sect1>
+      <sect1>
+        <title>Alignment issues</title>
+        <para>TBD</para>
+      </sect1>
+      <sect1>
+        <title>Calling into mac80211 from interrupts</title>
+!Pinclude/net/mac80211.h Calling mac80211 from interrupts
+      </sect1>
+      <sect1>
+        <title>functions/definitions</title>
+!Finclude/net/mac80211.h ieee80211_rx_status
+!Finclude/net/mac80211.h mac80211_rx_flags
+!Finclude/net/mac80211.h ieee80211_tx_control
+!Finclude/net/mac80211.h ieee80211_tx_status_flags
+!Finclude/net/mac80211.h ieee80211_rx
+!Finclude/net/mac80211.h ieee80211_rx_irqsafe
+!Finclude/net/mac80211.h ieee80211_tx_status
+!Finclude/net/mac80211.h ieee80211_tx_status_irqsafe
+!Finclude/net/mac80211.h ieee80211_rts_get
+!Finclude/net/mac80211.h ieee80211_rts_duration
+!Finclude/net/mac80211.h ieee80211_ctstoself_get
+!Finclude/net/mac80211.h ieee80211_ctstoself_duration
+!Finclude/net/mac80211.h ieee80211_generic_frame_duration
+!Finclude/net/mac80211.h ieee80211_get_hdrlen_from_skb
+!Finclude/net/mac80211.h ieee80211_get_hdrlen
+!Finclude/net/mac80211.h ieee80211_wake_queue
+!Finclude/net/mac80211.h ieee80211_stop_queue
+!Finclude/net/mac80211.h ieee80211_start_queues
+!Finclude/net/mac80211.h ieee80211_stop_queues
+!Finclude/net/mac80211.h ieee80211_wake_queues
+      </sect1>
+    </chapter>
+
+    <chapter id="filters">
+      <title>Frame filtering</title>
+!Pinclude/net/mac80211.h Frame filtering
+!Finclude/net/mac80211.h ieee80211_filter_flags
+    </chapter>
+  </part>
+
+  <part id="advanced">
+    <title>Advanced driver interface</title>
+    <partintro>
+      <para>
+       Information contained within this part of the book is
+       of interest only for advanced interaction of mac80211
+       with drivers to exploit more hardware capabilities and
+       improve performance.
+      </para>
+    </partintro>
+
+    <chapter id="hardware-crypto-offload">
+      <title>Hardware crypto acceleration</title>
+!Pinclude/net/mac80211.h Hardware crypto acceleration
+<!-- intentionally multiple !F lines to get proper order -->
+!Finclude/net/mac80211.h set_key_cmd
+!Finclude/net/mac80211.h ieee80211_key_conf
+!Finclude/net/mac80211.h ieee80211_key_alg
+!Finclude/net/mac80211.h ieee80211_key_flags
+    </chapter>
+
+    <chapter id="qos">
+      <title>Multiple queues and QoS support</title>
+      <para>TBD</para>
+!Finclude/net/mac80211.h ieee80211_tx_queue_params
+!Finclude/net/mac80211.h ieee80211_tx_queue_stats_data
+!Finclude/net/mac80211.h ieee80211_tx_queue
+    </chapter>
+
+    <chapter id="AP">
+      <title>Access point mode support</title>
+      <para>TBD</para>
+      <para>Some parts of the if_conf should be discussed here instead</para>
+      <para>
+        Insert notes about VLAN interfaces with hw crypto here or
+        in the hw crypto chapter.
+      </para>
+!Finclude/net/mac80211.h ieee80211_get_buffered_bc
+!Finclude/net/mac80211.h ieee80211_beacon_get
+    </chapter>
+
+    <chapter id="multi-iface">
+      <title>Supporting multiple virtual interfaces</title>
+      <para>TBD</para>
+      <para>
+        Note: WDS with identical MAC address should almost always be OK
+      </para>
+      <para>
+        Insert notes about having multiple virtual interfaces with
+        different MAC addresses here, note which configurations are
+        supported by mac80211, add notes about supporting hw crypto
+        with it.
+      </para>
+    </chapter>
+
+    <chapter id="hardware-scan-offload">
+      <title>Hardware scan offload</title>
+      <para>TBD</para>
+!Finclude/net/mac80211.h ieee80211_scan_completed
+    </chapter>
+  </part>
+
+  <part id="rate-control">
+    <title>Rate control interface</title>
+    <partintro>
+      <para>TBD</para>
+      <para>
+       This part of the book describes the rate control algorithm
+       interface and how it relates to mac80211 and drivers.
+      </para>
+    </partintro>
+    <chapter id="dummy">
+      <title>dummy chapter</title>
+      <para>TBD</para>
+    </chapter>
+  </part>
+
+  <part id="internal">
+    <title>Internals</title>
+    <partintro>
+      <para>TBD</para>
+      <para>
+       This part of the book describes mac80211 internals.
+      </para>
+    </partintro>
+
+    <chapter id="key-handling">
+      <title>Key handling</title>
+      <sect1>
+        <title>Key handling basics</title>
+!Pnet/mac80211/key.c Key handling basics
+      </sect1>
+      <sect1>
+        <title>MORE TBD</title>
+        <para>TBD</para>
+      </sect1>
+    </chapter>
+
+    <chapter id="rx-processing">
+      <title>Receive processing</title>
+      <para>TBD</para>
+    </chapter>
+
+    <chapter id="tx-processing">
+      <title>Transmit processing</title>
+      <para>TBD</para>
+    </chapter>
+
+    <chapter id="sta-info">
+      <title>Station info handling</title>
+      <sect1>
+        <title>Programming information</title>
+!Fnet/mac80211/sta_info.h sta_info
+!Fnet/mac80211/sta_info.h ieee80211_sta_info_flags
+      </sect1>
+      <sect1>
+        <title>STA information lifetime rules</title>
+!Pnet/mac80211/sta_info.c STA information lifetime rules
+      </sect1>
+    </chapter>
+
+    <chapter id="synchronisation">
+      <title>Synchronisation</title>
+      <para>TBD</para>
+      <para>Locking, lots of RCU</para>
+    </chapter>
+  </part>
+</book>

+ 27 - 26
drivers/net/wireless/ath5k/ath5k.h

@@ -140,7 +140,8 @@ enum ath5k_radio {
 	AR5K_RF5110	= 0,
 	AR5K_RF5110	= 0,
 	AR5K_RF5111	= 1,
 	AR5K_RF5111	= 1,
 	AR5K_RF5112	= 2,
 	AR5K_RF5112	= 2,
-	AR5K_RF5413	= 3,
+	AR5K_RF2413	= 3,
+	AR5K_RF5413	= 4,
 };
 };
 
 
 /*
 /*
@@ -168,12 +169,15 @@ struct ath5k_srev_name {
 #define AR5K_SREV_VER_AR5212	0x50
 #define AR5K_SREV_VER_AR5212	0x50
 #define AR5K_SREV_VER_AR5213	0x55
 #define AR5K_SREV_VER_AR5213	0x55
 #define AR5K_SREV_VER_AR5213A	0x59
 #define AR5K_SREV_VER_AR5213A	0x59
-#define AR5K_SREV_VER_AR2424	0xa0
-#define AR5K_SREV_VER_AR5424	0xa3
+#define AR5K_SREV_VER_AR2413	0x78
+#define AR5K_SREV_VER_AR2414	0x79
+#define AR5K_SREV_VER_AR2424	0xa0 /* PCI-E */
+#define AR5K_SREV_VER_AR5424	0xa3 /* PCI-E */
 #define AR5K_SREV_VER_AR5413	0xa4
 #define AR5K_SREV_VER_AR5413	0xa4
 #define AR5K_SREV_VER_AR5414	0xa5
 #define AR5K_SREV_VER_AR5414	0xa5
-#define AR5K_SREV_VER_AR5416	0xc0	/* ? */
-#define AR5K_SREV_VER_AR5418	0xca
+#define AR5K_SREV_VER_AR5416	0xc0 /* PCI-E */
+#define AR5K_SREV_VER_AR5418	0xca /* PCI-E */
+#define AR5K_SREV_VER_AR2425	0xe2 /* PCI-E */
 
 
 #define AR5K_SREV_RAD_5110	0x00
 #define AR5K_SREV_RAD_5110	0x00
 #define AR5K_SREV_RAD_5111	0x10
 #define AR5K_SREV_RAD_5111	0x10
@@ -183,8 +187,9 @@ struct ath5k_srev_name {
 #define AR5K_SREV_RAD_5112A	0x35
 #define AR5K_SREV_RAD_5112A	0x35
 #define AR5K_SREV_RAD_2112	0x40
 #define AR5K_SREV_RAD_2112	0x40
 #define AR5K_SREV_RAD_2112A	0x45
 #define AR5K_SREV_RAD_2112A	0x45
+#define AR5K_SREV_RAD_SC0	0x56	/* Found on 2413/2414 */
 #define AR5K_SREV_RAD_SC1	0x63	/* Found on 5413/5414 */
 #define AR5K_SREV_RAD_SC1	0x63	/* Found on 5413/5414 */
-#define AR5K_SREV_RAD_SC2	0xa2	/* Found on 2424/5424 */
+#define AR5K_SREV_RAD_SC2	0xa2	/* Found on 2424-5/5424 */
 #define AR5K_SREV_RAD_5133	0xc0	/* MIMO found on 5418 */
 #define AR5K_SREV_RAD_5133	0xc0	/* MIMO found on 5418 */
 
 
 /* IEEE defs */
 /* IEEE defs */
@@ -268,12 +273,13 @@ enum ath5k_driver_mode {
 #define SHPREAMBLE_FLAG(_ix) \
 #define SHPREAMBLE_FLAG(_ix) \
 	(HAS_SHPREAMBLE(_ix) ? AR5K_SET_SHORT_PREAMBLE : 0)
 	(HAS_SHPREAMBLE(_ix) ? AR5K_SET_SHORT_PREAMBLE : 0)
 
 
+
 /****************\
 /****************\
   TX DEFINITIONS
   TX DEFINITIONS
 \****************/
 \****************/
 
 
 /*
 /*
- * Tx Descriptor
+ * TX Status
  */
  */
 struct ath5k_tx_status {
 struct ath5k_tx_status {
 	u16	ts_seqnum;
 	u16	ts_seqnum;
@@ -421,7 +427,7 @@ enum ath5k_dmasize {
 \****************/
 \****************/
 
 
 /*
 /*
- * Rx Descriptor
+ * RX Status
  */
  */
 struct ath5k_rx_status {
 struct ath5k_rx_status {
 	u16	rs_datalen;
 	u16	rs_datalen;
@@ -452,8 +458,6 @@ struct ath5k_mib_stats {
 };
 };
 
 
 
 
-
-
 /**************************\
 /**************************\
  BEACON TIMERS DEFINITIONS
  BEACON TIMERS DEFINITIONS
 \**************************/
 \**************************/
@@ -495,29 +499,23 @@ struct ath5k_beacon_state {
 #define TSF_TO_TU(_tsf) (u32)((_tsf) >> 10)
 #define TSF_TO_TU(_tsf) (u32)((_tsf) >> 10)
 
 
 
 
-
 /********************\
 /********************\
   COMMON DEFINITIONS
   COMMON DEFINITIONS
 \********************/
 \********************/
 
 
 /*
 /*
- * Atheros descriptor
+ * Atheros hardware descriptor
+ * This is read and written to by the hardware
  */
  */
 struct ath5k_desc {
 struct ath5k_desc {
-	u32	ds_link;
-	u32	ds_data;
-	u32	ds_ctl0;
-	u32	ds_ctl1;
-	u32	ds_hw[4];
+	u32	ds_link;	/* physical address of the next descriptor */
+	u32	ds_data;	/* physical address of data buffer (skb) */
 
 
 	union {
 	union {
-		struct ath5k_rx_status rx;
-		struct ath5k_tx_status tx;
-	} ds_us;
-
-#define ds_rxstat ds_us.rx
-#define ds_txstat ds_us.tx
-
+		struct ath5k_hw_5210_tx_desc	ds_tx5210;
+		struct ath5k_hw_5212_tx_desc	ds_tx5212;
+		struct ath5k_hw_all_rx_desc	ds_rx;
+	} ud;
 } __packed;
 } __packed;
 
 
 #define AR5K_RXDESC_INTREQ	0x0020
 #define AR5K_RXDESC_INTREQ	0x0020
@@ -961,6 +959,7 @@ struct ath5k_hw {
 	u16			ah_phy_revision;
 	u16			ah_phy_revision;
 	u16			ah_radio_5ghz_revision;
 	u16			ah_radio_5ghz_revision;
 	u16			ah_radio_2ghz_revision;
 	u16			ah_radio_2ghz_revision;
+	u32			ah_phy_spending;
 
 
 	enum ath5k_version	ah_version;
 	enum ath5k_version	ah_version;
 	enum ath5k_radio	ah_radio;
 	enum ath5k_radio	ah_radio;
@@ -1036,8 +1035,10 @@ struct ath5k_hw {
 	int (*ah_setup_xtx_desc)(struct ath5k_hw *, struct ath5k_desc *,
 	int (*ah_setup_xtx_desc)(struct ath5k_hw *, struct ath5k_desc *,
 		unsigned int, unsigned int, unsigned int, unsigned int,
 		unsigned int, unsigned int, unsigned int, unsigned int,
 		unsigned int, unsigned int);
 		unsigned int, unsigned int);
-	int (*ah_proc_tx_desc)(struct ath5k_hw *, struct ath5k_desc *);
-	int (*ah_proc_rx_desc)(struct ath5k_hw *, struct ath5k_desc *);
+	int (*ah_proc_tx_desc)(struct ath5k_hw *, struct ath5k_desc *,
+		struct ath5k_tx_status *);
+	int (*ah_proc_rx_desc)(struct ath5k_hw *, struct ath5k_desc *,
+		struct ath5k_rx_status *);
 };
 };
 
 
 /*
 /*

+ 94 - 53
drivers/net/wireless/ath5k/base.c

@@ -118,6 +118,8 @@ static struct ath5k_srev_name srev_names[] = {
 	{ "5212",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5212 },
 	{ "5212",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5212 },
 	{ "5213",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5213 },
 	{ "5213",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5213 },
 	{ "5213A",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5213A },
 	{ "5213A",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5213A },
+	{ "2413",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR2413 },
+	{ "2414",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR2414 },
 	{ "2424",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR2424 },
 	{ "2424",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR2424 },
 	{ "5424",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5424 },
 	{ "5424",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5424 },
 	{ "5413",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5413 },
 	{ "5413",	AR5K_VERSION_VER,	AR5K_SREV_VER_AR5413 },
@@ -132,6 +134,7 @@ static struct ath5k_srev_name srev_names[] = {
 	{ "5112A",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5112A },
 	{ "5112A",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5112A },
 	{ "2112",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2112 },
 	{ "2112",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2112 },
 	{ "2112A",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2112A },
 	{ "2112A",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_2112A },
+	{ "SChip",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_SC0 },
 	{ "SChip",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_SC1 },
 	{ "SChip",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_SC1 },
 	{ "SChip",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_SC2 },
 	{ "SChip",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_SC2 },
 	{ "5133",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5133 },
 	{ "5133",	AR5K_VERSION_RAD,	AR5K_SREV_RAD_5133 },
@@ -280,7 +283,8 @@ static int 	ath5k_rx_start(struct ath5k_softc *sc);
 static void 	ath5k_rx_stop(struct ath5k_softc *sc);
 static void 	ath5k_rx_stop(struct ath5k_softc *sc);
 static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc,
 static unsigned int ath5k_rx_decrypted(struct ath5k_softc *sc,
 					struct ath5k_desc *ds,
 					struct ath5k_desc *ds,
-					struct sk_buff *skb);
+					struct sk_buff *skb,
+					struct ath5k_rx_status *rs);
 static void 	ath5k_tasklet_rx(unsigned long data);
 static void 	ath5k_tasklet_rx(unsigned long data);
 /* Tx handling */
 /* Tx handling */
 static void 	ath5k_tx_processq(struct ath5k_softc *sc,
 static void 	ath5k_tx_processq(struct ath5k_softc *sc,
@@ -1560,8 +1564,7 @@ ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 	 */
 	 */
 	spin_lock_bh(&txq->lock);
 	spin_lock_bh(&txq->lock);
 	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
 	list_for_each_entry_safe(bf, bf0, &txq->q, list) {
-		ath5k_debug_printtxbuf(sc, bf, !sc->ah->ah_proc_tx_desc(sc->ah,
-					bf->desc));
+		ath5k_debug_printtxbuf(sc, bf);
 
 
 		ath5k_txbuf_free(sc, bf);
 		ath5k_txbuf_free(sc, bf);
 
 
@@ -1686,20 +1689,20 @@ ath5k_rx_stop(struct ath5k_softc *sc)
 
 
 static unsigned int
 static unsigned int
 ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
 ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
-		struct sk_buff *skb)
+		struct sk_buff *skb, struct ath5k_rx_status *rs)
 {
 {
 	struct ieee80211_hdr *hdr = (void *)skb->data;
 	struct ieee80211_hdr *hdr = (void *)skb->data;
 	unsigned int keyix, hlen = ieee80211_get_hdrlen_from_skb(skb);
 	unsigned int keyix, hlen = ieee80211_get_hdrlen_from_skb(skb);
 
 
-	if (!(ds->ds_rxstat.rs_status & AR5K_RXERR_DECRYPT) &&
-			ds->ds_rxstat.rs_keyix != AR5K_RXKEYIX_INVALID)
+	if (!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
+			rs->rs_keyix != AR5K_RXKEYIX_INVALID)
 		return RX_FLAG_DECRYPTED;
 		return RX_FLAG_DECRYPTED;
 
 
 	/* Apparently when a default key is used to decrypt the packet
 	/* Apparently when a default key is used to decrypt the packet
 	   the hw does not set the index used to decrypt.  In such cases
 	   the hw does not set the index used to decrypt.  In such cases
 	   get the index from the packet. */
 	   get the index from the packet. */
 	if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED) &&
 	if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED) &&
-			!(ds->ds_rxstat.rs_status & AR5K_RXERR_DECRYPT) &&
+			!(rs->rs_status & AR5K_RXERR_DECRYPT) &&
 			skb->len >= hlen + 4) {
 			skb->len >= hlen + 4) {
 		keyix = skb->data[hlen + 3] >> 6;
 		keyix = skb->data[hlen + 3] >> 6;
 
 
@@ -1712,8 +1715,10 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
 
 
 
 
 static void
 static void
-ath5k_check_ibss_hw_merge(struct ath5k_softc *sc, struct sk_buff *skb)
+ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
+		     struct ieee80211_rx_status *rxs)
 {
 {
+	u64 tsf, bc_tstamp;
 	u32 hw_tu;
 	u32 hw_tu;
 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
 
 
@@ -1724,16 +1729,45 @@ ath5k_check_ibss_hw_merge(struct ath5k_softc *sc, struct sk_buff *skb)
 	    le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
 	    le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
 	    memcmp(mgmt->bssid, sc->ah->ah_bssid, ETH_ALEN) == 0) {
 	    memcmp(mgmt->bssid, sc->ah->ah_bssid, ETH_ALEN) == 0) {
 		/*
 		/*
-		 * Received an IBSS beacon with the same BSSID. Hardware might
-		 * have updated the TSF, check if we need to update timers.
+		 * Received an IBSS beacon with the same BSSID. Hardware *must*
+		 * have updated the local TSF. We have to work around various
+		 * hardware bugs, though...
 		 */
 		 */
-		hw_tu = TSF_TO_TU(ath5k_hw_get_tsf64(sc->ah));
-		if (hw_tu >= sc->nexttbtt) {
-			ath5k_beacon_update_timers(sc,
-				le64_to_cpu(mgmt->u.beacon.timestamp));
+		tsf = ath5k_hw_get_tsf64(sc->ah);
+		bc_tstamp = le64_to_cpu(mgmt->u.beacon.timestamp);
+		hw_tu = TSF_TO_TU(tsf);
+
+		ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+			"beacon %llx mactime %llx (diff %lld) tsf now %llx\n",
+			bc_tstamp, rxs->mactime,
+			(rxs->mactime - bc_tstamp), tsf);
+
+		/*
+		 * Sometimes the HW will give us a wrong tstamp in the rx
+		 * status, causing the timestamp extension to go wrong.
+		 * (This seems to happen especially with beacon frames bigger
+		 * than 78 byte (incl. FCS))
+		 * But we know that the receive timestamp must be later than the
+		 * timestamp of the beacon since HW must have synced to that.
+		 *
+		 * NOTE: here we assume mactime to be after the frame was
+		 * received, not like mac80211 which defines it at the start.
+		 */
+		if (bc_tstamp > rxs->mactime) {
 			ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
 			ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
-				"detected HW merge from received beacon\n");
+				"fixing mactime from %llx to %llx\n",
+				rxs->mactime, tsf);
+			rxs->mactime = tsf;
 		}
 		}
+
+		/*
+		 * Local TSF might have moved higher than our beacon timers,
+		 * in that case we have to update them to continue sending
+		 * beacons. This also takes care of synchronizing beacon sending
+		 * times with other stations.
+		 */
+		if (hw_tu >= sc->nexttbtt)
+			ath5k_beacon_update_timers(sc, bc_tstamp);
 	}
 	}
 }
 }
 
 
@@ -1742,12 +1776,11 @@ static void
 ath5k_tasklet_rx(unsigned long data)
 ath5k_tasklet_rx(unsigned long data)
 {
 {
 	struct ieee80211_rx_status rxs = {};
 	struct ieee80211_rx_status rxs = {};
+	struct ath5k_rx_status rs = {};
 	struct sk_buff *skb;
 	struct sk_buff *skb;
 	struct ath5k_softc *sc = (void *)data;
 	struct ath5k_softc *sc = (void *)data;
 	struct ath5k_buf *bf;
 	struct ath5k_buf *bf;
 	struct ath5k_desc *ds;
 	struct ath5k_desc *ds;
-	u16 len;
-	u8 stat;
 	int ret;
 	int ret;
 	int hdrlen;
 	int hdrlen;
 	int pad;
 	int pad;
@@ -1770,7 +1803,7 @@ ath5k_tasklet_rx(unsigned long data)
 		if (unlikely(ds->ds_link == bf->daddr)) /* this is the end */
 		if (unlikely(ds->ds_link == bf->daddr)) /* this is the end */
 			break;
 			break;
 
 
-		ret = sc->ah->ah_proc_rx_desc(sc->ah, ds);
+		ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs);
 		if (unlikely(ret == -EINPROGRESS))
 		if (unlikely(ret == -EINPROGRESS))
 			break;
 			break;
 		else if (unlikely(ret)) {
 		else if (unlikely(ret)) {
@@ -1779,16 +1812,15 @@ ath5k_tasklet_rx(unsigned long data)
 			return;
 			return;
 		}
 		}
 
 
-		if (unlikely(ds->ds_rxstat.rs_more)) {
+		if (unlikely(rs.rs_more)) {
 			ATH5K_WARN(sc, "unsupported jumbo\n");
 			ATH5K_WARN(sc, "unsupported jumbo\n");
 			goto next;
 			goto next;
 		}
 		}
 
 
-		stat = ds->ds_rxstat.rs_status;
-		if (unlikely(stat)) {
-			if (stat & AR5K_RXERR_PHY)
+		if (unlikely(rs.rs_status)) {
+			if (rs.rs_status & AR5K_RXERR_PHY)
 				goto next;
 				goto next;
-			if (stat & AR5K_RXERR_DECRYPT) {
+			if (rs.rs_status & AR5K_RXERR_DECRYPT) {
 				/*
 				/*
 				 * Decrypt error.  If the error occurred
 				 * Decrypt error.  If the error occurred
 				 * because there was no hardware key, then
 				 * because there was no hardware key, then
@@ -1799,30 +1831,29 @@ ath5k_tasklet_rx(unsigned long data)
 				 *
 				 *
 				 * XXX do key cache faulting
 				 * XXX do key cache faulting
 				 */
 				 */
-				if (ds->ds_rxstat.rs_keyix ==
-						AR5K_RXKEYIX_INVALID &&
-						!(stat & AR5K_RXERR_CRC))
+				if (rs.rs_keyix == AR5K_RXKEYIX_INVALID &&
+				    !(rs.rs_status & AR5K_RXERR_CRC))
 					goto accept;
 					goto accept;
 			}
 			}
-			if (stat & AR5K_RXERR_MIC) {
+			if (rs.rs_status & AR5K_RXERR_MIC) {
 				rxs.flag |= RX_FLAG_MMIC_ERROR;
 				rxs.flag |= RX_FLAG_MMIC_ERROR;
 				goto accept;
 				goto accept;
 			}
 			}
 
 
 			/* let crypto-error packets fall through in MNTR */
 			/* let crypto-error packets fall through in MNTR */
-			if ((stat & ~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) ||
+			if ((rs.rs_status &
+				~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) ||
 					sc->opmode != IEEE80211_IF_TYPE_MNTR)
 					sc->opmode != IEEE80211_IF_TYPE_MNTR)
 				goto next;
 				goto next;
 		}
 		}
 accept:
 accept:
-		len = ds->ds_rxstat.rs_datalen;
-		pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr, len,
-				PCI_DMA_FROMDEVICE);
+		pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr,
+				rs.rs_datalen, PCI_DMA_FROMDEVICE);
 		pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize,
 		pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize,
 				PCI_DMA_FROMDEVICE);
 				PCI_DMA_FROMDEVICE);
 		bf->skb = NULL;
 		bf->skb = NULL;
 
 
-		skb_put(skb, len);
+		skb_put(skb, rs.rs_datalen);
 
 
 		/*
 		/*
 		 * the hardware adds a padding to 4 byte boundaries between
 		 * the hardware adds a padding to 4 byte boundaries between
@@ -1844,8 +1875,19 @@ accept:
 		 * 15bit only. that means TSF extension has to be done within
 		 * 15bit only. that means TSF extension has to be done within
 		 * 32768usec (about 32ms). it might be necessary to move this to
 		 * 32768usec (about 32ms). it might be necessary to move this to
 		 * the interrupt handler, like it is done in madwifi.
 		 * the interrupt handler, like it is done in madwifi.
+		 *
+		 * Unfortunately we don't know when the hardware takes the rx
+		 * timestamp (beginning of phy frame, data frame, end of rx?).
+		 * The only thing we know is that it is hardware specific...
+		 * On AR5213 it seems the rx timestamp is at the end of the
+		 * frame, but i'm not sure.
+		 *
+		 * NOTE: mac80211 defines mactime at the beginning of the first
+		 * data symbol. Since we don't have any time references it's
+		 * impossible to comply to that. This affects IBSS merge only
+		 * right now, so it's not too bad...
 		 */
 		 */
-		rxs.mactime = ath5k_extend_tsf(sc->ah, ds->ds_rxstat.rs_tstamp);
+		rxs.mactime = ath5k_extend_tsf(sc->ah, rs.rs_tstamp);
 		rxs.flag |= RX_FLAG_TSFT;
 		rxs.flag |= RX_FLAG_TSFT;
 
 
 		rxs.freq = sc->curchan->center_freq;
 		rxs.freq = sc->curchan->center_freq;
@@ -1859,26 +1901,25 @@ accept:
 		/* noise floor in dBm, from the last noise calibration */
 		/* noise floor in dBm, from the last noise calibration */
 		rxs.noise = sc->ah->ah_noise_floor;
 		rxs.noise = sc->ah->ah_noise_floor;
 		/* signal level in dBm */
 		/* signal level in dBm */
-		rxs.ssi = rxs.noise + ds->ds_rxstat.rs_rssi;
+		rxs.ssi = rxs.noise + rs.rs_rssi;
 		/*
 		/*
 		 * "signal" is actually displayed as Link Quality by iwconfig
 		 * "signal" is actually displayed as Link Quality by iwconfig
 		 * we provide a percentage based on rssi (assuming max rssi 64)
 		 * we provide a percentage based on rssi (assuming max rssi 64)
 		 */
 		 */
-		rxs.signal = ds->ds_rxstat.rs_rssi * 100 / 64;
+		rxs.signal = rs.rs_rssi * 100 / 64;
 
 
-		rxs.antenna = ds->ds_rxstat.rs_antenna;
-		rxs.rate_idx = ath5k_hw_to_driver_rix(sc,
-			ds->ds_rxstat.rs_rate);
-		rxs.flag |= ath5k_rx_decrypted(sc, ds, skb);
+		rxs.antenna = rs.rs_antenna;
+		rxs.rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate);
+		rxs.flag |= ath5k_rx_decrypted(sc, ds, skb, &rs);
 
 
 		ath5k_debug_dump_skb(sc, skb, "RX  ", 0);
 		ath5k_debug_dump_skb(sc, skb, "RX  ", 0);
 
 
 		/* check beacons in IBSS mode */
 		/* check beacons in IBSS mode */
 		if (sc->opmode == IEEE80211_IF_TYPE_IBSS)
 		if (sc->opmode == IEEE80211_IF_TYPE_IBSS)
-			ath5k_check_ibss_hw_merge(sc, skb);
+			ath5k_check_ibss_tsf(sc, skb, &rxs);
 
 
 		__ieee80211_rx(sc->hw, skb, &rxs);
 		__ieee80211_rx(sc->hw, skb, &rxs);
-		sc->led_rxrate = ds->ds_rxstat.rs_rate;
+		sc->led_rxrate = rs.rs_rate;
 		ath5k_led_event(sc, ATH_LED_RX);
 		ath5k_led_event(sc, ATH_LED_RX);
 next:
 next:
 		list_move_tail(&bf->list, &sc->rxbuf);
 		list_move_tail(&bf->list, &sc->rxbuf);
@@ -1897,6 +1938,7 @@ static void
 ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 {
 {
 	struct ieee80211_tx_status txs = {};
 	struct ieee80211_tx_status txs = {};
+	struct ath5k_tx_status ts = {};
 	struct ath5k_buf *bf, *bf0;
 	struct ath5k_buf *bf, *bf0;
 	struct ath5k_desc *ds;
 	struct ath5k_desc *ds;
 	struct sk_buff *skb;
 	struct sk_buff *skb;
@@ -1909,7 +1951,7 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 		/* TODO only one segment */
 		/* TODO only one segment */
 		pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr,
 		pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr,
 				sc->desc_len, PCI_DMA_FROMDEVICE);
 				sc->desc_len, PCI_DMA_FROMDEVICE);
-		ret = sc->ah->ah_proc_tx_desc(sc->ah, ds);
+		ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts);
 		if (unlikely(ret == -EINPROGRESS))
 		if (unlikely(ret == -EINPROGRESS))
 			break;
 			break;
 		else if (unlikely(ret)) {
 		else if (unlikely(ret)) {
@@ -1924,17 +1966,16 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 				PCI_DMA_TODEVICE);
 				PCI_DMA_TODEVICE);
 
 
 		txs.control = bf->ctl;
 		txs.control = bf->ctl;
-		txs.retry_count = ds->ds_txstat.ts_shortretry +
-			ds->ds_txstat.ts_longretry / 6;
-		if (unlikely(ds->ds_txstat.ts_status)) {
+		txs.retry_count = ts.ts_shortretry + ts.ts_longretry / 6;
+		if (unlikely(ts.ts_status)) {
 			sc->ll_stats.dot11ACKFailureCount++;
 			sc->ll_stats.dot11ACKFailureCount++;
-			if (ds->ds_txstat.ts_status & AR5K_TXERR_XRETRY)
+			if (ts.ts_status & AR5K_TXERR_XRETRY)
 				txs.excessive_retries = 1;
 				txs.excessive_retries = 1;
-			else if (ds->ds_txstat.ts_status & AR5K_TXERR_FILT)
+			else if (ts.ts_status & AR5K_TXERR_FILT)
 				txs.flags |= IEEE80211_TX_STATUS_TX_FILTERED;
 				txs.flags |= IEEE80211_TX_STATUS_TX_FILTERED;
 		} else {
 		} else {
 			txs.flags |= IEEE80211_TX_STATUS_ACK;
 			txs.flags |= IEEE80211_TX_STATUS_ACK;
-			txs.ack_signal = ds->ds_txstat.ts_rssi;
+			txs.ack_signal = ts.ts_rssi;
 		}
 		}
 
 
 		ieee80211_tx_status(sc->hw, skb, &txs);
 		ieee80211_tx_status(sc->hw, skb, &txs);
@@ -2108,7 +2149,7 @@ ath5k_beacon_send(struct ath5k_softc *sc)
  * beacon timer registers.
  * beacon timer registers.
  *
  *
  * This is called in a variety of situations, e.g. when a beacon is received,
  * This is called in a variety of situations, e.g. when a beacon is received,
- * when a HW merge has been detected, but also when an new IBSS is created or
+ * when a TSF update has been detected, but also when an new IBSS is created or
  * when we otherwise know we have to update the timers, but we keep it in this
  * when we otherwise know we have to update the timers, but we keep it in this
  * function to have it all together in one place.
  * function to have it all together in one place.
  */
  */
@@ -2208,7 +2249,7 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
  * another AP to associate with.
  * another AP to associate with.
  *
  *
  * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
  * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
- * interrupts to detect HW merges only.
+ * interrupts to detect TSF updates only.
  *
  *
  * AP mode is missing.
  * AP mode is missing.
  */
  */
@@ -2228,7 +2269,7 @@ ath5k_beacon_config(struct ath5k_softc *sc)
 		 * hardware send the beacons automatically. We have to load it
 		 * hardware send the beacons automatically. We have to load it
 		 * only once here.
 		 * only once here.
 		 * We use the SWBA interrupt only to keep track of the beacon
 		 * We use the SWBA interrupt only to keep track of the beacon
-		 * timers in order to detect HW merges (automatic TSF updates).
+		 * timers in order to detect automatic TSF updates.
 		 */
 		 */
 		ath5k_beaconq_config(sc);
 		ath5k_beaconq_config(sc);
 
 
@@ -2441,8 +2482,8 @@ ath5k_intr(int irq, void *dev_id)
 				*
 				*
 				* In IBSS mode we use this interrupt just to
 				* In IBSS mode we use this interrupt just to
 				* keep track of the next TBTT (target beacon
 				* keep track of the next TBTT (target beacon
-				* transmission time) in order to detect hardware
-				* merges (TSF updates).
+				* transmission time) in order to detect wether
+				* automatic TSF updates happened.
 				*/
 				*/
 				if (sc->opmode == IEEE80211_IF_TYPE_IBSS) {
 				if (sc->opmode == IEEE80211_IF_TYPE_IBSS) {
 					 /* XXX: only if VEOL suppported */
 					 /* XXX: only if VEOL suppported */

+ 24 - 13
drivers/net/wireless/ath5k/debug.c

@@ -200,7 +200,8 @@ static ssize_t read_file_tsf(struct file *file, char __user *user_buf,
 {
 {
 	struct ath5k_softc *sc = file->private_data;
 	struct ath5k_softc *sc = file->private_data;
 	char buf[100];
 	char buf[100];
-	snprintf(buf, sizeof(buf), "0x%016llx\n", ath5k_hw_get_tsf64(sc->ah));
+	snprintf(buf, sizeof(buf), "0x%016llx\n",
+		 (unsigned long long)ath5k_hw_get_tsf64(sc->ah));
 	return simple_read_from_buffer(user_buf, count, ppos, buf, 19);
 	return simple_read_from_buffer(user_buf, count, ppos, buf, 19);
 }
 }
 
 
@@ -271,7 +272,8 @@ static ssize_t read_file_beacon(struct file *file, char __user *user_buf,
 
 
 	tsf = ath5k_hw_get_tsf64(sc->ah);
 	tsf = ath5k_hw_get_tsf64(sc->ah);
 	len += snprintf(buf+len, sizeof(buf)-len,
 	len += snprintf(buf+len, sizeof(buf)-len,
-		"TSF\t\t0x%016llx\tTU: %08x\n", tsf, TSF_TO_TU(tsf));
+		"TSF\t\t0x%016llx\tTU: %08x\n",
+		(unsigned long long)tsf, TSF_TO_TU(tsf));
 
 
 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 }
 }
@@ -497,15 +499,18 @@ ath5k_debug_dump_bands(struct ath5k_softc *sc)
 }
 }
 
 
 static inline void
 static inline void
-ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done)
+ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done,
+		       struct ath5k_rx_status *rs)
 {
 {
 	struct ath5k_desc *ds = bf->desc;
 	struct ath5k_desc *ds = bf->desc;
+	struct ath5k_hw_all_rx_desc *rd = &ds->ud.ds_rx;
 
 
 	printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n",
 	printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n",
 		ds, (unsigned long long)bf->daddr,
 		ds, (unsigned long long)bf->daddr,
-		ds->ds_link, ds->ds_data, ds->ds_ctl0, ds->ds_ctl1,
-		ds->ds_hw[0], ds->ds_hw[1],
-		!done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!');
+		ds->ds_link, ds->ds_data,
+		rd->rx_ctl.rx_control_0, rd->rx_ctl.rx_control_1,
+		rd->u.rx_stat.rx_status_0, rd->u.rx_stat.rx_status_0,
+		!done ? ' ' : (rs->rs_status == 0) ? '*' : '!');
 }
 }
 
 
 void
 void
@@ -513,6 +518,7 @@ ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah)
 {
 {
 	struct ath5k_desc *ds;
 	struct ath5k_desc *ds;
 	struct ath5k_buf *bf;
 	struct ath5k_buf *bf;
+	struct ath5k_rx_status rs = {};
 	int status;
 	int status;
 
 
 	if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
 	if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
@@ -524,9 +530,9 @@ ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah)
 	spin_lock_bh(&sc->rxbuflock);
 	spin_lock_bh(&sc->rxbuflock);
 	list_for_each_entry(bf, &sc->rxbuf, list) {
 	list_for_each_entry(bf, &sc->rxbuf, list) {
 		ds = bf->desc;
 		ds = bf->desc;
-		status = ah->ah_proc_rx_desc(ah, ds);
+		status = ah->ah_proc_rx_desc(ah, ds, &rs);
 		if (!status)
 		if (!status)
-			ath5k_debug_printrxbuf(bf, status == 0);
+			ath5k_debug_printrxbuf(bf, status == 0, &rs);
 	}
 	}
 	spin_unlock_bh(&sc->rxbuflock);
 	spin_unlock_bh(&sc->rxbuflock);
 }
 }
@@ -550,19 +556,24 @@ ath5k_debug_dump_skb(struct ath5k_softc *sc,
 }
 }
 
 
 void
 void
-ath5k_debug_printtxbuf(struct ath5k_softc *sc,
-			struct ath5k_buf *bf, int done)
+ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf)
 {
 {
 	struct ath5k_desc *ds = bf->desc;
 	struct ath5k_desc *ds = bf->desc;
+	struct ath5k_hw_5212_tx_desc *td = &ds->ud.ds_tx5212;
+	struct ath5k_tx_status ts = {};
+	int done;
 
 
 	if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
 	if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
 		return;
 		return;
 
 
+	done = sc->ah->ah_proc_tx_desc(sc->ah, bf->desc, &ts);
+
 	printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x "
 	printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x "
 		"%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link,
 		"%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link,
-		ds->ds_data, ds->ds_ctl0, ds->ds_ctl1,
-		ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3],
-		!done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!');
+		ds->ds_data, td->tx_ctl.tx_control_0, td->tx_ctl.tx_control_1,
+		td->tx_ctl.tx_control_2, td->tx_ctl.tx_control_3,
+		td->tx_stat.tx_status_0, td->tx_stat.tx_status_1,
+		done ? ' ' : (ts.ts_status == 0) ? '*' : '!');
 }
 }
 
 
 #endif /* ifdef CONFIG_ATH5K_DEBUG */
 #endif /* ifdef CONFIG_ATH5K_DEBUG */

+ 2 - 4
drivers/net/wireless/ath5k/debug.h

@@ -160,8 +160,7 @@ ath5k_debug_dump_skb(struct ath5k_softc *sc,
 			struct sk_buff *skb, const char *prefix, int tx);
 			struct sk_buff *skb, const char *prefix, int tx);
 
 
 void
 void
-ath5k_debug_printtxbuf(struct ath5k_softc *sc,
-			struct ath5k_buf *bf, int done);
+ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf);
 
 
 #else /* no debugging */
 #else /* no debugging */
 
 
@@ -199,8 +198,7 @@ ath5k_debug_dump_skb(struct ath5k_softc *sc,
 			struct sk_buff *skb, const char *prefix, int tx) {}
 			struct sk_buff *skb, const char *prefix, int tx) {}
 
 
 static inline void
 static inline void
-ath5k_debug_printtxbuf(struct ath5k_softc *sc,
-			struct ath5k_buf *bf, int done) {}
+ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf) {}
 
 
 #endif /* ifdef CONFIG_ATH5K_DEBUG */
 #endif /* ifdef CONFIG_ATH5K_DEBUG */
 
 

+ 250 - 191
drivers/net/wireless/ath5k/hw.c

@@ -48,14 +48,18 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
 static int ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
 static int ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
 	unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
 	unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
 	unsigned int);
 	unsigned int);
-static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *, struct ath5k_desc *);
+static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *, struct ath5k_desc *,
+					 struct ath5k_tx_status *);
 static int ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
 static int ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *, struct ath5k_desc *,
 	unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int,
 	unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int,
 	unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
 	unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
 	unsigned int, unsigned int);
 	unsigned int, unsigned int);
-static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *, struct ath5k_desc *);
-static int ath5k_hw_proc_new_rx_status(struct ath5k_hw *, struct ath5k_desc *);
-static int ath5k_hw_proc_old_rx_status(struct ath5k_hw *, struct ath5k_desc *);
+static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *, struct ath5k_desc *,
+					 struct ath5k_tx_status *);
+static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *, struct ath5k_desc *,
+					struct ath5k_rx_status *);
+static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *, struct ath5k_desc *,
+					struct ath5k_rx_status *);
 static int ath5k_hw_get_capabilities(struct ath5k_hw *);
 static int ath5k_hw_get_capabilities(struct ath5k_hw *);
 
 
 static int ath5k_eeprom_init(struct ath5k_hw *);
 static int ath5k_eeprom_init(struct ath5k_hw *);
@@ -174,9 +178,9 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
 	}
 	}
 
 
 	if (ah->ah_version == AR5K_AR5212)
 	if (ah->ah_version == AR5K_AR5212)
-		ah->ah_proc_rx_desc = ath5k_hw_proc_new_rx_status;
+		ah->ah_proc_rx_desc = ath5k_hw_proc_5212_rx_status;
 	else if (ah->ah_version <= AR5K_AR5211)
 	else if (ah->ah_version <= AR5K_AR5211)
-		ah->ah_proc_rx_desc = ath5k_hw_proc_old_rx_status;
+		ah->ah_proc_rx_desc = ath5k_hw_proc_5210_rx_status;
 
 
 	/* Bring device out of sleep and reset it's units */
 	/* Bring device out of sleep and reset it's units */
 	ret = ath5k_hw_nic_wakeup(ah, AR5K_INIT_MODE, true);
 	ret = ath5k_hw_nic_wakeup(ah, AR5K_INIT_MODE, true);
@@ -208,7 +212,7 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
 
 
 	/* Identify single chip solutions */
 	/* Identify single chip solutions */
 	if((srev <= AR5K_SREV_VER_AR5414) &&
 	if((srev <= AR5K_SREV_VER_AR5414) &&
-	(srev >= AR5K_SREV_VER_AR2424)) {
+	(srev >= AR5K_SREV_VER_AR2413)) {
 		ah->ah_single_chip = true;
 		ah->ah_single_chip = true;
 	} else {
 	} else {
 		ah->ah_single_chip = false;
 		ah->ah_single_chip = false;
@@ -223,10 +227,33 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
 		ah->ah_radio = AR5K_RF5110;
 		ah->ah_radio = AR5K_RF5110;
 	} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112) {
 	} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112) {
 		ah->ah_radio = AR5K_RF5111;
 		ah->ah_radio = AR5K_RF5111;
-	} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC1) {
+		ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5111;
+	} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC0) {
+
 		ah->ah_radio = AR5K_RF5112;
 		ah->ah_radio = AR5K_RF5112;
+
+		if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) {
+			ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112;
+		} else {
+			ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A;
+		}
+
+	} else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC1) {
+		ah->ah_radio = AR5K_RF2413;
+		ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A;
 	} else {
 	} else {
+
 		ah->ah_radio = AR5K_RF5413;
 		ah->ah_radio = AR5K_RF5413;
+
+		if (ah->ah_mac_srev <= AR5K_SREV_VER_AR5424 &&
+			ah->ah_mac_srev >= AR5K_SREV_VER_AR2424)
+			ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5424;
+		else if (ah->ah_mac_srev >= AR5K_SREV_VER_AR2425)
+			ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112;
+		else
+			ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A;
+
+
 	}
 	}
 
 
 	ah->ah_phy = AR5K_PHY(0);
 	ah->ah_phy = AR5K_PHY(0);
@@ -277,7 +304,8 @@ err:
  */
  */
 static int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
 static int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
 {
 {
-	u32 turbo, mode, clock;
+	struct pci_dev *pdev = ah->ah_sc->pdev;
+	u32 turbo, mode, clock, bus_flags;
 	int ret;
 	int ret;
 
 
 	turbo = 0;
 	turbo = 0;
@@ -354,9 +382,15 @@ static int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
 					AR5K_PHY_TURBO);
 					AR5K_PHY_TURBO);
 	}
 	}
 
 
-	/* ...reset chipset and PCI device */
-	if (ah->ah_single_chip == false && ath5k_hw_nic_reset(ah,
-				AR5K_RESET_CTL_CHIP | AR5K_RESET_CTL_PCI)) {
+	/* reseting PCI on PCI-E cards results card to hang
+	 * and always return 0xffff... so we ingore that flag
+	 * for PCI-E cards */
+	bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI;
+
+	/* Reset chipset */
+	ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
+		AR5K_RESET_CTL_BASEBAND | bus_flags);
+	if (ret) {
 		ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip + PCI\n");
 		ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip + PCI\n");
 		return -EIO;
 		return -EIO;
 	}
 	}
@@ -565,7 +599,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
 	struct ieee80211_channel *channel, bool change_channel)
 	struct ieee80211_channel *channel, bool change_channel)
 {
 {
 	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
 	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
-	u32 data, s_seq, s_ant, s_led[3];
+	struct pci_dev *pdev = ah->ah_sc->pdev;
+	u32 data, s_seq, s_ant, s_led[3], dma_size;
 	unsigned int i, mode, freq, ee_mode, ant[2];
 	unsigned int i, mode, freq, ee_mode, ant[2];
 	int ret;
 	int ret;
 
 
@@ -617,7 +652,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
 	if (ah->ah_version != AR5K_AR5210) {
 	if (ah->ah_version != AR5K_AR5210) {
 		if (ah->ah_radio != AR5K_RF5111 &&
 		if (ah->ah_radio != AR5K_RF5111 &&
 			ah->ah_radio != AR5K_RF5112 &&
 			ah->ah_radio != AR5K_RF5112 &&
-			ah->ah_radio != AR5K_RF5413) {
+			ah->ah_radio != AR5K_RF5413 &&
+			ah->ah_radio != AR5K_RF2413) {
 			ATH5K_ERR(ah->ah_sc,
 			ATH5K_ERR(ah->ah_sc,
 				"invalid phy radio: %u\n", ah->ah_radio);
 				"invalid phy radio: %u\n", ah->ah_radio);
 			return -EINVAL;
 			return -EINVAL;
@@ -692,15 +728,26 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
 		/*
 		/*
 		 * Write some more initial register settings
 		 * Write some more initial register settings
 		 */
 		 */
-		if (ah->ah_version > AR5K_AR5211){ /* found on 5213+ */
+		if (ah->ah_version == AR5K_AR5212) {
 			ath5k_hw_reg_write(ah, 0x0002a002, AR5K_PHY(11));
 			ath5k_hw_reg_write(ah, 0x0002a002, AR5K_PHY(11));
 
 
 			if (channel->hw_value == CHANNEL_G)
 			if (channel->hw_value == CHANNEL_G)
-				ath5k_hw_reg_write(ah, 0x00f80d80, AR5K_PHY(83)); /* 0x00fc0ec0 */
+				if (ah->ah_mac_srev < AR5K_SREV_VER_AR2413)
+					ath5k_hw_reg_write(ah, 0x00f80d80,
+						AR5K_PHY(83));
+				else if (ah->ah_mac_srev < AR5K_SREV_VER_AR2424)
+					ath5k_hw_reg_write(ah, 0x00380140,
+						AR5K_PHY(83));
+				else if (ah->ah_mac_srev < AR5K_SREV_VER_AR2425)
+					ath5k_hw_reg_write(ah, 0x00fc0ec0,
+						AR5K_PHY(83));
+				else /* 2425 */
+					ath5k_hw_reg_write(ah, 0x00fc0fc0,
+						AR5K_PHY(83));
 			else
 			else
-				ath5k_hw_reg_write(ah, 0x00000000, AR5K_PHY(83));
+				ath5k_hw_reg_write(ah, 0x00000000,
+					AR5K_PHY(83));
 
 
-			ath5k_hw_reg_write(ah, 0x000001b5, 0xa228); /* 0x000009b5 */
 			ath5k_hw_reg_write(ah, 0x000009b5, 0xa228);
 			ath5k_hw_reg_write(ah, 0x000009b5, 0xa228);
 			ath5k_hw_reg_write(ah, 0x0000000f, 0x8060);
 			ath5k_hw_reg_write(ah, 0x0000000f, 0x8060);
 			ath5k_hw_reg_write(ah, 0x00000000, 0xa254);
 			ath5k_hw_reg_write(ah, 0x00000000, 0xa254);
@@ -876,13 +923,24 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
 
 
 	/*
 	/*
 	 * Set Rx/Tx DMA Configuration
 	 * Set Rx/Tx DMA Configuration
-	 *(passing dma size not available on 5210)
+	 *
+	 * Set maximum DMA size (512) except for PCI-E cards since
+	 * it causes rx overruns and tx errors (tested on 5424 but since
+	 * rx overruns also occur on 5416/5418 with madwifi we set 128
+	 * for all PCI-E cards to be safe).
+	 *
+	 * In dumps this is 128 for allchips.
+	 *
+	 * XXX: need to check 5210 for this
+	 * TODO: Check out tx triger level, it's always 64 on dumps but I
+	 * guess we can tweak it and see how it goes ;-)
 	 */
 	 */
+	dma_size = (pdev->is_pcie) ? AR5K_DMASIZE_128B : AR5K_DMASIZE_512B;
 	if (ah->ah_version != AR5K_AR5210) {
 	if (ah->ah_version != AR5K_AR5210) {
-		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_SDMAMR,
-				AR5K_DMASIZE_512B | AR5K_TXCFG_DMASIZE);
-		AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_SDMAMW,
-				AR5K_DMASIZE_512B);
+		AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
+			AR5K_TXCFG_SDMAMR, dma_size);
+		AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
+			AR5K_RXCFG_SDMAMW, dma_size);
 	}
 	}
 
 
 	/*
 	/*
@@ -972,6 +1030,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
 
 
 	/*
 	/*
 	 * Set the 32MHz reference clock on 5212 phy clock sleep register
 	 * Set the 32MHz reference clock on 5212 phy clock sleep register
+	 *
+	 * TODO: Find out how to switch to external 32Khz clock to save power
 	 */
 	 */
 	if (ah->ah_version == AR5K_AR5212) {
 	if (ah->ah_version == AR5K_AR5212) {
 		ath5k_hw_reg_write(ah, AR5K_PHY_SCR_32MHZ, AR5K_PHY_SCR);
 		ath5k_hw_reg_write(ah, AR5K_PHY_SCR_32MHZ, AR5K_PHY_SCR);
@@ -979,9 +1039,15 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode,
 		ath5k_hw_reg_write(ah, AR5K_PHY_SCAL_32MHZ, AR5K_PHY_SCAL);
 		ath5k_hw_reg_write(ah, AR5K_PHY_SCAL_32MHZ, AR5K_PHY_SCAL);
 		ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK);
 		ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK);
 		ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY);
 		ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY);
-		ath5k_hw_reg_write(ah, ah->ah_radio == AR5K_RF5111 ?
-			AR5K_PHY_SPENDING_RF5111 : AR5K_PHY_SPENDING_RF5112,
-			AR5K_PHY_SPENDING);
+		ath5k_hw_reg_write(ah, ah->ah_phy_spending, AR5K_PHY_SPENDING);
+	}
+
+	if (ah->ah_version == AR5K_AR5212) {
+		ath5k_hw_reg_write(ah, 0x000100aa, 0x8118);
+		ath5k_hw_reg_write(ah, 0x00003210, 0x811c);
+		ath5k_hw_reg_write(ah, 0x00000052, 0x8108);
+		if (ah->ah_mac_srev >= AR5K_SREV_VER_AR2413)
+			ath5k_hw_reg_write(ah, 0x00000004, 0x8120);
 	}
 	}
 
 
 	/*
 	/*
@@ -2228,8 +2294,8 @@ void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id)
 	 * Set simple BSSID mask on 5212
 	 * Set simple BSSID mask on 5212
 	 */
 	 */
 	if (ah->ah_version == AR5K_AR5212) {
 	if (ah->ah_version == AR5K_AR5212) {
-		ath5k_hw_reg_write(ah, 0xfffffff, AR5K_BSS_IDM0);
-		ath5k_hw_reg_write(ah, 0xfffffff, AR5K_BSS_IDM1);
+		ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM0);
+		ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM1);
 	}
 	}
 
 
 	/*
 	/*
@@ -2374,6 +2440,8 @@ void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah)
 {
 {
 	ATH5K_TRACE(ah->ah_sc);
 	ATH5K_TRACE(ah->ah_sc);
 	AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
 	AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
+
+	/* TODO: ANI Support */
 }
 }
 
 
 /*
 /*
@@ -2383,6 +2451,8 @@ void ath5k_hw_stop_pcu_recv(struct ath5k_hw *ah)
 {
 {
 	ATH5K_TRACE(ah->ah_sc);
 	ATH5K_TRACE(ah->ah_sc);
 	AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
 	AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX);
+
+	/* TODO: ANI Support */
 }
 }
 
 
 /*
 /*
@@ -3456,10 +3526,10 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 	unsigned int rtscts_rate, unsigned int rtscts_duration)
 	unsigned int rtscts_rate, unsigned int rtscts_duration)
 {
 {
 	u32 frame_type;
 	u32 frame_type;
-	struct ath5k_hw_2w_tx_desc *tx_desc;
+	struct ath5k_hw_2w_tx_ctl *tx_ctl;
 	unsigned int frame_len;
 	unsigned int frame_len;
 
 
-	tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0;
+	tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
 
 
 	/*
 	/*
 	 * Validate input
 	 * Validate input
@@ -3478,12 +3548,8 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
-	/* Clear status descriptor */
-	memset(desc->ds_hw, 0, sizeof(struct ath5k_hw_tx_status));
-
-	/* Initialize control descriptor */
-	tx_desc->tx_control_0 = 0;
-	tx_desc->tx_control_1 = 0;
+	/* Clear descriptor */
+	memset(&desc->ud.ds_tx5210, 0, sizeof(struct ath5k_hw_5210_tx_desc));
 
 
 	/* Setup control descriptor */
 	/* Setup control descriptor */
 
 
@@ -3495,7 +3561,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 	if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
 	if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	tx_desc->tx_control_0 = frame_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN;
+	tx_ctl->tx_control_0 = frame_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN;
 
 
 	/* Verify and set buffer length */
 	/* Verify and set buffer length */
 
 
@@ -3506,7 +3572,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 	if (pkt_len & ~AR5K_2W_TX_DESC_CTL1_BUF_LEN)
 	if (pkt_len & ~AR5K_2W_TX_DESC_CTL1_BUF_LEN)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	tx_desc->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN;
+	tx_ctl->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN;
 
 
 	/*
 	/*
 	 * Verify and set header length
 	 * Verify and set header length
@@ -3515,7 +3581,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 	if (ah->ah_version == AR5K_AR5210) {
 	if (ah->ah_version == AR5K_AR5210) {
 		if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN)
 		if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN)
 			return -EINVAL;
 			return -EINVAL;
-		tx_desc->tx_control_0 |=
+		tx_ctl->tx_control_0 |=
 			AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN);
 			AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN);
 	}
 	}
 
 
@@ -3531,19 +3597,19 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 			frame_type = type /*<< 2 ?*/;
 			frame_type = type /*<< 2 ?*/;
 		}
 		}
 
 
-		tx_desc->tx_control_0 |=
+		tx_ctl->tx_control_0 |=
 			AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) |
 			AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) |
 			AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
 			AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
 	} else {
 	} else {
-		tx_desc->tx_control_0 |=
+		tx_ctl->tx_control_0 |=
 			AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) |
 			AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) |
 			AR5K_REG_SM(antenna_mode, AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT);
 			AR5K_REG_SM(antenna_mode, AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT);
-		tx_desc->tx_control_1 |=
+		tx_ctl->tx_control_1 |=
 			AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE);
 			AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE);
 	}
 	}
 #define _TX_FLAGS(_c, _flag)						\
 #define _TX_FLAGS(_c, _flag)						\
 	if (flags & AR5K_TXDESC_##_flag)				\
 	if (flags & AR5K_TXDESC_##_flag)				\
-		tx_desc->tx_control_##_c |=				\
+		tx_ctl->tx_control_##_c |=				\
 			AR5K_2W_TX_DESC_CTL##_c##_##_flag
 			AR5K_2W_TX_DESC_CTL##_c##_##_flag
 
 
 	_TX_FLAGS(0, CLRDMASK);
 	_TX_FLAGS(0, CLRDMASK);
@@ -3558,9 +3624,9 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 	 * WEP crap
 	 * WEP crap
 	 */
 	 */
 	if (key_index != AR5K_TXKEYIX_INVALID) {
 	if (key_index != AR5K_TXKEYIX_INVALID) {
-		tx_desc->tx_control_0 |=
+		tx_ctl->tx_control_0 |=
 			AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
 			AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
-		tx_desc->tx_control_1 |=
+		tx_ctl->tx_control_1 |=
 			AR5K_REG_SM(key_index,
 			AR5K_REG_SM(key_index,
 			AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
 			AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
 	}
 	}
@@ -3570,7 +3636,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 	 */
 	 */
 	if ((ah->ah_version == AR5K_AR5210) &&
 	if ((ah->ah_version == AR5K_AR5210) &&
 			(flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)))
 			(flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)))
-		tx_desc->tx_control_1 |= rtscts_duration &
+		tx_ctl->tx_control_1 |= rtscts_duration &
 				AR5K_2W_TX_DESC_CTL1_RTS_DURATION;
 				AR5K_2W_TX_DESC_CTL1_RTS_DURATION;
 
 
 	return 0;
 	return 0;
@@ -3586,13 +3652,11 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
 	unsigned int antenna_mode, unsigned int flags, unsigned int rtscts_rate,
 	unsigned int antenna_mode, unsigned int flags, unsigned int rtscts_rate,
 	unsigned int rtscts_duration)
 	unsigned int rtscts_duration)
 {
 {
-	struct ath5k_hw_4w_tx_desc *tx_desc;
-	struct ath5k_hw_tx_status *tx_status;
+	struct ath5k_hw_4w_tx_ctl *tx_ctl;
 	unsigned int frame_len;
 	unsigned int frame_len;
 
 
 	ATH5K_TRACE(ah->ah_sc);
 	ATH5K_TRACE(ah->ah_sc);
-	tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
-	tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2];
+	tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
 
 
 	/*
 	/*
 	 * Validate input
 	 * Validate input
@@ -3611,14 +3675,8 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
-	/* Clear status descriptor */
-	memset(tx_status, 0, sizeof(struct ath5k_hw_tx_status));
-
-	/* Initialize control descriptor */
-	tx_desc->tx_control_0 = 0;
-	tx_desc->tx_control_1 = 0;
-	tx_desc->tx_control_2 = 0;
-	tx_desc->tx_control_3 = 0;
+	/* Clear descriptor */
+	memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc));
 
 
 	/* Setup control descriptor */
 	/* Setup control descriptor */
 
 
@@ -3630,7 +3688,7 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
 	if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
 	if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	tx_desc->tx_control_0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
+	tx_ctl->tx_control_0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
 
 
 	/* Verify and set buffer length */
 	/* Verify and set buffer length */
 
 
@@ -3641,20 +3699,20 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
 	if (pkt_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN)
 	if (pkt_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	tx_desc->tx_control_1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
+	tx_ctl->tx_control_1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
 
 
-	tx_desc->tx_control_0 |=
+	tx_ctl->tx_control_0 |=
 		AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) |
 		AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) |
 		AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT);
 		AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT);
-	tx_desc->tx_control_1 |= AR5K_REG_SM(type,
+	tx_ctl->tx_control_1 |= AR5K_REG_SM(type,
 					AR5K_4W_TX_DESC_CTL1_FRAME_TYPE);
 					AR5K_4W_TX_DESC_CTL1_FRAME_TYPE);
-	tx_desc->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES,
+	tx_ctl->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES,
 					AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0);
 					AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0);
-	tx_desc->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
+	tx_ctl->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
 
 
 #define _TX_FLAGS(_c, _flag)			\
 #define _TX_FLAGS(_c, _flag)			\
 	if (flags & AR5K_TXDESC_##_flag)	\
 	if (flags & AR5K_TXDESC_##_flag)	\
-		tx_desc->tx_control_##_c |=	\
+		tx_ctl->tx_control_##_c |=	\
 			AR5K_4W_TX_DESC_CTL##_c##_##_flag
 			AR5K_4W_TX_DESC_CTL##_c##_##_flag
 
 
 	_TX_FLAGS(0, CLRDMASK);
 	_TX_FLAGS(0, CLRDMASK);
@@ -3670,8 +3728,8 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
 	 * WEP crap
 	 * WEP crap
 	 */
 	 */
 	if (key_index != AR5K_TXKEYIX_INVALID) {
 	if (key_index != AR5K_TXKEYIX_INVALID) {
-		tx_desc->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
-		tx_desc->tx_control_1 |= AR5K_REG_SM(key_index,
+		tx_ctl->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
+		tx_ctl->tx_control_1 |= AR5K_REG_SM(key_index,
 				AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
 				AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
 	}
 	}
 
 
@@ -3682,9 +3740,9 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
 		if ((flags & AR5K_TXDESC_RTSENA) &&
 		if ((flags & AR5K_TXDESC_RTSENA) &&
 				(flags & AR5K_TXDESC_CTSENA))
 				(flags & AR5K_TXDESC_CTSENA))
 			return -EINVAL;
 			return -EINVAL;
-		tx_desc->tx_control_2 |= rtscts_duration &
+		tx_ctl->tx_control_2 |= rtscts_duration &
 				AR5K_4W_TX_DESC_CTL2_RTS_DURATION;
 				AR5K_4W_TX_DESC_CTL2_RTS_DURATION;
-		tx_desc->tx_control_3 |= AR5K_REG_SM(rtscts_rate,
+		tx_ctl->tx_control_3 |= AR5K_REG_SM(rtscts_rate,
 				AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE);
 				AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE);
 	}
 	}
 
 
@@ -3699,7 +3757,7 @@ ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 	unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, u_int tx_tries2,
 	unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, u_int tx_tries2,
 	unsigned int tx_rate3, u_int tx_tries3)
 	unsigned int tx_rate3, u_int tx_tries3)
 {
 {
-	struct ath5k_hw_4w_tx_desc *tx_desc;
+	struct ath5k_hw_4w_tx_ctl *tx_ctl;
 
 
 	/*
 	/*
 	 * Rates can be 0 as long as the retry count is 0 too.
 	 * Rates can be 0 as long as the retry count is 0 too.
@@ -3716,14 +3774,14 @@ ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 	}
 	}
 
 
 	if (ah->ah_version == AR5K_AR5212) {
 	if (ah->ah_version == AR5K_AR5212) {
-		tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
+		tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
 
 
 #define _XTX_TRIES(_n)							\
 #define _XTX_TRIES(_n)							\
 	if (tx_tries##_n) {						\
 	if (tx_tries##_n) {						\
-		tx_desc->tx_control_2 |=				\
+		tx_ctl->tx_control_2 |=				\
 		    AR5K_REG_SM(tx_tries##_n,				\
 		    AR5K_REG_SM(tx_tries##_n,				\
 		    AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n);		\
 		    AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n);		\
-		tx_desc->tx_control_3 |=				\
+		tx_ctl->tx_control_3 |=				\
 		    AR5K_REG_SM(tx_rate##_n,				\
 		    AR5K_REG_SM(tx_rate##_n,				\
 		    AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n);		\
 		    AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n);		\
 	}
 	}
@@ -3744,13 +3802,15 @@ ath5k_hw_setup_xr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
  * Proccess the tx status descriptor on 5210/5211
  * Proccess the tx status descriptor on 5210/5211
  */
  */
 static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
 static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
-		struct ath5k_desc *desc)
+		struct ath5k_desc *desc, struct ath5k_tx_status *ts)
 {
 {
+	struct ath5k_hw_2w_tx_ctl *tx_ctl;
 	struct ath5k_hw_tx_status *tx_status;
 	struct ath5k_hw_tx_status *tx_status;
-	struct ath5k_hw_2w_tx_desc *tx_desc;
 
 
-	tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0;
-	tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[0];
+	ATH5K_TRACE(ah->ah_sc);
+
+	tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
+	tx_status = &desc->ud.ds_tx5210.tx_stat;
 
 
 	/* No frame has been send or error */
 	/* No frame has been send or error */
 	if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
 	if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
@@ -3759,32 +3819,32 @@ static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
 	/*
 	/*
 	 * Get descriptor status
 	 * Get descriptor status
 	 */
 	 */
-	desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
+	ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
 		AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
 		AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
-	desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
+	ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
 		AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
 		AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
-	desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
+	ts->ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
 		AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
 		AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
-	/*TODO: desc->ds_us.tx.ts_virtcol + test*/
-	desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
+	/*TODO: ts->ts_virtcol + test*/
+	ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
 		AR5K_DESC_TX_STATUS1_SEQ_NUM);
 		AR5K_DESC_TX_STATUS1_SEQ_NUM);
-	desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
+	ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
 		AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
 		AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
-	desc->ds_us.tx.ts_antenna = 1;
-	desc->ds_us.tx.ts_status = 0;
-	desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_0,
+	ts->ts_antenna = 1;
+	ts->ts_status = 0;
+	ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_0,
 		AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
 		AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
 
 
 	if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){
 	if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){
 		if (tx_status->tx_status_0 &
 		if (tx_status->tx_status_0 &
 				AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
 				AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
-			desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY;
+			ts->ts_status |= AR5K_TXERR_XRETRY;
 
 
 		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
 		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
-			desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO;
+			ts->ts_status |= AR5K_TXERR_FIFO;
 
 
 		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
 		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
-			desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT;
+			ts->ts_status |= AR5K_TXERR_FILT;
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -3794,14 +3854,15 @@ static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
  * Proccess a tx descriptor on 5212
  * Proccess a tx descriptor on 5212
  */
  */
 static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
 static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
-		struct ath5k_desc *desc)
+		struct ath5k_desc *desc, struct ath5k_tx_status *ts)
 {
 {
+	struct ath5k_hw_4w_tx_ctl *tx_ctl;
 	struct ath5k_hw_tx_status *tx_status;
 	struct ath5k_hw_tx_status *tx_status;
-	struct ath5k_hw_4w_tx_desc *tx_desc;
 
 
 	ATH5K_TRACE(ah->ah_sc);
 	ATH5K_TRACE(ah->ah_sc);
-	tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0;
-	tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2];
+
+	tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
+	tx_status = &desc->ud.ds_tx5212.tx_stat;
 
 
 	/* No frame has been send or error */
 	/* No frame has been send or error */
 	if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
 	if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
@@ -3810,42 +3871,42 @@ static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
 	/*
 	/*
 	 * Get descriptor status
 	 * Get descriptor status
 	 */
 	 */
-	desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
+	ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
 		AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
 		AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
-	desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
+	ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
 		AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
 		AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
-	desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
+	ts->ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
 		AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
 		AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
-	desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
+	ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
 		AR5K_DESC_TX_STATUS1_SEQ_NUM);
 		AR5K_DESC_TX_STATUS1_SEQ_NUM);
-	desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
+	ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
 		AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
 		AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
-	desc->ds_us.tx.ts_antenna = (tx_status->tx_status_1 &
+	ts->ts_antenna = (tx_status->tx_status_1 &
 		AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1;
 		AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1;
-	desc->ds_us.tx.ts_status = 0;
+	ts->ts_status = 0;
 
 
 	switch (AR5K_REG_MS(tx_status->tx_status_1,
 	switch (AR5K_REG_MS(tx_status->tx_status_1,
 			AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX)) {
 			AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX)) {
 	case 0:
 	case 0:
-		desc->ds_us.tx.ts_rate = tx_desc->tx_control_3 &
+		ts->ts_rate = tx_ctl->tx_control_3 &
 			AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
 			AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
 		break;
 		break;
 	case 1:
 	case 1:
-		desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
+		ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3,
 			AR5K_4W_TX_DESC_CTL3_XMIT_RATE1);
 			AR5K_4W_TX_DESC_CTL3_XMIT_RATE1);
-		desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
+		ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2,
 			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
 			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
 		break;
 		break;
 	case 2:
 	case 2:
-		desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
+		ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3,
 			AR5K_4W_TX_DESC_CTL3_XMIT_RATE2);
 			AR5K_4W_TX_DESC_CTL3_XMIT_RATE2);
-		desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
+		ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2,
 			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2);
 			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2);
 		break;
 		break;
 	case 3:
 	case 3:
-		desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3,
+		ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3,
 			AR5K_4W_TX_DESC_CTL3_XMIT_RATE3);
 			AR5K_4W_TX_DESC_CTL3_XMIT_RATE3);
-		desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2,
+		ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2,
 			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3);
 			AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3);
 		break;
 		break;
 	}
 	}
@@ -3853,13 +3914,13 @@ static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
 	if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){
 	if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){
 		if (tx_status->tx_status_0 &
 		if (tx_status->tx_status_0 &
 				AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
 				AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
-			desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY;
+			ts->ts_status |= AR5K_TXERR_XRETRY;
 
 
 		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
 		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
-			desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO;
+			ts->ts_status |= AR5K_TXERR_FIFO;
 
 
 		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
 		if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
-			desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT;
+			ts->ts_status |= AR5K_TXERR_FILT;
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -3875,31 +3936,27 @@ static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
 int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 			u32 size, unsigned int flags)
 			u32 size, unsigned int flags)
 {
 {
-	struct ath5k_rx_desc *rx_desc;
+	struct ath5k_hw_rx_ctl *rx_ctl;
 
 
 	ATH5K_TRACE(ah->ah_sc);
 	ATH5K_TRACE(ah->ah_sc);
-	rx_desc = (struct ath5k_rx_desc *)&desc->ds_ctl0;
+	rx_ctl = &desc->ud.ds_rx.rx_ctl;
 
 
 	/*
 	/*
-	 *Clear ds_hw
+	 * Clear the descriptor
 	 * If we don't clean the status descriptor,
 	 * If we don't clean the status descriptor,
 	 * while scanning we get too many results,
 	 * while scanning we get too many results,
 	 * most of them virtual, after some secs
 	 * most of them virtual, after some secs
 	 * of scanning system hangs. M.F.
 	 * of scanning system hangs. M.F.
 	*/
 	*/
-	memset(desc->ds_hw, 0, sizeof(desc->ds_hw));
-
-	/*Initialize rx descriptor*/
-	rx_desc->rx_control_0 = 0;
-	rx_desc->rx_control_1 = 0;
+	memset(&desc->ud.ds_rx, 0, sizeof(struct ath5k_hw_all_rx_desc));
 
 
 	/* Setup descriptor */
 	/* Setup descriptor */
-	rx_desc->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
-	if (unlikely(rx_desc->rx_control_1 != size))
+	rx_ctl->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
+	if (unlikely(rx_ctl->rx_control_1 != size))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	if (flags & AR5K_RXDESC_INTREQ)
 	if (flags & AR5K_RXDESC_INTREQ)
-		rx_desc->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
+		rx_ctl->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -3907,67 +3964,68 @@ int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
 /*
 /*
  * Proccess the rx status descriptor on 5210/5211
  * Proccess the rx status descriptor on 5210/5211
  */
  */
-static int ath5k_hw_proc_old_rx_status(struct ath5k_hw *ah,
-		struct ath5k_desc *desc)
+static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah,
+		struct ath5k_desc *desc, struct ath5k_rx_status *rs)
 {
 {
-	struct ath5k_hw_old_rx_status *rx_status;
+	struct ath5k_hw_rx_status *rx_status;
 
 
-	rx_status = (struct ath5k_hw_old_rx_status *)&desc->ds_hw[0];
+	rx_status = &desc->ud.ds_rx.u.rx_stat;
 
 
 	/* No frame received / not ready */
 	/* No frame received / not ready */
-	if (unlikely((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_DONE)
+	if (unlikely((rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_DONE)
 				== 0))
 				== 0))
 		return -EINPROGRESS;
 		return -EINPROGRESS;
 
 
 	/*
 	/*
 	 * Frame receive status
 	 * Frame receive status
 	 */
 	 */
-	desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 &
-		AR5K_OLD_RX_DESC_STATUS0_DATA_LEN;
-	desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
-		AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL);
-	desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
-		AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE);
-	desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 &
-		AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA;
-	desc->ds_us.rx.rs_more = rx_status->rx_status_0 &
-		AR5K_OLD_RX_DESC_STATUS0_MORE;
-	desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
-		AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
-	desc->ds_us.rx.rs_status = 0;
+	rs->rs_datalen = rx_status->rx_status_0 &
+		AR5K_5210_RX_DESC_STATUS0_DATA_LEN;
+	rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL);
+	rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE);
+	rs->rs_antenna = rx_status->rx_status_0 &
+		AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA;
+	rs->rs_more = rx_status->rx_status_0 &
+		AR5K_5210_RX_DESC_STATUS0_MORE;
+	/* TODO: this timestamp is 13 bit, later on we assume 15 bit */
+	rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
+		AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
+	rs->rs_status = 0;
 
 
 	/*
 	/*
 	 * Key table status
 	 * Key table status
 	 */
 	 */
-	if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID)
-		desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
-			AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX);
+	if (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID)
+		rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
+			AR5K_5210_RX_DESC_STATUS1_KEY_INDEX);
 	else
 	else
-		desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID;
+		rs->rs_keyix = AR5K_RXKEYIX_INVALID;
 
 
 	/*
 	/*
 	 * Receive/descriptor errors
 	 * Receive/descriptor errors
 	 */
 	 */
-	if ((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK)
-			== 0) {
-		if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR)
-			desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC;
+	if ((rx_status->rx_status_1 &
+			AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK) == 0) {
+		if (rx_status->rx_status_1 &
+				AR5K_5210_RX_DESC_STATUS1_CRC_ERROR)
+			rs->rs_status |= AR5K_RXERR_CRC;
 
 
 		if (rx_status->rx_status_1 &
 		if (rx_status->rx_status_1 &
-				AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN)
-			desc->ds_us.rx.rs_status |= AR5K_RXERR_FIFO;
+				AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN)
+			rs->rs_status |= AR5K_RXERR_FIFO;
 
 
 		if (rx_status->rx_status_1 &
 		if (rx_status->rx_status_1 &
-				AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR) {
-			desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY;
-			desc->ds_us.rx.rs_phyerr =
-				AR5K_REG_MS(rx_status->rx_status_1,
-					AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR);
+				AR5K_5210_RX_DESC_STATUS1_PHY_ERROR) {
+			rs->rs_status |= AR5K_RXERR_PHY;
+			rs->rs_phyerr = AR5K_REG_MS(rx_status->rx_status_1,
+					   AR5K_5210_RX_DESC_STATUS1_PHY_ERROR);
 		}
 		}
 
 
 		if (rx_status->rx_status_1 &
 		if (rx_status->rx_status_1 &
-				AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
-			desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT;
+				AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
+			rs->rs_status |= AR5K_RXERR_DECRYPT;
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -3976,71 +4034,72 @@ static int ath5k_hw_proc_old_rx_status(struct ath5k_hw *ah,
 /*
 /*
  * Proccess the rx status descriptor on 5212
  * Proccess the rx status descriptor on 5212
  */
  */
-static int ath5k_hw_proc_new_rx_status(struct ath5k_hw *ah,
-		struct ath5k_desc *desc)
+static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah,
+		struct ath5k_desc *desc, struct ath5k_rx_status *rs)
 {
 {
-	struct ath5k_hw_new_rx_status *rx_status;
+	struct ath5k_hw_rx_status *rx_status;
 	struct ath5k_hw_rx_error *rx_err;
 	struct ath5k_hw_rx_error *rx_err;
 
 
 	ATH5K_TRACE(ah->ah_sc);
 	ATH5K_TRACE(ah->ah_sc);
-	rx_status = (struct ath5k_hw_new_rx_status *)&desc->ds_hw[0];
+	rx_status = &desc->ud.ds_rx.u.rx_stat;
 
 
 	/* Overlay on error */
 	/* Overlay on error */
-	rx_err = (struct ath5k_hw_rx_error *)&desc->ds_hw[0];
+	rx_err = &desc->ud.ds_rx.u.rx_err;
 
 
 	/* No frame received / not ready */
 	/* No frame received / not ready */
-	if (unlikely((rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_DONE)
+	if (unlikely((rx_status->rx_status_1 & AR5K_5212_RX_DESC_STATUS1_DONE)
 				== 0))
 				== 0))
 		return -EINPROGRESS;
 		return -EINPROGRESS;
 
 
 	/*
 	/*
 	 * Frame receive status
 	 * Frame receive status
 	 */
 	 */
-	desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 &
-		AR5K_NEW_RX_DESC_STATUS0_DATA_LEN;
-	desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
-		AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL);
-	desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
-		AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE);
-	desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 &
-		AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA;
-	desc->ds_us.rx.rs_more = rx_status->rx_status_0 &
-		AR5K_NEW_RX_DESC_STATUS0_MORE;
-	desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
-		AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
-	desc->ds_us.rx.rs_status = 0;
+	rs->rs_datalen = rx_status->rx_status_0 &
+		AR5K_5212_RX_DESC_STATUS0_DATA_LEN;
+	rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL);
+	rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
+		AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE);
+	rs->rs_antenna = rx_status->rx_status_0 &
+		AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA;
+	rs->rs_more = rx_status->rx_status_0 &
+		AR5K_5212_RX_DESC_STATUS0_MORE;
+	rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
+		AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
+	rs->rs_status = 0;
 
 
 	/*
 	/*
 	 * Key table status
 	 * Key table status
 	 */
 	 */
-	if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID)
-		desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
-				AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX);
+	if (rx_status->rx_status_1 & AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID)
+		rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
+				AR5K_5212_RX_DESC_STATUS1_KEY_INDEX);
 	else
 	else
-		desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID;
+		rs->rs_keyix = AR5K_RXKEYIX_INVALID;
 
 
 	/*
 	/*
 	 * Receive/descriptor errors
 	 * Receive/descriptor errors
 	 */
 	 */
 	if ((rx_status->rx_status_1 &
 	if ((rx_status->rx_status_1 &
-			AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK) == 0) {
-		if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR)
-			desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC;
+			AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK) == 0) {
+		if (rx_status->rx_status_1 &
+				AR5K_5212_RX_DESC_STATUS1_CRC_ERROR)
+			rs->rs_status |= AR5K_RXERR_CRC;
 
 
 		if (rx_status->rx_status_1 &
 		if (rx_status->rx_status_1 &
-				AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR) {
-			desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY;
-			desc->ds_us.rx.rs_phyerr =
-				AR5K_REG_MS(rx_err->rx_error_1,
-					AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE);
+				AR5K_5212_RX_DESC_STATUS1_PHY_ERROR) {
+			rs->rs_status |= AR5K_RXERR_PHY;
+			rs->rs_phyerr = AR5K_REG_MS(rx_err->rx_error_1,
+					   AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE);
 		}
 		}
 
 
 		if (rx_status->rx_status_1 &
 		if (rx_status->rx_status_1 &
-				AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
-			desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT;
+				AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
+			rs->rs_status |= AR5K_RXERR_DECRYPT;
 
 
-		if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR)
-			desc->ds_us.rx.rs_status |= AR5K_RXERR_MIC;
+		if (rx_status->rx_status_1 &
+				AR5K_5212_RX_DESC_STATUS1_MIC_ERROR)
+			rs->rs_status |= AR5K_RXERR_MIC;
 	}
 	}
 
 
 	return 0;
 	return 0;

+ 89 - 61
drivers/net/wireless/ath5k/hw.h

@@ -173,7 +173,10 @@ struct ath5k_eeprom_info {
  * (rX: reserved fields possibily used by future versions of the ar5k chipset)
  * (rX: reserved fields possibily used by future versions of the ar5k chipset)
  */
  */
 
 
-struct ath5k_rx_desc {
+/*
+ * common hardware RX control descriptor
+ */
+struct ath5k_hw_rx_ctl {
 	u32	rx_control_0; /* RX control word 0 */
 	u32	rx_control_0; /* RX control word 0 */
 
 
 #define AR5K_DESC_RX_CTL0			0x00000000
 #define AR5K_DESC_RX_CTL0			0x00000000
@@ -185,69 +188,63 @@ struct ath5k_rx_desc {
 } __packed;
 } __packed;
 
 
 /*
 /*
- * 5210/5211 rx status descriptor
+ * common hardware RX status descriptor
+ * 5210/11 and 5212 differ only in the flags defined below
  */
  */
-struct ath5k_hw_old_rx_status {
+struct ath5k_hw_rx_status {
 	u32	rx_status_0; /* RX status word 0 */
 	u32	rx_status_0; /* RX status word 0 */
-
-#define AR5K_OLD_RX_DESC_STATUS0_DATA_LEN		0x00000fff
-#define AR5K_OLD_RX_DESC_STATUS0_MORE			0x00001000
-#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE		0x00078000
-#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE_S		15
-#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL		0x07f80000
-#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL_S	19
-#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA	0x38000000
-#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA_S	27
-
 	u32	rx_status_1; /* RX status word 1 */
 	u32	rx_status_1; /* RX status word 1 */
-
-#define AR5K_OLD_RX_DESC_STATUS1_DONE			0x00000001
-#define AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK	0x00000002
-#define AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR		0x00000004
-#define AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN		0x00000008
-#define AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR	0x00000010
-#define AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR		0x000000e0
-#define AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR_S		5
-#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID	0x00000100
-#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX		0x00007e00
-#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_S		9
-#define AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP	0x0fff8000
-#define AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S	15
-#define AR5K_OLD_RX_DESC_STATUS1_KEY_CACHE_MISS		0x10000000
 } __packed;
 } __packed;
 
 
+/* 5210/5211 */
+#define AR5K_5210_RX_DESC_STATUS0_DATA_LEN		0x00000fff
+#define AR5K_5210_RX_DESC_STATUS0_MORE			0x00001000
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE		0x00078000
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE_S	15
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL	0x07f80000
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL_S	19
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA	0x38000000
+#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA_S	27
+#define AR5K_5210_RX_DESC_STATUS1_DONE			0x00000001
+#define AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK	0x00000002
+#define AR5K_5210_RX_DESC_STATUS1_CRC_ERROR		0x00000004
+#define AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN		0x00000008
+#define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR	0x00000010
+#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR		0x000000e0
+#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR_S		5
+#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID	0x00000100
+#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX		0x00007e00
+#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_S		9
+#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP	0x0fff8000
+#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S	15
+#define AR5K_5210_RX_DESC_STATUS1_KEY_CACHE_MISS	0x10000000
+
+/* 5212 */
+#define AR5K_5212_RX_DESC_STATUS0_DATA_LEN		0x00000fff
+#define AR5K_5212_RX_DESC_STATUS0_MORE			0x00001000
+#define AR5K_5212_RX_DESC_STATUS0_DECOMP_CRC_ERROR	0x00002000
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE		0x000f8000
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE_S	15
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL	0x0ff00000
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL_S	20
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA	0xf0000000
+#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA_S	28
+#define AR5K_5212_RX_DESC_STATUS1_DONE			0x00000001
+#define AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK	0x00000002
+#define AR5K_5212_RX_DESC_STATUS1_CRC_ERROR		0x00000004
+#define AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR	0x00000008
+#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR		0x00000010
+#define AR5K_5212_RX_DESC_STATUS1_MIC_ERROR		0x00000020
+#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID	0x00000100
+#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX		0x0000fe00
+#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_S		9
+#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP	0x7fff0000
+#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S	16
+#define AR5K_5212_RX_DESC_STATUS1_KEY_CACHE_MISS	0x80000000
+
 /*
 /*
- * 5212 rx status descriptor
+ * common hardware RX error descriptor
  */
  */
-struct ath5k_hw_new_rx_status {
-	u32	rx_status_0; /* RX status word 0 */
-
-#define AR5K_NEW_RX_DESC_STATUS0_DATA_LEN		0x00000fff
-#define AR5K_NEW_RX_DESC_STATUS0_MORE			0x00001000
-#define AR5K_NEW_RX_DESC_STATUS0_DECOMP_CRC_ERROR	0x00002000
-#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE		0x000f8000
-#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE_S		15
-#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL		0x0ff00000
-#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL_S	20
-#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA	0xf0000000
-#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA_S	28
-
-	u32	rx_status_1; /* RX status word 1 */
-
-#define AR5K_NEW_RX_DESC_STATUS1_DONE			0x00000001
-#define AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK	0x00000002
-#define AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR		0x00000004
-#define AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR	0x00000008
-#define AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR		0x00000010
-#define AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR		0x00000020
-#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID	0x00000100
-#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX		0x0000fe00
-#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_S		9
-#define AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP	0x7fff0000
-#define AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S	16
-#define AR5K_NEW_RX_DESC_STATUS1_KEY_CACHE_MISS		0x80000000
-} __packed;
-
 struct ath5k_hw_rx_error {
 struct ath5k_hw_rx_error {
 	u32	rx_error_0; /* RX error word 0 */
 	u32	rx_error_0; /* RX error word 0 */
 
 
@@ -268,7 +265,10 @@ struct ath5k_hw_rx_error {
 #define AR5K_DESC_RX_PHY_ERROR_SERVICE		0xc0
 #define AR5K_DESC_RX_PHY_ERROR_SERVICE		0xc0
 #define AR5K_DESC_RX_PHY_ERROR_TRANSMITOVR	0xe0
 #define AR5K_DESC_RX_PHY_ERROR_TRANSMITOVR	0xe0
 
 
-struct ath5k_hw_2w_tx_desc {
+/*
+ * 5210/5211 hardware 2-word TX control descriptor
+ */
+struct ath5k_hw_2w_tx_ctl {
 	u32	tx_control_0; /* TX control word 0 */
 	u32	tx_control_0; /* TX control word 0 */
 
 
 #define AR5K_2W_TX_DESC_CTL0_FRAME_LEN		0x00000fff
 #define AR5K_2W_TX_DESC_CTL0_FRAME_LEN		0x00000fff
@@ -314,9 +314,9 @@ struct ath5k_hw_2w_tx_desc {
 #define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS     0x10
 #define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS     0x10
 
 
 /*
 /*
- * 5212 4-word tx control descriptor
+ * 5212 hardware 4-word TX control descriptor
  */
  */
-struct ath5k_hw_4w_tx_desc {
+struct ath5k_hw_4w_tx_ctl {
 	u32	tx_control_0; /* TX control word 0 */
 	u32	tx_control_0; /* TX control word 0 */
 
 
 #define AR5K_4W_TX_DESC_CTL0_FRAME_LEN		0x00000fff
 #define AR5K_4W_TX_DESC_CTL0_FRAME_LEN		0x00000fff
@@ -374,7 +374,7 @@ struct ath5k_hw_4w_tx_desc {
 } __packed;
 } __packed;
 
 
 /*
 /*
- * Common tx status descriptor
+ * Common TX status descriptor
  */
  */
 struct ath5k_hw_tx_status {
 struct ath5k_hw_tx_status {
 	u32	tx_status_0; /* TX status word 0 */
 	u32	tx_status_0; /* TX status word 0 */
@@ -414,6 +414,34 @@ struct ath5k_hw_tx_status {
 } __packed;
 } __packed;
 
 
 
 
+/*
+ * 5210/5211 hardware TX descriptor
+ */
+struct ath5k_hw_5210_tx_desc {
+	struct ath5k_hw_2w_tx_ctl	tx_ctl;
+	struct ath5k_hw_tx_status	tx_stat;
+} __packed;
+
+/*
+ * 5212 hardware TX descriptor
+ */
+struct ath5k_hw_5212_tx_desc {
+	struct ath5k_hw_4w_tx_ctl	tx_ctl;
+	struct ath5k_hw_tx_status	tx_stat;
+} __packed;
+
+/*
+ * common hardware RX descriptor
+ */
+struct ath5k_hw_all_rx_desc {
+	struct ath5k_hw_rx_ctl			rx_ctl;
+	union {
+		struct ath5k_hw_rx_status	rx_stat;
+		struct ath5k_hw_rx_error	rx_err;
+	} u;
+} __packed;
+
+
 /*
 /*
  * AR5K REGISTER ACCESS
  * AR5K REGISTER ACCESS
  */
  */

+ 231 - 2
drivers/net/wireless/ath5k/initvals.c

@@ -678,8 +678,8 @@ static const struct ath5k_ini ar5212_ini[] = {
 	{ AR5K_PHY(644), 0x00806333 },
 	{ AR5K_PHY(644), 0x00806333 },
 	{ AR5K_PHY(645), 0x00106c10 },
 	{ AR5K_PHY(645), 0x00106c10 },
 	{ AR5K_PHY(646), 0x009c4060 },
 	{ AR5K_PHY(646), 0x009c4060 },
-	/*{ AR5K_PHY(647), 0x1483800a },*/ /* Old value */
 	{ AR5K_PHY(647), 0x1483800a },
 	{ AR5K_PHY(647), 0x1483800a },
+	/* { AR5K_PHY(648), 0x018830c6 },*/ /* 2413 */
 	{ AR5K_PHY(648), 0x01831061 },
 	{ AR5K_PHY(648), 0x01831061 },
 	{ AR5K_PHY(649), 0x00000400 },
 	{ AR5K_PHY(649), 0x00000400 },
 	/*{ AR5K_PHY(650), 0x000001b5 },*/
 	/*{ AR5K_PHY(650), 0x000001b5 },*/
@@ -1081,6 +1081,207 @@ static const struct ath5k_ini_mode rf5413_ini_mode_end[] = {
 		{ 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0 } },
 		{ 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0 } },
 };
 };
 
 
+/* Initial mode-specific settings for RF2413/2414 (Written after ar5212_ini) */
+/* XXX: No dumps for turbog yet, so turbog is the same with g here with some
+ * minor tweaking based on dumps from other chips */
+static const struct ath5k_ini_mode rf2413_ini_mode_end[] = {
+	{ AR5K_TXCFG,
+	/*	      b		g	    gTurbo */
+		{ 0x00000015, 0x00000015, 0x00000015 } },
+	{ AR5K_USEC_5211,
+		{ 0x04e01395, 0x12e013ab, 0x098813cf } },
+	{ AR5K_PHY(10),
+		{ 0x05020000, 0x0a020001, 0x0a020001 } },
+	{ AR5K_PHY(13),
+		{ 0x00000e00, 0x00000e00, 0x00000e00 } },
+	{ AR5K_PHY(14),
+		{ 0x0000000a, 0x0000000a, 0x0000000a } },
+	{ AR5K_PHY(18),
+		{ 0x001a6a64, 0x001a6a64, 0x001a6a64 } },
+	{ AR5K_PHY(20),
+		{ 0x0de8b0da, 0x0c98b0da, 0x0c98b0da } },
+	{ AR5K_PHY_SIG,
+		{ 0x7ee80d2e, 0x7ec80d2e, 0x7ec80d2e } },
+	{ AR5K_PHY_AGCCOARSE,
+		{ 0x3137665e, 0x3139605e, 0x3139605e } },
+	{ AR5K_PHY(27),
+		{ 0x050cb081, 0x050cb081, 0x050cb081 } },
+	{ AR5K_PHY_RX_DELAY,
+		{ 0x0000044c, 0x00000898, 0x000007d0 } },
+	{ AR5K_PHY_FRAME_CTL_5211,
+		{ 0xf7b80d00, 0xf7b81000, 0xf7b81000 } },
+	{ AR5K_PHY_CCKTXCTL,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ AR5K_PHY(642),
+		{ 0xd03e6788, 0xd03e6788, 0xd03e6788 } },
+	{ AR5K_PHY_GAIN_2GHZ,
+		{ 0x0042c140, 0x0042c140, 0x0042c140 } },
+	{ 0xa21c,
+		{ 0x1863800a, 0x1883800a, 0x1883800a } },
+	{ AR5K_DCU_FP,
+		{ 0x000003e0, 0x000003e0, 0x000003e0 } },
+	{ 0x8060,
+		{ 0x0000000f, 0x0000000f, 0x0000000f } },
+	{ 0x8118,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x811c,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x8120,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x8124,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x8128,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x812c,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x8130,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x8134,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x8138,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x813c,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0x8140,
+		{ 0x800000a8, 0x800000a8, 0x800000a8 } },
+	{ 0x8144,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ AR5K_PHY_AGC,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ AR5K_PHY(11),
+		{ 0x0000a000, 0x0000a000, 0x0000a000 } },
+	{ AR5K_PHY(15),
+		{ 0x00200400, 0x00200400, 0x00200400 } },
+	{ AR5K_PHY(19),
+		{ 0x1284233c, 0x1284233c, 0x1284233c } },
+	{ AR5K_PHY_SCR,
+		{ 0x0000001f, 0x0000001f, 0x0000001f } },
+	{ AR5K_PHY_SLMT,
+		{ 0x00000080, 0x00000080, 0x00000080 } },
+	{ AR5K_PHY_SCAL,
+		{ 0x0000000e, 0x0000000e, 0x0000000e } },
+	{ AR5K_PHY(86),
+		{ 0x000000ff, 0x000000ff, 0x000000ff } },
+	{ AR5K_PHY(96),
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ AR5K_PHY(97),
+		{ 0x02800000, 0x02800000, 0x02800000 } },
+	{ AR5K_PHY(104),
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ AR5K_PHY(120),
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ AR5K_PHY(121),
+		{ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa } },
+	{ AR5K_PHY(122),
+		{ 0x3c466478, 0x3c466478, 0x3c466478 } },
+	{ AR5K_PHY(123),
+		{ 0x000000aa, 0x000000aa, 0x000000aa } },
+	{ AR5K_PHY_SCLOCK,
+		{ 0x0000000c, 0x0000000c, 0x0000000c } },
+	{ AR5K_PHY_SDELAY,
+		{ 0x000000ff, 0x000000ff, 0x000000ff } },
+	{ AR5K_PHY_SPENDING,
+		{ 0x00000014, 0x00000014, 0x00000014 } },
+	{ 0xa228,
+		{ 0x000009b5, 0x000009b5, 0x000009b5 } },
+	{ 0xa23c,
+		{ 0x93c889af, 0x93c889af, 0x93c889af } },
+	{ 0xa24c,
+		{ 0x00000001, 0x00000001, 0x00000001 } },
+	{ 0xa250,
+		{ 0x0000a000, 0x0000a000, 0x0000a000 } },
+	{ 0xa254,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0xa258,
+		{ 0x0cc75380, 0x0cc75380, 0x0cc75380 } },
+	{ 0xa25c,
+		{ 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01 } },
+	{ 0xa260,
+		{ 0x5f690f01, 0x5f690f01, 0x5f690f01 } },
+	{ 0xa264,
+		{ 0x00418a11, 0x00418a11, 0x00418a11 } },
+	{ 0xa268,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0xa26c,
+		{ 0x0c30c16a, 0x0c30c16a, 0x0c30c16a } },
+	{ 0xa270,
+		{ 0x00820820, 0x00820820, 0x00820820 } },
+	{ 0xa274,
+		{ 0x001b7caa, 0x001b7caa, 0x001b7caa } },
+	{ 0xa278,
+		{ 0x1ce739ce, 0x1ce739ce, 0x1ce739ce } },
+	{ 0xa27c,
+		{ 0x051701ce, 0x051701ce, 0x051701ce } },
+	{ 0xa300,
+		{ 0x18010000, 0x18010000, 0x18010000 } },
+	{ 0xa304,
+		{ 0x30032602, 0x30032602, 0x30032602 } },
+	{ 0xa308,
+		{ 0x48073e06, 0x48073e06, 0x48073e06 } },
+	{ 0xa30c,
+		{ 0x560b4c0a, 0x560b4c0a, 0x560b4c0a } },
+	{ 0xa310,
+		{ 0x641a600f, 0x641a600f, 0x641a600f } },
+	{ 0xa314,
+		{ 0x784f6e1b, 0x784f6e1b, 0x784f6e1b } },
+	{ 0xa318,
+		{ 0x868f7c5a, 0x868f7c5a, 0x868f7c5a } },
+	{ 0xa31c,
+		{ 0x8ecf865b, 0x8ecf865b, 0x8ecf865b } },
+	{ 0xa320,
+		{ 0x9d4f970f, 0x9d4f970f, 0x9d4f970f } },
+	{ 0xa324,
+		{ 0xa5cfa18f, 0xa5cfa18f, 0xa5cfa18f } },
+	{ 0xa328,
+		{ 0xb55faf1f, 0xb55faf1f, 0xb55faf1f } },
+	{ 0xa32c,
+		{ 0xbddfb99f, 0xbddfb99f, 0xbddfb99f } },
+	{ 0xa330,
+		{ 0xcd7fc73f, 0xcd7fc73f, 0xcd7fc73f } },
+	{ 0xa334,
+		{ 0xd5ffd1bf, 0xd5ffd1bf, 0xd5ffd1bf } },
+	{ 0xa338,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0xa33c,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0xa340,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0xa344,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 0xa348,
+		{ 0x3fffffff, 0x3fffffff, 0x3fffffff } },
+	{ 0xa34c,
+		{ 0x3fffffff, 0x3fffffff, 0x3fffffff } },
+	{ 0xa350,
+		{ 0x3fffffff, 0x3fffffff, 0x3fffffff } },
+	{ 0xa354,
+		{ 0x0003ffff, 0x0003ffff, 0x0003ffff } },
+	{ 0xa358,
+		{ 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f } },
+	{ 0xa35c,
+		{ 0x066c420f, 0x066c420f, 0x066c420f } },
+	{ 0xa360,
+		{ 0x0f282207, 0x0f282207, 0x0f282207 } },
+	{ 0xa364,
+		{ 0x17601685, 0x17601685, 0x17601685 } },
+	{ 0xa368,
+		{ 0x1f801104, 0x1f801104, 0x1f801104 } },
+	{ 0xa36c,
+		{ 0x37a00c03, 0x37a00c03, 0x37a00c03 } },
+	{ 0xa370,
+		{ 0x3fc40883, 0x3fc40883, 0x3fc40883 } },
+	{ 0xa374,
+		{ 0x57c00803, 0x57c00803, 0x57c00803 } },
+	{ 0xa378,
+		{ 0x5fd80682, 0x5fd80682, 0x5fd80682 } },
+	{ 0xa37c,
+		{ 0x7fe00482, 0x7fe00482, 0x7fe00482 } },
+	{ 0xa380,
+		{ 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba } },
+	{ 0xa384,
+		{ 0xf3307ff0, 0xf3307ff0, 0xf3307ff0 } },
+};
+
 /*
 /*
  * Initial BaseBand Gain settings for RF5111/5112 (AR5210 comes with
  * Initial BaseBand Gain settings for RF5111/5112 (AR5210 comes with
  * RF5110 only so initial BB Gain settings are included in AR5K_AR5210_INI)
  * RF5110 only so initial BB Gain settings are included in AR5K_AR5210_INI)
@@ -1290,29 +1491,57 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel)
 
 
 		/* Second set of mode-specific settings */
 		/* Second set of mode-specific settings */
 		if (ah->ah_radio == AR5K_RF5111){
 		if (ah->ah_radio == AR5K_RF5111){
+
 			ath5k_hw_ini_mode_registers(ah,
 			ath5k_hw_ini_mode_registers(ah,
 					ARRAY_SIZE(ar5212_rf5111_ini_mode_end),
 					ARRAY_SIZE(ar5212_rf5111_ini_mode_end),
 					ar5212_rf5111_ini_mode_end, mode);
 					ar5212_rf5111_ini_mode_end, mode);
+
 			/* Baseband gain table */
 			/* Baseband gain table */
 			ath5k_hw_ini_registers(ah,
 			ath5k_hw_ini_registers(ah,
 					ARRAY_SIZE(rf5111_ini_bbgain),
 					ARRAY_SIZE(rf5111_ini_bbgain),
 					rf5111_ini_bbgain, change_channel);
 					rf5111_ini_bbgain, change_channel);
+
 		} else if (ah->ah_radio == AR5K_RF5112){
 		} else if (ah->ah_radio == AR5K_RF5112){
+
 			ath5k_hw_ini_mode_registers(ah,
 			ath5k_hw_ini_mode_registers(ah,
 					ARRAY_SIZE(ar5212_rf5112_ini_mode_end),
 					ARRAY_SIZE(ar5212_rf5112_ini_mode_end),
 					ar5212_rf5112_ini_mode_end, mode);
 					ar5212_rf5112_ini_mode_end, mode);
-			/* Baseband gain table */
+
 			ath5k_hw_ini_registers(ah,
 			ath5k_hw_ini_registers(ah,
 					ARRAY_SIZE(rf5112_ini_bbgain),
 					ARRAY_SIZE(rf5112_ini_bbgain),
 					rf5112_ini_bbgain, change_channel);
 					rf5112_ini_bbgain, change_channel);
+
 		} else if (ah->ah_radio == AR5K_RF5413){
 		} else if (ah->ah_radio == AR5K_RF5413){
+
 			ath5k_hw_ini_mode_registers(ah,
 			ath5k_hw_ini_mode_registers(ah,
 					ARRAY_SIZE(rf5413_ini_mode_end),
 					ARRAY_SIZE(rf5413_ini_mode_end),
 					rf5413_ini_mode_end, mode);
 					rf5413_ini_mode_end, mode);
+
+			ath5k_hw_ini_registers(ah,
+					ARRAY_SIZE(rf5112_ini_bbgain),
+					rf5112_ini_bbgain, change_channel);
+
+		} else if (ah->ah_radio == AR5K_RF2413) {
+
+			if (mode < 2) {
+				ATH5K_ERR(ah->ah_sc,
+					"unsupported channel mode: %d\n", mode);
+				return -EINVAL;
+			}
+			mode = mode - 2;
+
+			/* Override a setting from ar5212_ini */
+			ath5k_hw_reg_write(ah, 0x018830c6, AR5K_PHY(648));
+
+			ath5k_hw_ini_mode_registers(ah,
+					ARRAY_SIZE(rf2413_ini_mode_end),
+					rf2413_ini_mode_end, mode);
+
 			/* Baseband gain table */
 			/* Baseband gain table */
 			ath5k_hw_ini_registers(ah,
 			ath5k_hw_ini_registers(ah,
 					ARRAY_SIZE(rf5112_ini_bbgain),
 					ARRAY_SIZE(rf5112_ini_bbgain),
 					rf5112_ini_bbgain, change_channel);
 					rf5112_ini_bbgain, change_channel);
+
 		}
 		}
 	/* For AR5211 */
 	/* For AR5211 */
 	} else if (ah->ah_version == AR5K_AR5211) {
 	} else if (ah->ah_version == AR5K_AR5211) {

+ 170 - 4
drivers/net/wireless/ath5k/phy.c

@@ -666,6 +666,75 @@ static const struct ath5k_ini_rf rfregs_5413[] = {
 	    { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } },
 	    { 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e, 0x0000000e } },
 };
 };
 
 
+/* RF2413/2414 mode-specific init registers */
+static const struct ath5k_ini_rf rfregs_2413[] = {
+	{ 1, AR5K_RF_BUFFER_CONTROL_4,
+		{ 0x00000020, 0x00000020, 0x00000020 } },
+	{ 2, AR5K_RF_BUFFER_CONTROL_3,
+		{ 0x02001408, 0x02001408, 0x02001408 } },
+	{ 3, AR5K_RF_BUFFER_CONTROL_6,
+		{ 0x00e020c0, 0x00e020c0, 0x00e020c0 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0xf0000000, 0xf0000000, 0xf0000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x03000000, 0x03000000, 0x03000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x40400000, 0x40400000, 0x40400000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x65050000, 0x65050000, 0x65050000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00420000, 0x00420000, 0x00420000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00b50000, 0x00b50000, 0x00b50000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00030000, 0x00030000, 0x00030000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00f70000, 0x00f70000, 0x00f70000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x009d0000, 0x009d0000, 0x009d0000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00220000, 0x00220000, 0x00220000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x04220000, 0x04220000, 0x04220000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00230018, 0x00230018, 0x00230018 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00280050, 0x00280050, 0x00280050 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x005000c3, 0x005000c3, 0x005000c3 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x0004007f, 0x0004007f, 0x0004007f } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00000458, 0x00000458, 0x00000458 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x00000000, 0x00000000, 0x00000000 } },
+	{ 6, AR5K_RF_BUFFER,
+		{ 0x0000c000, 0x0000c000, 0x0000c000 } },
+	{ 6, AR5K_RF_BUFFER_CONTROL_5,
+		{ 0x00400230, 0x00400230, 0x00400230 } },
+	{ 7, AR5K_RF_BUFFER,
+		{ 0x00006400, 0x00006400, 0x00006400 } },
+	{ 7, AR5K_RF_BUFFER,
+		{ 0x00000800, 0x00000800, 0x00000800 } },
+	{ 7, AR5K_RF_BUFFER_CONTROL_2,
+		{ 0x0000000e, 0x0000000e, 0x0000000e } },
+};
 
 
 /* Initial RF Gain settings for RF5112 */
 /* Initial RF Gain settings for RF5112 */
 static const struct ath5k_ini_rfgain rfgain_5112[] = {
 static const struct ath5k_ini_rfgain rfgain_5112[] = {
@@ -805,6 +874,74 @@ static const struct ath5k_ini_rfgain rfgain_5413[] = {
 	{ AR5K_RF_GAIN(63),	{ 0x000000f9, 0x000000f9 } },
 	{ AR5K_RF_GAIN(63),	{ 0x000000f9, 0x000000f9 } },
 };
 };
 
 
+/* Initial RF Gain settings for RF2413 */
+static const struct ath5k_ini_rfgain rfgain_2413[] = {
+	{ AR5K_RF_GAIN(0), { 0x00000000 } },
+	{ AR5K_RF_GAIN(1), { 0x00000040 } },
+	{ AR5K_RF_GAIN(2), { 0x00000080 } },
+	{ AR5K_RF_GAIN(3), { 0x00000181 } },
+	{ AR5K_RF_GAIN(4), { 0x000001c1 } },
+	{ AR5K_RF_GAIN(5), { 0x00000001 } },
+	{ AR5K_RF_GAIN(6), { 0x00000041 } },
+	{ AR5K_RF_GAIN(7), { 0x00000081 } },
+	{ AR5K_RF_GAIN(8), { 0x00000168 } },
+	{ AR5K_RF_GAIN(9), { 0x000001a8 } },
+	{ AR5K_RF_GAIN(10), { 0x000001e8 } },
+	{ AR5K_RF_GAIN(11), { 0x00000028 } },
+	{ AR5K_RF_GAIN(12), { 0x00000068 } },
+	{ AR5K_RF_GAIN(13), { 0x00000189 } },
+	{ AR5K_RF_GAIN(14), { 0x000001c9 } },
+	{ AR5K_RF_GAIN(15), { 0x00000009 } },
+	{ AR5K_RF_GAIN(16), { 0x00000049 } },
+	{ AR5K_RF_GAIN(17), { 0x00000089 } },
+	{ AR5K_RF_GAIN(18), { 0x00000190 } },
+	{ AR5K_RF_GAIN(19), { 0x000001d0 } },
+	{ AR5K_RF_GAIN(20), { 0x00000010 } },
+	{ AR5K_RF_GAIN(21), { 0x00000050 } },
+	{ AR5K_RF_GAIN(22), { 0x00000090 } },
+	{ AR5K_RF_GAIN(23), { 0x00000191 } },
+	{ AR5K_RF_GAIN(24), { 0x000001d1 } },
+	{ AR5K_RF_GAIN(25), { 0x00000011 } },
+	{ AR5K_RF_GAIN(26), { 0x00000051 } },
+	{ AR5K_RF_GAIN(27), { 0x00000091 } },
+	{ AR5K_RF_GAIN(28), { 0x00000178 } },
+	{ AR5K_RF_GAIN(29), { 0x000001b8 } },
+	{ AR5K_RF_GAIN(30), { 0x000001f8 } },
+	{ AR5K_RF_GAIN(31), { 0x00000038 } },
+	{ AR5K_RF_GAIN(32), { 0x00000078 } },
+	{ AR5K_RF_GAIN(33), { 0x00000199 } },
+	{ AR5K_RF_GAIN(34), { 0x000001d9 } },
+	{ AR5K_RF_GAIN(35), { 0x00000019 } },
+	{ AR5K_RF_GAIN(36), { 0x00000059 } },
+	{ AR5K_RF_GAIN(37), { 0x00000099 } },
+	{ AR5K_RF_GAIN(38), { 0x000000d9 } },
+	{ AR5K_RF_GAIN(39), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(40), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(41), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(42), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(43), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(44), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(45), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(46), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(47), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(48), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(49), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(50), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(51), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(52), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(53), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(54), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(55), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(56), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(57), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(58), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(59), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(60), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(61), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(62), { 0x000000f9 } },
+	{ AR5K_RF_GAIN(63), { 0x000000f9 } },
+};
+
 static const struct ath5k_gain_opt rfgain_opt_5112 = {
 static const struct ath5k_gain_opt rfgain_opt_5112 = {
 	1,
 	1,
 	8,
 	8,
@@ -955,7 +1092,6 @@ static s32 ath5k_hw_rfregs_gain_adjust(struct ath5k_hw *ah)
 		go = &rfgain_opt_5111;
 		go = &rfgain_opt_5111;
 		break;
 		break;
 	case AR5K_RF5112:
 	case AR5K_RF5112:
-	case AR5K_RF5413: /* ??? */
 		go = &rfgain_opt_5112;
 		go = &rfgain_opt_5112;
 		break;
 		break;
 	default:
 	default:
@@ -1226,8 +1362,21 @@ static int ath5k_hw_rf5413_rfregs(struct ath5k_hw *ah,
 
 
 	rf = ah->ah_rf_banks;
 	rf = ah->ah_rf_banks;
 
 
-	rf_ini = rfregs_5413;
-	rf_size = ARRAY_SIZE(rfregs_5413);
+	if (ah->ah_radio == AR5K_RF5413) {
+		rf_ini = rfregs_5413;
+		rf_size = ARRAY_SIZE(rfregs_5413);
+	} else if (ah->ah_radio == AR5K_RF2413) {
+		rf_ini = rfregs_2413;
+		rf_size = ARRAY_SIZE(rfregs_2413);
+		if (mode < 2) {
+			ATH5K_ERR(ah->ah_sc,
+				"invalid channel mode: %i\n", mode);
+			return -EINVAL;
+		}
+		mode = mode - 2;
+	} else {
+		return -EINVAL;
+	}
 
 
 	/* Copy values to modify them */
 	/* Copy values to modify them */
 	for (i = 0; i < rf_size; i++) {
 	for (i = 0; i < rf_size; i++) {
@@ -1286,6 +1435,10 @@ int ath5k_hw_rfregs(struct ath5k_hw *ah, struct ieee80211_channel *channel,
 		ah->ah_rf_banks_size = sizeof(rfregs_5413);
 		ah->ah_rf_banks_size = sizeof(rfregs_5413);
 		func = ath5k_hw_rf5413_rfregs;
 		func = ath5k_hw_rf5413_rfregs;
 		break;
 		break;
+	case AR5K_RF2413:
+		ah->ah_rf_banks_size = sizeof(rfregs_2413);
+		func = ath5k_hw_rf5413_rfregs;
+		break;
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
@@ -1324,6 +1477,11 @@ int ath5k_hw_rfgain(struct ath5k_hw *ah, unsigned int freq)
 		ath5k_rfg = rfgain_5413;
 		ath5k_rfg = rfgain_5413;
 		size = ARRAY_SIZE(rfgain_5413);
 		size = ARRAY_SIZE(rfgain_5413);
 		break;
 		break;
+	case AR5K_RF2413:
+		ath5k_rfg = rfgain_2413;
+		size = ARRAY_SIZE(rfgain_2413);
+		freq = 0; /* only 2Ghz */
+		break;
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
@@ -1398,7 +1556,6 @@ int ath5k_hw_set_rfgain_opt(struct ath5k_hw *ah)
 		ah->ah_gain.g_active = 1;
 		ah->ah_gain.g_active = 1;
 		break;
 		break;
 	case AR5K_RF5112:
 	case AR5K_RF5112:
-	case AR5K_RF5413: /* ??? */
 		ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default;
 		ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default;
 		ah->ah_gain.g_step =
 		ah->ah_gain.g_step =
 		    &rfgain_opt_5112.go_step[ah->ah_gain.g_step_idx];
 		    &rfgain_opt_5112.go_step[ah->ah_gain.g_step_idx];
@@ -2019,6 +2176,15 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	/*
+	 * RF2413 for some reason can't
+	 * transmit anything if we call
+	 * this funtion, so we skip it
+	 * until we fix txpower.
+	 */
+	if (ah->ah_radio == AR5K_RF2413)
+		return 0;
+
 	/* Reset TX power values */
 	/* Reset TX power values */
 	memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
 	memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
 	ah->ah_txpower.txp_tpc = tpc;
 	ah->ah_txpower.txp_tpc = tpc;

+ 3 - 1
drivers/net/wireless/ath5k/reg.h

@@ -1923,7 +1923,9 @@ after DFS is enabled */
 #define AR5K_PHY_SDELAY_32MHZ		0x000000ff
 #define AR5K_PHY_SDELAY_32MHZ		0x000000ff
 #define AR5K_PHY_SPENDING		0x99f8
 #define AR5K_PHY_SPENDING		0x99f8
 #define AR5K_PHY_SPENDING_RF5111	0x00000018
 #define AR5K_PHY_SPENDING_RF5111	0x00000018
-#define AR5K_PHY_SPENDING_RF5112	0x00000014
+#define AR5K_PHY_SPENDING_RF5112	0x00000014 /* <- i 've only seen this on 2425 dumps ! */
+#define AR5K_PHY_SPENDING_RF5112A	0x0000000e /* but since i only have 5112A-based chips */
+#define AR5K_PHY_SPENDING_RF5424	0x00000012 /* to test it might be also for old 5112.  */
 
 
 /*
 /*
  * Misc PHY/radio registers [5110 - 5111]
  * Misc PHY/radio registers [5110 - 5111]

+ 44 - 9
drivers/net/wireless/b43/b43.h

@@ -99,6 +99,8 @@
 #define B43_MMIO_TSF_2			0x636	/* core rev < 3 only */
 #define B43_MMIO_TSF_2			0x636	/* core rev < 3 only */
 #define B43_MMIO_TSF_3			0x638	/* core rev < 3 only */
 #define B43_MMIO_TSF_3			0x638	/* core rev < 3 only */
 #define B43_MMIO_RNG			0x65A
 #define B43_MMIO_RNG			0x65A
+#define B43_MMIO_IFSCTL			0x688 /* Interframe space control */
+#define  B43_MMIO_IFSCTL_USE_EDCF	0x0004
 #define B43_MMIO_POWERUP_DELAY		0x6A8
 #define B43_MMIO_POWERUP_DELAY		0x6A8
 
 
 /* SPROM boardflags_lo values */
 /* SPROM boardflags_lo values */
@@ -587,15 +589,13 @@ struct b43_phy {
 
 
 /* Data structures for DMA transmission, per 80211 core. */
 /* Data structures for DMA transmission, per 80211 core. */
 struct b43_dma {
 struct b43_dma {
-	struct b43_dmaring *tx_ring0;
-	struct b43_dmaring *tx_ring1;
-	struct b43_dmaring *tx_ring2;
-	struct b43_dmaring *tx_ring3;
-	struct b43_dmaring *tx_ring4;
-	struct b43_dmaring *tx_ring5;
-
-	struct b43_dmaring *rx_ring0;
-	struct b43_dmaring *rx_ring3;	/* only available on core.rev < 5 */
+	struct b43_dmaring *tx_ring_AC_BK; /* Background */
+	struct b43_dmaring *tx_ring_AC_BE; /* Best Effort */
+	struct b43_dmaring *tx_ring_AC_VI; /* Video */
+	struct b43_dmaring *tx_ring_AC_VO; /* Voice */
+	struct b43_dmaring *tx_ring_mcast; /* Multicast */
+
+	struct b43_dmaring *rx_ring;
 };
 };
 
 
 /* Context information for a noise calculation (Link Quality). */
 /* Context information for a noise calculation (Link Quality). */
@@ -621,6 +621,35 @@ struct b43_key {
 	u8 algorithm;
 	u8 algorithm;
 };
 };
 
 
+/* SHM offsets to the QOS data structures for the 4 different queues. */
+#define B43_QOS_PARAMS(queue)	(B43_SHM_SH_EDCFQ + \
+				 (B43_NR_QOSPARAMS * sizeof(u16) * (queue)))
+#define B43_QOS_BACKGROUND	B43_QOS_PARAMS(0)
+#define B43_QOS_BESTEFFORT	B43_QOS_PARAMS(1)
+#define B43_QOS_VIDEO		B43_QOS_PARAMS(2)
+#define B43_QOS_VOICE		B43_QOS_PARAMS(3)
+
+/* QOS parameter hardware data structure offsets. */
+#define B43_NR_QOSPARAMS	22
+enum {
+	B43_QOSPARAM_TXOP = 0,
+	B43_QOSPARAM_CWMIN,
+	B43_QOSPARAM_CWMAX,
+	B43_QOSPARAM_CWCUR,
+	B43_QOSPARAM_AIFS,
+	B43_QOSPARAM_BSLOTS,
+	B43_QOSPARAM_REGGAP,
+	B43_QOSPARAM_STATUS,
+};
+
+/* QOS parameters for a queue. */
+struct b43_qos_params {
+	/* The QOS parameters */
+	struct ieee80211_tx_queue_params p;
+	/* Does this need to get uploaded to hardware? */
+	bool need_hw_update;
+};
+
 struct b43_wldev;
 struct b43_wldev;
 
 
 /* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */
 /* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */
@@ -673,6 +702,12 @@ struct b43_wl {
 	struct sk_buff *current_beacon;
 	struct sk_buff *current_beacon;
 	bool beacon0_uploaded;
 	bool beacon0_uploaded;
 	bool beacon1_uploaded;
 	bool beacon1_uploaded;
+
+	/* The current QOS parameters for the 4 queues.
+	 * This is protected by the irq_lock. */
+	struct b43_qos_params qos_params[4];
+	/* Workqueue for updating QOS parameters in hardware. */
+	struct work_struct qos_update_work;
 };
 };
 
 
 /* In-memory representation of a cached microcode file. */
 /* In-memory representation of a cached microcode file. */

+ 178 - 208
drivers/net/wireless/b43/dma.c

@@ -38,6 +38,7 @@
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/skbuff.h>
 #include <linux/skbuff.h>
 #include <linux/etherdevice.h>
 #include <linux/etherdevice.h>
+#include <asm/div64.h>
 
 
 
 
 /* 32bit DMA ops. */
 /* 32bit DMA ops. */
@@ -291,52 +292,6 @@ static inline int request_slot(struct b43_dmaring *ring)
 	return slot;
 	return slot;
 }
 }
 
 
-/* Mac80211-queue to b43-ring mapping */
-static struct b43_dmaring *priority_to_txring(struct b43_wldev *dev,
-					      int queue_priority)
-{
-	struct b43_dmaring *ring;
-
-/*FIXME: For now we always run on TX-ring-1 */
-	return dev->dma.tx_ring1;
-
-	/* 0 = highest priority */
-	switch (queue_priority) {
-	default:
-		B43_WARN_ON(1);
-		/* fallthrough */
-	case 0:
-		ring = dev->dma.tx_ring3;
-		break;
-	case 1:
-		ring = dev->dma.tx_ring2;
-		break;
-	case 2:
-		ring = dev->dma.tx_ring1;
-		break;
-	case 3:
-		ring = dev->dma.tx_ring0;
-		break;
-	}
-
-	return ring;
-}
-
-/* b43-ring to mac80211-queue mapping */
-static inline int txring_to_priority(struct b43_dmaring *ring)
-{
-	static const u8 idx_to_prio[] = { 3, 2, 1, 0, };
-	unsigned int index;
-
-/*FIXME: have only one queue, for now */
-	return 0;
-
-	index = ring->index;
-	if (B43_WARN_ON(index >= ARRAY_SIZE(idx_to_prio)))
-		index = 0;
-	return idx_to_prio[index];
-}
-
 static u16 b43_dmacontroller_base(enum b43_dmatype type, int controller_idx)
 static u16 b43_dmacontroller_base(enum b43_dmatype type, int controller_idx)
 {
 {
 	static const u16 map64[] = {
 	static const u16 map64[] = {
@@ -924,16 +879,52 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
 	goto out;
 	goto out;
 }
 }
 
 
+#define divide(a, b)	({	\
+	typeof(a) __a = a;	\
+	do_div(__a, b);		\
+	__a;			\
+  })
+
+#define modulo(a, b)	({	\
+	typeof(a) __a = a;	\
+	do_div(__a, b);		\
+  })
+
 /* Main cleanup function. */
 /* Main cleanup function. */
-static void b43_destroy_dmaring(struct b43_dmaring *ring)
+static void b43_destroy_dmaring(struct b43_dmaring *ring,
+				const char *ringname)
 {
 {
 	if (!ring)
 	if (!ring)
 		return;
 		return;
 
 
-	b43dbg(ring->dev->wl, "DMA-%u 0x%04X (%s) max used slots: %d/%d\n",
-	       (unsigned int)(ring->type),
-	       ring->mmio_base,
-	       (ring->tx) ? "TX" : "RX", ring->max_used_slots, ring->nr_slots);
+#ifdef CONFIG_B43_DEBUG
+	{
+		/* Print some statistics. */
+		u64 failed_packets = ring->nr_failed_tx_packets;
+		u64 succeed_packets = ring->nr_succeed_tx_packets;
+		u64 nr_packets = failed_packets + succeed_packets;
+		u64 permille_failed = 0, average_tries = 0;
+
+		if (nr_packets)
+			permille_failed = divide(failed_packets * 1000, nr_packets);
+		if (nr_packets)
+			average_tries = divide(ring->nr_total_packet_tries * 100, nr_packets);
+
+		b43dbg(ring->dev->wl, "DMA-%u %s: "
+		       "Used slots %d/%d, Failed frames %llu/%llu = %llu.%01llu%%, "
+		       "Average tries %llu.%02llu\n",
+		       (unsigned int)(ring->type), ringname,
+		       ring->max_used_slots,
+		       ring->nr_slots,
+		       (unsigned long long)failed_packets,
+		       (unsigned long long)nr_packets,
+		       (unsigned long long)divide(permille_failed, 10),
+		       (unsigned long long)modulo(permille_failed, 10),
+		       (unsigned long long)divide(average_tries, 100),
+		       (unsigned long long)modulo(average_tries, 100));
+	}
+#endif /* DEBUG */
+
 	/* Device IRQs are disabled prior entering this function,
 	/* Device IRQs are disabled prior entering this function,
 	 * so no need to take care of concurrency with rx handler stuff.
 	 * so no need to take care of concurrency with rx handler stuff.
 	 */
 	 */
@@ -946,33 +937,26 @@ static void b43_destroy_dmaring(struct b43_dmaring *ring)
 	kfree(ring);
 	kfree(ring);
 }
 }
 
 
+#define destroy_ring(dma, ring) do {				\
+	b43_destroy_dmaring((dma)->ring, __stringify(ring));	\
+	(dma)->ring = NULL;					\
+    } while (0)
+
 void b43_dma_free(struct b43_wldev *dev)
 void b43_dma_free(struct b43_wldev *dev)
 {
 {
 	struct b43_dma *dma = &dev->dma;
 	struct b43_dma *dma = &dev->dma;
 
 
-	b43_destroy_dmaring(dma->rx_ring3);
-	dma->rx_ring3 = NULL;
-	b43_destroy_dmaring(dma->rx_ring0);
-	dma->rx_ring0 = NULL;
-
-	b43_destroy_dmaring(dma->tx_ring5);
-	dma->tx_ring5 = NULL;
-	b43_destroy_dmaring(dma->tx_ring4);
-	dma->tx_ring4 = NULL;
-	b43_destroy_dmaring(dma->tx_ring3);
-	dma->tx_ring3 = NULL;
-	b43_destroy_dmaring(dma->tx_ring2);
-	dma->tx_ring2 = NULL;
-	b43_destroy_dmaring(dma->tx_ring1);
-	dma->tx_ring1 = NULL;
-	b43_destroy_dmaring(dma->tx_ring0);
-	dma->tx_ring0 = NULL;
+	destroy_ring(dma, rx_ring);
+	destroy_ring(dma, tx_ring_AC_BK);
+	destroy_ring(dma, tx_ring_AC_BE);
+	destroy_ring(dma, tx_ring_AC_VI);
+	destroy_ring(dma, tx_ring_AC_VO);
+	destroy_ring(dma, tx_ring_mcast);
 }
 }
 
 
 int b43_dma_init(struct b43_wldev *dev)
 int b43_dma_init(struct b43_wldev *dev)
 {
 {
 	struct b43_dma *dma = &dev->dma;
 	struct b43_dma *dma = &dev->dma;
-	struct b43_dmaring *ring;
 	int err;
 	int err;
 	u64 dmamask;
 	u64 dmamask;
 	enum b43_dmatype type;
 	enum b43_dmatype type;
@@ -1002,83 +986,57 @@ int b43_dma_init(struct b43_wldev *dev)
 
 
 	err = -ENOMEM;
 	err = -ENOMEM;
 	/* setup TX DMA channels. */
 	/* setup TX DMA channels. */
-	ring = b43_setup_dmaring(dev, 0, 1, type);
-	if (!ring)
+	dma->tx_ring_AC_BK = b43_setup_dmaring(dev, 0, 1, type);
+	if (!dma->tx_ring_AC_BK)
 		goto out;
 		goto out;
-	dma->tx_ring0 = ring;
 
 
-	ring = b43_setup_dmaring(dev, 1, 1, type);
-	if (!ring)
-		goto err_destroy_tx0;
-	dma->tx_ring1 = ring;
+	dma->tx_ring_AC_BE = b43_setup_dmaring(dev, 1, 1, type);
+	if (!dma->tx_ring_AC_BE)
+		goto err_destroy_bk;
 
 
-	ring = b43_setup_dmaring(dev, 2, 1, type);
-	if (!ring)
-		goto err_destroy_tx1;
-	dma->tx_ring2 = ring;
+	dma->tx_ring_AC_VI = b43_setup_dmaring(dev, 2, 1, type);
+	if (!dma->tx_ring_AC_VI)
+		goto err_destroy_be;
 
 
-	ring = b43_setup_dmaring(dev, 3, 1, type);
-	if (!ring)
-		goto err_destroy_tx2;
-	dma->tx_ring3 = ring;
+	dma->tx_ring_AC_VO = b43_setup_dmaring(dev, 3, 1, type);
+	if (!dma->tx_ring_AC_VO)
+		goto err_destroy_vi;
 
 
-	ring = b43_setup_dmaring(dev, 4, 1, type);
-	if (!ring)
-		goto err_destroy_tx3;
-	dma->tx_ring4 = ring;
+	dma->tx_ring_mcast = b43_setup_dmaring(dev, 4, 1, type);
+	if (!dma->tx_ring_mcast)
+		goto err_destroy_vo;
 
 
-	ring = b43_setup_dmaring(dev, 5, 1, type);
-	if (!ring)
-		goto err_destroy_tx4;
-	dma->tx_ring5 = ring;
+	/* setup RX DMA channel. */
+	dma->rx_ring = b43_setup_dmaring(dev, 0, 0, type);
+	if (!dma->rx_ring)
+		goto err_destroy_mcast;
 
 
-	/* setup RX DMA channels. */
-	ring = b43_setup_dmaring(dev, 0, 0, type);
-	if (!ring)
-		goto err_destroy_tx5;
-	dma->rx_ring0 = ring;
-
-	if (dev->dev->id.revision < 5) {
-		ring = b43_setup_dmaring(dev, 3, 0, type);
-		if (!ring)
-			goto err_destroy_rx0;
-		dma->rx_ring3 = ring;
-	}
+	/* No support for the TX status DMA ring. */
+	B43_WARN_ON(dev->dev->id.revision < 5);
 
 
 	b43dbg(dev->wl, "%u-bit DMA initialized\n",
 	b43dbg(dev->wl, "%u-bit DMA initialized\n",
 	       (unsigned int)type);
 	       (unsigned int)type);
 	err = 0;
 	err = 0;
-      out:
+out:
 	return err;
 	return err;
 
 
-      err_destroy_rx0:
-	b43_destroy_dmaring(dma->rx_ring0);
-	dma->rx_ring0 = NULL;
-      err_destroy_tx5:
-	b43_destroy_dmaring(dma->tx_ring5);
-	dma->tx_ring5 = NULL;
-      err_destroy_tx4:
-	b43_destroy_dmaring(dma->tx_ring4);
-	dma->tx_ring4 = NULL;
-      err_destroy_tx3:
-	b43_destroy_dmaring(dma->tx_ring3);
-	dma->tx_ring3 = NULL;
-      err_destroy_tx2:
-	b43_destroy_dmaring(dma->tx_ring2);
-	dma->tx_ring2 = NULL;
-      err_destroy_tx1:
-	b43_destroy_dmaring(dma->tx_ring1);
-	dma->tx_ring1 = NULL;
-      err_destroy_tx0:
-	b43_destroy_dmaring(dma->tx_ring0);
-	dma->tx_ring0 = NULL;
-	goto out;
+err_destroy_mcast:
+	destroy_ring(dma, tx_ring_mcast);
+err_destroy_vo:
+	destroy_ring(dma, tx_ring_AC_VO);
+err_destroy_vi:
+	destroy_ring(dma, tx_ring_AC_VI);
+err_destroy_be:
+	destroy_ring(dma, tx_ring_AC_BE);
+err_destroy_bk:
+	destroy_ring(dma, tx_ring_AC_BK);
+	return err;
 }
 }
 
 
 /* Generate a cookie for the TX header. */
 /* Generate a cookie for the TX header. */
 static u16 generate_cookie(struct b43_dmaring *ring, int slot)
 static u16 generate_cookie(struct b43_dmaring *ring, int slot)
 {
 {
-	u16 cookie = 0x1000;
+	u16 cookie;
 
 
 	/* Use the upper 4 bits of the cookie as
 	/* Use the upper 4 bits of the cookie as
 	 * DMA controller ID and store the slot number
 	 * DMA controller ID and store the slot number
@@ -1088,30 +1046,9 @@ static u16 generate_cookie(struct b43_dmaring *ring, int slot)
 	 * It can also not be 0xFFFF because that is special
 	 * It can also not be 0xFFFF because that is special
 	 * for multicast frames.
 	 * for multicast frames.
 	 */
 	 */
-	switch (ring->index) {
-	case 0:
-		cookie = 0x1000;
-		break;
-	case 1:
-		cookie = 0x2000;
-		break;
-	case 2:
-		cookie = 0x3000;
-		break;
-	case 3:
-		cookie = 0x4000;
-		break;
-	case 4:
-		cookie = 0x5000;
-		break;
-	case 5:
-		cookie = 0x6000;
-		break;
-	default:
-		B43_WARN_ON(1);
-	}
+	cookie = (((u16)ring->index + 1) << 12);
 	B43_WARN_ON(slot & ~0x0FFF);
 	B43_WARN_ON(slot & ~0x0FFF);
-	cookie |= (u16) slot;
+	cookie |= (u16)slot;
 
 
 	return cookie;
 	return cookie;
 }
 }
@@ -1125,22 +1062,19 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot)
 
 
 	switch (cookie & 0xF000) {
 	switch (cookie & 0xF000) {
 	case 0x1000:
 	case 0x1000:
-		ring = dma->tx_ring0;
+		ring = dma->tx_ring_AC_BK;
 		break;
 		break;
 	case 0x2000:
 	case 0x2000:
-		ring = dma->tx_ring1;
+		ring = dma->tx_ring_AC_BE;
 		break;
 		break;
 	case 0x3000:
 	case 0x3000:
-		ring = dma->tx_ring2;
+		ring = dma->tx_ring_AC_VI;
 		break;
 		break;
 	case 0x4000:
 	case 0x4000:
-		ring = dma->tx_ring3;
+		ring = dma->tx_ring_AC_VO;
 		break;
 		break;
 	case 0x5000:
 	case 0x5000:
-		ring = dma->tx_ring4;
-		break;
-	case 0x6000:
-		ring = dma->tx_ring5;
+		ring = dma->tx_ring_mcast;
 		break;
 		break;
 	default:
 	default:
 		B43_WARN_ON(1);
 		B43_WARN_ON(1);
@@ -1272,6 +1206,37 @@ static inline int should_inject_overflow(struct b43_dmaring *ring)
 	return 0;
 	return 0;
 }
 }
 
 
+/* Static mapping of mac80211's queues (priorities) to b43 DMA rings. */
+static struct b43_dmaring * select_ring_by_priority(struct b43_wldev *dev,
+						    u8 queue_prio)
+{
+	struct b43_dmaring *ring;
+
+	if (b43_modparam_qos) {
+		/* 0 = highest priority */
+		switch (queue_prio) {
+		default:
+			B43_WARN_ON(1);
+			/* fallthrough */
+		case 0:
+			ring = dev->dma.tx_ring_AC_VO;
+			break;
+		case 1:
+			ring = dev->dma.tx_ring_AC_VI;
+			break;
+		case 2:
+			ring = dev->dma.tx_ring_AC_BE;
+			break;
+		case 3:
+			ring = dev->dma.tx_ring_AC_BK;
+			break;
+		}
+	} else
+		ring = dev->dma.tx_ring_AC_BE;
+
+	return ring;
+}
+
 int b43_dma_tx(struct b43_wldev *dev,
 int b43_dma_tx(struct b43_wldev *dev,
 	       struct sk_buff *skb, struct ieee80211_tx_control *ctl)
 	       struct sk_buff *skb, struct ieee80211_tx_control *ctl)
 {
 {
@@ -1288,13 +1253,13 @@ int b43_dma_tx(struct b43_wldev *dev,
 	hdr = (struct ieee80211_hdr *)skb->data;
 	hdr = (struct ieee80211_hdr *)skb->data;
 	if (ctl->flags & IEEE80211_TXCTL_SEND_AFTER_DTIM) {
 	if (ctl->flags & IEEE80211_TXCTL_SEND_AFTER_DTIM) {
 		/* The multicast ring will be sent after the DTIM */
 		/* The multicast ring will be sent after the DTIM */
-		ring = dev->dma.tx_ring4;
+		ring = dev->dma.tx_ring_mcast;
 		/* Set the more-data bit. Ucode will clear it on
 		/* Set the more-data bit. Ucode will clear it on
 		 * the last frame for us. */
 		 * the last frame for us. */
 		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
 		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
 	} else {
 	} else {
 		/* Decide by priority where to put this frame. */
 		/* Decide by priority where to put this frame. */
-		ring = priority_to_txring(dev, ctl->queue);
+		ring = select_ring_by_priority(dev, ctl->queue);
 	}
 	}
 
 
 	spin_lock_irqsave(&ring->lock, flags);
 	spin_lock_irqsave(&ring->lock, flags);
@@ -1309,6 +1274,11 @@ int b43_dma_tx(struct b43_wldev *dev,
 	 * That would be a mac80211 bug. */
 	 * That would be a mac80211 bug. */
 	B43_WARN_ON(ring->stopped);
 	B43_WARN_ON(ring->stopped);
 
 
+	/* Assign the queue number to the ring (if not already done before)
+	 * so TX status handling can use it. The queue to ring mapping is
+	 * static, so we don't need to store it per frame. */
+	ring->queue_prio = ctl->queue;
+
 	err = dma_tx_fragment(ring, skb, ctl);
 	err = dma_tx_fragment(ring, skb, ctl);
 	if (unlikely(err == -ENOKEY)) {
 	if (unlikely(err == -ENOKEY)) {
 		/* Drop this packet, as we don't have the encryption key
 		/* Drop this packet, as we don't have the encryption key
@@ -1325,7 +1295,7 @@ int b43_dma_tx(struct b43_wldev *dev,
 	if ((free_slots(ring) < SLOTS_PER_PACKET) ||
 	if ((free_slots(ring) < SLOTS_PER_PACKET) ||
 	    should_inject_overflow(ring)) {
 	    should_inject_overflow(ring)) {
 		/* This TX ring is full. */
 		/* This TX ring is full. */
-		ieee80211_stop_queue(dev->wl->hw, txring_to_priority(ring));
+		ieee80211_stop_queue(dev->wl->hw, ctl->queue);
 		ring->stopped = 1;
 		ring->stopped = 1;
 		if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
 		if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
 			b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index);
 			b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index);
@@ -1337,6 +1307,38 @@ out_unlock:
 	return err;
 	return err;
 }
 }
 
 
+static void b43_fill_txstatus_report(struct b43_dmaring *ring,
+				    struct ieee80211_tx_status *report,
+				    const struct b43_txstatus *status)
+{
+	bool frame_failed = 0;
+
+	if (status->acked) {
+		/* The frame was ACKed. */
+		report->flags |= IEEE80211_TX_STATUS_ACK;
+	} else {
+		/* The frame was not ACKed... */
+		if (!(report->control.flags & IEEE80211_TXCTL_NO_ACK)) {
+			/* ...but we expected an ACK. */
+			frame_failed = 1;
+			report->excessive_retries = 1;
+		}
+	}
+	if (status->frame_count == 0) {
+		/* The frame was not transmitted at all. */
+		report->retry_count = 0;
+	} else {
+		report->retry_count = status->frame_count - 1;
+#ifdef CONFIG_B43_DEBUG
+		if (frame_failed)
+			ring->nr_failed_tx_packets++;
+		else
+			ring->nr_succeed_tx_packets++;
+		ring->nr_total_packet_tries += status->frame_count;
+#endif /* DEBUG */
+	}
+}
+
 void b43_dma_handle_txstatus(struct b43_wldev *dev,
 void b43_dma_handle_txstatus(struct b43_wldev *dev,
 			     const struct b43_txstatus *status)
 			     const struct b43_txstatus *status)
 {
 {
@@ -1371,18 +1373,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
 			 * status of the transmission.
 			 * status of the transmission.
 			 * Some fields of txstat are already filled in dma_tx().
 			 * Some fields of txstat are already filled in dma_tx().
 			 */
 			 */
-			if (status->acked) {
-				meta->txstat.flags |= IEEE80211_TX_STATUS_ACK;
-			} else {
-				if (!(meta->txstat.control.flags
-				      & IEEE80211_TXCTL_NO_ACK))
-					meta->txstat.excessive_retries = 1;
-			}
-			if (status->frame_count == 0) {
-				/* The frame was not transmitted at all. */
-				meta->txstat.retry_count = 0;
-			} else
-				meta->txstat.retry_count = status->frame_count - 1;
+			b43_fill_txstatus_report(ring, &(meta->txstat), status);
 			ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb,
 			ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb,
 						    &(meta->txstat));
 						    &(meta->txstat));
 			/* skb is freed by ieee80211_tx_status_irqsafe() */
 			/* skb is freed by ieee80211_tx_status_irqsafe() */
@@ -1404,7 +1395,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
 	dev->stats.last_tx = jiffies;
 	dev->stats.last_tx = jiffies;
 	if (ring->stopped) {
 	if (ring->stopped) {
 		B43_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET);
 		B43_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET);
-		ieee80211_wake_queue(dev->wl->hw, txring_to_priority(ring));
+		ieee80211_wake_queue(dev->wl->hw, ring->queue_prio);
 		ring->stopped = 0;
 		ring->stopped = 0;
 		if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
 		if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
 			b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index);
 			b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index);
@@ -1425,7 +1416,7 @@ void b43_dma_get_tx_stats(struct b43_wldev *dev,
 
 
 	for (i = 0; i < nr_queues; i++) {
 	for (i = 0; i < nr_queues; i++) {
 		data = &(stats->data[i]);
 		data = &(stats->data[i]);
-		ring = priority_to_txring(dev, i);
+		ring = select_ring_by_priority(dev, i);
 
 
 		spin_lock_irqsave(&ring->lock, flags);
 		spin_lock_irqsave(&ring->lock, flags);
 		data->len = ring->used_slots / SLOTS_PER_PACKET;
 		data->len = ring->used_slots / SLOTS_PER_PACKET;
@@ -1451,25 +1442,6 @@ static void dma_rx(struct b43_dmaring *ring, int *slot)
 	sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize);
 	sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize);
 	skb = meta->skb;
 	skb = meta->skb;
 
 
-	if (ring->index == 3) {
-		/* We received an xmit status. */
-		struct b43_hwtxstatus *hw = (struct b43_hwtxstatus *)skb->data;
-		int i = 0;
-
-		while (hw->cookie == 0) {
-			if (i > 100)
-				break;
-			i++;
-			udelay(2);
-			barrier();
-		}
-		b43_handle_hwtxstatus(ring->dev, hw);
-		/* recycle the descriptor buffer. */
-		sync_descbuffer_for_device(ring, meta->dmaaddr,
-					   ring->rx_buffersize);
-
-		return;
-	}
 	rxhdr = (struct b43_rxhdr_fw4 *)skb->data;
 	rxhdr = (struct b43_rxhdr_fw4 *)skb->data;
 	len = le16_to_cpu(rxhdr->frame_len);
 	len = le16_to_cpu(rxhdr->frame_len);
 	if (len == 0) {
 	if (len == 0) {
@@ -1526,7 +1498,7 @@ static void dma_rx(struct b43_dmaring *ring, int *slot)
 	skb_pull(skb, ring->frameoffset);
 	skb_pull(skb, ring->frameoffset);
 
 
 	b43_rx(ring->dev, skb, rxhdr);
 	b43_rx(ring->dev, skb, rxhdr);
-      drop:
+drop:
 	return;
 	return;
 }
 }
 
 
@@ -1572,21 +1544,19 @@ static void b43_dma_tx_resume_ring(struct b43_dmaring *ring)
 void b43_dma_tx_suspend(struct b43_wldev *dev)
 void b43_dma_tx_suspend(struct b43_wldev *dev)
 {
 {
 	b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
 	b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
-	b43_dma_tx_suspend_ring(dev->dma.tx_ring0);
-	b43_dma_tx_suspend_ring(dev->dma.tx_ring1);
-	b43_dma_tx_suspend_ring(dev->dma.tx_ring2);
-	b43_dma_tx_suspend_ring(dev->dma.tx_ring3);
-	b43_dma_tx_suspend_ring(dev->dma.tx_ring4);
-	b43_dma_tx_suspend_ring(dev->dma.tx_ring5);
+	b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BK);
+	b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BE);
+	b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VI);
+	b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VO);
+	b43_dma_tx_suspend_ring(dev->dma.tx_ring_mcast);
 }
 }
 
 
 void b43_dma_tx_resume(struct b43_wldev *dev)
 void b43_dma_tx_resume(struct b43_wldev *dev)
 {
 {
-	b43_dma_tx_resume_ring(dev->dma.tx_ring5);
-	b43_dma_tx_resume_ring(dev->dma.tx_ring4);
-	b43_dma_tx_resume_ring(dev->dma.tx_ring3);
-	b43_dma_tx_resume_ring(dev->dma.tx_ring2);
-	b43_dma_tx_resume_ring(dev->dma.tx_ring1);
-	b43_dma_tx_resume_ring(dev->dma.tx_ring0);
+	b43_dma_tx_resume_ring(dev->dma.tx_ring_mcast);
+	b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VO);
+	b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VI);
+	b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BE);
+	b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BK);
 	b43_power_saving_ctl_bits(dev, 0);
 	b43_power_saving_ctl_bits(dev, 0);
 }
 }

+ 10 - 1
drivers/net/wireless/b43/dma.h

@@ -245,6 +245,9 @@ struct b43_dmaring {
 	enum b43_dmatype type;
 	enum b43_dmatype type;
 	/* Boolean. Is this ring stopped at ieee80211 level? */
 	/* Boolean. Is this ring stopped at ieee80211 level? */
 	bool stopped;
 	bool stopped;
+	/* The QOS priority assigned to this ring. Only used for TX rings.
+	 * This is the mac80211 "queue" value. */
+	u8 queue_prio;
 	/* Lock, only used for TX. */
 	/* Lock, only used for TX. */
 	spinlock_t lock;
 	spinlock_t lock;
 	struct b43_wldev *dev;
 	struct b43_wldev *dev;
@@ -253,7 +256,13 @@ struct b43_dmaring {
 	int max_used_slots;
 	int max_used_slots;
 	/* Last time we injected a ring overflow. */
 	/* Last time we injected a ring overflow. */
 	unsigned long last_injected_overflow;
 	unsigned long last_injected_overflow;
-#endif				/* CONFIG_B43_DEBUG */
+	/* Statistics: Number of successfully transmitted packets */
+	u64 nr_succeed_tx_packets;
+	/* Statistics: Number of failed TX packets */
+	u64 nr_failed_tx_packets;
+	/* Statistics: Total number of TX plus all retries. */
+	u64 nr_total_packet_tries;
+#endif /* CONFIG_B43_DEBUG */
 };
 };
 
 
 static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset)
 static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset)

+ 191 - 5
drivers/net/wireless/b43/main.c

@@ -78,6 +78,11 @@ static int modparam_nohwcrypt;
 module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
 module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
 MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
 MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
 
 
+int b43_modparam_qos = 1;
+module_param_named(qos, b43_modparam_qos, int, 0444);
+MODULE_PARM_DESC(qos, "Enable QOS support (default on)");
+
+
 static const struct ssb_device_id b43_ssb_tbl[] = {
 static const struct ssb_device_id b43_ssb_tbl[] = {
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5),
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5),
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 6),
 	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 6),
@@ -1589,11 +1594,10 @@ static void b43_interrupt_tasklet(struct b43_wldev *dev)
 
 
 	/* Check the DMA reason registers for received data. */
 	/* Check the DMA reason registers for received data. */
 	if (dma_reason[0] & B43_DMAIRQ_RX_DONE)
 	if (dma_reason[0] & B43_DMAIRQ_RX_DONE)
-		b43_dma_rx(dev->dma.rx_ring0);
-	if (dma_reason[3] & B43_DMAIRQ_RX_DONE)
-		b43_dma_rx(dev->dma.rx_ring3);
+		b43_dma_rx(dev->dma.rx_ring);
 	B43_WARN_ON(dma_reason[1] & B43_DMAIRQ_RX_DONE);
 	B43_WARN_ON(dma_reason[1] & B43_DMAIRQ_RX_DONE);
 	B43_WARN_ON(dma_reason[2] & B43_DMAIRQ_RX_DONE);
 	B43_WARN_ON(dma_reason[2] & B43_DMAIRQ_RX_DONE);
+	B43_WARN_ON(dma_reason[3] & B43_DMAIRQ_RX_DONE);
 	B43_WARN_ON(dma_reason[4] & B43_DMAIRQ_RX_DONE);
 	B43_WARN_ON(dma_reason[4] & B43_DMAIRQ_RX_DONE);
 	B43_WARN_ON(dma_reason[5] & B43_DMAIRQ_RX_DONE);
 	B43_WARN_ON(dma_reason[5] & B43_DMAIRQ_RX_DONE);
 
 
@@ -2708,10 +2712,178 @@ out:
 	return NETDEV_TX_OK;
 	return NETDEV_TX_OK;
 }
 }
 
 
+/* Locking: wl->irq_lock */
+static void b43_qos_params_upload(struct b43_wldev *dev,
+				  const struct ieee80211_tx_queue_params *p,
+				  u16 shm_offset)
+{
+	u16 params[B43_NR_QOSPARAMS];
+	int cw_min, cw_max, aifs, bslots, tmp;
+	unsigned int i;
+
+	const u16 aCWmin = 0x0001;
+	const u16 aCWmax = 0x03FF;
+
+	/* Calculate the default values for the parameters, if needed. */
+	switch (shm_offset) {
+	case B43_QOS_VOICE:
+		aifs = (p->aifs == -1) ? 2 : p->aifs;
+		cw_min = (p->cw_min == 0) ? ((aCWmin + 1) / 4 - 1) : p->cw_min;
+		cw_max = (p->cw_max == 0) ? ((aCWmin + 1) / 2 - 1) : p->cw_max;
+		break;
+	case B43_QOS_VIDEO:
+		aifs = (p->aifs == -1) ? 2 : p->aifs;
+		cw_min = (p->cw_min == 0) ? ((aCWmin + 1) / 2 - 1) : p->cw_min;
+		cw_max = (p->cw_max == 0) ? aCWmin : p->cw_max;
+		break;
+	case B43_QOS_BESTEFFORT:
+		aifs = (p->aifs == -1) ? 3 : p->aifs;
+		cw_min = (p->cw_min == 0) ? aCWmin : p->cw_min;
+		cw_max = (p->cw_max == 0) ? aCWmax : p->cw_max;
+		break;
+	case B43_QOS_BACKGROUND:
+		aifs = (p->aifs == -1) ? 7 : p->aifs;
+		cw_min = (p->cw_min == 0) ? aCWmin : p->cw_min;
+		cw_max = (p->cw_max == 0) ? aCWmax : p->cw_max;
+		break;
+	default:
+		B43_WARN_ON(1);
+		return;
+	}
+	if (cw_min <= 0)
+		cw_min = aCWmin;
+	if (cw_max <= 0)
+		cw_max = aCWmin;
+	bslots = b43_read16(dev, B43_MMIO_RNG) % cw_min;
+
+	memset(&params, 0, sizeof(params));
+
+	params[B43_QOSPARAM_TXOP] = p->txop * 32;
+	params[B43_QOSPARAM_CWMIN] = cw_min;
+	params[B43_QOSPARAM_CWMAX] = cw_max;
+	params[B43_QOSPARAM_CWCUR] = cw_min;
+	params[B43_QOSPARAM_AIFS] = aifs;
+	params[B43_QOSPARAM_BSLOTS] = bslots;
+	params[B43_QOSPARAM_REGGAP] = bslots + aifs;
+
+	for (i = 0; i < ARRAY_SIZE(params); i++) {
+		if (i == B43_QOSPARAM_STATUS) {
+			tmp = b43_shm_read16(dev, B43_SHM_SHARED,
+					     shm_offset + (i * 2));
+			/* Mark the parameters as updated. */
+			tmp |= 0x100;
+			b43_shm_write16(dev, B43_SHM_SHARED,
+					shm_offset + (i * 2),
+					tmp);
+		} else {
+			b43_shm_write16(dev, B43_SHM_SHARED,
+					shm_offset + (i * 2),
+					params[i]);
+		}
+	}
+}
+
+/* Update the QOS parameters in hardware. */
+static void b43_qos_update(struct b43_wldev *dev)
+{
+	struct b43_wl *wl = dev->wl;
+	struct b43_qos_params *params;
+	unsigned long flags;
+	unsigned int i;
+
+	/* Mapping of mac80211 queues to b43 SHM offsets. */
+	static const u16 qos_shm_offsets[] = {
+		[0] = B43_QOS_VOICE,
+		[1] = B43_QOS_VIDEO,
+		[2] = B43_QOS_BESTEFFORT,
+		[3] = B43_QOS_BACKGROUND,
+	};
+	BUILD_BUG_ON(ARRAY_SIZE(qos_shm_offsets) != ARRAY_SIZE(wl->qos_params));
+
+	b43_mac_suspend(dev);
+	spin_lock_irqsave(&wl->irq_lock, flags);
+
+	for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
+		params = &(wl->qos_params[i]);
+		if (params->need_hw_update) {
+			b43_qos_params_upload(dev, &(params->p),
+					      qos_shm_offsets[i]);
+			params->need_hw_update = 0;
+		}
+	}
+
+	spin_unlock_irqrestore(&wl->irq_lock, flags);
+	b43_mac_enable(dev);
+}
+
+static void b43_qos_clear(struct b43_wl *wl)
+{
+	struct b43_qos_params *params;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
+		params = &(wl->qos_params[i]);
+
+		memset(&(params->p), 0, sizeof(params->p));
+		params->p.aifs = -1;
+		params->need_hw_update = 1;
+	}
+}
+
+/* Initialize the core's QOS capabilities */
+static void b43_qos_init(struct b43_wldev *dev)
+{
+	struct b43_wl *wl = dev->wl;
+	unsigned int i;
+
+	/* Upload the current QOS parameters. */
+	for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++)
+		wl->qos_params[i].need_hw_update = 1;
+	b43_qos_update(dev);
+
+	/* Enable QOS support. */
+	b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF);
+	b43_write16(dev, B43_MMIO_IFSCTL,
+		    b43_read16(dev, B43_MMIO_IFSCTL)
+		    | B43_MMIO_IFSCTL_USE_EDCF);
+}
+
+static void b43_qos_update_work(struct work_struct *work)
+{
+	struct b43_wl *wl = container_of(work, struct b43_wl, qos_update_work);
+	struct b43_wldev *dev;
+
+	mutex_lock(&wl->mutex);
+	dev = wl->current_dev;
+	if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED)))
+		b43_qos_update(dev);
+	mutex_unlock(&wl->mutex);
+}
+
 static int b43_op_conf_tx(struct ieee80211_hw *hw,
 static int b43_op_conf_tx(struct ieee80211_hw *hw,
-			  int queue,
+			  int _queue,
 			  const struct ieee80211_tx_queue_params *params)
 			  const struct ieee80211_tx_queue_params *params)
 {
 {
+	struct b43_wl *wl = hw_to_b43_wl(hw);
+	unsigned long flags;
+	unsigned int queue = (unsigned int)_queue;
+	struct b43_qos_params *p;
+
+	if (queue >= ARRAY_SIZE(wl->qos_params)) {
+		/* Queue not available or don't support setting
+		 * params on this queue. Return success to not
+		 * confuse mac80211. */
+		return 0;
+	}
+
+	spin_lock_irqsave(&wl->irq_lock, flags);
+	p = &(wl->qos_params[queue]);
+	memcpy(&(p->p), params, sizeof(p->p));
+	p->need_hw_update = 1;
+	spin_unlock_irqrestore(&wl->irq_lock, flags);
+
+	queue_work(hw->workqueue, &wl->qos_update_work);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -3732,6 +3904,7 @@ static int b43_op_start(struct ieee80211_hw *hw)
 	memset(wl->mac_addr, 0, ETH_ALEN);
 	memset(wl->mac_addr, 0, ETH_ALEN);
 	wl->filter_flags = 0;
 	wl->filter_flags = 0;
 	wl->radiotap_enabled = 0;
 	wl->radiotap_enabled = 0;
+	b43_qos_clear(wl);
 
 
 	/* First register RFkill.
 	/* First register RFkill.
 	 * LEDs that are registered later depend on it. */
 	 * LEDs that are registered later depend on it. */
@@ -3773,6 +3946,7 @@ static void b43_op_stop(struct ieee80211_hw *hw)
 	struct b43_wldev *dev = wl->current_dev;
 	struct b43_wldev *dev = wl->current_dev;
 
 
 	b43_rfkill_exit(dev);
 	b43_rfkill_exit(dev);
+	cancel_work_sync(&(wl->qos_update_work));
 
 
 	mutex_lock(&wl->mutex);
 	mutex_lock(&wl->mutex);
 	if (b43_status(dev) >= B43_STAT_STARTED)
 	if (b43_status(dev) >= B43_STAT_STARTED)
@@ -3835,6 +4009,16 @@ static int b43_op_ibss_beacon_update(struct ieee80211_hw *hw,
 	return 0;
 	return 0;
 }
 }
 
 
+static void b43_op_sta_notify(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif,
+			      enum sta_notify_cmd notify_cmd,
+			      const u8 *addr)
+{
+	struct b43_wl *wl = hw_to_b43_wl(hw);
+
+	B43_WARN_ON(!vif || wl->vif != vif);
+}
+
 static const struct ieee80211_ops b43_hw_ops = {
 static const struct ieee80211_ops b43_hw_ops = {
 	.tx			= b43_op_tx,
 	.tx			= b43_op_tx,
 	.conf_tx		= b43_op_conf_tx,
 	.conf_tx		= b43_op_conf_tx,
@@ -3851,6 +4035,7 @@ static const struct ieee80211_ops b43_hw_ops = {
 	.set_retry_limit	= b43_op_set_retry_limit,
 	.set_retry_limit	= b43_op_set_retry_limit,
 	.set_tim		= b43_op_beacon_set_tim,
 	.set_tim		= b43_op_beacon_set_tim,
 	.beacon_update		= b43_op_ibss_beacon_update,
 	.beacon_update		= b43_op_ibss_beacon_update,
+	.sta_notify		= b43_op_sta_notify,
 };
 };
 
 
 /* Hard-reset the chip. Do not call this directly.
 /* Hard-reset the chip. Do not call this directly.
@@ -4122,7 +4307,7 @@ static int b43_wireless_init(struct ssb_device *dev)
 	hw->max_signal = 100;
 	hw->max_signal = 100;
 	hw->max_rssi = -110;
 	hw->max_rssi = -110;
 	hw->max_noise = -110;
 	hw->max_noise = -110;
-	hw->queues = 1;		/* FIXME: hardware has more queues */
+	hw->queues = b43_modparam_qos ? 4 : 1;
 	SET_IEEE80211_DEV(hw, dev->dev);
 	SET_IEEE80211_DEV(hw, dev->dev);
 	if (is_valid_ether_addr(sprom->et1mac))
 	if (is_valid_ether_addr(sprom->et1mac))
 		SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac);
 		SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac);
@@ -4138,6 +4323,7 @@ static int b43_wireless_init(struct ssb_device *dev)
 	spin_lock_init(&wl->shm_lock);
 	spin_lock_init(&wl->shm_lock);
 	mutex_init(&wl->mutex);
 	mutex_init(&wl->mutex);
 	INIT_LIST_HEAD(&wl->devlist);
 	INIT_LIST_HEAD(&wl->devlist);
+	INIT_WORK(&wl->qos_update_work, b43_qos_update_work);
 
 
 	ssb_set_devtypedata(dev, wl);
 	ssb_set_devtypedata(dev, wl);
 	b43info(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id);
 	b43info(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id);

+ 4 - 0
drivers/net/wireless/b43/main.h

@@ -38,6 +38,10 @@
 /* Magic helper macro to pad structures. Ignore those above. It's magic. */
 /* Magic helper macro to pad structures. Ignore those above. It's magic. */
 #define PAD_BYTES(nr_bytes)		P4D_BYTES( __LINE__ , (nr_bytes))
 #define PAD_BYTES(nr_bytes)		P4D_BYTES( __LINE__ , (nr_bytes))
 
 
+
+extern int b43_modparam_qos;
+
+
 /* Lightweight function to convert a frequency (in Mhz) to a channel number. */
 /* Lightweight function to convert a frequency (in Mhz) to a channel number. */
 static inline u8 b43_freq_to_channel_5ghz(int freq)
 static inline u8 b43_freq_to_channel_5ghz(int freq)
 {
 {

+ 0 - 27
drivers/net/wireless/b43/xmit.c

@@ -705,30 +705,3 @@ void b43_tx_resume(struct b43_wldev *dev)
 {
 {
 	b43_dma_tx_resume(dev);
 	b43_dma_tx_resume(dev);
 }
 }
-
-#if 0
-static void upload_qos_parms(struct b43_wldev *dev,
-			     const u16 * parms, u16 offset)
-{
-	int i;
-
-	for (i = 0; i < B43_NR_QOSPARMS; i++) {
-		b43_shm_write16(dev, B43_SHM_SHARED,
-				offset + (i * 2), parms[i]);
-	}
-}
-#endif
-
-/* Initialize the QoS parameters */
-void b43_qos_init(struct b43_wldev *dev)
-{
-	/* FIXME: This function must probably be called from the mac80211
-	 * config callback. */
-	return;
-
-	b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF);
-	//FIXME kill magic
-	b43_write16(dev, 0x688, b43_read16(dev, 0x688) | 0x4);
-
-	/*TODO: We might need some stack support here to get the values. */
-}

+ 0 - 12
drivers/net/wireless/b43/xmit.h

@@ -302,18 +302,6 @@ void b43_handle_hwtxstatus(struct b43_wldev *dev,
 void b43_tx_suspend(struct b43_wldev *dev);
 void b43_tx_suspend(struct b43_wldev *dev);
 void b43_tx_resume(struct b43_wldev *dev);
 void b43_tx_resume(struct b43_wldev *dev);
 
 
-#define B43_NR_QOSPARMS		22
-enum {
-	B43_QOSPARM_TXOP = 0,
-	B43_QOSPARM_CWMIN,
-	B43_QOSPARM_CWMAX,
-	B43_QOSPARM_CWCUR,
-	B43_QOSPARM_AIFS,
-	B43_QOSPARM_BSLOTS,
-	B43_QOSPARM_REGGAP,
-	B43_QOSPARM_STATUS,
-};
-void b43_qos_init(struct b43_wldev *dev);
 
 
 /* Helper functions for converting the key-table index from "firmware-format"
 /* Helper functions for converting the key-table index from "firmware-format"
  * to "raw-format" and back. The firmware API changed for this at some revision.
  * to "raw-format" and back. The firmware API changed for this at some revision.

+ 5 - 0
drivers/net/wireless/iwlwifi/Kconfig

@@ -1,7 +1,12 @@
+config IWLCORE
+	tristate "Intel Wireless Wifi Core"
+	depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
+
 config IWL4965
 config IWL4965
 	tristate "Intel Wireless WiFi 4965AGN"
 	tristate "Intel Wireless WiFi 4965AGN"
 	depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
 	depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
 	select FW_LOADER
 	select FW_LOADER
+	select IWLCORE
 	---help---
 	---help---
 	  Select to build the driver supporting the:
 	  Select to build the driver supporting the:
 
 

+ 3 - 0
drivers/net/wireless/iwlwifi/Makefile

@@ -1,3 +1,6 @@
+obj-$(CONFIG_IWLCORE)	+= iwlcore.o
+iwlcore-objs 		= iwl-core.o
+
 obj-$(CONFIG_IWL3945)	+= iwl3945.o
 obj-$(CONFIG_IWL3945)	+= iwl3945.o
 iwl3945-objs		= iwl3945-base.o iwl-3945.o iwl-3945-rs.o
 iwl3945-objs		= iwl3945-base.o iwl-3945.o iwl-3945-rs.o
 
 

+ 20 - 20
drivers/net/wireless/iwlwifi/iwl-3945-commands.h

@@ -666,26 +666,26 @@ struct iwl3945_rx_frame_hdr {
 	u8 payload[0];
 	u8 payload[0];
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
-#define	RX_RES_STATUS_NO_CRC32_ERROR	__constant_cpu_to_le32(1 << 0)
-#define	RX_RES_STATUS_NO_RXE_OVERFLOW	__constant_cpu_to_le32(1 << 1)
-
-#define	RX_RES_PHY_FLAGS_BAND_24_MSK	__constant_cpu_to_le16(1 << 0)
-#define	RX_RES_PHY_FLAGS_MOD_CCK_MSK		__constant_cpu_to_le16(1 << 1)
-#define	RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK	__constant_cpu_to_le16(1 << 2)
-#define	RX_RES_PHY_FLAGS_NARROW_BAND_MSK	__constant_cpu_to_le16(1 << 3)
-#define	RX_RES_PHY_FLAGS_ANTENNA_MSK		__constant_cpu_to_le16(0xf0)
-
-#define	RX_RES_STATUS_SEC_TYPE_MSK	(0x7 << 8)
-#define	RX_RES_STATUS_SEC_TYPE_NONE	(0x0 << 8)
-#define	RX_RES_STATUS_SEC_TYPE_WEP	(0x1 << 8)
-#define	RX_RES_STATUS_SEC_TYPE_CCMP	(0x2 << 8)
-#define	RX_RES_STATUS_SEC_TYPE_TKIP	(0x3 << 8)
-
-#define	RX_RES_STATUS_DECRYPT_TYPE_MSK	(0x3 << 11)
-#define	RX_RES_STATUS_NOT_DECRYPT	(0x0 << 11)
-#define	RX_RES_STATUS_DECRYPT_OK	(0x3 << 11)
-#define	RX_RES_STATUS_BAD_ICV_MIC	(0x1 << 11)
-#define	RX_RES_STATUS_BAD_KEY_TTAK	(0x2 << 11)
+#define RX_RES_STATUS_NO_CRC32_ERROR	__constant_cpu_to_le32(1 << 0)
+#define RX_RES_STATUS_NO_RXE_OVERFLOW	__constant_cpu_to_le32(1 << 1)
+
+#define RX_RES_PHY_FLAGS_BAND_24_MSK	__constant_cpu_to_le16(1 << 0)
+#define RX_RES_PHY_FLAGS_MOD_CCK_MSK		__constant_cpu_to_le16(1 << 1)
+#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK	__constant_cpu_to_le16(1 << 2)
+#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK	__constant_cpu_to_le16(1 << 3)
+#define RX_RES_PHY_FLAGS_ANTENNA_MSK		__constant_cpu_to_le16(0xf0)
+
+#define RX_RES_STATUS_SEC_TYPE_MSK	(0x7 << 8)
+#define RX_RES_STATUS_SEC_TYPE_NONE	(0x0 << 8)
+#define RX_RES_STATUS_SEC_TYPE_WEP	(0x1 << 8)
+#define RX_RES_STATUS_SEC_TYPE_CCMP	(0x2 << 8)
+#define RX_RES_STATUS_SEC_TYPE_TKIP	(0x3 << 8)
+
+#define RX_RES_STATUS_DECRYPT_TYPE_MSK	(0x3 << 11)
+#define RX_RES_STATUS_NOT_DECRYPT	(0x0 << 11)
+#define RX_RES_STATUS_DECRYPT_OK	(0x3 << 11)
+#define RX_RES_STATUS_BAD_ICV_MIC	(0x1 << 11)
+#define RX_RES_STATUS_BAD_KEY_TTAK	(0x2 << 11)
 
 
 struct iwl3945_rx_frame_end {
 struct iwl3945_rx_frame_end {
 	__le32 status;
 	__le32 status;

+ 80 - 0
drivers/net/wireless/iwlwifi/iwl-3945-core.h

@@ -0,0 +1,80 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __iwl_3945_dev_h__
+#define __iwl_3945_dev_h__
+
+#define IWL_PCI_DEVICE(dev, subdev, cfg) \
+	.vendor = PCI_VENDOR_ID_INTEL,  .device = (dev), \
+	.subvendor = PCI_ANY_ID, .subdevice = (subdev), \
+	.driver_data = (kernel_ulong_t)&(cfg)
+
+#define IWL_SKU_G       0x1
+#define IWL_SKU_A       0x2
+
+struct iwl_3945_cfg {
+	const char *name;
+	const char *fw_name;
+	unsigned int sku;
+};
+
+#endif /* __iwl_dev_h__ */

+ 16 - 1
drivers/net/wireless/iwlwifi/iwl-3945-debug.h

@@ -40,6 +40,15 @@ do { if (iwl3945_debug_level & (level)) \
 do { if ((iwl3945_debug_level & (level)) && net_ratelimit()) \
 do { if ((iwl3945_debug_level & (level)) && net_ratelimit()) \
   printk(KERN_ERR DRV_NAME": %c %s " fmt, \
   printk(KERN_ERR DRV_NAME": %c %s " fmt, \
 	 in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
 	 in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
+
+static inline void iwl3945_print_hex_dump(int level, void *p, u32 len)
+{
+	if (!(iwl3945_debug_level & level))
+		return;
+
+	print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1,
+			p, len, 1);
+}
 #else
 #else
 static inline void IWL_DEBUG(int level, const char *fmt, ...)
 static inline void IWL_DEBUG(int level, const char *fmt, ...)
 {
 {
@@ -47,7 +56,12 @@ static inline void IWL_DEBUG(int level, const char *fmt, ...)
 static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
 static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
 {
 {
 }
 }
-#endif				/* CONFIG_IWL3945_DEBUG */
+static inline void iwl3945_print_hex_dump(int level, void *p, u32 len)
+{
+}
+#endif	/* CONFIG_IWL3945_DEBUG */
+
+
 
 
 /*
 /*
  * To use the debug system;
  * To use the debug system;
@@ -143,6 +157,7 @@ static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
 	IWL_DEBUG_LIMIT(IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
 	IWL_DEBUG_LIMIT(IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
 #define IWL_DEBUG_HT(f, a...) IWL_DEBUG(IWL_DL_HT, f, ## a)
 #define IWL_DEBUG_HT(f, a...) IWL_DEBUG(IWL_DL_HT, f, ## a)
 #define IWL_DEBUG_STATS(f, a...) IWL_DEBUG(IWL_DL_STATS, f, ## a)
 #define IWL_DEBUG_STATS(f, a...) IWL_DEBUG(IWL_DL_STATS, f, ## a)
+#define IWL_DEBUG_STATS_LIMIT(f, a...) IWL_DEBUG_LIMIT(IWL_DL_STATS, f, ## a)
 #define IWL_DEBUG_TX_REPLY(f, a...) IWL_DEBUG(IWL_DL_TX_REPLY, f, ## a)
 #define IWL_DEBUG_TX_REPLY(f, a...) IWL_DEBUG(IWL_DL_TX_REPLY, f, ## a)
 #define IWL_DEBUG_QOS(f, a...)   IWL_DEBUG(IWL_DL_QOS, f, ## a)
 #define IWL_DEBUG_QOS(f, a...)   IWL_DEBUG(IWL_DL_QOS, f, ## a)
 #define IWL_DEBUG_RADIO(f, a...)  IWL_DEBUG(IWL_DL_RADIO, f, ## a)
 #define IWL_DEBUG_RADIO(f, a...)  IWL_DEBUG(IWL_DL_RADIO, f, ## a)

+ 0 - 174
drivers/net/wireless/iwlwifi/iwl-3945-hw.h

@@ -321,180 +321,6 @@ struct iwl3945_eeprom {
 #define PCI_REG_WUM8       0x0E8
 #define PCI_REG_WUM8       0x0E8
 #define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT         (0x80000000)
 #define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT         (0x80000000)
 
 
-/*=== CSR (control and status registers) ===*/
-#define CSR_BASE    (0x000)
-
-#define CSR_HW_IF_CONFIG_REG    (CSR_BASE+0x000) /* hardware interface config */
-#define CSR_INT_COALESCING      (CSR_BASE+0x004) /* accum ints, 32-usec units */
-#define CSR_INT                 (CSR_BASE+0x008) /* host interrupt status/ack */
-#define CSR_INT_MASK            (CSR_BASE+0x00c) /* host interrupt enable */
-#define CSR_FH_INT_STATUS       (CSR_BASE+0x010) /* busmaster int status/ack*/
-#define CSR_GPIO_IN             (CSR_BASE+0x018) /* read external chip pins */
-#define CSR_RESET               (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/
-#define CSR_GP_CNTRL            (CSR_BASE+0x024)
-
-/*
- * Hardware revision info
- * Bit fields:
- * 31-8:  Reserved
- *  7-4:  Type of device:  0x0 = 4965, 0xd = 3945
- *  3-2:  Revision step:  0 = A, 1 = B, 2 = C, 3 = D
- *  1-0:  "Dash" value, as in A-1, etc.
- */
-#define CSR_HW_REV              (CSR_BASE+0x028)
-
-/* EEPROM reads */
-#define CSR_EEPROM_REG          (CSR_BASE+0x02c)
-#define CSR_EEPROM_GP           (CSR_BASE+0x030)
-#define CSR_GP_UCODE		(CSR_BASE+0x044)
-#define CSR_UCODE_DRV_GP1       (CSR_BASE+0x054)
-#define CSR_UCODE_DRV_GP1_SET   (CSR_BASE+0x058)
-#define CSR_UCODE_DRV_GP1_CLR   (CSR_BASE+0x05c)
-#define CSR_UCODE_DRV_GP2       (CSR_BASE+0x060)
-#define CSR_GIO_CHICKEN_BITS    (CSR_BASE+0x100)
-
-/* Analog phase-lock-loop configuration (3945 only)
- * Set bit 24. */
-#define CSR_ANA_PLL_CFG         (CSR_BASE+0x20c)
-
-/* Bits for CSR_HW_IF_CONFIG_REG */
-#define CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MB         (0x00000100)
-#define CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MM         (0x00000200)
-#define CSR_HW_IF_CONFIG_REG_BIT_SKU_MRC            (0x00000400)
-#define CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE         (0x00000800)
-#define CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A    (0x00000000)
-#define CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B    (0x00001000)
-#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM     (0x00200000)
-
-/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
- * acknowledged (reset) by host writing "1" to flagged bits. */
-#define CSR_INT_BIT_FH_RX        (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */
-#define CSR_INT_BIT_HW_ERR       (1 << 29) /* DMA hardware error FH_INT[31] */
-#define CSR_INT_BIT_DNLD         (1 << 28) /* uCode Download */
-#define CSR_INT_BIT_FH_TX        (1 << 27) /* Tx DMA FH_INT[1:0] */
-#define CSR_INT_BIT_SCD          (1 << 26) /* TXQ pointer advanced */
-#define CSR_INT_BIT_SW_ERR       (1 << 25) /* uCode error */
-#define CSR_INT_BIT_RF_KILL      (1 << 7)  /* HW RFKILL switch GP_CNTRL[27] toggled */
-#define CSR_INT_BIT_CT_KILL      (1 << 6)  /* Critical temp (chip too hot) rfkill */
-#define CSR_INT_BIT_SW_RX        (1 << 3)  /* Rx, command responses, 3945 */
-#define CSR_INT_BIT_WAKEUP       (1 << 1)  /* NIC controller waking up (pwr mgmt) */
-#define CSR_INT_BIT_ALIVE        (1 << 0)  /* uCode interrupts once it initializes */
-
-#define CSR_INI_SET_MASK	(CSR_INT_BIT_FH_RX   | \
-				 CSR_INT_BIT_HW_ERR  | \
-				 CSR_INT_BIT_FH_TX   | \
-				 CSR_INT_BIT_SW_ERR  | \
-				 CSR_INT_BIT_RF_KILL | \
-				 CSR_INT_BIT_SW_RX   | \
-				 CSR_INT_BIT_WAKEUP  | \
-				 CSR_INT_BIT_ALIVE)
-
-/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
-#define CSR_FH_INT_BIT_ERR       (1 << 31) /* Error */
-#define CSR_FH_INT_BIT_HI_PRIOR  (1 << 30) /* High priority Rx, bypass coalescing */
-#define CSR_FH_INT_BIT_RX_CHNL2  (1 << 18) /* Rx channel 2 (3945 only) */
-#define CSR_FH_INT_BIT_RX_CHNL1  (1 << 17) /* Rx channel 1 */
-#define CSR_FH_INT_BIT_RX_CHNL0  (1 << 16) /* Rx channel 0 */
-#define CSR_FH_INT_BIT_TX_CHNL6  (1 << 6)  /* Tx channel 6 (3945 only) */
-#define CSR_FH_INT_BIT_TX_CHNL1  (1 << 1)  /* Tx channel 1 */
-#define CSR_FH_INT_BIT_TX_CHNL0  (1 << 0)  /* Tx channel 0 */
-
-#define CSR_FH_INT_RX_MASK	(CSR_FH_INT_BIT_HI_PRIOR | \
-				 CSR_FH_INT_BIT_RX_CHNL2 | \
-				 CSR_FH_INT_BIT_RX_CHNL1 | \
-				 CSR_FH_INT_BIT_RX_CHNL0)
-
-#define CSR_FH_INT_TX_MASK	(CSR_FH_INT_BIT_TX_CHNL6 | \
-				 CSR_FH_INT_BIT_TX_CHNL1 | \
-				 CSR_FH_INT_BIT_TX_CHNL0)
-
-
-/* RESET */
-#define CSR_RESET_REG_FLAG_NEVO_RESET                (0x00000001)
-#define CSR_RESET_REG_FLAG_FORCE_NMI                 (0x00000002)
-#define CSR_RESET_REG_FLAG_SW_RESET                  (0x00000080)
-#define CSR_RESET_REG_FLAG_MASTER_DISABLED           (0x00000100)
-#define CSR_RESET_REG_FLAG_STOP_MASTER               (0x00000200)
-
-/* GP (general purpose) CONTROL */
-#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY        (0x00000001)
-#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE              (0x00000004)
-#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ         (0x00000008)
-#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP         (0x00000010)
-
-#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN           (0x00000001)
-
-#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE         (0x07000000)
-#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE         (0x04000000)
-#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW          (0x08000000)
-
-
-/* EEPROM REG */
-#define CSR_EEPROM_REG_READ_VALID_MSK	(0x00000001)
-#define CSR_EEPROM_REG_BIT_CMD		(0x00000002)
-
-/* EEPROM GP */
-#define CSR_EEPROM_GP_VALID_MSK		(0x00000006)
-#define CSR_EEPROM_GP_BAD_SIGNATURE	(0x00000000)
-#define CSR_EEPROM_GP_IF_OWNER_MSK	(0x00000180)
-
-/* UCODE DRV GP */
-#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP             (0x00000001)
-#define CSR_UCODE_SW_BIT_RFKILL                     (0x00000002)
-#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED           (0x00000004)
-#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT      (0x00000008)
-
-/* GPIO */
-#define CSR_GPIO_IN_BIT_AUX_POWER                   (0x00000200)
-#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC                (0x00000000)
-#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC		CSR_GPIO_IN_BIT_AUX_POWER
-
-/* GI Chicken Bits */
-#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX  (0x00800000)
-#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER  (0x20000000)
-
-/* CSR_ANA_PLL_CFG */
-#define CSR_ANA_PLL_CFG_SH		(0x00880300)
-
-/*=== HBUS (Host-side Bus) ===*/
-#define HBUS_BASE	(0x400)
-
-/*
- * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM
- * structures, error log, event log, verifying uCode load).
- * First write to address register, then read from or write to data register
- * to complete the job.  Once the address register is set up, accesses to
- * data registers auto-increment the address by one dword.
- * Bit usage for address registers (read or write):
- *  0-31:  memory address within device
- */
-#define HBUS_TARG_MEM_RADDR     (HBUS_BASE+0x00c)
-#define HBUS_TARG_MEM_WADDR     (HBUS_BASE+0x010)
-#define HBUS_TARG_MEM_WDAT      (HBUS_BASE+0x018)
-#define HBUS_TARG_MEM_RDAT      (HBUS_BASE+0x01c)
-
-/*
- * Registers for accessing device's internal peripheral registers
- * (e.g. SCD, BSM, etc.).  First write to address register,
- * then read from or write to data register to complete the job.
- * Bit usage for address registers (read or write):
- *  0-15:  register address (offset) within device
- * 24-25:  (# bytes - 1) to read or write (e.g. 3 for dword)
- */
-#define HBUS_TARG_PRPH_WADDR    (HBUS_BASE+0x044)
-#define HBUS_TARG_PRPH_RADDR    (HBUS_BASE+0x048)
-#define HBUS_TARG_PRPH_WDAT     (HBUS_BASE+0x04c)
-#define HBUS_TARG_PRPH_RDAT     (HBUS_BASE+0x050)
-
-/*
- * Per-Tx-queue write pointer (index, really!) (3945 and 4965).
- * Indicates index to next TFD that driver will fill (1 past latest filled).
- * Bit usage:
- *  0-7:  queue write index
- * 11-8:  queue selector
- */
-#define HBUS_TARG_WRPTR         (HBUS_BASE+0x060)
-
 /* SCD (3945 Tx Frame Scheduler) */
 /* SCD (3945 Tx Frame Scheduler) */
 #define SCD_BASE                        (CSR_BASE + 0x2E00)
 #define SCD_BASE                        (CSR_BASE + 0x2E00)
 
 

+ 39 - 37
drivers/net/wireless/iwlwifi/iwl-3945-rs.c

@@ -158,9 +158,9 @@ static void iwl3945_clear_window(struct iwl3945_rate_scale_data *window)
 {
 {
 	window->data = 0;
 	window->data = 0;
 	window->success_counter = 0;
 	window->success_counter = 0;
-	window->success_ratio = IWL_INVALID_VALUE;
+	window->success_ratio = -1;
 	window->counter = 0;
 	window->counter = 0;
-	window->average_tpt = IWL_INVALID_VALUE;
+	window->average_tpt = IWL_INV_TPT;
 	window->stamp = 0;
 	window->stamp = 0;
 }
 }
 
 
@@ -459,22 +459,23 @@ static void rs_tx_status(void *priv_rate,
 	struct iwl3945_rs_sta *rs_sta;
 	struct iwl3945_rs_sta *rs_sta;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_supported_band *sband;
 
 
+	IWL_DEBUG_RATE("enter\n");
+
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
 
-	IWL_DEBUG_RATE("enter\n");
 
 
 	retries = tx_resp->retry_count;
 	retries = tx_resp->retry_count;
-	/* FIXME : this is wrong */
-	first_index = &sband->bitrates[0] - tx_resp->control.tx_rate;
+	first_index = tx_resp->control.tx_rate->hw_value;
 	if ((first_index < 0) || (first_index >= IWL_RATE_COUNT)) {
 	if ((first_index < 0) || (first_index >= IWL_RATE_COUNT)) {
 		IWL_DEBUG_RATE("leave: Rate out of bounds: %d\n", first_index);
 		IWL_DEBUG_RATE("leave: Rate out of bounds: %d\n", first_index);
 		return;
 		return;
 	}
 	}
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 	sta = sta_info_get(local, hdr->addr1);
 	if (!sta || !sta->rate_ctrl_priv) {
 	if (!sta || !sta->rate_ctrl_priv) {
-		if (sta)
-			sta_info_put(sta);
+		rcu_read_unlock();
 		IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
 		IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
 		return;
 		return;
 	}
 	}
@@ -547,7 +548,7 @@ static void rs_tx_status(void *priv_rate,
 
 
 	spin_unlock_irqrestore(&rs_sta->lock, flags);
 	spin_unlock_irqrestore(&rs_sta->lock, flags);
 
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 
 	IWL_DEBUG_RATE("leave\n");
 	IWL_DEBUG_RATE("leave\n");
 
 
@@ -633,7 +634,7 @@ static u16 iwl3945_get_adjacent_rate(struct iwl3945_rs_sta *rs_sta,
  *
  *
  */
  */
 static void rs_get_rate(void *priv_rate, struct net_device *dev,
 static void rs_get_rate(void *priv_rate, struct net_device *dev,
-			struct ieee80211_supported_band *band,
+			struct ieee80211_supported_band *sband,
 			struct sk_buff *skb,
 			struct sk_buff *skb,
 			struct rate_selection *sel)
 			struct rate_selection *sel)
 {
 {
@@ -643,9 +644,9 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
 	int index;
 	int index;
 	struct iwl3945_rs_sta *rs_sta;
 	struct iwl3945_rs_sta *rs_sta;
 	struct iwl3945_rate_scale_data *window = NULL;
 	struct iwl3945_rate_scale_data *window = NULL;
-	int current_tpt = IWL_INVALID_VALUE;
-	int low_tpt = IWL_INVALID_VALUE;
-	int high_tpt = IWL_INVALID_VALUE;
+	int current_tpt = IWL_INV_TPT;
+	int low_tpt = IWL_INV_TPT;
+	int high_tpt = IWL_INV_TPT;
 	u32 fail_count;
 	u32 fail_count;
 	s8 scale_action = 0;
 	s8 scale_action = 0;
 	unsigned long flags;
 	unsigned long flags;
@@ -658,6 +659,8 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
 
 
 	IWL_DEBUG_RATE("enter\n");
 	IWL_DEBUG_RATE("enter\n");
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 	sta = sta_info_get(local, hdr->addr1);
 
 
 	/* Send management frames and broadcast/multicast data using lowest
 	/* Send management frames and broadcast/multicast data using lowest
@@ -667,16 +670,15 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
 	    is_multicast_ether_addr(hdr->addr1) ||
 	    is_multicast_ether_addr(hdr->addr1) ||
 	    !sta || !sta->rate_ctrl_priv) {
 	    !sta || !sta->rate_ctrl_priv) {
 		IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
 		IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
-		sel->rate = rate_lowest(local, band, sta);
-		if (sta)
-			sta_info_put(sta);
+		sel->rate = rate_lowest(local, sband, sta);
+		rcu_read_unlock();
 		return;
 		return;
 	}
 	}
 
 
-	rate_mask = sta->supp_rates[band->band];
+	rate_mask = sta->supp_rates[sband->band];
 	index = min(sta->last_txrate_idx & 0xffff, IWL_RATE_COUNT - 1);
 	index = min(sta->last_txrate_idx & 0xffff, IWL_RATE_COUNT - 1);
 
 
-	if (priv->band == IEEE80211_BAND_5GHZ)
+	if (sband->band == IEEE80211_BAND_5GHZ)
 		rate_mask = rate_mask << IWL_FIRST_OFDM_RATE;
 		rate_mask = rate_mask << IWL_FIRST_OFDM_RATE;
 
 
 	rs_sta = (void *)sta->rate_ctrl_priv;
 	rs_sta = (void *)sta->rate_ctrl_priv;
@@ -708,7 +710,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
 
 
 	if (((fail_count <= IWL_RATE_MIN_FAILURE_TH) &&
 	if (((fail_count <= IWL_RATE_MIN_FAILURE_TH) &&
 	     (window->success_counter < IWL_RATE_MIN_SUCCESS_TH))) {
 	     (window->success_counter < IWL_RATE_MIN_SUCCESS_TH))) {
-		window->average_tpt = IWL_INVALID_VALUE;
+		window->average_tpt = IWL_INV_TPT;
 		spin_unlock_irqrestore(&rs_sta->lock, flags);
 		spin_unlock_irqrestore(&rs_sta->lock, flags);
 
 
 		IWL_DEBUG_RATE("Invalid average_tpt on rate %d: "
 		IWL_DEBUG_RATE("Invalid average_tpt on rate %d: "
@@ -727,7 +729,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
 	current_tpt = window->average_tpt;
 	current_tpt = window->average_tpt;
 
 
 	high_low = iwl3945_get_adjacent_rate(rs_sta, index, rate_mask,
 	high_low = iwl3945_get_adjacent_rate(rs_sta, index, rate_mask,
-					     band->band);
+					     sband->band);
 	low = high_low & 0xff;
 	low = high_low & 0xff;
 	high = (high_low >> 8) & 0xff;
 	high = (high_low >> 8) & 0xff;
 
 
@@ -744,19 +746,16 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
 	if ((window->success_ratio < IWL_RATE_DECREASE_TH) || !current_tpt) {
 	if ((window->success_ratio < IWL_RATE_DECREASE_TH) || !current_tpt) {
 		IWL_DEBUG_RATE("decrease rate because of low success_ratio\n");
 		IWL_DEBUG_RATE("decrease rate because of low success_ratio\n");
 		scale_action = -1;
 		scale_action = -1;
-	} else if ((low_tpt == IWL_INVALID_VALUE) &&
-		   (high_tpt == IWL_INVALID_VALUE))
+	} else if ((low_tpt == IWL_INV_TPT) && (high_tpt == IWL_INV_TPT))
 		scale_action = 1;
 		scale_action = 1;
-	else if ((low_tpt != IWL_INVALID_VALUE) &&
-		   (high_tpt != IWL_INVALID_VALUE)
-		   && (low_tpt < current_tpt)
-		   && (high_tpt < current_tpt)) {
+	else if ((low_tpt != IWL_INV_TPT) && (high_tpt != IWL_INV_TPT) &&
+		 (low_tpt < current_tpt) && (high_tpt < current_tpt)) {
 		IWL_DEBUG_RATE("No action -- low [%d] & high [%d] < "
 		IWL_DEBUG_RATE("No action -- low [%d] & high [%d] < "
 			       "current_tpt [%d]\n",
 			       "current_tpt [%d]\n",
 			       low_tpt, high_tpt, current_tpt);
 			       low_tpt, high_tpt, current_tpt);
 		scale_action = 0;
 		scale_action = 0;
 	} else {
 	} else {
-		if (high_tpt != IWL_INVALID_VALUE) {
+		if (high_tpt != IWL_INV_TPT) {
 			if (high_tpt > current_tpt)
 			if (high_tpt > current_tpt)
 				scale_action = 1;
 				scale_action = 1;
 			else {
 			else {
@@ -764,7 +763,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
 				    ("decrease rate because of high tpt\n");
 				    ("decrease rate because of high tpt\n");
 				scale_action = -1;
 				scale_action = -1;
 			}
 			}
-		} else if (low_tpt != IWL_INVALID_VALUE) {
+		} else if (low_tpt != IWL_INV_TPT) {
 			if (low_tpt > current_tpt) {
 			if (low_tpt > current_tpt) {
 				IWL_DEBUG_RATE
 				IWL_DEBUG_RATE
 				    ("decrease rate because of low tpt\n");
 				    ("decrease rate because of low tpt\n");
@@ -806,16 +805,16 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
  out:
  out:
 
 
 	sta->last_txrate_idx = index;
 	sta->last_txrate_idx = index;
-	if (priv->band == IEEE80211_BAND_5GHZ)
+	if (sband->band == IEEE80211_BAND_5GHZ)
 		sta->txrate_idx = sta->last_txrate_idx - IWL_FIRST_OFDM_RATE;
 		sta->txrate_idx = sta->last_txrate_idx - IWL_FIRST_OFDM_RATE;
 	else
 	else
 		sta->txrate_idx = sta->last_txrate_idx;
 		sta->txrate_idx = sta->last_txrate_idx;
 
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 
 	IWL_DEBUG_RATE("leave: %d\n", index);
 	IWL_DEBUG_RATE("leave: %d\n", index);
 
 
-	sel->rate = &priv->ieee_rates[index];
+	sel->rate = &sband->bitrates[sta->txrate_idx];
 }
 }
 
 
 static struct rate_control_ops rs_ops = {
 static struct rate_control_ops rs_ops = {
@@ -843,13 +842,15 @@ int iwl3945_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
 	unsigned long now = jiffies;
 	unsigned long now = jiffies;
 	u32 max_time = 0;
 	u32 max_time = 0;
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
 	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
 	if (!sta || !sta->rate_ctrl_priv) {
 	if (!sta || !sta->rate_ctrl_priv) {
-		if (sta) {
-			sta_info_put(sta);
+		if (sta)
 			IWL_DEBUG_RATE("leave - no private rate data!\n");
 			IWL_DEBUG_RATE("leave - no private rate data!\n");
-		} else
+		else
 			IWL_DEBUG_RATE("leave - no station!\n");
 			IWL_DEBUG_RATE("leave - no station!\n");
+		rcu_read_unlock();
 		return sprintf(buf, "station %d not found\n", sta_id);
 		return sprintf(buf, "station %d not found\n", sta_id);
 	}
 	}
 
 
@@ -890,7 +891,7 @@ int iwl3945_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
 		i = j;
 		i = j;
 	}
 	}
 	spin_unlock_irqrestore(&rs_sta->lock, flags);
 	spin_unlock_irqrestore(&rs_sta->lock, flags);
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 
 	/* Display the average rate of all samples taken.
 	/* Display the average rate of all samples taken.
 	 *
 	 *
@@ -927,11 +928,12 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
 		return;
 		return;
 	}
 	}
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
 	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
 	if (!sta || !sta->rate_ctrl_priv) {
 	if (!sta || !sta->rate_ctrl_priv) {
-		if (sta)
-			sta_info_put(sta);
 		IWL_DEBUG_RATE("leave - no private rate data!\n");
 		IWL_DEBUG_RATE("leave - no private rate data!\n");
+		rcu_read_unlock();
 		return;
 		return;
 	}
 	}
 
 
@@ -958,7 +960,7 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
 		break;
 		break;
 	}
 	}
 
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 	spin_unlock_irqrestore(&rs_sta->lock, flags);
 	spin_unlock_irqrestore(&rs_sta->lock, flags);
 
 
 	rssi = priv->last_rx_rssi;
 	rssi = priv->last_rx_rssi;

+ 3 - 3
drivers/net/wireless/iwlwifi/iwl-3945-rs.h

@@ -36,8 +36,8 @@ struct iwl3945_rate_info {
 	u8 next_rs;		/* next rate used in rs algo */
 	u8 next_rs;		/* next rate used in rs algo */
 	u8 prev_rs_tgg;		/* previous rate used in TGG rs algo */
 	u8 prev_rs_tgg;		/* previous rate used in TGG rs algo */
 	u8 next_rs_tgg;		/* next rate used in TGG rs algo */
 	u8 next_rs_tgg;		/* next rate used in TGG rs algo */
-        u8 table_rs_index;	/* index in rate scale table cmd */
-        u8 prev_table_rs;	/* prev in rate table cmd */
+	u8 table_rs_index;	/* index in rate scale table cmd */
+	u8 prev_table_rs;	/* prev in rate table cmd */
 };
 };
 
 
 /*
 /*
@@ -159,7 +159,7 @@ enum {
 
 
 #define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
 #define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
 
 
-#define IWL_INVALID_VALUE    -1
+#define IWL_INV_TPT    -1
 
 
 #define IWL_MIN_RSSI_VAL                 -100
 #define IWL_MIN_RSSI_VAL                 -100
 #define IWL_MAX_RSSI_VAL                    0
 #define IWL_MAX_RSSI_VAL                    0

+ 339 - 48
drivers/net/wireless/iwlwifi/iwl-3945.c

@@ -39,6 +39,7 @@
 #include <asm/unaligned.h>
 #include <asm/unaligned.h>
 #include <net/mac80211.h>
 #include <net/mac80211.h>
 
 
+#include "iwl-3945-core.h"
 #include "iwl-3945.h"
 #include "iwl-3945.h"
 #include "iwl-helpers.h"
 #include "iwl-helpers.h"
 #include "iwl-3945-rs.h"
 #include "iwl-3945-rs.h"
@@ -183,6 +184,16 @@ void iwl3945_disable_events(struct iwl3945_priv *priv)
 
 
 }
 }
 
 
+static int iwl3945_hwrate_to_plcp_idx(u8 plcp)
+{
+	int idx;
+
+	for (idx = 0; idx < IWL_RATE_COUNT; idx++)
+		if (iwl3945_rates[idx].plcp == plcp)
+			return idx;
+	return -1;
+}
+
 /**
 /**
  * iwl3945_get_antenna_flags - Get antenna flags for RXON command
  * iwl3945_get_antenna_flags - Get antenna flags for RXON command
  * @priv: eeprom and antenna fields are used to determine antenna flags
  * @priv: eeprom and antenna fields are used to determine antenna flags
@@ -216,14 +227,126 @@ __le32 iwl3945_get_antenna_flags(const struct iwl3945_priv *priv)
 	return 0;		/* "diversity" is default if error */
 	return 0;		/* "diversity" is default if error */
 }
 }
 
 
+#ifdef CONFIG_IWL3945_DEBUG
+#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x
+
+static const char *iwl3945_get_tx_fail_reason(u32 status)
+{
+	switch (status & TX_STATUS_MSK) {
+	case TX_STATUS_SUCCESS:
+		return "SUCCESS";
+		TX_STATUS_ENTRY(SHORT_LIMIT);
+		TX_STATUS_ENTRY(LONG_LIMIT);
+		TX_STATUS_ENTRY(FIFO_UNDERRUN);
+		TX_STATUS_ENTRY(MGMNT_ABORT);
+		TX_STATUS_ENTRY(NEXT_FRAG);
+		TX_STATUS_ENTRY(LIFE_EXPIRE);
+		TX_STATUS_ENTRY(DEST_PS);
+		TX_STATUS_ENTRY(ABORTED);
+		TX_STATUS_ENTRY(BT_RETRY);
+		TX_STATUS_ENTRY(STA_INVALID);
+		TX_STATUS_ENTRY(FRAG_DROPPED);
+		TX_STATUS_ENTRY(TID_DISABLE);
+		TX_STATUS_ENTRY(FRAME_FLUSHED);
+		TX_STATUS_ENTRY(INSUFFICIENT_CF_POLL);
+		TX_STATUS_ENTRY(TX_LOCKED);
+		TX_STATUS_ENTRY(NO_BEACON_ON_RADAR);
+	}
+
+	return "UNKNOWN";
+}
+#else
+static inline const char *iwl3945_get_tx_fail_reason(u32 status)
+{
+	return "";
+}
+#endif
+
+
+/**
+ * iwl3945_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd
+ *
+ * When FW advances 'R' index, all entries between old and new 'R' index
+ * need to be reclaimed. As result, some free space forms. If there is
+ * enough free space (> low mark), wake the stack that feeds us.
+ */
+static void iwl3945_tx_queue_reclaim(struct iwl3945_priv *priv,
+				     int txq_id, int index)
+{
+	struct iwl3945_tx_queue *txq = &priv->txq[txq_id];
+	struct iwl3945_queue *q = &txq->q;
+	struct iwl3945_tx_info *tx_info;
+
+	BUG_ON(txq_id == IWL_CMD_QUEUE_NUM);
+
+	for (index = iwl_queue_inc_wrap(index, q->n_bd); q->read_ptr != index;
+		q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+
+		tx_info = &txq->txb[txq->q.read_ptr];
+		ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb[0],
+					    &tx_info->status);
+		tx_info->skb[0] = NULL;
+		iwl3945_hw_txq_free_tfd(priv, txq);
+	}
+
+	if (iwl3945_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);
+}
+
+/**
+ * iwl3945_rx_reply_tx - Handle Tx response
+ */
+static void iwl3945_rx_reply_tx(struct iwl3945_priv *priv,
+			    struct iwl3945_rx_mem_buffer *rxb)
+{
+	struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data;
+	u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+	int txq_id = SEQ_TO_QUEUE(sequence);
+	int index = SEQ_TO_INDEX(sequence);
+	struct iwl3945_tx_queue *txq = &priv->txq[txq_id];
+	struct ieee80211_tx_status *tx_status;
+	struct iwl3945_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
+	u32  status = le32_to_cpu(tx_resp->status);
+	int rate_idx;
+
+	if ((index >= txq->q.n_bd) || (iwl3945_x2_queue_used(&txq->q, index) == 0)) {
+		IWL_ERROR("Read index for DMA queue txq_id (%d) index %d "
+			  "is out of range [0-%d] %d %d\n", txq_id,
+			  index, txq->q.n_bd, txq->q.write_ptr,
+			  txq->q.read_ptr);
+		return;
+	}
+
+	tx_status = &(txq->txb[txq->q.read_ptr].status);
+
+	tx_status->retry_count = tx_resp->failure_frame;
+	/* tx_status->rts_retry_count = tx_resp->failure_rts; */
+	tx_status->flags = ((status & TX_STATUS_MSK) == TX_STATUS_SUCCESS) ?
+				IEEE80211_TX_STATUS_ACK : 0;
+
+	IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n",
+			txq_id, iwl3945_get_tx_fail_reason(status), status,
+			tx_resp->rate, tx_resp->failure_frame);
+
+	rate_idx = iwl3945_hwrate_to_plcp_idx(tx_resp->rate);
+	tx_status->control.tx_rate = &priv->ieee_rates[rate_idx];
+	IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
+	iwl3945_tx_queue_reclaim(priv, txq_id, index);
+
+	if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
+		IWL_ERROR("TODO:  Implement Tx ABORT REQUIRED!!!\n");
+}
+
+
+
 /*****************************************************************************
 /*****************************************************************************
  *
  *
  * Intel PRO/Wireless 3945ABG/BG Network Connection
  * Intel PRO/Wireless 3945ABG/BG Network Connection
  *
  *
  *  RX handler implementations
  *  RX handler implementations
  *
  *
- *  Used by iwl-base.c
- *
  *****************************************************************************/
  *****************************************************************************/
 
 
 void iwl3945_hw_rx_statistics(struct iwl3945_priv *priv, struct iwl3945_rx_mem_buffer *rxb)
 void iwl3945_hw_rx_statistics(struct iwl3945_priv *priv, struct iwl3945_rx_mem_buffer *rxb)
@@ -238,6 +361,156 @@ void iwl3945_hw_rx_statistics(struct iwl3945_priv *priv, struct iwl3945_rx_mem_b
 	priv->last_statistics_time = jiffies;
 	priv->last_statistics_time = jiffies;
 }
 }
 
 
+/******************************************************************************
+ *
+ * Misc. internal state and helper functions
+ *
+ ******************************************************************************/
+#ifdef CONFIG_IWL3945_DEBUG
+
+/**
+ * iwl3945_report_frame - dump frame to syslog during debug sessions
+ *
+ * You may hack this function to show different aspects of received frames,
+ * including selective frame dumps.
+ * group100 parameter selects whether to show 1 out of 100 good frames.
+ */
+static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv,
+		      struct iwl3945_rx_packet *pkt,
+		      struct ieee80211_hdr *header, int group100)
+{
+	u32 to_us;
+	u32 print_summary = 0;
+	u32 print_dump = 0;	/* set to 1 to dump all frames' contents */
+	u32 hundred = 0;
+	u32 dataframe = 0;
+	u16 fc;
+	u16 seq_ctl;
+	u16 channel;
+	u16 phy_flags;
+	u16 length;
+	u16 status;
+	u16 bcn_tmr;
+	u32 tsf_low;
+	u64 tsf;
+	u8 rssi;
+	u8 agc;
+	u16 sig_avg;
+	u16 noise_diff;
+	struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
+	struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
+	struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
+	u8 *data = IWL_RX_DATA(pkt);
+
+	/* MAC header */
+	fc = le16_to_cpu(header->frame_control);
+	seq_ctl = le16_to_cpu(header->seq_ctrl);
+
+	/* metadata */
+	channel = le16_to_cpu(rx_hdr->channel);
+	phy_flags = le16_to_cpu(rx_hdr->phy_flags);
+	length = le16_to_cpu(rx_hdr->len);
+
+	/* end-of-frame status and timestamp */
+	status = le32_to_cpu(rx_end->status);
+	bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
+	tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
+	tsf = le64_to_cpu(rx_end->timestamp);
+
+	/* signal statistics */
+	rssi = rx_stats->rssi;
+	agc = rx_stats->agc;
+	sig_avg = le16_to_cpu(rx_stats->sig_avg);
+	noise_diff = le16_to_cpu(rx_stats->noise_diff);
+
+	to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
+
+	/* if data frame is to us and all is good,
+	 *   (optionally) print summary for only 1 out of every 100 */
+	if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) ==
+	    (IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
+		dataframe = 1;
+		if (!group100)
+			print_summary = 1;	/* print each frame */
+		else if (priv->framecnt_to_us < 100) {
+			priv->framecnt_to_us++;
+			print_summary = 0;
+		} else {
+			priv->framecnt_to_us = 0;
+			print_summary = 1;
+			hundred = 1;
+		}
+	} else {
+		/* print summary for all other frames */
+		print_summary = 1;
+	}
+
+	if (print_summary) {
+		char *title;
+		u32 rate;
+
+		if (hundred)
+			title = "100Frames";
+		else if (fc & IEEE80211_FCTL_RETRY)
+			title = "Retry";
+		else if (ieee80211_is_assoc_response(fc))
+			title = "AscRsp";
+		else if (ieee80211_is_reassoc_response(fc))
+			title = "RasRsp";
+		else if (ieee80211_is_probe_response(fc)) {
+			title = "PrbRsp";
+			print_dump = 1;	/* dump frame contents */
+		} else if (ieee80211_is_beacon(fc)) {
+			title = "Beacon";
+			print_dump = 1;	/* dump frame contents */
+		} else if (ieee80211_is_atim(fc))
+			title = "ATIM";
+		else if (ieee80211_is_auth(fc))
+			title = "Auth";
+		else if (ieee80211_is_deauth(fc))
+			title = "DeAuth";
+		else if (ieee80211_is_disassoc(fc))
+			title = "DisAssoc";
+		else
+			title = "Frame";
+
+		rate = iwl3945_hwrate_to_plcp_idx(rx_hdr->rate);
+		if (rate == -1)
+			rate = 0;
+		else
+			rate = iwl3945_rates[rate].ieee / 2;
+
+		/* print frame summary.
+		 * MAC addresses show just the last byte (for brevity),
+		 *    but you can hack it to show more, if you'd like to. */
+		if (dataframe)
+			IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, "
+				     "len=%u, rssi=%d, chnl=%d, rate=%u, \n",
+				     title, fc, header->addr1[5],
+				     length, rssi, channel, rate);
+		else {
+			/* src/dst addresses assume managed mode */
+			IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
+				     "src=0x%02x, rssi=%u, tim=%lu usec, "
+				     "phy=0x%02x, chnl=%d\n",
+				     title, fc, header->addr1[5],
+				     header->addr3[5], rssi,
+				     tsf_low - priv->scan_start_tsf,
+				     phy_flags, channel);
+		}
+	}
+	if (print_dump)
+		iwl3945_print_hex_dump(IWL_DL_RX, data, length);
+}
+#else
+static inline void iwl3945_dbg_report_frame(struct iwl3945_priv *priv,
+		      struct iwl3945_rx_packet *pkt,
+		      struct ieee80211_hdr *header, int group100)
+{
+}
+#endif
+
+
 static void iwl3945_add_radiotap(struct iwl3945_priv *priv,
 static void iwl3945_add_radiotap(struct iwl3945_priv *priv,
 				 struct sk_buff *skb,
 				 struct sk_buff *skb,
 				 struct iwl3945_rx_frame_hdr *rx_hdr,
 				 struct iwl3945_rx_frame_hdr *rx_hdr,
@@ -376,24 +649,28 @@ static void iwl3945_handle_data_packet(struct iwl3945_priv *priv, int is_data,
 static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
 static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
 				struct iwl3945_rx_mem_buffer *rxb)
 				struct iwl3945_rx_mem_buffer *rxb)
 {
 {
+	struct ieee80211_hdr *header;
+	struct ieee80211_rx_status rx_status;
 	struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data;
 	struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data;
 	struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
 	struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
 	struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
 	struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
 	struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
 	struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
-	struct ieee80211_hdr *header;
+	int snr;
 	u16 rx_stats_sig_avg = le16_to_cpu(rx_stats->sig_avg);
 	u16 rx_stats_sig_avg = le16_to_cpu(rx_stats->sig_avg);
 	u16 rx_stats_noise_diff = le16_to_cpu(rx_stats->noise_diff);
 	u16 rx_stats_noise_diff = le16_to_cpu(rx_stats->noise_diff);
-	struct ieee80211_rx_status stats = {
-		.mactime = le64_to_cpu(rx_end->timestamp),
-		.freq = ieee80211chan2mhz(le16_to_cpu(rx_hdr->channel)),
-		.band = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
-		IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ,
-		.antenna = 0,
-		.rate_idx = iwl3945_rate_index_from_plcp(rx_hdr->rate),
-		.flag = 0,
-	};
 	u8 network_packet;
 	u8 network_packet;
-	int snr;
+
+	rx_status.antenna = 0;
+	rx_status.flag = 0;
+	rx_status.mactime = le64_to_cpu(rx_end->timestamp);
+	rx_status.freq = ieee80211chan2mhz(le16_to_cpu(rx_hdr->channel));
+	rx_status.band = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
+				IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+
+	rx_status.rate_idx = iwl3945_hwrate_to_plcp_idx(rx_hdr->rate);
+
+	if (rx_status.band == IEEE80211_BAND_5GHZ)
+		rx_status.rate_idx -= IWL_FIRST_OFDM_RATE;
 
 
 	if ((unlikely(rx_stats->phy_count > 20))) {
 	if ((unlikely(rx_stats->phy_count > 20))) {
 		IWL_DEBUG_DROP
 		IWL_DEBUG_DROP
@@ -409,12 +686,12 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
 	}
 	}
 
 
 	if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
 	if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) {
-		iwl3945_handle_data_packet(priv, 1, rxb, &stats);
+		iwl3945_handle_data_packet(priv, 1, rxb, &rx_status);
 		return;
 		return;
 	}
 	}
 
 
 	/* Convert 3945's rssi indicator to dBm */
 	/* Convert 3945's rssi indicator to dBm */
-	stats.ssi = rx_stats->rssi - IWL_RSSI_OFFSET;
+	rx_status.ssi = rx_stats->rssi - IWL_RSSI_OFFSET;
 
 
 	/* Set default noise value to -127 */
 	/* Set default noise value to -127 */
 	if (priv->last_rx_noise == 0)
 	if (priv->last_rx_noise == 0)
@@ -430,50 +707,47 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
 	 *   signal-to-noise ratio (SNR) is (sig_avg / noise_diff).
 	 *   signal-to-noise ratio (SNR) is (sig_avg / noise_diff).
 	 * Convert linear SNR to dB SNR, then subtract that from rssi dBm
 	 * Convert linear SNR to dB SNR, then subtract that from rssi dBm
 	 *   to obtain noise level in dBm.
 	 *   to obtain noise level in dBm.
-	 * Calculate stats.signal (quality indicator in %) based on SNR. */
+	 * Calculate rx_status.signal (quality indicator in %) based on SNR. */
 	if (rx_stats_noise_diff) {
 	if (rx_stats_noise_diff) {
 		snr = rx_stats_sig_avg / rx_stats_noise_diff;
 		snr = rx_stats_sig_avg / rx_stats_noise_diff;
-		stats.noise = stats.ssi - iwl3945_calc_db_from_ratio(snr);
-		stats.signal = iwl3945_calc_sig_qual(stats.ssi, stats.noise);
+		rx_status.noise = rx_status.ssi -
+					iwl3945_calc_db_from_ratio(snr);
+		rx_status.signal = iwl3945_calc_sig_qual(rx_status.ssi,
+							 rx_status.noise);
 
 
 	/* If noise info not available, calculate signal quality indicator (%)
 	/* If noise info not available, calculate signal quality indicator (%)
 	 *   using just the dBm signal level. */
 	 *   using just the dBm signal level. */
 	} else {
 	} else {
-		stats.noise = priv->last_rx_noise;
-		stats.signal = iwl3945_calc_sig_qual(stats.ssi, 0);
+		rx_status.noise = priv->last_rx_noise;
+		rx_status.signal = iwl3945_calc_sig_qual(rx_status.ssi, 0);
 	}
 	}
 
 
 
 
 	IWL_DEBUG_STATS("Rssi %d noise %d qual %d sig_avg %d noise_diff %d\n",
 	IWL_DEBUG_STATS("Rssi %d noise %d qual %d sig_avg %d noise_diff %d\n",
-			stats.ssi, stats.noise, stats.signal,
+			rx_status.ssi, rx_status.noise, rx_status.signal,
 			rx_stats_sig_avg, rx_stats_noise_diff);
 			rx_stats_sig_avg, rx_stats_noise_diff);
 
 
-	/* can be covered by iwl3945_report_frame() in most cases */
-/*      IWL_DEBUG_RX("RX status: 0x%08X\n", rx_end->status); */
-
 	header = (struct ieee80211_hdr *)IWL_RX_DATA(pkt);
 	header = (struct ieee80211_hdr *)IWL_RX_DATA(pkt);
 
 
 	network_packet = iwl3945_is_network_packet(priv, header);
 	network_packet = iwl3945_is_network_packet(priv, header);
 
 
-#ifdef CONFIG_IWL3945_DEBUG
-	if (iwl3945_debug_level & IWL_DL_STATS && net_ratelimit())
-		IWL_DEBUG_STATS
-		    ("[%c] %d RSSI: %d Signal: %u, Noise: %u, Rate: %u\n",
-		     network_packet ? '*' : ' ',
-		     le16_to_cpu(rx_hdr->channel),
-		     stats.ssi, stats.ssi,
-		     stats.ssi, stats.rate_idx);
+	IWL_DEBUG_STATS_LIMIT("[%c] %d RSSI:%d Signal:%u, Noise:%u, Rate:%u\n",
+			      network_packet ? '*' : ' ',
+			      le16_to_cpu(rx_hdr->channel),
+			      rx_status.ssi, rx_status.ssi,
+			      rx_status.ssi, rx_status.rate_idx);
 
 
+#ifdef CONFIG_IWL3945_DEBUG
 	if (iwl3945_debug_level & (IWL_DL_RX))
 	if (iwl3945_debug_level & (IWL_DL_RX))
 		/* Set "1" to report good data frames in groups of 100 */
 		/* Set "1" to report good data frames in groups of 100 */
-		iwl3945_report_frame(priv, pkt, header, 1);
+		iwl3945_dbg_report_frame(priv, pkt, header, 1);
 #endif
 #endif
 
 
 	if (network_packet) {
 	if (network_packet) {
 		priv->last_beacon_time = le32_to_cpu(rx_end->beacon_timestamp);
 		priv->last_beacon_time = le32_to_cpu(rx_end->beacon_timestamp);
 		priv->last_tsf = le64_to_cpu(rx_end->timestamp);
 		priv->last_tsf = le64_to_cpu(rx_end->timestamp);
-		priv->last_rx_rssi = stats.ssi;
-		priv->last_rx_noise = stats.noise;
+		priv->last_rx_rssi = rx_status.ssi;
+		priv->last_rx_noise = rx_status.noise;
 	}
 	}
 
 
 	switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FTYPE) {
 	switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FTYPE) {
@@ -560,7 +834,7 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
 			}
 			}
 		}
 		}
 
 
-		iwl3945_handle_data_packet(priv, 0, rxb, &stats);
+		iwl3945_handle_data_packet(priv, 0, rxb, &rx_status);
 		break;
 		break;
 
 
 	case IEEE80211_FTYPE_CTL:
 	case IEEE80211_FTYPE_CTL:
@@ -577,7 +851,7 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv,
 				       print_mac(mac2, header->addr2),
 				       print_mac(mac2, header->addr2),
 				       print_mac(mac3, header->addr3));
 				       print_mac(mac3, header->addr3));
 		else
 		else
-			iwl3945_handle_data_packet(priv, 1, rxb, &stats);
+			iwl3945_handle_data_packet(priv, 1, rxb, &rx_status);
 		break;
 		break;
 	}
 	}
 	}
 	}
@@ -993,19 +1267,19 @@ int iwl3945_hw_nic_init(struct iwl3945_priv *priv)
 	if (rev_id & PCI_CFG_REV_ID_BIT_RTP)
 	if (rev_id & PCI_CFG_REV_ID_BIT_RTP)
 		IWL_DEBUG_INFO("RTP type \n");
 		IWL_DEBUG_INFO("RTP type \n");
 	else if (rev_id & PCI_CFG_REV_ID_BIT_BASIC_SKU) {
 	else if (rev_id & PCI_CFG_REV_ID_BIT_BASIC_SKU) {
-		IWL_DEBUG_INFO("ALM-MB type\n");
+		IWL_DEBUG_INFO("3945 RADIO-MB type\n");
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
-			    CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MB);
+			    CSR39_HW_IF_CONFIG_REG_BIT_3945_MB);
 	} else {
 	} else {
-		IWL_DEBUG_INFO("ALM-MM type\n");
+		IWL_DEBUG_INFO("3945 RADIO-MM type\n");
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
-			    CSR_HW_IF_CONFIG_REG_BIT_ALMAGOR_MM);
+			    CSR39_HW_IF_CONFIG_REG_BIT_3945_MM);
 	}
 	}
 
 
 	if (EEPROM_SKU_CAP_OP_MODE_MRC == priv->eeprom.sku_cap) {
 	if (EEPROM_SKU_CAP_OP_MODE_MRC == priv->eeprom.sku_cap) {
 		IWL_DEBUG_INFO("SKU OP mode is mrc\n");
 		IWL_DEBUG_INFO("SKU OP mode is mrc\n");
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
-			    CSR_HW_IF_CONFIG_REG_BIT_SKU_MRC);
+			    CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC);
 	} else
 	} else
 		IWL_DEBUG_INFO("SKU OP mode is basic\n");
 		IWL_DEBUG_INFO("SKU OP mode is basic\n");
 
 
@@ -1013,24 +1287,24 @@ int iwl3945_hw_nic_init(struct iwl3945_priv *priv)
 		IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
 		IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
 			       priv->eeprom.board_revision);
 			       priv->eeprom.board_revision);
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
-			    CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
+			    CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
 	} else {
 	} else {
 		IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
 		IWL_DEBUG_INFO("3945ABG revision is 0x%X\n",
 			       priv->eeprom.board_revision);
 			       priv->eeprom.board_revision);
 		iwl3945_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
 		iwl3945_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
-			      CSR_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
+			      CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE);
 	}
 	}
 
 
 	if (priv->eeprom.almgor_m_version <= 1) {
 	if (priv->eeprom.almgor_m_version <= 1) {
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
-			    CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A);
+			    CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A);
 		IWL_DEBUG_INFO("Card M type A version is 0x%X\n",
 		IWL_DEBUG_INFO("Card M type A version is 0x%X\n",
 			       priv->eeprom.almgor_m_version);
 			       priv->eeprom.almgor_m_version);
 	} else {
 	} else {
 		IWL_DEBUG_INFO("Card M type B version is 0x%X\n",
 		IWL_DEBUG_INFO("Card M type B version is 0x%X\n",
 			       priv->eeprom.almgor_m_version);
 			       priv->eeprom.almgor_m_version);
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
 		iwl3945_set_bit(priv, CSR_HW_IF_CONFIG_REG,
-			    CSR_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B);
+			    CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B);
 	}
 	}
 	spin_unlock_irqrestore(&priv->lock, flags);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 
@@ -2348,6 +2622,7 @@ unsigned int iwl3945_hw_get_beacon_cmd(struct iwl3945_priv *priv,
 
 
 void iwl3945_hw_rx_handler_setup(struct iwl3945_priv *priv)
 void iwl3945_hw_rx_handler_setup(struct iwl3945_priv *priv)
 {
 {
+	priv->rx_handlers[REPLY_TX] = iwl3945_rx_reply_tx;
 	priv->rx_handlers[REPLY_3945_RX] = iwl3945_rx_reply_rx;
 	priv->rx_handlers[REPLY_3945_RX] = iwl3945_rx_reply_rx;
 }
 }
 
 
@@ -2362,9 +2637,25 @@ void iwl3945_hw_cancel_deferred_work(struct iwl3945_priv *priv)
 	cancel_delayed_work(&priv->thermal_periodic);
 	cancel_delayed_work(&priv->thermal_periodic);
 }
 }
 
 
+static struct iwl_3945_cfg iwl3945_bg_cfg = {
+	.name = "3945BG",
+	.fw_name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode",
+	.sku = IWL_SKU_G,
+};
+
+static struct iwl_3945_cfg iwl3945_abg_cfg = {
+	.name = "3945ABG",
+	.fw_name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode",
+	.sku = IWL_SKU_A|IWL_SKU_G,
+};
+
 struct pci_device_id iwl3945_hw_card_ids[] = {
 struct pci_device_id iwl3945_hw_card_ids[] = {
-	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4222)},
-	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4227)},
+	{IWL_PCI_DEVICE(0x4222, 0x1005, iwl3945_bg_cfg)},
+	{IWL_PCI_DEVICE(0x4222, 0x1034, iwl3945_bg_cfg)},
+	{IWL_PCI_DEVICE(0x4222, 0x1044, iwl3945_bg_cfg)},
+	{IWL_PCI_DEVICE(0x4227, 0x1014, iwl3945_bg_cfg)},
+	{IWL_PCI_DEVICE(0x4222, PCI_ANY_ID, iwl3945_abg_cfg)},
+	{IWL_PCI_DEVICE(0x4227, PCI_ANY_ID, iwl3945_abg_cfg)},
 	{0}
 	{0}
 };
 };
 
 

+ 12 - 23
drivers/net/wireless/iwlwifi/iwl-3945.h

@@ -40,10 +40,17 @@
 extern struct pci_device_id iwl3945_hw_card_ids[];
 extern struct pci_device_id iwl3945_hw_card_ids[];
 
 
 #define DRV_NAME	"iwl3945"
 #define DRV_NAME	"iwl3945"
-#include "iwl-3945-hw.h"
+#include "iwl-csr.h"
 #include "iwl-prph.h"
 #include "iwl-prph.h"
+#include "iwl-3945-hw.h"
 #include "iwl-3945-debug.h"
 #include "iwl-3945-debug.h"
 
 
+/* Change firmware file name, using "-" and incrementing number,
+ *   *only* when uCode interface or architecture changes so that it
+ *   is not compatible with earlier drivers.
+ * This number will also appear in << 8 position of 1st dword of uCode file */
+#define IWL3945_UCODE_API "-1"
+
 /* Default noise level to report when noise measurement is not available.
 /* Default noise level to report when noise measurement is not available.
  *   This may be because we're:
  *   This may be because we're:
  *   1)  Not associated (4965, no beacon statistics being sent to driver)
  *   1)  Not associated (4965, no beacon statistics being sent to driver)
@@ -109,6 +116,9 @@ struct iwl3945_queue {
 				* space less than this */
 				* space less than this */
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
+int iwl3945_queue_space(const struct iwl3945_queue *q);
+int iwl3945_x2_queue_used(const struct iwl3945_queue *q, int i);
+
 #define MAX_NUM_OF_TBS          (20)
 #define MAX_NUM_OF_TBS          (20)
 
 
 /* One for each TFD */
 /* One for each TFD */
@@ -558,16 +568,6 @@ extern int iwl3945_is_network_packet(struct iwl3945_priv *priv,
 				 struct ieee80211_hdr *header);
 				 struct ieee80211_hdr *header);
 extern int iwl3945_power_init_handle(struct iwl3945_priv *priv);
 extern int iwl3945_power_init_handle(struct iwl3945_priv *priv);
 extern int iwl3945_eeprom_init(struct iwl3945_priv *priv);
 extern int iwl3945_eeprom_init(struct iwl3945_priv *priv);
-#ifdef CONFIG_IWL3945_DEBUG
-extern void iwl3945_report_frame(struct iwl3945_priv *priv,
-			     struct iwl3945_rx_packet *pkt,
-			     struct ieee80211_hdr *header, int group100);
-#else
-static inline void iwl3945_report_frame(struct iwl3945_priv *priv,
-				    struct iwl3945_rx_packet *pkt,
-				    struct ieee80211_hdr *header,
-				    int group100) {}
-#endif
 extern void iwl3945_handle_data_packet_monitor(struct iwl3945_priv *priv,
 extern void iwl3945_handle_data_packet_monitor(struct iwl3945_priv *priv,
 					   struct iwl3945_rx_mem_buffer *rxb,
 					   struct iwl3945_rx_mem_buffer *rxb,
 					   void *data, short len,
 					   void *data, short len,
@@ -691,6 +691,7 @@ struct iwl3945_priv {
 	struct ieee80211_hw *hw;
 	struct ieee80211_hw *hw;
 	struct ieee80211_channel *ieee_channels;
 	struct ieee80211_channel *ieee_channels;
 	struct ieee80211_rate *ieee_rates;
 	struct ieee80211_rate *ieee_rates;
+	struct iwl_3945_cfg *cfg; /* device configuration */
 
 
 	/* temporary frame storage list */
 	/* temporary frame storage list */
 	struct list_head free_frames;
 	struct list_head free_frames;
@@ -800,7 +801,6 @@ struct iwl3945_priv {
 	struct iwl3945_tx_queue txq[IWL_MAX_NUM_QUEUES];
 	struct iwl3945_tx_queue txq[IWL_MAX_NUM_QUEUES];
 
 
 	unsigned long status;
 	unsigned long status;
-	u32 config;
 
 
 	int last_rx_rssi;	/* From Rx packet statisitics */
 	int last_rx_rssi;	/* From Rx packet statisitics */
 	int last_rx_noise;	/* From beacon statistics */
 	int last_rx_noise;	/* From beacon statistics */
@@ -830,7 +830,6 @@ struct iwl3945_priv {
 	int is_open;
 	int is_open;
 
 
 	u8 mac80211_registered;
 	u8 mac80211_registered;
-	int is_abg;
 
 
 	u32 notif_missed_beacons;
 	u32 notif_missed_beacons;
 
 
@@ -950,16 +949,6 @@ static inline int is_channel_ibss(const struct iwl3945_channel_info *ch)
 	return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0;
 	return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0;
 }
 }
 
 
-static inline int iwl3945_rate_index_from_plcp(int plcp)
-{
-	int i;
-
-	for (i = 0; i < IWL_RATE_COUNT; i++)
-		if (iwl3945_rates[i].plcp == plcp)
-			return i;
-	return -1;
-}
-
 extern const struct iwl3945_channel_info *iwl3945_get_channel_info(
 extern const struct iwl3945_channel_info *iwl3945_get_channel_info(
 	const struct iwl3945_priv *priv, enum ieee80211_band band, u16 channel);
 	const struct iwl3945_priv *priv, enum ieee80211_band band, u16 channel);
 
 

+ 20 - 20
drivers/net/wireless/iwlwifi/iwl-4965-commands.h

@@ -875,26 +875,26 @@ struct iwl4965_rx_frame_hdr {
 	u8 payload[0];
 	u8 payload[0];
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
-#define	RX_RES_STATUS_NO_CRC32_ERROR	__constant_cpu_to_le32(1 << 0)
-#define	RX_RES_STATUS_NO_RXE_OVERFLOW	__constant_cpu_to_le32(1 << 1)
-
-#define	RX_RES_PHY_FLAGS_BAND_24_MSK	__constant_cpu_to_le16(1 << 0)
-#define	RX_RES_PHY_FLAGS_MOD_CCK_MSK		__constant_cpu_to_le16(1 << 1)
-#define	RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK	__constant_cpu_to_le16(1 << 2)
-#define	RX_RES_PHY_FLAGS_NARROW_BAND_MSK	__constant_cpu_to_le16(1 << 3)
-#define	RX_RES_PHY_FLAGS_ANTENNA_MSK		__constant_cpu_to_le16(0xf0)
-
-#define	RX_RES_STATUS_SEC_TYPE_MSK	(0x7 << 8)
-#define	RX_RES_STATUS_SEC_TYPE_NONE	(0x0 << 8)
-#define	RX_RES_STATUS_SEC_TYPE_WEP	(0x1 << 8)
-#define	RX_RES_STATUS_SEC_TYPE_CCMP	(0x2 << 8)
-#define	RX_RES_STATUS_SEC_TYPE_TKIP	(0x3 << 8)
-
-#define	RX_RES_STATUS_DECRYPT_TYPE_MSK	(0x3 << 11)
-#define	RX_RES_STATUS_NOT_DECRYPT	(0x0 << 11)
-#define	RX_RES_STATUS_DECRYPT_OK	(0x3 << 11)
-#define	RX_RES_STATUS_BAD_ICV_MIC	(0x1 << 11)
-#define	RX_RES_STATUS_BAD_KEY_TTAK	(0x2 << 11)
+#define RX_RES_STATUS_NO_CRC32_ERROR	__constant_cpu_to_le32(1 << 0)
+#define RX_RES_STATUS_NO_RXE_OVERFLOW	__constant_cpu_to_le32(1 << 1)
+
+#define RX_RES_PHY_FLAGS_BAND_24_MSK	__constant_cpu_to_le16(1 << 0)
+#define RX_RES_PHY_FLAGS_MOD_CCK_MSK		__constant_cpu_to_le16(1 << 1)
+#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK	__constant_cpu_to_le16(1 << 2)
+#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK	__constant_cpu_to_le16(1 << 3)
+#define RX_RES_PHY_FLAGS_ANTENNA_MSK		__constant_cpu_to_le16(0xf0)
+
+#define RX_RES_STATUS_SEC_TYPE_MSK	(0x7 << 8)
+#define RX_RES_STATUS_SEC_TYPE_NONE	(0x0 << 8)
+#define RX_RES_STATUS_SEC_TYPE_WEP	(0x1 << 8)
+#define RX_RES_STATUS_SEC_TYPE_CCMP	(0x2 << 8)
+#define RX_RES_STATUS_SEC_TYPE_TKIP	(0x3 << 8)
+
+#define RX_RES_STATUS_DECRYPT_TYPE_MSK	(0x3 << 11)
+#define RX_RES_STATUS_NOT_DECRYPT	(0x0 << 11)
+#define RX_RES_STATUS_DECRYPT_OK	(0x3 << 11)
+#define RX_RES_STATUS_BAD_ICV_MIC	(0x1 << 11)
+#define RX_RES_STATUS_BAD_KEY_TTAK	(0x2 << 11)
 
 
 struct iwl4965_rx_frame_end {
 struct iwl4965_rx_frame_end {
 	__le32 status;
 	__le32 status;

+ 16 - 0
drivers/net/wireless/iwlwifi/iwl-4965-debug.h

@@ -40,15 +40,30 @@ do { if (iwl4965_debug_level & (level)) \
 do { if ((iwl4965_debug_level & (level)) && net_ratelimit()) \
 do { if ((iwl4965_debug_level & (level)) && net_ratelimit()) \
   printk(KERN_ERR DRV_NAME": %c %s " fmt, \
   printk(KERN_ERR DRV_NAME": %c %s " fmt, \
 	 in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
 	 in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
+
+static inline void iwl4965_print_hex_dump(int level, void *p, u32 len)
+{
+	if (!(iwl4965_debug_level & level))
+		return;
+
+	print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1,
+			p, len, 1);
+}
 #else
 #else
+
 static inline void IWL_DEBUG(int level, const char *fmt, ...)
 static inline void IWL_DEBUG(int level, const char *fmt, ...)
 {
 {
 }
 }
 static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
 static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
 {
 {
 }
 }
+static inline void iwl4965_print_hex_dump(int level, void *p, u32 len)
+{
+}
 #endif				/* CONFIG_IWL4965_DEBUG */
 #endif				/* CONFIG_IWL4965_DEBUG */
 
 
+
+
 /*
 /*
  * To use the debug system;
  * To use the debug system;
  *
  *
@@ -143,6 +158,7 @@ static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...)
 	IWL_DEBUG_LIMIT(IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
 	IWL_DEBUG_LIMIT(IWL_DL_ASSOC | IWL_DL_INFO, f, ## a)
 #define IWL_DEBUG_HT(f, a...) IWL_DEBUG(IWL_DL_HT, f, ## a)
 #define IWL_DEBUG_HT(f, a...) IWL_DEBUG(IWL_DL_HT, f, ## a)
 #define IWL_DEBUG_STATS(f, a...) IWL_DEBUG(IWL_DL_STATS, f, ## a)
 #define IWL_DEBUG_STATS(f, a...) IWL_DEBUG(IWL_DL_STATS, f, ## a)
+#define IWL_DEBUG_STATS_LIMIT(f, a...) IWL_DEBUG_LIMIT(IWL_DL_STATS, f, ## a)
 #define IWL_DEBUG_TX_REPLY(f, a...) IWL_DEBUG(IWL_DL_TX_REPLY, f, ## a)
 #define IWL_DEBUG_TX_REPLY(f, a...) IWL_DEBUG(IWL_DL_TX_REPLY, f, ## a)
 #define IWL_DEBUG_QOS(f, a...)   IWL_DEBUG(IWL_DL_QOS, f, ## a)
 #define IWL_DEBUG_QOS(f, a...)   IWL_DEBUG(IWL_DL_QOS, f, ## a)
 #define IWL_DEBUG_RADIO(f, a...)  IWL_DEBUG(IWL_DL_RADIO, f, ## a)
 #define IWL_DEBUG_RADIO(f, a...)  IWL_DEBUG(IWL_DL_RADIO, f, ## a)

+ 0 - 175
drivers/net/wireless/iwlwifi/iwl-4965-hw.h

@@ -410,181 +410,6 @@ struct iwl4965_eeprom {
 #define PCI_REG_WUM8       0x0E8
 #define PCI_REG_WUM8       0x0E8
 #define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT         (0x80000000)
 #define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT         (0x80000000)
 
 
-/*=== CSR (control and status registers) ===*/
-#define CSR_BASE    (0x000)
-
-#define CSR_HW_IF_CONFIG_REG    (CSR_BASE+0x000) /* hardware interface config */
-#define CSR_INT_COALESCING      (CSR_BASE+0x004) /* accum ints, 32-usec units */
-#define CSR_INT                 (CSR_BASE+0x008) /* host interrupt status/ack */
-#define CSR_INT_MASK            (CSR_BASE+0x00c) /* host interrupt enable */
-#define CSR_FH_INT_STATUS       (CSR_BASE+0x010) /* busmaster int status/ack*/
-#define CSR_GPIO_IN             (CSR_BASE+0x018) /* read external chip pins */
-#define CSR_RESET               (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/
-#define CSR_GP_CNTRL            (CSR_BASE+0x024)
-
-/*
- * Hardware revision info
- * Bit fields:
- * 31-8:  Reserved
- *  7-4:  Type of device:  0x0 = 4965, 0xd = 3945
- *  3-2:  Revision step:  0 = A, 1 = B, 2 = C, 3 = D
- *  1-0:  "Dash" value, as in A-1, etc.
- *
- * NOTE:  Revision step affects calculation of CCK txpower for 4965.
- */
-#define CSR_HW_REV              (CSR_BASE+0x028)
-
-/* EEPROM reads */
-#define CSR_EEPROM_REG          (CSR_BASE+0x02c)
-#define CSR_EEPROM_GP           (CSR_BASE+0x030)
-#define CSR_GP_UCODE		(CSR_BASE+0x044)
-#define CSR_UCODE_DRV_GP1       (CSR_BASE+0x054)
-#define CSR_UCODE_DRV_GP1_SET   (CSR_BASE+0x058)
-#define CSR_UCODE_DRV_GP1_CLR   (CSR_BASE+0x05c)
-#define CSR_UCODE_DRV_GP2       (CSR_BASE+0x060)
-#define CSR_GIO_CHICKEN_BITS    (CSR_BASE+0x100)
-
-/*
- * Indicates hardware rev, to determine CCK backoff for txpower calculation.
- * Bit fields:
- *  3-2:  0 = A, 1 = B, 2 = C, 3 = D step
- */
-#define CSR_HW_REV_WA_REG	(CSR_BASE+0x22C)
-
-/* Hardware interface configuration bits */
-#define CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R	(0x00000010)
-#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER	(0x00000C00)
-#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI		(0x00000100)
-#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI	(0x00000200)
-#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000)
-
-/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
- * acknowledged (reset) by host writing "1" to flagged bits. */
-#define CSR_INT_BIT_FH_RX        (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */
-#define CSR_INT_BIT_HW_ERR       (1 << 29) /* DMA hardware error FH_INT[31] */
-#define CSR_INT_BIT_DNLD         (1 << 28) /* uCode Download */
-#define CSR_INT_BIT_FH_TX        (1 << 27) /* Tx DMA FH_INT[1:0] */
-#define CSR_INT_BIT_SCD          (1 << 26) /* TXQ pointer advanced */
-#define CSR_INT_BIT_SW_ERR       (1 << 25) /* uCode error */
-#define CSR_INT_BIT_RF_KILL      (1 << 7)  /* HW RFKILL switch GP_CNTRL[27] toggled */
-#define CSR_INT_BIT_CT_KILL      (1 << 6)  /* Critical temp (chip too hot) rfkill */
-#define CSR_INT_BIT_SW_RX        (1 << 3)  /* Rx, command responses, 3945 */
-#define CSR_INT_BIT_WAKEUP       (1 << 1)  /* NIC controller waking up (pwr mgmt) */
-#define CSR_INT_BIT_ALIVE        (1 << 0)  /* uCode interrupts once it initializes */
-
-#define CSR_INI_SET_MASK	(CSR_INT_BIT_FH_RX   | \
-				 CSR_INT_BIT_HW_ERR  | \
-				 CSR_INT_BIT_FH_TX   | \
-				 CSR_INT_BIT_SW_ERR  | \
-				 CSR_INT_BIT_RF_KILL | \
-				 CSR_INT_BIT_SW_RX   | \
-				 CSR_INT_BIT_WAKEUP  | \
-				 CSR_INT_BIT_ALIVE)
-
-/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
-#define CSR_FH_INT_BIT_ERR       (1 << 31) /* Error */
-#define CSR_FH_INT_BIT_HI_PRIOR  (1 << 30) /* High priority Rx, bypass coalescing */
-#define CSR_FH_INT_BIT_RX_CHNL1  (1 << 17) /* Rx channel 1 */
-#define CSR_FH_INT_BIT_RX_CHNL0  (1 << 16) /* Rx channel 0 */
-#define CSR_FH_INT_BIT_TX_CHNL1  (1 << 1)  /* Tx channel 1 */
-#define CSR_FH_INT_BIT_TX_CHNL0  (1 << 0)  /* Tx channel 0 */
-
-#define CSR_FH_INT_RX_MASK	(CSR_FH_INT_BIT_HI_PRIOR | \
-				 CSR_FH_INT_BIT_RX_CHNL1 | \
-				 CSR_FH_INT_BIT_RX_CHNL0)
-
-#define CSR_FH_INT_TX_MASK	(CSR_FH_INT_BIT_TX_CHNL1 | \
-				 CSR_FH_INT_BIT_TX_CHNL0)
-
-
-/* RESET */
-#define CSR_RESET_REG_FLAG_NEVO_RESET                (0x00000001)
-#define CSR_RESET_REG_FLAG_FORCE_NMI                 (0x00000002)
-#define CSR_RESET_REG_FLAG_SW_RESET                  (0x00000080)
-#define CSR_RESET_REG_FLAG_MASTER_DISABLED           (0x00000100)
-#define CSR_RESET_REG_FLAG_STOP_MASTER               (0x00000200)
-
-/* GP (general purpose) CONTROL */
-#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY        (0x00000001)
-#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE              (0x00000004)
-#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ         (0x00000008)
-#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP         (0x00000010)
-
-#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN           (0x00000001)
-
-#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE         (0x07000000)
-#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE         (0x04000000)
-#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW          (0x08000000)
-
-
-/* EEPROM REG */
-#define CSR_EEPROM_REG_READ_VALID_MSK	(0x00000001)
-#define CSR_EEPROM_REG_BIT_CMD		(0x00000002)
-
-/* EEPROM GP */
-#define CSR_EEPROM_GP_VALID_MSK		(0x00000006)
-#define CSR_EEPROM_GP_BAD_SIGNATURE	(0x00000000)
-#define CSR_EEPROM_GP_IF_OWNER_MSK	(0x00000180)
-
-/* UCODE DRV GP */
-#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP             (0x00000001)
-#define CSR_UCODE_SW_BIT_RFKILL                     (0x00000002)
-#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED           (0x00000004)
-#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT      (0x00000008)
-
-/* GPIO */
-#define CSR_GPIO_IN_BIT_AUX_POWER                   (0x00000200)
-#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC                (0x00000000)
-#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC		CSR_GPIO_IN_BIT_AUX_POWER
-
-/* GI Chicken Bits */
-#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX  (0x00800000)
-#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER  (0x20000000)
-
-/*=== HBUS (Host-side Bus) ===*/
-#define HBUS_BASE	(0x400)
-
-/*
- * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM
- * structures, error log, event log, verifying uCode load).
- * First write to address register, then read from or write to data register
- * to complete the job.  Once the address register is set up, accesses to
- * data registers auto-increment the address by one dword.
- * Bit usage for address registers (read or write):
- *  0-31:  memory address within device
- */
-#define HBUS_TARG_MEM_RADDR     (HBUS_BASE+0x00c)
-#define HBUS_TARG_MEM_WADDR     (HBUS_BASE+0x010)
-#define HBUS_TARG_MEM_WDAT      (HBUS_BASE+0x018)
-#define HBUS_TARG_MEM_RDAT      (HBUS_BASE+0x01c)
-
-/*
- * Registers for accessing device's internal peripheral registers
- * (e.g. SCD, BSM, etc.).  First write to address register,
- * then read from or write to data register to complete the job.
- * Bit usage for address registers (read or write):
- *  0-15:  register address (offset) within device
- * 24-25:  (# bytes - 1) to read or write (e.g. 3 for dword)
- */
-#define HBUS_TARG_PRPH_WADDR    (HBUS_BASE+0x044)
-#define HBUS_TARG_PRPH_RADDR    (HBUS_BASE+0x048)
-#define HBUS_TARG_PRPH_WDAT     (HBUS_BASE+0x04c)
-#define HBUS_TARG_PRPH_RDAT     (HBUS_BASE+0x050)
-
-/*
- * Per-Tx-queue write pointer (index, really!) (3945 and 4965).
- * Driver sets this to indicate index to next TFD that driver will fill
- * (1 past latest filled).
- * Bit usage:
- *  0-7:  queue write index (0-255)
- * 11-8:  queue selector (0-15)
- */
-#define HBUS_TARG_WRPTR         (HBUS_BASE+0x060)
-
-#define HBUS_TARG_MBX_C         (HBUS_BASE+0x030)
-
-#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED         (0x00000004)
-
 #define TFD_QUEUE_SIZE_MAX      (256)
 #define TFD_QUEUE_SIZE_MAX      (256)
 
 
 #define IWL_NUM_SCAN_RATES         (2)
 #define IWL_NUM_SCAN_RATES         (2)

+ 50 - 48
drivers/net/wireless/iwlwifi/iwl-4965-rs.c

@@ -570,7 +570,7 @@ static int rs_get_tbl_info_from_mcs(const struct iwl4965_rate *mcs_rate,
 	int index;
 	int index;
 	u32 ant_msk;
 	u32 ant_msk;
 
 
-	index = iwl4965_rate_index_from_plcp(mcs_rate->rate_n_flags);
+	index = iwl4965_hwrate_to_plcp_idx(mcs_rate->rate_n_flags);
 
 
 	if (index  == IWL_RATE_INVALID) {
 	if (index  == IWL_RATE_INVALID) {
 		*rate_idx = -1;
 		*rate_idx = -1;
@@ -823,6 +823,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct iwl4965_priv *priv = (struct iwl4965_priv *)priv_rate;
 	struct iwl4965_priv *priv = (struct iwl4965_priv *)priv_rate;
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_hw *hw = local_to_hw(local);
 	struct iwl4965_rate_scale_data *window = NULL;
 	struct iwl4965_rate_scale_data *window = NULL;
 	struct iwl4965_rate_scale_data *search_win = NULL;
 	struct iwl4965_rate_scale_data *search_win = NULL;
 	struct iwl4965_rate tx_mcs;
 	struct iwl4965_rate tx_mcs;
@@ -847,23 +848,22 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
 	if (retries > 15)
 	if (retries > 15)
 		retries = 15;
 		retries = 15;
 
 
+	rcu_read_lock();
 
 
 	sta = sta_info_get(local, hdr->addr1);
 	sta = sta_info_get(local, hdr->addr1);
 
 
-	if (!sta || !sta->rate_ctrl_priv) {
-		if (sta)
-			sta_info_put(sta);
-		return;
-	}
+	if (!sta || !sta->rate_ctrl_priv)
+		goto out;
+
 
 
 	lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv;
 	lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv;
 
 
 	if (!priv->lq_mngr.lq_ready)
 	if (!priv->lq_mngr.lq_ready)
-		return;
+		goto out;
 
 
 	if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
 	if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) &&
 	    !lq_sta->ibss_sta_added)
 	    !lq_sta->ibss_sta_added)
-		return;
+		goto out;
 
 
 	table = &lq_sta->lq;
 	table = &lq_sta->lq;
 	active_index = lq_sta->active_tbl;
 	active_index = lq_sta->active_tbl;
@@ -884,17 +884,6 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
 	search_win = (struct iwl4965_rate_scale_data *)
 	search_win = (struct iwl4965_rate_scale_data *)
 	    &(search_tbl->win[0]);
 	    &(search_tbl->win[0]);
 
 
-	tx_mcs.rate_n_flags = tx_resp->control.tx_rate->hw_value;
-
-	rs_get_tbl_info_from_mcs(&tx_mcs, priv->band,
-				  &tbl_type, &rs_index);
-	if ((rs_index < 0) || (rs_index >= IWL_RATE_COUNT)) {
-		IWL_DEBUG_RATE("bad rate index at: %d rate 0x%X\n",
-			     rs_index, tx_mcs.rate_n_flags);
-		sta_info_put(sta);
-		return;
-	}
-
 	/*
 	/*
 	 * Ignore this Tx frame response if its initial rate doesn't match
 	 * Ignore this Tx frame response if its initial rate doesn't match
 	 * that of latest Link Quality command.  There may be stragglers
 	 * that of latest Link Quality command.  There may be stragglers
@@ -903,14 +892,29 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
 	 * to check "search" mode, or a prior "search" mode after we've moved
 	 * to check "search" mode, or a prior "search" mode after we've moved
 	 * to a new "search" mode (which might become the new "active" mode).
 	 * to a new "search" mode (which might become the new "active" mode).
 	 */
 	 */
-	if (retries &&
-	    (tx_mcs.rate_n_flags !=
-				le32_to_cpu(table->rs_table[0].rate_n_flags))) {
-		IWL_DEBUG_RATE("initial rate does not match 0x%x 0x%x\n",
-				tx_mcs.rate_n_flags,
-				le32_to_cpu(table->rs_table[0].rate_n_flags));
-		sta_info_put(sta);
-		return;
+	tx_mcs.rate_n_flags = le32_to_cpu(table->rs_table[0].rate_n_flags);
+	rs_get_tbl_info_from_mcs(&tx_mcs, priv->band, &tbl_type, &rs_index);
+	if (priv->band == IEEE80211_BAND_5GHZ)
+		rs_index -= IWL_FIRST_OFDM_RATE;
+
+	if ((tx_resp->control.tx_rate == NULL) ||
+	    (tbl_type.is_SGI ^
+		!!(tx_resp->control.flags & IEEE80211_TXCTL_SHORT_GI)) ||
+	    (tbl_type.is_fat ^
+		!!(tx_resp->control.flags & IEEE80211_TXCTL_40_MHZ_WIDTH)) ||
+	    (tbl_type.is_dup ^
+		!!(tx_resp->control.flags & IEEE80211_TXCTL_DUP_DATA)) ||
+	    (tbl_type.antenna_type ^
+		tx_resp->control.antenna_sel_tx) ||
+	    (!!(tx_mcs.rate_n_flags & RATE_MCS_HT_MSK) ^
+		!!(tx_resp->control.flags & IEEE80211_TXCTL_OFDM_HT)) ||
+	    (!!(tx_mcs.rate_n_flags & RATE_MCS_GF_MSK) ^
+		!!(tx_resp->control.flags & IEEE80211_TXCTL_GREEN_FIELD)) ||
+	    (hw->wiphy->bands[priv->band]->bitrates[rs_index].bitrate !=
+		tx_resp->control.tx_rate->bitrate)) {
+		IWL_DEBUG_RATE("initial rate does not match 0x%x\n",
+				tx_mcs.rate_n_flags);
+		goto out;
 	}
 	}
 
 
 	/* Update frame history window with "failure" for each Tx retry. */
 	/* Update frame history window with "failure" for each Tx retry. */
@@ -959,14 +963,8 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
 	 * if Tx was successful first try, use original rate,
 	 * if Tx was successful first try, use original rate,
 	 * else look up the rate that was, finally, successful.
 	 * else look up the rate that was, finally, successful.
 	 */
 	 */
-	if (!tx_resp->retry_count)
-		tx_mcs.rate_n_flags = tx_resp->control.tx_rate->hw_value;
-	else
-		tx_mcs.rate_n_flags =
-			le32_to_cpu(table->rs_table[index].rate_n_flags);
-
-	rs_get_tbl_info_from_mcs(&tx_mcs, priv->band,
-				  &tbl_type, &rs_index);
+	tx_mcs.rate_n_flags = le32_to_cpu(table->rs_table[index].rate_n_flags);
+	rs_get_tbl_info_from_mcs(&tx_mcs, priv->band, &tbl_type, &rs_index);
 
 
 	/* Update frame history window with "success" if Tx got ACKed ... */
 	/* Update frame history window with "success" if Tx got ACKed ... */
 	if (tx_resp->flags & IEEE80211_TX_STATUS_ACK)
 	if (tx_resp->flags & IEEE80211_TX_STATUS_ACK)
@@ -1025,7 +1023,8 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
 
 
 	/* See if there's a better rate or modulation mode to try. */
 	/* See if there's a better rate or modulation mode to try. */
 	rs_rate_scale_perform(priv, dev, hdr, sta);
 	rs_rate_scale_perform(priv, dev, hdr, sta);
-	sta_info_put(sta);
+out:
+	rcu_read_unlock();
 	return;
 	return;
 }
 }
 
 
@@ -1921,7 +1920,7 @@ static void rs_rate_scale_perform(struct iwl4965_priv *priv,
 			tbl = &(lq_sta->lq_info[active_tbl]);
 			tbl = &(lq_sta->lq_info[active_tbl]);
 
 
 			/* Revert to "active" rate and throughput info */
 			/* Revert to "active" rate and throughput info */
-			index = iwl4965_rate_index_from_plcp(
+			index = iwl4965_hwrate_to_plcp_idx(
 				tbl->current_rate.rate_n_flags);
 				tbl->current_rate.rate_n_flags);
 			current_tpt = lq_sta->last_tpt;
 			current_tpt = lq_sta->last_tpt;
 
 
@@ -2077,7 +2076,7 @@ static void rs_rate_scale_perform(struct iwl4965_priv *priv,
 				rs_rate_scale_clear_window(&(tbl->win[i]));
 				rs_rate_scale_clear_window(&(tbl->win[i]));
 
 
 			/* Use new "search" start rate */
 			/* Use new "search" start rate */
-			index = iwl4965_rate_index_from_plcp(
+			index = iwl4965_hwrate_to_plcp_idx(
 					tbl->current_rate.rate_n_flags);
 					tbl->current_rate.rate_n_flags);
 
 
 			IWL_DEBUG_HT("Switch current  mcs: %X index: %d\n",
 			IWL_DEBUG_HT("Switch current  mcs: %X index: %d\n",
@@ -2219,6 +2218,8 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
 
 
 	IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n");
 	IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n");
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 	sta = sta_info_get(local, hdr->addr1);
 
 
 	/* Send management frames and broadcast/multicast data using lowest
 	/* Send management frames and broadcast/multicast data using lowest
@@ -2227,9 +2228,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
 	if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) ||
 	if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) ||
 	    !sta || !sta->rate_ctrl_priv) {
 	    !sta || !sta->rate_ctrl_priv) {
 		sel->rate = rate_lowest(local, sband, sta);
 		sel->rate = rate_lowest(local, sband, sta);
-		if (sta)
-			sta_info_put(sta);
-		return;
+		goto out;
 	}
 	}
 
 
 	lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv;
 	lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv;
@@ -2256,14 +2255,15 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
 			goto done;
 			goto done;
 	}
 	}
 
 
- done:
+done:
 	if ((i < 0) || (i > IWL_RATE_COUNT)) {
 	if ((i < 0) || (i > IWL_RATE_COUNT)) {
 		sel->rate = rate_lowest(local, sband, sta);
 		sel->rate = rate_lowest(local, sband, sta);
-		return;
+		goto out;
 	}
 	}
-	sta_info_put(sta);
 
 
 	sel->rate = &priv->ieee_rates[i];
 	sel->rate = &priv->ieee_rates[i];
+out:
+	rcu_read_unlock();
 }
 }
 
 
 static void *rs_alloc_sta(void *priv, gfp_t gfp)
 static void *rs_alloc_sta(void *priv, gfp_t gfp)
@@ -2735,13 +2735,15 @@ int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
 	u32 max_time = 0;
 	u32 max_time = 0;
 	u8 lq_type, antenna;
 	u8 lq_type, antenna;
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
 	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
 	if (!sta || !sta->rate_ctrl_priv) {
 	if (!sta || !sta->rate_ctrl_priv) {
-		if (sta) {
-			sta_info_put(sta);
+		if (sta)
 			IWL_DEBUG_RATE("leave - no private rate data!\n");
 			IWL_DEBUG_RATE("leave - no private rate data!\n");
-		} else
+		else
 			IWL_DEBUG_RATE("leave - no station!\n");
 			IWL_DEBUG_RATE("leave - no station!\n");
+		rcu_read_unlock();
 		return sprintf(buf, "station %d not found\n", sta_id);
 		return sprintf(buf, "station %d not found\n", sta_id);
 	}
 	}
 
 
@@ -2808,7 +2810,7 @@ int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
 			 "active_search %d rate index %d\n", lq_type, antenna,
 			 "active_search %d rate index %d\n", lq_type, antenna,
 			 lq_sta->search_better_tbl, sta->last_txrate_idx);
 			 lq_sta->search_better_tbl, sta->last_txrate_idx);
 
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 	return cnt;
 	return cnt;
 }
 }
 
 

+ 1 - 1
drivers/net/wireless/iwlwifi/iwl-4965-rs.h

@@ -259,7 +259,7 @@ static inline u8 iwl4965_get_prev_ieee_rate(u8 rate_index)
 	return rate;
 	return rate;
 }
 }
 
 
-extern int iwl4965_rate_index_from_plcp(int plcp);
+extern int iwl4965_hwrate_to_plcp_idx(u32 rate_n_flags);
 
 
 /**
 /**
  * iwl4965_fill_rs_info - Fill an output text buffer with the rate representation
  * iwl4965_fill_rs_info - Fill an output text buffer with the rate representation

+ 286 - 66
drivers/net/wireless/iwlwifi/iwl-4965.c

@@ -38,6 +38,7 @@
 #include <linux/etherdevice.h>
 #include <linux/etherdevice.h>
 #include <asm/unaligned.h>
 #include <asm/unaligned.h>
 
 
+#include "iwl-core.h"
 #include "iwl-4965.h"
 #include "iwl-4965.h"
 #include "iwl-helpers.h"
 #include "iwl-helpers.h"
 
 
@@ -122,6 +123,64 @@ static u8 is_single_stream(struct iwl4965_priv *priv)
 	return 0;
 	return 0;
 }
 }
 
 
+int iwl4965_hwrate_to_plcp_idx(u32 rate_n_flags)
+{
+	int idx = 0;
+
+	/* 4965 HT rate format */
+	if (rate_n_flags & RATE_MCS_HT_MSK) {
+		idx = (rate_n_flags & 0xff);
+
+		if (idx >= IWL_RATE_MIMO_6M_PLCP)
+			idx = idx - IWL_RATE_MIMO_6M_PLCP;
+
+		idx += IWL_FIRST_OFDM_RATE;
+		/* skip 9M not supported in ht*/
+		if (idx >= IWL_RATE_9M_INDEX)
+			idx += 1;
+		if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE))
+			return idx;
+
+	/* 4965 legacy rate format, search for match in table */
+	} else {
+		for (idx = 0; idx < ARRAY_SIZE(iwl4965_rates); idx++)
+			if (iwl4965_rates[idx].plcp == (rate_n_flags & 0xFF))
+				return idx;
+	}
+
+	return -1;
+}
+
+/**
+ * translate ucode response to mac80211 tx status control values
+ */
+void iwl4965_hwrate_to_tx_control(struct iwl4965_priv *priv, u32 rate_n_flags,
+				  struct ieee80211_tx_control *control)
+{
+	int rate_index;
+
+	control->antenna_sel_tx =
+		((rate_n_flags & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_A_POS);
+	if (rate_n_flags & RATE_MCS_HT_MSK)
+		control->flags |= IEEE80211_TXCTL_OFDM_HT;
+	if (rate_n_flags & RATE_MCS_GF_MSK)
+		control->flags |= IEEE80211_TXCTL_GREEN_FIELD;
+	if (rate_n_flags & RATE_MCS_FAT_MSK)
+		control->flags |= IEEE80211_TXCTL_40_MHZ_WIDTH;
+	if (rate_n_flags & RATE_MCS_DUP_MSK)
+		control->flags |= IEEE80211_TXCTL_DUP_DATA;
+	if (rate_n_flags & RATE_MCS_SGI_MSK)
+		control->flags |= IEEE80211_TXCTL_SHORT_GI;
+	/* since iwl4965_hwrate_to_plcp_idx is band indifferent, we always use
+	 * IEEE80211_BAND_2GHZ band as it contains all the rates */
+	rate_index = iwl4965_hwrate_to_plcp_idx(rate_n_flags);
+	if (rate_index == -1)
+		control->tx_rate = NULL;
+	else
+		control->tx_rate =
+			&priv->bands[IEEE80211_BAND_2GHZ].bitrates[rate_index];
+}
+
 /*
 /*
  * Determine how many receiver/antenna chains to use.
  * Determine how many receiver/antenna chains to use.
  * More provides better reception via diversity.  Fewer saves power.
  * More provides better reception via diversity.  Fewer saves power.
@@ -546,9 +605,9 @@ int iwl4965_hw_nic_init(struct iwl4965_priv *priv)
 	/* set CSR_HW_CONFIG_REG for uCode use */
 	/* set CSR_HW_CONFIG_REG for uCode use */
 
 
 	iwl4965_set_bit(priv, CSR_HW_IF_CONFIG_REG,
 	iwl4965_set_bit(priv, CSR_HW_IF_CONFIG_REG,
-			CSR_HW_IF_CONFIG_REG_BIT_KEDRON_R |
-			CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
-			CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
+			CSR49_HW_IF_CONFIG_REG_BIT_4965_R |
+			CSR49_HW_IF_CONFIG_REG_BIT_RADIO_SI |
+			CSR49_HW_IF_CONFIG_REG_BIT_MAC_SI);
 
 
 	rc = iwl4965_grab_nic_access(priv);
 	rc = iwl4965_grab_nic_access(priv);
 	if (rc < 0) {
 	if (rc < 0) {
@@ -3523,6 +3582,160 @@ static void iwl4965_update_ps_mode(struct iwl4965_priv *priv, u16 ps_bit, u8 *ad
 		}
 		}
 	}
 	}
 }
 }
+#ifdef CONFIG_IWL4965_DEBUG
+
+/**
+ * iwl4965_dbg_report_frame - dump frame to syslog during debug sessions
+ *
+ * You may hack this function to show different aspects of received frames,
+ * including selective frame dumps.
+ * group100 parameter selects whether to show 1 out of 100 good frames.
+ *
+ * TODO:  This was originally written for 3945, need to audit for
+ *        proper operation with 4965.
+ */
+static void iwl4965_dbg_report_frame(struct iwl4965_priv *priv,
+		      struct iwl4965_rx_packet *pkt,
+		      struct ieee80211_hdr *header, int group100)
+{
+	u32 to_us;
+	u32 print_summary = 0;
+	u32 print_dump = 0;	/* set to 1 to dump all frames' contents */
+	u32 hundred = 0;
+	u32 dataframe = 0;
+	u16 fc;
+	u16 seq_ctl;
+	u16 channel;
+	u16 phy_flags;
+	int rate_sym;
+	u16 length;
+	u16 status;
+	u16 bcn_tmr;
+	u32 tsf_low;
+	u64 tsf;
+	u8 rssi;
+	u8 agc;
+	u16 sig_avg;
+	u16 noise_diff;
+	struct iwl4965_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
+	struct iwl4965_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
+	struct iwl4965_rx_frame_end *rx_end = IWL_RX_END(pkt);
+	u8 *data = IWL_RX_DATA(pkt);
+
+	if (likely(!(iwl4965_debug_level & IWL_DL_RX)))
+		return;
+
+	/* MAC header */
+	fc = le16_to_cpu(header->frame_control);
+	seq_ctl = le16_to_cpu(header->seq_ctrl);
+
+	/* metadata */
+	channel = le16_to_cpu(rx_hdr->channel);
+	phy_flags = le16_to_cpu(rx_hdr->phy_flags);
+	rate_sym = rx_hdr->rate;
+	length = le16_to_cpu(rx_hdr->len);
+
+	/* end-of-frame status and timestamp */
+	status = le32_to_cpu(rx_end->status);
+	bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
+	tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
+	tsf = le64_to_cpu(rx_end->timestamp);
+
+	/* signal statistics */
+	rssi = rx_stats->rssi;
+	agc = rx_stats->agc;
+	sig_avg = le16_to_cpu(rx_stats->sig_avg);
+	noise_diff = le16_to_cpu(rx_stats->noise_diff);
+
+	to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
+
+	/* if data frame is to us and all is good,
+	 *   (optionally) print summary for only 1 out of every 100 */
+	if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) ==
+	    (IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
+		dataframe = 1;
+		if (!group100)
+			print_summary = 1;	/* print each frame */
+		else if (priv->framecnt_to_us < 100) {
+			priv->framecnt_to_us++;
+			print_summary = 0;
+		} else {
+			priv->framecnt_to_us = 0;
+			print_summary = 1;
+			hundred = 1;
+		}
+	} else {
+		/* print summary for all other frames */
+		print_summary = 1;
+	}
+
+	if (print_summary) {
+		char *title;
+		int rate_idx;
+		u32 bitrate;
+
+		if (hundred)
+			title = "100Frames";
+		else if (fc & IEEE80211_FCTL_RETRY)
+			title = "Retry";
+		else if (ieee80211_is_assoc_response(fc))
+			title = "AscRsp";
+		else if (ieee80211_is_reassoc_response(fc))
+			title = "RasRsp";
+		else if (ieee80211_is_probe_response(fc)) {
+			title = "PrbRsp";
+			print_dump = 1;	/* dump frame contents */
+		} else if (ieee80211_is_beacon(fc)) {
+			title = "Beacon";
+			print_dump = 1;	/* dump frame contents */
+		} else if (ieee80211_is_atim(fc))
+			title = "ATIM";
+		else if (ieee80211_is_auth(fc))
+			title = "Auth";
+		else if (ieee80211_is_deauth(fc))
+			title = "DeAuth";
+		else if (ieee80211_is_disassoc(fc))
+			title = "DisAssoc";
+		else
+			title = "Frame";
+
+		rate_idx = iwl4965_hwrate_to_plcp_idx(rate_sym);
+		if (unlikely(rate_idx == -1))
+			bitrate = 0;
+		else
+			bitrate = iwl4965_rates[rate_idx].ieee / 2;
+
+		/* print frame summary.
+		 * MAC addresses show just the last byte (for brevity),
+		 *    but you can hack it to show more, if you'd like to. */
+		if (dataframe)
+			IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, "
+				     "len=%u, rssi=%d, chnl=%d, rate=%u, \n",
+				     title, fc, header->addr1[5],
+				     length, rssi, channel, bitrate);
+		else {
+			/* src/dst addresses assume managed mode */
+			IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
+				     "src=0x%02x, rssi=%u, tim=%lu usec, "
+				     "phy=0x%02x, chnl=%d\n",
+				     title, fc, header->addr1[5],
+				     header->addr3[5], rssi,
+				     tsf_low - priv->scan_start_tsf,
+				     phy_flags, channel);
+		}
+	}
+	if (print_dump)
+		iwl4965_print_hex_dump(IWL_DL_RX, data, length);
+}
+#else
+static inline void iwl4965_dbg_report_frame(struct iwl4965_priv *priv,
+					    struct iwl4965_rx_packet *pkt,
+					    struct ieee80211_hdr *header,
+					    int group100)
+{
+}
+#endif
+
 
 
 #define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6)
 #define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6)
 
 
@@ -3531,6 +3744,8 @@ static void iwl4965_update_ps_mode(struct iwl4965_priv *priv, u16 ps_bit, u8 *ad
 static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
 static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
 				struct iwl4965_rx_mem_buffer *rxb)
 				struct iwl4965_rx_mem_buffer *rxb)
 {
 {
+	struct ieee80211_hdr *header;
+	struct ieee80211_rx_status rx_status;
 	struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data;
 	struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data;
 	/* Use phy data (Rx signal strength, etc.) contained within
 	/* Use phy data (Rx signal strength, etc.) contained within
 	 *   this rx packet for legacy frames,
 	 *   this rx packet for legacy frames,
@@ -3541,27 +3756,29 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
 		(struct iwl4965_rx_phy_res *)&priv->last_phy_res[1];
 		(struct iwl4965_rx_phy_res *)&priv->last_phy_res[1];
 	__le32 *rx_end;
 	__le32 *rx_end;
 	unsigned int len = 0;
 	unsigned int len = 0;
-	struct ieee80211_hdr *header;
 	u16 fc;
 	u16 fc;
-	struct ieee80211_rx_status stats = {
-		.mactime = le64_to_cpu(rx_start->timestamp),
-		.freq = ieee80211chan2mhz(le16_to_cpu(rx_start->channel)),
-		.band =
-			(rx_start->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
-			IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ,
-		.antenna = 0,
-		.rate_idx = iwl4965_rate_index_from_plcp(
-				le32_to_cpu(rx_start->rate_n_flags)),
-		.flag = 0,
-	};
 	u8 network_packet;
 	u8 network_packet;
 
 
+	rx_status.mactime = le64_to_cpu(rx_start->timestamp);
+	rx_status.freq = ieee80211chan2mhz(le16_to_cpu(rx_start->channel));
+	rx_status.band = (rx_start->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ?
+				IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+	rx_status.rate_idx = iwl4965_hwrate_to_plcp_idx(
+					le32_to_cpu(rx_start->rate_n_flags));
+
+	if (rx_status.band == IEEE80211_BAND_5GHZ)
+		rx_status.rate_idx -= IWL_FIRST_OFDM_RATE;
+
+	rx_status.antenna = 0;
+	rx_status.flag = 0;
+
 	if ((unlikely(rx_start->cfg_phy_cnt > 20))) {
 	if ((unlikely(rx_start->cfg_phy_cnt > 20))) {
 		IWL_DEBUG_DROP
 		IWL_DEBUG_DROP
 			("dsp size out of range [0,20]: "
 			("dsp size out of range [0,20]: "
 			 "%d/n", rx_start->cfg_phy_cnt);
 			 "%d/n", rx_start->cfg_phy_cnt);
 		return;
 		return;
 	}
 	}
+
 	if (!include_phy) {
 	if (!include_phy) {
 		if (priv->last_phy_res[0])
 		if (priv->last_phy_res[0])
 			rx_start = (struct iwl4965_rx_phy_res *)
 			rx_start = (struct iwl4965_rx_phy_res *)
@@ -3580,7 +3797,7 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
 						  + rx_start->cfg_phy_cnt);
 						  + rx_start->cfg_phy_cnt);
 
 
 		len = le16_to_cpu(rx_start->byte_count);
 		len = le16_to_cpu(rx_start->byte_count);
-		rx_end = (__le32 *) (pkt->u.raw + rx_start->cfg_phy_cnt +
+		rx_end = (__le32 *)(pkt->u.raw + rx_start->cfg_phy_cnt +
 				  sizeof(struct iwl4965_rx_phy_res) + len);
 				  sizeof(struct iwl4965_rx_phy_res) + len);
 	} else {
 	} else {
 		struct iwl4965_rx_mpdu_res_start *amsdu =
 		struct iwl4965_rx_mpdu_res_start *amsdu =
@@ -3603,7 +3820,7 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
 	priv->ucode_beacon_time = le32_to_cpu(rx_start->beacon_time_stamp);
 	priv->ucode_beacon_time = le32_to_cpu(rx_start->beacon_time_stamp);
 
 
 	/* Find max signal strength (dBm) among 3 antenna/receiver chains */
 	/* Find max signal strength (dBm) among 3 antenna/receiver chains */
-	stats.ssi = iwl4965_calc_rssi(rx_start);
+	rx_status.ssi = iwl4965_calc_rssi(rx_start);
 
 
 	/* Meaningful noise values are available only from beacon statistics,
 	/* Meaningful noise values are available only from beacon statistics,
 	 *   which are gathered only when associated, and indicate noise
 	 *   which are gathered only when associated, and indicate noise
@@ -3611,32 +3828,29 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
 	 * Ignore these noise values while scanning (other channels) */
 	 * Ignore these noise values while scanning (other channels) */
 	if (iwl4965_is_associated(priv) &&
 	if (iwl4965_is_associated(priv) &&
 	    !test_bit(STATUS_SCANNING, &priv->status)) {
 	    !test_bit(STATUS_SCANNING, &priv->status)) {
-		stats.noise = priv->last_rx_noise;
-		stats.signal = iwl4965_calc_sig_qual(stats.ssi, stats.noise);
+		rx_status.noise = priv->last_rx_noise;
+		rx_status.signal = iwl4965_calc_sig_qual(rx_status.ssi,
+							 rx_status.noise);
 	} else {
 	} else {
-		stats.noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
-		stats.signal = iwl4965_calc_sig_qual(stats.ssi, 0);
+		rx_status.noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
+		rx_status.signal = iwl4965_calc_sig_qual(rx_status.ssi, 0);
 	}
 	}
 
 
 	/* Reset beacon noise level if not associated. */
 	/* Reset beacon noise level if not associated. */
 	if (!iwl4965_is_associated(priv))
 	if (!iwl4965_is_associated(priv))
 		priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
 		priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
 
 
-#ifdef CONFIG_IWL4965_DEBUG
-	/* TODO:  Parts of iwl4965_report_frame are broken for 4965 */
-	if (iwl4965_debug_level & (IWL_DL_RX))
-		/* Set "1" to report good data frames in groups of 100 */
-		iwl4965_report_frame(priv, pkt, header, 1);
-
-	if (iwl4965_debug_level & (IWL_DL_RX | IWL_DL_STATS))
-	IWL_DEBUG_RX("Rssi %d, noise %d, qual %d, TSF %lu\n",
-		stats.ssi, stats.noise, stats.signal,
-		 (long unsigned int)le64_to_cpu(rx_start->timestamp));
-#endif
+	/* Set "1" to report good data frames in groups of 100 */
+	/* FIXME: need to optimze the call: */
+	iwl4965_dbg_report_frame(priv, pkt, header, 1);
+
+	IWL_DEBUG_STATS_LIMIT("Rssi %d, noise %d, qual %d, TSF %llu\n",
+			      rx_status.ssi, rx_status.noise, rx_status.signal,
+			      rx_status.mactime);
 
 
 	network_packet = iwl4965_is_network_packet(priv, header);
 	network_packet = iwl4965_is_network_packet(priv, header);
 	if (network_packet) {
 	if (network_packet) {
-		priv->last_rx_rssi = stats.ssi;
+		priv->last_rx_rssi = rx_status.ssi;
 		priv->last_beacon_time =  priv->ucode_beacon_time;
 		priv->last_beacon_time =  priv->ucode_beacon_time;
 		priv->last_tsf = le64_to_cpu(rx_start->timestamp);
 		priv->last_tsf = le64_to_cpu(rx_start->timestamp);
 	}
 	}
@@ -3739,7 +3953,7 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
 				return;
 				return;
 			}
 			}
 		}
 		}
-		iwl4965_handle_data_packet(priv, 0, include_phy, rxb, &stats);
+		iwl4965_handle_data_packet(priv, 0, include_phy, rxb, &rx_status);
 		break;
 		break;
 
 
 	case IEEE80211_FTYPE_CTL:
 	case IEEE80211_FTYPE_CTL:
@@ -3748,7 +3962,7 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
 		case IEEE80211_STYPE_BACK_REQ:
 		case IEEE80211_STYPE_BACK_REQ:
 			IWL_DEBUG_HT("IEEE80211_STYPE_BACK_REQ arrived\n");
 			IWL_DEBUG_HT("IEEE80211_STYPE_BACK_REQ arrived\n");
 			iwl4965_handle_data_packet(priv, 0, include_phy,
 			iwl4965_handle_data_packet(priv, 0, include_phy,
-						rxb, &stats);
+						rxb, &rx_status);
 			break;
 			break;
 		default:
 		default:
 			break;
 			break;
@@ -3778,7 +3992,7 @@ static void iwl4965_rx_reply_rx(struct iwl4965_priv *priv,
 				       print_mac(mac3, header->addr3));
 				       print_mac(mac3, header->addr3));
 		else
 		else
 			iwl4965_handle_data_packet(priv, 1, include_phy, rxb,
 			iwl4965_handle_data_packet(priv, 1, include_phy, rxb,
-						   &stats);
+						   &rx_status);
 		break;
 		break;
 	}
 	}
 	default:
 	default:
@@ -3900,11 +4114,10 @@ static int iwl4965_tx_status_reply_compressed_ba(struct iwl4965_priv *priv,
 	tx_status->flags |= IEEE80211_TX_STATUS_AMPDU;
 	tx_status->flags |= IEEE80211_TX_STATUS_AMPDU;
 	tx_status->ampdu_ack_map = successes;
 	tx_status->ampdu_ack_map = successes;
 	tx_status->ampdu_ack_len = agg->frame_count;
 	tx_status->ampdu_ack_len = agg->frame_count;
-	/* FIXME Wrong rate
-	tx_status->control.tx_rate = agg->rate_n_flags;
-	*/
+	iwl4965_hwrate_to_tx_control(priv, agg->rate_n_flags,
+				     &tx_status->control);
 
 
-	IWL_DEBUG_TX_REPLY("Bitmap %llx\n", bitmap);
+	IWL_DEBUG_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -3925,16 +4138,23 @@ static void iwl4965_tx_queue_stop_scheduler(struct iwl4965_priv *priv,
 
 
 /**
 /**
  * txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID
  * txq_id must be greater than IWL_BACK_QUEUE_FIRST_ID
+ * priv->lock must be held by the caller
  */
  */
 static int iwl4965_tx_queue_agg_disable(struct iwl4965_priv *priv, u16 txq_id,
 static int iwl4965_tx_queue_agg_disable(struct iwl4965_priv *priv, u16 txq_id,
 					u16 ssn_idx, u8 tx_fifo)
 					u16 ssn_idx, u8 tx_fifo)
 {
 {
+	int ret = 0;
+
 	if (IWL_BACK_QUEUE_FIRST_ID > txq_id) {
 	if (IWL_BACK_QUEUE_FIRST_ID > txq_id) {
 		IWL_WARNING("queue number too small: %d, must be > %d\n",
 		IWL_WARNING("queue number too small: %d, must be > %d\n",
 				txq_id, IWL_BACK_QUEUE_FIRST_ID);
 				txq_id, IWL_BACK_QUEUE_FIRST_ID);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	ret = iwl4965_grab_nic_access(priv);
+	if (ret)
+		return ret;
+
 	iwl4965_tx_queue_stop_scheduler(priv, txq_id);
 	iwl4965_tx_queue_stop_scheduler(priv, txq_id);
 
 
 	iwl4965_clear_bits_prph(priv, KDR_SCD_QUEUECHAIN_SEL, (1 << txq_id));
 	iwl4965_clear_bits_prph(priv, KDR_SCD_QUEUECHAIN_SEL, (1 << txq_id));
@@ -3948,6 +4168,8 @@ static int iwl4965_tx_queue_agg_disable(struct iwl4965_priv *priv, u16 txq_id,
 	iwl4965_txq_ctx_deactivate(priv, txq_id);
 	iwl4965_txq_ctx_deactivate(priv, txq_id);
 	iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
 	iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
 
 
+	iwl4965_release_nic_access(priv);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -4040,12 +4262,12 @@ static void iwl4965_rx_reply_compressed_ba(struct iwl4965_priv *priv,
 			   "%d, scd_ssn = %d\n",
 			   "%d, scd_ssn = %d\n",
 			   ba_resp->tid,
 			   ba_resp->tid,
 			   ba_resp->seq_ctl,
 			   ba_resp->seq_ctl,
-			   ba_resp->bitmap,
+			   (unsigned long long)ba_resp->bitmap,
 			   ba_resp->scd_flow,
 			   ba_resp->scd_flow,
 			   ba_resp->scd_ssn);
 			   ba_resp->scd_ssn);
 	IWL_DEBUG_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx \n",
 	IWL_DEBUG_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx \n",
 			   agg->start_idx,
 			   agg->start_idx,
-			   agg->bitmap);
+			   (unsigned long long)agg->bitmap);
 
 
 	/* Update driver's record of ACK vs. not for each frame in window */
 	/* Update driver's record of ACK vs. not for each frame in window */
 	iwl4965_tx_status_reply_compressed_ba(priv, agg, ba_resp);
 	iwl4965_tx_status_reply_compressed_ba(priv, agg, ba_resp);
@@ -4232,7 +4454,7 @@ static u8 iwl4965_is_channel_extension(struct iwl4965_priv *priv,
 	if (!is_channel_valid(ch_info))
 	if (!is_channel_valid(ch_info))
 		return 0;
 		return 0;
 
 
-	if (extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_AUTO)
+	if (extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_NONE)
 		return 0;
 		return 0;
 
 
 	if ((ch_info->fat_extension_channel == extension_chan_offset) ||
 	if ((ch_info->fat_extension_channel == extension_chan_offset) ||
@@ -4249,7 +4471,7 @@ static u8 iwl4965_is_fat_tx_allowed(struct iwl4965_priv *priv,
 
 
 	if ((!iwl_ht_conf->is_ht) ||
 	if ((!iwl_ht_conf->is_ht) ||
 	   (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) ||
 	   (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) ||
-	   (iwl_ht_conf->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_AUTO))
+	   (iwl_ht_conf->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_NONE))
 		return 0;
 		return 0;
 
 
 	if (sta_ht_inf) {
 	if (sta_ht_inf) {
@@ -4294,9 +4516,7 @@ void iwl4965_set_rxon_ht(struct iwl4965_priv *priv, struct iwl_ht_info *ht_info)
 	case IWL_EXT_CHANNEL_OFFSET_BELOW:
 	case IWL_EXT_CHANNEL_OFFSET_BELOW:
 		rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
 		rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
 		break;
 		break;
-	case IWL_EXT_CHANNEL_OFFSET_AUTO:
-		rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
-		break;
+	case IWL_EXT_CHANNEL_OFFSET_NONE:
 	default:
 	default:
 		rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
 		rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
 		break;
 		break;
@@ -4419,7 +4639,7 @@ static int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, const u8 *da,
 	int tx_fifo;
 	int tx_fifo;
 	int txq_id;
 	int txq_id;
 	int ssn = -1;
 	int ssn = -1;
-	int rc = 0;
+	int ret = 0;
 	unsigned long flags;
 	unsigned long flags;
 	struct iwl4965_tid_data *tid_data;
 	struct iwl4965_tid_data *tid_data;
 	DECLARE_MAC_BUF(mac);
 	DECLARE_MAC_BUF(mac);
@@ -4452,12 +4672,12 @@ static int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, const u8 *da,
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 
 
 	*start_seq_num = ssn;
 	*start_seq_num = ssn;
-	rc = iwl4965_tx_queue_agg_enable(priv, txq_id, tx_fifo,
-					   sta_id, tid, ssn);
-	if (rc)
-		return rc;
+	ret = iwl4965_tx_queue_agg_enable(priv, txq_id, tx_fifo,
+					  sta_id, tid, ssn);
+	if (ret)
+		return ret;
 
 
-	rc = 0;
+	ret = 0;
 	if (tid_data->tfds_in_queue == 0) {
 	if (tid_data->tfds_in_queue == 0) {
 		printk(KERN_ERR "HW queue is empty\n");
 		printk(KERN_ERR "HW queue is empty\n");
 		tid_data->agg.state = IWL_AGG_ON;
 		tid_data->agg.state = IWL_AGG_ON;
@@ -4467,7 +4687,7 @@ static int iwl4965_mac_ht_tx_agg_start(struct ieee80211_hw *hw, const u8 *da,
 				tid_data->tfds_in_queue);
 				tid_data->tfds_in_queue);
 		tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
 		tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
 	}
 	}
-	return rc;
+	return ret;
 }
 }
 
 
 static int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, const u8 *da,
 static int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, const u8 *da,
@@ -4477,7 +4697,7 @@ static int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, const u8 *da,
 	struct iwl4965_priv *priv = hw->priv;
 	struct iwl4965_priv *priv = hw->priv;
 	int tx_fifo_id, txq_id, sta_id, ssn = -1;
 	int tx_fifo_id, txq_id, sta_id, ssn = -1;
 	struct iwl4965_tid_data *tid_data;
 	struct iwl4965_tid_data *tid_data;
-	int rc, write_ptr, read_ptr;
+	int ret, write_ptr, read_ptr;
 	unsigned long flags;
 	unsigned long flags;
 	DECLARE_MAC_BUF(mac);
 	DECLARE_MAC_BUF(mac);
 
 
@@ -4517,17 +4737,11 @@ static int iwl4965_mac_ht_tx_agg_stop(struct ieee80211_hw *hw, const u8 *da,
 	priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;
 	priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;
 
 
 	spin_lock_irqsave(&priv->lock, flags);
 	spin_lock_irqsave(&priv->lock, flags);
-	rc = iwl4965_grab_nic_access(priv);
-	if (rc) {
-		spin_unlock_irqrestore(&priv->lock, flags);
-		return rc;
-	}
-	rc = iwl4965_tx_queue_agg_disable(priv, txq_id, ssn, tx_fifo_id);
-	iwl4965_release_nic_access(priv);
+	ret = iwl4965_tx_queue_agg_disable(priv, txq_id, ssn, tx_fifo_id);
 	spin_unlock_irqrestore(&priv->lock, flags);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 
-	if (rc)
-		return rc;
+	if (ret)
+		return ret;
 
 
 	ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, da, tid);
 	ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, da, tid);
 
 
@@ -4610,9 +4824,15 @@ void iwl4965_hw_cancel_deferred_work(struct iwl4965_priv *priv)
 	cancel_delayed_work(&priv->init_alive_start);
 	cancel_delayed_work(&priv->init_alive_start);
 }
 }
 
 
+static struct iwl_cfg iwl4965_agn_cfg = {
+	.name = "4965AGN",
+	.fw_name = "iwlwifi-4965" IWL4965_UCODE_API ".ucode",
+	.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
+};
+
 struct pci_device_id iwl4965_hw_card_ids[] = {
 struct pci_device_id iwl4965_hw_card_ids[] = {
-	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4229)},
-	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4230)},
+	{IWL_PCI_DEVICE(0x4229, PCI_ANY_ID, iwl4965_agn_cfg)},
+	{IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_agn_cfg)},
 	{0}
 	{0}
 };
 };
 
 

+ 16 - 17
drivers/net/wireless/iwlwifi/iwl-4965.h

@@ -41,9 +41,17 @@ extern struct pci_device_id iwl4965_hw_card_ids[];
 
 
 #define DRV_NAME        "iwl4965"
 #define DRV_NAME        "iwl4965"
 #include "iwl-4965-hw.h"
 #include "iwl-4965-hw.h"
+#include "iwl-csr.h"
 #include "iwl-prph.h"
 #include "iwl-prph.h"
 #include "iwl-4965-debug.h"
 #include "iwl-4965-debug.h"
 
 
+/* Change firmware file name, using "-" and incrementing number,
+ *   *only* when uCode interface or architecture changes so that it
+ *   is not compatible with earlier drivers.
+ * This number will also appear in << 8 position of 1st dword of uCode file */
+#define IWL4965_UCODE_API "-1"
+
+
 /* Default noise level to report when noise measurement is not available.
 /* Default noise level to report when noise measurement is not available.
  *   This may be because we're:
  *   This may be because we're:
  *   1)  Not associated (4965, no beacon statistics being sent to driver)
  *   1)  Not associated (4965, no beacon statistics being sent to driver)
@@ -633,16 +641,6 @@ extern int iwl4965_is_network_packet(struct iwl4965_priv *priv,
 				 struct ieee80211_hdr *header);
 				 struct ieee80211_hdr *header);
 extern int iwl4965_power_init_handle(struct iwl4965_priv *priv);
 extern int iwl4965_power_init_handle(struct iwl4965_priv *priv);
 extern int iwl4965_eeprom_init(struct iwl4965_priv *priv);
 extern int iwl4965_eeprom_init(struct iwl4965_priv *priv);
-#ifdef CONFIG_IWL4965_DEBUG
-extern void iwl4965_report_frame(struct iwl4965_priv *priv,
-			     struct iwl4965_rx_packet *pkt,
-			     struct ieee80211_hdr *header, int group100);
-#else
-static inline void iwl4965_report_frame(struct iwl4965_priv *priv,
-				    struct iwl4965_rx_packet *pkt,
-				    struct ieee80211_hdr *header,
-				    int group100) {}
-#endif
 extern void iwl4965_handle_data_packet_monitor(struct iwl4965_priv *priv,
 extern void iwl4965_handle_data_packet_monitor(struct iwl4965_priv *priv,
 					   struct iwl4965_rx_mem_buffer *rxb,
 					   struct iwl4965_rx_mem_buffer *rxb,
 					   void *data, short len,
 					   void *data, short len,
@@ -767,6 +765,9 @@ extern int iwl4965_set_fat_chan_info(struct iwl4965_priv *priv,
 				const struct iwl4965_eeprom_channel *eeprom_ch,
 				const struct iwl4965_eeprom_channel *eeprom_ch,
 				u8 fat_extension_channel);
 				u8 fat_extension_channel);
 extern void iwl4965_rf_kill_ct_config(struct iwl4965_priv *priv);
 extern void iwl4965_rf_kill_ct_config(struct iwl4965_priv *priv);
+extern void iwl4965_hwrate_to_tx_control(struct iwl4965_priv *priv,
+					 u32 rate_n_flags,
+					 struct ieee80211_tx_control *control);
 
 
 #ifdef CONFIG_IWL4965_HT
 #ifdef CONFIG_IWL4965_HT
 void iwl4965_init_ht_hw_capab(struct ieee80211_ht_info *ht_info,
 void iwl4965_init_ht_hw_capab(struct ieee80211_ht_info *ht_info,
@@ -808,11 +809,10 @@ struct iwl4965_kw {
 #define IWL_OPERATION_MODE_MIXED    2
 #define IWL_OPERATION_MODE_MIXED    2
 #define IWL_OPERATION_MODE_20MHZ    3
 #define IWL_OPERATION_MODE_20MHZ    3
 
 
-#define IWL_EXT_CHANNEL_OFFSET_AUTO   0
-#define IWL_EXT_CHANNEL_OFFSET_ABOVE  1
-#define IWL_EXT_CHANNEL_OFFSET_       2
-#define IWL_EXT_CHANNEL_OFFSET_BELOW  3
-#define IWL_EXT_CHANNEL_OFFSET_MAX    4
+#define IWL_EXT_CHANNEL_OFFSET_NONE      0
+#define IWL_EXT_CHANNEL_OFFSET_ABOVE     1
+#define IWL_EXT_CHANNEL_OFFSET_RESERVE1  2
+#define IWL_EXT_CHANNEL_OFFSET_BELOW     3
 
 
 #define NRG_NUM_PREV_STAT_L     20
 #define NRG_NUM_PREV_STAT_L     20
 #define NUM_RX_CHAINS           (3)
 #define NUM_RX_CHAINS           (3)
@@ -974,6 +974,7 @@ struct iwl4965_priv {
 	struct ieee80211_hw *hw;
 	struct ieee80211_hw *hw;
 	struct ieee80211_channel *ieee_channels;
 	struct ieee80211_channel *ieee_channels;
 	struct ieee80211_rate *ieee_rates;
 	struct ieee80211_rate *ieee_rates;
+	struct iwl_cfg *cfg;
 
 
 	/* temporary frame storage list */
 	/* temporary frame storage list */
 	struct list_head free_frames;
 	struct list_head free_frames;
@@ -1104,7 +1105,6 @@ struct iwl4965_priv {
 	u32 scd_base_addr;	/* scheduler sram base address */
 	u32 scd_base_addr;	/* scheduler sram base address */
 
 
 	unsigned long status;
 	unsigned long status;
-	u32 config;
 
 
 	int last_rx_rssi;	/* From Rx packet statisitics */
 	int last_rx_rssi;	/* From Rx packet statisitics */
 	int last_rx_noise;	/* From beacon statistics */
 	int last_rx_noise;	/* From beacon statistics */
@@ -1134,7 +1134,6 @@ struct iwl4965_priv {
 	int is_open;
 	int is_open;
 
 
 	u8 mac80211_registered;
 	u8 mac80211_registered;
-	int is_abg;
 
 
 	u32 notif_missed_beacons;
 	u32 notif_missed_beacons;
 
 

+ 47 - 0
drivers/net/wireless/iwlwifi/iwl-core.c

@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include "iwl-4965-debug.h"
+#include "iwl-core.h"
+
+MODULE_DESCRIPTION("iwl core");
+MODULE_VERSION(IWLWIFI_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL/BSD");
+
+#ifdef CONFIG_IWL4965_DEBUG
+u32 iwl4965_debug_level;
+EXPORT_SYMBOL(iwl4965_debug_level);
+#endif

+ 84 - 0
drivers/net/wireless/iwlwifi/iwl-core.h

@@ -0,0 +1,84 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __iwl_core_h__
+#define __iwl_core_h__
+
+#define IWLWIFI_VERSION "1.2.26k"
+#define DRV_COPYRIGHT	"Copyright(c) 2003-2008 Intel Corporation"
+
+#define IWL_PCI_DEVICE(dev, subdev, cfg) \
+	.vendor = PCI_VENDOR_ID_INTEL,  .device = (dev), \
+	.subvendor = PCI_ANY_ID, .subdevice = (subdev), \
+	.driver_data = (kernel_ulong_t)&(cfg)
+
+#define IWL_SKU_G       0x1
+#define IWL_SKU_A       0x2
+#define IWL_SKU_N       0x8
+
+struct iwl_cfg {
+	const char *name;
+	const char *fw_name;
+	unsigned int sku;
+};
+
+#endif /* __iwl_core_h__ */

+ 259 - 0
drivers/net/wireless/iwlwifi/iwl-csr.h

@@ -0,0 +1,259 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2007 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+/*=== CSR (control and status registers) ===*/
+#define CSR_BASE    (0x000)
+
+#define CSR_HW_IF_CONFIG_REG    (CSR_BASE+0x000) /* hardware interface config */
+#define CSR_INT_COALESCING      (CSR_BASE+0x004) /* accum ints, 32-usec units */
+#define CSR_INT                 (CSR_BASE+0x008) /* host interrupt status/ack */
+#define CSR_INT_MASK            (CSR_BASE+0x00c) /* host interrupt enable */
+#define CSR_FH_INT_STATUS       (CSR_BASE+0x010) /* busmaster int status/ack*/
+#define CSR_GPIO_IN             (CSR_BASE+0x018) /* read external chip pins */
+#define CSR_RESET               (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/
+#define CSR_GP_CNTRL            (CSR_BASE+0x024)
+
+/*
+ * Hardware revision info
+ * Bit fields:
+ * 31-8:  Reserved
+ *  7-4:  Type of device:  0x0 = 4965, 0xd = 3945
+ *  3-2:  Revision step:  0 = A, 1 = B, 2 = C, 3 = D
+ *  1-0:  "Dash" value, as in A-1, etc.
+ *
+ * NOTE:  Revision step affects calculation of CCK txpower for 4965.
+ */
+#define CSR_HW_REV              (CSR_BASE+0x028)
+
+/* EEPROM reads */
+#define CSR_EEPROM_REG          (CSR_BASE+0x02c)
+#define CSR_EEPROM_GP           (CSR_BASE+0x030)
+#define CSR_GP_UCODE		(CSR_BASE+0x044)
+#define CSR_UCODE_DRV_GP1       (CSR_BASE+0x054)
+#define CSR_UCODE_DRV_GP1_SET   (CSR_BASE+0x058)
+#define CSR_UCODE_DRV_GP1_CLR   (CSR_BASE+0x05c)
+#define CSR_UCODE_DRV_GP2       (CSR_BASE+0x060)
+#define CSR_GIO_CHICKEN_BITS    (CSR_BASE+0x100)
+
+/* Analog phase-lock-loop configuration (3945 only)
+ * Set bit 24. */
+#define CSR_ANA_PLL_CFG         (CSR_BASE+0x20c)
+/*
+ * Indicates hardware rev, to determine CCK backoff for txpower calculation.
+ * Bit fields:
+ *  3-2:  0 = A, 1 = B, 2 = C, 3 = D step
+ */
+#define CSR_HW_REV_WA_REG	(CSR_BASE+0x22C)
+
+/* Bits for CSR_HW_IF_CONFIG_REG */
+#define CSR49_HW_IF_CONFIG_REG_BIT_4965_R	(0x00000010)
+#define CSR49_HW_IF_CONFIG_REG_MSK_BOARD_VER	(0x00000C00)
+#define CSR49_HW_IF_CONFIG_REG_BIT_MAC_SI		(0x00000100)
+#define CSR49_HW_IF_CONFIG_REG_BIT_RADIO_SI	(0x00000200)
+
+#define CSR39_HW_IF_CONFIG_REG_BIT_3945_MB         (0x00000100)
+#define CSR39_HW_IF_CONFIG_REG_BIT_3945_MM         (0x00000200)
+#define CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC            (0x00000400)
+#define CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE         (0x00000800)
+#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A    (0x00000000)
+#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B    (0x00001000)
+
+#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM     (0x00200000)
+
+/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
+ * acknowledged (reset) by host writing "1" to flagged bits. */
+#define CSR_INT_BIT_FH_RX        (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */
+#define CSR_INT_BIT_HW_ERR       (1 << 29) /* DMA hardware error FH_INT[31] */
+#define CSR_INT_BIT_DNLD         (1 << 28) /* uCode Download */
+#define CSR_INT_BIT_FH_TX        (1 << 27) /* Tx DMA FH_INT[1:0] */
+#define CSR_INT_BIT_SCD          (1 << 26) /* TXQ pointer advanced */
+#define CSR_INT_BIT_SW_ERR       (1 << 25) /* uCode error */
+#define CSR_INT_BIT_RF_KILL      (1 << 7)  /* HW RFKILL switch GP_CNTRL[27] toggled */
+#define CSR_INT_BIT_CT_KILL      (1 << 6)  /* Critical temp (chip too hot) rfkill */
+#define CSR_INT_BIT_SW_RX        (1 << 3)  /* Rx, command responses, 3945 */
+#define CSR_INT_BIT_WAKEUP       (1 << 1)  /* NIC controller waking up (pwr mgmt) */
+#define CSR_INT_BIT_ALIVE        (1 << 0)  /* uCode interrupts once it initializes */
+
+#define CSR_INI_SET_MASK	(CSR_INT_BIT_FH_RX   | \
+				 CSR_INT_BIT_HW_ERR  | \
+				 CSR_INT_BIT_FH_TX   | \
+				 CSR_INT_BIT_SW_ERR  | \
+				 CSR_INT_BIT_RF_KILL | \
+				 CSR_INT_BIT_SW_RX   | \
+				 CSR_INT_BIT_WAKEUP  | \
+				 CSR_INT_BIT_ALIVE)
+
+/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
+#define CSR_FH_INT_BIT_ERR       (1 << 31) /* Error */
+#define CSR_FH_INT_BIT_HI_PRIOR  (1 << 30) /* High priority Rx, bypass coalescing */
+#define CSR39_FH_INT_BIT_RX_CHNL2  (1 << 18) /* Rx channel 2 (3945 only) */
+#define CSR_FH_INT_BIT_RX_CHNL1  (1 << 17) /* Rx channel 1 */
+#define CSR_FH_INT_BIT_RX_CHNL0  (1 << 16) /* Rx channel 0 */
+#define CSR39_FH_INT_BIT_TX_CHNL6  (1 << 6)  /* Tx channel 6 (3945 only) */
+#define CSR_FH_INT_BIT_TX_CHNL1  (1 << 1)  /* Tx channel 1 */
+#define CSR_FH_INT_BIT_TX_CHNL0  (1 << 0)  /* Tx channel 0 */
+
+#define CSR39_FH_INT_RX_MASK	(CSR_FH_INT_BIT_HI_PRIOR | \
+				 CSR39_FH_INT_BIT_RX_CHNL2 | \
+				 CSR_FH_INT_BIT_RX_CHNL1 | \
+				 CSR_FH_INT_BIT_RX_CHNL0)
+
+
+#define CSR39_FH_INT_TX_MASK	(CSR39_FH_INT_BIT_TX_CHNL6 | \
+				 CSR_FH_INT_BIT_TX_CHNL1 | \
+				 CSR_FH_INT_BIT_TX_CHNL0)
+
+#define CSR49_FH_INT_RX_MASK	(CSR_FH_INT_BIT_HI_PRIOR | \
+				 CSR_FH_INT_BIT_RX_CHNL1 | \
+				 CSR_FH_INT_BIT_RX_CHNL0)
+
+#define CSR49_FH_INT_TX_MASK	(CSR_FH_INT_BIT_TX_CHNL1 | \
+				 CSR_FH_INT_BIT_TX_CHNL0)
+
+
+/* RESET */
+#define CSR_RESET_REG_FLAG_NEVO_RESET                (0x00000001)
+#define CSR_RESET_REG_FLAG_FORCE_NMI                 (0x00000002)
+#define CSR_RESET_REG_FLAG_SW_RESET                  (0x00000080)
+#define CSR_RESET_REG_FLAG_MASTER_DISABLED           (0x00000100)
+#define CSR_RESET_REG_FLAG_STOP_MASTER               (0x00000200)
+
+/* GP (general purpose) CONTROL */
+#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY        (0x00000001)
+#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE              (0x00000004)
+#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ         (0x00000008)
+#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP         (0x00000010)
+
+#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN           (0x00000001)
+
+#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE         (0x07000000)
+#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE         (0x04000000)
+#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW          (0x08000000)
+
+
+/* EEPROM REG */
+#define CSR_EEPROM_REG_READ_VALID_MSK	(0x00000001)
+#define CSR_EEPROM_REG_BIT_CMD		(0x00000002)
+
+/* EEPROM GP */
+#define CSR_EEPROM_GP_VALID_MSK		(0x00000006)
+#define CSR_EEPROM_GP_BAD_SIGNATURE	(0x00000000)
+#define CSR_EEPROM_GP_IF_OWNER_MSK	(0x00000180)
+
+/* UCODE DRV GP */
+#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP             (0x00000001)
+#define CSR_UCODE_SW_BIT_RFKILL                     (0x00000002)
+#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED           (0x00000004)
+#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT      (0x00000008)
+
+/* GPIO */
+#define CSR_GPIO_IN_BIT_AUX_POWER                   (0x00000200)
+#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC                (0x00000000)
+#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC		CSR_GPIO_IN_BIT_AUX_POWER
+
+/* GI Chicken Bits */
+#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX  (0x00800000)
+#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER  (0x20000000)
+
+/*=== HBUS (Host-side Bus) ===*/
+#define HBUS_BASE	(0x400)
+/*
+ * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM
+ * structures, error log, event log, verifying uCode load).
+ * First write to address register, then read from or write to data register
+ * to complete the job.  Once the address register is set up, accesses to
+ * data registers auto-increment the address by one dword.
+ * Bit usage for address registers (read or write):
+ *  0-31:  memory address within device
+ */
+#define HBUS_TARG_MEM_RADDR     (HBUS_BASE+0x00c)
+#define HBUS_TARG_MEM_WADDR     (HBUS_BASE+0x010)
+#define HBUS_TARG_MEM_WDAT      (HBUS_BASE+0x018)
+#define HBUS_TARG_MEM_RDAT      (HBUS_BASE+0x01c)
+
+/*
+ * Registers for accessing device's internal peripheral registers
+ * (e.g. SCD, BSM, etc.).  First write to address register,
+ * then read from or write to data register to complete the job.
+ * Bit usage for address registers (read or write):
+ *  0-15:  register address (offset) within device
+ * 24-25:  (# bytes - 1) to read or write (e.g. 3 for dword)
+ */
+#define HBUS_TARG_PRPH_WADDR    (HBUS_BASE+0x044)
+#define HBUS_TARG_PRPH_RADDR    (HBUS_BASE+0x048)
+#define HBUS_TARG_PRPH_WDAT     (HBUS_BASE+0x04c)
+#define HBUS_TARG_PRPH_RDAT     (HBUS_BASE+0x050)
+
+/*
+ * Per-Tx-queue write pointer (index, really!) (3945 and 4965).
+ * Indicates index to next TFD that driver will fill (1 past latest filled).
+ * Bit usage:
+ *  0-7:  queue write index
+ * 11-8:  queue selector
+ */
+#define HBUS_TARG_WRPTR         (HBUS_BASE+0x060)
+#define HBUS_TARG_MBX_C         (HBUS_BASE+0x030)
+
+#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED         (0x00000004)
+
+
+

+ 205 - 0
drivers/net/wireless/iwlwifi/iwl-eeprom.c

@@ -0,0 +1,205 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+
+#include <net/mac80211.h>
+
+#include "iwl-4965-commands.h"
+#include "iwl-4965.h"
+#include "iwl-core.h"
+#include "iwl-4965-debug.h"
+#include "iwl-eeprom.h"
+#include "iwl-4965-io.h"
+
+/******************************************************************************
+ *
+ * EEPROM related functions
+ *
+******************************************************************************/
+
+int iwlcore_eeprom_verify_signature(struct iwl4965_priv *priv)
+{
+	u32 gp = iwl4965_read32(priv, CSR_EEPROM_GP);
+	if ((gp & CSR_EEPROM_GP_VALID_MSK) == CSR_EEPROM_GP_BAD_SIGNATURE) {
+		IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x", gp);
+		return -ENOENT;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(iwlcore_eeprom_verify_signature);
+
+/*
+ * The device's EEPROM semaphore prevents conflicts between driver and uCode
+ * when accessing the EEPROM; each access is a series of pulses to/from the
+ * EEPROM chip, not a single event, so even reads could conflict if they
+ * weren't arbitrated by the semaphore.
+ */
+int iwlcore_eeprom_acquire_semaphore(struct iwl4965_priv *priv)
+{
+	u16 count;
+	int ret;
+
+	for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) {
+		/* Request semaphore */
+		iwl4965_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+			CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
+
+		/* See if we got it */
+		ret = iwl4965_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
+					CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
+					CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
+					EEPROM_SEM_TIMEOUT);
+		if (ret >= 0) {
+			IWL_DEBUG_IO("Acquired semaphore after %d tries.\n",
+				count+1);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(iwlcore_eeprom_acquire_semaphore);
+
+void iwlcore_eeprom_release_semaphore(struct iwl4965_priv *priv)
+{
+	iwl4965_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
+		CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
+
+}
+EXPORT_SYMBOL(iwlcore_eeprom_release_semaphore);
+
+
+/**
+ * iwl_eeprom_init - read EEPROM contents
+ *
+ * Load the EEPROM contents from adapter into priv->eeprom
+ *
+ * NOTE:  This routine uses the non-debug IO access functions.
+ */
+int iwl_eeprom_init(struct iwl4965_priv *priv)
+{
+	u16 *e = (u16 *)&priv->eeprom;
+	u32 gp = iwl4965_read32(priv, CSR_EEPROM_GP);
+	u32 r;
+	int sz = sizeof(priv->eeprom);
+	int ret;
+	int i;
+	u16 addr;
+
+	/* The EEPROM structure has several padding buffers within it
+	 * and when adding new EEPROM maps is subject to programmer errors
+	 * which may be very difficult to identify without explicitly
+	 * checking the resulting size of the eeprom map. */
+	BUILD_BUG_ON(sizeof(priv->eeprom) != IWL_EEPROM_IMAGE_SIZE);
+
+	if ((gp & CSR_EEPROM_GP_VALID_MSK) == CSR_EEPROM_GP_BAD_SIGNATURE) {
+		IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x", gp);
+		return -ENOENT;
+	}
+
+	/* Make sure driver (instead of uCode) is allowed to read EEPROM */
+	ret = priv->cfg->ops->lib->eeprom_ops.acquire_semaphore(priv);
+	if (ret < 0) {
+		IWL_ERROR("Failed to acquire EEPROM semaphore.\n");
+		return -ENOENT;
+	}
+
+	/* eeprom is an array of 16bit values */
+	for (addr = 0; addr < sz; addr += sizeof(u16)) {
+		_iwl4965_write32(priv, CSR_EEPROM_REG, addr << 1);
+		_iwl4965_clear_bit(priv, CSR_EEPROM_REG, CSR_EEPROM_REG_BIT_CMD);
+
+		for (i = 0; i < IWL_EEPROM_ACCESS_TIMEOUT;
+					i += IWL_EEPROM_ACCESS_DELAY) {
+			r = _iwl4965_read_direct32(priv, CSR_EEPROM_REG);
+			if (r & CSR_EEPROM_REG_READ_VALID_MSK)
+				break;
+			udelay(IWL_EEPROM_ACCESS_DELAY);
+		}
+
+		if (!(r & CSR_EEPROM_REG_READ_VALID_MSK)) {
+			IWL_ERROR("Time out reading EEPROM[%d]", addr);
+			ret = -ETIMEDOUT;
+			goto done;
+		}
+		e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
+	}
+	ret = 0;
+
+done:
+	priv->cfg->ops->lib->eeprom_ops.release_semaphore(priv);
+	return ret;
+}
+EXPORT_SYMBOL(iwl_eeprom_init);
+
+
+void iwl_eeprom_get_mac(const struct iwl4965_priv *priv, u8 *mac)
+{
+	memcpy(mac, priv->eeprom.mac_address, 6);
+}
+EXPORT_SYMBOL(iwl_eeprom_get_mac);
+

+ 399 - 0
drivers/net/wireless/iwlwifi/iwl-eeprom.h

@@ -0,0 +1,399 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __iwl_eeprom_h__
+#define __iwl_eeprom_h__
+
+struct iwl4965_priv;
+
+/*
+ * EEPROM access time values:
+ *
+ * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG,
+ *   then clearing (with subsequent read/modify/write) CSR_EEPROM_REG bit
+ *   CSR_EEPROM_REG_BIT_CMD (0x2).
+ * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1).
+ * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec.
+ * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG.
+ */
+#define IWL_EEPROM_ACCESS_TIMEOUT	5000 /* uSec */
+#define IWL_EEPROM_ACCESS_DELAY		10   /* uSec */
+
+#define IWL_EEPROM_SEM_TIMEOUT 		10   /* milliseconds */
+#define IWL_EEPROM_SEM_RETRY_LIMIT	1000 /* number of attempts (not time) */
+
+
+/*
+ * Regulatory channel usage flags in EEPROM struct iwl4965_eeprom_channel.flags.
+ *
+ * IBSS and/or AP operation is allowed *only* on those channels with
+ * (VALID && IBSS && ACTIVE && !RADAR).  This restriction is in place because
+ * RADAR detection is not supported by the 4965 driver, but is a
+ * requirement for establishing a new network for legal operation on channels
+ * requiring RADAR detection or restricting ACTIVE scanning.
+ *
+ * NOTE:  "WIDE" flag does not indicate anything about "FAT" 40 MHz channels.
+ *        It only indicates that 20 MHz channel use is supported; FAT channel
+ *        usage is indicated by a separate set of regulatory flags for each
+ *        FAT channel pair.
+ *
+ * NOTE:  Using a channel inappropriately will result in a uCode error!
+ */
+#define IWL_NUM_TX_CALIB_GROUPS 5
+enum {
+	EEPROM_CHANNEL_VALID = (1 << 0),	/* usable for this SKU/geo */
+	EEPROM_CHANNEL_IBSS = (1 << 1),		/* usable as an IBSS channel */
+	/* Bit 2 Reserved */
+	EEPROM_CHANNEL_ACTIVE = (1 << 3),	/* active scanning allowed */
+	EEPROM_CHANNEL_RADAR = (1 << 4),	/* radar detection required */
+	EEPROM_CHANNEL_WIDE = (1 << 5),		/* 20 MHz channel okay */
+	EEPROM_CHANNEL_NARROW = (1 << 6),	/* 10 MHz channel (not used) */
+	EEPROM_CHANNEL_DFS = (1 << 7),	/* dynamic freq selection candidate */
+};
+
+/* SKU Capabilities */
+#define EEPROM_SKU_CAP_SW_RF_KILL_ENABLE                (1 << 0)
+#define EEPROM_SKU_CAP_HW_RF_KILL_ENABLE                (1 << 1)
+
+/* *regulatory* channel data format in eeprom, one for each channel.
+ * There are separate entries for FAT (40 MHz) vs. normal (20 MHz) channels. */
+struct iwl4965_eeprom_channel {
+	u8 flags;		/* EEPROM_CHANNEL_* flags copied from EEPROM */
+	s8 max_power_avg;	/* max power (dBm) on this chnl, limit 31 */
+} __attribute__ ((packed));
+
+/* 4965 has two radio transmitters (and 3 radio receivers) */
+#define EEPROM_TX_POWER_TX_CHAINS      (2)
+
+/* 4965 has room for up to 8 sets of txpower calibration data */
+#define EEPROM_TX_POWER_BANDS          (8)
+
+/* 4965 factory calibration measures txpower gain settings for
+ * each of 3 target output levels */
+#define EEPROM_TX_POWER_MEASUREMENTS   (3)
+
+#define EEPROM_4965_TX_POWER_VERSION        (2)
+
+/* 4965 driver does not work with txpower calibration version < 5.
+ * Look for this in calib_version member of struct iwl4965_eeprom. */
+#define EEPROM_TX_POWER_VERSION_NEW    (5)
+
+
+/*
+ * 4965 factory calibration data for one txpower level, on one channel,
+ * measured on one of the 2 tx chains (radio transmitter and associated
+ * antenna).  EEPROM contains:
+ *
+ * 1)  Temperature (degrees Celsius) of device when measurement was made.
+ *
+ * 2)  Gain table index used to achieve the target measurement power.
+ *     This refers to the "well-known" gain tables (see iwl-4965-hw.h).
+ *
+ * 3)  Actual measured output power, in half-dBm ("34" = 17 dBm).
+ *
+ * 4)  RF power amplifier detector level measurement (not used).
+ */
+struct iwl4965_eeprom_calib_measure {
+	u8 temperature;		/* Device temperature (Celsius) */
+	u8 gain_idx;		/* Index into gain table */
+	u8 actual_pow;		/* Measured RF output power, half-dBm */
+	s8 pa_det;		/* Power amp detector level (not used) */
+} __attribute__ ((packed));
+
+
+/*
+ * 4965 measurement set for one channel.  EEPROM contains:
+ *
+ * 1)  Channel number measured
+ *
+ * 2)  Measurements for each of 3 power levels for each of 2 radio transmitters
+ *     (a.k.a. "tx chains") (6 measurements altogether)
+ */
+struct iwl4965_eeprom_calib_ch_info {
+	u8 ch_num;
+	struct iwl4965_eeprom_calib_measure
+		measurements[EEPROM_TX_POWER_TX_CHAINS]
+			[EEPROM_TX_POWER_MEASUREMENTS];
+} __attribute__ ((packed));
+
+/*
+ * 4965 txpower subband info.
+ *
+ * For each frequency subband, EEPROM contains the following:
+ *
+ * 1)  First and last channels within range of the subband.  "0" values
+ *     indicate that this sample set is not being used.
+ *
+ * 2)  Sample measurement sets for 2 channels close to the range endpoints.
+ */
+struct iwl4965_eeprom_calib_subband_info {
+	u8 ch_from;	/* channel number of lowest channel in subband */
+	u8 ch_to;	/* channel number of highest channel in subband */
+	struct iwl4965_eeprom_calib_ch_info ch1;
+	struct iwl4965_eeprom_calib_ch_info ch2;
+} __attribute__ ((packed));
+
+
+/*
+ * 4965 txpower calibration info.  EEPROM contains:
+ *
+ * 1)  Factory-measured saturation power levels (maximum levels at which
+ *     tx power amplifier can output a signal without too much distortion).
+ *     There is one level for 2.4 GHz band and one for 5 GHz band.  These
+ *     values apply to all channels within each of the bands.
+ *
+ * 2)  Factory-measured power supply voltage level.  This is assumed to be
+ *     constant (i.e. same value applies to all channels/bands) while the
+ *     factory measurements are being made.
+ *
+ * 3)  Up to 8 sets of factory-measured txpower calibration values.
+ *     These are for different frequency ranges, since txpower gain
+ *     characteristics of the analog radio circuitry vary with frequency.
+ *
+ *     Not all sets need to be filled with data;
+ *     struct iwl4965_eeprom_calib_subband_info contains range of channels
+ *     (0 if unused) for each set of data.
+ */
+struct iwl4965_eeprom_calib_info {
+	u8 saturation_power24;	/* half-dBm (e.g. "34" = 17 dBm) */
+	u8 saturation_power52;	/* half-dBm */
+	s16 voltage;		/* signed */
+	struct iwl4965_eeprom_calib_subband_info
+		band_info[EEPROM_TX_POWER_BANDS];
+} __attribute__ ((packed));
+
+
+
+/*
+ * 4965 EEPROM map
+ */
+struct iwl4965_eeprom {
+	u8 reserved0[16];
+#define EEPROM_DEVICE_ID                    (2*0x08)	/* 2 bytes */
+	u16 device_id;		/* abs.ofs: 16 */
+	u8 reserved1[2];
+#define EEPROM_PMC                          (2*0x0A)	/* 2 bytes */
+	u16 pmc;		/* abs.ofs: 20 */
+	u8 reserved2[20];
+#define EEPROM_MAC_ADDRESS                  (2*0x15)	/* 6  bytes */
+	u8 mac_address[6];	/* abs.ofs: 42 */
+	u8 reserved3[58];
+#define EEPROM_BOARD_REVISION               (2*0x35)	/* 2  bytes */
+	u16 board_revision;	/* abs.ofs: 106 */
+	u8 reserved4[11];
+#define EEPROM_BOARD_PBA_NUMBER             (2*0x3B+1)	/* 9  bytes */
+	u8 board_pba_number[9];	/* abs.ofs: 119 */
+	u8 reserved5[8];
+#define EEPROM_VERSION                      (2*0x44)	/* 2  bytes */
+	u16 version;		/* abs.ofs: 136 */
+#define EEPROM_SKU_CAP                      (2*0x45)	/* 1  bytes */
+	u8 sku_cap;		/* abs.ofs: 138 */
+#define EEPROM_LEDS_MODE                    (2*0x45+1)	/* 1  bytes */
+	u8 leds_mode;		/* abs.ofs: 139 */
+#define EEPROM_OEM_MODE                     (2*0x46)	/* 2  bytes */
+	u16 oem_mode;
+#define EEPROM_WOWLAN_MODE                  (2*0x47)	/* 2  bytes */
+	u16 wowlan_mode;	/* abs.ofs: 142 */
+#define EEPROM_LEDS_TIME_INTERVAL           (2*0x48)	/* 2  bytes */
+	u16 leds_time_interval;	/* abs.ofs: 144 */
+#define EEPROM_LEDS_OFF_TIME                (2*0x49)	/* 1  bytes */
+	u8 leds_off_time;	/* abs.ofs: 146 */
+#define EEPROM_LEDS_ON_TIME                 (2*0x49+1)	/* 1  bytes */
+	u8 leds_on_time;	/* abs.ofs: 147 */
+#define EEPROM_ALMGOR_M_VERSION             (2*0x4A)	/* 1  bytes */
+	u8 almgor_m_version;	/* abs.ofs: 148 */
+#define EEPROM_ANTENNA_SWITCH_TYPE          (2*0x4A+1)	/* 1  bytes */
+	u8 antenna_switch_type;	/* abs.ofs: 149 */
+	u8 reserved6[8];
+#define EEPROM_4965_BOARD_REVISION          (2*0x4F)	/* 2 bytes */
+	u16 board_revision_4965;	/* abs.ofs: 158 */
+	u8 reserved7[13];
+#define EEPROM_4965_BOARD_PBA               (2*0x56+1)	/* 9 bytes */
+	u8 board_pba_number_4965[9];	/* abs.ofs: 173 */
+	u8 reserved8[10];
+#define EEPROM_REGULATORY_SKU_ID            (2*0x60)	/* 4  bytes */
+	u8 sku_id[4];		/* abs.ofs: 192 */
+
+/*
+ * Per-channel regulatory data.
+ *
+ * Each channel that *might* be supported by 3945 or 4965 has a fixed location
+ * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory
+ * txpower (MSB).
+ *
+ * Entries immediately below are for 20 MHz channel width.  FAT (40 MHz)
+ * channels (only for 4965, not supported by 3945) appear later in the EEPROM.
+ *
+ * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
+ */
+#define EEPROM_REGULATORY_BAND_1            (2*0x62)	/* 2  bytes */
+	u16 band_1_count;	/* abs.ofs: 196 */
+#define EEPROM_REGULATORY_BAND_1_CHANNELS   (2*0x63)	/* 28 bytes */
+	struct iwl4965_eeprom_channel band_1_channels[14]; /* abs.ofs: 196 */
+
+/*
+ * 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196,
+ * 5.0 GHz channels 7, 8, 11, 12, 16
+ * (4915-5080MHz) (none of these is ever supported)
+ */
+#define EEPROM_REGULATORY_BAND_2            (2*0x71)	/* 2  bytes */
+	u16 band_2_count;	/* abs.ofs: 226 */
+#define EEPROM_REGULATORY_BAND_2_CHANNELS   (2*0x72)	/* 26 bytes */
+	struct iwl4965_eeprom_channel band_2_channels[13]; /* abs.ofs: 228 */
+
+/*
+ * 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64
+ * (5170-5320MHz)
+ */
+#define EEPROM_REGULATORY_BAND_3            (2*0x7F)	/* 2  bytes */
+	u16 band_3_count;	/* abs.ofs: 254 */
+#define EEPROM_REGULATORY_BAND_3_CHANNELS   (2*0x80)	/* 24 bytes */
+	struct iwl4965_eeprom_channel band_3_channels[12]; /* abs.ofs: 256 */
+
+/*
+ * 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140
+ * (5500-5700MHz)
+ */
+#define EEPROM_REGULATORY_BAND_4            (2*0x8C)	/* 2  bytes */
+	u16 band_4_count;	/* abs.ofs: 280 */
+#define EEPROM_REGULATORY_BAND_4_CHANNELS   (2*0x8D)	/* 22 bytes */
+	struct iwl4965_eeprom_channel band_4_channels[11]; /* abs.ofs: 282 */
+
+/*
+ * 5.7 GHz channels 145, 149, 153, 157, 161, 165
+ * (5725-5825MHz)
+ */
+#define EEPROM_REGULATORY_BAND_5            (2*0x98)	/* 2  bytes */
+	u16 band_5_count;	/* abs.ofs: 304 */
+#define EEPROM_REGULATORY_BAND_5_CHANNELS   (2*0x99)	/* 12 bytes */
+	struct iwl4965_eeprom_channel band_5_channels[6]; /* abs.ofs: 306 */
+
+	u8 reserved10[2];
+
+
+/*
+ * 2.4 GHz FAT channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11)
+ *
+ * The channel listed is the center of the lower 20 MHz half of the channel.
+ * The overall center frequency is actually 2 channels (10 MHz) above that,
+ * and the upper half of each FAT channel is centered 4 channels (20 MHz) away
+ * from the lower half; e.g. the upper half of FAT channel 1 is channel 5,
+ * and the overall FAT channel width centers on channel 3.
+ *
+ * NOTE:  The RXON command uses 20 MHz channel numbers to specify the
+ *        control channel to which to tune.  RXON also specifies whether the
+ *        control channel is the upper or lower half of a FAT channel.
+ *
+ * NOTE:  4965 does not support FAT channels on 2.4 GHz.
+ */
+#define EEPROM_REGULATORY_BAND_24_FAT_CHANNELS (2*0xA0)	/* 14 bytes */
+	struct iwl4965_eeprom_channel band_24_channels[7]; /* abs.ofs: 320 */
+	u8 reserved11[2];
+
+/*
+ * 5.2 GHz FAT channels 36 (40), 44 (48), 52 (56), 60 (64),
+ * 100 (104), 108 (112), 116 (120), 124 (128), 132 (136), 149 (153), 157 (161)
+ */
+#define EEPROM_REGULATORY_BAND_52_FAT_CHANNELS (2*0xA8)	/* 22 bytes */
+	struct iwl4965_eeprom_channel band_52_channels[11]; /* abs.ofs: 336 */
+	u8 reserved12[6];
+
+/*
+ * 4965 driver requires txpower calibration format version 5 or greater.
+ * Driver does not work with txpower calibration version < 5.
+ * This value is simply a 16-bit number, no major/minor versions here.
+ */
+#define EEPROM_CALIB_VERSION_OFFSET            (2*0xB6)	/* 2 bytes */
+	u16 calib_version;	/* abs.ofs: 364 */
+	u8 reserved13[2];
+	u8 reserved14[96];	/* abs.ofs: 368 */
+
+/*
+ * 4965 Txpower calibration data.
+ */
+#define EEPROM_IWL_CALIB_TXPOWER_OFFSET        (2*0xE8)	/* 48  bytes */
+	struct iwl4965_eeprom_calib_info calib_info;	/* abs.ofs: 464 */
+
+	u8 reserved16[140];	/* fill out to full 1024 byte block */
+
+
+} __attribute__ ((packed));
+
+#define IWL_EEPROM_IMAGE_SIZE 1024
+
+/* End of EEPROM */
+
+struct iwl_eeprom_ops {
+	int (*verify_signature) (struct iwl4965_priv *priv);
+	int (*acquire_semaphore) (struct iwl4965_priv *priv);
+	void (*release_semaphore) (struct iwl4965_priv *priv);
+};
+
+
+void iwl_eeprom_get_mac(const struct iwl4965_priv *priv, u8 *mac);
+int iwl_eeprom_init(struct iwl4965_priv *priv);
+
+int iwlcore_eeprom_verify_signature(struct iwl4965_priv *priv);
+int iwlcore_eeprom_acquire_semaphore(struct iwl4965_priv *priv);
+void iwlcore_eeprom_release_semaphore(struct iwl4965_priv *priv);
+
+#endif  /* __iwl_eeprom_h__ */

+ 20 - 0
drivers/net/wireless/iwlwifi/iwl-helpers.h

@@ -254,6 +254,26 @@ static inline u8 iwl_get_dma_hi_address(dma_addr_t addr)
 	return sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0;
 	return sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0;
 }
 }
 
 
+/**
+ * iwl_queue_inc_wrap - increment queue index, wrap back to beginning
+ * @index -- current index
+ * @n_bd -- total number of entries in queue (must be power of 2)
+ */
+static inline int iwl_queue_inc_wrap(int index, int n_bd)
+{
+	return ++index & (n_bd - 1);
+}
+
+/**
+ * iwl_queue_dec_wrap - decrement queue index, wrap back to end
+ * @index -- current index
+ * @n_bd -- total number of entries in queue (must be power of 2)
+ */
+static inline int iwl_queue_dec_wrap(int index, int n_bd)
+{
+	return --index & (n_bd - 1);
+}
+
 /* TODO: Move fw_desc functions to iwl-pci.ko */
 /* TODO: Move fw_desc functions to iwl-pci.ko */
 static inline void iwl_free_fw_desc(struct pci_dev *pci_dev,
 static inline void iwl_free_fw_desc(struct pci_dev *pci_dev,
 				    struct fw_desc *desc)
 				    struct fw_desc *desc)

+ 94 - 437
drivers/net/wireless/iwlwifi/iwl3945-base.c

@@ -46,6 +46,7 @@
 
 
 #include <asm/div64.h>
 #include <asm/div64.h>
 
 
+#include "iwl-3945-core.h"
 #include "iwl-3945.h"
 #include "iwl-3945.h"
 #include "iwl-helpers.h"
 #include "iwl-helpers.h"
 
 
@@ -95,11 +96,6 @@ int iwl3945_param_queues_num = IWL_MAX_NUM_QUEUES; /* def: 8 Tx queues */
 #define DRV_COPYRIGHT	"Copyright(c) 2003-2007 Intel Corporation"
 #define DRV_COPYRIGHT	"Copyright(c) 2003-2007 Intel Corporation"
 #define DRV_VERSION     IWLWIFI_VERSION
 #define DRV_VERSION     IWLWIFI_VERSION
 
 
-/* Change firmware file name, using "-" and incrementing number,
- *   *only* when uCode interface or architecture changes so that it
- *   is not compatible with earlier drivers.
- * This number will also appear in << 8 position of 1st dword of uCode file */
-#define IWL3945_UCODE_API "-1"
 
 
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
 MODULE_VERSION(DRV_VERSION);
 MODULE_VERSION(DRV_VERSION);
@@ -162,17 +158,6 @@ static const char *iwl3945_escape_essid(const char *essid, u8 essid_len)
 	return escaped;
 	return escaped;
 }
 }
 
 
-static void iwl3945_print_hex_dump(int level, void *p, u32 len)
-{
-#ifdef CONFIG_IWL3945_DEBUG
-	if (!(iwl3945_debug_level & level))
-		return;
-
-	print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1,
-			p, len, 1);
-#endif
-}
-
 /*************** DMA-QUEUE-GENERAL-FUNCTIONS  *****
 /*************** DMA-QUEUE-GENERAL-FUNCTIONS  *****
  * DMA services
  * DMA services
  *
  *
@@ -198,7 +183,7 @@ static void iwl3945_print_hex_dump(int level, void *p, u32 len)
  * (#0-3) for data tx via EDCA.  An additional 2 HCCA queues are unused.
  * (#0-3) for data tx via EDCA.  An additional 2 HCCA queues are unused.
  ***************************************************/
  ***************************************************/
 
 
-static int iwl3945_queue_space(const struct iwl3945_queue *q)
+int iwl3945_queue_space(const struct iwl3945_queue *q)
 {
 {
 	int s = q->read_ptr - q->write_ptr;
 	int s = q->read_ptr - q->write_ptr;
 
 
@@ -214,33 +199,14 @@ static int iwl3945_queue_space(const struct iwl3945_queue *q)
 	return s;
 	return s;
 }
 }
 
 
-/**
- * iwl3945_queue_inc_wrap - increment queue index, wrap back to beginning
- * @index -- current index
- * @n_bd -- total number of entries in queue (must be power of 2)
- */
-static inline int iwl3945_queue_inc_wrap(int index, int n_bd)
-{
-	return ++index & (n_bd - 1);
-}
-
-/**
- * iwl3945_queue_dec_wrap - increment queue index, wrap back to end
- * @index -- current index
- * @n_bd -- total number of entries in queue (must be power of 2)
- */
-static inline int iwl3945_queue_dec_wrap(int index, int n_bd)
-{
-	return --index & (n_bd - 1);
-}
-
-static inline int x2_queue_used(const struct iwl3945_queue *q, int i)
+int iwl3945_x2_queue_used(const struct iwl3945_queue *q, int i)
 {
 {
 	return q->write_ptr > q->read_ptr ?
 	return q->write_ptr > q->read_ptr ?
 		(i >= q->read_ptr && i < q->write_ptr) :
 		(i >= q->read_ptr && i < q->write_ptr) :
 		!(i < q->read_ptr && i >= q->write_ptr);
 		!(i < q->read_ptr && i >= q->write_ptr);
 }
 }
 
 
+
 static inline u8 get_cmd_index(struct iwl3945_queue *q, u32 index, int is_huge)
 static inline u8 get_cmd_index(struct iwl3945_queue *q, u32 index, int is_huge)
 {
 {
 	/* This is for scan command, the big buffer at end of command array */
 	/* This is for scan command, the big buffer at end of command array */
@@ -261,8 +227,8 @@ static int iwl3945_queue_init(struct iwl3945_priv *priv, struct iwl3945_queue *q
 	q->n_window = slots_num;
 	q->n_window = slots_num;
 	q->id = id;
 	q->id = id;
 
 
-	/* count must be power-of-two size, otherwise iwl3945_queue_inc_wrap
-	 * and iwl3945_queue_dec_wrap are broken. */
+	/* count must be power-of-two size, otherwise iwl_queue_inc_wrap
+	 * and iwl_queue_dec_wrap are broken. */
 	BUG_ON(!is_power_of_2(count));
 	BUG_ON(!is_power_of_2(count));
 
 
 	/* slots_num must be power-of-two size, otherwise
 	/* slots_num must be power-of-two size, otherwise
@@ -362,7 +328,7 @@ int iwl3945_tx_queue_init(struct iwl3945_priv *priv,
 	txq->need_update = 0;
 	txq->need_update = 0;
 
 
 	/* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
 	/* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
-	 * iwl3945_queue_inc_wrap and iwl3945_queue_dec_wrap are broken. */
+	 * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
 	BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
 	BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
 
 
 	/* Initialize queue high/low-water, head/tail indexes */
 	/* Initialize queue high/low-water, head/tail indexes */
@@ -393,7 +359,7 @@ void iwl3945_tx_queue_free(struct iwl3945_priv *priv, struct iwl3945_tx_queue *t
 
 
 	/* first, empty all BD's */
 	/* first, empty all BD's */
 	for (; q->write_ptr != q->read_ptr;
 	for (; q->write_ptr != q->read_ptr;
-	     q->read_ptr = iwl3945_queue_inc_wrap(q->read_ptr, q->n_bd))
+	     q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd))
 		iwl3945_hw_txq_free_tfd(priv, txq);
 		iwl3945_hw_txq_free_tfd(priv, txq);
 
 
 	len = sizeof(struct iwl3945_cmd) * q->n_window;
 	len = sizeof(struct iwl3945_cmd) * q->n_window;
@@ -732,7 +698,7 @@ static int iwl3945_enqueue_hcmd(struct iwl3945_priv *priv, struct iwl3945_host_c
 	txq->need_update = 1;
 	txq->need_update = 1;
 
 
 	/* Increment and update queue's write index */
 	/* Increment and update queue's write index */
-	q->write_ptr = iwl3945_queue_inc_wrap(q->write_ptr, q->n_bd);
+	q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
 	ret = iwl3945_tx_queue_update_write_ptr(priv, txq);
 	ret = iwl3945_tx_queue_update_write_ptr(priv, txq);
 
 
 	spin_unlock_irqrestore(&priv->hcmd_lock, flags);
 	spin_unlock_irqrestore(&priv->hcmd_lock, flags);
@@ -1630,151 +1596,6 @@ int iwl3945_eeprom_init(struct iwl3945_priv *priv)
 	return 0;
 	return 0;
 }
 }
 
 
-/******************************************************************************
- *
- * Misc. internal state and helper functions
- *
- ******************************************************************************/
-#ifdef CONFIG_IWL3945_DEBUG
-
-/**
- * iwl3945_report_frame - dump frame to syslog during debug sessions
- *
- * You may hack this function to show different aspects of received frames,
- * including selective frame dumps.
- * group100 parameter selects whether to show 1 out of 100 good frames.
- */
-void iwl3945_report_frame(struct iwl3945_priv *priv,
-		      struct iwl3945_rx_packet *pkt,
-		      struct ieee80211_hdr *header, int group100)
-{
-	u32 to_us;
-	u32 print_summary = 0;
-	u32 print_dump = 0;	/* set to 1 to dump all frames' contents */
-	u32 hundred = 0;
-	u32 dataframe = 0;
-	u16 fc;
-	u16 seq_ctl;
-	u16 channel;
-	u16 phy_flags;
-	int rate_sym;
-	u16 length;
-	u16 status;
-	u16 bcn_tmr;
-	u32 tsf_low;
-	u64 tsf;
-	u8 rssi;
-	u8 agc;
-	u16 sig_avg;
-	u16 noise_diff;
-	struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
-	struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
-	struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
-	u8 *data = IWL_RX_DATA(pkt);
-
-	/* MAC header */
-	fc = le16_to_cpu(header->frame_control);
-	seq_ctl = le16_to_cpu(header->seq_ctrl);
-
-	/* metadata */
-	channel = le16_to_cpu(rx_hdr->channel);
-	phy_flags = le16_to_cpu(rx_hdr->phy_flags);
-	rate_sym = rx_hdr->rate;
-	length = le16_to_cpu(rx_hdr->len);
-
-	/* end-of-frame status and timestamp */
-	status = le32_to_cpu(rx_end->status);
-	bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
-	tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
-	tsf = le64_to_cpu(rx_end->timestamp);
-
-	/* signal statistics */
-	rssi = rx_stats->rssi;
-	agc = rx_stats->agc;
-	sig_avg = le16_to_cpu(rx_stats->sig_avg);
-	noise_diff = le16_to_cpu(rx_stats->noise_diff);
-
-	to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
-
-	/* if data frame is to us and all is good,
-	 *   (optionally) print summary for only 1 out of every 100 */
-	if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) ==
-	    (IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
-		dataframe = 1;
-		if (!group100)
-			print_summary = 1;	/* print each frame */
-		else if (priv->framecnt_to_us < 100) {
-			priv->framecnt_to_us++;
-			print_summary = 0;
-		} else {
-			priv->framecnt_to_us = 0;
-			print_summary = 1;
-			hundred = 1;
-		}
-	} else {
-		/* print summary for all other frames */
-		print_summary = 1;
-	}
-
-	if (print_summary) {
-		char *title;
-		u32 rate;
-
-		if (hundred)
-			title = "100Frames";
-		else if (fc & IEEE80211_FCTL_RETRY)
-			title = "Retry";
-		else if (ieee80211_is_assoc_response(fc))
-			title = "AscRsp";
-		else if (ieee80211_is_reassoc_response(fc))
-			title = "RasRsp";
-		else if (ieee80211_is_probe_response(fc)) {
-			title = "PrbRsp";
-			print_dump = 1;	/* dump frame contents */
-		} else if (ieee80211_is_beacon(fc)) {
-			title = "Beacon";
-			print_dump = 1;	/* dump frame contents */
-		} else if (ieee80211_is_atim(fc))
-			title = "ATIM";
-		else if (ieee80211_is_auth(fc))
-			title = "Auth";
-		else if (ieee80211_is_deauth(fc))
-			title = "DeAuth";
-		else if (ieee80211_is_disassoc(fc))
-			title = "DisAssoc";
-		else
-			title = "Frame";
-
-		rate = iwl3945_rate_index_from_plcp(rate_sym);
-		if (rate == -1)
-			rate = 0;
-		else
-			rate = iwl3945_rates[rate].ieee / 2;
-
-		/* print frame summary.
-		 * MAC addresses show just the last byte (for brevity),
-		 *    but you can hack it to show more, if you'd like to. */
-		if (dataframe)
-			IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, "
-				     "len=%u, rssi=%d, chnl=%d, rate=%u, \n",
-				     title, fc, header->addr1[5],
-				     length, rssi, channel, rate);
-		else {
-			/* src/dst addresses assume managed mode */
-			IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
-				     "src=0x%02x, rssi=%u, tim=%lu usec, "
-				     "phy=0x%02x, chnl=%d\n",
-				     title, fc, header->addr1[5],
-				     header->addr3[5], rssi,
-				     tsf_low - priv->scan_start_tsf,
-				     phy_flags, channel);
-		}
-	}
-	if (print_dump)
-		iwl3945_print_hex_dump(IWL_DL_RX, data, length);
-}
-#endif
-
 static void iwl3945_unset_hw_setting(struct iwl3945_priv *priv)
 static void iwl3945_unset_hw_setting(struct iwl3945_priv *priv)
 {
 {
 	if (priv->hw_setting.shared_virt)
 	if (priv->hw_setting.shared_virt)
@@ -2242,34 +2063,6 @@ int iwl3945_is_network_packet(struct iwl3945_priv *priv, struct ieee80211_hdr *h
 	return 1;
 	return 1;
 }
 }
 
 
-#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x
-
-static const char *iwl3945_get_tx_fail_reason(u32 status)
-{
-	switch (status & TX_STATUS_MSK) {
-	case TX_STATUS_SUCCESS:
-		return "SUCCESS";
-		TX_STATUS_ENTRY(SHORT_LIMIT);
-		TX_STATUS_ENTRY(LONG_LIMIT);
-		TX_STATUS_ENTRY(FIFO_UNDERRUN);
-		TX_STATUS_ENTRY(MGMNT_ABORT);
-		TX_STATUS_ENTRY(NEXT_FRAG);
-		TX_STATUS_ENTRY(LIFE_EXPIRE);
-		TX_STATUS_ENTRY(DEST_PS);
-		TX_STATUS_ENTRY(ABORTED);
-		TX_STATUS_ENTRY(BT_RETRY);
-		TX_STATUS_ENTRY(STA_INVALID);
-		TX_STATUS_ENTRY(FRAG_DROPPED);
-		TX_STATUS_ENTRY(TID_DISABLE);
-		TX_STATUS_ENTRY(FRAME_FLUSHED);
-		TX_STATUS_ENTRY(INSUFFICIENT_CF_POLL);
-		TX_STATUS_ENTRY(TX_LOCKED);
-		TX_STATUS_ENTRY(NO_BEACON_ON_RADAR);
-	}
-
-	return "UNKNOWN";
-}
-
 /**
 /**
  * iwl3945_scan_cancel - Cancel any currently executing HW scan
  * iwl3945_scan_cancel - Cancel any currently executing HW scan
  *
  *
@@ -2957,7 +2750,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv,
 			   ieee80211_get_hdrlen(fc));
 			   ieee80211_get_hdrlen(fc));
 
 
 	/* Tell device the write index *just past* this latest filled TFD */
 	/* Tell device the write index *just past* this latest filled TFD */
-	q->write_ptr = iwl3945_queue_inc_wrap(q->write_ptr, q->n_bd);
+	q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
 	rc = iwl3945_tx_queue_update_write_ptr(priv, txq);
 	rc = iwl3945_tx_queue_update_write_ptr(priv, txq);
 	spin_unlock_irqrestore(&priv->lock, flags);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 
@@ -3317,125 +3110,6 @@ static int iwl3945_get_measurement(struct iwl3945_priv *priv,
 }
 }
 #endif
 #endif
 
 
-static void iwl3945_txstatus_to_ieee(struct iwl3945_priv *priv,
-				 struct iwl3945_tx_info *tx_sta)
-{
-
-	tx_sta->status.ack_signal = 0;
-	tx_sta->status.excessive_retries = 0;
-	tx_sta->status.queue_length = 0;
-	tx_sta->status.queue_number = 0;
-
-	if (in_interrupt())
-		ieee80211_tx_status_irqsafe(priv->hw,
-					    tx_sta->skb[0], &(tx_sta->status));
-	else
-		ieee80211_tx_status(priv->hw,
-				    tx_sta->skb[0], &(tx_sta->status));
-
-	tx_sta->skb[0] = NULL;
-}
-
-/**
- * iwl3945_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd
- *
- * When FW advances 'R' index, all entries between old and new 'R' index
- * need to be reclaimed. As result, some free space forms. If there is
- * enough free space (> low mark), wake the stack that feeds us.
- */
-static int iwl3945_tx_queue_reclaim(struct iwl3945_priv *priv, int txq_id, int index)
-{
-	struct iwl3945_tx_queue *txq = &priv->txq[txq_id];
-	struct iwl3945_queue *q = &txq->q;
-	int nfreed = 0;
-
-	if ((index >= q->n_bd) || (x2_queue_used(q, index) == 0)) {
-		IWL_ERROR("Read index for DMA queue txq id (%d), index %d, "
-			  "is out of range [0-%d] %d %d.\n", txq_id,
-			  index, q->n_bd, q->write_ptr, q->read_ptr);
-		return 0;
-	}
-
-	for (index = iwl3945_queue_inc_wrap(index, q->n_bd);
-		q->read_ptr != index;
-		q->read_ptr = iwl3945_queue_inc_wrap(q->read_ptr, q->n_bd)) {
-		if (txq_id != IWL_CMD_QUEUE_NUM) {
-			iwl3945_txstatus_to_ieee(priv,
-					&(txq->txb[txq->q.read_ptr]));
-			iwl3945_hw_txq_free_tfd(priv, txq);
-		} else if (nfreed > 1) {
-			IWL_ERROR("HCMD skipped: index (%d) %d %d\n", index,
-					q->write_ptr, q->read_ptr);
-			queue_work(priv->workqueue, &priv->restart);
-		}
-		nfreed++;
-	}
-
-	if (iwl3945_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);
-
-
-	return nfreed;
-}
-
-static int iwl3945_is_tx_success(u32 status)
-{
-	return (status & 0xFF) == 0x1;
-}
-
-/******************************************************************************
- *
- * Generic RX handler implementations
- *
- ******************************************************************************/
-/**
- * iwl3945_rx_reply_tx - Handle Tx response
- */
-static void iwl3945_rx_reply_tx(struct iwl3945_priv *priv,
-			    struct iwl3945_rx_mem_buffer *rxb)
-{
-	struct iwl3945_rx_packet *pkt = (void *)rxb->skb->data;
-	u16 sequence = le16_to_cpu(pkt->hdr.sequence);
-	int txq_id = SEQ_TO_QUEUE(sequence);
-	int index = SEQ_TO_INDEX(sequence);
-	struct iwl3945_tx_queue *txq = &priv->txq[txq_id];
-	struct ieee80211_tx_status *tx_status;
-	struct iwl3945_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
-	u32  status = le32_to_cpu(tx_resp->status);
-
-	if ((index >= txq->q.n_bd) || (x2_queue_used(&txq->q, index) == 0)) {
-		IWL_ERROR("Read index for DMA queue txq_id (%d) index %d "
-			  "is out of range [0-%d] %d %d\n", txq_id,
-			  index, txq->q.n_bd, txq->q.write_ptr,
-			  txq->q.read_ptr);
-		return;
-	}
-
-	tx_status = &(txq->txb[txq->q.read_ptr].status);
-
-	tx_status->retry_count = tx_resp->failure_frame;
-	tx_status->queue_number = status;
-	tx_status->queue_length = tx_resp->bt_kill_count;
-	tx_status->queue_length |= tx_resp->failure_rts;
-
-	tx_status->flags =
-	    iwl3945_is_tx_success(status) ? IEEE80211_TX_STATUS_ACK : 0;
-
-	IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n",
-			txq_id, iwl3945_get_tx_fail_reason(status), status,
-			tx_resp->rate, tx_resp->failure_frame);
-
-	IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
-	if (index != -1)
-		iwl3945_tx_queue_reclaim(priv, txq_id, index);
-
-	if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
-		IWL_ERROR("TODO:  Implement Tx ABORT REQUIRED!!!\n");
-}
-
-
 static void iwl3945_rx_reply_alive(struct iwl3945_priv *priv,
 static void iwl3945_rx_reply_alive(struct iwl3945_priv *priv,
 			       struct iwl3945_rx_mem_buffer *rxb)
 			       struct iwl3945_rx_mem_buffer *rxb)
 {
 {
@@ -3782,12 +3456,43 @@ static void iwl3945_setup_rx_handlers(struct iwl3945_priv *priv)
 	priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] =
 	priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] =
 	    iwl3945_rx_scan_complete_notif;
 	    iwl3945_rx_scan_complete_notif;
 	priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl3945_rx_card_state_notif;
 	priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl3945_rx_card_state_notif;
-	priv->rx_handlers[REPLY_TX] = iwl3945_rx_reply_tx;
 
 
 	/* Set up hardware specific Rx handlers */
 	/* Set up hardware specific Rx handlers */
 	iwl3945_hw_rx_handler_setup(priv);
 	iwl3945_hw_rx_handler_setup(priv);
 }
 }
 
 
+/**
+ * iwl3945_cmd_queue_reclaim - Reclaim CMD queue entries
+ * When FW advances 'R' index, all entries between old and new 'R' index
+ * need to be reclaimed.
+ */
+static void iwl3945_cmd_queue_reclaim(struct iwl3945_priv *priv,
+				      int txq_id, int index)
+{
+	struct iwl3945_tx_queue *txq = &priv->txq[txq_id];
+	struct iwl3945_queue *q = &txq->q;
+	int nfreed = 0;
+
+	if ((index >= q->n_bd) || (iwl3945_x2_queue_used(q, index) == 0)) {
+		IWL_ERROR("Read index for DMA queue txq id (%d), index %d, "
+			  "is out of range [0-%d] %d %d.\n", txq_id,
+			  index, q->n_bd, q->write_ptr, q->read_ptr);
+		return;
+	}
+
+	for (index = iwl_queue_inc_wrap(index, q->n_bd); q->read_ptr != index;
+		q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+		if (nfreed > 1) {
+			IWL_ERROR("HCMD skipped: index (%d) %d %d\n", index,
+					q->write_ptr, q->read_ptr);
+			queue_work(priv->workqueue, &priv->restart);
+			break;
+		}
+		nfreed++;
+	}
+}
+
+
 /**
 /**
  * iwl3945_tx_cmd_complete - Pull unused buffers off the queue and reclaim them
  * iwl3945_tx_cmd_complete - Pull unused buffers off the queue and reclaim them
  * @rxb: Rx buffer to reclaim
  * @rxb: Rx buffer to reclaim
@@ -3807,12 +3512,6 @@ static void iwl3945_tx_cmd_complete(struct iwl3945_priv *priv,
 	int cmd_index;
 	int cmd_index;
 	struct iwl3945_cmd *cmd;
 	struct iwl3945_cmd *cmd;
 
 
-	/* If a Tx command is being handled and it isn't in the actual
-	 * command queue then there a command routing bug has been introduced
-	 * in the queue management code. */
-	if (txq_id != IWL_CMD_QUEUE_NUM)
-		IWL_ERROR("Error wrong command queue %d command id 0x%X\n",
-			  txq_id, pkt->hdr.cmd);
 	BUG_ON(txq_id != IWL_CMD_QUEUE_NUM);
 	BUG_ON(txq_id != IWL_CMD_QUEUE_NUM);
 
 
 	cmd_index = get_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, huge);
 	cmd_index = get_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, huge);
@@ -3826,7 +3525,7 @@ static void iwl3945_tx_cmd_complete(struct iwl3945_priv *priv,
 		   !cmd->meta.u.callback(priv, cmd, rxb->skb))
 		   !cmd->meta.u.callback(priv, cmd, rxb->skb))
 		rxb->skb = NULL;
 		rxb->skb = NULL;
 
 
-	iwl3945_tx_queue_reclaim(priv, txq_id, index);
+	iwl3945_cmd_queue_reclaim(priv, txq_id, index);
 
 
 	if (!(cmd->meta.flags & CMD_ASYNC)) {
 	if (!(cmd->meta.flags & CMD_ASYNC)) {
 		clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
 		clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
@@ -4506,8 +4205,7 @@ static void iwl3945_dump_nic_error_log(struct iwl3945_priv *priv)
 
 
 	if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
 	if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
 		IWL_ERROR("Start IWL Error Log Dump:\n");
 		IWL_ERROR("Start IWL Error Log Dump:\n");
-		IWL_ERROR("Status: 0x%08lX, Config: %08X count: %d\n",
-			  priv->status, priv->config, count);
+		IWL_ERROR("Status: 0x%08lX, count: %d\n", priv->status, count);
 	}
 	}
 
 
 	IWL_ERROR("Desc       Time       asrtPC  blink2 "
 	IWL_ERROR("Desc       Time       asrtPC  blink2 "
@@ -4727,9 +4425,9 @@ static void iwl3945_irq_tasklet(struct iwl3945_priv *priv)
 	 * atomic, make sure that inta covers all the interrupts that
 	 * atomic, make sure that inta covers all the interrupts that
 	 * we've discovered, even if FH interrupt came in just after
 	 * we've discovered, even if FH interrupt came in just after
 	 * reading CSR_INT. */
 	 * reading CSR_INT. */
-	if (inta_fh & CSR_FH_INT_RX_MASK)
+	if (inta_fh & CSR39_FH_INT_RX_MASK)
 		inta |= CSR_INT_BIT_FH_RX;
 		inta |= CSR_INT_BIT_FH_RX;
-	if (inta_fh & CSR_FH_INT_TX_MASK)
+	if (inta_fh & CSR39_FH_INT_TX_MASK)
 		inta |= CSR_INT_BIT_FH_TX;
 		inta |= CSR_INT_BIT_FH_TX;
 
 
 	/* Now service all interrupt bits discovered above. */
 	/* Now service all interrupt bits discovered above. */
@@ -5119,11 +4817,12 @@ static int iwl3945_init_channel_map(struct iwl3945_priv *priv)
 			ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
 			ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
 			ch_info->min_power = 0;
 			ch_info->min_power = 0;
 
 
-			IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x"
+			IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x"
 				       " %ddBm): Ad-Hoc %ssupported\n",
 				       " %ddBm): Ad-Hoc %ssupported\n",
 				       ch_info->channel,
 				       ch_info->channel,
 				       is_channel_a_band(ch_info) ?
 				       is_channel_a_band(ch_info) ?
 				       "5.2" : "2.4",
 				       "5.2" : "2.4",
+				       CHECK_AND_PRINT(VALID),
 				       CHECK_AND_PRINT(IBSS),
 				       CHECK_AND_PRINT(IBSS),
 				       CHECK_AND_PRINT(ACTIVE),
 				       CHECK_AND_PRINT(ACTIVE),
 				       CHECK_AND_PRINT(RADAR),
 				       CHECK_AND_PRINT(RADAR),
@@ -5333,7 +5032,7 @@ static void iwl3945_init_hw_rates(struct iwl3945_priv *priv,
 static int iwl3945_init_geos(struct iwl3945_priv *priv)
 static int iwl3945_init_geos(struct iwl3945_priv *priv)
 {
 {
 	struct iwl3945_channel_info *ch;
 	struct iwl3945_channel_info *ch;
-	struct ieee80211_supported_band *band;
+	struct ieee80211_supported_band *sband;
 	struct ieee80211_channel *channels;
 	struct ieee80211_channel *channels;
 	struct ieee80211_channel *geo_ch;
 	struct ieee80211_channel *geo_ch;
 	struct ieee80211_rate *rates;
 	struct ieee80211_rate *rates;
@@ -5351,7 +5050,7 @@ static int iwl3945_init_geos(struct iwl3945_priv *priv)
 	if (!channels)
 	if (!channels)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	rates = kzalloc((sizeof(struct ieee80211_rate) * (IWL_MAX_RATES + 1)),
+	rates = kzalloc((sizeof(struct ieee80211_rate) * (IWL_RATE_COUNT + 1)),
 			GFP_KERNEL);
 			GFP_KERNEL);
 	if (!rates) {
 	if (!rates) {
 		kfree(channels);
 		kfree(channels);
@@ -5359,38 +5058,38 @@ static int iwl3945_init_geos(struct iwl3945_priv *priv)
 	}
 	}
 
 
 	/* 5.2GHz channels start after the 2.4GHz channels */
 	/* 5.2GHz channels start after the 2.4GHz channels */
-	band = &priv->bands[IEEE80211_BAND_5GHZ];
-	band->channels = &channels[ARRAY_SIZE(iwl3945_eeprom_band_1)];
-	band->bitrates = &rates[4];
-	band->n_bitrates = 8;	/* just OFDM */
-
-	band = &priv->bands[IEEE80211_BAND_2GHZ];
-	band->channels = channels;
-	band->bitrates = rates;
-	band->n_bitrates = 12;	/* OFDM & CCK */
+	sband = &priv->bands[IEEE80211_BAND_5GHZ];
+	sband->channels = &channels[ARRAY_SIZE(iwl3945_eeprom_band_1)];
+	/* just OFDM */
+	sband->bitrates = &rates[IWL_FIRST_OFDM_RATE];
+	sband->n_bitrates = IWL_RATE_COUNT - IWL_FIRST_OFDM_RATE;
+
+	sband = &priv->bands[IEEE80211_BAND_2GHZ];
+	sband->channels = channels;
+	/* OFDM & CCK */
+	sband->bitrates = rates;
+	sband->n_bitrates = IWL_RATE_COUNT;
 
 
 	priv->ieee_channels = channels;
 	priv->ieee_channels = channels;
 	priv->ieee_rates = rates;
 	priv->ieee_rates = rates;
 
 
 	iwl3945_init_hw_rates(priv, rates);
 	iwl3945_init_hw_rates(priv, rates);
 
 
-	for (i = 0, geo_ch = channels; i < priv->channel_count; i++) {
+	for (i = 0;  i < priv->channel_count; i++) {
 		ch = &priv->channel_info[i];
 		ch = &priv->channel_info[i];
 
 
-		if (!is_channel_valid(ch)) {
-			IWL_DEBUG_INFO("Channel %d [%sGHz] is restricted -- "
-				    "skipping.\n",
-				    ch->channel, is_channel_a_band(ch) ?
-				    "5.2" : "2.4");
+		/* FIXME: might be removed if scan is OK*/
+		if (!is_channel_valid(ch))
 			continue;
 			continue;
-		}
 
 
 		if (is_channel_a_band(ch))
 		if (is_channel_a_band(ch))
-			geo_ch = &priv->bands[IEEE80211_BAND_5GHZ].channels[priv->bands[IEEE80211_BAND_5GHZ].n_channels++];
+			sband =  &priv->bands[IEEE80211_BAND_5GHZ];
 		else
 		else
-			geo_ch = &priv->bands[IEEE80211_BAND_2GHZ].channels[priv->bands[IEEE80211_BAND_2GHZ].n_channels++];
+			sband =  &priv->bands[IEEE80211_BAND_2GHZ];
 
 
-		geo_ch->center_freq = ieee80211chan2mhz(ch->channel);
+		geo_ch = &sband->channels[sband->n_channels++];
+
+		geo_ch->center_freq = ieee80211_channel_to_frequency(ch->channel);
 		geo_ch->max_power = ch->max_power_avg;
 		geo_ch->max_power = ch->max_power_avg;
 		geo_ch->max_antenna_gain = 0xff;
 		geo_ch->max_antenna_gain = 0xff;
 		geo_ch->hw_value = ch->channel;
 		geo_ch->hw_value = ch->channel;
@@ -5408,16 +5107,28 @@ static int iwl3945_init_geos(struct iwl3945_priv *priv)
 			if (ch->max_power_avg > priv->max_channel_txpower_limit)
 			if (ch->max_power_avg > priv->max_channel_txpower_limit)
 				priv->max_channel_txpower_limit =
 				priv->max_channel_txpower_limit =
 				    ch->max_power_avg;
 				    ch->max_power_avg;
-		} else
+		} else {
 			geo_ch->flags |= IEEE80211_CHAN_DISABLED;
 			geo_ch->flags |= IEEE80211_CHAN_DISABLED;
+		}
+
+		/* Save flags for reg domain usage */
+		geo_ch->orig_flags = geo_ch->flags;
+
+		IWL_DEBUG_INFO("Channel %d Freq=%d[%sGHz] %s flag=0%X\n",
+				ch->channel, geo_ch->center_freq,
+				is_channel_a_band(ch) ?  "5.2" : "2.4",
+				geo_ch->flags & IEEE80211_CHAN_DISABLED ?
+				"restricted" : "valid",
+				 geo_ch->flags);
 	}
 	}
 
 
-	if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && priv->is_abg) {
+	if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) &&
+	     priv->cfg->sku & IWL_SKU_A) {
 		printk(KERN_INFO DRV_NAME
 		printk(KERN_INFO DRV_NAME
 		       ": Incorrectly detected BG card as ABG.  Please send "
 		       ": Incorrectly detected BG card as ABG.  Please send "
 		       "your PCI ID 0x%04X:0x%04X to maintainer.\n",
 		       "your PCI ID 0x%04X:0x%04X to maintainer.\n",
 		       priv->pci_dev->device, priv->pci_dev->subsystem_device);
 		       priv->pci_dev->device, priv->pci_dev->subsystem_device);
-		priv->is_abg = 0;
+		 priv->cfg->sku &= ~IWL_SKU_A;
 	}
 	}
 
 
 	printk(KERN_INFO DRV_NAME
 	printk(KERN_INFO DRV_NAME
@@ -5764,7 +5475,7 @@ static int iwl3945_read_ucode(struct iwl3945_priv *priv)
 	int ret = 0;
 	int ret = 0;
 	const struct firmware *ucode_raw;
 	const struct firmware *ucode_raw;
 	/* firmware file name contains uCode/driver compatibility version */
 	/* firmware file name contains uCode/driver compatibility version */
-	const char *name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode";
+	const char *name = priv->cfg->fw_name;
 	u8 *src;
 	u8 *src;
 	size_t len;
 	size_t len;
 	u32 ver, inst_size, data_size, init_size, init_data_size, boot_size;
 	u32 ver, inst_size, data_size, init_size, init_data_size, boot_size;
@@ -7152,6 +6863,12 @@ static int iwl3945_mac_config_interface(struct ieee80211_hw *hw,
 	if (conf == NULL)
 	if (conf == NULL)
 		return -EIO;
 		return -EIO;
 
 
+	if (priv->vif != vif) {
+		IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
+		mutex_unlock(&priv->mutex);
+		return 0;
+	}
+
 	/* XXX: this MUST use conf->mac_addr */
 	/* XXX: this MUST use conf->mac_addr */
 
 
 	if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) &&
 	if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) &&
@@ -7176,17 +6893,6 @@ static int iwl3945_mac_config_interface(struct ieee80211_hw *hw,
 	if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) &&
 	if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) &&
 	    !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) {
 	    !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) {
  */
  */
-	if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
-		IWL_DEBUG_MAC80211("leave - scanning\n");
-		mutex_unlock(&priv->mutex);
-		return 0;
-	}
-
-	if (priv->vif != vif) {
-		IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
-		mutex_unlock(&priv->mutex);
-		return 0;
-	}
 
 
 	if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
 	if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
 		if (!conf->bssid) {
 		if (!conf->bssid) {
@@ -7884,31 +7590,6 @@ static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR,
 		   show_measurement, store_measurement);
 		   show_measurement, store_measurement);
 #endif /* CONFIG_IWL3945_SPECTRUM_MEASUREMENT */
 #endif /* CONFIG_IWL3945_SPECTRUM_MEASUREMENT */
 
 
-static ssize_t show_rate(struct device *d,
-			 struct device_attribute *attr, char *buf)
-{
-	struct iwl3945_priv *priv = dev_get_drvdata(d);
-	unsigned long flags;
-	int i;
-
-	spin_lock_irqsave(&priv->sta_lock, flags);
-	if (priv->iw_mode == IEEE80211_IF_TYPE_STA)
-		i = priv->stations[IWL_AP_ID].current_rate.s.rate;
-	else
-		i = priv->stations[IWL_STA_ID].current_rate.s.rate;
-	spin_unlock_irqrestore(&priv->sta_lock, flags);
-
-	i = iwl3945_rate_index_from_plcp(i);
-	if (i == -1)
-		return sprintf(buf, "0\n");
-
-	return sprintf(buf, "%d%s\n",
-		       (iwl3945_rates[i].ieee >> 1),
-		       (iwl3945_rates[i].ieee & 0x1) ? ".5" : "");
-}
-
-static DEVICE_ATTR(rate, S_IRUSR, show_rate, NULL);
-
 static ssize_t store_retry_rate(struct device *d,
 static ssize_t store_retry_rate(struct device *d,
 				struct device_attribute *attr,
 				struct device_attribute *attr,
 				const char *buf, size_t count)
 				const char *buf, size_t count)
@@ -8199,7 +7880,6 @@ static struct attribute *iwl3945_sysfs_entries[] = {
 	&dev_attr_measurement.attr,
 	&dev_attr_measurement.attr,
 #endif
 #endif
 	&dev_attr_power_level.attr,
 	&dev_attr_power_level.attr,
-	&dev_attr_rate.attr,
 	&dev_attr_retry_rate.attr,
 	&dev_attr_retry_rate.attr,
 	&dev_attr_rf_kill.attr,
 	&dev_attr_rf_kill.attr,
 	&dev_attr_rs_window.attr,
 	&dev_attr_rs_window.attr,
@@ -8238,9 +7918,9 @@ static struct ieee80211_ops iwl3945_hw_ops = {
 static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 {
 	int err = 0;
 	int err = 0;
-	u32 pci_id;
 	struct iwl3945_priv *priv;
 	struct iwl3945_priv *priv;
 	struct ieee80211_hw *hw;
 	struct ieee80211_hw *hw;
+	struct iwl_3945_cfg *cfg = (struct iwl_3945_cfg *)(ent->driver_data);
 	int i;
 	int i;
 	DECLARE_MAC_BUF(mac);
 	DECLARE_MAC_BUF(mac);
 
 
@@ -8276,6 +7956,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 	priv->hw = hw;
 	priv->hw = hw;
 
 
 	priv->pci_dev = pdev;
 	priv->pci_dev = pdev;
+	priv->cfg = cfg;
 
 
 	/* Select antenna (may be helpful if only one antenna is connected) */
 	/* Select antenna (may be helpful if only one antenna is connected) */
 	priv->antenna = (enum iwl3945_antenna)iwl3945_param_antenna;
 	priv->antenna = (enum iwl3945_antenna)iwl3945_param_antenna;
@@ -8365,32 +8046,8 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 
 
 	priv->iw_mode = IEEE80211_IF_TYPE_STA;
 	priv->iw_mode = IEEE80211_IF_TYPE_STA;
 
 
-	pci_id =
-	    (priv->pci_dev->device << 16) | priv->pci_dev->subsystem_device;
-
-	switch (pci_id) {
-	case 0x42221005:	/* 0x4222 0x8086 0x1005 is BG SKU */
-	case 0x42221034:	/* 0x4222 0x8086 0x1034 is BG SKU */
-	case 0x42271014:	/* 0x4227 0x8086 0x1014 is BG SKU */
-	case 0x42221044:	/* 0x4222 0x8086 0x1044 is BG SKU */
-		priv->is_abg = 0;
-		break;
-
-	/*
-	 * Rest are assumed ABG SKU -- if this is not the
-	 * case then the card will get the wrong 'Detected'
-	 * line in the kernel log however the code that
-	 * initializes the GEO table will detect no A-band
-	 * channels and remove the is_abg mask.
-	 */
-	default:
-		priv->is_abg = 1;
-		break;
-	}
-
 	printk(KERN_INFO DRV_NAME
 	printk(KERN_INFO DRV_NAME
-	       ": Detected Intel PRO/Wireless 3945%sBG Network Connection\n",
-	       priv->is_abg ? "A" : "");
+		": Detected Intel Wireless WiFi Link %s\n", priv->cfg->name);
 
 
 	/* Device-specific setup */
 	/* Device-specific setup */
 	if (iwl3945_hw_set_hw_setting(priv)) {
 	if (iwl3945_hw_set_hw_setting(priv)) {

+ 79 - 272
drivers/net/wireless/iwlwifi/iwl4965-base.c

@@ -45,13 +45,10 @@
 
 
 #include <asm/div64.h>
 #include <asm/div64.h>
 
 
+#include "iwl-core.h"
 #include "iwl-4965.h"
 #include "iwl-4965.h"
 #include "iwl-helpers.h"
 #include "iwl-helpers.h"
 
 
-#ifdef CONFIG_IWL4965_DEBUG
-u32 iwl4965_debug_level;
-#endif
-
 static int iwl4965_tx_queue_update_write_ptr(struct iwl4965_priv *priv,
 static int iwl4965_tx_queue_update_write_ptr(struct iwl4965_priv *priv,
 				  struct iwl4965_tx_queue *txq);
 				  struct iwl4965_tx_queue *txq);
 
 
@@ -90,15 +87,8 @@ int iwl4965_param_amsdu_size_8K;   /* def: enable 8K amsdu size */
 #define VS
 #define VS
 #endif
 #endif
 
 
-#define IWLWIFI_VERSION "1.2.26k" VD VS
-#define DRV_COPYRIGHT	"Copyright(c) 2003-2007 Intel Corporation"
-#define DRV_VERSION     IWLWIFI_VERSION
+#define DRV_VERSION     IWLWIFI_VERSION VD VS
 
 
-/* Change firmware file name, using "-" and incrementing number,
- *   *only* when uCode interface or architecture changes so that it
- *   is not compatible with earlier drivers.
- * This number will also appear in << 8 position of 1st dword of uCode file */
-#define IWL4965_UCODE_API "-1"
 
 
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
 MODULE_VERSION(DRV_VERSION);
 MODULE_VERSION(DRV_VERSION);
@@ -161,17 +151,6 @@ static const char *iwl4965_escape_essid(const char *essid, u8 essid_len)
 	return escaped;
 	return escaped;
 }
 }
 
 
-static void iwl4965_print_hex_dump(int level, void *p, u32 len)
-{
-#ifdef CONFIG_IWL4965_DEBUG
-	if (!(iwl4965_debug_level & level))
-		return;
-
-	print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1,
-			p, len, 1);
-#endif
-}
-
 /*************** DMA-QUEUE-GENERAL-FUNCTIONS  *****
 /*************** DMA-QUEUE-GENERAL-FUNCTIONS  *****
  * DMA services
  * DMA services
  *
  *
@@ -215,25 +194,6 @@ int iwl4965_queue_space(const struct iwl4965_queue *q)
 	return s;
 	return s;
 }
 }
 
 
-/**
- * iwl4965_queue_inc_wrap - increment queue index, wrap back to beginning
- * @index -- current index
- * @n_bd -- total number of entries in queue (must be power of 2)
- */
-static inline int iwl4965_queue_inc_wrap(int index, int n_bd)
-{
-	return ++index & (n_bd - 1);
-}
-
-/**
- * iwl4965_queue_dec_wrap - decrement queue index, wrap back to end
- * @index -- current index
- * @n_bd -- total number of entries in queue (must be power of 2)
- */
-static inline int iwl4965_queue_dec_wrap(int index, int n_bd)
-{
-	return --index & (n_bd - 1);
-}
 
 
 static inline int x2_queue_used(const struct iwl4965_queue *q, int i)
 static inline int x2_queue_used(const struct iwl4965_queue *q, int i)
 {
 {
@@ -262,8 +222,8 @@ static int iwl4965_queue_init(struct iwl4965_priv *priv, struct iwl4965_queue *q
 	q->n_window = slots_num;
 	q->n_window = slots_num;
 	q->id = id;
 	q->id = id;
 
 
-	/* count must be power-of-two size, otherwise iwl4965_queue_inc_wrap
-	 * and iwl4965_queue_dec_wrap are broken. */
+	/* count must be power-of-two size, otherwise iwl_queue_inc_wrap
+	 * and iwl_queue_dec_wrap are broken. */
 	BUG_ON(!is_power_of_2(count));
 	BUG_ON(!is_power_of_2(count));
 
 
 	/* slots_num must be power-of-two size, otherwise
 	/* slots_num must be power-of-two size, otherwise
@@ -363,7 +323,7 @@ int iwl4965_tx_queue_init(struct iwl4965_priv *priv,
 	txq->need_update = 0;
 	txq->need_update = 0;
 
 
 	/* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
 	/* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
-	 * iwl4965_queue_inc_wrap and iwl4965_queue_dec_wrap are broken. */
+	 * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
 	BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
 	BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
 
 
 	/* Initialize queue's high/low-water marks, and head/tail indexes */
 	/* Initialize queue's high/low-water marks, and head/tail indexes */
@@ -394,7 +354,7 @@ void iwl4965_tx_queue_free(struct iwl4965_priv *priv, struct iwl4965_tx_queue *t
 
 
 	/* first, empty all BD's */
 	/* first, empty all BD's */
 	for (; q->write_ptr != q->read_ptr;
 	for (; q->write_ptr != q->read_ptr;
-	     q->read_ptr = iwl4965_queue_inc_wrap(q->read_ptr, q->n_bd))
+	     q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd))
 		iwl4965_hw_txq_free_tfd(priv, txq);
 		iwl4965_hw_txq_free_tfd(priv, txq);
 
 
 	len = sizeof(struct iwl4965_cmd) * q->n_window;
 	len = sizeof(struct iwl4965_cmd) * q->n_window;
@@ -735,7 +695,7 @@ static int iwl4965_enqueue_hcmd(struct iwl4965_priv *priv, struct iwl4965_host_c
 	ret = iwl4965_tx_queue_update_wr_ptr(priv, txq, 0);
 	ret = iwl4965_tx_queue_update_wr_ptr(priv, txq, 0);
 
 
 	/* Increment and update queue's write index */
 	/* Increment and update queue's write index */
-	q->write_ptr = iwl4965_queue_inc_wrap(q->write_ptr, q->n_bd);
+	q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
 	iwl4965_tx_queue_update_write_ptr(priv, txq);
 	iwl4965_tx_queue_update_write_ptr(priv, txq);
 
 
 	spin_unlock_irqrestore(&priv->hcmd_lock, flags);
 	spin_unlock_irqrestore(&priv->hcmd_lock, flags);
@@ -1551,34 +1511,6 @@ unsigned int iwl4965_fill_beacon_frame(struct iwl4965_priv *priv,
 	return priv->ibss_beacon->len;
 	return priv->ibss_beacon->len;
 }
 }
 
 
-int iwl4965_rate_index_from_plcp(int plcp)
-{
-	int i = 0;
-
-	/* 4965 HT rate format */
-	if (plcp & RATE_MCS_HT_MSK) {
-		i = (plcp & 0xff);
-
-		if (i >= IWL_RATE_MIMO_6M_PLCP)
-			i = i - IWL_RATE_MIMO_6M_PLCP;
-
-		i += IWL_FIRST_OFDM_RATE;
-		/* skip 9M not supported in ht*/
-		if (i >= IWL_RATE_9M_INDEX)
-			i += 1;
-		if ((i >= IWL_FIRST_OFDM_RATE) &&
-		    (i <= IWL_LAST_OFDM_RATE))
-			return i;
-
-	/* 4965 legacy rate format, search for match in table */
-	} else {
-		for (i = 0; i < ARRAY_SIZE(iwl4965_rates); i++)
-			if (iwl4965_rates[i].plcp == (plcp &0xFF))
-				return i;
-	}
-	return -1;
-}
-
 static u8 iwl4965_rate_get_lowest_plcp(int rate_mask)
 static u8 iwl4965_rate_get_lowest_plcp(int rate_mask)
 {
 {
 	u8 i;
 	u8 i;
@@ -1712,148 +1644,6 @@ done:
  * Misc. internal state and helper functions
  * Misc. internal state and helper functions
  *
  *
  ******************************************************************************/
  ******************************************************************************/
-#ifdef CONFIG_IWL4965_DEBUG
-
-/**
- * iwl4965_report_frame - dump frame to syslog during debug sessions
- *
- * You may hack this function to show different aspects of received frames,
- * including selective frame dumps.
- * group100 parameter selects whether to show 1 out of 100 good frames.
- *
- * TODO:  This was originally written for 3945, need to audit for
- *        proper operation with 4965.
- */
-void iwl4965_report_frame(struct iwl4965_priv *priv,
-		      struct iwl4965_rx_packet *pkt,
-		      struct ieee80211_hdr *header, int group100)
-{
-	u32 to_us;
-	u32 print_summary = 0;
-	u32 print_dump = 0;	/* set to 1 to dump all frames' contents */
-	u32 hundred = 0;
-	u32 dataframe = 0;
-	u16 fc;
-	u16 seq_ctl;
-	u16 channel;
-	u16 phy_flags;
-	int rate_sym;
-	u16 length;
-	u16 status;
-	u16 bcn_tmr;
-	u32 tsf_low;
-	u64 tsf;
-	u8 rssi;
-	u8 agc;
-	u16 sig_avg;
-	u16 noise_diff;
-	struct iwl4965_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
-	struct iwl4965_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
-	struct iwl4965_rx_frame_end *rx_end = IWL_RX_END(pkt);
-	u8 *data = IWL_RX_DATA(pkt);
-
-	/* MAC header */
-	fc = le16_to_cpu(header->frame_control);
-	seq_ctl = le16_to_cpu(header->seq_ctrl);
-
-	/* metadata */
-	channel = le16_to_cpu(rx_hdr->channel);
-	phy_flags = le16_to_cpu(rx_hdr->phy_flags);
-	rate_sym = rx_hdr->rate;
-	length = le16_to_cpu(rx_hdr->len);
-
-	/* end-of-frame status and timestamp */
-	status = le32_to_cpu(rx_end->status);
-	bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
-	tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
-	tsf = le64_to_cpu(rx_end->timestamp);
-
-	/* signal statistics */
-	rssi = rx_stats->rssi;
-	agc = rx_stats->agc;
-	sig_avg = le16_to_cpu(rx_stats->sig_avg);
-	noise_diff = le16_to_cpu(rx_stats->noise_diff);
-
-	to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
-
-	/* if data frame is to us and all is good,
-	 *   (optionally) print summary for only 1 out of every 100 */
-	if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) ==
-	    (IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) {
-		dataframe = 1;
-		if (!group100)
-			print_summary = 1;	/* print each frame */
-		else if (priv->framecnt_to_us < 100) {
-			priv->framecnt_to_us++;
-			print_summary = 0;
-		} else {
-			priv->framecnt_to_us = 0;
-			print_summary = 1;
-			hundred = 1;
-		}
-	} else {
-		/* print summary for all other frames */
-		print_summary = 1;
-	}
-
-	if (print_summary) {
-		char *title;
-		u32 rate;
-
-		if (hundred)
-			title = "100Frames";
-		else if (fc & IEEE80211_FCTL_RETRY)
-			title = "Retry";
-		else if (ieee80211_is_assoc_response(fc))
-			title = "AscRsp";
-		else if (ieee80211_is_reassoc_response(fc))
-			title = "RasRsp";
-		else if (ieee80211_is_probe_response(fc)) {
-			title = "PrbRsp";
-			print_dump = 1;	/* dump frame contents */
-		} else if (ieee80211_is_beacon(fc)) {
-			title = "Beacon";
-			print_dump = 1;	/* dump frame contents */
-		} else if (ieee80211_is_atim(fc))
-			title = "ATIM";
-		else if (ieee80211_is_auth(fc))
-			title = "Auth";
-		else if (ieee80211_is_deauth(fc))
-			title = "DeAuth";
-		else if (ieee80211_is_disassoc(fc))
-			title = "DisAssoc";
-		else
-			title = "Frame";
-
-		rate = iwl4965_rate_index_from_plcp(rate_sym);
-		if (rate == -1)
-			rate = 0;
-		else
-			rate = iwl4965_rates[rate].ieee / 2;
-
-		/* print frame summary.
-		 * MAC addresses show just the last byte (for brevity),
-		 *    but you can hack it to show more, if you'd like to. */
-		if (dataframe)
-			IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, "
-				     "len=%u, rssi=%d, chnl=%d, rate=%u, \n",
-				     title, fc, header->addr1[5],
-				     length, rssi, channel, rate);
-		else {
-			/* src/dst addresses assume managed mode */
-			IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
-				     "src=0x%02x, rssi=%u, tim=%lu usec, "
-				     "phy=0x%02x, chnl=%d\n",
-				     title, fc, header->addr1[5],
-				     header->addr3[5], rssi,
-				     tsf_low - priv->scan_start_tsf,
-				     phy_flags, channel);
-		}
-	}
-	if (print_dump)
-		iwl4965_print_hex_dump(IWL_DL_RX, data, length);
-}
-#endif
 
 
 static void iwl4965_unset_hw_setting(struct iwl4965_priv *priv)
 static void iwl4965_unset_hw_setting(struct iwl4965_priv *priv)
 {
 {
@@ -3088,7 +2878,7 @@ static int iwl4965_tx_skb(struct iwl4965_priv *priv,
 	iwl4965_tx_queue_update_wr_ptr(priv, txq, len);
 	iwl4965_tx_queue_update_wr_ptr(priv, txq, len);
 
 
 	/* Tell device the write index *just past* this latest filled TFD */
 	/* Tell device the write index *just past* this latest filled TFD */
-	q->write_ptr = iwl4965_queue_inc_wrap(q->write_ptr, q->n_bd);
+	q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
 	rc = iwl4965_tx_queue_update_write_ptr(priv, txq);
 	rc = iwl4965_tx_queue_update_write_ptr(priv, txq);
 	spin_unlock_irqrestore(&priv->lock, flags);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 
@@ -3482,9 +3272,9 @@ int iwl4965_tx_queue_reclaim(struct iwl4965_priv *priv, int txq_id, int index)
 		return 0;
 		return 0;
 	}
 	}
 
 
-	for (index = iwl4965_queue_inc_wrap(index, q->n_bd);
+	for (index = iwl_queue_inc_wrap(index, q->n_bd);
 		q->read_ptr != index;
 		q->read_ptr != index;
-		q->read_ptr = iwl4965_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+		q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
 		if (txq_id != IWL_CMD_QUEUE_NUM) {
 		if (txq_id != IWL_CMD_QUEUE_NUM) {
 			iwl4965_txstatus_to_ieee(priv,
 			iwl4965_txstatus_to_ieee(priv,
 					&(txq->txb[txq->q.read_ptr]));
 					&(txq->txb[txq->q.read_ptr]));
@@ -3591,9 +3381,9 @@ static int iwl4965_tx_status_reply_tx(struct iwl4965_priv *priv,
 		tx_status->control.flags &= ~IEEE80211_TXCTL_AMPDU;
 		tx_status->control.flags &= ~IEEE80211_TXCTL_AMPDU;
 		tx_status->flags = iwl4965_is_tx_success(status)?
 		tx_status->flags = iwl4965_is_tx_success(status)?
 			IEEE80211_TX_STATUS_ACK : 0;
 			IEEE80211_TX_STATUS_ACK : 0;
-		/* FIXME Wrong Rate
-		tx_status->control.tx_rate =
-				iwl4965_hw_get_rate_n_flags(tx_resp->rate_n_flags); */
+		iwl4965_hwrate_to_tx_control(priv,
+					     le32_to_cpu(tx_resp->rate_n_flags),
+					     &tx_status->control);
 		/* FIXME: code repetition end */
 		/* FIXME: code repetition end */
 
 
 		IWL_DEBUG_TX_REPLY("1 Frame 0x%x failure :%d\n",
 		IWL_DEBUG_TX_REPLY("1 Frame 0x%x failure :%d\n",
@@ -3729,7 +3519,7 @@ static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv,
 
 
 		if (txq->q.read_ptr != (scd_ssn & 0xff)) {
 		if (txq->q.read_ptr != (scd_ssn & 0xff)) {
 			int freed;
 			int freed;
-			index = iwl4965_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
+			index = iwl_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
 			IWL_DEBUG_TX_REPLY("Retry scheduler reclaim scd_ssn "
 			IWL_DEBUG_TX_REPLY("Retry scheduler reclaim scd_ssn "
 					   "%d index %d\n", scd_ssn , index);
 					   "%d index %d\n", scd_ssn , index);
 			freed = iwl4965_tx_queue_reclaim(priv, txq_id, index);
 			freed = iwl4965_tx_queue_reclaim(priv, txq_id, index);
@@ -3750,9 +3540,10 @@ static void iwl4965_rx_reply_tx(struct iwl4965_priv *priv,
 	tx_status->queue_number = status;
 	tx_status->queue_number = status;
 	tx_status->queue_length = tx_resp->bt_kill_count;
 	tx_status->queue_length = tx_resp->bt_kill_count;
 	tx_status->queue_length |= tx_resp->failure_rts;
 	tx_status->queue_length |= tx_resp->failure_rts;
-
 	tx_status->flags =
 	tx_status->flags =
 	    iwl4965_is_tx_success(status) ? IEEE80211_TX_STATUS_ACK : 0;
 	    iwl4965_is_tx_success(status) ? IEEE80211_TX_STATUS_ACK : 0;
+	iwl4965_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
+				     &tx_status->control);
 
 
 	IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) rate_n_flags 0x%x "
 	IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) rate_n_flags 0x%x "
 		     "retries %d\n", txq_id, iwl4965_get_tx_fail_reason(status),
 		     "retries %d\n", txq_id, iwl4965_get_tx_fail_reason(status),
@@ -4886,8 +4677,7 @@ static void iwl4965_dump_nic_error_log(struct iwl4965_priv *priv)
 
 
 	if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
 	if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
 		IWL_ERROR("Start IWL Error Log Dump:\n");
 		IWL_ERROR("Start IWL Error Log Dump:\n");
-		IWL_ERROR("Status: 0x%08lX, Config: %08X count: %d\n",
-			  priv->status, priv->config, count);
+		IWL_ERROR("Status: 0x%08lX, count: %d\n", priv->status, count);
 	}
 	}
 
 
 	desc = iwl4965_read_targ_mem(priv, base + 1 * sizeof(u32));
 	desc = iwl4965_read_targ_mem(priv, base + 1 * sizeof(u32));
@@ -5099,9 +4889,9 @@ static void iwl4965_irq_tasklet(struct iwl4965_priv *priv)
 	 * atomic, make sure that inta covers all the interrupts that
 	 * atomic, make sure that inta covers all the interrupts that
 	 * we've discovered, even if FH interrupt came in just after
 	 * we've discovered, even if FH interrupt came in just after
 	 * reading CSR_INT. */
 	 * reading CSR_INT. */
-	if (inta_fh & CSR_FH_INT_RX_MASK)
+	if (inta_fh & CSR49_FH_INT_RX_MASK)
 		inta |= CSR_INT_BIT_FH_RX;
 		inta |= CSR_INT_BIT_FH_RX;
-	if (inta_fh & CSR_FH_INT_TX_MASK)
+	if (inta_fh & CSR49_FH_INT_TX_MASK)
 		inta |= CSR_INT_BIT_FH_TX;
 		inta |= CSR_INT_BIT_FH_TX;
 
 
 	/* Now service all interrupt bits discovered above. */
 	/* Now service all interrupt bits discovered above. */
@@ -5502,11 +5292,12 @@ static int iwl4965_init_channel_map(struct iwl4965_priv *priv)
 			ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
 			ch_info->scan_power = eeprom_ch_info[ch].max_power_avg;
 			ch_info->min_power = 0;
 			ch_info->min_power = 0;
 
 
-			IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x"
+			IWL_DEBUG_INFO("Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x"
 				       " %ddBm): Ad-Hoc %ssupported\n",
 				       " %ddBm): Ad-Hoc %ssupported\n",
 				       ch_info->channel,
 				       ch_info->channel,
 				       is_channel_a_band(ch_info) ?
 				       is_channel_a_band(ch_info) ?
 				       "5.2" : "2.4",
 				       "5.2" : "2.4",
+				       CHECK_AND_PRINT(VALID),
 				       CHECK_AND_PRINT(IBSS),
 				       CHECK_AND_PRINT(IBSS),
 				       CHECK_AND_PRINT(ACTIVE),
 				       CHECK_AND_PRINT(ACTIVE),
 				       CHECK_AND_PRINT(RADAR),
 				       CHECK_AND_PRINT(RADAR),
@@ -5749,7 +5540,7 @@ static void iwl4965_init_hw_rates(struct iwl4965_priv *priv,
 static int iwl4965_init_geos(struct iwl4965_priv *priv)
 static int iwl4965_init_geos(struct iwl4965_priv *priv)
 {
 {
 	struct iwl4965_channel_info *ch;
 	struct iwl4965_channel_info *ch;
-	struct ieee80211_supported_band *band;
+	struct ieee80211_supported_band *sband;
 	struct ieee80211_channel *channels;
 	struct ieee80211_channel *channels;
 	struct ieee80211_channel *geo_ch;
 	struct ieee80211_channel *geo_ch;
 	struct ieee80211_rate *rates;
 	struct ieee80211_rate *rates;
@@ -5767,7 +5558,7 @@ static int iwl4965_init_geos(struct iwl4965_priv *priv)
 	if (!channels)
 	if (!channels)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	rates = kzalloc((sizeof(struct ieee80211_rate) * (IWL_MAX_RATES + 1)),
+	rates = kzalloc((sizeof(struct ieee80211_rate) * (IWL_RATE_COUNT + 1)),
 			GFP_KERNEL);
 			GFP_KERNEL);
 	if (!rates) {
 	if (!rates) {
 		kfree(channels);
 		kfree(channels);
@@ -5775,42 +5566,42 @@ static int iwl4965_init_geos(struct iwl4965_priv *priv)
 	}
 	}
 
 
 	/* 5.2GHz channels start after the 2.4GHz channels */
 	/* 5.2GHz channels start after the 2.4GHz channels */
-	band = &priv->bands[IEEE80211_BAND_5GHZ];
-	band->channels = &channels[ARRAY_SIZE(iwl4965_eeprom_band_1)];
-	band->bitrates = &rates[4];
-	band->n_bitrates = 8;	/* just OFDM */
+	sband = &priv->bands[IEEE80211_BAND_5GHZ];
+	sband->channels = &channels[ARRAY_SIZE(iwl4965_eeprom_band_1)];
+	/* just OFDM */
+	sband->bitrates = &rates[IWL_FIRST_OFDM_RATE];
+	sband->n_bitrates = IWL_RATE_COUNT - IWL_FIRST_OFDM_RATE;
 
 
-	iwl4965_init_ht_hw_capab(&band->ht_info, IEEE80211_BAND_5GHZ);
+	iwl4965_init_ht_hw_capab(&sband->ht_info, IEEE80211_BAND_5GHZ);
 
 
-	band = &priv->bands[IEEE80211_BAND_2GHZ];
-	band->channels = channels;
-	band->bitrates = rates;
-	band->n_bitrates = 12;	/* OFDM & CCK */
+	sband = &priv->bands[IEEE80211_BAND_2GHZ];
+	sband->channels = channels;
+	/* OFDM & CCK */
+	sband->bitrates = rates;
+	sband->n_bitrates = IWL_RATE_COUNT;
 
 
-	iwl4965_init_ht_hw_capab(&band->ht_info, IEEE80211_BAND_2GHZ);
+	iwl4965_init_ht_hw_capab(&sband->ht_info, IEEE80211_BAND_2GHZ);
 
 
 	priv->ieee_channels = channels;
 	priv->ieee_channels = channels;
 	priv->ieee_rates = rates;
 	priv->ieee_rates = rates;
 
 
 	iwl4965_init_hw_rates(priv, rates);
 	iwl4965_init_hw_rates(priv, rates);
 
 
-	for (i = 0, geo_ch = channels; i < priv->channel_count; i++) {
+	for (i = 0;  i < priv->channel_count; i++) {
 		ch = &priv->channel_info[i];
 		ch = &priv->channel_info[i];
 
 
-		if (!is_channel_valid(ch)) {
-			IWL_DEBUG_INFO("Channel %d [%sGHz] is restricted -- "
-				    "skipping.\n",
-				    ch->channel, is_channel_a_band(ch) ?
-				    "5.2" : "2.4");
+		/* FIXME: might be removed if scan is OK */
+		if (!is_channel_valid(ch))
 			continue;
 			continue;
-		}
 
 
-		if (is_channel_a_band(ch)) {
-			geo_ch = &priv->bands[IEEE80211_BAND_5GHZ].channels[priv->bands[IEEE80211_BAND_5GHZ].n_channels++];
-		} else
-			geo_ch = &priv->bands[IEEE80211_BAND_2GHZ].channels[priv->bands[IEEE80211_BAND_2GHZ].n_channels++];
+		if (is_channel_a_band(ch))
+			sband =  &priv->bands[IEEE80211_BAND_5GHZ];
+		else
+			sband =  &priv->bands[IEEE80211_BAND_2GHZ];
+
+		geo_ch = &sband->channels[sband->n_channels++];
 
 
-		geo_ch->center_freq = ieee80211chan2mhz(ch->channel);
+		geo_ch->center_freq = ieee80211_channel_to_frequency(ch->channel);
 		geo_ch->max_power = ch->max_power_avg;
 		geo_ch->max_power = ch->max_power_avg;
 		geo_ch->max_antenna_gain = 0xff;
 		geo_ch->max_antenna_gain = 0xff;
 		geo_ch->hw_value = ch->channel;
 		geo_ch->hw_value = ch->channel;
@@ -5828,16 +5619,28 @@ static int iwl4965_init_geos(struct iwl4965_priv *priv)
 			if (ch->max_power_avg > priv->max_channel_txpower_limit)
 			if (ch->max_power_avg > priv->max_channel_txpower_limit)
 				priv->max_channel_txpower_limit =
 				priv->max_channel_txpower_limit =
 				    ch->max_power_avg;
 				    ch->max_power_avg;
-		} else
+		} else {
 			geo_ch->flags |= IEEE80211_CHAN_DISABLED;
 			geo_ch->flags |= IEEE80211_CHAN_DISABLED;
+		}
+
+		/* Save flags for reg domain usage */
+		geo_ch->orig_flags = geo_ch->flags;
+
+		IWL_DEBUG_INFO("Channel %d Freq=%d[%sGHz] %s flag=0%X\n",
+				ch->channel, geo_ch->center_freq,
+				is_channel_a_band(ch) ?  "5.2" : "2.4",
+				geo_ch->flags & IEEE80211_CHAN_DISABLED ?
+				"restricted" : "valid",
+				 geo_ch->flags);
 	}
 	}
 
 
-	if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && priv->is_abg) {
+	if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) &&
+	     priv->cfg->sku & IWL_SKU_A) {
 		printk(KERN_INFO DRV_NAME
 		printk(KERN_INFO DRV_NAME
 		       ": Incorrectly detected BG card as ABG.  Please send "
 		       ": Incorrectly detected BG card as ABG.  Please send "
 		       "your PCI ID 0x%04X:0x%04X to maintainer.\n",
 		       "your PCI ID 0x%04X:0x%04X to maintainer.\n",
 		       priv->pci_dev->device, priv->pci_dev->subsystem_device);
 		       priv->pci_dev->device, priv->pci_dev->subsystem_device);
-		priv->is_abg = 0;
+		priv->cfg->sku &= ~IWL_SKU_A;
 	}
 	}
 
 
 	printk(KERN_INFO DRV_NAME
 	printk(KERN_INFO DRV_NAME
@@ -6186,7 +5989,7 @@ static int iwl4965_read_ucode(struct iwl4965_priv *priv)
 	struct iwl4965_ucode *ucode;
 	struct iwl4965_ucode *ucode;
 	int ret;
 	int ret;
 	const struct firmware *ucode_raw;
 	const struct firmware *ucode_raw;
-	const char *name = "iwlwifi-4965" IWL4965_UCODE_API ".ucode";
+	const char *name = priv->cfg->fw_name;
 	u8 *src;
 	u8 *src;
 	size_t len;
 	size_t len;
 	u32 ver, inst_size, data_size, init_size, init_data_size, boot_size;
 	u32 ver, inst_size, data_size, init_size, init_data_size, boot_size;
@@ -7596,6 +7399,12 @@ static int iwl4965_mac_config_interface(struct ieee80211_hw *hw,
 	if (conf == NULL)
 	if (conf == NULL)
 		return -EIO;
 		return -EIO;
 
 
+	if (priv->vif != vif) {
+		IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
+		mutex_unlock(&priv->mutex);
+		return 0;
+	}
+
 	if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) &&
 	if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) &&
 	    (!conf->beacon || !conf->ssid_len)) {
 	    (!conf->beacon || !conf->ssid_len)) {
 		IWL_DEBUG_MAC80211
 		IWL_DEBUG_MAC80211
@@ -7618,17 +7427,6 @@ static int iwl4965_mac_config_interface(struct ieee80211_hw *hw,
 	if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) &&
 	if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) &&
 	    !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) {
 	    !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) {
  */
  */
-	if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
-		IWL_DEBUG_MAC80211("leave - scanning\n");
-		mutex_unlock(&priv->mutex);
-		return 0;
-	}
-
-	if (priv->vif != vif) {
-		IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
-		mutex_unlock(&priv->mutex);
-		return 0;
-	}
 
 
 	if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
 	if (priv->iw_mode == IEEE80211_IF_TYPE_AP) {
 		if (!conf->bssid) {
 		if (!conf->bssid) {
@@ -8127,15 +7925,21 @@ static void iwl4965_ht_info_fill(struct ieee80211_conf *conf,
 	iwl_conf->is_green_field = !!(ht_conf->cap & IEEE80211_HT_CAP_GRN_FLD);
 	iwl_conf->is_green_field = !!(ht_conf->cap & IEEE80211_HT_CAP_GRN_FLD);
 	iwl_conf->max_amsdu_size =
 	iwl_conf->max_amsdu_size =
 		!!(ht_conf->cap & IEEE80211_HT_CAP_MAX_AMSDU);
 		!!(ht_conf->cap & IEEE80211_HT_CAP_MAX_AMSDU);
+
 	iwl_conf->supported_chan_width =
 	iwl_conf->supported_chan_width =
 		!!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH);
 		!!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH);
+	iwl_conf->extension_chan_offset =
+		ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_SEC_OFFSET;
+	/* If no above or below channel supplied disable FAT channel */
+	if (iwl_conf->extension_chan_offset != IWL_EXT_CHANNEL_OFFSET_ABOVE &&
+	    iwl_conf->extension_chan_offset != IWL_EXT_CHANNEL_OFFSET_BELOW)
+		iwl_conf->supported_chan_width = 0;
+
 	iwl_conf->tx_mimo_ps_mode =
 	iwl_conf->tx_mimo_ps_mode =
 		(u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2);
 		(u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2);
 	memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16);
 	memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16);
 
 
 	iwl_conf->control_channel = ht_bss_conf->primary_channel;
 	iwl_conf->control_channel = ht_bss_conf->primary_channel;
-	iwl_conf->extension_chan_offset =
-		ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_SEC_OFFSET;
 	iwl_conf->tx_chan_width =
 	iwl_conf->tx_chan_width =
 		!!(ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_WIDTH);
 		!!(ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_WIDTH);
 	iwl_conf->ht_protection =
 	iwl_conf->ht_protection =
@@ -8776,6 +8580,7 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 	int err = 0;
 	int err = 0;
 	struct iwl4965_priv *priv;
 	struct iwl4965_priv *priv;
 	struct ieee80211_hw *hw;
 	struct ieee80211_hw *hw;
+	struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
 	int i;
 	int i;
 	DECLARE_MAC_BUF(mac);
 	DECLARE_MAC_BUF(mac);
 
 
@@ -8809,6 +8614,7 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 	IWL_DEBUG_INFO("*** LOAD DRIVER ***\n");
 	IWL_DEBUG_INFO("*** LOAD DRIVER ***\n");
 	priv = hw->priv;
 	priv = hw->priv;
 	priv->hw = hw;
 	priv->hw = hw;
+	priv->cfg = cfg;
 
 
 	priv->pci_dev = pdev;
 	priv->pci_dev = pdev;
 	priv->antenna = (enum iwl4965_antenna)iwl4965_param_antenna;
 	priv->antenna = (enum iwl4965_antenna)iwl4965_param_antenna;
@@ -8911,8 +8717,9 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 	/* Choose which receivers/antennas to use */
 	/* Choose which receivers/antennas to use */
 	iwl4965_set_rxon_chain(priv);
 	iwl4965_set_rxon_chain(priv);
 
 
+
 	printk(KERN_INFO DRV_NAME
 	printk(KERN_INFO DRV_NAME
-	       ": Detected Intel Wireless WiFi Link 4965AGN\n");
+		": Detected Intel Wireless WiFi Link %s\n", priv->cfg->name);
 
 
 	/* Device-specific setup */
 	/* Device-specific setup */
 	if (iwl4965_hw_set_hw_setting(priv)) {
 	if (iwl4965_hw_set_hw_setting(priv)) {

+ 2 - 10
drivers/net/wireless/libertas/assoc.c

@@ -349,11 +349,7 @@ static int assoc_helper_wpa_keys(struct lbs_private *priv,
 
 
 	if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
 	if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
 		clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
 		clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
-		ret = lbs_prepare_and_send_command(priv,
-					CMD_802_11_KEY_MATERIAL,
-					CMD_ACT_SET,
-					CMD_OPTION_WAITFORRSP,
-					0, assoc_req);
+		ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
 		assoc_req->flags = flags;
 		assoc_req->flags = flags;
 	}
 	}
 
 
@@ -363,11 +359,7 @@ static int assoc_helper_wpa_keys(struct lbs_private *priv,
 	if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
 	if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
 		clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
 		clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
 
 
-		ret = lbs_prepare_and_send_command(priv,
-					CMD_802_11_KEY_MATERIAL,
-					CMD_ACT_SET,
-					CMD_OPTION_WAITFORRSP,
-					0, assoc_req);
+		ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
 		assoc_req->flags = flags;
 		assoc_req->flags = flags;
 	}
 	}
 
 

+ 75 - 56
drivers/net/wireless/libertas/cmd.c

@@ -338,75 +338,103 @@ int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
 	return ret;
 	return ret;
 }
 }
 
 
-static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset,
-                            struct enc_key * pkey)
+static void set_one_wpa_key(struct MrvlIEtype_keyParamSet *keyparam,
+                            struct enc_key *key)
 {
 {
 	lbs_deb_enter(LBS_DEB_CMD);
 	lbs_deb_enter(LBS_DEB_CMD);
 
 
-	if (pkey->flags & KEY_INFO_WPA_ENABLED) {
-		pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED);
-	}
-	if (pkey->flags & KEY_INFO_WPA_UNICAST) {
-		pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
-	}
-	if (pkey->flags & KEY_INFO_WPA_MCAST) {
-		pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
-	}
+	if (key->flags & KEY_INFO_WPA_ENABLED)
+		keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED);
+	if (key->flags & KEY_INFO_WPA_UNICAST)
+		keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
+	if (key->flags & KEY_INFO_WPA_MCAST)
+		keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
+
+	keyparam->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
+	keyparam->keytypeid = cpu_to_le16(key->type);
+	keyparam->keylen = cpu_to_le16(key->len);
+	memcpy(keyparam->key, key->key, key->len);
 
 
-	pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
-	pkeyparamset->keytypeid = cpu_to_le16(pkey->type);
-	pkeyparamset->keylen = cpu_to_le16(pkey->len);
-	memcpy(pkeyparamset->key, pkey->key, pkey->len);
-	pkeyparamset->length = cpu_to_le16(  sizeof(pkeyparamset->keytypeid)
-	                                        + sizeof(pkeyparamset->keyinfo)
-	                                        + sizeof(pkeyparamset->keylen)
-	                                        + sizeof(pkeyparamset->key));
+	/* Length field doesn't include the {type,length} header */
+	keyparam->length = cpu_to_le16(sizeof(*keyparam) - 4);
 	lbs_deb_leave(LBS_DEB_CMD);
 	lbs_deb_leave(LBS_DEB_CMD);
 }
 }
 
 
-static int lbs_cmd_802_11_key_material(struct lbs_private *priv,
-					struct cmd_ds_command *cmd,
-					u16 cmd_action,
-					u32 cmd_oid, void *pdata_buf)
+int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
+				struct assoc_request *assoc)
 {
 {
-	struct cmd_ds_802_11_key_material *pkeymaterial =
-	    &cmd->params.keymaterial;
-	struct assoc_request * assoc_req = pdata_buf;
+	struct cmd_ds_802_11_key_material cmd;
 	int ret = 0;
 	int ret = 0;
 	int index = 0;
 	int index = 0;
 
 
 	lbs_deb_enter(LBS_DEB_CMD);
 	lbs_deb_enter(LBS_DEB_CMD);
 
 
-	cmd->command = cpu_to_le16(CMD_802_11_KEY_MATERIAL);
-	pkeymaterial->action = cpu_to_le16(cmd_action);
+	cmd.action = cpu_to_le16(cmd_action);
+	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
 
 
 	if (cmd_action == CMD_ACT_GET) {
 	if (cmd_action == CMD_ACT_GET) {
-		cmd->size = cpu_to_le16(S_DS_GEN + sizeof (pkeymaterial->action));
-		ret = 0;
-		goto done;
-	}
+		cmd.hdr.size = cpu_to_le16(S_DS_GEN + 2);
+	} else {
+		memset(cmd.keyParamSet, 0, sizeof(cmd.keyParamSet));
 
 
-	memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet));
+		if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc->flags)) {
+			set_one_wpa_key(&cmd.keyParamSet[index],
+					&assoc->wpa_unicast_key);
+			index++;
+		}
 
 
-	if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
-		set_one_wpa_key(&pkeymaterial->keyParamSet[index],
-		                &assoc_req->wpa_unicast_key);
-		index++;
-	}
+		if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc->flags)) {
+			set_one_wpa_key(&cmd.keyParamSet[index],
+					&assoc->wpa_mcast_key);
+			index++;
+		}
 
 
-	if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
-		set_one_wpa_key(&pkeymaterial->keyParamSet[index],
-		                &assoc_req->wpa_mcast_key);
-		index++;
+		/* The common header and as many keys as we included */
+		cmd.hdr.size = cpu_to_le16(offsetof(typeof(cmd),
+						    keyParamSet[index]));
 	}
 	}
+	ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd);
+	/* Copy the returned key to driver private data */
+	if (!ret && cmd_action == CMD_ACT_GET) {
+		void *buf_ptr = cmd.keyParamSet;
+		void *resp_end = &(&cmd)[1];
+
+		while (buf_ptr < resp_end) {
+			struct MrvlIEtype_keyParamSet *keyparam = buf_ptr;
+			struct enc_key *key;
+			uint16_t param_set_len = le16_to_cpu(keyparam->length);
+			uint16_t key_len = le16_to_cpu(keyparam->keylen);
+			uint16_t key_flags = le16_to_cpu(keyparam->keyinfo);
+			uint16_t key_type = le16_to_cpu(keyparam->keytypeid);
+			void *end;
+
+			end = (void *)keyparam + sizeof(keyparam->type)
+				+ sizeof(keyparam->length) + param_set_len;
+
+			/* Make sure we don't access past the end of the IEs */
+			if (end > resp_end)
+				break;
 
 
-	cmd->size = cpu_to_le16(  S_DS_GEN
-	                        + sizeof (pkeymaterial->action)
-	                        + (index * sizeof(struct MrvlIEtype_keyParamSet)));
+			if (key_flags & KEY_INFO_WPA_UNICAST)
+				key = &priv->wpa_unicast_key;
+			else if (key_flags & KEY_INFO_WPA_MCAST)
+				key = &priv->wpa_mcast_key;
+			else
+				break;
 
 
-	ret = 0;
+			/* Copy returned key into driver */
+			memset(key, 0, sizeof(struct enc_key));
+			if (key_len > sizeof(key->key))
+				break;
+			key->type = key_type;
+			key->flags = key_flags;
+			key->len = key_len;
+			memcpy(key->key, keyparam->key, key->len);
+
+			buf_ptr = end + 1;
+		}
+	}
 
 
-done:
 	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
 	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
 	return ret;
 	return ret;
 }
 }
@@ -1354,10 +1382,6 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
 		ret = lbs_cmd_802_11_ps_mode(priv, cmdptr, cmd_action);
 		ret = lbs_cmd_802_11_ps_mode(priv, cmdptr, cmd_action);
 		break;
 		break;
 
 
-	case CMD_802_11_SCAN:
-		ret = lbs_cmd_80211_scan(priv, cmdptr, pdata_buf);
-		break;
-
 	case CMD_MAC_CONTROL:
 	case CMD_MAC_CONTROL:
 		ret = lbs_cmd_mac_control(priv, cmdptr);
 		ret = lbs_cmd_mac_control(priv, cmdptr);
 		break;
 		break;
@@ -1435,11 +1459,6 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
 		ret = lbs_cmd_80211_ad_hoc_stop(priv, cmdptr);
 		ret = lbs_cmd_80211_ad_hoc_stop(priv, cmdptr);
 		break;
 		break;
 
 
-	case CMD_802_11_KEY_MATERIAL:
-		ret = lbs_cmd_802_11_key_material(priv, cmdptr, cmd_action,
-				cmd_oid, pdata_buf);
-		break;
-
 	case CMD_802_11_PAIRWISE_TSC:
 	case CMD_802_11_PAIRWISE_TSC:
 		break;
 		break;
 	case CMD_802_11_GROUP_TSC:
 	case CMD_802_11_GROUP_TSC:

+ 2 - 0
drivers/net/wireless/libertas/cmd.h

@@ -57,5 +57,7 @@ int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
 			   struct assoc_request *assoc);
 			   struct assoc_request *assoc);
 int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
 int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
 			      uint16_t *enable);
 			      uint16_t *enable);
+int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
+				struct assoc_request *assoc);
 
 
 #endif /* _LBS_CMD_H */
 #endif /* _LBS_CMD_H */

+ 0 - 63
drivers/net/wireless/libertas/cmdresp.c

@@ -204,61 +204,6 @@ static int lbs_ret_802_11_snmp_mib(struct lbs_private *priv,
 	return 0;
 	return 0;
 }
 }
 
 
-static int lbs_ret_802_11_key_material(struct lbs_private *priv,
-					struct cmd_ds_command *resp)
-{
-	struct cmd_ds_802_11_key_material *pkeymaterial =
-	    &resp->params.keymaterial;
-	u16 action = le16_to_cpu(pkeymaterial->action);
-
-	lbs_deb_enter(LBS_DEB_CMD);
-
-	/* Copy the returned key to driver private data */
-	if (action == CMD_ACT_GET) {
-		u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet;
-		u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size));
-
-		while (buf_ptr < resp_end) {
-			struct MrvlIEtype_keyParamSet * pkeyparamset =
-			    (struct MrvlIEtype_keyParamSet *) buf_ptr;
-			struct enc_key * pkey;
-			u16 param_set_len = le16_to_cpu(pkeyparamset->length);
-			u16 key_len = le16_to_cpu(pkeyparamset->keylen);
-			u16 key_flags = le16_to_cpu(pkeyparamset->keyinfo);
-			u16 key_type = le16_to_cpu(pkeyparamset->keytypeid);
-			u8 * end;
-
-			end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type)
-			                          + sizeof (pkeyparamset->length)
-			                          + param_set_len;
-			/* Make sure we don't access past the end of the IEs */
-			if (end > resp_end)
-				break;
-
-			if (key_flags & KEY_INFO_WPA_UNICAST)
-				pkey = &priv->wpa_unicast_key;
-			else if (key_flags & KEY_INFO_WPA_MCAST)
-				pkey = &priv->wpa_mcast_key;
-			else
-				break;
-
-			/* Copy returned key into driver */
-			memset(pkey, 0, sizeof(struct enc_key));
-			if (key_len > sizeof(pkey->key))
-				break;
-			pkey->type = key_type;
-			pkey->flags = key_flags;
-			pkey->len = key_len;
-			memcpy(pkey->key, pkeyparamset->key, pkey->len);
-
-			buf_ptr = end + 1;
-		}
-	}
-
-	lbs_deb_enter(LBS_DEB_CMD);
-	return 0;
-}
-
 static int lbs_ret_802_11_mac_address(struct lbs_private *priv,
 static int lbs_ret_802_11_mac_address(struct lbs_private *priv,
 				       struct cmd_ds_command *resp)
 				       struct cmd_ds_command *resp)
 {
 {
@@ -407,10 +352,6 @@ static inline int handle_cmd_response(struct lbs_private *priv,
 		ret = lbs_ret_reg_access(priv, respcmd, resp);
 		ret = lbs_ret_reg_access(priv, respcmd, resp);
 		break;
 		break;
 
 
-	case CMD_RET(CMD_802_11_SCAN):
-		ret = lbs_ret_80211_scan(priv, resp);
-		break;
-
 	case CMD_RET(CMD_802_11_GET_LOG):
 	case CMD_RET(CMD_802_11_GET_LOG):
 		ret = lbs_ret_get_log(priv, resp);
 		ret = lbs_ret_get_log(priv, resp);
 		break;
 		break;
@@ -475,10 +416,6 @@ static inline int handle_cmd_response(struct lbs_private *priv,
 		ret = lbs_ret_80211_ad_hoc_stop(priv, resp);
 		ret = lbs_ret_80211_ad_hoc_stop(priv, resp);
 		break;
 		break;
 
 
-	case CMD_RET(CMD_802_11_KEY_MATERIAL):
-		ret = lbs_ret_802_11_key_material(priv, resp);
-		break;
-
 	case CMD_RET(CMD_802_11_EEPROM_ACCESS):
 	case CMD_RET(CMD_802_11_EEPROM_ACCESS):
 		ret = lbs_ret_802_11_eeprom_access(priv, resp);
 		ret = lbs_ret_802_11_eeprom_access(priv, resp);
 		break;
 		break;

+ 11 - 8
drivers/net/wireless/libertas/hostcmd.h

@@ -174,9 +174,11 @@ struct cmd_ds_802_11_subscribe_event {
  * Define data structure for CMD_802_11_SCAN
  * Define data structure for CMD_802_11_SCAN
  */
  */
 struct cmd_ds_802_11_scan {
 struct cmd_ds_802_11_scan {
-	u8 bsstype;
-	u8 bssid[ETH_ALEN];
-	u8 tlvbuffer[1];
+	struct cmd_header hdr;
+
+	uint8_t bsstype;
+	uint8_t bssid[ETH_ALEN];
+	uint8_t tlvbuffer[0];
 #if 0
 #if 0
 	mrvlietypes_ssidparamset_t ssidParamSet;
 	mrvlietypes_ssidparamset_t ssidParamSet;
 	mrvlietypes_chanlistparamset_t ChanListParamSet;
 	mrvlietypes_chanlistparamset_t ChanListParamSet;
@@ -185,9 +187,11 @@ struct cmd_ds_802_11_scan {
 };
 };
 
 
 struct cmd_ds_802_11_scan_rsp {
 struct cmd_ds_802_11_scan_rsp {
+	struct cmd_header hdr;
+
 	__le16 bssdescriptsize;
 	__le16 bssdescriptsize;
-	u8 nr_sets;
-	u8 bssdesc_and_tlvbuffer[1];
+	uint8_t nr_sets;
+	uint8_t bssdesc_and_tlvbuffer[0];
 };
 };
 
 
 struct cmd_ds_802_11_get_log {
 struct cmd_ds_802_11_get_log {
@@ -572,6 +576,8 @@ struct cmd_ds_host_sleep {
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
 struct cmd_ds_802_11_key_material {
 struct cmd_ds_802_11_key_material {
+	struct cmd_header hdr;
+
 	__le16 action;
 	__le16 action;
 	struct MrvlIEtype_keyParamSet keyParamSet[2];
 	struct MrvlIEtype_keyParamSet keyParamSet[2];
 } __attribute__ ((packed));
 } __attribute__ ((packed));
@@ -689,8 +695,6 @@ struct cmd_ds_command {
 	/* command Body */
 	/* command Body */
 	union {
 	union {
 		struct cmd_ds_802_11_ps_mode psmode;
 		struct cmd_ds_802_11_ps_mode psmode;
-		struct cmd_ds_802_11_scan scan;
-		struct cmd_ds_802_11_scan_rsp scanresp;
 		struct cmd_ds_mac_control macctrl;
 		struct cmd_ds_mac_control macctrl;
 		struct cmd_ds_802_11_associate associate;
 		struct cmd_ds_802_11_associate associate;
 		struct cmd_ds_802_11_deauthenticate deauth;
 		struct cmd_ds_802_11_deauthenticate deauth;
@@ -712,7 +716,6 @@ struct cmd_ds_command {
 		struct cmd_ds_802_11_rssi_rsp rssirsp;
 		struct cmd_ds_802_11_rssi_rsp rssirsp;
 		struct cmd_ds_802_11_disassociate dassociate;
 		struct cmd_ds_802_11_disassociate dassociate;
 		struct cmd_ds_802_11_mac_address macadd;
 		struct cmd_ds_802_11_mac_address macadd;
-		struct cmd_ds_802_11_key_material keymaterial;
 		struct cmd_ds_mac_reg_access macreg;
 		struct cmd_ds_mac_reg_access macreg;
 		struct cmd_ds_bbp_reg_access bbpreg;
 		struct cmd_ds_bbp_reg_access bbpreg;
 		struct cmd_ds_rf_reg_access rfreg;
 		struct cmd_ds_rf_reg_access rfreg;

文件差异内容过多而无法显示
+ 221 - 313
drivers/net/wireless/libertas/scan.c


+ 3 - 51
drivers/net/wireless/libertas/scan.h

@@ -17,56 +17,15 @@
  */
  */
 #define LBS_IOCTL_USER_SCAN_CHAN_MAX  50
 #define LBS_IOCTL_USER_SCAN_CHAN_MAX  50
 
 
-//! Infrastructure BSS scan type in lbs_scan_cmd_config
+//! Infrastructure BSS scan type in cmd_ds_802_11_scan
 #define LBS_SCAN_BSS_TYPE_BSS         1
 #define LBS_SCAN_BSS_TYPE_BSS         1
 
 
-//! Adhoc BSS scan type in lbs_scan_cmd_config
+//! Adhoc BSS scan type in cmd_ds_802_11_scan
 #define LBS_SCAN_BSS_TYPE_IBSS        2
 #define LBS_SCAN_BSS_TYPE_IBSS        2
 
 
-//! Adhoc or Infrastructure BSS scan type in lbs_scan_cmd_config, no filter
+//! Adhoc or Infrastructure BSS scan type in cmd_ds_802_11_scan, no filter
 #define LBS_SCAN_BSS_TYPE_ANY         3
 #define LBS_SCAN_BSS_TYPE_ANY         3
 
 
-/**
- * @brief Structure used internally in the wlan driver to configure a scan.
- *
- * Sent to the command processing module to configure the firmware
- *   scan command prepared by lbs_cmd_80211_scan.
- *
- * @sa lbs_scan_networks
- *
- */
-struct lbs_scan_cmd_config {
-    /**
-     *  @brief BSS type to be sent in the firmware command
-     *
-     *  Field can be used to restrict the types of networks returned in the
-     *    scan.  valid settings are:
-     *
-     *   - LBS_SCAN_BSS_TYPE_BSS  (infrastructure)
-     *   - LBS_SCAN_BSS_TYPE_IBSS (adhoc)
-     *   - LBS_SCAN_BSS_TYPE_ANY  (unrestricted, adhoc and infrastructure)
-     */
-	u8 bsstype;
-
-    /**
-     *  @brief Specific BSSID used to filter scan results in the firmware
-     */
-	u8 bssid[ETH_ALEN];
-
-    /**
-     *  @brief length of TLVs sent in command starting at tlvBuffer
-     */
-	int tlvbufferlen;
-
-    /**
-     *  @brief SSID TLV(s) and ChanList TLVs to be sent in the firmware command
-     *
-     *  @sa TLV_TYPE_CHANLIST, mrvlietypes_chanlistparamset_t
-     *  @sa TLV_TYPE_SSID, mrvlietypes_ssidparamset_t
-     */
-	u8 tlvbuffer[1];	//!< SSID TLV(s) and ChanList TLVs are stored here
-};
-
 /**
 /**
  *  @brief IOCTL channel sub-structure sent in lbs_ioctl_user_scan_cfg
  *  @brief IOCTL channel sub-structure sent in lbs_ioctl_user_scan_cfg
  *
  *
@@ -179,13 +138,6 @@ int lbs_find_best_network_ssid(struct lbs_private *priv, u8 *out_ssid,
 int lbs_send_specific_ssid_scan(struct lbs_private *priv, u8 *ssid,
 int lbs_send_specific_ssid_scan(struct lbs_private *priv, u8 *ssid,
 				u8 ssid_len, u8 clear_ssid);
 				u8 ssid_len, u8 clear_ssid);
 
 
-int lbs_cmd_80211_scan(struct lbs_private *priv,
-				struct cmd_ds_command *cmd,
-				void *pdata_buf);
-
-int lbs_ret_80211_scan(struct lbs_private *priv,
-				struct cmd_ds_command *resp);
-
 int lbs_scan_networks(struct lbs_private *priv,
 int lbs_scan_networks(struct lbs_private *priv,
 	const struct lbs_ioctl_user_scan_cfg *puserscanin,
 	const struct lbs_ioctl_user_scan_cfg *puserscanin,
                 int full_scan);
                 int full_scan);

+ 13 - 0
drivers/net/wireless/libertas/types.h

@@ -239,4 +239,17 @@ struct mrvlietypes_ledgpio {
 	struct led_pin ledpin[1];
 	struct led_pin ledpin[1];
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
+struct led_bhv {
+	uint8_t	firmwarestate;
+	uint8_t	led;
+	uint8_t	ledstate;
+	uint8_t	ledarg;
+} __attribute__ ((packed));
+
+
+struct mrvlietypes_ledbhv {
+	struct mrvlietypesheader header;
+	struct led_bhv ledbhv[1];
+} __attribute__ ((packed));
+
 #endif
 #endif

+ 1 - 0
drivers/net/wireless/zd1211rw/zd_chip.c

@@ -809,6 +809,7 @@ static int hw_init_hmac(struct zd_chip *chip)
 		{ CR_AFTER_PNP,			0x1 },
 		{ CR_AFTER_PNP,			0x1 },
 		{ CR_WEP_PROTECT,		0x114 },
 		{ CR_WEP_PROTECT,		0x114 },
 		{ CR_IFS_VALUE,			IFS_VALUE_DEFAULT },
 		{ CR_IFS_VALUE,			IFS_VALUE_DEFAULT },
+		{ CR_CAM_MODE,			MODE_AP_WDS},
 	};
 	};
 
 
 	ZD_ASSERT(mutex_is_locked(&chip->mutex));
 	ZD_ASSERT(mutex_is_locked(&chip->mutex));

+ 8 - 0
drivers/net/wireless/zd1211rw/zd_chip.h

@@ -489,6 +489,7 @@ enum {
 
 
 #define CR_RX_OFFSET			CTL_REG(0x065c)
 #define CR_RX_OFFSET			CTL_REG(0x065c)
 
 
+#define CR_BCN_LENGTH			CTL_REG(0x0664)
 #define CR_PHY_DELAY			CTL_REG(0x066C)
 #define CR_PHY_DELAY			CTL_REG(0x066C)
 #define CR_BCN_FIFO			CTL_REG(0x0670)
 #define CR_BCN_FIFO			CTL_REG(0x0670)
 #define CR_SNIFFER_ON			CTL_REG(0x0674)
 #define CR_SNIFFER_ON			CTL_REG(0x0674)
@@ -545,6 +546,8 @@ enum {
 #define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \
 #define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \
 	RX_FILTER_CFEND | RX_FILTER_CFACK)
 	RX_FILTER_CFEND | RX_FILTER_CFACK)
 
 
+#define BCN_MODE_IBSS			0x2000000
+
 /* Monitor mode sets filter to 0xfffff */
 /* Monitor mode sets filter to 0xfffff */
 
 
 #define CR_ACK_TIMEOUT_EXT		CTL_REG(0x0690)
 #define CR_ACK_TIMEOUT_EXT		CTL_REG(0x0690)
@@ -578,6 +581,11 @@ enum {
 
 
 /* CAM: Continuous Access Mode (power management) */
 /* CAM: Continuous Access Mode (power management) */
 #define CR_CAM_MODE			CTL_REG(0x0700)
 #define CR_CAM_MODE			CTL_REG(0x0700)
+#define MODE_IBSS			0x0
+#define MODE_AP				0x1
+#define MODE_STA			0x2
+#define MODE_AP_WDS			0x3
+
 #define CR_CAM_ROLL_TB_LOW		CTL_REG(0x0704)
 #define CR_CAM_ROLL_TB_LOW		CTL_REG(0x0704)
 #define CR_CAM_ROLL_TB_HIGH		CTL_REG(0x0708)
 #define CR_CAM_ROLL_TB_HIGH		CTL_REG(0x0708)
 #define CR_CAM_ADDRESS			CTL_REG(0x070C)
 #define CR_CAM_ADDRESS			CTL_REG(0x070C)

+ 73 - 2
drivers/net/wireless/zd1211rw/zd_mac.c

@@ -475,6 +475,46 @@ static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
 	/* FIXME: Management frame? */
 	/* FIXME: Management frame? */
 }
 }
 
 
+void zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon)
+{
+	struct zd_mac *mac = zd_hw_mac(hw);
+	u32 tmp, j = 0;
+	/* 4 more bytes for tail CRC */
+	u32 full_len = beacon->len + 4;
+	zd_iowrite32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, 0);
+	zd_ioread32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, &tmp);
+	while (tmp & 0x2) {
+		zd_ioread32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, &tmp);
+		if ((++j % 100) == 0) {
+			printk(KERN_ERR "CR_BCN_FIFO_SEMAPHORE not ready\n");
+			if (j >= 500)  {
+				printk(KERN_ERR "Giving up beacon config.\n");
+				return;
+			}
+		}
+		msleep(1);
+	}
+
+	zd_iowrite32(&mac->chip, CR_BCN_FIFO, full_len - 1);
+	if (zd_chip_is_zd1211b(&mac->chip))
+		zd_iowrite32(&mac->chip, CR_BCN_LENGTH, full_len - 1);
+
+	for (j = 0 ; j < beacon->len; j++)
+		zd_iowrite32(&mac->chip, CR_BCN_FIFO,
+				*((u8 *)(beacon->data + j)));
+
+	for (j = 0; j < 4; j++)
+		zd_iowrite32(&mac->chip, CR_BCN_FIFO, 0x0);
+
+	zd_iowrite32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, 1);
+	/* 802.11b/g 2.4G CCK 1Mb
+	 * 802.11a, not yet implemented, uses different values (see GPL vendor
+	 * driver)
+	 */
+	zd_iowrite32(&mac->chip, CR_BCN_PLCP_CFG, 0x00000400 |
+			(full_len << 19));
+}
+
 static int fill_ctrlset(struct zd_mac *mac,
 static int fill_ctrlset(struct zd_mac *mac,
 			struct sk_buff *skb,
 			struct sk_buff *skb,
 			struct ieee80211_tx_control *control)
 			struct ieee80211_tx_control *control)
@@ -709,6 +749,7 @@ static int zd_op_add_interface(struct ieee80211_hw *hw,
 
 
 	switch (conf->type) {
 	switch (conf->type) {
 	case IEEE80211_IF_TYPE_MNTR:
 	case IEEE80211_IF_TYPE_MNTR:
+	case IEEE80211_IF_TYPE_MESH_POINT:
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_STA:
 		mac->type = conf->type;
 		mac->type = conf->type;
 		break;
 		break;
@@ -738,15 +779,43 @@ static int zd_op_config_interface(struct ieee80211_hw *hw,
 				   struct ieee80211_if_conf *conf)
 				   struct ieee80211_if_conf *conf)
 {
 {
 	struct zd_mac *mac = zd_hw_mac(hw);
 	struct zd_mac *mac = zd_hw_mac(hw);
+	int associated;
+
+	if (mac->type == IEEE80211_IF_TYPE_MESH_POINT) {
+		associated = true;
+		if (conf->beacon) {
+			zd_mac_config_beacon(hw, conf->beacon);
+			kfree_skb(conf->beacon);
+			zd_set_beacon_interval(&mac->chip, BCN_MODE_IBSS |
+					hw->conf.beacon_int);
+		}
+	} else
+		associated = is_valid_ether_addr(conf->bssid);
 
 
 	spin_lock_irq(&mac->lock);
 	spin_lock_irq(&mac->lock);
-	mac->associated = is_valid_ether_addr(conf->bssid);
+	mac->associated = associated;
 	spin_unlock_irq(&mac->lock);
 	spin_unlock_irq(&mac->lock);
 
 
 	/* TODO: do hardware bssid filtering */
 	/* TODO: do hardware bssid filtering */
 	return 0;
 	return 0;
 }
 }
 
 
+void zd_process_intr(struct work_struct *work)
+{
+	u16 int_status;
+	struct zd_mac *mac = container_of(work, struct zd_mac, process_intr);
+
+	int_status = le16_to_cpu(*(u16 *)(mac->intr_buffer+4));
+	if (int_status & INT_CFG_NEXT_BCN) {
+		if (net_ratelimit())
+			dev_dbg_f(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");
+	} else
+		dev_dbg_f(zd_mac_dev(mac), "Unsupported interrupt\n");
+
+	zd_chip_enable_hwint(&mac->chip);
+}
+
+
 static void set_multicast_hash_handler(struct work_struct *work)
 static void set_multicast_hash_handler(struct work_struct *work)
 {
 {
 	struct zd_mac *mac =
 	struct zd_mac *mac =
@@ -912,7 +981,8 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
 
 
 	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;
 	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;
 
 
-	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS;
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+		    IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE;
 	hw->max_rssi = 100;
 	hw->max_rssi = 100;
 	hw->max_signal = 100;
 	hw->max_signal = 100;
 
 
@@ -926,6 +996,7 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
 	INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler);
 	INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler);
 	INIT_WORK(&mac->set_rts_cts_work, set_rts_cts_work);
 	INIT_WORK(&mac->set_rts_cts_work, set_rts_cts_work);
 	INIT_WORK(&mac->set_rx_filter_work, set_rx_filter_handler);
 	INIT_WORK(&mac->set_rx_filter_work, set_rx_filter_handler);
+	INIT_WORK(&mac->process_intr, zd_process_intr);
 
 
 	SET_IEEE80211_DEV(hw, &intf->dev);
 	SET_IEEE80211_DEV(hw, &intf->dev);
 	return hw;
 	return hw;

+ 3 - 0
drivers/net/wireless/zd1211rw/zd_mac.h

@@ -172,12 +172,15 @@ struct zd_tx_skb_control_block {
 struct zd_mac {
 struct zd_mac {
 	struct zd_chip chip;
 	struct zd_chip chip;
 	spinlock_t lock;
 	spinlock_t lock;
+	spinlock_t intr_lock;
 	struct ieee80211_hw *hw;
 	struct ieee80211_hw *hw;
 	struct housekeeping housekeeping;
 	struct housekeeping housekeeping;
 	struct work_struct set_multicast_hash_work;
 	struct work_struct set_multicast_hash_work;
 	struct work_struct set_rts_cts_work;
 	struct work_struct set_rts_cts_work;
 	struct work_struct set_rx_filter_work;
 	struct work_struct set_rx_filter_work;
+	struct work_struct process_intr;
 	struct zd_mc_hash multicast_hash;
 	struct zd_mc_hash multicast_hash;
+	u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
 	u8 regdomain;
 	u8 regdomain;
 	u8 default_regdomain;
 	u8 default_regdomain;
 	int type;
 	int type;

+ 9 - 2
drivers/net/wireless/zd1211rw/zd_usb.c

@@ -97,6 +97,7 @@ MODULE_DEVICE_TABLE(usb, usb_ids);
 #define FW_ZD1211B_PREFIX	"zd1211/zd1211b_"
 #define FW_ZD1211B_PREFIX	"zd1211/zd1211b_"
 
 
 /* USB device initialization */
 /* USB device initialization */
+static void int_urb_complete(struct urb *urb);
 
 
 static int request_fw_file(
 static int request_fw_file(
 	const struct firmware **fw, const char *name, struct device *device)
 	const struct firmware **fw, const char *name, struct device *device)
@@ -336,11 +337,18 @@ static inline void handle_regs_int(struct urb *urb)
 	struct zd_usb *usb = urb->context;
 	struct zd_usb *usb = urb->context;
 	struct zd_usb_interrupt *intr = &usb->intr;
 	struct zd_usb_interrupt *intr = &usb->intr;
 	int len;
 	int len;
+	u16 int_num;
 
 
 	ZD_ASSERT(in_interrupt());
 	ZD_ASSERT(in_interrupt());
 	spin_lock(&intr->lock);
 	spin_lock(&intr->lock);
 
 
-	if (intr->read_regs_enabled) {
+	int_num = le16_to_cpu(*(u16 *)(urb->transfer_buffer+2));
+	if (int_num == CR_INTERRUPT) {
+		struct zd_mac *mac = zd_hw_mac(zd_usb_to_hw(urb->context));
+		memcpy(&mac->intr_buffer, urb->transfer_buffer,
+				USB_MAX_EP_INT_BUFFER);
+		schedule_work(&mac->process_intr);
+	} else if (intr->read_regs_enabled) {
 		intr->read_regs.length = len = urb->actual_length;
 		intr->read_regs.length = len = urb->actual_length;
 
 
 		if (len > sizeof(intr->read_regs.buffer))
 		if (len > sizeof(intr->read_regs.buffer))
@@ -351,7 +359,6 @@ static inline void handle_regs_int(struct urb *urb)
 		goto out;
 		goto out;
 	}
 	}
 
 
-	dev_dbg_f(urb_dev(urb), "regs interrupt ignored\n");
 out:
 out:
 	spin_unlock(&intr->lock);
 	spin_unlock(&intr->lock);
 }
 }

+ 9 - 0
drivers/ssb/Kconfig

@@ -125,4 +125,13 @@ config SSB_DRIVER_EXTIF
 
 
 	  If unsure, say N
 	  If unsure, say N
 
 
+config SSB_DRIVER_GIGE
+	bool "SSB Broadcom Gigabit Ethernet driver"
+	depends on SSB_PCIHOST_POSSIBLE && SSB_EMBEDDED && MIPS
+	help
+	  Driver for the Sonics Silicon Backplane attached
+	  Broadcom Gigabit Ethernet.
+
+	  If unsure, say N
+
 endmenu
 endmenu

+ 1 - 0
drivers/ssb/Makefile

@@ -11,6 +11,7 @@ ssb-y					+= driver_chipcommon.o
 ssb-$(CONFIG_SSB_DRIVER_MIPS)		+= driver_mipscore.o
 ssb-$(CONFIG_SSB_DRIVER_MIPS)		+= driver_mipscore.o
 ssb-$(CONFIG_SSB_DRIVER_EXTIF)		+= driver_extif.o
 ssb-$(CONFIG_SSB_DRIVER_EXTIF)		+= driver_extif.o
 ssb-$(CONFIG_SSB_DRIVER_PCICORE)	+= driver_pcicore.o
 ssb-$(CONFIG_SSB_DRIVER_PCICORE)	+= driver_pcicore.o
+ssb-$(CONFIG_SSB_DRIVER_GIGE)		+= driver_gige.o
 
 
 # b43 pci-ssb-bridge driver
 # b43 pci-ssb-bridge driver
 # Not strictly a part of SSB, but kept here for convenience
 # Not strictly a part of SSB, but kept here for convenience

+ 294 - 0
drivers/ssb/driver_gige.c

@@ -0,0 +1,294 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom Gigabit Ethernet core driver
+ *
+ * Copyright 2008, Broadcom Corporation
+ * Copyright 2008, Michael Buesch <mb@bu3sch.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/ssb/ssb_driver_gige.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+
+
+/*
+MODULE_DESCRIPTION("SSB Broadcom Gigabit Ethernet driver");
+MODULE_AUTHOR("Michael Buesch");
+MODULE_LICENSE("GPL");
+*/
+
+static const struct ssb_device_id ssb_gige_tbl[] = {
+	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET_GBIT, SSB_ANY_REV),
+	SSB_DEVTABLE_END
+};
+/* MODULE_DEVICE_TABLE(ssb, ssb_gige_tbl); */
+
+
+static inline u8 gige_read8(struct ssb_gige *dev, u16 offset)
+{
+	return ssb_read8(dev->dev, offset);
+}
+
+static inline u16 gige_read16(struct ssb_gige *dev, u16 offset)
+{
+	return ssb_read16(dev->dev, offset);
+}
+
+static inline u32 gige_read32(struct ssb_gige *dev, u16 offset)
+{
+	return ssb_read32(dev->dev, offset);
+}
+
+static inline void gige_write8(struct ssb_gige *dev,
+			       u16 offset, u8 value)
+{
+	ssb_write8(dev->dev, offset, value);
+}
+
+static inline void gige_write16(struct ssb_gige *dev,
+				u16 offset, u16 value)
+{
+	ssb_write16(dev->dev, offset, value);
+}
+
+static inline void gige_write32(struct ssb_gige *dev,
+				u16 offset, u32 value)
+{
+	ssb_write32(dev->dev, offset, value);
+}
+
+static inline
+u8 gige_pcicfg_read8(struct ssb_gige *dev, unsigned int offset)
+{
+	BUG_ON(offset >= 256);
+	return gige_read8(dev, SSB_GIGE_PCICFG + offset);
+}
+
+static inline
+u16 gige_pcicfg_read16(struct ssb_gige *dev, unsigned int offset)
+{
+	BUG_ON(offset >= 256);
+	return gige_read16(dev, SSB_GIGE_PCICFG + offset);
+}
+
+static inline
+u32 gige_pcicfg_read32(struct ssb_gige *dev, unsigned int offset)
+{
+	BUG_ON(offset >= 256);
+	return gige_read32(dev, SSB_GIGE_PCICFG + offset);
+}
+
+static inline
+void gige_pcicfg_write8(struct ssb_gige *dev,
+			unsigned int offset, u8 value)
+{
+	BUG_ON(offset >= 256);
+	gige_write8(dev, SSB_GIGE_PCICFG + offset, value);
+}
+
+static inline
+void gige_pcicfg_write16(struct ssb_gige *dev,
+			 unsigned int offset, u16 value)
+{
+	BUG_ON(offset >= 256);
+	gige_write16(dev, SSB_GIGE_PCICFG + offset, value);
+}
+
+static inline
+void gige_pcicfg_write32(struct ssb_gige *dev,
+			 unsigned int offset, u32 value)
+{
+	BUG_ON(offset >= 256);
+	gige_write32(dev, SSB_GIGE_PCICFG + offset, value);
+}
+
+static int ssb_gige_pci_read_config(struct pci_bus *bus, unsigned int devfn,
+				    int reg, int size, u32 *val)
+{
+	struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops);
+	unsigned long flags;
+
+	if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if (reg >= 256)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	switch (size) {
+	case 1:
+		*val = gige_pcicfg_read8(dev, reg);
+		break;
+	case 2:
+		*val = gige_pcicfg_read16(dev, reg);
+		break;
+	case 4:
+		*val = gige_pcicfg_read32(dev, reg);
+		break;
+	default:
+		WARN_ON(1);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int ssb_gige_pci_write_config(struct pci_bus *bus, unsigned int devfn,
+				     int reg, int size, u32 val)
+{
+	struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops);
+	unsigned long flags;
+
+	if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if (reg >= 256)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	switch (size) {
+	case 1:
+		gige_pcicfg_write8(dev, reg, val);
+		break;
+	case 2:
+		gige_pcicfg_write16(dev, reg, val);
+		break;
+	case 4:
+		gige_pcicfg_write32(dev, reg, val);
+		break;
+	default:
+		WARN_ON(1);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int ssb_gige_probe(struct ssb_device *sdev, const struct ssb_device_id *id)
+{
+	struct ssb_gige *dev;
+	u32 base, tmslow, tmshigh;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	dev->dev = sdev;
+
+	spin_lock_init(&dev->lock);
+	dev->pci_controller.pci_ops = &dev->pci_ops;
+	dev->pci_controller.io_resource = &dev->io_resource;
+	dev->pci_controller.mem_resource = &dev->mem_resource;
+	dev->pci_controller.io_map_base = 0x800;
+	dev->pci_ops.read = ssb_gige_pci_read_config;
+	dev->pci_ops.write = ssb_gige_pci_write_config;
+
+	dev->io_resource.name = SSB_GIGE_IO_RES_NAME;
+	dev->io_resource.start = 0x800;
+	dev->io_resource.end = 0x8FF;
+	dev->io_resource.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED;
+
+	if (!ssb_device_is_enabled(sdev))
+		ssb_device_enable(sdev, 0);
+
+	/* Setup BAR0. This is a 64k MMIO region. */
+	base = ssb_admatch_base(ssb_read32(sdev, SSB_ADMATCH1));
+	gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_0, base);
+	gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_1, 0);
+
+	dev->mem_resource.name = SSB_GIGE_MEM_RES_NAME;
+	dev->mem_resource.start = base;
+	dev->mem_resource.end = base + 0x10000 - 1;
+	dev->mem_resource.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED;
+
+	/* Enable the memory region. */
+	gige_pcicfg_write16(dev, PCI_COMMAND,
+			    gige_pcicfg_read16(dev, PCI_COMMAND)
+			    | PCI_COMMAND_MEMORY);
+
+	/* Write flushing is controlled by the Flush Status Control register.
+	 * We want to flush every register write with a timeout and we want
+	 * to disable the IRQ mask while flushing to avoid concurrency.
+	 * Note that automatic write flushing does _not_ work from
+	 * an IRQ handler. The driver must flush manually by reading a register.
+	 */
+	gige_write32(dev, SSB_GIGE_SHIM_FLUSHSTAT, 0x00000068);
+
+	/* Check if we have an RGMII or GMII PHY-bus.
+	 * On RGMII do not bypass the DLLs */
+	tmslow = ssb_read32(sdev, SSB_TMSLOW);
+	tmshigh = ssb_read32(sdev, SSB_TMSHIGH);
+	if (tmshigh & SSB_GIGE_TMSHIGH_RGMII) {
+		tmslow &= ~SSB_GIGE_TMSLOW_TXBYPASS;
+		tmslow &= ~SSB_GIGE_TMSLOW_RXBYPASS;
+		dev->has_rgmii = 1;
+	} else {
+		tmslow |= SSB_GIGE_TMSLOW_TXBYPASS;
+		tmslow |= SSB_GIGE_TMSLOW_RXBYPASS;
+		dev->has_rgmii = 0;
+	}
+	tmslow |= SSB_GIGE_TMSLOW_DLLEN;
+	ssb_write32(sdev, SSB_TMSLOW, tmslow);
+
+	ssb_set_drvdata(sdev, dev);
+	register_pci_controller(&dev->pci_controller);
+
+	return 0;
+}
+
+bool pdev_is_ssb_gige_core(struct pci_dev *pdev)
+{
+	if (!pdev->resource[0].name)
+		return 0;
+	return (strcmp(pdev->resource[0].name, SSB_GIGE_MEM_RES_NAME) == 0);
+}
+EXPORT_SYMBOL(pdev_is_ssb_gige_core);
+
+int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
+				   struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = ssb_get_drvdata(sdev);
+	struct resource *res;
+
+	if (pdev->bus->ops != &dev->pci_ops) {
+		/* The PCI device is not on this SSB GigE bridge device. */
+		return -ENODEV;
+	}
+
+	/* Fixup the PCI resources. */
+	res = &(pdev->resource[0]);
+	res->flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED;
+	res->name = dev->mem_resource.name;
+	res->start = dev->mem_resource.start;
+	res->end = dev->mem_resource.end;
+
+	/* Fixup interrupt lines. */
+	pdev->irq = ssb_mips_irq(sdev) + 2;
+	pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, pdev->irq);
+
+	return 0;
+}
+
+int ssb_gige_map_irq(struct ssb_device *sdev,
+		     const struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = ssb_get_drvdata(sdev);
+
+	if (pdev->bus->ops != &dev->pci_ops) {
+		/* The PCI device is not on this SSB GigE bridge device. */
+		return -ENODEV;
+	}
+
+	return ssb_mips_irq(sdev) + 2;
+}
+
+static struct ssb_driver ssb_gige_driver = {
+	.name		= "BCM-GigE",
+	.id_table	= ssb_gige_tbl,
+	.probe		= ssb_gige_probe,
+};
+
+int ssb_gige_init(void)
+{
+	return ssb_driver_register(&ssb_gige_driver);
+}

+ 1 - 0
drivers/ssb/driver_mipscore.c

@@ -209,6 +209,7 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
 			/* fallthrough */
 			/* fallthrough */
 		case SSB_DEV_PCI:
 		case SSB_DEV_PCI:
 		case SSB_DEV_ETHERNET:
 		case SSB_DEV_ETHERNET:
+		case SSB_DEV_ETHERNET_GBIT:
 		case SSB_DEV_80211:
 		case SSB_DEV_80211:
 		case SSB_DEV_USB20_HOST:
 		case SSB_DEV_USB20_HOST:
 			/* These devices get their own IRQ line if available, the rest goes on IRQ0 */
 			/* These devices get their own IRQ line if available, the rest goes on IRQ0 */

+ 89 - 71
drivers/ssb/driver_pcicore.c

@@ -60,77 +60,6 @@ static DEFINE_SPINLOCK(cfgspace_lock);
 /* Core to access the external PCI config space. Can only have one. */
 /* Core to access the external PCI config space. Can only have one. */
 static struct ssb_pcicore *extpci_core;
 static struct ssb_pcicore *extpci_core;
 
 
-static u32 ssb_pcicore_pcibus_iobase = 0x100;
-static u32 ssb_pcicore_pcibus_membase = SSB_PCI_DMA;
-
-int pcibios_plat_dev_init(struct pci_dev *d)
-{
-	struct resource *res;
-	int pos, size;
-	u32 *base;
-
-	ssb_printk(KERN_INFO "PCI: Fixing up device %s\n",
-		   pci_name(d));
-
-	/* Fix up resource bases */
-	for (pos = 0; pos < 6; pos++) {
-		res = &d->resource[pos];
-		if (res->flags & IORESOURCE_IO)
-			base = &ssb_pcicore_pcibus_iobase;
-		else
-			base = &ssb_pcicore_pcibus_membase;
-		res->flags |= IORESOURCE_PCI_FIXED;
-		if (res->end) {
-			size = res->end - res->start + 1;
-			if (*base & (size - 1))
-				*base = (*base + size) & ~(size - 1);
-			res->start = *base;
-			res->end = res->start + size - 1;
-			*base += size;
-			pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start);
-		}
-		/* Fix up PCI bridge BAR0 only */
-		if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0)
-			break;
-	}
-	/* Fix up interrupt lines */
-	d->irq = ssb_mips_irq(extpci_core->dev) + 2;
-	pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);
-
-	return 0;
-}
-
-static void __init ssb_fixup_pcibridge(struct pci_dev *dev)
-{
-	u8 lat;
-
-	if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
-		return;
-
-	ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev));
-
-	/* Enable PCI bridge bus mastering and memory space */
-	pci_set_master(dev);
-	if (pcibios_enable_device(dev, ~0) < 0) {
-		ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n");
-		return;
-	}
-
-	/* Enable PCI bridge BAR1 prefetch and burst */
-	pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3);
-
-	/* Make sure our latency is high enough to handle the devices behind us */
-	lat = 168;
-	ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n",
-		   pci_name(dev), lat);
-	pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
-}
-DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_fixup_pcibridge);
-
-int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
-{
-	return ssb_mips_irq(extpci_core->dev) + 2;
-}
 
 
 static u32 get_cfgspace_addr(struct ssb_pcicore *pc,
 static u32 get_cfgspace_addr(struct ssb_pcicore *pc,
 			     unsigned int bus, unsigned int dev,
 			     unsigned int bus, unsigned int dev,
@@ -320,6 +249,95 @@ static struct pci_controller ssb_pcicore_controller = {
 	.mem_offset	= 0x24000000,
 	.mem_offset	= 0x24000000,
 };
 };
 
 
+static u32 ssb_pcicore_pcibus_iobase = 0x100;
+static u32 ssb_pcicore_pcibus_membase = SSB_PCI_DMA;
+
+/* This function is called when doing a pci_enable_device().
+ * We must first check if the device is a device on the PCI-core bridge. */
+int ssb_pcicore_plat_dev_init(struct pci_dev *d)
+{
+	struct resource *res;
+	int pos, size;
+	u32 *base;
+
+	if (d->bus->ops != &ssb_pcicore_pciops) {
+		/* This is not a device on the PCI-core bridge. */
+		return -ENODEV;
+	}
+
+	ssb_printk(KERN_INFO "PCI: Fixing up device %s\n",
+		   pci_name(d));
+
+	/* Fix up resource bases */
+	for (pos = 0; pos < 6; pos++) {
+		res = &d->resource[pos];
+		if (res->flags & IORESOURCE_IO)
+			base = &ssb_pcicore_pcibus_iobase;
+		else
+			base = &ssb_pcicore_pcibus_membase;
+		res->flags |= IORESOURCE_PCI_FIXED;
+		if (res->end) {
+			size = res->end - res->start + 1;
+			if (*base & (size - 1))
+				*base = (*base + size) & ~(size - 1);
+			res->start = *base;
+			res->end = res->start + size - 1;
+			*base += size;
+			pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start);
+		}
+		/* Fix up PCI bridge BAR0 only */
+		if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0)
+			break;
+	}
+	/* Fix up interrupt lines */
+	d->irq = ssb_mips_irq(extpci_core->dev) + 2;
+	pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);
+
+	return 0;
+}
+
+/* Early PCI fixup for a device on the PCI-core bridge. */
+static void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev)
+{
+	u8 lat;
+
+	if (dev->bus->ops != &ssb_pcicore_pciops) {
+		/* This is not a device on the PCI-core bridge. */
+		return;
+	}
+	if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
+		return;
+
+	ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev));
+
+	/* Enable PCI bridge bus mastering and memory space */
+	pci_set_master(dev);
+	if (pcibios_enable_device(dev, ~0) < 0) {
+		ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n");
+		return;
+	}
+
+	/* Enable PCI bridge BAR1 prefetch and burst */
+	pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3);
+
+	/* Make sure our latency is high enough to handle the devices behind us */
+	lat = 168;
+	ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n",
+		   pci_name(dev), lat);
+	pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge);
+
+/* PCI device IRQ mapping. */
+int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	if (dev->bus->ops != &ssb_pcicore_pciops) {
+		/* This is not a device on the PCI-core bridge. */
+		return -ENODEV;
+	}
+	return ssb_mips_irq(extpci_core->dev) + 2;
+}
+
 static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
 static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
 {
 {
 	u32 val;
 	u32 val;

+ 90 - 0
drivers/ssb/embedded.c

@@ -10,6 +10,9 @@
 
 
 #include <linux/ssb/ssb.h>
 #include <linux/ssb/ssb.h>
 #include <linux/ssb/ssb_embedded.h>
 #include <linux/ssb/ssb_embedded.h>
+#include <linux/ssb/ssb_driver_pci.h>
+#include <linux/ssb/ssb_driver_gige.h>
+#include <linux/pci.h>
 
 
 #include "ssb_private.h"
 #include "ssb_private.h"
 
 
@@ -130,3 +133,90 @@ u32 ssb_gpio_polarity(struct ssb_bus *bus, u32 mask, u32 value)
 	return res;
 	return res;
 }
 }
 EXPORT_SYMBOL(ssb_gpio_polarity);
 EXPORT_SYMBOL(ssb_gpio_polarity);
+
+#ifdef CONFIG_SSB_DRIVER_GIGE
+static int gige_pci_init_callback(struct ssb_bus *bus, unsigned long data)
+{
+	struct pci_dev *pdev = (struct pci_dev *)data;
+	struct ssb_device *dev;
+	unsigned int i;
+	int res;
+
+	for (i = 0; i < bus->nr_devices; i++) {
+		dev = &(bus->devices[i]);
+		if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT)
+			continue;
+		if (!dev->dev ||
+		    !dev->dev->driver ||
+		    !device_is_registered(dev->dev))
+			continue;
+		res = ssb_gige_pcibios_plat_dev_init(dev, pdev);
+		if (res >= 0)
+			return res;
+	}
+
+	return -ENODEV;
+}
+#endif /* CONFIG_SSB_DRIVER_GIGE */
+
+int ssb_pcibios_plat_dev_init(struct pci_dev *dev)
+{
+	int err;
+
+	err = ssb_pcicore_plat_dev_init(dev);
+	if (!err)
+		return 0;
+#ifdef CONFIG_SSB_DRIVER_GIGE
+	err = ssb_for_each_bus_call((unsigned long)dev, gige_pci_init_callback);
+	if (err >= 0)
+		return err;
+#endif
+	/* This is not a PCI device on any SSB device. */
+
+	return -ENODEV;
+}
+
+#ifdef CONFIG_SSB_DRIVER_GIGE
+static int gige_map_irq_callback(struct ssb_bus *bus, unsigned long data)
+{
+	const struct pci_dev *pdev = (const struct pci_dev *)data;
+	struct ssb_device *dev;
+	unsigned int i;
+	int res;
+
+	for (i = 0; i < bus->nr_devices; i++) {
+		dev = &(bus->devices[i]);
+		if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT)
+			continue;
+		if (!dev->dev ||
+		    !dev->dev->driver ||
+		    !device_is_registered(dev->dev))
+			continue;
+		res = ssb_gige_map_irq(dev, pdev);
+		if (res >= 0)
+			return res;
+	}
+
+	return -ENODEV;
+}
+#endif /* CONFIG_SSB_DRIVER_GIGE */
+
+int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	int res;
+
+	/* Check if this PCI device is a device on a SSB bus or device
+	 * and return the IRQ number for it. */
+
+	res = ssb_pcicore_pcibios_map_irq(dev, slot, pin);
+	if (res >= 0)
+		return res;
+#ifdef CONFIG_SSB_DRIVER_GIGE
+	res = ssb_for_each_bus_call((unsigned long)dev, gige_map_irq_callback);
+	if (res >= 0)
+		return res;
+#endif
+	/* This is not a PCI device on any SSB device. */
+
+	return -ENODEV;
+}

+ 29 - 1
drivers/ssb/main.c

@@ -14,6 +14,7 @@
 #include <linux/io.h>
 #include <linux/io.h>
 #include <linux/ssb/ssb.h>
 #include <linux/ssb/ssb.h>
 #include <linux/ssb/ssb_regs.h>
 #include <linux/ssb/ssb_regs.h>
+#include <linux/ssb/ssb_driver_gige.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping.h>
 #include <linux/pci.h>
 #include <linux/pci.h>
 
 
@@ -68,6 +69,25 @@ found:
 }
 }
 #endif /* CONFIG_SSB_PCIHOST */
 #endif /* CONFIG_SSB_PCIHOST */
 
 
+int ssb_for_each_bus_call(unsigned long data,
+			  int (*func)(struct ssb_bus *bus, unsigned long data))
+{
+	struct ssb_bus *bus;
+	int res;
+
+	ssb_buses_lock();
+	list_for_each_entry(bus, &buses, list) {
+		res = func(bus, data);
+		if (res >= 0) {
+			ssb_buses_unlock();
+			return res;
+		}
+	}
+	ssb_buses_unlock();
+
+	return -ENODEV;
+}
+
 static struct ssb_device *ssb_device_get(struct ssb_device *dev)
 static struct ssb_device *ssb_device_get(struct ssb_device *dev)
 {
 {
 	if (dev)
 	if (dev)
@@ -1171,7 +1191,14 @@ static int __init ssb_modinit(void)
 	err = b43_pci_ssb_bridge_init();
 	err = b43_pci_ssb_bridge_init();
 	if (err) {
 	if (err) {
 		ssb_printk(KERN_ERR "Broadcom 43xx PCI-SSB-bridge "
 		ssb_printk(KERN_ERR "Broadcom 43xx PCI-SSB-bridge "
-			   "initialization failed");
+			   "initialization failed\n");
+		/* don't fail SSB init because of this */
+		err = 0;
+	}
+	err = ssb_gige_init();
+	if (err) {
+		ssb_printk(KERN_ERR "SSB Broadcom Gigabit Ethernet "
+			   "driver initialization failed\n");
 		/* don't fail SSB init because of this */
 		/* don't fail SSB init because of this */
 		err = 0;
 		err = 0;
 	}
 	}
@@ -1185,6 +1212,7 @@ fs_initcall(ssb_modinit);
 
 
 static void __exit ssb_modexit(void)
 static void __exit ssb_modexit(void)
 {
 {
+	ssb_gige_exit();
 	b43_pci_ssb_bridge_exit();
 	b43_pci_ssb_bridge_exit();
 	bus_unregister(&ssb_bustype);
 	bus_unregister(&ssb_bustype);
 }
 }

+ 2 - 0
drivers/ssb/ssb_private.h

@@ -118,6 +118,8 @@ extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m);
 extern int ssb_devices_freeze(struct ssb_bus *bus);
 extern int ssb_devices_freeze(struct ssb_bus *bus);
 extern int ssb_devices_thaw(struct ssb_bus *bus);
 extern int ssb_devices_thaw(struct ssb_bus *bus);
 extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev);
 extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev);
+int ssb_for_each_bus_call(unsigned long data,
+			  int (*func)(struct ssb_bus *bus, unsigned long data));
 
 
 /* b43_pci_bridge.c */
 /* b43_pci_bridge.c */
 #ifdef CONFIG_SSB_B43_PCI_BRIDGE
 #ifdef CONFIG_SSB_B43_PCI_BRIDGE

+ 35 - 0
include/linux/ieee80211.h

@@ -97,6 +97,7 @@
 #define IEEE80211_MAX_FRAME_LEN		2352
 #define IEEE80211_MAX_FRAME_LEN		2352
 
 
 #define IEEE80211_MAX_SSID_LEN		32
 #define IEEE80211_MAX_SSID_LEN		32
+#define IEEE80211_MAX_MESH_ID_LEN	32
 
 
 struct ieee80211_hdr {
 struct ieee80211_hdr {
 	__le16 frame_control;
 	__le16 frame_control;
@@ -109,6 +110,16 @@ struct ieee80211_hdr {
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
 
 
+struct ieee80211s_hdr {
+	u8 flags;
+	u8 ttl;
+	u8 seqnum[3];
+	u8 eaddr1[6];
+	u8 eaddr2[6];
+	u8 eaddr3[6];
+} __attribute__ ((packed));
+
+
 struct ieee80211_mgmt {
 struct ieee80211_mgmt {
 	__le16 frame_control;
 	__le16 frame_control;
 	__le16 duration;
 	__le16 duration;
@@ -206,6 +217,23 @@ struct ieee80211_mgmt {
 					__le16 params;
 					__le16 params;
 					__le16 reason_code;
 					__le16 reason_code;
 				} __attribute__((packed)) delba;
 				} __attribute__((packed)) delba;
+				struct{
+					u8 action_code;
+					/* capab_info for open and confirm,
+					 * reason for close
+					 */
+					__le16 aux;
+					/* Followed in plink_confirm by status
+					 * code, AID and supported rates,
+					 * and directly by supported rates in
+					 * plink_open and plink_close
+					 */
+					u8 variable[0];
+				} __attribute__((packed)) plink_action;
+				struct{
+					u8 action_code;
+					u8 variable[0];
+				} __attribute__((packed)) mesh_action;
 			} u;
 			} u;
 		} __attribute__ ((packed)) action;
 		} __attribute__ ((packed)) action;
 	} u;
 	} u;
@@ -437,6 +465,13 @@ enum ieee80211_eid {
 	WLAN_EID_TS_DELAY = 43,
 	WLAN_EID_TS_DELAY = 43,
 	WLAN_EID_TCLAS_PROCESSING = 44,
 	WLAN_EID_TCLAS_PROCESSING = 44,
 	WLAN_EID_QOS_CAPA = 46,
 	WLAN_EID_QOS_CAPA = 46,
+	/* 802.11s */
+	WLAN_EID_MESH_CONFIG = 36,      /* Pending IEEE 802.11 ANA approval */
+	WLAN_EID_MESH_ID = 37,          /* Pending IEEE 802.11 ANA approval */
+	WLAN_EID_PEER_LINK = 40,	/* Pending IEEE 802.11 ANA approval */
+	WLAN_EID_PREQ = 53,		/* Pending IEEE 802.11 ANA approval */
+	WLAN_EID_PREP = 54,		/* Pending IEEE 802.11 ANA approval */
+	WLAN_EID_PERR = 55,		/* Pending IEEE 802.11 ANA approval */
 	/* 802.11h */
 	/* 802.11h */
 	WLAN_EID_PWR_CONSTRAINT = 32,
 	WLAN_EID_PWR_CONSTRAINT = 32,
 	WLAN_EID_PWR_CAPABILITY = 33,
 	WLAN_EID_PWR_CAPABILITY = 33,

+ 101 - 18
include/linux/nl80211.h

@@ -78,6 +78,18 @@
  *	or, if no MAC address given, all stations, on the interface identified
  *	or, if no MAC address given, all stations, on the interface identified
  *	by %NL80211_ATTR_IFINDEX.
  *	by %NL80211_ATTR_IFINDEX.
  *
  *
+ * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
+ * 	destination %NL80211_ATTR_MAC on the interface identified by
+ * 	%NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_MPATH:  Set mesh path attributes for mesh path to
+ * 	destination %NL80211_ATTR_MAC on the interface identified by
+ * 	%NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
+ *	the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
+ *	or, if no MAC address given, all mesh paths, on the interface identified
+ *	by %NL80211_ATTR_IFINDEX.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
  */
@@ -112,6 +124,11 @@ enum nl80211_commands {
 
 
 	/* add commands here */
 	/* add commands here */
 
 
+	NL80211_CMD_GET_MPATH,
+	NL80211_CMD_SET_MPATH,
+	NL80211_CMD_NEW_MPATH,
+	NL80211_CMD_DEL_MPATH,
+
 	/* used to define NL80211_CMD_MAX below */
 	/* used to define NL80211_CMD_MAX below */
 	__NL80211_CMD_AFTER_LAST,
 	__NL80211_CMD_AFTER_LAST,
 	NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
 	NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
@@ -157,13 +174,21 @@ enum nl80211_commands {
  *	restriction (at most %NL80211_MAX_SUPP_RATES).
  *	restriction (at most %NL80211_MAX_SUPP_RATES).
  * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
  * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
  *	to, or the AP interface the station was originally added to to.
  *	to, or the AP interface the station was originally added to to.
- * @NL80211_ATTR_STA_STATS: statistics for a station, part of station info
+ * @NL80211_ATTR_STA_INFO: information about a station, part of station info
  *	given for %NL80211_CMD_GET_STATION, nested attribute containing
  *	given for %NL80211_CMD_GET_STATION, nested attribute containing
- *	info as possible, see &enum nl80211_sta_stats.
+ *	info as possible, see &enum nl80211_sta_info.
  *
  *
  * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands,
  * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands,
  *	consisting of a nested array.
  *	consisting of a nested array.
  *
  *
+ * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
+ * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link.
+ * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
+ * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
+ * 	info given for %NL80211_CMD_GET_MPATH, nested attribute described at
+ *	&enum nl80211_mpath_info.
+ *
+ *
  * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
  * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
  *      &enum nl80211_mntr_flags.
  *      &enum nl80211_mntr_flags.
  *
  *
@@ -199,7 +224,7 @@ enum nl80211_attrs {
 	NL80211_ATTR_STA_LISTEN_INTERVAL,
 	NL80211_ATTR_STA_LISTEN_INTERVAL,
 	NL80211_ATTR_STA_SUPPORTED_RATES,
 	NL80211_ATTR_STA_SUPPORTED_RATES,
 	NL80211_ATTR_STA_VLAN,
 	NL80211_ATTR_STA_VLAN,
-	NL80211_ATTR_STA_STATS,
+	NL80211_ATTR_STA_INFO,
 
 
 	NL80211_ATTR_WIPHY_BANDS,
 	NL80211_ATTR_WIPHY_BANDS,
 
 
@@ -207,6 +232,11 @@ enum nl80211_attrs {
 
 
 	/* add attributes here, update the policy in nl80211.c */
 	/* add attributes here, update the policy in nl80211.c */
 
 
+	NL80211_ATTR_MESH_ID,
+	NL80211_ATTR_STA_PLINK_ACTION,
+	NL80211_ATTR_MPATH_NEXT_HOP,
+	NL80211_ATTR_MPATH_INFO,
+
 	__NL80211_ATTR_AFTER_LAST,
 	__NL80211_ATTR_AFTER_LAST,
 	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
 	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
 };
 };
@@ -223,6 +253,7 @@ enum nl80211_attrs {
  * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points
  * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points
  * @NL80211_IFTYPE_WDS: wireless distribution interface
  * @NL80211_IFTYPE_WDS: wireless distribution interface
  * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
  * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
+ * @NL80211_IFTYPE_MESH_POINT: mesh point
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
  * @__NL80211_IFTYPE_AFTER_LAST: internal use
  * @__NL80211_IFTYPE_AFTER_LAST: internal use
  *
  *
@@ -238,6 +269,7 @@ enum nl80211_iftype {
 	NL80211_IFTYPE_AP_VLAN,
 	NL80211_IFTYPE_AP_VLAN,
 	NL80211_IFTYPE_WDS,
 	NL80211_IFTYPE_WDS,
 	NL80211_IFTYPE_MONITOR,
 	NL80211_IFTYPE_MONITOR,
+	NL80211_IFTYPE_MESH_POINT,
 
 
 	/* keep last */
 	/* keep last */
 	__NL80211_IFTYPE_AFTER_LAST,
 	__NL80211_IFTYPE_AFTER_LAST,
@@ -267,27 +299,78 @@ enum nl80211_sta_flags {
 };
 };
 
 
 /**
 /**
- * enum nl80211_sta_stats - station statistics
+ * enum nl80211_sta_info - station information
  *
  *
- * These attribute types are used with %NL80211_ATTR_STA_STATS
+ * These attribute types are used with %NL80211_ATTR_STA_INFO
  * when getting information about a station.
  * when getting information about a station.
  *
  *
- * @__NL80211_STA_STAT_INVALID: attribute number 0 is reserved
- * @NL80211_STA_STAT_INACTIVE_TIME: time since last activity (u32, msecs)
- * @NL80211_STA_STAT_RX_BYTES: total received bytes (u32, from this station)
- * @NL80211_STA_STAT_TX_BYTES: total transmitted bytes (u32, to this station)
- * @__NL80211_STA_STAT_AFTER_LAST: internal
- * @NL80211_STA_STAT_MAX: highest possible station stats attribute
+ * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
+ * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
+ * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
+ */
+enum nl80211_sta_info {
+	__NL80211_STA_INFO_INVALID,
+	NL80211_STA_INFO_INACTIVE_TIME,
+	NL80211_STA_INFO_RX_BYTES,
+	NL80211_STA_INFO_TX_BYTES,
+	NL80211_STA_INFO_LLID,
+	NL80211_STA_INFO_PLID,
+	NL80211_STA_INFO_PLINK_STATE,
+
+	/* keep last */
+	__NL80211_STA_INFO_AFTER_LAST,
+	NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mpath_flags - nl80211 mesh path flags
+ *
+ * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
+ * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running
+ * @NL80211_MPATH_FLAG_DSN_VALID: the mesh path contains a valid DSN
+ * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set
+ * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded
+ */
+enum nl80211_mpath_flags {
+	NL80211_MPATH_FLAG_ACTIVE =	1<<0,
+	NL80211_MPATH_FLAG_RESOLVING =	1<<1,
+	NL80211_MPATH_FLAG_DSN_VALID =	1<<2,
+	NL80211_MPATH_FLAG_FIXED =	1<<3,
+	NL80211_MPATH_FLAG_RESOLVED =	1<<4,
+};
+
+/**
+ * enum nl80211_mpath_info - mesh path information
+ *
+ * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting
+ * information about a mesh path.
+ *
+ * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination
+ * @NL80211_ATTR_MPATH_DSN: destination sequence number
+ * @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path
+ * @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now
+ * @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in
+ * 	&enum nl80211_mpath_flags;
+ * @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+ * @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries
  */
  */
-enum nl80211_sta_stats {
-	__NL80211_STA_STAT_INVALID,
-	NL80211_STA_STAT_INACTIVE_TIME,
-	NL80211_STA_STAT_RX_BYTES,
-	NL80211_STA_STAT_TX_BYTES,
+enum nl80211_mpath_info {
+	__NL80211_MPATH_INFO_INVALID,
+	NL80211_MPATH_INFO_FRAME_QLEN,
+	NL80211_MPATH_INFO_DSN,
+	NL80211_MPATH_INFO_METRIC,
+	NL80211_MPATH_INFO_EXPTIME,
+	NL80211_MPATH_INFO_FLAGS,
+	NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
+	NL80211_MPATH_INFO_DISCOVERY_RETRIES,
 
 
 	/* keep last */
 	/* keep last */
-	__NL80211_STA_STAT_AFTER_LAST,
-	NL80211_STA_STAT_MAX = __NL80211_STA_STAT_AFTER_LAST - 1
+	__NL80211_MPATH_INFO_AFTER_LAST,
+	NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1
 };
 };
 
 
 /**
 /**

+ 7 - 0
include/linux/ssb/ssb.h

@@ -422,5 +422,12 @@ extern int ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl);
 extern u32 ssb_admatch_base(u32 adm);
 extern u32 ssb_admatch_base(u32 adm);
 extern u32 ssb_admatch_size(u32 adm);
 extern u32 ssb_admatch_size(u32 adm);
 
 
+/* PCI device mapping and fixup routines.
+ * Called from the architecture pcibios init code.
+ * These are only available on SSB_EMBEDDED configurations. */
+#ifdef CONFIG_SSB_EMBEDDED
+int ssb_pcibios_plat_dev_init(struct pci_dev *dev);
+int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
+#endif /* CONFIG_SSB_EMBEDDED */
 
 
 #endif /* LINUX_SSB_H_ */
 #endif /* LINUX_SSB_H_ */

+ 174 - 0
include/linux/ssb/ssb_driver_gige.h

@@ -0,0 +1,174 @@
+#ifndef LINUX_SSB_DRIVER_GIGE_H_
+#define LINUX_SSB_DRIVER_GIGE_H_
+
+#include <linux/ssb/ssb.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+
+#ifdef CONFIG_SSB_DRIVER_GIGE
+
+
+#define SSB_GIGE_PCIIO			0x0000 /* PCI I/O Registers (1024 bytes) */
+#define SSB_GIGE_RESERVED		0x0400 /* Reserved (1024 bytes) */
+#define SSB_GIGE_PCICFG			0x0800 /* PCI config space (256 bytes) */
+#define SSB_GIGE_SHIM_FLUSHSTAT		0x0C00 /* PCI to OCP: Flush status control (32bit) */
+#define SSB_GIGE_SHIM_FLUSHRDA		0x0C04 /* PCI to OCP: Flush read address (32bit) */
+#define SSB_GIGE_SHIM_FLUSHTO		0x0C08 /* PCI to OCP: Flush timeout counter (32bit) */
+#define SSB_GIGE_SHIM_BARRIER		0x0C0C /* PCI to OCP: Barrier register (32bit) */
+#define SSB_GIGE_SHIM_MAOCPSI		0x0C10 /* PCI to OCP: MaocpSI Control (32bit) */
+#define SSB_GIGE_SHIM_SIOCPMA		0x0C14 /* PCI to OCP: SiocpMa Control (32bit) */
+
+/* TM Status High flags */
+#define SSB_GIGE_TMSHIGH_RGMII		0x00010000 /* Have an RGMII PHY-bus */
+/* TM Status Low flags */
+#define SSB_GIGE_TMSLOW_TXBYPASS	0x00080000 /* TX bypass (no delay) */
+#define SSB_GIGE_TMSLOW_RXBYPASS	0x00100000 /* RX bypass (no delay) */
+#define SSB_GIGE_TMSLOW_DLLEN		0x01000000 /* Enable DLL controls */
+
+/* Boardflags (low) */
+#define SSB_GIGE_BFL_ROBOSWITCH		0x0010
+
+
+#define SSB_GIGE_MEM_RES_NAME		"SSB Broadcom 47xx GigE memory"
+#define SSB_GIGE_IO_RES_NAME		"SSB Broadcom 47xx GigE I/O"
+
+struct ssb_gige {
+	struct ssb_device *dev;
+
+	spinlock_t lock;
+
+	/* True, if the device has an RGMII bus.
+	 * False, if the device has a GMII bus. */
+	bool has_rgmii;
+
+	/* The PCI controller device. */
+	struct pci_controller pci_controller;
+	struct pci_ops pci_ops;
+	struct resource mem_resource;
+	struct resource io_resource;
+};
+
+/* Check whether a PCI device is a SSB Gigabit Ethernet core. */
+extern bool pdev_is_ssb_gige_core(struct pci_dev *pdev);
+
+/* Convert a pci_dev pointer to a ssb_gige pointer. */
+static inline struct ssb_gige * pdev_to_ssb_gige(struct pci_dev *pdev)
+{
+	if (!pdev_is_ssb_gige_core(pdev))
+		return NULL;
+	return container_of(pdev->bus->ops, struct ssb_gige, pci_ops);
+}
+
+/* Returns whether the PHY is connected by an RGMII bus. */
+static inline bool ssb_gige_is_rgmii(struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
+	return (dev ? dev->has_rgmii : 0);
+}
+
+/* Returns whether we have a Roboswitch. */
+static inline bool ssb_gige_have_roboswitch(struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
+	if (dev)
+		return !!(dev->dev->bus->sprom.boardflags_lo &
+			  SSB_GIGE_BFL_ROBOSWITCH);
+	return 0;
+}
+
+/* Returns whether we can only do one DMA at once. */
+static inline bool ssb_gige_one_dma_at_once(struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
+	if (dev)
+		return ((dev->dev->bus->chip_id == 0x4785) &&
+			(dev->dev->bus->chip_rev < 2));
+	return 0;
+}
+
+/* Returns whether we must flush posted writes. */
+static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
+	if (dev)
+		return (dev->dev->bus->chip_id == 0x4785);
+	return 0;
+}
+
+extern char * nvram_get(const char *name);
+/* Get the device MAC address */
+static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr)
+{
+#ifdef CONFIG_BCM947XX
+	char *res = nvram_get("et0macaddr");
+	if (res)
+		memcpy(macaddr, res, 6);
+#endif
+}
+
+extern int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
+					  struct pci_dev *pdev);
+extern int ssb_gige_map_irq(struct ssb_device *sdev,
+			    const struct pci_dev *pdev);
+
+/* The GigE driver is not a standalone module, because we don't have support
+ * for unregistering the driver. So we could not unload the module anyway. */
+extern int ssb_gige_init(void);
+static inline void ssb_gige_exit(void)
+{
+	/* Currently we can not unregister the GigE driver,
+	 * because we can not unregister the PCI bridge. */
+	BUG();
+}
+
+
+#else /* CONFIG_SSB_DRIVER_GIGE */
+/* Gigabit Ethernet driver disabled */
+
+
+static inline int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
+						 struct pci_dev *pdev)
+{
+	return -ENOSYS;
+}
+static inline int ssb_gige_map_irq(struct ssb_device *sdev,
+				   const struct pci_dev *pdev)
+{
+	return -ENOSYS;
+}
+static inline int ssb_gige_init(void)
+{
+	return 0;
+}
+static inline void ssb_gige_exit(void)
+{
+}
+
+static inline bool pdev_is_ssb_gige_core(struct pci_dev *pdev)
+{
+	return 0;
+}
+static inline struct ssb_gige * pdev_to_ssb_gige(struct pci_dev *pdev)
+{
+	return NULL;
+}
+static inline bool ssb_gige_is_rgmii(struct pci_dev *pdev)
+{
+	return 0;
+}
+static inline bool ssb_gige_have_roboswitch(struct pci_dev *pdev)
+{
+	return 0;
+}
+static inline bool ssb_gige_one_dma_at_once(struct pci_dev *pdev)
+{
+	return 0;
+}
+static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev)
+{
+	return 0;
+}
+
+#endif /* CONFIG_SSB_DRIVER_GIGE */
+#endif /* LINUX_SSB_DRIVER_GIGE_H_ */

+ 19 - 0
include/linux/ssb/ssb_driver_pci.h

@@ -1,6 +1,11 @@
 #ifndef LINUX_SSB_PCICORE_H_
 #ifndef LINUX_SSB_PCICORE_H_
 #define LINUX_SSB_PCICORE_H_
 #define LINUX_SSB_PCICORE_H_
 
 
+#include <linux/types.h>
+
+struct pci_dev;
+
+
 #ifdef CONFIG_SSB_DRIVER_PCICORE
 #ifdef CONFIG_SSB_DRIVER_PCICORE
 
 
 /* PCI core registers. */
 /* PCI core registers. */
@@ -88,6 +93,9 @@ extern void ssb_pcicore_init(struct ssb_pcicore *pc);
 extern int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
 extern int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
 					  struct ssb_device *dev);
 					  struct ssb_device *dev);
 
 
+int ssb_pcicore_plat_dev_init(struct pci_dev *d);
+int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
+
 
 
 #else /* CONFIG_SSB_DRIVER_PCICORE */
 #else /* CONFIG_SSB_DRIVER_PCICORE */
 
 
@@ -107,5 +115,16 @@ int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
 	return 0;
 	return 0;
 }
 }
 
 
+static inline
+int ssb_pcicore_plat_dev_init(struct pci_dev *d)
+{
+	return -ENODEV;
+}
+static inline
+int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	return -ENODEV;
+}
+
 #endif /* CONFIG_SSB_DRIVER_PCICORE */
 #endif /* CONFIG_SSB_DRIVER_PCICORE */
 #endif /* LINUX_SSB_PCICORE_H_ */
 #endif /* LINUX_SSB_PCICORE_H_ */

+ 1 - 0
include/linux/wireless.h

@@ -455,6 +455,7 @@
 #define IW_MODE_REPEAT	4	/* Wireless Repeater (forwarder) */
 #define IW_MODE_REPEAT	4	/* Wireless Repeater (forwarder) */
 #define IW_MODE_SECOND	5	/* Secondary master/repeater (backup) */
 #define IW_MODE_SECOND	5	/* Secondary master/repeater (backup) */
 #define IW_MODE_MONITOR	6	/* Passive monitor (listen only) */
 #define IW_MODE_MONITOR	6	/* Passive monitor (listen only) */
+#define IW_MODE_MESH	7	/* Mesh (IEEE 802.11s) network */
 
 
 /* Statistics flags (bitmask in updated) */
 /* Statistics flags (bitmask in updated) */
 #define IW_QUAL_QUAL_UPDATED	0x01	/* Value was updated since last read */
 #define IW_QUAL_QUAL_UPDATED	0x01	/* Value was updated since last read */

+ 122 - 17
include/net/cfg80211.h

@@ -12,6 +12,16 @@
  * Copyright 2006, 2007	Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2006, 2007	Johannes Berg <johannes@sipsolutions.net>
  */
  */
 
 
+/**
+ * struct vif_params - describes virtual interface parameters
+ * @mesh_id: mesh ID to use
+ * @mesh_id_len: length of the mesh ID
+ */
+struct vif_params {
+       u8 *mesh_id;
+       int mesh_id_len;
+};
+
 /* Radiotap header iteration
 /* Radiotap header iteration
  *   implemented in net/wireless/radiotap.c
  *   implemented in net/wireless/radiotap.c
  *   docs in Documentation/networking/radiotap-headers.txt
  *   docs in Documentation/networking/radiotap-headers.txt
@@ -108,6 +118,19 @@ enum station_flags {
 	STATION_FLAG_WME		= 1<<NL80211_STA_FLAG_WME,
 	STATION_FLAG_WME		= 1<<NL80211_STA_FLAG_WME,
 };
 };
 
 
+/**
+ * enum plink_action - actions to perform in mesh peers
+ *
+ * @PLINK_ACTION_INVALID: action 0 is reserved
+ * @PLINK_ACTION_OPEN: start mesh peer link establishment
+ * @PLINK_ACTION_BLOCL: block traffic from this mesh peer
+ */
+enum plink_actions {
+	PLINK_ACTION_INVALID,
+	PLINK_ACTION_OPEN,
+	PLINK_ACTION_BLOCK,
+};
+
 /**
 /**
  * struct station_parameters - station parameters
  * struct station_parameters - station parameters
  *
  *
@@ -128,39 +151,52 @@ struct station_parameters {
 	int listen_interval;
 	int listen_interval;
 	u16 aid;
 	u16 aid;
 	u8 supported_rates_len;
 	u8 supported_rates_len;
+	u8 plink_action;
 };
 };
 
 
 /**
 /**
- * enum station_stats_flags - station statistics flags
+ * enum station_info_flags - station information flags
  *
  *
- * Used by the driver to indicate which info in &struct station_stats
- * it has filled in during get_station().
+ * Used by the driver to indicate which info in &struct station_info
+ * it has filled in during get_station() or dump_station().
  *
  *
- * @STATION_STAT_INACTIVE_TIME: @inactive_time filled
- * @STATION_STAT_RX_BYTES: @rx_bytes filled
- * @STATION_STAT_TX_BYTES: @tx_bytes filled
+ * @STATION_INFO_INACTIVE_TIME: @inactive_time filled
+ * @STATION_INFO_RX_BYTES: @rx_bytes filled
+ * @STATION_INFO_TX_BYTES: @tx_bytes filled
+ * @STATION_INFO_LLID: @llid filled
+ * @STATION_INFO_PLID: @plid filled
+ * @STATION_INFO_PLINK_STATE: @plink_state filled
  */
  */
-enum station_stats_flags {
-	STATION_STAT_INACTIVE_TIME	= 1<<0,
-	STATION_STAT_RX_BYTES		= 1<<1,
-	STATION_STAT_TX_BYTES		= 1<<2,
+enum station_info_flags {
+	STATION_INFO_INACTIVE_TIME	= 1<<0,
+	STATION_INFO_RX_BYTES		= 1<<1,
+	STATION_INFO_TX_BYTES		= 1<<2,
+	STATION_INFO_LLID		= 1<<3,
+	STATION_INFO_PLID		= 1<<4,
+	STATION_INFO_PLINK_STATE	= 1<<5,
 };
 };
 
 
 /**
 /**
- * struct station_stats - station statistics
+ * struct station_info - station information
  *
  *
- * Station information filled by driver for get_station().
+ * Station information filled by driver for get_station() and dump_station.
  *
  *
- * @filled: bitflag of flags from &enum station_stats_flags
+ * @filled: bitflag of flags from &enum station_info_flags
  * @inactive_time: time since last station activity (tx/rx) in milliseconds
  * @inactive_time: time since last station activity (tx/rx) in milliseconds
  * @rx_bytes: bytes received from this station
  * @rx_bytes: bytes received from this station
  * @tx_bytes: bytes transmitted to this station
  * @tx_bytes: bytes transmitted to this station
+ * @llid: mesh local link id
+ * @plid: mesh peer link id
+ * @plink_state: mesh peer link state
  */
  */
-struct station_stats {
+struct station_info {
 	u32 filled;
 	u32 filled;
 	u32 inactive_time;
 	u32 inactive_time;
 	u32 rx_bytes;
 	u32 rx_bytes;
 	u32 tx_bytes;
 	u32 tx_bytes;
+	u16 llid;
+	u16 plid;
+	u8 plink_state;
 };
 };
 
 
 /**
 /**
@@ -183,6 +219,56 @@ enum monitor_flags {
 	MONITOR_FLAG_COOK_FRAMES	= 1<<NL80211_MNTR_FLAG_COOK_FRAMES,
 	MONITOR_FLAG_COOK_FRAMES	= 1<<NL80211_MNTR_FLAG_COOK_FRAMES,
 };
 };
 
 
+/**
+ * enum mpath_info_flags -  mesh path information flags
+ *
+ * Used by the driver to indicate which info in &struct mpath_info it has filled
+ * in during get_station() or dump_station().
+ *
+ * MPATH_INFO_FRAME_QLEN: @frame_qlen filled
+ * MPATH_INFO_DSN: @dsn filled
+ * MPATH_INFO_METRIC: @metric filled
+ * MPATH_INFO_EXPTIME: @exptime filled
+ * MPATH_INFO_DISCOVERY_TIMEOUT: @discovery_timeout filled
+ * MPATH_INFO_DISCOVERY_RETRIES: @discovery_retries filled
+ * MPATH_INFO_FLAGS: @flags filled
+ */
+enum mpath_info_flags {
+	MPATH_INFO_FRAME_QLEN		= BIT(0),
+	MPATH_INFO_DSN			= BIT(1),
+	MPATH_INFO_METRIC		= BIT(2),
+	MPATH_INFO_EXPTIME		= BIT(3),
+	MPATH_INFO_DISCOVERY_TIMEOUT	= BIT(4),
+	MPATH_INFO_DISCOVERY_RETRIES	= BIT(5),
+	MPATH_INFO_FLAGS		= BIT(6),
+};
+
+/**
+ * struct mpath_info - mesh path information
+ *
+ * Mesh path information filled by driver for get_mpath() and dump_mpath().
+ *
+ * @filled: bitfield of flags from &enum mpath_info_flags
+ * @frame_qlen: number of queued frames for this destination
+ * @dsn: destination sequence number
+ * @metric: metric (cost) of this mesh path
+ * @exptime: expiration time for the mesh path from now, in msecs
+ * @flags: mesh path flags
+ * @discovery_timeout: total mesh path discovery timeout, in msecs
+ * @discovery_retries: mesh path discovery retries
+ */
+struct mpath_info {
+	u32 filled;
+	u32 frame_qlen;
+	u32 dsn;
+	u32 metric;
+	u32 exptime;
+	u32 discovery_timeout;
+	u8 discovery_retries;
+	u8 flags;
+};
+
+
 /* from net/wireless.h */
 /* from net/wireless.h */
 struct wiphy;
 struct wiphy;
 
 
@@ -230,13 +316,17 @@ struct wiphy;
  * @del_station: Remove a station; @mac may be NULL to remove all stations.
  * @del_station: Remove a station; @mac may be NULL to remove all stations.
  *
  *
  * @change_station: Modify a given station.
  * @change_station: Modify a given station.
+ *
+ * @set_mesh_cfg: set mesh parameters (by now, just mesh id)
  */
  */
 struct cfg80211_ops {
 struct cfg80211_ops {
 	int	(*add_virtual_intf)(struct wiphy *wiphy, char *name,
 	int	(*add_virtual_intf)(struct wiphy *wiphy, char *name,
-				    enum nl80211_iftype type, u32 *flags);
+				    enum nl80211_iftype type, u32 *flags,
+				    struct vif_params *params);
 	int	(*del_virtual_intf)(struct wiphy *wiphy, int ifindex);
 	int	(*del_virtual_intf)(struct wiphy *wiphy, int ifindex);
 	int	(*change_virtual_intf)(struct wiphy *wiphy, int ifindex,
 	int	(*change_virtual_intf)(struct wiphy *wiphy, int ifindex,
-				       enum nl80211_iftype type, u32 *flags);
+				       enum nl80211_iftype type, u32 *flags,
+				       struct vif_params *params);
 
 
 	int	(*add_key)(struct wiphy *wiphy, struct net_device *netdev,
 	int	(*add_key)(struct wiphy *wiphy, struct net_device *netdev,
 			   u8 key_index, u8 *mac_addr,
 			   u8 key_index, u8 *mac_addr,
@@ -264,7 +354,22 @@ struct cfg80211_ops {
 	int	(*change_station)(struct wiphy *wiphy, struct net_device *dev,
 	int	(*change_station)(struct wiphy *wiphy, struct net_device *dev,
 				  u8 *mac, struct station_parameters *params);
 				  u8 *mac, struct station_parameters *params);
 	int	(*get_station)(struct wiphy *wiphy, struct net_device *dev,
 	int	(*get_station)(struct wiphy *wiphy, struct net_device *dev,
-			       u8 *mac, struct station_stats *stats);
+			       u8 *mac, struct station_info *sinfo);
+	int	(*dump_station)(struct wiphy *wiphy, struct net_device *dev,
+			       int idx, u8 *mac, struct station_info *sinfo);
+
+	int	(*add_mpath)(struct wiphy *wiphy, struct net_device *dev,
+			       u8 *dst, u8 *next_hop);
+	int	(*del_mpath)(struct wiphy *wiphy, struct net_device *dev,
+			       u8 *dst);
+	int	(*change_mpath)(struct wiphy *wiphy, struct net_device *dev,
+				  u8 *dst, u8 *next_hop);
+	int	(*get_mpath)(struct wiphy *wiphy, struct net_device *dev,
+			       u8 *dst, u8 *next_hop,
+			       struct mpath_info *pinfo);
+	int	(*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
+			       int idx, u8 *dst, u8 *next_hop,
+			       struct mpath_info *pinfo);
 };
 };
 
 
 #endif /* __NET_CFG80211_H */
 #endif /* __NET_CFG80211_H */

+ 72 - 34
include/net/mac80211.h

@@ -205,6 +205,62 @@ struct ieee80211_bss_conf {
 	bool use_short_preamble;
 	bool use_short_preamble;
 };
 };
 
 
+/**
+ * enum mac80211_tx_control_flags - flags to describe Tx configuration for
+ * 				    the Tx frame
+ *
+ * These flags are used with the @flags member of &ieee80211_tx_control
+ *
+ * @IEEE80211_TXCTL_REQ_TX_STATUS: request TX status callback for this frame.
+ * @IEEE80211_TXCTL_DO_NOT_ENCRYPT: send this frame without encryption;
+ * 				    e.g., for EAPOL frame
+ * @IEEE80211_TXCTL_USE_RTS_CTS: use RTS-CTS before sending frame
+ * @IEEE80211_TXCTL_USE_CTS_PROTECT: use CTS protection for the frame (e.g.,
+ * 				     for combined 802.11g / 802.11b networks)
+ * @IEEE80211_TXCTL_NO_ACK: tell the low level not to wait for an ack
+ * @IEEE80211_TXCTL_RATE_CTRL_PROBE
+ * @EEE80211_TXCTL_CLEAR_PS_FILT: clear powersave filter
+ *                                 for destination station
+ * @IEEE80211_TXCTL_REQUEUE:
+ * @IEEE80211_TXCTL_FIRST_FRAGMENT: this is a first fragment of the frame
+ * @IEEE80211_TXCTL_LONG_RETRY_LIMIT: this frame should be send using the
+ * 				      through set_retry_limit configured long
+ * 				      retry value
+ * @IEEE80211_TXCTL_EAPOL_FRAME: internal to mac80211
+ * @IEEE80211_TXCTL_SEND_AFTER_DTIM: send this frame after DTIM beacon
+ * @IEEE80211_TXCTL_AMPDU: this frame should be sent as part of an A-MPDU
+ * @IEEE80211_TXCTL_OFDM_HT: this frame can be sent in HT OFDM rates. number
+ * 			     of streams when this flag is on can be extracted
+ *			     from antenna_sel_tx, so if 1 antenna is marked
+ *			     use SISO, 2 antennas marked use MIMO, n antennas
+ *			     marked use MIMO_n.
+ * @IEEE80211_TXCTL_GREEN_FIELD: use green field protection for this frame
+ * @IEEE80211_TXCTL_40_MHZ_WIDTH: send this frame using 40 Mhz channel width
+ * @IEEE80211_TXCTL_DUP_DATA: duplicate data frame on both 20 Mhz channels
+ * @IEEE80211_TXCTL_SHORT_GI: send this frame using short guard interval
+ */
+enum mac80211_tx_control_flags {
+	IEEE80211_TXCTL_REQ_TX_STATUS		= (1<<0),
+	IEEE80211_TXCTL_DO_NOT_ENCRYPT		= (1<<1),
+	IEEE80211_TXCTL_USE_RTS_CTS		= (1<<2),
+	IEEE80211_TXCTL_USE_CTS_PROTECT		= (1<<3),
+	IEEE80211_TXCTL_NO_ACK			= (1<<4),
+	IEEE80211_TXCTL_RATE_CTRL_PROBE		= (1<<5),
+	IEEE80211_TXCTL_CLEAR_PS_FILT		= (1<<6),
+	IEEE80211_TXCTL_REQUEUE			= (1<<7),
+	IEEE80211_TXCTL_FIRST_FRAGMENT		= (1<<8),
+	IEEE80211_TXCTL_SHORT_PREAMBLE		= (1<<9),
+	IEEE80211_TXCTL_LONG_RETRY_LIMIT	= (1<<10),
+	IEEE80211_TXCTL_EAPOL_FRAME		= (1<<11),
+	IEEE80211_TXCTL_SEND_AFTER_DTIM		= (1<<12),
+	IEEE80211_TXCTL_AMPDU			= (1<<13),
+	IEEE80211_TXCTL_OFDM_HT			= (1<<14),
+	IEEE80211_TXCTL_GREEN_FIELD		= (1<<15),
+	IEEE80211_TXCTL_40_MHZ_WIDTH		= (1<<16),
+	IEEE80211_TXCTL_DUP_DATA		= (1<<17),
+	IEEE80211_TXCTL_SHORT_GI		= (1<<18),
+};
+
 /* Transmit control fields. This data structure is passed to low-level driver
 /* Transmit control fields. This data structure is passed to low-level driver
  * with each TX frame. The low-level driver is responsible for configuring
  * with each TX frame. The low-level driver is responsible for configuring
  * the hardware to use given values (depending on what is supported). */
  * the hardware to use given values (depending on what is supported). */
@@ -219,42 +275,14 @@ struct ieee80211_tx_control {
 	/* retry rate for the last retries */
 	/* retry rate for the last retries */
 	struct ieee80211_rate *alt_retry_rate;
 	struct ieee80211_rate *alt_retry_rate;
 
 
-#define IEEE80211_TXCTL_REQ_TX_STATUS	(1<<0)/* request TX status callback for
-						* this frame */
-#define IEEE80211_TXCTL_DO_NOT_ENCRYPT	(1<<1) /* send this frame without
-						* encryption; e.g., for EAPOL
-						* frames */
-#define IEEE80211_TXCTL_USE_RTS_CTS	(1<<2) /* use RTS-CTS before sending
-						* frame */
-#define IEEE80211_TXCTL_USE_CTS_PROTECT	(1<<3) /* use CTS protection for the
-						* frame (e.g., for combined
-						* 802.11g / 802.11b networks) */
-#define IEEE80211_TXCTL_NO_ACK		(1<<4) /* tell the low level not to
-						* wait for an ack */
-#define IEEE80211_TXCTL_RATE_CTRL_PROBE	(1<<5)
-#define IEEE80211_TXCTL_CLEAR_PS_FILT	(1<<6) /* clear powersave filter
-						* for destination station */
-#define IEEE80211_TXCTL_REQUEUE		(1<<7)
-#define IEEE80211_TXCTL_FIRST_FRAGMENT	(1<<8) /* this is a first fragment of
-						* the frame */
-#define IEEE80211_TXCTL_SHORT_PREAMBLE	(1<<9)
-#define IEEE80211_TXCTL_LONG_RETRY_LIMIT (1<<10) /* this frame should be send
-						  * using the through
-						  * set_retry_limit configured
-						  * long retry value */
-#define IEEE80211_TXCTL_EAPOL_FRAME	(1<<11) /* internal to mac80211 */
-#define IEEE80211_TXCTL_SEND_AFTER_DTIM	(1<<12) /* send this frame after DTIM
-						 * beacon */
-#define IEEE80211_TXCTL_AMPDU		(1<<13) /* this frame should be sent
-						 * as part of an A-MPDU */
-	u32 flags;			       /* tx control flags defined
-						* above */
+	u32 flags;		/* tx control flags defined above */
 	u8 key_idx;		/* keyidx from hw->set_key(), undefined if
 	u8 key_idx;		/* keyidx from hw->set_key(), undefined if
 				 * IEEE80211_TXCTL_DO_NOT_ENCRYPT is set */
 				 * IEEE80211_TXCTL_DO_NOT_ENCRYPT is set */
 	u8 retry_limit;		/* 1 = only first attempt, 2 = one retry, ..
 	u8 retry_limit;		/* 1 = only first attempt, 2 = one retry, ..
 				 * This could be used when set_retry_limit
 				 * This could be used when set_retry_limit
 				 * is not implemented by the driver */
 				 * is not implemented by the driver */
-	u8 antenna_sel_tx; 	/* 0 = default/diversity, 1 = Ant0, 2 = Ant1 */
+	u8 antenna_sel_tx; 	/* 0 = default/diversity, otherwise bit
+				 * position represents antenna number used */
 	u8 icv_len;		/* length of the ICV/MIC field in octets */
 	u8 icv_len;		/* length of the ICV/MIC field in octets */
 	u8 iv_len;		/* length of the IV field in octets */
 	u8 iv_len;		/* length of the IV field in octets */
 	u8 queue;		/* hardware queue to use for this frame;
 	u8 queue;		/* hardware queue to use for this frame;
@@ -407,7 +435,6 @@ enum ieee80211_conf_flags {
  * @channel: the channel to tune to
  * @channel: the channel to tune to
  */
  */
 struct ieee80211_conf {
 struct ieee80211_conf {
-	unsigned int regulatory_domain;
 	int radio_enabled;
 	int radio_enabled;
 
 
 	int beacon_int;
 	int beacon_int;
@@ -437,12 +464,14 @@ struct ieee80211_conf {
  * @IEEE80211_IF_TYPE_WDS: interface in WDS mode.
  * @IEEE80211_IF_TYPE_WDS: interface in WDS mode.
  * @IEEE80211_IF_TYPE_VLAN: VLAN interface bound to an AP, drivers
  * @IEEE80211_IF_TYPE_VLAN: VLAN interface bound to an AP, drivers
  *	will never see this type.
  *	will never see this type.
+ * @IEEE80211_IF_TYPE_MESH_POINT: 802.11s mesh point
  */
  */
 enum ieee80211_if_types {
 enum ieee80211_if_types {
 	IEEE80211_IF_TYPE_INVALID,
 	IEEE80211_IF_TYPE_INVALID,
 	IEEE80211_IF_TYPE_AP,
 	IEEE80211_IF_TYPE_AP,
 	IEEE80211_IF_TYPE_STA,
 	IEEE80211_IF_TYPE_STA,
 	IEEE80211_IF_TYPE_IBSS,
 	IEEE80211_IF_TYPE_IBSS,
+	IEEE80211_IF_TYPE_MESH_POINT,
 	IEEE80211_IF_TYPE_MNTR,
 	IEEE80211_IF_TYPE_MNTR,
 	IEEE80211_IF_TYPE_WDS,
 	IEEE80211_IF_TYPE_WDS,
 	IEEE80211_IF_TYPE_VLAN,
 	IEEE80211_IF_TYPE_VLAN,
@@ -464,6 +493,14 @@ struct ieee80211_vif {
 	u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
 	u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
 };
 };
 
 
+static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
+{
+#ifdef CONFIG_MAC80211_MESH
+	return vif->type == IEEE80211_IF_TYPE_MESH_POINT;
+#endif
+	return false;
+}
+
 /**
 /**
  * struct ieee80211_if_init_conf - initial configuration of an interface
  * struct ieee80211_if_init_conf - initial configuration of an interface
  *
  *
@@ -1087,8 +1124,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
 /**
 /**
  * ieee80211_register_hw - Register hardware device
  * ieee80211_register_hw - Register hardware device
  *
  *
- * You must call this function before any other functions
- * except ieee80211_register_hwmode.
+ * You must call this function before any other functions in
+ * mac80211. Note that before a hardware can be registered, you
+ * need to fill the contained wiphy's information.
  *
  *
  * @hw: the device to register as returned by ieee80211_alloc_hw()
  * @hw: the device to register as returned by ieee80211_alloc_hw()
  */
  */

+ 16 - 0
net/mac80211/Kconfig

@@ -81,6 +81,15 @@ config MAC80211_RC_SIMPLE
 	  Say N unless you know what you are doing.
 	  Say N unless you know what you are doing.
 endmenu
 endmenu
 
 
+config MAC80211_MESH
+	bool "Enable mac80211 mesh networking (pre-802.11s) support"
+	depends on MAC80211 && EXPERIMENTAL
+	---help---
+	 This options enables support of Draft 802.11s mesh networking.
+	 The implementation is based on Draft 1.08 of the Mesh Networking
+	 amendment. For more information visit http://o11s.org/.
+
+
 config MAC80211_LEDS
 config MAC80211_LEDS
 	bool "Enable LED triggers"
 	bool "Enable LED triggers"
 	depends on MAC80211 && LEDS_TRIGGERS
 	depends on MAC80211 && LEDS_TRIGGERS
@@ -166,3 +175,10 @@ config MAC80211_VERBOSE_PS_DEBUG
 	---help---
 	---help---
 	  Say Y here to print out verbose powersave
 	  Say Y here to print out verbose powersave
 	  mode debug messages.
 	  mode debug messages.
+
+config MAC80211_VERBOSE_MPL_DEBUG
+	bool "Verbose mesh peer link debugging"
+	depends on MAC80211_DEBUG && MAC80211_MESH
+	---help---
+	  Say Y here to print out verbose mesh peer link
+	  debug messages.

+ 6 - 0
net/mac80211/Makefile

@@ -36,6 +36,12 @@ mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
 	debugfs_netdev.o \
 	debugfs_netdev.o \
 	debugfs_key.o
 	debugfs_key.o
 
 
+mac80211-$(CONFIG_MAC80211_MESH) += \
+	mesh.o \
+	mesh_pathtbl.o \
+	mesh_plink.o \
+	mesh_hwmp.o
+
 
 
 # Build rate control algorithm(s)
 # Build rate control algorithm(s)
 CFLAGS_rc80211_simple.o += -DRC80211_SIMPLE_COMPILE
 CFLAGS_rc80211_simple.o += -DRC80211_SIMPLE_COMPILE

+ 312 - 48
net/mac80211/cfg.c

@@ -15,6 +15,7 @@
 #include "ieee80211_i.h"
 #include "ieee80211_i.h"
 #include "cfg.h"
 #include "cfg.h"
 #include "ieee80211_rate.h"
 #include "ieee80211_rate.h"
+#include "mesh.h"
 
 
 static enum ieee80211_if_types
 static enum ieee80211_if_types
 nl80211_type_to_mac80211_type(enum nl80211_iftype type)
 nl80211_type_to_mac80211_type(enum nl80211_iftype type)
@@ -28,13 +29,18 @@ nl80211_type_to_mac80211_type(enum nl80211_iftype type)
 		return IEEE80211_IF_TYPE_STA;
 		return IEEE80211_IF_TYPE_STA;
 	case NL80211_IFTYPE_MONITOR:
 	case NL80211_IFTYPE_MONITOR:
 		return IEEE80211_IF_TYPE_MNTR;
 		return IEEE80211_IF_TYPE_MNTR;
+#ifdef CONFIG_MAC80211_MESH
+	case NL80211_IFTYPE_MESH_POINT:
+		return IEEE80211_IF_TYPE_MESH_POINT;
+#endif
 	default:
 	default:
 		return IEEE80211_IF_TYPE_INVALID;
 		return IEEE80211_IF_TYPE_INVALID;
 	}
 	}
 }
 }
 
 
 static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
 static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
-			       enum nl80211_iftype type, u32 *flags)
+			       enum nl80211_iftype type, u32 *flags,
+			       struct vif_params *params)
 {
 {
 	struct ieee80211_local *local = wiphy_priv(wiphy);
 	struct ieee80211_local *local = wiphy_priv(wiphy);
 	enum ieee80211_if_types itype;
 	enum ieee80211_if_types itype;
@@ -49,7 +55,7 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
 	if (itype == IEEE80211_IF_TYPE_INVALID)
 	if (itype == IEEE80211_IF_TYPE_INVALID)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	err = ieee80211_if_add(local->mdev, name, &dev, itype);
+	err = ieee80211_if_add(local->mdev, name, &dev, itype, params);
 	if (err || itype != IEEE80211_IF_TYPE_MNTR || !flags)
 	if (err || itype != IEEE80211_IF_TYPE_MNTR || !flags)
 		return err;
 		return err;
 
 
@@ -78,7 +84,8 @@ static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
 }
 }
 
 
 static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
 static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
-				  enum nl80211_iftype type, u32 *flags)
+				  enum nl80211_iftype type, u32 *flags,
+				  struct vif_params *params)
 {
 {
 	struct ieee80211_local *local = wiphy_priv(wiphy);
 	struct ieee80211_local *local = wiphy_priv(wiphy);
 	struct net_device *dev;
 	struct net_device *dev;
@@ -108,6 +115,11 @@ static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
 	ieee80211_if_reinit(dev);
 	ieee80211_if_reinit(dev);
 	ieee80211_if_set_type(dev, itype);
 	ieee80211_if_set_type(dev, itype);
 
 
+	if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len)
+		ieee80211_if_sta_set_mesh_id(&sdata->u.sta,
+					     params->mesh_id_len,
+					     params->mesh_id);
+
 	if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR || !flags)
 	if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR || !flags)
 		return 0;
 		return 0;
 
 
@@ -122,7 +134,6 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_sub_if_data *sdata;
 	struct sta_info *sta = NULL;
 	struct sta_info *sta = NULL;
 	enum ieee80211_key_alg alg;
 	enum ieee80211_key_alg alg;
-	int ret;
 	struct ieee80211_key *key;
 	struct ieee80211_key *key;
 
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -156,12 +167,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
 
 
 	ieee80211_key_link(key, sdata, sta);
 	ieee80211_key_link(key, sdata, sta);
 
 
-	ret = 0;
-
-	if (sta)
-		sta_info_put(sta);
-
-	return ret;
+	return 0;
 }
 }
 
 
 static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
 static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
@@ -170,7 +176,6 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_sub_if_data *sdata;
 	struct sta_info *sta;
 	struct sta_info *sta;
 	int ret;
 	int ret;
-	struct ieee80211_key *key;
 
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
 
@@ -181,21 +186,18 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
 
 
 		ret = 0;
 		ret = 0;
 		if (sta->key) {
 		if (sta->key) {
-			key = sta->key;
-			ieee80211_key_free(key);
+			ieee80211_key_free(sta->key);
 			WARN_ON(sta->key);
 			WARN_ON(sta->key);
 		} else
 		} else
 			ret = -ENOENT;
 			ret = -ENOENT;
 
 
-		sta_info_put(sta);
 		return ret;
 		return ret;
 	}
 	}
 
 
 	if (!sdata->keys[key_idx])
 	if (!sdata->keys[key_idx])
 		return -ENOENT;
 		return -ENOENT;
 
 
-	key = sdata->keys[key_idx];
-	ieee80211_key_free(key);
+	ieee80211_key_free(sdata->keys[key_idx]);
 	WARN_ON(sdata->keys[key_idx]);
 	WARN_ON(sdata->keys[key_idx]);
 
 
 	return 0;
 	return 0;
@@ -278,8 +280,6 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
 	err = 0;
 	err = 0;
 
 
  out:
  out:
-	if (sta)
-		sta_info_put(sta);
 	return err;
 	return err;
 }
 }
 
 
@@ -295,29 +295,73 @@ static int ieee80211_config_default_key(struct wiphy *wiphy,
 	return 0;
 	return 0;
 }
 }
 
 
+static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
+{
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+	sinfo->filled = STATION_INFO_INACTIVE_TIME |
+			STATION_INFO_RX_BYTES |
+			STATION_INFO_TX_BYTES;
+
+	sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
+	sinfo->rx_bytes = sta->rx_bytes;
+	sinfo->tx_bytes = sta->tx_bytes;
+
+	if (ieee80211_vif_is_mesh(&sdata->vif)) {
+#ifdef CONFIG_MAC80211_MESH
+		sinfo->filled |= STATION_INFO_LLID |
+				 STATION_INFO_PLID |
+				 STATION_INFO_PLINK_STATE;
+
+		sinfo->llid = le16_to_cpu(sta->llid);
+		sinfo->plid = le16_to_cpu(sta->plid);
+		sinfo->plink_state = sta->plink_state;
+#endif
+	}
+}
+
+
+static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+				 int idx, u8 *mac, struct station_info *sinfo)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct sta_info *sta;
+	int ret = -ENOENT;
+
+	rcu_read_lock();
+
+	sta = sta_info_get_by_idx(local, idx, dev);
+	if (sta) {
+		ret = 0;
+		memcpy(mac, sta->addr, ETH_ALEN);
+		sta_set_sinfo(sta, sinfo);
+	}
+
+	rcu_read_unlock();
+
+	return ret;
+}
+
 static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
 static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
-				 u8 *mac, struct station_stats *stats)
+				 u8 *mac, struct station_info *sinfo)
 {
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sta_info *sta;
 	struct sta_info *sta;
+	int ret = -ENOENT;
 
 
-	sta = sta_info_get(local, mac);
-	if (!sta)
-		return -ENOENT;
+	rcu_read_lock();
 
 
 	/* XXX: verify sta->dev == dev */
 	/* XXX: verify sta->dev == dev */
 
 
-	stats->filled = STATION_STAT_INACTIVE_TIME |
-			STATION_STAT_RX_BYTES |
-			STATION_STAT_TX_BYTES;
-
-	stats->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
-	stats->rx_bytes = sta->rx_bytes;
-	stats->tx_bytes = sta->tx_bytes;
+	sta = sta_info_get(local, mac);
+	if (sta) {
+		ret = 0;
+		sta_set_sinfo(sta, sinfo);
+	}
 
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 
-	return 0;
+	return ret;
 }
 }
 
 
 /*
 /*
@@ -510,8 +554,8 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)
 	msg->xid_info[1] = 1;	/* LLC types/classes: Type 1 LLC */
 	msg->xid_info[1] = 1;	/* LLC types/classes: Type 1 LLC */
 	msg->xid_info[2] = 0;	/* XID sender's receive window size (RW) */
 	msg->xid_info[2] = 0;	/* XID sender's receive window size (RW) */
 
 
-	skb->dev = sta->dev;
-	skb->protocol = eth_type_trans(skb, sta->dev);
+	skb->dev = sta->sdata->dev;
+	skb->protocol = eth_type_trans(skb, sta->sdata->dev);
 	memset(skb->cb, 0, sizeof(skb->cb));
 	memset(skb->cb, 0, sizeof(skb->cb));
 	netif_rx(skb);
 	netif_rx(skb);
 }
 }
@@ -523,6 +567,13 @@ static void sta_apply_parameters(struct ieee80211_local *local,
 	u32 rates;
 	u32 rates;
 	int i, j;
 	int i, j;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_supported_band *sband;
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+	/*
+	 * FIXME: updating the flags is racy when this function is
+	 *	  called from ieee80211_change_station(), this will
+	 *	  be resolved in a future patch.
+	 */
 
 
 	if (params->station_flags & STATION_FLAG_CHANGED) {
 	if (params->station_flags & STATION_FLAG_CHANGED) {
 		sta->flags &= ~WLAN_STA_AUTHORIZED;
 		sta->flags &= ~WLAN_STA_AUTHORIZED;
@@ -538,6 +589,13 @@ static void sta_apply_parameters(struct ieee80211_local *local,
 			sta->flags |= WLAN_STA_WME;
 			sta->flags |= WLAN_STA_WME;
 	}
 	}
 
 
+	/*
+	 * FIXME: updating the following information is racy when this
+	 *	  function is called from ieee80211_change_station().
+	 *	  However, all this information should be static so
+	 *	  maybe we should just reject attemps to change it.
+	 */
+
 	if (params->aid) {
 	if (params->aid) {
 		sta->aid = params->aid;
 		sta->aid = params->aid;
 		if (sta->aid > IEEE80211_MAX_AID)
 		if (sta->aid > IEEE80211_MAX_AID)
@@ -560,6 +618,17 @@ static void sta_apply_parameters(struct ieee80211_local *local,
 		}
 		}
 		sta->supp_rates[local->oper_channel->band] = rates;
 		sta->supp_rates[local->oper_channel->band] = rates;
 	}
 	}
+
+	if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
+		switch (params->plink_action) {
+		case PLINK_ACTION_OPEN:
+			mesh_plink_open(sta);
+			break;
+		case PLINK_ACTION_BLOCK:
+			mesh_plink_block(sta);
+			break;
+		}
+	}
 }
 }
 
 
 static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
@@ -568,6 +637,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sta_info *sta;
 	struct sta_info *sta;
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_sub_if_data *sdata;
+	int err;
 
 
 	/* Prevent a race with changing the rate control algorithm */
 	/* Prevent a race with changing the rate control algorithm */
 	if (!netif_running(dev))
 	if (!netif_running(dev))
@@ -582,14 +652,15 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 	} else
 	} else
 		sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 		sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
 
-	sta = sta_info_add(local, dev, mac, GFP_KERNEL);
-	if (IS_ERR(sta))
-		return PTR_ERR(sta);
+	if (compare_ether_addr(mac, dev->dev_addr) == 0)
+		return -EINVAL;
 
 
-	sta->dev = sdata->dev;
-	if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
-	    sdata->vif.type == IEEE80211_IF_TYPE_AP)
-		ieee80211_send_layer2_update(sta);
+	if (is_multicast_ether_addr(mac))
+		return -EINVAL;
+
+	sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
+	if (!sta)
+		return -ENOMEM;
 
 
 	sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
 	sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
 
 
@@ -597,7 +668,20 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 
 
 	rate_control_rate_init(sta, local);
 	rate_control_rate_init(sta, local);
 
 
-	sta_info_put(sta);
+	rcu_read_lock();
+
+	err = sta_info_insert(sta);
+	if (err) {
+		sta_info_destroy(sta);
+		rcu_read_unlock();
+		return err;
+	}
+
+	if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
+	    sdata->vif.type == IEEE80211_IF_TYPE_AP)
+		ieee80211_send_layer2_update(sta);
+
+	rcu_read_unlock();
 
 
 	return 0;
 	return 0;
 }
 }
@@ -605,7 +689,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 				 u8 *mac)
 				 u8 *mac)
 {
 {
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta;
 	struct sta_info *sta;
 
 
 	if (mac) {
 	if (mac) {
@@ -614,10 +699,14 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 		if (!sta)
 		if (!sta)
 			return -ENOENT;
 			return -ENOENT;
 
 
-		sta_info_free(sta);
-		sta_info_put(sta);
+		sta_info_unlink(&sta);
+
+		if (sta) {
+			synchronize_rcu();
+			sta_info_destroy(sta);
+		}
 	} else
 	} else
-		sta_info_flush(local, dev);
+		sta_info_flush(local, sdata);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -636,23 +725,190 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 	if (!sta)
 	if (!sta)
 		return -ENOENT;
 		return -ENOENT;
 
 
-	if (params->vlan && params->vlan != sta->dev) {
+	if (params->vlan && params->vlan != sta->sdata->dev) {
 		vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 		vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
 
 		if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN ||
 		if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN ||
 		    vlansdata->vif.type != IEEE80211_IF_TYPE_AP)
 		    vlansdata->vif.type != IEEE80211_IF_TYPE_AP)
 			return -EINVAL;
 			return -EINVAL;
 
 
-		sta->dev = params->vlan;
+		sta->sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 		ieee80211_send_layer2_update(sta);
 		ieee80211_send_layer2_update(sta);
 	}
 	}
 
 
 	sta_apply_parameters(local, sta, params);
 	sta_apply_parameters(local, sta, params);
 
 
-	sta_info_put(sta);
+	return 0;
+}
 
 
+#ifdef CONFIG_MAC80211_MESH
+static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
+				 u8 *dst, u8 *next_hop)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct mesh_path *mpath;
+	struct sta_info *sta;
+	int err;
+
+	if (!netif_running(dev))
+		return -ENETDOWN;
+
+	if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+		return -ENOTSUPP;
+
+	rcu_read_lock();
+	sta = sta_info_get(local, next_hop);
+	if (!sta) {
+		rcu_read_unlock();
+		return -ENOENT;
+	}
+
+	err = mesh_path_add(dst, dev);
+	if (err) {
+		rcu_read_unlock();
+		return err;
+	}
+
+	mpath = mesh_path_lookup(dst, dev);
+	if (!mpath) {
+		rcu_read_unlock();
+		return -ENXIO;
+	}
+	mesh_path_fix_nexthop(mpath, sta);
+
+	rcu_read_unlock();
+	return 0;
+}
+
+static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
+				 u8 *dst)
+{
+	if (dst)
+		return mesh_path_del(dst, dev);
+
+	mesh_path_flush(dev);
+	return 0;
+}
+
+static int ieee80211_change_mpath(struct wiphy *wiphy,
+				    struct net_device *dev,
+				    u8 *dst, u8 *next_hop)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct mesh_path *mpath;
+	struct sta_info *sta;
+
+	if (!netif_running(dev))
+		return -ENETDOWN;
+
+	if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+		return -ENOTSUPP;
+
+	rcu_read_lock();
+
+	sta = sta_info_get(local, next_hop);
+	if (!sta) {
+		rcu_read_unlock();
+		return -ENOENT;
+	}
+
+	mpath = mesh_path_lookup(dst, dev);
+	if (!mpath) {
+		rcu_read_unlock();
+		return -ENOENT;
+	}
+
+	mesh_path_fix_nexthop(mpath, sta);
+
+	rcu_read_unlock();
+	return 0;
+}
+
+static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
+			    struct mpath_info *pinfo)
+{
+	if (mpath->next_hop)
+		memcpy(next_hop, mpath->next_hop->addr, ETH_ALEN);
+	else
+		memset(next_hop, 0, ETH_ALEN);
+
+	pinfo->filled = MPATH_INFO_FRAME_QLEN |
+			MPATH_INFO_DSN |
+			MPATH_INFO_METRIC |
+			MPATH_INFO_EXPTIME |
+			MPATH_INFO_DISCOVERY_TIMEOUT |
+			MPATH_INFO_DISCOVERY_RETRIES |
+			MPATH_INFO_FLAGS;
+
+	pinfo->frame_qlen = mpath->frame_queue.qlen;
+	pinfo->dsn = mpath->dsn;
+	pinfo->metric = mpath->metric;
+	if (time_before(jiffies, mpath->exp_time))
+		pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
+	pinfo->discovery_timeout =
+			jiffies_to_msecs(mpath->discovery_timeout);
+	pinfo->discovery_retries = mpath->discovery_retries;
+	pinfo->flags = 0;
+	if (mpath->flags & MESH_PATH_ACTIVE)
+		pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
+	if (mpath->flags & MESH_PATH_RESOLVING)
+		pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
+	if (mpath->flags & MESH_PATH_DSN_VALID)
+		pinfo->flags |= NL80211_MPATH_FLAG_DSN_VALID;
+	if (mpath->flags & MESH_PATH_FIXED)
+		pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
+	if (mpath->flags & MESH_PATH_RESOLVING)
+		pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
+
+	pinfo->flags = mpath->flags;
+}
+
+static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
+			       u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
+
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct mesh_path *mpath;
+
+	if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+		return -ENOTSUPP;
+
+	rcu_read_lock();
+	mpath = mesh_path_lookup(dst, dev);
+	if (!mpath) {
+		rcu_read_unlock();
+		return -ENOENT;
+	}
+	memcpy(dst, mpath->dst, ETH_ALEN);
+	mpath_set_pinfo(mpath, next_hop, pinfo);
+	rcu_read_unlock();
+	return 0;
+}
+
+static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
+				 int idx, u8 *dst, u8 *next_hop,
+				 struct mpath_info *pinfo)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct mesh_path *mpath;
+
+	if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
+		return -ENOTSUPP;
+
+	rcu_read_lock();
+	mpath = mesh_path_lookup_by_idx(idx, dev);
+	if (!mpath) {
+		rcu_read_unlock();
+		return -ENOENT;
+	}
+	memcpy(dst, mpath->dst, ETH_ALEN);
+	mpath_set_pinfo(mpath, next_hop, pinfo);
+	rcu_read_unlock();
 	return 0;
 	return 0;
 }
 }
+#endif
 
 
 struct cfg80211_ops mac80211_config_ops = {
 struct cfg80211_ops mac80211_config_ops = {
 	.add_virtual_intf = ieee80211_add_iface,
 	.add_virtual_intf = ieee80211_add_iface,
@@ -669,4 +925,12 @@ struct cfg80211_ops mac80211_config_ops = {
 	.del_station = ieee80211_del_station,
 	.del_station = ieee80211_del_station,
 	.change_station = ieee80211_change_station,
 	.change_station = ieee80211_change_station,
 	.get_station = ieee80211_get_station,
 	.get_station = ieee80211_get_station,
+	.dump_station = ieee80211_dump_station,
+#ifdef CONFIG_MAC80211_MESH
+	.add_mpath = ieee80211_add_mpath,
+	.del_mpath = ieee80211_del_mpath,
+	.change_mpath = ieee80211_change_mpath,
+	.get_mpath = ieee80211_get_mpath,
+	.dump_mpath = ieee80211_dump_mpath,
+#endif
 };
 };

+ 197 - 0
net/mac80211/debugfs_netdev.c

@@ -39,6 +39,29 @@ static ssize_t ieee80211_if_read(
 	return ret;
 	return ret;
 }
 }
 
 
+#ifdef CONFIG_MAC80211_MESH
+static ssize_t ieee80211_if_write(
+	struct ieee80211_sub_if_data *sdata,
+	char const __user *userbuf,
+	size_t count, loff_t *ppos,
+	int (*format)(struct ieee80211_sub_if_data *, char *))
+{
+	char buf[10];
+	int buf_size;
+
+	memset(buf, 0x00, sizeof(buf));
+	buf_size = min(count, (sizeof(buf)-1));
+	read_lock(&dev_base_lock);
+	if (copy_from_user(buf, userbuf, buf_size))
+		goto endwrite;
+	if (sdata->dev->reg_state == NETREG_REGISTERED)
+		(*format)(sdata, buf);
+endwrite:
+	read_unlock(&dev_base_lock);
+	return count;
+}
+#endif
+
 #define IEEE80211_IF_FMT(name, field, format_string)			\
 #define IEEE80211_IF_FMT(name, field, format_string)			\
 static ssize_t ieee80211_if_fmt_##name(					\
 static ssize_t ieee80211_if_fmt_##name(					\
 	const struct ieee80211_sub_if_data *sdata, char *buf,		\
 	const struct ieee80211_sub_if_data *sdata, char *buf,		\
@@ -46,6 +69,19 @@ static ssize_t ieee80211_if_fmt_##name(					\
 {									\
 {									\
 	return scnprintf(buf, buflen, format_string, sdata->field);	\
 	return scnprintf(buf, buflen, format_string, sdata->field);	\
 }
 }
+#define IEEE80211_IF_WFMT(name, field, type)				\
+static int ieee80211_if_wfmt_##name(					\
+	struct ieee80211_sub_if_data *sdata, char *buf)			\
+{									\
+	unsigned long tmp;						\
+	char *endp;							\
+									\
+	tmp = simple_strtoul(buf, &endp, 0);				\
+	if ((endp == buf) || ((type)tmp != tmp))			\
+		return -EINVAL;						\
+	sdata->field = tmp;						\
+	return 0;							\
+}
 #define IEEE80211_IF_FMT_DEC(name, field)				\
 #define IEEE80211_IF_FMT_DEC(name, field)				\
 		IEEE80211_IF_FMT(name, field, "%d\n")
 		IEEE80211_IF_FMT(name, field, "%d\n")
 #define IEEE80211_IF_FMT_HEX(name, field)				\
 #define IEEE80211_IF_FMT_HEX(name, field)				\
@@ -88,6 +124,34 @@ static const struct file_operations name##_ops = {			\
 		IEEE80211_IF_FMT_##format(name, field)			\
 		IEEE80211_IF_FMT_##format(name, field)			\
 		__IEEE80211_IF_FILE(name)
 		__IEEE80211_IF_FILE(name)
 
 
+#define __IEEE80211_IF_WFILE(name)					\
+static ssize_t ieee80211_if_read_##name(struct file *file,		\
+					char __user *userbuf,		\
+					size_t count, loff_t *ppos)	\
+{									\
+	return ieee80211_if_read(file->private_data,			\
+				 userbuf, count, ppos,			\
+				 ieee80211_if_fmt_##name);		\
+}									\
+static ssize_t ieee80211_if_write_##name(struct file *file,		\
+					const char __user *userbuf,	\
+					size_t count, loff_t *ppos)	\
+{									\
+	return ieee80211_if_write(file->private_data,			\
+				 userbuf, count, ppos,			\
+				 ieee80211_if_wfmt_##name);		\
+}									\
+static const struct file_operations name##_ops = {			\
+	.read = ieee80211_if_read_##name,				\
+	.write = ieee80211_if_write_##name,				\
+	.open = mac80211_open_file_generic,				\
+}
+
+#define IEEE80211_IF_WFILE(name, field, format, type)			\
+		IEEE80211_IF_FMT_##format(name, field)			\
+		IEEE80211_IF_WFMT(name, field, type)			\
+		__IEEE80211_IF_WFILE(name)
+
 /* common attributes */
 /* common attributes */
 IEEE80211_IF_FILE(channel_use, channel_use, DEC);
 IEEE80211_IF_FILE(channel_use, channel_use, DEC);
 IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
 IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
@@ -106,6 +170,7 @@ IEEE80211_IF_FILE(assoc_tries, u.sta.assoc_tries, DEC);
 IEEE80211_IF_FILE(auth_algs, u.sta.auth_algs, HEX);
 IEEE80211_IF_FILE(auth_algs, u.sta.auth_algs, HEX);
 IEEE80211_IF_FILE(auth_alg, u.sta.auth_alg, DEC);
 IEEE80211_IF_FILE(auth_alg, u.sta.auth_alg, DEC);
 IEEE80211_IF_FILE(auth_transaction, u.sta.auth_transaction, DEC);
 IEEE80211_IF_FILE(auth_transaction, u.sta.auth_transaction, DEC);
+IEEE80211_IF_FILE(num_beacons_sta, u.sta.num_beacons, DEC);
 
 
 static ssize_t ieee80211_if_fmt_flags(
 static ssize_t ieee80211_if_fmt_flags(
 	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
 	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
@@ -139,6 +204,42 @@ __IEEE80211_IF_FILE(num_buffered_multicast);
 /* WDS attributes */
 /* WDS attributes */
 IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
 IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
 
 
+#ifdef CONFIG_MAC80211_MESH
+/* Mesh stats attributes */
+IEEE80211_IF_FILE(fwded_frames, u.sta.mshstats.fwded_frames, DEC);
+IEEE80211_IF_FILE(dropped_frames_ttl, u.sta.mshstats.dropped_frames_ttl, DEC);
+IEEE80211_IF_FILE(dropped_frames_no_route,
+		u.sta.mshstats.dropped_frames_no_route, DEC);
+IEEE80211_IF_FILE(estab_plinks, u.sta.mshstats.estab_plinks, ATOMIC);
+
+/* Mesh parameters */
+IEEE80211_IF_WFILE(dot11MeshMaxRetries,
+		u.sta.mshcfg.dot11MeshMaxRetries, DEC, u8);
+IEEE80211_IF_WFILE(dot11MeshRetryTimeout,
+		u.sta.mshcfg.dot11MeshRetryTimeout, DEC, u16);
+IEEE80211_IF_WFILE(dot11MeshConfirmTimeout,
+		u.sta.mshcfg.dot11MeshConfirmTimeout, DEC, u16);
+IEEE80211_IF_WFILE(dot11MeshHoldingTimeout,
+		u.sta.mshcfg.dot11MeshHoldingTimeout, DEC, u16);
+IEEE80211_IF_WFILE(dot11MeshTTL, u.sta.mshcfg.dot11MeshTTL, DEC, u8);
+IEEE80211_IF_WFILE(auto_open_plinks, u.sta.mshcfg.auto_open_plinks, DEC, bool);
+IEEE80211_IF_WFILE(dot11MeshMaxPeerLinks,
+		u.sta.mshcfg.dot11MeshMaxPeerLinks, DEC, u16);
+IEEE80211_IF_WFILE(dot11MeshHWMPactivePathTimeout,
+		u.sta.mshcfg.dot11MeshHWMPactivePathTimeout, DEC, u32);
+IEEE80211_IF_WFILE(dot11MeshHWMPpreqMinInterval,
+		u.sta.mshcfg.dot11MeshHWMPpreqMinInterval, DEC, u16);
+IEEE80211_IF_WFILE(dot11MeshHWMPnetDiameterTraversalTime,
+		u.sta.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC, u16);
+IEEE80211_IF_WFILE(dot11MeshHWMPmaxPREQretries,
+		u.sta.mshcfg.dot11MeshHWMPmaxPREQretries, DEC, u8);
+IEEE80211_IF_WFILE(path_refresh_time,
+		u.sta.mshcfg.path_refresh_time, DEC, u32);
+IEEE80211_IF_WFILE(min_discovery_timeout,
+		u.sta.mshcfg.min_discovery_timeout, DEC, u16);
+#endif
+
+
 #define DEBUGFS_ADD(name, type)\
 #define DEBUGFS_ADD(name, type)\
 	sdata->debugfs.type.name = debugfs_create_file(#name, 0444,\
 	sdata->debugfs.type.name = debugfs_create_file(#name, 0444,\
 		sdata->debugfsdir, sdata, &name##_ops);
 		sdata->debugfsdir, sdata, &name##_ops);
@@ -161,6 +262,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
 	DEBUGFS_ADD(auth_alg, sta);
 	DEBUGFS_ADD(auth_alg, sta);
 	DEBUGFS_ADD(auth_transaction, sta);
 	DEBUGFS_ADD(auth_transaction, sta);
 	DEBUGFS_ADD(flags, sta);
 	DEBUGFS_ADD(flags, sta);
+	DEBUGFS_ADD(num_beacons_sta, sta);
 }
 }
 
 
 static void add_ap_files(struct ieee80211_sub_if_data *sdata)
 static void add_ap_files(struct ieee80211_sub_if_data *sdata)
@@ -192,12 +294,57 @@ static void add_monitor_files(struct ieee80211_sub_if_data *sdata)
 {
 {
 }
 }
 
 
+#ifdef CONFIG_MAC80211_MESH
+#define MESHSTATS_ADD(name)\
+	sdata->mesh_stats.name = debugfs_create_file(#name, 0444,\
+		sdata->mesh_stats_dir, sdata, &name##_ops);
+
+static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
+{
+	sdata->mesh_stats_dir = debugfs_create_dir("mesh_stats",
+				sdata->debugfsdir);
+	MESHSTATS_ADD(fwded_frames);
+	MESHSTATS_ADD(dropped_frames_ttl);
+	MESHSTATS_ADD(dropped_frames_no_route);
+	MESHSTATS_ADD(estab_plinks);
+}
+
+#define MESHPARAMS_ADD(name)\
+	sdata->mesh_config.name = debugfs_create_file(#name, 0644,\
+		sdata->mesh_config_dir, sdata, &name##_ops);
+
+static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
+{
+	sdata->mesh_config_dir = debugfs_create_dir("mesh_config",
+				sdata->debugfsdir);
+	MESHPARAMS_ADD(dot11MeshMaxRetries);
+	MESHPARAMS_ADD(dot11MeshRetryTimeout);
+	MESHPARAMS_ADD(dot11MeshConfirmTimeout);
+	MESHPARAMS_ADD(dot11MeshHoldingTimeout);
+	MESHPARAMS_ADD(dot11MeshTTL);
+	MESHPARAMS_ADD(auto_open_plinks);
+	MESHPARAMS_ADD(dot11MeshMaxPeerLinks);
+	MESHPARAMS_ADD(dot11MeshHWMPactivePathTimeout);
+	MESHPARAMS_ADD(dot11MeshHWMPpreqMinInterval);
+	MESHPARAMS_ADD(dot11MeshHWMPnetDiameterTraversalTime);
+	MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries);
+	MESHPARAMS_ADD(path_refresh_time);
+	MESHPARAMS_ADD(min_discovery_timeout);
+}
+#endif
+
 static void add_files(struct ieee80211_sub_if_data *sdata)
 static void add_files(struct ieee80211_sub_if_data *sdata)
 {
 {
 	if (!sdata->debugfsdir)
 	if (!sdata->debugfsdir)
 		return;
 		return;
 
 
 	switch (sdata->vif.type) {
 	switch (sdata->vif.type) {
+	case IEEE80211_IF_TYPE_MESH_POINT:
+#ifdef CONFIG_MAC80211_MESH
+		add_mesh_stats(sdata);
+		add_mesh_config(sdata);
+#endif
+		/* fall through */
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_IBSS:
 	case IEEE80211_IF_TYPE_IBSS:
 		add_sta_files(sdata);
 		add_sta_files(sdata);
@@ -243,6 +390,7 @@ static void del_sta_files(struct ieee80211_sub_if_data *sdata)
 	DEBUGFS_DEL(auth_alg, sta);
 	DEBUGFS_DEL(auth_alg, sta);
 	DEBUGFS_DEL(auth_transaction, sta);
 	DEBUGFS_DEL(auth_transaction, sta);
 	DEBUGFS_DEL(flags, sta);
 	DEBUGFS_DEL(flags, sta);
+	DEBUGFS_DEL(num_beacons_sta, sta);
 }
 }
 
 
 static void del_ap_files(struct ieee80211_sub_if_data *sdata)
 static void del_ap_files(struct ieee80211_sub_if_data *sdata)
@@ -274,12 +422,61 @@ static void del_monitor_files(struct ieee80211_sub_if_data *sdata)
 {
 {
 }
 }
 
 
+#ifdef CONFIG_MAC80211_MESH
+#define MESHSTATS_DEL(name)			\
+	do {						\
+		debugfs_remove(sdata->mesh_stats.name);	\
+		sdata->mesh_stats.name = NULL;		\
+	} while (0)
+
+static void del_mesh_stats(struct ieee80211_sub_if_data *sdata)
+{
+	MESHSTATS_DEL(fwded_frames);
+	MESHSTATS_DEL(dropped_frames_ttl);
+	MESHSTATS_DEL(dropped_frames_no_route);
+	MESHSTATS_DEL(estab_plinks);
+	debugfs_remove(sdata->mesh_stats_dir);
+	sdata->mesh_stats_dir = NULL;
+}
+
+#define MESHPARAMS_DEL(name)			\
+	do {						\
+		debugfs_remove(sdata->mesh_config.name);	\
+		sdata->mesh_config.name = NULL;		\
+	} while (0)
+
+static void del_mesh_config(struct ieee80211_sub_if_data *sdata)
+{
+	MESHPARAMS_DEL(dot11MeshMaxRetries);
+	MESHPARAMS_DEL(dot11MeshRetryTimeout);
+	MESHPARAMS_DEL(dot11MeshConfirmTimeout);
+	MESHPARAMS_DEL(dot11MeshHoldingTimeout);
+	MESHPARAMS_DEL(dot11MeshTTL);
+	MESHPARAMS_DEL(auto_open_plinks);
+	MESHPARAMS_DEL(dot11MeshMaxPeerLinks);
+	MESHPARAMS_DEL(dot11MeshHWMPactivePathTimeout);
+	MESHPARAMS_DEL(dot11MeshHWMPpreqMinInterval);
+	MESHPARAMS_DEL(dot11MeshHWMPnetDiameterTraversalTime);
+	MESHPARAMS_DEL(dot11MeshHWMPmaxPREQretries);
+	MESHPARAMS_DEL(path_refresh_time);
+	MESHPARAMS_DEL(min_discovery_timeout);
+	debugfs_remove(sdata->mesh_config_dir);
+	sdata->mesh_config_dir = NULL;
+}
+#endif
+
 static void del_files(struct ieee80211_sub_if_data *sdata, int type)
 static void del_files(struct ieee80211_sub_if_data *sdata, int type)
 {
 {
 	if (!sdata->debugfsdir)
 	if (!sdata->debugfsdir)
 		return;
 		return;
 
 
 	switch (type) {
 	switch (type) {
+	case IEEE80211_IF_TYPE_MESH_POINT:
+#ifdef CONFIG_MAC80211_MESH
+		del_mesh_stats(sdata);
+		del_mesh_config(sdata);
+#endif
+		/* fall through */
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_IBSS:
 	case IEEE80211_IF_TYPE_IBSS:
 		del_sta_files(sdata);
 		del_sta_files(sdata);

+ 3 - 3
net/mac80211/debugfs_sta.c

@@ -51,7 +51,7 @@ static const struct file_operations sta_ ##name## _ops = {		\
 		STA_OPS(name)
 		STA_OPS(name)
 
 
 STA_FILE(aid, aid, D);
 STA_FILE(aid, aid, D);
-STA_FILE(dev, dev->name, S);
+STA_FILE(dev, sdata->dev->name, S);
 STA_FILE(rx_packets, rx_packets, LU);
 STA_FILE(rx_packets, rx_packets, LU);
 STA_FILE(tx_packets, tx_packets, LU);
 STA_FILE(tx_packets, tx_packets, LU);
 STA_FILE(rx_bytes, rx_bytes, LU);
 STA_FILE(rx_bytes, rx_bytes, LU);
@@ -67,7 +67,7 @@ STA_FILE(last_rssi, last_rssi, D);
 STA_FILE(last_signal, last_signal, D);
 STA_FILE(last_signal, last_signal, D);
 STA_FILE(last_noise, last_noise, D);
 STA_FILE(last_noise, last_noise, D);
 STA_FILE(channel_use, channel_use, D);
 STA_FILE(channel_use, channel_use, D);
-STA_FILE(wep_weak_iv_count, wep_weak_iv_count, D);
+STA_FILE(wep_weak_iv_count, wep_weak_iv_count, LU);
 
 
 static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
 static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
 			      size_t count, loff_t *ppos)
 			      size_t count, loff_t *ppos)
@@ -200,7 +200,7 @@ static ssize_t sta_agg_status_write(struct file *file,
 		const char __user *user_buf, size_t count, loff_t *ppos)
 		const char __user *user_buf, size_t count, loff_t *ppos)
 {
 {
 	struct sta_info *sta = file->private_data;
 	struct sta_info *sta = file->private_data;
-	struct net_device *dev = sta->dev;
+	struct net_device *dev = sta->sdata->dev;
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_hw *hw = &local->hw;
 	struct ieee80211_hw *hw = &local->hw;
 	u8 *da = sta->addr;
 	u8 *da = sta->addr;

+ 2 - 0
net/mac80211/debugfs_sta.h

@@ -1,6 +1,8 @@
 #ifndef __MAC80211_DEBUGFS_STA_H
 #ifndef __MAC80211_DEBUGFS_STA_H
 #define __MAC80211_DEBUGFS_STA_H
 #define __MAC80211_DEBUGFS_STA_H
 
 
+#include "sta_info.h"
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 #ifdef CONFIG_MAC80211_DEBUGFS
 void ieee80211_sta_debugfs_add(struct sta_info *sta);
 void ieee80211_sta_debugfs_add(struct sta_info *sta);
 void ieee80211_sta_debugfs_remove(struct sta_info *sta);
 void ieee80211_sta_debugfs_remove(struct sta_info *sta);

+ 97 - 60
net/mac80211/ieee80211.c

@@ -26,6 +26,7 @@
 
 
 #include "ieee80211_i.h"
 #include "ieee80211_i.h"
 #include "ieee80211_rate.h"
 #include "ieee80211_rate.h"
+#include "mesh.h"
 #include "wep.h"
 #include "wep.h"
 #include "wme.h"
 #include "wme.h"
 #include "aes_ccm.h"
 #include "aes_ccm.h"
@@ -138,9 +139,15 @@ static void ieee80211_master_set_multicast_list(struct net_device *dev)
 
 
 static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
 static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
 {
 {
+	int meshhdrlen;
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+	meshhdrlen = (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) ? 5 : 0;
+
 	/* FIX: what would be proper limits for MTU?
 	/* FIX: what would be proper limits for MTU?
 	 * This interface uses 802.3 frames. */
 	 * This interface uses 802.3 frames. */
-	if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6) {
+	if (new_mtu < 256 ||
+		new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) {
 		printk(KERN_WARNING "%s: invalid MTU %d\n",
 		printk(KERN_WARNING "%s: invalid MTU %d\n",
 		       dev->name, new_mtu);
 		       dev->name, new_mtu);
 		return -EINVAL;
 		return -EINVAL;
@@ -176,6 +183,7 @@ static int ieee80211_open(struct net_device *dev)
 	struct ieee80211_if_init_conf conf;
 	struct ieee80211_if_init_conf conf;
 	int res;
 	int res;
 	bool need_hw_reconfig = 0;
 	bool need_hw_reconfig = 0;
+	struct sta_info *sta;
 
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
 
@@ -249,6 +257,20 @@ static int ieee80211_open(struct net_device *dev)
 	case IEEE80211_IF_TYPE_WDS:
 	case IEEE80211_IF_TYPE_WDS:
 		if (is_zero_ether_addr(sdata->u.wds.remote_addr))
 		if (is_zero_ether_addr(sdata->u.wds.remote_addr))
 			return -ENOLINK;
 			return -ENOLINK;
+
+		/* Create STA entry for the WDS peer */
+		sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
+				     GFP_KERNEL);
+		if (!sta)
+			return -ENOMEM;
+
+		sta->flags |= WLAN_STA_AUTHORIZED;
+
+		res = sta_info_insert(sta);
+		if (res) {
+			sta_info_destroy(sta);
+			return res;
+		}
 		break;
 		break;
 	case IEEE80211_IF_TYPE_VLAN:
 	case IEEE80211_IF_TYPE_VLAN:
 		if (!sdata->u.vlan.ap)
 		if (!sdata->u.vlan.ap)
@@ -258,6 +280,7 @@ static int ieee80211_open(struct net_device *dev)
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_MNTR:
 	case IEEE80211_IF_TYPE_MNTR:
 	case IEEE80211_IF_TYPE_IBSS:
 	case IEEE80211_IF_TYPE_IBSS:
+	case IEEE80211_IF_TYPE_MESH_POINT:
 		/* no special treatment */
 		/* no special treatment */
 		break;
 		break;
 	case IEEE80211_IF_TYPE_INVALID:
 	case IEEE80211_IF_TYPE_INVALID:
@@ -359,24 +382,51 @@ static int ieee80211_open(struct net_device *dev)
 
 
 static int ieee80211_stop(struct net_device *dev)
 static int ieee80211_stop(struct net_device *dev)
 {
 {
-	struct ieee80211_sub_if_data *sdata;
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_init_conf conf;
 	struct ieee80211_if_init_conf conf;
 	struct sta_info *sta;
 	struct sta_info *sta;
 	int i;
 	int i;
 
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	/*
+	 * Stop TX on this interface first.
+	 */
+	netif_stop_queue(dev);
 
 
-	list_for_each_entry(sta, &local->sta_list, list) {
-		if (sta->dev == dev)
+	/*
+	 * Now delete all active aggregation sessions.
+	 */
+	rcu_read_lock();
+
+	list_for_each_entry_rcu(sta, &local->sta_list, list) {
+		if (sta->sdata == sdata)
 			for (i = 0; i <  STA_TID_NUM; i++)
 			for (i = 0; i <  STA_TID_NUM; i++)
-				ieee80211_sta_stop_rx_ba_session(sta->dev,
+				ieee80211_sta_stop_rx_ba_session(sdata->dev,
 						sta->addr, i,
 						sta->addr, i,
 						WLAN_BACK_RECIPIENT,
 						WLAN_BACK_RECIPIENT,
 						WLAN_REASON_QSTA_LEAVE_QBSS);
 						WLAN_REASON_QSTA_LEAVE_QBSS);
 	}
 	}
 
 
-	netif_stop_queue(dev);
+	rcu_read_unlock();
+
+	/*
+	 * Remove all stations associated with this interface.
+	 *
+	 * This must be done before calling ops->remove_interface()
+	 * because otherwise we can later invoke ops->sta_notify()
+	 * whenever the STAs are removed, and that invalidates driver
+	 * assumptions about always getting a vif pointer that is valid
+	 * (because if we remove a STA after ops->remove_interface()
+	 * the driver will have removed the vif info already!)
+	 *
+	 * We could relax this and only unlink the stations from the
+	 * hash table and list but keep them on a per-sdata list that
+	 * will be inserted back again when the interface is brought
+	 * up again, but I don't currently see a use case for that,
+	 * except with WDS which gets a STA entry created when it is
+	 * brought up.
+	 */
+	sta_info_flush(local, sdata);
 
 
 	/*
 	/*
 	 * Don't count this interface for promisc/allmulti while it
 	 * Don't count this interface for promisc/allmulti while it
@@ -440,6 +490,7 @@ static int ieee80211_stop(struct net_device *dev)
 		ieee80211_configure_filter(local);
 		ieee80211_configure_filter(local);
 		netif_tx_unlock_bh(local->mdev);
 		netif_tx_unlock_bh(local->mdev);
 		break;
 		break;
+	case IEEE80211_IF_TYPE_MESH_POINT:
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_IBSS:
 	case IEEE80211_IF_TYPE_IBSS:
 		sdata->u.sta.state = IEEE80211_DISABLED;
 		sdata->u.sta.state = IEEE80211_DISABLED;
@@ -511,9 +562,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 				print_mac(mac, ra), tid);
 				print_mac(mac, ra), tid);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, ra);
 	sta = sta_info_get(local, ra);
 	if (!sta) {
 	if (!sta) {
 		printk(KERN_DEBUG "Could not find the station\n");
 		printk(KERN_DEBUG "Could not find the station\n");
+		rcu_read_unlock();
 		return -ENOENT;
 		return -ENOENT;
 	}
 	}
 
 
@@ -553,7 +607,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 		spin_unlock_bh(&local->mdev->queue_lock);
 		spin_unlock_bh(&local->mdev->queue_lock);
 		goto start_ba_exit;
 		goto start_ba_exit;
 	}
 	}
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 
 
 	/* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
 	/* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
 	 * call back right away, it must see that the flow has begun */
 	 * call back right away, it must see that the flow has begun */
@@ -590,7 +644,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 			sta->ampdu_mlme.dialog_token_allocator;
 			sta->ampdu_mlme.dialog_token_allocator;
 	sta->ampdu_mlme.tid_tx[tid].ssn = start_seq_num;
 	sta->ampdu_mlme.tid_tx[tid].ssn = start_seq_num;
 
 
-	ieee80211_send_addba_request(sta->dev, ra, tid,
+	ieee80211_send_addba_request(sta->sdata->dev, ra, tid,
 			 sta->ampdu_mlme.tid_tx[tid].dialog_token,
 			 sta->ampdu_mlme.tid_tx[tid].dialog_token,
 			 sta->ampdu_mlme.tid_tx[tid].ssn,
 			 sta->ampdu_mlme.tid_tx[tid].ssn,
 			 0x40, 5000);
 			 0x40, 5000);
@@ -603,7 +657,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 
 
 start_ba_exit:
 start_ba_exit:
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
-	sta_info_put(sta);
+	rcu_read_unlock();
 	return ret;
 	return ret;
 }
 }
 EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
 EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
@@ -626,9 +680,12 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
 				print_mac(mac, ra), tid);
 				print_mac(mac, ra), tid);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
 
+	rcu_read_lock();
 	sta = sta_info_get(local, ra);
 	sta = sta_info_get(local, ra);
-	if (!sta)
+	if (!sta) {
+		rcu_read_unlock();
 		return -ENOENT;
 		return -ENOENT;
+	}
 
 
 	/* check if the TID is in aggregation */
 	/* check if the TID is in aggregation */
 	state = &sta->ampdu_mlme.tid_tx[tid].state;
 	state = &sta->ampdu_mlme.tid_tx[tid].state;
@@ -662,7 +719,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
 
 
 stop_BA_exit:
 stop_BA_exit:
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
-	sta_info_put(sta);
+	rcu_read_unlock();
 	return ret;
 	return ret;
 }
 }
 EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
 EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
@@ -680,8 +737,10 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 		return;
 		return;
 	}
 	}
 
 
+	rcu_read_lock();
 	sta = sta_info_get(local, ra);
 	sta = sta_info_get(local, ra);
 	if (!sta) {
 	if (!sta) {
+		rcu_read_unlock();
 		printk(KERN_DEBUG "Could not find station: %s\n",
 		printk(KERN_DEBUG "Could not find station: %s\n",
 				print_mac(mac, ra));
 				print_mac(mac, ra));
 		return;
 		return;
@@ -694,7 +753,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 		printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
 		printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
 				*state);
 				*state);
 		spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
 		spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
-		sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 		return;
 	}
 	}
 
 
@@ -707,7 +766,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
 		ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
 		ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
 	}
 	}
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
-	sta_info_put(sta);
+	rcu_read_unlock();
 }
 }
 EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
 EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
 
 
@@ -728,10 +787,12 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
 	printk(KERN_DEBUG "Stop a BA session requested on DA %s tid %d\n",
 	printk(KERN_DEBUG "Stop a BA session requested on DA %s tid %d\n",
 				print_mac(mac, ra), tid);
 				print_mac(mac, ra), tid);
 
 
+	rcu_read_lock();
 	sta = sta_info_get(local, ra);
 	sta = sta_info_get(local, ra);
 	if (!sta) {
 	if (!sta) {
 		printk(KERN_DEBUG "Could not find station: %s\n",
 		printk(KERN_DEBUG "Could not find station: %s\n",
 				print_mac(mac, ra));
 				print_mac(mac, ra));
+		rcu_read_unlock();
 		return;
 		return;
 	}
 	}
 	state = &sta->ampdu_mlme.tid_tx[tid].state;
 	state = &sta->ampdu_mlme.tid_tx[tid].state;
@@ -739,13 +800,13 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
 	spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
 	spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
 	if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
 	if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
 		printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
 		printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
-		sta_info_put(sta);
 		spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
 		spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+		rcu_read_unlock();
 		return;
 		return;
 	}
 	}
 
 
 	if (*state & HT_AGG_STATE_INITIATOR_MSK)
 	if (*state & HT_AGG_STATE_INITIATOR_MSK)
-		ieee80211_send_delba(sta->dev, ra, tid,
+		ieee80211_send_delba(sta->sdata->dev, ra, tid,
 			WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
 			WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
 
 
 	agg_queue = sta->tid_to_tx_q[tid];
 	agg_queue = sta->tid_to_tx_q[tid];
@@ -766,7 +827,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
 	sta->ampdu_mlme.tid_tx[tid].addba_req_num = 0;
 	sta->ampdu_mlme.tid_tx[tid].addba_req_num = 0;
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
 
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 }
 }
 EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
 EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
 
 
@@ -867,44 +928,6 @@ void ieee80211_if_setup(struct net_device *dev)
 	dev->destructor = ieee80211_if_free;
 	dev->destructor = ieee80211_if_free;
 }
 }
 
 
-/* WDS specialties */
-
-int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	struct sta_info *sta;
-	DECLARE_MAC_BUF(mac);
-
-	if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
-		return 0;
-
-	/* Create STA entry for the new peer */
-	sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL);
-	if (IS_ERR(sta))
-		return PTR_ERR(sta);
-
-	sta->flags |= WLAN_STA_AUTHORIZED;
-
-	sta_info_put(sta);
-
-	/* Remove STA entry for the old peer */
-	sta = sta_info_get(local, sdata->u.wds.remote_addr);
-	if (sta) {
-		sta_info_free(sta);
-		sta_info_put(sta);
-	} else {
-		printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
-		       "peer %s\n",
-		       dev->name, print_mac(mac, sdata->u.wds.remote_addr));
-	}
-
-	/* Update WDS link data */
-	memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
-
-	return 0;
-}
-
 /* everything else */
 /* everything else */
 
 
 static int __ieee80211_if_config(struct net_device *dev,
 static int __ieee80211_if_config(struct net_device *dev,
@@ -925,6 +948,9 @@ static int __ieee80211_if_config(struct net_device *dev,
 		conf.bssid = sdata->u.sta.bssid;
 		conf.bssid = sdata->u.sta.bssid;
 		conf.ssid = sdata->u.sta.ssid;
 		conf.ssid = sdata->u.sta.ssid;
 		conf.ssid_len = sdata->u.sta.ssid_len;
 		conf.ssid_len = sdata->u.sta.ssid_len;
+	} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+		conf.beacon = beacon;
+		ieee80211_start_mesh(dev);
 	} else if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
 	} else if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
 		conf.ssid = sdata->u.ap.ssid;
 		conf.ssid = sdata->u.ap.ssid;
 		conf.ssid_len = sdata->u.ap.ssid_len;
 		conf.ssid_len = sdata->u.ap.ssid_len;
@@ -937,6 +963,11 @@ static int __ieee80211_if_config(struct net_device *dev,
 
 
 int ieee80211_if_config(struct net_device *dev)
 int ieee80211_if_config(struct net_device *dev)
 {
 {
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT &&
+	    (local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE))
+		return ieee80211_if_config_beacon(dev);
 	return __ieee80211_if_config(dev, NULL, NULL);
 	return __ieee80211_if_config(dev, NULL, NULL);
 }
 }
 
 
@@ -1311,6 +1342,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
 		return;
 		return;
 	}
 	}
 
 
+	rcu_read_lock();
+
 	if (status->excessive_retries) {
 	if (status->excessive_retries) {
 		struct sta_info *sta;
 		struct sta_info *sta;
 		sta = sta_info_get(local, hdr->addr1);
 		sta = sta_info_get(local, hdr->addr1);
@@ -1324,10 +1357,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
 				status->flags |= IEEE80211_TX_STATUS_TX_FILTERED;
 				status->flags |= IEEE80211_TX_STATUS_TX_FILTERED;
 				ieee80211_handle_filtered_frame(local, sta,
 				ieee80211_handle_filtered_frame(local, sta,
 								skb, status);
 								skb, status);
-				sta_info_put(sta);
+				rcu_read_unlock();
 				return;
 				return;
 			}
 			}
-			sta_info_put(sta);
 		}
 		}
 	}
 	}
 
 
@@ -1337,12 +1369,14 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
 		if (sta) {
 		if (sta) {
 			ieee80211_handle_filtered_frame(local, sta, skb,
 			ieee80211_handle_filtered_frame(local, sta, skb,
 							status);
 							status);
-			sta_info_put(sta);
+			rcu_read_unlock();
 			return;
 			return;
 		}
 		}
 	} else
 	} else
 		rate_control_tx_status(local->mdev, skb, status);
 		rate_control_tx_status(local->mdev, skb, status);
 
 
+	rcu_read_unlock();
+
 	ieee80211_led_tx(local, 0);
 	ieee80211_led_tx(local, 0);
 
 
 	/* SNMP counters
 	/* SNMP counters
@@ -1662,7 +1696,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
 
 	/* add one default STA interface */
 	/* add one default STA interface */
 	result = ieee80211_if_add(local->mdev, "wlan%d", NULL,
 	result = ieee80211_if_add(local->mdev, "wlan%d", NULL,
-				  IEEE80211_IF_TYPE_STA);
+				  IEEE80211_IF_TYPE_STA, NULL);
 	if (result)
 	if (result)
 		printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
 		printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
 		       wiphy_name(local->hw.wiphy));
 		       wiphy_name(local->hw.wiphy));
@@ -1801,6 +1835,9 @@ static void __exit ieee80211_exit(void)
 	rc80211_simple_exit();
 	rc80211_simple_exit();
 	rc80211_pid_exit();
 	rc80211_pid_exit();
 
 
+	if (mesh_allocated)
+		ieee80211s_stop();
+
 	ieee80211_wme_unregister();
 	ieee80211_wme_unregister();
 	ieee80211_debugfs_netdev_exit();
 	ieee80211_debugfs_netdev_exit();
 }
 }

+ 267 - 45
net/mac80211/ieee80211_i.h

@@ -90,6 +90,11 @@ struct ieee80211_sta_bss {
 	size_t wmm_ie_len;
 	size_t wmm_ie_len;
 	u8 *ht_ie;
 	u8 *ht_ie;
 	size_t ht_ie_len;
 	size_t ht_ie_len;
+#ifdef CONFIG_MAC80211_MESH
+	u8 *mesh_id;
+	size_t mesh_id_len;
+	u8 *mesh_cfg;
+#endif
 #define IEEE80211_MAX_SUPP_RATES 32
 #define IEEE80211_MAX_SUPP_RATES 32
 	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
 	u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
 	size_t supp_rates_len;
 	size_t supp_rates_len;
@@ -107,32 +112,81 @@ struct ieee80211_sta_bss {
 	u8 erp_value;
 	u8 erp_value;
 };
 };
 
 
+static inline u8 *bss_mesh_cfg(struct ieee80211_sta_bss *bss)
+{
+#ifdef CONFIG_MAC80211_MESH
+	return bss->mesh_cfg;
+#endif
+	return NULL;
+}
+
+static inline u8 *bss_mesh_id(struct ieee80211_sta_bss *bss)
+{
+#ifdef CONFIG_MAC80211_MESH
+	return bss->mesh_id;
+#endif
+	return NULL;
+}
+
+static inline u8 bss_mesh_id_len(struct ieee80211_sta_bss *bss)
+{
+#ifdef CONFIG_MAC80211_MESH
+	return bss->mesh_id_len;
+#endif
+	return 0;
+}
+
 
 
 typedef unsigned __bitwise__ ieee80211_tx_result;
 typedef unsigned __bitwise__ ieee80211_tx_result;
 #define TX_CONTINUE	((__force ieee80211_tx_result) 0u)
 #define TX_CONTINUE	((__force ieee80211_tx_result) 0u)
 #define TX_DROP		((__force ieee80211_tx_result) 1u)
 #define TX_DROP		((__force ieee80211_tx_result) 1u)
 #define TX_QUEUED	((__force ieee80211_tx_result) 2u)
 #define TX_QUEUED	((__force ieee80211_tx_result) 2u)
 
 
+#define IEEE80211_TX_FRAGMENTED		BIT(0)
+#define IEEE80211_TX_UNICAST		BIT(1)
+#define IEEE80211_TX_PS_BUFFERED	BIT(2)
+#define IEEE80211_TX_PROBE_LAST_FRAG	BIT(3)
+#define IEEE80211_TX_INJECTED		BIT(4)
+
+struct ieee80211_tx_data {
+	struct sk_buff *skb;
+	struct net_device *dev;
+	struct ieee80211_local *local;
+	struct ieee80211_sub_if_data *sdata;
+	struct sta_info *sta;
+	u16 fc, ethertype;
+	struct ieee80211_key *key;
+	unsigned int flags;
+
+	struct ieee80211_tx_control *control;
+	struct ieee80211_channel *channel;
+	struct ieee80211_rate *rate;
+	/* use this rate (if set) for last fragment; rate can
+	 * be set to lower rate for the first fragments, e.g.,
+	 * when using CTS protection with IEEE 802.11g. */
+	struct ieee80211_rate *last_frag_rate;
+
+	/* Extra fragments (in addition to the first fragment
+	 * in skb) */
+	int num_extra_frag;
+	struct sk_buff **extra_frag;
+};
+
+
 typedef unsigned __bitwise__ ieee80211_rx_result;
 typedef unsigned __bitwise__ ieee80211_rx_result;
 #define RX_CONTINUE		((__force ieee80211_rx_result) 0u)
 #define RX_CONTINUE		((__force ieee80211_rx_result) 0u)
 #define RX_DROP_UNUSABLE	((__force ieee80211_rx_result) 1u)
 #define RX_DROP_UNUSABLE	((__force ieee80211_rx_result) 1u)
 #define RX_DROP_MONITOR		((__force ieee80211_rx_result) 2u)
 #define RX_DROP_MONITOR		((__force ieee80211_rx_result) 2u)
 #define RX_QUEUED		((__force ieee80211_rx_result) 3u)
 #define RX_QUEUED		((__force ieee80211_rx_result) 3u)
 
 
-
-/* flags used in struct ieee80211_txrx_data.flags */
-/* whether the MSDU was fragmented */
-#define IEEE80211_TXRXD_FRAGMENTED		BIT(0)
-#define IEEE80211_TXRXD_TXUNICAST		BIT(1)
-#define IEEE80211_TXRXD_TXPS_BUFFERED		BIT(2)
-#define IEEE80211_TXRXD_TXPROBE_LAST_FRAG	BIT(3)
-#define IEEE80211_TXRXD_RXIN_SCAN		BIT(4)
+#define IEEE80211_RX_IN_SCAN		BIT(0)
 /* frame is destined to interface currently processed (incl. multicast frames) */
 /* frame is destined to interface currently processed (incl. multicast frames) */
-#define IEEE80211_TXRXD_RXRA_MATCH		BIT(5)
-#define IEEE80211_TXRXD_TX_INJECTED		BIT(6)
-#define IEEE80211_TXRXD_RX_AMSDU		BIT(7)
-#define IEEE80211_TXRXD_RX_CMNTR_REPORTED	BIT(8)
-struct ieee80211_txrx_data {
+#define IEEE80211_RX_RA_MATCH		BIT(1)
+#define IEEE80211_RX_AMSDU		BIT(2)
+#define IEEE80211_RX_CMNTR_REPORTED	BIT(3)
+#define IEEE80211_RX_FRAGMENTED		BIT(4)
+
+struct ieee80211_rx_data {
 	struct sk_buff *skb;
 	struct sk_buff *skb;
 	struct net_device *dev;
 	struct net_device *dev;
 	struct ieee80211_local *local;
 	struct ieee80211_local *local;
@@ -141,31 +195,14 @@ struct ieee80211_txrx_data {
 	u16 fc, ethertype;
 	u16 fc, ethertype;
 	struct ieee80211_key *key;
 	struct ieee80211_key *key;
 	unsigned int flags;
 	unsigned int flags;
-	union {
-		struct {
-			struct ieee80211_tx_control *control;
-			struct ieee80211_channel *channel;
-			struct ieee80211_rate *rate;
-			/* use this rate (if set) for last fragment; rate can
-			 * be set to lower rate for the first fragments, e.g.,
-			 * when using CTS protection with IEEE 802.11g. */
-			struct ieee80211_rate *last_frag_rate;
-
-			/* Extra fragments (in addition to the first fragment
-			 * in skb) */
-			int num_extra_frag;
-			struct sk_buff **extra_frag;
-		} tx;
-		struct {
-			struct ieee80211_rx_status *status;
-			struct ieee80211_rate *rate;
-			int sent_ps_buffered;
-			int queue;
-			int load;
-			u32 tkip_iv32;
-			u16 tkip_iv16;
-		} rx;
-	} u;
+
+	struct ieee80211_rx_status *status;
+	struct ieee80211_rate *rate;
+	int sent_ps_buffered;
+	int queue;
+	int load;
+	u32 tkip_iv32;
+	u16 tkip_iv16;
 };
 };
 
 
 /* flags used in struct ieee80211_tx_packet_data.flags */
 /* flags used in struct ieee80211_tx_packet_data.flags */
@@ -227,6 +264,41 @@ struct ieee80211_if_vlan {
 	struct list_head list;
 	struct list_head list;
 };
 };
 
 
+struct mesh_stats {
+	__u32 fwded_frames;		/* Mesh forwarded frames */
+	__u32 dropped_frames_ttl;	/* Not transmitted since mesh_ttl == 0*/
+	__u32 dropped_frames_no_route;	/* Not transmitted, no route found */
+	atomic_t estab_plinks;
+};
+
+#define PREQ_Q_F_START		0x1
+#define PREQ_Q_F_REFRESH	0x2
+struct mesh_preq_queue {
+	struct list_head list;
+	u8 dst[ETH_ALEN];
+	u8 flags;
+};
+
+struct mesh_config {
+	/* Timeouts in ms */
+	/* Mesh plink management parameters */
+	u16 dot11MeshRetryTimeout;
+	u16 dot11MeshConfirmTimeout;
+	u16 dot11MeshHoldingTimeout;
+	u16 dot11MeshMaxPeerLinks;
+	u8  dot11MeshMaxRetries;
+	u8  dot11MeshTTL;
+	bool auto_open_plinks;
+	/* HWMP parameters */
+	u32 dot11MeshHWMPactivePathTimeout;
+	u16 dot11MeshHWMPpreqMinInterval;
+	u16 dot11MeshHWMPnetDiameterTraversalTime;
+	u8  dot11MeshHWMPmaxPREQretries;
+	u32 path_refresh_time;
+	u16 min_discovery_timeout;
+};
+
+
 /* flags used in struct ieee80211_if_sta.flags */
 /* flags used in struct ieee80211_if_sta.flags */
 #define IEEE80211_STA_SSID_SET		BIT(0)
 #define IEEE80211_STA_SSID_SET		BIT(0)
 #define IEEE80211_STA_BSSID_SET		BIT(1)
 #define IEEE80211_STA_BSSID_SET		BIT(1)
@@ -245,7 +317,8 @@ struct ieee80211_if_sta {
 	enum {
 	enum {
 		IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
 		IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
 		IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
 		IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
-		IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
+		IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED,
+		IEEE80211_MESH_UP
 	} state;
 	} state;
 	struct timer_list timer;
 	struct timer_list timer;
 	struct work_struct work;
 	struct work_struct work;
@@ -254,6 +327,34 @@ struct ieee80211_if_sta {
 	size_t ssid_len;
 	size_t ssid_len;
 	u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
 	u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
 	size_t scan_ssid_len;
 	size_t scan_ssid_len;
+#ifdef CONFIG_MAC80211_MESH
+	struct timer_list mesh_path_timer;
+	u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
+	bool accepting_plinks;
+	size_t mesh_id_len;
+	/* Active Path Selection Protocol Identifier */
+	u8 mesh_pp_id[4];
+	/* Active Path Selection Metric Identifier */
+	u8 mesh_pm_id[4];
+	/* Congestion Control Mode Identifier */
+	u8 mesh_cc_id[4];
+	/* Local mesh Destination Sequence Number */
+	u32 dsn;
+	/* Last used PREQ ID */
+	u32 preq_id;
+	atomic_t mpaths;
+	/* Timestamp of last DSN update */
+	unsigned long last_dsn_update;
+	/* Timestamp of last DSN sent */
+	unsigned long last_preq;
+	struct mesh_rmc *rmc;
+	spinlock_t mesh_preq_queue_lock;
+	struct mesh_preq_queue preq_queue;
+	int preq_queue_len;
+	struct mesh_stats mshstats;
+	struct mesh_config mshcfg;
+	u8 mesh_seqnum[3];
+#endif
 	u16 aid;
 	u16 aid;
 	u16 ap_capab, capab;
 	u16 ap_capab, capab;
 	u8 *extra_ie; /* to be added to the end of AssocReq */
 	u8 *extra_ie; /* to be added to the end of AssocReq */
@@ -286,8 +387,25 @@ struct ieee80211_if_sta {
 	u32 supp_rates_bits[IEEE80211_NUM_BANDS];
 	u32 supp_rates_bits[IEEE80211_NUM_BANDS];
 
 
 	int wmm_last_param_set;
 	int wmm_last_param_set;
+	int num_beacons; /* number of TXed beacon frames by this STA */
 };
 };
 
 
+static inline void ieee80211_if_sta_set_mesh_id(struct ieee80211_if_sta *ifsta,
+						u8 mesh_id_len, u8 *mesh_id)
+{
+#ifdef CONFIG_MAC80211_MESH
+	ifsta->mesh_id_len = mesh_id_len;
+	memcpy(ifsta->mesh_id, mesh_id, mesh_id_len);
+#endif
+}
+
+#ifdef CONFIG_MAC80211_MESH
+#define IEEE80211_IFSTA_MESH_CTR_INC(sta, name)	\
+	do { (sta)->mshstats.name++; } while (0)
+#else
+#define IEEE80211_IFSTA_MESH_CTR_INC(sta, name) \
+	do { } while (0)
+#endif
 
 
 /* flags used in struct ieee80211_sub_if_data.flags */
 /* flags used in struct ieee80211_sub_if_data.flags */
 #define IEEE80211_SDATA_ALLMULTI	BIT(0)
 #define IEEE80211_SDATA_ALLMULTI	BIT(0)
@@ -365,6 +483,7 @@ struct ieee80211_sub_if_data {
 			struct dentry *auth_alg;
 			struct dentry *auth_alg;
 			struct dentry *auth_transaction;
 			struct dentry *auth_transaction;
 			struct dentry *flags;
 			struct dentry *flags;
+			struct dentry *num_beacons_sta;
 		} sta;
 		} sta;
 		struct {
 		struct {
 			struct dentry *channel_use;
 			struct dentry *channel_use;
@@ -390,6 +509,35 @@ struct ieee80211_sub_if_data {
 		} monitor;
 		} monitor;
 		struct dentry *default_key;
 		struct dentry *default_key;
 	} debugfs;
 	} debugfs;
+
+#ifdef CONFIG_MAC80211_MESH
+	struct dentry *mesh_stats_dir;
+	struct {
+		struct dentry *fwded_frames;
+		struct dentry *dropped_frames_ttl;
+		struct dentry *dropped_frames_no_route;
+		struct dentry *estab_plinks;
+		struct timer_list mesh_path_timer;
+	} mesh_stats;
+
+	struct dentry *mesh_config_dir;
+	struct {
+		struct dentry *dot11MeshRetryTimeout;
+		struct dentry *dot11MeshConfirmTimeout;
+		struct dentry *dot11MeshHoldingTimeout;
+		struct dentry *dot11MeshMaxRetries;
+		struct dentry *dot11MeshTTL;
+		struct dentry *auto_open_plinks;
+		struct dentry *dot11MeshMaxPeerLinks;
+		struct dentry *dot11MeshHWMPactivePathTimeout;
+		struct dentry *dot11MeshHWMPpreqMinInterval;
+		struct dentry *dot11MeshHWMPnetDiameterTraversalTime;
+		struct dentry *dot11MeshHWMPmaxPREQretries;
+		struct dentry *path_refresh_time;
+		struct dentry *min_discovery_timeout;
+	} mesh_config;
+#endif
+
 #endif
 #endif
 	/* must be last, dynamically sized area in this! */
 	/* must be last, dynamically sized area in this! */
 	struct ieee80211_vif vif;
 	struct ieee80211_vif vif;
@@ -426,6 +574,7 @@ struct ieee80211_local {
 	unsigned int filter_flags; /* FIF_* */
 	unsigned int filter_flags; /* FIF_* */
 	struct iw_statistics wstats;
 	struct iw_statistics wstats;
 	u8 wstats_flags;
 	u8 wstats_flags;
+	bool tim_in_locked_section; /* see ieee80211_beacon_get() */
 	int tx_headroom; /* required headroom for hardware/radiotap */
 	int tx_headroom; /* required headroom for hardware/radiotap */
 
 
 	enum {
 	enum {
@@ -443,9 +592,15 @@ struct ieee80211_local {
 	struct sk_buff_head skb_queue;
 	struct sk_buff_head skb_queue;
 	struct sk_buff_head skb_queue_unreliable;
 	struct sk_buff_head skb_queue_unreliable;
 
 
-	/* Station data structures */
-	rwlock_t sta_lock; /* protects STA data structures */
-	int num_sta; /* number of stations in sta_list */
+	/* Station data */
+	/*
+	 * The lock only protects the list, hash, timer and counter
+	 * against manipulation, reads are done in RCU. Additionally,
+	 * the lock protects each BSS's TIM bitmap and a few items
+	 * in a STA info structure.
+	 */
+	spinlock_t sta_lock;
+	unsigned long num_sta;
 	struct list_head sta_list;
 	struct list_head sta_list;
 	struct sta_info *sta_hash[STA_HASH_SIZE];
 	struct sta_info *sta_hash[STA_HASH_SIZE];
 	struct timer_list sta_cleanup;
 	struct timer_list sta_cleanup;
@@ -617,6 +772,57 @@ struct ieee80211_ra_tid {
 	u16 tid;
 	u16 tid;
 };
 };
 
 
+/* Parsed Information Elements */
+struct ieee802_11_elems {
+	/* pointers to IEs */
+	u8 *ssid;
+	u8 *supp_rates;
+	u8 *fh_params;
+	u8 *ds_params;
+	u8 *cf_params;
+	u8 *tim;
+	u8 *ibss_params;
+	u8 *challenge;
+	u8 *wpa;
+	u8 *rsn;
+	u8 *erp_info;
+	u8 *ext_supp_rates;
+	u8 *wmm_info;
+	u8 *wmm_param;
+	u8 *ht_cap_elem;
+	u8 *ht_info_elem;
+	u8 *mesh_config;
+	u8 *mesh_id;
+	u8 *peer_link;
+	u8 *preq;
+	u8 *prep;
+	u8 *perr;
+
+	/* length of them, respectively */
+	u8 ssid_len;
+	u8 supp_rates_len;
+	u8 fh_params_len;
+	u8 ds_params_len;
+	u8 cf_params_len;
+	u8 tim_len;
+	u8 ibss_params_len;
+	u8 challenge_len;
+	u8 wpa_len;
+	u8 rsn_len;
+	u8 erp_info_len;
+	u8 ext_supp_rates_len;
+	u8 wmm_info_len;
+	u8 wmm_param_len;
+	u8 ht_cap_elem_len;
+	u8 ht_info_elem_len;
+	u8 mesh_config_len;
+	u8 mesh_id_len;
+	u8 peer_link_len;
+	u8 preq_len;
+	u8 prep_len;
+	u8 perr_len;
+};
+
 static inline struct ieee80211_local *hw_to_local(
 static inline struct ieee80211_local *hw_to_local(
 	struct ieee80211_hw *hw)
 	struct ieee80211_hw *hw)
 {
 {
@@ -651,8 +857,7 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
 int ieee80211_hw_config(struct ieee80211_local *local);
 int ieee80211_hw_config(struct ieee80211_local *local);
 int ieee80211_if_config(struct net_device *dev);
 int ieee80211_if_config(struct net_device *dev);
 int ieee80211_if_config_beacon(struct net_device *dev);
 int ieee80211_if_config_beacon(struct net_device *dev);
-void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx);
-int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr);
+void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
 void ieee80211_if_setup(struct net_device *dev);
 void ieee80211_if_setup(struct net_device *dev);
 int ieee80211_hw_config_ht(struct ieee80211_local *local, int enable_ht,
 int ieee80211_hw_config_ht(struct ieee80211_local *local, int enable_ht,
 			   struct ieee80211_ht_info *req_ht_cap,
 			   struct ieee80211_ht_info *req_ht_cap,
@@ -686,6 +891,7 @@ int ieee80211_set_compression(struct ieee80211_local *local,
 			      struct net_device *dev, struct sta_info *sta);
 			      struct net_device *dev, struct sta_info *sta);
 int ieee80211_set_freq(struct ieee80211_local *local, int freq);
 int ieee80211_set_freq(struct ieee80211_local *local, int freq);
 /* ieee80211_sta.c */
 /* ieee80211_sta.c */
+#define IEEE80211_FC(type, stype) cpu_to_le16(type | stype)
 void ieee80211_sta_timer(unsigned long data);
 void ieee80211_sta_timer(unsigned long data);
 void ieee80211_sta_work(struct work_struct *work);
 void ieee80211_sta_work(struct work_struct *work);
 void ieee80211_sta_scan_work(struct work_struct *work);
 void ieee80211_sta_scan_work(struct work_struct *work);
@@ -726,9 +932,25 @@ void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *da,
 				u16 tid, u16 initiator, u16 reason);
 				u16 tid, u16 initiator, u16 reason);
 void sta_rx_agg_session_timer_expired(unsigned long data);
 void sta_rx_agg_session_timer_expired(unsigned long data);
 void sta_addba_resp_timer_expired(unsigned long data);
 void sta_addba_resp_timer_expired(unsigned long data);
+u64 ieee80211_sta_get_rates(struct ieee80211_local *local,
+			    struct ieee802_11_elems *elems,
+			    enum ieee80211_band band);
+void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb,
+		int encrypt);
+void ieee802_11_parse_elems(u8 *start, size_t len,
+				   struct ieee802_11_elems *elems);
+
+#ifdef CONFIG_MAC80211_MESH
+void ieee80211_start_mesh(struct net_device *dev);
+#else
+static inline void ieee80211_start_mesh(struct net_device *dev)
+{}
+#endif
+
 /* ieee80211_iface.c */
 /* ieee80211_iface.c */
 int ieee80211_if_add(struct net_device *dev, const char *name,
 int ieee80211_if_add(struct net_device *dev, const char *name,
-		     struct net_device **new_dev, int type);
+		     struct net_device **new_dev, int type,
+		     struct vif_params *params);
 void ieee80211_if_set_type(struct net_device *dev, int type);
 void ieee80211_if_set_type(struct net_device *dev, int type);
 void ieee80211_if_reinit(struct net_device *dev);
 void ieee80211_if_reinit(struct net_device *dev);
 void __ieee80211_if_del(struct ieee80211_local *local,
 void __ieee80211_if_del(struct ieee80211_local *local,

+ 22 - 14
net/mac80211/ieee80211_iface.c

@@ -15,6 +15,7 @@
 #include "ieee80211_i.h"
 #include "ieee80211_i.h"
 #include "sta_info.h"
 #include "sta_info.h"
 #include "debugfs_netdev.h"
 #include "debugfs_netdev.h"
+#include "mesh.h"
 
 
 void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata)
 void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata)
 {
 {
@@ -39,7 +40,8 @@ static void ieee80211_if_sdata_deinit(struct ieee80211_sub_if_data *sdata)
 
 
 /* Must be called with rtnl lock held. */
 /* Must be called with rtnl lock held. */
 int ieee80211_if_add(struct net_device *dev, const char *name,
 int ieee80211_if_add(struct net_device *dev, const char *name,
-		     struct net_device **new_dev, int type)
+		     struct net_device **new_dev, int type,
+		     struct vif_params *params)
 {
 {
 	struct net_device *ndev;
 	struct net_device *ndev;
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
@@ -78,6 +80,12 @@ int ieee80211_if_add(struct net_device *dev, const char *name,
 	ieee80211_debugfs_add_netdev(sdata);
 	ieee80211_debugfs_add_netdev(sdata);
 	ieee80211_if_set_type(ndev, type);
 	ieee80211_if_set_type(ndev, type);
 
 
+	if (ieee80211_vif_is_mesh(&sdata->vif) &&
+	    params && params->mesh_id_len)
+		ieee80211_if_sta_set_mesh_id(&sdata->u.sta,
+					     params->mesh_id_len,
+					     params->mesh_id);
+
 	/* we're under RTNL so all this is fine */
 	/* we're under RTNL so all this is fine */
 	if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) {
 	if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) {
 		__ieee80211_if_del(local, sdata);
 		__ieee80211_if_del(local, sdata);
@@ -134,6 +142,7 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
 		sdata->bss = &sdata->u.ap;
 		sdata->bss = &sdata->u.ap;
 		INIT_LIST_HEAD(&sdata->u.ap.vlans);
 		INIT_LIST_HEAD(&sdata->u.ap.vlans);
 		break;
 		break;
+	case IEEE80211_IF_TYPE_MESH_POINT:
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_IBSS: {
 	case IEEE80211_IF_TYPE_IBSS: {
 		struct ieee80211_sub_if_data *msdata;
 		struct ieee80211_sub_if_data *msdata;
@@ -155,6 +164,9 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
 
 
 		msdata = IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev);
 		msdata = IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev);
 		sdata->bss = &msdata->u.ap;
 		sdata->bss = &msdata->u.ap;
+
+		if (ieee80211_vif_is_mesh(&sdata->vif))
+			ieee80211_mesh_init_sdata(sdata);
 		break;
 		break;
 	}
 	}
 	case IEEE80211_IF_TYPE_MNTR:
 	case IEEE80211_IF_TYPE_MNTR:
@@ -175,8 +187,8 @@ void ieee80211_if_reinit(struct net_device *dev)
 {
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	struct sta_info *sta;
 	struct sk_buff *skb;
 	struct sk_buff *skb;
+	int flushed;
 
 
 	ASSERT_RTNL();
 	ASSERT_RTNL();
 
 
@@ -184,6 +196,10 @@ void ieee80211_if_reinit(struct net_device *dev)
 
 
 	ieee80211_if_sdata_deinit(sdata);
 	ieee80211_if_sdata_deinit(sdata);
 
 
+	/* Need to handle mesh specially to allow eliding the function call */
+	if (ieee80211_vif_is_mesh(&sdata->vif))
+		mesh_rmc_free(dev);
+
 	switch (sdata->vif.type) {
 	switch (sdata->vif.type) {
 	case IEEE80211_IF_TYPE_INVALID:
 	case IEEE80211_IF_TYPE_INVALID:
 		/* cannot happen */
 		/* cannot happen */
@@ -224,17 +240,9 @@ void ieee80211_if_reinit(struct net_device *dev)
 		break;
 		break;
 	}
 	}
 	case IEEE80211_IF_TYPE_WDS:
 	case IEEE80211_IF_TYPE_WDS:
-		sta = sta_info_get(local, sdata->u.wds.remote_addr);
-		if (sta) {
-			sta_info_free(sta);
-			sta_info_put(sta);
-		} else {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-			printk(KERN_DEBUG "%s: Someone had deleted my STA "
-			       "entry for the WDS link\n", dev->name);
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-		}
+		/* nothing to do */
 		break;
 		break;
+	case IEEE80211_IF_TYPE_MESH_POINT:
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_IBSS:
 	case IEEE80211_IF_TYPE_IBSS:
 		kfree(sdata->u.sta.extra_ie);
 		kfree(sdata->u.sta.extra_ie);
@@ -257,8 +265,8 @@ void ieee80211_if_reinit(struct net_device *dev)
 		break;
 		break;
 	}
 	}
 
 
-	/* remove all STAs that are bound to this virtual interface */
-	sta_info_flush(local, dev);
+	flushed = sta_info_flush(local, sdata);
+	WARN_ON(flushed);
 
 
 	memset(&sdata->u, 0, sizeof(sdata->u));
 	memset(&sdata->u, 0, sizeof(sdata->u));
 	ieee80211_if_sdata_init(sdata);
 	ieee80211_if_sdata_init(sdata);

+ 30 - 31
net/mac80211/ieee80211_ioctl.c

@@ -33,8 +33,7 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
 				    size_t key_len)
 				    size_t key_len)
 {
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	int ret;
-	struct sta_info *sta = NULL;
+	struct sta_info *sta;
 	struct ieee80211_key *key;
 	struct ieee80211_key *key;
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_sub_if_data *sdata;
 
 
@@ -51,24 +50,23 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
 			key = sdata->keys[idx];
 			key = sdata->keys[idx];
 		} else {
 		} else {
 			sta = sta_info_get(local, sta_addr);
 			sta = sta_info_get(local, sta_addr);
-			if (!sta) {
-				ret = -ENOENT;
-				key = NULL;
-				goto err_out;
-			}
-
+			if (!sta)
+				return -ENOENT;
 			key = sta->key;
 			key = sta->key;
 		}
 		}
 
 
 		if (!key)
 		if (!key)
-			ret = -ENOENT;
-		else
-			ret = 0;
+			return -ENOENT;
+
+		ieee80211_key_free(key);
+		return 0;
 	} else {
 	} else {
 		key = ieee80211_key_alloc(alg, idx, key_len, _key);
 		key = ieee80211_key_alloc(alg, idx, key_len, _key);
 		if (!key)
 		if (!key)
 			return -ENOMEM;
 			return -ENOMEM;
 
 
+		sta = NULL;
+
 		if (!is_broadcast_ether_addr(sta_addr)) {
 		if (!is_broadcast_ether_addr(sta_addr)) {
 			set_tx_key = 0;
 			set_tx_key = 0;
 			/*
 			/*
@@ -78,14 +76,14 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
 			 * work around this.
 			 * work around this.
 			 */
 			 */
 			if (idx != 0 && alg != ALG_WEP) {
 			if (idx != 0 && alg != ALG_WEP) {
-				ret = -EINVAL;
-				goto err_out;
+				ieee80211_key_free(key);
+				return -EINVAL;
 			}
 			}
 
 
 			sta = sta_info_get(local, sta_addr);
 			sta = sta_info_get(local, sta_addr);
 			if (!sta) {
 			if (!sta) {
-				ret = -ENOENT;
-				goto err_out;
+				ieee80211_key_free(key);
+				return -ENOENT;
 			}
 			}
 		}
 		}
 
 
@@ -93,18 +91,9 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
 
 
 		if (set_tx_key || (!sta && !sdata->default_key && key))
 		if (set_tx_key || (!sta && !sdata->default_key && key))
 			ieee80211_set_default_key(sdata, idx);
 			ieee80211_set_default_key(sdata, idx);
-
-		/* don't free key later */
-		key = NULL;
-
-		ret = 0;
 	}
 	}
 
 
- err_out:
-	if (sta)
-		sta_info_put(sta);
-	ieee80211_key_free(key);
-	return ret;
+	return 0;
 }
 }
 
 
 static int ieee80211_ioctl_siwgenie(struct net_device *dev,
 static int ieee80211_ioctl_siwgenie(struct net_device *dev,
@@ -479,10 +468,20 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
 		ieee80211_sta_req_auth(dev, &sdata->u.sta);
 		ieee80211_sta_req_auth(dev, &sdata->u.sta);
 		return 0;
 		return 0;
 	} else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
 	} else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
-		if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
-			   ETH_ALEN) == 0)
-			return 0;
-		return ieee80211_if_update_wds(dev, (u8 *) &ap_addr->sa_data);
+		/*
+		 * If it is necessary to update the WDS peer address
+		 * while the interface is running, then we need to do
+		 * more work here, namely if it is running we need to
+		 * add a new and remove the old STA entry, this is
+		 * normally handled by _open() and _stop().
+		 */
+		if (netif_running(dev))
+			return -EBUSY;
+
+		memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
+		       ETH_ALEN);
+
+		return 0;
 	}
 	}
 
 
 	return -EOPNOTSUPP;
 	return -EOPNOTSUPP;
@@ -525,6 +524,7 @@ static int ieee80211_ioctl_siwscan(struct net_device *dev,
 
 
 	if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
 	if (sdata->vif.type != IEEE80211_IF_TYPE_STA &&
 	    sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
 	    sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
+	    sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT &&
 	    sdata->vif.type != IEEE80211_IF_TYPE_AP)
 	    sdata->vif.type != IEEE80211_IF_TYPE_AP)
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
 
 
@@ -624,7 +624,7 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
 	else
 	else
 		rate->value = 0;
 		rate->value = 0;
 	rate->value *= 100000;
 	rate->value *= 100000;
-	sta_info_put(sta);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -999,7 +999,6 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev
 		wstats->qual.qual = sta->last_signal;
 		wstats->qual.qual = sta->last_signal;
 		wstats->qual.noise = sta->last_noise;
 		wstats->qual.noise = sta->last_noise;
 		wstats->qual.updated = local->wstats_flags;
 		wstats->qual.updated = local->wstats_flags;
-		sta_info_put(sta);
 	}
 	}
 	return wstats;
 	return wstats;
 }
 }

+ 5 - 3
net/mac80211/ieee80211_rate.c

@@ -170,9 +170,12 @@ void rate_control_get_rate(struct net_device *dev,
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct rate_control_ref *ref = local->rate_ctrl;
 	struct rate_control_ref *ref = local->rate_ctrl;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-	struct sta_info *sta = sta_info_get(local, hdr->addr1);
+	struct sta_info *sta;
 	int i;
 	int i;
 
 
+	rcu_read_lock();
+	sta = sta_info_get(local, hdr->addr1);
+
 	memset(sel, 0, sizeof(struct rate_selection));
 	memset(sel, 0, sizeof(struct rate_selection));
 
 
 	ref->ops->get_rate(ref->priv, dev, sband, skb, sel);
 	ref->ops->get_rate(ref->priv, dev, sband, skb, sel);
@@ -190,8 +193,7 @@ void rate_control_get_rate(struct net_device *dev,
 		}
 		}
 	}
 	}
 
 
-	if (sta)
-		sta_info_put(sta);
+	rcu_read_unlock();
 }
 }
 
 
 struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)
 struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)

+ 1 - 0
net/mac80211/ieee80211_rate.h

@@ -14,6 +14,7 @@
 #include <linux/netdevice.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
 #include <linux/skbuff.h>
 #include <linux/types.h>
 #include <linux/types.h>
+#include <linux/kref.h>
 #include <net/mac80211.h>
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
 #include "ieee80211_i.h"
 #include "sta_info.h"
 #include "sta_info.h"

文件差异内容过多而无法显示
+ 420 - 148
net/mac80211/ieee80211_sta.c


+ 12 - 6
net/mac80211/key.c

@@ -20,8 +20,8 @@
 #include "aes_ccm.h"
 #include "aes_ccm.h"
 
 
 
 
-/*
- * Key handling basics
+/**
+ * DOC: Key handling basics
  *
  *
  * Key handling in mac80211 is done based on per-interface (sub_if_data)
  * Key handling in mac80211 is done based on per-interface (sub_if_data)
  * keys and per-station keys. Since each station belongs to an interface,
  * keys and per-station keys. Since each station belongs to an interface,
@@ -174,6 +174,9 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
 {
 {
 	int idx, defkey;
 	int idx, defkey;
 
 
+	if (new)
+		list_add(&new->list, &sdata->key_list);
+
 	if (sta) {
 	if (sta) {
 		rcu_assign_pointer(sta->key, new);
 		rcu_assign_pointer(sta->key, new);
 	} else {
 	} else {
@@ -190,9 +193,6 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
 			ieee80211_set_default_key(sdata, -1);
 			ieee80211_set_default_key(sdata, -1);
 
 
 		rcu_assign_pointer(sdata->keys[idx], new);
 		rcu_assign_pointer(sdata->keys[idx], new);
-		if (new)
-			list_add(&new->list, &sdata->key_list);
-
 		if (defkey && new)
 		if (defkey && new)
 			ieee80211_set_default_key(sdata, new->conf.keyidx);
 			ieee80211_set_default_key(sdata, new->conf.keyidx);
 	}
 	}
@@ -240,14 +240,17 @@ void ieee80211_key_link(struct ieee80211_key *key,
 		if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
 		if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
 			struct sta_info *ap;
 			struct sta_info *ap;
 
 
+			rcu_read_lock();
+
 			/* same here, the AP could be using QoS */
 			/* same here, the AP could be using QoS */
 			ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
 			ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
 			if (ap) {
 			if (ap) {
 				if (ap->flags & WLAN_STA_WME)
 				if (ap->flags & WLAN_STA_WME)
 					key->conf.flags |=
 					key->conf.flags |=
 						IEEE80211_KEY_FLAG_WMM_STA;
 						IEEE80211_KEY_FLAG_WMM_STA;
-				sta_info_put(ap);
 			}
 			}
+
+			rcu_read_unlock();
 		}
 		}
 	}
 	}
 
 
@@ -290,6 +293,9 @@ void ieee80211_key_free(struct ieee80211_key *key)
 			__ieee80211_key_replace(key->sdata, key->sta,
 			__ieee80211_key_replace(key->sdata, key->sta,
 						key, NULL);
 						key, NULL);
 
 
+		/*
+		 * Do NOT remove this without looking at sta_info_destroy()
+		 */
 		synchronize_rcu();
 		synchronize_rcu();
 
 
 		/*
 		/*

+ 449 - 0
net/mac80211/mesh.c

@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2008 open80211s Ltd.
+ * Authors:    Luis Carlos Cobo <luisca@cozybit.com>
+ * 	       Javier Cardona <javier@cozybit.com>
+ *
+ * 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.
+ */
+
+#include "ieee80211_i.h"
+#include "mesh.h"
+
+#define PP_OFFSET 	1		/* Path Selection Protocol */
+#define PM_OFFSET	5		/* Path Selection Metric   */
+#define CC_OFFSET	9		/* Congestion Control Mode */
+#define CAPAB_OFFSET 17
+#define ACCEPT_PLINKS 0x80
+
+int mesh_allocated;
+static struct kmem_cache *rm_cache;
+
+void ieee80211s_init(void)
+{
+	mesh_pathtbl_init();
+	mesh_allocated = 1;
+	rm_cache = kmem_cache_create("mesh_rmc", sizeof(struct rmc_entry),
+				     0, 0, NULL);
+}
+
+void ieee80211s_stop(void)
+{
+	mesh_pathtbl_unregister();
+	kmem_cache_destroy(rm_cache);
+}
+
+/**
+ * mesh_matches_local - check if the config of a mesh point matches ours
+ *
+ * @ie: information elements of a management frame from the mesh peer
+ * @dev: local mesh interface
+ *
+ * This function checks if the mesh configuration of a mesh point matches the
+ * local mesh configuration, i.e. if both nodes belong to the same mesh network.
+ */
+bool mesh_matches_local(struct ieee802_11_elems *ie, struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_if_sta *sta = &sdata->u.sta;
+
+	/*
+	 * As support for each feature is added, check for matching
+	 * - On mesh config capabilities
+	 *   - Power Save Support En
+	 *   - Sync support enabled
+	 *   - Sync support active
+	 *   - Sync support required from peer
+	 *   - MDA enabled
+	 * - Power management control on fc
+	 */
+	if (sta->mesh_id_len == ie->mesh_id_len &&
+		memcmp(sta->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 &&
+		memcmp(sta->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 &&
+		memcmp(sta->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 &&
+		memcmp(sta->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0)
+		return true;
+
+	return false;
+}
+
+/**
+ * mesh_peer_accepts_plinks - check if an mp is willing to establish peer links
+ *
+ * @ie: information elements of a management frame from the mesh peer
+ * @dev: local mesh interface
+ */
+bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie,
+			      struct net_device *dev)
+{
+	return (*(ie->mesh_config + CAPAB_OFFSET) & ACCEPT_PLINKS) != 0;
+}
+
+/**
+ * mesh_accept_plinks_update: update accepting_plink in local mesh beacons
+ *
+ * @sdata: mesh interface in which mesh beacons are going to be updated
+ */
+void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)
+{
+	bool free_plinks;
+
+	/* In case mesh_plink_free_count > 0 and mesh_plinktbl_capacity == 0,
+	 * the mesh interface might be able to establish plinks with peers that
+	 * are already on the table but are not on PLINK_ESTAB state. However,
+	 * in general the mesh interface is not accepting peer link requests
+	 * from new peers, and that must be reflected in the beacon
+	 */
+	free_plinks = mesh_plink_availables(sdata);
+
+	if (free_plinks != sdata->u.sta.accepting_plinks)
+		ieee80211_sta_timer((unsigned long) sdata);
+}
+
+void mesh_ids_set_default(struct ieee80211_if_sta *sta)
+{
+	u8 def_id[4] = {0x00, 0x0F, 0xAC, 0xff};
+
+	memcpy(sta->mesh_pp_id, def_id, 4);
+	memcpy(sta->mesh_pm_id, def_id, 4);
+	memcpy(sta->mesh_cc_id, def_id, 4);
+}
+
+int mesh_rmc_init(struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	int i;
+
+	sdata->u.sta.rmc = kmalloc(sizeof(struct mesh_rmc), GFP_KERNEL);
+	if (!sdata->u.sta.rmc)
+		return -ENOMEM;
+	sdata->u.sta.rmc->idx_mask = RMC_BUCKETS - 1;
+	for (i = 0; i < RMC_BUCKETS; i++)
+		INIT_LIST_HEAD(&sdata->u.sta.rmc->bucket[i].list);
+	return 0;
+}
+
+void mesh_rmc_free(struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct mesh_rmc *rmc = sdata->u.sta.rmc;
+	struct rmc_entry *p, *n;
+	int i;
+
+	if (!sdata->u.sta.rmc)
+		return;
+
+	for (i = 0; i < RMC_BUCKETS; i++)
+		list_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) {
+			list_del(&p->list);
+			kmem_cache_free(rm_cache, p);
+		}
+
+	kfree(rmc);
+	sdata->u.sta.rmc = NULL;
+}
+
+/**
+ * mesh_rmc_check - Check frame in recent multicast cache and add if absent.
+ *
+ * @sa:		source address
+ * @mesh_hdr:	mesh_header
+ *
+ * Returns: 0 if the frame is not in the cache, nonzero otherwise.
+ *
+ * Checks using the source address and the mesh sequence number if we have
+ * received this frame lately. If the frame is not in the cache, it is added to
+ * it.
+ */
+int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
+		   struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct mesh_rmc *rmc = sdata->u.sta.rmc;
+	u32 seqnum = 0;
+	int entries = 0;
+	u8 idx;
+	struct rmc_entry *p, *n;
+
+	/* Don't care about endianness since only match matters */
+	memcpy(&seqnum, mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));
+	idx = mesh_hdr->seqnum[0] & rmc->idx_mask;
+	list_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) {
+		++entries;
+		if (time_after(jiffies, p->exp_time) ||
+				(entries == RMC_QUEUE_MAX_LEN)) {
+			list_del(&p->list);
+			kmem_cache_free(rm_cache, p);
+			--entries;
+		} else if ((seqnum == p->seqnum)
+				&& (memcmp(sa, p->sa, ETH_ALEN) == 0))
+			return -1;
+	}
+
+	p = kmem_cache_alloc(rm_cache, GFP_ATOMIC);
+	if (!p) {
+		printk(KERN_DEBUG "o11s: could not allocate RMC entry\n");
+		return 0;
+	}
+	p->seqnum = seqnum;
+	p->exp_time = jiffies + RMC_TIMEOUT;
+	memcpy(p->sa, sa, ETH_ALEN);
+	list_add(&p->list, &rmc->bucket[idx].list);
+	return 0;
+}
+
+void mesh_mgmt_ies_add(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_supported_band *sband;
+	u8 *pos;
+	int len, i, rate;
+
+	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+	len = sband->n_bitrates;
+	if (len > 8)
+		len = 8;
+	pos = skb_put(skb, len + 2);
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = len;
+	for (i = 0; i < len; i++) {
+		rate = sband->bitrates[i].bitrate;
+		*pos++ = (u8) (rate / 5);
+	}
+
+	if (sband->n_bitrates > len) {
+		pos = skb_put(skb, sband->n_bitrates - len + 2);
+		*pos++ = WLAN_EID_EXT_SUPP_RATES;
+		*pos++ = sband->n_bitrates - len;
+		for (i = len; i < sband->n_bitrates; i++) {
+			rate = sband->bitrates[i].bitrate;
+			*pos++ = (u8) (rate / 5);
+		}
+	}
+
+	pos = skb_put(skb, 2 + sdata->u.sta.mesh_id_len);
+	*pos++ = WLAN_EID_MESH_ID;
+	*pos++ = sdata->u.sta.mesh_id_len;
+	if (sdata->u.sta.mesh_id_len)
+		memcpy(pos, sdata->u.sta.mesh_id, sdata->u.sta.mesh_id_len);
+
+	pos = skb_put(skb, 21);
+	*pos++ = WLAN_EID_MESH_CONFIG;
+	*pos++ = MESH_CFG_LEN;
+	/* Version */
+	*pos++ = 1;
+
+	/* Active path selection protocol ID */
+	memcpy(pos, sdata->u.sta.mesh_pp_id, 4);
+	pos += 4;
+
+	/* Active path selection metric ID   */
+	memcpy(pos, sdata->u.sta.mesh_pm_id, 4);
+	pos += 4;
+
+	/* Congestion control mode identifier */
+	memcpy(pos, sdata->u.sta.mesh_cc_id, 4);
+	pos += 4;
+
+	/* Channel precedence:
+	 * Not running simple channel unification protocol
+	 */
+	memset(pos, 0x00, 4);
+	pos += 4;
+
+	/* Mesh capability */
+	sdata->u.sta.accepting_plinks = mesh_plink_availables(sdata);
+	*pos++ = sdata->u.sta.accepting_plinks ? ACCEPT_PLINKS : 0x00;
+	*pos++ = 0x00;
+
+	return;
+}
+
+u32 mesh_table_hash(u8 *addr, struct net_device *dev, struct mesh_table *tbl)
+{
+	/* Use last four bytes of hw addr and interface index as hash index */
+	return jhash_2words(*(u32 *)(addr+2), dev->ifindex, tbl->hash_rnd)
+		& tbl->hash_mask;
+}
+
+u8 mesh_id_hash(u8 *mesh_id, int mesh_id_len)
+{
+	if (!mesh_id_len)
+		return 1;
+	else if (mesh_id_len == 1)
+		return (u8) mesh_id[0];
+	else
+		return (u8) (mesh_id[0] + 2 * mesh_id[1]);
+}
+
+struct mesh_table *mesh_table_alloc(int size_order)
+{
+	int i;
+	struct mesh_table *newtbl;
+
+	newtbl = kmalloc(sizeof(struct mesh_table), GFP_KERNEL);
+	if (!newtbl)
+		return NULL;
+
+	newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) *
+			(1 << size_order), GFP_KERNEL);
+
+	if (!newtbl->hash_buckets) {
+		kfree(newtbl);
+		return NULL;
+	}
+
+	newtbl->hashwlock = kmalloc(sizeof(spinlock_t) *
+			(1 << size_order), GFP_KERNEL);
+	if (!newtbl->hashwlock) {
+		kfree(newtbl->hash_buckets);
+		kfree(newtbl);
+		return NULL;
+	}
+
+	newtbl->size_order = size_order;
+	newtbl->hash_mask = (1 << size_order) - 1;
+	atomic_set(&newtbl->entries,  0);
+	get_random_bytes(&newtbl->hash_rnd,
+			sizeof(newtbl->hash_rnd));
+	for (i = 0; i <= newtbl->hash_mask; i++)
+		spin_lock_init(&newtbl->hashwlock[i]);
+
+	return newtbl;
+}
+
+void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
+{
+	struct hlist_head *mesh_hash;
+	struct hlist_node *p, *q;
+	int i;
+
+	mesh_hash = tbl->hash_buckets;
+	for (i = 0; i <= tbl->hash_mask; i++) {
+		spin_lock(&tbl->hashwlock[i]);
+		hlist_for_each_safe(p, q, &mesh_hash[i]) {
+			tbl->free_node(p, free_leafs);
+			atomic_dec(&tbl->entries);
+		}
+		spin_unlock(&tbl->hashwlock[i]);
+	}
+	kfree(tbl->hash_buckets);
+	kfree(tbl->hashwlock);
+	kfree(tbl);
+}
+
+static void ieee80211_mesh_path_timer(unsigned long data)
+{
+	struct ieee80211_sub_if_data *sdata =
+		(struct ieee80211_sub_if_data *) data;
+	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+	struct ieee80211_local *local = wdev_priv(&sdata->wdev);
+
+	queue_work(local->hw.workqueue, &ifsta->work);
+}
+
+struct mesh_table *mesh_table_grow(struct mesh_table *tbl)
+{
+	struct mesh_table *newtbl;
+	struct hlist_head *oldhash;
+	struct hlist_node *p;
+	int err = 0;
+	int i;
+
+	if (atomic_read(&tbl->entries)
+			< tbl->mean_chain_len * (tbl->hash_mask + 1)) {
+		err = -EPERM;
+		goto endgrow;
+	}
+
+	newtbl = mesh_table_alloc(tbl->size_order + 1);
+	if (!newtbl) {
+		err = -ENOMEM;
+		goto endgrow;
+	}
+
+	newtbl->free_node = tbl->free_node;
+	newtbl->mean_chain_len = tbl->mean_chain_len;
+	newtbl->copy_node = tbl->copy_node;
+	atomic_set(&newtbl->entries, atomic_read(&tbl->entries));
+
+	oldhash = tbl->hash_buckets;
+	for (i = 0; i <= tbl->hash_mask; i++)
+		hlist_for_each(p, &oldhash[i])
+			tbl->copy_node(p, newtbl);
+
+endgrow:
+	if (err)
+		return NULL;
+	else
+		return newtbl;
+}
+
+/**
+ * ieee80211_new_mesh_header - create a new mesh header
+ * @meshhdr:    uninitialized mesh header
+ * @sdata:	mesh interface to be used
+ *
+ * Return the header length.
+ */
+int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
+		struct ieee80211_sub_if_data *sdata)
+{
+	meshhdr->flags = 0;
+	meshhdr->ttl = sdata->u.sta.mshcfg.dot11MeshTTL;
+
+	meshhdr->seqnum[0] = sdata->u.sta.mesh_seqnum[0]++;
+	meshhdr->seqnum[1] = sdata->u.sta.mesh_seqnum[1];
+	meshhdr->seqnum[2] = sdata->u.sta.mesh_seqnum[2];
+
+	if (sdata->u.sta.mesh_seqnum[0] == 0) {
+		sdata->u.sta.mesh_seqnum[1]++;
+		if (sdata->u.sta.mesh_seqnum[1] == 0)
+			sdata->u.sta.mesh_seqnum[2]++;
+	}
+
+	return 5;
+}
+
+void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
+{
+	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+
+	ifsta->mshcfg.dot11MeshRetryTimeout = MESH_RET_T;
+	ifsta->mshcfg.dot11MeshConfirmTimeout = MESH_CONF_T;
+	ifsta->mshcfg.dot11MeshHoldingTimeout = MESH_HOLD_T;
+	ifsta->mshcfg.dot11MeshMaxRetries = MESH_MAX_RETR;
+	ifsta->mshcfg.dot11MeshTTL = MESH_TTL;
+	ifsta->mshcfg.auto_open_plinks = true;
+	ifsta->mshcfg.dot11MeshMaxPeerLinks =
+		MESH_MAX_ESTAB_PLINKS;
+	ifsta->mshcfg.dot11MeshHWMPactivePathTimeout =
+		MESH_PATH_TIMEOUT;
+	ifsta->mshcfg.dot11MeshHWMPpreqMinInterval =
+		MESH_PREQ_MIN_INT;
+	ifsta->mshcfg.dot11MeshHWMPnetDiameterTraversalTime =
+		MESH_DIAM_TRAVERSAL_TIME;
+	ifsta->mshcfg.dot11MeshHWMPmaxPREQretries =
+		MESH_MAX_PREQ_RETRIES;
+	ifsta->mshcfg.path_refresh_time =
+		MESH_PATH_REFRESH_TIME;
+	ifsta->mshcfg.min_discovery_timeout =
+		MESH_MIN_DISCOVERY_TIMEOUT;
+	ifsta->accepting_plinks = true;
+	ifsta->preq_id = 0;
+	ifsta->dsn = 0;
+	atomic_set(&ifsta->mpaths, 0);
+	mesh_rmc_init(sdata->dev);
+	ifsta->last_preq = jiffies;
+	/* Allocate all mesh structures when creating the first mesh interface. */
+	if (!mesh_allocated)
+		ieee80211s_init();
+	mesh_ids_set_default(ifsta);
+	setup_timer(&ifsta->mesh_path_timer,
+		    ieee80211_mesh_path_timer,
+		    (unsigned long) sdata);
+	INIT_LIST_HEAD(&ifsta->preq_queue.list);
+	spin_lock_init(&ifsta->mesh_preq_queue_lock);
+}

+ 290 - 0
net/mac80211/mesh.h

@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2008 open80211s Ltd.
+ * Authors:    Luis Carlos Cobo <luisca@cozybit.com>
+ *             Javier Cardona <javier@cozybit.com>
+ *
+ * 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.
+ */
+
+#ifndef IEEE80211S_H
+#define IEEE80211S_H
+
+#include <linux/types.h>
+#include <linux/jhash.h>
+#include "ieee80211_i.h"
+
+
+/* Data structures */
+
+/**
+ * enum mesh_path_flags - mac80211 mesh path flags
+ *
+ *
+ *
+ * @MESH_PATH_ACTIVE: the mesh path is can be used for forwarding
+ * @MESH_PATH_RESOLVED: the discovery process is running for this mesh path
+ * @MESH_PATH_DSN_VALID: the mesh path contains a valid destination sequence
+ * 	number
+ * @MESH_PATH_FIXED: the mesh path has been manually set and should not be
+ * 	modified
+ * @MESH_PATH_RESOLVED: the mesh path can has been resolved
+ *
+ * MESH_PATH_RESOLVED and MESH_PATH_DELETE are used by the mesh path timer to
+ * decide when to stop or cancel the mesh path discovery.
+ */
+enum mesh_path_flags {
+	MESH_PATH_ACTIVE =	BIT(0),
+	MESH_PATH_RESOLVING =	BIT(1),
+	MESH_PATH_DSN_VALID =	BIT(2),
+	MESH_PATH_FIXED	=	BIT(3),
+	MESH_PATH_RESOLVED =	BIT(4),
+};
+
+/**
+ * struct mesh_path - mac80211 mesh path structure
+ *
+ * @dst: mesh path destination mac address
+ * @dev: mesh path device
+ * @next_hop: mesh neighbor to which frames for this destination will be
+ * 	forwarded
+ * @timer: mesh path discovery timer
+ * @frame_queue: pending queue for frames sent to this destination while the
+ * 	path is unresolved
+ * @dsn: destination sequence number of the destination
+ * @metric: current metric to this destination
+ * @hop_count: hops to destination
+ * @exp_time: in jiffies, when the path will expire or when it expired
+ * @discovery_timeout: timeout (lapse in jiffies) used for the last discovery
+ * 	retry
+ * @discovery_retries: number of discovery retries
+ * @flags: mesh path flags, as specified on &enum mesh_path_flags
+ * @state_lock: mesh pat state lock
+ *
+ *
+ * The combination of dst and dev is unique in the mesh path table. Since the
+ * next_hop STA is only protected by RCU as well, deleting the STA must also
+ * remove/substitute the mesh_path structure and wait until that is no longer
+ * reachable before destroying the STA completely.
+ */
+struct mesh_path {
+	u8 dst[ETH_ALEN];
+	struct net_device *dev;
+	struct sta_info *next_hop;
+	struct timer_list timer;
+	struct sk_buff_head frame_queue;
+	struct rcu_head rcu;
+	u32 dsn;
+	u32 metric;
+	u8 hop_count;
+	unsigned long exp_time;
+	u32 discovery_timeout;
+	u8 discovery_retries;
+	enum mesh_path_flags flags;
+	spinlock_t state_lock;
+};
+
+/**
+ * struct mesh_table
+ *
+ * @hash_buckets: array of hash buckets of the table
+ * @hashwlock: array of locks to protect write operations, one per bucket
+ * @hash_mask: 2^size_order - 1, used to compute hash idx
+ * @hash_rnd: random value used for hash computations
+ * @entries: number of entries in the table
+ * @free_node: function to free nodes of the table
+ * @copy_node: fuction to copy nodes of the table
+ * @size_order: determines size of the table, there will be 2^size_order hash
+ *	buckets
+ * @mean_chain_len: maximum average length for the hash buckets' list, if it is
+ *	reached, the table will grow
+ */
+struct mesh_table {
+	/* Number of buckets will be 2^N */
+	struct hlist_head *hash_buckets;
+	spinlock_t *hashwlock;		/* One per bucket, for add/del */
+	unsigned int hash_mask;		/* (2^size_order) - 1 */
+	__u32 hash_rnd;			/* Used for hash generation */
+	atomic_t entries;		/* Up to MAX_MESH_NEIGHBOURS */
+	void (*free_node) (struct hlist_node *p, bool free_leafs);
+	void (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl);
+	int size_order;
+	int mean_chain_len;
+};
+
+/* Recent multicast cache */
+/* RMC_BUCKETS must be a power of 2, maximum 256 */
+#define RMC_BUCKETS		256
+#define RMC_QUEUE_MAX_LEN	4
+#define RMC_TIMEOUT		(3 * HZ)
+
+/**
+ * struct rmc_entry - entry in the Recent Multicast Cache
+ *
+ * @seqnum: mesh sequence number of the frame
+ * @exp_time: expiration time of the entry, in jiffies
+ * @sa: source address of the frame
+ *
+ * The Recent Multicast Cache keeps track of the latest multicast frames that
+ * have been received by a mesh interface and discards received multicast frames
+ * that are found in the cache.
+ */
+struct rmc_entry {
+	struct list_head list;
+	u32 seqnum;
+	unsigned long exp_time;
+	u8 sa[ETH_ALEN];
+};
+
+struct mesh_rmc {
+	struct rmc_entry bucket[RMC_BUCKETS];
+	u8 idx_mask;
+};
+
+
+/* Mesh IEs constants */
+#define MESH_CFG_LEN		19
+
+/*
+ * MESH_CFG_COMP_LEN Includes:
+ * 	- Active path selection protocol ID.
+ * 	- Active path selection metric ID.
+ * 	- Congestion control mode identifier.
+ * 	- Channel precedence.
+ * Does not include mesh capabilities, which may vary across nodes in the same
+ * mesh
+ */
+#define MESH_CFG_CMP_LEN 	17
+
+/* Default values, timeouts in ms */
+#define MESH_TTL 		5
+#define MESH_MAX_RETR	 	3
+#define MESH_RET_T 		100
+#define MESH_CONF_T 		100
+#define MESH_HOLD_T 		100
+
+#define MESH_PATH_TIMEOUT	5000
+/* Minimum interval between two consecutive PREQs originated by the same
+ * interface
+ */
+#define MESH_PREQ_MIN_INT	10
+#define MESH_DIAM_TRAVERSAL_TIME 50
+/* Paths will be refreshed if they are closer than PATH_REFRESH_TIME to their
+ * expiration
+ */
+#define MESH_PATH_REFRESH_TIME			1000
+#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME)
+
+#define MESH_MAX_PREQ_RETRIES 4
+#define MESH_PATH_EXPIRE (600 * HZ)
+
+/* Default maximum number of established plinks per interface */
+#define MESH_MAX_ESTAB_PLINKS	32
+
+/* Default maximum number of plinks per interface */
+#define MESH_MAX_PLINKS		256
+
+/* Maximum number of paths per interface */
+#define MESH_MAX_MPATHS		1024
+
+/* Pending ANA approval */
+#define PLINK_CATEGORY		30
+#define MESH_PATH_SEL_CATEGORY	32
+
+/* Mesh Header Flags */
+#define IEEE80211S_FLAGS_AE	0x3
+
+/* Public interfaces */
+/* Various */
+u8 mesh_id_hash(u8 *mesh_id, int mesh_id_len);
+int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
+int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
+		struct ieee80211_sub_if_data *sdata);
+int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
+		struct net_device *dev);
+bool mesh_matches_local(struct ieee802_11_elems *ie, struct net_device *dev);
+void mesh_ids_set_default(struct ieee80211_if_sta *sta);
+void mesh_mgmt_ies_add(struct sk_buff *skb, struct net_device *dev);
+void mesh_rmc_free(struct net_device *dev);
+int mesh_rmc_init(struct net_device *dev);
+void ieee80211s_init(void);
+void ieee80211s_stop(void);
+void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
+
+/* Mesh paths */
+int mesh_nexthop_lookup(u8 *next_hop, struct sk_buff *skb,
+		struct net_device *dev);
+void mesh_path_start_discovery(struct net_device *dev);
+struct mesh_path *mesh_path_lookup(u8 *dst, struct net_device *dev);
+struct mesh_path *mesh_path_lookup_by_idx(int idx, struct net_device *dev);
+void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);
+void mesh_path_expire(struct net_device *dev);
+void mesh_path_flush(struct net_device *dev);
+void mesh_rx_path_sel_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
+		size_t len);
+int mesh_path_add(u8 *dst, struct net_device *dev);
+/* Mesh plinks */
+void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev,
+		bool add);
+bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie,
+			      struct net_device *dev);
+void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);
+void mesh_plink_broken(struct sta_info *sta);
+void mesh_plink_deactivate(struct sta_info *sta);
+int mesh_plink_open(struct sta_info *sta);
+int mesh_plink_close(struct sta_info *sta);
+void mesh_plink_block(struct sta_info *sta);
+void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
+			 size_t len, struct ieee80211_rx_status *rx_status);
+
+/* Private interfaces */
+/* Mesh tables */
+struct mesh_table *mesh_table_alloc(int size_order);
+void mesh_table_free(struct mesh_table *tbl, bool free_leafs);
+struct mesh_table *mesh_table_grow(struct mesh_table *tbl);
+u32 mesh_table_hash(u8 *addr, struct net_device *dev, struct mesh_table *tbl);
+/* Mesh paths */
+int mesh_path_error_tx(u8 *dest, __le32 dest_dsn, u8 *ra,
+		struct net_device *dev);
+void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta);
+void mesh_path_flush_pending(struct mesh_path *mpath);
+void mesh_path_tx_pending(struct mesh_path *mpath);
+int mesh_pathtbl_init(void);
+void mesh_pathtbl_unregister(void);
+int mesh_path_del(u8 *addr, struct net_device *dev);
+void mesh_path_timer(unsigned long data);
+void mesh_path_flush_by_nexthop(struct sta_info *sta);
+void mesh_path_discard_frame(struct sk_buff *skb, struct net_device *dev);
+
+#ifdef CONFIG_MAC80211_MESH
+extern int mesh_allocated;
+
+static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata)
+{
+	return sdata->u.sta.mshcfg.dot11MeshMaxPeerLinks -
+	       atomic_read(&sdata->u.sta.mshstats.estab_plinks);
+}
+
+static inline bool mesh_plink_availables(struct ieee80211_sub_if_data *sdata)
+{
+	return (min_t(long, mesh_plink_free_count(sdata),
+		   MESH_MAX_PLINKS - sdata->local->num_sta)) > 0;
+}
+
+static inline void mesh_path_activate(struct mesh_path *mpath)
+{
+	mpath->flags |= MESH_PATH_ACTIVE | MESH_PATH_RESOLVED;
+}
+
+#define for_each_mesh_entry(x, p, node, i) \
+	for (i = 0; i <= x->hash_mask; i++) \
+		hlist_for_each_entry_rcu(node, p, &x->hash_buckets[i], list)
+
+#else
+#define mesh_allocated	0
+#endif
+
+#define MESH_PREQ(skb)	(skb->cb + 30)
+
+#endif /* IEEE80211S_H */

+ 857 - 0
net/mac80211/mesh_hwmp.c

@@ -0,0 +1,857 @@
+/*
+ * Copyright (c) 2008 open80211s Ltd.
+ * Author:     Luis Carlos Cobo <luisca@cozybit.com>
+ *
+ * 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.
+ */
+
+#include <asm/unaligned.h>
+#include "mesh.h"
+
+#define IEEE80211_FC(type, stype) cpu_to_le16(type | stype)
+
+#define TEST_FRAME_LEN	8192
+#define MAX_METRIC	0xffffffff
+#define ARITH_SHIFT	8
+
+/* Number of frames buffered per destination for unresolved destinations */
+#define MESH_FRAME_QUEUE_LEN	10
+#define MAX_PREQ_QUEUE_LEN	64
+
+/* Destination only */
+#define MP_F_DO	0x1
+/* Reply and forward */
+#define MP_F_RF	0x2
+
+static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae)
+{
+	if (ae)
+		offset += 6;
+	return le32_to_cpu(get_unaligned((__le32 *) (preq_elem + offset)));
+}
+
+/* HWMP IE processing macros */
+#define AE_F			(1<<6)
+#define AE_F_SET(x)		(*x & AE_F)
+#define PREQ_IE_FLAGS(x)	(*(x))
+#define PREQ_IE_HOPCOUNT(x)	(*(x + 1))
+#define PREQ_IE_TTL(x)		(*(x + 2))
+#define PREQ_IE_PREQ_ID(x)	u32_field_get(x, 3, 0)
+#define PREQ_IE_ORIG_ADDR(x)	(x + 7)
+#define PREQ_IE_ORIG_DSN(x)	u32_field_get(x, 13, 0);
+#define PREQ_IE_LIFETIME(x)	u32_field_get(x, 17, AE_F_SET(x));
+#define PREQ_IE_METRIC(x) 	u32_field_get(x, 21, AE_F_SET(x));
+#define PREQ_IE_DST_F(x)	(*(AE_F_SET(x) ? x + 32 : x + 26))
+#define PREQ_IE_DST_ADDR(x) 	(AE_F_SET(x) ? x + 33 : x + 27)
+#define PREQ_IE_DST_DSN(x) 	u32_field_get(x, 33, AE_F_SET(x));
+
+
+#define PREP_IE_FLAGS(x)	PREQ_IE_FLAGS(x)
+#define PREP_IE_HOPCOUNT(x)	PREQ_IE_HOPCOUNT(x)
+#define PREP_IE_TTL(x)		PREQ_IE_TTL(x)
+#define PREP_IE_ORIG_ADDR(x)	(x + 3)
+#define PREP_IE_ORIG_DSN(x)	u32_field_get(x, 9, 0);
+#define PREP_IE_LIFETIME(x)	u32_field_get(x, 13, AE_F_SET(x));
+#define PREP_IE_METRIC(x)	u32_field_get(x, 17, AE_F_SET(x));
+#define PREP_IE_DST_ADDR(x)	(AE_F_SET(x) ? x + 27 : x + 21)
+#define PREP_IE_DST_DSN(x)	u32_field_get(x, 27, AE_F_SET(x));
+
+#define PERR_IE_DST_ADDR(x)	(x + 2)
+#define PERR_IE_DST_DSN(x)	u32_field_get(x, 8, 0);
+
+#define TU_TO_EXP_TIME(x) (jiffies + msecs_to_jiffies(x * 1024 / 1000))
+#define MSEC_TO_TU(x) (x*1000/1024)
+#define DSN_GT(x, y) ((long) (y) - (long) (x) < 0)
+#define DSN_LT(x, y) ((long) (x) - (long) (y) < 0)
+
+#define net_traversal_jiffies(s) \
+	msecs_to_jiffies(s->u.sta.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
+#define default_lifetime(s) \
+	MSEC_TO_TU(s->u.sta.mshcfg.dot11MeshHWMPactivePathTimeout)
+#define min_preq_int_jiff(s) \
+	(msecs_to_jiffies(s->u.sta.mshcfg.dot11MeshHWMPpreqMinInterval))
+#define max_preq_retries(s) (s->u.sta.mshcfg.dot11MeshHWMPmaxPREQretries)
+#define disc_timeout_jiff(s) \
+	msecs_to_jiffies(sdata->u.sta.mshcfg.min_discovery_timeout)
+
+enum mpath_frame_type {
+	MPATH_PREQ = 0,
+	MPATH_PREP,
+	MPATH_PERR
+};
+
+static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
+		u8 *orig_addr, __le32 orig_dsn, u8 dst_flags, u8 *dst,
+		__le32 dst_dsn, u8 *da, u8 hop_count, u8 ttl, __le32 lifetime,
+		__le32 metric, __le32 preq_id, struct net_device *dev)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+	struct ieee80211_mgmt *mgmt;
+	u8 *pos;
+	int ie_len;
+
+	if (!skb)
+		return -1;
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+	/* 25 is the size of the common mgmt part (24) plus the size of the
+	 * common action part (1)
+	 */
+	mgmt = (struct ieee80211_mgmt *)
+		skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action));
+	memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action));
+	mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+					   IEEE80211_STYPE_ACTION);
+
+	memcpy(mgmt->da, da, ETH_ALEN);
+	memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+	/* BSSID is left zeroed, wildcard value */
+	mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
+	mgmt->u.action.u.mesh_action.action_code = action;
+
+	switch (action) {
+	case MPATH_PREQ:
+		ie_len = 37;
+		pos = skb_put(skb, 2 + ie_len);
+		*pos++ = WLAN_EID_PREQ;
+		break;
+	case MPATH_PREP:
+		ie_len = 31;
+		pos = skb_put(skb, 2 + ie_len);
+		*pos++ = WLAN_EID_PREP;
+		break;
+	default:
+		kfree(skb);
+		return -ENOTSUPP;
+		break;
+	}
+	*pos++ = ie_len;
+	*pos++ = flags;
+	*pos++ = hop_count;
+	*pos++ = ttl;
+	if (action == MPATH_PREQ) {
+		memcpy(pos, &preq_id, 4);
+		pos += 4;
+	}
+	memcpy(pos, orig_addr, ETH_ALEN);
+	pos += ETH_ALEN;
+	memcpy(pos, &orig_dsn, 4);
+	pos += 4;
+	memcpy(pos, &lifetime, 4);
+	pos += 4;
+	memcpy(pos, &metric, 4);
+	pos += 4;
+	if (action == MPATH_PREQ) {
+		/* destination count */
+		*pos++ = 1;
+		*pos++ = dst_flags;
+	}
+	memcpy(pos, dst, ETH_ALEN);
+	pos += ETH_ALEN;
+	memcpy(pos, &dst_dsn, 4);
+
+	ieee80211_sta_tx(dev, skb, 0);
+	return 0;
+}
+
+/**
+ * mesh_send_path error - Sends a PERR mesh management frame
+ *
+ * @dst: broken destination
+ * @dst_dsn: dsn of the broken destination
+ * @ra: node this frame is addressed to
+ */
+int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
+		struct net_device *dev)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+	struct ieee80211_mgmt *mgmt;
+	u8 *pos;
+	int ie_len;
+
+	if (!skb)
+		return -1;
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+	/* 25 is the size of the common mgmt part (24) plus the size of the
+	 * common action part (1)
+	 */
+	mgmt = (struct ieee80211_mgmt *)
+		skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action));
+	memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action));
+	mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+					   IEEE80211_STYPE_ACTION);
+
+	memcpy(mgmt->da, ra, ETH_ALEN);
+	memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+	/* BSSID is left zeroed, wildcard value */
+	mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
+	mgmt->u.action.u.mesh_action.action_code = MPATH_PERR;
+	ie_len = 12;
+	pos = skb_put(skb, 2 + ie_len);
+	*pos++ = WLAN_EID_PERR;
+	*pos++ = ie_len;
+	/* mode flags, reserved */
+	*pos++ = 0;
+	/* number of destinations */
+	*pos++ = 1;
+	memcpy(pos, dst, ETH_ALEN);
+	pos += ETH_ALEN;
+	memcpy(pos, &dst_dsn, 4);
+
+	ieee80211_sta_tx(dev, skb, 0);
+	return 0;
+}
+
+static u32 airtime_link_metric_get(struct ieee80211_local *local,
+				   struct sta_info *sta)
+{
+	struct ieee80211_supported_band *sband;
+	/* This should be adjusted for each device */
+	int device_constant = 1 << ARITH_SHIFT;
+	int test_frame_len = TEST_FRAME_LEN << ARITH_SHIFT;
+	int s_unit = 1 << ARITH_SHIFT;
+	int rate, err;
+	u32 tx_time, estimated_retx;
+	u64 result;
+
+	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+	if (sta->fail_avg >= 100)
+		return MAX_METRIC;
+	err = (sta->fail_avg << ARITH_SHIFT) / 100;
+
+	/* bitrate is in units of 100 Kbps, while we need rate in units of
+	 * 1Mbps. This will be corrected on tx_time computation.
+	 */
+	rate = sband->bitrates[sta->txrate_idx].bitrate;
+	tx_time = (device_constant + 10 * test_frame_len / rate);
+	estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err));
+	result = (tx_time * estimated_retx) >> (2 * ARITH_SHIFT) ;
+	return (u32)result;
+}
+
+/**
+ * hwmp_route_info_get - Update routing info to originator and transmitter
+ *
+ * @dev: local mesh interface
+ * @mgmt: mesh management frame
+ * @hwmp_ie: hwmp information element (PREP or PREQ)
+ *
+ * This function updates the path routing information to the originator and the
+ * transmitter of a HWMP PREQ or PREP fram.
+ *
+ * Returns: metric to frame originator or 0 if the frame should not be further
+ * processed
+ *
+ * Notes: this function is the only place (besides user-provided info) where
+ * path routing information is updated.
+ */
+static u32 hwmp_route_info_get(struct net_device *dev,
+			    struct ieee80211_mgmt *mgmt,
+			    u8 *hwmp_ie)
+{
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct mesh_path *mpath;
+	struct sta_info *sta;
+	bool fresh_info;
+	u8 *orig_addr, *ta;
+	u32 orig_dsn, orig_metric;
+	unsigned long orig_lifetime, exp_time;
+	u32 last_hop_metric, new_metric;
+	bool process = true;
+	u8 action = mgmt->u.action.u.mesh_action.action_code;
+
+	rcu_read_lock();
+	sta = sta_info_get(local, mgmt->sa);
+	if (!sta) {
+		rcu_read_unlock();
+		return 0;
+	}
+
+	last_hop_metric = airtime_link_metric_get(local, sta);
+	/* Update and check originator routing info */
+	fresh_info = true;
+
+	switch (action) {
+	case MPATH_PREQ:
+		orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie);
+		orig_dsn = PREQ_IE_ORIG_DSN(hwmp_ie);
+		orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie);
+		orig_metric = PREQ_IE_METRIC(hwmp_ie);
+		break;
+	case MPATH_PREP:
+		/* Originator here refers to the MP that was the destination in
+		 * the Path Request. The draft refers to that MP as the
+		 * destination address, even though usually it is the origin of
+		 * the PREP frame. We divert from the nomenclature in the draft
+		 * so that we can easily use a single function to gather path
+		 * information from both PREQ and PREP frames.
+		 */
+		orig_addr = PREP_IE_ORIG_ADDR(hwmp_ie);
+		orig_dsn = PREP_IE_ORIG_DSN(hwmp_ie);
+		orig_lifetime = PREP_IE_LIFETIME(hwmp_ie);
+		orig_metric = PREP_IE_METRIC(hwmp_ie);
+		break;
+	default:
+		rcu_read_unlock();
+		return 0;
+	}
+	new_metric = orig_metric + last_hop_metric;
+	if (new_metric < orig_metric)
+		new_metric = MAX_METRIC;
+	exp_time = TU_TO_EXP_TIME(orig_lifetime);
+
+	if (memcmp(orig_addr, dev->dev_addr, ETH_ALEN) == 0) {
+		/* This MP is the originator, we are not interested in this
+		 * frame, except for updating transmitter's path info.
+		 */
+		process = false;
+		fresh_info = false;
+	} else {
+		mpath = mesh_path_lookup(orig_addr, dev);
+		if (mpath) {
+			spin_lock_bh(&mpath->state_lock);
+			if (mpath->flags & MESH_PATH_FIXED)
+				fresh_info = false;
+			else if ((mpath->flags & MESH_PATH_ACTIVE) &&
+			    (mpath->flags & MESH_PATH_DSN_VALID)) {
+				if (DSN_GT(mpath->dsn, orig_dsn) ||
+				    (mpath->dsn == orig_dsn &&
+				     action == MPATH_PREQ &&
+				     new_metric > mpath->metric)) {
+					process = false;
+					fresh_info = false;
+				}
+			}
+		} else {
+			mesh_path_add(orig_addr, dev);
+			mpath = mesh_path_lookup(orig_addr, dev);
+			if (!mpath) {
+				rcu_read_unlock();
+				return 0;
+			}
+			spin_lock_bh(&mpath->state_lock);
+		}
+
+		if (fresh_info) {
+			mesh_path_assign_nexthop(mpath, sta);
+			mpath->flags |= MESH_PATH_DSN_VALID;
+			mpath->metric = new_metric;
+			mpath->dsn = orig_dsn;
+			mpath->exp_time = time_after(mpath->exp_time, exp_time)
+					  ?  mpath->exp_time : exp_time;
+			mesh_path_activate(mpath);
+			spin_unlock_bh(&mpath->state_lock);
+			mesh_path_tx_pending(mpath);
+			/* draft says preq_id should be saved to, but there does
+			 * not seem to be any use for it, skipping by now
+			 */
+		} else
+			spin_unlock_bh(&mpath->state_lock);
+	}
+
+	/* Update and check transmitter routing info */
+	ta = mgmt->sa;
+	if (memcmp(orig_addr, ta, ETH_ALEN) == 0)
+		fresh_info = false;
+	else {
+		fresh_info = true;
+
+		mpath = mesh_path_lookup(ta, dev);
+		if (mpath) {
+			spin_lock_bh(&mpath->state_lock);
+			if ((mpath->flags & MESH_PATH_FIXED) ||
+				((mpath->flags & MESH_PATH_ACTIVE) &&
+					(last_hop_metric > mpath->metric)))
+				fresh_info = false;
+		} else {
+			mesh_path_add(ta, dev);
+			mpath = mesh_path_lookup(ta, dev);
+			if (!mpath) {
+				rcu_read_unlock();
+				return 0;
+			}
+			spin_lock_bh(&mpath->state_lock);
+		}
+
+		if (fresh_info) {
+			mesh_path_assign_nexthop(mpath, sta);
+			mpath->flags &= ~MESH_PATH_DSN_VALID;
+			mpath->metric = last_hop_metric;
+			mpath->exp_time = time_after(mpath->exp_time, exp_time)
+					  ?  mpath->exp_time : exp_time;
+			mesh_path_activate(mpath);
+			spin_unlock_bh(&mpath->state_lock);
+			mesh_path_tx_pending(mpath);
+		} else
+			spin_unlock_bh(&mpath->state_lock);
+	}
+
+	rcu_read_unlock();
+
+	return process ? new_metric : 0;
+}
+
+static void hwmp_preq_frame_process(struct net_device *dev,
+				    struct ieee80211_mgmt *mgmt,
+				    u8 *preq_elem, u32 metric) {
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+	struct mesh_path *mpath;
+	u8 *dst_addr, *orig_addr;
+	u8 dst_flags, ttl;
+	u32 orig_dsn, dst_dsn, lifetime;
+	bool reply = false;
+	bool forward = true;
+
+	/* Update destination DSN, if present */
+	dst_addr = PREQ_IE_DST_ADDR(preq_elem);
+	orig_addr = PREQ_IE_ORIG_ADDR(preq_elem);
+	dst_dsn = PREQ_IE_DST_DSN(preq_elem);
+	orig_dsn = PREQ_IE_ORIG_DSN(preq_elem);
+	dst_flags = PREQ_IE_DST_F(preq_elem);
+
+	if (memcmp(dst_addr, dev->dev_addr, ETH_ALEN) == 0) {
+		forward = false;
+		reply = true;
+		metric = 0;
+		if (time_after(jiffies, ifsta->last_dsn_update +
+					net_traversal_jiffies(sdata)) ||
+		    time_before(jiffies, ifsta->last_dsn_update)) {
+			dst_dsn = ++ifsta->dsn;
+			ifsta->last_dsn_update = jiffies;
+		}
+	} else {
+		rcu_read_lock();
+		mpath = mesh_path_lookup(dst_addr, dev);
+		if (mpath) {
+			if ((!(mpath->flags & MESH_PATH_DSN_VALID)) ||
+					DSN_LT(mpath->dsn, dst_dsn)) {
+				mpath->dsn = dst_dsn;
+				mpath->flags &= MESH_PATH_DSN_VALID;
+			} else if ((!(dst_flags & MP_F_DO)) &&
+					(mpath->flags & MESH_PATH_ACTIVE)) {
+				reply = true;
+				metric = mpath->metric;
+				dst_dsn = mpath->dsn;
+				if (dst_flags & MP_F_RF)
+					dst_flags |= MP_F_DO;
+				else
+					forward = false;
+			}
+		}
+		rcu_read_unlock();
+	}
+
+	if (reply) {
+		lifetime = PREQ_IE_LIFETIME(preq_elem);
+		ttl = ifsta->mshcfg.dot11MeshTTL;
+		if (ttl != 0)
+			mesh_path_sel_frame_tx(MPATH_PREP, 0, dst_addr,
+				cpu_to_le32(dst_dsn), 0, orig_addr,
+				cpu_to_le32(orig_dsn), mgmt->sa, 0, ttl,
+				cpu_to_le32(lifetime), cpu_to_le32(metric),
+				0, dev);
+		else
+			ifsta->mshstats.dropped_frames_ttl++;
+	}
+
+	if (forward) {
+		u32 preq_id;
+		u8 hopcount, flags;
+
+		ttl = PREQ_IE_TTL(preq_elem);
+		lifetime = PREQ_IE_LIFETIME(preq_elem);
+		if (ttl <= 1) {
+			ifsta->mshstats.dropped_frames_ttl++;
+			return;
+		}
+		--ttl;
+		flags = PREQ_IE_FLAGS(preq_elem);
+		preq_id = PREQ_IE_PREQ_ID(preq_elem);
+		hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1;
+		mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr,
+				cpu_to_le32(orig_dsn), dst_flags, dst_addr,
+				cpu_to_le32(dst_dsn), dev->broadcast,
+				hopcount, ttl, cpu_to_le32(lifetime),
+				cpu_to_le32(metric), cpu_to_le32(preq_id),
+				dev);
+		ifsta->mshstats.fwded_frames++;
+	}
+}
+
+
+static void hwmp_prep_frame_process(struct net_device *dev,
+				    struct ieee80211_mgmt *mgmt,
+				    u8 *prep_elem, u32 metric)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct mesh_path *mpath;
+	u8 *dst_addr, *orig_addr;
+	u8 ttl, hopcount, flags;
+	u8 next_hop[ETH_ALEN];
+	u32 dst_dsn, orig_dsn, lifetime;
+
+	/* Note that we divert from the draft nomenclature and denominate
+	 * destination to what the draft refers to as origininator. So in this
+	 * function destnation refers to the final destination of the PREP,
+	 * which corresponds with the originator of the PREQ which this PREP
+	 * replies
+	 */
+	dst_addr = PREP_IE_DST_ADDR(prep_elem);
+	if (memcmp(dst_addr, dev->dev_addr, ETH_ALEN) == 0)
+		/* destination, no forwarding required */
+		return;
+
+	ttl = PREP_IE_TTL(prep_elem);
+	if (ttl <= 1) {
+		sdata->u.sta.mshstats.dropped_frames_ttl++;
+		return;
+	}
+
+	rcu_read_lock();
+	mpath = mesh_path_lookup(dst_addr, dev);
+	if (mpath)
+		spin_lock_bh(&mpath->state_lock);
+	else
+		goto fail;
+	if (!(mpath->flags & MESH_PATH_ACTIVE)) {
+		spin_unlock_bh(&mpath->state_lock);
+		goto fail;
+	}
+	memcpy(next_hop, mpath->next_hop->addr, ETH_ALEN);
+	spin_unlock_bh(&mpath->state_lock);
+	--ttl;
+	flags = PREP_IE_FLAGS(prep_elem);
+	lifetime = PREP_IE_LIFETIME(prep_elem);
+	hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1;
+	orig_addr = PREP_IE_ORIG_ADDR(prep_elem);
+	dst_dsn = PREP_IE_DST_DSN(prep_elem);
+	orig_dsn = PREP_IE_ORIG_DSN(prep_elem);
+
+	mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr,
+		cpu_to_le32(orig_dsn), 0, dst_addr,
+		cpu_to_le32(dst_dsn), mpath->next_hop->addr, hopcount, ttl,
+		cpu_to_le32(lifetime), cpu_to_le32(metric),
+		0, dev);
+	rcu_read_unlock();
+	sdata->u.sta.mshstats.fwded_frames++;
+	return;
+
+fail:
+	rcu_read_unlock();
+	sdata->u.sta.mshstats.dropped_frames_no_route++;
+	return;
+}
+
+static void hwmp_perr_frame_process(struct net_device *dev,
+			     struct ieee80211_mgmt *mgmt, u8 *perr_elem)
+{
+	struct mesh_path *mpath;
+	u8 *ta, *dst_addr;
+	u32 dst_dsn;
+
+	ta = mgmt->sa;
+	dst_addr = PERR_IE_DST_ADDR(perr_elem);
+	dst_dsn = PERR_IE_DST_DSN(perr_elem);
+	rcu_read_lock();
+	mpath = mesh_path_lookup(dst_addr, dev);
+	if (mpath) {
+		spin_lock_bh(&mpath->state_lock);
+		if (mpath->flags & MESH_PATH_ACTIVE &&
+		    memcmp(ta, mpath->next_hop->addr, ETH_ALEN) == 0 &&
+		    (!(mpath->flags & MESH_PATH_DSN_VALID) ||
+		    DSN_GT(dst_dsn, mpath->dsn))) {
+			mpath->flags &= ~MESH_PATH_ACTIVE;
+			mpath->dsn = dst_dsn;
+			spin_unlock_bh(&mpath->state_lock);
+			mesh_path_error_tx(dst_addr, cpu_to_le32(dst_dsn),
+					   dev->broadcast, dev);
+		} else
+			spin_unlock_bh(&mpath->state_lock);
+	}
+	rcu_read_unlock();
+}
+
+
+
+void mesh_rx_path_sel_frame(struct net_device *dev,
+			    struct ieee80211_mgmt *mgmt,
+			    size_t len)
+{
+	struct ieee802_11_elems elems;
+	size_t baselen;
+	u32 last_hop_metric;
+
+	baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
+	ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
+			len - baselen, &elems);
+
+	switch (mgmt->u.action.u.mesh_action.action_code) {
+	case MPATH_PREQ:
+		if (!elems.preq || elems.preq_len != 37)
+			/* Right now we support just 1 destination and no AE */
+			return;
+		last_hop_metric = hwmp_route_info_get(dev, mgmt, elems.preq);
+		if (!last_hop_metric)
+			return;
+		hwmp_preq_frame_process(dev, mgmt, elems.preq, last_hop_metric);
+		break;
+	case MPATH_PREP:
+		if (!elems.prep || elems.prep_len != 31)
+			/* Right now we support no AE */
+			return;
+		last_hop_metric = hwmp_route_info_get(dev, mgmt, elems.prep);
+		if (!last_hop_metric)
+			return;
+		hwmp_prep_frame_process(dev, mgmt, elems.prep, last_hop_metric);
+		break;
+	case MPATH_PERR:
+		if (!elems.perr || elems.perr_len != 12)
+			/* Right now we support only one destination per PERR */
+			return;
+		hwmp_perr_frame_process(dev, mgmt, elems.perr);
+	default:
+		return;
+	}
+
+}
+
+/**
+ * mesh_queue_preq - queue a PREQ to a given destination
+ *
+ * @mpath: mesh path to discover
+ * @flags: special attributes of the PREQ to be sent
+ *
+ * Locking: the function must be called from within a rcu read lock block.
+ *
+ */
+static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
+{
+	struct ieee80211_sub_if_data *sdata =
+		IEEE80211_DEV_TO_SUB_IF(mpath->dev);
+	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+	struct mesh_preq_queue *preq_node;
+
+	preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_KERNEL);
+	if (!preq_node) {
+		printk(KERN_DEBUG "Mesh HWMP: could not allocate PREQ node\n");
+		return;
+	}
+
+	spin_lock(&ifsta->mesh_preq_queue_lock);
+	if (ifsta->preq_queue_len == MAX_PREQ_QUEUE_LEN) {
+		spin_unlock(&ifsta->mesh_preq_queue_lock);
+		kfree(preq_node);
+		if (printk_ratelimit())
+			printk(KERN_DEBUG "Mesh HWMP: PREQ node queue full\n");
+		return;
+	}
+
+	memcpy(preq_node->dst, mpath->dst, ETH_ALEN);
+	preq_node->flags = flags;
+
+	list_add_tail(&preq_node->list, &ifsta->preq_queue.list);
+	++ifsta->preq_queue_len;
+	spin_unlock(&ifsta->mesh_preq_queue_lock);
+
+	if (time_after(jiffies, ifsta->last_preq + min_preq_int_jiff(sdata)))
+		queue_work(sdata->local->hw.workqueue, &ifsta->work);
+
+	else if (time_before(jiffies, ifsta->last_preq)) {
+		/* avoid long wait if did not send preqs for a long time
+		 * and jiffies wrapped around
+		 */
+		ifsta->last_preq = jiffies - min_preq_int_jiff(sdata) - 1;
+		queue_work(sdata->local->hw.workqueue, &ifsta->work);
+	} else
+		mod_timer(&ifsta->mesh_path_timer, ifsta->last_preq +
+						min_preq_int_jiff(sdata));
+}
+
+/**
+ * mesh_path_start_discovery - launch a path discovery from the PREQ queue
+ *
+ * @dev: local mesh interface
+ */
+void mesh_path_start_discovery(struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata =
+		IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+	struct mesh_preq_queue *preq_node;
+	struct mesh_path *mpath;
+	u8 ttl, dst_flags;
+	u32 lifetime;
+
+	spin_lock(&ifsta->mesh_preq_queue_lock);
+	if (!ifsta->preq_queue_len ||
+		time_before(jiffies, ifsta->last_preq +
+				min_preq_int_jiff(sdata))) {
+		spin_unlock(&ifsta->mesh_preq_queue_lock);
+		return;
+	}
+
+	preq_node = list_first_entry(&ifsta->preq_queue.list,
+			struct mesh_preq_queue, list);
+	list_del(&preq_node->list);
+	--ifsta->preq_queue_len;
+	spin_unlock(&ifsta->mesh_preq_queue_lock);
+
+	rcu_read_lock();
+	mpath = mesh_path_lookup(preq_node->dst, dev);
+	if (!mpath)
+		goto enddiscovery;
+
+	spin_lock_bh(&mpath->state_lock);
+	if (preq_node->flags & PREQ_Q_F_START) {
+		if (mpath->flags & MESH_PATH_RESOLVING) {
+			spin_unlock_bh(&mpath->state_lock);
+			goto enddiscovery;
+		} else {
+			mpath->flags &= ~MESH_PATH_RESOLVED;
+			mpath->flags |= MESH_PATH_RESOLVING;
+			mpath->discovery_retries = 0;
+			mpath->discovery_timeout = disc_timeout_jiff(sdata);
+		}
+	} else if (!(mpath->flags & MESH_PATH_RESOLVING) ||
+			mpath->flags & MESH_PATH_RESOLVED) {
+		mpath->flags &= ~MESH_PATH_RESOLVING;
+		spin_unlock_bh(&mpath->state_lock);
+		goto enddiscovery;
+	}
+
+	ifsta->last_preq = jiffies;
+
+	if (time_after(jiffies, ifsta->last_dsn_update +
+				net_traversal_jiffies(sdata)) ||
+	    time_before(jiffies, ifsta->last_dsn_update)) {
+		++ifsta->dsn;
+		sdata->u.sta.last_dsn_update = jiffies;
+	}
+	lifetime = default_lifetime(sdata);
+	ttl = sdata->u.sta.mshcfg.dot11MeshTTL;
+	if (ttl == 0) {
+		sdata->u.sta.mshstats.dropped_frames_ttl++;
+		spin_unlock_bh(&mpath->state_lock);
+		goto enddiscovery;
+	}
+
+	if (preq_node->flags & PREQ_Q_F_REFRESH)
+		dst_flags = MP_F_DO;
+	else
+		dst_flags = MP_F_RF;
+
+	spin_unlock_bh(&mpath->state_lock);
+	mesh_path_sel_frame_tx(MPATH_PREQ, 0, dev->dev_addr,
+			cpu_to_le32(ifsta->dsn), dst_flags, mpath->dst,
+			cpu_to_le32(mpath->dsn), dev->broadcast, 0,
+			ttl, cpu_to_le32(lifetime), 0,
+			cpu_to_le32(ifsta->preq_id++), dev);
+	mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout);
+
+enddiscovery:
+	rcu_read_unlock();
+	kfree(preq_node);
+}
+
+/**
+ * ieee80211s_lookup_nexthop - put the appropriate next hop on a mesh frame
+ *
+ * @next_hop: output argument for next hop address
+ * @skb: frame to be sent
+ * @dev: network device the frame will be sent through
+ *
+ * Returns: 0 if the next hop was found. Nonzero otherwise. If no next hop is
+ * found, the function will start a path discovery and queue the frame so it is
+ * sent when the path is resolved. This means the caller must not free the skb
+ * in this case.
+ */
+int mesh_nexthop_lookup(u8 *next_hop, struct sk_buff *skb,
+		struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct sk_buff *skb_to_free = NULL;
+	struct mesh_path *mpath;
+	int err = 0;
+
+	rcu_read_lock();
+	mpath = mesh_path_lookup(skb->data, dev);
+
+	if (!mpath) {
+		mesh_path_add(skb->data, dev);
+		mpath = mesh_path_lookup(skb->data, dev);
+		if (!mpath) {
+			dev_kfree_skb(skb);
+			sdata->u.sta.mshstats.dropped_frames_no_route++;
+			err = -ENOSPC;
+			goto endlookup;
+		}
+	}
+
+	if (mpath->flags & MESH_PATH_ACTIVE) {
+		if (time_after(jiffies, mpath->exp_time -
+			msecs_to_jiffies(sdata->u.sta.mshcfg.path_refresh_time))
+				&& skb->pkt_type != PACKET_OTHERHOST
+				&& !(mpath->flags & MESH_PATH_RESOLVING)
+				&& !(mpath->flags & MESH_PATH_FIXED)) {
+			mesh_queue_preq(mpath,
+					PREQ_Q_F_START | PREQ_Q_F_REFRESH);
+		}
+		memcpy(next_hop, mpath->next_hop->addr,
+				ETH_ALEN);
+	} else {
+		if (!(mpath->flags & MESH_PATH_RESOLVING)) {
+			/* Start discovery only if it is not running yet */
+			mesh_queue_preq(mpath, PREQ_Q_F_START);
+		}
+
+		if (skb_queue_len(&mpath->frame_queue) >=
+				MESH_FRAME_QUEUE_LEN) {
+			skb_to_free = mpath->frame_queue.next;
+			skb_unlink(skb_to_free, &mpath->frame_queue);
+		}
+
+		skb_queue_tail(&mpath->frame_queue, skb);
+		if (skb_to_free)
+			mesh_path_discard_frame(skb_to_free, dev);
+		err = -ENOENT;
+	}
+
+endlookup:
+	rcu_read_unlock();
+	return err;
+}
+
+void mesh_path_timer(unsigned long data)
+{
+	struct ieee80211_sub_if_data *sdata;
+	struct mesh_path *mpath;
+
+	rcu_read_lock();
+	mpath = (struct mesh_path *) data;
+	mpath = rcu_dereference(mpath);
+	if (!mpath)
+		goto endmpathtimer;
+	spin_lock_bh(&mpath->state_lock);
+	sdata = IEEE80211_DEV_TO_SUB_IF(mpath->dev);
+	if (mpath->flags & MESH_PATH_RESOLVED ||
+			(!(mpath->flags & MESH_PATH_RESOLVING)))
+		mpath->flags &= ~(MESH_PATH_RESOLVING | MESH_PATH_RESOLVED);
+	else if (mpath->discovery_retries < max_preq_retries(sdata)) {
+		++mpath->discovery_retries;
+		mpath->discovery_timeout *= 2;
+		mesh_queue_preq(mpath, 0);
+	} else {
+		mpath->flags = 0;
+		mpath->exp_time = jiffies;
+		mesh_path_flush_pending(mpath);
+	}
+
+	spin_unlock_bh(&mpath->state_lock);
+endmpathtimer:
+	rcu_read_unlock();
+}

+ 516 - 0
net/mac80211/mesh_pathtbl.c

@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2008 open80211s Ltd.
+ * Author:     Luis Carlos Cobo <luisca@cozybit.com>
+ *
+ * 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.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+#include "mesh.h"
+
+/* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */
+#define INIT_PATHS_SIZE_ORDER	2
+
+/* Keep the mean chain length below this constant */
+#define MEAN_CHAIN_LEN		2
+
+#define MPATH_EXPIRED(mpath) ((mpath->flags & MESH_PATH_ACTIVE) && \
+				time_after(jiffies, mpath->exp_time) && \
+				!(mpath->flags & MESH_PATH_FIXED))
+
+struct mpath_node {
+	struct hlist_node list;
+	struct rcu_head rcu;
+	/* This indirection allows two different tables to point to the same
+	 * mesh_path structure, useful when resizing
+	 */
+	struct mesh_path *mpath;
+};
+
+static struct mesh_table *mesh_paths;
+
+/* This lock will have the grow table function as writer and add / delete nodes
+ * as readers. When reading the table (i.e. doing lookups) we are well protected
+ * by RCU
+ */
+static DEFINE_RWLOCK(pathtbl_resize_lock);
+
+/**
+ *
+ * mesh_path_assign_nexthop - update mesh path next hop
+ *
+ * @mpath: mesh path to update
+ * @sta: next hop to assign
+ *
+ * Locking: mpath->state_lock must be held when calling this function
+ */
+void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
+{
+	rcu_assign_pointer(mpath->next_hop, sta);
+}
+
+
+/**
+ * mesh_path_lookup - look up a path in the mesh path table
+ * @dst: hardware address (ETH_ALEN length) of destination
+ * @dev: local interface
+ *
+ * Returns: pointer to the mesh path structure, or NULL if not found
+ *
+ * Locking: must be called within a read rcu section.
+ */
+struct mesh_path *mesh_path_lookup(u8 *dst, struct net_device *dev)
+{
+	struct mesh_path *mpath;
+	struct hlist_node *n;
+	struct hlist_head *bucket;
+	struct mesh_table *tbl;
+	struct mpath_node *node;
+
+	tbl = rcu_dereference(mesh_paths);
+
+	bucket = &tbl->hash_buckets[mesh_table_hash(dst, dev, tbl)];
+	hlist_for_each_entry_rcu(node, n, bucket, list) {
+		mpath = node->mpath;
+		if (mpath->dev == dev &&
+				memcmp(dst, mpath->dst, ETH_ALEN) == 0) {
+			if (MPATH_EXPIRED(mpath)) {
+				spin_lock_bh(&mpath->state_lock);
+				if (MPATH_EXPIRED(mpath))
+					mpath->flags &= ~MESH_PATH_ACTIVE;
+				spin_unlock_bh(&mpath->state_lock);
+			}
+			return mpath;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * mesh_path_lookup_by_idx - look up a path in the mesh path table by its index
+ * @idx: index
+ * @dev: local interface, or NULL for all entries
+ *
+ * Returns: pointer to the mesh path structure, or NULL if not found.
+ *
+ * Locking: must be called within a read rcu section.
+ */
+struct mesh_path *mesh_path_lookup_by_idx(int idx, struct net_device *dev)
+{
+	struct mpath_node *node;
+	struct hlist_node *p;
+	int i;
+	int j = 0;
+
+	for_each_mesh_entry(mesh_paths, p, node, i) {
+		if (dev && node->mpath->dev != dev)
+			continue;
+		if (j++ == idx) {
+			if (MPATH_EXPIRED(node->mpath)) {
+				spin_lock_bh(&node->mpath->state_lock);
+				if (MPATH_EXPIRED(node->mpath))
+					node->mpath->flags &= ~MESH_PATH_ACTIVE;
+				spin_unlock_bh(&node->mpath->state_lock);
+			}
+			return node->mpath;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * mesh_path_add - allocate and add a new path to the mesh path table
+ * @addr: destination address of the path (ETH_ALEN length)
+ * @dev: local interface
+ *
+ * Returns: 0 on sucess
+ *
+ * State: the initial state of the new path is set to 0
+ */
+int mesh_path_add(u8 *dst, struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct mesh_path *mpath, *new_mpath;
+	struct mpath_node *node, *new_node;
+	struct hlist_head *bucket;
+	struct hlist_node *n;
+	int grow = 0;
+	int err = 0;
+	u32 hash_idx;
+
+	if (memcmp(dst, dev->dev_addr, ETH_ALEN) == 0)
+		/* never add ourselves as neighbours */
+		return -ENOTSUPP;
+
+	if (is_multicast_ether_addr(dst))
+		return -ENOTSUPP;
+
+	if (atomic_add_unless(&sdata->u.sta.mpaths, 1, MESH_MAX_MPATHS) == 0)
+		return -ENOSPC;
+
+	read_lock(&pathtbl_resize_lock);
+
+	new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL);
+	if (!new_mpath) {
+		atomic_dec(&sdata->u.sta.mpaths);
+		err = -ENOMEM;
+		goto endadd2;
+	}
+	memcpy(new_mpath->dst, dst, ETH_ALEN);
+	new_mpath->dev = dev;
+	new_mpath->flags = 0;
+	skb_queue_head_init(&new_mpath->frame_queue);
+	new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
+	new_node->mpath = new_mpath;
+	new_mpath->timer.data = (unsigned long) new_mpath;
+	new_mpath->timer.function = mesh_path_timer;
+	new_mpath->exp_time = jiffies;
+	spin_lock_init(&new_mpath->state_lock);
+	init_timer(&new_mpath->timer);
+
+	hash_idx = mesh_table_hash(dst, dev, mesh_paths);
+	bucket = &mesh_paths->hash_buckets[hash_idx];
+
+	spin_lock(&mesh_paths->hashwlock[hash_idx]);
+
+	hlist_for_each_entry(node, n, bucket, list) {
+		mpath = node->mpath;
+		if (mpath->dev == dev && memcmp(dst, mpath->dst, ETH_ALEN)
+				== 0) {
+			err = -EEXIST;
+			atomic_dec(&sdata->u.sta.mpaths);
+			kfree(new_node);
+			kfree(new_mpath);
+			goto endadd;
+		}
+	}
+
+	hlist_add_head_rcu(&new_node->list, bucket);
+	if (atomic_inc_return(&mesh_paths->entries) >=
+		mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1))
+		grow = 1;
+
+endadd:
+	spin_unlock(&mesh_paths->hashwlock[hash_idx]);
+endadd2:
+	read_unlock(&pathtbl_resize_lock);
+	if (!err && grow) {
+		struct mesh_table *oldtbl, *newtbl;
+
+		write_lock(&pathtbl_resize_lock);
+		oldtbl = mesh_paths;
+		newtbl = mesh_table_grow(mesh_paths);
+		if (!newtbl) {
+			write_unlock(&pathtbl_resize_lock);
+			return -ENOMEM;
+		}
+		rcu_assign_pointer(mesh_paths, newtbl);
+		synchronize_rcu();
+		mesh_table_free(oldtbl, false);
+		write_unlock(&pathtbl_resize_lock);
+	}
+	return err;
+}
+
+
+/**
+ * mesh_plink_broken - deactivates paths and sends perr when a link breaks
+ *
+ * @sta: broken peer link
+ *
+ * This function must be called from the rate control algorithm if enough
+ * delivery errors suggest that a peer link is no longer usable.
+ */
+void mesh_plink_broken(struct sta_info *sta)
+{
+	struct mesh_path *mpath;
+	struct mpath_node *node;
+	struct hlist_node *p;
+	struct net_device *dev = sta->sdata->dev;
+	int i;
+
+	rcu_read_lock();
+	for_each_mesh_entry(mesh_paths, p, node, i) {
+		mpath = node->mpath;
+		spin_lock_bh(&mpath->state_lock);
+		if (mpath->next_hop == sta &&
+		    mpath->flags & MESH_PATH_ACTIVE &&
+		    !(mpath->flags & MESH_PATH_FIXED)) {
+			mpath->flags &= ~MESH_PATH_ACTIVE;
+			++mpath->dsn;
+			spin_unlock_bh(&mpath->state_lock);
+			mesh_path_error_tx(mpath->dst,
+					cpu_to_le32(mpath->dsn),
+					dev->broadcast, dev);
+		} else
+		spin_unlock_bh(&mpath->state_lock);
+	}
+	rcu_read_unlock();
+}
+EXPORT_SYMBOL(mesh_plink_broken);
+
+/**
+ * mesh_path_flush_by_nexthop - Deletes mesh paths if their next hop matches
+ *
+ * @sta - mesh peer to match
+ *
+ * RCU notes: this function is called when a mesh plink transitions from
+ * PLINK_ESTAB to any other state, since PLINK_ESTAB state is the only one that
+ * allows path creation. This will happen before the sta can be freed (because
+ * sta_info_destroy() calls this) so any reader in a rcu read block will be
+ * protected against the plink disappearing.
+ */
+void mesh_path_flush_by_nexthop(struct sta_info *sta)
+{
+	struct mesh_path *mpath;
+	struct mpath_node *node;
+	struct hlist_node *p;
+	int i;
+
+	for_each_mesh_entry(mesh_paths, p, node, i) {
+		mpath = node->mpath;
+		if (mpath->next_hop == sta)
+			mesh_path_del(mpath->dst, mpath->dev);
+	}
+}
+
+void mesh_path_flush(struct net_device *dev)
+{
+	struct mesh_path *mpath;
+	struct mpath_node *node;
+	struct hlist_node *p;
+	int i;
+
+	for_each_mesh_entry(mesh_paths, p, node, i) {
+		mpath = node->mpath;
+		if (mpath->dev == dev)
+			mesh_path_del(mpath->dst, mpath->dev);
+	}
+}
+
+static void mesh_path_node_reclaim(struct rcu_head *rp)
+{
+	struct mpath_node *node = container_of(rp, struct mpath_node, rcu);
+	struct ieee80211_sub_if_data *sdata =
+		IEEE80211_DEV_TO_SUB_IF(node->mpath->dev);
+
+	del_timer_sync(&node->mpath->timer);
+	atomic_dec(&sdata->u.sta.mpaths);
+	kfree(node->mpath);
+	kfree(node);
+}
+
+/**
+ * mesh_path_del - delete a mesh path from the table
+ *
+ * @addr: dst address (ETH_ALEN length)
+ * @dev: local interface
+ *
+ * Returns: 0 if succesful
+ */
+int mesh_path_del(u8 *addr, struct net_device *dev)
+{
+	struct mesh_path *mpath;
+	struct mpath_node *node;
+	struct hlist_head *bucket;
+	struct hlist_node *n;
+	int hash_idx;
+	int err = 0;
+
+	read_lock(&pathtbl_resize_lock);
+	hash_idx = mesh_table_hash(addr, dev, mesh_paths);
+	bucket = &mesh_paths->hash_buckets[hash_idx];
+
+	spin_lock(&mesh_paths->hashwlock[hash_idx]);
+	hlist_for_each_entry(node, n, bucket, list) {
+		mpath = node->mpath;
+		if (mpath->dev == dev &&
+				memcmp(addr, mpath->dst, ETH_ALEN) == 0) {
+			spin_lock_bh(&mpath->state_lock);
+			mpath->flags |= MESH_PATH_RESOLVING;
+			hlist_del_rcu(&node->list);
+			call_rcu(&node->rcu, mesh_path_node_reclaim);
+			atomic_dec(&mesh_paths->entries);
+			spin_unlock_bh(&mpath->state_lock);
+			goto enddel;
+		}
+	}
+
+	err = -ENXIO;
+enddel:
+	spin_unlock(&mesh_paths->hashwlock[hash_idx]);
+	read_unlock(&pathtbl_resize_lock);
+	return err;
+}
+
+/**
+ * mesh_path_tx_pending - sends pending frames in a mesh path queue
+ *
+ * @mpath: mesh path to activate
+ *
+ * Locking: the state_lock of the mpath structure must NOT be held when calling
+ * this function.
+ */
+void mesh_path_tx_pending(struct mesh_path *mpath)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&mpath->frame_queue)) &&
+			(mpath->flags & MESH_PATH_ACTIVE))
+		dev_queue_xmit(skb);
+}
+
+/**
+ * mesh_path_discard_frame - discard a frame whose path could not be resolved
+ *
+ * @skb: frame to discard
+ * @dev: network device the frame was to be sent through
+ *
+ * If the frame was beign forwarded from another MP, a PERR frame will be sent
+ * to the precursor.
+ *
+ * Locking: the function must me called within a rcu_read_lock region
+ */
+void mesh_path_discard_frame(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct mesh_path *mpath;
+	u32 dsn = 0;
+
+	if (skb->pkt_type == PACKET_OTHERHOST) {
+		struct ieee80211s_hdr *prev_meshhdr;
+		int mshhdrlen;
+		u8 *ra, *da;
+
+		prev_meshhdr = ((struct ieee80211s_hdr *)skb->cb);
+		mshhdrlen = ieee80211_get_mesh_hdrlen(prev_meshhdr);
+		da = skb->data;
+		ra = MESH_PREQ(skb);
+		mpath = mesh_path_lookup(da, dev);
+		if (mpath)
+			dsn = ++mpath->dsn;
+		mesh_path_error_tx(skb->data, cpu_to_le32(dsn), ra, dev);
+	}
+
+	kfree_skb(skb);
+	sdata->u.sta.mshstats.dropped_frames_no_route++;
+}
+
+/**
+ * mesh_path_flush_pending - free the pending queue of a mesh path
+ *
+ * @mpath: mesh path whose queue has to be freed
+ *
+ * Locking: the function must me called withing a rcu_read_lock region
+ */
+void mesh_path_flush_pending(struct mesh_path *mpath)
+{
+	struct ieee80211_sub_if_data *sdata;
+	struct sk_buff *skb;
+
+	sdata = IEEE80211_DEV_TO_SUB_IF(mpath->dev);
+
+	while ((skb = skb_dequeue(&mpath->frame_queue)) &&
+			(mpath->flags & MESH_PATH_ACTIVE))
+		mesh_path_discard_frame(skb, mpath->dev);
+}
+
+/**
+ * mesh_path_fix_nexthop - force a specific next hop for a mesh path
+ *
+ * @mpath: the mesh path to modify
+ * @next_hop: the next hop to force
+ *
+ * Locking: this function must be called holding mpath->state_lock
+ */
+void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop)
+{
+	spin_lock_bh(&mpath->state_lock);
+	mesh_path_assign_nexthop(mpath, next_hop);
+	mpath->dsn = 0xffff;
+	mpath->metric = 0;
+	mpath->hop_count = 0;
+	mpath->exp_time = 0;
+	mpath->flags |= MESH_PATH_FIXED;
+	mesh_path_activate(mpath);
+	spin_unlock_bh(&mpath->state_lock);
+	mesh_path_tx_pending(mpath);
+}
+
+static void mesh_path_node_free(struct hlist_node *p, bool free_leafs)
+{
+	struct mesh_path *mpath;
+	struct mpath_node *node = hlist_entry(p, struct mpath_node, list);
+	mpath = node->mpath;
+	hlist_del_rcu(p);
+	synchronize_rcu();
+	if (free_leafs)
+		kfree(mpath);
+	kfree(node);
+}
+
+static void mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl)
+{
+	struct mesh_path *mpath;
+	struct mpath_node *node, *new_node;
+	u32 hash_idx;
+
+	node = hlist_entry(p, struct mpath_node, list);
+	mpath = node->mpath;
+	new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
+	new_node->mpath = mpath;
+	hash_idx = mesh_table_hash(mpath->dst, mpath->dev, newtbl);
+	hlist_add_head(&new_node->list,
+			&newtbl->hash_buckets[hash_idx]);
+}
+
+int mesh_pathtbl_init(void)
+{
+	mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
+	mesh_paths->free_node = &mesh_path_node_free;
+	mesh_paths->copy_node = &mesh_path_node_copy;
+	mesh_paths->mean_chain_len = MEAN_CHAIN_LEN;
+	if (!mesh_paths)
+		return -ENOMEM;
+	return 0;
+}
+
+void mesh_path_expire(struct net_device *dev)
+{
+	struct mesh_path *mpath;
+	struct mpath_node *node;
+	struct hlist_node *p;
+	int i;
+
+	read_lock(&pathtbl_resize_lock);
+	for_each_mesh_entry(mesh_paths, p, node, i) {
+		if (node->mpath->dev != dev)
+			continue;
+		mpath = node->mpath;
+		spin_lock_bh(&mpath->state_lock);
+		if ((!(mpath->flags & MESH_PATH_RESOLVING)) &&
+		    (!(mpath->flags & MESH_PATH_FIXED)) &&
+			time_after(jiffies,
+			 mpath->exp_time + MESH_PATH_EXPIRE)) {
+			spin_unlock_bh(&mpath->state_lock);
+			mesh_path_del(mpath->dst, mpath->dev);
+		} else
+			spin_unlock_bh(&mpath->state_lock);
+	}
+	read_unlock(&pathtbl_resize_lock);
+}
+
+void mesh_pathtbl_unregister(void)
+{
+	mesh_table_free(mesh_paths, true);
+}

+ 761 - 0
net/mac80211/mesh_plink.c

@@ -0,0 +1,761 @@
+/*
+ * Copyright (c) 2008 open80211s Ltd.
+ * Author:     Luis Carlos Cobo <luisca@cozybit.com>
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/random.h>
+#include "ieee80211_i.h"
+#include "ieee80211_rate.h"
+#include "mesh.h"
+
+#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
+#define mpl_dbg(fmt, args...)	printk(KERN_DEBUG fmt, ##args)
+#else
+#define mpl_dbg(fmt, args...)	do { (void)(0); } while (0)
+#endif
+
+#define IEEE80211_FC(type, stype) cpu_to_le16(type | stype)
+#define PLINK_GET_FRAME_SUBTYPE(p) (p)
+#define PLINK_GET_LLID(p) (p + 1)
+#define PLINK_GET_PLID(p) (p + 3)
+
+#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
+				jiffies + HZ * t / 1000))
+
+/* Peer link cancel reasons, all subject to ANA approval */
+#define MESH_LINK_CANCELLED			2
+#define MESH_MAX_NEIGHBORS			3
+#define MESH_CAPABILITY_POLICY_VIOLATION	4
+#define MESH_CLOSE_RCVD				5
+#define MESH_MAX_RETRIES			6
+#define MESH_CONFIRM_TIMEOUT			7
+#define MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS	8
+#define MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE	9
+#define MESH_SECURITY_FAILED_VERIFICATION	10
+
+#define dot11MeshMaxRetries(s) (s->u.sta.mshcfg.dot11MeshMaxRetries)
+#define dot11MeshRetryTimeout(s) (s->u.sta.mshcfg.dot11MeshRetryTimeout)
+#define dot11MeshConfirmTimeout(s) (s->u.sta.mshcfg.dot11MeshConfirmTimeout)
+#define dot11MeshHoldingTimeout(s) (s->u.sta.mshcfg.dot11MeshHoldingTimeout)
+#define dot11MeshMaxPeerLinks(s) (s->u.sta.mshcfg.dot11MeshMaxPeerLinks)
+
+enum plink_frame_type {
+	PLINK_OPEN = 0,
+	PLINK_CONFIRM,
+	PLINK_CLOSE
+};
+
+enum plink_event {
+	PLINK_UNDEFINED,
+	OPN_ACPT,
+	OPN_RJCT,
+	OPN_IGNR,
+	CNF_ACPT,
+	CNF_RJCT,
+	CNF_IGNR,
+	CLS_ACPT,
+	CLS_IGNR
+};
+
+static inline
+void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
+{
+	atomic_inc(&sdata->u.sta.mshstats.estab_plinks);
+	mesh_accept_plinks_update(sdata);
+}
+
+static inline
+void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
+{
+	atomic_dec(&sdata->u.sta.mshstats.estab_plinks);
+	mesh_accept_plinks_update(sdata);
+}
+
+/**
+ * mesh_plink_fsm_restart - restart a mesh peer link finite state machine
+ *
+ * @sta: mes peer link to restart
+ *
+ * Locking: this function must be called holding sta->plink_lock
+ */
+static inline void mesh_plink_fsm_restart(struct sta_info *sta)
+{
+	sta->plink_state = PLINK_LISTEN;
+	sta->llid = sta->plid = sta->reason = 0;
+	sta->plink_retries = 0;
+}
+
+static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
+					 u8 *hw_addr, u64 rates)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct sta_info *sta;
+
+	if (local->num_sta >= MESH_MAX_PLINKS)
+		return NULL;
+
+	sta = sta_info_alloc(sdata, hw_addr, GFP_ATOMIC);
+	if (!sta)
+		return NULL;
+
+	sta->flags |= WLAN_STA_AUTHORIZED;
+	sta->supp_rates[local->hw.conf.channel->band] = rates;
+
+	return sta;
+}
+
+/**
+ * mesh_plink_deactivate - deactivate mesh peer link
+ *
+ * @sta: mesh peer link to deactivate
+ *
+ * All mesh paths with this peer as next hop will be flushed
+ *
+ * Locking: the caller must hold sta->plink_lock
+ */
+static void __mesh_plink_deactivate(struct sta_info *sta)
+{
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+	if (sta->plink_state == PLINK_ESTAB)
+		mesh_plink_dec_estab_count(sdata);
+	sta->plink_state = PLINK_BLOCKED;
+	mesh_path_flush_by_nexthop(sta);
+}
+
+/**
+ * __mesh_plink_deactivate - deactivate mesh peer link
+ *
+ * @sta: mesh peer link to deactivate
+ *
+ * All mesh paths with this peer as next hop will be flushed
+ */
+void mesh_plink_deactivate(struct sta_info *sta)
+{
+	spin_lock_bh(&sta->plink_lock);
+	__mesh_plink_deactivate(sta);
+	spin_unlock_bh(&sta->plink_lock);
+}
+
+static int mesh_plink_frame_tx(struct net_device *dev,
+		enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid,
+		__le16 reason) {
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+	struct ieee80211_mgmt *mgmt;
+	bool include_plid = false;
+	u8 *pos;
+	int ie_len;
+
+	if (!skb)
+		return -1;
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+	/* 25 is the size of the common mgmt part (24) plus the size of the
+	 * common action part (1)
+	 */
+	mgmt = (struct ieee80211_mgmt *)
+		skb_put(skb, 25 + sizeof(mgmt->u.action.u.plink_action));
+	memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.plink_action));
+	mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+					   IEEE80211_STYPE_ACTION);
+	memcpy(mgmt->da, da, ETH_ALEN);
+	memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+	/* BSSID is left zeroed, wildcard value */
+	mgmt->u.action.category = PLINK_CATEGORY;
+	mgmt->u.action.u.plink_action.action_code = action;
+
+	if (action == PLINK_CLOSE)
+		mgmt->u.action.u.plink_action.aux = reason;
+	else {
+		mgmt->u.action.u.plink_action.aux = cpu_to_le16(0x0);
+		if (action == PLINK_CONFIRM) {
+			pos = skb_put(skb, 4);
+			/* two-byte status code followed by two-byte AID */
+			memset(pos, 0, 4);
+		}
+		mesh_mgmt_ies_add(skb, dev);
+	}
+
+	/* Add Peer Link Management element */
+	switch (action) {
+	case PLINK_OPEN:
+		ie_len = 3;
+		break;
+	case PLINK_CONFIRM:
+		ie_len = 5;
+		include_plid = true;
+		break;
+	case PLINK_CLOSE:
+	default:
+		if (!plid)
+			ie_len = 5;
+		else {
+			ie_len = 7;
+			include_plid = true;
+		}
+		break;
+	}
+
+	pos = skb_put(skb, 2 + ie_len);
+	*pos++ = WLAN_EID_PEER_LINK;
+	*pos++ = ie_len;
+	*pos++ = action;
+	memcpy(pos, &llid, 2);
+	if (include_plid) {
+		pos += 2;
+		memcpy(pos, &plid, 2);
+	}
+	if (action == PLINK_CLOSE) {
+		pos += 2;
+		memcpy(pos, &reason, 2);
+	}
+
+	ieee80211_sta_tx(dev, skb, 0);
+	return 0;
+}
+
+void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev,
+			   bool peer_accepting_plinks)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct sta_info *sta;
+
+	rcu_read_lock();
+
+	sta = sta_info_get(local, hw_addr);
+	if (!sta) {
+		sta = mesh_plink_alloc(sdata, hw_addr, rates);
+		if (!sta) {
+			rcu_read_unlock();
+			return;
+		}
+		if (sta_info_insert(sta)) {
+			sta_info_destroy(sta);
+			rcu_read_unlock();
+			return;
+		}
+	}
+
+	sta->last_rx = jiffies;
+	sta->supp_rates[local->hw.conf.channel->band] = rates;
+	if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN &&
+			sdata->u.sta.accepting_plinks &&
+			sdata->u.sta.mshcfg.auto_open_plinks)
+		mesh_plink_open(sta);
+
+	rcu_read_unlock();
+}
+
+static void mesh_plink_timer(unsigned long data)
+{
+	struct sta_info *sta;
+	__le16 llid, plid, reason;
+	struct net_device *dev = NULL;
+	struct ieee80211_sub_if_data *sdata;
+#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
+	DECLARE_MAC_BUF(mac);
+#endif
+
+	/*
+	 * This STA is valid because sta_info_destroy() will
+	 * del_timer_sync() this timer after having made sure
+	 * it cannot be readded (by deleting the plink.)
+	 */
+	sta = (struct sta_info *) data;
+
+	spin_lock_bh(&sta->plink_lock);
+	if (sta->ignore_plink_timer) {
+		sta->ignore_plink_timer = false;
+		spin_unlock_bh(&sta->plink_lock);
+		return;
+	}
+	mpl_dbg("Mesh plink timer for %s fired on state %d\n",
+			print_mac(mac, sta->addr), sta->plink_state);
+	reason = 0;
+	llid = sta->llid;
+	plid = sta->plid;
+	sdata = sta->sdata;
+	dev = sdata->dev;
+
+	switch (sta->plink_state) {
+	case PLINK_OPN_RCVD:
+	case PLINK_OPN_SNT:
+		/* retry timer */
+		if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
+			u32 rand;
+			mpl_dbg("Mesh plink for %s (retry, timeout): %d %d\n",
+					print_mac(mac, sta->addr),
+					sta->plink_retries, sta->plink_timeout);
+			get_random_bytes(&rand, sizeof(u32));
+			sta->plink_timeout = sta->plink_timeout +
+					     rand % sta->plink_timeout;
+			++sta->plink_retries;
+			mod_plink_timer(sta, sta->plink_timeout);
+			spin_unlock_bh(&sta->plink_lock);
+			mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid,
+					    0, 0);
+			break;
+		}
+		reason = cpu_to_le16(MESH_MAX_RETRIES);
+		/* fall through on else */
+	case PLINK_CNF_RCVD:
+		/* confirm timer */
+		if (!reason)
+			reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT);
+		sta->plink_state = PLINK_HOLDING;
+		mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
+		spin_unlock_bh(&sta->plink_lock);
+		mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid,
+				    reason);
+		break;
+	case PLINK_HOLDING:
+		/* holding timer */
+		del_timer(&sta->plink_timer);
+		mesh_plink_fsm_restart(sta);
+		spin_unlock_bh(&sta->plink_lock);
+		break;
+	default:
+		spin_unlock_bh(&sta->plink_lock);
+		break;
+	}
+}
+
+static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
+{
+	sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
+	sta->plink_timer.data = (unsigned long) sta;
+	sta->plink_timer.function = mesh_plink_timer;
+	sta->plink_timeout = timeout;
+	add_timer(&sta->plink_timer);
+}
+
+int mesh_plink_open(struct sta_info *sta)
+{
+	__le16 llid;
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
+	DECLARE_MAC_BUF(mac);
+#endif
+
+	spin_lock_bh(&sta->plink_lock);
+	get_random_bytes(&llid, 2);
+	sta->llid = llid;
+	if (sta->plink_state != PLINK_LISTEN) {
+		spin_unlock_bh(&sta->plink_lock);
+		return -EBUSY;
+	}
+	sta->plink_state = PLINK_OPN_SNT;
+	mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
+	spin_unlock_bh(&sta->plink_lock);
+	mpl_dbg("Mesh plink: starting establishment with %s\n",
+		print_mac(mac, sta->addr));
+
+	return mesh_plink_frame_tx(sdata->dev, PLINK_OPEN,
+				   sta->addr, llid, 0, 0);
+}
+
+void mesh_plink_block(struct sta_info *sta)
+{
+#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
+	DECLARE_MAC_BUF(mac);
+#endif
+
+	spin_lock_bh(&sta->plink_lock);
+	__mesh_plink_deactivate(sta);
+	sta->plink_state = PLINK_BLOCKED;
+	spin_unlock_bh(&sta->plink_lock);
+}
+
+int mesh_plink_close(struct sta_info *sta)
+{
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+	__le16 llid, plid, reason;
+#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
+	DECLARE_MAC_BUF(mac);
+#endif
+
+	mpl_dbg("Mesh plink: closing link with %s\n",
+			print_mac(mac, sta->addr));
+	spin_lock_bh(&sta->plink_lock);
+	sta->reason = cpu_to_le16(MESH_LINK_CANCELLED);
+	reason = sta->reason;
+
+	if (sta->plink_state == PLINK_LISTEN ||
+	    sta->plink_state == PLINK_BLOCKED) {
+		mesh_plink_fsm_restart(sta);
+		spin_unlock_bh(&sta->plink_lock);
+		return 0;
+	} else if (sta->plink_state == PLINK_ESTAB) {
+		__mesh_plink_deactivate(sta);
+		/* The timer should not be running */
+		mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
+	} else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)))
+		sta->ignore_plink_timer = true;
+
+	sta->plink_state = PLINK_HOLDING;
+	llid = sta->llid;
+	plid = sta->plid;
+	spin_unlock_bh(&sta->plink_lock);
+	mesh_plink_frame_tx(sta->sdata->dev, PLINK_CLOSE, sta->addr, llid,
+			    plid, reason);
+	return 0;
+}
+
+void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
+			 size_t len, struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
+	struct ieee802_11_elems elems;
+	struct sta_info *sta;
+	enum plink_event event;
+	enum plink_frame_type ftype;
+	size_t baselen;
+	u8 ie_len;
+	u8 *baseaddr;
+	__le16 plid, llid, reason;
+#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
+	DECLARE_MAC_BUF(mac);
+#endif
+
+	if (is_multicast_ether_addr(mgmt->da)) {
+		mpl_dbg("Mesh plink: ignore frame from multicast address");
+		return;
+	}
+
+	baseaddr = mgmt->u.action.u.plink_action.variable;
+	baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt;
+	if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) {
+		baseaddr += 4;
+		baselen -= 4;
+	}
+	ieee802_11_parse_elems(baseaddr, len - baselen, &elems);
+	if (!elems.peer_link) {
+		mpl_dbg("Mesh plink: missing necessary peer link ie\n");
+		return;
+	}
+
+	ftype = *((u8 *)PLINK_GET_FRAME_SUBTYPE(elems.peer_link));
+	ie_len = elems.peer_link_len;
+	if ((ftype == PLINK_OPEN && ie_len != 3) ||
+	    (ftype == PLINK_CONFIRM && ie_len != 5) ||
+	    (ftype == PLINK_CLOSE && ie_len != 5 && ie_len != 7)) {
+		mpl_dbg("Mesh plink: incorrect plink ie length\n");
+		return;
+	}
+
+	if (ftype != PLINK_CLOSE && (!elems.mesh_id || !elems.mesh_config)) {
+		mpl_dbg("Mesh plink: missing necessary ie\n");
+		return;
+	}
+	/* Note the lines below are correct, the llid in the frame is the plid
+	 * from the point of view of this host.
+	 */
+	memcpy(&plid, PLINK_GET_LLID(elems.peer_link), 2);
+	if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 7))
+		memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2);
+
+	rcu_read_lock();
+
+	sta = sta_info_get(local, mgmt->sa);
+	if (!sta && ftype != PLINK_OPEN) {
+		mpl_dbg("Mesh plink: cls or cnf from unknown peer\n");
+		rcu_read_unlock();
+		return;
+	}
+
+	if (sta && sta->plink_state == PLINK_BLOCKED) {
+		rcu_read_unlock();
+		return;
+	}
+
+	/* Now we will figure out the appropriate event... */
+	event = PLINK_UNDEFINED;
+	if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, dev))) {
+		switch (ftype) {
+		case PLINK_OPEN:
+			event = OPN_RJCT;
+			break;
+		case PLINK_CONFIRM:
+			event = CNF_RJCT;
+			break;
+		case PLINK_CLOSE:
+			/* avoid warning */
+			break;
+		}
+		spin_lock_bh(&sta->plink_lock);
+	} else if (!sta) {
+		/* ftype == PLINK_OPEN */
+		u64 rates;
+		if (!mesh_plink_free_count(sdata)) {
+			mpl_dbg("Mesh plink error: no more free plinks\n");
+			rcu_read_unlock();
+			return;
+		}
+
+		rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
+		sta = mesh_plink_alloc(sdata, mgmt->sa, rates);
+		if (!sta) {
+			mpl_dbg("Mesh plink error: plink table full\n");
+			rcu_read_unlock();
+			return;
+		}
+		if (sta_info_insert(sta)) {
+			sta_info_destroy(sta);
+			rcu_read_unlock();
+			return;
+		}
+		event = OPN_ACPT;
+		spin_lock_bh(&sta->plink_lock);
+	} else {
+		spin_lock_bh(&sta->plink_lock);
+		switch (ftype) {
+		case PLINK_OPEN:
+			if (!mesh_plink_free_count(sdata) ||
+			    (sta->plid && sta->plid != plid))
+				event = OPN_IGNR;
+			else
+				event = OPN_ACPT;
+			break;
+		case PLINK_CONFIRM:
+			if (!mesh_plink_free_count(sdata) ||
+			    (sta->llid != llid || sta->plid != plid))
+				event = CNF_IGNR;
+			else
+				event = CNF_ACPT;
+			break;
+		case PLINK_CLOSE:
+			if (sta->plink_state == PLINK_ESTAB)
+				/* Do not check for llid or plid. This does not
+				 * follow the standard but since multiple plinks
+				 * per sta are not supported, it is necessary in
+				 * order to avoid a livelock when MP A sees an
+				 * establish peer link to MP B but MP B does not
+				 * see it. This can be caused by a timeout in
+				 * B's peer link establishment or B beign
+				 * restarted.
+				 */
+				event = CLS_ACPT;
+			else if (sta->plid != plid)
+				event = CLS_IGNR;
+			else if (ie_len == 7 && sta->llid != llid)
+				event = CLS_IGNR;
+			else
+				event = CLS_ACPT;
+			break;
+		default:
+			mpl_dbg("Mesh plink: unknown frame subtype\n");
+			spin_unlock_bh(&sta->plink_lock);
+			rcu_read_unlock();
+			return;
+		}
+	}
+
+	mpl_dbg("Mesh plink (peer, state, llid, plid, event): %s %d %d %d %d\n",
+			print_mac(mac, mgmt->sa), sta->plink_state,
+			le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
+			event);
+	reason = 0;
+	switch (sta->plink_state) {
+		/* spin_unlock as soon as state is updated at each case */
+	case PLINK_LISTEN:
+		switch (event) {
+		case CLS_ACPT:
+			mesh_plink_fsm_restart(sta);
+			spin_unlock_bh(&sta->plink_lock);
+			break;
+		case OPN_ACPT:
+			sta->plink_state = PLINK_OPN_RCVD;
+			sta->plid = plid;
+			get_random_bytes(&llid, 2);
+			sta->llid = llid;
+			mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
+			spin_unlock_bh(&sta->plink_lock);
+			mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid,
+					    0, 0);
+			mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr,
+					    llid, plid, 0);
+			break;
+		default:
+			spin_unlock_bh(&sta->plink_lock);
+			break;
+		}
+		break;
+
+	case PLINK_OPN_SNT:
+		switch (event) {
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);
+		case CLS_ACPT:
+			if (!reason)
+				reason = cpu_to_le16(MESH_CLOSE_RCVD);
+			sta->reason = reason;
+			sta->plink_state = PLINK_HOLDING;
+			if (!mod_plink_timer(sta,
+					     dot11MeshHoldingTimeout(sdata)))
+				sta->ignore_plink_timer = true;
+
+			llid = sta->llid;
+			spin_unlock_bh(&sta->plink_lock);
+			mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
+					    plid, reason);
+			break;
+		case OPN_ACPT:
+			/* retry timer is left untouched */
+			sta->plink_state = PLINK_OPN_RCVD;
+			sta->plid = plid;
+			llid = sta->llid;
+			spin_unlock_bh(&sta->plink_lock);
+			mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
+					    plid, 0);
+			break;
+		case CNF_ACPT:
+			sta->plink_state = PLINK_CNF_RCVD;
+			if (!mod_plink_timer(sta,
+					     dot11MeshConfirmTimeout(sdata)))
+				sta->ignore_plink_timer = true;
+
+			spin_unlock_bh(&sta->plink_lock);
+			break;
+		default:
+			spin_unlock_bh(&sta->plink_lock);
+			break;
+		}
+		break;
+
+	case PLINK_OPN_RCVD:
+		switch (event) {
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);
+		case CLS_ACPT:
+			if (!reason)
+				reason = cpu_to_le16(MESH_CLOSE_RCVD);
+			sta->reason = reason;
+			sta->plink_state = PLINK_HOLDING;
+			if (!mod_plink_timer(sta,
+					     dot11MeshHoldingTimeout(sdata)))
+				sta->ignore_plink_timer = true;
+
+			llid = sta->llid;
+			spin_unlock_bh(&sta->plink_lock);
+			mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
+					    plid, reason);
+			break;
+		case OPN_ACPT:
+			llid = sta->llid;
+			spin_unlock_bh(&sta->plink_lock);
+			mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
+					    plid, 0);
+			break;
+		case CNF_ACPT:
+			del_timer(&sta->plink_timer);
+			sta->plink_state = PLINK_ESTAB;
+			mesh_plink_inc_estab_count(sdata);
+			spin_unlock_bh(&sta->plink_lock);
+			mpl_dbg("Mesh plink with %s ESTABLISHED\n",
+					print_mac(mac, sta->addr));
+			break;
+		default:
+			spin_unlock_bh(&sta->plink_lock);
+			break;
+		}
+		break;
+
+	case PLINK_CNF_RCVD:
+		switch (event) {
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);
+		case CLS_ACPT:
+			if (!reason)
+				reason = cpu_to_le16(MESH_CLOSE_RCVD);
+			sta->reason = reason;
+			sta->plink_state = PLINK_HOLDING;
+			if (!mod_plink_timer(sta,
+					     dot11MeshHoldingTimeout(sdata)))
+				sta->ignore_plink_timer = true;
+
+			llid = sta->llid;
+			spin_unlock_bh(&sta->plink_lock);
+			mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
+					    plid, reason);
+			break;
+		case OPN_ACPT:
+			del_timer(&sta->plink_timer);
+			sta->plink_state = PLINK_ESTAB;
+			mesh_plink_inc_estab_count(sdata);
+			spin_unlock_bh(&sta->plink_lock);
+			mpl_dbg("Mesh plink with %s ESTABLISHED\n",
+					print_mac(mac, sta->addr));
+			mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
+					    plid, 0);
+			break;
+		default:
+			spin_unlock_bh(&sta->plink_lock);
+			break;
+		}
+		break;
+
+	case PLINK_ESTAB:
+		switch (event) {
+		case CLS_ACPT:
+			reason = cpu_to_le16(MESH_CLOSE_RCVD);
+			sta->reason = reason;
+			__mesh_plink_deactivate(sta);
+			sta->plink_state = PLINK_HOLDING;
+			llid = sta->llid;
+			mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
+			spin_unlock_bh(&sta->plink_lock);
+			mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
+					    plid, reason);
+			break;
+		case OPN_ACPT:
+			llid = sta->llid;
+			spin_unlock_bh(&sta->plink_lock);
+			mesh_plink_frame_tx(dev, PLINK_CONFIRM, sta->addr, llid,
+					    plid, 0);
+			break;
+		default:
+			spin_unlock_bh(&sta->plink_lock);
+			break;
+		}
+		break;
+	case PLINK_HOLDING:
+		switch (event) {
+		case CLS_ACPT:
+			if (del_timer(&sta->plink_timer))
+				sta->ignore_plink_timer = 1;
+			mesh_plink_fsm_restart(sta);
+			spin_unlock_bh(&sta->plink_lock);
+			break;
+		case OPN_ACPT:
+		case CNF_ACPT:
+		case OPN_RJCT:
+		case CNF_RJCT:
+			llid = sta->llid;
+			reason = sta->reason;
+			spin_unlock_bh(&sta->plink_lock);
+			mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
+					    plid, reason);
+			break;
+		default:
+			spin_unlock_bh(&sta->plink_lock);
+		}
+		break;
+	default:
+		/* should not get here, PLINK_BLOCKED is dealt with at the
+		 * beggining of the function
+		 */
+		spin_unlock_bh(&sta->plink_lock);
+		break;
+	}
+
+	rcu_read_unlock();
+}

+ 25 - 11
net/mac80211/rc80211_pid_algo.c

@@ -15,7 +15,7 @@
 #include <linux/debugfs.h>
 #include <linux/debugfs.h>
 #include <net/mac80211.h>
 #include <net/mac80211.h>
 #include "ieee80211_rate.h"
 #include "ieee80211_rate.h"
-
+#include "mesh.h"
 #include "rc80211_pid.h"
 #include "rc80211_pid.h"
 
 
 
 
@@ -77,7 +77,7 @@ static void rate_control_pid_adjust_rate(struct ieee80211_local *local,
 	int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
 	int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
 	int cur = sta->txrate_idx;
 	int cur = sta->txrate_idx;
 
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 	band = sband->band;
 	band = sband->band;
 	n_bitrates = sband->n_bitrates;
 	n_bitrates = sband->n_bitrates;
@@ -148,6 +148,9 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
 				    struct ieee80211_local *local,
 				    struct ieee80211_local *local,
 				    struct sta_info *sta)
 				    struct sta_info *sta)
 {
 {
+#ifdef CONFIG_MAC80211_MESH
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+#endif
 	struct rc_pid_sta_info *spinfo = sta->rate_ctrl_priv;
 	struct rc_pid_sta_info *spinfo = sta->rate_ctrl_priv;
 	struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
 	struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_supported_band *sband;
@@ -178,7 +181,14 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
 		pf = spinfo->last_pf;
 		pf = spinfo->last_pf;
 	else {
 	else {
 		pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
 		pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
+#ifdef CONFIG_MAC80211_MESH
+		if (pf == 100 &&
+		    sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT)
+			mesh_plink_broken(sta);
+#endif
 		pf <<= RC_PID_ARITH_SHIFT;
 		pf <<= RC_PID_ARITH_SHIFT;
+		sta->fail_avg = ((pf + (spinfo->last_pf << 3)) / 9)
+					>> RC_PID_ARITH_SHIFT;
 	}
 	}
 
 
 	spinfo->tx_num_xmit = 0;
 	spinfo->tx_num_xmit = 0;
@@ -239,23 +249,25 @@ static void rate_control_pid_tx_status(void *priv, struct net_device *dev,
 	unsigned long period;
 	unsigned long period;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_supported_band *sband;
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 	sta = sta_info_get(local, hdr->addr1);
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
 
 	if (!sta)
 	if (!sta)
-		return;
+		goto unlock;
 
 
 	/* Don't update the state if we're not controlling the rate. */
 	/* Don't update the state if we're not controlling the rate. */
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
 		sta->txrate_idx = sdata->bss->max_ratectrl_rateidx;
 		sta->txrate_idx = sdata->bss->max_ratectrl_rateidx;
-		return;
+		goto unlock;
 	}
 	}
 
 
 	/* Ignore all frames that were sent with a different rate than the rate
 	/* Ignore all frames that were sent with a different rate than the rate
 	 * we currently advise mac80211 to use. */
 	 * we currently advise mac80211 to use. */
 	if (status->control.tx_rate != &sband->bitrates[sta->txrate_idx])
 	if (status->control.tx_rate != &sband->bitrates[sta->txrate_idx])
-		goto ignore;
+		goto unlock;
 
 
 	spinfo = sta->rate_ctrl_priv;
 	spinfo = sta->rate_ctrl_priv;
 	spinfo->tx_num_xmit++;
 	spinfo->tx_num_xmit++;
@@ -293,8 +305,8 @@ static void rate_control_pid_tx_status(void *priv, struct net_device *dev,
 	if (time_after(jiffies, spinfo->last_sample + period))
 	if (time_after(jiffies, spinfo->last_sample + period))
 		rate_control_pid_sample(pinfo, local, sta);
 		rate_control_pid_sample(pinfo, local, sta);
 
 
-ignore:
-	sta_info_put(sta);
+ unlock:
+	rcu_read_unlock();
 }
 }
 
 
 static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
 static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
@@ -309,6 +321,8 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
 	int rateidx;
 	int rateidx;
 	u16 fc;
 	u16 fc;
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 	sta = sta_info_get(local, hdr->addr1);
 
 
 	/* Send management frames and broadcast/multicast data using lowest
 	/* Send management frames and broadcast/multicast data using lowest
@@ -317,8 +331,7 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
 	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
 	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
 	    is_multicast_ether_addr(hdr->addr1) || !sta) {
 	    is_multicast_ether_addr(hdr->addr1) || !sta) {
 		sel->rate = rate_lowest(local, sband, sta);
 		sel->rate = rate_lowest(local, sband, sta);
-		if (sta)
-			sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 		return;
 	}
 	}
 
 
@@ -334,7 +347,7 @@ static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
 
 
 	sta->last_txrate_idx = rateidx;
 	sta->last_txrate_idx = rateidx;
 
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 
 	sel->rate = &sband->bitrates[rateidx];
 	sel->rate = &sband->bitrates[rateidx];
 
 
@@ -357,6 +370,7 @@ static void rate_control_pid_rate_init(void *priv, void *priv_sta,
 
 
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 	sta->txrate_idx = rate_lowest_index(local, sband, sta);
 	sta->txrate_idx = rate_lowest_index(local, sband, sta);
+	sta->fail_avg = 0;
 }
 }
 
 
 static void *rate_control_pid_alloc(struct ieee80211_local *local)
 static void *rate_control_pid_alloc(struct ieee80211_local *local)

+ 11 - 7
net/mac80211/rc80211_simple.c

@@ -40,7 +40,7 @@ static void rate_control_rate_inc(struct ieee80211_local *local,
 	int i = sta->txrate_idx;
 	int i = sta->txrate_idx;
 	int maxrate;
 	int maxrate;
 
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
 		/* forced unicast rate - do not change STA rate */
 		/* forced unicast rate - do not change STA rate */
 		return;
 		return;
@@ -70,7 +70,7 @@ static void rate_control_rate_dec(struct ieee80211_local *local,
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_supported_band *sband;
 	int i = sta->txrate_idx;
 	int i = sta->txrate_idx;
 
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
 		/* forced unicast rate - do not change STA rate */
 		/* forced unicast rate - do not change STA rate */
 		return;
 		return;
@@ -118,10 +118,12 @@ static void rate_control_simple_tx_status(void *priv, struct net_device *dev,
 	struct sta_info *sta;
 	struct sta_info *sta;
 	struct sta_rate_control *srctrl;
 	struct sta_rate_control *srctrl;
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 	sta = sta_info_get(local, hdr->addr1);
 
 
 	if (!sta)
 	if (!sta)
-	    return;
+		goto unlock;
 
 
 	srctrl = sta->rate_ctrl_priv;
 	srctrl = sta->rate_ctrl_priv;
 	srctrl->tx_num_xmit++;
 	srctrl->tx_num_xmit++;
@@ -191,7 +193,8 @@ static void rate_control_simple_tx_status(void *priv, struct net_device *dev,
 		}
 		}
 	}
 	}
 
 
-	sta_info_put(sta);
+ unlock:
+	rcu_read_unlock();
 }
 }
 
 
 
 
@@ -208,6 +211,8 @@ rate_control_simple_get_rate(void *priv, struct net_device *dev,
 	int rateidx;
 	int rateidx;
 	u16 fc;
 	u16 fc;
 
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 	sta = sta_info_get(local, hdr->addr1);
 
 
 	/* Send management frames and broadcast/multicast data using lowest
 	/* Send management frames and broadcast/multicast data using lowest
@@ -216,8 +221,7 @@ rate_control_simple_get_rate(void *priv, struct net_device *dev,
 	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
 	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
 	    is_multicast_ether_addr(hdr->addr1) || !sta) {
 	    is_multicast_ether_addr(hdr->addr1) || !sta) {
 		sel->rate = rate_lowest(local, sband, sta);
 		sel->rate = rate_lowest(local, sband, sta);
-		if (sta)
-			sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 		return;
 	}
 	}
 
 
@@ -233,7 +237,7 @@ rate_control_simple_get_rate(void *priv, struct net_device *dev,
 
 
 	sta->last_txrate_idx = rateidx;
 	sta->last_txrate_idx = rateidx;
 
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 
 	sel->rate = &sband->bitrates[rateidx];
 	sel->rate = &sband->bitrates[rateidx];
 }
 }

+ 210 - 99
net/mac80211/rx.c

@@ -20,6 +20,7 @@
 
 
 #include "ieee80211_i.h"
 #include "ieee80211_i.h"
 #include "ieee80211_led.h"
 #include "ieee80211_led.h"
+#include "mesh.h"
 #include "wep.h"
 #include "wep.h"
 #include "wpa.h"
 #include "wpa.h"
 #include "tkip.h"
 #include "tkip.h"
@@ -250,7 +251,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
 }
 }
 
 
 
 
-static void ieee80211_parse_qos(struct ieee80211_txrx_data *rx)
+static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
 {
 {
 	u8 *data = rx->skb->data;
 	u8 *data = rx->skb->data;
 	int tid;
 	int tid;
@@ -261,9 +262,9 @@ static void ieee80211_parse_qos(struct ieee80211_txrx_data *rx)
 		/* frame has qos control */
 		/* frame has qos control */
 		tid = qc[0] & QOS_CONTROL_TID_MASK;
 		tid = qc[0] & QOS_CONTROL_TID_MASK;
 		if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
 		if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
-			rx->flags |= IEEE80211_TXRXD_RX_AMSDU;
+			rx->flags |= IEEE80211_RX_AMSDU;
 		else
 		else
-			rx->flags &= ~IEEE80211_TXRXD_RX_AMSDU;
+			rx->flags &= ~IEEE80211_RX_AMSDU;
 	} else {
 	} else {
 		if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
 		if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
 			/* Separate TID for management frames */
 			/* Separate TID for management frames */
@@ -279,13 +280,13 @@ static void ieee80211_parse_qos(struct ieee80211_txrx_data *rx)
 	if (rx->sta)
 	if (rx->sta)
 		I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]);
 		I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]);
 
 
-	rx->u.rx.queue = tid;
+	rx->queue = tid;
 	/* Set skb->priority to 1d tag if highest order bit of TID is not set.
 	/* Set skb->priority to 1d tag if highest order bit of TID is not set.
 	 * For now, set skb->priority to 0 for other cases. */
 	 * For now, set skb->priority to 0 for other cases. */
 	rx->skb->priority = (tid > 7) ? 0 : tid;
 	rx->skb->priority = (tid > 7) ? 0 : tid;
 }
 }
 
 
-static void ieee80211_verify_ip_alignment(struct ieee80211_txrx_data *rx)
+static void ieee80211_verify_ip_alignment(struct ieee80211_rx_data *rx)
 {
 {
 #ifdef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT
 #ifdef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT
 	int hdrlen;
 	int hdrlen;
@@ -313,7 +314,7 @@ static void ieee80211_verify_ip_alignment(struct ieee80211_txrx_data *rx)
 	 * to move the 802.11 header further back in that case.
 	 * to move the 802.11 header further back in that case.
 	 */
 	 */
 	hdrlen = ieee80211_get_hdrlen(rx->fc);
 	hdrlen = ieee80211_get_hdrlen(rx->fc);
-	if (rx->flags & IEEE80211_TXRXD_RX_AMSDU)
+	if (rx->flags & IEEE80211_RX_AMSDU)
 		hdrlen += ETH_HLEN;
 		hdrlen += ETH_HLEN;
 	WARN_ON_ONCE(((unsigned long)(rx->skb->data + hdrlen)) & 3);
 	WARN_ON_ONCE(((unsigned long)(rx->skb->data + hdrlen)) & 3);
 #endif
 #endif
@@ -356,32 +357,32 @@ static u32 ieee80211_rx_load_stats(struct ieee80211_local *local,
 /* rx handlers */
 /* rx handlers */
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_if_stats(struct ieee80211_rx_data *rx)
 {
 {
 	if (rx->sta)
 	if (rx->sta)
-		rx->sta->channel_use_raw += rx->u.rx.load;
-	rx->sdata->channel_use_raw += rx->u.rx.load;
+		rx->sta->channel_use_raw += rx->load;
+	rx->sdata->channel_use_raw += rx->load;
 	return RX_CONTINUE;
 	return RX_CONTINUE;
 }
 }
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
 {
 {
 	struct ieee80211_local *local = rx->local;
 	struct ieee80211_local *local = rx->local;
 	struct sk_buff *skb = rx->skb;
 	struct sk_buff *skb = rx->skb;
 
 
 	if (unlikely(local->sta_hw_scanning))
 	if (unlikely(local->sta_hw_scanning))
-		return ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
+		return ieee80211_sta_rx_scan(rx->dev, skb, rx->status);
 
 
 	if (unlikely(local->sta_sw_scanning)) {
 	if (unlikely(local->sta_sw_scanning)) {
 		/* drop all the other packets during a software scan anyway */
 		/* drop all the other packets during a software scan anyway */
-		if (ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status)
+		if (ieee80211_sta_rx_scan(rx->dev, skb, rx->status)
 		    != RX_QUEUED)
 		    != RX_QUEUED)
 			dev_kfree_skb(skb);
 			dev_kfree_skb(skb);
 		return RX_QUEUED;
 		return RX_QUEUED;
 	}
 	}
 
 
-	if (unlikely(rx->flags & IEEE80211_TXRXD_RXIN_SCAN)) {
+	if (unlikely(rx->flags & IEEE80211_RX_IN_SCAN)) {
 		/* scanning finished during invoking of handlers */
 		/* scanning finished during invoking of handlers */
 		I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
 		I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
 		return RX_DROP_UNUSABLE;
 		return RX_DROP_UNUSABLE;
@@ -391,23 +392,75 @@ ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
 }
 }
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
+ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
+{
+	int hdrlen = ieee80211_get_hdrlen(rx->fc);
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
+
+#define msh_h_get(h, l) ((struct ieee80211s_hdr *) ((u8 *)h + l))
+
+	if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
+		if (!((rx->fc & IEEE80211_FCTL_FROMDS) &&
+		      (rx->fc & IEEE80211_FCTL_TODS)))
+			return RX_DROP_MONITOR;
+		if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0)
+			return RX_DROP_MONITOR;
+	}
+
+	/* If there is not an established peer link and this is not a peer link
+	 * establisment frame, beacon or probe, drop the frame.
+	 */
+
+	if (!rx->sta || sta_plink_state(rx->sta) != PLINK_ESTAB) {
+		struct ieee80211_mgmt *mgmt;
+
+		if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
+			return RX_DROP_MONITOR;
+
+		switch (rx->fc & IEEE80211_FCTL_STYPE) {
+		case IEEE80211_STYPE_ACTION:
+			mgmt = (struct ieee80211_mgmt *)hdr;
+			if (mgmt->u.action.category != PLINK_CATEGORY)
+				return RX_DROP_MONITOR;
+			/* fall through on else */
+		case IEEE80211_STYPE_PROBE_REQ:
+		case IEEE80211_STYPE_PROBE_RESP:
+		case IEEE80211_STYPE_BEACON:
+			return RX_CONTINUE;
+			break;
+		default:
+			return RX_DROP_MONITOR;
+		}
+
+	 } else if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA &&
+		    is_broadcast_ether_addr(hdr->addr1) &&
+		    mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->dev))
+		return RX_DROP_MONITOR;
+#undef msh_h_get
+
+	return RX_CONTINUE;
+}
+
+
+static ieee80211_rx_result
+ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
 {
 {
 	struct ieee80211_hdr *hdr;
 	struct ieee80211_hdr *hdr;
+
 	hdr = (struct ieee80211_hdr *) rx->skb->data;
 	hdr = (struct ieee80211_hdr *) rx->skb->data;
 
 
 	/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
 	/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
 	if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
 	if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
 		if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
 		if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
-			     rx->sta->last_seq_ctrl[rx->u.rx.queue] ==
+			     rx->sta->last_seq_ctrl[rx->queue] ==
 			     hdr->seq_ctrl)) {
 			     hdr->seq_ctrl)) {
-			if (rx->flags & IEEE80211_TXRXD_RXRA_MATCH) {
+			if (rx->flags & IEEE80211_RX_RA_MATCH) {
 				rx->local->dot11FrameDuplicateCount++;
 				rx->local->dot11FrameDuplicateCount++;
 				rx->sta->num_duplicates++;
 				rx->sta->num_duplicates++;
 			}
 			}
 			return RX_DROP_MONITOR;
 			return RX_DROP_MONITOR;
 		} else
 		} else
-			rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
+			rx->sta->last_seq_ctrl[rx->queue] = hdr->seq_ctrl;
 	}
 	}
 
 
 	if (unlikely(rx->skb->len < 16)) {
 	if (unlikely(rx->skb->len < 16)) {
@@ -423,6 +476,10 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
 	 * deauth/disassoc frames when needed. In addition, hostapd is
 	 * deauth/disassoc frames when needed. In addition, hostapd is
 	 * responsible for filtering on both auth and assoc states.
 	 * responsible for filtering on both auth and assoc states.
 	 */
 	 */
+
+	if (ieee80211_vif_is_mesh(&rx->sdata->vif))
+		return ieee80211_rx_mesh_check(rx);
+
 	if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
 	if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
 		      ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
 		      ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
 		       (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
 		       (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
@@ -431,7 +488,7 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
 		if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
 		if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
 		     !(rx->fc & IEEE80211_FCTL_TODS) &&
 		     !(rx->fc & IEEE80211_FCTL_TODS) &&
 		     (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
 		     (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
-		    || !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
+		    || !(rx->flags & IEEE80211_RX_RA_MATCH)) {
 			/* Drop IBSS frames and frames for other hosts
 			/* Drop IBSS frames and frames for other hosts
 			 * silently. */
 			 * silently. */
 			return RX_DROP_MONITOR;
 			return RX_DROP_MONITOR;
@@ -445,7 +502,7 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
 
 
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 {
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
 	int keyidx;
 	int keyidx;
@@ -486,7 +543,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
 	 * No point in finding a key and decrypting if the frame is neither
 	 * No point in finding a key and decrypting if the frame is neither
 	 * addressed to us nor a multicast frame.
 	 * addressed to us nor a multicast frame.
 	 */
 	 */
-	if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 		return RX_CONTINUE;
 		return RX_CONTINUE;
 
 
 	if (rx->sta)
 	if (rx->sta)
@@ -504,8 +561,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
 		 * we somehow allow the driver to tell us which key
 		 * we somehow allow the driver to tell us which key
 		 * the hardware used if this flag is set?
 		 * the hardware used if this flag is set?
 		 */
 		 */
-		if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
-		    (rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED))
+		if ((rx->status->flag & RX_FLAG_DECRYPTED) &&
+		    (rx->status->flag & RX_FLAG_IV_STRIPPED))
 			return RX_CONTINUE;
 			return RX_CONTINUE;
 
 
 		hdrlen = ieee80211_get_hdrlen(rx->fc);
 		hdrlen = ieee80211_get_hdrlen(rx->fc);
@@ -546,8 +603,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
 	/* Check for weak IVs if possible */
 	/* Check for weak IVs if possible */
 	if (rx->sta && rx->key->conf.alg == ALG_WEP &&
 	if (rx->sta && rx->key->conf.alg == ALG_WEP &&
 	    ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
 	    ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
-	    (!(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED) ||
-	     !(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) &&
+	    (!(rx->status->flag & RX_FLAG_IV_STRIPPED) ||
+	     !(rx->status->flag & RX_FLAG_DECRYPTED)) &&
 	    ieee80211_wep_is_weak_iv(rx->skb, rx->key))
 	    ieee80211_wep_is_weak_iv(rx->skb, rx->key))
 		rx->sta->wep_weak_iv_count++;
 		rx->sta->wep_weak_iv_count++;
 
 
@@ -564,7 +621,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
 	}
 	}
 
 
 	/* either the frame has been decrypted or will be dropped */
 	/* either the frame has been decrypted or will be dropped */
-	rx->u.rx.status->flag |= RX_FLAG_DECRYPTED;
+	rx->status->flag |= RX_FLAG_DECRYPTED;
 
 
 	return result;
 	return result;
 }
 }
@@ -574,7 +631,7 @@ static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta)
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_sub_if_data *sdata;
 	DECLARE_MAC_BUF(mac);
 	DECLARE_MAC_BUF(mac);
 
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 
 
 	if (sdata->bss)
 	if (sdata->bss)
 		atomic_inc(&sdata->bss->num_sta_ps);
 		atomic_inc(&sdata->bss->num_sta_ps);
@@ -595,7 +652,7 @@ static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta)
 	struct ieee80211_tx_packet_data *pkt_data;
 	struct ieee80211_tx_packet_data *pkt_data;
 	DECLARE_MAC_BUF(mac);
 	DECLARE_MAC_BUF(mac);
 
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 
 
 	if (sdata->bss)
 	if (sdata->bss)
 		atomic_dec(&sdata->bss->num_sta_ps);
 		atomic_dec(&sdata->bss->num_sta_ps);
@@ -634,7 +691,7 @@ static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta)
 }
 }
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
 {
 {
 	struct sta_info *sta = rx->sta;
 	struct sta_info *sta = rx->sta;
 	struct net_device *dev = rx->dev;
 	struct net_device *dev = rx->dev;
@@ -657,24 +714,26 @@ ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
 		/* Update last_rx only for unicast frames in order to prevent
 		/* Update last_rx only for unicast frames in order to prevent
 		 * the Probe Request frames (the only broadcast frames from a
 		 * the Probe Request frames (the only broadcast frames from a
 		 * STA in infrastructure mode) from keeping a connection alive.
 		 * STA in infrastructure mode) from keeping a connection alive.
+		 * Mesh beacons will update last_rx when if they are found to
+		 * match the current local configuration when processed.
 		 */
 		 */
 		sta->last_rx = jiffies;
 		sta->last_rx = jiffies;
 	}
 	}
 
 
-	if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 		return RX_CONTINUE;
 		return RX_CONTINUE;
 
 
 	sta->rx_fragments++;
 	sta->rx_fragments++;
 	sta->rx_bytes += rx->skb->len;
 	sta->rx_bytes += rx->skb->len;
-	sta->last_rssi = rx->u.rx.status->ssi;
-	sta->last_signal = rx->u.rx.status->signal;
-	sta->last_noise = rx->u.rx.status->noise;
+	sta->last_rssi = rx->status->ssi;
+	sta->last_signal = rx->status->signal;
+	sta->last_noise = rx->status->noise;
 
 
 	if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
 	if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
 		/* Change STA power saving mode only in the end of a frame
 		/* Change STA power saving mode only in the end of a frame
 		 * exchange sequence */
 		 * exchange sequence */
 		if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
 		if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
-			rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta);
+			rx->sent_ps_buffered += ap_sta_ps_end(dev, sta);
 		else if (!(sta->flags & WLAN_STA_PS) &&
 		else if (!(sta->flags & WLAN_STA_PS) &&
 			 (rx->fc & IEEE80211_FCTL_PM))
 			 (rx->fc & IEEE80211_FCTL_PM))
 			ap_sta_ps_start(dev, sta);
 			ap_sta_ps_start(dev, sta);
@@ -779,7 +838,7 @@ ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata,
 }
 }
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
 {
 {
 	struct ieee80211_hdr *hdr;
 	struct ieee80211_hdr *hdr;
 	u16 sc;
 	u16 sc;
@@ -805,14 +864,14 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
 	if (frag == 0) {
 	if (frag == 0) {
 		/* This is the first fragment of a new frame. */
 		/* This is the first fragment of a new frame. */
 		entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
 		entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
-						 rx->u.rx.queue, &(rx->skb));
+						 rx->queue, &(rx->skb));
 		if (rx->key && rx->key->conf.alg == ALG_CCMP &&
 		if (rx->key && rx->key->conf.alg == ALG_CCMP &&
 		    (rx->fc & IEEE80211_FCTL_PROTECTED)) {
 		    (rx->fc & IEEE80211_FCTL_PROTECTED)) {
 			/* Store CCMP PN so that we can verify that the next
 			/* Store CCMP PN so that we can verify that the next
 			 * fragment has a sequential PN value. */
 			 * fragment has a sequential PN value. */
 			entry->ccmp = 1;
 			entry->ccmp = 1;
 			memcpy(entry->last_pn,
 			memcpy(entry->last_pn,
-			       rx->key->u.ccmp.rx_pn[rx->u.rx.queue],
+			       rx->key->u.ccmp.rx_pn[rx->queue],
 			       CCMP_PN_LEN);
 			       CCMP_PN_LEN);
 		}
 		}
 		return RX_QUEUED;
 		return RX_QUEUED;
@@ -822,7 +881,7 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
 	 * fragment cache. Add this fragment to the end of the pending entry.
 	 * fragment cache. Add this fragment to the end of the pending entry.
 	 */
 	 */
 	entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
 	entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
-					  rx->u.rx.queue, hdr);
+					  rx->queue, hdr);
 	if (!entry) {
 	if (!entry) {
 		I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
 		I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
 		return RX_DROP_MONITOR;
 		return RX_DROP_MONITOR;
@@ -841,7 +900,7 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
 			if (pn[i])
 			if (pn[i])
 				break;
 				break;
 		}
 		}
-		rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue];
+		rpn = rx->key->u.ccmp.rx_pn[rx->queue];
 		if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
 		if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
 			if (net_ratelimit())
 			if (net_ratelimit())
 				printk(KERN_DEBUG "%s: defrag: CCMP PN not "
 				printk(KERN_DEBUG "%s: defrag: CCMP PN not "
@@ -882,7 +941,7 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
 	}
 	}
 
 
 	/* Complete frame has been reassembled - process it now */
 	/* Complete frame has been reassembled - process it now */
-	rx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+	rx->flags |= IEEE80211_RX_FRAGMENTED;
 
 
  out:
  out:
 	if (rx->sta)
 	if (rx->sta)
@@ -895,7 +954,7 @@ ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
 }
 }
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
 {
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
 	struct sk_buff *skb;
 	struct sk_buff *skb;
@@ -905,7 +964,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
 	if (likely(!rx->sta ||
 	if (likely(!rx->sta ||
 		   (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
 		   (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
 		   (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
 		   (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
-		   !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)))
+		   !(rx->flags & IEEE80211_RX_RA_MATCH)))
 		return RX_CONTINUE;
 		return RX_CONTINUE;
 
 
 	if ((sdata->vif.type != IEEE80211_IF_TYPE_AP) &&
 	if ((sdata->vif.type != IEEE80211_IF_TYPE_AP) &&
@@ -949,7 +1008,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
 		if (no_pending_pkts)
 		if (no_pending_pkts)
 			sta_info_clear_tim_bit(rx->sta);
 			sta_info_clear_tim_bit(rx->sta);
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-	} else if (!rx->u.rx.sent_ps_buffered) {
+	} else if (!rx->sent_ps_buffered) {
 		/*
 		/*
 		 * FIXME: This can be the result of a race condition between
 		 * FIXME: This can be the result of a race condition between
 		 *	  us expiring a frame and the station polling for it.
 		 *	  us expiring a frame and the station polling for it.
@@ -970,7 +1029,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
 }
 }
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx)
 {
 {
 	u16 fc = rx->fc;
 	u16 fc = rx->fc;
 	u8 *data = rx->skb->data;
 	u8 *data = rx->skb->data;
@@ -990,7 +1049,7 @@ ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
 }
 }
 
 
 static int
 static int
-ieee80211_802_1x_port_control(struct ieee80211_txrx_data *rx)
+ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
 {
 {
 	if (unlikely(!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED))) {
 	if (unlikely(!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED))) {
 #ifdef CONFIG_MAC80211_DEBUG
 #ifdef CONFIG_MAC80211_DEBUG
@@ -1005,13 +1064,13 @@ ieee80211_802_1x_port_control(struct ieee80211_txrx_data *rx)
 }
 }
 
 
 static int
 static int
-ieee80211_drop_unencrypted(struct ieee80211_txrx_data *rx)
+ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx)
 {
 {
 	/*
 	/*
 	 * Pass through unencrypted frames if the hardware has
 	 * Pass through unencrypted frames if the hardware has
 	 * decrypted them already.
 	 * decrypted them already.
 	 */
 	 */
-	if (rx->u.rx.status->flag & RX_FLAG_DECRYPTED)
+	if (rx->status->flag & RX_FLAG_DECRYPTED)
 		return 0;
 		return 0;
 
 
 	/* Drop unencrypted frames if key is set. */
 	/* Drop unencrypted frames if key is set. */
@@ -1028,7 +1087,7 @@ ieee80211_drop_unencrypted(struct ieee80211_txrx_data *rx)
 }
 }
 
 
 static int
 static int
-ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
+ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
 {
 {
 	struct net_device *dev = rx->dev;
 	struct net_device *dev = rx->dev;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
@@ -1050,6 +1109,21 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
 
 
 	hdrlen = ieee80211_get_hdrlen(fc);
 	hdrlen = ieee80211_get_hdrlen(fc);
 
 
+	if (ieee80211_vif_is_mesh(&sdata->vif)) {
+		int meshhdrlen = ieee80211_get_mesh_hdrlen(
+				(struct ieee80211s_hdr *) (skb->data + hdrlen));
+		/* Copy on cb:
+		 *  - mesh header: to be used for mesh forwarding
+		 * decision. It will also be used as mesh header template at
+		 * tx.c:ieee80211_subif_start_xmit() if interface
+		 * type is mesh and skb->pkt_type == PACKET_OTHERHOST
+		 *  - ta: to be used if a RERR needs to be sent.
+		 */
+		memcpy(skb->cb, skb->data + hdrlen, meshhdrlen);
+		memcpy(MESH_PREQ(skb), hdr->addr2, ETH_ALEN);
+		hdrlen += meshhdrlen;
+	}
+
 	/* convert IEEE 802.11 header + possible LLC headers into Ethernet
 	/* convert IEEE 802.11 header + possible LLC headers into Ethernet
 	 * header
 	 * header
 	 * IEEE 802.11 address fields:
 	 * IEEE 802.11 address fields:
@@ -1083,9 +1157,10 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
 		memcpy(dst, hdr->addr3, ETH_ALEN);
 		memcpy(dst, hdr->addr3, ETH_ALEN);
 		memcpy(src, hdr->addr4, ETH_ALEN);
 		memcpy(src, hdr->addr4, ETH_ALEN);
 
 
-		if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_WDS)) {
-			if (net_ratelimit())
-				printk(KERN_DEBUG "%s: dropped FromDS&ToDS "
+		 if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_WDS &&
+			     sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)) {
+			 if (net_ratelimit())
+				 printk(KERN_DEBUG "%s: dropped FromDS&ToDS "
 				       "frame (RA=%s TA=%s DA=%s SA=%s)\n",
 				       "frame (RA=%s TA=%s DA=%s SA=%s)\n",
 				       rx->dev->name,
 				       rx->dev->name,
 				       print_mac(mac, hdr->addr1),
 				       print_mac(mac, hdr->addr1),
@@ -1160,7 +1235,7 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
 /*
 /*
  * requires that rx->skb is a frame with ethernet header
  * requires that rx->skb is a frame with ethernet header
  */
  */
-static bool ieee80211_frame_allowed(struct ieee80211_txrx_data *rx)
+static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx)
 {
 {
 	static const u8 pae_group_addr[ETH_ALEN]
 	static const u8 pae_group_addr[ETH_ALEN]
 		= { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
 		= { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
@@ -1186,7 +1261,7 @@ static bool ieee80211_frame_allowed(struct ieee80211_txrx_data *rx)
  * requires that rx->skb is a frame with ethernet header
  * requires that rx->skb is a frame with ethernet header
  */
  */
 static void
 static void
-ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
+ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
 {
 {
 	struct net_device *dev = rx->dev;
 	struct net_device *dev = rx->dev;
 	struct ieee80211_local *local = rx->local;
 	struct ieee80211_local *local = rx->local;
@@ -1200,7 +1275,7 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
 
 
 	if (local->bridge_packets && (sdata->vif.type == IEEE80211_IF_TYPE_AP ||
 	if (local->bridge_packets && (sdata->vif.type == IEEE80211_IF_TYPE_AP ||
 				      sdata->vif.type == IEEE80211_IF_TYPE_VLAN) &&
 				      sdata->vif.type == IEEE80211_IF_TYPE_VLAN) &&
-	    (rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
+	    (rx->flags & IEEE80211_RX_RA_MATCH)) {
 		if (is_multicast_ether_addr(ehdr->h_dest)) {
 		if (is_multicast_ether_addr(ehdr->h_dest)) {
 			/*
 			/*
 			 * send multicast frames both to higher layers in
 			 * send multicast frames both to higher layers in
@@ -1212,7 +1287,7 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
 				       "multicast frame\n", dev->name);
 				       "multicast frame\n", dev->name);
 		} else {
 		} else {
 			dsta = sta_info_get(local, skb->data);
 			dsta = sta_info_get(local, skb->data);
-			if (dsta && dsta->dev == dev) {
+			if (dsta && dsta->sdata->dev == dev) {
 				/*
 				/*
 				 * The destination station is associated to
 				 * The destination station is associated to
 				 * this AP (in this VLAN), so send the frame
 				 * this AP (in this VLAN), so send the frame
@@ -1222,8 +1297,38 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
 				xmit_skb = skb;
 				xmit_skb = skb;
 				skb = NULL;
 				skb = NULL;
 			}
 			}
-			if (dsta)
-				sta_info_put(dsta);
+		}
+	}
+
+	/* Mesh forwarding */
+	if (ieee80211_vif_is_mesh(&sdata->vif)) {
+		u8 *mesh_ttl = &((struct ieee80211s_hdr *)skb->cb)->ttl;
+		(*mesh_ttl)--;
+
+		if (is_multicast_ether_addr(skb->data)) {
+			if (*mesh_ttl > 0) {
+				xmit_skb = skb_copy(skb, GFP_ATOMIC);
+				if (!xmit_skb && net_ratelimit())
+					printk(KERN_DEBUG "%s: failed to clone "
+					       "multicast frame\n", dev->name);
+				else
+					xmit_skb->pkt_type = PACKET_OTHERHOST;
+			} else
+				IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta,
+							     dropped_frames_ttl);
+		} else if (skb->pkt_type != PACKET_OTHERHOST &&
+			compare_ether_addr(dev->dev_addr, skb->data) != 0) {
+			if (*mesh_ttl == 0) {
+				IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.sta,
+							     dropped_frames_ttl);
+				dev_kfree_skb(skb);
+				skb = NULL;
+			} else {
+				xmit_skb = skb;
+				xmit_skb->pkt_type = PACKET_OTHERHOST;
+				if (!(dev->flags & IFF_PROMISC))
+					skb  = NULL;
+			}
 		}
 		}
 	}
 	}
 
 
@@ -1244,7 +1349,7 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
 }
 }
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
 {
 {
 	struct net_device *dev = rx->dev;
 	struct net_device *dev = rx->dev;
 	struct ieee80211_local *local = rx->local;
 	struct ieee80211_local *local = rx->local;
@@ -1264,7 +1369,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx)
 	if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
 	if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
 		return RX_DROP_MONITOR;
 		return RX_DROP_MONITOR;
 
 
-	if (!(rx->flags & IEEE80211_TXRXD_RX_AMSDU))
+	if (!(rx->flags & IEEE80211_RX_AMSDU))
 		return RX_CONTINUE;
 		return RX_CONTINUE;
 
 
 	err = ieee80211_data_to_8023(rx);
 	err = ieee80211_data_to_8023(rx);
@@ -1361,7 +1466,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx)
 }
 }
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
 {
 {
 	struct net_device *dev = rx->dev;
 	struct net_device *dev = rx->dev;
 	u16 fc;
 	u16 fc;
@@ -1392,7 +1497,7 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
 }
 }
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_ctrl(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
 {
 {
 	struct ieee80211_local *local = rx->local;
 	struct ieee80211_local *local = rx->local;
 	struct ieee80211_hw *hw = &local->hw;
 	struct ieee80211_hw *hw = &local->hw;
@@ -1435,18 +1540,19 @@ ieee80211_rx_h_ctrl(struct ieee80211_txrx_data *rx)
 }
 }
 
 
 static ieee80211_rx_result
 static ieee80211_rx_result
-ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
 {
 {
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_sub_if_data *sdata;
 
 
-	if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 		return RX_DROP_MONITOR;
 		return RX_DROP_MONITOR;
 
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
 	sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
 	if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
 	if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
-	     sdata->vif.type == IEEE80211_IF_TYPE_IBSS) &&
+	     sdata->vif.type == IEEE80211_IF_TYPE_IBSS ||
+	     sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) &&
 	    !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
 	    !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
-		ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
+		ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->status);
 	else
 	else
 		return RX_DROP_MONITOR;
 		return RX_DROP_MONITOR;
 
 
@@ -1455,7 +1561,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
 
 
 static void ieee80211_rx_michael_mic_report(struct net_device *dev,
 static void ieee80211_rx_michael_mic_report(struct net_device *dev,
 					    struct ieee80211_hdr *hdr,
 					    struct ieee80211_hdr *hdr,
-					    struct ieee80211_txrx_data *rx)
+					    struct ieee80211_rx_data *rx)
 {
 {
 	int keyidx, hdrlen;
 	int keyidx, hdrlen;
 	DECLARE_MAC_BUF(mac);
 	DECLARE_MAC_BUF(mac);
@@ -1525,7 +1631,8 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
 	rx->skb = NULL;
 	rx->skb = NULL;
 }
 }
 
 
-static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx)
+/* TODO: use IEEE80211_RX_FRAGMENTED */
+static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx)
 {
 {
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_local *local = rx->local;
 	struct ieee80211_local *local = rx->local;
@@ -1538,9 +1645,9 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx)
 	} __attribute__ ((packed)) *rthdr;
 	} __attribute__ ((packed)) *rthdr;
 	struct sk_buff *skb = rx->skb, *skb2;
 	struct sk_buff *skb = rx->skb, *skb2;
 	struct net_device *prev_dev = NULL;
 	struct net_device *prev_dev = NULL;
-	struct ieee80211_rx_status *status = rx->u.rx.status;
+	struct ieee80211_rx_status *status = rx->status;
 
 
-	if (rx->flags & IEEE80211_TXRXD_RX_CMNTR_REPORTED)
+	if (rx->flags & IEEE80211_RX_CMNTR_REPORTED)
 		goto out_free_skb;
 		goto out_free_skb;
 
 
 	if (skb_headroom(skb) < sizeof(*rthdr) &&
 	if (skb_headroom(skb) < sizeof(*rthdr) &&
@@ -1555,7 +1662,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx)
 			    (1 << IEEE80211_RADIOTAP_RATE) |
 			    (1 << IEEE80211_RADIOTAP_RATE) |
 			    (1 << IEEE80211_RADIOTAP_CHANNEL));
 			    (1 << IEEE80211_RADIOTAP_CHANNEL));
 
 
-	rthdr->rate = rx->u.rx.rate->bitrate / 5;
+	rthdr->rate = rx->rate->bitrate / 5;
 	rthdr->chan_freq = cpu_to_le16(status->freq);
 	rthdr->chan_freq = cpu_to_le16(status->freq);
 
 
 	if (status->band == IEEE80211_BAND_5GHZ)
 	if (status->band == IEEE80211_BAND_5GHZ)
@@ -1598,14 +1705,14 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx)
 	} else
 	} else
 		goto out_free_skb;
 		goto out_free_skb;
 
 
-	rx->flags |= IEEE80211_TXRXD_RX_CMNTR_REPORTED;
+	rx->flags |= IEEE80211_RX_CMNTR_REPORTED;
 	return;
 	return;
 
 
  out_free_skb:
  out_free_skb:
 	dev_kfree_skb(skb);
 	dev_kfree_skb(skb);
 }
 }
 
 
-typedef ieee80211_rx_result (*ieee80211_rx_handler)(struct ieee80211_txrx_data *);
+typedef ieee80211_rx_result (*ieee80211_rx_handler)(struct ieee80211_rx_data *);
 static ieee80211_rx_handler ieee80211_rx_handlers[] =
 static ieee80211_rx_handler ieee80211_rx_handlers[] =
 {
 {
 	ieee80211_rx_h_if_stats,
 	ieee80211_rx_h_if_stats,
@@ -1629,7 +1736,7 @@ static ieee80211_rx_handler ieee80211_rx_handlers[] =
 };
 };
 
 
 static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
 static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
-					 struct ieee80211_txrx_data *rx,
+					 struct ieee80211_rx_data *rx,
 					 struct sk_buff *skb)
 					 struct sk_buff *skb)
 {
 {
 	ieee80211_rx_handler *handler;
 	ieee80211_rx_handler *handler;
@@ -1672,7 +1779,7 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
 /* main receive path */
 /* main receive path */
 
 
 static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
 static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
-				u8 *bssid, struct ieee80211_txrx_data *rx,
+				u8 *bssid, struct ieee80211_rx_data *rx,
 				struct ieee80211_hdr *hdr)
 				struct ieee80211_hdr *hdr)
 {
 {
 	int multicast = is_multicast_ether_addr(hdr->addr1);
 	int multicast = is_multicast_ether_addr(hdr->addr1);
@@ -1682,15 +1789,15 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
 		if (!bssid)
 		if (!bssid)
 			return 0;
 			return 0;
 		if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
 		if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
-			if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+			if (!(rx->flags & IEEE80211_RX_IN_SCAN))
 				return 0;
 				return 0;
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		} else if (!multicast &&
 		} else if (!multicast &&
 			   compare_ether_addr(sdata->dev->dev_addr,
 			   compare_ether_addr(sdata->dev->dev_addr,
 					      hdr->addr1) != 0) {
 					      hdr->addr1) != 0) {
 			if (!(sdata->dev->flags & IFF_PROMISC))
 			if (!(sdata->dev->flags & IFF_PROMISC))
 				return 0;
 				return 0;
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		}
 		}
 		break;
 		break;
 	case IEEE80211_IF_TYPE_IBSS:
 	case IEEE80211_IF_TYPE_IBSS:
@@ -1700,19 +1807,29 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
 		    (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)
 		    (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)
 			return 1;
 			return 1;
 		else if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
 		else if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
-			if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+			if (!(rx->flags & IEEE80211_RX_IN_SCAN))
 				return 0;
 				return 0;
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		} else if (!multicast &&
 		} else if (!multicast &&
 			   compare_ether_addr(sdata->dev->dev_addr,
 			   compare_ether_addr(sdata->dev->dev_addr,
 					      hdr->addr1) != 0) {
 					      hdr->addr1) != 0) {
 			if (!(sdata->dev->flags & IFF_PROMISC))
 			if (!(sdata->dev->flags & IFF_PROMISC))
 				return 0;
 				return 0;
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		} else if (!rx->sta)
 		} else if (!rx->sta)
 			rx->sta = ieee80211_ibss_add_sta(sdata->dev, rx->skb,
 			rx->sta = ieee80211_ibss_add_sta(sdata->dev, rx->skb,
 							 bssid, hdr->addr2);
 							 bssid, hdr->addr2);
 		break;
 		break;
+	case IEEE80211_IF_TYPE_MESH_POINT:
+		if (!multicast &&
+		    compare_ether_addr(sdata->dev->dev_addr,
+				       hdr->addr1) != 0) {
+			if (!(sdata->dev->flags & IFF_PROMISC))
+				return 0;
+
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
+		}
+		break;
 	case IEEE80211_IF_TYPE_VLAN:
 	case IEEE80211_IF_TYPE_VLAN:
 	case IEEE80211_IF_TYPE_AP:
 	case IEEE80211_IF_TYPE_AP:
 		if (!bssid) {
 		if (!bssid) {
@@ -1721,12 +1838,12 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
 				return 0;
 				return 0;
 		} else if (!ieee80211_bssid_match(bssid,
 		} else if (!ieee80211_bssid_match(bssid,
 					sdata->dev->dev_addr)) {
 					sdata->dev->dev_addr)) {
-			if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+			if (!(rx->flags & IEEE80211_RX_IN_SCAN))
 				return 0;
 				return 0;
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		}
 		}
 		if (sdata->dev == sdata->local->mdev &&
 		if (sdata->dev == sdata->local->mdev &&
-		    !(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+		    !(rx->flags & IEEE80211_RX_IN_SCAN))
 			/* do not receive anything via
 			/* do not receive anything via
 			 * master device when not scanning */
 			 * master device when not scanning */
 			return 0;
 			return 0;
@@ -1763,7 +1880,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_hdr *hdr;
 	struct ieee80211_hdr *hdr;
-	struct ieee80211_txrx_data rx;
+	struct ieee80211_rx_data rx;
 	u16 type;
 	u16 type;
 	int prepares;
 	int prepares;
 	struct ieee80211_sub_if_data *prev = NULL;
 	struct ieee80211_sub_if_data *prev = NULL;
@@ -1775,9 +1892,9 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 	rx.skb = skb;
 	rx.skb = skb;
 	rx.local = local;
 	rx.local = local;
 
 
-	rx.u.rx.status = status;
-	rx.u.rx.load = load;
-	rx.u.rx.rate = rate;
+	rx.status = status;
+	rx.load = load;
+	rx.rate = rate;
 	rx.fc = le16_to_cpu(hdr->frame_control);
 	rx.fc = le16_to_cpu(hdr->frame_control);
 	type = rx.fc & IEEE80211_FCTL_FTYPE;
 	type = rx.fc & IEEE80211_FCTL_FTYPE;
 
 
@@ -1786,17 +1903,17 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 
 
 	rx.sta = sta_info_get(local, hdr->addr2);
 	rx.sta = sta_info_get(local, hdr->addr2);
 	if (rx.sta) {
 	if (rx.sta) {
-		rx.dev = rx.sta->dev;
-		rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
+		rx.sdata = rx.sta->sdata;
+		rx.dev = rx.sta->sdata->dev;
 	}
 	}
 
 
 	if ((status->flag & RX_FLAG_MMIC_ERROR)) {
 	if ((status->flag & RX_FLAG_MMIC_ERROR)) {
 		ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx);
 		ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx);
-		goto end;
+		return;
 	}
 	}
 
 
 	if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
 	if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
-		rx.flags |= IEEE80211_TXRXD_RXIN_SCAN;
+		rx.flags |= IEEE80211_RX_IN_SCAN;
 
 
 	ieee80211_parse_qos(&rx);
 	ieee80211_parse_qos(&rx);
 	ieee80211_verify_ip_alignment(&rx);
 	ieee80211_verify_ip_alignment(&rx);
@@ -1811,7 +1928,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 			continue;
 			continue;
 
 
 		bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
 		bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
-		rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
+		rx.flags |= IEEE80211_RX_RA_MATCH;
 		prepares = prepare_for_handlers(sdata, bssid, &rx, hdr);
 		prepares = prepare_for_handlers(sdata, bssid, &rx, hdr);
 
 
 		if (!prepares)
 		if (!prepares)
@@ -1851,10 +1968,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 		ieee80211_invoke_rx_handlers(prev, &rx, skb);
 		ieee80211_invoke_rx_handlers(prev, &rx, skb);
 	} else
 	} else
 		dev_kfree_skb(skb);
 		dev_kfree_skb(skb);
-
- end:
-	if (rx.sta)
-		sta_info_put(rx.sta);
 }
 }
 
 
 #define SEQ_MODULO 0x1000
 #define SEQ_MODULO 0x1000
@@ -2031,7 +2144,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
 	/* if this mpdu is fragmented - terminate rx aggregation session */
 	/* if this mpdu is fragmented - terminate rx aggregation session */
 	sc = le16_to_cpu(hdr->seq_ctrl);
 	sc = le16_to_cpu(hdr->seq_ctrl);
 	if (sc & IEEE80211_SCTL_FRAG) {
 	if (sc & IEEE80211_SCTL_FRAG) {
-		ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr,
+		ieee80211_sta_stop_rx_ba_session(sta->sdata->dev, sta->addr,
 			tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
 			tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
 		ret = 1;
 		ret = 1;
 		goto end_reorder;
 		goto end_reorder;
@@ -2041,9 +2154,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
 	mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
 	mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
 	ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb,
 	ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb,
 						mpdu_seq_num, 0);
 						mpdu_seq_num, 0);
-end_reorder:
-	if (sta)
-		sta_info_put(sta);
+ end_reorder:
 	return ret;
 	return ret;
 }
 }
 
 

+ 320 - 144
net/mac80211/sta_info.c

@@ -15,21 +15,52 @@
 #include <linux/skbuff.h>
 #include <linux/skbuff.h>
 #include <linux/if_arp.h>
 #include <linux/if_arp.h>
 #include <linux/timer.h>
 #include <linux/timer.h>
+#include <linux/rtnetlink.h>
 
 
 #include <net/mac80211.h>
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
 #include "ieee80211_i.h"
 #include "ieee80211_rate.h"
 #include "ieee80211_rate.h"
 #include "sta_info.h"
 #include "sta_info.h"
 #include "debugfs_sta.h"
 #include "debugfs_sta.h"
+#include "mesh.h"
 
 
-/* Caller must hold local->sta_lock */
-static void sta_info_hash_add(struct ieee80211_local *local,
-			      struct sta_info *sta)
-{
-	sta->hnext = local->sta_hash[STA_HASH(sta->addr)];
-	local->sta_hash[STA_HASH(sta->addr)] = sta;
-}
-
+/**
+ * DOC: STA information lifetime rules
+ *
+ * STA info structures (&struct sta_info) are managed in a hash table
+ * for faster lookup and a list for iteration. They are managed using
+ * RCU, i.e. access to the list and hash table is protected by RCU.
+ *
+ * Upon allocating a STA info structure with sta_info_alloc(), the caller owns
+ * that structure. It must then either destroy it using sta_info_destroy()
+ * (which is pretty useless) or insert it into the hash table using
+ * sta_info_insert() which demotes the reference from ownership to a regular
+ * RCU-protected reference; if the function is called without protection by an
+ * RCU critical section the reference is instantly invalidated.
+ *
+ * Because there are debugfs entries for each station, and adding those
+ * must be able to sleep, it is also possible to "pin" a station entry,
+ * that means it can be removed from the hash table but not be freed.
+ * See the comment in __sta_info_unlink() for more information.
+ *
+ * In order to remove a STA info structure, the caller needs to first
+ * unlink it (sta_info_unlink()) from the list and hash tables and
+ * then wait for an RCU synchronisation before it can be freed. Due to
+ * the pinning and the possibility of multiple callers trying to remove
+ * the same STA info at the same time, sta_info_unlink() can clear the
+ * STA info pointer it is passed to indicate that the STA info is owned
+ * by somebody else now.
+ *
+ * If sta_info_unlink() did not clear the pointer then the caller owns
+ * the STA info structure now and is responsible of destroying it with
+ * a call to sta_info_destroy(), not before RCU synchronisation, of
+ * course. Note that sta_info_destroy() must be protected by the RTNL.
+ *
+ * In all other cases, there is no concept of ownership on a STA entry,
+ * each structure is owned by the global hash table/list until it is
+ * removed. All users of the structure need to be RCU protected so that
+ * the structure won't be freed before they are done using it.
+ */
 
 
 /* Caller must hold local->sta_lock */
 /* Caller must hold local->sta_lock */
 static int sta_info_hash_del(struct ieee80211_local *local,
 static int sta_info_hash_del(struct ieee80211_local *local,
@@ -41,109 +72,152 @@ static int sta_info_hash_del(struct ieee80211_local *local,
 	if (!s)
 	if (!s)
 		return -ENOENT;
 		return -ENOENT;
 	if (s == sta) {
 	if (s == sta) {
-		local->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+		rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)],
+				   s->hnext);
 		return 0;
 		return 0;
 	}
 	}
 
 
 	while (s->hnext && s->hnext != sta)
 	while (s->hnext && s->hnext != sta)
 		s = s->hnext;
 		s = s->hnext;
 	if (s->hnext) {
 	if (s->hnext) {
-		s->hnext = sta->hnext;
+		rcu_assign_pointer(s->hnext, sta->hnext);
 		return 0;
 		return 0;
 	}
 	}
 
 
 	return -ENOENT;
 	return -ENOENT;
 }
 }
 
 
-/* must hold local->sta_lock */
+/* protected by RCU */
 static struct sta_info *__sta_info_find(struct ieee80211_local *local,
 static struct sta_info *__sta_info_find(struct ieee80211_local *local,
 					u8 *addr)
 					u8 *addr)
 {
 {
 	struct sta_info *sta;
 	struct sta_info *sta;
 
 
-	sta = local->sta_hash[STA_HASH(addr)];
+	sta = rcu_dereference(local->sta_hash[STA_HASH(addr)]);
 	while (sta) {
 	while (sta) {
 		if (compare_ether_addr(sta->addr, addr) == 0)
 		if (compare_ether_addr(sta->addr, addr) == 0)
 			break;
 			break;
-		sta = sta->hnext;
+		sta = rcu_dereference(sta->hnext);
 	}
 	}
 	return sta;
 	return sta;
 }
 }
 
 
 struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
 struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
 {
 {
-	struct sta_info *sta;
-
-	read_lock_bh(&local->sta_lock);
-	sta = __sta_info_find(local, addr);
-	if (sta)
-		__sta_info_get(sta);
-	read_unlock_bh(&local->sta_lock);
-
-	return sta;
+	return __sta_info_find(local, addr);
 }
 }
 EXPORT_SYMBOL(sta_info_get);
 EXPORT_SYMBOL(sta_info_get);
 
 
+struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
+				     struct net_device *dev)
+{
+	struct sta_info *sta;
+	int i = 0;
+
+	list_for_each_entry_rcu(sta, &local->sta_list, list) {
+		if (dev && dev != sta->sdata->dev)
+			continue;
+		if (i < idx) {
+			++i;
+			continue;
+		}
+		return sta;
+	}
+
+	return NULL;
+}
 
 
-static void sta_info_release(struct kref *kref)
+void sta_info_destroy(struct sta_info *sta)
 {
 {
-	struct sta_info *sta = container_of(kref, struct sta_info, kref);
 	struct ieee80211_local *local = sta->local;
 	struct ieee80211_local *local = sta->local;
 	struct sk_buff *skb;
 	struct sk_buff *skb;
 	int i;
 	int i;
+	DECLARE_MAC_BUF(mbuf);
+
+	if (!sta)
+		return;
+
+	ASSERT_RTNL();
+	might_sleep();
+
+	rate_control_remove_sta_debugfs(sta);
+	ieee80211_sta_debugfs_remove(sta);
+
+#ifdef CONFIG_MAC80211_MESH
+	if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+		mesh_plink_deactivate(sta);
+#endif
+
+	/*
+	 * NOTE: This will call synchronize_rcu() internally to
+	 * make sure no key references can be in use. We rely on
+	 * that here for the mesh code!
+	 */
+	ieee80211_key_free(sta->key);
+	WARN_ON(sta->key);
+
+#ifdef CONFIG_MAC80211_MESH
+	if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+		del_timer_sync(&sta->plink_timer);
+#endif
 
 
-	/* free sta structure; it has already been removed from
-	 * hash table etc. external structures. Make sure that all
-	 * buffered frames are release (one might have been added
-	 * after sta_info_free() was called). */
 	while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
 	while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
 		local->total_ps_buffered--;
 		local->total_ps_buffered--;
 		dev_kfree_skb_any(skb);
 		dev_kfree_skb_any(skb);
 	}
 	}
-	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
+
+	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
 		dev_kfree_skb_any(skb);
 		dev_kfree_skb_any(skb);
-	}
+
 	for (i = 0; i <  STA_TID_NUM; i++) {
 	for (i = 0; i <  STA_TID_NUM; i++) {
 		del_timer_sync(&sta->ampdu_mlme.tid_rx[i].session_timer);
 		del_timer_sync(&sta->ampdu_mlme.tid_rx[i].session_timer);
 		del_timer_sync(&sta->ampdu_mlme.tid_tx[i].addba_resp_timer);
 		del_timer_sync(&sta->ampdu_mlme.tid_tx[i].addba_resp_timer);
 	}
 	}
 	rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
 	rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
 	rate_control_put(sta->rate_ctrl);
 	rate_control_put(sta->rate_ctrl);
+
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	printk(KERN_DEBUG "%s: Destroyed STA %s\n",
+	       wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->addr));
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+
 	kfree(sta);
 	kfree(sta);
 }
 }
 
 
 
 
-void sta_info_put(struct sta_info *sta)
+/* Caller must hold local->sta_lock */
+static void sta_info_hash_add(struct ieee80211_local *local,
+			      struct sta_info *sta)
 {
 {
-	kref_put(&sta->kref, sta_info_release);
+	sta->hnext = local->sta_hash[STA_HASH(sta->addr)];
+	rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)], sta);
 }
 }
-EXPORT_SYMBOL(sta_info_put);
 
 
-
-struct sta_info *sta_info_add(struct ieee80211_local *local,
-			      struct net_device *dev, u8 *addr, gfp_t gfp)
+struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
+				u8 *addr, gfp_t gfp)
 {
 {
+	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta;
 	struct sta_info *sta;
 	int i;
 	int i;
-	DECLARE_MAC_BUF(mac);
+	DECLARE_MAC_BUF(mbuf);
 
 
 	sta = kzalloc(sizeof(*sta), gfp);
 	sta = kzalloc(sizeof(*sta), gfp);
 	if (!sta)
 	if (!sta)
-		return ERR_PTR(-ENOMEM);
+		return NULL;
 
 
-	kref_init(&sta->kref);
+	memcpy(sta->addr, addr, ETH_ALEN);
+	sta->local = local;
+	sta->sdata = sdata;
 
 
 	sta->rate_ctrl = rate_control_get(local->rate_ctrl);
 	sta->rate_ctrl = rate_control_get(local->rate_ctrl);
-	sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp);
+	sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
+						     gfp);
 	if (!sta->rate_ctrl_priv) {
 	if (!sta->rate_ctrl_priv) {
 		rate_control_put(sta->rate_ctrl);
 		rate_control_put(sta->rate_ctrl);
 		kfree(sta);
 		kfree(sta);
-		return ERR_PTR(-ENOMEM);
+		return NULL;
 	}
 	}
 
 
-	memcpy(sta->addr, addr, ETH_ALEN);
-	sta->local = local;
-	sta->dev = dev;
 	spin_lock_init(&sta->ampdu_mlme.ampdu_rx);
 	spin_lock_init(&sta->ampdu_mlme.ampdu_rx);
 	spin_lock_init(&sta->ampdu_mlme.ampdu_tx);
 	spin_lock_init(&sta->ampdu_mlme.ampdu_tx);
 	for (i = 0; i < STA_TID_NUM; i++) {
 	for (i = 0; i < STA_TID_NUM; i++) {
@@ -168,35 +242,68 @@ struct sta_info *sta_info_add(struct ieee80211_local *local,
 	}
 	}
 	skb_queue_head_init(&sta->ps_tx_buf);
 	skb_queue_head_init(&sta->ps_tx_buf);
 	skb_queue_head_init(&sta->tx_filtered);
 	skb_queue_head_init(&sta->tx_filtered);
-	write_lock_bh(&local->sta_lock);
-	/* mark sta as used (by caller) */
-	__sta_info_get(sta);
+
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	printk(KERN_DEBUG "%s: Allocated STA %s\n",
+	       wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->addr));
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+
+#ifdef CONFIG_MAC80211_MESH
+	sta->plink_state = PLINK_LISTEN;
+	spin_lock_init(&sta->plink_lock);
+	init_timer(&sta->plink_timer);
+#endif
+
+	return sta;
+}
+
+int sta_info_insert(struct sta_info *sta)
+{
+	struct ieee80211_local *local = sta->local;
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+	unsigned long flags;
+	DECLARE_MAC_BUF(mac);
+
+	/*
+	 * Can't be a WARN_ON because it can be triggered through a race:
+	 * something inserts a STA (on one CPU) without holding the RTNL
+	 * and another CPU turns off the net device.
+	 */
+	if (unlikely(!netif_running(sdata->dev)))
+		return -ENETDOWN;
+
+	if (WARN_ON(compare_ether_addr(sta->addr, sdata->dev->dev_addr) == 0))
+		return -EINVAL;
+
+	if (WARN_ON(is_multicast_ether_addr(sta->addr)))
+		return -EINVAL;
+
+	spin_lock_irqsave(&local->sta_lock, flags);
 	/* check if STA exists already */
 	/* check if STA exists already */
-	if (__sta_info_find(local, addr)) {
-		write_unlock_bh(&local->sta_lock);
-		sta_info_put(sta);
-		return ERR_PTR(-EEXIST);
+	if (__sta_info_find(local, sta->addr)) {
+		spin_unlock_irqrestore(&local->sta_lock, flags);
+		return -EEXIST;
 	}
 	}
 	list_add(&sta->list, &local->sta_list);
 	list_add(&sta->list, &local->sta_list);
 	local->num_sta++;
 	local->num_sta++;
 	sta_info_hash_add(local, sta);
 	sta_info_hash_add(local, sta);
-	if (local->ops->sta_notify) {
-		struct ieee80211_sub_if_data *sdata;
 
 
-		sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	/* notify driver */
+	if (local->ops->sta_notify) {
 		if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
 		if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
 			sdata = sdata->u.vlan.ap;
 			sdata = sdata->u.vlan.ap;
 
 
 		local->ops->sta_notify(local_to_hw(local), &sdata->vif,
 		local->ops->sta_notify(local_to_hw(local), &sdata->vif,
-				       STA_NOTIFY_ADD, addr);
+				       STA_NOTIFY_ADD, sta->addr);
 	}
 	}
-	write_unlock_bh(&local->sta_lock);
 
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-	printk(KERN_DEBUG "%s: Added STA %s\n",
-	       wiphy_name(local->hw.wiphy), print_mac(mac, addr));
+	printk(KERN_DEBUG "%s: Inserted STA %s\n",
+	       wiphy_name(local->hw.wiphy), print_mac(mac, sta->addr));
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
 
+	spin_unlock_irqrestore(&local->sta_lock, flags);
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 #ifdef CONFIG_MAC80211_DEBUGFS
 	/* debugfs entry adding might sleep, so schedule process
 	/* debugfs entry adding might sleep, so schedule process
 	 * context task for adding entry for STAs that do not yet
 	 * context task for adding entry for STAs that do not yet
@@ -204,7 +311,10 @@ struct sta_info *sta_info_add(struct ieee80211_local *local,
 	queue_work(local->hw.workqueue, &local->sta_debugfs_add);
 	queue_work(local->hw.workqueue, &local->sta_debugfs_add);
 #endif
 #endif
 
 
-	return sta;
+	if (ieee80211_vif_is_mesh(&sdata->vif))
+		mesh_accept_plinks_update(sdata);
+
+	return 0;
 }
 }
 
 
 static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
 static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
@@ -230,19 +340,20 @@ static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss,
 {
 {
 	if (bss)
 	if (bss)
 		__bss_tim_set(bss, sta->aid);
 		__bss_tim_set(bss, sta->aid);
-	if (sta->local->ops->set_tim)
+	if (sta->local->ops->set_tim) {
+		sta->local->tim_in_locked_section = true;
 		sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 1);
 		sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 1);
+		sta->local->tim_in_locked_section = false;
+	}
 }
 }
 
 
 void sta_info_set_tim_bit(struct sta_info *sta)
 void sta_info_set_tim_bit(struct sta_info *sta)
 {
 {
-	struct ieee80211_sub_if_data *sdata;
-
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	unsigned long flags;
 
 
-	read_lock_bh(&sta->local->sta_lock);
-	__sta_info_set_tim_bit(sdata->bss, sta);
-	read_unlock_bh(&sta->local->sta_lock);
+	spin_lock_irqsave(&sta->local->sta_lock, flags);
+	__sta_info_set_tim_bit(sta->sdata->bss, sta);
+	spin_unlock_irqrestore(&sta->local->sta_lock, flags);
 }
 }
 
 
 static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss,
 static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss,
@@ -250,88 +361,135 @@ static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss,
 {
 {
 	if (bss)
 	if (bss)
 		__bss_tim_clear(bss, sta->aid);
 		__bss_tim_clear(bss, sta->aid);
-	if (sta->local->ops->set_tim)
+	if (sta->local->ops->set_tim) {
+		sta->local->tim_in_locked_section = true;
 		sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 0);
 		sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 0);
+		sta->local->tim_in_locked_section = false;
+	}
 }
 }
 
 
 void sta_info_clear_tim_bit(struct sta_info *sta)
 void sta_info_clear_tim_bit(struct sta_info *sta)
 {
 {
-	struct ieee80211_sub_if_data *sdata;
+	unsigned long flags;
 
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	spin_lock_irqsave(&sta->local->sta_lock, flags);
+	__sta_info_clear_tim_bit(sta->sdata->bss, sta);
+	spin_unlock_irqrestore(&sta->local->sta_lock, flags);
+}
 
 
-	read_lock_bh(&sta->local->sta_lock);
-	__sta_info_clear_tim_bit(sdata->bss, sta);
-	read_unlock_bh(&sta->local->sta_lock);
+/*
+ * See comment in __sta_info_unlink,
+ * caller must hold local->sta_lock.
+ */
+static void __sta_info_pin(struct sta_info *sta)
+{
+	WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_NORMAL);
+	sta->pin_status = STA_INFO_PIN_STAT_PINNED;
 }
 }
 
 
-/* Caller must hold local->sta_lock */
-void sta_info_remove(struct sta_info *sta)
+/*
+ * See comment in __sta_info_unlink, returns sta if it
+ * needs to be destroyed.
+ */
+static struct sta_info *__sta_info_unpin(struct sta_info *sta)
 {
 {
-	struct ieee80211_local *local = sta->local;
-	struct ieee80211_sub_if_data *sdata;
+	struct sta_info *ret = NULL;
+	unsigned long flags;
 
 
-	/* don't do anything if we've been removed already */
-	if (sta_info_hash_del(local, sta))
-		return;
+	spin_lock_irqsave(&sta->local->sta_lock, flags);
+	WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_DESTROY &&
+		sta->pin_status != STA_INFO_PIN_STAT_PINNED);
+	if (sta->pin_status == STA_INFO_PIN_STAT_DESTROY)
+		ret = sta;
+	sta->pin_status = STA_INFO_PIN_STAT_NORMAL;
+	spin_unlock_irqrestore(&sta->local->sta_lock, flags);
 
 
-	list_del(&sta->list);
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
-	if (sta->flags & WLAN_STA_PS) {
-		sta->flags &= ~WLAN_STA_PS;
-		if (sdata->bss)
-			atomic_dec(&sdata->bss->num_sta_ps);
-		__sta_info_clear_tim_bit(sdata->bss, sta);
-	}
-	local->num_sta--;
+	return ret;
 }
 }
 
 
-void sta_info_free(struct sta_info *sta)
+static void __sta_info_unlink(struct sta_info **sta)
 {
 {
-	struct sk_buff *skb;
-	struct ieee80211_local *local = sta->local;
-	DECLARE_MAC_BUF(mac);
+	struct ieee80211_local *local = (*sta)->local;
+	struct ieee80211_sub_if_data *sdata = (*sta)->sdata;
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	DECLARE_MAC_BUF(mbuf);
+#endif
+	/*
+	 * pull caller's reference if we're already gone.
+	 */
+	if (sta_info_hash_del(local, *sta)) {
+		*sta = NULL;
+		return;
+	}
 
 
-	might_sleep();
+	/*
+	 * Also pull caller's reference if the STA is pinned by the
+	 * task that is adding the debugfs entries. In that case, we
+	 * leave the STA "to be freed".
+	 *
+	 * The rules are not trivial, but not too complex either:
+	 *  (1) pin_status is only modified under the sta_lock
+	 *  (2) sta_info_debugfs_add_work() will set the status
+	 *	to PINNED when it found an item that needs a new
+	 *	debugfs directory created. In that case, that item
+	 *	must not be freed although all *RCU* users are done
+	 *	with it. Hence, we tell the caller of _unlink()
+	 *	that the item is already gone (as can happen when
+	 *	two tasks try to unlink/destroy at the same time)
+	 *  (3) We set the pin_status to DESTROY here when we
+	 *	find such an item.
+	 *  (4) sta_info_debugfs_add_work() will reset the pin_status
+	 *	from PINNED to NORMAL when it is done with the item,
+	 *	but will check for DESTROY before resetting it in
+	 *	which case it will free the item.
+	 */
+	if ((*sta)->pin_status == STA_INFO_PIN_STAT_PINNED) {
+		(*sta)->pin_status = STA_INFO_PIN_STAT_DESTROY;
+		*sta = NULL;
+		return;
+	}
 
 
-	write_lock_bh(&local->sta_lock);
-	sta_info_remove(sta);
-	write_unlock_bh(&local->sta_lock);
+	list_del(&(*sta)->list);
 
 
-	while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
-		local->total_ps_buffered--;
-		dev_kfree_skb(skb);
-	}
-	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
-		dev_kfree_skb(skb);
+	if ((*sta)->flags & WLAN_STA_PS) {
+		(*sta)->flags &= ~WLAN_STA_PS;
+		if (sdata->bss)
+			atomic_dec(&sdata->bss->num_sta_ps);
+		__sta_info_clear_tim_bit(sdata->bss, *sta);
 	}
 	}
 
 
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-	printk(KERN_DEBUG "%s: Removed STA %s\n",
-	       wiphy_name(local->hw.wiphy), print_mac(mac, sta->addr));
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-
-	ieee80211_key_free(sta->key);
-	WARN_ON(sta->key);
+	local->num_sta--;
 
 
 	if (local->ops->sta_notify) {
 	if (local->ops->sta_notify) {
-		struct ieee80211_sub_if_data *sdata;
-
-		sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
-
 		if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
 		if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
 			sdata = sdata->u.vlan.ap;
 			sdata = sdata->u.vlan.ap;
 
 
 		local->ops->sta_notify(local_to_hw(local), &sdata->vif,
 		local->ops->sta_notify(local_to_hw(local), &sdata->vif,
-				       STA_NOTIFY_REMOVE, sta->addr);
+				       STA_NOTIFY_REMOVE, (*sta)->addr);
 	}
 	}
 
 
-	rate_control_remove_sta_debugfs(sta);
-	ieee80211_sta_debugfs_remove(sta);
+	if (ieee80211_vif_is_mesh(&sdata->vif)) {
+		mesh_accept_plinks_update(sdata);
+#ifdef CONFIG_MAC80211_MESH
+		del_timer(&(*sta)->plink_timer);
+#endif
+	}
 
 
-	sta_info_put(sta);
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	printk(KERN_DEBUG "%s: Removed STA %s\n",
+	       wiphy_name(local->hw.wiphy), print_mac(mbuf, (*sta)->addr));
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 }
 }
 
 
+void sta_info_unlink(struct sta_info **sta)
+{
+	struct ieee80211_local *local = (*sta)->local;
+	unsigned long flags;
+
+	spin_lock_irqsave(&local->sta_lock, flags);
+	__sta_info_unlink(sta);
+	spin_unlock_irqrestore(&local->sta_lock, flags);
+}
 
 
 static inline int sta_info_buffer_expired(struct ieee80211_local *local,
 static inline int sta_info_buffer_expired(struct ieee80211_local *local,
 					  struct sta_info *sta,
 					  struct sta_info *sta,
@@ -377,7 +535,7 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
 		if (!skb)
 		if (!skb)
 			break;
 			break;
 
 
-		sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+		sdata = sta->sdata;
 		local->total_ps_buffered--;
 		local->total_ps_buffered--;
 		printk(KERN_DEBUG "Buffered frame expired (STA "
 		printk(KERN_DEBUG "Buffered frame expired (STA "
 		       "%s)\n", print_mac(mac, sta->addr));
 		       "%s)\n", print_mac(mac, sta->addr));
@@ -394,13 +552,10 @@ static void sta_info_cleanup(unsigned long data)
 	struct ieee80211_local *local = (struct ieee80211_local *) data;
 	struct ieee80211_local *local = (struct ieee80211_local *) data;
 	struct sta_info *sta;
 	struct sta_info *sta;
 
 
-	read_lock_bh(&local->sta_lock);
-	list_for_each_entry(sta, &local->sta_list, list) {
-		__sta_info_get(sta);
+	rcu_read_lock();
+	list_for_each_entry_rcu(sta, &local->sta_list, list)
 		sta_info_cleanup_expire_buffered(local, sta);
 		sta_info_cleanup_expire_buffered(local, sta);
-		sta_info_put(sta);
-	}
-	read_unlock_bh(&local->sta_lock);
+	rcu_read_unlock();
 
 
 	local->sta_cleanup.expires =
 	local->sta_cleanup.expires =
 		round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
 		round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
@@ -408,37 +563,45 @@ static void sta_info_cleanup(unsigned long data)
 }
 }
 
 
 #ifdef CONFIG_MAC80211_DEBUGFS
 #ifdef CONFIG_MAC80211_DEBUGFS
-static void sta_info_debugfs_add_task(struct work_struct *work)
+static void sta_info_debugfs_add_work(struct work_struct *work)
 {
 {
 	struct ieee80211_local *local =
 	struct ieee80211_local *local =
 		container_of(work, struct ieee80211_local, sta_debugfs_add);
 		container_of(work, struct ieee80211_local, sta_debugfs_add);
 	struct sta_info *sta, *tmp;
 	struct sta_info *sta, *tmp;
+	unsigned long flags;
 
 
 	while (1) {
 	while (1) {
 		sta = NULL;
 		sta = NULL;
-		read_lock_bh(&local->sta_lock);
+
+		spin_lock_irqsave(&local->sta_lock, flags);
 		list_for_each_entry(tmp, &local->sta_list, list) {
 		list_for_each_entry(tmp, &local->sta_list, list) {
 			if (!tmp->debugfs.dir) {
 			if (!tmp->debugfs.dir) {
 				sta = tmp;
 				sta = tmp;
-				__sta_info_get(sta);
+				__sta_info_pin(sta);
 				break;
 				break;
 			}
 			}
 		}
 		}
-		read_unlock_bh(&local->sta_lock);
+		spin_unlock_irqrestore(&local->sta_lock, flags);
 
 
 		if (!sta)
 		if (!sta)
 			break;
 			break;
 
 
 		ieee80211_sta_debugfs_add(sta);
 		ieee80211_sta_debugfs_add(sta);
 		rate_control_add_sta_debugfs(sta);
 		rate_control_add_sta_debugfs(sta);
-		sta_info_put(sta);
+
+		sta = __sta_info_unpin(sta);
+
+		if (sta) {
+			synchronize_rcu();
+			sta_info_destroy(sta);
+		}
 	}
 	}
 }
 }
 #endif
 #endif
 
 
 void sta_info_init(struct ieee80211_local *local)
 void sta_info_init(struct ieee80211_local *local)
 {
 {
-	rwlock_init(&local->sta_lock);
+	spin_lock_init(&local->sta_lock);
 	INIT_LIST_HEAD(&local->sta_list);
 	INIT_LIST_HEAD(&local->sta_list);
 
 
 	setup_timer(&local->sta_cleanup, sta_info_cleanup,
 	setup_timer(&local->sta_cleanup, sta_info_cleanup,
@@ -447,7 +610,7 @@ void sta_info_init(struct ieee80211_local *local)
 		round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
 		round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
 
 
 #ifdef CONFIG_MAC80211_DEBUGFS
 #ifdef CONFIG_MAC80211_DEBUGFS
-	INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_task);
+	INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_work);
 #endif
 #endif
 }
 }
 
 
@@ -465,25 +628,38 @@ void sta_info_stop(struct ieee80211_local *local)
 
 
 /**
 /**
  * sta_info_flush - flush matching STA entries from the STA table
  * sta_info_flush - flush matching STA entries from the STA table
+ *
+ * Returns the number of removed STA entries.
+ *
  * @local: local interface data
  * @local: local interface data
- * @dev: matching rule for the net device (sta->dev) or %NULL to match all STAs
+ * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
  */
  */
-void sta_info_flush(struct ieee80211_local *local, struct net_device *dev)
+int sta_info_flush(struct ieee80211_local *local,
+		    struct ieee80211_sub_if_data *sdata)
 {
 {
 	struct sta_info *sta, *tmp;
 	struct sta_info *sta, *tmp;
 	LIST_HEAD(tmp_list);
 	LIST_HEAD(tmp_list);
+	int ret = 0;
+	unsigned long flags;
 
 
-	write_lock_bh(&local->sta_lock);
-	list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
-		if (!dev || dev == sta->dev) {
-			__sta_info_get(sta);
-			sta_info_remove(sta);
-			list_add_tail(&sta->list, &tmp_list);
-		}
-	write_unlock_bh(&local->sta_lock);
+	might_sleep();
 
 
-	list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
-		sta_info_free(sta);
-		sta_info_put(sta);
+	spin_lock_irqsave(&local->sta_lock, flags);
+	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
+		if (!sdata || sdata == sta->sdata) {
+			__sta_info_unlink(&sta);
+			if (sta) {
+				list_add_tail(&sta->list, &tmp_list);
+				ret++;
+			}
+		}
 	}
 	}
+	spin_unlock_irqrestore(&local->sta_lock, flags);
+
+	synchronize_rcu();
+
+	list_for_each_entry_safe(sta, tmp, &tmp_list, list)
+		sta_info_destroy(sta);
+
+	return ret;
 }
 }

+ 170 - 62
net/mac80211/sta_info.h

@@ -12,7 +12,6 @@
 #include <linux/list.h>
 #include <linux/list.h>
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/if_ether.h>
 #include <linux/if_ether.h>
-#include <linux/kref.h>
 #include "ieee80211_key.h"
 #include "ieee80211_key.h"
 
 
 /**
 /**
@@ -107,6 +106,29 @@ struct tid_ampdu_rx {
 	struct timer_list session_timer;
 	struct timer_list session_timer;
 };
 };
 
 
+/**
+ * enum plink_state - state of a mesh peer link finite state machine
+ *
+ * @PLINK_LISTEN: initial state, considered the implicit state of non existant
+ * 	mesh peer links
+ * @PLINK_OPN_SNT: mesh plink open frame has been sent to this mesh peer
+ * @PLINK_OPN_RCVD: mesh plink open frame has been received from this mesh peer
+ * @PLINK_CNF_RCVD: mesh plink confirm frame has been received from this mesh
+ * 	peer
+ * @PLINK_ESTAB: mesh peer link is established
+ * @PLINK_HOLDING: mesh peer link is being closed or cancelled
+ * @PLINK_BLOCKED: all frames transmitted from this mesh plink are discarded
+ */
+enum plink_state {
+	PLINK_LISTEN,
+	PLINK_OPN_SNT,
+	PLINK_OPN_RCVD,
+	PLINK_CNF_RCVD,
+	PLINK_ESTAB,
+	PLINK_HOLDING,
+	PLINK_BLOCKED
+};
+
 /**
 /**
  * struct sta_ampdu_mlme - STA aggregation information.
  * struct sta_ampdu_mlme - STA aggregation information.
  *
  *
@@ -124,75 +146,132 @@ struct sta_ampdu_mlme {
 	u8 dialog_token_allocator;
 	u8 dialog_token_allocator;
 };
 };
 
 
+
+/* see __sta_info_unlink */
+#define STA_INFO_PIN_STAT_NORMAL	0
+#define STA_INFO_PIN_STAT_PINNED	1
+#define STA_INFO_PIN_STAT_DESTROY	2
+
+/**
+ * struct sta_info - STA information
+ *
+ * This structure collects information about a station that
+ * mac80211 is communicating with.
+ *
+ * @list: global linked list entry
+ * @hnext: hash table linked list pointer
+ * @local: pointer to the global information
+ * @addr: MAC address of this STA
+ * @aid: STA's unique AID (1..2007, 0 = not assigned yet),
+ *	only used in AP (and IBSS?) mode
+ * @flags: STA flags, see &enum ieee80211_sta_info_flags
+ * @ps_tx_buf: buffer of frames to transmit to this station
+ *	when it leaves power saving state
+ * @tx_filtered: buffer of frames we already tried to transmit
+ *	but were filtered by hardware due to STA having entered
+ *	power saving state
+ * @rx_packets: Number of MSDUs received from this STA
+ * @rx_bytes: Number of bytes received from this STA
+ * @supp_rates: Bitmap of supported rates (per band)
+ * @ht_info: HT capabilities of this STA
+ */
 struct sta_info {
 struct sta_info {
-	struct kref kref;
+	/* General information, mostly static */
 	struct list_head list;
 	struct list_head list;
-	struct sta_info *hnext; /* next entry in hash table list */
-
+	struct sta_info *hnext;
 	struct ieee80211_local *local;
 	struct ieee80211_local *local;
-
-	u8 addr[ETH_ALEN];
-	u16 aid; /* STA's unique AID (1..2007), 0 = not yet assigned */
-	u32 flags; /* WLAN_STA_ */
-
-	struct sk_buff_head ps_tx_buf; /* buffer of TX frames for station in
-					* power saving state */
-	struct sk_buff_head tx_filtered; /* buffer of TX frames that were
-					  * already given to low-level driver,
-					  * but were filtered */
-	unsigned long rx_packets, tx_packets; /* number of RX/TX MSDUs */
-	unsigned long rx_bytes, tx_bytes;
-	unsigned long tx_retry_failed, tx_retry_count;
-	unsigned long tx_filtered_count;
-
-	unsigned int wep_weak_iv_count; /* number of RX frames with weak IV */
-
-	unsigned long last_rx;
-	/* bitmap of supported rates per band */
-	u64 supp_rates[IEEE80211_NUM_BANDS];
-	int txrate_idx;
-	/* last rates used to send a frame to this STA */
-	int last_txrate_idx, last_nonerp_txrate_idx;
-
-	struct net_device *dev; /* which net device is this station associated
-				 * to */
-
+	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_key *key;
 	struct ieee80211_key *key;
-
-	u32 tx_num_consecutive_failures;
-	u32 tx_num_mpdu_ok;
-	u32 tx_num_mpdu_fail;
-
 	struct rate_control_ref *rate_ctrl;
 	struct rate_control_ref *rate_ctrl;
 	void *rate_ctrl_priv;
 	void *rate_ctrl_priv;
+	struct ieee80211_ht_info ht_info;
+	u64 supp_rates[IEEE80211_NUM_BANDS];
+	u8 addr[ETH_ALEN];
+	u16 aid;
+	u16 listen_interval;
 
 
-	/* last received seq/frag number from this STA (per RX queue) */
-	__le16 last_seq_ctrl[NUM_RX_DATA_QUEUES];
+	/*
+	 * for use by the internal lifetime management,
+	 * see __sta_info_unlink
+	 */
+	u8 pin_status;
+
+	/* frequently updated information, needs locking? */
+	u32 flags;
+
+	/*
+	 * STA powersave frame queues, no more than the internal
+	 * locking required.
+	 */
+	struct sk_buff_head ps_tx_buf;
+	struct sk_buff_head tx_filtered;
+
+	/* Updated from RX path only, no locking requirements */
+	unsigned long rx_packets, rx_bytes;
+	unsigned long wep_weak_iv_count;
+	unsigned long last_rx;
 	unsigned long num_duplicates; /* number of duplicate frames received
 	unsigned long num_duplicates; /* number of duplicate frames received
 				       * from this STA */
 				       * from this STA */
-	unsigned long tx_fragments; /* number of transmitted MPDUs */
 	unsigned long rx_fragments; /* number of received MPDUs */
 	unsigned long rx_fragments; /* number of received MPDUs */
 	unsigned long rx_dropped; /* number of dropped MPDUs from this STA */
 	unsigned long rx_dropped; /* number of dropped MPDUs from this STA */
-
 	int last_rssi; /* RSSI of last received frame from this STA */
 	int last_rssi; /* RSSI of last received frame from this STA */
 	int last_signal; /* signal of last received frame from this STA */
 	int last_signal; /* signal of last received frame from this STA */
 	int last_noise; /* noise of last received frame from this STA */
 	int last_noise; /* noise of last received frame from this STA */
-	int channel_use;
-	int channel_use_raw;
-
+	/* last received seq/frag number from this STA (per RX queue) */
+	__le16 last_seq_ctrl[NUM_RX_DATA_QUEUES];
 #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
 #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
 	unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES];
 	unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES];
+#endif
+
+	/* Updated from TX status path only, no locking requirements */
+	unsigned long tx_filtered_count;
+	unsigned long tx_retry_failed, tx_retry_count;
+	/* TODO: update in generic code not rate control? */
+	u32 tx_num_consecutive_failures;
+	u32 tx_num_mpdu_ok;
+	u32 tx_num_mpdu_fail;
+	/* moving percentage of failed MSDUs */
+	unsigned int fail_avg;
+
+	/* Updated from TX path only, no locking requirements */
+	unsigned long tx_packets; /* number of RX/TX MSDUs */
+	unsigned long tx_bytes;
+	unsigned long tx_fragments; /* number of transmitted MPDUs */
+	int txrate_idx;
+	int last_txrate_idx;
+#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
 	unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES];
 	unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES];
-#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */
+#endif
 
 
-	u16 listen_interval;
+	/* Debug counters, no locking doesn't matter */
+	int channel_use;
+	int channel_use_raw;
 
 
-	struct ieee80211_ht_info ht_info; /* 802.11n HT capabilities
-					     of this STA */
+	/*
+	 * Aggregation information, comes with own locking.
+	 */
 	struct sta_ampdu_mlme ampdu_mlme;
 	struct sta_ampdu_mlme ampdu_mlme;
-	u8 timer_to_tid[STA_TID_NUM];	/* convert timer id to tid */
+	u8 timer_to_tid[STA_TID_NUM];	/* identity mapping to ID timers */
 	u8 tid_to_tx_q[STA_TID_NUM];	/* map tid to tx queue */
 	u8 tid_to_tx_q[STA_TID_NUM];	/* map tid to tx queue */
 
 
+#ifdef CONFIG_MAC80211_MESH
+	/*
+	 * Mesh peer link attributes
+	 * TODO: move to a sub-structure that is referenced with pointer?
+	 */
+	__le16 llid;		/* Local link ID */
+	__le16 plid;		/* Peer link ID */
+	__le16 reason;		/* Cancel reason on PLINK_HOLDING state */
+	u8 plink_retries;	/* Retries in establishment */
+	bool ignore_plink_timer;
+	enum plink_state plink_state;
+	u32 plink_timeout;
+	struct timer_list plink_timer;
+	spinlock_t plink_lock;	/* For peer_state reads / updates and other
+				   updates in the structure. Ensures robust
+				   transitions for the peerlink FSM */
+#endif
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 #ifdef CONFIG_MAC80211_DEBUGFS
 	struct sta_info_debugfsdentries {
 	struct sta_info_debugfsdentries {
 		struct dentry *dir;
 		struct dentry *dir;
@@ -209,6 +288,14 @@ struct sta_info {
 #endif
 #endif
 };
 };
 
 
+static inline enum plink_state sta_plink_state(struct sta_info *sta)
+{
+#ifdef CONFIG_MAC80211_MESH
+	return sta->plink_state;
+#endif
+	return PLINK_LISTEN;
+}
+
 
 
 /* Maximum number of concurrently registered stations */
 /* Maximum number of concurrently registered stations */
 #define MAX_STA_COUNT 2007
 #define MAX_STA_COUNT 2007
@@ -228,23 +315,44 @@ struct sta_info {
  */
  */
 #define STA_INFO_CLEANUP_INTERVAL (10 * HZ)
 #define STA_INFO_CLEANUP_INTERVAL (10 * HZ)
 
 
-static inline void __sta_info_get(struct sta_info *sta)
-{
-	kref_get(&sta->kref);
-}
+/*
+ * Get a STA info, must have be under RCU read lock.
+ */
+struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr);
+/*
+ * Get STA info by index, BROKEN!
+ */
+struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
+				      struct net_device *dev);
+/*
+ * Create a new STA info, caller owns returned structure
+ * until sta_info_insert().
+ */
+struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
+				u8 *addr, gfp_t gfp);
+/*
+ * Insert STA info into hash table/list, returns zero or a
+ * -EEXIST if (if the same MAC address is already present).
+ *
+ * Calling this without RCU protection makes the caller
+ * relinquish its reference to @sta.
+ */
+int sta_info_insert(struct sta_info *sta);
+/*
+ * Unlink a STA info from the hash table/list.
+ * This can NULL the STA pointer if somebody else
+ * has already unlinked it.
+ */
+void sta_info_unlink(struct sta_info **sta);
+
+void sta_info_destroy(struct sta_info *sta);
+void sta_info_set_tim_bit(struct sta_info *sta);
+void sta_info_clear_tim_bit(struct sta_info *sta);
 
 
-struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr);
-void sta_info_put(struct sta_info *sta);
-struct sta_info *sta_info_add(struct ieee80211_local *local,
-			      struct net_device *dev, u8 *addr, gfp_t gfp);
-void sta_info_remove(struct sta_info *sta);
-void sta_info_free(struct sta_info *sta);
 void sta_info_init(struct ieee80211_local *local);
 void sta_info_init(struct ieee80211_local *local);
 int sta_info_start(struct ieee80211_local *local);
 int sta_info_start(struct ieee80211_local *local);
 void sta_info_stop(struct ieee80211_local *local);
 void sta_info_stop(struct ieee80211_local *local);
-void sta_info_flush(struct ieee80211_local *local, struct net_device *dev);
-
-void sta_info_set_tim_bit(struct sta_info *sta);
-void sta_info_clear_tim_bit(struct sta_info *sta);
+int sta_info_flush(struct ieee80211_local *local,
+		    struct ieee80211_sub_if_data *sdata);
 
 
 #endif /* STA_INFO_H */
 #endif /* STA_INFO_H */

+ 260 - 172
net/mac80211/tx.c

@@ -26,6 +26,7 @@
 
 
 #include "ieee80211_i.h"
 #include "ieee80211_i.h"
 #include "ieee80211_led.h"
 #include "ieee80211_led.h"
+#include "mesh.h"
 #include "wep.h"
 #include "wep.h"
 #include "wpa.h"
 #include "wpa.h"
 #include "wme.h"
 #include "wme.h"
@@ -86,11 +87,11 @@ static inline void ieee80211_dump_frame(const char *ifname, const char *title,
 }
 }
 #endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
 #endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
 
 
-static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr,
+static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
 			      int next_frag_len)
 			      int next_frag_len)
 {
 {
 	int rate, mrate, erp, dur, i;
 	int rate, mrate, erp, dur, i;
-	struct ieee80211_rate *txrate = tx->u.tx.rate;
+	struct ieee80211_rate *txrate = tx->rate;
 	struct ieee80211_local *local = tx->local;
 	struct ieee80211_local *local = tx->local;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_supported_band *sband;
 
 
@@ -233,7 +234,7 @@ static int inline is_ieee80211_device(struct net_device *dev,
 /* tx handlers */
 /* tx handlers */
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
 {
 {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 	struct sk_buff *skb = tx->skb;
 	struct sk_buff *skb = tx->skb;
@@ -241,7 +242,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 	u32 sta_flags;
 	u32 sta_flags;
 
 
-	if (unlikely(tx->flags & IEEE80211_TXRXD_TX_INJECTED))
+	if (unlikely(tx->flags & IEEE80211_TX_INJECTED))
 		return TX_CONTINUE;
 		return TX_CONTINUE;
 
 
 	if (unlikely(tx->local->sta_sw_scanning) &&
 	if (unlikely(tx->local->sta_sw_scanning) &&
@@ -249,12 +250,15 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
 	     (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
 	     (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
 		return TX_DROP;
 		return TX_DROP;
 
 
-	if (tx->flags & IEEE80211_TXRXD_TXPS_BUFFERED)
+	if (tx->sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT)
+		return TX_CONTINUE;
+
+	if (tx->flags & IEEE80211_TX_PS_BUFFERED)
 		return TX_CONTINUE;
 		return TX_CONTINUE;
 
 
 	sta_flags = tx->sta ? tx->sta->flags : 0;
 	sta_flags = tx->sta ? tx->sta->flags : 0;
 
 
-	if (likely(tx->flags & IEEE80211_TXRXD_TXUNICAST)) {
+	if (likely(tx->flags & IEEE80211_TX_UNICAST)) {
 		if (unlikely(!(sta_flags & WLAN_STA_ASSOC) &&
 		if (unlikely(!(sta_flags & WLAN_STA_ASSOC) &&
 			     tx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
 			     tx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
 			     (tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
 			     (tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
@@ -284,7 +288,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
 }
 }
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_sequence(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
 {
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
 
 
@@ -323,10 +327,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
 		}
 		}
 		total += skb_queue_len(&ap->ps_bc_buf);
 		total += skb_queue_len(&ap->ps_bc_buf);
 	}
 	}
-	rcu_read_unlock();
 
 
-	read_lock_bh(&local->sta_lock);
-	list_for_each_entry(sta, &local->sta_list, list) {
+	list_for_each_entry_rcu(sta, &local->sta_list, list) {
 		skb = skb_dequeue(&sta->ps_tx_buf);
 		skb = skb_dequeue(&sta->ps_tx_buf);
 		if (skb) {
 		if (skb) {
 			purged++;
 			purged++;
@@ -334,7 +336,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
 		}
 		}
 		total += skb_queue_len(&sta->ps_tx_buf);
 		total += skb_queue_len(&sta->ps_tx_buf);
 	}
 	}
-	read_unlock_bh(&local->sta_lock);
+
+	rcu_read_unlock();
 
 
 	local->total_ps_buffered = total;
 	local->total_ps_buffered = total;
 	printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
 	printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
@@ -342,7 +345,7 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
 }
 }
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
 {
 {
 	/*
 	/*
 	 * broadcast/multicast frame
 	 * broadcast/multicast frame
@@ -379,13 +382,13 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
 	}
 	}
 
 
 	/* buffered in hardware */
 	/* buffered in hardware */
-	tx->u.tx.control->flags |= IEEE80211_TXCTL_SEND_AFTER_DTIM;
+	tx->control->flags |= IEEE80211_TXCTL_SEND_AFTER_DTIM;
 
 
 	return TX_CONTINUE;
 	return TX_CONTINUE;
 }
 }
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
 {
 {
 	struct sta_info *sta = tx->sta;
 	struct sta_info *sta = tx->sta;
 	DECLARE_MAC_BUF(mac);
 	DECLARE_MAC_BUF(mac);
@@ -439,32 +442,32 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx)
 }
 }
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
 {
 {
-	if (unlikely(tx->flags & IEEE80211_TXRXD_TXPS_BUFFERED))
+	if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
 		return TX_CONTINUE;
 		return TX_CONTINUE;
 
 
-	if (tx->flags & IEEE80211_TXRXD_TXUNICAST)
+	if (tx->flags & IEEE80211_TX_UNICAST)
 		return ieee80211_tx_h_unicast_ps_buf(tx);
 		return ieee80211_tx_h_unicast_ps_buf(tx);
 	else
 	else
 		return ieee80211_tx_h_multicast_ps_buf(tx);
 		return ieee80211_tx_h_multicast_ps_buf(tx);
 }
 }
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
 {
 {
 	struct ieee80211_key *key;
 	struct ieee80211_key *key;
 	u16 fc = tx->fc;
 	u16 fc = tx->fc;
 
 
-	if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
+	if (unlikely(tx->control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
 		tx->key = NULL;
 		tx->key = NULL;
 	else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
 	else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
 		tx->key = key;
 		tx->key = key;
 	else if ((key = rcu_dereference(tx->sdata->default_key)))
 	else if ((key = rcu_dereference(tx->sdata->default_key)))
 		tx->key = key;
 		tx->key = key;
 	else if (tx->sdata->drop_unencrypted &&
 	else if (tx->sdata->drop_unencrypted &&
-		 !(tx->u.tx.control->flags & IEEE80211_TXCTL_EAPOL_FRAME) &&
-		 !(tx->flags & IEEE80211_TXRXD_TX_INJECTED)) {
+		 !(tx->control->flags & IEEE80211_TXCTL_EAPOL_FRAME) &&
+		 !(tx->flags & IEEE80211_TX_INJECTED)) {
 		I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
 		I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
 		return TX_DROP;
 		return TX_DROP;
 	} else
 	} else
@@ -493,13 +496,13 @@ ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx)
 	}
 	}
 
 
 	if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
 	if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-		tx->u.tx.control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
+		tx->control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
 
 
 	return TX_CONTINUE;
 	return TX_CONTINUE;
 }
 }
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
 {
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
 	size_t hdrlen, per_fragm, num_fragm, payload_len, left;
 	size_t hdrlen, per_fragm, num_fragm, payload_len, left;
@@ -509,7 +512,7 @@ ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
 	u8 *pos;
 	u8 *pos;
 	int frag_threshold = tx->local->fragmentation_threshold;
 	int frag_threshold = tx->local->fragmentation_threshold;
 
 
-	if (!(tx->flags & IEEE80211_TXRXD_FRAGMENTED))
+	if (!(tx->flags & IEEE80211_TX_FRAGMENTED))
 		return TX_CONTINUE;
 		return TX_CONTINUE;
 
 
 	first = tx->skb;
 	first = tx->skb;
@@ -561,8 +564,8 @@ ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
 	}
 	}
 	skb_trim(first, hdrlen + per_fragm);
 	skb_trim(first, hdrlen + per_fragm);
 
 
-	tx->u.tx.num_extra_frag = num_fragm - 1;
-	tx->u.tx.extra_frag = frags;
+	tx->num_extra_frag = num_fragm - 1;
+	tx->extra_frag = frags;
 
 
 	return TX_CONTINUE;
 	return TX_CONTINUE;
 
 
@@ -579,7 +582,7 @@ ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
 }
 }
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_encrypt(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
 {
 {
 	if (!tx->key)
 	if (!tx->key)
 		return TX_CONTINUE;
 		return TX_CONTINUE;
@@ -599,56 +602,56 @@ ieee80211_tx_h_encrypt(struct ieee80211_txrx_data *tx)
 }
 }
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
 {
 {
 	struct rate_selection rsel;
 	struct rate_selection rsel;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_supported_band *sband;
 
 
 	sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
 	sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
 
 
-	if (likely(!tx->u.tx.rate)) {
+	if (likely(!tx->rate)) {
 		rate_control_get_rate(tx->dev, sband, tx->skb, &rsel);
 		rate_control_get_rate(tx->dev, sband, tx->skb, &rsel);
-		tx->u.tx.rate = rsel.rate;
+		tx->rate = rsel.rate;
 		if (unlikely(rsel.probe)) {
 		if (unlikely(rsel.probe)) {
-			tx->u.tx.control->flags |=
+			tx->control->flags |=
 				IEEE80211_TXCTL_RATE_CTRL_PROBE;
 				IEEE80211_TXCTL_RATE_CTRL_PROBE;
-			tx->flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
-			tx->u.tx.control->alt_retry_rate = tx->u.tx.rate;
-			tx->u.tx.rate = rsel.probe;
+			tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
+			tx->control->alt_retry_rate = tx->rate;
+			tx->rate = rsel.probe;
 		} else
 		} else
-			tx->u.tx.control->alt_retry_rate = NULL;
+			tx->control->alt_retry_rate = NULL;
 
 
-		if (!tx->u.tx.rate)
+		if (!tx->rate)
 			return TX_DROP;
 			return TX_DROP;
 	} else
 	} else
-		tx->u.tx.control->alt_retry_rate = NULL;
+		tx->control->alt_retry_rate = NULL;
 
 
 	if (tx->sdata->bss_conf.use_cts_prot &&
 	if (tx->sdata->bss_conf.use_cts_prot &&
-	    (tx->flags & IEEE80211_TXRXD_FRAGMENTED) && rsel.nonerp) {
-		tx->u.tx.last_frag_rate = tx->u.tx.rate;
+	    (tx->flags & IEEE80211_TX_FRAGMENTED) && rsel.nonerp) {
+		tx->last_frag_rate = tx->rate;
 		if (rsel.probe)
 		if (rsel.probe)
-			tx->flags &= ~IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
+			tx->flags &= ~IEEE80211_TX_PROBE_LAST_FRAG;
 		else
 		else
-			tx->flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
-		tx->u.tx.rate = rsel.nonerp;
-		tx->u.tx.control->tx_rate = rsel.nonerp;
-		tx->u.tx.control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
+			tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
+		tx->rate = rsel.nonerp;
+		tx->control->tx_rate = rsel.nonerp;
+		tx->control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
 	} else {
 	} else {
-		tx->u.tx.last_frag_rate = tx->u.tx.rate;
-		tx->u.tx.control->tx_rate = tx->u.tx.rate;
+		tx->last_frag_rate = tx->rate;
+		tx->control->tx_rate = tx->rate;
 	}
 	}
-	tx->u.tx.control->tx_rate = tx->u.tx.rate;
+	tx->control->tx_rate = tx->rate;
 
 
 	return TX_CONTINUE;
 	return TX_CONTINUE;
 }
 }
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
 {
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
 	u16 fc = le16_to_cpu(hdr->frame_control);
 	u16 fc = le16_to_cpu(hdr->frame_control);
 	u16 dur;
 	u16 dur;
-	struct ieee80211_tx_control *control = tx->u.tx.control;
+	struct ieee80211_tx_control *control = tx->control;
 
 
 	if (!control->retry_limit) {
 	if (!control->retry_limit) {
 		if (!is_multicast_ether_addr(hdr->addr1)) {
 		if (!is_multicast_ether_addr(hdr->addr1)) {
@@ -670,7 +673,7 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
 		}
 		}
 	}
 	}
 
 
-	if (tx->flags & IEEE80211_TXRXD_FRAGMENTED) {
+	if (tx->flags & IEEE80211_TX_FRAGMENTED) {
 		/* Do not use multiple retry rates when sending fragmented
 		/* Do not use multiple retry rates when sending fragmented
 		 * frames.
 		 * frames.
 		 * TODO: The last fragment could still use multiple retry
 		 * TODO: The last fragment could still use multiple retry
@@ -682,8 +685,8 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
 	 * there are associated non-ERP stations and RTS/CTS is not configured
 	 * there are associated non-ERP stations and RTS/CTS is not configured
 	 * for the frame. */
 	 * for the frame. */
 	if ((tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) &&
 	if ((tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) &&
-	    (tx->u.tx.rate->flags & IEEE80211_RATE_ERP_G) &&
-	    (tx->flags & IEEE80211_TXRXD_TXUNICAST) &&
+	    (tx->rate->flags & IEEE80211_RATE_ERP_G) &&
+	    (tx->flags & IEEE80211_TX_UNICAST) &&
 	    tx->sdata->bss_conf.use_cts_prot &&
 	    tx->sdata->bss_conf.use_cts_prot &&
 	    !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
 	    !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
 		control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
 		control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
@@ -692,18 +695,18 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
 	 * short preambles at the selected rate and short preambles are
 	 * short preambles at the selected rate and short preambles are
 	 * available on the network at the current point in time. */
 	 * available on the network at the current point in time. */
 	if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
 	if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
-	    (tx->u.tx.rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
+	    (tx->rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
 	    tx->sdata->bss_conf.use_short_preamble &&
 	    tx->sdata->bss_conf.use_short_preamble &&
 	    (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) {
 	    (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) {
-		tx->u.tx.control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
+		tx->control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
 	}
 	}
 
 
 	/* Setup duration field for the first fragment of the frame. Duration
 	/* Setup duration field for the first fragment of the frame. Duration
 	 * for remaining fragments will be updated when they are being sent
 	 * for remaining fragments will be updated when they are being sent
 	 * to low-level driver in ieee80211_tx(). */
 	 * to low-level driver in ieee80211_tx(). */
 	dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1),
 	dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1),
-				 (tx->flags & IEEE80211_TXRXD_FRAGMENTED) ?
-				 tx->u.tx.extra_frag[0]->len : 0);
+				 (tx->flags & IEEE80211_TX_FRAGMENTED) ?
+				 tx->extra_frag[0]->len : 0);
 	hdr->duration_id = cpu_to_le16(dur);
 	hdr->duration_id = cpu_to_le16(dur);
 
 
 	if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
 	if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
@@ -719,7 +722,7 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
 		control->alt_retry_rate = NULL;
 		control->alt_retry_rate = NULL;
 
 
 		/* Use min(data rate, max base rate) as CTS/RTS rate */
 		/* Use min(data rate, max base rate) as CTS/RTS rate */
-		rate = tx->u.tx.rate;
+		rate = tx->rate;
 		baserate = NULL;
 		baserate = NULL;
 
 
 		for (idx = 0; idx < sband->n_bitrates; idx++) {
 		for (idx = 0; idx < sband->n_bitrates; idx++) {
@@ -741,12 +744,12 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
 		tx->sta->tx_packets++;
 		tx->sta->tx_packets++;
 		tx->sta->tx_fragments++;
 		tx->sta->tx_fragments++;
 		tx->sta->tx_bytes += tx->skb->len;
 		tx->sta->tx_bytes += tx->skb->len;
-		if (tx->u.tx.extra_frag) {
+		if (tx->extra_frag) {
 			int i;
 			int i;
-			tx->sta->tx_fragments += tx->u.tx.num_extra_frag;
-			for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+			tx->sta->tx_fragments += tx->num_extra_frag;
+			for (i = 0; i < tx->num_extra_frag; i++) {
 				tx->sta->tx_bytes +=
 				tx->sta->tx_bytes +=
-					tx->u.tx.extra_frag[i]->len;
+					tx->extra_frag[i]->len;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -755,13 +758,13 @@ ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
 }
 }
 
 
 static ieee80211_tx_result
 static ieee80211_tx_result
-ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_load_stats(struct ieee80211_tx_data *tx)
 {
 {
 	struct ieee80211_local *local = tx->local;
 	struct ieee80211_local *local = tx->local;
 	struct sk_buff *skb = tx->skb;
 	struct sk_buff *skb = tx->skb;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	u32 load = 0, hdrtime;
 	u32 load = 0, hdrtime;
-	struct ieee80211_rate *rate = tx->u.tx.rate;
+	struct ieee80211_rate *rate = tx->rate;
 
 
 	/* TODO: this could be part of tx_status handling, so that the number
 	/* TODO: this could be part of tx_status handling, so that the number
 	 * of retries would be known; TX rate should in that case be stored
 	 * of retries would be known; TX rate should in that case be stored
@@ -772,8 +775,8 @@ ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
 	/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
 	/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
 	 * 1 usec = 1/8 * (1080 / 10) = 13.5 */
 	 * 1 usec = 1/8 * (1080 / 10) = 13.5 */
 
 
-	if (tx->u.tx.channel->band == IEEE80211_BAND_5GHZ ||
-	    (tx->u.tx.channel->band == IEEE80211_BAND_2GHZ &&
+	if (tx->channel->band == IEEE80211_BAND_5GHZ ||
+	    (tx->channel->band == IEEE80211_BAND_2GHZ &&
 	     rate->flags & IEEE80211_RATE_ERP_G))
 	     rate->flags & IEEE80211_RATE_ERP_G))
 		hdrtime = CHAN_UTIL_HDR_SHORT;
 		hdrtime = CHAN_UTIL_HDR_SHORT;
 	else
 	else
@@ -783,20 +786,20 @@ ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
 	if (!is_multicast_ether_addr(hdr->addr1))
 	if (!is_multicast_ether_addr(hdr->addr1))
 		load += hdrtime;
 		load += hdrtime;
 
 
-	if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
+	if (tx->control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
 		load += 2 * hdrtime;
 		load += 2 * hdrtime;
-	else if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
+	else if (tx->control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
 		load += hdrtime;
 		load += hdrtime;
 
 
 	/* TODO: optimise again */
 	/* TODO: optimise again */
 	load += skb->len * CHAN_UTIL_RATE_LCM / rate->bitrate;
 	load += skb->len * CHAN_UTIL_RATE_LCM / rate->bitrate;
 
 
-	if (tx->u.tx.extra_frag) {
+	if (tx->extra_frag) {
 		int i;
 		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+		for (i = 0; i < tx->num_extra_frag; i++) {
 			load += 2 * hdrtime;
 			load += 2 * hdrtime;
-			load += tx->u.tx.extra_frag[i]->len *
-				tx->u.tx.rate->bitrate;
+			load += tx->extra_frag[i]->len *
+				tx->rate->bitrate;
 		}
 		}
 	}
 	}
 
 
@@ -811,7 +814,7 @@ ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
 }
 }
 
 
 
 
-typedef ieee80211_tx_result (*ieee80211_tx_handler)(struct ieee80211_txrx_data *);
+typedef ieee80211_tx_result (*ieee80211_tx_handler)(struct ieee80211_tx_data *);
 static ieee80211_tx_handler ieee80211_tx_handlers[] =
 static ieee80211_tx_handler ieee80211_tx_handlers[] =
 {
 {
 	ieee80211_tx_h_check_assoc,
 	ieee80211_tx_h_check_assoc,
@@ -834,7 +837,7 @@ static ieee80211_tx_handler ieee80211_tx_handlers[] =
  * with Radiotap Header -- only called for monitor mode interface
  * with Radiotap Header -- only called for monitor mode interface
  */
  */
 static ieee80211_tx_result
 static ieee80211_tx_result
-__ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
+__ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
 			      struct sk_buff *skb)
 			      struct sk_buff *skb)
 {
 {
 	/*
 	/*
@@ -850,13 +853,13 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
 		(struct ieee80211_radiotap_header *) skb->data;
 		(struct ieee80211_radiotap_header *) skb->data;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_supported_band *sband;
 	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
 	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
-	struct ieee80211_tx_control *control = tx->u.tx.control;
+	struct ieee80211_tx_control *control = tx->control;
 
 
 	sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
 	sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
 
 
 	control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
 	control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
-	tx->flags |= IEEE80211_TXRXD_TX_INJECTED;
-	tx->flags &= ~IEEE80211_TXRXD_FRAGMENTED;
+	tx->flags |= IEEE80211_TX_INJECTED;
+	tx->flags &= ~IEEE80211_TX_FRAGMENTED;
 
 
 	/*
 	/*
 	 * for every radiotap entry that is present
 	 * for every radiotap entry that is present
@@ -892,7 +895,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
 				r = &sband->bitrates[i];
 				r = &sband->bitrates[i];
 
 
 				if (r->bitrate == target_rate) {
 				if (r->bitrate == target_rate) {
-					tx->u.tx.rate = r;
+					tx->rate = r;
 					break;
 					break;
 				}
 				}
 			}
 			}
@@ -930,7 +933,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
 				control->flags &=
 				control->flags &=
 					~IEEE80211_TXCTL_DO_NOT_ENCRYPT;
 					~IEEE80211_TXCTL_DO_NOT_ENCRYPT;
 			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG)
 			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG)
-				tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+				tx->flags |= IEEE80211_TX_FRAGMENTED;
 			break;
 			break;
 
 
 		/*
 		/*
@@ -961,7 +964,7 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
  * initialises @tx
  * initialises @tx
  */
  */
 static ieee80211_tx_result
 static ieee80211_tx_result
-__ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
+__ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
 		       struct sk_buff *skb,
 		       struct sk_buff *skb,
 		       struct net_device *dev,
 		       struct net_device *dev,
 		       struct ieee80211_tx_control *control)
 		       struct ieee80211_tx_control *control)
@@ -977,12 +980,12 @@ __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
 	tx->dev = dev; /* use original interface */
 	tx->dev = dev; /* use original interface */
 	tx->local = local;
 	tx->local = local;
 	tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	tx->u.tx.control = control;
+	tx->control = control;
 	/*
 	/*
 	 * Set this flag (used below to indicate "automatic fragmentation"),
 	 * Set this flag (used below to indicate "automatic fragmentation"),
 	 * it will be cleared/left by radiotap as desired.
 	 * it will be cleared/left by radiotap as desired.
 	 */
 	 */
-	tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+	tx->flags |= IEEE80211_TX_FRAGMENTED;
 
 
 	/* process and remove the injection radiotap header */
 	/* process and remove the injection radiotap header */
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1003,20 +1006,20 @@ __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
 	tx->fc = le16_to_cpu(hdr->frame_control);
 	tx->fc = le16_to_cpu(hdr->frame_control);
 
 
 	if (is_multicast_ether_addr(hdr->addr1)) {
 	if (is_multicast_ether_addr(hdr->addr1)) {
-		tx->flags &= ~IEEE80211_TXRXD_TXUNICAST;
+		tx->flags &= ~IEEE80211_TX_UNICAST;
 		control->flags |= IEEE80211_TXCTL_NO_ACK;
 		control->flags |= IEEE80211_TXCTL_NO_ACK;
 	} else {
 	} else {
-		tx->flags |= IEEE80211_TXRXD_TXUNICAST;
+		tx->flags |= IEEE80211_TX_UNICAST;
 		control->flags &= ~IEEE80211_TXCTL_NO_ACK;
 		control->flags &= ~IEEE80211_TXCTL_NO_ACK;
 	}
 	}
 
 
-	if (tx->flags & IEEE80211_TXRXD_FRAGMENTED) {
-		if ((tx->flags & IEEE80211_TXRXD_TXUNICAST) &&
+	if (tx->flags & IEEE80211_TX_FRAGMENTED) {
+		if ((tx->flags & IEEE80211_TX_UNICAST) &&
 		    skb->len + FCS_LEN > local->fragmentation_threshold &&
 		    skb->len + FCS_LEN > local->fragmentation_threshold &&
 		    !local->ops->set_frag_threshold)
 		    !local->ops->set_frag_threshold)
-			tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+			tx->flags |= IEEE80211_TX_FRAGMENTED;
 		else
 		else
-			tx->flags &= ~IEEE80211_TXRXD_FRAGMENTED;
+			tx->flags &= ~IEEE80211_TX_FRAGMENTED;
 	}
 	}
 
 
 	if (!tx->sta)
 	if (!tx->sta)
@@ -1039,7 +1042,7 @@ __ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
 /*
 /*
  * NB: @tx is uninitialised when passed in here
  * NB: @tx is uninitialised when passed in here
  */
  */
-static int ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
+static int ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
 				struct sk_buff *skb,
 				struct sk_buff *skb,
 				struct net_device *mdev,
 				struct net_device *mdev,
 				struct ieee80211_tx_control *control)
 				struct ieee80211_tx_control *control)
@@ -1062,9 +1065,9 @@ static int ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
 }
 }
 
 
 static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
 static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
-			  struct ieee80211_txrx_data *tx)
+			  struct ieee80211_tx_data *tx)
 {
 {
-	struct ieee80211_tx_control *control = tx->u.tx.control;
+	struct ieee80211_tx_control *control = tx->control;
 	int ret, i;
 	int ret, i;
 
 
 	if (!ieee80211_qdisc_installed(local->mdev) &&
 	if (!ieee80211_qdisc_installed(local->mdev) &&
@@ -1081,20 +1084,20 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
 		local->mdev->trans_start = jiffies;
 		local->mdev->trans_start = jiffies;
 		ieee80211_led_tx(local, 1);
 		ieee80211_led_tx(local, 1);
 	}
 	}
-	if (tx->u.tx.extra_frag) {
+	if (tx->extra_frag) {
 		control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
 		control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
 				    IEEE80211_TXCTL_USE_CTS_PROTECT |
 				    IEEE80211_TXCTL_USE_CTS_PROTECT |
 				    IEEE80211_TXCTL_CLEAR_PS_FILT |
 				    IEEE80211_TXCTL_CLEAR_PS_FILT |
 				    IEEE80211_TXCTL_FIRST_FRAGMENT);
 				    IEEE80211_TXCTL_FIRST_FRAGMENT);
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
-			if (!tx->u.tx.extra_frag[i])
+		for (i = 0; i < tx->num_extra_frag; i++) {
+			if (!tx->extra_frag[i])
 				continue;
 				continue;
 			if (__ieee80211_queue_stopped(local, control->queue))
 			if (__ieee80211_queue_stopped(local, control->queue))
 				return IEEE80211_TX_FRAG_AGAIN;
 				return IEEE80211_TX_FRAG_AGAIN;
-			if (i == tx->u.tx.num_extra_frag) {
-				control->tx_rate = tx->u.tx.last_frag_rate;
+			if (i == tx->num_extra_frag) {
+				control->tx_rate = tx->last_frag_rate;
 
 
-				if (tx->flags & IEEE80211_TXRXD_TXPROBE_LAST_FRAG)
+				if (tx->flags & IEEE80211_TX_PROBE_LAST_FRAG)
 					control->flags |=
 					control->flags |=
 						IEEE80211_TXCTL_RATE_CTRL_PROBE;
 						IEEE80211_TXCTL_RATE_CTRL_PROBE;
 				else
 				else
@@ -1104,18 +1107,18 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
 
 
 			ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
 			ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
 					     "TX to low-level driver",
 					     "TX to low-level driver",
-					     tx->u.tx.extra_frag[i]);
+					     tx->extra_frag[i]);
 			ret = local->ops->tx(local_to_hw(local),
 			ret = local->ops->tx(local_to_hw(local),
-					    tx->u.tx.extra_frag[i],
+					    tx->extra_frag[i],
 					    control);
 					    control);
 			if (ret)
 			if (ret)
 				return IEEE80211_TX_FRAG_AGAIN;
 				return IEEE80211_TX_FRAG_AGAIN;
 			local->mdev->trans_start = jiffies;
 			local->mdev->trans_start = jiffies;
 			ieee80211_led_tx(local, 1);
 			ieee80211_led_tx(local, 1);
-			tx->u.tx.extra_frag[i] = NULL;
+			tx->extra_frag[i] = NULL;
 		}
 		}
-		kfree(tx->u.tx.extra_frag);
-		tx->u.tx.extra_frag = NULL;
+		kfree(tx->extra_frag);
+		tx->extra_frag = NULL;
 	}
 	}
 	return IEEE80211_TX_OK;
 	return IEEE80211_TX_OK;
 }
 }
@@ -1126,7 +1129,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sta_info *sta;
 	struct sta_info *sta;
 	ieee80211_tx_handler *handler;
 	ieee80211_tx_handler *handler;
-	struct ieee80211_txrx_data tx;
+	struct ieee80211_tx_data tx;
 	ieee80211_tx_result res = TX_DROP, res_prepare;
 	ieee80211_tx_result res = TX_DROP, res_prepare;
 	int ret, i;
 	int ret, i;
 
 
@@ -1137,22 +1140,19 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
 		return 0;
 		return 0;
 	}
 	}
 
 
+	rcu_read_lock();
+
 	/* initialises tx */
 	/* initialises tx */
 	res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
 	res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
 
 
 	if (res_prepare == TX_DROP) {
 	if (res_prepare == TX_DROP) {
 		dev_kfree_skb(skb);
 		dev_kfree_skb(skb);
+		rcu_read_unlock();
 		return 0;
 		return 0;
 	}
 	}
 
 
-	/*
-	 * key references are protected using RCU and this requires that
-	 * we are in a read-site RCU section during receive processing
-	 */
-	rcu_read_lock();
-
 	sta = tx.sta;
 	sta = tx.sta;
-	tx.u.tx.channel = local->hw.conf.channel;
+	tx.channel = local->hw.conf.channel;
 
 
 	for (handler = ieee80211_tx_handlers; *handler != NULL;
 	for (handler = ieee80211_tx_handlers; *handler != NULL;
 	     handler++) {
 	     handler++) {
@@ -1163,9 +1163,6 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
 
 
 	skb = tx.skb; /* handlers are allowed to change skb */
 	skb = tx.skb; /* handlers are allowed to change skb */
 
 
-	if (sta)
-		sta_info_put(sta);
-
 	if (unlikely(res == TX_DROP)) {
 	if (unlikely(res == TX_DROP)) {
 		I802_DEBUG_INC(local->tx_handlers_drop);
 		I802_DEBUG_INC(local->tx_handlers_drop);
 		goto drop;
 		goto drop;
@@ -1177,18 +1174,18 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
 		return 0;
 		return 0;
 	}
 	}
 
 
-	if (tx.u.tx.extra_frag) {
-		for (i = 0; i < tx.u.tx.num_extra_frag; i++) {
+	if (tx.extra_frag) {
+		for (i = 0; i < tx.num_extra_frag; i++) {
 			int next_len, dur;
 			int next_len, dur;
 			struct ieee80211_hdr *hdr =
 			struct ieee80211_hdr *hdr =
 				(struct ieee80211_hdr *)
 				(struct ieee80211_hdr *)
-				tx.u.tx.extra_frag[i]->data;
+				tx.extra_frag[i]->data;
 
 
-			if (i + 1 < tx.u.tx.num_extra_frag) {
-				next_len = tx.u.tx.extra_frag[i + 1]->len;
+			if (i + 1 < tx.num_extra_frag) {
+				next_len = tx.extra_frag[i + 1]->len;
 			} else {
 			} else {
 				next_len = 0;
 				next_len = 0;
-				tx.u.tx.rate = tx.u.tx.last_frag_rate;
+				tx.rate = tx.last_frag_rate;
 			}
 			}
 			dur = ieee80211_duration(&tx, 0, next_len);
 			dur = ieee80211_duration(&tx, 0, next_len);
 			hdr->duration_id = cpu_to_le16(dur);
 			hdr->duration_id = cpu_to_le16(dur);
@@ -1223,11 +1220,11 @@ retry:
 		memcpy(&store->control, control,
 		memcpy(&store->control, control,
 		       sizeof(struct ieee80211_tx_control));
 		       sizeof(struct ieee80211_tx_control));
 		store->skb = skb;
 		store->skb = skb;
-		store->extra_frag = tx.u.tx.extra_frag;
-		store->num_extra_frag = tx.u.tx.num_extra_frag;
-		store->last_frag_rate = tx.u.tx.last_frag_rate;
+		store->extra_frag = tx.extra_frag;
+		store->num_extra_frag = tx.num_extra_frag;
+		store->last_frag_rate = tx.last_frag_rate;
 		store->last_frag_rate_ctrl_probe =
 		store->last_frag_rate_ctrl_probe =
-			!!(tx.flags & IEEE80211_TXRXD_TXPROBE_LAST_FRAG);
+			!!(tx.flags & IEEE80211_TX_PROBE_LAST_FRAG);
 	}
 	}
 	rcu_read_unlock();
 	rcu_read_unlock();
 	return 0;
 	return 0;
@@ -1235,10 +1232,10 @@ retry:
  drop:
  drop:
 	if (skb)
 	if (skb)
 		dev_kfree_skb(skb);
 		dev_kfree_skb(skb);
-	for (i = 0; i < tx.u.tx.num_extra_frag; i++)
-		if (tx.u.tx.extra_frag[i])
-			dev_kfree_skb(tx.u.tx.extra_frag[i]);
-	kfree(tx.u.tx.extra_frag);
+	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();
 	rcu_read_unlock();
 	return 0;
 	return 0;
 }
 }
@@ -1384,8 +1381,9 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
 	struct ieee80211_tx_packet_data *pkt_data;
 	struct ieee80211_tx_packet_data *pkt_data;
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_sub_if_data *sdata;
 	int ret = 1, head_need;
 	int ret = 1, head_need;
-	u16 ethertype, hdrlen, fc;
+	u16 ethertype, hdrlen,  meshhdrlen = 0, fc;
 	struct ieee80211_hdr hdr;
 	struct ieee80211_hdr hdr;
+	struct ieee80211s_hdr mesh_hdr;
 	const u8 *encaps_data;
 	const u8 *encaps_data;
 	int encaps_len, skip_header_bytes;
 	int encaps_len, skip_header_bytes;
 	int nh_pos, h_pos;
 	int nh_pos, h_pos;
@@ -1427,6 +1425,37 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
 		memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
 		memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
 		hdrlen = 30;
 		hdrlen = 30;
 		break;
 		break;
+#ifdef CONFIG_MAC80211_MESH
+	case IEEE80211_IF_TYPE_MESH_POINT:
+		fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
+		/* RA TA DA SA */
+		if (is_multicast_ether_addr(skb->data))
+			memcpy(hdr.addr1, skb->data, ETH_ALEN);
+		else if (mesh_nexthop_lookup(hdr.addr1, skb, dev))
+				return 0;
+		memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+		memcpy(hdr.addr3, skb->data, ETH_ALEN);
+		memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+		if (skb->pkt_type == PACKET_OTHERHOST) {
+			/* Forwarded frame, keep mesh ttl and seqnum */
+			struct ieee80211s_hdr *prev_meshhdr;
+			prev_meshhdr = ((struct ieee80211s_hdr *)skb->cb);
+			meshhdrlen = ieee80211_get_mesh_hdrlen(prev_meshhdr);
+			memcpy(&mesh_hdr, prev_meshhdr, meshhdrlen);
+			sdata->u.sta.mshstats.fwded_frames++;
+		} else {
+			if (!sdata->u.sta.mshcfg.dot11MeshTTL) {
+				/* Do not send frames with mesh_ttl == 0 */
+				sdata->u.sta.mshstats.dropped_frames_ttl++;
+				ret = 0;
+				goto fail;
+			}
+			meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
+							       sdata);
+		}
+		hdrlen = 30;
+		break;
+#endif
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_STA:
 		fc |= IEEE80211_FCTL_TODS;
 		fc |= IEEE80211_FCTL_TODS;
 		/* BSSID SA DA */
 		/* BSSID SA DA */
@@ -1453,11 +1482,11 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
 	 * in AP mode)
 	 * in AP mode)
 	 */
 	 */
 	if (!is_multicast_ether_addr(hdr.addr1)) {
 	if (!is_multicast_ether_addr(hdr.addr1)) {
+		rcu_read_lock();
 		sta = sta_info_get(local, hdr.addr1);
 		sta = sta_info_get(local, hdr.addr1);
-		if (sta) {
+		if (sta)
 			sta_flags = sta->flags;
 			sta_flags = sta->flags;
-			sta_info_put(sta);
-		}
+		rcu_read_unlock();
 	}
 	}
 
 
 	/* receiver is QoS enabled, use a QoS type frame */
 	/* receiver is QoS enabled, use a QoS type frame */
@@ -1471,8 +1500,8 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
 	 * EAPOL frames from the local station.
 	 * EAPOL frames from the local station.
 	 */
 	 */
 	if (unlikely(!is_multicast_ether_addr(hdr.addr1) &&
 	if (unlikely(!is_multicast_ether_addr(hdr.addr1) &&
-		     !(sta_flags & WLAN_STA_AUTHORIZED) &&
-		     !(ethertype == ETH_P_PAE &&
+		      !(sta_flags & WLAN_STA_AUTHORIZED) &&
+		      !(ethertype == ETH_P_PAE &&
 		       compare_ether_addr(dev->dev_addr,
 		       compare_ether_addr(dev->dev_addr,
 					  skb->data + ETH_ALEN) == 0))) {
 					  skb->data + ETH_ALEN) == 0))) {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@@ -1525,7 +1554,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
 	 * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
 	 * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
 	 * alloc_skb() (net/core/skbuff.c)
 	 * alloc_skb() (net/core/skbuff.c)
 	 */
 	 */
-	head_need = hdrlen + encaps_len + local->tx_headroom;
+	head_need = hdrlen + encaps_len + meshhdrlen + local->tx_headroom;
 	head_need -= skb_headroom(skb);
 	head_need -= skb_headroom(skb);
 
 
 	/* We are going to modify skb data, so make a copy of it if happens to
 	/* We are going to modify skb data, so make a copy of it if happens to
@@ -1559,6 +1588,12 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
 		h_pos += encaps_len;
 		h_pos += encaps_len;
 	}
 	}
 
 
+	if (meshhdrlen > 0) {
+		memcpy(skb_push(skb, meshhdrlen), &mesh_hdr, meshhdrlen);
+		nh_pos += meshhdrlen;
+		h_pos += meshhdrlen;
+	}
+
 	if (fc & IEEE80211_STYPE_QOS_DATA) {
 	if (fc & IEEE80211_STYPE_QOS_DATA) {
 		__le16 *qos_control;
 		__le16 *qos_control;
 
 
@@ -1628,7 +1663,7 @@ void ieee80211_tx_pending(unsigned long data)
 	struct ieee80211_local *local = (struct ieee80211_local *)data;
 	struct ieee80211_local *local = (struct ieee80211_local *)data;
 	struct net_device *dev = local->mdev;
 	struct net_device *dev = local->mdev;
 	struct ieee80211_tx_stored_packet *store;
 	struct ieee80211_tx_stored_packet *store;
-	struct ieee80211_txrx_data tx;
+	struct ieee80211_tx_data tx;
 	int i, ret, reschedule = 0;
 	int i, ret, reschedule = 0;
 
 
 	netif_tx_lock_bh(dev);
 	netif_tx_lock_bh(dev);
@@ -1640,13 +1675,13 @@ void ieee80211_tx_pending(unsigned long data)
 			continue;
 			continue;
 		}
 		}
 		store = &local->pending_packet[i];
 		store = &local->pending_packet[i];
-		tx.u.tx.control = &store->control;
-		tx.u.tx.extra_frag = store->extra_frag;
-		tx.u.tx.num_extra_frag = store->num_extra_frag;
-		tx.u.tx.last_frag_rate = store->last_frag_rate;
+		tx.control = &store->control;
+		tx.extra_frag = store->extra_frag;
+		tx.num_extra_frag = store->num_extra_frag;
+		tx.last_frag_rate = store->last_frag_rate;
 		tx.flags = 0;
 		tx.flags = 0;
 		if (store->last_frag_rate_ctrl_probe)
 		if (store->last_frag_rate_ctrl_probe)
-			tx.flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
+			tx.flags |= IEEE80211_TX_PROBE_LAST_FRAG;
 		ret = __ieee80211_tx(local, store->skb, &tx);
 		ret = __ieee80211_tx(local, store->skb, &tx);
 		if (ret) {
 		if (ret) {
 			if (ret == IEEE80211_TX_FRAG_AGAIN)
 			if (ret == IEEE80211_TX_FRAG_AGAIN)
@@ -1680,7 +1715,6 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
 
 
 	/* Generate bitmap for TIM only if there are any STAs in power save
 	/* Generate bitmap for TIM only if there are any STAs in power save
 	 * mode. */
 	 * mode. */
-	read_lock_bh(&local->sta_lock);
 	if (atomic_read(&bss->num_sta_ps) > 0)
 	if (atomic_read(&bss->num_sta_ps) > 0)
 		/* in the hope that this is faster than
 		/* in the hope that this is faster than
 		 * checking byte-for-byte */
 		 * checking byte-for-byte */
@@ -1731,7 +1765,6 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
 		*pos++ = aid0; /* Bitmap control */
 		*pos++ = aid0; /* Bitmap control */
 		*pos++ = 0; /* Part Virt Bitmap */
 		*pos++ = 0; /* Part Virt Bitmap */
 	}
 	}
-	read_unlock_bh(&local->sta_lock);
 }
 }
 
 
 struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
 struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
@@ -1746,6 +1779,10 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
 	struct rate_selection rsel;
 	struct rate_selection rsel;
 	struct beacon_data *beacon;
 	struct beacon_data *beacon;
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_supported_band *sband;
+	struct ieee80211_mgmt *mgmt;
+	int *num_beacons;
+	bool err = true;
+	u8 *pos;
 
 
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
 
@@ -1753,11 +1790,84 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
 
 
 	sdata = vif_to_sdata(vif);
 	sdata = vif_to_sdata(vif);
 	bdev = sdata->dev;
 	bdev = sdata->dev;
-	ap = &sdata->u.ap;
 
 
-	beacon = rcu_dereference(ap->beacon);
+	if (sdata->vif.type == IEEE80211_IF_TYPE_AP) {
+		ap = &sdata->u.ap;
+		beacon = rcu_dereference(ap->beacon);
+		if (ap && beacon) {
+			/*
+			 * headroom, head length,
+			 * tail length and maximum TIM length
+			 */
+			skb = dev_alloc_skb(local->tx_headroom +
+					    beacon->head_len +
+					    beacon->tail_len + 256);
+			if (!skb)
+				goto out;
+
+			skb_reserve(skb, local->tx_headroom);
+			memcpy(skb_put(skb, beacon->head_len), beacon->head,
+			       beacon->head_len);
+
+			ieee80211_include_sequence(sdata,
+					(struct ieee80211_hdr *)skb->data);
+
+			/*
+			 * Not very nice, but we want to allow the driver to call
+			 * ieee80211_beacon_get() as a response to the set_tim()
+			 * callback. That, however, is already invoked under the
+			 * sta_lock to guarantee consistent and race-free update
+			 * of the tim bitmap in mac80211 and the driver.
+			 */
+			if (local->tim_in_locked_section) {
+				ieee80211_beacon_add_tim(local, ap, skb, beacon);
+			} else {
+				unsigned long flags;
+
+				spin_lock_irqsave(&local->sta_lock, flags);
+				ieee80211_beacon_add_tim(local, ap, skb, beacon);
+				spin_unlock_irqrestore(&local->sta_lock, flags);
+			}
+
+			if (beacon->tail)
+				memcpy(skb_put(skb, beacon->tail_len),
+				       beacon->tail, beacon->tail_len);
+
+			num_beacons = &ap->num_beacons;
+
+			err = false;
+		}
+	} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+		/* headroom, head length, tail length and maximum TIM length */
+		skb = dev_alloc_skb(local->tx_headroom + 400);
+		if (!skb)
+			goto out;
+
+		skb_reserve(skb, local->hw.extra_tx_headroom);
+		mgmt = (struct ieee80211_mgmt *)
+			skb_put(skb, 24 + sizeof(mgmt->u.beacon));
+		memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
+		mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+						   IEEE80211_STYPE_BEACON);
+		memset(mgmt->da, 0xff, ETH_ALEN);
+		memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+		/* BSSID is left zeroed, wildcard value */
+		mgmt->u.beacon.beacon_int =
+			cpu_to_le16(local->hw.conf.beacon_int);
+		mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */
 
 
-	if (!ap || sdata->vif.type != IEEE80211_IF_TYPE_AP || !beacon) {
+		pos = skb_put(skb, 2);
+		*pos++ = WLAN_EID_SSID;
+		*pos++ = 0x0;
+
+		mesh_mgmt_ies_add(skb, sdata->dev);
+
+		num_beacons = &sdata->u.sta.num_beacons;
+
+		err = false;
+	}
+
+	if (err) {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 		if (net_ratelimit())
 		if (net_ratelimit())
 			printk(KERN_DEBUG "no beacon data avail for %s\n",
 			printk(KERN_DEBUG "no beacon data avail for %s\n",
@@ -1767,24 +1877,6 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
 		goto out;
 		goto out;
 	}
 	}
 
 
-	/* headroom, head length, tail length and maximum TIM length */
-	skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
-			    beacon->tail_len + 256);
-	if (!skb)
-		goto out;
-
-	skb_reserve(skb, local->tx_headroom);
-	memcpy(skb_put(skb, beacon->head_len), beacon->head,
-	       beacon->head_len);
-
-	ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data);
-
-	ieee80211_beacon_add_tim(local, ap, skb, beacon);
-
-	if (beacon->tail)
-		memcpy(skb_put(skb, beacon->tail_len), beacon->tail,
-		       beacon->tail_len);
-
 	if (control) {
 	if (control) {
 		rate_control_get_rate(local->mdev, sband, skb, &rsel);
 		rate_control_get_rate(local->mdev, sband, skb, &rsel);
 		if (!rsel.rate) {
 		if (!rsel.rate) {
@@ -1808,10 +1900,8 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
 		control->retry_limit = 1;
 		control->retry_limit = 1;
 		control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT;
 		control->flags |= IEEE80211_TXCTL_CLEAR_PS_FILT;
 	}
 	}
-
-	ap->num_beacons++;
-
- out:
+	(*num_beacons)++;
+out:
 	rcu_read_unlock();
 	rcu_read_unlock();
 	return skb;
 	return skb;
 }
 }
@@ -1859,7 +1949,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
 	struct sk_buff *skb;
 	struct sk_buff *skb;
 	struct sta_info *sta;
 	struct sta_info *sta;
 	ieee80211_tx_handler *handler;
 	ieee80211_tx_handler *handler;
-	struct ieee80211_txrx_data tx;
+	struct ieee80211_tx_data tx;
 	ieee80211_tx_result res = TX_DROP;
 	ieee80211_tx_result res = TX_DROP;
 	struct net_device *bdev;
 	struct net_device *bdev;
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_sub_if_data *sdata;
@@ -1881,7 +1971,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
 		rcu_read_unlock();
 		rcu_read_unlock();
 		return NULL;
 		return NULL;
 	}
 	}
-	rcu_read_unlock();
 
 
 	if (bss->dtim_count != 0)
 	if (bss->dtim_count != 0)
 		return NULL; /* send buffered bc/mc only after DTIM beacon */
 		return NULL; /* send buffered bc/mc only after DTIM beacon */
@@ -1907,8 +1996,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
 		dev_kfree_skb_any(skb);
 		dev_kfree_skb_any(skb);
 	}
 	}
 	sta = tx.sta;
 	sta = tx.sta;
-	tx.flags |= IEEE80211_TXRXD_TXPS_BUFFERED;
-	tx.u.tx.channel = local->hw.conf.channel;
+	tx.flags |= IEEE80211_TX_PS_BUFFERED;
+	tx.channel = local->hw.conf.channel;
 
 
 	for (handler = ieee80211_tx_handlers; *handler != NULL; handler++) {
 	for (handler = ieee80211_tx_handlers; *handler != NULL; handler++) {
 		res = (*handler)(&tx);
 		res = (*handler)(&tx);
@@ -1926,8 +2015,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
 		skb = NULL;
 		skb = NULL;
 	}
 	}
 
 
-	if (sta)
-		sta_info_put(sta);
+	rcu_read_unlock();
 
 
 	return skb;
 	return skb;
 }
 }

+ 24 - 4
net/mac80211/util.c

@@ -26,6 +26,7 @@
 
 
 #include "ieee80211_i.h"
 #include "ieee80211_i.h"
 #include "ieee80211_rate.h"
 #include "ieee80211_rate.h"
+#include "mesh.h"
 #include "wme.h"
 #include "wme.h"
 
 
 /* privid for wiphys to determine whether they belong to us or not */
 /* privid for wiphys to determine whether they belong to us or not */
@@ -146,17 +147,35 @@ int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
 }
 }
 EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
 EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
 
 
-void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx)
+int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
+{
+	int ae = meshhdr->flags & IEEE80211S_FLAGS_AE;
+	/* 7.1.3.5a.2 */
+	switch (ae) {
+	case 0:
+		return 5;
+	case 1:
+		return 11;
+	case 2:
+		return 17;
+	case 3:
+		return 23;
+	default:
+		return 5;
+	}
+}
+
+void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
 {
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
 
 
 	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
 	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-	if (tx->u.tx.extra_frag) {
+	if (tx->extra_frag) {
 		struct ieee80211_hdr *fhdr;
 		struct ieee80211_hdr *fhdr;
 		int i;
 		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+		for (i = 0; i < tx->num_extra_frag; i++) {
 			fhdr = (struct ieee80211_hdr *)
 			fhdr = (struct ieee80211_hdr *)
-				tx->u.tx.extra_frag[i]->data;
+				tx->extra_frag[i]->data;
 			fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
 			fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
 		}
 		}
 	}
 	}
@@ -382,6 +401,7 @@ void ieee80211_iterate_active_interfaces(
 		case IEEE80211_IF_TYPE_STA:
 		case IEEE80211_IF_TYPE_STA:
 		case IEEE80211_IF_TYPE_IBSS:
 		case IEEE80211_IF_TYPE_IBSS:
 		case IEEE80211_IF_TYPE_WDS:
 		case IEEE80211_IF_TYPE_WDS:
+		case IEEE80211_IF_TYPE_MESH_POINT:
 			break;
 			break;
 		}
 		}
 		if (sdata->dev == local->mdev)
 		if (sdata->dev == local->mdev)

+ 12 - 12
net/mac80211/wep.c

@@ -306,14 +306,14 @@ u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
 }
 }
 
 
 ieee80211_rx_result
 ieee80211_rx_result
-ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx)
+ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
 {
 {
 	if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
 	if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
 	    ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
 	    ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
 	     (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH))
 	     (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH))
 		return RX_CONTINUE;
 		return RX_CONTINUE;
 
 
-	if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) {
+	if (!(rx->status->flag & RX_FLAG_DECRYPTED)) {
 		if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
 		if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
 #ifdef CONFIG_MAC80211_DEBUG
 #ifdef CONFIG_MAC80211_DEBUG
 			if (net_ratelimit())
 			if (net_ratelimit())
@@ -322,7 +322,7 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx)
 #endif /* CONFIG_MAC80211_DEBUG */
 #endif /* CONFIG_MAC80211_DEBUG */
 			return RX_DROP_UNUSABLE;
 			return RX_DROP_UNUSABLE;
 		}
 		}
-	} else if (!(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED)) {
+	} else if (!(rx->status->flag & RX_FLAG_IV_STRIPPED)) {
 		ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
 		ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
 		/* remove ICV */
 		/* remove ICV */
 		skb_trim(rx->skb, rx->skb->len - 4);
 		skb_trim(rx->skb, rx->skb->len - 4);
@@ -331,13 +331,13 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx)
 	return RX_CONTINUE;
 	return RX_CONTINUE;
 }
 }
 
 
-static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
+static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
 {
 {
 	if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
 	if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
 		if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
 		if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
 			return -1;
 			return -1;
 	} else {
 	} else {
-		tx->u.tx.control->key_idx = tx->key->conf.hw_key_idx;
+		tx->control->key_idx = tx->key->conf.hw_key_idx;
 		if (tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) {
 		if (tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) {
 			if (!ieee80211_wep_add_iv(tx->local, skb, tx->key))
 			if (!ieee80211_wep_add_iv(tx->local, skb, tx->key))
 				return -1;
 				return -1;
@@ -347,21 +347,21 @@ static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
 }
 }
 
 
 ieee80211_tx_result
 ieee80211_tx_result
-ieee80211_crypto_wep_encrypt(struct ieee80211_txrx_data *tx)
+ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
 {
 {
-	tx->u.tx.control->iv_len = WEP_IV_LEN;
-	tx->u.tx.control->icv_len = WEP_ICV_LEN;
-	ieee80211_tx_set_iswep(tx);
+	tx->control->iv_len = WEP_IV_LEN;
+	tx->control->icv_len = WEP_ICV_LEN;
+	ieee80211_tx_set_protected(tx);
 
 
 	if (wep_encrypt_skb(tx, tx->skb) < 0) {
 	if (wep_encrypt_skb(tx, tx->skb) < 0) {
 		I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
 		I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
 		return TX_DROP;
 		return TX_DROP;
 	}
 	}
 
 
-	if (tx->u.tx.extra_frag) {
+	if (tx->extra_frag) {
 		int i;
 		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
-			if (wep_encrypt_skb(tx, tx->u.tx.extra_frag[i]) < 0) {
+		for (i = 0; i < tx->num_extra_frag; i++) {
+			if (wep_encrypt_skb(tx, tx->extra_frag[i]) < 0) {
 				I802_DEBUG_INC(tx->local->
 				I802_DEBUG_INC(tx->local->
 					       tx_handlers_drop_wep);
 					       tx_handlers_drop_wep);
 				return TX_DROP;
 				return TX_DROP;

+ 2 - 2
net/mac80211/wep.h

@@ -29,8 +29,8 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
 u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
 u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
 
 
 ieee80211_rx_result
 ieee80211_rx_result
-ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx);
+ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx);
 ieee80211_tx_result
 ieee80211_tx_result
-ieee80211_crypto_wep_encrypt(struct ieee80211_txrx_data *tx);
+ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx);
 
 
 #endif /* WEP_H */
 #endif /* WEP_H */

部分文件因为文件数量过多而无法显示