[kernel] r12059 - in dists/trunk/linux-2.6/debian/patches: features/all series

Maximilian Attems maks at alioth.debian.org
Wed Aug 13 10:15:54 UTC 2008


Author: maks
Date: Wed Aug 13 10:15:53 2008
New Revision: 12059

Log:
refresh latest fedora at76.patch

this is the version from before attempted mac80211 port,
which is known to work.

Modified:
   dists/trunk/linux-2.6/debian/patches/features/all/at76.patch
   dists/trunk/linux-2.6/debian/patches/series/1~experimental.1

Modified: dists/trunk/linux-2.6/debian/patches/features/all/at76.patch
==============================================================================
--- dists/trunk/linux-2.6/debian/patches/features/all/at76.patch	(original)
+++ dists/trunk/linux-2.6/debian/patches/features/all/at76.patch	Wed Aug 13 10:15:53 2008
@@ -1,4 +1,4 @@
-commit 38ec6fbd236318179e28c71bc1b3dc94166e4be2
+commit 3316a19c45ab4408455958dd91aa4d50cbd38a4a
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Mon Feb 4 00:05:19 2008 -0500
 
@@ -9,7 +9,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 4ed58b00a2df9772cfa631f53af52c38207d78ac
+commit 82477a6ce80732630ee2a37119d0341ec95dda1b
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:41:25 2007 -0400
 
@@ -18,7 +18,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 3a39b0c26c19b4f26ab90f8cd170e68da3c8fb91
+commit 29fde48679696cbf51536f4fe477dcafd382c89c
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:41:18 2007 -0400
 
@@ -27,7 +27,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 69a965e9dde88c1dec49a0e840020bbdfc2263ad
+commit a518a5d9a5a7ad29293366e73a3bc395d7417102
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:41:12 2007 -0400
 
@@ -39,7 +39,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit e007ed4c5328f857201d66c8564978bd4150a9a5
+commit 77809e77886d8507109be71c47fc93081a853348
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:41:06 2007 -0400
 
@@ -56,7 +56,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 84d9a4914d2f678728b0b9a4c4b360e93b2e2efa
+commit faaca95f9e608a1f63ba9273965afb26e329e701
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:40:59 2007 -0400
 
@@ -72,7 +72,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 46e958f2e0334bfeb1436d8066e1797804212178
+commit 6cd24cdbdffd1b325357e7d59935744f90946602
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:40:53 2007 -0400
 
@@ -91,7 +91,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit cbf56decc3dd24be59bdcd7af14ae6b6fc1d7265
+commit 7248e9b6e86a4e873388c5c0ecb1c19445600bce
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:40:47 2007 -0400
 
@@ -103,7 +103,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 73363e219953a6bcafa0087b415c0ce154a13940
+commit c811300758190835c27f7ee892e559dcbe59a03e
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:40:40 2007 -0400
 
@@ -114,7 +114,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 65e5ad191757d5febb31e97a8a08dbd672d0662d
+commit 9d9f3d7aee3895ab7bf51ae4278899a5bb247bfe
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:40:34 2007 -0400
 
@@ -123,7 +123,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 8f3811adf092d19f14111166af05f10cb2efa651
+commit 2364b6879b671d250e74fb05a773e45828c95f8b
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:40:27 2007 -0400
 
@@ -135,7 +135,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 0048a3f2c0de2f6ba969731f51eaaa73b3e168e2
+commit 7ccc1dca2781f79ba27a105249f12b5616ee536d
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:40:21 2007 -0400
 
@@ -147,7 +147,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 20eab45fc567b85f8aaa131eceb8d53ac14d3fd6
+commit 23554a4e27c9ef10690c1b6fb82f3d2efb64b905
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:40:15 2007 -0400
 
@@ -156,7 +156,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit b96c9fd4b3770d5b731ed3d207644b2ffec80313
+commit ba89da27c6fde14a7e74ae2ee40b17056a213115
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:40:08 2007 -0400
 
@@ -165,7 +165,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 6abdf0d9679fb3cc0b2ed539e24cbba058f33043
+commit 95003db91c724fcd3ca75409a0f16da7ae53e7bb
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:40:02 2007 -0400
 
@@ -177,7 +177,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 53c4d65cd1f4455b6d0c6ce605a6853e9108c6ae
+commit d45761f5556737b3f83ca51ef28315814a2eb0da
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:39:55 2007 -0400
 
@@ -188,7 +188,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 2a000a0fb3a21674791023759e24e1bdc3c197ef
+commit 1edd66829ef8d1baa7a156fe9239bcf07ca2c0a6
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:39:49 2007 -0400
 
@@ -200,7 +200,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 59268149e41b94bc2bd3554fcd12d238e01bdc07
+commit 4c3ad2dc228dd0d86be552e8fa0e7dad0d5bdfe2
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:39:42 2007 -0400
 
@@ -211,7 +211,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 206f30d4d21565358128aa8ca210d518797ba285
+commit 3667f0785f1d27edf28f6aaca1b5e4abe25db2eb
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:39:36 2007 -0400
 
@@ -222,7 +222,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit afd74d244ce1d869e82758c650f49a867501762f
+commit 6c6ab910bd49a987c726ef6bfb63be4bdd674b0f
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:39:30 2007 -0400
 
@@ -231,7 +231,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 2235301c13ec2c6367ef129477be8967631a436f
+commit 7ca1fbb860ab91d90415df9213f5ff205a119968
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:39:23 2007 -0400
 
@@ -242,7 +242,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit a38e0e268f2219660c91a3813d11c1440cb75faf
+commit b5e08bb0c99675de557ef9f310f2e0fb8d5cc81a
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:39:17 2007 -0400
 
@@ -254,7 +254,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 451c1cd7fcdad82175c80fb0e3e4a0568b41c17a
+commit a50e255ad0b7ae2091f477fcf6750ce77a8248fb
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:39:11 2007 -0400
 
@@ -266,7 +266,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 447c17aaf1833217f82b97be42b333c72b966db2
+commit 403974a3ef3076639a4edb74c04c24ca0b460180
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:39:04 2007 -0400
 
@@ -278,7 +278,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 951329488d45d11d25914b4da0ab93f94d72582d
+commit 90b86d1f90723ee1dcdf564766c0b3199afa2e74
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:38:58 2007 -0400
 
@@ -287,7 +287,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit ae5f9aab3f18f4dcb9a83e490996990711c53cb4
+commit ece33de2d302c114a54698607259d560459d39db
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:38:51 2007 -0400
 
@@ -298,7 +298,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 3086da15d597fb24b7254c4e638b380597e7f7c0
+commit 6ccc1309415700d10f79d3b888b1bb7ac29170c2
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sun Sep 30 14:38:45 2007 -0400
 
@@ -307,7 +307,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit f6ea5e4633ef565282f4fdfd6efb4fce47ba63e6
+commit 9f2f9fa994ac1767bd27353c4bc151c39bf883df
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:37:34 2007 -0400
 
@@ -318,7 +318,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 27147842aa12fb6f3628c8b7bfe0d1e9e41ee6b8
+commit 9f5e5be4d16b494a26fc3737fc97427b22e29ee1
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:37:29 2007 -0400
 
@@ -330,7 +330,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 14a38765e92afa44f94b78911cb5f1d4bc717a29
+commit dd623dbc6a58a32d8250219d6a771e55f1d926c8
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:37:23 2007 -0400
 
@@ -341,7 +341,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit b72a145cc8352a033cffd36e7edd3afc5ab8c692
+commit 1d5b838dd81ba67dc627a96d860baec0e36f584d
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:37:17 2007 -0400
 
@@ -350,7 +350,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 21b01509a7a669f557fd0ef822058624bcaac6a4
+commit 7daaf8538f2b479f315a0ea1860fe099f01e6fa7
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:37:11 2007 -0400
 
@@ -359,7 +359,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 798293c1bbfc815ed7acd6b5390226292bbad05c
+commit d27f4b3b62a3486e09e184ce0b61ea4508f9aa8f
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:37:06 2007 -0400
 
@@ -368,7 +368,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 99de835fbdf7d9987511a3ffef63685edeb256f8
+commit ecc3863b16c56824e25b2160453ceee8a508f853
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:37:00 2007 -0400
 
@@ -377,7 +377,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit dc15d751590deb46a8f1aee782bc4f66a811ba58
+commit c77ad228fe687a7a8cd97ac828b74ad5786cf887
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:36:54 2007 -0400
 
@@ -389,7 +389,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit ec943e753321c78047b5e70d846deed79e3d7e0d
+commit 34f240d5c41c5b4dace0240c2cea6b14ec06f026
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:36:49 2007 -0400
 
@@ -398,7 +398,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 474a3bd835a1d6ce0567b6f0ce71ce17520edf85
+commit 3c54047b4c1fcbeeec29fad99a89c94c51e6b794
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:36:43 2007 -0400
 
@@ -410,7 +410,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit e5d3864054c7bae4bde6681148db5fe293d3972f
+commit b170e507588d3c4a53e6c64980f3e7ce2aec1d09
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:36:37 2007 -0400
 
@@ -419,7 +419,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 020e78ae379a4153b7c87aaeffca9d6694f0badb
+commit 157ede19d732e5dfee841e0f118281e4b6790c58
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:36:31 2007 -0400
 
@@ -436,7 +436,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 83d03b994903ffe7f6ab0b540780852412387030
+commit 9b1d8d0b33f9182bb052ee6e70a9b8a0aa3fa0f2
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:36:26 2007 -0400
 
@@ -447,7 +447,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 2510182b5f69324e37869cfee1d9b78c5a1c8cfe
+commit 855e36c680af75bae89164d1251f0529b51b602a
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:36:20 2007 -0400
 
@@ -460,7 +460,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit eb542472fb6621bed0d9a176e62ec261e162b3ac
+commit b6ef8eca79defe0b34ac8ccb463c0e4b242581f1
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:36:14 2007 -0400
 
@@ -471,7 +471,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 0c1af75ef1a71060ddd0e32311b058feaa63ed23
+commit fc30a32862cfa8a4e66565c7831458d85b784059
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:36:09 2007 -0400
 
@@ -483,7 +483,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 71734a46b9d85b85b4e8672461f321f39e48b4aa
+commit 849bca5ecef33821a16a2aba06a9e711b03673d7
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:36:03 2007 -0400
 
@@ -492,7 +492,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 3b19b79d75b5fa2746a277f52abf2690fe1a43f1
+commit 25d0c50b09990a3f20ead4b84fe0db5f7d1a5ce0
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:57 2007 -0400
 
@@ -505,7 +505,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 76403fd6c014238c564b38d9052298fc9e2a5078
+commit 93fea2be9c3a92e30f28aec53e5468090f11ca9c
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:52 2007 -0400
 
@@ -517,7 +517,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit c56dfaaaada275f4a5c3cdef3dba3e6c714ce03f
+commit 9ea989b20c9aee36b2223fa6f98a98c63dd7df11
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:46 2007 -0400
 
@@ -529,7 +529,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit c8a13ceeddd58a0c304da7eb8578e44197edad38
+commit ac42233d0e3cd7bd3434f2a07d8941e3daae2c6b
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:40 2007 -0400
 
@@ -538,7 +538,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit ca2c72f021bef2fbb8c2c56e6d0650ba7891f7ca
+commit ebb0c0e6310291517b87e2d20f2bf4f9990de4b5
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:34 2007 -0400
 
@@ -550,7 +550,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 1c4f1ca9448b9300e484e39d57882a7ae38a5729
+commit 18e9c45377a017c8bd9db092596c43afaac08a4e
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:29 2007 -0400
 
@@ -562,7 +562,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit fef6c2352ca33980d9aba9232b70aeecace2a134
+commit 8a26847f9223046b95efb4dd60b4916e52d80a27
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:23 2007 -0400
 
@@ -573,7 +573,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit aa7b9455703286c9823bcbd0a55f3418a0a1b47a
+commit 5a9a14c10d294f9b8338425918291f31a6176ee8
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:17 2007 -0400
 
@@ -582,7 +582,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 9c13ed40aeed909e386950c302f6d9c130a92b2a
+commit 240f936685cec822f6a0c9f0dec29343c378f94c
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:11 2007 -0400
 
@@ -594,7 +594,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 8a2cb93b3bfc2c23a394f5e4fc8dd6837503640a
+commit 74942a136c0e5948448cfc933e6f11eaf4a28ec1
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:06 2007 -0400
 
@@ -605,7 +605,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 5c777c0514de7cbc6652f10ad55feab36b13d21b
+commit 1362544a8a960b1cbc72baa5ca0ed72bdff69151
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:35:00 2007 -0400
 
@@ -617,7 +617,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 10bbb7977ec0cbea6087d30af14147516a5eeac5
+commit 8a97f47646414035c5e939e335266531cb7ccf17
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:34:54 2007 -0400
 
@@ -629,7 +629,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 2646301bdd06efe9677bb2ef96acc9c450cf1af8
+commit 31f31b7906c86707ca296e23b8f26cd2ff918bd2
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:34:49 2007 -0400
 
@@ -638,7 +638,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 0a9cfd63f1773ce2196d15d5ff1214c3c693d8a7
+commit 3c1c1b75b79dc97522b6cee4290d49f00cd7ae20
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:34:43 2007 -0400
 
@@ -647,7 +647,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 684ad32c3e727b65066a896124ed73834f14b087
+commit a89c328a9dcc9985eaca48aa85399db83d3848c9
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:34:37 2007 -0400
 
@@ -656,7 +656,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit cb9222a5c85ff3634475f04a7949dc7b966c1902
+commit 96f4aca7d406e878f378bd84a6bd32a4d02569f5
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:34:31 2007 -0400
 
@@ -665,7 +665,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 92315ab89a2ffffcb5ef24554489b06e322ea17c
+commit 551fe4634ebf8bd5ad314b048ab65f69f4f237e5
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:34:26 2007 -0400
 
@@ -674,7 +674,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit 46211c37a56499d49829d8db2c230712d6053790
+commit c77dd96fc4ecc8455fb3b329b0b38cbe5bd16719
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Sat Sep 1 00:34:20 2007 -0400
 
@@ -688,7 +688,7 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-commit cfc3a1a100a86079980e814674004793ad161390
+commit a73c05ab296bec1e760a8a20916c2c3e331d7a2f
 Author: Pavel Roskin <proski at gnu.org>
 Date:   Thu Aug 23 14:40:17 2007 -0400
 
@@ -705,664 +705,10 @@
     Signed-off-by: Pavel Roskin <proski at gnu.org>
     Signed-off-by: John W. Linville <linville at tuxdriver.com>
 
-diff -up linux-2.6.24.noarch/MAINTAINERS.orig linux-2.6.24.noarch/MAINTAINERS
---- linux-2.6.24.noarch/MAINTAINERS.orig	2008-02-05 22:45:15.000000000 -0500
-+++ linux-2.6.24.noarch/MAINTAINERS	2008-02-05 22:46:17.000000000 -0500
-@@ -713,6 +713,15 @@ W:	http://www.thekelleys.org.uk/atmel
- W:	http://atmelwlandriver.sourceforge.net/
- S:	Maintained
- 
-+ATMEL USB WIRELESS DRIVER
-+P:	Pavel Roskin
-+M:	proski at gnu.org
-+L:	linux-wireless at vger.kernel.org
-+L:	at76c503a-user at lists.berlios.de
-+L:	at76c503a-develop at lists.berlios.de
-+W:	http://at76c503a.berlios.de/
-+S:	Maintained
-+
- AUDIT SUBSYSTEM
- P:	Al Viro
- M:	viro at zeniv.linux.org.uk
-diff -up /dev/null linux-2.6.24.noarch/drivers/net/wireless/at76_usb.h
---- /dev/null	2008-02-05 08:32:52.710614252 -0500
-+++ linux-2.6.24.noarch/drivers/net/wireless/at76_usb.h	2008-02-05 22:46:17.000000000 -0500
-@@ -0,0 +1,619 @@
-+/*
-+ * Copyright (c) 2002,2003 Oliver Kurth
-+ *	     (c) 2003,2004 Joerg Albert <joerg.albert at gmx.de>
-+ *	     (c) 2007 Guido Guenther <agx at sigxcpu.org>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License as
-+ * published by the Free Software Foundation; either version 2 of
-+ * the License, or (at your option) any later version.
-+ *
-+ * This driver was based on information from the Sourceforge driver
-+ * released and maintained by Atmel:
-+ *
-+ *  http://sourceforge.net/projects/atmelwlandriver/
-+ *
-+ * Although the code was completely re-written,
-+ * it would have been impossible without Atmel's decision to
-+ * release an Open Source driver (unfortunately the firmware was
-+ * kept binary only). Thanks for that decision to Atmel!
-+ */
-+
-+#ifndef _AT76_USB_H
-+#define _AT76_USB_H
-+
-+/* Board types */
-+enum board_type {
-+	BOARD_503_ISL3861 = 1,
-+	BOARD_503_ISL3863 = 2,
-+	BOARD_503 = 3,
-+	BOARD_503_ACC = 4,
-+	BOARD_505 = 5,
-+	BOARD_505_2958 = 6,
-+	BOARD_505A = 7,
-+	BOARD_505AMX = 8
-+};
-+
-+/* our private ioctl's */
-+/* preamble length (0 - long, 1 - short, 2 - auto) */
-+#define AT76_SET_SHORT_PREAMBLE		(SIOCIWFIRSTPRIV + 0)
-+#define AT76_GET_SHORT_PREAMBLE		(SIOCIWFIRSTPRIV + 1)
-+/* which debug channels are enabled */
-+#define AT76_SET_DEBUG			(SIOCIWFIRSTPRIV + 2)
-+#define AT76_GET_DEBUG			(SIOCIWFIRSTPRIV + 3)
-+/* power save mode (incl. the Atmel proprietary smart save mode) */
-+#define AT76_SET_POWERSAVE_MODE		(SIOCIWFIRSTPRIV + 4)
-+#define AT76_GET_POWERSAVE_MODE		(SIOCIWFIRSTPRIV + 5)
-+/* min and max channel times for scan */
-+#define AT76_SET_SCAN_TIMES		(SIOCIWFIRSTPRIV + 6)
-+#define AT76_GET_SCAN_TIMES		(SIOCIWFIRSTPRIV + 7)
-+/* scan mode (0 - active, 1 - passive) */
-+#define AT76_SET_SCAN_MODE		(SIOCIWFIRSTPRIV + 8)
-+#define AT76_GET_SCAN_MODE		(SIOCIWFIRSTPRIV + 9)
-+
-+#define CMD_STATUS_IDLE				0x00
-+#define CMD_STATUS_COMPLETE			0x01
-+#define CMD_STATUS_UNKNOWN			0x02
-+#define CMD_STATUS_INVALID_PARAMETER		0x03
-+#define CMD_STATUS_FUNCTION_NOT_SUPPORTED	0x04
-+#define CMD_STATUS_TIME_OUT			0x07
-+#define CMD_STATUS_IN_PROGRESS			0x08
-+#define CMD_STATUS_HOST_FAILURE			0xff
-+#define CMD_STATUS_SCAN_FAILED			0xf0
-+
-+/* answers to get op mode */
-+#define OPMODE_NONE				0x00
-+#define OPMODE_NORMAL_NIC_WITH_FLASH		0x01
-+#define OPMODE_HW_CONFIG_MODE			0x02
-+#define OPMODE_DFU_MODE_WITH_FLASH		0x03
-+#define OPMODE_NORMAL_NIC_WITHOUT_FLASH		0x04
-+
-+#define CMD_SET_MIB		0x01
-+#define CMD_GET_MIB		0x02
-+#define CMD_SCAN		0x03
-+#define CMD_JOIN		0x04
-+#define CMD_START_IBSS		0x05
-+#define CMD_RADIO_ON		0x06
-+#define CMD_RADIO_OFF		0x07
-+#define CMD_STARTUP		0x0B
-+
-+#define MIB_LOCAL		0x01
-+#define MIB_MAC_ADDR		0x02
-+#define MIB_MAC			0x03
-+#define MIB_MAC_MGMT		0x05
-+#define MIB_MAC_WEP		0x06
-+#define MIB_PHY			0x07
-+#define MIB_FW_VERSION		0x08
-+#define MIB_MDOMAIN		0x09
-+
-+#define ADHOC_MODE		1
-+#define INFRASTRUCTURE_MODE	2
-+
-+/* values for struct mib_local, field preamble_type */
-+#define PREAMBLE_TYPE_LONG	0
-+#define PREAMBLE_TYPE_SHORT	1
-+#define PREAMBLE_TYPE_AUTO	2
-+
-+/* values for tx_rate */
-+#define TX_RATE_1MBIT		0
-+#define TX_RATE_2MBIT		1
-+#define TX_RATE_5_5MBIT 	2
-+#define TX_RATE_11MBIT		3
-+#define TX_RATE_AUTO		4
-+
-+/* power management modes */
-+#define AT76_PM_OFF		1
-+#define AT76_PM_ON		2
-+#define AT76_PM_SMART		3
-+
-+struct hwcfg_r505 {
-+	u8 cr39_values[14];
-+	u8 reserved1[14];
-+	u8 bb_cr[14];
-+	u8 pidvid[4];
-+	u8 mac_addr[ETH_ALEN];
-+	u8 regulatory_domain;
-+	u8 reserved2[14];
-+	u8 cr15_values[14];
-+	u8 reserved3[3];
-+} __attribute__((packed));
-+
-+struct hwcfg_rfmd {
-+	u8 cr20_values[14];
-+	u8 cr21_values[14];
-+	u8 bb_cr[14];
-+	u8 pidvid[4];
-+	u8 mac_addr[ETH_ALEN];
-+	u8 regulatory_domain;
-+	u8 low_power_values[14];
-+	u8 normal_power_values[14];
-+	u8 reserved1[3];
-+} __attribute__((packed));
-+
-+struct hwcfg_intersil {
-+	u8 mac_addr[ETH_ALEN];
-+	u8 cr31_values[14];
-+	u8 cr58_values[14];
-+	u8 pidvid[4];
-+	u8 regulatory_domain;
-+	u8 reserved[1];
-+} __attribute__((packed));
-+
-+union at76_hwcfg {
-+	struct hwcfg_intersil i;
-+	struct hwcfg_rfmd r3;
-+	struct hwcfg_r505 r5;
-+};
-+
-+#define WEP_SMALL_KEY_LEN	(40 / 8)
-+#define WEP_LARGE_KEY_LEN	(104 / 8)
-+
-+struct at76_card_config {
-+	u8 exclude_unencrypted;
-+	u8 promiscuous_mode;
-+	u8 short_retry_limit;
-+	u8 encryption_type;
-+	__le16 rts_threshold;
-+	__le16 fragmentation_threshold;	/* 256..2346 */
-+	u8 basic_rate_set[4];
-+	u8 auto_rate_fallback;	/* 0,1 */
-+	u8 channel;
-+	u8 privacy_invoked;
-+	u8 wep_default_key_id;	/* 0..3 */
-+	u8 current_ssid[32];
-+	u8 wep_default_key_value[4][WEP_KEY_LEN];
-+	u8 ssid_len;
-+	u8 short_preamble;
-+	__le16 beacon_period;
-+} __attribute__((packed));
-+
-+struct at76_command {
-+	u8 cmd;
-+	u8 reserved;
-+	__le16 size;
-+	u8 data[0];
-+} __attribute__((packed));
-+
-+/* Length of Atmel-specific Rx header before 802.11 frame */
-+#define AT76_RX_HDRLEN offsetof(struct at76_rx_buffer, packet)
-+
-+struct at76_rx_buffer {
-+	__le16 wlength;
-+	u8 rx_rate;
-+	u8 newbss;
-+	u8 fragmentation;
-+	u8 rssi;
-+	u8 link_quality;
-+	u8 noise_level;
-+	__le32 rx_time;
-+	u8 packet[IEEE80211_FRAME_LEN + IEEE80211_FCS_LEN];
-+} __attribute__((packed));
-+
-+/* Length of Atmel-specific Tx header before 802.11 frame */
-+#define AT76_TX_HDRLEN offsetof(struct at76_tx_buffer, packet)
-+
-+struct at76_tx_buffer {
-+	__le16 wlength;
-+	u8 tx_rate;
-+	u8 padding;
-+	u8 reserved[4];
-+	u8 packet[IEEE80211_FRAME_LEN + IEEE80211_FCS_LEN];
-+} __attribute__((packed));
-+
-+/* defines for scan_type below */
-+#define SCAN_TYPE_ACTIVE	0
-+#define SCAN_TYPE_PASSIVE	1
-+
-+struct at76_req_scan {
-+	u8 bssid[ETH_ALEN];
-+	u8 essid[32];
-+	u8 scan_type;
-+	u8 channel;
-+	__le16 probe_delay;
-+	__le16 min_channel_time;
-+	__le16 max_channel_time;
-+	u8 essid_size;
-+	u8 international_scan;
-+} __attribute__((packed));
-+
-+struct at76_req_ibss {
-+	u8 bssid[ETH_ALEN];
-+	u8 essid[32];
-+	u8 bss_type;
-+	u8 channel;
-+	u8 essid_size;
-+	u8 reserved[3];
-+} __attribute__((packed));
-+
-+struct at76_req_join {
-+	u8 bssid[ETH_ALEN];
-+	u8 essid[32];
-+	u8 bss_type;
-+	u8 channel;
-+	__le16 timeout;
-+	u8 essid_size;
-+	u8 reserved;
-+} __attribute__((packed));
-+
-+struct set_mib_buffer {
-+	u8 type;
-+	u8 size;
-+	u8 index;
-+	u8 reserved;
-+	union {
-+		u8 byte;
-+		__le16 word;
-+		u8 addr[ETH_ALEN];
-+	} data;
-+} __attribute__((packed));
-+
-+struct mib_local {
-+	u16 reserved0;
-+	u8 beacon_enable;
-+	u8 txautorate_fallback;
-+	u8 reserved1;
-+	u8 ssid_size;
-+	u8 promiscuous_mode;
-+	u16 reserved2;
-+	u8 preamble_type;
-+	u16 reserved3;
-+} __attribute__((packed));
-+
-+struct mib_mac_addr {
-+	u8 mac_addr[ETH_ALEN];
-+	u8 res[2];		/* ??? */
-+	u8 group_addr[4][ETH_ALEN];
-+	u8 group_addr_status[4];
-+} __attribute__((packed));
-+
-+struct mib_mac {
-+	__le32 max_tx_msdu_lifetime;
-+	__le32 max_rx_lifetime;
-+	__le16 frag_threshold;
-+	__le16 rts_threshold;
-+	__le16 cwmin;
-+	__le16 cwmax;
-+	u8 short_retry_time;
-+	u8 long_retry_time;
-+	u8 scan_type;		/* active or passive */
-+	u8 scan_channel;
-+	__le16 probe_delay;	/* delay before ProbeReq in active scan, RO */
-+	__le16 min_channel_time;
-+	__le16 max_channel_time;
-+	__le16 listen_interval;
-+	u8 desired_ssid[32];
-+	u8 desired_bssid[ETH_ALEN];
-+	u8 desired_bsstype;	/* ad-hoc or infrastructure */
-+	u8 reserved2;
-+} __attribute__((packed));
-+
-+struct mib_mac_mgmt {
-+	__le16 beacon_period;
-+	__le16 CFP_max_duration;
-+	__le16 medium_occupancy_limit;
-+	__le16 station_id;	/* assoc id */
-+	__le16 ATIM_window;
-+	u8 CFP_mode;
-+	u8 privacy_option_implemented;
-+	u8 DTIM_period;
-+	u8 CFP_period;
-+	u8 current_bssid[ETH_ALEN];
-+	u8 current_essid[32];
-+	u8 current_bss_type;
-+	u8 power_mgmt_mode;
-+	/* rfmd and 505 */
-+	u8 ibss_change;
-+	u8 res;
-+	u8 multi_domain_capability_implemented;
-+	u8 multi_domain_capability_enabled;
-+	u8 country_string[3];
-+	u8 reserved[3];
-+} __attribute__((packed));
-+
-+struct mib_mac_wep {
-+	u8 privacy_invoked;	/* 0 disable encr., 1 enable encr */
-+	u8 wep_default_key_id;
-+	u8 wep_key_mapping_len;
-+	u8 exclude_unencrypted;
-+	__le32 wep_icv_error_count;
-+	__le32 wep_excluded_count;
-+	u8 wep_default_keyvalue[WEP_KEYS][WEP_KEY_LEN];
-+	u8 encryption_level;	/* 1 for 40bit, 2 for 104bit encryption */
-+} __attribute__((packed));
-+
-+struct mib_phy {
-+	__le32 ed_threshold;
-+
-+	__le16 slot_time;
-+	__le16 sifs_time;
-+	__le16 preamble_length;
-+	__le16 plcp_header_length;
-+	__le16 mpdu_max_length;
-+	__le16 cca_mode_supported;
-+
-+	u8 operation_rate_set[4];
-+	u8 channel_id;
-+	u8 current_cca_mode;
-+	u8 phy_type;
-+	u8 current_reg_domain;
-+} __attribute__((packed));
-+
-+struct mib_fw_version {
-+	u8 major;
-+	u8 minor;
-+	u8 patch;
-+	u8 build;
-+} __attribute__((packed));
-+
-+struct mib_mdomain {
-+	u8 tx_powerlevel[14];
-+	u8 channel_list[14];	/* 0 for invalid channels */
-+} __attribute__((packed));
-+
-+struct at76_fw_header {
-+	__le32 crc;		/* CRC32 of the whole image */
-+	__le32 board_type;	/* firmware compatibility code */
-+	u8 build;		/* firmware build number */
-+	u8 patch;		/* firmware patch level */
-+	u8 minor;		/* firmware minor version */
-+	u8 major;		/* firmware major version */
-+	__le32 str_offset;	/* offset of the copyright string */
-+	__le32 int_fw_offset;	/* internal firmware image offset */
-+	__le32 int_fw_len;	/* internal firmware image length */
-+	__le32 ext_fw_offset;	/* external firmware image offset */
-+	__le32 ext_fw_len;	/* external firmware image length */
-+} __attribute__((packed));
-+
-+enum mac_state {
-+	MAC_INIT,
-+	MAC_SCANNING,
-+	MAC_AUTH,
-+	MAC_ASSOC,
-+	MAC_JOINING,
-+	MAC_CONNECTED,
-+	MAC_OWN_IBSS
-+};
-+
-+/* a description of a regulatory domain and the allowed channels */
-+struct reg_domain {
-+	u16 code;
-+	char const *name;
-+	u32 channel_map;	/* if bit N is set, channel (N+1) is allowed */
-+};
-+
-+/* how long do we keep a (I)BSS in the bss_list in jiffies
-+   this should be long enough for the user to retrieve the table
-+   (by iwlist ?) after the device started, because all entries from
-+   other channels than the one the device locks on get removed, too */
-+#define BSS_LIST_TIMEOUT	(120 * HZ)
-+/* struct to store BSS info found during scan */
-+#define BSS_LIST_MAX_RATE_LEN	32	/* 32 rates should be enough ... */
-+
-+struct bss_info {
-+	struct list_head list;
-+
-+	u8 bssid[ETH_ALEN];	/* bssid */
-+	u8 ssid[IW_ESSID_MAX_SIZE];	/* essid */
-+	u8 ssid_len;		/* length of ssid above */
-+	u8 channel;
-+	u16 capa;		/* BSS capabilities */
-+	u16 beacon_interval;	/* beacon interval, Kus (1024 microseconds) */
-+	u8 rates[BSS_LIST_MAX_RATE_LEN];	/* supported rates in units of
-+						   500 kbps, ORed with 0x80 for
-+						   basic rates */
-+	u8 rates_len;
-+
-+	/* quality of received beacon */
-+	u8 rssi;
-+	u8 link_qual;
-+	u8 noise_level;
-+
-+	unsigned long last_rx;	/* time (jiffies) of last beacon received */
-+};
-+
-+/* a rx data buffer to collect rx fragments */
-+struct rx_data_buf {
-+	u8 sender[ETH_ALEN];	/* sender address */
-+	u16 seqnr;		/* sequence number */
-+	u16 fragnr;		/* last fragment received */
-+	unsigned long last_rx;	/* jiffies of last rx */
-+	struct sk_buff *skb;	/* == NULL if entry is free */
-+};
-+
-+#define NR_RX_DATA_BUF		8
-+
-+/* Data for one loaded firmware file */
-+struct fwentry {
-+	const char *const fwname;
-+	const struct firmware *fw;
-+	int extfw_size;
-+	int intfw_size;
-+	/* pointer to loaded firmware, no need to free */
-+	u8 *extfw;		/* external firmware, extfw_size bytes long */
-+	u8 *intfw;		/* internal firmware, intfw_size bytes long */
-+	enum board_type board_type;	/* board type */
-+	struct mib_fw_version fw_version;
-+	int loaded;		/* Loaded and parsed successfully */
-+};
-+
-+struct at76_priv {
-+	struct usb_device *udev;	/* USB device pointer */
-+	struct net_device *netdev;	/* net device pointer */
-+	struct net_device_stats stats;	/* net device stats */
-+	struct iw_statistics wstats;	/* wireless stats */
-+
-+	struct sk_buff *rx_skb;	/* skbuff for receiving data */
-+	void *bulk_out_buffer;	/* buffer for sending data */
-+
-+	struct urb *tx_urb;	/* URB for sending data */
-+	struct urb *rx_urb;	/* URB for receiving data */
-+
-+	unsigned int tx_pipe;	/* bulk out pipe */
-+	unsigned int rx_pipe;	/* bulk in pipe */
-+
-+	struct mutex mtx;	/* locks this structure */
-+
-+	/* work queues */
-+	struct work_struct work_assoc_done;
-+	struct work_struct work_join;
-+	struct work_struct work_new_bss;
-+	struct work_struct work_start_scan;
-+	struct work_struct work_set_promisc;
-+	struct work_struct work_submit_rx;
-+	struct delayed_work dwork_restart;
-+	struct delayed_work dwork_get_scan;
-+	struct delayed_work dwork_beacon;
-+	struct delayed_work dwork_auth;
-+	struct delayed_work dwork_assoc;
-+
-+	struct tasklet_struct rx_tasklet;
-+
-+	/* the WEP stuff */
-+	int wep_enabled;	/* 1 if WEP is enabled */
-+	int wep_key_id;		/* key id to be used */
-+	u8 wep_keys[WEP_KEYS][WEP_KEY_LEN];	/* the four WEP keys,
-+						   5 or 13 bytes are used */
-+	u8 wep_keys_len[WEP_KEYS];	/* the length of the above keys */
-+
-+	int channel;
-+	int iw_mode;
-+	u8 bssid[ETH_ALEN];
-+	u8 essid[IW_ESSID_MAX_SIZE];
-+	int essid_size;
-+	int radio_on;
-+	int promisc;
-+
-+	int preamble_type;	/* 0 - long, 1 - short, 2 - auto */
-+	int auth_mode;		/* authentication type: 0 open, 1 shared key */
-+	int txrate;		/* 0,1,2,3 = 1,2,5.5,11 Mbps, 4 is auto */
-+	int frag_threshold;	/* threshold for fragmentation of tx packets */
-+	int rts_threshold;	/* threshold for RTS mechanism */
-+	int short_retry_limit;
-+
-+	int scan_min_time;	/* scan min channel time */
-+	int scan_max_time;	/* scan max channel time */
-+	int scan_mode;		/* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */
-+	int scan_need_any;	/* if set, need to scan for any ESSID */
-+
-+	/* the list we got from scanning */
-+	spinlock_t bss_list_spinlock;	/* protects bss_list operations */
-+	struct list_head bss_list;	/* list of BSS we got beacons from */
-+	struct timer_list bss_list_timer;	/* timer to purge old entries
-+						   from bss_list */
-+	struct bss_info *curr_bss;	/* current BSS */
-+	u16 assoc_id;		/* current association ID, if associated */
-+
-+	u8 wanted_bssid[ETH_ALEN];
-+	int wanted_bssid_valid;	/* != 0 if wanted_bssid is to be used */
-+
-+	/* some data for infrastructure mode only */
-+	spinlock_t mgmt_spinlock;	/* this spinlock protects access to
-+					   next_mgmt_bulk */
-+
-+	struct at76_tx_buffer *next_mgmt_bulk;	/* pending management msg to
-+						   send via bulk out */
-+	enum mac_state mac_state;
-+	enum {
-+		SCAN_IDLE,
-+		SCAN_IN_PROGRESS,
-+		SCAN_COMPLETED
-+	} scan_state;
-+	time_t last_scan;
-+
-+	int retries;		/* remaining retries in case of timeout when
-+				 * sending AuthReq or AssocReq */
-+	u8 pm_mode;		/* power management mode */
-+	u32 pm_period;		/* power management period in microseconds */
-+
-+	struct reg_domain const *domain;	/* reg domain description */
-+
-+	/* iwspy support */
-+	spinlock_t spy_spinlock;
-+	struct iw_spy_data spy_data;
-+
-+	struct iw_public_data wireless_data;
-+
-+	/* These fields contain HW config provided by the device (not all of
-+	 * these fields are used by all board types) */
-+	u8 mac_addr[ETH_ALEN];
-+	u8 regulatory_domain;
-+
-+	struct at76_card_config card_config;
-+
-+	/* store rx fragments until complete */
-+	struct rx_data_buf rx_data[NR_RX_DATA_BUF];
-+
-+	enum board_type board_type;
-+	struct mib_fw_version fw_version;
-+
-+	unsigned int device_unplugged:1;
-+	unsigned int netdev_registered:1;
-+	struct set_mib_buffer mib_buf;	/* global buffer for set_mib calls */
-+
-+	/* beacon counting */
-+	int beacon_period;	/* period of mgmt beacons, Kus */
-+	int beacons_received;
-+	unsigned long beacons_last_qual;	/* time we restarted counting
-+						   beacons */
-+};
-+
-+struct at76_rx_radiotap {
-+	struct ieee80211_radiotap_header rt_hdr;
-+	__le64 rt_tsft;
-+	u8 rt_flags;
-+	u8 rt_rate;
-+	s8 rt_signal;
-+	s8 rt_noise;
-+};
-+
-+#define AT76_RX_RADIOTAP_PRESENT		  \
-+	((1 << IEEE80211_RADIOTAP_TSFT)		| \
-+	(1 << IEEE80211_RADIOTAP_FLAGS)		| \
-+	(1 << IEEE80211_RADIOTAP_RATE)		| \
-+	(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)	| \
-+	(1 << IEEE80211_RADIOTAP_DB_ANTNOISE))
-+
-+#define BEACON_MAX_DATA_LENGTH	1500
-+
-+/* the maximum size of an AssocReq packet */
-+#define ASSOCREQ_MAX_SIZE \
-+  (AT76_TX_HDRLEN + sizeof(struct ieee80211_assoc_request) + \
-+   1 + 1 + IW_ESSID_MAX_SIZE + 1 + 1 + 4)
-+
-+/* for shared secret auth, add the challenge text size */
-+#define AUTH_FRAME_SIZE (AT76_TX_HDRLEN + sizeof(struct ieee80211_auth))
-+
-+/* Maximal number of AuthReq retries */
-+#define AUTH_RETRIES		3
-+
-+/* Maximal number of AssocReq retries */
-+#define ASSOC_RETRIES		3
-+
-+/* Beacon timeout in managed mode when we are connected */
-+#define BEACON_TIMEOUT		(10 * HZ)
-+
-+/* Timeout for authentication response */
-+#define AUTH_TIMEOUT		(1 * HZ)
-+
-+/* Timeout for association response */
-+#define ASSOC_TIMEOUT		(1 * HZ)
-+
-+/* Polling interval when scan is running */
-+#define SCAN_POLL_INTERVAL	(HZ / 4)
-+
-+/* Command completion timeout */
-+#define CMD_COMPLETION_TIMEOUT	(5 * HZ)
-+
-+#define DEF_RTS_THRESHOLD	1536
-+#define DEF_FRAG_THRESHOLD	1536
-+#define DEF_SHORT_RETRY_LIMIT	8
-+#define DEF_CHANNEL		10
-+#define DEF_SCAN_MIN_TIME	10
-+#define DEF_SCAN_MAX_TIME	120
-+
-+#define MAX_RTS_THRESHOLD	(MAX_FRAG_THRESHOLD + 1)
-+
-+/* the max padding size for tx in bytes (see calc_padding) */
-+#define MAX_PADDING_SIZE	53
-+
-+#endif				/* _AT76_USB_H */
-diff -up linux-2.6.24.noarch/drivers/net/wireless/Makefile.orig linux-2.6.24.noarch/drivers/net/wireless/Makefile
---- linux-2.6.24.noarch/drivers/net/wireless/Makefile.orig	2008-02-05 22:45:16.000000000 -0500
-+++ linux-2.6.24.noarch/drivers/net/wireless/Makefile	2008-02-05 22:46:17.000000000 -0500
-@@ -32,6 +32,8 @@ obj-$(CONFIG_ATMEL)             += atmel
- obj-$(CONFIG_PCI_ATMEL)         += atmel_pci.o 
- obj-$(CONFIG_PCMCIA_ATMEL)      += atmel_cs.o
- 
-+obj-$(CONFIG_USB_ATMEL)	        += at76_usb.o
-+
- obj-$(CONFIG_PRISM54)		+= prism54/
- 
- obj-$(CONFIG_HOSTAP)		+= hostap/
-diff -up /dev/null linux-2.6.24.noarch/drivers/net/wireless/at76_usb.c
---- /dev/null	2008-02-05 08:32:52.710614252 -0500
-+++ linux-2.6.24.noarch/drivers/net/wireless/at76_usb.c	2008-02-05 22:46:17.000000000 -0500
-@@ -0,0 +1,5558 @@
+diff -up /dev/null linux-2.6.26.noarch/drivers/net/wireless/at76_usb.c
+--- /dev/null	2008-08-01 08:46:19.471002774 -0400
++++ linux-2.6.26.noarch/drivers/net/wireless/at76_usb.c	2008-08-01 11:33:05.000000000 -0400
+@@ -0,0 +1,5559 @@
 +/*
 + * at76c503/at76c505 USB driver
 + *
@@ -3696,7 +3042,7 @@
 +		iwe->cmd = SIOCGIWAP;
 +		iwe->u.ap_addr.sa_family = ARPHRD_ETHER;
 +		memcpy(iwe->u.ap_addr.sa_data, curr_bss->bssid, 6);
-+		curr_pos = iwe_stream_add_event(curr_pos,
++		curr_pos = iwe_stream_add_event(info, curr_pos,
 +						extra + IW_SCAN_MAX_DATA, iwe,
 +						IW_EV_ADDR_LEN);
 +
@@ -3704,7 +3050,7 @@
 +		iwe->cmd = SIOCGIWESSID;
 +		iwe->u.data.flags = 1;
 +
-+		curr_pos = iwe_stream_add_point(curr_pos,
++		curr_pos = iwe_stream_add_point(info, curr_pos,
 +						extra + IW_SCAN_MAX_DATA, iwe,
 +						curr_bss->ssid);
 +
@@ -3715,14 +3061,14 @@
 +		    IW_MODE_MASTER : IW_MODE_AUTO;
 +		/* IW_MODE_AUTO = 0 which I thought is
 +		 * the most logical value to return in this case */
