[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