-+		curr_pos = iwe_stream_add_event(curr_pos,
++		curr_pos = iwe_stream_add_event(info, curr_pos,
 +						extra + IW_SCAN_MAX_DATA, iwe,
 +						IW_EV_UINT_LEN);
 +
 +		iwe->cmd = SIOCGIWFREQ;
 +		iwe->u.freq.m = curr_bss->channel;
 +		iwe->u.freq.e = 0;
-+		curr_pos = iwe_stream_add_event(curr_pos,
++		curr_pos = iwe_stream_add_event(info, curr_pos,
 +						extra + IW_SCAN_MAX_DATA, iwe,
 +						IW_EV_FREQ_LEN);
 +
@@ -3733,7 +3079,7 @@
 +			iwe->u.data.flags = IW_ENCODE_DISABLED;
 +
 +		iwe->u.data.length = 0;
-+		curr_pos = iwe_stream_add_point(curr_pos,
++		curr_pos = iwe_stream_add_point(info, curr_pos,
 +						extra + IW_SCAN_MAX_DATA, iwe,
 +						NULL);
 +
@@ -3752,7 +3098,7 @@
 +			iwe->u.qual.updated |= IW_QUAL_QUAL_INVALID;
 +		}
 +		/* Add new value to event */
-+		curr_pos = iwe_stream_add_event(curr_pos,
++		curr_pos = iwe_stream_add_event(info, curr_pos,
 +						extra + IW_SCAN_MAX_DATA, iwe,
 +						IW_EV_QUAL_LEN);
 +
@@ -3770,7 +3116,8 @@
 +			iwe->u.bitrate.value =
 +			    ((curr_bss->rates[i] & 0x7f) * 500000);
 +			/* Add new value to event */
-+			curr_val = iwe_stream_add_value(curr_pos, curr_val,
++			curr_val = iwe_stream_add_value(info, curr_pos,
++							curr_val,
 +							extra +
 +							IW_SCAN_MAX_DATA, iwe,
 +							IW_EV_PARAM_LEN);
@@ -4096,2835 +3443,3458 @@
 +	return -EIWCOMMIT;
 +}
 +
-+static int at76_iw_handler_get_encode(struct net_device *netdev,
-+				      struct iw_request_info *info,
-+				      struct iw_point *encoding, char *extra)
++static int at76_iw_handler_get_encode(struct net_device *netdev,
++				      struct iw_request_info *info,
++				      struct iw_point *encoding, char *extra)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
++	int index = (encoding->flags & IW_ENCODE_INDEX) - 1;
++
++	if ((index < 0) || (index >= WEP_KEYS))
++		index = priv->wep_key_id;
++
++	encoding->flags =
++	    (priv->auth_mode == WLAN_AUTH_SHARED_KEY) ?
++	    IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN;
++
++	if (!priv->wep_enabled)
++		encoding->flags |= IW_ENCODE_DISABLED;
++
++	if (encoding->pointer) {
++		encoding->length = priv->wep_keys_len[index];
++
++		memcpy(extra, priv->wep_keys[index], priv->wep_keys_len[index]);
++
++		encoding->flags |= (index + 1);
++	}
++
++	at76_dbg(DBG_IOCTL, "%s: SIOCGIWENCODE - enc.flags %08x "
++		 "pointer %p len %d", netdev->name, encoding->flags,
++		 encoding->pointer, encoding->length);
++	at76_dbg(DBG_IOCTL,
++		 "%s: SIOCGIWENCODE - wepstate: enabled %s key_id %d "
++		 "key_len %d auth_mode %s", netdev->name,
++		 (priv->wep_enabled) ? "true" : "false", priv->wep_key_id + 1,
++		 priv->wep_keys_len[priv->wep_key_id],
++		 (priv->auth_mode ==
++		  WLAN_AUTH_SHARED_KEY) ? "restricted" : "open");
++
++	return 0;
++}
++
++static int at76_iw_handler_set_power(struct net_device *netdev,
++				     struct iw_request_info *info,
++				     struct iw_param *prq, char *extra)
++{
++	int err = -EIWCOMMIT;
++	struct at76_priv *priv = netdev_priv(netdev);
++
++	at76_dbg(DBG_IOCTL,
++		 "%s: SIOCSIWPOWER - disabled %s flags 0x%x value 0x%x",
++		 netdev->name, (prq->disabled) ? "true" : "false", prq->flags,
++		 prq->value);
++
++	if (prq->disabled)
++		priv->pm_mode = AT76_PM_OFF;
++	else {
++		switch (prq->flags & IW_POWER_MODE) {
++		case IW_POWER_ALL_R:
++		case IW_POWER_ON:
++			break;
++		default:
++			err = -EINVAL;
++			goto exit;
++		}
++		if (prq->flags & IW_POWER_PERIOD)
++			priv->pm_period = prq->value;
++
++		if (prq->flags & IW_POWER_TIMEOUT) {
++			err = -EINVAL;
++			goto exit;
++		}
++		priv->pm_mode = AT76_PM_ON;
++	}
++exit:
++	return err;
++}
++
++static int at76_iw_handler_get_power(struct net_device *netdev,
++				     struct iw_request_info *info,
++				     struct iw_param *power, char *extra)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
++
++	power->disabled = (priv->pm_mode == AT76_PM_OFF);
++	if (!power->disabled) {
++		power->flags = IW_POWER_PERIOD | IW_POWER_ALL_R;
++		power->value = priv->pm_period;
++	}
++
++	at76_dbg(DBG_IOCTL, "%s: SIOCGIWPOWER - %s flags 0x%x value 0x%x",
++		 netdev->name, power->disabled ? "disabled" : "enabled",
++		 power->flags, power->value);
++
++	return 0;
++}
++
++/*******************************************************************************
++ * Private IOCTLS
++ */
++static int at76_iw_set_short_preamble(struct net_device *netdev,
++				      struct iw_request_info *info, char *name,
++				      char *extra)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
++	int val = *((int *)name);
++	int ret = -EIWCOMMIT;
++
++	at76_dbg(DBG_IOCTL, "%s: AT76_SET_SHORT_PREAMBLE, %d",
++		 netdev->name, val);
++
++	if (val < PREAMBLE_TYPE_LONG || val > PREAMBLE_TYPE_AUTO)
++		ret = -EINVAL;
++	else
++		priv->preamble_type = val;
++
++	return ret;
++}
++
++static int at76_iw_get_short_preamble(struct net_device *netdev,
++				      struct iw_request_info *info,
++				      union iwreq_data *wrqu, char *extra)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
++
++	snprintf(wrqu->name, sizeof(wrqu->name), "%s (%d)",
++		 preambles[priv->preamble_type], priv->preamble_type);
++	return 0;
++}
++
++static int at76_iw_set_debug(struct net_device *netdev,
++			     struct iw_request_info *info,
++			     struct iw_point *data, char *extra)
++{
++	char *ptr;
++	u32 val;
++
++	if (data->length > 0) {
++		val = simple_strtol(extra, &ptr, 0);
++
++		if (ptr == extra)
++			val = DBG_DEFAULTS;
++
++		at76_dbg(DBG_IOCTL, "%s: AT76_SET_DEBUG input %d: %s -> 0x%x",
++			 netdev->name, data->length, extra, val);
++	} else
++		val = DBG_DEFAULTS;
++
++	at76_dbg(DBG_IOCTL, "%s: AT76_SET_DEBUG, old 0x%x, new 0x%x",
++		 netdev->name, at76_debug, val);
++
++	/* jal: some more output to pin down lockups */
++	at76_dbg(DBG_IOCTL, "%s: netif running %d queue_stopped %d "
++		 "carrier_ok %d", netdev->name, netif_running(netdev),
++		 netif_queue_stopped(netdev), netif_carrier_ok(netdev));
++
++	at76_debug = val;
++
++	return 0;
++}
++
++static int at76_iw_get_debug(struct net_device *netdev,
++			     struct iw_request_info *info,
++			     union iwreq_data *wrqu, char *extra)
++{
++	snprintf(wrqu->name, sizeof(wrqu->name), "0x%08x", at76_debug);
++	return 0;
++}
++
++static int at76_iw_set_powersave_mode(struct net_device *netdev,
++				      struct iw_request_info *info, char *name,
++				      char *extra)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
++	int val = *((int *)name);
++	int ret = -EIWCOMMIT;
++
++	at76_dbg(DBG_IOCTL, "%s: AT76_SET_POWERSAVE_MODE, %d (%s)",
++		 netdev->name, val,
++		 val == AT76_PM_OFF ? "active" : val == AT76_PM_ON ? "save" :
++		 val == AT76_PM_SMART ? "smart save" : "<invalid>");
++	if (val < AT76_PM_OFF || val > AT76_PM_SMART)
++		ret = -EINVAL;
++	else
++		priv->pm_mode = val;
++
++	return ret;
++}
++
++static int at76_iw_get_powersave_mode(struct net_device *netdev,
++				      struct iw_request_info *info,
++				      union iwreq_data *wrqu, char *extra)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
++	int *param = (int *)extra;
++
++	param[0] = priv->pm_mode;
++	return 0;
++}
++
++static int at76_iw_set_scan_times(struct net_device *netdev,
++				  struct iw_request_info *info, char *name,
++				  char *extra)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
++	int mint = *((int *)name);
++	int maxt = *((int *)name + 1);
++	int ret = -EIWCOMMIT;
++
++	at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_TIMES - min %d max %d",
++		 netdev->name, mint, maxt);
++	if (mint <= 0 || maxt <= 0 || mint > maxt)
++		ret = -EINVAL;
++	else {
++		priv->scan_min_time = mint;
++		priv->scan_max_time = maxt;
++	}
++
++	return ret;
++}
++
++static int at76_iw_get_scan_times(struct net_device *netdev,
++				  struct iw_request_info *info,
++				  union iwreq_data *wrqu, char *extra)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
++	int *param = (int *)extra;
++
++	param[0] = priv->scan_min_time;
++	param[1] = priv->scan_max_time;
++	return 0;
++}
++
++static int at76_iw_set_scan_mode(struct net_device *netdev,
++				 struct iw_request_info *info, char *name,
++				 char *extra)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
++	int val = *((int *)name);
++	int ret = -EIWCOMMIT;
++
++	at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_MODE - mode %s",
++		 netdev->name, (val = SCAN_TYPE_ACTIVE) ? "active" :
++		 (val = SCAN_TYPE_PASSIVE) ? "passive" : "<invalid>");
++
++	if (val != SCAN_TYPE_ACTIVE && val != SCAN_TYPE_PASSIVE)
++		ret = -EINVAL;
++	else
++		priv->scan_mode = val;
++
++	return ret;
++}
++
++static int at76_iw_get_scan_mode(struct net_device *netdev,
++				 struct iw_request_info *info,
++				 union iwreq_data *wrqu, char *extra)
 +{
 +	struct at76_priv *priv = netdev_priv(netdev);
-+	int index = (encoding->flags & IW_ENCODE_INDEX) - 1;
++	int *param = (int *)extra;
 +
-+	if ((index < 0) || (index >= WEP_KEYS))
-+		index = priv->wep_key_id;
++	param[0] = priv->scan_mode;
++	return 0;
++}
 +
-+	encoding->flags =
-+	    (priv->auth_mode == WLAN_AUTH_SHARED_KEY) ?
-+	    IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN;
++#define AT76_SET_HANDLER(h, f) [h - SIOCIWFIRST] = (iw_handler) f
 +
-+	if (!priv->wep_enabled)
-+		encoding->flags |= IW_ENCODE_DISABLED;
++/* Standard wireless handlers */
++static const iw_handler at76_handlers[] = {
++	AT76_SET_HANDLER(SIOCSIWCOMMIT, at76_iw_handler_commit),
++	AT76_SET_HANDLER(SIOCGIWNAME, at76_iw_handler_get_name),
++	AT76_SET_HANDLER(SIOCSIWFREQ, at76_iw_handler_set_freq),
++	AT76_SET_HANDLER(SIOCGIWFREQ, at76_iw_handler_get_freq),
++	AT76_SET_HANDLER(SIOCSIWMODE, at76_iw_handler_set_mode),
++	AT76_SET_HANDLER(SIOCGIWMODE, at76_iw_handler_get_mode),
++	AT76_SET_HANDLER(SIOCGIWRANGE, at76_iw_handler_get_range),
++	AT76_SET_HANDLER(SIOCSIWSPY, at76_iw_handler_set_spy),
++	AT76_SET_HANDLER(SIOCGIWSPY, at76_iw_handler_get_spy),
++	AT76_SET_HANDLER(SIOCSIWTHRSPY, at76_iw_handler_set_thrspy),
++	AT76_SET_HANDLER(SIOCGIWTHRSPY, at76_iw_handler_get_thrspy),
++	AT76_SET_HANDLER(SIOCSIWAP, at76_iw_handler_set_wap),
++	AT76_SET_HANDLER(SIOCGIWAP, at76_iw_handler_get_wap),
++	AT76_SET_HANDLER(SIOCSIWSCAN, at76_iw_handler_set_scan),
++	AT76_SET_HANDLER(SIOCGIWSCAN, at76_iw_handler_get_scan),
++	AT76_SET_HANDLER(SIOCSIWESSID, at76_iw_handler_set_essid),
++	AT76_SET_HANDLER(SIOCGIWESSID, at76_iw_handler_get_essid),
++	AT76_SET_HANDLER(SIOCSIWRATE, at76_iw_handler_set_rate),
++	AT76_SET_HANDLER(SIOCGIWRATE, at76_iw_handler_get_rate),
++	AT76_SET_HANDLER(SIOCSIWRTS, at76_iw_handler_set_rts),
++	AT76_SET_HANDLER(SIOCGIWRTS, at76_iw_handler_get_rts),
++	AT76_SET_HANDLER(SIOCSIWFRAG, at76_iw_handler_set_frag),
++	AT76_SET_HANDLER(SIOCGIWFRAG, at76_iw_handler_get_frag),
++	AT76_SET_HANDLER(SIOCGIWTXPOW, at76_iw_handler_get_txpow),
++	AT76_SET_HANDLER(SIOCSIWRETRY, at76_iw_handler_set_retry),
++	AT76_SET_HANDLER(SIOCGIWRETRY, at76_iw_handler_get_retry),
++	AT76_SET_HANDLER(SIOCSIWENCODE, at76_iw_handler_set_encode),
++	AT76_SET_HANDLER(SIOCGIWENCODE, at76_iw_handler_get_encode),
++	AT76_SET_HANDLER(SIOCSIWPOWER, at76_iw_handler_set_power),
++	AT76_SET_HANDLER(SIOCGIWPOWER, at76_iw_handler_get_power)
++};
 +
-+	if (encoding->pointer) {
-+		encoding->length = priv->wep_keys_len[index];
++#define AT76_SET_PRIV(h, f) [h - SIOCIWFIRSTPRIV] = (iw_handler) f
 +
-+		memcpy(extra, priv->wep_keys[index], priv->wep_keys_len[index]);
++/* Private wireless handlers */
++static const iw_handler at76_priv_handlers[] = {
++	AT76_SET_PRIV(AT76_SET_SHORT_PREAMBLE, at76_iw_set_short_preamble),
++	AT76_SET_PRIV(AT76_GET_SHORT_PREAMBLE, at76_iw_get_short_preamble),
++	AT76_SET_PRIV(AT76_SET_DEBUG, at76_iw_set_debug),
++	AT76_SET_PRIV(AT76_GET_DEBUG, at76_iw_get_debug),
++	AT76_SET_PRIV(AT76_SET_POWERSAVE_MODE, at76_iw_set_powersave_mode),
++	AT76_SET_PRIV(AT76_GET_POWERSAVE_MODE, at76_iw_get_powersave_mode),
++	AT76_SET_PRIV(AT76_SET_SCAN_TIMES, at76_iw_set_scan_times),
++	AT76_SET_PRIV(AT76_GET_SCAN_TIMES, at76_iw_get_scan_times),
++	AT76_SET_PRIV(AT76_SET_SCAN_MODE, at76_iw_set_scan_mode),
++	AT76_SET_PRIV(AT76_GET_SCAN_MODE, at76_iw_get_scan_mode),
++};
 +
-+		encoding->flags |= (index + 1);
-+	}
++/* Names and arguments of private wireless handlers */
++static const struct iw_priv_args at76_priv_args[] = {
++	/* 0 - long, 1 - short */
++	{AT76_SET_SHORT_PREAMBLE,
++	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"},
 +
-+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWENCODE - enc.flags %08x "
-+		 "pointer %p len %d", netdev->name, encoding->flags,
-+		 encoding->pointer, encoding->length);
-+	at76_dbg(DBG_IOCTL,
-+		 "%s: SIOCGIWENCODE - wepstate: enabled %s key_id %d "
-+		 "key_len %d auth_mode %s", netdev->name,
-+		 (priv->wep_enabled) ? "true" : "false", priv->wep_key_id + 1,
-+		 priv->wep_keys_len[priv->wep_key_id],
-+		 (priv->auth_mode ==
-+		  WLAN_AUTH_SHARED_KEY) ? "restricted" : "open");
++	{AT76_GET_SHORT_PREAMBLE,
++	 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 10, "get_preamble"},
 +
-+	return 0;
-+}
++	/* we must pass the new debug mask as a string, because iwpriv cannot
++	 * parse hex numbers starting with 0x :-(  */
++	{AT76_SET_DEBUG,
++	 IW_PRIV_TYPE_CHAR | 10, 0, "set_debug"},
 +
-+static int at76_iw_handler_set_power(struct net_device *netdev,
-+				     struct iw_request_info *info,
-+				     struct iw_param *prq, char *extra)
-+{
-+	int err = -EIWCOMMIT;
-+	struct at76_priv *priv = netdev_priv(netdev);
++	{AT76_GET_DEBUG,
++	 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 10, "get_debug"},
 +
-+	at76_dbg(DBG_IOCTL,
-+		 "%s: SIOCSIWPOWER - disabled %s flags 0x%x value 0x%x",
-+		 netdev->name, (prq->disabled) ? "true" : "false", prq->flags,
-+		 prq->value);
++	/* 1 - active, 2 - power save, 3 - smart power save */
++	{AT76_SET_POWERSAVE_MODE,
++	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_powersave"},
 +
-+	if (prq->disabled)
-+		priv->pm_mode = AT76_PM_OFF;
-+	else {
-+		switch (prq->flags & IW_POWER_MODE) {
-+		case IW_POWER_ALL_R:
-+		case IW_POWER_ON:
-+			break;
-+		default:
-+			err = -EINVAL;
-+			goto exit;
-+		}
-+		if (prq->flags & IW_POWER_PERIOD)
-+			priv->pm_period = prq->value;
++	{AT76_GET_POWERSAVE_MODE,
++	 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_powersave"},
 +
-+		if (prq->flags & IW_POWER_TIMEOUT) {
-+			err = -EINVAL;
-+			goto exit;
-+		}
-+		priv->pm_mode = AT76_PM_ON;
-+	}
-+exit:
-+	return err;
-+}
++	/* min_channel_time, max_channel_time */
++	{AT76_SET_SCAN_TIMES,
++	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_scan_times"},
 +
-+static int at76_iw_handler_get_power(struct net_device *netdev,
-+				     struct iw_request_info *info,
-+				     struct iw_param *power, char *extra)
-+{
-+	struct at76_priv *priv = netdev_priv(netdev);
++	{AT76_GET_SCAN_TIMES,
++	 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, "get_scan_times"},
 +
-+	power->disabled = (priv->pm_mode == AT76_PM_OFF);
-+	if (!power->disabled) {
-+		power->flags = IW_POWER_PERIOD | IW_POWER_ALL_R;
-+		power->value = priv->pm_period;
-+	}
++	/* 0 - active, 1 - passive scan */
++	{AT76_SET_SCAN_MODE,
++	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_scan_mode"},
 +
-+	at76_dbg(DBG_IOCTL, "%s: SIOCGIWPOWER - %s flags 0x%x value 0x%x",
-+		 netdev->name, power->disabled ? "disabled" : "enabled",
-+		 power->flags, power->value);
++	{AT76_GET_SCAN_MODE,
++	 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scan_mode"},
++};
 +
-+	return 0;
-+}
++static const struct iw_handler_def at76_handler_def = {
++	.num_standard = ARRAY_SIZE(at76_handlers),
++	.num_private = ARRAY_SIZE(at76_priv_handlers),
++	.num_private_args = ARRAY_SIZE(at76_priv_args),
++	.standard = at76_handlers,
++	.private = at76_priv_handlers,
++	.private_args = at76_priv_args,
++	.get_wireless_stats = at76_get_wireless_stats,
++};
 +
-+/*******************************************************************************
-+ * Private IOCTLS
-+ */
-+static int at76_iw_set_short_preamble(struct net_device *netdev,
-+				      struct iw_request_info *info, char *name,
-+				      char *extra)
++static const u8 snapsig[] = { 0xaa, 0xaa, 0x03 };
++
++/* RFC 1042 encapsulates Ethernet frames in 802.2 SNAP (0xaa, 0xaa, 0x03) with
++ * a SNAP OID of 0 (0x00, 0x00, 0x00) */
++static const u8 rfc1042sig[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
++
++static int at76_tx(struct sk_buff *skb, struct net_device *netdev)
 +{
 +	struct at76_priv *priv = netdev_priv(netdev);
-+	int val = *((int *)name);
-+	int ret = -EIWCOMMIT;
++	struct net_device_stats *stats = &priv->stats;
++	int ret = 0;
++	int wlen;
++	int submit_len;
++	struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer;
++	struct ieee80211_hdr_3addr *i802_11_hdr =
++	    (struct ieee80211_hdr_3addr *)tx_buffer->packet;
++	u8 *payload = i802_11_hdr->payload;
++	struct ethhdr *eh = (struct ethhdr *)skb->data;
 +
-+	at76_dbg(DBG_IOCTL, "%s: AT76_SET_SHORT_PREAMBLE, %d",
-+		 netdev->name, val);
++	if (netif_queue_stopped(netdev)) {
++		printk(KERN_ERR "%s: %s called while netdev is stopped\n",
++		       netdev->name, __func__);
++		/* skip this packet */
++		dev_kfree_skb(skb);
++		return 0;
++	}
 +
-+	if (val < PREAMBLE_TYPE_LONG || val > PREAMBLE_TYPE_AUTO)
-+		ret = -EINVAL;
-+	else
-+		priv->preamble_type = val;
++	if (priv->tx_urb->status == -EINPROGRESS) {
++		printk(KERN_ERR "%s: %s called while tx urb is pending\n",
++		       netdev->name, __func__);
++		/* skip this packet */
++		dev_kfree_skb(skb);
++		return 0;
++	}
 +
-+	return ret;
-+}
++	if (skb->len < ETH_HLEN) {
++		printk(KERN_ERR "%s: %s: skb too short (%d)\n",
++		       netdev->name, __func__, skb->len);
++		dev_kfree_skb(skb);
++		return 0;
++	}
 +
-+static int at76_iw_get_short_preamble(struct net_device *netdev,
-+				      struct iw_request_info *info,
-+				      union iwreq_data *wrqu, char *extra)
-+{
-+	struct at76_priv *priv = netdev_priv(netdev);
++	at76_ledtrig_tx_activity();	/* tell ledtrigger we send a packet */
++
++	/* we can get rid of memcpy if we set netdev->hard_header_len to
++	   reserve enough space, but we would need to keep the skb around */
++
++	if (ntohs(eh->h_proto) <= ETH_DATA_LEN) {
++		/* this is a 802.3 packet */
++		if (skb->len >= ETH_HLEN + sizeof(rfc1042sig)
++		    && skb->data[ETH_HLEN] == rfc1042sig[0]
++		    && skb->data[ETH_HLEN + 1] == rfc1042sig[1]) {
++			/* higher layer delivered SNAP header - keep it */
++			memcpy(payload, skb->data + ETH_HLEN,
++			       skb->len - ETH_HLEN);
++			wlen = IEEE80211_3ADDR_LEN + skb->len - ETH_HLEN;
++		} else {
++			printk(KERN_ERR "%s: dropping non-SNAP 802.2 packet "
++			       "(DSAP 0x%02x SSAP 0x%02x cntrl 0x%02x)\n",
++			       priv->netdev->name, skb->data[ETH_HLEN],
++			       skb->data[ETH_HLEN + 1],
++			       skb->data[ETH_HLEN + 2]);
++			dev_kfree_skb(skb);
++			return 0;
++		}
++	} else {
++		/* add RFC 1042 header in front */
++		memcpy(payload, rfc1042sig, sizeof(rfc1042sig));
++		memcpy(payload + sizeof(rfc1042sig), &eh->h_proto,
++		       skb->len - offsetof(struct ethhdr, h_proto));
++		wlen = IEEE80211_3ADDR_LEN + sizeof(rfc1042sig) + skb->len -
++		    offsetof(struct ethhdr, h_proto);
++	}
 +
-+	snprintf(wrqu->name, sizeof(wrqu->name), "%s (%d)",
-+		 preambles[priv->preamble_type], priv->preamble_type);
-+	return 0;
-+}
++	/* make wireless header */
++	i802_11_hdr->frame_ctl =
++	    cpu_to_le16(IEEE80211_FTYPE_DATA |
++			(priv->wep_enabled ? IEEE80211_FCTL_PROTECTED : 0) |
++			(priv->iw_mode ==
++			 IW_MODE_INFRA ? IEEE80211_FCTL_TODS : 0));
 +
-+static int at76_iw_set_debug(struct net_device *netdev,
-+			     struct iw_request_info *info,
-+			     struct iw_point *data, char *extra)
-+{
-+	char *ptr;
-+	u32 val;
++	if (priv->iw_mode == IW_MODE_ADHOC) {
++		memcpy(i802_11_hdr->addr1, eh->h_dest, ETH_ALEN);
++		memcpy(i802_11_hdr->addr2, eh->h_source, ETH_ALEN);
++		memcpy(i802_11_hdr->addr3, priv->bssid, ETH_ALEN);
++	} else if (priv->iw_mode == IW_MODE_INFRA) {
++		memcpy(i802_11_hdr->addr1, priv->bssid, ETH_ALEN);
++		memcpy(i802_11_hdr->addr2, eh->h_source, ETH_ALEN);
++		memcpy(i802_11_hdr->addr3, eh->h_dest, ETH_ALEN);
++	}
 +
-+	if (data->length > 0) {
-+		val = simple_strtol(extra, &ptr, 0);
++	i802_11_hdr->duration_id = cpu_to_le16(0);
++	i802_11_hdr->seq_ctl = cpu_to_le16(0);
 +
-+		if (ptr == extra)
-+			val = DBG_DEFAULTS;
++	/* setup 'Atmel' header */
++	tx_buffer->wlength = cpu_to_le16(wlen);
++	tx_buffer->tx_rate = priv->txrate;
++	/* for broadcast destination addresses, the firmware 0.100.x
++	   seems to choose the highest rate set with CMD_STARTUP in
++	   basic_rate_set replacing this value */
 +
-+		at76_dbg(DBG_IOCTL, "%s: AT76_SET_DEBUG input %d: %s -> 0x%x",
-+			 netdev->name, data->length, extra, val);
-+	} else
-+		val = DBG_DEFAULTS;
++	memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved));
 +
-+	at76_dbg(DBG_IOCTL, "%s: AT76_SET_DEBUG, old 0x%x, new 0x%x",
-+		 netdev->name, at76_debug, val);
++	tx_buffer->padding = at76_calc_padding(wlen);
++	submit_len = wlen + AT76_TX_HDRLEN + tx_buffer->padding;
 +
-+	/* jal: some more output to pin down lockups */
-+	at76_dbg(DBG_IOCTL, "%s: netif running %d queue_stopped %d "
-+		 "carrier_ok %d", netdev->name, netif_running(netdev),
-+		 netif_queue_stopped(netdev), netif_carrier_ok(netdev));
++	at76_dbg(DBG_TX_DATA_CONTENT, "%s skb->data %s", priv->netdev->name,
++		 hex2str(skb->data, 32));
++	at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr %s",
++		 priv->netdev->name,
++		 le16_to_cpu(tx_buffer->wlength),
++		 tx_buffer->padding, tx_buffer->tx_rate,
++		 hex2str(i802_11_hdr, sizeof(*i802_11_hdr)));
++	at76_dbg(DBG_TX_DATA_CONTENT, "%s payload %s", priv->netdev->name,
++		 hex2str(payload, 48));
 +
-+	at76_debug = val;
++	/* send stuff */
++	netif_stop_queue(netdev);
++	netdev->trans_start = jiffies;
 +
-+	return 0;
-+}
++	usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer,
++			  submit_len, at76_tx_callback, priv);
++	ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC);
++	if (ret) {
++		stats->tx_errors++;
++		printk(KERN_ERR "%s: error in tx submit urb: %d\n",
++		       netdev->name, ret);
++		if (ret == -EINVAL)
++			printk(KERN_ERR
++			       "%s: -EINVAL: tx urb %p hcpriv %p complete %p\n",
++			       priv->netdev->name, priv->tx_urb,
++			       priv->tx_urb->hcpriv, priv->tx_urb->complete);
++	} else {
++		stats->tx_bytes += skb->len;
++		dev_kfree_skb(skb);
++	}
 +
-+static int at76_iw_get_debug(struct net_device *netdev,
-+			     struct iw_request_info *info,
-+			     union iwreq_data *wrqu, char *extra)
-+{
-+	snprintf(wrqu->name, sizeof(wrqu->name), "0x%08x", at76_debug);
-+	return 0;
++	return ret;
 +}
 +
-+static int at76_iw_set_powersave_mode(struct net_device *netdev,
-+				      struct iw_request_info *info, char *name,
-+				      char *extra)
++static void at76_tx_timeout(struct net_device *netdev)
 +{
 +	struct at76_priv *priv = netdev_priv(netdev);
-+	int val = *((int *)name);
-+	int ret = -EIWCOMMIT;
 +
-+	at76_dbg(DBG_IOCTL, "%s: AT76_SET_POWERSAVE_MODE, %d (%s)",
-+		 netdev->name, val,
-+		 val == AT76_PM_OFF ? "active" : val == AT76_PM_ON ? "save" :
-+		 val == AT76_PM_SMART ? "smart save" : "<invalid>");
-+	if (val < AT76_PM_OFF || val > AT76_PM_SMART)
-+		ret = -EINVAL;
-+	else
-+		priv->pm_mode = val;
++	if (!priv)
++		return;
++	warn("%s: tx timeout.", netdev->name);
 +
-+	return ret;
++	usb_unlink_urb(priv->tx_urb);
++	priv->stats.tx_errors++;
 +}
 +
-+static int at76_iw_get_powersave_mode(struct net_device *netdev,
-+				      struct iw_request_info *info,
-+				      union iwreq_data *wrqu, char *extra)
++static int at76_submit_rx_urb(struct at76_priv *priv)
 +{
-+	struct at76_priv *priv = netdev_priv(netdev);
-+	int *param = (int *)extra;
-+
-+	param[0] = priv->pm_mode;
-+	return 0;
-+}
++	int ret;
++	int size;
++	struct sk_buff *skb = priv->rx_skb;
 +
-+static int at76_iw_set_scan_times(struct net_device *netdev,
-+				  struct iw_request_info *info, char *name,
-+				  char *extra)
-+{
-+	struct at76_priv *priv = netdev_priv(netdev);
-+	int mint = *((int *)name);
-+	int maxt = *((int *)name + 1);
-+	int ret = -EIWCOMMIT;
++	if (!priv->rx_urb) {
++		printk(KERN_ERR "%s: %s: priv->rx_urb is NULL\n",
++		       priv->netdev->name, __func__);
++		return -EFAULT;
++	}
 +
-+	at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_TIMES - min %d max %d",
-+		 netdev->name, mint, maxt);
-+	if (mint <= 0 || maxt <= 0 || mint > maxt)
-+		ret = -EINVAL;
-+	else {
-+		priv->scan_min_time = mint;
-+		priv->scan_max_time = maxt;
++	if (!skb) {
++		skb = dev_alloc_skb(sizeof(struct at76_rx_buffer));
++		if (!skb) {
++			printk(KERN_ERR "%s: cannot allocate rx skbuff\n",
++			       priv->netdev->name);
++			ret = -ENOMEM;
++			goto exit;
++		}
++		priv->rx_skb = skb;
++	} else {
++		skb_push(skb, skb_headroom(skb));
++		skb_trim(skb, 0);
 +	}
 +
-+	return ret;
-+}
++	size = skb_tailroom(skb);
++	usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe,
++			  skb_put(skb, size), size, at76_rx_callback, priv);
++	ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC);
++	if (ret < 0) {
++		if (ret == -ENODEV)
++			at76_dbg(DBG_DEVSTART,
++				 "usb_submit_urb returned -ENODEV");
++		else
++			printk(KERN_ERR "%s: rx, usb_submit_urb failed: %d\n",
++			       priv->netdev->name, ret);
++	}
 +
-+static int at76_iw_get_scan_times(struct net_device *netdev,
-+				  struct iw_request_info *info,
-+				  union iwreq_data *wrqu, char *extra)
-+{
-+	struct at76_priv *priv = netdev_priv(netdev);
-+	int *param = (int *)extra;
++exit:
++	if (ret < 0 && ret != -ENODEV)
++		printk(KERN_ERR "%s: cannot submit rx urb - please unload the "
++		       "driver and/or power cycle the device\n",
++		       priv->netdev->name);
 +
-+	param[0] = priv->scan_min_time;
-+	param[1] = priv->scan_max_time;
-+	return 0;
++	return ret;
 +}
 +
-+static int at76_iw_set_scan_mode(struct net_device *netdev,
-+				 struct iw_request_info *info, char *name,
-+				 char *extra)
++static int at76_open(struct net_device *netdev)
 +{
 +	struct at76_priv *priv = netdev_priv(netdev);
-+	int val = *((int *)name);
-+	int ret = -EIWCOMMIT;
-+
-+	at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_MODE - mode %s",
-+		 netdev->name, (val = SCAN_TYPE_ACTIVE) ? "active" :
-+		 (val = SCAN_TYPE_PASSIVE) ? "passive" : "<invalid>");
-+
-+	if (val != SCAN_TYPE_ACTIVE && val != SCAN_TYPE_PASSIVE)
-+		ret = -EINVAL;
-+	else
-+		priv->scan_mode = val;
++	int ret = 0;
 +
-+	return ret;
-+}
++	at76_dbg(DBG_PROC_ENTRY, "%s(): entry", __func__);
 +
-+static int at76_iw_get_scan_mode(struct net_device *netdev,
-+				 struct iw_request_info *info,
-+				 union iwreq_data *wrqu, char *extra)
-+{
-+	struct at76_priv *priv = netdev_priv(netdev);
-+	int *param = (int *)extra;
++	if (mutex_lock_interruptible(&priv->mtx))
++		return -EINTR;
 +
-+	param[0] = priv->scan_mode;
-+	return 0;
-+}
++	/* if netdev->dev_addr != priv->mac_addr we must
++	   set the mac address in the device ! */
++	if (compare_ether_addr(netdev->dev_addr, priv->mac_addr)) {
++		if (at76_add_mac_address(priv, netdev->dev_addr) >= 0)
++			at76_dbg(DBG_PROGRESS, "%s: set new MAC addr %s",
++				 netdev->name, mac2str(netdev->dev_addr));
++	}
 +
-+#define AT76_SET_HANDLER(h, f) [h - SIOCIWFIRST] = (iw_handler) f
++	priv->scan_state = SCAN_IDLE;
++	priv->last_scan = jiffies;
 +
-+/* Standard wireless handlers */
-+static const iw_handler at76_handlers[] = {
-+	AT76_SET_HANDLER(SIOCSIWCOMMIT, at76_iw_handler_commit),
-+	AT76_SET_HANDLER(SIOCGIWNAME, at76_iw_handler_get_name),
-+	AT76_SET_HANDLER(SIOCSIWFREQ, at76_iw_handler_set_freq),
-+	AT76_SET_HANDLER(SIOCGIWFREQ, at76_iw_handler_get_freq),
-+	AT76_SET_HANDLER(SIOCSIWMODE, at76_iw_handler_set_mode),
-+	AT76_SET_HANDLER(SIOCGIWMODE, at76_iw_handler_get_mode),
-+	AT76_SET_HANDLER(SIOCGIWRANGE, at76_iw_handler_get_range),
-+	AT76_SET_HANDLER(SIOCSIWSPY, at76_iw_handler_set_spy),
-+	AT76_SET_HANDLER(SIOCGIWSPY, at76_iw_handler_get_spy),
-+	AT76_SET_HANDLER(SIOCSIWTHRSPY, at76_iw_handler_set_thrspy),
-+	AT76_SET_HANDLER(SIOCGIWTHRSPY, at76_iw_handler_get_thrspy),
-+	AT76_SET_HANDLER(SIOCSIWAP, at76_iw_handler_set_wap),
-+	AT76_SET_HANDLER(SIOCGIWAP, at76_iw_handler_get_wap),
-+	AT76_SET_HANDLER(SIOCSIWSCAN, at76_iw_handler_set_scan),
-+	AT76_SET_HANDLER(SIOCGIWSCAN, at76_iw_handler_get_scan),
-+	AT76_SET_HANDLER(SIOCSIWESSID, at76_iw_handler_set_essid),
-+	AT76_SET_HANDLER(SIOCGIWESSID, at76_iw_handler_get_essid),
-+	AT76_SET_HANDLER(SIOCSIWRATE, at76_iw_handler_set_rate),
-+	AT76_SET_HANDLER(SIOCGIWRATE, at76_iw_handler_get_rate),
-+	AT76_SET_HANDLER(SIOCSIWRTS, at76_iw_handler_set_rts),
-+	AT76_SET_HANDLER(SIOCGIWRTS, at76_iw_handler_get_rts),
-+	AT76_SET_HANDLER(SIOCSIWFRAG, at76_iw_handler_set_frag),
-+	AT76_SET_HANDLER(SIOCGIWFRAG, at76_iw_handler_get_frag),
-+	AT76_SET_HANDLER(SIOCGIWTXPOW, at76_iw_handler_get_txpow),
-+	AT76_SET_HANDLER(SIOCSIWRETRY, at76_iw_handler_set_retry),
-+	AT76_SET_HANDLER(SIOCGIWRETRY, at76_iw_handler_get_retry),
-+	AT76_SET_HANDLER(SIOCSIWENCODE, at76_iw_handler_set_encode),
-+	AT76_SET_HANDLER(SIOCGIWENCODE, at76_iw_handler_get_encode),
-+	AT76_SET_HANDLER(SIOCSIWPOWER, at76_iw_handler_set_power),
-+	AT76_SET_HANDLER(SIOCGIWPOWER, at76_iw_handler_get_power)
-+};
++	ret = at76_submit_rx_urb(priv);
++	if (ret < 0) {
++		printk(KERN_ERR "%s: open: submit_rx_urb failed: %d\n",
++		       netdev->name, ret);
++		goto error;
++	}
 +
-+#define AT76_SET_PRIV(h, f) [h - SIOCIWFIRSTPRIV] = (iw_handler) f
++	schedule_delayed_work(&priv->dwork_restart, 0);
 +
-+/* Private wireless handlers */
-+static const iw_handler at76_priv_handlers[] = {
-+	AT76_SET_PRIV(AT76_SET_SHORT_PREAMBLE, at76_iw_set_short_preamble),
-+	AT76_SET_PRIV(AT76_GET_SHORT_PREAMBLE, at76_iw_get_short_preamble),
-+	AT76_SET_PRIV(AT76_SET_DEBUG, at76_iw_set_debug),
-+	AT76_SET_PRIV(AT76_GET_DEBUG, at76_iw_get_debug),
-+	AT76_SET_PRIV(AT76_SET_POWERSAVE_MODE, at76_iw_set_powersave_mode),
-+	AT76_SET_PRIV(AT76_GET_POWERSAVE_MODE, at76_iw_get_powersave_mode),
-+	AT76_SET_PRIV(AT76_SET_SCAN_TIMES, at76_iw_set_scan_times),
-+	AT76_SET_PRIV(AT76_GET_SCAN_TIMES, at76_iw_get_scan_times),
-+	AT76_SET_PRIV(AT76_SET_SCAN_MODE, at76_iw_set_scan_mode),
-+	AT76_SET_PRIV(AT76_GET_SCAN_MODE, at76_iw_get_scan_mode),
-+};
++	at76_dbg(DBG_PROC_ENTRY, "%s(): end", __func__);
++error:
++	mutex_unlock(&priv->mtx);
++	return ret < 0 ? ret : 0;
++}
 +
-+/* Names and arguments of private wireless handlers */
-+static const struct iw_priv_args at76_priv_args[] = {
-+	/* 0 - long, 1 - short */
-+	{AT76_SET_SHORT_PREAMBLE,
-+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"},
++static int at76_stop(struct net_device *netdev)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
 +
-+	{AT76_GET_SHORT_PREAMBLE,
-+	 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 10, "get_preamble"},
++	at76_dbg(DBG_DEVSTART, "%s: ENTER", __func__);
 +
-+	/* we must pass the new debug mask as a string, because iwpriv cannot
-+	 * parse hex numbers starting with 0x :-(  */
-+	{AT76_SET_DEBUG,
-+	 IW_PRIV_TYPE_CHAR | 10, 0, "set_debug"},
++	if (mutex_lock_interruptible(&priv->mtx))
++		return -EINTR;
 +
-+	{AT76_GET_DEBUG,
-+	 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 10, "get_debug"},
++	at76_quiesce(priv);
 +
-+	/* 1 - active, 2 - power save, 3 - smart power save */
-+	{AT76_SET_POWERSAVE_MODE,
-+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_powersave"},
++	if (!priv->device_unplugged) {
++		/* We are called by "ifconfig ethX down", not because the
++		 * device is not available anymore. */
++		at76_set_radio(priv, 0);
 +
-+	{AT76_GET_POWERSAVE_MODE,
-+	 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_powersave"},
++		/* We unlink rx_urb because at76_open() re-submits it.
++		 * If unplugged, at76_delete_device() takes care of it. */
++		usb_kill_urb(priv->rx_urb);
++	}
 +
-+	/* min_channel_time, max_channel_time */
-+	{AT76_SET_SCAN_TIMES,
-+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_scan_times"},
++	/* free the bss_list */
++	at76_free_bss_list(priv);
 +
-+	{AT76_GET_SCAN_TIMES,
-+	 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, "get_scan_times"},
++	mutex_unlock(&priv->mtx);
++	at76_dbg(DBG_DEVSTART, "%s: EXIT", __func__);
 +
-+	/* 0 - active, 1 - passive scan */
-+	{AT76_SET_SCAN_MODE,
-+	 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_scan_mode"},
++	return 0;
++}
 +
-+	{AT76_GET_SCAN_MODE,
-+	 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scan_mode"},
-+};
++static void at76_ethtool_get_drvinfo(struct net_device *netdev,
++				     struct ethtool_drvinfo *info)
++{
++	struct at76_priv *priv = netdev_priv(netdev);
 +
-+static const struct iw_handler_def at76_handler_def = {
-+	.num_standard = ARRAY_SIZE(at76_handlers),
-+	.num_private = ARRAY_SIZE(at76_priv_handlers),
-+	.num_private_args = ARRAY_SIZE(at76_priv_args),
-+	.standard = at76_handlers,
-+	.private = at76_priv_handlers,
-+	.private_args = at76_priv_args,
-+	.get_wireless_stats = at76_get_wireless_stats,
-+};
++	strncpy(info->driver, DRIVER_NAME, sizeof(info->driver));
++	strncpy(info->version, DRIVER_VERSION, sizeof(info->version));
 +
-+static const u8 snapsig[] = { 0xaa, 0xaa, 0x03 };
++	usb_make_path(priv->udev, info->bus_info, sizeof(info->bus_info));
 +
-+/* RFC 1042 encapsulates Ethernet frames in 802.2 SNAP (0xaa, 0xaa, 0x03) with
-+ * a SNAP OID of 0 (0x00, 0x00, 0x00) */
-+static const u8 rfc1042sig[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
++	snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d-%d",
++		 priv->fw_version.major, priv->fw_version.minor,
++		 priv->fw_version.patch, priv->fw_version.build);
++}
 +
-+static int at76_tx(struct sk_buff *skb, struct net_device *netdev)
++static u32 at76_ethtool_get_link(struct net_device *netdev)
 +{
 +	struct at76_priv *priv = netdev_priv(netdev);
-+	struct net_device_stats *stats = &priv->stats;
-+	int ret = 0;
-+	int wlen;
-+	int submit_len;
-+	struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer;
-+	struct ieee80211_hdr_3addr *i802_11_hdr =
-+	    (struct ieee80211_hdr_3addr *)tx_buffer->packet;
-+	u8 *payload = i802_11_hdr->payload;
-+	struct ethhdr *eh = (struct ethhdr *)skb->data;
++	return priv->mac_state == MAC_CONNECTED;
++}
 +
-+	if (netif_queue_stopped(netdev)) {
-+		printk(KERN_ERR "%s: %s called while netdev is stopped\n",
-+		       netdev->name, __func__);
-+		/* skip this packet */
-+		dev_kfree_skb(skb);
-+		return 0;
-+	}
++static struct ethtool_ops at76_ethtool_ops = {
++	.get_drvinfo = at76_ethtool_get_drvinfo,
++	.get_link = at76_ethtool_get_link,
++};
 +
-+	if (priv->tx_urb->status == -EINPROGRESS) {
-+		printk(KERN_ERR "%s: %s called while tx urb is pending\n",
-+		       netdev->name, __func__);
-+		/* skip this packet */
-+		dev_kfree_skb(skb);
-+		return 0;
++/* Download external firmware */
++static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe)
++{
++	int ret;
++	int op_mode;
++	int blockno = 0;
++	int bsize;
++	u8 *block;
++	u8 *buf = fwe->extfw;
++	int size = fwe->extfw_size;
++
++	if (!buf || !size)
++		return -ENOENT;
++
++	op_mode = at76_get_op_mode(udev);
++	at76_dbg(DBG_DEVSTART, "opmode %d", op_mode);
++
++	if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) {
++		dev_printk(KERN_ERR, &udev->dev, "unexpected opmode %d\n",
++			   op_mode);
++		return -EINVAL;
 +	}
 +
-+	if (skb->len < ETH_HLEN) {
-+		printk(KERN_ERR "%s: %s: skb too short (%d)\n",
-+		       netdev->name, __func__, skb->len);
-+		dev_kfree_skb(skb);
-+		return 0;
++	block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL);
++	if (!block)
++		return -ENOMEM;
++
++	at76_dbg(DBG_DEVSTART, "downloading external firmware");
++
++	/* for fw >= 0.100, the device needs an extra empty block */
++	do {
++		bsize = min_t(int, size, FW_BLOCK_SIZE);
++		memcpy(block, buf, bsize);
++		at76_dbg(DBG_DEVSTART,
++			 "ext fw, size left = %5d, bsize = %4d, blockno = %2d",
++			 size, bsize, blockno);
++		ret = at76_load_ext_fw_block(udev, blockno, block, bsize);
++		if (ret != bsize) {
++			dev_printk(KERN_ERR, &udev->dev,
++				   "loading %dth firmware block failed: %d\n",
++				   blockno, ret);
++			goto exit;
++		}
++		buf += bsize;
++		size -= bsize;
++		blockno++;
++	} while (bsize > 0);
++
++	if (at76_is_505a(fwe->board_type)) {
++		at76_dbg(DBG_DEVSTART, "200 ms delay for 505a");
++		schedule_timeout_interruptible(HZ / 5 + 1);
 +	}
 +
-+	at76_ledtrig_tx_activity();	/* tell ledtrigger we send a packet */
++exit:
++	kfree(block);
++	if (ret < 0)
++		dev_printk(KERN_ERR, &udev->dev,
++			   "downloading external firmware failed: %d\n", ret);
++	return ret;
++}
 +
-+	/* we can get rid of memcpy if we set netdev->hard_header_len to
-+	   reserve enough space, but we would need to keep the skb around */
++/* Download internal firmware */
++static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe)
++{
++	int ret;
++	int need_remap = !at76_is_505a(fwe->board_type);
 +
-+	if (ntohs(eh->h_proto) <= ETH_DATA_LEN) {
-+		/* this is a 802.3 packet */
-+		if (skb->len >= ETH_HLEN + sizeof(rfc1042sig)
-+		    && skb->data[ETH_HLEN] == rfc1042sig[0]
-+		    && skb->data[ETH_HLEN + 1] == rfc1042sig[1]) {
-+			/* higher layer delivered SNAP header - keep it */
-+			memcpy(payload, skb->data + ETH_HLEN,
-+			       skb->len - ETH_HLEN);
-+			wlen = IEEE80211_3ADDR_LEN + skb->len - ETH_HLEN;
-+		} else {
-+			printk(KERN_ERR "%s: dropping non-SNAP 802.2 packet "
-+			       "(DSAP 0x%02x SSAP 0x%02x cntrl 0x%02x)\n",
-+			       priv->netdev->name, skb->data[ETH_HLEN],
-+			       skb->data[ETH_HLEN + 1],
-+			       skb->data[ETH_HLEN + 2]);
-+			dev_kfree_skb(skb);
-+			return 0;
++	ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size,
++				   need_remap ? 0 : 2 * HZ);
++
++	if (ret < 0) {
++		dev_printk(KERN_ERR, &udev->dev,
++			   "downloading internal fw failed with %d\n", ret);
++		goto exit;
++	}
++
++	at76_dbg(DBG_DEVSTART, "sending REMAP");
++
++	/* no REMAP for 505A (see SF driver) */
++	if (need_remap) {
++		ret = at76_remap(udev);
++		if (ret < 0) {
++			dev_printk(KERN_ERR, &udev->dev,
++				   "sending REMAP failed with %d\n", ret);
++			goto exit;
 +		}
-+	} else {
-+		/* add RFC 1042 header in front */
-+		memcpy(payload, rfc1042sig, sizeof(rfc1042sig));
-+		memcpy(payload + sizeof(rfc1042sig), &eh->h_proto,
-+		       skb->len - offsetof(struct ethhdr, h_proto));
-+		wlen = IEEE80211_3ADDR_LEN + sizeof(rfc1042sig) + skb->len -
-+		    offsetof(struct ethhdr, h_proto);
 +	}
 +
-+	/* make wireless header */
-+	i802_11_hdr->frame_ctl =
-+	    cpu_to_le16(IEEE80211_FTYPE_DATA |
-+			(priv->wep_enabled ? IEEE80211_FCTL_PROTECTED : 0) |
-+			(priv->iw_mode ==
-+			 IW_MODE_INFRA ? IEEE80211_FCTL_TODS : 0));
++	at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds");
++	schedule_timeout_interruptible(2 * HZ + 1);
++	usb_reset_device(udev);
++
++exit:
++	return ret;
++}
++
++static int at76_match_essid(struct at76_priv *priv, struct bss_info *ptr)
++{
++	/* common criteria for both modi */
 +
-+	if (priv->iw_mode == IW_MODE_ADHOC) {
-+		memcpy(i802_11_hdr->addr1, eh->h_dest, ETH_ALEN);
-+		memcpy(i802_11_hdr->addr2, eh->h_source, ETH_ALEN);
-+		memcpy(i802_11_hdr->addr3, priv->bssid, ETH_ALEN);
-+	} else if (priv->iw_mode == IW_MODE_INFRA) {
-+		memcpy(i802_11_hdr->addr1, priv->bssid, ETH_ALEN);
-+		memcpy(i802_11_hdr->addr2, eh->h_source, ETH_ALEN);
-+		memcpy(i802_11_hdr->addr3, eh->h_dest, ETH_ALEN);
-+	}
++	int ret = (priv->essid_size == 0 /* ANY ssid */  ||
++		   (priv->essid_size == ptr->ssid_len &&
++		    !memcmp(priv->essid, ptr->ssid, ptr->ssid_len)));
++	if (!ret)
++		at76_dbg(DBG_BSS_MATCH,
++			 "%s bss table entry %p: essid didn't match",
++			 priv->netdev->name, ptr);
++	return ret;
++}
 +
-+	i802_11_hdr->duration_id = cpu_to_le16(0);
-+	i802_11_hdr->seq_ctl = cpu_to_le16(0);
++static inline int at76_match_mode(struct at76_priv *priv, struct bss_info *ptr)
++{
++	int ret;
 +
-+	/* setup 'Atmel' header */
-+	tx_buffer->wlength = cpu_to_le16(wlen);
-+	tx_buffer->tx_rate = priv->txrate;
-+	/* for broadcast destination addresses, the firmware 0.100.x
-+	   seems to choose the highest rate set with CMD_STARTUP in
-+	   basic_rate_set replacing this value */
++	if (priv->iw_mode == IW_MODE_ADHOC)
++		ret = ptr->capa & WLAN_CAPABILITY_IBSS;
++	else
++		ret = ptr->capa & WLAN_CAPABILITY_ESS;
++	if (!ret)
++		at76_dbg(DBG_BSS_MATCH,
++			 "%s bss table entry %p: mode didn't match",
++			 priv->netdev->name, ptr);
++	return ret;
++}
 +
-+	memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved));
++static int at76_match_rates(struct at76_priv *priv, struct bss_info *ptr)
++{
++	int i;
 +
-+	tx_buffer->padding = at76_calc_padding(wlen);
-+	submit_len = wlen + AT76_TX_HDRLEN + tx_buffer->padding;
++	for (i = 0; i < ptr->rates_len; i++) {
++		u8 rate = ptr->rates[i];
 +
-+	at76_dbg(DBG_TX_DATA_CONTENT, "%s skb->data %s", priv->netdev->name,
-+		 hex2str(skb->data, 32));
-+	at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr %s",
-+		 priv->netdev->name,
-+		 le16_to_cpu(tx_buffer->wlength),
-+		 tx_buffer->padding, tx_buffer->tx_rate,
-+		 hex2str(i802_11_hdr, sizeof(*i802_11_hdr)));
-+	at76_dbg(DBG_TX_DATA_CONTENT, "%s payload %s", priv->netdev->name,
-+		 hex2str(payload, 48));
++		if (!(rate & 0x80))
++			continue;
 +
-+	/* send stuff */
-+	netif_stop_queue(netdev);
-+	netdev->trans_start = jiffies;
++		/* this is a basic rate we have to support
++		   (see IEEE802.11, ch. 7.3.2.2) */
++		if (rate != (0x80 | hw_rates[0])
++		    && rate != (0x80 | hw_rates[1])
++		    && rate != (0x80 | hw_rates[2])
++		    && rate != (0x80 | hw_rates[3])) {
++			at76_dbg(DBG_BSS_MATCH,
++				 "%s: bss table entry %p: basic rate %02x not "
++				 "supported", priv->netdev->name, ptr, rate);
++			return 0;
++		}
++	}
 +
-+	usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer,
-+			  submit_len, at76_tx_callback, priv);
-+	ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC);
-+	if (ret) {
-+		stats->tx_errors++;
-+		printk(KERN_ERR "%s: error in tx submit urb: %d\n",
-+		       netdev->name, ret);
-+		if (ret == -EINVAL)
-+			printk(KERN_ERR
-+			       "%s: -EINVAL: tx urb %p hcpriv %p complete %p\n",
-+			       priv->netdev->name, priv->tx_urb,
-+			       priv->tx_urb->hcpriv, priv->tx_urb->complete);
-+	} else {
-+		stats->tx_bytes += skb->len;
-+		dev_kfree_skb(skb);
++	/* if we use short preamble, the bss must support it */
++	if (priv->preamble_type == PREAMBLE_TYPE_SHORT &&
++	    !(ptr->capa & WLAN_CAPABILITY_SHORT_PREAMBLE)) {
++		at76_dbg(DBG_BSS_MATCH,
++			 "%s: %p does not support short preamble",
++			 priv->netdev->name, ptr);
++		return 0;
++	} else
++		return 1;
++}
++
++static inline int at76_match_wep(struct at76_priv *priv, struct bss_info *ptr)
++{
++	if (!priv->wep_enabled && ptr->capa & WLAN_CAPABILITY_PRIVACY) {
++		/* we have disabled WEP, but the BSS signals privacy */
++		at76_dbg(DBG_BSS_MATCH,
++			 "%s: bss table entry %p: requires encryption",
++			 priv->netdev->name, ptr);
++		return 0;
 +	}
++	/* otherwise if the BSS does not signal privacy it may well
++	   accept encrypted packets from us ... */
++	return 1;
++}
 +
-+	return ret;
++static inline int at76_match_bssid(struct at76_priv *priv, struct bss_info *ptr)
++{
++	if (!priv->wanted_bssid_valid ||
++	    !compare_ether_addr(ptr->bssid, priv->wanted_bssid))
++		return 1;
++
++	at76_dbg(DBG_BSS_MATCH,
++		 "%s: requested bssid - %s does not match",
++		 priv->netdev->name, mac2str(priv->wanted_bssid));
++	at76_dbg(DBG_BSS_MATCH,
++		 "      AP bssid - %s of bss table entry %p",
++		 mac2str(ptr->bssid), ptr);
++	return 0;
 +}
 +
-+static void at76_tx_timeout(struct net_device *netdev)
++/**
++ * at76_match_bss - try to find a matching bss in priv->bss
++ *
++ * last - last bss tried
++ *
++ * last == NULL signals a new round starting with priv->bss_list.next
++ * this function must be called inside an acquired priv->bss_list_spinlock
++ * otherwise the timeout on bss may remove the newly chosen entry
++ */
++static struct bss_info *at76_match_bss(struct at76_priv *priv,
++				       struct bss_info *last)
 +{
-+	struct at76_priv *priv = netdev_priv(netdev);
++	struct bss_info *ptr = NULL;
++	struct list_head *curr;
 +
-+	if (!priv)
-+		return;
-+	warn("%s: tx timeout.", netdev->name);
++	curr = last ? last->list.next : priv->bss_list.next;
++	while (curr != &priv->bss_list) {
++		ptr = list_entry(curr, struct bss_info, list);
++		if (at76_match_essid(priv, ptr) && at76_match_mode(priv, ptr)
++		    && at76_match_wep(priv, ptr) && at76_match_rates(priv, ptr)
++		    && at76_match_bssid(priv, ptr))
++			break;
++		curr = curr->next;
++	}
 +
-+	usb_unlink_urb(priv->tx_urb);
-+	priv->stats.tx_errors++;
++	if (curr == &priv->bss_list)
++		ptr = NULL;
++	/* otherwise ptr points to the struct bss_info we have chosen */
++
++	at76_dbg(DBG_BSS_TABLE, "%s %s: returned %p", priv->netdev->name,
++		 __func__, ptr);
++	return ptr;
 +}
 +
-+static int at76_submit_rx_urb(struct at76_priv *priv)
++/* Start joining a matching BSS, or create own IBSS */
++static void at76_work_join(struct work_struct *work)
 +{
++	struct at76_priv *priv = container_of(work, struct at76_priv,
++					      work_join);
 +	int ret;
-+	int size;
-+	struct sk_buff *skb = priv->rx_skb;
++	unsigned long flags;
 +
-+	if (!priv->rx_urb) {
-+		printk(KERN_ERR "%s: %s: priv->rx_urb is NULL\n",
-+		       priv->netdev->name, __func__);
-+		return -EFAULT;
-+	}
++	mutex_lock(&priv->mtx);
 +
-+	if (!skb) {
-+		skb = dev_alloc_skb(sizeof(struct at76_rx_buffer));
-+		if (!skb) {
-+			printk(KERN_ERR "%s: cannot allocate rx skbuff\n",
-+			       priv->netdev->name);
-+			ret = -ENOMEM;
++	WARN_ON(priv->mac_state != MAC_JOINING);
++	if (priv->mac_state != MAC_JOINING)
++		goto exit;
++
++	/* secure the access to priv->curr_bss ! */
++	spin_lock_irqsave(&priv->bss_list_spinlock, flags);
++	priv->curr_bss = at76_match_bss(priv, priv->curr_bss);
++	spin_unlock_irqrestore(&priv->bss_list_spinlock, flags);
++
++	if (!priv->curr_bss) {
++		/* here we haven't found a matching (i)bss ... */
++		if (priv->iw_mode == IW_MODE_ADHOC) {
++			at76_set_mac_state(priv, MAC_OWN_IBSS);
++			at76_start_ibss(priv);
 +			goto exit;
 +		}
-+		priv->rx_skb = skb;
-+	} else {
-+		skb_push(skb, skb_headroom(skb));
-+		skb_trim(skb, 0);
++		/* haven't found a matching BSS in infra mode - try again */
++		at76_set_mac_state(priv, MAC_SCANNING);
++		schedule_work(&priv->work_start_scan);
++		goto exit;
 +	}
 +
-+	size = skb_tailroom(skb);
-+	usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe,
-+			  skb_put(skb, size), size, at76_rx_callback, priv);
-+	ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC);
++	ret = at76_join_bss(priv, priv->curr_bss);
 +	if (ret < 0) {
-+		if (ret == -ENODEV)
-+			at76_dbg(DBG_DEVSTART,
-+				 "usb_submit_urb returned -ENODEV");
-+		else
-+			printk(KERN_ERR "%s: rx, usb_submit_urb failed: %d\n",
++		printk(KERN_ERR "%s: join_bss failed with %d\n",
++		       priv->netdev->name, ret);
++		goto exit;
++	}
++
++	ret = at76_wait_completion(priv, CMD_JOIN);
++	if (ret != CMD_STATUS_COMPLETE) {
++		if (ret != CMD_STATUS_TIME_OUT)
++			printk(KERN_ERR "%s: join_bss completed with %d\n",
 +			       priv->netdev->name, ret);
++		else
++			printk(KERN_INFO "%s: join_bss ssid %s timed out\n",
++			       priv->netdev->name,
++			       mac2str(priv->curr_bss->bssid));
++
++		/* retry next BSS immediately */
++		schedule_work(&priv->work_join);
++		goto exit;
++	}
++
++	/* here we have joined the (I)BSS */
++	if (priv->iw_mode == IW_MODE_ADHOC) {
++		struct bss_info *bptr = priv->curr_bss;
++		at76_set_mac_state(priv, MAC_CONNECTED);
++		/* get ESSID, BSSID and channel for priv->curr_bss */
++		priv->essid_size = bptr->ssid_len;
++		memcpy(priv->essid, bptr->ssid, bptr->ssid_len);
++		memcpy(priv->bssid, bptr->bssid, ETH_ALEN);
++		priv->channel = bptr->channel;
++		at76_iwevent_bss_connect(priv->netdev, bptr->bssid);
++		netif_carrier_on(priv->netdev);
++		netif_start_queue(priv->netdev);
++		/* just to be sure */
++		cancel_delayed_work(&priv->dwork_get_scan);
++		cancel_delayed_work(&priv->dwork_auth);
++		cancel_delayed_work(&priv->dwork_assoc);
++	} else {
++		/* send auth req */
++		priv->retries = AUTH_RETRIES;
++		at76_set_mac_state(priv, MAC_AUTH);
++		at76_auth_req(priv, priv->curr_bss, 1, NULL);
++		at76_dbg(DBG_MGMT_TIMER,
++			 "%s:%d: starting mgmt_timer + HZ", __func__, __LINE__);
++		schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT);
 +	}
 +
 +exit:
-+	if (ret < 0 && ret != -ENODEV)
-+		printk(KERN_ERR "%s: cannot submit rx urb - please unload the "
-+		       "driver and/or power cycle the device\n",
-+		       priv->netdev->name);
-+
-+	return ret;
++	mutex_unlock(&priv->mtx);
 +}
 +
-+static int at76_open(struct net_device *netdev)
++/* Reap scan results */
++static void at76_dwork_get_scan(struct work_struct *work)
 +{
-+	struct at76_priv *priv = netdev_priv(netdev);
-+	int ret = 0;
-+
-+	at76_dbg(DBG_PROC_ENTRY, "%s(): entry", __func__);
++	int status;
++	int ret;
++	struct at76_priv *priv = container_of(work, struct at76_priv,
++					      dwork_get_scan.work);
 +
-+	if (mutex_lock_interruptible(&priv->mtx))
-+		return -EINTR;
++	mutex_lock(&priv->mtx);
++	WARN_ON(priv->mac_state != MAC_SCANNING);
++	if (priv->mac_state != MAC_SCANNING)
++		goto exit;
 +
-+	/* if netdev->dev_addr != priv->mac_addr we must
-+	   set the mac address in the device ! */
-+	if (compare_ether_addr(netdev->dev_addr, priv->mac_addr)) {
-+		if (at76_add_mac_address(priv, netdev->dev_addr) >= 0)
-+			at76_dbg(DBG_PROGRESS, "%s: set new MAC addr %s",
-+				 netdev->name, mac2str(netdev->dev_addr));
++	status = at76_get_cmd_status(priv->udev, CMD_SCAN);
++	if (status < 0) {
++		printk(KERN_ERR "%s: %s: at76_get_cmd_status failed with %d\n",
++		       priv->netdev->name, __func__, status);
++		status = CMD_STATUS_IN_PROGRESS;
++		/* INFO: Hope it was a one off error - if not, scanning
++		   further down the line and stop this cycle */
 +	}
++	at76_dbg(DBG_PROGRESS,
++		 "%s %s: got cmd_status %d (state %s, need_any %d)",
++		 priv->netdev->name, __func__, status,
++		 mac_states[priv->mac_state], priv->scan_need_any);
 +
-+	priv->scan_state = SCAN_IDLE;
-+	priv->last_scan = jiffies;
++	if (status != CMD_STATUS_COMPLETE) {
++		if ((status != CMD_STATUS_IN_PROGRESS) &&
++		    (status != CMD_STATUS_IDLE))
++			printk(KERN_ERR "%s: %s: Bad scan status: %s\n",
++			       priv->netdev->name, __func__,
++			       at76_get_cmd_status_string(status));
 +
-+	ret = at76_submit_rx_urb(priv);
-+	if (ret < 0) {
-+		printk(KERN_ERR "%s: open: submit_rx_urb failed: %d\n",
-+		       netdev->name, ret);
-+		goto error;
++		/* the first cmd status after scan start is always a IDLE ->
++		   start the timer to poll again until COMPLETED */
++		at76_dbg(DBG_MGMT_TIMER,
++			 "%s:%d: starting mgmt_timer for %d ticks",
++			 __func__, __LINE__, SCAN_POLL_INTERVAL);
++		schedule_delayed_work(&priv->dwork_get_scan,
++				      SCAN_POLL_INTERVAL);
++		goto exit;
 +	}
 +
-+	schedule_delayed_work(&priv->dwork_restart, 0);
++	if (at76_debug & DBG_BSS_TABLE)
++		at76_dump_bss_table(priv);
 +
-+	at76_dbg(DBG_PROC_ENTRY, "%s(): end", __func__);
-+error:
++	if (priv->scan_need_any) {
++		ret = at76_start_scan(priv, 0);
++		if (ret < 0)
++			printk(KERN_ERR
++			       "%s: %s: start_scan (ANY) failed with %d\n",
++			       priv->netdev->name, __func__, ret);
++		at76_dbg(DBG_MGMT_TIMER,
++			 "%s:%d: starting mgmt_timer for %d ticks", __func__,
++			 __LINE__, SCAN_POLL_INTERVAL);
++		schedule_delayed_work(&priv->dwork_get_scan,
++				      SCAN_POLL_INTERVAL);
++		priv->scan_need_any = 0;
++	} else {
++		priv->scan_state = SCAN_COMPLETED;
++		/* report the end of scan to user space */
++		at76_iwevent_scan_complete(priv->netdev);
++		at76_set_mac_state(priv, MAC_JOINING);
++		schedule_work(&priv->work_join);
++	}
++
++exit:
 +	mutex_unlock(&priv->mtx);
-+	return ret < 0 ? ret : 0;
 +}
 +
-+static int at76_stop(struct net_device *netdev)
++/* Handle loss of beacons from the AP */
++static void at76_dwork_beacon(struct work_struct *work)
 +{
-+	struct at76_priv *priv = netdev_priv(netdev);
-+
-+	at76_dbg(DBG_DEVSTART, "%s: ENTER", __func__);
-+
-+	if (mutex_lock_interruptible(&priv->mtx))
-+		return -EINTR;
-+
-+	at76_quiesce(priv);
++	struct at76_priv *priv = container_of(work, struct at76_priv,
++					      dwork_beacon.work);
 +
-+	if (!priv->device_unplugged) {
-+		/* We are called by "ifconfig ethX down", not because the
-+		 * device is not available anymore. */
-+		at76_set_radio(priv, 0);
++	mutex_lock(&priv->mtx);
++	if (priv->mac_state != MAC_CONNECTED || priv->iw_mode != IW_MODE_INFRA)
++		goto exit;
 +
-+		/* We unlink rx_urb because at76_open() re-submits it.
-+		 * If unplugged, at76_delete_device() takes care of it. */
-+		usb_kill_urb(priv->rx_urb);
-+	}
++	/* We haven't received any beacons from out AP for BEACON_TIMEOUT */
++	printk(KERN_INFO "%s: lost beacon bssid %s\n",
++	       priv->netdev->name, mac2str(priv->curr_bss->bssid));
 +
-+	/* free the bss_list */
-+	at76_free_bss_list(priv);
++	netif_carrier_off(priv->netdev);
++	netif_stop_queue(priv->netdev);
++	at76_iwevent_bss_disconnect(priv->netdev);
++	at76_set_mac_state(priv, MAC_SCANNING);
++	schedule_work(&priv->work_start_scan);
 +
++exit:
 +	mutex_unlock(&priv->mtx);
-+	at76_dbg(DBG_DEVSTART, "%s: EXIT", __func__);
-+
-+	return 0;
 +}
 +
-+static void at76_ethtool_get_drvinfo(struct net_device *netdev,
-+				     struct ethtool_drvinfo *info)
++/* Handle authentication response timeout */
++static void at76_dwork_auth(struct work_struct *work)
 +{
-+	struct at76_priv *priv = netdev_priv(netdev);
++	struct at76_priv *priv = container_of(work, struct at76_priv,
++					      dwork_auth.work);
 +
-+	strncpy(info->driver, DRIVER_NAME, sizeof(info->driver));
-+	strncpy(info->version, DRIVER_VERSION, sizeof(info->version));
++	mutex_lock(&priv->mtx);
++	WARN_ON(priv->mac_state != MAC_AUTH);
++	if (priv->mac_state != MAC_AUTH)
++		goto exit;
 +
-+	usb_make_path(priv->udev, info->bus_info, sizeof(info->bus_info));
++	at76_dbg(DBG_PROGRESS, "%s: authentication response timeout",
++		 priv->netdev->name);
 +
-+	snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d-%d",
-+		 priv->fw_version.major, priv->fw_version.minor,
-+		 priv->fw_version.patch, priv->fw_version.build);
-+}
++	if (priv->retries-- >= 0) {
++		at76_auth_req(priv, priv->curr_bss, 1, NULL);
++		at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ",
++			 __func__, __LINE__);
++		schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT);
++	} else {
++		/* try to get next matching BSS */
++		at76_set_mac_state(priv, MAC_JOINING);
++		schedule_work(&priv->work_join);
++	}
 +
-+static u32 at76_ethtool_get_link(struct net_device *netdev)
-+{
-+	struct at76_priv *priv = netdev_priv(netdev);
-+	return priv->mac_state == MAC_CONNECTED;
++exit:
++	mutex_unlock(&priv->mtx);
 +}
 +
-+static struct ethtool_ops at76_ethtool_ops = {
-+	.get_drvinfo = at76_ethtool_get_drvinfo,
-+	.get_link = at76_ethtool_get_link,
-+};
-+
-+/* Download external firmware */
-+static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe)
++/* Handle association response timeout */
++static void at76_dwork_assoc(struct work_struct *work)
 +{
-+	int ret;
-+	int op_mode;
-+	int blockno = 0;
-+	int bsize;
-+	u8 *block;
-+	u8 *buf = fwe->extfw;
-+	int size = fwe->extfw_size;
-+
-+	if (!buf || !size)
-+		return -ENOENT;
-+
-+	op_mode = at76_get_op_mode(udev);
-+	at76_dbg(DBG_DEVSTART, "opmode %d", op_mode);
-+
-+	if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) {
-+		dev_printk(KERN_ERR, &udev->dev, "unexpected opmode %d\n",
-+			   op_mode);
-+		return -EINVAL;
-+	}
-+
-+	block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL);
-+	if (!block)
-+		return -ENOMEM;
++	struct at76_priv *priv = container_of(work, struct at76_priv,
++					      dwork_assoc.work);
 +
-+	at76_dbg(DBG_DEVSTART, "downloading external firmware");
++	mutex_lock(&priv->mtx);
++	WARN_ON(priv->mac_state != MAC_ASSOC);
++	if (priv->mac_state != MAC_ASSOC)
++		goto exit;
 +
-+	/* for fw >= 0.100, the device needs an extra empty block */
-+	do {
-+		bsize = min_t(int, size, FW_BLOCK_SIZE);
-+		memcpy(block, buf, bsize);
-+		at76_dbg(DBG_DEVSTART,
-+			 "ext fw, size left = %5d, bsize = %4d, blockno = %2d",
-+			 size, bsize, blockno);
-+		ret = at76_load_ext_fw_block(udev, blockno, block, bsize);
-+		if (ret != bsize) {
-+			dev_printk(KERN_ERR, &udev->dev,
-+				   "loading %dth firmware block failed: %d\n",
-+				   blockno, ret);
-+			goto exit;
-+		}
-+		buf += bsize;
-+		size -= bsize;
-+		blockno++;
-+	} while (bsize > 0);
++	at76_dbg(DBG_PROGRESS, "%s: association response timeout",
++		 priv->netdev->name);
 +
-+	if (at76_is_505a(fwe->board_type)) {
-+		at76_dbg(DBG_DEVSTART, "200 ms delay for 505a");
-+		schedule_timeout_interruptible(HZ / 5 + 1);
++	if (priv->retries-- >= 0) {
++		at76_assoc_req(priv, priv->curr_bss);
++		at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ",
++			 __func__, __LINE__);
++		schedule_delayed_work(&priv->dwork_assoc, ASSOC_TIMEOUT);
++	} else {
++		/* try to get next matching BSS */
++		at76_set_mac_state(priv, MAC_JOINING);
++		schedule_work(&priv->work_join);
 +	}
 +
 +exit:
-+	kfree(block);
-+	if (ret < 0)
-+		dev_printk(KERN_ERR, &udev->dev,
-+			   "downloading external firmware failed: %d\n", ret);
-+	return ret;
++	mutex_unlock(&priv->mtx);
 +}
 +
-+/* Download internal firmware */
-+static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe)
++/* Read new bssid in ad-hoc mode */
++static void at76_work_new_bss(struct work_struct *work)
 +{
++	struct at76_priv *priv = container_of(work, struct at76_priv,
++					      work_new_bss);
 +	int ret;
-+	int need_remap = !at76_is_505a(fwe->board_type);
++	struct mib_mac_mgmt mac_mgmt;
 +
-+	ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size,
-+				   need_remap ? 0 : 2 * HZ);
++	mutex_lock(&priv->mtx);
 +
++	ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, &mac_mgmt,
++			   sizeof(struct mib_mac_mgmt));
 +	if (ret < 0) {
-+		dev_printk(KERN_ERR, &udev->dev,
-+			   "downloading internal fw failed with %d\n", ret);
++		printk(KERN_ERR "%s: at76_get_mib failed: %d\n",
++		       priv->netdev->name, ret);
 +		goto exit;
 +	}
 +
-+	at76_dbg(DBG_DEVSTART, "sending REMAP");
-+
-+	/* no REMAP for 505A (see SF driver) */
-+	if (need_remap) {
-+		ret = at76_remap(udev);
-+		if (ret < 0) {
-+			dev_printk(KERN_ERR, &udev->dev,
-+				   "sending REMAP failed with %d\n", ret);
-+			goto exit;
-+		}
-+	}
++	at76_dbg(DBG_PROGRESS, "ibss_change = 0x%2x", mac_mgmt.ibss_change);
++	memcpy(priv->bssid, mac_mgmt.current_bssid, ETH_ALEN);
++	at76_dbg(DBG_PROGRESS, "using BSSID %s", mac2str(priv->bssid));
 +
-+	at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds");
-+	schedule_timeout_interruptible(2 * HZ + 1);
-+	usb_reset_device(udev);
++	at76_iwevent_bss_connect(priv->netdev, priv->bssid);
 +
-+exit:
-+	return ret;
-+}
++	priv->mib_buf.type = MIB_MAC_MGMT;
++	priv->mib_buf.size = 1;
++	priv->mib_buf.index = offsetof(struct mib_mac_mgmt, ibss_change);
++	priv->mib_buf.data.byte = 0;
 +
-+static int at76_match_essid(struct at76_priv *priv, struct bss_info *ptr)
-+{
-+	/* common criteria for both modi */
++	ret = at76_set_mib(priv, &priv->mib_buf);
++	if (ret < 0)
++		printk(KERN_ERR "%s: set_mib (ibss change ok) failed: %d\n",
++		       priv->netdev->name, ret);
 +
-+	int ret = (priv->essid_size == 0 /* ANY ssid */  ||
-+		   (priv->essid_size == ptr->ssid_len &&
-+		    !memcmp(priv->essid, ptr->ssid, ptr->ssid_len)));
-+	if (!ret)
-+		at76_dbg(DBG_BSS_MATCH,
-+			 "%s bss table entry %p: essid didn't match",
-+			 priv->netdev->name, ptr);
-+	return ret;
++exit:
++	mutex_unlock(&priv->mtx);
 +}
 +
-+static inline int at76_match_mode(struct at76_priv *priv, struct bss_info *ptr)
++static int at76_startup_device(struct at76_priv *priv)
 +{
++	struct at76_card_config *ccfg = &priv->card_config;
 +	int ret;
 +
-+	if (priv->iw_mode == IW_MODE_ADHOC)
-+		ret = ptr->capa & WLAN_CAPABILITY_IBSS;
-+	else
-+		ret = ptr->capa & WLAN_CAPABILITY_ESS;
-+	if (!ret)
-+		at76_dbg(DBG_BSS_MATCH,
-+			 "%s bss table entry %p: mode didn't match",
-+			 priv->netdev->name, ptr);
-+	return ret;
-+}
-+
-+static int at76_match_rates(struct at76_priv *priv, struct bss_info *ptr)
-+{
-+	int i;
++	at76_dbg(DBG_PARAMS,
++		 "%s param: ssid %.*s (%s) mode %s ch %d wep %s key %d "
++		 "keylen %d", priv->netdev->name, priv->essid_size, priv->essid,
++		 hex2str(priv->essid, IW_ESSID_MAX_SIZE),
++		 priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra",
++		 priv->channel, priv->wep_enabled ? "enabled" : "disabled",
++		 priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]);
++	at76_dbg(DBG_PARAMS,
++		 "%s param: preamble %s rts %d retry %d frag %d "
++		 "txrate %s auth_mode %d", priv->netdev->name,
++		 preambles[priv->preamble_type], priv->rts_threshold,
++		 priv->short_retry_limit, priv->frag_threshold,
++		 priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate ==
++		 TX_RATE_2MBIT ? "2MBit" : priv->txrate ==
++		 TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate ==
++		 TX_RATE_11MBIT ? "11MBit" : priv->txrate ==
++		 TX_RATE_AUTO ? "auto" : "<invalid>", priv->auth_mode);
++	at76_dbg(DBG_PARAMS,
++		 "%s param: pm_mode %d pm_period %d auth_mode %s "
++		 "scan_times %d %d scan_mode %s",
++		 priv->netdev->name, priv->pm_mode, priv->pm_period,
++		 priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret",
++		 priv->scan_min_time, priv->scan_max_time,
++		 priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive");
 +
-+	for (i = 0; i < ptr->rates_len; i++) {
-+		u8 rate = ptr->rates[i];
++	memset(ccfg, 0, sizeof(struct at76_card_config));
++	ccfg->promiscuous_mode = 0;
++	ccfg->short_retry_limit = priv->short_retry_limit;
 +
-+		if (!(rate & 0x80))
-+			continue;
++	if (priv->wep_enabled) {
++		if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN)
++			ccfg->encryption_type = 2;
++		else
++			ccfg->encryption_type = 1;
 +
-+		/* this is a basic rate we have to support
-+		   (see IEEE802.11, ch. 7.3.2.2) */
-+		if (rate != (0x80 | hw_rates[0])
-+		    && rate != (0x80 | hw_rates[1])
-+		    && rate != (0x80 | hw_rates[2])
-+		    && rate != (0x80 | hw_rates[3])) {
-+			at76_dbg(DBG_BSS_MATCH,
-+				 "%s: bss table entry %p: basic rate %02x not "
-+				 "supported", priv->netdev->name, ptr, rate);
-+			return 0;
-+		}
++		/* jal: always exclude unencrypted if WEP is active */
++		ccfg->exclude_unencrypted = 1;
++	} else {
++		ccfg->exclude_unencrypted = 0;
++		ccfg->encryption_type = 0;
 +	}
 +
-+	/* if we use short preamble, the bss must support it */
-+	if (priv->preamble_type == PREAMBLE_TYPE_SHORT &&
-+	    !(ptr->capa & WLAN_CAPABILITY_SHORT_PREAMBLE)) {
-+		at76_dbg(DBG_BSS_MATCH,
-+			 "%s: %p does not support short preamble",
-+			 priv->netdev->name, ptr);
-+		return 0;
-+	} else
-+		return 1;
-+}
-+
-+static inline int at76_match_wep(struct at76_priv *priv, struct bss_info *ptr)
-+{
-+	if (!priv->wep_enabled && ptr->capa & WLAN_CAPABILITY_PRIVACY) {
-+		/* we have disabled WEP, but the BSS signals privacy */
-+		at76_dbg(DBG_BSS_MATCH,
-+			 "%s: bss table entry %p: requires encryption",
-+			 priv->netdev->name, ptr);
-+		return 0;
-+	}
-+	/* otherwise if the BSS does not signal privacy it may well
-+	   accept encrypted packets from us ... */
-+	return 1;
-+}
++	ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold);
++	ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold);
 +
-+static inline int at76_match_bssid(struct at76_priv *priv, struct bss_info *ptr)
-+{
-+	if (!priv->wanted_bssid_valid ||
-+	    !compare_ether_addr(ptr->bssid, priv->wanted_bssid))
-+		return 1;
++	memcpy(ccfg->basic_rate_set, hw_rates, 4);
++	/* jal: really needed, we do a set_mib for autorate later ??? */
++	ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0);
++	ccfg->channel = priv->channel;
++	ccfg->privacy_invoked = priv->wep_enabled;
++	memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE);
++	ccfg->ssid_len = priv->essid_size;
 +
-+	at76_dbg(DBG_BSS_MATCH,
-+		 "%s: requested bssid - %s does not match",
-+		 priv->netdev->name, mac2str(priv->wanted_bssid));
-+	at76_dbg(DBG_BSS_MATCH,
-+		 "      AP bssid - %s of bss table entry %p",
-+		 mac2str(ptr->bssid), ptr);
-+	return 0;
-+}
++	ccfg->wep_default_key_id = priv->wep_key_id;
++	memcpy(ccfg->wep_default_key_value, priv->wep_keys, 4 * WEP_KEY_LEN);
 +
-+/**
-+ * at76_match_bss - try to find a matching bss in priv->bss
-+ *
-+ * last - last bss tried
-+ *
-+ * last == NULL signals a new round starting with priv->bss_list.next
-+ * this function must be called inside an acquired priv->bss_list_spinlock
-+ * otherwise the timeout on bss may remove the newly chosen entry
-+ */
-+static struct bss_info *at76_match_bss(struct at76_priv *priv,
-+				       struct bss_info *last)
-+{
-+	struct bss_info *ptr = NULL;
-+	struct list_head *curr;
++	ccfg->short_preamble = priv->preamble_type;
++	ccfg->beacon_period = cpu_to_le16(priv->beacon_period);
 +
-+	curr = last ? last->list.next : priv->bss_list.next;
-+	while (curr != &priv->bss_list) {
-+		ptr = list_entry(curr, struct bss_info, list);
-+		if (at76_match_essid(priv, ptr) && at76_match_mode(priv, ptr)
-+		    && at76_match_wep(priv, ptr) && at76_match_rates(priv, ptr)
-+		    && at76_match_bssid(priv, ptr))
-+			break;
-+		curr = curr->next;
++	ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config,
++				    sizeof(struct at76_card_config));
++	if (ret < 0) {
++		printk(KERN_ERR "%s: at76_set_card_command failed: %d\n",
++		       priv->netdev->name, ret);
++		return ret;
 +	}
 +
-+	if (curr == &priv->bss_list)
-+		ptr = NULL;
-+	/* otherwise ptr points to the struct bss_info we have chosen */
++	at76_wait_completion(priv, CMD_STARTUP);
 +
-+	at76_dbg(DBG_BSS_TABLE, "%s %s: returned %p", priv->netdev->name,
-+		 __func__, ptr);
-+	return ptr;
-+}
++	/* remove BSSID from previous run */
++	memset(priv->bssid, 0, ETH_ALEN);
 +
-+/* Start joining a matching BSS, or create own IBSS */
-+static void at76_work_join(struct work_struct *work)
-+{
-+	struct at76_priv *priv = container_of(work, struct at76_priv,
-+					      work_join);
-+	int ret;
-+	unsigned long flags;
++	if (at76_set_radio(priv, 1) == 1)
++		at76_wait_completion(priv, CMD_RADIO_ON);
 +
-+	mutex_lock(&priv->mtx);
++	ret = at76_set_preamble(priv, priv->preamble_type);
++	if (ret < 0)
++		return ret;
 +
-+	WARN_ON(priv->mac_state != MAC_JOINING);
-+	if (priv->mac_state != MAC_JOINING)
-+		goto exit;
++	ret = at76_set_frag(priv, priv->frag_threshold);
++	if (ret < 0)
++		return ret;
 +
-+	/* secure the access to priv->curr_bss ! */
-+	spin_lock_irqsave(&priv->bss_list_spinlock, flags);
-+	priv->curr_bss = at76_match_bss(priv, priv->curr_bss);
-+	spin_unlock_irqrestore(&priv->bss_list_spinlock, flags);
++	ret = at76_set_rts(priv, priv->rts_threshold);
++	if (ret < 0)
++		return ret;
 +
-+	if (!priv->curr_bss) {
-+		/* here we haven't found a matching (i)bss ... */
-+		if (priv->iw_mode == IW_MODE_ADHOC) {
-+			at76_set_mac_state(priv, MAC_OWN_IBSS);
-+			at76_start_ibss(priv);
-+			goto exit;
-+		}
-+		/* haven't found a matching BSS in infra mode - try again */
-+		at76_set_mac_state(priv, MAC_SCANNING);
-+		schedule_work(&priv->work_start_scan);
-+		goto exit;
-+	}
++	ret = at76_set_autorate_fallback(priv,
++					 priv->txrate == TX_RATE_AUTO ? 1 : 0);
++	if (ret < 0)
++		return ret;
 +
-+	ret = at76_join_bss(priv, priv->curr_bss);
-+	if (ret < 0) {
-+		printk(KERN_ERR "%s: join_bss failed with %d\n",
-+		       priv->netdev->name, ret);
-+		goto exit;
++	ret = at76_set_pm_mode(priv);
++	if (ret < 0)
++		return ret;
++
++	if (at76_debug & DBG_MIB) {
++		at76_dump_mib_mac(priv);
++		at76_dump_mib_mac_addr(priv);
++		at76_dump_mib_mac_mgmt(priv);
++		at76_dump_mib_mac_wep(priv);
++		at76_dump_mib_mdomain(priv);
++		at76_dump_mib_phy(priv);
++		at76_dump_mib_local(priv);
 +	}
 +
-+	ret = at76_wait_completion(priv, CMD_JOIN);
-+	if (ret != CMD_STATUS_COMPLETE) {
-+		if (ret != CMD_STATUS_TIME_OUT)
-+			printk(KERN_ERR "%s: join_bss completed with %d\n",
-+			       priv->netdev->name, ret);
-+		else
-+			printk(KERN_INFO "%s: join_bss ssid %s timed out\n",
-+			       priv->netdev->name,
-+			       mac2str(priv->curr_bss->bssid));
++	return 0;
++}
 +
-+		/* retry next BSS immediately */
-+		schedule_work(&priv->work_join);
-+		goto exit;
-+	}
++/* Restart the interface */
++static void at76_dwork_restart(struct work_struct *work)
++{
++	struct at76_priv *priv = container_of(work, struct at76_priv,
++					      dwork_restart.work);
 +
-+	/* here we have joined the (I)BSS */
-+	if (priv->iw_mode == IW_MODE_ADHOC) {
-+		struct bss_info *bptr = priv->curr_bss;
-+		at76_set_mac_state(priv, MAC_CONNECTED);
-+		/* get ESSID, BSSID and channel for priv->curr_bss */
-+		priv->essid_size = bptr->ssid_len;
-+		memcpy(priv->essid, bptr->ssid, bptr->ssid_len);
-+		memcpy(priv->bssid, bptr->bssid, ETH_ALEN);
-+		priv->channel = bptr->channel;
-+		at76_iwevent_bss_connect(priv->netdev, bptr->bssid);
-+		netif_carrier_on(priv->netdev);
-+		netif_start_queue(priv->netdev);
-+		/* just to be sure */
-+		cancel_delayed_work(&priv->dwork_get_scan);
-+		cancel_delayed_work(&priv->dwork_auth);
-+		cancel_delayed_work(&priv->dwork_assoc);
++	mutex_lock(&priv->mtx);
++
++	netif_carrier_off(priv->netdev);	/* stop netdev watchdog */
++	netif_stop_queue(priv->netdev);	/* stop tx data packets */
++
++	at76_startup_device(priv);
++
++	if (priv->iw_mode != IW_MODE_MONITOR) {
++		priv->netdev->type = ARPHRD_ETHER;
++		at76_set_mac_state(priv, MAC_SCANNING);
++		schedule_work(&priv->work_start_scan);
 +	} else {
-+		/* send auth req */
-+		priv->retries = AUTH_RETRIES;
-+		at76_set_mac_state(priv, MAC_AUTH);
-+		at76_auth_req(priv, priv->curr_bss, 1, NULL);
-+		at76_dbg(DBG_MGMT_TIMER,
-+			 "%s:%d: starting mgmt_timer + HZ", __func__, __LINE__);
-+		schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT);
++		priv->netdev->type = ARPHRD_IEEE80211_RADIOTAP;
++		at76_start_monitor(priv);
 +	}
 +
-+exit:
 +	mutex_unlock(&priv->mtx);
 +}
 +
-+/* Reap scan results */
-+static void at76_dwork_get_scan(struct work_struct *work)
++/* Initiate scanning */
++static void at76_work_start_scan(struct work_struct *work)
 +{
-+	int status;
-+	int ret;
 +	struct at76_priv *priv = container_of(work, struct at76_priv,
-+					      dwork_get_scan.work);
++					      work_start_scan);
++	int ret;
 +
 +	mutex_lock(&priv->mtx);
++
 +	WARN_ON(priv->mac_state != MAC_SCANNING);
 +	if (priv->mac_state != MAC_SCANNING)
 +		goto exit;
 +
-+	status = at76_get_cmd_status(priv->udev, CMD_SCAN);
-+	if (status < 0) {
-+		printk(KERN_ERR "%s: %s: at76_get_cmd_status failed with %d\n",
-+		       priv->netdev->name, __func__, status);
-+		status = CMD_STATUS_IN_PROGRESS;
-+		/* INFO: Hope it was a one off error - if not, scanning
-+		   further down the line and stop this cycle */
-+	}
-+	at76_dbg(DBG_PROGRESS,
-+		 "%s %s: got cmd_status %d (state %s, need_any %d)",
-+		 priv->netdev->name, __func__, status,
-+		 mac_states[priv->mac_state], priv->scan_need_any);
++	/* only clear the bss list when a scan is actively initiated,
++	 * otherwise simply rely on at76_bss_list_timeout */
++	if (priv->scan_state == SCAN_IN_PROGRESS) {
++		at76_free_bss_list(priv);
++		priv->scan_need_any = 1;
++	} else
++		priv->scan_need_any = 0;
 +
-+	if (status != CMD_STATUS_COMPLETE) {
-+		if ((status != CMD_STATUS_IN_PROGRESS) &&
-+		    (status != CMD_STATUS_IDLE))
-+			printk(KERN_ERR "%s: %s: Bad scan status: %s\n",
-+			       priv->netdev->name, __func__,
-+			       at76_get_cmd_status_string(status));
++	ret = at76_start_scan(priv, 1);
 +
-+		/* the first cmd status after scan start is always a IDLE ->
-+		   start the timer to poll again until COMPLETED */
++	if (ret < 0)
++		printk(KERN_ERR "%s: %s: start_scan failed with %d\n",
++		       priv->netdev->name, __func__, ret);
++	else {
 +		at76_dbg(DBG_MGMT_TIMER,
 +			 "%s:%d: starting mgmt_timer for %d ticks",
 +			 __func__, __LINE__, SCAN_POLL_INTERVAL);
 +		schedule_delayed_work(&priv->dwork_get_scan,
 +				      SCAN_POLL_INTERVAL);
-+		goto exit;
 +	}
 +
-+	if (at76_debug & DBG_BSS_TABLE)
-+		at76_dump_bss_table(priv);
++exit:
++	mutex_unlock(&priv->mtx);
++}
 +
-+	if (priv->scan_need_any) {
-+		ret = at76_start_scan(priv, 0);
-+		if (ret < 0)
-+			printk(KERN_ERR
-+			       "%s: %s: start_scan (ANY) failed with %d\n",
-+			       priv->netdev->name, __func__, ret);
-+		at76_dbg(DBG_MGMT_TIMER,
-+			 "%s:%d: starting mgmt_timer for %d ticks", __func__,
-+			 __LINE__, SCAN_POLL_INTERVAL);
-+		schedule_delayed_work(&priv->dwork_get_scan,
-+				      SCAN_POLL_INTERVAL);
-+		priv->scan_need_any = 0;
-+	} else {
-+		priv->scan_state = SCAN_COMPLETED;
-+		/* report the end of scan to user space */
-+		at76_iwevent_scan_complete(priv->netdev);
-+		at76_set_mac_state(priv, MAC_JOINING);
-+		schedule_work(&priv->work_join);
-+	}
++/* Enable or disable promiscuous mode */
++static void at76_work_set_promisc(struct work_struct *work)
++{
++	struct at76_priv *priv = container_of(work, struct at76_priv,
++					      work_set_promisc);
++	int ret = 0;
++
++	mutex_lock(&priv->mtx);
++
++	priv->mib_buf.type = MIB_LOCAL;
++	priv->mib_buf.size = 1;
++	priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode);
++	priv->mib_buf.data.byte = priv->promisc ? 1 : 0;
++
++	ret = at76_set_mib(priv, &priv->mib_buf);
++	if (ret < 0)
++		printk(KERN_ERR "%s: set_mib (promiscuous_mode) failed: %d\n",
++		       priv->netdev->name, ret);
 +
-+exit:
 +	mutex_unlock(&priv->mtx);
 +}
 +
-+/* Handle loss of beacons from the AP */
-+static void at76_dwork_beacon(struct work_struct *work)
++/* Submit Rx urb back to the device */
++static void at76_work_submit_rx(struct work_struct *work)
 +{
 +	struct at76_priv *priv = container_of(work, struct at76_priv,
-+					      dwork_beacon.work);
++					      work_submit_rx);
 +
 +	mutex_lock(&priv->mtx);
-+	if (priv->mac_state != MAC_CONNECTED || priv->iw_mode != IW_MODE_INFRA)
-+		goto exit;
++	at76_submit_rx_urb(priv);
++	mutex_unlock(&priv->mtx);
++}
 +
-+	/* We haven't received any beacons from out AP for BEACON_TIMEOUT */
-+	printk(KERN_INFO "%s: lost beacon bssid %s\n",
-+	       priv->netdev->name, mac2str(priv->curr_bss->bssid));
++/* We got an association response */
++static void at76_rx_mgmt_assoc(struct at76_priv *priv,
++			       struct at76_rx_buffer *buf)
++{
++	struct ieee80211_assoc_response *resp =
++	    (struct ieee80211_assoc_response *)buf->packet;
++	u16 assoc_id = le16_to_cpu(resp->aid);
++	u16 status = le16_to_cpu(resp->status);
 +
-+	netif_carrier_off(priv->netdev);
-+	netif_stop_queue(priv->netdev);
-+	at76_iwevent_bss_disconnect(priv->netdev);
-+	at76_set_mac_state(priv, MAC_SCANNING);
-+	schedule_work(&priv->work_start_scan);
++	at76_dbg(DBG_RX_MGMT, "%s: rx AssocResp bssid %s capa 0x%04x status "
++		 "0x%04x assoc_id 0x%04x rates %s", priv->netdev->name,
++		 mac2str(resp->header.addr3), le16_to_cpu(resp->capability),
++		 status, assoc_id, hex2str(resp->info_element->data,
++					   resp->info_element->len));
++
++	if (priv->mac_state != MAC_ASSOC) {
++		printk(KERN_INFO "%s: AssocResp in state %s ignored\n",
++		       priv->netdev->name, mac_states[priv->mac_state]);
++		return;
++	}
++
++	BUG_ON(!priv->curr_bss);
++
++	cancel_delayed_work(&priv->dwork_assoc);
++	if (status == WLAN_STATUS_SUCCESS) {
++		struct bss_info *ptr = priv->curr_bss;
++		priv->assoc_id = assoc_id & 0x3fff;
++		/* update iwconfig params */
++		memcpy(priv->bssid, ptr->bssid, ETH_ALEN);
++		memcpy(priv->essid, ptr->ssid, ptr->ssid_len);
++		priv->essid_size = ptr->ssid_len;
++		priv->channel = ptr->channel;
++		schedule_work(&priv->work_assoc_done);
++	} else {
++		at76_set_mac_state(priv, MAC_JOINING);
++		schedule_work(&priv->work_join);
++	}
++}
++
++/* Process disassociation request from the AP */
++static void at76_rx_mgmt_disassoc(struct at76_priv *priv,
++				  struct at76_rx_buffer *buf)
++{
++	struct ieee80211_disassoc *resp =
++	    (struct ieee80211_disassoc *)buf->packet;
++	struct ieee80211_hdr_3addr *mgmt = &resp->header;
++
++	at76_dbg(DBG_RX_MGMT,
++		 "%s: rx DisAssoc bssid %s reason 0x%04x destination %s",
++		 priv->netdev->name, mac2str(mgmt->addr3),
++		 le16_to_cpu(resp->reason), mac2str(mgmt->addr1));
++
++	/* We are not connected, ignore */
++	if (priv->mac_state == MAC_SCANNING || priv->mac_state == MAC_INIT
++	    || !priv->curr_bss)
++		return;
++
++	/* Not our BSSID, ignore */
++	if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid))
++		return;
++
++	/* Not for our STA and not broadcast, ignore */
++	if (compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1)
++	    && !is_broadcast_ether_addr(mgmt->addr1))
++		return;
++
++	if (priv->mac_state != MAC_ASSOC && priv->mac_state != MAC_CONNECTED
++	    && priv->mac_state != MAC_JOINING) {
++		printk(KERN_INFO "%s: DisAssoc in state %s ignored\n",
++		       priv->netdev->name, mac_states[priv->mac_state]);
++		return;
++	}
++
++	if (priv->mac_state == MAC_CONNECTED) {
++		netif_carrier_off(priv->netdev);
++		netif_stop_queue(priv->netdev);
++		at76_iwevent_bss_disconnect(priv->netdev);
++	}
++	cancel_delayed_work(&priv->dwork_get_scan);
++	cancel_delayed_work(&priv->dwork_beacon);
++	cancel_delayed_work(&priv->dwork_auth);
++	cancel_delayed_work(&priv->dwork_assoc);
++	at76_set_mac_state(priv, MAC_JOINING);
++	schedule_work(&priv->work_join);
++}
++
++static void at76_rx_mgmt_auth(struct at76_priv *priv,
++			      struct at76_rx_buffer *buf)
++{
++	struct ieee80211_auth *resp = (struct ieee80211_auth *)buf->packet;
++	struct ieee80211_hdr_3addr *mgmt = &resp->header;
++	int seq_nr = le16_to_cpu(resp->transaction);
++	int alg = le16_to_cpu(resp->algorithm);
++	int status = le16_to_cpu(resp->status);
 +
-+exit:
-+	mutex_unlock(&priv->mtx);
-+}
++	at76_dbg(DBG_RX_MGMT,
++		 "%s: rx AuthFrame bssid %s alg %d seq_nr %d status %d "
++		 "destination %s", priv->netdev->name, mac2str(mgmt->addr3),
++		 alg, seq_nr, status, mac2str(mgmt->addr1));
 +
-+/* Handle authentication response timeout */
-+static void at76_dwork_auth(struct work_struct *work)
-+{
-+	struct at76_priv *priv = container_of(work, struct at76_priv,
-+					      dwork_auth.work);
++	if (alg == WLAN_AUTH_SHARED_KEY && seq_nr == 2)
++		at76_dbg(DBG_RX_MGMT, "%s: AuthFrame challenge %s ...",
++			 priv->netdev->name, hex2str(resp->info_element, 18));
 +
-+	mutex_lock(&priv->mtx);
-+	WARN_ON(priv->mac_state != MAC_AUTH);
-+	if (priv->mac_state != MAC_AUTH)
-+		goto exit;
++	if (priv->mac_state != MAC_AUTH) {
++		printk(KERN_INFO "%s: ignored AuthFrame in state %s\n",
++		       priv->netdev->name, mac_states[priv->mac_state]);
++		return;
++	}
++	if (priv->auth_mode != alg) {
++		printk(KERN_INFO "%s: ignored AuthFrame for alg %d\n",
++		       priv->netdev->name, alg);
++		return;
++	}
 +
-+	at76_dbg(DBG_PROGRESS, "%s: authentication response timeout",
-+		 priv->netdev->name);
++	BUG_ON(!priv->curr_bss);
 +
-+	if (priv->retries-- >= 0) {
-+		at76_auth_req(priv, priv->curr_bss, 1, NULL);
-+		at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ",
-+			 __func__, __LINE__);
-+		schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT);
-+	} else {
-+		/* try to get next matching BSS */
++	/* Not our BSSID or not for our STA, ignore */
++	if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid)
++	    || compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1))
++		return;
++
++	cancel_delayed_work(&priv->dwork_auth);
++	if (status != WLAN_STATUS_SUCCESS) {
++		/* try to join next bss */
 +		at76_set_mac_state(priv, MAC_JOINING);
 +		schedule_work(&priv->work_join);
++		return;
 +	}
 +
-+exit:
-+	mutex_unlock(&priv->mtx);
++	if (priv->auth_mode == WLAN_AUTH_OPEN || seq_nr == 4) {
++		priv->retries = ASSOC_RETRIES;
++		at76_set_mac_state(priv, MAC_ASSOC);
++		at76_assoc_req(priv, priv->curr_bss);
++		at76_dbg(DBG_MGMT_TIMER,
++			 "%s:%d: starting mgmt_timer + HZ", __func__, __LINE__);
++		schedule_delayed_work(&priv->dwork_assoc, ASSOC_TIMEOUT);
++		return;
++	}
++
++	WARN_ON(seq_nr != 2);
++	at76_auth_req(priv, priv->curr_bss, seq_nr + 1, resp->info_element);
++	at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __func__,
++		 __LINE__);
++	schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT);
 +}
 +
-+/* Handle association response timeout */
-+static void at76_dwork_assoc(struct work_struct *work)
++static void at76_rx_mgmt_deauth(struct at76_priv *priv,
++				struct at76_rx_buffer *buf)
 +{
-+	struct at76_priv *priv = container_of(work, struct at76_priv,
-+					      dwork_assoc.work);
-+
-+	mutex_lock(&priv->mtx);
-+	WARN_ON(priv->mac_state != MAC_ASSOC);
-+	if (priv->mac_state != MAC_ASSOC)
-+		goto exit;
++	struct ieee80211_disassoc *resp =
++	    (struct ieee80211_disassoc *)buf->packet;
++	struct ieee80211_hdr_3addr *mgmt = &resp->header;
 +
-+	at76_dbg(DBG_PROGRESS, "%s: association response timeout",
-+		 priv->netdev->name);
++	at76_dbg(DBG_RX_MGMT | DBG_PROGRESS,
++		 "%s: rx DeAuth bssid %s reason 0x%04x destination %s",
++		 priv->netdev->name, mac2str(mgmt->addr3),
++		 le16_to_cpu(resp->reason), mac2str(mgmt->addr1));
 +
-+	if (priv->retries-- >= 0) {
-+		at76_assoc_req(priv, priv->curr_bss);
-+		at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ",
-+			 __func__, __LINE__);
-+		schedule_delayed_work(&priv->dwork_assoc, ASSOC_TIMEOUT);
-+	} else {
-+		/* try to get next matching BSS */
-+		at76_set_mac_state(priv, MAC_JOINING);
-+		schedule_work(&priv->work_join);
++	if (priv->mac_state != MAC_AUTH && priv->mac_state != MAC_ASSOC
++	    && priv->mac_state != MAC_CONNECTED) {
++		printk(KERN_INFO "%s: DeAuth in state %s ignored\n",
++		       priv->netdev->name, mac_states[priv->mac_state]);
++		return;
 +	}
 +
-+exit:
-+	mutex_unlock(&priv->mtx);
++	BUG_ON(!priv->curr_bss);
++
++	/* Not our BSSID, ignore */
++	if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid))
++		return;
++
++	/* Not for our STA and not broadcast, ignore */
++	if (compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1)
++	    && !is_broadcast_ether_addr(mgmt->addr1))
++		return;
++
++	if (priv->mac_state == MAC_CONNECTED)
++		at76_iwevent_bss_disconnect(priv->netdev);
++
++	at76_set_mac_state(priv, MAC_JOINING);
++	schedule_work(&priv->work_join);
++	cancel_delayed_work(&priv->dwork_get_scan);
++	cancel_delayed_work(&priv->dwork_beacon);
++	cancel_delayed_work(&priv->dwork_auth);
++	cancel_delayed_work(&priv->dwork_assoc);
 +}
 +
-+/* Read new bssid in ad-hoc mode */
-+static void at76_work_new_bss(struct work_struct *work)
++static void at76_rx_mgmt_beacon(struct at76_priv *priv,
++				struct at76_rx_buffer *buf)
 +{
-+	struct at76_priv *priv = container_of(work, struct at76_priv,
-+					      work_new_bss);
-+	int ret;
-+	struct mib_mac_mgmt mac_mgmt;
++	int varpar_len;
++	/* beacon content */
++	struct ieee80211_beacon *bdata = (struct ieee80211_beacon *)buf->packet;
++	struct ieee80211_hdr_3addr *mgmt = &bdata->header;
 +
-+	mutex_lock(&priv->mtx);
++	struct list_head *lptr;
++	struct bss_info *match;	/* entry matching addr3 with its bssid */
++	int new_entry = 0;
++	int len;
++	struct ieee80211_info_element *ie;
++	int have_ssid = 0;
++	int have_rates = 0;
++	int have_channel = 0;
++	int keep_going = 1;
++	unsigned long flags;
 +
-+	ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, &mac_mgmt,
-+			   sizeof(struct mib_mac_mgmt));
-+	if (ret < 0) {
-+		printk(KERN_ERR "%s: at76_get_mib failed: %d\n",
-+		       priv->netdev->name, ret);
-+		goto exit;
++	spin_lock_irqsave(&priv->bss_list_spinlock, flags);
++	if (priv->mac_state == MAC_CONNECTED) {
++		/* in state MAC_CONNECTED we use the mgmt_timer to control
++		   the beacon of the BSS */
++		BUG_ON(!priv->curr_bss);
++
++		if (!compare_ether_addr(priv->curr_bss->bssid, mgmt->addr3)) {
++			/* We got our AP's beacon, defer the timeout handler.
++			   Kill pending work first, as schedule_delayed_work()
++			   won't do it. */
++			cancel_delayed_work(&priv->dwork_beacon);
++			schedule_delayed_work(&priv->dwork_beacon,
++					      BEACON_TIMEOUT);
++			priv->curr_bss->rssi = buf->rssi;
++			priv->beacons_received++;
++			goto exit;
++		}
 +	}
 +
-+	at76_dbg(DBG_PROGRESS, "ibss_change = 0x%2x", mac_mgmt.ibss_change);
-+	memcpy(priv->bssid, mac_mgmt.current_bssid, ETH_ALEN);
-+	at76_dbg(DBG_PROGRESS, "using BSSID %s", mac2str(priv->bssid));
++	/* look if we have this BSS already in the list */
++	match = NULL;
 +
-+	at76_iwevent_bss_connect(priv->netdev, priv->bssid);
++	if (!list_empty(&priv->bss_list)) {
++		list_for_each(lptr, &priv->bss_list) {
++			struct bss_info *bss_ptr =
++			    list_entry(lptr, struct bss_info, list);
++			if (!compare_ether_addr(bss_ptr->bssid, mgmt->addr3)) {
++				match = bss_ptr;
++				break;
++			}
++		}
++	}
 +
-+	priv->mib_buf.type = MIB_MAC_MGMT;
-+	priv->mib_buf.size = 1;
-+	priv->mib_buf.index = offsetof(struct mib_mac_mgmt, ibss_change);
-+	priv->mib_buf.data.byte = 0;
++	if (!match) {
++		/* BSS not in the list - append it */
++		match = kzalloc(sizeof(struct bss_info), GFP_ATOMIC);
++		if (!match) {
++			at76_dbg(DBG_BSS_TABLE,
++				 "%s: cannot kmalloc new bss info (%zd byte)",
++				 priv->netdev->name, sizeof(struct bss_info));
++			goto exit;
++		}
++		new_entry = 1;
++		list_add_tail(&match->list, &priv->bss_list);
++	}
 +
-+	ret = at76_set_mib(priv, &priv->mib_buf);
-+	if (ret < 0)
-+		printk(KERN_ERR "%s: set_mib (ibss change ok) failed: %d\n",
-+		       priv->netdev->name, ret);
++	match->capa = le16_to_cpu(bdata->capability);
++	match->beacon_interval = le16_to_cpu(bdata->beacon_interval);
++	match->rssi = buf->rssi;
++	match->link_qual = buf->link_quality;
++	match->noise_level = buf->noise_level;
++	memcpy(match->bssid, mgmt->addr3, ETH_ALEN);
++	at76_dbg(DBG_RX_BEACON, "%s: bssid %s", priv->netdev->name,
++		 mac2str(match->bssid));
 +
-+exit:
-+	mutex_unlock(&priv->mtx);
-+}
++	ie = bdata->info_element;
 +
-+static int at76_startup_device(struct at76_priv *priv)
-+{
-+	struct at76_card_config *ccfg = &priv->card_config;
-+	int ret;
++	/* length of var length beacon parameters */
++	varpar_len = min_t(int, le16_to_cpu(buf->wlength) -
++			   sizeof(struct ieee80211_beacon),
++			   BEACON_MAX_DATA_LENGTH);
 +
-+	at76_dbg(DBG_PARAMS,
-+		 "%s param: ssid %.*s (%s) mode %s ch %d wep %s key %d "
-+		 "keylen %d", priv->netdev->name, priv->essid_size, priv->essid,
-+		 hex2str(priv->essid, IW_ESSID_MAX_SIZE),
-+		 priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra",
-+		 priv->channel, priv->wep_enabled ? "enabled" : "disabled",
-+		 priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]);
-+	at76_dbg(DBG_PARAMS,
-+		 "%s param: preamble %s rts %d retry %d frag %d "
-+		 "txrate %s auth_mode %d", priv->netdev->name,
-+		 preambles[priv->preamble_type], priv->rts_threshold,
-+		 priv->short_retry_limit, priv->frag_threshold,
-+		 priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate ==
-+		 TX_RATE_2MBIT ? "2MBit" : priv->txrate ==
-+		 TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate ==
-+		 TX_RATE_11MBIT ? "11MBit" : priv->txrate ==
-+		 TX_RATE_AUTO ? "auto" : "<invalid>", priv->auth_mode);
-+	at76_dbg(DBG_PARAMS,
-+		 "%s param: pm_mode %d pm_period %d auth_mode %s "
-+		 "scan_times %d %d scan_mode %s",
-+		 priv->netdev->name, priv->pm_mode, priv->pm_period,
-+		 priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret",
-+		 priv->scan_min_time, priv->scan_max_time,
-+		 priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive");
++	/* This routine steps through the bdata->data array to get
++	 * some useful information about the access point.
++	 * Currently, this implementation supports receipt of: SSID,
++	 * supported transfer rates and channel, in any order, with some
++	 * tolerance for intermittent unknown codes (although this
++	 * functionality may not be necessary as the useful information will
++	 * usually arrive in consecutively, but there have been some
++	 * reports of some of the useful information fields arriving in a
++	 * different order).
++	 * It does not support any more IE types although MFIE_TYPE_TIM may
++	 * be supported (on my AP at least).
++	 * The bdata->data array is about 1500 bytes long but only ~36 of those
++	 * bytes are useful, hence the have_ssid etc optimizations. */
 +
-+	memset(ccfg, 0, sizeof(struct at76_card_config));
-+	ccfg->promiscuous_mode = 0;
-+	ccfg->short_retry_limit = priv->short_retry_limit;
++	while (keep_going &&
++	       ((&ie->data[ie->len] - (u8 *)bdata->info_element) <=
++		varpar_len)) {
 +
-+	if (priv->wep_enabled) {
-+		if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN)
-+			ccfg->encryption_type = 2;
-+		else
-+			ccfg->encryption_type = 1;
++		switch (ie->id) {
 +
-+		/* jal: always exclude unencrypted if WEP is active */
-+		ccfg->exclude_unencrypted = 1;
-+	} else {
-+		ccfg->exclude_unencrypted = 0;
-+		ccfg->encryption_type = 0;
-+	}
++		case MFIE_TYPE_SSID:
++			if (have_ssid)
++				break;
 +
-+	ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold);
-+	ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold);
++			len = min_t(int, IW_ESSID_MAX_SIZE, ie->len);
 +
-+	memcpy(ccfg->basic_rate_set, hw_rates, 4);
-+	/* jal: really needed, we do a set_mib for autorate later ??? */
-+	ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0);
-+	ccfg->channel = priv->channel;
-+	ccfg->privacy_invoked = priv->wep_enabled;
-+	memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE);
-+	ccfg->ssid_len = priv->essid_size;
++			/* we copy only if this is a new entry,
++			   or the incoming SSID is not a hidden SSID. This
++			   will protect us from overwriting a real SSID read
++			   in a ProbeResponse with a hidden one from a
++			   following beacon. */
++			if (!new_entry && at76_is_hidden_ssid(ie->data, len)) {
++				have_ssid = 1;
++				break;
++			}
 +
-+	ccfg->wep_default_key_id = priv->wep_key_id;
-+	memcpy(ccfg->wep_default_key_value, priv->wep_keys, 4 * WEP_KEY_LEN);
++			match->ssid_len = len;
++			memcpy(match->ssid, ie->data, len);
++			at76_dbg(DBG_RX_BEACON, "%s: SSID - %.*s",
++				 priv->netdev->name, len, match->ssid);
++			have_ssid = 1;
++			break;
 +
-+	ccfg->short_preamble = priv->preamble_type;
-+	ccfg->beacon_period = cpu_to_le16(priv->beacon_period);
++		case MFIE_TYPE_RATES:
++			if (have_rates)
++				break;
 +
-+	ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config,
-+				    sizeof(struct at76_card_config));
-+	if (ret < 0) {
-+		printk(KERN_ERR "%s: at76_set_card_command failed: %d\n",
-+		       priv->netdev->name, ret);
-+		return ret;
-+	}
++			match->rates_len =
++			    min_t(int, sizeof(match->rates), ie->len);
++			memcpy(match->rates, ie->data, match->rates_len);
++			have_rates = 1;
++			at76_dbg(DBG_RX_BEACON, "%s: SUPPORTED RATES %s",
++				 priv->netdev->name,
++				 hex2str(ie->data, ie->len));
++			break;
 +
-+	at76_wait_completion(priv, CMD_STARTUP);
++		case MFIE_TYPE_DS_SET:
++			if (have_channel)
++				break;
 +
-+	/* remove BSSID from previous run */
-+	memset(priv->bssid, 0, ETH_ALEN);
++			match->channel = ie->data[0];
++			have_channel = 1;
++			at76_dbg(DBG_RX_BEACON, "%s: CHANNEL - %d",
++				 priv->netdev->name, match->channel);
++			break;
 +
-+	if (at76_set_radio(priv, 1) == 1)
-+		at76_wait_completion(priv, CMD_RADIO_ON);
++		case MFIE_TYPE_CF_SET:
++		case MFIE_TYPE_TIM:
++		case MFIE_TYPE_IBSS_SET:
++		default:
++			at76_dbg(DBG_RX_BEACON, "%s: beacon IE id %d len %d %s",
++				 priv->netdev->name, ie->id, ie->len,
++				 hex2str(ie->data, ie->len));
++			break;
++		}
 +
-+	ret = at76_set_preamble(priv, priv->preamble_type);
-+	if (ret < 0)
-+		return ret;
++		/* advance to the next informational element */
++		next_ie(&ie);
 +
-+	ret = at76_set_frag(priv, priv->frag_threshold);
-+	if (ret < 0)
-+		return ret;
++		/* Optimization: after all, the bdata->data array is
++		 * varpar_len bytes long, whereas we get all of the useful
++		 * information after only ~36 bytes, this saves us a lot of
++		 * time (and trouble as the remaining portion of the array
++		 * could be full of junk)
++		 * Comment this out if you want to see what other information
++		 * comes from the AP - although little of it may be useful */
++	}
 +
-+	ret = at76_set_rts(priv, priv->rts_threshold);
-+	if (ret < 0)
-+		return ret;
++	at76_dbg(DBG_RX_BEACON, "%s: Finished processing beacon data",
++		 priv->netdev->name);
 +
-+	ret = at76_set_autorate_fallback(priv,
-+					 priv->txrate == TX_RATE_AUTO ? 1 : 0);
-+	if (ret < 0)
-+		return ret;
++	match->last_rx = jiffies;	/* record last rx of beacon */
 +
-+	ret = at76_set_pm_mode(priv);
-+	if (ret < 0)
-+		return ret;
++exit:
++	spin_unlock_irqrestore(&priv->bss_list_spinlock, flags);
++}
 +
-+	if (at76_debug & DBG_MIB) {
-+		at76_dump_mib_mac(priv);
-+		at76_dump_mib_mac_addr(priv);
-+		at76_dump_mib_mac_mgmt(priv);
-+		at76_dump_mib_mac_wep(priv);
-+		at76_dump_mib_mdomain(priv);
-+		at76_dump_mib_phy(priv);
-+		at76_dump_mib_local(priv);
-+	}
++/* Calculate the link level from a given rx_buffer */
++static void at76_calc_level(struct at76_priv *priv, struct at76_rx_buffer *buf,
++			    struct iw_quality *qual)
++{
++	/* just a guess for now, might be different for other chips */
++	int max_rssi = 42;
 +
-+	return 0;
++	qual->level = (buf->rssi * 100 / max_rssi);
++	if (qual->level > 100)
++		qual->level = 100;
++	qual->updated |= IW_QUAL_LEVEL_UPDATED;
 +}
 +
-+/* Restart the interface */
-+static void at76_dwork_restart(struct work_struct *work)
++/* Calculate the link quality from a given rx_buffer */
++static void at76_calc_qual(struct at76_priv *priv, struct at76_rx_buffer *buf,
++			   struct iw_quality *qual)
 +{
-+	struct at76_priv *priv = container_of(work, struct at76_priv,
-+					      dwork_restart.work);
-+
-+	mutex_lock(&priv->mtx);
++	if (at76_is_intersil(priv->board_type))
++		qual->qual = buf->link_quality;
++	else {
++		unsigned long elapsed;
 +
-+	netif_carrier_off(priv->netdev);	/* stop netdev watchdog */
-+	netif_stop_queue(priv->netdev);	/* stop tx data packets */
++		/* Update qual at most once a second */
++		elapsed = jiffies - priv->beacons_last_qual;
++		if (elapsed < 1 * HZ)
++			return;
 +
-+	at76_startup_device(priv);
++		qual->qual = qual->level * priv->beacons_received *
++		    msecs_to_jiffies(priv->beacon_period) / elapsed;
 +
-+	if (priv->iw_mode != IW_MODE_MONITOR) {
-+		priv->netdev->type = ARPHRD_ETHER;
-+		at76_set_mac_state(priv, MAC_SCANNING);
-+		schedule_work(&priv->work_start_scan);
-+	} else {
-+		priv->netdev->type = ARPHRD_IEEE80211_RADIOTAP;
-+		at76_start_monitor(priv);
++		priv->beacons_last_qual = jiffies;
++		priv->beacons_received = 0;
 +	}
-+
-+	mutex_unlock(&priv->mtx);
++	qual->qual = (qual->qual > 100) ? 100 : qual->qual;
++	qual->updated |= IW_QUAL_QUAL_UPDATED;
 +}
 +
-+/* Initiate scanning */
-+static void at76_work_start_scan(struct work_struct *work)
++/* Calculate the noise quality from a given rx_buffer */
++static void at76_calc_noise(struct at76_priv *priv, struct at76_rx_buffer *buf,
++			    struct iw_quality *qual)
 +{
-+	struct at76_priv *priv = container_of(work, struct at76_priv,
-+					      work_start_scan);
-+	int ret;
-+
-+	mutex_lock(&priv->mtx);
-+
-+	WARN_ON(priv->mac_state != MAC_SCANNING);
-+	if (priv->mac_state != MAC_SCANNING)
-+		goto exit;
-+
-+	/* only clear the bss list when a scan is actively initiated,
-+	 * otherwise simply rely on at76_bss_list_timeout */
-+	if (priv->scan_state == SCAN_IN_PROGRESS) {
-+		at76_free_bss_list(priv);
-+		priv->scan_need_any = 1;
-+	} else
-+		priv->scan_need_any = 0;
++	qual->noise = 0;
++	qual->updated |= IW_QUAL_NOISE_INVALID;
++}
 +
-+	ret = at76_start_scan(priv, 1);
++static void at76_update_wstats(struct at76_priv *priv,
++			       struct at76_rx_buffer *buf)
++{
++	struct iw_quality *qual = &priv->wstats.qual;
 +
-+	if (ret < 0)
-+		printk(KERN_ERR "%s: %s: start_scan failed with %d\n",
-+		       priv->netdev->name, __func__, ret);
-+	else {
-+		at76_dbg(DBG_MGMT_TIMER,
-+			 "%s:%d: starting mgmt_timer for %d ticks",
-+			 __func__, __LINE__, SCAN_POLL_INTERVAL);
-+		schedule_delayed_work(&priv->dwork_get_scan,
-+				      SCAN_POLL_INTERVAL);
++	if (buf->rssi && priv->mac_state == MAC_CONNECTED) {
++		qual->updated = 0;
++		at76_calc_level(priv, buf, qual);
++		at76_calc_qual(priv, buf, qual);
++		at76_calc_noise(priv, buf, qual);
++	} else {
++		qual->qual = 0;
++		qual->level = 0;
++		qual->noise = 0;
++		qual->updated = IW_QUAL_ALL_INVALID;
 +	}
-+
-+exit:
-+	mutex_unlock(&priv->mtx);
 +}
 +
-+/* Enable or disable promiscuous mode */
-+static void at76_work_set_promisc(struct work_struct *work)
++static void at76_rx_mgmt(struct at76_priv *priv, struct at76_rx_buffer *buf)
 +{
-+	struct at76_priv *priv = container_of(work, struct at76_priv,
-+					      work_set_promisc);
-+	int ret = 0;
-+
-+	mutex_lock(&priv->mtx);
++	struct ieee80211_hdr_3addr *mgmt =
++	    (struct ieee80211_hdr_3addr *)buf->packet;
++	u16 framectl = le16_to_cpu(mgmt->frame_ctl);
 +
-+	priv->mib_buf.type = MIB_LOCAL;
-+	priv->mib_buf.size = 1;
-+	priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode);
-+	priv->mib_buf.data.byte = priv->promisc ? 1 : 0;
++	/* update wstats */
++	if (priv->mac_state != MAC_INIT && priv->mac_state != MAC_SCANNING) {
++		/* jal: this is a dirty hack needed by Tim in ad-hoc mode */
++		/* Data packets always seem to have a 0 link level, so we
++		   only read link quality info from management packets.
++		   Atmel driver actually averages the present, and previous
++		   values, we just present the raw value at the moment - TJS */
++		if (priv->iw_mode == IW_MODE_ADHOC
++		    || (priv->curr_bss
++			&& !compare_ether_addr(mgmt->addr3,
++					       priv->curr_bss->bssid)))
++			at76_update_wstats(priv, buf);
++	}
 +
-+	ret = at76_set_mib(priv, &priv->mib_buf);
-+	if (ret < 0)
-+		printk(KERN_ERR "%s: set_mib (promiscuous_mode) failed: %d\n",
-+		       priv->netdev->name, ret);
++	at76_dbg(DBG_RX_MGMT_CONTENT, "%s rx mgmt framectl 0x%x %s",
++		 priv->netdev->name, framectl,
++		 hex2str(mgmt, le16_to_cpu(buf->wlength)));
 +
-+	mutex_unlock(&priv->mtx);
-+}
++	switch (framectl & IEEE80211_FCTL_STYPE) {
++	case IEEE80211_STYPE_BEACON:
++	case IEEE80211_STYPE_PROBE_RESP:
++		at76_rx_mgmt_beacon(priv, buf);
++		break;
 +
-+/* Submit Rx urb back to the device */
-+static void at76_work_submit_rx(struct work_struct *work)
-+{
-+	struct at76_priv *priv = container_of(work, struct at76_priv,
-+					      work_submit_rx);
++	case IEEE80211_STYPE_ASSOC_RESP:
++		at76_rx_mgmt_assoc(priv, buf);
++		break;
 +
-+	mutex_lock(&priv->mtx);
-+	at76_submit_rx_urb(priv);
-+	mutex_unlock(&priv->mtx);
-+}
++	case IEEE80211_STYPE_DISASSOC:
++		at76_rx_mgmt_disassoc(priv, buf);
++		break;
 +
-+/* We got an association response */
-+static void at76_rx_mgmt_assoc(struct at76_priv *priv,
-+			       struct at76_rx_buffer *buf)
-+{
-+	struct ieee80211_assoc_response *resp =
-+	    (struct ieee80211_assoc_response *)buf->packet;
-+	u16 assoc_id = le16_to_cpu(resp->aid);
-+	u16 status = le16_to_cpu(resp->status);
++	case IEEE80211_STYPE_AUTH:
++		at76_rx_mgmt_auth(priv, buf);
++		break;
 +
-+	at76_dbg(DBG_RX_MGMT, "%s: rx AssocResp bssid %s capa 0x%04x status "
-+		 "0x%04x assoc_id 0x%04x rates %s", priv->netdev->name,
-+		 mac2str(resp->header.addr3), le16_to_cpu(resp->capability),
-+		 status, assoc_id, hex2str(resp->info_element->data,
-+					   resp->info_element->len));
++	case IEEE80211_STYPE_DEAUTH:
++		at76_rx_mgmt_deauth(priv, buf);
++		break;
 +
-+	if (priv->mac_state != MAC_ASSOC) {
-+		printk(KERN_INFO "%s: AssocResp in state %s ignored\n",
-+		       priv->netdev->name, mac_states[priv->mac_state]);
-+		return;
++	default:
++		printk(KERN_DEBUG "%s: ignoring frame with framectl 0x%04x\n",
++		       priv->netdev->name, framectl);
 +	}
 +
-+	BUG_ON(!priv->curr_bss);
-+
-+	cancel_delayed_work(&priv->dwork_assoc);
-+	if (status == WLAN_STATUS_SUCCESS) {
-+		struct bss_info *ptr = priv->curr_bss;
-+		priv->assoc_id = assoc_id & 0x3fff;
-+		/* update iwconfig params */
-+		memcpy(priv->bssid, ptr->bssid, ETH_ALEN);
-+		memcpy(priv->essid, ptr->ssid, ptr->ssid_len);
-+		priv->essid_size = ptr->ssid_len;
-+		priv->channel = ptr->channel;
-+		schedule_work(&priv->work_assoc_done);
-+	} else {
-+		at76_set_mac_state(priv, MAC_JOINING);
-+		schedule_work(&priv->work_join);
-+	}
++	return;
 +}
 +
-+/* Process disassociation request from the AP */
-+static void at76_rx_mgmt_disassoc(struct at76_priv *priv,
-+				  struct at76_rx_buffer *buf)
++/* Convert the 802.11 header into an ethernet-style header, make skb
++ * ready for consumption by netif_rx() */
++static void at76_ieee80211_to_eth(struct sk_buff *skb, int iw_mode)
 +{
-+	struct ieee80211_disassoc *resp =
-+	    (struct ieee80211_disassoc *)buf->packet;
-+	struct ieee80211_hdr_3addr *mgmt = &resp->header;
++	struct ieee80211_hdr_3addr *i802_11_hdr;
++	struct ethhdr *eth_hdr_p;
++	u8 *src_addr;
++	u8 *dest_addr;
 +
-+	at76_dbg(DBG_RX_MGMT,
-+		 "%s: rx DisAssoc bssid %s reason 0x%04x destination %s",
-+		 priv->netdev->name, mac2str(mgmt->addr3),
-+		 le16_to_cpu(resp->reason), mac2str(mgmt->addr1));
++	i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data;
 +
-+	/* We are not connected, ignore */
-+	if (priv->mac_state == MAC_SCANNING || priv->mac_state == MAC_INIT
-+	    || !priv->curr_bss)
-+		return;
++	/* That would be the ethernet header if the hardware converted
++	 * the frame for us.  Make sure the source and the destination
++	 * match the 802.11 header.  Which hardware does it? */
++	eth_hdr_p = (struct ethhdr *)skb_pull(skb, IEEE80211_3ADDR_LEN);
 +
-+	/* Not our BSSID, ignore */
-+	if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid))
-+		return;
++	dest_addr = i802_11_hdr->addr1;
++	if (iw_mode == IW_MODE_ADHOC)
++		src_addr = i802_11_hdr->addr2;
++	else
++		src_addr = i802_11_hdr->addr3;
 +
-+	/* Not for our STA and not broadcast, ignore */
-+	if (compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1)
-+	    && !is_broadcast_ether_addr(mgmt->addr1))
-+		return;
++	if (!compare_ether_addr(eth_hdr_p->h_source, src_addr) &&
++	    !compare_ether_addr(eth_hdr_p->h_dest, dest_addr))
++		/* Yes, we already have an ethernet header */
++		skb_reset_mac_header(skb);
++	else {
++		u16 len;
 +
-+	if (priv->mac_state != MAC_ASSOC && priv->mac_state != MAC_CONNECTED
-+	    && priv->mac_state != MAC_JOINING) {
-+		printk(KERN_INFO "%s: DisAssoc in state %s ignored\n",
-+		       priv->netdev->name, mac_states[priv->mac_state]);
-+		return;
-+	}
++		/* Need to build an ethernet header */
++		if (!memcmp(skb->data, snapsig, sizeof(snapsig))) {
++			/* SNAP frame - decapsulate, keep proto */
++			skb_push(skb, offsetof(struct ethhdr, h_proto) -
++				 sizeof(rfc1042sig));
++			len = 0;
++		} else {
++			/* 802.3 frame, proto is length */
++			len = skb->len;
++			skb_push(skb, ETH_HLEN);
++		}
 +
-+	if (priv->mac_state == MAC_CONNECTED) {
-+		netif_carrier_off(priv->netdev);
-+		netif_stop_queue(priv->netdev);
-+		at76_iwevent_bss_disconnect(priv->netdev);
++		skb_reset_mac_header(skb);
++		eth_hdr_p = eth_hdr(skb);
++		/* This needs to be done in this order (eth_hdr_p->h_dest may
++		 * overlap src_addr) */
++		memcpy(eth_hdr_p->h_source, src_addr, ETH_ALEN);
++		memcpy(eth_hdr_p->h_dest, dest_addr, ETH_ALEN);
++		if (len)
++			eth_hdr_p->h_proto = htons(len);
 +	}
-+	cancel_delayed_work(&priv->dwork_get_scan);
-+	cancel_delayed_work(&priv->dwork_beacon);
-+	cancel_delayed_work(&priv->dwork_auth);
-+	cancel_delayed_work(&priv->dwork_assoc);
-+	at76_set_mac_state(priv, MAC_JOINING);
-+	schedule_work(&priv->work_join);
++
++	skb->protocol = eth_type_trans(skb, skb->dev);
 +}
 +
-+static void at76_rx_mgmt_auth(struct at76_priv *priv,
-+			      struct at76_rx_buffer *buf)
++/* Check for fragmented data in priv->rx_skb. If the packet was no fragment
++   or it was the last of a fragment set a skb containing the whole packet
++   is returned for further processing. Otherwise we get NULL and are
++   done and the packet is either stored inside the fragment buffer
++   or thrown away.  Every returned skb starts with the ieee802_11 header
++   and contains _no_ FCS at the end */
++static struct sk_buff *at76_check_for_rx_frags(struct at76_priv *priv)
 +{
-+	struct ieee80211_auth *resp = (struct ieee80211_auth *)buf->packet;
-+	struct ieee80211_hdr_3addr *mgmt = &resp->header;
-+	int seq_nr = le16_to_cpu(resp->transaction);
-+	int alg = le16_to_cpu(resp->algorithm);
-+	int status = le16_to_cpu(resp->status);
++	struct sk_buff *skb = priv->rx_skb;
++	struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data;
++	struct ieee80211_hdr_3addr *i802_11_hdr =
++	    (struct ieee80211_hdr_3addr *)buf->packet;
++	/* seq_ctrl, fragment_number, sequence number of new packet */
++	u16 sctl = le16_to_cpu(i802_11_hdr->seq_ctl);
++	u16 fragnr = sctl & 0xf;
++	u16 seqnr = sctl >> 4;
++	u16 frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl);
 +
-+	at76_dbg(DBG_RX_MGMT,
-+		 "%s: rx AuthFrame bssid %s alg %d seq_nr %d status %d "
-+		 "destination %s", priv->netdev->name, mac2str(mgmt->addr3),
-+		 alg, seq_nr, status, mac2str(mgmt->addr1));
++	/* Length including the IEEE802.11 header, but without the trailing
++	 * FCS and without the Atmel Rx header */
++	int length = le16_to_cpu(buf->wlength) - IEEE80211_FCS_LEN;
 +
-+	if (alg == WLAN_AUTH_SHARED_KEY && seq_nr == 2)
-+		at76_dbg(DBG_RX_MGMT, "%s: AuthFrame challenge %s ...",
-+			 priv->netdev->name, hex2str(resp->info_element, 18));
++	/* where does the data payload start in skb->data ? */
++	u8 *data = i802_11_hdr->payload;
 +
-+	if (priv->mac_state != MAC_AUTH) {
-+		printk(KERN_INFO "%s: ignored AuthFrame in state %s\n",
-+		       priv->netdev->name, mac_states[priv->mac_state]);
-+		return;
-+	}
-+	if (priv->auth_mode != alg) {
-+		printk(KERN_INFO "%s: ignored AuthFrame for alg %d\n",
-+		       priv->netdev->name, alg);
-+		return;
-+	}
++	/* length of payload, excl. the trailing FCS */
++	int data_len = length - IEEE80211_3ADDR_LEN;
 +
-+	BUG_ON(!priv->curr_bss);
++	int i;
++	struct rx_data_buf *bptr, *optr;
++	unsigned long oldest = ~0UL;
 +
-+	/* Not our BSSID or not for our STA, ignore */
-+	if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid)
-+	    || compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1))
-+		return;
++	at76_dbg(DBG_RX_FRAGS,
++		 "%s: rx data frame_ctl %04x addr2 %s seq/frag %d/%d "
++		 "length %d data %d: %s ...", priv->netdev->name, frame_ctl,
++		 mac2str(i802_11_hdr->addr2), seqnr, fragnr, length, data_len,
++		 hex2str(data, 32));
 +
-+	cancel_delayed_work(&priv->dwork_auth);
-+	if (status != WLAN_STATUS_SUCCESS) {
-+		/* try to join next bss */
-+		at76_set_mac_state(priv, MAC_JOINING);
-+		schedule_work(&priv->work_join);
-+		return;
-+	}
++	at76_dbg(DBG_RX_FRAGS_SKB, "%s: incoming skb: head %p data %p "
++		 "tail %p end %p len %d", priv->netdev->name, skb->head,
++		 skb->data, skb_tail_pointer(skb), skb_end_pointer(skb),
++		 skb->len);
 +
-+	if (priv->auth_mode == WLAN_AUTH_OPEN || seq_nr == 4) {
-+		priv->retries = ASSOC_RETRIES;
-+		at76_set_mac_state(priv, MAC_ASSOC);
-+		at76_assoc_req(priv, priv->curr_bss);
-+		at76_dbg(DBG_MGMT_TIMER,
-+			 "%s:%d: starting mgmt_timer + HZ", __func__, __LINE__);
-+		schedule_delayed_work(&priv->dwork_assoc, ASSOC_TIMEOUT);
-+		return;
++	if (data_len < 0) {
++		/* make sure data starts in the buffer */
++		printk(KERN_INFO "%s: data frame too short\n",
++		       priv->netdev->name);
++		return NULL;
 +	}
 +
-+	WARN_ON(seq_nr != 2);
-+	at76_auth_req(priv, priv->curr_bss, seq_nr + 1, resp->info_element);
-+	at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __func__,
-+		 __LINE__);
-+	schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT);
-+}
++	WARN_ON(length <= AT76_RX_HDRLEN);
++	if (length <= AT76_RX_HDRLEN)
++		return NULL;
++
++	/* remove the at76_rx_buffer header - we don't need it anymore */
++	/* we need the IEEE802.11 header (for the addresses) if this packet
++	   is the first of a chain */
++	skb_pull(skb, AT76_RX_HDRLEN);
 +
-+static void at76_rx_mgmt_deauth(struct at76_priv *priv,
-+				struct at76_rx_buffer *buf)
-+{
-+	struct ieee80211_disassoc *resp =
-+	    (struct ieee80211_disassoc *)buf->packet;
-+	struct ieee80211_hdr_3addr *mgmt = &resp->header;
++	/* remove FCS at end */
++	skb_trim(skb, length);
 +
-+	at76_dbg(DBG_RX_MGMT | DBG_PROGRESS,
-+		 "%s: rx DeAuth bssid %s reason 0x%04x destination %s",
-+		 priv->netdev->name, mac2str(mgmt->addr3),
-+		 le16_to_cpu(resp->reason), mac2str(mgmt->addr1));
++	at76_dbg(DBG_RX_FRAGS_SKB, "%s: trimmed skb: head %p data %p tail %p "
++		 "end %p len %d data %p data_len %d", priv->netdev->name,
++		 skb->head, skb->data, skb_tail_pointer(skb),
++		 skb_end_pointer(skb), skb->len, data, data_len);
 +
-+	if (priv->mac_state != MAC_AUTH && priv->mac_state != MAC_ASSOC
-+	    && priv->mac_state != MAC_CONNECTED) {
-+		printk(KERN_INFO "%s: DeAuth in state %s ignored\n",
-+		       priv->netdev->name, mac_states[priv->mac_state]);
-+		return;
++	if (fragnr == 0 && !(frame_ctl & IEEE80211_FCTL_MOREFRAGS)) {
++		/* unfragmented packet received */
++		/* Use a new skb for the next receive */
++		priv->rx_skb = NULL;
++		at76_dbg(DBG_RX_FRAGS, "%s: unfragmented", priv->netdev->name);
++		return skb;
 +	}
 +
-+	BUG_ON(!priv->curr_bss);
++	/* look if we've got a chain for the sender address.
++	   afterwards optr points to first free or the oldest entry,
++	   or, if i < NR_RX_DATA_BUF, bptr points to the entry for the
++	   sender address */
++	/* determining the oldest entry doesn't cope with jiffies wrapping
++	   but I don't care to delete a young entry at these rare moments ... */
 +
-+	/* Not our BSSID, ignore */
-+	if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid))
-+		return;
++	bptr = priv->rx_data;
++	optr = NULL;
++	for (i = 0; i < NR_RX_DATA_BUF; i++, bptr++) {
++		if (!bptr->skb) {
++			optr = bptr;
++			oldest = 0UL;
++			continue;
++		}
 +
-+	/* Not for our STA and not broadcast, ignore */
-+	if (compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1)
-+	    && !is_broadcast_ether_addr(mgmt->addr1))
-+		return;
++		if (!compare_ether_addr(i802_11_hdr->addr2, bptr->sender))
++			break;
 +
-+	if (priv->mac_state == MAC_CONNECTED)
-+		at76_iwevent_bss_disconnect(priv->netdev);
++		if (!optr) {
++			optr = bptr;
++			oldest = bptr->last_rx;
++		} else if (bptr->last_rx < oldest)
++			optr = bptr;
++	}
 +
-+	at76_set_mac_state(priv, MAC_JOINING);
-+	schedule_work(&priv->work_join);
-+	cancel_delayed_work(&priv->dwork_get_scan);
-+	cancel_delayed_work(&priv->dwork_beacon);
-+	cancel_delayed_work(&priv->dwork_auth);
-+	cancel_delayed_work(&priv->dwork_assoc);
-+}
++	if (i < NR_RX_DATA_BUF) {
 +
-+static void at76_rx_mgmt_beacon(struct at76_priv *priv,
-+				struct at76_rx_buffer *buf)
-+{
-+	int varpar_len;
-+	/* beacon content */
-+	struct ieee80211_beacon *bdata = (struct ieee80211_beacon *)buf->packet;
-+	struct ieee80211_hdr_3addr *mgmt = &bdata->header;
++		at76_dbg(DBG_RX_FRAGS, "%s: %d. cacheentry (seq/frag = %d/%d) "
++			 "matched sender addr",
++			 priv->netdev->name, i, bptr->seqnr, bptr->fragnr);
 +
-+	struct list_head *lptr;
-+	struct bss_info *match;	/* entry matching addr3 with its bssid */
-+	int new_entry = 0;
-+	int len;
-+	struct ieee80211_info_element *ie;
-+	int have_ssid = 0;
-+	int have_rates = 0;
-+	int have_channel = 0;
-+	int keep_going = 1;
-+	unsigned long flags;
++		/* bptr points to an entry for the sender address */
++		if (bptr->seqnr == seqnr) {
++			int left;
++			/* the fragment has the current sequence number */
++			if (((bptr->fragnr + 1) & 0xf) != fragnr) {
++				/* wrong fragment number -> ignore it */
++				/* is & 0xf necessary above ??? */
++				at76_dbg(DBG_RX_FRAGS,
++					 "%s: frag nr mismatch: %d + 1 != %d",
++					 priv->netdev->name, bptr->fragnr,
++					 fragnr);
++				return NULL;
++			}
++			bptr->last_rx = jiffies;
++			/* the next following fragment number ->
++			   add the data at the end */
 +
-+	spin_lock_irqsave(&priv->bss_list_spinlock, flags);
-+	if (priv->mac_state == MAC_CONNECTED) {
-+		/* in state MAC_CONNECTED we use the mgmt_timer to control
-+		   the beacon of the BSS */
-+		BUG_ON(!priv->curr_bss);
++			/* for test only ??? */
++			left = skb_tailroom(bptr->skb);
++			if (left < data_len)
++				printk(KERN_INFO
++				       "%s: only %d byte free (need %d)\n",
++				       priv->netdev->name, left, data_len);
++			else
++				memcpy(skb_put(bptr->skb, data_len), data,
++				       data_len);
 +
-+		if (!compare_ether_addr(priv->curr_bss->bssid, mgmt->addr3)) {
-+			/* We got our AP's beacon, defer the timeout handler.
-+			   Kill pending work first, as schedule_delayed_work()
-+			   won't do it. */
-+			cancel_delayed_work(&priv->dwork_beacon);
-+			schedule_delayed_work(&priv->dwork_beacon,
-+					      BEACON_TIMEOUT);
-+			priv->curr_bss->rssi = buf->rssi;
-+			priv->beacons_received++;
-+			goto exit;
-+		}
-+	}
++			bptr->fragnr = fragnr;
++			if (frame_ctl & IEEE80211_FCTL_MOREFRAGS)
++				return NULL;
 +
-+	/* look if we have this BSS already in the list */
-+	match = NULL;
++			/* this was the last fragment - send it */
++			skb = bptr->skb;
++			bptr->skb = NULL;	/* free the entry */
++			at76_dbg(DBG_RX_FRAGS, "%s: last frag of seq %d",
++				 priv->netdev->name, seqnr);
++			return skb;
++		}
 +
-+	if (!list_empty(&priv->bss_list)) {
-+		list_for_each(lptr, &priv->bss_list) {
-+			struct bss_info *bss_ptr =
-+			    list_entry(lptr, struct bss_info, list);
-+			if (!compare_ether_addr(bss_ptr->bssid, mgmt->addr3)) {
-+				match = bss_ptr;
-+				break;
-+			}
++		/* got another sequence number */
++		if (fragnr == 0) {
++			/* it's the start of a new chain - replace the
++			   old one by this */
++			/* bptr->sender has the correct value already */
++			at76_dbg(DBG_RX_FRAGS,
++				 "%s: start of new seq %d, removing old seq %d",
++				 priv->netdev->name, seqnr, bptr->seqnr);
++			bptr->seqnr = seqnr;
++			bptr->fragnr = 0;
++			bptr->last_rx = jiffies;
++			/* swap bptr->skb and priv->rx_skb */
++			skb = bptr->skb;
++			bptr->skb = priv->rx_skb;
++			priv->rx_skb = skb;
++		} else {
++			/* it from the middle of a new chain ->
++			   delete the old entry and skip the new one */
++			at76_dbg(DBG_RX_FRAGS,
++				 "%s: middle of new seq %d (%d) "
++				 "removing old seq %d",
++				 priv->netdev->name, seqnr, fragnr,
++				 bptr->seqnr);
++			dev_kfree_skb(bptr->skb);
++			bptr->skb = NULL;
 +		}
++		return NULL;
 +	}
 +
-+	if (!match) {
-+		/* BSS not in the list - append it */
-+		match = kzalloc(sizeof(struct bss_info), GFP_ATOMIC);
-+		if (!match) {
-+			at76_dbg(DBG_BSS_TABLE,
-+				 "%s: cannot kmalloc new bss info (%zd byte)",
-+				 priv->netdev->name, sizeof(struct bss_info));
-+			goto exit;
-+		}
-+		new_entry = 1;
-+		list_add_tail(&match->list, &priv->bss_list);
++	/* if we didn't find a chain for the sender address, optr
++	   points either to the first free or the oldest entry */
++
++	if (fragnr != 0) {
++		/* this is not the begin of a fragment chain ... */
++		at76_dbg(DBG_RX_FRAGS,
++			 "%s: no chain for non-first fragment (%d)",
++			 priv->netdev->name, fragnr);
++		return NULL;
 +	}
 +
-+	match->capa = le16_to_cpu(bdata->capability);
-+	match->beacon_interval = le16_to_cpu(bdata->beacon_interval);
-+	match->rssi = buf->rssi;
-+	match->link_qual = buf->link_quality;
-+	match->noise_level = buf->noise_level;
-+	memcpy(match->bssid, mgmt->addr3, ETH_ALEN);
-+	at76_dbg(DBG_RX_BEACON, "%s: bssid %s", priv->netdev->name,
-+		 mac2str(match->bssid));
++	BUG_ON(!optr);
++	if (optr->skb) {
++		/* swap the skb's */
++		skb = optr->skb;
++		optr->skb = priv->rx_skb;
++		priv->rx_skb = skb;
 +
-+	ie = bdata->info_element;
++		at76_dbg(DBG_RX_FRAGS,
++			 "%s: free old contents: sender %s seq/frag %d/%d",
++			 priv->netdev->name, mac2str(optr->sender),
++			 optr->seqnr, optr->fragnr);
 +
-+	/* length of var length beacon parameters */
-+	varpar_len = min_t(int, le16_to_cpu(buf->wlength) -
-+			   sizeof(struct ieee80211_beacon),
-+			   BEACON_MAX_DATA_LENGTH);
++	} else {
++		/* take the skb from priv->rx_skb */
++		optr->skb = priv->rx_skb;
++		/* let at76_submit_rx_urb() allocate a new skb */
++		priv->rx_skb = NULL;
 +
-+	/* This routine steps through the bdata->data array to get
-+	 * some useful information about the access point.
-+	 * Currently, this implementation supports receipt of: SSID,
-+	 * supported transfer rates and channel, in any order, with some
-+	 * tolerance for intermittent unknown codes (although this
-+	 * functionality may not be necessary as the useful information will
-+	 * usually arrive in consecutively, but there have been some
-+	 * reports of some of the useful information fields arriving in a
-+	 * different order).
-+	 * It does not support any more IE types although MFIE_TYPE_TIM may
-+	 * be supported (on my AP at least).
-+	 * The bdata->data array is about 1500 bytes long but only ~36 of those
-+	 * bytes are useful, hence the have_ssid etc optimizations. */
++		at76_dbg(DBG_RX_FRAGS, "%s: use a free entry",
++			 priv->netdev->name);
++	}
++	memcpy(optr->sender, i802_11_hdr->addr2, ETH_ALEN);
++	optr->seqnr = seqnr;
++	optr->fragnr = 0;
++	optr->last_rx = jiffies;
++
++	return NULL;
++}
++
++/* Rx interrupt: we expect the complete data buffer in priv->rx_skb */
++static void at76_rx_data(struct at76_priv *priv)
++{
++	struct net_device *netdev = priv->netdev;
++	struct net_device_stats *stats = &priv->stats;
++	struct sk_buff *skb = priv->rx_skb;
++	struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data;
++	struct ieee80211_hdr_3addr *i802_11_hdr;
++	int length = le16_to_cpu(buf->wlength);
 +
-+	while (keep_going &&
-+	       ((&ie->data[ie->len] - (u8 *)bdata->info_element) <=
-+		varpar_len)) {
++	at76_dbg(DBG_RX_DATA, "%s received data packet: %s", netdev->name,
++		 hex2str(skb->data, AT76_RX_HDRLEN));
 +
-+		switch (ie->id) {
++	at76_dbg(DBG_RX_DATA_CONTENT, "rx packet: %s",
++		 hex2str(skb->data + AT76_RX_HDRLEN, length));
 +
-+		case MFIE_TYPE_SSID:
-+			if (have_ssid)
-+				break;
++	skb = at76_check_for_rx_frags(priv);
++	if (!skb)
++		return;
 +
-+			len = min_t(int, IW_ESSID_MAX_SIZE, ie->len);
++	/* Atmel header and the FCS are already removed */
++	i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data;
 +
-+			/* we copy only if this is a new entry,
-+			   or the incoming SSID is not a hidden SSID. This
-+			   will protect us from overwriting a real SSID read
-+			   in a ProbeResponse with a hidden one from a
-+			   following beacon. */
-+			if (!new_entry && at76_is_hidden_ssid(ie->data, len)) {
-+				have_ssid = 1;
-+				break;
-+			}
++	skb->dev = netdev;
++	skb->ip_summed = CHECKSUM_NONE;	/* TODO: should check CRC */
 +
-+			match->ssid_len = len;
-+			memcpy(match->ssid, ie->data, len);
-+			at76_dbg(DBG_RX_BEACON, "%s: SSID - %.*s",
-+				 priv->netdev->name, len, match->ssid);
-+			have_ssid = 1;
-+			break;
++	if (is_broadcast_ether_addr(i802_11_hdr->addr1)) {
++		if (!compare_ether_addr(i802_11_hdr->addr1, netdev->broadcast))
++			skb->pkt_type = PACKET_BROADCAST;
++		else
++			skb->pkt_type = PACKET_MULTICAST;
++	} else if (compare_ether_addr(i802_11_hdr->addr1, netdev->dev_addr))
++		skb->pkt_type = PACKET_OTHERHOST;
 +
-+		case MFIE_TYPE_RATES:
-+			if (have_rates)
-+				break;
++	at76_ieee80211_to_eth(skb, priv->iw_mode);
 +
-+			match->rates_len =
-+			    min_t(int, sizeof(match->rates), ie->len);
-+			memcpy(match->rates, ie->data, match->rates_len);
-+			have_rates = 1;
-+			at76_dbg(DBG_RX_BEACON, "%s: SUPPORTED RATES %s",
-+				 priv->netdev->name,
-+				 hex2str(ie->data, ie->len));
-+			break;
++	netdev->last_rx = jiffies;
++	netif_rx(skb);
++	stats->rx_packets++;
++	stats->rx_bytes += length;
 +
-+		case MFIE_TYPE_DS_SET:
-+			if (have_channel)
-+				break;
++	return;
++}
 +
-+			match->channel = ie->data[0];
-+			have_channel = 1;
-+			at76_dbg(DBG_RX_BEACON, "%s: CHANNEL - %d",
-+				 priv->netdev->name, match->channel);
-+			break;
++static void at76_rx_monitor_mode(struct at76_priv *priv)
++{
++	struct at76_rx_radiotap *rt;
++	u8 *payload;
++	int skblen;
++	struct net_device *netdev = priv->netdev;
++	struct at76_rx_buffer *buf =
++	    (struct at76_rx_buffer *)priv->rx_skb->data;
++	/* length including the IEEE802.11 header and the trailing FCS,
++	   but not at76_rx_buffer */
++	int length = le16_to_cpu(buf->wlength);
++	struct sk_buff *skb = priv->rx_skb;
++	struct net_device_stats *stats = &priv->stats;
 +
-+		case MFIE_TYPE_CF_SET:
-+		case MFIE_TYPE_TIM:
-+		case MFIE_TYPE_IBSS_SET:
-+		default:
-+			at76_dbg(DBG_RX_BEACON, "%s: beacon IE id %d len %d %s",
-+				 priv->netdev->name, ie->id, ie->len,
-+				 hex2str(ie->data, ie->len));
-+			break;
-+		}
++	if (length < IEEE80211_FCS_LEN) {
++		/* buffer contains no data */
++		at76_dbg(DBG_MONITOR_MODE,
++			 "%s: MONITOR MODE: rx skb without data",
++			 priv->netdev->name);
++		return;
++	}
 +
-+		/* advance to the next informational element */
-+		next_ie(&ie);
++	skblen = sizeof(struct at76_rx_radiotap) + length;
 +
-+		/* Optimization: after all, the bdata->data array is
-+		 * varpar_len bytes long, whereas we get all of the useful
-+		 * information after only ~36 bytes, this saves us a lot of
-+		 * time (and trouble as the remaining portion of the array
-+		 * could be full of junk)
-+		 * Comment this out if you want to see what other information
-+		 * comes from the AP - although little of it may be useful */
++	skb = dev_alloc_skb(skblen);
++	if (!skb) {
++		printk(KERN_ERR "%s: MONITOR MODE: dev_alloc_skb for radiotap "
++		       "header returned NULL\n", priv->netdev->name);
++		return;
 +	}
 +
-+	at76_dbg(DBG_RX_BEACON, "%s: Finished processing beacon data",
-+		 priv->netdev->name);
++	skb_put(skb, skblen);
 +
-+	match->last_rx = jiffies;	/* record last rx of beacon */
++	rt = (struct at76_rx_radiotap *)skb->data;
++	payload = skb->data + sizeof(struct at76_rx_radiotap);
 +
-+exit:
-+	spin_unlock_irqrestore(&priv->bss_list_spinlock, flags);
-+}
++	rt->rt_hdr.it_version = 0;
++	rt->rt_hdr.it_pad = 0;
++	rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct at76_rx_radiotap));
++	rt->rt_hdr.it_present = cpu_to_le32(AT76_RX_RADIOTAP_PRESENT);
 +
-+/* Calculate the link level from a given rx_buffer */
-+static void at76_calc_level(struct at76_priv *priv, struct at76_rx_buffer *buf,
-+			    struct iw_quality *qual)
-+{
-+	/* just a guess for now, might be different for other chips */
-+	int max_rssi = 42;
++	rt->rt_tsft = cpu_to_le64(le32_to_cpu(buf->rx_time));
++	rt->rt_rate = hw_rates[buf->rx_rate] & (~0x80);
++	rt->rt_signal = buf->rssi;
++	rt->rt_noise = buf->noise_level;
++	rt->rt_flags = IEEE80211_RADIOTAP_F_FCS;
++	if (buf->fragmentation)
++		rt->rt_flags |= IEEE80211_RADIOTAP_F_FRAG;
 +
-+	qual->level = (buf->rssi * 100 / max_rssi);
-+	if (qual->level > 100)
-+		qual->level = 100;
-+	qual->updated |= IW_QUAL_LEVEL_UPDATED;
++	memcpy(payload, buf->packet, length);
++	skb->dev = netdev;
++	skb->ip_summed = CHECKSUM_NONE;
++	skb_reset_mac_header(skb);
++	skb->pkt_type = PACKET_OTHERHOST;
++	skb->protocol = htons(ETH_P_802_2);
++
++	netdev->last_rx = jiffies;
++	netif_rx(skb);
++	stats->rx_packets++;
++	stats->rx_bytes += length;
 +}
 +
-+/* Calculate the link quality from a given rx_buffer */
-+static void at76_calc_qual(struct at76_priv *priv, struct at76_rx_buffer *buf,
-+			   struct iw_quality *qual)
++/* Check if we spy on the sender address in buf and update stats */
++static void at76_iwspy_update(struct at76_priv *priv,
++			      struct at76_rx_buffer *buf)
 +{
-+	if (at76_is_intersil(priv->board_type))
-+		qual->qual = buf->link_quality;
-+	else {
-+		unsigned long elapsed;
++	struct ieee80211_hdr_3addr *hdr =
++	    (struct ieee80211_hdr_3addr *)buf->packet;
++	struct iw_quality qual;
 +
-+		/* Update qual at most once a second */
-+		elapsed = jiffies - priv->beacons_last_qual;
-+		if (elapsed < 1 * HZ)
-+			return;
++	/* We can only set the level here */
++	qual.updated = IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
++	qual.level = 0;
++	qual.noise = 0;
++	at76_calc_level(priv, buf, &qual);
 +
-+		qual->qual = qual->level * priv->beacons_received *
-+		    msecs_to_jiffies(priv->beacon_period) / elapsed;
++	spin_lock_bh(&priv->spy_spinlock);
 +
-+		priv->beacons_last_qual = jiffies;
-+		priv->beacons_received = 0;
-+	}
-+	qual->qual = (qual->qual > 100) ? 100 : qual->qual;
-+	qual->updated |= IW_QUAL_QUAL_UPDATED;
-+}
++	if (priv->spy_data.spy_number > 0)
++		wireless_spy_update(priv->netdev, hdr->addr2, &qual);
 +
-+/* Calculate the noise quality from a given rx_buffer */
-+static void at76_calc_noise(struct at76_priv *priv, struct at76_rx_buffer *buf,
-+			    struct iw_quality *qual)
-+{
-+	qual->noise = 0;
-+	qual->updated |= IW_QUAL_NOISE_INVALID;
++	spin_unlock_bh(&priv->spy_spinlock);
 +}
 +
-+static void at76_update_wstats(struct at76_priv *priv,
-+			       struct at76_rx_buffer *buf)
++static void at76_rx_tasklet(unsigned long param)
 +{
-+	struct iw_quality *qual = &priv->wstats.qual;
++	struct urb *urb = (struct urb *)param;
++	struct at76_priv *priv = urb->context;
++	struct net_device *netdev = priv->netdev;
++	struct at76_rx_buffer *buf;
++	struct ieee80211_hdr_3addr *i802_11_hdr;
++	u16 frame_ctl;
 +
-+	if (buf->rssi && priv->mac_state == MAC_CONNECTED) {
-+		qual->updated = 0;
-+		at76_calc_level(priv, buf, qual);
-+		at76_calc_qual(priv, buf, qual);
-+		at76_calc_noise(priv, buf, qual);
-+	} else {
-+		qual->qual = 0;
-+		qual->level = 0;
-+		qual->noise = 0;
-+		qual->updated = IW_QUAL_ALL_INVALID;
++	if (priv->device_unplugged) {
++		at76_dbg(DBG_DEVSTART, "device unplugged");
++		if (urb)
++			at76_dbg(DBG_DEVSTART, "urb status %d", urb->status);
++		return;
 +	}
-+}
 +
-+static void at76_rx_mgmt(struct at76_priv *priv, struct at76_rx_buffer *buf)
-+{
-+	struct ieee80211_hdr_3addr *mgmt =
-+	    (struct ieee80211_hdr_3addr *)buf->packet;
-+	u16 framectl = le16_to_cpu(mgmt->frame_ctl);
++	if (!priv->rx_skb || !netdev || !priv->rx_skb->data)
++		return;
 +
-+	/* update wstats */
-+	if (priv->mac_state != MAC_INIT && priv->mac_state != MAC_SCANNING) {
-+		/* jal: this is a dirty hack needed by Tim in ad-hoc mode */
-+		/* Data packets always seem to have a 0 link level, so we
-+		   only read link quality info from management packets.
-+		   Atmel driver actually averages the present, and previous
-+		   values, we just present the raw value at the moment - TJS */
-+		if (priv->iw_mode == IW_MODE_ADHOC
-+		    || (priv->curr_bss
-+			&& !compare_ether_addr(mgmt->addr3,
-+					       priv->curr_bss->bssid)))
-+			at76_update_wstats(priv, buf);
++	buf = (struct at76_rx_buffer *)priv->rx_skb->data;
++
++	i802_11_hdr = (struct ieee80211_hdr_3addr *)buf->packet;
++
++	frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl);
++
++	if (urb->status != 0) {
++		if (urb->status != -ENOENT && urb->status != -ECONNRESET)
++			at76_dbg(DBG_URB,
++				 "%s %s: - nonzero Rx bulk status received: %d",
++				 __func__, netdev->name, urb->status);
++		return;
++	}
++
++	at76_dbg(DBG_RX_ATMEL_HDR,
++		 "%s: rx frame: rate %d rssi %d noise %d link %d %s",
++		 priv->netdev->name, buf->rx_rate, buf->rssi, buf->noise_level,
++		 buf->link_quality, hex2str(i802_11_hdr, 48));
++	if (priv->iw_mode == IW_MODE_MONITOR) {
++		at76_rx_monitor_mode(priv);
++		goto exit;
 +	}
 +
-+	at76_dbg(DBG_RX_MGMT_CONTENT, "%s rx mgmt framectl 0x%x %s",
-+		 priv->netdev->name, framectl,
-+		 hex2str(mgmt, le16_to_cpu(buf->wlength)));
++	/* there is a new bssid around, accept it: */
++	if (buf->newbss && priv->iw_mode == IW_MODE_ADHOC) {
++		at76_dbg(DBG_PROGRESS, "%s: rx newbss", netdev->name);
++		schedule_work(&priv->work_new_bss);
++	}
 +
-+	switch (framectl & IEEE80211_FCTL_STYPE) {
-+	case IEEE80211_STYPE_BEACON:
-+	case IEEE80211_STYPE_PROBE_RESP:
-+		at76_rx_mgmt_beacon(priv, buf);
++	switch (frame_ctl & IEEE80211_FCTL_FTYPE) {
++	case IEEE80211_FTYPE_DATA:
++		at76_rx_data(priv);
 +		break;
 +
-+	case IEEE80211_STYPE_ASSOC_RESP:
-+		at76_rx_mgmt_assoc(priv, buf);
-+		break;
++	case IEEE80211_FTYPE_MGMT:
++		/* jal: TODO: find out if we can update iwspy also on
++		   other frames than management (might depend on the
++		   radio chip / firmware version !) */
 +
-+	case IEEE80211_STYPE_DISASSOC:
-+		at76_rx_mgmt_disassoc(priv, buf);
-+		break;
++		at76_iwspy_update(priv, buf);
 +
-+	case IEEE80211_STYPE_AUTH:
-+		at76_rx_mgmt_auth(priv, buf);
++		at76_rx_mgmt(priv, buf);
 +		break;
 +
-+	case IEEE80211_STYPE_DEAUTH:
-+		at76_rx_mgmt_deauth(priv, buf);
++	case IEEE80211_FTYPE_CTL:
++		at76_dbg(DBG_RX_CTRL, "%s: ignored ctrl frame: %04x",
++			 priv->netdev->name, frame_ctl);
 +		break;
 +
 +	default:
 +		printk(KERN_DEBUG "%s: ignoring frame with framectl 0x%04x\n",
-+		       priv->netdev->name, framectl);
++		       priv->netdev->name, frame_ctl);
 +	}
-+
-+	return;
++exit:
++	at76_submit_rx_urb(priv);
 +}
 +
-+/* Convert the 802.11 header into an ethernet-style header, make skb
-+ * ready for consumption by netif_rx() */
-+static void at76_ieee80211_to_eth(struct sk_buff *skb, int iw_mode)
++/* Load firmware into kernel memory and parse it */
++static struct fwentry *at76_load_firmware(struct usb_device *udev,
++					  enum board_type board_type)
 +{
-+	struct ieee80211_hdr_3addr *i802_11_hdr;
-+	struct ethhdr *eth_hdr_p;
-+	u8 *src_addr;
-+	u8 *dest_addr;
++	int ret;
++	char *str;
++	struct at76_fw_header *fwh;
++	struct fwentry *fwe = &firmwares[board_type];
 +
-+	i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data;
++	mutex_lock(&fw_mutex);
 +
-+	/* That would be the ethernet header if the hardware converted
-+	 * the frame for us.  Make sure the source and the destination
-+	 * match the 802.11 header.  Which hardware does it? */
-+	eth_hdr_p = (struct ethhdr *)skb_pull(skb, IEEE80211_3ADDR_LEN);
++	if (fwe->loaded) {
++		at76_dbg(DBG_FW, "re-using previously loaded fw");
++		goto exit;
++	}
 +
-+	dest_addr = i802_11_hdr->addr1;
-+	if (iw_mode == IW_MODE_ADHOC)
-+		src_addr = i802_11_hdr->addr2;
-+	else
-+		src_addr = i802_11_hdr->addr3;
++	at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname);
++	ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev);
++	if (ret < 0) {
++		dev_printk(KERN_ERR, &udev->dev, "firmware %s not found!\n",
++			   fwe->fwname);
++		dev_printk(KERN_ERR, &udev->dev,
++			   "you may need to download the firmware from "
++			   "http://developer.berlios.de/projects/at76c503a/");
++		goto exit;
++	}
 +
-+	if (!compare_ether_addr(eth_hdr_p->h_source, src_addr) &&
-+	    !compare_ether_addr(eth_hdr_p->h_dest, dest_addr))
-+		/* Yes, we already have an ethernet header */
-+		skb_reset_mac_header(skb);
-+	else {
-+		u16 len;
++	at76_dbg(DBG_FW, "got it.");
++	fwh = (struct at76_fw_header *)(fwe->fw->data);
 +
-+		/* Need to build an ethernet header */
-+		if (!memcmp(skb->data, snapsig, sizeof(snapsig))) {
-+			/* SNAP frame - decapsulate, keep proto */
-+			skb_push(skb, offsetof(struct ethhdr, h_proto) -
-+				 sizeof(rfc1042sig));
-+			len = 0;
-+		} else {
-+			/* 802.3 frame, proto is length */
-+			len = skb->len;
-+			skb_push(skb, ETH_HLEN);
-+		}
++	if (fwe->fw->size <= sizeof(*fwh)) {
++		dev_printk(KERN_ERR, &udev->dev,
++			   "firmware is too short (0x%zx)\n", fwe->fw->size);
++		goto exit;
++	}
 +
-+		skb_reset_mac_header(skb);
-+		eth_hdr_p = eth_hdr(skb);
-+		/* This needs to be done in this order (eth_hdr_p->h_dest may
-+		 * overlap src_addr) */
-+		memcpy(eth_hdr_p->h_source, src_addr, ETH_ALEN);
-+		memcpy(eth_hdr_p->h_dest, dest_addr, ETH_ALEN);
-+		if (len)
-+			eth_hdr_p->h_proto = htons(len);
++	/* CRC currently not checked */
++	fwe->board_type = le32_to_cpu(fwh->board_type);
++	if (fwe->board_type != board_type) {
++		dev_printk(KERN_ERR, &udev->dev,
++			   "board type mismatch, requested %u, got %u\n",
++			   board_type, fwe->board_type);
++		goto exit;
 +	}
 +
-+	skb->protocol = eth_type_trans(skb, skb->dev);
-+}
++	fwe->fw_version.major = fwh->major;
++	fwe->fw_version.minor = fwh->minor;
++	fwe->fw_version.patch = fwh->patch;
++	fwe->fw_version.build = fwh->build;
 +
-+/* Check for fragmented data in priv->rx_skb. If the packet was no fragment
-+   or it was the last of a fragment set a skb containing the whole packet
-+   is returned for further processing. Otherwise we get NULL and are
-+   done and the packet is either stored inside the fragment buffer
-+   or thrown away.  Every returned skb starts with the ieee802_11 header
-+   and contains _no_ FCS at the end */
-+static struct sk_buff *at76_check_for_rx_frags(struct at76_priv *priv)
-+{
-+	struct sk_buff *skb = priv->rx_skb;
-+	struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data;
-+	struct ieee80211_hdr_3addr *i802_11_hdr =
-+	    (struct ieee80211_hdr_3addr *)buf->packet;
-+	/* seq_ctrl, fragment_number, sequence number of new packet */
-+	u16 sctl = le16_to_cpu(i802_11_hdr->seq_ctl);
-+	u16 fragnr = sctl & 0xf;
-+	u16 seqnr = sctl >> 4;
-+	u16 frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl);
++	str = (char *)fwh + le32_to_cpu(fwh->str_offset);
++	fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset);
++	fwe->intfw_size = le32_to_cpu(fwh->int_fw_len);
++	fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset);
++	fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len);
 +
-+	/* Length including the IEEE802.11 header, but without the trailing
-+	 * FCS and without the Atmel Rx header */
-+	int length = le16_to_cpu(buf->wlength) - IEEE80211_FCS_LEN;
++	fwe->loaded = 1;
 +
-+	/* where does the data payload start in skb->data ? */
-+	u8 *data = i802_11_hdr->payload;
++	dev_printk(KERN_DEBUG, &udev->dev,
++		   "using firmware %s (version %d.%d.%d-%d)\n",
++		   fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build);
 +
-+	/* length of payload, excl. the trailing FCS */
-+	int data_len = length - IEEE80211_3ADDR_LEN;
++	at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type,
++		 le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len),
++		 le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len));
++	at76_dbg(DBG_DEVSTART, "firmware id %s", str);
 +
-+	int i;
-+	struct rx_data_buf *bptr, *optr;
-+	unsigned long oldest = ~0UL;
++exit:
++	mutex_unlock(&fw_mutex);
 +
-+	at76_dbg(DBG_RX_FRAGS,
-+		 "%s: rx data frame_ctl %04x addr2 %s seq/frag %d/%d "
-+		 "length %d data %d: %s ...", priv->netdev->name, frame_ctl,
-+		 mac2str(i802_11_hdr->addr2), seqnr, fragnr, length, data_len,
-+		 hex2str(data, 32));
++	if (fwe->loaded)
++		return fwe;
++	else
++		return NULL;
++}
 +
-+	at76_dbg(DBG_RX_FRAGS_SKB, "%s: incoming skb: head %p data %p "
-+		 "tail %p end %p len %d", priv->netdev->name, skb->head,
-+		 skb->data, skb_tail_pointer(skb), skb_end_pointer(skb),
-+		 skb->len);
++/* Allocate network device and initialize private data */
++static struct at76_priv *at76_alloc_new_device(struct usb_device *udev)
++{
++	struct net_device *netdev;
++	struct at76_priv *priv;
++	int i;
 +
-+	if (data_len < 0) {
-+		/* make sure data starts in the buffer */
-+		printk(KERN_INFO "%s: data frame too short\n",
-+		       priv->netdev->name);
++	/* allocate memory for our device state and initialize it */
++	netdev = alloc_etherdev(sizeof(struct at76_priv));
++	if (!netdev) {
++		dev_printk(KERN_ERR, &udev->dev, "out of memory\n");
 +		return NULL;
 +	}
 +
-+	WARN_ON(length <= AT76_RX_HDRLEN);
-+	if (length <= AT76_RX_HDRLEN)
-+		return NULL;
-+
-+	/* remove the at76_rx_buffer header - we don't need it anymore */
-+	/* we need the IEEE802.11 header (for the addresses) if this packet
-+	   is the first of a chain */
-+	skb_pull(skb, AT76_RX_HDRLEN);
++	priv = netdev_priv(netdev);
 +
-+	/* remove FCS at end */
-+	skb_trim(skb, length);
++	priv->udev = udev;
++	priv->netdev = netdev;
 +
-+	at76_dbg(DBG_RX_FRAGS_SKB, "%s: trimmed skb: head %p data %p tail %p "
-+		 "end %p len %d data %p data_len %d", priv->netdev->name,
-+		 skb->head, skb->data, skb_tail_pointer(skb),
-+		 skb_end_pointer(skb), skb->len, data, data_len);
++	mutex_init(&priv->mtx);
++	INIT_WORK(&priv->work_assoc_done, at76_work_assoc_done);
++	INIT_WORK(&priv->work_join, at76_work_join);
++	INIT_WORK(&priv->work_new_bss, at76_work_new_bss);
++	INIT_WORK(&priv->work_start_scan, at76_work_start_scan);
++	INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc);
++	INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx);
++	INIT_DELAYED_WORK(&priv->dwork_restart, at76_dwork_restart);
++	INIT_DELAYED_WORK(&priv->dwork_get_scan, at76_dwork_get_scan);
++	INIT_DELAYED_WORK(&priv->dwork_beacon, at76_dwork_beacon);
++	INIT_DELAYED_WORK(&priv->dwork_auth, at76_dwork_auth);
++	INIT_DELAYED_WORK(&priv->dwork_assoc, at76_dwork_assoc);
 +
-+	if (fragnr == 0 && !(frame_ctl & IEEE80211_FCTL_MOREFRAGS)) {
-+		/* unfragmented packet received */
-+		/* Use a new skb for the next receive */
-+		priv->rx_skb = NULL;
-+		at76_dbg(DBG_RX_FRAGS, "%s: unfragmented", priv->netdev->name);
-+		return skb;
-+	}
++	spin_lock_init(&priv->mgmt_spinlock);
++	priv->next_mgmt_bulk = NULL;
++	priv->mac_state = MAC_INIT;
 +
-+	/* look if we've got a chain for the sender address.
-+	   afterwards optr points to first free or the oldest entry,
-+	   or, if i < NR_RX_DATA_BUF, bptr points to the entry for the
-+	   sender address */
-+	/* determining the oldest entry doesn't cope with jiffies wrapping
-+	   but I don't care to delete a young entry at these rare moments ... */
++	/* initialize empty BSS list */
++	priv->curr_bss = NULL;
++	INIT_LIST_HEAD(&priv->bss_list);
++	spin_lock_init(&priv->bss_list_spinlock);
 +
-+	bptr = priv->rx_data;
-+	optr = NULL;
-+	for (i = 0; i < NR_RX_DATA_BUF; i++, bptr++) {
-+		if (!bptr->skb) {
-+			optr = bptr;
-+			oldest = 0UL;
-+			continue;
-+		}
++	init_timer(&priv->bss_list_timer);
++	priv->bss_list_timer.data = (unsigned long)priv;
++	priv->bss_list_timer.function = at76_bss_list_timeout;
 +
-+		if (!compare_ether_addr(i802_11_hdr->addr2, bptr->sender))
-+			break;
++	spin_lock_init(&priv->spy_spinlock);
 +
-+		if (!optr) {
-+			optr = bptr;
-+			oldest = bptr->last_rx;
-+		} else if (bptr->last_rx < oldest)
-+			optr = bptr;
-+	}
++	/* mark all rx data entries as unused */
++	for (i = 0; i < NR_RX_DATA_BUF; i++)
++		priv->rx_data[i].skb = NULL;
 +
-+	if (i < NR_RX_DATA_BUF) {
++	priv->rx_tasklet.func = at76_rx_tasklet;
++	priv->rx_tasklet.data = 0;
 +
-+		at76_dbg(DBG_RX_FRAGS, "%s: %d. cacheentry (seq/frag = %d/%d) "
-+			 "matched sender addr",
-+			 priv->netdev->name, i, bptr->seqnr, bptr->fragnr);
++	priv->pm_mode = AT76_PM_OFF;
++	priv->pm_period = 0;
 +
-+		/* bptr points to an entry for the sender address */
-+		if (bptr->seqnr == seqnr) {
-+			int left;
-+			/* the fragment has the current sequence number */
-+			if (((bptr->fragnr + 1) & 0xf) != fragnr) {
-+				/* wrong fragment number -> ignore it */
-+				/* is & 0xf necessary above ??? */
-+				at76_dbg(DBG_RX_FRAGS,
-+					 "%s: frag nr mismatch: %d + 1 != %d",
-+					 priv->netdev->name, bptr->fragnr,
-+					 fragnr);
-+				return NULL;
-+			}
-+			bptr->last_rx = jiffies;
-+			/* the next following fragment number ->
-+			   add the data at the end */
++	return priv;
++}
 +
-+			/* for test only ??? */
-+			left = skb_tailroom(bptr->skb);
-+			if (left < data_len)
-+				printk(KERN_INFO
-+				       "%s: only %d byte free (need %d)\n",
-+				       priv->netdev->name, left, data_len);
-+			else
-+				memcpy(skb_put(bptr->skb, data_len), data,
-+				       data_len);
++static int at76_alloc_urbs(struct at76_priv *priv,
++			   struct usb_interface *interface)
++{
++	struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out;
++	int i;
++	int buffer_size;
++	struct usb_host_interface *iface_desc;
 +
-+			bptr->fragnr = fragnr;
-+			if (frame_ctl & IEEE80211_FCTL_MOREFRAGS)
-+				return NULL;
++	at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__);
 +
-+			/* this was the last fragment - send it */
-+			skb = bptr->skb;
-+			bptr->skb = NULL;	/* free the entry */
-+			at76_dbg(DBG_RX_FRAGS, "%s: last frag of seq %d",
-+				 priv->netdev->name, seqnr);
-+			return skb;
-+		}
++	at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__,
++		 interface->altsetting[0].desc.bNumEndpoints);
 +
-+		/* got another sequence number */
-+		if (fragnr == 0) {
-+			/* it's the start of a new chain - replace the
-+			   old one by this */
-+			/* bptr->sender has the correct value already */
-+			at76_dbg(DBG_RX_FRAGS,
-+				 "%s: start of new seq %d, removing old seq %d",
-+				 priv->netdev->name, seqnr, bptr->seqnr);
-+			bptr->seqnr = seqnr;
-+			bptr->fragnr = 0;
-+			bptr->last_rx = jiffies;
-+			/* swap bptr->skb and priv->rx_skb */
-+			skb = bptr->skb;
-+			bptr->skb = priv->rx_skb;
-+			priv->rx_skb = skb;
-+		} else {
-+			/* it from the middle of a new chain ->
-+			   delete the old entry and skip the new one */
-+			at76_dbg(DBG_RX_FRAGS,
-+				 "%s: middle of new seq %d (%d) "
-+				 "removing old seq %d",
-+				 priv->netdev->name, seqnr, fragnr,
-+				 bptr->seqnr);
-+			dev_kfree_skb(bptr->skb);
-+			bptr->skb = NULL;
-+		}
-+		return NULL;
-+	}
++	ep_in = NULL;
++	ep_out = NULL;
++	iface_desc = interface->cur_altsetting;
++	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
++		endpoint = &iface_desc->endpoint[i].desc;
 +
-+	/* if we didn't find a chain for the sender address, optr
-+	   points either to the first free or the oldest entry */
++		at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x",
++			 __func__, i, endpoint->bEndpointAddress,
++			 endpoint->bmAttributes);
 +
-+	if (fragnr != 0) {
-+		/* this is not the begin of a fragment chain ... */
-+		at76_dbg(DBG_RX_FRAGS,
-+			 "%s: no chain for non-first fragment (%d)",
-+			 priv->netdev->name, fragnr);
-+		return NULL;
++		if (!ep_in && usb_endpoint_is_bulk_in(endpoint))
++			ep_in = endpoint;
++
++		if (!ep_out && usb_endpoint_is_bulk_out(endpoint))
++			ep_out = endpoint;
 +	}
 +
-+	BUG_ON(!optr);
-+	if (optr->skb) {
-+		/* swap the skb's */
-+		skb = optr->skb;
-+		optr->skb = priv->rx_skb;
-+		priv->rx_skb = skb;
++	if (!ep_in || !ep_out) {
++		dev_printk(KERN_ERR, &interface->dev,
++			   "bulk endpoints missing\n");
++		return -ENXIO;
++	}
 +
-+		at76_dbg(DBG_RX_FRAGS,
-+			 "%s: free old contents: sender %s seq/frag %d/%d",
-+			 priv->netdev->name, mac2str(optr->sender),
-+			 optr->seqnr, optr->fragnr);
++	priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress);
++	priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress);
 +
-+	} else {
-+		/* take the skb from priv->rx_skb */
-+		optr->skb = priv->rx_skb;
-+		/* let at76_submit_rx_urb() allocate a new skb */
-+		priv->rx_skb = NULL;
++	priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
++	priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
++	if (!priv->rx_urb || !priv->tx_urb) {
++		dev_printk(KERN_ERR, &interface->dev, "cannot allocate URB\n");
++		return -ENOMEM;
++	}
 +
-+		at76_dbg(DBG_RX_FRAGS, "%s: use a free entry",
-+			 priv->netdev->name);
++	buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE;
++	priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
++	if (!priv->bulk_out_buffer) {
++		dev_printk(KERN_ERR, &interface->dev,
++			   "cannot allocate output buffer\n");
++		return -ENOMEM;
 +	}
-+	memcpy(optr->sender, i802_11_hdr->addr2, ETH_ALEN);
-+	optr->seqnr = seqnr;
-+	optr->fragnr = 0;
-+	optr->last_rx = jiffies;
 +
-+	return NULL;
++	at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__);
++
++	return 0;
 +}
 +
-+/* Rx interrupt: we expect the complete data buffer in priv->rx_skb */
-+static void at76_rx_data(struct at76_priv *priv)
++/* Register network device and initialize the hardware */
++static int at76_init_new_device(struct at76_priv *priv,
++				struct usb_interface *interface)
 +{
 +	struct net_device *netdev = priv->netdev;
-+	struct net_device_stats *stats = &priv->stats;
-+	struct sk_buff *skb = priv->rx_skb;
-+	struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data;
-+	struct ieee80211_hdr_3addr *i802_11_hdr;
-+	int length = le16_to_cpu(buf->wlength);
++	int ret;
 +
-+	at76_dbg(DBG_RX_DATA, "%s received data packet: %s", netdev->name,
-+		 hex2str(skb->data, AT76_RX_HDRLEN));
++	/* set up the endpoint information */
++	/* check out the endpoints */
 +
-+	at76_dbg(DBG_RX_DATA_CONTENT, "rx packet: %s",
-+		 hex2str(skb->data + AT76_RX_HDRLEN, length));
++	at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints",
++		 interface->cur_altsetting->desc.bNumEndpoints);
 +
-+	skb = at76_check_for_rx_frags(priv);
-+	if (!skb)
-+		return;
++	ret = at76_alloc_urbs(priv, interface);
++	if (ret < 0)
++		goto exit;
 +
-+	/* Atmel header and the FCS are already removed */
-+	i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data;
++	/* MAC address */
++	ret = at76_get_hw_config(priv);
++	if (ret < 0) {
++		dev_printk(KERN_ERR, &interface->dev,
++			   "cannot get MAC address\n");
++		goto exit;
++	}
 +
-+	skb->dev = netdev;
-+	skb->ip_summed = CHECKSUM_NONE;	/* TODO: should check CRC */
++	priv->domain = at76_get_reg_domain(priv->regulatory_domain);
++	/* init. netdev->dev_addr */
++	memcpy(netdev->dev_addr, priv->mac_addr, ETH_ALEN);
 +
-+	if (is_broadcast_ether_addr(i802_11_hdr->addr1)) {
-+		if (!compare_ether_addr(i802_11_hdr->addr1, netdev->broadcast))
-+			skb->pkt_type = PACKET_BROADCAST;
-+		else
-+			skb->pkt_type = PACKET_MULTICAST;
-+	} else if (compare_ether_addr(i802_11_hdr->addr1, netdev->dev_addr))
-+		skb->pkt_type = PACKET_OTHERHOST;
++	priv->channel = DEF_CHANNEL;
++	priv->iw_mode = IW_MODE_INFRA;
++	priv->rts_threshold = DEF_RTS_THRESHOLD;
++	priv->frag_threshold = DEF_FRAG_THRESHOLD;
++	priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT;
++	priv->txrate = TX_RATE_AUTO;
++	priv->preamble_type = PREAMBLE_TYPE_LONG;
++	priv->beacon_period = 100;
++	priv->beacons_last_qual = jiffies;
++	priv->auth_mode = WLAN_AUTH_OPEN;
++	priv->scan_min_time = DEF_SCAN_MIN_TIME;
++	priv->scan_max_time = DEF_SCAN_MAX_TIME;
++	priv->scan_mode = SCAN_TYPE_ACTIVE;
 +
-+	at76_ieee80211_to_eth(skb, priv->iw_mode);
++	netdev->flags &= ~IFF_MULTICAST;	/* not yet or never */
++	netdev->open = at76_open;
++	netdev->stop = at76_stop;
++	netdev->get_stats = at76_get_stats;
++	netdev->ethtool_ops = &at76_ethtool_ops;
 +
-+	netdev->last_rx = jiffies;
-+	netif_rx(skb);
-+	stats->rx_packets++;
-+	stats->rx_bytes += length;
++	/* Add pointers to enable iwspy support. */
++	priv->wireless_data.spy_data = &priv->spy_data;
++	netdev->wireless_data = &priv->wireless_data;
++
++	netdev->hard_start_xmit = at76_tx;
++	netdev->tx_timeout = at76_tx_timeout;
++	netdev->watchdog_timeo = 2 * HZ;
++	netdev->wireless_handlers = &at76_handler_def;
++	netdev->set_multicast_list = at76_set_multicast;
++	netdev->set_mac_address = at76_set_mac_address;
++	dev_alloc_name(netdev, "wlan%d");
++
++	ret = register_netdev(priv->netdev);
++	if (ret) {
++		dev_printk(KERN_ERR, &interface->dev,
++			   "cannot register netdevice (status %d)!\n", ret);
++		goto exit;
++	}
++	priv->netdev_registered = 1;
++
++	printk(KERN_INFO "%s: USB %s, MAC %s, firmware %d.%d.%d-%d\n",
++	       netdev->name, interface->dev.bus_id, mac2str(priv->mac_addr),
++	       priv->fw_version.major, priv->fw_version.minor,
++	       priv->fw_version.patch, priv->fw_version.build);
++	printk(KERN_INFO "%s: regulatory domain 0x%02x: %s\n", netdev->name,
++	       priv->regulatory_domain, priv->domain->name);
 +
-+	return;
++	/* we let this timer run the whole time this driver instance lives */
++	mod_timer(&priv->bss_list_timer, jiffies + BSS_LIST_TIMEOUT);
++
++exit:
++	return ret;
 +}
 +
-+static void at76_rx_monitor_mode(struct at76_priv *priv)
++static void at76_delete_device(struct at76_priv *priv)
 +{
-+	struct at76_rx_radiotap *rt;
-+	u8 *payload;
-+	int skblen;
-+	struct net_device *netdev = priv->netdev;
-+	struct at76_rx_buffer *buf =
-+	    (struct at76_rx_buffer *)priv->rx_skb->data;
-+	/* length including the IEEE802.11 header and the trailing FCS,
-+	   but not at76_rx_buffer */
-+	int length = le16_to_cpu(buf->wlength);
-+	struct sk_buff *skb = priv->rx_skb;
-+	struct net_device_stats *stats = &priv->stats;
-+
-+	if (length < IEEE80211_FCS_LEN) {
-+		/* buffer contains no data */
-+		at76_dbg(DBG_MONITOR_MODE,
-+			 "%s: MONITOR MODE: rx skb without data",
-+			 priv->netdev->name);
-+		return;
-+	}
++	int i;
 +
-+	skblen = sizeof(struct at76_rx_radiotap) + length;
++	at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__);
 +
-+	skb = dev_alloc_skb(skblen);
-+	if (!skb) {
-+		printk(KERN_ERR "%s: MONITOR MODE: dev_alloc_skb for radiotap "
-+		       "header returned NULL\n", priv->netdev->name);
-+		return;
-+	}
++	/* The device is gone, don't bother turning it off */
++	priv->device_unplugged = 1;
 +
-+	skb_put(skb, skblen);
++	if (priv->netdev_registered)
++		unregister_netdev(priv->netdev);
 +
-+	rt = (struct at76_rx_radiotap *)skb->data;
-+	payload = skb->data + sizeof(struct at76_rx_radiotap);
++	/* assuming we used keventd, it must quiesce too */
++	flush_scheduled_work();
 +
-+	rt->rt_hdr.it_version = 0;
-+	rt->rt_hdr.it_pad = 0;
-+	rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct at76_rx_radiotap));
-+	rt->rt_hdr.it_present = cpu_to_le32(AT76_RX_RADIOTAP_PRESENT);
++	kfree(priv->bulk_out_buffer);
 +
-+	rt->rt_tsft = cpu_to_le64(le32_to_cpu(buf->rx_time));
-+	rt->rt_rate = hw_rates[buf->rx_rate] & (~0x80);
-+	rt->rt_signal = buf->rssi;
-+	rt->rt_noise = buf->noise_level;
-+	rt->rt_flags = IEEE80211_RADIOTAP_F_FCS;
-+	if (buf->fragmentation)
-+		rt->rt_flags |= IEEE80211_RADIOTAP_F_FRAG;
++	if (priv->tx_urb) {
++		usb_kill_urb(priv->tx_urb);
++		usb_free_urb(priv->tx_urb);
++	}
++	if (priv->rx_urb) {
++		usb_kill_urb(priv->rx_urb);
++		usb_free_urb(priv->rx_urb);
++	}
 +
-+	memcpy(payload, buf->packet, length);
-+	skb->dev = netdev;
-+	skb->ip_summed = CHECKSUM_NONE;
-+	skb_reset_mac_header(skb);
-+	skb->pkt_type = PACKET_OTHERHOST;
-+	skb->protocol = htons(ETH_P_802_2);
++	at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__);
 +
-+	netdev->last_rx = jiffies;
-+	netif_rx(skb);
-+	stats->rx_packets++;
-+	stats->rx_bytes += length;
-+}
++	if (priv->rx_skb)
++		kfree_skb(priv->rx_skb);
 +
-+/* Check if we spy on the sender address in buf and update stats */
-+static void at76_iwspy_update(struct at76_priv *priv,
-+			      struct at76_rx_buffer *buf)
-+{
-+	struct ieee80211_hdr_3addr *hdr =
-+	    (struct ieee80211_hdr_3addr *)buf->packet;
-+	struct iw_quality qual;
++	at76_free_bss_list(priv);
++	del_timer_sync(&priv->bss_list_timer);
++	cancel_delayed_work(&priv->dwork_get_scan);
++	cancel_delayed_work(&priv->dwork_beacon);
++	cancel_delayed_work(&priv->dwork_auth);
++	cancel_delayed_work(&priv->dwork_assoc);
 +
-+	/* We can only set the level here */
-+	qual.updated = IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
-+	qual.level = 0;
-+	qual.noise = 0;
-+	at76_calc_level(priv, buf, &qual);
++	if (priv->mac_state == MAC_CONNECTED)
++		at76_iwevent_bss_disconnect(priv->netdev);
 +
-+	spin_lock_bh(&priv->spy_spinlock);
++	for (i = 0; i < NR_RX_DATA_BUF; i++)
++		if (priv->rx_data[i].skb) {
++			dev_kfree_skb(priv->rx_data[i].skb);
++			priv->rx_data[i].skb = NULL;
++		}
++	usb_put_dev(priv->udev);
 +
-+	if (priv->spy_data.spy_number > 0)
-+		wireless_spy_update(priv->netdev, hdr->addr2, &qual);
++	at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/netdev", __func__);
++	free_netdev(priv->netdev);	/* priv is in netdev */
 +
-+	spin_unlock_bh(&priv->spy_spinlock);
++	at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__);
 +}
 +
-+static void at76_rx_tasklet(unsigned long param)
++static int at76_probe(struct usb_interface *interface,
++		      const struct usb_device_id *id)
 +{
-+	struct urb *urb = (struct urb *)param;
-+	struct at76_priv *priv = urb->context;
-+	struct net_device *netdev = priv->netdev;
-+	struct at76_rx_buffer *buf;
-+	struct ieee80211_hdr_3addr *i802_11_hdr;
-+	u16 frame_ctl;
-+
-+	if (priv->device_unplugged) {
-+		at76_dbg(DBG_DEVSTART, "device unplugged");
-+		if (urb)
-+			at76_dbg(DBG_DEVSTART, "urb status %d", urb->status);
-+		return;
-+	}
++	int ret;
++	struct at76_priv *priv;
++	struct fwentry *fwe;
++	struct usb_device *udev;
++	int op_mode;
++	int need_ext_fw = 0;
++	struct mib_fw_version fwv;
++	int board_type = (int)id->driver_info;
 +
-+	if (!priv->rx_skb || !netdev || !priv->rx_skb->data)
-+		return;
++	udev = usb_get_dev(interface_to_usbdev(interface));
 +
-+	buf = (struct at76_rx_buffer *)priv->rx_skb->data;
++	/* Load firmware into kernel memory */
++	fwe = at76_load_firmware(udev, board_type);
++	if (!fwe) {
++		ret = -ENOENT;
++		goto error;
++	}
 +
-+	i802_11_hdr = (struct ieee80211_hdr_3addr *)buf->packet;
++	op_mode = at76_get_op_mode(udev);
 +
-+	frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl);
++	at76_dbg(DBG_DEVSTART, "opmode %d", op_mode);
 +
-+	if (urb->status != 0) {
-+		if (urb->status != -ENOENT && urb->status != -ECONNRESET)
-+			at76_dbg(DBG_URB,
-+				 "%s %s: - nonzero Rx bulk status received: %d",
-+				 __func__, netdev->name, urb->status);
-+		return;
-+	}
++	/* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ???
++	   we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */
 +
-+	at76_dbg(DBG_RX_ATMEL_HDR,
-+		 "%s: rx frame: rate %d rssi %d noise %d link %d %s",
-+		 priv->netdev->name, buf->rx_rate, buf->rssi, buf->noise_level,
-+		 buf->link_quality, hex2str(i802_11_hdr, 48));
-+	if (priv->iw_mode == IW_MODE_MONITOR) {
-+		at76_rx_monitor_mode(priv);
-+		goto exit;
++	if (op_mode == OPMODE_HW_CONFIG_MODE) {
++		dev_printk(KERN_ERR, &interface->dev,
++			   "cannot handle a device in HW_CONFIG_MODE\n");
++		ret = -EBUSY;
++		goto error;
 +	}
 +
-+	/* there is a new bssid around, accept it: */
-+	if (buf->newbss && priv->iw_mode == IW_MODE_ADHOC) {
-+		at76_dbg(DBG_PROGRESS, "%s: rx newbss", netdev->name);
-+		schedule_work(&priv->work_new_bss);
++	if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH
++	    && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) {
++		/* download internal firmware part */
++		dev_printk(KERN_DEBUG, &interface->dev,
++			   "downloading internal firmware\n");
++		ret = at76_load_internal_fw(udev, fwe);
++		if (ret < 0) {
++			dev_printk(KERN_ERR, &interface->dev,
++				   "error %d downloading internal firmware\n",
++				   ret);
++			goto error;
++		}
++		usb_put_dev(udev);
++		return ret;
 +	}
 +
-+	switch (frame_ctl & IEEE80211_FCTL_FTYPE) {
-+	case IEEE80211_FTYPE_DATA:
-+		at76_rx_data(priv);
-+		break;
-+
-+	case IEEE80211_FTYPE_MGMT:
-+		/* jal: TODO: find out if we can update iwspy also on
-+		   other frames than management (might depend on the
-+		   radio chip / firmware version !) */
-+
-+		at76_iwspy_update(priv, buf);
-+
-+		at76_rx_mgmt(priv, buf);
-+		break;
-+
-+	case IEEE80211_FTYPE_CTL:
-+		at76_dbg(DBG_RX_CTRL, "%s: ignored ctrl frame: %04x",
-+			 priv->netdev->name, frame_ctl);
-+		break;
++	/* Internal firmware already inside the device.  Get firmware
++	 * version to test if external firmware is loaded.
++	 * This works only for newer firmware, e.g. the Intersil 0.90.x
++	 * says "control timeout on ep0in" and subsequent
++	 * at76_get_op_mode() fail too :-( */
 +
-+	default:
-+		printk(KERN_DEBUG "%s: ignoring frame with framectl 0x%04x\n",
-+		       priv->netdev->name, frame_ctl);
-+	}
-+exit:
-+	at76_submit_rx_urb(priv);
-+}
++	/* if version >= 0.100.x.y or device with built-in flash we can
++	 * query the device for the fw version */
++	if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100)
++	    || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) {
++		ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
++		if (ret < 0 || (fwv.major | fwv.minor) == 0)
++			need_ext_fw = 1;
++	} else
++		/* No way to check firmware version, reload to be sure */
++		need_ext_fw = 1;
 +
-+/* Load firmware into kernel memory and parse it */
-+static struct fwentry *at76_load_firmware(struct usb_device *udev,
-+					  enum board_type board_type)
-+{
-+	int ret;
-+	char *str;
-+	struct at76_fw_header *fwh;
-+	struct fwentry *fwe = &firmwares[board_type];
++	if (need_ext_fw) {
++		dev_printk(KERN_DEBUG, &interface->dev,
++			   "downloading external firmware\n");
 +
-+	mutex_lock(&fw_mutex);
++		ret = at76_load_external_fw(udev, fwe);
++		if (ret)
++			goto error;
 +
-+	if (fwe->loaded) {
-+		at76_dbg(DBG_FW, "re-using previously loaded fw");
-+		goto exit;
++		/* Re-check firmware version */
++		ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
++		if (ret < 0) {
++			dev_printk(KERN_ERR, &interface->dev,
++				   "error %d getting firmware version\n", ret);
++			goto error;
++		}
 +	}
 +
-+	at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname);
-+	ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev);
-+	if (ret < 0) {
-+		dev_printk(KERN_ERR, &udev->dev, "firmware %s not found!\n",
-+			   fwe->fwname);
-+		dev_printk(KERN_ERR, &udev->dev,
-+			   "you may need to download the firmware from "
-+			   "http://developer.berlios.de/projects/at76c503a/");
-+		goto exit;
++	priv = at76_alloc_new_device(udev);
++	if (!priv) {
++		ret = -ENOMEM;
++		goto error;
 +	}
 +
-+	at76_dbg(DBG_FW, "got it.");
-+	fwh = (struct at76_fw_header *)(fwe->fw->data);
++	SET_NETDEV_DEV(priv->netdev, &interface->dev);
++	usb_set_intfdata(interface, priv);
 +
-+	if (fwe->fw->size <= sizeof(*fwh)) {
-+		dev_printk(KERN_ERR, &udev->dev,
-+			   "firmware is too short (0x%zx)\n", fwe->fw->size);
-+		goto exit;
-+	}
++	memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version));
++	priv->board_type = board_type;
 +
-+	/* CRC currently not checked */
-+	fwe->board_type = le32_to_cpu(fwh->board_type);
-+	if (fwe->board_type != board_type) {
-+		dev_printk(KERN_ERR, &udev->dev,
-+			   "board type mismatch, requested %u, got %u\n",
-+			   board_type, fwe->board_type);
-+		goto exit;
-+	}
++	ret = at76_init_new_device(priv, interface);
++	if (ret < 0)
++		at76_delete_device(priv);
 +
-+	fwe->fw_version.major = fwh->major;
-+	fwe->fw_version.minor = fwh->minor;
-+	fwe->fw_version.patch = fwh->patch;
-+	fwe->fw_version.build = fwh->build;
++	return ret;
 +
-+	str = (char *)fwh + le32_to_cpu(fwh->str_offset);
-+	fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset);
-+	fwe->intfw_size = le32_to_cpu(fwh->int_fw_len);
-+	fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset);
-+	fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len);
++error:
++	usb_put_dev(udev);
++	return ret;
++}
 +
-+	fwe->loaded = 1;
++static void at76_disconnect(struct usb_interface *interface)
++{
++	struct at76_priv *priv;
 +
-+	dev_printk(KERN_DEBUG, &udev->dev,
-+		   "using firmware %s (version %d.%d.%d-%d)\n",
-+		   fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build);
++	priv = usb_get_intfdata(interface);
++	usb_set_intfdata(interface, NULL);
 +
-+	at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type,
-+		 le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len),
-+		 le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len));
-+	at76_dbg(DBG_DEVSTART, "firmware id %s", str);
++	/* Disconnect after loading internal firmware */
++	if (!priv)
++		return;
 +
-+exit:
-+	mutex_unlock(&fw_mutex);
++	printk(KERN_INFO "%s: disconnecting\n", priv->netdev->name);
++	at76_delete_device(priv);
++	dev_printk(KERN_INFO, &interface->dev, "disconnected\n");
++}
 +
-+	if (fwe->loaded)
-+		return fwe;
-+	else
-+		return NULL;
++/* Structure for registering this driver with the USB subsystem */
++static struct usb_driver at76_driver = {
++	.name = DRIVER_NAME,
++	.probe = at76_probe,
++	.disconnect = at76_disconnect,
++	.id_table = dev_table,
++};
++
++static int __init at76_mod_init(void)
++{
++	int result;
++
++	printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n");
++
++	mutex_init(&fw_mutex);
++
++	/* register this driver with the USB subsystem */
++	result = usb_register(&at76_driver);
++	if (result < 0)
++		printk(KERN_ERR DRIVER_NAME
++		       ": usb_register failed (status %d)\n", result);
++
++	led_trigger_register_simple("at76_usb-tx", &ledtrig_tx);
++	return result;
 +}
 +
-+/* Allocate network device and initialize private data */
-+static struct at76_priv *at76_alloc_new_device(struct usb_device *udev)
++static void __exit at76_mod_exit(void)
 +{
-+	struct net_device *netdev;
-+	struct at76_priv *priv;
 +	int i;
 +
-+	/* allocate memory for our device state and initialize it */
-+	netdev = alloc_etherdev(sizeof(struct at76_priv));
-+	if (!netdev) {
-+		dev_printk(KERN_ERR, &udev->dev, "out of memory\n");
-+		return NULL;
++	printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n");
++	usb_deregister(&at76_driver);
++	for (i = 0; i < ARRAY_SIZE(firmwares); i++) {
++		if (firmwares[i].fw)
++			release_firmware(firmwares[i].fw);
 +	}
++	led_trigger_unregister_simple(ledtrig_tx);
++}
 +
-+	priv = netdev_priv(netdev);
++module_param_named(debug, at76_debug, int, 0600);
++MODULE_PARM_DESC(debug, "Debugging level");
 +
-+	priv->udev = udev;
-+	priv->netdev = netdev;
++module_init(at76_mod_init);
++module_exit(at76_mod_exit);
 +
-+	mutex_init(&priv->mtx);
-+	INIT_WORK(&priv->work_assoc_done, at76_work_assoc_done);
-+	INIT_WORK(&priv->work_join, at76_work_join);
-+	INIT_WORK(&priv->work_new_bss, at76_work_new_bss);
-+	INIT_WORK(&priv->work_start_scan, at76_work_start_scan);
-+	INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc);
-+	INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx);
-+	INIT_DELAYED_WORK(&priv->dwork_restart, at76_dwork_restart);
-+	INIT_DELAYED_WORK(&priv->dwork_get_scan, at76_dwork_get_scan);
-+	INIT_DELAYED_WORK(&priv->dwork_beacon, at76_dwork_beacon);
-+	INIT_DELAYED_WORK(&priv->dwork_auth, at76_dwork_auth);
-+	INIT_DELAYED_WORK(&priv->dwork_assoc, at76_dwork_assoc);
++MODULE_AUTHOR("Oliver Kurth <oku at masqmail.cx>");
++MODULE_AUTHOR("Joerg Albert <joerg.albert at gmx.de>");
++MODULE_AUTHOR("Alex <alex at foogod.com>");
++MODULE_AUTHOR("Nick Jones");
++MODULE_AUTHOR("Balint Seeber <n0_5p4m_p13453 at hotmail.com>");
++MODULE_AUTHOR("Pavel Roskin <proski at gnu.org>");
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_LICENSE("GPL");
+diff -up /dev/null linux-2.6.26.noarch/drivers/net/wireless/at76_usb.h
+--- /dev/null	2008-08-01 08:46:19.471002774 -0400
++++ linux-2.6.26.noarch/drivers/net/wireless/at76_usb.h	2008-08-01 11:33:05.000000000 -0400
+@@ -0,0 +1,619 @@
++/*
++ * Copyright (c) 2002,2003 Oliver Kurth
++ *	     (c) 2003,2004 Joerg Albert <joerg.albert at gmx.de>
++ *	     (c) 2007 Guido Guenther <agx at sigxcpu.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This driver was based on information from the Sourceforge driver
++ * released and maintained by Atmel:
++ *
++ *  http://sourceforge.net/projects/atmelwlandriver/
++ *
++ * Although the code was completely re-written,
++ * it would have been impossible without Atmel's decision to
++ * release an Open Source driver (unfortunately the firmware was
++ * kept binary only). Thanks for that decision to Atmel!
++ */
 +
-+	spin_lock_init(&priv->mgmt_spinlock);
-+	priv->next_mgmt_bulk = NULL;
-+	priv->mac_state = MAC_INIT;
++#ifndef _AT76_USB_H
++#define _AT76_USB_H
 +
-+	/* initialize empty BSS list */
-+	priv->curr_bss = NULL;
-+	INIT_LIST_HEAD(&priv->bss_list);
-+	spin_lock_init(&priv->bss_list_spinlock);
++/* Board types */
++enum board_type {
++	BOARD_503_ISL3861 = 1,
++	BOARD_503_ISL3863 = 2,
++	BOARD_503 = 3,
++	BOARD_503_ACC = 4,
++	BOARD_505 = 5,
++	BOARD_505_2958 = 6,
++	BOARD_505A = 7,
++	BOARD_505AMX = 8
++};
 +
-+	init_timer(&priv->bss_list_timer);
-+	priv->bss_list_timer.data = (unsigned long)priv;
-+	priv->bss_list_timer.function = at76_bss_list_timeout;
++/* our private ioctl's */
++/* preamble length (0 - long, 1 - short, 2 - auto) */
++#define AT76_SET_SHORT_PREAMBLE		(SIOCIWFIRSTPRIV + 0)
++#define AT76_GET_SHORT_PREAMBLE		(SIOCIWFIRSTPRIV + 1)
++/* which debug channels are enabled */
++#define AT76_SET_DEBUG			(SIOCIWFIRSTPRIV + 2)
++#define AT76_GET_DEBUG			(SIOCIWFIRSTPRIV + 3)
++/* power save mode (incl. the Atmel proprietary smart save mode) */
++#define AT76_SET_POWERSAVE_MODE		(SIOCIWFIRSTPRIV + 4)
++#define AT76_GET_POWERSAVE_MODE		(SIOCIWFIRSTPRIV + 5)
++/* min and max channel times for scan */
++#define AT76_SET_SCAN_TIMES		(SIOCIWFIRSTPRIV + 6)
++#define AT76_GET_SCAN_TIMES		(SIOCIWFIRSTPRIV + 7)
++/* scan mode (0 - active, 1 - passive) */
++#define AT76_SET_SCAN_MODE		(SIOCIWFIRSTPRIV + 8)
++#define AT76_GET_SCAN_MODE		(SIOCIWFIRSTPRIV + 9)
 +
-+	spin_lock_init(&priv->spy_spinlock);
++#define CMD_STATUS_IDLE				0x00
++#define CMD_STATUS_COMPLETE			0x01
++#define CMD_STATUS_UNKNOWN			0x02
++#define CMD_STATUS_INVALID_PARAMETER		0x03
++#define CMD_STATUS_FUNCTION_NOT_SUPPORTED	0x04
++#define CMD_STATUS_TIME_OUT			0x07
++#define CMD_STATUS_IN_PROGRESS			0x08
++#define CMD_STATUS_HOST_FAILURE			0xff
++#define CMD_STATUS_SCAN_FAILED			0xf0
 +
-+	/* mark all rx data entries as unused */
-+	for (i = 0; i < NR_RX_DATA_BUF; i++)
-+		priv->rx_data[i].skb = NULL;
++/* answers to get op mode */
++#define OPMODE_NONE				0x00
++#define OPMODE_NORMAL_NIC_WITH_FLASH		0x01
++#define OPMODE_HW_CONFIG_MODE			0x02
++#define OPMODE_DFU_MODE_WITH_FLASH		0x03
++#define OPMODE_NORMAL_NIC_WITHOUT_FLASH		0x04
++
++#define CMD_SET_MIB		0x01
++#define CMD_GET_MIB		0x02
++#define CMD_SCAN		0x03
++#define CMD_JOIN		0x04
++#define CMD_START_IBSS		0x05
++#define CMD_RADIO_ON		0x06
++#define CMD_RADIO_OFF		0x07
++#define CMD_STARTUP		0x0B
++
++#define MIB_LOCAL		0x01
++#define MIB_MAC_ADDR		0x02
++#define MIB_MAC			0x03
++#define MIB_MAC_MGMT		0x05
++#define MIB_MAC_WEP		0x06
++#define MIB_PHY			0x07
++#define MIB_FW_VERSION		0x08
++#define MIB_MDOMAIN		0x09
++
++#define ADHOC_MODE		1
++#define INFRASTRUCTURE_MODE	2
++
++/* values for struct mib_local, field preamble_type */
++#define PREAMBLE_TYPE_LONG	0
++#define PREAMBLE_TYPE_SHORT	1
++#define PREAMBLE_TYPE_AUTO	2
++
++/* values for tx_rate */
++#define TX_RATE_1MBIT		0
++#define TX_RATE_2MBIT		1
++#define TX_RATE_5_5MBIT 	2
++#define TX_RATE_11MBIT		3
++#define TX_RATE_AUTO		4
 +
-+	priv->rx_tasklet.func = at76_rx_tasklet;
-+	priv->rx_tasklet.data = 0;
++/* power management modes */
++#define AT76_PM_OFF		1
++#define AT76_PM_ON		2
++#define AT76_PM_SMART		3
 +
-+	priv->pm_mode = AT76_PM_OFF;
-+	priv->pm_period = 0;
++struct hwcfg_r505 {
++	u8 cr39_values[14];
++	u8 reserved1[14];
++	u8 bb_cr[14];
++	u8 pidvid[4];
++	u8 mac_addr[ETH_ALEN];
++	u8 regulatory_domain;
++	u8 reserved2[14];
++	u8 cr15_values[14];
++	u8 reserved3[3];
++} __attribute__((packed));
 +
-+	return priv;
-+}
++struct hwcfg_rfmd {
++	u8 cr20_values[14];
++	u8 cr21_values[14];
++	u8 bb_cr[14];
++	u8 pidvid[4];
++	u8 mac_addr[ETH_ALEN];
++	u8 regulatory_domain;
++	u8 low_power_values[14];
++	u8 normal_power_values[14];
++	u8 reserved1[3];
++} __attribute__((packed));
 +
-+static int at76_alloc_urbs(struct at76_priv *priv,
-+			   struct usb_interface *interface)
-+{
-+	struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out;
-+	int i;
-+	int buffer_size;
-+	struct usb_host_interface *iface_desc;
++struct hwcfg_intersil {
++	u8 mac_addr[ETH_ALEN];
++	u8 cr31_values[14];
++	u8 cr58_values[14];
++	u8 pidvid[4];
++	u8 regulatory_domain;
++	u8 reserved[1];
++} __attribute__((packed));
 +
-+	at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__);
++union at76_hwcfg {
++	struct hwcfg_intersil i;
++	struct hwcfg_rfmd r3;
++	struct hwcfg_r505 r5;
++};
 +
-+	at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__,
-+		 interface->altsetting[0].desc.bNumEndpoints);
++#define WEP_SMALL_KEY_LEN	(40 / 8)
++#define WEP_LARGE_KEY_LEN	(104 / 8)
 +
-+	ep_in = NULL;
-+	ep_out = NULL;
-+	iface_desc = interface->cur_altsetting;
-+	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
-+		endpoint = &iface_desc->endpoint[i].desc;
++struct at76_card_config {
++	u8 exclude_unencrypted;
++	u8 promiscuous_mode;
++	u8 short_retry_limit;
++	u8 encryption_type;
++	__le16 rts_threshold;
++	__le16 fragmentation_threshold;	/* 256..2346 */
++	u8 basic_rate_set[4];
++	u8 auto_rate_fallback;	/* 0,1 */
++	u8 channel;
++	u8 privacy_invoked;
++	u8 wep_default_key_id;	/* 0..3 */
++	u8 current_ssid[32];
++	u8 wep_default_key_value[4][WEP_KEY_LEN];
++	u8 ssid_len;
++	u8 short_preamble;
++	__le16 beacon_period;
++} __attribute__((packed));
 +
-+		at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x",
-+			 __func__, i, endpoint->bEndpointAddress,
-+			 endpoint->bmAttributes);
++struct at76_command {
++	u8 cmd;
++	u8 reserved;
++	__le16 size;
++	u8 data[0];
++} __attribute__((packed));
 +
-+		if (!ep_in && usb_endpoint_is_bulk_in(endpoint))
-+			ep_in = endpoint;
++/* Length of Atmel-specific Rx header before 802.11 frame */
++#define AT76_RX_HDRLEN offsetof(struct at76_rx_buffer, packet)
 +
-+		if (!ep_out && usb_endpoint_is_bulk_out(endpoint))
-+			ep_out = endpoint;
-+	}
++struct at76_rx_buffer {
++	__le16 wlength;
++	u8 rx_rate;
++	u8 newbss;
++	u8 fragmentation;
++	u8 rssi;
++	u8 link_quality;
++	u8 noise_level;
++	__le32 rx_time;
++	u8 packet[IEEE80211_FRAME_LEN + IEEE80211_FCS_LEN];
++} __attribute__((packed));
 +
-+	if (!ep_in || !ep_out) {
-+		dev_printk(KERN_ERR, &interface->dev,
-+			   "bulk endpoints missing\n");
-+		return -ENXIO;
-+	}
++/* Length of Atmel-specific Tx header before 802.11 frame */
++#define AT76_TX_HDRLEN offsetof(struct at76_tx_buffer, packet)
 +
-+	priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress);
-+	priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress);
++struct at76_tx_buffer {
++	__le16 wlength;
++	u8 tx_rate;
++	u8 padding;
++	u8 reserved[4];
++	u8 packet[IEEE80211_FRAME_LEN + IEEE80211_FCS_LEN];
++} __attribute__((packed));
 +
-+	priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
-+	priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
-+	if (!priv->rx_urb || !priv->tx_urb) {
-+		dev_printk(KERN_ERR, &interface->dev, "cannot allocate URB\n");
-+		return -ENOMEM;
-+	}
++/* defines for scan_type below */
++#define SCAN_TYPE_ACTIVE	0
++#define SCAN_TYPE_PASSIVE	1
 +
-+	buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE;
-+	priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
-+	if (!priv->bulk_out_buffer) {
-+		dev_printk(KERN_ERR, &interface->dev,
-+			   "cannot allocate output buffer\n");
-+		return -ENOMEM;
-+	}
++struct at76_req_scan {
++	u8 bssid[ETH_ALEN];
++	u8 essid[32];
++	u8 scan_type;
++	u8 channel;
++	__le16 probe_delay;
++	__le16 min_channel_time;
++	__le16 max_channel_time;
++	u8 essid_size;
++	u8 international_scan;
++} __attribute__((packed));
 +
-+	at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__);
++struct at76_req_ibss {
++	u8 bssid[ETH_ALEN];
++	u8 essid[32];
++	u8 bss_type;
++	u8 channel;
++	u8 essid_size;
++	u8 reserved[3];
++} __attribute__((packed));
 +
-+	return 0;
-+}
++struct at76_req_join {
++	u8 bssid[ETH_ALEN];
++	u8 essid[32];
++	u8 bss_type;
++	u8 channel;
++	__le16 timeout;
++	u8 essid_size;
++	u8 reserved;
++} __attribute__((packed));
 +
-+/* Register network device and initialize the hardware */
-+static int at76_init_new_device(struct at76_priv *priv,
-+				struct usb_interface *interface)
-+{
-+	struct net_device *netdev = priv->netdev;
-+	int ret;
++struct set_mib_buffer {
++	u8 type;
++	u8 size;
++	u8 index;
++	u8 reserved;
++	union {
++		u8 byte;
++		__le16 word;
++		u8 addr[ETH_ALEN];
++	} data;
++} __attribute__((packed));
 +
-+	/* set up the endpoint information */
-+	/* check out the endpoints */
++struct mib_local {
++	u16 reserved0;
++	u8 beacon_enable;
++	u8 txautorate_fallback;
++	u8 reserved1;
++	u8 ssid_size;
++	u8 promiscuous_mode;
++	u16 reserved2;
++	u8 preamble_type;
++	u16 reserved3;
++} __attribute__((packed));
 +
-+	at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints",
-+		 interface->cur_altsetting->desc.bNumEndpoints);
++struct mib_mac_addr {
++	u8 mac_addr[ETH_ALEN];
++	u8 res[2];		/* ??? */
++	u8 group_addr[4][ETH_ALEN];
++	u8 group_addr_status[4];
++} __attribute__((packed));
 +
-+	ret = at76_alloc_urbs(priv, interface);
-+	if (ret < 0)
-+		goto exit;
++struct mib_mac {
++	__le32 max_tx_msdu_lifetime;
++	__le32 max_rx_lifetime;
++	__le16 frag_threshold;
++	__le16 rts_threshold;
++	__le16 cwmin;
++	__le16 cwmax;
++	u8 short_retry_time;
++	u8 long_retry_time;
++	u8 scan_type;		/* active or passive */
++	u8 scan_channel;
++	__le16 probe_delay;	/* delay before ProbeReq in active scan, RO */
++	__le16 min_channel_time;
++	__le16 max_channel_time;
++	__le16 listen_interval;
++	u8 desired_ssid[32];
++	u8 desired_bssid[ETH_ALEN];
++	u8 desired_bsstype;	/* ad-hoc or infrastructure */
++	u8 reserved2;
++} __attribute__((packed));
 +
-+	/* MAC address */
-+	ret = at76_get_hw_config(priv);
-+	if (ret < 0) {
-+		dev_printk(KERN_ERR, &interface->dev,
-+			   "cannot get MAC address\n");
-+		goto exit;
-+	}
++struct mib_mac_mgmt {
++	__le16 beacon_period;
++	__le16 CFP_max_duration;
++	__le16 medium_occupancy_limit;
++	__le16 station_id;	/* assoc id */
++	__le16 ATIM_window;
++	u8 CFP_mode;
++	u8 privacy_option_implemented;
++	u8 DTIM_period;
++	u8 CFP_period;
++	u8 current_bssid[ETH_ALEN];
++	u8 current_essid[32];
++	u8 current_bss_type;
++	u8 power_mgmt_mode;
++	/* rfmd and 505 */
++	u8 ibss_change;
++	u8 res;
++	u8 multi_domain_capability_implemented;
++	u8 multi_domain_capability_enabled;
++	u8 country_string[3];
++	u8 reserved[3];
++} __attribute__((packed));
 +
-+	priv->domain = at76_get_reg_domain(priv->regulatory_domain);
-+	/* init. netdev->dev_addr */
-+	memcpy(netdev->dev_addr, priv->mac_addr, ETH_ALEN);
++struct mib_mac_wep {
++	u8 privacy_invoked;	/* 0 disable encr., 1 enable encr */
++	u8 wep_default_key_id;
++	u8 wep_key_mapping_len;
++	u8 exclude_unencrypted;
++	__le32 wep_icv_error_count;
++	__le32 wep_excluded_count;
++	u8 wep_default_keyvalue[WEP_KEYS][WEP_KEY_LEN];
++	u8 encryption_level;	/* 1 for 40bit, 2 for 104bit encryption */
++} __attribute__((packed));
 +
-+	priv->channel = DEF_CHANNEL;
-+	priv->iw_mode = IW_MODE_INFRA;
-+	priv->rts_threshold = DEF_RTS_THRESHOLD;
-+	priv->frag_threshold = DEF_FRAG_THRESHOLD;
-+	priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT;
-+	priv->txrate = TX_RATE_AUTO;
-+	priv->preamble_type = PREAMBLE_TYPE_LONG;
-+	priv->beacon_period = 100;
-+	priv->beacons_last_qual = jiffies;
-+	priv->auth_mode = WLAN_AUTH_OPEN;
-+	priv->scan_min_time = DEF_SCAN_MIN_TIME;
-+	priv->scan_max_time = DEF_SCAN_MAX_TIME;
-+	priv->scan_mode = SCAN_TYPE_ACTIVE;
++struct mib_phy {
++	__le32 ed_threshold;
 +
-+	netdev->flags &= ~IFF_MULTICAST;	/* not yet or never */
-+	netdev->open = at76_open;
-+	netdev->stop = at76_stop;
-+	netdev->get_stats = at76_get_stats;
-+	netdev->ethtool_ops = &at76_ethtool_ops;
++	__le16 slot_time;
++	__le16 sifs_time;
++	__le16 preamble_length;
++	__le16 plcp_header_length;
++	__le16 mpdu_max_length;
++	__le16 cca_mode_supported;
 +
-+	/* Add pointers to enable iwspy support. */
-+	priv->wireless_data.spy_data = &priv->spy_data;
-+	netdev->wireless_data = &priv->wireless_data;
++	u8 operation_rate_set[4];
++	u8 channel_id;
++	u8 current_cca_mode;
++	u8 phy_type;
++	u8 current_reg_domain;
++} __attribute__((packed));
 +
-+	netdev->hard_start_xmit = at76_tx;
-+	netdev->tx_timeout = at76_tx_timeout;
-+	netdev->watchdog_timeo = 2 * HZ;
-+	netdev->wireless_handlers = &at76_handler_def;
-+	netdev->set_multicast_list = at76_set_multicast;
-+	netdev->set_mac_address = at76_set_mac_address;
-+	dev_alloc_name(netdev, "wlan%d");
++struct mib_fw_version {
++	u8 major;
++	u8 minor;
++	u8 patch;
++	u8 build;
++} __attribute__((packed));
 +
-+	ret = register_netdev(priv->netdev);
-+	if (ret) {
-+		dev_printk(KERN_ERR, &interface->dev,
-+			   "cannot register netdevice (status %d)!\n", ret);
-+		goto exit;
-+	}
-+	priv->netdev_registered = 1;
++struct mib_mdomain {
++	u8 tx_powerlevel[14];
++	u8 channel_list[14];	/* 0 for invalid channels */
++} __attribute__((packed));
 +
-+	printk(KERN_INFO "%s: USB %s, MAC %s, firmware %d.%d.%d-%d\n",
-+	       netdev->name, interface->dev.bus_id, mac2str(priv->mac_addr),
-+	       priv->fw_version.major, priv->fw_version.minor,
-+	       priv->fw_version.patch, priv->fw_version.build);
-+	printk(KERN_INFO "%s: regulatory domain 0x%02x: %s\n", netdev->name,
-+	       priv->regulatory_domain, priv->domain->name);
++struct at76_fw_header {
++	__le32 crc;		/* CRC32 of the whole image */
++	__le32 board_type;	/* firmware compatibility code */
++	u8 build;		/* firmware build number */
++	u8 patch;		/* firmware patch level */
++	u8 minor;		/* firmware minor version */
++	u8 major;		/* firmware major version */
++	__le32 str_offset;	/* offset of the copyright string */
++	__le32 int_fw_offset;	/* internal firmware image offset */
++	__le32 int_fw_len;	/* internal firmware image length */
++	__le32 ext_fw_offset;	/* external firmware image offset */
++	__le32 ext_fw_len;	/* external firmware image length */
++} __attribute__((packed));
 +
-+	/* we let this timer run the whole time this driver instance lives */
-+	mod_timer(&priv->bss_list_timer, jiffies + BSS_LIST_TIMEOUT);
++enum mac_state {
++	MAC_INIT,
++	MAC_SCANNING,
++	MAC_AUTH,
++	MAC_ASSOC,
++	MAC_JOINING,
++	MAC_CONNECTED,
++	MAC_OWN_IBSS
++};
 +
-+exit:
-+	return ret;
-+}
++/* a description of a regulatory domain and the allowed channels */
++struct reg_domain {
++	u16 code;
++	char const *name;
++	u32 channel_map;	/* if bit N is set, channel (N+1) is allowed */
++};
 +
-+static void at76_delete_device(struct at76_priv *priv)
-+{
-+	int i;
++/* how long do we keep a (I)BSS in the bss_list in jiffies
++   this should be long enough for the user to retrieve the table
++   (by iwlist ?) after the device started, because all entries from
++   other channels than the one the device locks on get removed, too */
++#define BSS_LIST_TIMEOUT	(120 * HZ)
++/* struct to store BSS info found during scan */
++#define BSS_LIST_MAX_RATE_LEN	32	/* 32 rates should be enough ... */
 +
-+	at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__);
++struct bss_info {
++	struct list_head list;
 +
-+	/* The device is gone, don't bother turning it off */
-+	priv->device_unplugged = 1;
++	u8 bssid[ETH_ALEN];	/* bssid */
++	u8 ssid[IW_ESSID_MAX_SIZE];	/* essid */
++	u8 ssid_len;		/* length of ssid above */
++	u8 channel;
++	u16 capa;		/* BSS capabilities */
++	u16 beacon_interval;	/* beacon interval, Kus (1024 microseconds) */
++	u8 rates[BSS_LIST_MAX_RATE_LEN];	/* supported rates in units of
++						   500 kbps, ORed with 0x80 for
++						   basic rates */
++	u8 rates_len;
 +
-+	if (priv->netdev_registered)
-+		unregister_netdev(priv->netdev);
++	/* quality of received beacon */
++	u8 rssi;
++	u8 link_qual;
++	u8 noise_level;
 +
-+	/* assuming we used keventd, it must quiesce too */
-+	flush_scheduled_work();
++	unsigned long last_rx;	/* time (jiffies) of last beacon received */
++};
 +
-+	kfree(priv->bulk_out_buffer);
++/* a rx data buffer to collect rx fragments */
++struct rx_data_buf {
++	u8 sender[ETH_ALEN];	/* sender address */
++	u16 seqnr;		/* sequence number */
++	u16 fragnr;		/* last fragment received */
++	unsigned long last_rx;	/* jiffies of last rx */
++	struct sk_buff *skb;	/* == NULL if entry is free */
++};
 +
-+	if (priv->tx_urb) {
-+		usb_kill_urb(priv->tx_urb);
-+		usb_free_urb(priv->tx_urb);
-+	}
-+	if (priv->rx_urb) {
-+		usb_kill_urb(priv->rx_urb);
-+		usb_free_urb(priv->rx_urb);
-+	}
++#define NR_RX_DATA_BUF		8
 +
-+	at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__);
++/* Data for one loaded firmware file */
++struct fwentry {
++	const char *const fwname;
++	const struct firmware *fw;
++	int extfw_size;
++	int intfw_size;
++	/* pointer to loaded firmware, no need to free */
++	u8 *extfw;		/* external firmware, extfw_size bytes long */
++	u8 *intfw;		/* internal firmware, intfw_size bytes long */
++	enum board_type board_type;	/* board type */
++	struct mib_fw_version fw_version;
++	int loaded;		/* Loaded and parsed successfully */
++};
 +
-+	if (priv->rx_skb)
-+		kfree_skb(priv->rx_skb);
++struct at76_priv {
++	struct usb_device *udev;	/* USB device pointer */
++	struct net_device *netdev;	/* net device pointer */
++	struct net_device_stats stats;	/* net device stats */
++	struct iw_statistics wstats;	/* wireless stats */
 +
-+	at76_free_bss_list(priv);
-+	del_timer_sync(&priv->bss_list_timer);
-+	cancel_delayed_work(&priv->dwork_get_scan);
-+	cancel_delayed_work(&priv->dwork_beacon);
-+	cancel_delayed_work(&priv->dwork_auth);
-+	cancel_delayed_work(&priv->dwork_assoc);
++	struct sk_buff *rx_skb;	/* skbuff for receiving data */
++	void *bulk_out_buffer;	/* buffer for sending data */
 +
-+	if (priv->mac_state == MAC_CONNECTED)
-+		at76_iwevent_bss_disconnect(priv->netdev);
++	struct urb *tx_urb;	/* URB for sending data */
++	struct urb *rx_urb;	/* URB for receiving data */
 +
-+	for (i = 0; i < NR_RX_DATA_BUF; i++)
-+		if (priv->rx_data[i].skb) {
-+			dev_kfree_skb(priv->rx_data[i].skb);
-+			priv->rx_data[i].skb = NULL;
-+		}
-+	usb_put_dev(priv->udev);
++	unsigned int tx_pipe;	/* bulk out pipe */
++	unsigned int rx_pipe;	/* bulk in pipe */
 +
-+	at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/netdev", __func__);
-+	free_netdev(priv->netdev);	/* priv is in netdev */
++	struct mutex mtx;	/* locks this structure */
 +
-+	at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__);
-+}
++	/* work queues */
++	struct work_struct work_assoc_done;
++	struct work_struct work_join;
++	struct work_struct work_new_bss;
++	struct work_struct work_start_scan;
++	struct work_struct work_set_promisc;
++	struct work_struct work_submit_rx;
++	struct delayed_work dwork_restart;
++	struct delayed_work dwork_get_scan;
++	struct delayed_work dwork_beacon;
++	struct delayed_work dwork_auth;
++	struct delayed_work dwork_assoc;
 +
-+static int at76_probe(struct usb_interface *interface,
-+		      const struct usb_device_id *id)
-+{
-+	int ret;
-+	struct at76_priv *priv;
-+	struct fwentry *fwe;
-+	struct usb_device *udev;
-+	int op_mode;
-+	int need_ext_fw = 0;
-+	struct mib_fw_version fwv;
-+	int board_type = (int)id->driver_info;
++	struct tasklet_struct rx_tasklet;
 +
-+	udev = usb_get_dev(interface_to_usbdev(interface));
++	/* the WEP stuff */
++	int wep_enabled;	/* 1 if WEP is enabled */
++	int wep_key_id;		/* key id to be used */
++	u8 wep_keys[WEP_KEYS][WEP_KEY_LEN];	/* the four WEP keys,
++						   5 or 13 bytes are used */
++	u8 wep_keys_len[WEP_KEYS];	/* the length of the above keys */
 +
-+	/* Load firmware into kernel memory */
-+	fwe = at76_load_firmware(udev, board_type);
-+	if (!fwe) {
-+		ret = -ENOENT;
-+		goto error;
-+	}
++	int channel;
++	int iw_mode;
++	u8 bssid[ETH_ALEN];
++	u8 essid[IW_ESSID_MAX_SIZE];
++	int essid_size;
++	int radio_on;
++	int promisc;
 +
-+	op_mode = at76_get_op_mode(udev);
++	int preamble_type;	/* 0 - long, 1 - short, 2 - auto */
++	int auth_mode;		/* authentication type: 0 open, 1 shared key */
++	int txrate;		/* 0,1,2,3 = 1,2,5.5,11 Mbps, 4 is auto */
++	int frag_threshold;	/* threshold for fragmentation of tx packets */
++	int rts_threshold;	/* threshold for RTS mechanism */
++	int short_retry_limit;
 +
-+	at76_dbg(DBG_DEVSTART, "opmode %d", op_mode);
++	int scan_min_time;	/* scan min channel time */
++	int scan_max_time;	/* scan max channel time */
++	int scan_mode;		/* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */
++	int scan_need_any;	/* if set, need to scan for any ESSID */
 +
-+	/* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ???
-+	   we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */
++	/* the list we got from scanning */
++	spinlock_t bss_list_spinlock;	/* protects bss_list operations */
++	struct list_head bss_list;	/* list of BSS we got beacons from */
++	struct timer_list bss_list_timer;	/* timer to purge old entries
++						   from bss_list */
++	struct bss_info *curr_bss;	/* current BSS */
++	u16 assoc_id;		/* current association ID, if associated */
 +
-+	if (op_mode == OPMODE_HW_CONFIG_MODE) {
-+		dev_printk(KERN_ERR, &interface->dev,
-+			   "cannot handle a device in HW_CONFIG_MODE\n");
-+		ret = -EBUSY;
-+		goto error;
-+	}
++	u8 wanted_bssid[ETH_ALEN];
++	int wanted_bssid_valid;	/* != 0 if wanted_bssid is to be used */
 +
-+	if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH
-+	    && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) {
-+		/* download internal firmware part */
-+		dev_printk(KERN_DEBUG, &interface->dev,
-+			   "downloading internal firmware\n");
-+		ret = at76_load_internal_fw(udev, fwe);
-+		if (ret < 0) {
-+			dev_printk(KERN_ERR, &interface->dev,
-+				   "error %d downloading internal firmware\n",
-+				   ret);
-+			goto error;
-+		}
-+		usb_put_dev(udev);
-+		return ret;
-+	}
++	/* some data for infrastructure mode only */
++	spinlock_t mgmt_spinlock;	/* this spinlock protects access to
++					   next_mgmt_bulk */
 +
-+	/* Internal firmware already inside the device.  Get firmware
-+	 * version to test if external firmware is loaded.
-+	 * This works only for newer firmware, e.g. the Intersil 0.90.x
-+	 * says "control timeout on ep0in" and subsequent
-+	 * at76_get_op_mode() fail too :-( */
++	struct at76_tx_buffer *next_mgmt_bulk;	/* pending management msg to
++						   send via bulk out */
++	enum mac_state mac_state;
++	enum {
++		SCAN_IDLE,
++		SCAN_IN_PROGRESS,
++		SCAN_COMPLETED
++	} scan_state;
++	time_t last_scan;
 +
-+	/* if version >= 0.100.x.y or device with built-in flash we can
-+	 * query the device for the fw version */
-+	if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100)
-+	    || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) {
-+		ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
-+		if (ret < 0 || (fwv.major | fwv.minor) == 0)
-+			need_ext_fw = 1;
-+	} else
-+		/* No way to check firmware version, reload to be sure */
-+		need_ext_fw = 1;
++	int retries;		/* remaining retries in case of timeout when
++				 * sending AuthReq or AssocReq */
++	u8 pm_mode;		/* power management mode */
++	u32 pm_period;		/* power management period in microseconds */
 +
-+	if (need_ext_fw) {
-+		dev_printk(KERN_DEBUG, &interface->dev,
-+			   "downloading external firmware\n");
++	struct reg_domain const *domain;	/* reg domain description */
 +
-+		ret = at76_load_external_fw(udev, fwe);
-+		if (ret)
-+			goto error;
++	/* iwspy support */
++	spinlock_t spy_spinlock;
++	struct iw_spy_data spy_data;
 +
-+		/* Re-check firmware version */
-+		ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
-+		if (ret < 0) {
-+			dev_printk(KERN_ERR, &interface->dev,
-+				   "error %d getting firmware version\n", ret);
-+			goto error;
-+		}
-+	}
++	struct iw_public_data wireless_data;
 +
-+	priv = at76_alloc_new_device(udev);
-+	if (!priv) {
-+		ret = -ENOMEM;
-+		goto error;
-+	}
++	/* These fields contain HW config provided by the device (not all of
++	 * these fields are used by all board types) */
++	u8 mac_addr[ETH_ALEN];
++	u8 regulatory_domain;
 +
-+	SET_NETDEV_DEV(priv->netdev, &interface->dev);
-+	usb_set_intfdata(interface, priv);
++	struct at76_card_config card_config;
 +
-+	memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version));
-+	priv->board_type = board_type;
++	/* store rx fragments until complete */
++	struct rx_data_buf rx_data[NR_RX_DATA_BUF];
 +
-+	ret = at76_init_new_device(priv, interface);
-+	if (ret < 0)
-+		at76_delete_device(priv);
++	enum board_type board_type;
++	struct mib_fw_version fw_version;
 +
-+	return ret;
++	unsigned int device_unplugged:1;
++	unsigned int netdev_registered:1;
++	struct set_mib_buffer mib_buf;	/* global buffer for set_mib calls */
 +
-+error:
-+	usb_put_dev(udev);
-+	return ret;
-+}
++	/* beacon counting */
++	int beacon_period;	/* period of mgmt beacons, Kus */
++	int beacons_received;
++	unsigned long beacons_last_qual;	/* time we restarted counting
++						   beacons */
++};
 +
-+static void at76_disconnect(struct usb_interface *interface)
-+{
-+	struct at76_priv *priv;
++struct at76_rx_radiotap {
++	struct ieee80211_radiotap_header rt_hdr;
++	__le64 rt_tsft;
++	u8 rt_flags;
++	u8 rt_rate;
++	s8 rt_signal;
++	s8 rt_noise;
++};
 +
-+	priv = usb_get_intfdata(interface);
-+	usb_set_intfdata(interface, NULL);
++#define AT76_RX_RADIOTAP_PRESENT		  \
++	((1 << IEEE80211_RADIOTAP_TSFT)		| \
++	(1 << IEEE80211_RADIOTAP_FLAGS)		| \
++	(1 << IEEE80211_RADIOTAP_RATE)		| \
++	(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)	| \
++	(1 << IEEE80211_RADIOTAP_DB_ANTNOISE))
 +
-+	/* Disconnect after loading internal firmware */
-+	if (!priv)
-+		return;
++#define BEACON_MAX_DATA_LENGTH	1500
 +
-+	printk(KERN_INFO "%s: disconnecting\n", priv->netdev->name);
-+	at76_delete_device(priv);
-+	dev_printk(KERN_INFO, &interface->dev, "disconnected\n");
-+}
++/* the maximum size of an AssocReq packet */
++#define ASSOCREQ_MAX_SIZE \
++  (AT76_TX_HDRLEN + sizeof(struct ieee80211_assoc_request) + \
++   1 + 1 + IW_ESSID_MAX_SIZE + 1 + 1 + 4)
 +
-+/* Structure for registering this driver with the USB subsystem */
-+static struct usb_driver at76_driver = {
-+	.name = DRIVER_NAME,
-+	.probe = at76_probe,
-+	.disconnect = at76_disconnect,
-+	.id_table = dev_table,
-+};
++/* for shared secret auth, add the challenge text size */
++#define AUTH_FRAME_SIZE (AT76_TX_HDRLEN + sizeof(struct ieee80211_auth))
 +
-+static int __init at76_mod_init(void)
-+{
-+	int result;
++/* Maximal number of AuthReq retries */
++#define AUTH_RETRIES		3
 +
-+	printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n");
++/* Maximal number of AssocReq retries */
++#define ASSOC_RETRIES		3
 +
-+	mutex_init(&fw_mutex);
++/* Beacon timeout in managed mode when we are connected */
++#define BEACON_TIMEOUT		(10 * HZ)
 +
-+	/* register this driver with the USB subsystem */
-+	result = usb_register(&at76_driver);
-+	if (result < 0)
-+		printk(KERN_ERR DRIVER_NAME
-+		       ": usb_register failed (status %d)\n", result);
++/* Timeout for authentication response */
++#define AUTH_TIMEOUT		(1 * HZ)
 +
-+	led_trigger_register_simple("at76_usb-tx", &ledtrig_tx);
-+	return result;
-+}
++/* Timeout for association response */
++#define ASSOC_TIMEOUT		(1 * HZ)
 +
-+static void __exit at76_mod_exit(void)
-+{
-+	int i;
++/* Polling interval when scan is running */
++#define SCAN_POLL_INTERVAL	(HZ / 4)
 +
-+	printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n");
-+	usb_deregister(&at76_driver);
-+	for (i = 0; i < ARRAY_SIZE(firmwares); i++) {
-+		if (firmwares[i].fw)
-+			release_firmware(firmwares[i].fw);
-+	}
-+	led_trigger_unregister_simple(ledtrig_tx);
-+}
++/* Command completion timeout */
++#define CMD_COMPLETION_TIMEOUT	(5 * HZ)
 +
-+module_param_named(debug, at76_debug, int, 0600);
-+MODULE_PARM_DESC(debug, "Debugging level");
++#define DEF_RTS_THRESHOLD	1536
++#define DEF_FRAG_THRESHOLD	1536
++#define DEF_SHORT_RETRY_LIMIT	8
++#define DEF_CHANNEL		10
++#define DEF_SCAN_MIN_TIME	10
++#define DEF_SCAN_MAX_TIME	120
 +
-+module_init(at76_mod_init);
-+module_exit(at76_mod_exit);
++#define MAX_RTS_THRESHOLD	(MAX_FRAG_THRESHOLD + 1)
 +
-+MODULE_AUTHOR("Oliver Kurth <oku at masqmail.cx>");
-+MODULE_AUTHOR("Joerg Albert <joerg.albert at gmx.de>");
-+MODULE_AUTHOR("Alex <alex at foogod.com>");
-+MODULE_AUTHOR("Nick Jones");
-+MODULE_AUTHOR("Balint Seeber <n0_5p4m_p13453 at hotmail.com>");
-+MODULE_AUTHOR("Pavel Roskin <proski at gnu.org>");
-+MODULE_DESCRIPTION(DRIVER_DESC);
-+MODULE_LICENSE("GPL");
-diff -up linux-2.6.24.noarch/drivers/net/wireless/Kconfig.orig linux-2.6.24.noarch/drivers/net/wireless/Kconfig
---- linux-2.6.24.noarch/drivers/net/wireless/Kconfig.orig	2008-02-05 22:45:16.000000000 -0500
-+++ linux-2.6.24.noarch/drivers/net/wireless/Kconfig	2008-02-05 22:46:17.000000000 -0500
-@@ -451,6 +451,14 @@ config PCMCIA_ATMEL
++/* the max padding size for tx in bytes (see calc_padding) */
++#define MAX_PADDING_SIZE	53
++
++#endif				/* _AT76_USB_H */
+diff -up linux-2.6.26.noarch/drivers/net/wireless/Kconfig.orig linux-2.6.26.noarch/drivers/net/wireless/Kconfig
+--- linux-2.6.26.noarch/drivers/net/wireless/Kconfig.orig	2008-08-01 11:32:35.000000000 -0400
++++ linux-2.6.26.noarch/drivers/net/wireless/Kconfig	2008-08-01 11:33:05.000000000 -0400
+@@ -456,6 +456,14 @@ config PCMCIA_ATMEL
  	  Enable support for PCMCIA cards containing the
  	  Atmel at76c502 and at76c504 chips.
  
@@ -6939,3 +6909,34 @@
  config AIRO_CS
  	tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
  	depends on PCMCIA && (BROKEN || !M32R) && WLAN_80211
+diff -up linux-2.6.26.noarch/drivers/net/wireless/Makefile.orig linux-2.6.26.noarch/drivers/net/wireless/Makefile
+--- linux-2.6.26.noarch/drivers/net/wireless/Makefile.orig	2008-08-01 11:32:35.000000000 -0400
++++ linux-2.6.26.noarch/drivers/net/wireless/Makefile	2008-08-01 11:33:05.000000000 -0400
+@@ -32,6 +32,8 @@ obj-$(CONFIG_ATMEL)             += atmel
+ obj-$(CONFIG_PCI_ATMEL)         += atmel_pci.o 
+ obj-$(CONFIG_PCMCIA_ATMEL)      += atmel_cs.o
+ 
++obj-$(CONFIG_USB_ATMEL)	        += at76_usb.o
++
+ obj-$(CONFIG_PRISM54)		+= prism54/
+ 
+ obj-$(CONFIG_HOSTAP)		+= hostap/
+diff -up linux-2.6.26.noarch/MAINTAINERS.orig linux-2.6.26.noarch/MAINTAINERS
+--- linux-2.6.26.noarch/MAINTAINERS.orig	2008-08-01 11:32:35.000000000 -0400
++++ linux-2.6.26.noarch/MAINTAINERS	2008-08-01 11:33:05.000000000 -0400
+@@ -781,6 +781,15 @@ W:	http://www.thekelleys.org.uk/atmel
+ W:	http://atmelwlandriver.sourceforge.net/
+ S:	Maintained
+ 
++ATMEL USB WIRELESS DRIVER
++P:	Pavel Roskin
++M:	proski at gnu.org
++L:	linux-wireless at vger.kernel.org
++L:	at76c503a-user at lists.berlios.de
++L:	at76c503a-develop at lists.berlios.de
++W:	http://at76c503a.berlios.de/
++S:	Maintained
++
+ AUDIT SUBSYSTEM
+ P:	Al Viro
+ M:	viro at zeniv.linux.org.uk

Modified: dists/trunk/linux-2.6/debian/patches/series/1~experimental.1
==============================================================================
--- dists/trunk/linux-2.6/debian/patches/series/1~experimental.1	(original)
+++ dists/trunk/linux-2.6/debian/patches/series/1~experimental.1	Wed Aug 13 10:15:53 2008
@@ -34,8 +34,7 @@
 + features/arm/ts409-export-leds.patch
 + features/arm/tsx09-fix-key-codes.patch
 + features/arm/ts409-export-reset.patch
-# at76 fails with: error: too few arguments to function 'iwe_stream_add_value'
-#+ features/all/at76.patch 
++ features/all/at76.patch 
 + bugfix/fix-hifn_795X-divdi3.patch
 + bugfix/all/mtd-prevent-physmap-from-causing-request_module-runaway-loop-modprobe-net-pf-1.patch
 + bugfix/powerpc/mm-mol.patch



More information about the Kernel-svn-changes mailing list