diff options
author | Samuel Ortiz <sameo@openedhand.com> | 2007-08-23 16:52:44 +0000 |
---|---|---|
committer | Samuel Ortiz <sameo@openedhand.com> | 2007-08-23 16:52:44 +0000 |
commit | 62a6dcc798d01680668b2a37a3a882e57e3dbe94 (patch) | |
tree | be1e283d03e053c5ae8b6268ae787a7c4c70989c /meta/packages | |
parent | d1e1cc0d9ad08d726beced6e5deaf2963d4f05c4 (diff) | |
download | poky-62a6dcc798d01680668b2a37a3a882e57e3dbe94.tar.gz |
linux-rp: Add 802.11 driver for HTC Universal.
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@2547 311d38ba-8fff-0310-9ca6-ca027cbcb966
Diffstat (limited to 'meta/packages')
-rw-r--r-- | meta/packages/linux/linux-rp-2.6.22/defconfig-htcuniversal | 8 | ||||
-rw-r--r-- | meta/packages/linux/linux-rp-2.6.22/htcuni-acx.patch | 33526 | ||||
-rw-r--r-- | meta/packages/linux/linux-rp_2.6.22.bb | 2 |
3 files changed, 33535 insertions, 1 deletions
diff --git a/meta/packages/linux/linux-rp-2.6.22/defconfig-htcuniversal b/meta/packages/linux/linux-rp-2.6.22/defconfig-htcuniversal index 0df4c6a10b..04e9070b80 100644 --- a/meta/packages/linux/linux-rp-2.6.22/defconfig-htcuniversal +++ b/meta/packages/linux/linux-rp-2.6.22/defconfig-htcuniversal | |||
@@ -1,7 +1,7 @@ | |||
1 | # | 1 | # |
2 | # Automatically generated make config: don't edit | 2 | # Automatically generated make config: don't edit |
3 | # Linux kernel version: 2.6.22 | 3 | # Linux kernel version: 2.6.22 |
4 | # Thu Aug 23 14:47:39 2007 | 4 | # Thu Aug 23 16:10:44 2007 |
5 | # | 5 | # |
6 | CONFIG_ARM=y | 6 | CONFIG_ARM=y |
7 | CONFIG_SYS_SUPPORTS_APM_EMULATION=y | 7 | CONFIG_SYS_SUPPORTS_APM_EMULATION=y |
@@ -583,8 +583,14 @@ CONFIG_NETDEV_10000=y | |||
583 | # | 583 | # |
584 | # Wireless LAN | 584 | # Wireless LAN |
585 | # | 585 | # |
586 | CONFIG_NET_RADIO=y | ||
587 | # CONFIG_NET_WIRELESS_RTNETLINK is not set | ||
586 | # CONFIG_WLAN_PRE80211 is not set | 588 | # CONFIG_WLAN_PRE80211 is not set |
587 | # CONFIG_WLAN_80211 is not set | 589 | # CONFIG_WLAN_80211 is not set |
590 | CONFIG_ACX=m | ||
591 | CONFIG_ACX_MEM=y | ||
592 | # CONFIG_ACX_CS is not set | ||
593 | CONFIG_ACX_HTCUNIVERSAL=m | ||
588 | # CONFIG_WAN is not set | 594 | # CONFIG_WAN is not set |
589 | CONFIG_PPP=m | 595 | CONFIG_PPP=m |
590 | # CONFIG_PPP_MULTILINK is not set | 596 | # CONFIG_PPP_MULTILINK is not set |
diff --git a/meta/packages/linux/linux-rp-2.6.22/htcuni-acx.patch b/meta/packages/linux/linux-rp-2.6.22/htcuni-acx.patch new file mode 100644 index 0000000000..769674c935 --- /dev/null +++ b/meta/packages/linux/linux-rp-2.6.22/htcuni-acx.patch | |||
@@ -0,0 +1,33526 @@ | |||
1 | --- | ||
2 | drivers/net/wireless/Kconfig | 31 | ||
3 | drivers/net/wireless/Makefile | 2 | ||
4 | drivers/net/wireless/acx/Kconfig | 113 | ||
5 | drivers/net/wireless/acx/Makefile | 21 | ||
6 | drivers/net/wireless/acx/acx.h | 14 | ||
7 | drivers/net/wireless/acx/acx_config.h | 50 | ||
8 | drivers/net/wireless/acx/acx_func.h | 710 ++ | ||
9 | drivers/net/wireless/acx/acx_hw.h | 18 | ||
10 | drivers/net/wireless/acx/acx_struct.h | 2114 ++++++++ | ||
11 | drivers/net/wireless/acx/common.c | 7388 ++++++++++++++++++++++++++++ | ||
12 | drivers/net/wireless/acx/conv.c | 504 + | ||
13 | drivers/net/wireless/acx/cs.c | 5703 +++++++++++++++++++++ | ||
14 | drivers/net/wireless/acx/htcsable_acx.c | 118 | ||
15 | drivers/net/wireless/acx/htcuniversal_acx.c | 108 | ||
16 | drivers/net/wireless/acx/hx4700_acx.c | 108 | ||
17 | drivers/net/wireless/acx/ioctl.c | 2748 ++++++++++ | ||
18 | drivers/net/wireless/acx/mem.c | 5363 ++++++++++++++++++++ | ||
19 | drivers/net/wireless/acx/pci.c | 4234 ++++++++++++++++ | ||
20 | drivers/net/wireless/acx/rx3000_acx.c | 110 | ||
21 | drivers/net/wireless/acx/setrate.c | 213 | ||
22 | drivers/net/wireless/acx/usb.c | 1922 +++++++ | ||
23 | drivers/net/wireless/acx/wlan.c | 424 + | ||
24 | drivers/net/wireless/acx/wlan_compat.h | 260 | ||
25 | drivers/net/wireless/acx/wlan_hdr.h | 497 + | ||
26 | drivers/net/wireless/acx/wlan_mgmt.h | 582 ++ | ||
27 | 25 files changed, 33355 insertions(+) | ||
28 | |||
29 | Index: linux-2.6.22/drivers/net/wireless/acx/acx_config.h | ||
30 | =================================================================== | ||
31 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
32 | +++ linux-2.6.22/drivers/net/wireless/acx/acx_config.h 2007-08-23 18:46:40.000000000 +0200 | ||
33 | @@ -0,0 +1,50 @@ | ||
34 | +#define ACX_RELEASE "v0.3.36" | ||
35 | + | ||
36 | +/* | ||
37 | + * Test out all the channels in reg domain 0x10 | ||
38 | + */ | ||
39 | +#define ACX_ALLOW_ALLCHANNELS | ||
40 | + | ||
41 | +/* set to 0 if you don't want any debugging code to be compiled in */ | ||
42 | +/* set to 1 if you want some debugging */ | ||
43 | +/* set to 2 if you want extensive debug log */ | ||
44 | +#define ACX_DEBUG 0 | ||
45 | + | ||
46 | +/* | ||
47 | + * Since we'll be changing channels a lot | ||
48 | +#define ACX_DEFAULT_MSG (L_ASSOC|L_INIT) | ||
49 | +*/ | ||
50 | +#define ACX_DEFAULT_MSG (L_ASSOC|L_INIT) | ||
51 | + | ||
52 | +/* assume 32bit I/O width | ||
53 | + * (16bit is also compatible with Compact Flash) */ | ||
54 | +#define ACX_IO_WIDTH 32 | ||
55 | + | ||
56 | +/* Set this to 1 if you want monitor mode to use | ||
57 | + * phy header. Currently it is not useful anyway since we | ||
58 | + * don't know what useful info (if any) is in phy header. | ||
59 | + * If you want faster/smaller code, say 0 here */ | ||
60 | +#define WANT_PHY_HDR 0 | ||
61 | + | ||
62 | +/* whether to do Tx descriptor cleanup in softirq (i.e. not in IRQ | ||
63 | + * handler) or not. Note that doing it later does slightly increase | ||
64 | + * system load, so still do that stuff in the IRQ handler for now, | ||
65 | + * even if that probably means worse latency */ | ||
66 | +#define TX_CLEANUP_IN_SOFTIRQ 0 | ||
67 | + | ||
68 | +/* if you want very experimental 802.11 power save mode features */ | ||
69 | +#define POWER_SAVE_80211 0 | ||
70 | + | ||
71 | +/* if you want very early packet fragmentation bits and pieces */ | ||
72 | +#define ACX_FRAGMENTATION 0 | ||
73 | + | ||
74 | +/* Locking: */ | ||
75 | +/* very talkative */ | ||
76 | +/* #define PARANOID_LOCKING 1 */ | ||
77 | +/* normal (use when bug-free) */ | ||
78 | +#define DO_LOCKING 1 | ||
79 | +/* else locking is disabled! */ | ||
80 | + | ||
81 | +/* 0 - normal mode */ | ||
82 | +/* 1 - development/debug: probe for IEs on modprobe */ | ||
83 | +#define CMD_DISCOVERY 0 | ||
84 | Index: linux-2.6.22/drivers/net/wireless/acx/acx_func.h | ||
85 | =================================================================== | ||
86 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
87 | +++ linux-2.6.22/drivers/net/wireless/acx/acx_func.h 2007-08-23 18:34:19.000000000 +0200 | ||
88 | @@ -0,0 +1,710 @@ | ||
89 | +/*********************************************************************** | ||
90 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
91 | +** | ||
92 | +** The contents of this file are subject to the Mozilla Public | ||
93 | +** License Version 1.1 (the "License"); you may not use this file | ||
94 | +** except in compliance with the License. You may obtain a copy of | ||
95 | +** the License at http://www.mozilla.org/MPL/ | ||
96 | +** | ||
97 | +** Software distributed under the License is distributed on an "AS | ||
98 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
99 | +** implied. See the License for the specific language governing | ||
100 | +** rights and limitations under the License. | ||
101 | +** | ||
102 | +** Alternatively, the contents of this file may be used under the | ||
103 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
104 | +** case the provisions of the GPL are applicable instead of the | ||
105 | +** above. If you wish to allow the use of your version of this file | ||
106 | +** only under the terms of the GPL and not to allow others to use | ||
107 | +** your version of this file under the MPL, indicate your decision | ||
108 | +** by deleting the provisions above and replace them with the notice | ||
109 | +** and other provisions required by the GPL. If you do not delete | ||
110 | +** the provisions above, a recipient may use your version of this | ||
111 | +** file under either the MPL or the GPL. | ||
112 | +** --------------------------------------------------------------------- | ||
113 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
114 | +** made directly to: | ||
115 | +** | ||
116 | +** acx100-users@lists.sf.net | ||
117 | +** http://acx100.sf.net | ||
118 | +** --------------------------------------------------------------------- | ||
119 | +*/ | ||
120 | + | ||
121 | + | ||
122 | +/*********************************************************************** | ||
123 | +** LOGGING | ||
124 | +** | ||
125 | +** - Avoid SHOUTING needlessly. Avoid excessive verbosity. | ||
126 | +** Gradually remove messages which are old debugging aids. | ||
127 | +** | ||
128 | +** - Use printk() for messages which are to be always logged. | ||
129 | +** Supply either 'acx:' or '<devname>:' prefix so that user | ||
130 | +** can figure out who's speaking among other kernel chatter. | ||
131 | +** acx: is for general issues (e.g. "acx: no firmware image!") | ||
132 | +** while <devname>: is related to a particular device | ||
133 | +** (think about multi-card setup). Double check that message | ||
134 | +** is not confusing to the average user. | ||
135 | +** | ||
136 | +** - use printk KERN_xxx level only if message is not a WARNING | ||
137 | +** but is INFO, ERR etc. | ||
138 | +** | ||
139 | +** - Use printk_ratelimited() for messages which may flood | ||
140 | +** (e.g. "rx DUP pkt!"). | ||
141 | +** | ||
142 | +** - Use log() for messages which may be omitted (and they | ||
143 | +** _will_ be omitted in non-debug builds). Note that | ||
144 | +** message levels may be disabled at compile-time selectively, | ||
145 | +** thus select them wisely. Example: L_DEBUG is the lowest | ||
146 | +** (most likely to be compiled out) -> use for less important stuff. | ||
147 | +** | ||
148 | +** - Do not print important stuff with log(), or else people | ||
149 | +** will never build non-debug driver. | ||
150 | +** | ||
151 | +** Style: | ||
152 | +** hex: capital letters, zero filled (e.g. 0x02AC) | ||
153 | +** str: dont start from capitals, no trailing periods ("tx: queue is stopped") | ||
154 | +*/ | ||
155 | +#if ACX_DEBUG > 1 | ||
156 | + | ||
157 | +void log_fn_enter(const char *funcname); | ||
158 | +void log_fn_exit(const char *funcname); | ||
159 | +void log_fn_exit_v(const char *funcname, int v); | ||
160 | + | ||
161 | +#define FN_ENTER \ | ||
162 | + do { \ | ||
163 | + if (unlikely(acx_debug & L_FUNC)) { \ | ||
164 | + log_fn_enter(__func__); \ | ||
165 | + } \ | ||
166 | + } while (0) | ||
167 | + | ||
168 | +#define FN_EXIT1(v) \ | ||
169 | + do { \ | ||
170 | + if (unlikely(acx_debug & L_FUNC)) { \ | ||
171 | + log_fn_exit_v(__func__, v); \ | ||
172 | + } \ | ||
173 | + } while (0) | ||
174 | +#define FN_EXIT0 \ | ||
175 | + do { \ | ||
176 | + if (unlikely(acx_debug & L_FUNC)) { \ | ||
177 | + log_fn_exit(__func__); \ | ||
178 | + } \ | ||
179 | + } while (0) | ||
180 | + | ||
181 | +#else | ||
182 | + | ||
183 | +#define FN_ENTER | ||
184 | +#define FN_EXIT1(v) | ||
185 | +#define FN_EXIT0 | ||
186 | + | ||
187 | +#endif /* ACX_DEBUG > 1 */ | ||
188 | + | ||
189 | + | ||
190 | +#if ACX_DEBUG | ||
191 | + | ||
192 | +#define log(chan, args...) \ | ||
193 | + do { \ | ||
194 | + if (acx_debug & (chan)) \ | ||
195 | + printk(KERN_DEBUG args); \ | ||
196 | + } while (0) | ||
197 | +#define printk_ratelimited(args...) printk(args) | ||
198 | + | ||
199 | +#else /* Non-debug build: */ | ||
200 | + | ||
201 | +#define log(chan, args...) | ||
202 | +/* Standard way of log flood prevention */ | ||
203 | +#define printk_ratelimited(args...) \ | ||
204 | +do { \ | ||
205 | + if (printk_ratelimit()) \ | ||
206 | + printk(args); \ | ||
207 | +} while (0) | ||
208 | + | ||
209 | +#endif /* ACX_DEBUG */ | ||
210 | + | ||
211 | +void acx_print_mac(const char *head, const u8 *mac, const char *tail); | ||
212 | + | ||
213 | +/* Optimized out to nothing in non-debug build */ | ||
214 | +static inline void | ||
215 | +acxlog_mac(int level, const char *head, const u8 *mac, const char *tail) | ||
216 | +{ | ||
217 | + if (acx_debug & level) { | ||
218 | + acx_print_mac(head, mac, tail); | ||
219 | + } | ||
220 | +} | ||
221 | + | ||
222 | + | ||
223 | +/*********************************************************************** | ||
224 | +** MAC address helpers | ||
225 | +*/ | ||
226 | +static inline void | ||
227 | +MAC_COPY(u8 *mac, const u8 *src) | ||
228 | +{ | ||
229 | + *(u32*)mac = *(u32*)src; | ||
230 | + ((u16*)mac)[2] = ((u16*)src)[2]; | ||
231 | + /* kernel's memcpy will do the same: memcpy(dst, src, ETH_ALEN); */ | ||
232 | +} | ||
233 | + | ||
234 | +static inline void | ||
235 | +MAC_FILL(u8 *mac, u8 val) | ||
236 | +{ | ||
237 | + memset(mac, val, ETH_ALEN); | ||
238 | +} | ||
239 | + | ||
240 | +static inline void | ||
241 | +MAC_BCAST(u8 *mac) | ||
242 | +{ | ||
243 | + ((u16*)mac)[2] = *(u32*)mac = -1; | ||
244 | +} | ||
245 | + | ||
246 | +static inline void | ||
247 | +MAC_ZERO(u8 *mac) | ||
248 | +{ | ||
249 | + ((u16*)mac)[2] = *(u32*)mac = 0; | ||
250 | +} | ||
251 | + | ||
252 | +static inline int | ||
253 | +mac_is_equal(const u8 *a, const u8 *b) | ||
254 | +{ | ||
255 | + /* can't beat this */ | ||
256 | + return memcmp(a, b, ETH_ALEN) == 0; | ||
257 | +} | ||
258 | + | ||
259 | +static inline int | ||
260 | +mac_is_bcast(const u8 *mac) | ||
261 | +{ | ||
262 | + /* AND together 4 first bytes with sign-extended 2 last bytes | ||
263 | + ** Only bcast address gives 0xffffffff. +1 gives 0 */ | ||
264 | + return ( *(s32*)mac & ((s16*)mac)[2] ) + 1 == 0; | ||
265 | +} | ||
266 | + | ||
267 | +static inline int | ||
268 | +mac_is_zero(const u8 *mac) | ||
269 | +{ | ||
270 | + return ( *(u32*)mac | ((u16*)mac)[2] ) == 0; | ||
271 | +} | ||
272 | + | ||
273 | +static inline int | ||
274 | +mac_is_directed(const u8 *mac) | ||
275 | +{ | ||
276 | + return (mac[0] & 1)==0; | ||
277 | +} | ||
278 | + | ||
279 | +static inline int | ||
280 | +mac_is_mcast(const u8 *mac) | ||
281 | +{ | ||
282 | + return (mac[0] & 1) && !mac_is_bcast(mac); | ||
283 | +} | ||
284 | + | ||
285 | +#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X" | ||
286 | +#define MAC(bytevector) \ | ||
287 | + ((unsigned char *)bytevector)[0], \ | ||
288 | + ((unsigned char *)bytevector)[1], \ | ||
289 | + ((unsigned char *)bytevector)[2], \ | ||
290 | + ((unsigned char *)bytevector)[3], \ | ||
291 | + ((unsigned char *)bytevector)[4], \ | ||
292 | + ((unsigned char *)bytevector)[5] | ||
293 | + | ||
294 | + | ||
295 | +/*********************************************************************** | ||
296 | +** Random helpers | ||
297 | +*/ | ||
298 | +#define TO_STRING(x) #x | ||
299 | +#define STRING(x) TO_STRING(x) | ||
300 | + | ||
301 | +#define CLEAR_BIT(val, mask) ((val) &= ~(mask)) | ||
302 | +#define SET_BIT(val, mask) ((val) |= (mask)) | ||
303 | + | ||
304 | +/* undefined if v==0 */ | ||
305 | +static inline unsigned int | ||
306 | +lowest_bit(u16 v) | ||
307 | +{ | ||
308 | + unsigned int n = 0; | ||
309 | + while (!(v & 0xf)) { v>>=4; n+=4; } | ||
310 | + while (!(v & 1)) { v>>=1; n++; } | ||
311 | + return n; | ||
312 | +} | ||
313 | + | ||
314 | +/* undefined if v==0 */ | ||
315 | +static inline unsigned int | ||
316 | +highest_bit(u16 v) | ||
317 | +{ | ||
318 | + unsigned int n = 0; | ||
319 | + while (v>0xf) { v>>=4; n+=4; } | ||
320 | + while (v>1) { v>>=1; n++; } | ||
321 | + return n; | ||
322 | +} | ||
323 | + | ||
324 | +/* undefined if v==0 */ | ||
325 | +static inline int | ||
326 | +has_only_one_bit(u16 v) | ||
327 | +{ | ||
328 | + return ((v-1) ^ v) >= v; | ||
329 | +} | ||
330 | + | ||
331 | + | ||
332 | +static inline int | ||
333 | +is_hidden_essid(char *essid) | ||
334 | +{ | ||
335 | + return (('\0' == essid[0]) || | ||
336 | + ((' ' == essid[0]) && ('\0' == essid[1]))); | ||
337 | +} | ||
338 | + | ||
339 | +/*********************************************************************** | ||
340 | +** LOCKING | ||
341 | +** We have adev->sem and adev->lock. | ||
342 | +** | ||
343 | +** We employ following naming convention in order to get locking right: | ||
344 | +** | ||
345 | +** acx_e_xxxx - external entry points called from process context. | ||
346 | +** It is okay to sleep. adev->sem is to be taken on entry. | ||
347 | +** acx_i_xxxx - external entry points possibly called from atomic context. | ||
348 | +** Sleeping is not allowed (and thus down(sem) is not legal!) | ||
349 | +** acx_s_xxxx - potentially sleeping functions. Do not ever call under lock! | ||
350 | +** acx_l_xxxx - functions which expect lock to be already taken. | ||
351 | +** rest - non-sleeping functions which do not require locking | ||
352 | +** but may be run under lock | ||
353 | +** | ||
354 | +** A small number of local helpers do not have acx_[eisl]_ prefix. | ||
355 | +** They are always close to caller and are to be reviewed locally. | ||
356 | +** | ||
357 | +** Theory of operation: | ||
358 | +** | ||
359 | +** All process-context entry points (_e_ functions) take sem | ||
360 | +** immediately. IRQ handler and other 'atomic-context' entry points | ||
361 | +** (_i_ functions) take lock immediately on entry, but dont take sem | ||
362 | +** because that might sleep. | ||
363 | +** | ||
364 | +** Thus *all* code is either protected by sem or lock, or both. | ||
365 | +** | ||
366 | +** Code which must not run concurrently with IRQ takes lock. | ||
367 | +** Such code is marked with _l_. | ||
368 | +** | ||
369 | +** This results in the following rules of thumb useful in code review: | ||
370 | +** | ||
371 | +** + If a function calls _s_ fn, it must be an _s_ itself. | ||
372 | +** + You can call _l_ fn only (a) from another _l_ fn | ||
373 | +** or (b) from _s_, _e_ or _i_ fn by taking lock, calling _l_, | ||
374 | +** and dropping lock. | ||
375 | +** + All IRQ code runs under lock. | ||
376 | +** + Any _s_ fn is running under sem. | ||
377 | +** + Code under sem can race only with IRQ code. | ||
378 | +** + Code under sem+lock cannot race with anything. | ||
379 | +*/ | ||
380 | + | ||
381 | +/* These functions *must* be inline or they will break horribly on SPARC, due | ||
382 | + * to its weird semantics for save/restore flags */ | ||
383 | + | ||
384 | +#if defined(PARANOID_LOCKING) /* Lock debugging */ | ||
385 | + | ||
386 | +void acx_lock_debug(acx_device_t *adev, const char* where); | ||
387 | +void acx_unlock_debug(acx_device_t *adev, const char* where); | ||
388 | +void acx_down_debug(acx_device_t *adev, const char* where); | ||
389 | +void acx_up_debug(acx_device_t *adev, const char* where); | ||
390 | +void acx_lock_unhold(void); | ||
391 | +void acx_sem_unhold(void); | ||
392 | + | ||
393 | +static inline void | ||
394 | +acx_lock_helper(acx_device_t *adev, unsigned long *fp, const char* where) | ||
395 | +{ | ||
396 | + acx_lock_debug(adev, where); | ||
397 | + spin_lock_irqsave(&adev->lock, *fp); | ||
398 | +} | ||
399 | +static inline void | ||
400 | +acx_unlock_helper(acx_device_t *adev, unsigned long *fp, const char* where) | ||
401 | +{ | ||
402 | + acx_unlock_debug(adev, where); | ||
403 | + spin_unlock_irqrestore(&adev->lock, *fp); | ||
404 | +} | ||
405 | +static inline void | ||
406 | +acx_down_helper(acx_device_t *adev, const char* where) | ||
407 | +{ | ||
408 | + acx_down_debug(adev, where); | ||
409 | +} | ||
410 | +static inline void | ||
411 | +acx_up_helper(acx_device_t *adev, const char* where) | ||
412 | +{ | ||
413 | + acx_up_debug(adev, where); | ||
414 | +} | ||
415 | +#define acx_lock(adev, flags) acx_lock_helper(adev, &(flags), __FILE__ ":" STRING(__LINE__)) | ||
416 | +#define acx_unlock(adev, flags) acx_unlock_helper(adev, &(flags), __FILE__ ":" STRING(__LINE__)) | ||
417 | +#define acx_sem_lock(adev) acx_down_helper(adev, __FILE__ ":" STRING(__LINE__)) | ||
418 | +#define acx_sem_unlock(adev) acx_up_helper(adev, __FILE__ ":" STRING(__LINE__)) | ||
419 | + | ||
420 | +#elif defined(DO_LOCKING) | ||
421 | + | ||
422 | +#define acx_lock(adev, flags) spin_lock_irqsave(&adev->lock, flags) | ||
423 | +#define acx_unlock(adev, flags) spin_unlock_irqrestore(&adev->lock, flags) | ||
424 | +#define acx_sem_lock(adev) down(&adev->sem) | ||
425 | +#define acx_sem_unlock(adev) up(&adev->sem) | ||
426 | +#define acx_lock_unhold() ((void)0) | ||
427 | +#define acx_sem_unhold() ((void)0) | ||
428 | + | ||
429 | +#else /* no locking! :( */ | ||
430 | + | ||
431 | +#define acx_lock(adev, flags) ((void)0) | ||
432 | +#define acx_unlock(adev, flags) ((void)0) | ||
433 | +#define acx_sem_lock(adev) ((void)0) | ||
434 | +#define acx_sem_unlock(adev) ((void)0) | ||
435 | +#define acx_lock_unhold() ((void)0) | ||
436 | +#define acx_sem_unhold() ((void)0) | ||
437 | + | ||
438 | +#endif | ||
439 | + | ||
440 | + | ||
441 | +/*********************************************************************** | ||
442 | +*/ | ||
443 | + | ||
444 | +/* Can race with rx path (which is not protected by sem): | ||
445 | +** rx -> process_[re]assocresp() -> set_status(ASSOCIATED) -> wake_queue() | ||
446 | +** Can race with tx_complete IRQ: | ||
447 | +** IRQ -> acxpci_l_clean_txdesc -> acx_wake_queue | ||
448 | +** Review carefully all callsites */ | ||
449 | +static inline void | ||
450 | +acx_stop_queue(struct net_device *ndev, const char *msg) | ||
451 | +{ | ||
452 | + if (netif_queue_stopped(ndev)) | ||
453 | + return; | ||
454 | + | ||
455 | + netif_stop_queue(ndev); | ||
456 | + if (msg) | ||
457 | + log(L_BUFT, "tx: stop queue %s\n", msg); | ||
458 | +} | ||
459 | + | ||
460 | +static inline int | ||
461 | +acx_queue_stopped(struct net_device *ndev) | ||
462 | +{ | ||
463 | + return netif_queue_stopped(ndev); | ||
464 | +} | ||
465 | + | ||
466 | +/* | ||
467 | +static inline void | ||
468 | +acx_start_queue(struct net_device *ndev, const char *msg) | ||
469 | +{ | ||
470 | + netif_start_queue(ndev); | ||
471 | + if (msg) | ||
472 | + log(L_BUFT, "tx: start queue %s\n", msg); | ||
473 | +} | ||
474 | +*/ | ||
475 | + | ||
476 | +static inline void | ||
477 | +acx_wake_queue(struct net_device *ndev, const char *msg) | ||
478 | +{ | ||
479 | + netif_wake_queue(ndev); | ||
480 | + if (msg) | ||
481 | + log(L_BUFT, "tx: wake queue %s\n", msg); | ||
482 | +} | ||
483 | + | ||
484 | +static inline void | ||
485 | +acx_carrier_off(struct net_device *ndev, const char *msg) | ||
486 | +{ | ||
487 | + netif_carrier_off(ndev); | ||
488 | + if (msg) | ||
489 | + log(L_BUFT, "tx: carrier off %s\n", msg); | ||
490 | +} | ||
491 | + | ||
492 | +static inline void | ||
493 | +acx_carrier_on(struct net_device *ndev, const char *msg) | ||
494 | +{ | ||
495 | + netif_carrier_on(ndev); | ||
496 | + if (msg) | ||
497 | + log(L_BUFT, "tx: carrier on %s\n", msg); | ||
498 | +} | ||
499 | + | ||
500 | +/* This function does not need locking UNLESS you call it | ||
501 | +** as acx_set_status(ACX_STATUS_4_ASSOCIATED), bacause this can | ||
502 | +** wake queue. This can race with stop_queue elsewhere. */ | ||
503 | +void acx_set_status(acx_device_t *adev, u16 status); | ||
504 | + | ||
505 | + | ||
506 | +/*********************************************************************** | ||
507 | +** Communication with firmware | ||
508 | +*/ | ||
509 | +#define CMD_TIMEOUT_MS(n) (n) | ||
510 | +#define ACX_CMD_TIMEOUT_DEFAULT CMD_TIMEOUT_MS(50) | ||
511 | + | ||
512 | +#if ACX_DEBUG | ||
513 | + | ||
514 | +/* We want to log cmd names */ | ||
515 | +int acxpci_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr); | ||
516 | +int acxmem_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr); | ||
517 | +int acxusb_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr); | ||
518 | +static inline int | ||
519 | +acx_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr) | ||
520 | +{ | ||
521 | + if (IS_MEM(adev)) | ||
522 | + return acxmem_s_issue_cmd_timeo_debug(adev, cmd, param, len, timeout, cmdstr); | ||
523 | + if (IS_PCI(adev)) | ||
524 | + return acxpci_s_issue_cmd_timeo_debug(adev, cmd, param, len, timeout, cmdstr); | ||
525 | + return acxusb_s_issue_cmd_timeo_debug(adev, cmd, param, len, timeout, cmdstr); | ||
526 | +} | ||
527 | +#define acx_s_issue_cmd(adev,cmd,param,len) \ | ||
528 | + acx_s_issue_cmd_timeo_debug(adev,cmd,param,len,ACX_CMD_TIMEOUT_DEFAULT,#cmd) | ||
529 | +#define acx_s_issue_cmd_timeo(adev,cmd,param,len,timeo) \ | ||
530 | + acx_s_issue_cmd_timeo_debug(adev,cmd,param,len,timeo,#cmd) | ||
531 | +int acx_s_configure_debug(acx_device_t *adev, void *pdr, int type, const char* str); | ||
532 | +#define acx_s_configure(adev,pdr,type) \ | ||
533 | + acx_s_configure_debug(adev,pdr,type,#type) | ||
534 | +int acx_s_interrogate_debug(acx_device_t *adev, void *pdr, int type, const char* str); | ||
535 | +#define acx_s_interrogate(adev,pdr,type) \ | ||
536 | + acx_s_interrogate_debug(adev,pdr,type,#type) | ||
537 | + | ||
538 | +#else | ||
539 | + | ||
540 | +int acxpci_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout); | ||
541 | +int acxmem_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout); | ||
542 | +int acxusb_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout); | ||
543 | +static inline int | ||
544 | +acx_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout) | ||
545 | +{ | ||
546 | + if (IS_MEM(adev)) | ||
547 | + return acxmem_s_issue_cmd_timeo(adev, cmd, param, len, timeout); | ||
548 | + if (IS_PCI(adev)) | ||
549 | + return acxpci_s_issue_cmd_timeo(adev, cmd, param, len, timeout); | ||
550 | + return acxusb_s_issue_cmd_timeo(adev, cmd, param, len, timeout); | ||
551 | +} | ||
552 | +static inline int | ||
553 | +acx_s_issue_cmd(acx_device_t *adev, unsigned cmd, void *param, unsigned len) | ||
554 | +{ | ||
555 | + if (IS_MEM(adev)) | ||
556 | + return acxmem_s_issue_cmd_timeo(adev, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT); | ||
557 | + if (IS_PCI(adev)) | ||
558 | + return acxpci_s_issue_cmd_timeo(adev, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT); | ||
559 | + return acxusb_s_issue_cmd_timeo(adev, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT); | ||
560 | +} | ||
561 | +int acx_s_configure(acx_device_t *adev, void *pdr, int type); | ||
562 | +int acx_s_interrogate(acx_device_t *adev, void *pdr, int type); | ||
563 | + | ||
564 | +#endif | ||
565 | + | ||
566 | +void acx_s_cmd_start_scan(acx_device_t *adev); | ||
567 | + | ||
568 | + | ||
569 | +/*********************************************************************** | ||
570 | +** Ioctls | ||
571 | +*/ | ||
572 | +int | ||
573 | +acx111pci_ioctl_info( | ||
574 | + struct net_device *ndev, | ||
575 | + struct iw_request_info *info, | ||
576 | + struct iw_param *vwrq, | ||
577 | + char *extra); | ||
578 | +int | ||
579 | +acx100pci_ioctl_set_phy_amp_bias( | ||
580 | + struct net_device *ndev, | ||
581 | + struct iw_request_info *info, | ||
582 | + struct iw_param *vwrq, | ||
583 | + char *extra); | ||
584 | +int | ||
585 | +acx100mem_ioctl_set_phy_amp_bias( | ||
586 | + struct net_device *ndev, | ||
587 | + struct iw_request_info *info, | ||
588 | + struct iw_param *vwrq, | ||
589 | + char *extra); | ||
590 | + | ||
591 | + | ||
592 | +/*********************************************************************** | ||
593 | +** /proc | ||
594 | +*/ | ||
595 | +#ifdef CONFIG_PROC_FS | ||
596 | +int acx_proc_register_entries(const struct net_device *ndev); | ||
597 | +int acx_proc_unregister_entries(const struct net_device *ndev); | ||
598 | +#else | ||
599 | +static inline int | ||
600 | +acx_proc_register_entries(const struct net_device *ndev) { return OK; } | ||
601 | +static inline int | ||
602 | +acx_proc_unregister_entries(const struct net_device *ndev) { return OK; } | ||
603 | +#endif | ||
604 | + | ||
605 | + | ||
606 | +/*********************************************************************** | ||
607 | +*/ | ||
608 | +firmware_image_t *acx_s_read_fw(struct device *dev, const char *file, u32 *size); | ||
609 | +int acxpci_s_upload_radio(acx_device_t *adev); | ||
610 | +int acxmem_s_upload_radio(acx_device_t *adev); | ||
611 | + | ||
612 | + | ||
613 | +/*********************************************************************** | ||
614 | +** Unsorted yet :) | ||
615 | +*/ | ||
616 | +int acxpci_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf); | ||
617 | +int acxmem_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf); | ||
618 | +int acxusb_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf); | ||
619 | +static inline int | ||
620 | +acx_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf) | ||
621 | +{ | ||
622 | + if (IS_MEM(adev)) | ||
623 | + return acxmem_s_read_phy_reg(adev, reg, charbuf); | ||
624 | + if (IS_PCI(adev)) | ||
625 | + return acxpci_s_read_phy_reg(adev, reg, charbuf); | ||
626 | + return acxusb_s_read_phy_reg(adev, reg, charbuf); | ||
627 | +} | ||
628 | + | ||
629 | +int acxpci_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value); | ||
630 | +int acxmem_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value); | ||
631 | +int acxusb_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value); | ||
632 | +static inline int | ||
633 | +acx_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value) | ||
634 | +{ | ||
635 | + if (IS_MEM(adev)) | ||
636 | + return acxmem_s_write_phy_reg(adev, reg, value); | ||
637 | + if (IS_PCI(adev)) | ||
638 | + return acxpci_s_write_phy_reg(adev, reg, value); | ||
639 | + return acxusb_s_write_phy_reg(adev, reg, value); | ||
640 | +} | ||
641 | + | ||
642 | +tx_t* acxpci_l_alloc_tx(acx_device_t *adev); | ||
643 | +tx_t* acxmem_l_alloc_tx(acx_device_t *adev); | ||
644 | +tx_t* acxusb_l_alloc_tx(acx_device_t *adev); | ||
645 | +static inline tx_t* | ||
646 | +acx_l_alloc_tx(acx_device_t *adev) | ||
647 | +{ | ||
648 | + if (IS_MEM(adev)) | ||
649 | + return acxmem_l_alloc_tx(adev); | ||
650 | + if (IS_PCI(adev)) | ||
651 | + return acxpci_l_alloc_tx(adev); | ||
652 | + return acxusb_l_alloc_tx(adev); | ||
653 | +} | ||
654 | + | ||
655 | +void acxusb_l_dealloc_tx(tx_t *tx_opaque); | ||
656 | +void acxmem_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque); | ||
657 | +static inline void | ||
658 | +acx_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque) | ||
659 | +{ | ||
660 | +#ifdef ACX_MEM | ||
661 | + acxmem_l_dealloc_tx (adev, tx_opaque); | ||
662 | +#else | ||
663 | + if (IS_USB(adev)) | ||
664 | + acxusb_l_dealloc_tx(tx_opaque); | ||
665 | +#endif | ||
666 | +} | ||
667 | + | ||
668 | +void* acxpci_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque); | ||
669 | +void* acxmem_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque); | ||
670 | +void* acxusb_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque); | ||
671 | +static inline void* | ||
672 | +acx_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque) | ||
673 | +{ | ||
674 | +#if defined (ACX_MEM) | ||
675 | + return acxmem_l_get_txbuf(adev, tx_opaque); | ||
676 | +#else | ||
677 | + if (IS_PCI(adev)) | ||
678 | + return acxpci_l_get_txbuf(adev, tx_opaque); | ||
679 | + return acxusb_l_get_txbuf(adev, tx_opaque); | ||
680 | +#endif | ||
681 | +} | ||
682 | + | ||
683 | +void acxpci_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len); | ||
684 | +void acxmem_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len); | ||
685 | +void acxusb_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len); | ||
686 | +static inline void | ||
687 | +acx_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len) | ||
688 | +{ | ||
689 | +#if defined (ACX_MEM) | ||
690 | + acxmem_l_tx_data(adev, tx_opaque, len); | ||
691 | +#else | ||
692 | + if (IS_PCI(adev)) | ||
693 | + acxpci_l_tx_data(adev, tx_opaque, len); | ||
694 | + else | ||
695 | + acxusb_l_tx_data(adev, tx_opaque, len); | ||
696 | +#endif | ||
697 | +} | ||
698 | + | ||
699 | +static inline wlan_hdr_t* | ||
700 | +acx_get_wlan_hdr(acx_device_t *adev, const rxbuffer_t *rxbuf) | ||
701 | +{ | ||
702 | + return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + adev->phy_header_len); | ||
703 | +} | ||
704 | + | ||
705 | +void acxpci_l_power_led(acx_device_t *adev, int enable); | ||
706 | +int acxpci_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf); | ||
707 | +unsigned int acxpci_l_clean_txdesc(acx_device_t *adev); | ||
708 | +void acxpci_l_clean_txdesc_emergency(acx_device_t *adev); | ||
709 | +int acxpci_s_create_hostdesc_queues(acx_device_t *adev); | ||
710 | +void acxpci_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start); | ||
711 | +void acxpci_free_desc_queues(acx_device_t *adev); | ||
712 | +char* acxpci_s_proc_diag_output(char *p, acx_device_t *adev); | ||
713 | +int acxpci_proc_eeprom_output(char *p, acx_device_t *adev); | ||
714 | +void acxpci_set_interrupt_mask(acx_device_t *adev); | ||
715 | +int acx100pci_s_set_tx_level(acx_device_t *adev, u8 level_dbm); | ||
716 | + | ||
717 | +void acxmem_l_power_led(acx_device_t *adev, int enable); | ||
718 | +int acxmem_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf); | ||
719 | +unsigned int acxmem_l_clean_txdesc(acx_device_t *adev); | ||
720 | +void acxmem_l_clean_txdesc_emergency(acx_device_t *adev); | ||
721 | +int acxmem_s_create_hostdesc_queues(acx_device_t *adev); | ||
722 | +void acxmem_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start); | ||
723 | +void acxmem_free_desc_queues(acx_device_t *adev); | ||
724 | +char* acxmem_s_proc_diag_output(char *p, acx_device_t *adev); | ||
725 | +int acxmem_proc_eeprom_output(char *p, acx_device_t *adev); | ||
726 | +void acxmem_set_interrupt_mask(acx_device_t *adev); | ||
727 | +int acx100mem_s_set_tx_level(acx_device_t *adev, u8 level_dbm); | ||
728 | + | ||
729 | +void acx_s_msleep(int ms); | ||
730 | +int acx_s_init_mac(acx_device_t *adev); | ||
731 | +void acx_set_reg_domain(acx_device_t *adev, unsigned char reg_dom_id); | ||
732 | +void acx_set_timer(acx_device_t *adev, int timeout_us); | ||
733 | +void acx_update_capabilities(acx_device_t *adev); | ||
734 | +void acx_s_start(acx_device_t *adev); | ||
735 | + | ||
736 | +void acx_s_update_card_settings(acx_device_t *adev); | ||
737 | +void acx_s_parse_configoption(acx_device_t *adev, const acx111_ie_configoption_t *pcfg); | ||
738 | +void acx_l_update_ratevector(acx_device_t *adev); | ||
739 | + | ||
740 | +void acx_init_task_scheduler(acx_device_t *adev); | ||
741 | +void acx_schedule_task(acx_device_t *adev, unsigned int set_flag); | ||
742 | + | ||
743 | +int acx_e_ioctl_old(struct net_device *ndev, struct ifreq *ifr, int cmd); | ||
744 | + | ||
745 | +client_t *acx_l_sta_list_get(acx_device_t *adev, const u8 *address); | ||
746 | +void acx_l_sta_list_del(acx_device_t *adev, client_t *clt); | ||
747 | + | ||
748 | +int acx_l_transmit_disassoc(acx_device_t *adev, client_t *clt); | ||
749 | +void acx_i_timer(unsigned long a); | ||
750 | +int acx_s_complete_scan(acx_device_t *adev); | ||
751 | + | ||
752 | +struct sk_buff *acx_rxbuf_to_ether(acx_device_t *adev, rxbuffer_t *rxbuf); | ||
753 | +int acx_ether_to_txbuf(acx_device_t *adev, void *txbuf, const struct sk_buff *skb); | ||
754 | + | ||
755 | +u8 acx_signal_determine_quality(u8 signal, u8 noise); | ||
756 | + | ||
757 | +void acx_l_process_rxbuf(acx_device_t *adev, rxbuffer_t *rxbuf); | ||
758 | +void acx_l_handle_txrate_auto(acx_device_t *adev, struct client *txc, | ||
759 | + u16 intended_rate, u8 rate100, u16 rate111, u8 error, | ||
760 | + int pkts_to_ignore); | ||
761 | + | ||
762 | +void acx_dump_bytes(const void *, int); | ||
763 | +void acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr); | ||
764 | + | ||
765 | +u8 acx_rate111to100(u16); | ||
766 | + | ||
767 | +void acx_s_set_defaults(acx_device_t *adev); | ||
768 | + | ||
769 | +#if !ACX_DEBUG | ||
770 | +static inline const char* acx_get_packet_type_string(u16 fc) { return ""; } | ||
771 | +#else | ||
772 | +const char* acx_get_packet_type_string(u16 fc); | ||
773 | +#endif | ||
774 | +const char* acx_cmd_status_str(unsigned int state); | ||
775 | + | ||
776 | +int acx_i_start_xmit(struct sk_buff *skb, struct net_device *ndev); | ||
777 | + | ||
778 | +void great_inquisitor(acx_device_t *adev); | ||
779 | + | ||
780 | +void acx_s_get_firmware_version(acx_device_t *adev); | ||
781 | +void acx_display_hardware_details(acx_device_t *adev); | ||
782 | + | ||
783 | +int acx_e_change_mtu(struct net_device *ndev, int mtu); | ||
784 | +struct net_device_stats* acx_e_get_stats(struct net_device *ndev); | ||
785 | +struct iw_statistics* acx_e_get_wireless_stats(struct net_device *ndev); | ||
786 | + | ||
787 | +#ifdef ACX_MEM | ||
788 | +int __init acxmem_e_init_module(void); | ||
789 | +void __exit acxmem_e_cleanup_module(void); | ||
790 | +void acxmem_e_release(struct device *dev); | ||
791 | +#else | ||
792 | +int __init acxpci_e_init_module(void); | ||
793 | +int __init acxusb_e_init_module(void); | ||
794 | +void __exit acxpci_e_cleanup_module(void); | ||
795 | +void __exit acxusb_e_cleanup_module(void); | ||
796 | +#endif | ||
797 | +int __init acx_cs_init(void); | ||
798 | +void __exit acx_cs_cleanup(void); | ||
799 | Index: linux-2.6.22/drivers/net/wireless/acx/acx.h | ||
800 | =================================================================== | ||
801 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
802 | +++ linux-2.6.22/drivers/net/wireless/acx/acx.h 2007-08-23 18:34:19.000000000 +0200 | ||
803 | @@ -0,0 +1,14 @@ | ||
804 | +#if defined(CONFIG_ACX_MEM) && !defined(ACX_MEM) | ||
805 | +#define ACX_MEM | ||
806 | +#endif | ||
807 | + | ||
808 | +#if defined(CONFIG_ACX_CS) && !defined(ACX_MEM) | ||
809 | +#define ACX_MEM | ||
810 | +#endif | ||
811 | + | ||
812 | +#include "acx_config.h" | ||
813 | +#include "wlan_compat.h" | ||
814 | +#include "wlan_hdr.h" | ||
815 | +#include "wlan_mgmt.h" | ||
816 | +#include "acx_struct.h" | ||
817 | +#include "acx_func.h" | ||
818 | Index: linux-2.6.22/drivers/net/wireless/acx/acx_hw.h | ||
819 | =================================================================== | ||
820 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
821 | +++ linux-2.6.22/drivers/net/wireless/acx/acx_hw.h 2007-08-23 18:34:19.000000000 +0200 | ||
822 | @@ -0,0 +1,18 @@ | ||
823 | +/* | ||
824 | + * Interface for ACX slave memory driver | ||
825 | + * | ||
826 | + * Copyright (c) 2006 SDG Systems, LLC | ||
827 | + * | ||
828 | + * GPL | ||
829 | + * | ||
830 | + */ | ||
831 | + | ||
832 | +#ifndef _ACX_HW_H | ||
833 | +#define _ACX_HW_H | ||
834 | + | ||
835 | +struct acx_hardware_data { | ||
836 | + int (*start_hw)( void ); | ||
837 | + int (*stop_hw)( void ); | ||
838 | +}; | ||
839 | + | ||
840 | +#endif /* _ACX_HW_H */ | ||
841 | Index: linux-2.6.22/drivers/net/wireless/acx/acx_struct.h | ||
842 | =================================================================== | ||
843 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
844 | +++ linux-2.6.22/drivers/net/wireless/acx/acx_struct.h 2007-08-23 18:34:19.000000000 +0200 | ||
845 | @@ -0,0 +1,2114 @@ | ||
846 | +/*********************************************************************** | ||
847 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
848 | +** | ||
849 | +** The contents of this file are subject to the Mozilla Public | ||
850 | +** License Version 1.1 (the "License"); you may not use this file | ||
851 | +** except in compliance with the License. You may obtain a copy of | ||
852 | +** the License at http://www.mozilla.org/MPL/ | ||
853 | +** | ||
854 | +** Software distributed under the License is distributed on an "AS | ||
855 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
856 | +** implied. See the License for the specific language governing | ||
857 | +** rights and limitations under the License. | ||
858 | +** | ||
859 | +** Alternatively, the contents of this file may be used under the | ||
860 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
861 | +** case the provisions of the GPL are applicable instead of the | ||
862 | +** above. If you wish to allow the use of your version of this file | ||
863 | +** only under the terms of the GPL and not to allow others to use | ||
864 | +** your version of this file under the MPL, indicate your decision | ||
865 | +** by deleting the provisions above and replace them with the notice | ||
866 | +** and other provisions required by the GPL. If you do not delete | ||
867 | +** the provisions above, a recipient may use your version of this | ||
868 | +** file under either the MPL or the GPL. | ||
869 | +** --------------------------------------------------------------------- | ||
870 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
871 | +** made directly to: | ||
872 | +** | ||
873 | +** acx100-users@lists.sf.net | ||
874 | +** http://acx100.sf.net | ||
875 | +** --------------------------------------------------------------------- | ||
876 | +*/ | ||
877 | + | ||
878 | +/*********************************************************************** | ||
879 | +** Forward declarations of types | ||
880 | +*/ | ||
881 | +typedef struct tx tx_t; | ||
882 | +typedef struct acx_device acx_device_t; | ||
883 | +typedef struct client client_t; | ||
884 | +typedef struct rxdesc rxdesc_t; | ||
885 | +typedef struct txdesc txdesc_t; | ||
886 | +typedef struct rxhostdesc rxhostdesc_t; | ||
887 | +typedef struct txhostdesc txhostdesc_t; | ||
888 | + | ||
889 | + | ||
890 | +/*********************************************************************** | ||
891 | +** Debug / log functionality | ||
892 | +*/ | ||
893 | +enum { | ||
894 | + L_LOCK = (ACX_DEBUG>1)*0x0001, /* locking debug log */ | ||
895 | + L_INIT = (ACX_DEBUG>0)*0x0002, /* special card initialization logging */ | ||
896 | + L_IRQ = (ACX_DEBUG>0)*0x0004, /* interrupt stuff */ | ||
897 | + L_ASSOC = (ACX_DEBUG>0)*0x0008, /* assocation (network join) and station log */ | ||
898 | + L_FUNC = (ACX_DEBUG>1)*0x0020, /* logging of function enter / leave */ | ||
899 | + L_XFER = (ACX_DEBUG>1)*0x0080, /* logging of transfers and mgmt */ | ||
900 | + L_DATA = (ACX_DEBUG>1)*0x0100, /* logging of transfer data */ | ||
901 | + L_DEBUG = (ACX_DEBUG>1)*0x0200, /* log of debug info */ | ||
902 | + L_IOCTL = (ACX_DEBUG>0)*0x0400, /* log ioctl calls */ | ||
903 | + L_CTL = (ACX_DEBUG>1)*0x0800, /* log of low-level ctl commands */ | ||
904 | + L_BUFR = (ACX_DEBUG>1)*0x1000, /* debug rx buffer mgmt (ring buffer etc.) */ | ||
905 | + L_XFER_BEACON = (ACX_DEBUG>1)*0x2000, /* also log beacon packets */ | ||
906 | + L_BUFT = (ACX_DEBUG>1)*0x4000, /* debug tx buffer mgmt (ring buffer etc.) */ | ||
907 | + L_USBRXTX = (ACX_DEBUG>0)*0x8000, /* debug USB rx/tx operations */ | ||
908 | + L_BUF = L_BUFR + L_BUFT, | ||
909 | + L_ANY = 0xffff | ||
910 | +}; | ||
911 | + | ||
912 | +#if ACX_DEBUG | ||
913 | +extern unsigned int acx_debug; | ||
914 | +#else | ||
915 | +enum { acx_debug = 0 }; | ||
916 | +#endif | ||
917 | + | ||
918 | + | ||
919 | +/*********************************************************************** | ||
920 | +** Random helpers | ||
921 | +*/ | ||
922 | +#define ACX_PACKED __attribute__ ((packed)) | ||
923 | + | ||
924 | +#define VEC_SIZE(a) (sizeof(a)/sizeof(a[0])) | ||
925 | + | ||
926 | +/* Use worker_queues for 2.5/2.6 kernels and queue tasks for 2.4 kernels | ||
927 | + (used for the 'bottom half' of the interrupt routine) */ | ||
928 | + | ||
929 | +#include <linux/workqueue.h> | ||
930 | +#define USE_WORKER_TASKS | ||
931 | +#define WORK_STRUCT struct work_struct | ||
932 | +#define SCHEDULE_WORK schedule_work | ||
933 | +#define FLUSH_SCHEDULED_WORK flush_scheduled_work | ||
934 | + | ||
935 | + | ||
936 | +/*********************************************************************** | ||
937 | +** Constants | ||
938 | +*/ | ||
939 | +#define OK 0 | ||
940 | +#define NOT_OK 1 | ||
941 | + | ||
942 | +/* The supported chip models */ | ||
943 | +#define CHIPTYPE_ACX100 1 | ||
944 | +#define CHIPTYPE_ACX111 2 | ||
945 | + | ||
946 | +#define IS_ACX100(adev) ((adev)->chip_type == CHIPTYPE_ACX100) | ||
947 | +#define IS_ACX111(adev) ((adev)->chip_type == CHIPTYPE_ACX111) | ||
948 | + | ||
949 | +/* Supported interfaces */ | ||
950 | +#define DEVTYPE_PCI 0 | ||
951 | +#define DEVTYPE_USB 1 | ||
952 | +#define DEVTYPE_MEM 2 | ||
953 | + | ||
954 | +#if !defined(CONFIG_ACX_PCI) && !defined(CONFIG_ACX_USB) && !defined(CONFIG_ACX_MEM) && !defined(CONFIG_ACX_CS) | ||
955 | +#error Driver must include PCI, USB, PCMCIA or memory mapped interface support. You selected none of them. | ||
956 | +#endif | ||
957 | + | ||
958 | +#if defined(CONFIG_ACX_PCI) | ||
959 | + #if !defined(CONFIG_ACX_USB) | ||
960 | + #define IS_PCI(adev) 1 | ||
961 | + #else | ||
962 | + #define IS_PCI(adev) ((adev)->dev_type == DEVTYPE_PCI) | ||
963 | + #endif | ||
964 | +#else | ||
965 | + #define IS_PCI(adev) 0 | ||
966 | +#endif | ||
967 | + | ||
968 | +#if defined(CONFIG_ACX_USB) | ||
969 | + #if !defined(CONFIG_ACX_PCI) | ||
970 | + #define IS_USB(adev) 1 | ||
971 | + #else | ||
972 | + #define IS_USB(adev) ((adev)->dev_type == DEVTYPE_USB) | ||
973 | + #endif | ||
974 | +#else | ||
975 | + #define IS_USB(adev) 0 | ||
976 | +#endif | ||
977 | + | ||
978 | +#if defined(CONFIG_ACX_MEM) || defined(CONFIG_ACX_CS) | ||
979 | + #define IS_MEM(adev) 1 | ||
980 | +#else | ||
981 | + #define IS_MEM(adev) 0 | ||
982 | +#endif | ||
983 | + | ||
984 | +/* Driver defaults */ | ||
985 | +#define DEFAULT_DTIM_INTERVAL 10 | ||
986 | +/* used to be 2048, but FreeBSD driver changed it to 4096 to work properly | ||
987 | +** in noisy wlans */ | ||
988 | +#define DEFAULT_MSDU_LIFETIME 4096 | ||
989 | +#define DEFAULT_RTS_THRESHOLD 2312 /* max. size: disable RTS mechanism */ | ||
990 | +#define DEFAULT_BEACON_INTERVAL 100 | ||
991 | + | ||
992 | +#define ACX100_BAP_DATALEN_MAX 4096 | ||
993 | +#define ACX100_RID_GUESSING_MAXLEN 2048 /* I'm not really sure */ | ||
994 | +#define ACX100_RIDDATA_MAXLEN ACX100_RID_GUESSING_MAXLEN | ||
995 | + | ||
996 | +/* Support Constants */ | ||
997 | +/* Radio type names, found in Win98 driver's TIACXLN.INF */ | ||
998 | +#define RADIO_MAXIM_0D 0x0d | ||
999 | +#define RADIO_RFMD_11 0x11 | ||
1000 | +#define RADIO_RALINK_15 0x15 | ||
1001 | +/* used in ACX111 cards (WG311v2, WL-121, ...): */ | ||
1002 | +#define RADIO_RADIA_16 0x16 | ||
1003 | +/* most likely *sometimes* used in ACX111 cards: */ | ||
1004 | +#define RADIO_UNKNOWN_17 0x17 | ||
1005 | +/* FwRad19.bin was found in a Safecom driver; must be an ACX111 radio: */ | ||
1006 | +#define RADIO_UNKNOWN_19 0x19 | ||
1007 | +#define RADIO_UNKNOWN_1B 0x1b /* radio in SafeCom SWLUT-54125 USB adapter; entirely unknown!! */ | ||
1008 | + | ||
1009 | +/* Controller Commands */ | ||
1010 | +/* can be found in table cmdTable in firmware "Rev. 1.5.0" (FW150) */ | ||
1011 | +#define ACX1xx_CMD_RESET 0x00 | ||
1012 | +#define ACX1xx_CMD_INTERROGATE 0x01 | ||
1013 | +#define ACX1xx_CMD_CONFIGURE 0x02 | ||
1014 | +#define ACX1xx_CMD_ENABLE_RX 0x03 | ||
1015 | +#define ACX1xx_CMD_ENABLE_TX 0x04 | ||
1016 | +#define ACX1xx_CMD_DISABLE_RX 0x05 | ||
1017 | +#define ACX1xx_CMD_DISABLE_TX 0x06 | ||
1018 | +#define ACX1xx_CMD_FLUSH_QUEUE 0x07 | ||
1019 | +#define ACX1xx_CMD_SCAN 0x08 | ||
1020 | +#define ACX1xx_CMD_STOP_SCAN 0x09 | ||
1021 | +#define ACX1xx_CMD_CONFIG_TIM 0x0a | ||
1022 | +#define ACX1xx_CMD_JOIN 0x0b | ||
1023 | +#define ACX1xx_CMD_WEP_MGMT 0x0c | ||
1024 | +#ifdef OLD_FIRMWARE_VERSIONS | ||
1025 | +#define ACX100_CMD_HALT 0x0e /* mapped to unknownCMD in FW150 */ | ||
1026 | +#else | ||
1027 | +#define ACX1xx_CMD_MEM_READ 0x0d | ||
1028 | +#define ACX1xx_CMD_MEM_WRITE 0x0e | ||
1029 | +#endif | ||
1030 | +#define ACX1xx_CMD_SLEEP 0x0f | ||
1031 | +#define ACX1xx_CMD_WAKE 0x10 | ||
1032 | +#define ACX1xx_CMD_UNKNOWN_11 0x11 /* mapped to unknownCMD in FW150 */ | ||
1033 | +#define ACX100_CMD_INIT_MEMORY 0x12 | ||
1034 | +#define ACX1FF_CMD_DISABLE_RADIO 0x12 /* new firmware? TNETW1450? */ | ||
1035 | +#define ACX1xx_CMD_CONFIG_BEACON 0x13 | ||
1036 | +#define ACX1xx_CMD_CONFIG_PROBE_RESPONSE 0x14 | ||
1037 | +#define ACX1xx_CMD_CONFIG_NULL_DATA 0x15 | ||
1038 | +#define ACX1xx_CMD_CONFIG_PROBE_REQUEST 0x16 | ||
1039 | +#define ACX1xx_CMD_FCC_TEST 0x17 | ||
1040 | +#define ACX1xx_CMD_RADIOINIT 0x18 | ||
1041 | +#define ACX111_CMD_RADIOCALIB 0x19 | ||
1042 | +#define ACX1FF_CMD_NOISE_HISTOGRAM 0x1c /* new firmware? TNETW1450? */ | ||
1043 | +#define ACX1FF_CMD_RX_RESET 0x1d /* new firmware? TNETW1450? */ | ||
1044 | +#define ACX1FF_CMD_LNA_CONTROL 0x20 /* new firmware? TNETW1450? */ | ||
1045 | +#define ACX1FF_CMD_CONTROL_DBG_TRACE 0x21 /* new firmware? TNETW1450? */ | ||
1046 | + | ||
1047 | +/* 'After Interrupt' Commands */ | ||
1048 | +#define ACX_AFTER_IRQ_CMD_STOP_SCAN 0x01 | ||
1049 | +#define ACX_AFTER_IRQ_CMD_ASSOCIATE 0x02 | ||
1050 | +#define ACX_AFTER_IRQ_CMD_RADIO_RECALIB 0x04 | ||
1051 | +#define ACX_AFTER_IRQ_UPDATE_CARD_CFG 0x08 | ||
1052 | +#define ACX_AFTER_IRQ_TX_CLEANUP 0x10 | ||
1053 | +#define ACX_AFTER_IRQ_COMPLETE_SCAN 0x20 | ||
1054 | +#define ACX_AFTER_IRQ_RESTART_SCAN 0x40 | ||
1055 | + | ||
1056 | +/*********************************************************************** | ||
1057 | +** Tx/Rx buffer sizes and watermarks | ||
1058 | +** | ||
1059 | +** This will alloc and use DMAable buffers of | ||
1060 | +** WLAN_A4FR_MAXLEN_WEP_FCS * (RX_CNT + TX_CNT) bytes | ||
1061 | +** RX/TX_CNT=32 -> ~150k DMA buffers | ||
1062 | +** RX/TX_CNT=16 -> ~75k DMA buffers | ||
1063 | +** | ||
1064 | +** 2005-10-10: reduced memory usage by lowering both to 16 | ||
1065 | +*/ | ||
1066 | +#define RX_CNT 16 | ||
1067 | +#define TX_CNT 16 | ||
1068 | + | ||
1069 | +/* we clean up txdescs when we have N free txdesc: */ | ||
1070 | +#define TX_CLEAN_BACKLOG (TX_CNT/4) | ||
1071 | +#define TX_START_CLEAN (TX_CNT - TX_CLEAN_BACKLOG) | ||
1072 | +#define TX_EMERG_CLEAN 2 | ||
1073 | +/* we stop queue if we have < N free txbufs: */ | ||
1074 | +#define TX_STOP_QUEUE 3 | ||
1075 | +/* we start queue if we have >= N free txbufs: */ | ||
1076 | +#define TX_START_QUEUE 5 | ||
1077 | + | ||
1078 | +/*********************************************************************** | ||
1079 | +** Interrogate/Configure cmd constants | ||
1080 | +** | ||
1081 | +** NB: length includes JUST the data part of the IE | ||
1082 | +** (does not include size of the (type,len) pair) | ||
1083 | +** | ||
1084 | +** TODO: seems that acx100, acx100usb, acx111 have some differences, | ||
1085 | +** fix code with regard to this! | ||
1086 | +*/ | ||
1087 | + | ||
1088 | +#define DEF_IE(name, val, len) enum { ACX##name=val, ACX##name##_LEN=len } | ||
1089 | + | ||
1090 | +/* Information Elements: Network Parameters, Static Configuration Entities */ | ||
1091 | +/* these are handled by real_cfgtable in firmware "Rev 1.5.0" (FW150) */ | ||
1092 | +DEF_IE(1xx_IE_UNKNOWN_00 ,0x0000, -1); /* mapped to cfgInvalid in FW150 */ | ||
1093 | +DEF_IE(100_IE_ACX_TIMER ,0x0001, 0x10); | ||
1094 | +DEF_IE(1xx_IE_POWER_MGMT ,0x0002, 0x06); /* TNETW1450: length 0x18!! */ | ||
1095 | +DEF_IE(1xx_IE_QUEUE_CONFIG ,0x0003, 0x1c); | ||
1096 | +DEF_IE(100_IE_BLOCK_SIZE ,0x0004, 0x02); | ||
1097 | +DEF_IE(1FF_IE_SLOT_TIME ,0x0004, 0x08); /* later firmware versions only? */ | ||
1098 | +DEF_IE(1xx_IE_MEMORY_CONFIG_OPTIONS ,0x0005, 0x14); | ||
1099 | +DEF_IE(1FF_IE_QUEUE_HEAD ,0x0005, 0x14 /* FIXME: length? */); | ||
1100 | +DEF_IE(1xx_IE_RATE_FALLBACK ,0x0006, 0x01); /* TNETW1450: length 2 */ | ||
1101 | +DEF_IE(100_IE_WEP_OPTIONS ,0x0007, 0x03); | ||
1102 | +DEF_IE(111_IE_RADIO_BAND ,0x0007, -1); | ||
1103 | +DEF_IE(1FF_IE_TIMING_CFG ,0x0007, -1); /* later firmware versions; TNETW1450 only? */ | ||
1104 | +DEF_IE(100_IE_SSID ,0x0008, 0x20); /* huh? */ | ||
1105 | +DEF_IE(1xx_IE_MEMORY_MAP ,0x0008, 0x28); /* huh? TNETW1450 has length 0x40!! */ | ||
1106 | +DEF_IE(1xx_IE_SCAN_STATUS ,0x0009, 0x04); /* mapped to cfgInvalid in FW150 */ | ||
1107 | +DEF_IE(1xx_IE_ASSOC_ID ,0x000a, 0x02); | ||
1108 | +DEF_IE(1xx_IE_UNKNOWN_0B ,0x000b, -1); /* mapped to cfgInvalid in FW150 */ | ||
1109 | +DEF_IE(1FF_IE_TX_POWER_LEVEL_TABLE ,0x000b, 0x18); /* later firmware versions; TNETW1450 only? */ | ||
1110 | +DEF_IE(100_IE_UNKNOWN_0C ,0x000c, -1); /* very small implementation in FW150! */ | ||
1111 | +/* ACX100 has an equivalent struct in the cmd mailbox directly after reset. | ||
1112 | + * 0x14c seems extremely large, will trash stack on failure (memset!) | ||
1113 | + * in case of small input struct --> OOPS! */ | ||
1114 | +DEF_IE(111_IE_CONFIG_OPTIONS ,0x000c, 0x14c); | ||
1115 | +DEF_IE(1xx_IE_FWREV ,0x000d, 0x18); | ||
1116 | +DEF_IE(1xx_IE_FCS_ERROR_COUNT ,0x000e, 0x04); | ||
1117 | +DEF_IE(1xx_IE_MEDIUM_USAGE ,0x000f, 0x08); | ||
1118 | +DEF_IE(1xx_IE_RXCONFIG ,0x0010, 0x04); | ||
1119 | +DEF_IE(100_IE_UNKNOWN_11 ,0x0011, -1); /* NONBINARY: large implementation in FW150! link quality readings or so? */ | ||
1120 | +DEF_IE(111_IE_QUEUE_THRESH ,0x0011, -1); | ||
1121 | +DEF_IE(100_IE_UNKNOWN_12 ,0x0012, -1); /* NONBINARY: VERY large implementation in FW150!! */ | ||
1122 | +DEF_IE(111_IE_BSS_POWER_SAVE ,0x0012, /* -1 */ 2); | ||
1123 | +DEF_IE(1xx_IE_FIRMWARE_STATISTICS ,0x0013, 0x9c); /* TNETW1450: length 0x134!! */ | ||
1124 | +DEF_IE(1FF_IE_RX_INTR_CONFIG ,0x0014, 0x14); /* later firmware versions, TNETW1450 only? */ | ||
1125 | +DEF_IE(1xx_IE_FEATURE_CONFIG ,0x0015, 0x08); | ||
1126 | +DEF_IE(111_IE_KEY_CHOOSE ,0x0016, 0x04); /* for rekeying. really len=4?? */ | ||
1127 | +DEF_IE(1FF_IE_MISC_CONFIG_TABLE ,0x0017, 0x04); /* later firmware versions, TNETW1450 only? */ | ||
1128 | +DEF_IE(1FF_IE_WONE_CONFIG ,0x0018, -1); /* later firmware versions, TNETW1450 only? */ | ||
1129 | +DEF_IE(1FF_IE_TID_CONFIG ,0x001a, 0x2c); /* later firmware versions, TNETW1450 only? */ | ||
1130 | +DEF_IE(1FF_IE_CALIB_ASSESSMENT ,0x001e, 0x04); /* later firmware versions, TNETW1450 only? */ | ||
1131 | +DEF_IE(1FF_IE_BEACON_FILTER_OPTIONS ,0x001f, 0x02); /* later firmware versions, TNETW1450 only? */ | ||
1132 | +DEF_IE(1FF_IE_LOW_RSSI_THRESH_OPT ,0x0020, 0x04); /* later firmware versions, TNETW1450 only? */ | ||
1133 | +DEF_IE(1FF_IE_NOISE_HISTOGRAM_RESULTS ,0x0021, 0x30); /* later firmware versions, TNETW1450 only? */ | ||
1134 | +DEF_IE(1FF_IE_PACKET_DETECT_THRESH ,0x0023, 0x04); /* later firmware versions, TNETW1450 only? */ | ||
1135 | +DEF_IE(1FF_IE_TX_CONFIG_OPTIONS ,0x0024, 0x04); /* later firmware versions, TNETW1450 only? */ | ||
1136 | +DEF_IE(1FF_IE_CCA_THRESHOLD ,0x0025, 0x02); /* later firmware versions, TNETW1450 only? */ | ||
1137 | +DEF_IE(1FF_IE_EVENT_MASK ,0x0026, 0x08); /* later firmware versions, TNETW1450 only? */ | ||
1138 | +DEF_IE(1FF_IE_DTIM_PERIOD ,0x0027, 0x02); /* later firmware versions, TNETW1450 only? */ | ||
1139 | +DEF_IE(1FF_IE_ACI_CONFIG_SET ,0x0029, 0x06); /* later firmware versions; maybe TNETW1450 only? */ | ||
1140 | +DEF_IE(1FF_IE_EEPROM_VER ,0x0030, 0x04); /* later firmware versions; maybe TNETW1450 only? */ | ||
1141 | +DEF_IE(1xx_IE_DOT11_STATION_ID ,0x1001, 0x06); | ||
1142 | +DEF_IE(100_IE_DOT11_UNKNOWN_1002 ,0x1002, -1); /* mapped to cfgInvalid in FW150 */ | ||
1143 | +DEF_IE(111_IE_DOT11_FRAG_THRESH ,0x1002, -1); /* mapped to cfgInvalid in FW150; TNETW1450 has length 2!! */ | ||
1144 | +DEF_IE(100_IE_DOT11_BEACON_PERIOD ,0x1003, 0x02); /* mapped to cfgInvalid in FW150 */ | ||
1145 | +DEF_IE(1xx_IE_DOT11_DTIM_PERIOD ,0x1004, -1); /* mapped to cfgInvalid in FW150 */ | ||
1146 | +DEF_IE(1FF_IE_DOT11_MAX_RX_LIFETIME ,0x1004, -1); /* later firmware versions; maybe TNETW1450 only? */ | ||
1147 | +DEF_IE(1xx_IE_DOT11_SHORT_RETRY_LIMIT ,0x1005, 0x01); /* TNETW1450: length 2 */ | ||
1148 | +DEF_IE(1xx_IE_DOT11_LONG_RETRY_LIMIT ,0x1006, 0x01); /* TNETW1450: length 2 */ | ||
1149 | +DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE ,0x1007, 0x20); /* configure default keys; TNETW1450 has length 0x24!! */ | ||
1150 | +DEF_IE(1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME ,0x1008, 0x04); | ||
1151 | +DEF_IE(1xx_IE_DOT11_GROUP_ADDR ,0x1009, -1); | ||
1152 | +DEF_IE(1xx_IE_DOT11_CURRENT_REG_DOMAIN ,0x100a, 0x02); | ||
1153 | +/* It's harmless to have larger struct. Use USB case always. */ | ||
1154 | +DEF_IE(1xx_IE_DOT11_CURRENT_ANTENNA ,0x100b, 0x02); /* in fact len=1 for PCI */ | ||
1155 | +DEF_IE(1xx_IE_DOT11_UNKNOWN_100C ,0x100c, -1); /* mapped to cfgInvalid in FW150 */ | ||
1156 | +DEF_IE(1xx_IE_DOT11_TX_POWER_LEVEL ,0x100d, 0x01); /* TNETW1450 has length 2!! */ | ||
1157 | +DEF_IE(1xx_IE_DOT11_CURRENT_CCA_MODE ,0x100e, 0x02); /* in fact len=1 for PCI */ | ||
1158 | +/* USB doesn't return anything - len==0?! */ | ||
1159 | +DEF_IE(100_IE_DOT11_ED_THRESHOLD ,0x100f, 0x04); | ||
1160 | +DEF_IE(1xx_IE_DOT11_WEP_DEFAULT_KEY_SET ,0x1010, 0x01); /* set default key ID; TNETW1450: length 2 */ | ||
1161 | +DEF_IE(100_IE_DOT11_UNKNOWN_1011 ,0x1011, -1); /* mapped to cfgInvalid in FW150 */ | ||
1162 | +DEF_IE(1FF_IE_DOT11_CURR_5GHZ_REGDOM ,0x1011, -1); /* later firmware versions; maybe TNETW1450 only? */ | ||
1163 | +DEF_IE(100_IE_DOT11_UNKNOWN_1012 ,0x1012, -1); /* mapped to cfgInvalid in FW150 */ | ||
1164 | +DEF_IE(100_IE_DOT11_UNKNOWN_1013 ,0x1013, -1); /* mapped to cfgInvalid in FW150 */ | ||
1165 | + | ||
1166 | +#if 0 | ||
1167 | +/* Experimentally obtained on acx100, fw 1.9.8.b | ||
1168 | +** -1 means that fw returned 'invalid IE' | ||
1169 | +** 0200 FC00 nnnn... are test read contents: u16 type, u16 len, data | ||
1170 | +** (AA are poison bytes marking bytes not written by fw) | ||
1171 | +** | ||
1172 | +** Looks like acx100 fw does not update len field (thus len=256-4=FC here) | ||
1173 | +** A number of IEs seem to trash type,len fields | ||
1174 | +** IEs marked 'huge' return gobs of data (no poison bytes remain) | ||
1175 | +*/ | ||
1176 | +DEF_IE(100_IE_INVAL_00, 0x0000, -1); | ||
1177 | +DEF_IE(100_IE_INVAL_01, 0x0001, -1); /* IE_ACX_TIMER, len=16 on older fw */ | ||
1178 | +DEF_IE(100_IE_POWER_MGMT, 0x0002, 4); /* 0200FC00 00040000 AAAAAAAA */ | ||
1179 | +DEF_IE(100_IE_QUEUE_CONFIG, 0x0003, 28); /* 0300FC00 48060000 9CAD0000 0101AAAA DCB00000 E4B00000 9CAA0000 00AAAAAA */ | ||
1180 | +DEF_IE(100_IE_BLOCK_SIZE, 0x0004, 2); /* 0400FC00 0001AAAA AAAAAAAA AAAAAAAA */ | ||
1181 | +/* write only: */ | ||
1182 | +DEF_IE(100_IE_MEMORY_CONFIG_OPTIONS, 0x0005, 20); | ||
1183 | +DEF_IE(100_IE_RATE_FALLBACK, 0x0006, 1); /* 0600FC00 00AAAAAA AAAAAAAA AAAAAAAA */ | ||
1184 | +/* write only: */ | ||
1185 | +DEF_IE(100_IE_WEP_OPTIONS, 0x0007, 3); | ||
1186 | +DEF_IE(100_IE_MEMORY_MAP, 0x0008, 40); /* huge: 0800FC00 30000000 6CA20000 70A20000... */ | ||
1187 | +/* gives INVAL on read: */ | ||
1188 | +DEF_IE(100_IE_SCAN_STATUS, 0x0009, -1); | ||
1189 | +DEF_IE(100_IE_ASSOC_ID, 0x000a, 2); /* huge: 0A00FC00 00000000 01040800 00000000... */ | ||
1190 | +DEF_IE(100_IE_INVAL_0B, 0x000b, -1); | ||
1191 | +/* 'command rejected': */ | ||
1192 | +DEF_IE(100_IE_CONFIG_OPTIONS, 0x000c, -3); | ||
1193 | +DEF_IE(100_IE_FWREV, 0x000d, 24); /* 0D00FC00 52657620 312E392E 382E6200 AAAAAAAA AAAAAAAA 05050201 AAAAAAAA */ | ||
1194 | +DEF_IE(100_IE_FCS_ERROR_COUNT, 0x000e, 4); | ||
1195 | +DEF_IE(100_IE_MEDIUM_USAGE, 0x000f, 8); /* E41F0000 2D780300 FCC91300 AAAAAAAA */ | ||
1196 | +DEF_IE(100_IE_RXCONFIG, 0x0010, 4); /* 1000FC00 00280000 AAAAAAAA AAAAAAAA */ | ||
1197 | +DEF_IE(100_IE_QUEUE_THRESH, 0x0011, 12); /* 1100FC00 AAAAAAAA 00000000 00000000 */ | ||
1198 | +DEF_IE(100_IE_BSS_POWER_SAVE, 0x0012, 1); /* 1200FC00 00AAAAAA AAAAAAAA AAAAAAAA */ | ||
1199 | +/* read only, variable len */ | ||
1200 | +DEF_IE(100_IE_FIRMWARE_STATISTICS, 0x0013, 256); /* 0000AC00 00000000 ... */ | ||
1201 | +DEF_IE(100_IE_INT_CONFIG, 0x0014, 20); /* 00000000 00000000 00000000 00000000 5D74D105 00000000 AAAAAAAA AAAAAAAA */ | ||
1202 | +DEF_IE(100_IE_FEATURE_CONFIG, 0x0015, 8); /* 1500FC00 16000000 AAAAAAAA AAAAAAAA */ | ||
1203 | +/* returns 'invalid MAC': */ | ||
1204 | +DEF_IE(100_IE_KEY_CHOOSE, 0x0016, -4); | ||
1205 | +DEF_IE(100_IE_INVAL_17, 0x0017, -1); | ||
1206 | +DEF_IE(100_IE_UNKNOWN_18, 0x0018, 0); /* null len?! 1800FC00 AAAAAAAA AAAAAAAA AAAAAAAA */ | ||
1207 | +DEF_IE(100_IE_UNKNOWN_19, 0x0019, 256); /* huge: 1900FC00 9C1F00EA FEFFFFEA FEFFFFEA... */ | ||
1208 | +DEF_IE(100_IE_INVAL_1A, 0x001A, -1); | ||
1209 | + | ||
1210 | +DEF_IE(100_IE_DOT11_INVAL_1000, 0x1000, -1); | ||
1211 | +DEF_IE(100_IE_DOT11_STATION_ID, 0x1001, 6); /* huge: 0110FC00 58B10E2F 03000000 00000000... */ | ||
1212 | +DEF_IE(100_IE_DOT11_INVAL_1002, 0x1002, -1); | ||
1213 | +DEF_IE(100_IE_DOT11_INVAL_1003, 0x1003, -1); | ||
1214 | +DEF_IE(100_IE_DOT11_INVAL_1004, 0x1004, -1); | ||
1215 | +DEF_IE(100_IE_DOT11_SHORT_RETRY_LIMIT, 0x1005, 1); | ||
1216 | +DEF_IE(100_IE_DOT11_LONG_RETRY_LIMIT, 0x1006, 1); | ||
1217 | +/* write only: */ | ||
1218 | +DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE, 0x1007, 32); | ||
1219 | +DEF_IE(100_IE_DOT11_MAX_XMIT_MSDU_LIFETIME, 0x1008, 4); /* huge: 0810FC00 00020000 F4010000 00000000... */ | ||
1220 | +/* undoc but returns something */ | ||
1221 | +DEF_IE(100_IE_DOT11_GROUP_ADDR, 0x1009, 12); /* huge: 0910FC00 00000000 00000000 00000000... */ | ||
1222 | +DEF_IE(100_IE_DOT11_CURRENT_REG_DOMAIN, 0x100a, 1); /* 0A10FC00 30AAAAAA AAAAAAAA AAAAAAAA */ | ||
1223 | +DEF_IE(100_IE_DOT11_CURRENT_ANTENNA, 0x100b, 1); /* 0B10FC00 8FAAAAAA AAAAAAAA AAAAAAAA */ | ||
1224 | +DEF_IE(100_IE_DOT11_INVAL_100C, 0x100c, -1); | ||
1225 | +DEF_IE(100_IE_DOT11_TX_POWER_LEVEL, 0x100d, 2); /* 00000000 0100AAAA AAAAAAAA AAAAAAAA */ | ||
1226 | +DEF_IE(100_IE_DOT11_CURRENT_CCA_MODE, 0x100e, 1); /* 0E10FC00 0DAAAAAA AAAAAAAA AAAAAAAA */ | ||
1227 | +DEF_IE(100_IE_DOT11_ED_THRESHOLD, 0x100f, 4); /* 0F10FC00 70000000 AAAAAAAA AAAAAAAA */ | ||
1228 | +/* set default key ID */ | ||
1229 | +DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_SET, 0x1010, 1); /* 1010FC00 00AAAAAA AAAAAAAA AAAAAAAA */ | ||
1230 | +DEF_IE(100_IE_DOT11_INVAL_1011, 0x1011, -1); | ||
1231 | +DEF_IE(100_IE_DOT11_INVAL_1012, 0x1012, -1); | ||
1232 | +DEF_IE(100_IE_DOT11_INVAL_1013, 0x1013, -1); | ||
1233 | +DEF_IE(100_IE_DOT11_UNKNOWN_1014, 0x1014, 256); /* huge */ | ||
1234 | +DEF_IE(100_IE_DOT11_UNKNOWN_1015, 0x1015, 256); /* huge */ | ||
1235 | +DEF_IE(100_IE_DOT11_UNKNOWN_1016, 0x1016, 256); /* huge */ | ||
1236 | +DEF_IE(100_IE_DOT11_UNKNOWN_1017, 0x1017, 256); /* huge */ | ||
1237 | +DEF_IE(100_IE_DOT11_UNKNOWN_1018, 0x1018, 256); /* huge */ | ||
1238 | +DEF_IE(100_IE_DOT11_UNKNOWN_1019, 0x1019, 256); /* huge */ | ||
1239 | +#endif | ||
1240 | + | ||
1241 | +#if 0 | ||
1242 | +/* Experimentally obtained on PCI acx111 Xterasys XN-2522g, fw 1.2.1.34 | ||
1243 | +** -1 means that fw returned 'invalid IE' | ||
1244 | +** 0400 0800 nnnn... are test read contents: u16 type, u16 len, data | ||
1245 | +** (AA are poison bytes marking bytes not written by fw) | ||
1246 | +** | ||
1247 | +** Looks like acx111 fw reports real len! | ||
1248 | +*/ | ||
1249 | +DEF_IE(111_IE_INVAL_00, 0x0000, -1); | ||
1250 | +DEF_IE(111_IE_INVAL_01, 0x0001, -1); | ||
1251 | +DEF_IE(111_IE_POWER_MGMT, 0x0002, 12); | ||
1252 | +/* write only, variable len: 12 + rxqueue_cnt*8 + txqueue_cnt*4: */ | ||
1253 | +DEF_IE(111_IE_MEMORY_CONFIG, 0x0003, 24); | ||
1254 | +DEF_IE(111_IE_BLOCK_SIZE, 0x0004, 8); /* 04000800 AA00AAAA AAAAAAAA */ | ||
1255 | +/* variable len: 8 + rxqueue_cnt*8 + txqueue_cnt*8: */ | ||
1256 | +DEF_IE(111_IE_QUEUE_HEAD, 0x0005, 24); | ||
1257 | +DEF_IE(111_IE_RATE_FALLBACK, 0x0006, 1); | ||
1258 | +/* acx100 name:WEP_OPTIONS */ | ||
1259 | +/* said to have len:1 (not true, actually returns 12 bytes): */ | ||
1260 | +DEF_IE(111_IE_RADIO_BAND, 0x0007, 12); /* 07000C00 AAAA1F00 FF03AAAA AAAAAAAA */ | ||
1261 | +DEF_IE(111_IE_MEMORY_MAP, 0x0008, 48); | ||
1262 | +/* said to have len:4, but gives INVAL on read: */ | ||
1263 | +DEF_IE(111_IE_SCAN_STATUS, 0x0009, -1); | ||
1264 | +DEF_IE(111_IE_ASSOC_ID, 0x000a, 2); | ||
1265 | +/* write only, len is not known: */ | ||
1266 | +DEF_IE(111_IE_UNKNOWN_0B, 0x000b, 0); | ||
1267 | +/* read only, variable len. I see 67 byte reads: */ | ||
1268 | +DEF_IE(111_IE_CONFIG_OPTIONS, 0x000c, 67); /* 0C004300 01160500 ... */ | ||
1269 | +DEF_IE(111_IE_FWREV, 0x000d, 24); | ||
1270 | +DEF_IE(111_IE_FCS_ERROR_COUNT, 0x000e, 4); | ||
1271 | +DEF_IE(111_IE_MEDIUM_USAGE, 0x000f, 8); | ||
1272 | +DEF_IE(111_IE_RXCONFIG, 0x0010, 4); | ||
1273 | +DEF_IE(111_IE_QUEUE_THRESH, 0x0011, 12); | ||
1274 | +DEF_IE(111_IE_BSS_POWER_SAVE, 0x0012, 1); | ||
1275 | +/* read only, variable len. I see 240 byte reads: */ | ||
1276 | +DEF_IE(111_IE_FIRMWARE_STATISTICS, 0x0013, 240); /* 1300F000 00000000 ... */ | ||
1277 | +/* said to have len=17. looks like fw pads it to 20: */ | ||
1278 | +DEF_IE(111_IE_INT_CONFIG, 0x0014, 20); /* 14001400 00000000 00000000 00000000 00000000 00000000 */ | ||
1279 | +DEF_IE(111_IE_FEATURE_CONFIG, 0x0015, 8); | ||
1280 | +/* said to be name:KEY_INDICATOR, len:4, but gives INVAL on read: */ | ||
1281 | +DEF_IE(111_IE_KEY_CHOOSE, 0x0016, -1); | ||
1282 | +/* said to have len:4, but in fact returns 8: */ | ||
1283 | +DEF_IE(111_IE_MAX_USB_XFR, 0x0017, 8); /* 17000800 00014000 00000000 */ | ||
1284 | +DEF_IE(111_IE_INVAL_18, 0x0018, -1); | ||
1285 | +DEF_IE(111_IE_INVAL_19, 0x0019, -1); | ||
1286 | +/* undoc but returns something: */ | ||
1287 | +/* huh, fw indicates len=20 but uses 4 more bytes in buffer??? */ | ||
1288 | +DEF_IE(111_IE_UNKNOWN_1A, 0x001A, 20); /* 1A001400 AA00AAAA 0000020F FF030000 00020000 00000007 04000000 */ | ||
1289 | + | ||
1290 | +DEF_IE(111_IE_DOT11_INVAL_1000, 0x1000, -1); | ||
1291 | +DEF_IE(111_IE_DOT11_STATION_ID, 0x1001, 6); | ||
1292 | +DEF_IE(111_IE_DOT11_FRAG_THRESH, 0x1002, 2); | ||
1293 | +/* acx100 only? gives INVAL on read: */ | ||
1294 | +DEF_IE(111_IE_DOT11_BEACON_PERIOD, 0x1003, -1); | ||
1295 | +/* said to be MAX_RECV_MSDU_LIFETIME: */ | ||
1296 | +DEF_IE(111_IE_DOT11_DTIM_PERIOD, 0x1004, 4); | ||
1297 | +DEF_IE(111_IE_DOT11_SHORT_RETRY_LIMIT, 0x1005, 1); | ||
1298 | +DEF_IE(111_IE_DOT11_LONG_RETRY_LIMIT, 0x1006, 1); | ||
1299 | +/* acx100 only? gives INVAL on read: */ | ||
1300 | +DEF_IE(111_IE_DOT11_WEP_DEFAULT_KEY_WRITE, 0x1007, -1); | ||
1301 | +DEF_IE(111_IE_DOT11_MAX_XMIT_MSDU_LIFETIME, 0x1008, 4); | ||
1302 | +/* undoc but returns something. maybe it's 2 multicast MACs to listen to? */ | ||
1303 | +DEF_IE(111_IE_DOT11_GROUP_ADDR, 0x1009, 12); /* 09100C00 00000000 00000000 00000000 */ | ||
1304 | +DEF_IE(111_IE_DOT11_CURRENT_REG_DOMAIN, 0x100a, 1); | ||
1305 | +DEF_IE(111_IE_DOT11_CURRENT_ANTENNA, 0x100b, 2); | ||
1306 | +DEF_IE(111_IE_DOT11_INVAL_100C, 0x100c, -1); | ||
1307 | +DEF_IE(111_IE_DOT11_TX_POWER_LEVEL, 0x100d, 1); | ||
1308 | +/* said to have len=1 but gives INVAL on read: */ | ||
1309 | +DEF_IE(111_IE_DOT11_CURRENT_CCA_MODE, 0x100e, -1); | ||
1310 | +/* said to have len=4 but gives INVAL on read: */ | ||
1311 | +DEF_IE(111_IE_DOT11_ED_THRESHOLD, 0x100f, -1); | ||
1312 | +/* set default key ID. write only: */ | ||
1313 | +DEF_IE(111_IE_DOT11_WEP_DEFAULT_KEY_SET, 0x1010, 1); | ||
1314 | +/* undoc but returns something: */ | ||
1315 | +DEF_IE(111_IE_DOT11_UNKNOWN_1011, 0x1011, 1); /* 11100100 20 */ | ||
1316 | +DEF_IE(111_IE_DOT11_INVAL_1012, 0x1012, -1); | ||
1317 | +DEF_IE(111_IE_DOT11_INVAL_1013, 0x1013, -1); | ||
1318 | +#endif | ||
1319 | + | ||
1320 | + | ||
1321 | +/*********************************************************************** | ||
1322 | +**Information Frames Structures | ||
1323 | +*/ | ||
1324 | + | ||
1325 | +/* Used in beacon frames and the like */ | ||
1326 | +#define DOT11RATEBYTE_1 (1*2) | ||
1327 | +#define DOT11RATEBYTE_2 (2*2) | ||
1328 | +#define DOT11RATEBYTE_5_5 (5*2+1) | ||
1329 | +#define DOT11RATEBYTE_11 (11*2) | ||
1330 | +#define DOT11RATEBYTE_22 (22*2) | ||
1331 | +#define DOT11RATEBYTE_6_G (6*2) | ||
1332 | +#define DOT11RATEBYTE_9_G (9*2) | ||
1333 | +#define DOT11RATEBYTE_12_G (12*2) | ||
1334 | +#define DOT11RATEBYTE_18_G (18*2) | ||
1335 | +#define DOT11RATEBYTE_24_G (24*2) | ||
1336 | +#define DOT11RATEBYTE_36_G (36*2) | ||
1337 | +#define DOT11RATEBYTE_48_G (48*2) | ||
1338 | +#define DOT11RATEBYTE_54_G (54*2) | ||
1339 | +#define DOT11RATEBYTE_BASIC 0x80 /* flags rates included in basic rate set */ | ||
1340 | + | ||
1341 | + | ||
1342 | +/*********************************************************************** | ||
1343 | +** rxbuffer_t | ||
1344 | +** | ||
1345 | +** This is the format of rx data returned by acx | ||
1346 | +*/ | ||
1347 | + | ||
1348 | +/* I've hoped it's a 802.11 PHY header, but no... | ||
1349 | + * so far, I've seen on acx111: | ||
1350 | + * 0000 3a00 0000 0000 IBSS Beacons | ||
1351 | + * 0000 3c00 0000 0000 ESS Beacons | ||
1352 | + * 0000 2700 0000 0000 Probe requests | ||
1353 | + * --vda | ||
1354 | + */ | ||
1355 | +typedef struct phy_hdr { | ||
1356 | + u8 unknown[4]; | ||
1357 | + u8 acx111_unknown[4]; | ||
1358 | +} ACX_PACKED phy_hdr_t; | ||
1359 | + | ||
1360 | +/* seems to be a bit similar to hfa384x_rx_frame. | ||
1361 | + * These fields are still not quite obvious, though. | ||
1362 | + * Some seem to have different meanings... */ | ||
1363 | + | ||
1364 | +#define RXBUF_HDRSIZE 12 | ||
1365 | +#define RXBUF_BYTES_RCVD(adev, rxbuf) \ | ||
1366 | + ((le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0xfff) - (adev)->phy_header_len) | ||
1367 | +#define RXBUF_BYTES_USED(rxbuf) \ | ||
1368 | + ((le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0xfff) + RXBUF_HDRSIZE) | ||
1369 | +/* USBism */ | ||
1370 | +#define RXBUF_IS_TXSTAT(rxbuf) (le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0x8000) | ||
1371 | +/* | ||
1372 | +mac_cnt_rcvd: | ||
1373 | + 12 bits: length of frame from control field to first byte of FCS | ||
1374 | + 3 bits: reserved | ||
1375 | + 1 bit: 1 = it's a tx status info, not a rx packet (USB only) | ||
1376 | + | ||
1377 | +mac_cnt_mblks: | ||
1378 | + 6 bits: number of memory block used to store frame in adapter memory | ||
1379 | + 1 bit: Traffic Indicator bit in TIM of received Beacon was set | ||
1380 | + | ||
1381 | +mac_status: 1 byte (bitmap): | ||
1382 | + 7 Matching BSSID | ||
1383 | + 6 Matching SSID | ||
1384 | + 5 BDCST Address 1 field is a broadcast | ||
1385 | + 4 VBM received beacon frame has more than one set bit (?!) | ||
1386 | + 3 TIM Set bit representing this station is set in TIM of received beacon | ||
1387 | + 2 GROUP Address 1 is a multicast | ||
1388 | + 1 ADDR1 Address 1 matches our MAC | ||
1389 | + 0 FCSGD FSC is good | ||
1390 | + | ||
1391 | +phy_stat_baseband: 1 byte (bitmap): | ||
1392 | + 7 Preamble frame had a long preamble | ||
1393 | + 6 PLCP Error CRC16 error in PLCP header | ||
1394 | + 5 Unsup_Mod unsupported modulation | ||
1395 | + 4 Selected Antenna antenna 1 was used to receive this frame | ||
1396 | + 3 PBCC/CCK frame used: 1=PBCC, 0=CCK modulation | ||
1397 | + 2 OFDM frame used OFDM modulation | ||
1398 | + 1 TI Protection protection frame was detected | ||
1399 | + 0 Reserved | ||
1400 | + | ||
1401 | +phy_plcp_signal: 1 byte: | ||
1402 | + Receive PLCP Signal field from the Baseband Processor | ||
1403 | + | ||
1404 | +phy_level: 1 byte: | ||
1405 | + receive AGC gain level (can be used to measure receive signal strength) | ||
1406 | + | ||
1407 | +phy_snr: 1 byte: | ||
1408 | + estimated noise power of equalized receive signal | ||
1409 | + at input of FEC decoder (can be used to measure receive signal quality) | ||
1410 | + | ||
1411 | +time: 4 bytes: | ||
1412 | + timestamp sampled from either the Access Manager TSF counter | ||
1413 | + or free-running microsecond counter when the MAC receives | ||
1414 | + first byte of PLCP header. | ||
1415 | +*/ | ||
1416 | + | ||
1417 | +typedef struct rxbuffer { | ||
1418 | + u16 mac_cnt_rcvd; /* only 12 bits are len! (0xfff) */ | ||
1419 | + u8 mac_cnt_mblks; | ||
1420 | + u8 mac_status; | ||
1421 | + u8 phy_stat_baseband; /* bit 0x80: used LNA (Low-Noise Amplifier) */ | ||
1422 | + u8 phy_plcp_signal; | ||
1423 | + u8 phy_level; /* PHY stat */ | ||
1424 | + u8 phy_snr; /* PHY stat */ | ||
1425 | + u32 time; /* timestamp upon MAC rcv first byte */ | ||
1426 | +/* 4-byte (acx100) or 8-byte (acx111) phy header will be here | ||
1427 | +** if RX_CFG1_INCLUDE_PHY_HDR is in effect: | ||
1428 | +** phy_hdr_t phy */ | ||
1429 | + wlan_hdr_a3_t hdr_a3; | ||
1430 | + /* maximally sized data part of wlan packet */ | ||
1431 | + u8 data_a3[WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN]; | ||
1432 | + /* can add hdr/data_a4 if needed */ | ||
1433 | +} ACX_PACKED rxbuffer_t; | ||
1434 | + | ||
1435 | + | ||
1436 | +/*--- Firmware statistics ----------------------------------------------------*/ | ||
1437 | + | ||
1438 | +/* define a random 100 bytes more to catch firmware versions which | ||
1439 | + * provide a bigger struct */ | ||
1440 | +#define FW_STATS_FUTURE_EXTENSION 100 | ||
1441 | + | ||
1442 | +typedef struct fw_stats_tx { | ||
1443 | + u32 tx_desc_of; | ||
1444 | +} ACX_PACKED fw_stats_tx_t; | ||
1445 | + | ||
1446 | +typedef struct fw_stats_rx { | ||
1447 | + u32 rx_oom; | ||
1448 | + u32 rx_hdr_of; | ||
1449 | + u32 rx_hw_stuck; /* old: u32 rx_hdr_use_next */ | ||
1450 | + u32 rx_dropped_frame; | ||
1451 | + u32 rx_frame_ptr_err; | ||
1452 | + u32 rx_xfr_hint_trig; | ||
1453 | + u32 rx_aci_events; /* later versions only */ | ||
1454 | + u32 rx_aci_resets; /* later versions only */ | ||
1455 | +} ACX_PACKED fw_stats_rx_t; | ||
1456 | + | ||
1457 | +typedef struct fw_stats_dma { | ||
1458 | + u32 rx_dma_req; | ||
1459 | + u32 rx_dma_err; | ||
1460 | + u32 tx_dma_req; | ||
1461 | + u32 tx_dma_err; | ||
1462 | +} ACX_PACKED fw_stats_dma_t; | ||
1463 | + | ||
1464 | +typedef struct fw_stats_irq { | ||
1465 | + u32 cmd_cplt; | ||
1466 | + u32 fiq; | ||
1467 | + u32 rx_hdrs; | ||
1468 | + u32 rx_cmplt; | ||
1469 | + u32 rx_mem_of; | ||
1470 | + u32 rx_rdys; | ||
1471 | + u32 irqs; | ||
1472 | + u32 tx_procs; | ||
1473 | + u32 decrypt_done; | ||
1474 | + u32 dma_0_done; | ||
1475 | + u32 dma_1_done; | ||
1476 | + u32 tx_exch_complet; | ||
1477 | + u32 commands; | ||
1478 | + u32 rx_procs; | ||
1479 | + u32 hw_pm_mode_changes; | ||
1480 | + u32 host_acks; | ||
1481 | + u32 pci_pm; | ||
1482 | + u32 acm_wakeups; | ||
1483 | +} ACX_PACKED fw_stats_irq_t; | ||
1484 | + | ||
1485 | +typedef struct fw_stats_wep { | ||
1486 | + u32 wep_key_count; | ||
1487 | + u32 wep_default_key_count; | ||
1488 | + u32 dot11_def_key_mib; | ||
1489 | + u32 wep_key_not_found; | ||
1490 | + u32 wep_decrypt_fail; | ||
1491 | + u32 wep_pkt_decrypt; | ||
1492 | + u32 wep_decrypt_irqs; | ||
1493 | +} ACX_PACKED fw_stats_wep_t; | ||
1494 | + | ||
1495 | +typedef struct fw_stats_pwr { | ||
1496 | + u32 tx_start_ctr; | ||
1497 | + u32 no_ps_tx_too_short; | ||
1498 | + u32 rx_start_ctr; | ||
1499 | + u32 no_ps_rx_too_short; | ||
1500 | + u32 lppd_started; | ||
1501 | + u32 no_lppd_too_noisy; | ||
1502 | + u32 no_lppd_too_short; | ||
1503 | + u32 no_lppd_matching_frame; | ||
1504 | +} ACX_PACKED fw_stats_pwr_t; | ||
1505 | + | ||
1506 | +typedef struct fw_stats_mic { | ||
1507 | + u32 mic_rx_pkts; | ||
1508 | + u32 mic_calc_fail; | ||
1509 | +} ACX_PACKED fw_stats_mic_t; | ||
1510 | + | ||
1511 | +typedef struct fw_stats_aes { | ||
1512 | + u32 aes_enc_fail; | ||
1513 | + u32 aes_dec_fail; | ||
1514 | + u32 aes_enc_pkts; | ||
1515 | + u32 aes_dec_pkts; | ||
1516 | + u32 aes_enc_irq; | ||
1517 | + u32 aes_dec_irq; | ||
1518 | +} ACX_PACKED fw_stats_aes_t; | ||
1519 | + | ||
1520 | +typedef struct fw_stats_event { | ||
1521 | + u32 heartbeat; | ||
1522 | + u32 calibration; | ||
1523 | + u32 rx_mismatch; | ||
1524 | + u32 rx_mem_empty; | ||
1525 | + u32 rx_pool; | ||
1526 | + u32 oom_late; | ||
1527 | + u32 phy_tx_err; | ||
1528 | + u32 tx_stuck; | ||
1529 | +} ACX_PACKED fw_stats_event_t; | ||
1530 | + | ||
1531 | +/* mainly for size calculation only */ | ||
1532 | +typedef struct fw_stats { | ||
1533 | + u16 type; | ||
1534 | + u16 len; | ||
1535 | + fw_stats_tx_t tx; | ||
1536 | + fw_stats_rx_t rx; | ||
1537 | + fw_stats_dma_t dma; | ||
1538 | + fw_stats_irq_t irq; | ||
1539 | + fw_stats_wep_t wep; | ||
1540 | + fw_stats_pwr_t pwr; | ||
1541 | + fw_stats_mic_t mic; | ||
1542 | + fw_stats_aes_t aes; | ||
1543 | + fw_stats_event_t evt; | ||
1544 | + u8 _padding[FW_STATS_FUTURE_EXTENSION]; | ||
1545 | +} fw_stats_t; | ||
1546 | + | ||
1547 | +/* Firmware version struct */ | ||
1548 | + | ||
1549 | +typedef struct fw_ver { | ||
1550 | + u16 cmd; | ||
1551 | + u16 size; | ||
1552 | + char fw_id[20]; | ||
1553 | + u32 hw_id; | ||
1554 | +} ACX_PACKED fw_ver_t; | ||
1555 | + | ||
1556 | +#define FW_ID_SIZE 20 | ||
1557 | + | ||
1558 | +typedef struct shared_queueindicator { | ||
1559 | + u32 indicator; | ||
1560 | + u16 host_lock; | ||
1561 | + u16 fw_lock; | ||
1562 | +} ACX_PACKED queueindicator_t; | ||
1563 | + | ||
1564 | +/*--- WEP stuff --------------------------------------------------------------*/ | ||
1565 | +#define DOT11_MAX_DEFAULT_WEP_KEYS 4 | ||
1566 | + | ||
1567 | +/* non-firmware struct, no packing necessary */ | ||
1568 | +typedef struct wep_key { | ||
1569 | + size_t size; /* most often used member first */ | ||
1570 | + u8 index; | ||
1571 | + u8 key[29]; | ||
1572 | + u16 strange_filler; | ||
1573 | +} wep_key_t; /* size = 264 bytes (33*8) */ | ||
1574 | +/* FIXME: We don't have size 264! Or is there 2 bytes beyond the key | ||
1575 | + * (strange_filler)? */ | ||
1576 | + | ||
1577 | +/* non-firmware struct, no packing necessary */ | ||
1578 | +typedef struct key_struct { | ||
1579 | + u8 addr[ETH_ALEN]; /* 0x00 */ | ||
1580 | + u16 filler1; /* 0x06 */ | ||
1581 | + u32 filler2; /* 0x08 */ | ||
1582 | + u32 index; /* 0x0c */ | ||
1583 | + u16 len; /* 0x10 */ | ||
1584 | + u8 key[29]; /* 0x12; is this long enough??? */ | ||
1585 | +} key_struct_t; /* size = 276. FIXME: where is the remaining space?? */ | ||
1586 | + | ||
1587 | + | ||
1588 | +/*--- Client (peer) info -----------------------------------------------------*/ | ||
1589 | +/* adev->sta_list[] is used for: | ||
1590 | +** accumulating and processing of scan results | ||
1591 | +** keeping client info in AP mode | ||
1592 | +** keeping AP info in STA mode (AP is the only one 'client') | ||
1593 | +** keeping peer info in ad-hoc mode | ||
1594 | +** non-firmware struct --> no packing necessary */ | ||
1595 | +enum { | ||
1596 | + CLIENT_EMPTY_SLOT_0 = 0, | ||
1597 | + CLIENT_EXIST_1 = 1, | ||
1598 | + CLIENT_AUTHENTICATED_2 = 2, | ||
1599 | + CLIENT_ASSOCIATED_3 = 3, | ||
1600 | + CLIENT_JOIN_CANDIDATE = 4 | ||
1601 | +}; | ||
1602 | +struct client { | ||
1603 | + /* most frequent access first */ | ||
1604 | + u8 used; /* misnamed, more like 'status' */ | ||
1605 | + struct client* next; | ||
1606 | + unsigned long mtime; /* last time we heard it, in jiffies */ | ||
1607 | + size_t essid_len; /* length of ESSID (without '\0') */ | ||
1608 | + u32 sir; /* Standard IR */ | ||
1609 | + u32 snr; /* Signal to Noise Ratio */ | ||
1610 | + u16 aid; /* association ID */ | ||
1611 | + u16 seq; /* from client's auth req */ | ||
1612 | + u16 auth_alg; /* from client's auth req */ | ||
1613 | + u16 cap_info; /* from client's assoc req */ | ||
1614 | + u16 rate_cap; /* what client supports (all rates) */ | ||
1615 | + u16 rate_bas; /* what client supports (basic rates) */ | ||
1616 | + u16 rate_cfg; /* what is allowed (by iwconfig etc) */ | ||
1617 | + u16 rate_cur; /* currently used rate mask */ | ||
1618 | + u8 rate_100; /* currently used rate byte (acx100 only) */ | ||
1619 | + u8 address[ETH_ALEN]; | ||
1620 | + u8 bssid[ETH_ALEN]; /* ad-hoc hosts can have bssid != mac */ | ||
1621 | + u8 channel; | ||
1622 | + u8 auth_step; | ||
1623 | + u8 ignore_count; | ||
1624 | + u8 fallback_count; | ||
1625 | + u8 stepup_count; | ||
1626 | + char essid[IW_ESSID_MAX_SIZE + 1]; /* ESSID and trailing '\0' */ | ||
1627 | +/* FIXME: this one is too damn big */ | ||
1628 | + char challenge_text[WLAN_CHALLENGE_LEN]; | ||
1629 | +}; | ||
1630 | + | ||
1631 | + | ||
1632 | +/*********************************************************************** | ||
1633 | +** Hardware structures | ||
1634 | +*/ | ||
1635 | + | ||
1636 | +/* An opaque typesafe helper type | ||
1637 | + * | ||
1638 | + * Some hardware fields are actually pointers, | ||
1639 | + * but they have to remain u32, since using ptr instead | ||
1640 | + * (8 bytes on 64bit systems!) would disrupt the fixed descriptor | ||
1641 | + * format the acx firmware expects in the non-user area. | ||
1642 | + * Since we cannot cram an 8 byte ptr into 4 bytes, we need to | ||
1643 | + * enforce that pointed to data remains in low memory | ||
1644 | + * (address value needs to fit in 4 bytes) on 64bit systems. | ||
1645 | + * | ||
1646 | + * This is easy to get wrong, thus we are using a small struct | ||
1647 | + * and special macros to access it. Macros will check for | ||
1648 | + * attempts to overflow an acx_ptr with value > 0xffffffff. | ||
1649 | + * | ||
1650 | + * Attempts to use acx_ptr without macros result in compile-time errors */ | ||
1651 | + | ||
1652 | +typedef struct { | ||
1653 | + u32 v; | ||
1654 | +} ACX_PACKED acx_ptr; | ||
1655 | + | ||
1656 | +#if ACX_DEBUG | ||
1657 | +#define CHECK32(n) BUG_ON(sizeof(n)>4 && (long)(n)>0xffffff00) | ||
1658 | +#else | ||
1659 | +#define CHECK32(n) ((void)0) | ||
1660 | +#endif | ||
1661 | + | ||
1662 | +/* acx_ptr <-> integer conversion */ | ||
1663 | +#define cpu2acx(n) ({ CHECK32(n); ((acx_ptr){ .v = cpu_to_le32(n) }); }) | ||
1664 | +#define acx2cpu(a) (le32_to_cpu(a.v)) | ||
1665 | + | ||
1666 | +/* acx_ptr <-> pointer conversion */ | ||
1667 | +#define ptr2acx(p) ({ CHECK32(p); ((acx_ptr){ .v = cpu_to_le32((u32)(long)(p)) }); }) | ||
1668 | +#define acx2ptr(a) ((void*)le32_to_cpu(a.v)) | ||
1669 | + | ||
1670 | +/* Values for rate field (acx100 only) */ | ||
1671 | +#define RATE100_1 10 | ||
1672 | +#define RATE100_2 20 | ||
1673 | +#define RATE100_5 55 | ||
1674 | +#define RATE100_11 110 | ||
1675 | +#define RATE100_22 220 | ||
1676 | +/* This bit denotes use of PBCC: | ||
1677 | +** (PBCC encoding is usable with 11 and 22 Mbps speeds only) */ | ||
1678 | +#define RATE100_PBCC511 0x80 | ||
1679 | + | ||
1680 | +/* Bit values for rate111 field */ | ||
1681 | +#define RATE111_1 0x0001 /* DBPSK */ | ||
1682 | +#define RATE111_2 0x0002 /* DQPSK */ | ||
1683 | +#define RATE111_5 0x0004 /* CCK or PBCC */ | ||
1684 | +#define RATE111_6 0x0008 /* CCK-OFDM or OFDM */ | ||
1685 | +#define RATE111_9 0x0010 /* CCK-OFDM or OFDM */ | ||
1686 | +#define RATE111_11 0x0020 /* CCK or PBCC */ | ||
1687 | +#define RATE111_12 0x0040 /* CCK-OFDM or OFDM */ | ||
1688 | +#define RATE111_18 0x0080 /* CCK-OFDM or OFDM */ | ||
1689 | +#define RATE111_22 0x0100 /* PBCC */ | ||
1690 | +#define RATE111_24 0x0200 /* CCK-OFDM or OFDM */ | ||
1691 | +#define RATE111_36 0x0400 /* CCK-OFDM or OFDM */ | ||
1692 | +#define RATE111_48 0x0800 /* CCK-OFDM or OFDM */ | ||
1693 | +#define RATE111_54 0x1000 /* CCK-OFDM or OFDM */ | ||
1694 | +#define RATE111_RESERVED 0x2000 | ||
1695 | +#define RATE111_PBCC511 0x4000 /* PBCC mod at 5.5 or 11Mbit (else CCK) */ | ||
1696 | +#define RATE111_SHORTPRE 0x8000 /* short preamble */ | ||
1697 | +/* Special 'try everything' value */ | ||
1698 | +#define RATE111_ALL 0x1fff | ||
1699 | +/* These bits denote acx100 compatible settings */ | ||
1700 | +#define RATE111_ACX100_COMPAT 0x0127 | ||
1701 | +/* These bits denote 802.11b compatible settings */ | ||
1702 | +#define RATE111_80211B_COMPAT 0x0027 | ||
1703 | + | ||
1704 | +/* Descriptor Ctl field bits | ||
1705 | + * init value is 0x8e, "idle" value is 0x82 (in idle tx descs) | ||
1706 | + */ | ||
1707 | +#define DESC_CTL_SHORT_PREAMBLE 0x01 /* preamble type: 0 = long; 1 = short */ | ||
1708 | +#define DESC_CTL_FIRSTFRAG 0x02 /* this is the 1st frag of the frame */ | ||
1709 | +#define DESC_CTL_AUTODMA 0x04 | ||
1710 | +#define DESC_CTL_RECLAIM 0x08 /* ready to reuse */ | ||
1711 | +#define DESC_CTL_HOSTDONE 0x20 /* host has finished processing */ | ||
1712 | +#define DESC_CTL_ACXDONE 0x40 /* acx has finished processing */ | ||
1713 | +/* host owns the desc [has to be released last, AFTER modifying all other desc fields!] */ | ||
1714 | +#define DESC_CTL_HOSTOWN 0x80 | ||
1715 | +#define DESC_CTL_ACXDONE_HOSTOWN (DESC_CTL_ACXDONE | DESC_CTL_HOSTOWN) | ||
1716 | + | ||
1717 | +/* Descriptor Status field | ||
1718 | + */ | ||
1719 | +#define DESC_STATUS_FULL (1 << 31) | ||
1720 | + | ||
1721 | +/* NB: some bits may be interesting for Monitor mode tx (aka Raw tx): */ | ||
1722 | +#define DESC_CTL2_SEQ 0x01 /* don't increase sequence field */ | ||
1723 | +#define DESC_CTL2_FCS 0x02 /* don't add the FCS */ | ||
1724 | +#define DESC_CTL2_MORE_FRAG 0x04 | ||
1725 | +#define DESC_CTL2_RETRY 0x08 /* don't increase retry field */ | ||
1726 | +#define DESC_CTL2_POWER 0x10 /* don't increase power mgmt. field */ | ||
1727 | +#define DESC_CTL2_RTS 0x20 /* do RTS/CTS magic before sending */ | ||
1728 | +#define DESC_CTL2_WEP 0x40 /* encrypt this frame */ | ||
1729 | +#define DESC_CTL2_DUR 0x80 /* don't increase duration field */ | ||
1730 | + | ||
1731 | +/*********************************************************************** | ||
1732 | +** PCI structures | ||
1733 | +*/ | ||
1734 | +/* IRQ Constants | ||
1735 | +** (outside of "#ifdef PCI" because USB (mis)uses HOST_INT_SCAN_COMPLETE) */ | ||
1736 | +#define HOST_INT_RX_DATA 0x0001 | ||
1737 | +#define HOST_INT_TX_COMPLETE 0x0002 | ||
1738 | +#define HOST_INT_TX_XFER 0x0004 | ||
1739 | +#define HOST_INT_RX_COMPLETE 0x0008 | ||
1740 | +#define HOST_INT_DTIM 0x0010 | ||
1741 | +#define HOST_INT_BEACON 0x0020 | ||
1742 | +#define HOST_INT_TIMER 0x0040 | ||
1743 | +#define HOST_INT_KEY_NOT_FOUND 0x0080 | ||
1744 | +#define HOST_INT_IV_ICV_FAILURE 0x0100 | ||
1745 | +#define HOST_INT_CMD_COMPLETE 0x0200 | ||
1746 | +#define HOST_INT_INFO 0x0400 | ||
1747 | +#define HOST_INT_OVERFLOW 0x0800 | ||
1748 | +#define HOST_INT_PROCESS_ERROR 0x1000 | ||
1749 | +#define HOST_INT_SCAN_COMPLETE 0x2000 | ||
1750 | +#define HOST_INT_FCS_THRESHOLD 0x4000 | ||
1751 | +#define HOST_INT_UNKNOWN 0x8000 | ||
1752 | + | ||
1753 | +/* Outside of "#ifdef PCI" because USB needs to know sizeof() | ||
1754 | +** of txdesc and rxdesc: */ | ||
1755 | +struct txdesc { | ||
1756 | + acx_ptr pNextDesc; /* pointer to next txdesc */ | ||
1757 | + acx_ptr HostMemPtr; /* 0x04 */ | ||
1758 | + acx_ptr AcxMemPtr; /* 0x08 */ | ||
1759 | + u32 tx_time; /* 0x0c */ | ||
1760 | + u16 total_length; /* 0x10 */ | ||
1761 | + u16 Reserved; /* 0x12 */ | ||
1762 | + | ||
1763 | +/* The following 16 bytes do not change when acx100 owns the descriptor */ | ||
1764 | +/* BUG: fw clears last byte of this area which is supposedly reserved | ||
1765 | +** for driver use. amd64 blew up. We dare not use it now */ | ||
1766 | + u32 dummy[4]; | ||
1767 | + | ||
1768 | + u8 Ctl_8; /* 0x24, 8bit value */ | ||
1769 | + u8 Ctl2_8; /* 0x25, 8bit value */ | ||
1770 | + u8 error; /* 0x26 */ | ||
1771 | + u8 ack_failures; /* 0x27 */ | ||
1772 | + | ||
1773 | + union { | ||
1774 | + /* | ||
1775 | + * Packing doesn't work correctly on ARM unless unions are on | ||
1776 | + * 4 byte boundaries. | ||
1777 | + */ | ||
1778 | + struct { | ||
1779 | + u8 rts_failures; /* 0x28 */ | ||
1780 | + u8 rts_ok; /* 0x29 */ | ||
1781 | + u16 d1; | ||
1782 | + } ACX_PACKED rts; | ||
1783 | + struct { | ||
1784 | + u16 d1; | ||
1785 | + u8 rate; /* 0x2a */ | ||
1786 | + u8 queue_ctrl; /* 0x2b */ | ||
1787 | + } ACX_PACKED r1; | ||
1788 | + struct { | ||
1789 | + u16 d1; | ||
1790 | + u16 rate111; /* 0x2a */ | ||
1791 | + } ACX_PACKED r2; | ||
1792 | + } ACX_PACKED u; | ||
1793 | + u32 queue_info; /* 0x2c (acx100, reserved on acx111) */ | ||
1794 | +} ACX_PACKED; /* size : 48 = 0x30 */ | ||
1795 | +/* NB: acx111 txdesc structure is 4 byte larger */ | ||
1796 | +/* All these 4 extra bytes are reserved. tx alloc code takes them into account */ | ||
1797 | + | ||
1798 | +struct rxdesc { | ||
1799 | + acx_ptr pNextDesc; /* 0x00 */ | ||
1800 | + acx_ptr HostMemPtr; /* 0x04 */ | ||
1801 | + acx_ptr ACXMemPtr; /* 0x08 */ | ||
1802 | + u32 rx_time; /* 0x0c */ | ||
1803 | + u16 total_length; /* 0x10 */ | ||
1804 | + u16 WEP_length; /* 0x12 */ | ||
1805 | + u32 WEP_ofs; /* 0x14 */ | ||
1806 | + | ||
1807 | +/* the following 16 bytes do not change when acx100 owns the descriptor */ | ||
1808 | + u8 driverWorkspace[16]; /* 0x18 */ | ||
1809 | + | ||
1810 | + u8 Ctl_8; | ||
1811 | + u8 rate; | ||
1812 | + u8 error; | ||
1813 | + u8 SNR; /* Signal-to-Noise Ratio */ | ||
1814 | + u8 RxLevel; | ||
1815 | + u8 queue_ctrl; | ||
1816 | + u16 unknown; | ||
1817 | + u32 unknown2; | ||
1818 | +} ACX_PACKED; /* size 52 = 0x34 */ | ||
1819 | + | ||
1820 | +#if defined(ACX_PCI) || defined(ACX_MEM) | ||
1821 | + | ||
1822 | +/* Register I/O offsets */ | ||
1823 | +#define ACX100_EEPROM_ID_OFFSET 0x380 | ||
1824 | + | ||
1825 | +/* please add further ACX hardware register definitions only when | ||
1826 | + it turns out you need them in the driver, and please try to use | ||
1827 | + firmware functionality instead, since using direct I/O access instead | ||
1828 | + of letting the firmware do it might confuse the firmware's state | ||
1829 | + machine */ | ||
1830 | + | ||
1831 | +/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION | ||
1832 | +** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */ | ||
1833 | +enum { | ||
1834 | + IO_ACX_SOFT_RESET = 0, | ||
1835 | + | ||
1836 | + IO_ACX_SLV_MEM_ADDR, | ||
1837 | + IO_ACX_SLV_MEM_DATA, | ||
1838 | + IO_ACX_SLV_MEM_CTL, | ||
1839 | + IO_ACX_SLV_END_CTL, | ||
1840 | + | ||
1841 | + IO_ACX_FEMR, /* Function Event Mask */ | ||
1842 | + | ||
1843 | + IO_ACX_INT_TRIG, | ||
1844 | + IO_ACX_IRQ_MASK, | ||
1845 | + IO_ACX_IRQ_STATUS_NON_DES, | ||
1846 | + IO_ACX_IRQ_STATUS_CLEAR, /* CLEAR = clear on read */ | ||
1847 | + IO_ACX_IRQ_ACK, | ||
1848 | + IO_ACX_HINT_TRIG, | ||
1849 | + | ||
1850 | + IO_ACX_ENABLE, | ||
1851 | + | ||
1852 | + IO_ACX_EEPROM_CTL, | ||
1853 | + IO_ACX_EEPROM_ADDR, | ||
1854 | + IO_ACX_EEPROM_DATA, | ||
1855 | + IO_ACX_EEPROM_CFG, | ||
1856 | + | ||
1857 | + IO_ACX_PHY_ADDR, | ||
1858 | + IO_ACX_PHY_DATA, | ||
1859 | + IO_ACX_PHY_CTL, | ||
1860 | + | ||
1861 | + IO_ACX_GPIO_OE, | ||
1862 | + | ||
1863 | + IO_ACX_GPIO_OUT, | ||
1864 | + | ||
1865 | + IO_ACX_CMD_MAILBOX_OFFS, | ||
1866 | + IO_ACX_INFO_MAILBOX_OFFS, | ||
1867 | + IO_ACX_EEPROM_INFORMATION, | ||
1868 | + | ||
1869 | + IO_ACX_EE_START, | ||
1870 | + IO_ACX_SOR_CFG, | ||
1871 | + IO_ACX_ECPU_CTRL | ||
1872 | +}; | ||
1873 | +/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION | ||
1874 | +** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */ | ||
1875 | + | ||
1876 | +/* Values for IO_ACX_INT_TRIG register: */ | ||
1877 | +/* inform hw that rxdesc in queue needs processing */ | ||
1878 | +#define INT_TRIG_RXPRC 0x08 | ||
1879 | +/* inform hw that txdesc in queue needs processing */ | ||
1880 | +#define INT_TRIG_TXPRC 0x04 | ||
1881 | +/* ack that we received info from info mailbox */ | ||
1882 | +#define INT_TRIG_INFOACK 0x02 | ||
1883 | +/* inform hw that we have filled command mailbox */ | ||
1884 | +#define INT_TRIG_CMD 0x01 | ||
1885 | + | ||
1886 | +struct txhostdesc { | ||
1887 | + acx_ptr data_phy; /* 0x00 [u8 *] */ | ||
1888 | + u16 data_offset; /* 0x04 */ | ||
1889 | + u16 reserved; /* 0x06 */ | ||
1890 | + u16 Ctl_16; /* 16bit value, endianness!! */ | ||
1891 | + u16 length; /* 0x0a */ | ||
1892 | + acx_ptr desc_phy_next; /* 0x0c [txhostdesc *] */ | ||
1893 | + acx_ptr pNext; /* 0x10 [txhostdesc *] */ | ||
1894 | + u32 Status; /* 0x14, unused on Tx */ | ||
1895 | +/* From here on you can use this area as you want (variable length, too!) */ | ||
1896 | + u8 *data; | ||
1897 | +} ACX_PACKED; | ||
1898 | + | ||
1899 | +struct rxhostdesc { | ||
1900 | + acx_ptr data_phy; /* 0x00 [rxbuffer_t *] */ | ||
1901 | + u16 data_offset; /* 0x04 */ | ||
1902 | + u16 reserved; /* 0x06 */ | ||
1903 | + u16 Ctl_16; /* 0x08; 16bit value, endianness!! */ | ||
1904 | + u16 length; /* 0x0a */ | ||
1905 | + acx_ptr desc_phy_next; /* 0x0c [rxhostdesc_t *] */ | ||
1906 | + acx_ptr pNext; /* 0x10 [rxhostdesc_t *] */ | ||
1907 | + u32 Status; /* 0x14 */ | ||
1908 | +/* From here on you can use this area as you want (variable length, too!) */ | ||
1909 | + rxbuffer_t *data; | ||
1910 | +} ACX_PACKED; | ||
1911 | + | ||
1912 | +#endif /* ACX_PCI */ | ||
1913 | + | ||
1914 | +/*********************************************************************** | ||
1915 | +** USB structures and constants | ||
1916 | +*/ | ||
1917 | +#ifdef ACX_USB | ||
1918 | + | ||
1919 | +/* Used for usb_txbuffer.desc field */ | ||
1920 | +#define USB_TXBUF_TXDESC 0xA | ||
1921 | +/* Size of header (everything up to data[]) */ | ||
1922 | +#define USB_TXBUF_HDRSIZE 14 | ||
1923 | +typedef struct usb_txbuffer { | ||
1924 | + u16 desc; | ||
1925 | + u16 mpdu_len; | ||
1926 | + u8 queue_index; | ||
1927 | + u8 rate; | ||
1928 | + u32 hostdata; | ||
1929 | + u8 ctrl1; | ||
1930 | + u8 ctrl2; | ||
1931 | + u16 data_len; | ||
1932 | + /* wlan packet content is placed here: */ | ||
1933 | + u8 data[WLAN_A4FR_MAXLEN_WEP_FCS]; | ||
1934 | +} ACX_PACKED usb_txbuffer_t; | ||
1935 | + | ||
1936 | +/* USB returns either rx packets (see rxbuffer) or | ||
1937 | +** these "tx status" structs: */ | ||
1938 | +typedef struct usb_txstatus { | ||
1939 | + u16 mac_cnt_rcvd; /* only 12 bits are len! (0xfff) */ | ||
1940 | + u8 queue_index; | ||
1941 | + u8 mac_status; /* seen 0x20 on tx failure */ | ||
1942 | + u32 hostdata; | ||
1943 | + u8 rate; | ||
1944 | + u8 ack_failures; | ||
1945 | + u8 rts_failures; | ||
1946 | + u8 rts_ok; | ||
1947 | +} ACX_PACKED usb_txstatus_t; | ||
1948 | + | ||
1949 | +typedef struct usb_tx { | ||
1950 | + unsigned busy:1; | ||
1951 | + struct urb *urb; | ||
1952 | + acx_device_t *adev; | ||
1953 | + /* actual USB bulk output data block is here: */ | ||
1954 | + usb_txbuffer_t bulkout; | ||
1955 | +} usb_tx_t; | ||
1956 | + | ||
1957 | +struct usb_rx_plain { | ||
1958 | + unsigned busy:1; | ||
1959 | + struct urb *urb; | ||
1960 | + acx_device_t *adev; | ||
1961 | + rxbuffer_t bulkin; | ||
1962 | +}; | ||
1963 | + | ||
1964 | +typedef struct usb_rx { | ||
1965 | + unsigned busy:1; | ||
1966 | + struct urb *urb; | ||
1967 | + acx_device_t *adev; | ||
1968 | + rxbuffer_t bulkin; | ||
1969 | + /* Make entire structure 4k. Report if it breaks something. */ | ||
1970 | + u8 padding[4*1024 - sizeof(struct usb_rx_plain)]; | ||
1971 | +} usb_rx_t; | ||
1972 | +#endif /* ACX_USB */ | ||
1973 | + | ||
1974 | + | ||
1975 | +/* Config Option structs */ | ||
1976 | + | ||
1977 | +typedef struct co_antennas { | ||
1978 | + u8 type; | ||
1979 | + u8 len; | ||
1980 | + u8 list[2]; | ||
1981 | +} ACX_PACKED co_antennas_t; | ||
1982 | + | ||
1983 | +typedef struct co_powerlevels { | ||
1984 | + u8 type; | ||
1985 | + u8 len; | ||
1986 | + u16 list[8]; | ||
1987 | +} ACX_PACKED co_powerlevels_t; | ||
1988 | + | ||
1989 | +typedef struct co_datarates { | ||
1990 | + u8 type; | ||
1991 | + u8 len; | ||
1992 | + u8 list[8]; | ||
1993 | +} ACX_PACKED co_datarates_t; | ||
1994 | + | ||
1995 | +typedef struct co_domains { | ||
1996 | + u8 type; | ||
1997 | + u8 len; | ||
1998 | + u8 list[6]; | ||
1999 | +} ACX_PACKED co_domains_t; | ||
2000 | + | ||
2001 | +typedef struct co_product_id { | ||
2002 | + u8 type; | ||
2003 | + u8 len; | ||
2004 | + u8 list[128]; | ||
2005 | +} ACX_PACKED co_product_id_t; | ||
2006 | + | ||
2007 | +typedef struct co_manuf_id { | ||
2008 | + u8 type; | ||
2009 | + u8 len; | ||
2010 | + u8 list[128]; | ||
2011 | +} ACX_PACKED co_manuf_t; | ||
2012 | + | ||
2013 | +typedef struct co_fixed { | ||
2014 | + char NVSv[8]; | ||
2015 | +/* u16 NVS_vendor_offs; ACX111-only */ | ||
2016 | +/* u16 unknown; ACX111-only */ | ||
2017 | + u8 MAC[6]; /* ACX100-only */ | ||
2018 | + u16 probe_delay; /* ACX100-only */ | ||
2019 | + u32 eof_memory; | ||
2020 | + u8 dot11CCAModes; | ||
2021 | + u8 dot11Diversity; | ||
2022 | + u8 dot11ShortPreambleOption; | ||
2023 | + u8 dot11PBCCOption; | ||
2024 | + u8 dot11ChannelAgility; | ||
2025 | + u8 dot11PhyType; /* FIXME: does 802.11 call it "dot11PHYType"? */ | ||
2026 | + u8 dot11TempType; | ||
2027 | + u8 table_count; | ||
2028 | +} ACX_PACKED co_fixed_t; | ||
2029 | + | ||
2030 | +typedef struct acx111_ie_configoption { | ||
2031 | + u16 type; | ||
2032 | + u16 len; | ||
2033 | +/* Do not access below members directly, they are in fact variable length */ | ||
2034 | + co_fixed_t fixed; | ||
2035 | + co_antennas_t antennas; | ||
2036 | + co_powerlevels_t power_levels; | ||
2037 | + co_datarates_t data_rates; | ||
2038 | + co_domains_t domains; | ||
2039 | + co_product_id_t product_id; | ||
2040 | + co_manuf_t manufacturer; | ||
2041 | + u8 _padding[4]; | ||
2042 | +} ACX_PACKED acx111_ie_configoption_t; | ||
2043 | + | ||
2044 | + | ||
2045 | +/*********************************************************************** | ||
2046 | +** Main acx per-device data structure | ||
2047 | +*/ | ||
2048 | +#define ACX_STATE_FW_LOADED 0x01 | ||
2049 | +#define ACX_STATE_IFACE_UP 0x02 | ||
2050 | + | ||
2051 | +/* MAC mode (BSS type) defines | ||
2052 | + * Note that they shouldn't be redefined, since they are also used | ||
2053 | + * during communication with firmware */ | ||
2054 | +#define ACX_MODE_0_ADHOC 0 | ||
2055 | +#define ACX_MODE_1_UNUSED 1 | ||
2056 | +#define ACX_MODE_2_STA 2 | ||
2057 | +#define ACX_MODE_3_AP 3 | ||
2058 | +/* These are our own inventions. Sending these to firmware | ||
2059 | +** makes it stop emitting beacons, which is exactly what we want | ||
2060 | +** for these modes */ | ||
2061 | +#define ACX_MODE_MONITOR 0xfe | ||
2062 | +#define ACX_MODE_OFF 0xff | ||
2063 | +/* 'Submode': identifies exact status of ADHOC/STA host */ | ||
2064 | +#define ACX_STATUS_0_STOPPED 0 | ||
2065 | +#define ACX_STATUS_1_SCANNING 1 | ||
2066 | +#define ACX_STATUS_2_WAIT_AUTH 2 | ||
2067 | +#define ACX_STATUS_3_AUTHENTICATED 3 | ||
2068 | +#define ACX_STATUS_4_ASSOCIATED 4 | ||
2069 | + | ||
2070 | +/* FIXME: this should be named something like struct acx_priv (typedef'd to | ||
2071 | + * acx_priv_t) */ | ||
2072 | + | ||
2073 | +/* non-firmware struct, no packing necessary */ | ||
2074 | +struct acx_device { | ||
2075 | + /* most frequent accesses first (dereferencing and cache line!) */ | ||
2076 | + | ||
2077 | + /*** Locking ***/ | ||
2078 | + /* FIXME: try to convert semaphore to more efficient mutex according | ||
2079 | + to Ingo Molnar's docs (but not before driver is in mainline or | ||
2080 | + pre-mutex Linux 2.6.10 is very outdated). */ | ||
2081 | + struct semaphore sem; | ||
2082 | + spinlock_t lock; | ||
2083 | +#if defined(PARANOID_LOCKING) /* Lock debugging */ | ||
2084 | + const char *last_sem; | ||
2085 | + const char *last_lock; | ||
2086 | + unsigned long sem_time; | ||
2087 | + unsigned long lock_time; | ||
2088 | +#endif | ||
2089 | +#ifdef ACX_MEM | ||
2090 | + spinlock_t txbuf_lock; | ||
2091 | +#endif | ||
2092 | + | ||
2093 | + /*** Linux network device ***/ | ||
2094 | + struct net_device *ndev; /* pointer to linux netdevice */ | ||
2095 | + | ||
2096 | + /*** Device statistics ***/ | ||
2097 | + struct net_device_stats stats; /* net device statistics */ | ||
2098 | +#ifdef WIRELESS_EXT | ||
2099 | + struct iw_statistics wstats; /* wireless statistics */ | ||
2100 | +#endif | ||
2101 | + /*** Power managment ***/ | ||
2102 | + struct pm_dev *pm; /* PM crap */ | ||
2103 | + | ||
2104 | + /*** Management timer ***/ | ||
2105 | + struct timer_list mgmt_timer; | ||
2106 | + | ||
2107 | + /*** Hardware identification ***/ | ||
2108 | + const char *chip_name; | ||
2109 | + u8 dev_type; | ||
2110 | + u8 chip_type; | ||
2111 | + u8 form_factor; | ||
2112 | + u8 radio_type; | ||
2113 | + u8 eeprom_version; | ||
2114 | + | ||
2115 | + /*** Config retrieved from EEPROM ***/ | ||
2116 | + char cfgopt_NVSv[8]; | ||
2117 | + u16 cfgopt_NVS_vendor_offs; | ||
2118 | + u8 cfgopt_MAC[6]; | ||
2119 | + u16 cfgopt_probe_delay; | ||
2120 | + u32 cfgopt_eof_memory; | ||
2121 | + u8 cfgopt_dot11CCAModes; | ||
2122 | + u8 cfgopt_dot11Diversity; | ||
2123 | + u8 cfgopt_dot11ShortPreambleOption; | ||
2124 | + u8 cfgopt_dot11PBCCOption; | ||
2125 | + u8 cfgopt_dot11ChannelAgility; | ||
2126 | + u8 cfgopt_dot11PhyType; | ||
2127 | + u8 cfgopt_dot11TempType; | ||
2128 | + co_antennas_t cfgopt_antennas; | ||
2129 | + co_powerlevels_t cfgopt_power_levels; | ||
2130 | + co_datarates_t cfgopt_data_rates; | ||
2131 | + co_domains_t cfgopt_domains; | ||
2132 | + co_product_id_t cfgopt_product_id; | ||
2133 | + co_manuf_t cfgopt_manufacturer; | ||
2134 | + | ||
2135 | + /*** Firmware identification ***/ | ||
2136 | + char firmware_version[FW_ID_SIZE+1]; | ||
2137 | + u32 firmware_numver; | ||
2138 | + u32 firmware_id; | ||
2139 | + const u16 *ie_len; | ||
2140 | + const u16 *ie_len_dot11; | ||
2141 | + | ||
2142 | + /*** Device state ***/ | ||
2143 | + u16 dev_state_mask; | ||
2144 | + u8 led_power; /* power LED status */ | ||
2145 | + u32 get_mask; /* mask of settings to fetch from the card */ | ||
2146 | + u32 set_mask; /* mask of settings to write to the card */ | ||
2147 | + | ||
2148 | + /* Barely used in USB case */ | ||
2149 | + u16 irq_status; | ||
2150 | + | ||
2151 | + u8 after_interrupt_jobs; /* mini job list for doing actions after an interrupt occurred */ | ||
2152 | + WORK_STRUCT after_interrupt_task; /* our task for after interrupt actions */ | ||
2153 | + | ||
2154 | + /*** scanning ***/ | ||
2155 | + u16 scan_count; /* number of times to do channel scan */ | ||
2156 | + u8 scan_mode; /* 0 == active, 1 == passive, 2 == background */ | ||
2157 | + u8 scan_rate; | ||
2158 | + u16 scan_duration; | ||
2159 | + u16 scan_probe_delay; | ||
2160 | +#if WIRELESS_EXT > 15 | ||
2161 | + struct iw_spy_data spy_data; /* FIXME: needs to be implemented! */ | ||
2162 | +#endif | ||
2163 | + | ||
2164 | + /*** Wireless network settings ***/ | ||
2165 | + /* copy of the device address (ifconfig hw ether) that we actually use | ||
2166 | + ** for 802.11; copied over from the network device's MAC address | ||
2167 | + ** (ifconfig) when it makes sense only */ | ||
2168 | + u8 dev_addr[MAX_ADDR_LEN]; | ||
2169 | + u8 bssid[ETH_ALEN]; /* the BSSID after having joined */ | ||
2170 | + u8 ap[ETH_ALEN]; /* The AP we want, FF:FF:FF:FF:FF:FF is any */ | ||
2171 | + u16 aid; /* The Association ID sent from the AP / last used AID if we're an AP */ | ||
2172 | + u16 mode; /* mode from iwconfig */ | ||
2173 | + int monitor_type; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_PRISM */ | ||
2174 | + u16 status; /* 802.11 association status */ | ||
2175 | + u8 essid_active; /* specific ESSID active, or select any? */ | ||
2176 | + u8 essid_len; /* to avoid dozens of strlen() */ | ||
2177 | + /* INCLUDES \0 termination for easy printf - but many places | ||
2178 | + ** simply want the string data memcpy'd plus a length indicator! | ||
2179 | + ** Keep that in mind... */ | ||
2180 | + char essid[IW_ESSID_MAX_SIZE+1]; | ||
2181 | + /* essid we are going to use for association, in case of "essid 'any'" | ||
2182 | + ** and in case of hidden ESSID (use configured ESSID then) */ | ||
2183 | + char essid_for_assoc[IW_ESSID_MAX_SIZE+1]; | ||
2184 | + char nick[IW_ESSID_MAX_SIZE+1]; /* see essid! */ | ||
2185 | + u8 channel; | ||
2186 | + u8 reg_dom_id; /* reg domain setting */ | ||
2187 | + u16 reg_dom_chanmask; | ||
2188 | + u16 auth_or_assoc_retries; | ||
2189 | + u16 scan_retries; | ||
2190 | + unsigned long scan_start; /* YES, jiffies is defined as "unsigned long" */ | ||
2191 | + | ||
2192 | + /* stations known to us (if we're an ap) */ | ||
2193 | + client_t sta_list[32]; /* tab is larger than list, so that */ | ||
2194 | + client_t *sta_hash_tab[64]; /* hash collisions are not likely */ | ||
2195 | + client_t *ap_client; /* this one is our AP (STA mode only) */ | ||
2196 | + | ||
2197 | + int dup_count; | ||
2198 | + int nondup_count; | ||
2199 | + unsigned long dup_msg_expiry; | ||
2200 | + u16 last_seq_ctrl; /* duplicate packet detection */ | ||
2201 | + | ||
2202 | + /* 802.11 power save mode */ | ||
2203 | + u8 ps_wakeup_cfg; | ||
2204 | + u8 ps_listen_interval; | ||
2205 | + u8 ps_options; | ||
2206 | + u8 ps_hangover_period; | ||
2207 | + u32 ps_enhanced_transition_time; | ||
2208 | + u32 ps_beacon_rx_time; | ||
2209 | + | ||
2210 | + /*** PHY settings ***/ | ||
2211 | + u8 fallback_threshold; | ||
2212 | + u8 stepup_threshold; | ||
2213 | + u16 rate_basic; | ||
2214 | + u16 rate_oper; | ||
2215 | + u16 rate_bcast; | ||
2216 | + u16 rate_bcast100; | ||
2217 | + u8 rate_auto; /* false if "iwconfig rate N" (WITHOUT 'auto'!) */ | ||
2218 | + u8 preamble_mode; /* 0 == Long Preamble, 1 == Short, 2 == Auto */ | ||
2219 | + u8 preamble_cur; | ||
2220 | + | ||
2221 | + u8 tx_disabled; | ||
2222 | + u8 tx_level_dbm; | ||
2223 | + /* u8 tx_level_val; */ | ||
2224 | + /* u8 tx_level_auto; whether to do automatic power adjustment */ | ||
2225 | + | ||
2226 | + unsigned long recalib_time_last_success; | ||
2227 | + unsigned long recalib_time_last_attempt; | ||
2228 | + int recalib_failure_count; | ||
2229 | + int recalib_msg_ratelimit; | ||
2230 | + int retry_errors_msg_ratelimit; | ||
2231 | + | ||
2232 | + unsigned long brange_time_last_state_change; /* time the power LED was last changed */ | ||
2233 | + u8 brange_last_state; /* last state of the LED */ | ||
2234 | + u8 brange_max_quality; /* maximum quality that equates to full speed */ | ||
2235 | + | ||
2236 | + u8 sensitivity; | ||
2237 | + u8 antenna; /* antenna settings */ | ||
2238 | + u8 ed_threshold; /* energy detect threshold */ | ||
2239 | + u8 cca; /* clear channel assessment */ | ||
2240 | + | ||
2241 | + u16 rts_threshold; | ||
2242 | + u16 frag_threshold; | ||
2243 | + u32 short_retry; | ||
2244 | + u32 long_retry; | ||
2245 | + u16 msdu_lifetime; | ||
2246 | + u16 listen_interval; /* given in units of beacon interval */ | ||
2247 | + u32 beacon_interval; | ||
2248 | + | ||
2249 | + u16 capabilities; | ||
2250 | + u8 rate_supported_len; | ||
2251 | + u8 rate_supported[13]; | ||
2252 | + | ||
2253 | + /*** Encryption settings (WEP) ***/ | ||
2254 | + u32 auth_alg; /* used in transmit_authen1 */ | ||
2255 | + u8 wep_enabled; | ||
2256 | + u8 wep_restricted; | ||
2257 | + u8 wep_current_index; | ||
2258 | + wep_key_t wep_keys[DOT11_MAX_DEFAULT_WEP_KEYS]; /* the default WEP keys */ | ||
2259 | + key_struct_t wep_key_struct[10]; | ||
2260 | + | ||
2261 | + /*** Unknown ***/ | ||
2262 | + u8 dtim_interval; | ||
2263 | + | ||
2264 | +#ifdef ACX_MEM | ||
2265 | + u32 acx_txbuf_start; | ||
2266 | + int acx_txbuf_numblocks; | ||
2267 | + u32 acx_txbuf_free; /* addr of head of free list */ | ||
2268 | + int acx_txbuf_blocks_free; /* how many are still open */ | ||
2269 | + queueindicator_t *acx_queue_indicator; | ||
2270 | +#endif | ||
2271 | + | ||
2272 | + /*** Card Rx/Tx management ***/ | ||
2273 | + u16 rx_config_1; | ||
2274 | + u16 rx_config_2; | ||
2275 | + u16 memblocksize; | ||
2276 | + unsigned int tx_free; | ||
2277 | + unsigned int tx_head; /* keep as close as possible to Tx stuff below (cache line) */ | ||
2278 | + u16 phy_header_len; | ||
2279 | + | ||
2280 | +/************************************************************************* | ||
2281 | + *** PCI/USB/... must be last or else hw agnostic code breaks horribly *** | ||
2282 | + *************************************************************************/ | ||
2283 | + | ||
2284 | + /* hack to let common code compile. FIXME */ | ||
2285 | + dma_addr_t rxhostdesc_startphy; | ||
2286 | + | ||
2287 | + /*** PCI stuff ***/ | ||
2288 | +#if defined(ACX_PCI) || defined(ACX_MEM) | ||
2289 | + /* pointers to tx buffers, tx host descriptors (in host memory) | ||
2290 | + ** and tx descs in device memory */ | ||
2291 | + unsigned int tx_tail; | ||
2292 | + u8 *txbuf_start; | ||
2293 | + txhostdesc_t *txhostdesc_start; | ||
2294 | + txdesc_t *txdesc_start; /* points to PCI-mapped memory */ | ||
2295 | + dma_addr_t txbuf_startphy; | ||
2296 | + dma_addr_t txhostdesc_startphy; | ||
2297 | + /* sizes of above host memory areas */ | ||
2298 | + unsigned int txbuf_area_size; | ||
2299 | + unsigned int txhostdesc_area_size; | ||
2300 | + | ||
2301 | + unsigned int txdesc_size; /* size of txdesc; ACX111 = ACX100 + 4 */ | ||
2302 | + client_t *txc[TX_CNT]; | ||
2303 | + u16 txr[TX_CNT]; | ||
2304 | + | ||
2305 | + /* same for rx */ | ||
2306 | + unsigned int rx_tail; | ||
2307 | + rxbuffer_t *rxbuf_start; | ||
2308 | + rxhostdesc_t *rxhostdesc_start; | ||
2309 | + rxdesc_t *rxdesc_start; | ||
2310 | + /* physical addresses of above host memory areas */ | ||
2311 | + dma_addr_t rxbuf_startphy; | ||
2312 | + /* dma_addr_t rxhostdesc_startphy; */ | ||
2313 | + unsigned int rxbuf_area_size; | ||
2314 | + unsigned int rxhostdesc_area_size; | ||
2315 | + | ||
2316 | + u8 need_radio_fw; | ||
2317 | + u8 irqs_active; /* whether irq sending is activated */ | ||
2318 | + | ||
2319 | + const u16 *io; /* points to ACX100 or ACX111 PCI I/O register address set */ | ||
2320 | + | ||
2321 | +#ifdef ACX_PCI | ||
2322 | + struct pci_dev *pdev; | ||
2323 | +#endif | ||
2324 | +#ifdef ACX_MEM | ||
2325 | + struct device *dev; | ||
2326 | +#endif | ||
2327 | + | ||
2328 | +#ifdef ACX_PCI | ||
2329 | + unsigned long membase; | ||
2330 | +#endif | ||
2331 | +#ifdef ACX_MEM | ||
2332 | + volatile u32 *membase; | ||
2333 | +#endif | ||
2334 | + unsigned long membase2; | ||
2335 | +#ifdef ACX_PCI | ||
2336 | + void __iomem *iobase; | ||
2337 | +#endif | ||
2338 | +#ifdef ACX_MEM | ||
2339 | + volatile u32 *iobase; | ||
2340 | +#endif | ||
2341 | + void __iomem *iobase2; | ||
2342 | + /* command interface */ | ||
2343 | + u8 __iomem *cmd_area; | ||
2344 | + u8 __iomem *info_area; | ||
2345 | + | ||
2346 | + u16 irq_mask; /* interrupt types to mask out (not wanted) with many IRQs activated */ | ||
2347 | + u16 irq_mask_off; /* interrupt types to mask out (not wanted) with IRQs off */ | ||
2348 | + unsigned int irq_loops_this_jiffy; | ||
2349 | + unsigned long irq_last_jiffies; | ||
2350 | +#endif | ||
2351 | + | ||
2352 | + /*** USB stuff ***/ | ||
2353 | +#ifdef ACX_USB | ||
2354 | + struct usb_device *usbdev; | ||
2355 | + | ||
2356 | + rxbuffer_t rxtruncbuf; | ||
2357 | + | ||
2358 | + usb_tx_t *usb_tx; | ||
2359 | + usb_rx_t *usb_rx; | ||
2360 | + | ||
2361 | + int bulkinep; /* bulk-in endpoint */ | ||
2362 | + int bulkoutep; /* bulk-out endpoint */ | ||
2363 | + int rxtruncsize; | ||
2364 | +#endif | ||
2365 | + | ||
2366 | +}; | ||
2367 | + | ||
2368 | +static inline acx_device_t* | ||
2369 | +ndev2adev(struct net_device *ndev) | ||
2370 | +{ | ||
2371 | + return netdev_priv(ndev); | ||
2372 | +} | ||
2373 | + | ||
2374 | + | ||
2375 | +/* For use with ACX1xx_IE_RXCONFIG */ | ||
2376 | +/* bit description | ||
2377 | + * 13 include additional header (length etc.) *required* | ||
2378 | + * struct is defined in 'struct rxbuffer' | ||
2379 | + * is this bit acx100 only? does acx111 always put the header, | ||
2380 | + * and bit setting is irrelevant? --vda | ||
2381 | + * 10 receive frames only with SSID used in last join cmd | ||
2382 | + * 9 discard broadcast | ||
2383 | + * 8 receive packets for multicast address 1 | ||
2384 | + * 7 receive packets for multicast address 0 | ||
2385 | + * 6 discard all multicast packets | ||
2386 | + * 5 discard frames from foreign BSSID | ||
2387 | + * 4 discard frames with foreign destination MAC address | ||
2388 | + * 3 promiscuous mode (receive ALL frames, disable filter) | ||
2389 | + * 2 include FCS | ||
2390 | + * 1 include phy header | ||
2391 | + * 0 ??? | ||
2392 | + */ | ||
2393 | +#define RX_CFG1_INCLUDE_RXBUF_HDR 0x2000 /* ACX100 only */ | ||
2394 | +#define RX_CFG1_FILTER_SSID 0x0400 | ||
2395 | +#define RX_CFG1_FILTER_BCAST 0x0200 | ||
2396 | +#define RX_CFG1_RCV_MC_ADDR1 0x0100 | ||
2397 | +#define RX_CFG1_RCV_MC_ADDR0 0x0080 | ||
2398 | +#define RX_CFG1_FILTER_ALL_MULTI 0x0040 | ||
2399 | +#define RX_CFG1_FILTER_BSSID 0x0020 | ||
2400 | +#define RX_CFG1_FILTER_MAC 0x0010 | ||
2401 | +#define RX_CFG1_RCV_PROMISCUOUS 0x0008 | ||
2402 | +#define RX_CFG1_INCLUDE_FCS 0x0004 | ||
2403 | +#define RX_CFG1_INCLUDE_PHY_HDR (WANT_PHY_HDR ? 0x0002 : 0) | ||
2404 | +/* bit description | ||
2405 | + * 11 receive association requests etc. | ||
2406 | + * 10 receive authentication frames | ||
2407 | + * 9 receive beacon frames | ||
2408 | + * 8 receive contention free packets | ||
2409 | + * 7 receive control frames | ||
2410 | + * 6 receive data frames | ||
2411 | + * 5 receive broken frames | ||
2412 | + * 4 receive management frames | ||
2413 | + * 3 receive probe requests | ||
2414 | + * 2 receive probe responses | ||
2415 | + * 1 receive RTS/CTS/ACK frames | ||
2416 | + * 0 receive other | ||
2417 | + */ | ||
2418 | +#define RX_CFG2_RCV_ASSOC_REQ 0x0800 | ||
2419 | +#define RX_CFG2_RCV_AUTH_FRAMES 0x0400 | ||
2420 | +#define RX_CFG2_RCV_BEACON_FRAMES 0x0200 | ||
2421 | +#define RX_CFG2_RCV_CONTENTION_FREE 0x0100 | ||
2422 | +#define RX_CFG2_RCV_CTRL_FRAMES 0x0080 | ||
2423 | +#define RX_CFG2_RCV_DATA_FRAMES 0x0040 | ||
2424 | +#define RX_CFG2_RCV_BROKEN_FRAMES 0x0020 | ||
2425 | +#define RX_CFG2_RCV_MGMT_FRAMES 0x0010 | ||
2426 | +#define RX_CFG2_RCV_PROBE_REQ 0x0008 | ||
2427 | +#define RX_CFG2_RCV_PROBE_RESP 0x0004 | ||
2428 | +#define RX_CFG2_RCV_ACK_FRAMES 0x0002 | ||
2429 | +#define RX_CFG2_RCV_OTHER 0x0001 | ||
2430 | + | ||
2431 | +/* For use with ACX1xx_IE_FEATURE_CONFIG */ | ||
2432 | +#define FEATURE1_80MHZ_CLOCK 0x00000040L | ||
2433 | +#define FEATURE1_4X 0x00000020L | ||
2434 | +#define FEATURE1_LOW_RX 0x00000008L | ||
2435 | +#define FEATURE1_EXTRA_LOW_RX 0x00000001L | ||
2436 | + | ||
2437 | +#define FEATURE2_SNIFFER 0x00000080L | ||
2438 | +#define FEATURE2_NO_TXCRYPT 0x00000001L | ||
2439 | + | ||
2440 | +/*-- get and set mask values --*/ | ||
2441 | +#define GETSET_LED_POWER 0x00000001L | ||
2442 | +#define GETSET_STATION_ID 0x00000002L | ||
2443 | +#define SET_TEMPLATES 0x00000004L | ||
2444 | +#define SET_STA_LIST 0x00000008L | ||
2445 | +#define GETSET_TX 0x00000010L | ||
2446 | +#define GETSET_RX 0x00000020L | ||
2447 | +#define SET_RXCONFIG 0x00000040L | ||
2448 | +#define GETSET_ANTENNA 0x00000080L | ||
2449 | +#define GETSET_SENSITIVITY 0x00000100L | ||
2450 | +#define GETSET_TXPOWER 0x00000200L | ||
2451 | +#define GETSET_ED_THRESH 0x00000400L | ||
2452 | +#define GETSET_CCA 0x00000800L | ||
2453 | +#define GETSET_POWER_80211 0x00001000L | ||
2454 | +#define GETSET_RETRY 0x00002000L | ||
2455 | +#define GETSET_REG_DOMAIN 0x00004000L | ||
2456 | +#define GETSET_CHANNEL 0x00008000L | ||
2457 | +/* Used when ESSID changes etc and we need to scan for AP anew */ | ||
2458 | +#define GETSET_RESCAN 0x00010000L | ||
2459 | +#define GETSET_MODE 0x00020000L | ||
2460 | +#define GETSET_WEP 0x00040000L | ||
2461 | +#define SET_WEP_OPTIONS 0x00080000L | ||
2462 | +#define SET_MSDU_LIFETIME 0x00100000L | ||
2463 | +#define SET_RATE_FALLBACK 0x00200000L | ||
2464 | + | ||
2465 | +/* keep in sync with the above */ | ||
2466 | +#define GETSET_ALL (0 \ | ||
2467 | +/* GETSET_LED_POWER */ | 0x00000001L \ | ||
2468 | +/* GETSET_STATION_ID */ | 0x00000002L \ | ||
2469 | +/* SET_TEMPLATES */ | 0x00000004L \ | ||
2470 | +/* SET_STA_LIST */ | 0x00000008L \ | ||
2471 | +/* GETSET_TX */ | 0x00000010L \ | ||
2472 | +/* GETSET_RX */ | 0x00000020L \ | ||
2473 | +/* SET_RXCONFIG */ | 0x00000040L \ | ||
2474 | +/* GETSET_ANTENNA */ | 0x00000080L \ | ||
2475 | +/* GETSET_SENSITIVITY */| 0x00000100L \ | ||
2476 | +/* GETSET_TXPOWER */ | 0x00000200L \ | ||
2477 | +/* GETSET_ED_THRESH */ | 0x00000400L \ | ||
2478 | +/* GETSET_CCA */ | 0x00000800L \ | ||
2479 | +/* GETSET_POWER_80211 */| 0x00001000L \ | ||
2480 | +/* GETSET_RETRY */ | 0x00002000L \ | ||
2481 | +/* GETSET_REG_DOMAIN */ | 0x00004000L \ | ||
2482 | +/* GETSET_CHANNEL */ | 0x00008000L \ | ||
2483 | +/* GETSET_RESCAN */ | 0x00010000L \ | ||
2484 | +/* GETSET_MODE */ | 0x00020000L \ | ||
2485 | +/* GETSET_WEP */ | 0x00040000L \ | ||
2486 | +/* SET_WEP_OPTIONS */ | 0x00080000L \ | ||
2487 | +/* SET_MSDU_LIFETIME */ | 0x00100000L \ | ||
2488 | +/* SET_RATE_FALLBACK */ | 0x00200000L \ | ||
2489 | + ) | ||
2490 | + | ||
2491 | + | ||
2492 | +/*********************************************************************** | ||
2493 | +** Firmware loading | ||
2494 | +*/ | ||
2495 | +#include <linux/firmware.h> /* request_firmware() */ | ||
2496 | +#include <linux/pci.h> /* struct pci_device */ | ||
2497 | + | ||
2498 | + | ||
2499 | +/*********************************************************************** | ||
2500 | +*/ | ||
2501 | +typedef struct acx100_ie_memblocksize { | ||
2502 | + u16 type; | ||
2503 | + u16 len; | ||
2504 | + u16 size; | ||
2505 | +} ACX_PACKED acx100_ie_memblocksize_t; | ||
2506 | + | ||
2507 | +typedef struct acx100_ie_queueconfig { | ||
2508 | + u16 type; | ||
2509 | + u16 len; | ||
2510 | + u32 AreaSize; | ||
2511 | + u32 RxQueueStart; | ||
2512 | + u8 QueueOptions; | ||
2513 | + u8 NumTxQueues; | ||
2514 | + u8 NumRxDesc; /* for USB only */ | ||
2515 | + u8 pad1; | ||
2516 | + u32 QueueEnd; | ||
2517 | + u32 HostQueueEnd; /* QueueEnd2 */ | ||
2518 | + u32 TxQueueStart; | ||
2519 | + u8 TxQueuePri; | ||
2520 | + u8 NumTxDesc; | ||
2521 | + u16 pad2; | ||
2522 | +} ACX_PACKED acx100_ie_queueconfig_t; | ||
2523 | + | ||
2524 | +typedef struct acx111_ie_queueconfig { | ||
2525 | + u16 type; | ||
2526 | + u16 len; | ||
2527 | + u32 tx_memory_block_address; | ||
2528 | + u32 rx_memory_block_address; | ||
2529 | + u32 rx1_queue_address; | ||
2530 | + u32 reserved1; | ||
2531 | + u32 tx1_queue_address; | ||
2532 | + u8 tx1_attributes; | ||
2533 | + u16 reserved2; | ||
2534 | + u8 reserved3; | ||
2535 | +} ACX_PACKED acx111_ie_queueconfig_t; | ||
2536 | + | ||
2537 | +typedef struct acx100_ie_memconfigoption { | ||
2538 | + u16 type; | ||
2539 | + u16 len; | ||
2540 | + u32 DMA_config; | ||
2541 | + acx_ptr pRxHostDesc; | ||
2542 | + u32 rx_mem; | ||
2543 | + u32 tx_mem; | ||
2544 | + u16 RxBlockNum; | ||
2545 | + u16 TxBlockNum; | ||
2546 | +} ACX_PACKED acx100_ie_memconfigoption_t; | ||
2547 | + | ||
2548 | +typedef struct acx111_ie_memoryconfig { | ||
2549 | + u16 type; | ||
2550 | + u16 len; | ||
2551 | + u16 no_of_stations; | ||
2552 | + u16 memory_block_size; | ||
2553 | + u8 tx_rx_memory_block_allocation; | ||
2554 | + u8 count_rx_queues; | ||
2555 | + u8 count_tx_queues; | ||
2556 | + u8 options; | ||
2557 | + u8 fragmentation; | ||
2558 | + u16 reserved1; | ||
2559 | + u8 reserved2; | ||
2560 | + | ||
2561 | + /* start of rx1 block */ | ||
2562 | + u8 rx_queue1_count_descs; | ||
2563 | + u8 rx_queue1_reserved1; | ||
2564 | + u8 rx_queue1_type; /* must be set to 7 */ | ||
2565 | + u8 rx_queue1_prio; /* must be set to 0 */ | ||
2566 | + acx_ptr rx_queue1_host_rx_start; | ||
2567 | + /* end of rx1 block */ | ||
2568 | + | ||
2569 | + /* start of tx1 block */ | ||
2570 | + u8 tx_queue1_count_descs; | ||
2571 | + u8 tx_queue1_reserved1; | ||
2572 | + u8 tx_queue1_reserved2; | ||
2573 | + u8 tx_queue1_attributes; | ||
2574 | + /* end of tx1 block */ | ||
2575 | +} ACX_PACKED acx111_ie_memoryconfig_t; | ||
2576 | + | ||
2577 | +typedef struct acx_ie_memmap { | ||
2578 | + u16 type; | ||
2579 | + u16 len; | ||
2580 | + u32 CodeStart; | ||
2581 | + u32 CodeEnd; | ||
2582 | + u32 WEPCacheStart; | ||
2583 | + u32 WEPCacheEnd; | ||
2584 | + u32 PacketTemplateStart; | ||
2585 | + u32 PacketTemplateEnd; | ||
2586 | + u32 QueueStart; | ||
2587 | + u32 QueueEnd; | ||
2588 | + u32 PoolStart; | ||
2589 | + u32 PoolEnd; | ||
2590 | +} ACX_PACKED acx_ie_memmap_t; | ||
2591 | + | ||
2592 | +typedef struct acx111_ie_feature_config { | ||
2593 | + u16 type; | ||
2594 | + u16 len; | ||
2595 | + u32 feature_options; | ||
2596 | + u32 data_flow_options; | ||
2597 | +} ACX_PACKED acx111_ie_feature_config_t; | ||
2598 | + | ||
2599 | +typedef struct acx111_ie_tx_level { | ||
2600 | + u16 type; | ||
2601 | + u16 len; | ||
2602 | + u8 level; | ||
2603 | +} ACX_PACKED acx111_ie_tx_level_t; | ||
2604 | + | ||
2605 | +#define PS_CFG_ENABLE 0x80 | ||
2606 | +#define PS_CFG_PENDING 0x40 /* status flag when entering PS */ | ||
2607 | +#define PS_CFG_WAKEUP_MODE_MASK 0x07 | ||
2608 | +#define PS_CFG_WAKEUP_BY_HOST 0x03 | ||
2609 | +#define PS_CFG_WAKEUP_EACH_ITVL 0x02 | ||
2610 | +#define PS_CFG_WAKEUP_ON_DTIM 0x01 | ||
2611 | +#define PS_CFG_WAKEUP_ALL_BEAC 0x00 | ||
2612 | + | ||
2613 | +/* Enhanced PS mode: sleep until Rx Beacon w/ the STA's AID bit set | ||
2614 | +** in the TIM; newer firmwares only(?) */ | ||
2615 | +#define PS_OPT_ENA_ENHANCED_PS 0x04 | ||
2616 | +#define PS_OPT_TX_PSPOLL 0x02 /* send PSPoll frame to fetch waiting frames from AP (on frame with matching AID) */ | ||
2617 | +#define PS_OPT_STILL_RCV_BCASTS 0x01 | ||
2618 | + | ||
2619 | +typedef struct acx100_ie_powersave { | ||
2620 | + u16 type; | ||
2621 | + u16 len; | ||
2622 | + u8 wakeup_cfg; | ||
2623 | + u8 listen_interval; /* for EACH_ITVL: wake up every "beacon units" interval */ | ||
2624 | + u8 options; | ||
2625 | + u8 hangover_period; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */ | ||
2626 | + u16 enhanced_ps_transition_time; /* rem. wake time for Enh. PS */ | ||
2627 | +} ACX_PACKED acx100_ie_powersave_t; | ||
2628 | + | ||
2629 | +typedef struct acx111_ie_powersave { | ||
2630 | + u16 type; | ||
2631 | + u16 len; | ||
2632 | + u8 wakeup_cfg; | ||
2633 | + u8 listen_interval; /* for EACH_ITVL: wake up every "beacon units" interval */ | ||
2634 | + u8 options; | ||
2635 | + u8 hangover_period; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */ | ||
2636 | + u32 beacon_rx_time; | ||
2637 | + u32 enhanced_ps_transition_time; /* rem. wake time for Enh. PS */ | ||
2638 | +} ACX_PACKED acx111_ie_powersave_t; | ||
2639 | + | ||
2640 | + | ||
2641 | +/*********************************************************************** | ||
2642 | +** Commands and template structures | ||
2643 | +*/ | ||
2644 | + | ||
2645 | +/* | ||
2646 | +** SCAN command structure | ||
2647 | +** | ||
2648 | +** even though acx100 scan rates match RATE100 constants, | ||
2649 | +** acx111 ones do not match! Therefore we do not use RATE100 #defines */ | ||
2650 | +#define ACX_SCAN_RATE_1 10 | ||
2651 | +#define ACX_SCAN_RATE_2 20 | ||
2652 | +#define ACX_SCAN_RATE_5 55 | ||
2653 | +#define ACX_SCAN_RATE_11 110 | ||
2654 | +#define ACX_SCAN_RATE_22 220 | ||
2655 | +#define ACX_SCAN_RATE_PBCC 0x80 /* OR with this if needed */ | ||
2656 | +#define ACX_SCAN_OPT_ACTIVE 0x00 /* a bit mask */ | ||
2657 | +#define ACX_SCAN_OPT_PASSIVE 0x01 | ||
2658 | +/* Background scan: we go into Power Save mode (by transmitting | ||
2659 | +** NULL data frame to AP with the power mgmt bit set), do the scan, | ||
2660 | +** and then exit Power Save mode. A plus is that AP buffers frames | ||
2661 | +** for us while we do background scan. Thus we avoid frame losses. | ||
2662 | +** Background scan can be active or passive, just like normal one */ | ||
2663 | +#define ACX_SCAN_OPT_BACKGROUND 0x02 | ||
2664 | +typedef struct acx100_scan { | ||
2665 | + u16 count; /* number of scans to do, 0xffff == continuous */ | ||
2666 | + u16 start_chan; | ||
2667 | + u16 flags; /* channel list mask; 0x8000 == all channels? */ | ||
2668 | + u8 max_rate; /* max. probe rate */ | ||
2669 | + u8 options; /* bit mask, see defines above */ | ||
2670 | + u16 chan_duration; | ||
2671 | + u16 max_probe_delay; | ||
2672 | +} ACX_PACKED acx100_scan_t; /* length 0xc */ | ||
2673 | + | ||
2674 | +#define ACX111_SCAN_RATE_6 0x0B | ||
2675 | +#define ACX111_SCAN_RATE_9 0x0F | ||
2676 | +#define ACX111_SCAN_RATE_12 0x0A | ||
2677 | +#define ACX111_SCAN_RATE_18 0x0E | ||
2678 | +#define ACX111_SCAN_RATE_24 0x09 | ||
2679 | +#define ACX111_SCAN_RATE_36 0x0D | ||
2680 | +#define ACX111_SCAN_RATE_48 0x08 | ||
2681 | +#define ACX111_SCAN_RATE_54 0x0C | ||
2682 | +#define ACX111_SCAN_OPT_5GHZ 0x04 /* else 2.4GHZ */ | ||
2683 | +#define ACX111_SCAN_MOD_SHORTPRE 0x01 /* you can combine SHORTPRE and PBCC */ | ||
2684 | +#define ACX111_SCAN_MOD_PBCC 0x80 | ||
2685 | +#define ACX111_SCAN_MOD_OFDM 0x40 | ||
2686 | +typedef struct acx111_scan { | ||
2687 | + u16 count; /* number of scans to do */ | ||
2688 | + u8 channel_list_select; /* 0: scan all channels, 1: from chan_list only */ | ||
2689 | + u16 reserved1; | ||
2690 | + u8 reserved2; | ||
2691 | + u8 rate; /* rate for probe requests (if active scan) */ | ||
2692 | + u8 options; /* bit mask, see defines above */ | ||
2693 | + u16 chan_duration; /* min time to wait for reply on one channel (in TU) */ | ||
2694 | + /* (active scan only) (802.11 section 11.1.3.2.2) */ | ||
2695 | + u16 max_probe_delay; /* max time to wait for reply on one channel (active scan) */ | ||
2696 | + /* time to listen on a channel (passive scan) */ | ||
2697 | + u8 modulation; | ||
2698 | + u8 channel_list[26]; /* bits 7:0 first byte: channels 8:1 */ | ||
2699 | + /* bits 7:0 second byte: channels 16:9 */ | ||
2700 | + /* 26 bytes is enough to cover 802.11a */ | ||
2701 | +} ACX_PACKED acx111_scan_t; | ||
2702 | + | ||
2703 | + | ||
2704 | +/* | ||
2705 | +** Radio calibration command structure | ||
2706 | +*/ | ||
2707 | +typedef struct acx111_cmd_radiocalib { | ||
2708 | +/* 0x80000000 == automatic calibration by firmware, according to interval; | ||
2709 | + * bits 0..3: select calibration methods to go through: | ||
2710 | + * calib based on DC, AfeDC, Tx mismatch, Tx equilization */ | ||
2711 | + u32 methods; | ||
2712 | + u32 interval; | ||
2713 | +} ACX_PACKED acx111_cmd_radiocalib_t; | ||
2714 | + | ||
2715 | + | ||
2716 | +/* | ||
2717 | +** Packet template structures | ||
2718 | +** | ||
2719 | +** Packet templates store contents of Beacon, Probe response, Probe request, | ||
2720 | +** Null data frame, and TIM data frame. Firmware automatically transmits | ||
2721 | +** contents of template at appropriate time: | ||
2722 | +** - Beacon: when configured as AP or Ad-hoc | ||
2723 | +** - Probe response: when configured as AP or Ad-hoc, whenever | ||
2724 | +** a Probe request frame is received | ||
2725 | +** - Probe request: when host issues SCAN command (active) | ||
2726 | +** - Null data frame: when entering 802.11 power save mode | ||
2727 | +** - TIM data: at the end of Beacon frames (if no TIM template | ||
2728 | +** is configured, then transmits default TIM) | ||
2729 | +** NB: | ||
2730 | +** - size field must be set to size of actual template | ||
2731 | +** (NOT sizeof(struct) - templates are variable in length), | ||
2732 | +** size field is not itself counted. | ||
2733 | +** - members flagged with an asterisk must be initialized with host, | ||
2734 | +** rest must be zero filled. | ||
2735 | +** - variable length fields shown only in comments */ | ||
2736 | +typedef struct acx_template_tim { | ||
2737 | + u16 size; | ||
2738 | + u8 tim_eid; /* 00 1 TIM IE ID * */ | ||
2739 | + u8 len; /* 01 1 Length * */ | ||
2740 | + u8 dtim_cnt; /* 02 1 DTIM Count */ | ||
2741 | + u8 dtim_period; /* 03 1 DTIM Period */ | ||
2742 | + u8 bitmap_ctrl; /* 04 1 Bitmap Control * (except bit0) */ | ||
2743 | + /* 05 n Partial Virtual Bitmap * */ | ||
2744 | + u8 variable[0x100 - 1-1-1-1-1]; | ||
2745 | +} ACX_PACKED acx_template_tim_t; | ||
2746 | + | ||
2747 | +typedef struct acx_template_probereq { | ||
2748 | + u16 size; | ||
2749 | + u16 fc; /* 00 2 fc * */ | ||
2750 | + u16 dur; /* 02 2 Duration */ | ||
2751 | + u8 da[6]; /* 04 6 Destination Address * */ | ||
2752 | + u8 sa[6]; /* 0A 6 Source Address * */ | ||
2753 | + u8 bssid[6]; /* 10 6 BSSID * */ | ||
2754 | + u16 seq; /* 16 2 Sequence Control */ | ||
2755 | + /* 18 n SSID * */ | ||
2756 | + /* nn n Supported Rates * */ | ||
2757 | + u8 variable[0x44 - 2-2-6-6-6-2]; | ||
2758 | +} ACX_PACKED acx_template_probereq_t; | ||
2759 | + | ||
2760 | +typedef struct acx_template_proberesp { | ||
2761 | + u16 size; | ||
2762 | + u16 fc; /* 00 2 fc * (bits [15:12] and [10:8] per 802.11 section 7.1.3.1) */ | ||
2763 | + u16 dur; /* 02 2 Duration */ | ||
2764 | + u8 da[6]; /* 04 6 Destination Address */ | ||
2765 | + u8 sa[6]; /* 0A 6 Source Address */ | ||
2766 | + u8 bssid[6]; /* 10 6 BSSID */ | ||
2767 | + u16 seq; /* 16 2 Sequence Control */ | ||
2768 | + u8 timestamp[8];/* 18 8 Timestamp */ | ||
2769 | + u16 beacon_interval; /* 20 2 Beacon Interval * */ | ||
2770 | + u16 cap; /* 22 2 Capability Information * */ | ||
2771 | + /* 24 n SSID * */ | ||
2772 | + /* nn n Supported Rates * */ | ||
2773 | + /* nn 1 DS Parameter Set * */ | ||
2774 | + u8 variable[0x54 - 2-2-6-6-6-2-8-2-2]; | ||
2775 | +} ACX_PACKED acx_template_proberesp_t; | ||
2776 | +#define acx_template_beacon_t acx_template_proberesp_t | ||
2777 | +#define acx_template_beacon acx_template_proberesp | ||
2778 | + | ||
2779 | +typedef struct acx_template_nullframe { | ||
2780 | + u16 size; | ||
2781 | + struct wlan_hdr_a3 hdr; | ||
2782 | +} ACX_PACKED acx_template_nullframe_t; | ||
2783 | + | ||
2784 | + | ||
2785 | +/* | ||
2786 | +** JOIN command structure | ||
2787 | +** | ||
2788 | +** as opposed to acx100, acx111 dtim interval is AFTER rates_basic111. | ||
2789 | +** NOTE: took me about an hour to get !@#$%^& packing right --> struct packing is eeeeevil... */ | ||
2790 | +typedef struct acx_joinbss { | ||
2791 | + u8 bssid[ETH_ALEN]; | ||
2792 | + u16 beacon_interval; | ||
2793 | + union { | ||
2794 | + struct { | ||
2795 | + u8 dtim_interval; | ||
2796 | + u8 rates_basic; | ||
2797 | + u8 rates_supported; | ||
2798 | + /* | ||
2799 | + * ARM compiler doesn't pack correctly unless unions | ||
2800 | + * inside structures are multiples of 4 bytes. Ugh. | ||
2801 | + */ | ||
2802 | + u8 genfrm_txrate; /* generated frame (bcn, proberesp, RTS, PSpoll) tx rate */ | ||
2803 | + } ACX_PACKED acx100; | ||
2804 | + struct { | ||
2805 | + u16 rates_basic; | ||
2806 | + u8 dtim_interval; | ||
2807 | + u8 genfrm_txrate; /* generated frame (bcn, proberesp, RTS, PSpoll) tx rate */ | ||
2808 | + } ACX_PACKED acx111; | ||
2809 | + /* | ||
2810 | + * ARM compiler doesn't pack correctly unles unions are aligned on | ||
2811 | + * 4 byte boundaries and are multiples of 4 bytes. | ||
2812 | + */ | ||
2813 | + struct { | ||
2814 | + u8 d1; | ||
2815 | + u8 d2; | ||
2816 | + u8 d3; | ||
2817 | + u8 genfrm_txrate; | ||
2818 | + } ACX_PACKED txrate; | ||
2819 | + } ACX_PACKED u; | ||
2820 | + u8 genfrm_mod_pre; /* generated frame modulation/preamble: | ||
2821 | + ** bit7: PBCC, bit6: OFDM (else CCK/DQPSK/DBPSK) | ||
2822 | + ** bit5: short pre */ | ||
2823 | + u8 macmode; /* BSS Type, must be one of ACX_MODE_xxx */ | ||
2824 | + u8 channel; | ||
2825 | + u8 essid_len; | ||
2826 | + char essid[IW_ESSID_MAX_SIZE]; | ||
2827 | +} ACX_PACKED acx_joinbss_t; | ||
2828 | + | ||
2829 | +#define JOINBSS_RATES_1 0x01 | ||
2830 | +#define JOINBSS_RATES_2 0x02 | ||
2831 | +#define JOINBSS_RATES_5 0x04 | ||
2832 | +#define JOINBSS_RATES_11 0x08 | ||
2833 | +#define JOINBSS_RATES_22 0x10 | ||
2834 | + | ||
2835 | +/* Looks like missing bits are used to indicate 11g rates! | ||
2836 | +** (it follows from the fact that constants below match 1:1 to RATE111_nn) | ||
2837 | +** This was actually seen! Look at that Assoc Request sent by acx111, | ||
2838 | +** it _does_ contain 11g rates in basic set: | ||
2839 | +01:30:20.070772 Beacon (xxx) [1.0* 2.0* 5.5* 11.0* 6.0* 9.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ESS CH: 1 | ||
2840 | +01:30:20.074425 Authentication (Open System)-1: Succesful | ||
2841 | +01:30:20.076539 Authentication (Open System)-2: | ||
2842 | +01:30:20.076620 Acknowledgment | ||
2843 | +01:30:20.088546 Assoc Request (xxx) [1.0* 2.0* 5.5* 6.0* 9.0* 11.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] | ||
2844 | +01:30:20.122413 Assoc Response AID(1) :: Succesful | ||
2845 | +01:30:20.122679 Acknowledgment | ||
2846 | +01:30:20.173204 Beacon (xxx) [1.0* 2.0* 5.5* 11.0* 6.0* 9.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ESS CH: 1 | ||
2847 | +*/ | ||
2848 | +#define JOINBSS_RATES_BASIC111_1 0x0001 | ||
2849 | +#define JOINBSS_RATES_BASIC111_2 0x0002 | ||
2850 | +#define JOINBSS_RATES_BASIC111_5 0x0004 | ||
2851 | +#define JOINBSS_RATES_BASIC111_11 0x0020 | ||
2852 | +#define JOINBSS_RATES_BASIC111_22 0x0100 | ||
2853 | + | ||
2854 | + | ||
2855 | +/*********************************************************************** | ||
2856 | +*/ | ||
2857 | +typedef struct mem_read_write { | ||
2858 | + u16 addr; | ||
2859 | + u16 type; /* 0x0 int. RAM / 0xffff MAC reg. / 0x81 PHY RAM / 0x82 PHY reg.; or maybe it's actually 0x30 for MAC? Better verify it by writing and reading back and checking whether the value holds! */ | ||
2860 | + u32 len; | ||
2861 | + u32 data; | ||
2862 | +} ACX_PACKED mem_read_write_t; | ||
2863 | + | ||
2864 | +typedef struct firmware_image { | ||
2865 | + u32 chksum; | ||
2866 | + u32 size; | ||
2867 | + u8 data[1]; /* the byte array of the actual firmware... */ | ||
2868 | +} ACX_PACKED firmware_image_t; | ||
2869 | + | ||
2870 | +typedef struct acx_cmd_radioinit { | ||
2871 | + u32 offset; | ||
2872 | + u32 len; | ||
2873 | +} ACX_PACKED acx_cmd_radioinit_t; | ||
2874 | + | ||
2875 | +typedef struct acx100_ie_wep_options { | ||
2876 | + u16 type; | ||
2877 | + u16 len; | ||
2878 | + u16 NumKeys; /* max # of keys */ | ||
2879 | + u8 WEPOption; /* 0 == decrypt default key only, 1 == override decrypt */ | ||
2880 | + u8 Pad; /* used only for acx111 */ | ||
2881 | +} ACX_PACKED acx100_ie_wep_options_t; | ||
2882 | + | ||
2883 | +typedef struct ie_dot11WEPDefaultKey { | ||
2884 | + u16 type; | ||
2885 | + u16 len; | ||
2886 | + u8 action; | ||
2887 | + u8 keySize; | ||
2888 | + u8 defaultKeyNum; | ||
2889 | + u8 key[29]; /* check this! was Key[19] */ | ||
2890 | +} ACX_PACKED ie_dot11WEPDefaultKey_t; | ||
2891 | + | ||
2892 | +typedef struct acx111WEPDefaultKey { | ||
2893 | + u8 MacAddr[ETH_ALEN]; | ||
2894 | + u16 action; /* NOTE: this is a u16, NOT a u8!! */ | ||
2895 | + u16 reserved; | ||
2896 | + u8 keySize; | ||
2897 | + u8 type; | ||
2898 | + u8 index; | ||
2899 | + u8 defaultKeyNum; | ||
2900 | + u8 counter[6]; | ||
2901 | + u8 key[32]; /* up to 32 bytes (for TKIP!) */ | ||
2902 | +} ACX_PACKED acx111WEPDefaultKey_t; | ||
2903 | + | ||
2904 | +typedef struct ie_dot11WEPDefaultKeyID { | ||
2905 | + u16 type; | ||
2906 | + u16 len; | ||
2907 | + u8 KeyID; | ||
2908 | +} ACX_PACKED ie_dot11WEPDefaultKeyID_t; | ||
2909 | + | ||
2910 | +typedef struct acx100_cmd_wep_mgmt { | ||
2911 | + u8 MacAddr[ETH_ALEN]; | ||
2912 | + u16 Action; | ||
2913 | + u16 KeySize; | ||
2914 | + u8 Key[29]; /* 29*8 == 232bits == WEP256 */ | ||
2915 | +} ACX_PACKED acx100_cmd_wep_mgmt_t; | ||
2916 | + | ||
2917 | +typedef struct acx_ie_generic { | ||
2918 | + u16 type; | ||
2919 | + u16 len; | ||
2920 | + union { | ||
2921 | + /* Association ID IE: just a 16bit value: */ | ||
2922 | + u16 aid; | ||
2923 | + /* generic member for quick implementation of commands */ | ||
2924 | + u8 bytes[32]; | ||
2925 | + } ACX_PACKED m; | ||
2926 | +} ACX_PACKED acx_ie_generic_t; | ||
2927 | + | ||
2928 | +/*********************************************************************** | ||
2929 | +*/ | ||
2930 | +#define CHECK_SIZEOF(type,size) { \ | ||
2931 | + extern void BUG_bad_size_for_##type(void); \ | ||
2932 | + if (sizeof(type)!=(size)) BUG_bad_size_for_##type(); \ | ||
2933 | +} | ||
2934 | + | ||
2935 | +static inline void | ||
2936 | +acx_struct_size_check(void) | ||
2937 | +{ | ||
2938 | + CHECK_SIZEOF(txdesc_t, 0x30); | ||
2939 | + CHECK_SIZEOF(acx100_ie_memconfigoption_t, 24); | ||
2940 | + CHECK_SIZEOF(acx100_ie_queueconfig_t, 0x20); | ||
2941 | + CHECK_SIZEOF(acx_joinbss_t, 0x30); | ||
2942 | + /* IEs need 4 bytes for (type,len) tuple */ | ||
2943 | + CHECK_SIZEOF(acx111_ie_configoption_t, ACX111_IE_CONFIG_OPTIONS_LEN + 4); | ||
2944 | +} | ||
2945 | + | ||
2946 | + | ||
2947 | +/*********************************************************************** | ||
2948 | +** Global data | ||
2949 | +*/ | ||
2950 | +extern const u8 acx_bitpos2ratebyte[]; | ||
2951 | +extern const u8 acx_bitpos2rate100[]; | ||
2952 | + | ||
2953 | +extern const u8 acx_reg_domain_ids[]; | ||
2954 | +extern const char * const acx_reg_domain_strings[]; | ||
2955 | +enum { | ||
2956 | + acx_reg_domain_ids_len = 8 | ||
2957 | +}; | ||
2958 | + | ||
2959 | +extern const struct iw_handler_def acx_ioctl_handler_def; | ||
2960 | Index: linux-2.6.22/drivers/net/wireless/acx/common.c | ||
2961 | =================================================================== | ||
2962 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
2963 | +++ linux-2.6.22/drivers/net/wireless/acx/common.c 2007-08-23 18:34:19.000000000 +0200 | ||
2964 | @@ -0,0 +1,7388 @@ | ||
2965 | +/*********************************************************************** | ||
2966 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
2967 | +** | ||
2968 | +** The contents of this file are subject to the Mozilla Public | ||
2969 | +** License Version 1.1 (the "License"); you may not use this file | ||
2970 | +** except in compliance with the License. You may obtain a copy of | ||
2971 | +** the License at http://www.mozilla.org/MPL/ | ||
2972 | +** | ||
2973 | +** Software distributed under the License is distributed on an "AS | ||
2974 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
2975 | +** implied. See the License for the specific language governing | ||
2976 | +** rights and limitations under the License. | ||
2977 | +** | ||
2978 | +** Alternatively, the contents of this file may be used under the | ||
2979 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
2980 | +** case the provisions of the GPL are applicable instead of the | ||
2981 | +** above. If you wish to allow the use of your version of this file | ||
2982 | +** only under the terms of the GPL and not to allow others to use | ||
2983 | +** your version of this file under the MPL, indicate your decision | ||
2984 | +** by deleting the provisions above and replace them with the notice | ||
2985 | +** and other provisions required by the GPL. If you do not delete | ||
2986 | +** the provisions above, a recipient may use your version of this | ||
2987 | +** file under either the MPL or the GPL. | ||
2988 | +** --------------------------------------------------------------------- | ||
2989 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
2990 | +** made directly to: | ||
2991 | +** | ||
2992 | +** acx100-users@lists.sf.net | ||
2993 | +** http://acx100.sf.net | ||
2994 | +** --------------------------------------------------------------------- | ||
2995 | +*/ | ||
2996 | + | ||
2997 | +#include <linux/version.h> | ||
2998 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) | ||
2999 | +#include <linux/config.h> | ||
3000 | +#endif | ||
3001 | +#include <linux/module.h> | ||
3002 | +#include <linux/kernel.h> | ||
3003 | +#include <linux/sched.h> | ||
3004 | +#include <linux/types.h> | ||
3005 | +#include <linux/slab.h> | ||
3006 | +#include <linux/delay.h> | ||
3007 | +#include <linux/proc_fs.h> | ||
3008 | +#include <linux/if_arp.h> | ||
3009 | +#include <linux/rtnetlink.h> | ||
3010 | +#include <linux/netdevice.h> | ||
3011 | +#include <linux/etherdevice.h> | ||
3012 | +#include <linux/wireless.h> | ||
3013 | +#include <linux/pm.h> | ||
3014 | +#include <linux/vmalloc.h> | ||
3015 | +#include <net/iw_handler.h> | ||
3016 | + | ||
3017 | +#include "acx_hw.h" | ||
3018 | +#include "acx.h" | ||
3019 | + | ||
3020 | + | ||
3021 | +/*********************************************************************** | ||
3022 | +*/ | ||
3023 | +static client_t *acx_l_sta_list_alloc(acx_device_t *adev); | ||
3024 | +static client_t *acx_l_sta_list_get_from_hash(acx_device_t *adev, const u8 *address); | ||
3025 | + | ||
3026 | +static int acx_l_process_data_frame_master(acx_device_t *adev, rxbuffer_t *rxbuf); | ||
3027 | +static int acx_l_process_data_frame_client(acx_device_t *adev, rxbuffer_t *rxbuf); | ||
3028 | +/* static int acx_l_process_NULL_frame(acx_device_t *adev, rxbuffer_t *rxbuf, int vala); */ | ||
3029 | +static int acx_l_process_mgmt_frame(acx_device_t *adev, rxbuffer_t *rxbuf); | ||
3030 | +static void acx_l_process_disassoc_from_sta(acx_device_t *adev, const wlan_fr_disassoc_t *req); | ||
3031 | +static void acx_l_process_disassoc_from_ap(acx_device_t *adev, const wlan_fr_disassoc_t *req); | ||
3032 | +static void acx_l_process_deauth_from_sta(acx_device_t *adev, const wlan_fr_deauthen_t *req); | ||
3033 | +static void acx_l_process_deauth_from_ap(acx_device_t *adev, const wlan_fr_deauthen_t *req); | ||
3034 | +static int acx_l_process_probe_response(acx_device_t *adev, wlan_fr_proberesp_t *req, const rxbuffer_t *rxbuf); | ||
3035 | +static int acx_l_process_assocresp(acx_device_t *adev, const wlan_fr_assocresp_t *req); | ||
3036 | +static int acx_l_process_reassocresp(acx_device_t *adev, const wlan_fr_reassocresp_t *req); | ||
3037 | +static int acx_l_process_authen(acx_device_t *adev, const wlan_fr_authen_t *req); | ||
3038 | +static int acx_l_transmit_assocresp(acx_device_t *adev, const wlan_fr_assocreq_t *req); | ||
3039 | +static int acx_l_transmit_reassocresp(acx_device_t *adev, const wlan_fr_reassocreq_t *req); | ||
3040 | +static int acx_l_transmit_deauthen(acx_device_t *adev, const u8 *addr, u16 reason); | ||
3041 | +static int acx_l_transmit_authen1(acx_device_t *adev); | ||
3042 | +static int acx_l_transmit_authen2(acx_device_t *adev, const wlan_fr_authen_t *req, client_t *clt); | ||
3043 | +static int acx_l_transmit_authen3(acx_device_t *adev, const wlan_fr_authen_t *req); | ||
3044 | +static int acx_l_transmit_authen4(acx_device_t *adev, const wlan_fr_authen_t *req); | ||
3045 | +static int acx_l_transmit_assoc_req(acx_device_t *adev); | ||
3046 | + | ||
3047 | + | ||
3048 | +/*********************************************************************** | ||
3049 | +*/ | ||
3050 | +#if ACX_DEBUG | ||
3051 | +unsigned int acx_debug /* will add __read_mostly later */ = ACX_DEFAULT_MSG; | ||
3052 | +/* parameter is 'debug', corresponding var is acx_debug */ | ||
3053 | +module_param_named(debug, acx_debug, uint, 0); | ||
3054 | +MODULE_PARM_DESC(debug, "Debug level mask (see L_xxx constants)"); | ||
3055 | +#endif | ||
3056 | + | ||
3057 | +#ifdef MODULE_LICENSE | ||
3058 | +MODULE_LICENSE("Dual MPL/GPL"); | ||
3059 | +#endif | ||
3060 | +/* USB had this: MODULE_AUTHOR("Martin Wawro <martin.wawro AT uni-dortmund.de>"); */ | ||
3061 | +MODULE_AUTHOR("ACX100 Open Source Driver development team"); | ||
3062 | +MODULE_DESCRIPTION("Driver for TI ACX1xx based wireless cards (CardBus/PCI/USB)"); | ||
3063 | + | ||
3064 | + | ||
3065 | +/*********************************************************************** | ||
3066 | +*/ | ||
3067 | +/* Probably a number of acx's intermediate buffers for USB transfers, | ||
3068 | +** not to be confused with number of descriptors in tx/rx rings | ||
3069 | +** (which are not directly accessible to host in USB devices) */ | ||
3070 | +#define USB_RX_CNT 10 | ||
3071 | +#define USB_TX_CNT 10 | ||
3072 | + | ||
3073 | + | ||
3074 | +/*********************************************************************** | ||
3075 | +*/ | ||
3076 | + | ||
3077 | +/* minutes to wait until next radio recalibration: */ | ||
3078 | +#define RECALIB_PAUSE 5 | ||
3079 | + | ||
3080 | +/* Please keep acx_reg_domain_ids_len in sync... */ | ||
3081 | +const u8 acx_reg_domain_ids[acx_reg_domain_ids_len] = | ||
3082 | + { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40, 0x41, 0x51 }; | ||
3083 | +static const u16 reg_domain_channel_masks[acx_reg_domain_ids_len] = | ||
3084 | +#ifdef ACX_ALLOW_ALLCHANNELS | ||
3085 | + { 0x3fff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc }; | ||
3086 | +#else | ||
3087 | + { 0x07ff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc }; | ||
3088 | +#endif | ||
3089 | +const char * const | ||
3090 | +acx_reg_domain_strings[] = { | ||
3091 | + /* 0 */ " 1-11 FCC (USA)", | ||
3092 | + /* 1 */ " 1-11 DOC/IC (Canada)", | ||
3093 | +/* BTW: WLAN use in ETSI is regulated by ETSI standard EN 300 328-2 V1.1.2 */ | ||
3094 | + /* 2 */ " 1-13 ETSI (Europe)", | ||
3095 | + /* 3 */ "10-11 Spain", | ||
3096 | + /* 4 */ "10-13 France", | ||
3097 | + /* 5 */ " 14 MKK (Japan)", | ||
3098 | + /* 6 */ " 1-14 MKK1", | ||
3099 | + /* 7 */ " 3-9 Israel (not all firmware versions)", | ||
3100 | + NULL /* needs to remain as last entry */ | ||
3101 | +}; | ||
3102 | + | ||
3103 | + | ||
3104 | + | ||
3105 | +/*********************************************************************** | ||
3106 | +** Debugging support | ||
3107 | +*/ | ||
3108 | +#ifdef PARANOID_LOCKING | ||
3109 | +static unsigned max_lock_time; | ||
3110 | +static unsigned max_sem_time; | ||
3111 | + | ||
3112 | +void | ||
3113 | +acx_lock_unhold() { max_lock_time = 0; } | ||
3114 | +void | ||
3115 | +acx_sem_unhold() { max_sem_time = 0; } | ||
3116 | + | ||
3117 | +static inline const char* | ||
3118 | +sanitize_str(const char *s) | ||
3119 | +{ | ||
3120 | + const char* t = strrchr(s, '/'); | ||
3121 | + if (t) return t + 1; | ||
3122 | + return s; | ||
3123 | +} | ||
3124 | + | ||
3125 | +void | ||
3126 | +acx_lock_debug(acx_device_t *adev, const char* where) | ||
3127 | +{ | ||
3128 | + unsigned int count = 100*1000*1000; | ||
3129 | + where = sanitize_str(where); | ||
3130 | + while (--count) { | ||
3131 | + if (!spin_is_locked(&adev->lock)) break; | ||
3132 | + cpu_relax(); | ||
3133 | + } | ||
3134 | + if (!count) { | ||
3135 | + printk(KERN_EMERG "LOCKUP: already taken at %s!\n", adev->last_lock); | ||
3136 | + BUG(); | ||
3137 | + } | ||
3138 | + adev->last_lock = where; | ||
3139 | + rdtscl(adev->lock_time); | ||
3140 | +} | ||
3141 | +void | ||
3142 | +acx_unlock_debug(acx_device_t *adev, const char* where) | ||
3143 | +{ | ||
3144 | +#ifdef SMP | ||
3145 | + if (!spin_is_locked(&adev->lock)) { | ||
3146 | + where = sanitize_str(where); | ||
3147 | + printk(KERN_EMERG "STRAY UNLOCK at %s!\n", where); | ||
3148 | + BUG(); | ||
3149 | + } | ||
3150 | +#endif | ||
3151 | + if (acx_debug & L_LOCK) { | ||
3152 | + unsigned long diff; | ||
3153 | + rdtscl(diff); | ||
3154 | + diff -= adev->lock_time; | ||
3155 | + if (diff > max_lock_time) { | ||
3156 | + where = sanitize_str(where); | ||
3157 | + printk("max lock hold time %ld CPU ticks from %s " | ||
3158 | + "to %s\n", diff, adev->last_lock, where); | ||
3159 | + max_lock_time = diff; | ||
3160 | + } | ||
3161 | + } | ||
3162 | +} | ||
3163 | +void | ||
3164 | +acx_down_debug(acx_device_t *adev, const char* where) | ||
3165 | +{ | ||
3166 | + int sem_count; | ||
3167 | + unsigned long timeout = jiffies + 5*HZ; | ||
3168 | + | ||
3169 | + where = sanitize_str(where); | ||
3170 | + | ||
3171 | + for (;;) { | ||
3172 | + sem_count = atomic_read(&adev->sem.count); | ||
3173 | + if (sem_count) break; | ||
3174 | + if (time_after(jiffies, timeout)) | ||
3175 | + break; | ||
3176 | + msleep(5); | ||
3177 | + } | ||
3178 | + if (!sem_count) { | ||
3179 | + printk(KERN_EMERG "D STATE at %s! last sem at %s\n", | ||
3180 | + where, adev->last_sem); | ||
3181 | + dump_stack(); | ||
3182 | + } | ||
3183 | + adev->last_sem = where; | ||
3184 | + adev->sem_time = jiffies; | ||
3185 | + down(&adev->sem); | ||
3186 | + if (acx_debug & L_LOCK) { | ||
3187 | + printk("%s: sem_down %d -> %d\n", | ||
3188 | + where, sem_count, atomic_read(&adev->sem.count)); | ||
3189 | + } | ||
3190 | +} | ||
3191 | +void | ||
3192 | +acx_up_debug(acx_device_t *adev, const char* where) | ||
3193 | +{ | ||
3194 | + int sem_count = atomic_read(&adev->sem.count); | ||
3195 | + if (sem_count) { | ||
3196 | + where = sanitize_str(where); | ||
3197 | + printk(KERN_EMERG "STRAY UP at %s! sem.count=%d\n", where, sem_count); | ||
3198 | + dump_stack(); | ||
3199 | + } | ||
3200 | + if (acx_debug & L_LOCK) { | ||
3201 | + unsigned long diff = jiffies - adev->sem_time; | ||
3202 | + if (diff > max_sem_time) { | ||
3203 | + where = sanitize_str(where); | ||
3204 | + printk("max sem hold time %ld jiffies from %s " | ||
3205 | + "to %s\n", diff, adev->last_sem, where); | ||
3206 | + max_sem_time = diff; | ||
3207 | + } | ||
3208 | + } | ||
3209 | + up(&adev->sem); | ||
3210 | + if (acx_debug & L_LOCK) { | ||
3211 | + where = sanitize_str(where); | ||
3212 | + printk("%s: sem_up %d -> %d\n", | ||
3213 | + where, sem_count, atomic_read(&adev->sem.count)); | ||
3214 | + } | ||
3215 | +} | ||
3216 | +#endif /* PARANOID_LOCKING */ | ||
3217 | + | ||
3218 | + | ||
3219 | +/*********************************************************************** | ||
3220 | +*/ | ||
3221 | +#if ACX_DEBUG > 1 | ||
3222 | + | ||
3223 | +static int acx_debug_func_indent; | ||
3224 | +#define DEBUG_TSC 0 | ||
3225 | +#define FUNC_INDENT_INCREMENT 2 | ||
3226 | + | ||
3227 | +#if DEBUG_TSC | ||
3228 | +#define TIMESTAMP(d) unsigned long d; rdtscl(d) | ||
3229 | +#else | ||
3230 | +#define TIMESTAMP(d) unsigned long d = jiffies | ||
3231 | +#endif | ||
3232 | + | ||
3233 | +static const char | ||
3234 | +spaces[] = " " " "; /* Nx10 spaces */ | ||
3235 | + | ||
3236 | +void | ||
3237 | +log_fn_enter(const char *funcname) | ||
3238 | +{ | ||
3239 | + int indent; | ||
3240 | + TIMESTAMP(d); | ||
3241 | + | ||
3242 | + indent = acx_debug_func_indent; | ||
3243 | + if (indent >= sizeof(spaces)) | ||
3244 | + indent = sizeof(spaces)-1; | ||
3245 | + | ||
3246 | + printk("%08ld %s==> %s\n", | ||
3247 | + d % 100000000, | ||
3248 | + spaces + (sizeof(spaces)-1) - indent, | ||
3249 | + funcname | ||
3250 | + ); | ||
3251 | + | ||
3252 | + acx_debug_func_indent += FUNC_INDENT_INCREMENT; | ||
3253 | +} | ||
3254 | +void | ||
3255 | +log_fn_exit(const char *funcname) | ||
3256 | +{ | ||
3257 | + int indent; | ||
3258 | + TIMESTAMP(d); | ||
3259 | + | ||
3260 | + acx_debug_func_indent -= FUNC_INDENT_INCREMENT; | ||
3261 | + | ||
3262 | + indent = acx_debug_func_indent; | ||
3263 | + if (indent >= sizeof(spaces)) | ||
3264 | + indent = sizeof(spaces)-1; | ||
3265 | + | ||
3266 | + printk("%08ld %s<== %s\n", | ||
3267 | + d % 100000000, | ||
3268 | + spaces + (sizeof(spaces)-1) - indent, | ||
3269 | + funcname | ||
3270 | + ); | ||
3271 | +} | ||
3272 | +void | ||
3273 | +log_fn_exit_v(const char *funcname, int v) | ||
3274 | +{ | ||
3275 | + int indent; | ||
3276 | + TIMESTAMP(d); | ||
3277 | + | ||
3278 | + acx_debug_func_indent -= FUNC_INDENT_INCREMENT; | ||
3279 | + | ||
3280 | + indent = acx_debug_func_indent; | ||
3281 | + if (indent >= sizeof(spaces)) | ||
3282 | + indent = sizeof(spaces)-1; | ||
3283 | + | ||
3284 | + printk("%08ld %s<== %s: %08X\n", | ||
3285 | + d % 100000000, | ||
3286 | + spaces + (sizeof(spaces)-1) - indent, | ||
3287 | + funcname, | ||
3288 | + v | ||
3289 | + ); | ||
3290 | +} | ||
3291 | +#endif /* ACX_DEBUG > 1 */ | ||
3292 | + | ||
3293 | + | ||
3294 | +/*********************************************************************** | ||
3295 | +** Basically a msleep with logging | ||
3296 | +*/ | ||
3297 | +void | ||
3298 | +acx_s_msleep(int ms) | ||
3299 | +{ | ||
3300 | + FN_ENTER; | ||
3301 | + msleep(ms); | ||
3302 | + FN_EXIT0; | ||
3303 | +} | ||
3304 | + | ||
3305 | + | ||
3306 | +/*********************************************************************** | ||
3307 | +** Not inlined: it's larger than it seems | ||
3308 | +*/ | ||
3309 | +void | ||
3310 | +acx_print_mac(const char *head, const u8 *mac, const char *tail) | ||
3311 | +{ | ||
3312 | + printk("%s"MACSTR"%s", head, MAC(mac), tail); | ||
3313 | +} | ||
3314 | + | ||
3315 | + | ||
3316 | +/*********************************************************************** | ||
3317 | +** acx_get_status_name | ||
3318 | +*/ | ||
3319 | +static const char* | ||
3320 | +acx_get_status_name(u16 status) | ||
3321 | +{ | ||
3322 | + static const char * const str[] = { | ||
3323 | + "STOPPED", "SCANNING", "WAIT_AUTH", | ||
3324 | + "AUTHENTICATED", "ASSOCIATED", "INVALID??" | ||
3325 | + }; | ||
3326 | + if (status > VEC_SIZE(str)-1) | ||
3327 | + status = VEC_SIZE(str)-1; | ||
3328 | + | ||
3329 | + return str[status]; | ||
3330 | +} | ||
3331 | + | ||
3332 | + | ||
3333 | +/*********************************************************************** | ||
3334 | +** acx_get_packet_type_string | ||
3335 | +*/ | ||
3336 | +#if ACX_DEBUG | ||
3337 | +const char* | ||
3338 | +acx_get_packet_type_string(u16 fc) | ||
3339 | +{ | ||
3340 | + static const char * const mgmt_arr[] = { | ||
3341 | + "MGMT/AssocReq", "MGMT/AssocResp", "MGMT/ReassocReq", | ||
3342 | + "MGMT/ReassocResp", "MGMT/ProbeReq", "MGMT/ProbeResp", | ||
3343 | + "MGMT/UNKNOWN", "MGMT/UNKNOWN", "MGMT/Beacon", "MGMT/ATIM", | ||
3344 | + "MGMT/Disassoc", "MGMT/Authen", "MGMT/Deauthen" | ||
3345 | + }; | ||
3346 | + static const char * const ctl_arr[] = { | ||
3347 | + "CTL/PSPoll", "CTL/RTS", "CTL/CTS", "CTL/Ack", "CTL/CFEnd", | ||
3348 | + "CTL/CFEndCFAck" | ||
3349 | + }; | ||
3350 | + static const char * const data_arr[] = { | ||
3351 | + "DATA/DataOnly", "DATA/Data CFAck", "DATA/Data CFPoll", | ||
3352 | + "DATA/Data CFAck/CFPoll", "DATA/Null", "DATA/CFAck", | ||
3353 | + "DATA/CFPoll", "DATA/CFAck/CFPoll" | ||
3354 | + }; | ||
3355 | + const char *str; | ||
3356 | + u8 fstype = (WF_FC_FSTYPE & fc) >> 4; | ||
3357 | + u8 ctl; | ||
3358 | + | ||
3359 | + switch (WF_FC_FTYPE & fc) { | ||
3360 | + case WF_FTYPE_MGMT: | ||
3361 | + if (fstype < VEC_SIZE(mgmt_arr)) | ||
3362 | + str = mgmt_arr[fstype]; | ||
3363 | + else | ||
3364 | + str = "MGMT/UNKNOWN"; | ||
3365 | + break; | ||
3366 | + case WF_FTYPE_CTL: | ||
3367 | + ctl = fstype - 0x0a; | ||
3368 | + if (ctl < VEC_SIZE(ctl_arr)) | ||
3369 | + str = ctl_arr[ctl]; | ||
3370 | + else | ||
3371 | + str = "CTL/UNKNOWN"; | ||
3372 | + break; | ||
3373 | + case WF_FTYPE_DATA: | ||
3374 | + if (fstype < VEC_SIZE(data_arr)) | ||
3375 | + str = data_arr[fstype]; | ||
3376 | + else | ||
3377 | + str = "DATA/UNKNOWN"; | ||
3378 | + break; | ||
3379 | + default: | ||
3380 | + str = "UNKNOWN"; | ||
3381 | + break; | ||
3382 | + } | ||
3383 | + return str; | ||
3384 | +} | ||
3385 | +#endif | ||
3386 | + | ||
3387 | + | ||
3388 | +/*********************************************************************** | ||
3389 | +** acx_wlan_reason_str | ||
3390 | +*/ | ||
3391 | +static inline const char* | ||
3392 | +acx_wlan_reason_str(u16 reason) | ||
3393 | +{ | ||
3394 | + static const char* const reason_str[] = { | ||
3395 | + /* 0 */ "?", | ||
3396 | + /* 1 */ "unspecified", | ||
3397 | + /* 2 */ "prev auth is not valid", | ||
3398 | + /* 3 */ "leaving BBS", | ||
3399 | + /* 4 */ "due to inactivity", | ||
3400 | + /* 5 */ "AP is busy", | ||
3401 | + /* 6 */ "got class 2 frame from non-auth'ed STA", | ||
3402 | + /* 7 */ "got class 3 frame from non-assoc'ed STA", | ||
3403 | + /* 8 */ "STA has left BSS", | ||
3404 | + /* 9 */ "assoc without auth is not allowed", | ||
3405 | + /* 10 */ "bad power setting (802.11h)", | ||
3406 | + /* 11 */ "bad channel (802.11i)", | ||
3407 | + /* 12 */ "?", | ||
3408 | + /* 13 */ "invalid IE", | ||
3409 | + /* 14 */ "MIC failure", | ||
3410 | + /* 15 */ "four-way handshake timeout", | ||
3411 | + /* 16 */ "group key handshake timeout", | ||
3412 | + /* 17 */ "IE is different", | ||
3413 | + /* 18 */ "invalid group cipher", | ||
3414 | + /* 19 */ "invalid pairwise cipher", | ||
3415 | + /* 20 */ "invalid AKMP", | ||
3416 | + /* 21 */ "unsupported RSN version", | ||
3417 | + /* 22 */ "invalid RSN IE cap", | ||
3418 | + /* 23 */ "802.1x failed", | ||
3419 | + /* 24 */ "cipher suite rejected" | ||
3420 | + }; | ||
3421 | + return reason < VEC_SIZE(reason_str) ? reason_str[reason] : "?"; | ||
3422 | +} | ||
3423 | + | ||
3424 | + | ||
3425 | +/*********************************************************************** | ||
3426 | +** acx_cmd_status_str | ||
3427 | +*/ | ||
3428 | +const char* | ||
3429 | +acx_cmd_status_str(unsigned int state) | ||
3430 | +{ | ||
3431 | + static const char * const cmd_error_strings[] = { | ||
3432 | + "Idle", | ||
3433 | + "Success", | ||
3434 | + "Unknown Command", | ||
3435 | + "Invalid Information Element", | ||
3436 | + "Channel rejected", | ||
3437 | + "Channel invalid in current regulatory domain", | ||
3438 | + "MAC invalid", | ||
3439 | + "Command rejected (read-only information element)", | ||
3440 | + "Command rejected", | ||
3441 | + "Already asleep", | ||
3442 | + "TX in progress", | ||
3443 | + "Already awake", | ||
3444 | + "Write only", | ||
3445 | + "RX in progress", | ||
3446 | + "Invalid parameter", | ||
3447 | + "Scan in progress", | ||
3448 | + "Failed" | ||
3449 | + }; | ||
3450 | + return state < VEC_SIZE(cmd_error_strings) ? | ||
3451 | + cmd_error_strings[state] : "?"; | ||
3452 | +} | ||
3453 | + | ||
3454 | + | ||
3455 | +/*********************************************************************** | ||
3456 | +** get_status_string | ||
3457 | +*/ | ||
3458 | +static inline const char* | ||
3459 | +get_status_string(unsigned int status) | ||
3460 | +{ | ||
3461 | + /* A bit shortened, but hopefully still understandable */ | ||
3462 | + static const char * const status_str[] = { | ||
3463 | + /* 0 */ "Successful", | ||
3464 | + /* 1 */ "Unspecified failure", | ||
3465 | + /* 2 */ "reserved", | ||
3466 | + /* 3 */ "reserved", | ||
3467 | + /* 4 */ "reserved", | ||
3468 | + /* 5 */ "reserved", | ||
3469 | + /* 6 */ "reserved", | ||
3470 | + /* 7 */ "reserved", | ||
3471 | + /* 8 */ "reserved", | ||
3472 | + /* 9 */ "reserved", | ||
3473 | + /*10 */ "Cannot support all requested capabilities in Capability Information field", | ||
3474 | + /*11 */ "Reassoc denied (reason outside of 802.11b scope)", | ||
3475 | + /*12 */ "Assoc denied (reason outside of 802.11b scope) -- maybe MAC filtering by peer?", | ||
3476 | + /*13 */ "Responding station doesnt support specified auth algorithm -- maybe WEP auth Open vs. Restricted?", | ||
3477 | + /*14 */ "Auth rejected: wrong transaction sequence number", | ||
3478 | + /*15 */ "Auth rejected: challenge failure", | ||
3479 | + /*16 */ "Auth rejected: timeout for next frame in sequence", | ||
3480 | + /*17 */ "Assoc denied: too many STAs on this AP", | ||
3481 | + /*18 */ "Assoc denied: requesting STA doesnt support all data rates in basic set", | ||
3482 | + /*19 */ "Assoc denied: requesting STA doesnt support Short Preamble", | ||
3483 | + /*20 */ "Assoc denied: requesting STA doesnt support PBCC Modulation", | ||
3484 | + /*21 */ "Assoc denied: requesting STA doesnt support Channel Agility" | ||
3485 | + /*22 */ "reserved", | ||
3486 | + /*23 */ "reserved", | ||
3487 | + /*24 */ "reserved", | ||
3488 | + /*25 */ "Assoc denied: requesting STA doesnt support Short Slot Time", | ||
3489 | + /*26 */ "Assoc denied: requesting STA doesnt support DSSS-OFDM" | ||
3490 | + }; | ||
3491 | + | ||
3492 | + return status_str[status < VEC_SIZE(status_str) ? status : 2]; | ||
3493 | +} | ||
3494 | + | ||
3495 | + | ||
3496 | +/*********************************************************************** | ||
3497 | +*/ | ||
3498 | +void | ||
3499 | +acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr) | ||
3500 | +{ | ||
3501 | + if (acx_debug & L_ASSOC) { | ||
3502 | + int offset = (u8*)ie_ptr - (u8*)hdr; | ||
3503 | + printk("acx: unknown EID %d in mgmt frame at offset %d. IE: ", | ||
3504 | + ie_ptr->eid, offset); | ||
3505 | + /* IE len can be bogus, IE can extend past packet end. Oh well... */ | ||
3506 | + acx_dump_bytes(ie_ptr, ie_ptr->len + 2); | ||
3507 | + if (acx_debug & L_DATA) { | ||
3508 | + printk("frame (%s): ", | ||
3509 | + acx_get_packet_type_string(le16_to_cpu(hdr->fc))); | ||
3510 | + acx_dump_bytes(hdr, len); | ||
3511 | + } | ||
3512 | + } | ||
3513 | +} | ||
3514 | + | ||
3515 | + | ||
3516 | +/*********************************************************************** | ||
3517 | +*/ | ||
3518 | +#if ACX_DEBUG | ||
3519 | +void | ||
3520 | +acx_dump_bytes(const void *data, int num) | ||
3521 | +{ | ||
3522 | + const u8* ptr = (const u8*)data; | ||
3523 | + | ||
3524 | + if (num <= 0) { | ||
3525 | + printk("\n"); | ||
3526 | + return; | ||
3527 | + } | ||
3528 | + | ||
3529 | + while (num >= 16) { | ||
3530 | + printk( "%02X %02X %02X %02X %02X %02X %02X %02X " | ||
3531 | + "%02X %02X %02X %02X %02X %02X %02X %02X\n", | ||
3532 | + ptr[0], ptr[1], ptr[2], ptr[3], | ||
3533 | + ptr[4], ptr[5], ptr[6], ptr[7], | ||
3534 | + ptr[8], ptr[9], ptr[10], ptr[11], | ||
3535 | + ptr[12], ptr[13], ptr[14], ptr[15]); | ||
3536 | + num -= 16; | ||
3537 | + ptr += 16; | ||
3538 | + } | ||
3539 | + if (num > 0) { | ||
3540 | + while (--num > 0) | ||
3541 | + printk("%02X ", *ptr++); | ||
3542 | + printk("%02X\n", *ptr); | ||
3543 | + } | ||
3544 | +} | ||
3545 | +#endif | ||
3546 | + | ||
3547 | + | ||
3548 | +/*********************************************************************** | ||
3549 | +** acx_s_get_firmware_version | ||
3550 | +*/ | ||
3551 | +void | ||
3552 | +acx_s_get_firmware_version(acx_device_t *adev) | ||
3553 | +{ | ||
3554 | + fw_ver_t fw; | ||
3555 | + u8 hexarr[4] = { 0, 0, 0, 0 }; | ||
3556 | + int hexidx = 0, val = 0; | ||
3557 | + const char *num; | ||
3558 | + char c; | ||
3559 | + | ||
3560 | + FN_ENTER; | ||
3561 | + | ||
3562 | + memset(fw.fw_id, 'E', FW_ID_SIZE); | ||
3563 | + acx_s_interrogate(adev, &fw, ACX1xx_IE_FWREV); | ||
3564 | + memcpy(adev->firmware_version, fw.fw_id, FW_ID_SIZE); | ||
3565 | + adev->firmware_version[FW_ID_SIZE] = '\0'; | ||
3566 | + | ||
3567 | + log(L_DEBUG, "fw_ver: fw_id='%s' hw_id=%08X\n", | ||
3568 | + adev->firmware_version, fw.hw_id); | ||
3569 | + | ||
3570 | + if (strncmp(fw.fw_id, "Rev ", 4) != 0) { | ||
3571 | + printk("acx: strange firmware version string " | ||
3572 | + "'%s', please report\n", adev->firmware_version); | ||
3573 | + adev->firmware_numver = 0x01090407; /* assume 1.9.4.7 */ | ||
3574 | + } else { | ||
3575 | + num = &fw.fw_id[4]; | ||
3576 | + while (1) { | ||
3577 | + c = *num++; | ||
3578 | + if ((c == '.') || (c == '\0')) { | ||
3579 | + hexarr[hexidx++] = val; | ||
3580 | + if ((hexidx > 3) || (c == '\0')) /* end? */ | ||
3581 | + break; | ||
3582 | + val = 0; | ||
3583 | + continue; | ||
3584 | + } | ||
3585 | + if ((c >= '0') && (c <= '9')) | ||
3586 | + c -= '0'; | ||
3587 | + else | ||
3588 | + c = c - 'a' + (char)10; | ||
3589 | + val = val*16 + c; | ||
3590 | + } | ||
3591 | + | ||
3592 | + adev->firmware_numver = (u32)( | ||
3593 | + (hexarr[0] << 24) | (hexarr[1] << 16) | ||
3594 | + | (hexarr[2] << 8) | hexarr[3]); | ||
3595 | + log(L_DEBUG, "firmware_numver 0x%08X\n", adev->firmware_numver); | ||
3596 | + } | ||
3597 | + if (IS_ACX111(adev)) { | ||
3598 | + if (adev->firmware_numver == 0x00010011) { | ||
3599 | + /* This one does not survive floodpinging */ | ||
3600 | + printk("acx: firmware '%s' is known to be buggy, " | ||
3601 | + "please upgrade\n", adev->firmware_version); | ||
3602 | + } | ||
3603 | + } | ||
3604 | + | ||
3605 | + adev->firmware_id = le32_to_cpu(fw.hw_id); | ||
3606 | + | ||
3607 | + /* we're able to find out more detailed chip names now */ | ||
3608 | + switch (adev->firmware_id & 0xffff0000) { | ||
3609 | + case 0x01010000: | ||
3610 | + case 0x01020000: | ||
3611 | + adev->chip_name = "TNETW1100A"; | ||
3612 | + break; | ||
3613 | + case 0x01030000: | ||
3614 | + adev->chip_name = "TNETW1100B"; | ||
3615 | + break; | ||
3616 | + case 0x03000000: | ||
3617 | + case 0x03010000: | ||
3618 | + adev->chip_name = "TNETW1130"; | ||
3619 | + break; | ||
3620 | + case 0x04030000: /* 0x04030101 is TNETW1450 */ | ||
3621 | + adev->chip_name = "TNETW1450"; | ||
3622 | + break; | ||
3623 | + default: | ||
3624 | + printk("acx: unknown chip ID 0x%08X, " | ||
3625 | + "please report\n", adev->firmware_id); | ||
3626 | + break; | ||
3627 | + } | ||
3628 | + | ||
3629 | + FN_EXIT0; | ||
3630 | +} | ||
3631 | + | ||
3632 | + | ||
3633 | +/*********************************************************************** | ||
3634 | +** acx_display_hardware_details | ||
3635 | +** | ||
3636 | +** Displays hw/fw version, radio type etc... | ||
3637 | +*/ | ||
3638 | +void | ||
3639 | +acx_display_hardware_details(acx_device_t *adev) | ||
3640 | +{ | ||
3641 | + const char *radio_str, *form_str; | ||
3642 | + | ||
3643 | + FN_ENTER; | ||
3644 | + | ||
3645 | + switch (adev->radio_type) { | ||
3646 | + case RADIO_MAXIM_0D: | ||
3647 | + radio_str = "Maxim"; | ||
3648 | + break; | ||
3649 | + case RADIO_RFMD_11: | ||
3650 | + radio_str = "RFMD"; | ||
3651 | + break; | ||
3652 | + case RADIO_RALINK_15: | ||
3653 | + radio_str = "Ralink"; | ||
3654 | + break; | ||
3655 | + case RADIO_RADIA_16: | ||
3656 | + radio_str = "Radia"; | ||
3657 | + break; | ||
3658 | + case RADIO_UNKNOWN_17: | ||
3659 | + /* TI seems to have a radio which is | ||
3660 | + * additionally 802.11a capable, too */ | ||
3661 | + radio_str = "802.11a/b/g radio?! Please report"; | ||
3662 | + break; | ||
3663 | + case RADIO_UNKNOWN_19: | ||
3664 | + radio_str = "A radio used by Safecom cards?! Please report"; | ||
3665 | + break; | ||
3666 | + case RADIO_UNKNOWN_1B: | ||
3667 | + radio_str = "An unknown radio used by TNETW1450 USB adapters"; | ||
3668 | + break; | ||
3669 | + default: | ||
3670 | + radio_str = "UNKNOWN, please report radio type name!"; | ||
3671 | + break; | ||
3672 | + } | ||
3673 | + | ||
3674 | + switch (adev->form_factor) { | ||
3675 | + case 0x00: | ||
3676 | + form_str = "unspecified"; | ||
3677 | + break; | ||
3678 | + case 0x01: | ||
3679 | + form_str = "(mini-)PCI / CardBus"; | ||
3680 | + break; | ||
3681 | + case 0x02: | ||
3682 | + form_str = "USB"; | ||
3683 | + break; | ||
3684 | + case 0x03: | ||
3685 | + form_str = "Compact Flash"; | ||
3686 | + break; | ||
3687 | + default: | ||
3688 | + form_str = "UNKNOWN, please report"; | ||
3689 | + break; | ||
3690 | + } | ||
3691 | + | ||
3692 | + printk("acx: === chipset %s, radio type 0x%02X (%s), " | ||
3693 | + "form factor 0x%02X (%s), EEPROM version 0x%02X: " | ||
3694 | + "uploaded firmware '%s' ===\n", | ||
3695 | + adev->chip_name, adev->radio_type, radio_str, | ||
3696 | + adev->form_factor, form_str, adev->eeprom_version, | ||
3697 | + adev->firmware_version); | ||
3698 | + | ||
3699 | + FN_EXIT0; | ||
3700 | +} | ||
3701 | + | ||
3702 | + | ||
3703 | +/*********************************************************************** | ||
3704 | +*/ | ||
3705 | +int | ||
3706 | +acx_e_change_mtu(struct net_device *ndev, int mtu) | ||
3707 | +{ | ||
3708 | + enum { | ||
3709 | + MIN_MTU = 256, | ||
3710 | + MAX_MTU = WLAN_DATA_MAXLEN - (ETH_HLEN) | ||
3711 | + }; | ||
3712 | + | ||
3713 | + if (mtu < MIN_MTU || mtu > MAX_MTU) | ||
3714 | + return -EINVAL; | ||
3715 | + | ||
3716 | + ndev->mtu = mtu; | ||
3717 | + return 0; | ||
3718 | +} | ||
3719 | + | ||
3720 | + | ||
3721 | +/*********************************************************************** | ||
3722 | +** acx_e_get_stats, acx_e_get_wireless_stats | ||
3723 | +*/ | ||
3724 | +struct net_device_stats* | ||
3725 | +acx_e_get_stats(struct net_device *ndev) | ||
3726 | +{ | ||
3727 | + acx_device_t *adev = ndev2adev(ndev); | ||
3728 | + return &adev->stats; | ||
3729 | +} | ||
3730 | + | ||
3731 | +struct iw_statistics* | ||
3732 | +acx_e_get_wireless_stats(struct net_device *ndev) | ||
3733 | +{ | ||
3734 | + acx_device_t *adev = ndev2adev(ndev); | ||
3735 | + return &adev->wstats; | ||
3736 | +} | ||
3737 | + | ||
3738 | + | ||
3739 | +/*********************************************************************** | ||
3740 | +** maps acx111 tx descr rate field to acx100 one | ||
3741 | +*/ | ||
3742 | +const u8 | ||
3743 | +acx_bitpos2rate100[] = { | ||
3744 | + RATE100_1 ,/* 0 */ | ||
3745 | + RATE100_2 ,/* 1 */ | ||
3746 | + RATE100_5 ,/* 2 */ | ||
3747 | + RATE100_2 ,/* 3, should not happen */ | ||
3748 | + RATE100_2 ,/* 4, should not happen */ | ||
3749 | + RATE100_11 ,/* 5 */ | ||
3750 | + RATE100_2 ,/* 6, should not happen */ | ||
3751 | + RATE100_2 ,/* 7, should not happen */ | ||
3752 | + RATE100_22 ,/* 8 */ | ||
3753 | + RATE100_2 ,/* 9, should not happen */ | ||
3754 | + RATE100_2 ,/* 10, should not happen */ | ||
3755 | + RATE100_2 ,/* 11, should not happen */ | ||
3756 | + RATE100_2 ,/* 12, should not happen */ | ||
3757 | + RATE100_2 ,/* 13, should not happen */ | ||
3758 | + RATE100_2 ,/* 14, should not happen */ | ||
3759 | + RATE100_2 ,/* 15, should not happen */ | ||
3760 | +}; | ||
3761 | + | ||
3762 | +u8 | ||
3763 | +acx_rate111to100(u16 r) { | ||
3764 | + return acx_bitpos2rate100[highest_bit(r)]; | ||
3765 | +} | ||
3766 | + | ||
3767 | + | ||
3768 | +/*********************************************************************** | ||
3769 | +** Calculate level like the feb 2003 windows driver seems to do | ||
3770 | +*/ | ||
3771 | +static u8 | ||
3772 | +acx_signal_to_winlevel(u8 rawlevel) | ||
3773 | +{ | ||
3774 | + /* u8 winlevel = (u8) (0.5 + 0.625 * rawlevel); */ | ||
3775 | + u8 winlevel = ((4 + (rawlevel * 5)) / 8); | ||
3776 | + | ||
3777 | + if (winlevel > 100) | ||
3778 | + winlevel = 100; | ||
3779 | + return winlevel; | ||
3780 | +} | ||
3781 | + | ||
3782 | +u8 | ||
3783 | +acx_signal_determine_quality(u8 signal, u8 noise) | ||
3784 | +{ | ||
3785 | + int qual; | ||
3786 | + | ||
3787 | + qual = (((signal - 30) * 100 / 70) + (100 - noise * 4)) / 2; | ||
3788 | + | ||
3789 | + if (qual > 100) | ||
3790 | + return 100; | ||
3791 | + if (qual < 0) | ||
3792 | + return 0; | ||
3793 | + return qual; | ||
3794 | +} | ||
3795 | + | ||
3796 | + | ||
3797 | +/*********************************************************************** | ||
3798 | +** Interrogate/configure commands | ||
3799 | +*/ | ||
3800 | + | ||
3801 | +/* FIXME: the lengths given here probably aren't always correct. | ||
3802 | + * They should be gradually replaced by proper "sizeof(acx1XX_ie_XXXX)-4", | ||
3803 | + * unless the firmware actually expects a different length than the struct length */ | ||
3804 | +static const u16 | ||
3805 | +acx100_ie_len[] = { | ||
3806 | + 0, | ||
3807 | + ACX100_IE_ACX_TIMER_LEN, | ||
3808 | + sizeof(acx100_ie_powersave_t)-4, /* is that 6 or 8??? */ | ||
3809 | + ACX1xx_IE_QUEUE_CONFIG_LEN, | ||
3810 | + ACX100_IE_BLOCK_SIZE_LEN, | ||
3811 | + ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN, | ||
3812 | + ACX1xx_IE_RATE_FALLBACK_LEN, | ||
3813 | + ACX100_IE_WEP_OPTIONS_LEN, | ||
3814 | + ACX1xx_IE_MEMORY_MAP_LEN, /* ACX1xx_IE_SSID_LEN, */ | ||
3815 | + 0, | ||
3816 | + ACX1xx_IE_ASSOC_ID_LEN, | ||
3817 | + 0, | ||
3818 | + ACX111_IE_CONFIG_OPTIONS_LEN, | ||
3819 | + ACX1xx_IE_FWREV_LEN, | ||
3820 | + ACX1xx_IE_FCS_ERROR_COUNT_LEN, | ||
3821 | + ACX1xx_IE_MEDIUM_USAGE_LEN, | ||
3822 | + ACX1xx_IE_RXCONFIG_LEN, | ||
3823 | + 0, | ||
3824 | + 0, | ||
3825 | + sizeof(fw_stats_t)-4, | ||
3826 | + 0, | ||
3827 | + ACX1xx_IE_FEATURE_CONFIG_LEN, | ||
3828 | + ACX111_IE_KEY_CHOOSE_LEN, | ||
3829 | + ACX1FF_IE_MISC_CONFIG_TABLE_LEN, | ||
3830 | + ACX1FF_IE_WONE_CONFIG_LEN, | ||
3831 | + 0, | ||
3832 | + ACX1FF_IE_TID_CONFIG_LEN, | ||
3833 | + 0, | ||
3834 | + 0, | ||
3835 | + 0, | ||
3836 | + ACX1FF_IE_CALIB_ASSESSMENT_LEN, | ||
3837 | + ACX1FF_IE_BEACON_FILTER_OPTIONS_LEN, | ||
3838 | + ACX1FF_IE_LOW_RSSI_THRESH_OPT_LEN, | ||
3839 | + ACX1FF_IE_NOISE_HISTOGRAM_RESULTS_LEN, | ||
3840 | + 0, | ||
3841 | + ACX1FF_IE_PACKET_DETECT_THRESH_LEN, | ||
3842 | + ACX1FF_IE_TX_CONFIG_OPTIONS_LEN, | ||
3843 | + ACX1FF_IE_CCA_THRESHOLD_LEN, | ||
3844 | + ACX1FF_IE_EVENT_MASK_LEN, | ||
3845 | + ACX1FF_IE_DTIM_PERIOD_LEN, | ||
3846 | + 0, | ||
3847 | + ACX1FF_IE_ACI_CONFIG_SET_LEN, | ||
3848 | + 0, | ||
3849 | + 0, | ||
3850 | + 0, | ||
3851 | + 0, | ||
3852 | + 0, | ||
3853 | + 0, | ||
3854 | + ACX1FF_IE_EEPROM_VER_LEN, | ||
3855 | +}; | ||
3856 | + | ||
3857 | +static const u16 | ||
3858 | +acx100_ie_len_dot11[] = { | ||
3859 | + 0, | ||
3860 | + ACX1xx_IE_DOT11_STATION_ID_LEN, | ||
3861 | + 0, | ||
3862 | + ACX100_IE_DOT11_BEACON_PERIOD_LEN, | ||
3863 | + ACX1xx_IE_DOT11_DTIM_PERIOD_LEN, | ||
3864 | + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN, | ||
3865 | + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN, | ||
3866 | + ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE_LEN, | ||
3867 | + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN, | ||
3868 | + 0, | ||
3869 | + ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN, | ||
3870 | + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN, | ||
3871 | + 0, | ||
3872 | + ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN, | ||
3873 | + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN, | ||
3874 | + ACX100_IE_DOT11_ED_THRESHOLD_LEN, | ||
3875 | + ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN, | ||
3876 | + 0, | ||
3877 | + 0, | ||
3878 | + 0, | ||
3879 | +}; | ||
3880 | + | ||
3881 | +static const u16 | ||
3882 | +acx111_ie_len[] = { | ||
3883 | + 0, | ||
3884 | + ACX100_IE_ACX_TIMER_LEN, | ||
3885 | + sizeof(acx111_ie_powersave_t)-4, | ||
3886 | + ACX1xx_IE_QUEUE_CONFIG_LEN, | ||
3887 | + ACX100_IE_BLOCK_SIZE_LEN, | ||
3888 | + ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN, | ||
3889 | + ACX1xx_IE_RATE_FALLBACK_LEN, | ||
3890 | + ACX100_IE_WEP_OPTIONS_LEN, | ||
3891 | + ACX1xx_IE_MEMORY_MAP_LEN, /* ACX1xx_IE_SSID_LEN, */ | ||
3892 | + 0, | ||
3893 | + ACX1xx_IE_ASSOC_ID_LEN, | ||
3894 | + 0, | ||
3895 | + ACX111_IE_CONFIG_OPTIONS_LEN, | ||
3896 | + ACX1xx_IE_FWREV_LEN, | ||
3897 | + ACX1xx_IE_FCS_ERROR_COUNT_LEN, | ||
3898 | + ACX1xx_IE_MEDIUM_USAGE_LEN, | ||
3899 | + ACX1xx_IE_RXCONFIG_LEN, | ||
3900 | + 0, | ||
3901 | + 0, | ||
3902 | + sizeof(fw_stats_t)-4, | ||
3903 | + 0, | ||
3904 | + ACX1xx_IE_FEATURE_CONFIG_LEN, | ||
3905 | + ACX111_IE_KEY_CHOOSE_LEN, | ||
3906 | + ACX1FF_IE_MISC_CONFIG_TABLE_LEN, | ||
3907 | + ACX1FF_IE_WONE_CONFIG_LEN, | ||
3908 | + 0, | ||
3909 | + ACX1FF_IE_TID_CONFIG_LEN, | ||
3910 | + 0, | ||
3911 | + 0, | ||
3912 | + 0, | ||
3913 | + ACX1FF_IE_CALIB_ASSESSMENT_LEN, | ||
3914 | + ACX1FF_IE_BEACON_FILTER_OPTIONS_LEN, | ||
3915 | + ACX1FF_IE_LOW_RSSI_THRESH_OPT_LEN, | ||
3916 | + ACX1FF_IE_NOISE_HISTOGRAM_RESULTS_LEN, | ||
3917 | + 0, | ||
3918 | + ACX1FF_IE_PACKET_DETECT_THRESH_LEN, | ||
3919 | + ACX1FF_IE_TX_CONFIG_OPTIONS_LEN, | ||
3920 | + ACX1FF_IE_CCA_THRESHOLD_LEN, | ||
3921 | + ACX1FF_IE_EVENT_MASK_LEN, | ||
3922 | + ACX1FF_IE_DTIM_PERIOD_LEN, | ||
3923 | + 0, | ||
3924 | + ACX1FF_IE_ACI_CONFIG_SET_LEN, | ||
3925 | + 0, | ||
3926 | + 0, | ||
3927 | + 0, | ||
3928 | + 0, | ||
3929 | + 0, | ||
3930 | + 0, | ||
3931 | + ACX1FF_IE_EEPROM_VER_LEN, | ||
3932 | +}; | ||
3933 | + | ||
3934 | +static const u16 | ||
3935 | +acx111_ie_len_dot11[] = { | ||
3936 | + 0, | ||
3937 | + ACX1xx_IE_DOT11_STATION_ID_LEN, | ||
3938 | + 0, | ||
3939 | + ACX100_IE_DOT11_BEACON_PERIOD_LEN, | ||
3940 | + ACX1xx_IE_DOT11_DTIM_PERIOD_LEN, | ||
3941 | + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN, | ||
3942 | + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN, | ||
3943 | + ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE_LEN, | ||
3944 | + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN, | ||
3945 | + 0, | ||
3946 | + ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN, | ||
3947 | + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN, | ||
3948 | + 0, | ||
3949 | + ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN, | ||
3950 | + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN, | ||
3951 | + ACX100_IE_DOT11_ED_THRESHOLD_LEN, | ||
3952 | + ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN, | ||
3953 | + 0, | ||
3954 | + 0, | ||
3955 | + 0, | ||
3956 | +}; | ||
3957 | + | ||
3958 | + | ||
3959 | +#undef FUNC | ||
3960 | +#define FUNC "configure" | ||
3961 | +#if !ACX_DEBUG | ||
3962 | +int | ||
3963 | +acx_s_configure(acx_device_t *adev, void *pdr, int type) | ||
3964 | +{ | ||
3965 | +#else | ||
3966 | +int | ||
3967 | +acx_s_configure_debug(acx_device_t *adev, void *pdr, int type, const char* typestr) | ||
3968 | +{ | ||
3969 | +#endif | ||
3970 | + u16 len; | ||
3971 | + int res; | ||
3972 | + | ||
3973 | + if (type < 0x1000) | ||
3974 | + len = adev->ie_len[type]; | ||
3975 | + else | ||
3976 | + len = adev->ie_len_dot11[type - 0x1000]; | ||
3977 | + | ||
3978 | + log(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len); | ||
3979 | + if (unlikely(!len)) { | ||
3980 | + log(L_DEBUG, "zero-length type %s?!\n", typestr); | ||
3981 | + } | ||
3982 | + | ||
3983 | + ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type); | ||
3984 | + ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len); | ||
3985 | + res = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIGURE, pdr, len + 4); | ||
3986 | + if (unlikely(OK != res)) { | ||
3987 | +#if ACX_DEBUG | ||
3988 | + printk("%s: "FUNC"(type:%s) FAILED\n", adev->ndev->name, typestr); | ||
3989 | +#else | ||
3990 | + printk("%s: "FUNC"(type:0x%X) FAILED\n", adev->ndev->name, type); | ||
3991 | +#endif | ||
3992 | + /* dump_stack() is already done in issue_cmd() */ | ||
3993 | + } | ||
3994 | + return res; | ||
3995 | +} | ||
3996 | + | ||
3997 | +#undef FUNC | ||
3998 | +#define FUNC "interrogate" | ||
3999 | +#if !ACX_DEBUG | ||
4000 | +int | ||
4001 | +acx_s_interrogate(acx_device_t *adev, void *pdr, int type) | ||
4002 | +{ | ||
4003 | +#else | ||
4004 | +int | ||
4005 | +acx_s_interrogate_debug(acx_device_t *adev, void *pdr, int type, | ||
4006 | + const char* typestr) | ||
4007 | +{ | ||
4008 | +#endif | ||
4009 | + u16 len; | ||
4010 | + int res; | ||
4011 | + | ||
4012 | + /* FIXME: no check whether this exceeds the array yet. | ||
4013 | + * We should probably remember the number of entries... */ | ||
4014 | + if (type < 0x1000) | ||
4015 | + len = adev->ie_len[type]; | ||
4016 | + else | ||
4017 | + len = adev->ie_len_dot11[type-0x1000]; | ||
4018 | + | ||
4019 | + log(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len); | ||
4020 | + | ||
4021 | + ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type); | ||
4022 | + ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len); | ||
4023 | + res = acx_s_issue_cmd(adev, ACX1xx_CMD_INTERROGATE, pdr, len + 4); | ||
4024 | + if (unlikely(OK != res)) { | ||
4025 | +#if ACX_DEBUG | ||
4026 | + printk("%s: "FUNC"(type:%s) FAILED\n", adev->ndev->name, typestr); | ||
4027 | +#else | ||
4028 | + printk("%s: "FUNC"(type:0x%X) FAILED\n", adev->ndev->name, type); | ||
4029 | +#endif | ||
4030 | + /* dump_stack() is already done in issue_cmd() */ | ||
4031 | + } | ||
4032 | + return res; | ||
4033 | +} | ||
4034 | + | ||
4035 | +#if CMD_DISCOVERY | ||
4036 | +void | ||
4037 | +great_inquisitor(acx_device_t *adev) | ||
4038 | +{ | ||
4039 | + static struct { | ||
4040 | + u16 type; | ||
4041 | + u16 len; | ||
4042 | + /* 0x200 was too large here: */ | ||
4043 | + u8 data[0x100 - 4]; | ||
4044 | + } ACX_PACKED ie; | ||
4045 | + u16 type; | ||
4046 | + | ||
4047 | + FN_ENTER; | ||
4048 | + | ||
4049 | + /* 0..0x20, 0x1000..0x1020 */ | ||
4050 | + for (type = 0; type <= 0x1020; type++) { | ||
4051 | + if (type == 0x21) | ||
4052 | + type = 0x1000; | ||
4053 | + ie.type = cpu_to_le16(type); | ||
4054 | + ie.len = cpu_to_le16(sizeof(ie) - 4); | ||
4055 | + acx_s_issue_cmd(adev, ACX1xx_CMD_INTERROGATE, &ie, sizeof(ie)); | ||
4056 | + } | ||
4057 | + FN_EXIT0; | ||
4058 | +} | ||
4059 | +#endif | ||
4060 | + | ||
4061 | + | ||
4062 | +#ifdef CONFIG_PROC_FS | ||
4063 | +/*********************************************************************** | ||
4064 | +** /proc files | ||
4065 | +*/ | ||
4066 | +/*********************************************************************** | ||
4067 | +** acx_l_proc_output | ||
4068 | +** Generate content for our /proc entry | ||
4069 | +** | ||
4070 | +** Arguments: | ||
4071 | +** buf is a pointer to write output to | ||
4072 | +** adev is the usual pointer to our private struct acx_device | ||
4073 | +** Returns: | ||
4074 | +** number of bytes actually written to buf | ||
4075 | +** Side effects: | ||
4076 | +** none | ||
4077 | +*/ | ||
4078 | +static int | ||
4079 | +acx_l_proc_output(char *buf, acx_device_t *adev) | ||
4080 | +{ | ||
4081 | + char *p = buf; | ||
4082 | + int i; | ||
4083 | + | ||
4084 | + FN_ENTER; | ||
4085 | + | ||
4086 | + p += sprintf(p, | ||
4087 | + "acx driver version:\t\t" ACX_RELEASE "\n" | ||
4088 | + "Wireless extension version:\t" STRING(WIRELESS_EXT) "\n" | ||
4089 | + "chip name:\t\t\t%s (0x%08X)\n" | ||
4090 | + "radio type:\t\t\t0x%02X\n" | ||
4091 | + "form factor:\t\t\t0x%02X\n" | ||
4092 | + "EEPROM version:\t\t\t0x%02X\n" | ||
4093 | + "firmware version:\t\t%s (0x%08X)\n", | ||
4094 | + adev->chip_name, adev->firmware_id, | ||
4095 | + adev->radio_type, | ||
4096 | + adev->form_factor, | ||
4097 | + adev->eeprom_version, | ||
4098 | + adev->firmware_version, adev->firmware_numver); | ||
4099 | + | ||
4100 | + for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { | ||
4101 | + struct client *bss = &adev->sta_list[i]; | ||
4102 | + if (!bss->used) continue; | ||
4103 | + p += sprintf(p, "BSS %u BSSID "MACSTR" ESSID %s channel %u " | ||
4104 | + "Cap 0x%X SIR %u SNR %u\n", | ||
4105 | + i, MAC(bss->bssid), (char*)bss->essid, bss->channel, | ||
4106 | + bss->cap_info, bss->sir, bss->snr); | ||
4107 | + } | ||
4108 | + p += sprintf(p, "status:\t\t\t%u (%s)\n", | ||
4109 | + adev->status, acx_get_status_name(adev->status)); | ||
4110 | + | ||
4111 | + FN_EXIT1(p - buf); | ||
4112 | + return p - buf; | ||
4113 | +} | ||
4114 | + | ||
4115 | + | ||
4116 | +/*********************************************************************** | ||
4117 | +*/ | ||
4118 | +static int | ||
4119 | +acx_s_proc_diag_output(char *buf, acx_device_t *adev) | ||
4120 | +{ | ||
4121 | + char *p = buf; | ||
4122 | + unsigned long flags; | ||
4123 | + unsigned int len = 0, partlen; | ||
4124 | + u32 temp1, temp2; | ||
4125 | + u8 *st, *st_end; | ||
4126 | +#ifdef __BIG_ENDIAN | ||
4127 | + u8 *st2; | ||
4128 | +#endif | ||
4129 | + fw_stats_t *fw_stats; | ||
4130 | + char *part_str = NULL; | ||
4131 | + fw_stats_tx_t *tx = NULL; | ||
4132 | + fw_stats_rx_t *rx = NULL; | ||
4133 | + fw_stats_dma_t *dma = NULL; | ||
4134 | + fw_stats_irq_t *irq = NULL; | ||
4135 | + fw_stats_wep_t *wep = NULL; | ||
4136 | + fw_stats_pwr_t *pwr = NULL; | ||
4137 | + fw_stats_mic_t *mic = NULL; | ||
4138 | + fw_stats_aes_t *aes = NULL; | ||
4139 | + fw_stats_event_t *evt = NULL; | ||
4140 | + | ||
4141 | + FN_ENTER; | ||
4142 | + | ||
4143 | + acx_lock(adev, flags); | ||
4144 | + | ||
4145 | +#if defined (ACX_MEM) | ||
4146 | + p = acxmem_s_proc_diag_output(p, adev); | ||
4147 | +#else | ||
4148 | + if (IS_PCI(adev)) | ||
4149 | + p = acxpci_s_proc_diag_output(p, adev); | ||
4150 | +#endif | ||
4151 | + | ||
4152 | + p += sprintf(p, | ||
4153 | + "\n" | ||
4154 | + "** network status **\n" | ||
4155 | + "dev_state_mask 0x%04X\n" | ||
4156 | + "status %u (%s), " | ||
4157 | + "mode %u, channel %u, " | ||
4158 | + "reg_dom_id 0x%02X, reg_dom_chanmask 0x%04X, ", | ||
4159 | + adev->dev_state_mask, | ||
4160 | + adev->status, acx_get_status_name(adev->status), | ||
4161 | + adev->mode, adev->channel, | ||
4162 | + adev->reg_dom_id, adev->reg_dom_chanmask | ||
4163 | + ); | ||
4164 | + p += sprintf(p, | ||
4165 | + "ESSID \"%s\", essid_active %d, essid_len %d, " | ||
4166 | + "essid_for_assoc \"%s\", nick \"%s\"\n" | ||
4167 | + "WEP ena %d, restricted %d, idx %d\n", | ||
4168 | + adev->essid, adev->essid_active, (int)adev->essid_len, | ||
4169 | + adev->essid_for_assoc, adev->nick, | ||
4170 | + adev->wep_enabled, adev->wep_restricted, | ||
4171 | + adev->wep_current_index); | ||
4172 | + p += sprintf(p, "dev_addr "MACSTR"\n", MAC(adev->dev_addr)); | ||
4173 | + p += sprintf(p, "bssid "MACSTR"\n", MAC(adev->bssid)); | ||
4174 | + p += sprintf(p, "ap_filter "MACSTR"\n", MAC(adev->ap)); | ||
4175 | + | ||
4176 | + p += sprintf(p, | ||
4177 | + "\n" | ||
4178 | + "** PHY status **\n" | ||
4179 | + "tx_disabled %d, tx_level_dbm %d\n" /* "tx_level_val %d, tx_level_auto %d\n" */ | ||
4180 | + "sensitivity %d, antenna 0x%02X, ed_threshold %d, cca %d, preamble_mode %d\n" | ||
4181 | + "rate_basic 0x%04X, rate_oper 0x%04X\n" | ||
4182 | + "rts_threshold %d, frag_threshold %d, short_retry %d, long_retry %d\n" | ||
4183 | + "msdu_lifetime %d, listen_interval %d, beacon_interval %d\n", | ||
4184 | + adev->tx_disabled, adev->tx_level_dbm, /* adev->tx_level_val, adev->tx_level_auto, */ | ||
4185 | + adev->sensitivity, adev->antenna, adev->ed_threshold, adev->cca, adev->preamble_mode, | ||
4186 | + adev->rate_basic, adev->rate_oper, | ||
4187 | + adev->rts_threshold, adev->frag_threshold, adev->short_retry, adev->long_retry, | ||
4188 | + adev->msdu_lifetime, adev->listen_interval, adev->beacon_interval); | ||
4189 | + | ||
4190 | + acx_unlock(adev, flags); | ||
4191 | + | ||
4192 | + p += sprintf(p, | ||
4193 | + "\n" | ||
4194 | + "** Firmware **\n" | ||
4195 | + "NOTE: version dependent statistics layout, " | ||
4196 | + "please report if you suspect wrong parsing!\n" | ||
4197 | + "\n" | ||
4198 | + "version \"%s\"\n", adev->firmware_version); | ||
4199 | + | ||
4200 | + /* TODO: may replace kmalloc/memset with kzalloc once | ||
4201 | + * Linux 2.6.14 is widespread */ | ||
4202 | + fw_stats = kmalloc(sizeof(*fw_stats), GFP_KERNEL); | ||
4203 | + if (!fw_stats) { | ||
4204 | + FN_EXIT1(0); | ||
4205 | + return 0; | ||
4206 | + } | ||
4207 | + memset(fw_stats, 0, sizeof(*fw_stats)); | ||
4208 | + | ||
4209 | + st = (u8 *)fw_stats; | ||
4210 | + | ||
4211 | + part_str = "statistics query command"; | ||
4212 | + | ||
4213 | + if (OK != acx_s_interrogate(adev, st, ACX1xx_IE_FIRMWARE_STATISTICS)) | ||
4214 | + goto fw_stats_end; | ||
4215 | + | ||
4216 | + st += sizeof(u16); | ||
4217 | + len = *(u16 *)st; | ||
4218 | + | ||
4219 | + if (len > sizeof(*fw_stats)) { | ||
4220 | + p += sprintf(p, | ||
4221 | + "firmware version with bigger fw_stats struct detected\n" | ||
4222 | + "(%u vs. %u), please report\n", len, sizeof(fw_stats_t)); | ||
4223 | + if (len > sizeof(*fw_stats)) { | ||
4224 | + p += sprintf(p, "struct size exceeded allocation!\n"); | ||
4225 | + len = sizeof(*fw_stats); | ||
4226 | + } | ||
4227 | + } | ||
4228 | + st += sizeof(u16); | ||
4229 | + st_end = st - 2*sizeof(u16) + len; | ||
4230 | + | ||
4231 | +#ifdef __BIG_ENDIAN | ||
4232 | + /* let's make one bold assumption here: | ||
4233 | + * (hopefully!) *all* statistics fields are u32 only, | ||
4234 | + * thus if we need to make endianness corrections | ||
4235 | + * we can simply do them in one go, in advance */ | ||
4236 | + st2 = (u8 *)fw_stats; | ||
4237 | + for (temp1 = 0; temp1 < len; temp1 += 4, st2 += 4) | ||
4238 | + *(u32 *)st2 = le32_to_cpu(*(u32 *)st2); | ||
4239 | +#endif | ||
4240 | + | ||
4241 | + part_str = "Rx/Tx"; | ||
4242 | + | ||
4243 | + /* directly at end of a struct part? --> no error! */ | ||
4244 | + if (st == st_end) | ||
4245 | + goto fw_stats_end; | ||
4246 | + | ||
4247 | + tx = (fw_stats_tx_t *)st; | ||
4248 | + st += sizeof(fw_stats_tx_t); | ||
4249 | + rx = (fw_stats_rx_t *)st; | ||
4250 | + st += sizeof(fw_stats_rx_t); | ||
4251 | + partlen = sizeof(fw_stats_tx_t) + sizeof(fw_stats_rx_t); | ||
4252 | + | ||
4253 | + if (IS_ACX100(adev)) { | ||
4254 | + /* at least ACX100 PCI F/W 1.9.8.b | ||
4255 | + * and ACX100 USB F/W 1.0.7-USB | ||
4256 | + * don't have those two fields... */ | ||
4257 | + st -= 2*sizeof(u32); | ||
4258 | + | ||
4259 | + /* our parsing doesn't quite match this firmware yet, | ||
4260 | + * log failure */ | ||
4261 | + if (st > st_end) | ||
4262 | + goto fw_stats_fail; | ||
4263 | + temp1 = temp2 = 999999999; | ||
4264 | + } else { | ||
4265 | + if (st > st_end) | ||
4266 | + goto fw_stats_fail; | ||
4267 | + temp1 = rx->rx_aci_events; | ||
4268 | + temp2 = rx->rx_aci_resets; | ||
4269 | + } | ||
4270 | + | ||
4271 | + p += sprintf(p, | ||
4272 | + "%s:\n" | ||
4273 | + " tx_desc_overfl %u\n" | ||
4274 | + " rx_OutOfMem %u, rx_hdr_overfl %u, rx_hw_stuck %u\n" | ||
4275 | + " rx_dropped_frame %u, rx_frame_ptr_err %u, rx_xfr_hint_trig %u\n" | ||
4276 | + " rx_aci_events %u, rx_aci_resets %u\n", | ||
4277 | + part_str, | ||
4278 | + tx->tx_desc_of, | ||
4279 | + rx->rx_oom, | ||
4280 | + rx->rx_hdr_of, | ||
4281 | + rx->rx_hw_stuck, | ||
4282 | + rx->rx_dropped_frame, | ||
4283 | + rx->rx_frame_ptr_err, | ||
4284 | + rx->rx_xfr_hint_trig, | ||
4285 | + temp1, | ||
4286 | + temp2); | ||
4287 | + | ||
4288 | + part_str = "DMA"; | ||
4289 | + | ||
4290 | + if (st == st_end) | ||
4291 | + goto fw_stats_end; | ||
4292 | + | ||
4293 | + dma = (fw_stats_dma_t *)st; | ||
4294 | + partlen = sizeof(fw_stats_dma_t); | ||
4295 | + st += partlen; | ||
4296 | + | ||
4297 | + if (st > st_end) | ||
4298 | + goto fw_stats_fail; | ||
4299 | + | ||
4300 | + p += sprintf(p, | ||
4301 | + "%s:\n" | ||
4302 | + " rx_dma_req %u, rx_dma_err %u, tx_dma_req %u, tx_dma_err %u\n", | ||
4303 | + part_str, | ||
4304 | + dma->rx_dma_req, | ||
4305 | + dma->rx_dma_err, | ||
4306 | + dma->tx_dma_req, | ||
4307 | + dma->tx_dma_err); | ||
4308 | + | ||
4309 | + part_str = "IRQ"; | ||
4310 | + | ||
4311 | + if (st == st_end) | ||
4312 | + goto fw_stats_end; | ||
4313 | + | ||
4314 | + irq = (fw_stats_irq_t *)st; | ||
4315 | + partlen = sizeof(fw_stats_irq_t); | ||
4316 | + st += partlen; | ||
4317 | + | ||
4318 | + if (st > st_end) | ||
4319 | + goto fw_stats_fail; | ||
4320 | + | ||
4321 | + p += sprintf(p, | ||
4322 | + "%s:\n" | ||
4323 | + " cmd_cplt %u, fiq %u\n" | ||
4324 | + " rx_hdrs %u, rx_cmplt %u, rx_mem_overfl %u, rx_rdys %u\n" | ||
4325 | + " irqs %u, tx_procs %u, decrypt_done %u\n" | ||
4326 | + " dma_0_done %u, dma_1_done %u, tx_exch_complet %u\n" | ||
4327 | + " commands %u, rx_procs %u, hw_pm_mode_changes %u\n" | ||
4328 | + " host_acks %u, pci_pm %u, acm_wakeups %u\n", | ||
4329 | + part_str, | ||
4330 | + irq->cmd_cplt, | ||
4331 | + irq->fiq, | ||
4332 | + irq->rx_hdrs, | ||
4333 | + irq->rx_cmplt, | ||
4334 | + irq->rx_mem_of, | ||
4335 | + irq->rx_rdys, | ||
4336 | + irq->irqs, | ||
4337 | + irq->tx_procs, | ||
4338 | + irq->decrypt_done, | ||
4339 | + irq->dma_0_done, | ||
4340 | + irq->dma_1_done, | ||
4341 | + irq->tx_exch_complet, | ||
4342 | + irq->commands, | ||
4343 | + irq->rx_procs, | ||
4344 | + irq->hw_pm_mode_changes, | ||
4345 | + irq->host_acks, | ||
4346 | + irq->pci_pm, | ||
4347 | + irq->acm_wakeups); | ||
4348 | + | ||
4349 | + part_str = "WEP"; | ||
4350 | + | ||
4351 | + if (st == st_end) | ||
4352 | + goto fw_stats_end; | ||
4353 | + | ||
4354 | + wep = (fw_stats_wep_t *)st; | ||
4355 | + partlen = sizeof(fw_stats_wep_t); | ||
4356 | + st += partlen; | ||
4357 | + | ||
4358 | + if ( | ||
4359 | + (IS_PCI(adev) && IS_ACX100(adev)) | ||
4360 | + || (IS_USB(adev) && IS_ACX100(adev)) | ||
4361 | + || (IS_MEM(adev) && IS_ACX100(adev)) | ||
4362 | + ) { | ||
4363 | + /* at least ACX100 PCI F/W 1.9.8.b, | ||
4364 | + * ACX100 USB F/W 1.0.7-USB | ||
4365 | + * and ACX100 Generic Slave F/W 1.10.7.K | ||
4366 | + * don't have those two fields... | ||
4367 | + */ | ||
4368 | + st -= 2*sizeof(u32); | ||
4369 | + if (st > st_end) | ||
4370 | + goto fw_stats_fail; | ||
4371 | + temp1 = temp2 = 999999999; | ||
4372 | + } else { | ||
4373 | + if (st > st_end) | ||
4374 | + goto fw_stats_fail; | ||
4375 | + temp1 = wep->wep_pkt_decrypt; | ||
4376 | + temp2 = wep->wep_decrypt_irqs; | ||
4377 | + } | ||
4378 | + | ||
4379 | + p += sprintf(p, | ||
4380 | + "%s:\n" | ||
4381 | + " wep_key_count %u, wep_default_key_count %u, dot11_def_key_mib %u\n" | ||
4382 | + " wep_key_not_found %u, wep_decrypt_fail %u\n" | ||
4383 | + " wep_pkt_decrypt %u, wep_decrypt_irqs %u\n", | ||
4384 | + part_str, | ||
4385 | + wep->wep_key_count, | ||
4386 | + wep->wep_default_key_count, | ||
4387 | + wep->dot11_def_key_mib, | ||
4388 | + wep->wep_key_not_found, | ||
4389 | + wep->wep_decrypt_fail, | ||
4390 | + temp1, | ||
4391 | + temp2); | ||
4392 | + | ||
4393 | + part_str = "power"; | ||
4394 | + | ||
4395 | + if (st == st_end) | ||
4396 | + goto fw_stats_end; | ||
4397 | + | ||
4398 | + pwr = (fw_stats_pwr_t *)st; | ||
4399 | + partlen = sizeof(fw_stats_pwr_t); | ||
4400 | + st += partlen; | ||
4401 | + | ||
4402 | + if (st > st_end) | ||
4403 | + goto fw_stats_fail; | ||
4404 | + | ||
4405 | + p += sprintf(p, | ||
4406 | + "%s:\n" | ||
4407 | + " tx_start_ctr %u, no_ps_tx_too_short %u\n" | ||
4408 | + " rx_start_ctr %u, no_ps_rx_too_short %u\n" | ||
4409 | + " lppd_started %u\n" | ||
4410 | + " no_lppd_too_noisy %u, no_lppd_too_short %u, no_lppd_matching_frame %u\n", | ||
4411 | + part_str, | ||
4412 | + pwr->tx_start_ctr, | ||
4413 | + pwr->no_ps_tx_too_short, | ||
4414 | + pwr->rx_start_ctr, | ||
4415 | + pwr->no_ps_rx_too_short, | ||
4416 | + pwr->lppd_started, | ||
4417 | + pwr->no_lppd_too_noisy, | ||
4418 | + pwr->no_lppd_too_short, | ||
4419 | + pwr->no_lppd_matching_frame); | ||
4420 | + | ||
4421 | + part_str = "MIC"; | ||
4422 | + | ||
4423 | + if (st == st_end) | ||
4424 | + goto fw_stats_end; | ||
4425 | + | ||
4426 | + mic = (fw_stats_mic_t *)st; | ||
4427 | + partlen = sizeof(fw_stats_mic_t); | ||
4428 | + st += partlen; | ||
4429 | + | ||
4430 | + if (st > st_end) | ||
4431 | + goto fw_stats_fail; | ||
4432 | + | ||
4433 | + p += sprintf(p, | ||
4434 | + "%s:\n" | ||
4435 | + " mic_rx_pkts %u, mic_calc_fail %u\n", | ||
4436 | + part_str, | ||
4437 | + mic->mic_rx_pkts, | ||
4438 | + mic->mic_calc_fail); | ||
4439 | + | ||
4440 | + part_str = "AES"; | ||
4441 | + | ||
4442 | + if (st == st_end) | ||
4443 | + goto fw_stats_end; | ||
4444 | + | ||
4445 | + aes = (fw_stats_aes_t *)st; | ||
4446 | + partlen = sizeof(fw_stats_aes_t); | ||
4447 | + st += partlen; | ||
4448 | + | ||
4449 | + if (st > st_end) | ||
4450 | + goto fw_stats_fail; | ||
4451 | + | ||
4452 | + p += sprintf(p, | ||
4453 | + "%s:\n" | ||
4454 | + " aes_enc_fail %u, aes_dec_fail %u\n" | ||
4455 | + " aes_enc_pkts %u, aes_dec_pkts %u\n" | ||
4456 | + " aes_enc_irq %u, aes_dec_irq %u\n", | ||
4457 | + part_str, | ||
4458 | + aes->aes_enc_fail, | ||
4459 | + aes->aes_dec_fail, | ||
4460 | + aes->aes_enc_pkts, | ||
4461 | + aes->aes_dec_pkts, | ||
4462 | + aes->aes_enc_irq, | ||
4463 | + aes->aes_dec_irq); | ||
4464 | + | ||
4465 | + part_str = "event"; | ||
4466 | + | ||
4467 | + if (st == st_end) | ||
4468 | + goto fw_stats_end; | ||
4469 | + | ||
4470 | + evt = (fw_stats_event_t *)st; | ||
4471 | + partlen = sizeof(fw_stats_event_t); | ||
4472 | + st += partlen; | ||
4473 | + | ||
4474 | + if (st > st_end) | ||
4475 | + goto fw_stats_fail; | ||
4476 | + | ||
4477 | + p += sprintf(p, | ||
4478 | + "%s:\n" | ||
4479 | + " heartbeat %u, calibration %u\n" | ||
4480 | + " rx_mismatch %u, rx_mem_empty %u, rx_pool %u\n" | ||
4481 | + " oom_late %u\n" | ||
4482 | + " phy_tx_err %u, tx_stuck %u\n", | ||
4483 | + part_str, | ||
4484 | + evt->heartbeat, | ||
4485 | + evt->calibration, | ||
4486 | + evt->rx_mismatch, | ||
4487 | + evt->rx_mem_empty, | ||
4488 | + evt->rx_pool, | ||
4489 | + evt->oom_late, | ||
4490 | + evt->phy_tx_err, | ||
4491 | + evt->tx_stuck); | ||
4492 | + | ||
4493 | + if (st < st_end) | ||
4494 | + goto fw_stats_bigger; | ||
4495 | + | ||
4496 | + goto fw_stats_end; | ||
4497 | + | ||
4498 | +fw_stats_fail: | ||
4499 | + st -= partlen; | ||
4500 | + p += sprintf(p, | ||
4501 | + "failed at %s part (size %u), offset %u (struct size %u), " | ||
4502 | + "please report\n", part_str, partlen, | ||
4503 | + (int)st - (int)fw_stats, len); | ||
4504 | + | ||
4505 | +fw_stats_bigger: | ||
4506 | + for (; st < st_end; st += 4) | ||
4507 | + p += sprintf(p, | ||
4508 | + "UNKN%3d: %u\n", (int)st - (int)fw_stats, *(u32 *)st); | ||
4509 | + | ||
4510 | +fw_stats_end: | ||
4511 | + kfree(fw_stats); | ||
4512 | + | ||
4513 | + FN_EXIT1(p - buf); | ||
4514 | + return p - buf; | ||
4515 | +} | ||
4516 | + | ||
4517 | + | ||
4518 | +/*********************************************************************** | ||
4519 | +*/ | ||
4520 | +static int | ||
4521 | +acx_s_proc_phy_output(char *buf, acx_device_t *adev) | ||
4522 | +{ | ||
4523 | + char *p = buf; | ||
4524 | + int i; | ||
4525 | + | ||
4526 | + FN_ENTER; | ||
4527 | + | ||
4528 | + /* | ||
4529 | + if (RADIO_RFMD_11 != adev->radio_type) { | ||
4530 | + printk("sorry, not yet adapted for radio types " | ||
4531 | + "other than RFMD, please verify " | ||
4532 | + "PHY size etc. first!\n"); | ||
4533 | + goto end; | ||
4534 | + } | ||
4535 | + */ | ||
4536 | + | ||
4537 | + /* The PHY area is only 0x80 bytes long; further pages after that | ||
4538 | + * only have some page number registers with altered value, | ||
4539 | + * all other registers remain the same. */ | ||
4540 | + for (i = 0; i < 0x80; i++) { | ||
4541 | + acx_s_read_phy_reg(adev, i, p++); | ||
4542 | + } | ||
4543 | + | ||
4544 | + FN_EXIT1(p - buf); | ||
4545 | + return p - buf; | ||
4546 | +} | ||
4547 | + | ||
4548 | + | ||
4549 | +/*********************************************************************** | ||
4550 | +** acx_e_read_proc_XXXX | ||
4551 | +** Handle our /proc entry | ||
4552 | +** | ||
4553 | +** Arguments: | ||
4554 | +** standard kernel read_proc interface | ||
4555 | +** Returns: | ||
4556 | +** number of bytes written to buf | ||
4557 | +** Side effects: | ||
4558 | +** none | ||
4559 | +*/ | ||
4560 | +static int | ||
4561 | +acx_e_read_proc(char *buf, char **start, off_t offset, int count, | ||
4562 | + int *eof, void *data) | ||
4563 | +{ | ||
4564 | + acx_device_t *adev = (acx_device_t*)data; | ||
4565 | + unsigned long flags; | ||
4566 | + int length; | ||
4567 | + | ||
4568 | + FN_ENTER; | ||
4569 | + | ||
4570 | + acx_sem_lock(adev); | ||
4571 | + acx_lock(adev, flags); | ||
4572 | + /* fill buf */ | ||
4573 | + length = acx_l_proc_output(buf, adev); | ||
4574 | + acx_unlock(adev, flags); | ||
4575 | + acx_sem_unlock(adev); | ||
4576 | + | ||
4577 | + /* housekeeping */ | ||
4578 | + if (length <= offset + count) | ||
4579 | + *eof = 1; | ||
4580 | + *start = buf + offset; | ||
4581 | + length -= offset; | ||
4582 | + if (length > count) | ||
4583 | + length = count; | ||
4584 | + if (length < 0) | ||
4585 | + length = 0; | ||
4586 | + FN_EXIT1(length); | ||
4587 | + return length; | ||
4588 | +} | ||
4589 | + | ||
4590 | +static char _buf[32768]; | ||
4591 | +static int | ||
4592 | +acx_e_read_proc_diag(char *buf, char **start, off_t offset, int count, | ||
4593 | + int *eof, void *data) | ||
4594 | +{ | ||
4595 | + acx_device_t *adev = (acx_device_t*)data; | ||
4596 | + int length; | ||
4597 | + | ||
4598 | + FN_ENTER; | ||
4599 | + | ||
4600 | + acx_sem_lock(adev); | ||
4601 | + /* fill buf */ | ||
4602 | + length = acx_s_proc_diag_output(_buf, adev); | ||
4603 | + acx_sem_unlock(adev); | ||
4604 | + | ||
4605 | + memcpy(buf, _buf + offset, count); | ||
4606 | + | ||
4607 | + /* housekeeping */ | ||
4608 | + if (length <= offset + count) | ||
4609 | + *eof = 1; | ||
4610 | + *start = count; | ||
4611 | + length -= offset; | ||
4612 | + if (length > count) | ||
4613 | + length = count; | ||
4614 | + if (length < 0) | ||
4615 | + length = 0; | ||
4616 | + FN_EXIT1(length); | ||
4617 | + return length; | ||
4618 | +} | ||
4619 | + | ||
4620 | +static int | ||
4621 | +acx_e_read_proc_eeprom(char *buf, char **start, off_t offset, int count, | ||
4622 | + int *eof, void *data) | ||
4623 | +{ | ||
4624 | + acx_device_t *adev = (acx_device_t*)data; | ||
4625 | + int length; | ||
4626 | + | ||
4627 | + FN_ENTER; | ||
4628 | + | ||
4629 | + /* fill buf */ | ||
4630 | + length = 0; | ||
4631 | +#if defined (ACX_MEM) | ||
4632 | + acx_sem_lock(adev); | ||
4633 | + length = acxmem_proc_eeprom_output(buf, adev); | ||
4634 | + acx_sem_unlock(adev); | ||
4635 | +#else | ||
4636 | + if (IS_PCI(adev)) { | ||
4637 | + acx_sem_lock(adev); | ||
4638 | + length = acxpci_proc_eeprom_output(buf, adev); | ||
4639 | + acx_sem_unlock(adev); | ||
4640 | + } | ||
4641 | +#endif | ||
4642 | + | ||
4643 | + /* housekeeping */ | ||
4644 | + if (length <= offset + count) | ||
4645 | + *eof = 1; | ||
4646 | + *start = buf + offset; | ||
4647 | + length -= offset; | ||
4648 | + if (length > count) | ||
4649 | + length = count; | ||
4650 | + if (length < 0) | ||
4651 | + length = 0; | ||
4652 | + FN_EXIT1(length); | ||
4653 | + return length; | ||
4654 | +} | ||
4655 | + | ||
4656 | +static int | ||
4657 | +acx_e_read_proc_phy(char *buf, char **start, off_t offset, int count, | ||
4658 | + int *eof, void *data) | ||
4659 | +{ | ||
4660 | + acx_device_t *adev = (acx_device_t*)data; | ||
4661 | + int length; | ||
4662 | + | ||
4663 | + FN_ENTER; | ||
4664 | + | ||
4665 | + acx_sem_lock(adev); | ||
4666 | + /* fill buf */ | ||
4667 | + length = acx_s_proc_phy_output(buf, adev); | ||
4668 | + acx_sem_unlock(adev); | ||
4669 | + | ||
4670 | + /* housekeeping */ | ||
4671 | + if (length <= offset + count) | ||
4672 | + *eof = 1; | ||
4673 | + *start = buf + offset; | ||
4674 | + length -= offset; | ||
4675 | + if (length > count) | ||
4676 | + length = count; | ||
4677 | + if (length < 0) | ||
4678 | + length = 0; | ||
4679 | + FN_EXIT1(length); | ||
4680 | + return length; | ||
4681 | +} | ||
4682 | + | ||
4683 | + | ||
4684 | +/*********************************************************************** | ||
4685 | +** /proc files registration | ||
4686 | +*/ | ||
4687 | +static const char * const | ||
4688 | +proc_files[] = { "", "_diag", "_eeprom", "_phy" }; | ||
4689 | + | ||
4690 | +static read_proc_t * const | ||
4691 | +proc_funcs[] = { | ||
4692 | + acx_e_read_proc, | ||
4693 | + acx_e_read_proc_diag, | ||
4694 | + acx_e_read_proc_eeprom, | ||
4695 | + acx_e_read_proc_phy | ||
4696 | +}; | ||
4697 | + | ||
4698 | +static int | ||
4699 | +manage_proc_entries(const struct net_device *ndev, int remove) | ||
4700 | +{ | ||
4701 | + acx_device_t *adev = ndev2adev((struct net_device *)ndev); | ||
4702 | + char procbuf[80]; | ||
4703 | + int i; | ||
4704 | + | ||
4705 | + for (i = 0; i < VEC_SIZE(proc_files); i++) { | ||
4706 | + snprintf(procbuf, sizeof(procbuf), | ||
4707 | + "driver/acx_%s%s", ndev->name, proc_files[i]); | ||
4708 | + log(L_INIT, "%sing /proc entry %s\n", | ||
4709 | + remove ? "remov" : "creat", procbuf); | ||
4710 | + if (!remove) { | ||
4711 | + if (!create_proc_read_entry(procbuf, 0, 0, proc_funcs[i], adev)) { | ||
4712 | + printk("acx: cannot register /proc entry %s\n", procbuf); | ||
4713 | + return NOT_OK; | ||
4714 | + } | ||
4715 | + } else { | ||
4716 | + remove_proc_entry(procbuf, NULL); | ||
4717 | + } | ||
4718 | + } | ||
4719 | + return OK; | ||
4720 | +} | ||
4721 | + | ||
4722 | +int | ||
4723 | +acx_proc_register_entries(const struct net_device *ndev) | ||
4724 | +{ | ||
4725 | + return manage_proc_entries(ndev, 0); | ||
4726 | +} | ||
4727 | + | ||
4728 | +int | ||
4729 | +acx_proc_unregister_entries(const struct net_device *ndev) | ||
4730 | +{ | ||
4731 | + return manage_proc_entries(ndev, 1); | ||
4732 | +} | ||
4733 | +#endif /* CONFIG_PROC_FS */ | ||
4734 | + | ||
4735 | + | ||
4736 | +/*********************************************************************** | ||
4737 | +** acx_cmd_join_bssid | ||
4738 | +** | ||
4739 | +** Common code for both acx100 and acx111. | ||
4740 | +*/ | ||
4741 | +/* NB: does NOT match RATE100_nn but matches ACX[111]_SCAN_RATE_n */ | ||
4742 | +static const u8 | ||
4743 | +bitpos2genframe_txrate[] = { | ||
4744 | + 10, /* 0. 1 Mbit/s */ | ||
4745 | + 20, /* 1. 2 Mbit/s */ | ||
4746 | + 55, /* 2. 5.5 Mbit/s */ | ||
4747 | + 0x0B, /* 3. 6 Mbit/s */ | ||
4748 | + 0x0F, /* 4. 9 Mbit/s */ | ||
4749 | + 110, /* 5. 11 Mbit/s */ | ||
4750 | + 0x0A, /* 6. 12 Mbit/s */ | ||
4751 | + 0x0E, /* 7. 18 Mbit/s */ | ||
4752 | + 220, /* 8. 22 Mbit/s */ | ||
4753 | + 0x09, /* 9. 24 Mbit/s */ | ||
4754 | + 0x0D, /* 10. 36 Mbit/s */ | ||
4755 | + 0x08, /* 11. 48 Mbit/s */ | ||
4756 | + 0x0C, /* 12. 54 Mbit/s */ | ||
4757 | + 10, /* 13. 1 Mbit/s, should never happen */ | ||
4758 | + 10, /* 14. 1 Mbit/s, should never happen */ | ||
4759 | + 10, /* 15. 1 Mbit/s, should never happen */ | ||
4760 | +}; | ||
4761 | + | ||
4762 | +/* Looks scary, eh? | ||
4763 | +** Actually, each one compiled into one AND and one SHIFT, | ||
4764 | +** 31 bytes in x86 asm (more if uints are replaced by u16/u8) */ | ||
4765 | +static inline unsigned int | ||
4766 | +rate111to5bits(unsigned int rate) | ||
4767 | +{ | ||
4768 | + return (rate & 0x7) | ||
4769 | + | ( (rate & RATE111_11) / (RATE111_11/JOINBSS_RATES_11) ) | ||
4770 | + | ( (rate & RATE111_22) / (RATE111_22/JOINBSS_RATES_22) ) | ||
4771 | + ; | ||
4772 | +} | ||
4773 | + | ||
4774 | +static void | ||
4775 | +acx_s_cmd_join_bssid(acx_device_t *adev, const u8 *bssid) | ||
4776 | +{ | ||
4777 | + acx_joinbss_t tmp; | ||
4778 | + int dtim_interval; | ||
4779 | + int i; | ||
4780 | + | ||
4781 | + if (mac_is_zero(bssid)) | ||
4782 | + return; | ||
4783 | + | ||
4784 | + FN_ENTER; | ||
4785 | + | ||
4786 | + dtim_interval = (ACX_MODE_0_ADHOC == adev->mode) ? | ||
4787 | + 1 : adev->dtim_interval; | ||
4788 | + | ||
4789 | + memset(&tmp, 0, sizeof(tmp)); | ||
4790 | + | ||
4791 | + for (i = 0; i < ETH_ALEN; i++) { | ||
4792 | + tmp.bssid[i] = bssid[ETH_ALEN-1 - i]; | ||
4793 | + } | ||
4794 | + | ||
4795 | + tmp.beacon_interval = cpu_to_le16(adev->beacon_interval); | ||
4796 | + | ||
4797 | + /* Basic rate set. Control frame responses (such as ACK or CTS frames) | ||
4798 | + ** are sent with one of these rates */ | ||
4799 | + if (IS_ACX111(adev)) { | ||
4800 | + /* It was experimentally determined that rates_basic | ||
4801 | + ** can take 11g rates as well, not only rates | ||
4802 | + ** defined with JOINBSS_RATES_BASIC111_nnn. | ||
4803 | + ** Just use RATE111_nnn constants... */ | ||
4804 | + tmp.u.acx111.dtim_interval = dtim_interval; | ||
4805 | + tmp.u.acx111.rates_basic = cpu_to_le16(adev->rate_basic); | ||
4806 | + log(L_ASSOC, "rates_basic:%04X, rates_supported:%04X\n", | ||
4807 | + adev->rate_basic, adev->rate_oper); | ||
4808 | + } else { | ||
4809 | + tmp.u.acx100.dtim_interval = dtim_interval; | ||
4810 | + tmp.u.acx100.rates_basic = rate111to5bits(adev->rate_basic); | ||
4811 | + tmp.u.acx100.rates_supported = rate111to5bits(adev->rate_oper); | ||
4812 | + log(L_ASSOC, "rates_basic:%04X->%02X, " | ||
4813 | + "rates_supported:%04X->%02X\n", | ||
4814 | + adev->rate_basic, tmp.u.acx100.rates_basic, | ||
4815 | + adev->rate_oper, tmp.u.acx100.rates_supported); | ||
4816 | + } | ||
4817 | + | ||
4818 | + /* Setting up how Beacon, Probe Response, RTS, and PS-Poll frames | ||
4819 | + ** will be sent (rate/modulation/preamble) */ | ||
4820 | + tmp.u.txrate.genfrm_txrate = bitpos2genframe_txrate[lowest_bit(adev->rate_basic)]; | ||
4821 | + tmp.genfrm_mod_pre = 0; /* FIXME: was = adev->capab_short (which was always 0); */ | ||
4822 | + /* we can use short pre *if* all peers can understand it */ | ||
4823 | + /* FIXME #2: we need to correctly set PBCC/OFDM bits here too */ | ||
4824 | + | ||
4825 | + /* we switch fw to STA mode in MONITOR mode, it seems to be | ||
4826 | + ** the only mode where fw does not emit beacons by itself | ||
4827 | + ** but allows us to send anything (we really want to retain | ||
4828 | + ** ability to tx arbitrary frames in MONITOR mode) | ||
4829 | + */ | ||
4830 | + tmp.macmode = (adev->mode != ACX_MODE_MONITOR ? adev->mode : ACX_MODE_2_STA); | ||
4831 | + tmp.channel = adev->channel; | ||
4832 | + tmp.essid_len = adev->essid_len; | ||
4833 | + /* NOTE: the code memcpy'd essid_len + 1 before, which is WRONG! */ | ||
4834 | + memcpy(tmp.essid, adev->essid, tmp.essid_len); | ||
4835 | + acx_s_issue_cmd(adev, ACX1xx_CMD_JOIN, &tmp, tmp.essid_len + 0x11); | ||
4836 | + | ||
4837 | + log(L_ASSOC|L_DEBUG, "BSS_Type = %u\n", tmp.macmode); | ||
4838 | + acxlog_mac(L_ASSOC|L_DEBUG, "JoinBSSID MAC:", adev->bssid, "\n"); | ||
4839 | + | ||
4840 | + acx_update_capabilities(adev); | ||
4841 | + FN_EXIT0; | ||
4842 | +} | ||
4843 | + | ||
4844 | + | ||
4845 | +/*********************************************************************** | ||
4846 | +** acx_s_cmd_start_scan | ||
4847 | +** | ||
4848 | +** Issue scan command to the hardware | ||
4849 | +** | ||
4850 | +** unified function for both ACX111 and ACX100 | ||
4851 | +*/ | ||
4852 | +static void | ||
4853 | +acx_s_scan_chan(acx_device_t *adev) | ||
4854 | +{ | ||
4855 | + union { | ||
4856 | + acx111_scan_t acx111; | ||
4857 | + acx100_scan_t acx100; | ||
4858 | + } s; | ||
4859 | + | ||
4860 | + FN_ENTER; | ||
4861 | + | ||
4862 | + memset(&s, 0, sizeof(s)); | ||
4863 | + | ||
4864 | + /* first common positions... */ | ||
4865 | + | ||
4866 | + s.acx111.count = cpu_to_le16(adev->scan_count); | ||
4867 | + s.acx111.rate = adev->scan_rate; | ||
4868 | + s.acx111.options = adev->scan_mode; | ||
4869 | + s.acx111.chan_duration = cpu_to_le16(adev->scan_duration); | ||
4870 | + s.acx111.max_probe_delay = cpu_to_le16(adev->scan_probe_delay); | ||
4871 | + | ||
4872 | + /* ...then differences */ | ||
4873 | + | ||
4874 | + if (IS_ACX111(adev)) { | ||
4875 | + s.acx111.channel_list_select = 0; /* scan every allowed channel */ | ||
4876 | + /*s.acx111.channel_list_select = 1;*/ /* scan given channels */ | ||
4877 | + /*s.acx111.modulation = 0x40;*/ /* long preamble? OFDM? -> only for active scan */ | ||
4878 | + s.acx111.modulation = 0; | ||
4879 | + /*s.acx111.channel_list[0] = 6; | ||
4880 | + s.acx111.channel_list[1] = 4;*/ | ||
4881 | + } else { | ||
4882 | + s.acx100.start_chan = cpu_to_le16(1); | ||
4883 | + s.acx100.flags = cpu_to_le16(0x8000); | ||
4884 | + } | ||
4885 | + | ||
4886 | + acx_s_issue_cmd(adev, ACX1xx_CMD_SCAN, &s, sizeof(s)); | ||
4887 | + FN_EXIT0; | ||
4888 | +} | ||
4889 | + | ||
4890 | + | ||
4891 | +void | ||
4892 | +acx_s_cmd_start_scan(acx_device_t *adev) | ||
4893 | +{ | ||
4894 | + /* time_before check is 'just in case' thing */ | ||
4895 | + if (!(adev->irq_status & HOST_INT_SCAN_COMPLETE) | ||
4896 | + && time_before(jiffies, adev->scan_start + 10*HZ) | ||
4897 | + ) { | ||
4898 | + log(L_INIT, "start_scan: seems like previous scan " | ||
4899 | + "is still running. Not starting anew. Please report\n"); | ||
4900 | + return; | ||
4901 | + } | ||
4902 | + | ||
4903 | + log(L_INIT, "starting radio scan\n"); | ||
4904 | + /* remember that fw is commanded to do scan */ | ||
4905 | + adev->scan_start = jiffies; | ||
4906 | + CLEAR_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE); | ||
4907 | + /* issue it */ | ||
4908 | + acx_s_scan_chan(adev); | ||
4909 | +} | ||
4910 | + | ||
4911 | + | ||
4912 | +/*********************************************************************** | ||
4913 | +** acx111 feature config | ||
4914 | +*/ | ||
4915 | +static int | ||
4916 | +acx111_s_get_feature_config(acx_device_t *adev, | ||
4917 | + u32 *feature_options, u32 *data_flow_options) | ||
4918 | +{ | ||
4919 | + struct acx111_ie_feature_config feat; | ||
4920 | + | ||
4921 | + if (!IS_ACX111(adev)) { | ||
4922 | + return NOT_OK; | ||
4923 | + } | ||
4924 | + | ||
4925 | + memset(&feat, 0, sizeof(feat)); | ||
4926 | + | ||
4927 | + if (OK != acx_s_interrogate(adev, &feat, ACX1xx_IE_FEATURE_CONFIG)) { | ||
4928 | + return NOT_OK; | ||
4929 | + } | ||
4930 | + log(L_DEBUG, | ||
4931 | + "got Feature option:0x%X, DataFlow option: 0x%X\n", | ||
4932 | + feat.feature_options, | ||
4933 | + feat.data_flow_options); | ||
4934 | + | ||
4935 | + if (feature_options) | ||
4936 | + *feature_options = le32_to_cpu(feat.feature_options); | ||
4937 | + if (data_flow_options) | ||
4938 | + *data_flow_options = le32_to_cpu(feat.data_flow_options); | ||
4939 | + | ||
4940 | + return OK; | ||
4941 | +} | ||
4942 | + | ||
4943 | +static int | ||
4944 | +acx111_s_set_feature_config(acx_device_t *adev, | ||
4945 | + u32 feature_options, u32 data_flow_options, | ||
4946 | + unsigned int mode /* 0 == remove, 1 == add, 2 == set */) | ||
4947 | +{ | ||
4948 | + struct acx111_ie_feature_config feat; | ||
4949 | + | ||
4950 | + if (!IS_ACX111(adev)) { | ||
4951 | + return NOT_OK; | ||
4952 | + } | ||
4953 | + | ||
4954 | + if ((mode < 0) || (mode > 2)) | ||
4955 | + return NOT_OK; | ||
4956 | + | ||
4957 | + if (mode != 2) | ||
4958 | + /* need to modify old data */ | ||
4959 | + acx111_s_get_feature_config(adev, &feat.feature_options, &feat.data_flow_options); | ||
4960 | + else { | ||
4961 | + /* need to set a completely new value */ | ||
4962 | + feat.feature_options = 0; | ||
4963 | + feat.data_flow_options = 0; | ||
4964 | + } | ||
4965 | + | ||
4966 | + if (mode == 0) { /* remove */ | ||
4967 | + CLEAR_BIT(feat.feature_options, cpu_to_le32(feature_options)); | ||
4968 | + CLEAR_BIT(feat.data_flow_options, cpu_to_le32(data_flow_options)); | ||
4969 | + } else { /* add or set */ | ||
4970 | + SET_BIT(feat.feature_options, cpu_to_le32(feature_options)); | ||
4971 | + SET_BIT(feat.data_flow_options, cpu_to_le32(data_flow_options)); | ||
4972 | + } | ||
4973 | + | ||
4974 | + log(L_DEBUG, | ||
4975 | + "old: feature 0x%08X dataflow 0x%08X. mode: %u\n" | ||
4976 | + "new: feature 0x%08X dataflow 0x%08X\n", | ||
4977 | + feature_options, data_flow_options, mode, | ||
4978 | + le32_to_cpu(feat.feature_options), | ||
4979 | + le32_to_cpu(feat.data_flow_options)); | ||
4980 | + | ||
4981 | + if (OK != acx_s_configure(adev, &feat, ACX1xx_IE_FEATURE_CONFIG)) { | ||
4982 | + return NOT_OK; | ||
4983 | + } | ||
4984 | + | ||
4985 | + return OK; | ||
4986 | +} | ||
4987 | + | ||
4988 | +static inline int | ||
4989 | +acx111_s_feature_off(acx_device_t *adev, u32 f, u32 d) | ||
4990 | +{ | ||
4991 | + return acx111_s_set_feature_config(adev, f, d, 0); | ||
4992 | +} | ||
4993 | +static inline int | ||
4994 | +acx111_s_feature_on(acx_device_t *adev, u32 f, u32 d) | ||
4995 | +{ | ||
4996 | + return acx111_s_set_feature_config(adev, f, d, 1); | ||
4997 | +} | ||
4998 | +static inline int | ||
4999 | +acx111_s_feature_set(acx_device_t *adev, u32 f, u32 d) | ||
5000 | +{ | ||
5001 | + return acx111_s_set_feature_config(adev, f, d, 2); | ||
5002 | +} | ||
5003 | + | ||
5004 | + | ||
5005 | +/*********************************************************************** | ||
5006 | +** acx100_s_init_memory_pools | ||
5007 | +*/ | ||
5008 | +static int | ||
5009 | +acx100_s_init_memory_pools(acx_device_t *adev, const acx_ie_memmap_t *mmt) | ||
5010 | +{ | ||
5011 | + acx100_ie_memblocksize_t MemoryBlockSize; | ||
5012 | + acx100_ie_memconfigoption_t MemoryConfigOption; | ||
5013 | + int TotalMemoryBlocks; | ||
5014 | + int RxBlockNum; | ||
5015 | + int TotalRxBlockSize; | ||
5016 | + int TxBlockNum; | ||
5017 | + int TotalTxBlockSize; | ||
5018 | + | ||
5019 | + FN_ENTER; | ||
5020 | + | ||
5021 | + /* Let's see if we can follow this: | ||
5022 | + first we select our memory block size (which I think is | ||
5023 | + completely arbitrary) */ | ||
5024 | + MemoryBlockSize.size = cpu_to_le16(adev->memblocksize); | ||
5025 | + | ||
5026 | + /* Then we alert the card to our decision of block size */ | ||
5027 | + if (OK != acx_s_configure(adev, &MemoryBlockSize, ACX100_IE_BLOCK_SIZE)) { | ||
5028 | + goto bad; | ||
5029 | + } | ||
5030 | + | ||
5031 | + /* We figure out how many total blocks we can create, using | ||
5032 | + the block size we chose, and the beginning and ending | ||
5033 | + memory pointers, i.e.: end-start/size */ | ||
5034 | + TotalMemoryBlocks = (le32_to_cpu(mmt->PoolEnd) - le32_to_cpu(mmt->PoolStart)) / adev->memblocksize; | ||
5035 | + | ||
5036 | + log(L_DEBUG, "TotalMemoryBlocks=%u (%u bytes)\n", | ||
5037 | + TotalMemoryBlocks, TotalMemoryBlocks*adev->memblocksize); | ||
5038 | + | ||
5039 | + /* MemoryConfigOption.DMA_config bitmask: | ||
5040 | + access to ACX memory is to be done: | ||
5041 | + 0x00080000 using PCI conf space?! | ||
5042 | + 0x00040000 using IO instructions? | ||
5043 | + 0x00000000 using memory access instructions | ||
5044 | + 0x00020000 using local memory block linked list (else what?) | ||
5045 | + 0x00010000 using host indirect descriptors (else host must access ACX memory?) | ||
5046 | + */ | ||
5047 | +#if defined (ACX_MEM) | ||
5048 | + /* | ||
5049 | + * ACX ignores DMA_config for generic slave mode. | ||
5050 | + */ | ||
5051 | + MemoryConfigOption.DMA_config = 0; | ||
5052 | + /* Declare start of the Rx host pool */ | ||
5053 | + MemoryConfigOption.pRxHostDesc = cpu2acx(0); | ||
5054 | + log(L_DEBUG, "pRxHostDesc 0x%08X, rxhostdesc_startphy 0x%lX\n", | ||
5055 | + acx2cpu(MemoryConfigOption.pRxHostDesc), | ||
5056 | + (long)adev->rxhostdesc_startphy); | ||
5057 | +#else | ||
5058 | + if (IS_PCI(adev)) { | ||
5059 | + MemoryConfigOption.DMA_config = cpu_to_le32(0x30000); | ||
5060 | + /* Declare start of the Rx host pool */ | ||
5061 | + MemoryConfigOption.pRxHostDesc = cpu2acx(adev->rxhostdesc_startphy); | ||
5062 | + log(L_DEBUG, "pRxHostDesc 0x%08X, rxhostdesc_startphy 0x%lX\n", | ||
5063 | + acx2cpu(MemoryConfigOption.pRxHostDesc), | ||
5064 | + (long)adev->rxhostdesc_startphy); | ||
5065 | + } else { | ||
5066 | + MemoryConfigOption.DMA_config = cpu_to_le32(0x20000); | ||
5067 | + } | ||
5068 | +#endif | ||
5069 | + | ||
5070 | + /* 50% of the allotment of memory blocks go to tx descriptors */ | ||
5071 | + TxBlockNum = TotalMemoryBlocks / 2; | ||
5072 | + MemoryConfigOption.TxBlockNum = cpu_to_le16(TxBlockNum); | ||
5073 | + | ||
5074 | + /* and 50% go to the rx descriptors */ | ||
5075 | + RxBlockNum = TotalMemoryBlocks - TxBlockNum; | ||
5076 | + MemoryConfigOption.RxBlockNum = cpu_to_le16(RxBlockNum); | ||
5077 | + | ||
5078 | + /* size of the tx and rx descriptor queues */ | ||
5079 | + TotalTxBlockSize = TxBlockNum * adev->memblocksize; | ||
5080 | + TotalRxBlockSize = RxBlockNum * adev->memblocksize; | ||
5081 | + log(L_DEBUG, "TxBlockNum %u RxBlockNum %u TotalTxBlockSize %u " | ||
5082 | + "TotalTxBlockSize %u\n", TxBlockNum, RxBlockNum, | ||
5083 | + TotalTxBlockSize, TotalRxBlockSize); | ||
5084 | + | ||
5085 | + | ||
5086 | + /* align the tx descriptor queue to an alignment of 0x20 (32 bytes) */ | ||
5087 | + MemoryConfigOption.rx_mem = | ||
5088 | + cpu_to_le32((le32_to_cpu(mmt->PoolStart) + 0x1f) & ~0x1f); | ||
5089 | + | ||
5090 | + /* align the rx descriptor queue to units of 0x20 | ||
5091 | + * and offset it by the tx descriptor queue */ | ||
5092 | + MemoryConfigOption.tx_mem = | ||
5093 | + cpu_to_le32((le32_to_cpu(mmt->PoolStart) + TotalRxBlockSize + 0x1f) & ~0x1f); | ||
5094 | + log(L_DEBUG, "rx_mem %08X rx_mem %08X\n", | ||
5095 | + MemoryConfigOption.tx_mem, MemoryConfigOption.rx_mem); | ||
5096 | + | ||
5097 | + /* alert the device to our decision */ | ||
5098 | + if (OK != acx_s_configure(adev, &MemoryConfigOption, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) { | ||
5099 | + goto bad; | ||
5100 | + } | ||
5101 | + | ||
5102 | + /* and tell the device to kick it into gear */ | ||
5103 | + if (OK != acx_s_issue_cmd(adev, ACX100_CMD_INIT_MEMORY, NULL, 0)) { | ||
5104 | + goto bad; | ||
5105 | + } | ||
5106 | +#ifdef ACX_MEM | ||
5107 | + /* | ||
5108 | + * slave memory interface has to manage the transmit pools for the ACX, | ||
5109 | + * so it needs to know what we chose here. | ||
5110 | + */ | ||
5111 | + adev->acx_txbuf_start = MemoryConfigOption.tx_mem; | ||
5112 | + adev->acx_txbuf_numblocks = MemoryConfigOption.TxBlockNum; | ||
5113 | +#endif | ||
5114 | + | ||
5115 | + FN_EXIT1(OK); | ||
5116 | + return OK; | ||
5117 | +bad: | ||
5118 | + FN_EXIT1(NOT_OK); | ||
5119 | + return NOT_OK; | ||
5120 | +} | ||
5121 | + | ||
5122 | + | ||
5123 | +/*********************************************************************** | ||
5124 | +** acx100_s_create_dma_regions | ||
5125 | +** | ||
5126 | +** Note that this fn messes up heavily with hardware, but we cannot | ||
5127 | +** lock it (we need to sleep). Not a problem since IRQs can't happen | ||
5128 | +*/ | ||
5129 | +static int | ||
5130 | +acx100_s_create_dma_regions(acx_device_t *adev) | ||
5131 | +{ | ||
5132 | + acx100_ie_queueconfig_t queueconf; | ||
5133 | + acx_ie_memmap_t memmap; | ||
5134 | + int res = NOT_OK; | ||
5135 | + u32 tx_queue_start, rx_queue_start; | ||
5136 | + | ||
5137 | + FN_ENTER; | ||
5138 | + | ||
5139 | + /* read out the acx100 physical start address for the queues */ | ||
5140 | + if (OK != acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) { | ||
5141 | + goto fail; | ||
5142 | + } | ||
5143 | + | ||
5144 | + tx_queue_start = le32_to_cpu(memmap.QueueStart); | ||
5145 | + rx_queue_start = tx_queue_start + TX_CNT * sizeof(txdesc_t); | ||
5146 | + | ||
5147 | + log(L_DEBUG, "initializing Queue Indicator\n"); | ||
5148 | + | ||
5149 | + memset(&queueconf, 0, sizeof(queueconf)); | ||
5150 | + | ||
5151 | + /* Not needed for PCI or slave memory, so we can avoid setting them altogether */ | ||
5152 | + if (IS_USB(adev)) { | ||
5153 | + queueconf.NumTxDesc = USB_TX_CNT; | ||
5154 | + queueconf.NumRxDesc = USB_RX_CNT; | ||
5155 | + } | ||
5156 | + | ||
5157 | + /* calculate size of queues */ | ||
5158 | + queueconf.AreaSize = cpu_to_le32( | ||
5159 | + TX_CNT * sizeof(txdesc_t) + | ||
5160 | + RX_CNT * sizeof(rxdesc_t) + 8 | ||
5161 | + ); | ||
5162 | + queueconf.NumTxQueues = 1; /* number of tx queues */ | ||
5163 | + /* sets the beginning of the tx descriptor queue */ | ||
5164 | + queueconf.TxQueueStart = memmap.QueueStart; | ||
5165 | + /* done by memset: queueconf.TxQueuePri = 0; */ | ||
5166 | + queueconf.RxQueueStart = cpu_to_le32(rx_queue_start); | ||
5167 | + queueconf.QueueOptions = 1; /* auto reset descriptor */ | ||
5168 | + /* sets the end of the rx descriptor queue */ | ||
5169 | + queueconf.QueueEnd = cpu_to_le32( | ||
5170 | + rx_queue_start + RX_CNT * sizeof(rxdesc_t) | ||
5171 | + ); | ||
5172 | + /* sets the beginning of the next queue */ | ||
5173 | + queueconf.HostQueueEnd = cpu_to_le32(le32_to_cpu(queueconf.QueueEnd) + 8); | ||
5174 | + if (OK != acx_s_configure(adev, &queueconf, ACX1xx_IE_QUEUE_CONFIG)) { | ||
5175 | + goto fail; | ||
5176 | + } | ||
5177 | + | ||
5178 | +#if defined (ACX_MEM) | ||
5179 | + /* sets the beginning of the rx descriptor queue, after the tx descrs */ | ||
5180 | + adev->acx_queue_indicator = | ||
5181 | + (queueindicator_t *) le32_to_cpu (queueconf.QueueEnd); | ||
5182 | + if (OK != acxmem_s_create_hostdesc_queues(adev)) | ||
5183 | + goto fail; | ||
5184 | + | ||
5185 | + acxmem_create_desc_queues(adev, tx_queue_start, rx_queue_start); | ||
5186 | +#else | ||
5187 | + if (IS_PCI(adev)) { | ||
5188 | + /* sets the beginning of the rx descriptor queue, after the tx descrs */ | ||
5189 | + if (OK != acxpci_s_create_hostdesc_queues(adev)) | ||
5190 | + goto fail; | ||
5191 | + acxpci_create_desc_queues(adev, tx_queue_start, rx_queue_start); | ||
5192 | + } | ||
5193 | +#endif | ||
5194 | + | ||
5195 | + if (OK != acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) { | ||
5196 | + goto fail; | ||
5197 | + } | ||
5198 | + | ||
5199 | + /* | ||
5200 | + * Have to make sure we skip past the Queue Indicator (QueueEnd) and Host Queue Indicator | ||
5201 | + * maps, each of which are 8 bytes and follow immediately after the transmit and | ||
5202 | + * receive queues. | ||
5203 | + */ | ||
5204 | + memmap.PoolStart = cpu_to_le32( | ||
5205 | + (le32_to_cpu(memmap.QueueEnd) + 4 + 0x1f) & ~0x1f | ||
5206 | + ); | ||
5207 | + | ||
5208 | + if (OK != acx_s_configure(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) { | ||
5209 | + goto fail; | ||
5210 | + } | ||
5211 | + | ||
5212 | + if (OK != acx100_s_init_memory_pools(adev, &memmap)) { | ||
5213 | + goto fail; | ||
5214 | + } | ||
5215 | + | ||
5216 | + res = OK; | ||
5217 | + goto end; | ||
5218 | + | ||
5219 | +fail: | ||
5220 | + acx_s_msleep(1000); /* ? */ | ||
5221 | +#if defined (ACX_MEM) | ||
5222 | + acxmem_free_desc_queues(adev); | ||
5223 | +#else | ||
5224 | + if (IS_PCI(adev)) | ||
5225 | + acxpci_free_desc_queues(adev); | ||
5226 | +#endif | ||
5227 | +end: | ||
5228 | + FN_EXIT1(res); | ||
5229 | + return res; | ||
5230 | +} | ||
5231 | + | ||
5232 | + | ||
5233 | +/*********************************************************************** | ||
5234 | +** acx111_s_create_dma_regions | ||
5235 | +** | ||
5236 | +** Note that this fn messes heavily with hardware, but we cannot | ||
5237 | +** lock it (we need to sleep). Not a problem since IRQs can't happen | ||
5238 | +*/ | ||
5239 | +#define ACX111_PERCENT(percent) ((percent)/5) | ||
5240 | + | ||
5241 | +static int | ||
5242 | +acx111_s_create_dma_regions(acx_device_t *adev) | ||
5243 | +{ | ||
5244 | + struct acx111_ie_memoryconfig memconf; | ||
5245 | + struct acx111_ie_queueconfig queueconf; | ||
5246 | + u32 tx_queue_start, rx_queue_start; | ||
5247 | + | ||
5248 | + FN_ENTER; | ||
5249 | + | ||
5250 | + /* Calculate memory positions and queue sizes */ | ||
5251 | + | ||
5252 | + /* Set up our host descriptor pool + data pool */ | ||
5253 | +#if defined (ACX_MEM) | ||
5254 | + if (OK != acxmem_s_create_hostdesc_queues(adev)) | ||
5255 | + goto fail; | ||
5256 | +#else | ||
5257 | + if (IS_PCI(adev)) { | ||
5258 | + if (OK != acxpci_s_create_hostdesc_queues(adev)) | ||
5259 | + goto fail; | ||
5260 | + } | ||
5261 | +#endif | ||
5262 | + | ||
5263 | + memset(&memconf, 0, sizeof(memconf)); | ||
5264 | + /* the number of STAs (STA contexts) to support | ||
5265 | + ** NB: was set to 1 and everything seemed to work nevertheless... */ | ||
5266 | + memconf.no_of_stations = cpu_to_le16(VEC_SIZE(adev->sta_list)); | ||
5267 | + /* specify the memory block size. Default is 256 */ | ||
5268 | + memconf.memory_block_size = cpu_to_le16(adev->memblocksize); | ||
5269 | + /* let's use 50%/50% for tx/rx (specify percentage, units of 5%) */ | ||
5270 | + memconf.tx_rx_memory_block_allocation = ACX111_PERCENT(50); | ||
5271 | + /* set the count of our queues | ||
5272 | + ** NB: struct acx111_ie_memoryconfig shall be modified | ||
5273 | + ** if we ever will switch to more than one rx and/or tx queue */ | ||
5274 | + memconf.count_rx_queues = 1; | ||
5275 | + memconf.count_tx_queues = 1; | ||
5276 | + /* 0 == Busmaster Indirect Memory Organization, which is what we want | ||
5277 | + * (using linked host descs with their allocated mem). | ||
5278 | + * 2 == Generic Bus Slave */ | ||
5279 | + /* done by memset: memconf.options = 0; */ | ||
5280 | + /* let's use 25% for fragmentations and 75% for frame transfers | ||
5281 | + * (specified in units of 5%) */ | ||
5282 | + memconf.fragmentation = ACX111_PERCENT(75); | ||
5283 | + /* Rx descriptor queue config */ | ||
5284 | + memconf.rx_queue1_count_descs = RX_CNT; | ||
5285 | + memconf.rx_queue1_type = 7; /* must be set to 7 */ | ||
5286 | + /* done by memset: memconf.rx_queue1_prio = 0; low prio */ | ||
5287 | +#if defined (ACX_MEM) | ||
5288 | + memconf.rx_queue1_host_rx_start = cpu2acx(adev->rxhostdesc_startphy); | ||
5289 | +#else | ||
5290 | + if (IS_PCI(adev)) { | ||
5291 | + memconf.rx_queue1_host_rx_start = cpu2acx(adev->rxhostdesc_startphy); | ||
5292 | + } | ||
5293 | +#endif | ||
5294 | + /* Tx descriptor queue config */ | ||
5295 | + memconf.tx_queue1_count_descs = TX_CNT; | ||
5296 | + /* done by memset: memconf.tx_queue1_attributes = 0; lowest priority */ | ||
5297 | + | ||
5298 | + /* NB1: this looks wrong: (memconf,ACX1xx_IE_QUEUE_CONFIG), | ||
5299 | + ** (queueconf,ACX1xx_IE_MEMORY_CONFIG_OPTIONS) look swapped, eh? | ||
5300 | + ** But it is actually correct wrt IE numbers. | ||
5301 | + ** NB2: sizeof(memconf) == 28 == 0x1c but configure(ACX1xx_IE_QUEUE_CONFIG) | ||
5302 | + ** writes 0x20 bytes (because same IE for acx100 uses struct acx100_ie_queueconfig | ||
5303 | + ** which is 4 bytes larger. what a mess. TODO: clean it up) */ | ||
5304 | + if (OK != acx_s_configure(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG)) { | ||
5305 | + goto fail; | ||
5306 | + } | ||
5307 | + | ||
5308 | + acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS); | ||
5309 | + | ||
5310 | + tx_queue_start = le32_to_cpu(queueconf.tx1_queue_address); | ||
5311 | + rx_queue_start = le32_to_cpu(queueconf.rx1_queue_address); | ||
5312 | + | ||
5313 | + log(L_INIT, "dump queue head (from card):\n" | ||
5314 | + "len: %u\n" | ||
5315 | + "tx_memory_block_address: %X\n" | ||
5316 | + "rx_memory_block_address: %X\n" | ||
5317 | + "tx1_queue address: %X\n" | ||
5318 | + "rx1_queue address: %X\n", | ||
5319 | + le16_to_cpu(queueconf.len), | ||
5320 | + le32_to_cpu(queueconf.tx_memory_block_address), | ||
5321 | + le32_to_cpu(queueconf.rx_memory_block_address), | ||
5322 | + tx_queue_start, | ||
5323 | + rx_queue_start); | ||
5324 | + | ||
5325 | +#if defined (ACX_MEM) | ||
5326 | + acxmem_create_desc_queues(adev, tx_queue_start, rx_queue_start); | ||
5327 | +#else | ||
5328 | + if (IS_PCI(adev)) | ||
5329 | + acxpci_create_desc_queues(adev, tx_queue_start, rx_queue_start); | ||
5330 | +#endif | ||
5331 | + | ||
5332 | + FN_EXIT1(OK); | ||
5333 | + return OK; | ||
5334 | +fail: | ||
5335 | +#if defined (ACX_MEM) | ||
5336 | + acxmem_free_desc_queues(adev); | ||
5337 | +#else | ||
5338 | + if (IS_PCI(adev)) | ||
5339 | + acxpci_free_desc_queues(adev); | ||
5340 | +#endif | ||
5341 | + | ||
5342 | + FN_EXIT1(NOT_OK); | ||
5343 | + return NOT_OK; | ||
5344 | +} | ||
5345 | + | ||
5346 | + | ||
5347 | +/*********************************************************************** | ||
5348 | +*/ | ||
5349 | +static void | ||
5350 | +acx_s_initialize_rx_config(acx_device_t *adev) | ||
5351 | +{ | ||
5352 | + struct { | ||
5353 | + u16 id; | ||
5354 | + u16 len; | ||
5355 | + u16 rx_cfg1; | ||
5356 | + u16 rx_cfg2; | ||
5357 | + } ACX_PACKED cfg; | ||
5358 | + | ||
5359 | + switch (adev->mode) { | ||
5360 | + case ACX_MODE_OFF: | ||
5361 | + adev->rx_config_1 = (u16) (0 | ||
5362 | + /* | RX_CFG1_INCLUDE_RXBUF_HDR */ | ||
5363 | + /* | RX_CFG1_FILTER_SSID */ | ||
5364 | + /* | RX_CFG1_FILTER_BCAST */ | ||
5365 | + /* | RX_CFG1_RCV_MC_ADDR1 */ | ||
5366 | + /* | RX_CFG1_RCV_MC_ADDR0 */ | ||
5367 | + /* | RX_CFG1_FILTER_ALL_MULTI */ | ||
5368 | + /* | RX_CFG1_FILTER_BSSID */ | ||
5369 | + /* | RX_CFG1_FILTER_MAC */ | ||
5370 | + /* | RX_CFG1_RCV_PROMISCUOUS */ | ||
5371 | + /* | RX_CFG1_INCLUDE_FCS */ | ||
5372 | + /* | RX_CFG1_INCLUDE_PHY_HDR */ | ||
5373 | + ); | ||
5374 | + adev->rx_config_2 = (u16) (0 | ||
5375 | + /*| RX_CFG2_RCV_ASSOC_REQ */ | ||
5376 | + /*| RX_CFG2_RCV_AUTH_FRAMES */ | ||
5377 | + /*| RX_CFG2_RCV_BEACON_FRAMES */ | ||
5378 | + /*| RX_CFG2_RCV_CONTENTION_FREE */ | ||
5379 | + /*| RX_CFG2_RCV_CTRL_FRAMES */ | ||
5380 | + /*| RX_CFG2_RCV_DATA_FRAMES */ | ||
5381 | + /*| RX_CFG2_RCV_BROKEN_FRAMES */ | ||
5382 | + /*| RX_CFG2_RCV_MGMT_FRAMES */ | ||
5383 | + /*| RX_CFG2_RCV_PROBE_REQ */ | ||
5384 | + /*| RX_CFG2_RCV_PROBE_RESP */ | ||
5385 | + /*| RX_CFG2_RCV_ACK_FRAMES */ | ||
5386 | + /*| RX_CFG2_RCV_OTHER */ | ||
5387 | + ); | ||
5388 | + break; | ||
5389 | + case ACX_MODE_MONITOR: | ||
5390 | + adev->rx_config_1 = (u16) (0 | ||
5391 | + /* | RX_CFG1_INCLUDE_RXBUF_HDR */ | ||
5392 | + /* | RX_CFG1_FILTER_SSID */ | ||
5393 | + /* | RX_CFG1_FILTER_BCAST */ | ||
5394 | + /* | RX_CFG1_RCV_MC_ADDR1 */ | ||
5395 | + /* | RX_CFG1_RCV_MC_ADDR0 */ | ||
5396 | + /* | RX_CFG1_FILTER_ALL_MULTI */ | ||
5397 | + /* | RX_CFG1_FILTER_BSSID */ | ||
5398 | + /* | RX_CFG1_FILTER_MAC */ | ||
5399 | + | RX_CFG1_RCV_PROMISCUOUS | ||
5400 | + /* | RX_CFG1_INCLUDE_FCS */ | ||
5401 | + /* | RX_CFG1_INCLUDE_PHY_HDR */ | ||
5402 | + ); | ||
5403 | + adev->rx_config_2 = (u16) (0 | ||
5404 | + | RX_CFG2_RCV_ASSOC_REQ | ||
5405 | + | RX_CFG2_RCV_AUTH_FRAMES | ||
5406 | + | RX_CFG2_RCV_BEACON_FRAMES | ||
5407 | + | RX_CFG2_RCV_CONTENTION_FREE | ||
5408 | + | RX_CFG2_RCV_CTRL_FRAMES | ||
5409 | + | RX_CFG2_RCV_DATA_FRAMES | ||
5410 | + | RX_CFG2_RCV_BROKEN_FRAMES | ||
5411 | + | RX_CFG2_RCV_MGMT_FRAMES | ||
5412 | + | RX_CFG2_RCV_PROBE_REQ | ||
5413 | + | RX_CFG2_RCV_PROBE_RESP | ||
5414 | + | RX_CFG2_RCV_ACK_FRAMES | ||
5415 | + | RX_CFG2_RCV_OTHER | ||
5416 | + ); | ||
5417 | + break; | ||
5418 | + default: | ||
5419 | + adev->rx_config_1 = (u16) (0 | ||
5420 | + /* | RX_CFG1_INCLUDE_RXBUF_HDR */ | ||
5421 | + /* | RX_CFG1_FILTER_SSID */ | ||
5422 | + /* | RX_CFG1_FILTER_BCAST */ | ||
5423 | + /* | RX_CFG1_RCV_MC_ADDR1 */ | ||
5424 | + /* | RX_CFG1_RCV_MC_ADDR0 */ | ||
5425 | + /* | RX_CFG1_FILTER_ALL_MULTI */ | ||
5426 | + /* | RX_CFG1_FILTER_BSSID */ | ||
5427 | + | RX_CFG1_FILTER_MAC | ||
5428 | + /* | RX_CFG1_RCV_PROMISCUOUS */ | ||
5429 | + /* | RX_CFG1_INCLUDE_FCS */ | ||
5430 | + /* | RX_CFG1_INCLUDE_PHY_HDR */ | ||
5431 | + ); | ||
5432 | + adev->rx_config_2 = (u16) (0 | ||
5433 | + | RX_CFG2_RCV_ASSOC_REQ | ||
5434 | + | RX_CFG2_RCV_AUTH_FRAMES | ||
5435 | + | RX_CFG2_RCV_BEACON_FRAMES | ||
5436 | + | RX_CFG2_RCV_CONTENTION_FREE | ||
5437 | + | RX_CFG2_RCV_CTRL_FRAMES | ||
5438 | + | RX_CFG2_RCV_DATA_FRAMES | ||
5439 | + /*| RX_CFG2_RCV_BROKEN_FRAMES */ | ||
5440 | + | RX_CFG2_RCV_MGMT_FRAMES | ||
5441 | + | RX_CFG2_RCV_PROBE_REQ | ||
5442 | + | RX_CFG2_RCV_PROBE_RESP | ||
5443 | + /*| RX_CFG2_RCV_ACK_FRAMES */ | ||
5444 | + | RX_CFG2_RCV_OTHER | ||
5445 | + ); | ||
5446 | + break; | ||
5447 | + } | ||
5448 | + adev->rx_config_1 |= RX_CFG1_INCLUDE_RXBUF_HDR; | ||
5449 | + | ||
5450 | + if ((adev->rx_config_1 & RX_CFG1_INCLUDE_PHY_HDR) | ||
5451 | + || (adev->firmware_numver >= 0x02000000)) | ||
5452 | + adev->phy_header_len = IS_ACX111(adev) ? 8 : 4; | ||
5453 | + else | ||
5454 | + adev->phy_header_len = 0; | ||
5455 | + | ||
5456 | + log(L_INIT, "setting RXconfig to %04X:%04X\n", | ||
5457 | + adev->rx_config_1, adev->rx_config_2); | ||
5458 | + cfg.rx_cfg1 = cpu_to_le16(adev->rx_config_1); | ||
5459 | + cfg.rx_cfg2 = cpu_to_le16(adev->rx_config_2); | ||
5460 | + acx_s_configure(adev, &cfg, ACX1xx_IE_RXCONFIG); | ||
5461 | +} | ||
5462 | + | ||
5463 | + | ||
5464 | +/*********************************************************************** | ||
5465 | +** acx_s_set_defaults | ||
5466 | +*/ | ||
5467 | +void | ||
5468 | +acx_s_set_defaults(acx_device_t *adev) | ||
5469 | +{ | ||
5470 | + unsigned long flags; | ||
5471 | + | ||
5472 | + FN_ENTER; | ||
5473 | + | ||
5474 | + /* do it before getting settings, prevent bogus channel 0 warning */ | ||
5475 | + adev->channel = 1; | ||
5476 | + | ||
5477 | + /* query some settings from the card. | ||
5478 | + * NOTE: for some settings, e.g. CCA and ED (ACX100!), an initial | ||
5479 | + * query is REQUIRED, otherwise the card won't work correctly! */ | ||
5480 | + adev->get_mask = GETSET_ANTENNA|GETSET_SENSITIVITY|GETSET_STATION_ID|GETSET_REG_DOMAIN; | ||
5481 | + /* Only ACX100 supports ED and CCA */ | ||
5482 | + if (IS_ACX100(adev)) | ||
5483 | + adev->get_mask |= GETSET_CCA|GETSET_ED_THRESH; | ||
5484 | + | ||
5485 | + acx_s_update_card_settings(adev); | ||
5486 | + | ||
5487 | + acx_lock(adev, flags); | ||
5488 | + | ||
5489 | + /* set our global interrupt mask */ | ||
5490 | +#if defined (ACX_MEM) | ||
5491 | + acxmem_set_interrupt_mask(adev); | ||
5492 | +#else | ||
5493 | + if (IS_PCI(adev)) | ||
5494 | + acxpci_set_interrupt_mask(adev); | ||
5495 | +#endif | ||
5496 | + | ||
5497 | + adev->led_power = 1; /* LED is active on startup */ | ||
5498 | + adev->brange_max_quality = 60; /* LED blink max quality is 60 */ | ||
5499 | + adev->brange_time_last_state_change = jiffies; | ||
5500 | + | ||
5501 | + /* copy the MAC address we just got from the card | ||
5502 | + * into our MAC address used during current 802.11 session */ | ||
5503 | + MAC_COPY(adev->dev_addr, adev->ndev->dev_addr); | ||
5504 | + MAC_BCAST(adev->ap); | ||
5505 | + | ||
5506 | + adev->essid_len = | ||
5507 | + snprintf(adev->essid, sizeof(adev->essid), "STA%02X%02X%02X", | ||
5508 | + adev->dev_addr[3], adev->dev_addr[4], adev->dev_addr[5]); | ||
5509 | + adev->essid_active = 1; | ||
5510 | + | ||
5511 | + /* we have a nick field to waste, so why not abuse it | ||
5512 | + * to announce the driver version? ;-) */ | ||
5513 | + strncpy(adev->nick, "acx " ACX_RELEASE, IW_ESSID_MAX_SIZE); | ||
5514 | + | ||
5515 | +#if defined (ACX_MEM) | ||
5516 | + adev->reg_dom_id = adev->cfgopt_domains.list[0]; | ||
5517 | +#else | ||
5518 | + if (IS_PCI(adev)) { /* FIXME: this should be made to apply to USB, too! */ | ||
5519 | + /* first regulatory domain entry in EEPROM == default reg. domain */ | ||
5520 | + adev->reg_dom_id = adev->cfgopt_domains.list[0]; | ||
5521 | + } | ||
5522 | +#endif | ||
5523 | + | ||
5524 | + /* 0xffff would be better, but then we won't get a "scan complete" | ||
5525 | + * interrupt, so our current infrastructure will fail: */ | ||
5526 | + adev->scan_count = 1; | ||
5527 | + adev->scan_mode = ACX_SCAN_OPT_ACTIVE; | ||
5528 | + adev->scan_duration = 100; | ||
5529 | + adev->scan_probe_delay = 200; | ||
5530 | + /* reported to break scanning: adev->scan_probe_delay = adev->cfgopt_probe_delay; */ | ||
5531 | + adev->scan_rate = ACX_SCAN_RATE_1; | ||
5532 | + | ||
5533 | + adev->mode = ACX_MODE_2_STA; | ||
5534 | + adev->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM; | ||
5535 | + adev->listen_interval = 100; | ||
5536 | + adev->beacon_interval = DEFAULT_BEACON_INTERVAL; | ||
5537 | + adev->dtim_interval = DEFAULT_DTIM_INTERVAL; | ||
5538 | + | ||
5539 | + adev->msdu_lifetime = DEFAULT_MSDU_LIFETIME; | ||
5540 | + | ||
5541 | + adev->rts_threshold = DEFAULT_RTS_THRESHOLD; | ||
5542 | + adev->frag_threshold = 2346; | ||
5543 | + | ||
5544 | + /* use standard default values for retry limits */ | ||
5545 | + adev->short_retry = 7; /* max. retries for (short) non-RTS packets */ | ||
5546 | + adev->long_retry = 4; /* max. retries for long (RTS) packets */ | ||
5547 | + | ||
5548 | + adev->preamble_mode = 2; /* auto */ | ||
5549 | + adev->fallback_threshold = 3; | ||
5550 | + adev->stepup_threshold = 10; | ||
5551 | + adev->rate_bcast = RATE111_1; | ||
5552 | + adev->rate_bcast100 = RATE100_1; | ||
5553 | + adev->rate_basic = RATE111_1 | RATE111_2; | ||
5554 | + adev->rate_auto = 1; | ||
5555 | + if (IS_ACX111(adev)) { | ||
5556 | + adev->rate_oper = RATE111_ALL; | ||
5557 | + } else { | ||
5558 | + adev->rate_oper = RATE111_ACX100_COMPAT; | ||
5559 | + } | ||
5560 | + | ||
5561 | + /* Supported Rates element - the rates here are given in units of | ||
5562 | + * 500 kbit/s, plus 0x80 added. See 802.11-1999.pdf item 7.3.2.2 */ | ||
5563 | + acx_l_update_ratevector(adev); | ||
5564 | + | ||
5565 | + /* set some more defaults */ | ||
5566 | + if (IS_ACX111(adev)) { | ||
5567 | + /* 30mW (15dBm) is default, at least in my acx111 card: */ | ||
5568 | + adev->tx_level_dbm = 15; | ||
5569 | + } else { | ||
5570 | + /* don't use max. level, since it might be dangerous | ||
5571 | + * (e.g. WRT54G people experience | ||
5572 | + * excessive Tx power damage!) */ | ||
5573 | + adev->tx_level_dbm = 18; | ||
5574 | + /* | ||
5575 | + * Lower power for the iPaq hx4700 | ||
5576 | + */ | ||
5577 | + if (IS_MEM(adev)) { | ||
5578 | + adev->tx_level_dbm = 14; | ||
5579 | + } | ||
5580 | + } | ||
5581 | + /* adev->tx_level_auto = 1; */ | ||
5582 | + if (IS_ACX111(adev)) { | ||
5583 | + /* start with sensitivity level 1 out of 3: */ | ||
5584 | + adev->sensitivity = 1; | ||
5585 | + } | ||
5586 | + | ||
5587 | +/* #define ENABLE_POWER_SAVE */ | ||
5588 | +#ifdef ENABLE_POWER_SAVE | ||
5589 | + adev->ps_wakeup_cfg = PS_CFG_ENABLE | PS_CFG_WAKEUP_ALL_BEAC; | ||
5590 | + adev->ps_listen_interval = 1; | ||
5591 | + adev->ps_options = PS_OPT_ENA_ENHANCED_PS | PS_OPT_TX_PSPOLL | PS_OPT_STILL_RCV_BCASTS; | ||
5592 | + adev->ps_hangover_period = 30; | ||
5593 | + adev->ps_enhanced_transition_time = 0; | ||
5594 | +#else | ||
5595 | + adev->ps_wakeup_cfg = 0; | ||
5596 | + adev->ps_listen_interval = 0; | ||
5597 | + adev->ps_options = 0; | ||
5598 | + adev->ps_hangover_period = 0; | ||
5599 | + adev->ps_enhanced_transition_time = 0; | ||
5600 | +#endif | ||
5601 | + | ||
5602 | + /* These settings will be set in fw on ifup */ | ||
5603 | + adev->set_mask = 0 | ||
5604 | + | GETSET_RETRY | ||
5605 | + | SET_MSDU_LIFETIME | ||
5606 | + /* configure card to do rate fallback when in auto rate mode */ | ||
5607 | + | SET_RATE_FALLBACK | ||
5608 | + | SET_RXCONFIG | ||
5609 | + | GETSET_TXPOWER | ||
5610 | + /* better re-init the antenna value we got above */ | ||
5611 | + | GETSET_ANTENNA | ||
5612 | +#if POWER_SAVE_80211 | ||
5613 | + | GETSET_POWER_80211 | ||
5614 | +#endif | ||
5615 | + ; | ||
5616 | + | ||
5617 | + acx_unlock(adev, flags); | ||
5618 | + acx_lock_unhold(); /* hold time 844814 CPU ticks @2GHz */ | ||
5619 | + | ||
5620 | + acx_s_initialize_rx_config(adev); | ||
5621 | + | ||
5622 | + FN_EXIT0; | ||
5623 | +} | ||
5624 | + | ||
5625 | + | ||
5626 | +/*********************************************************************** | ||
5627 | +** FIXME: this should be solved in a general way for all radio types | ||
5628 | +** by decoding the radio firmware module, | ||
5629 | +** since it probably has some standard structure describing how to | ||
5630 | +** set the power level of the radio module which it controls. | ||
5631 | +** Or maybe not, since the radio module probably has a function interface | ||
5632 | +** instead which then manages Tx level programming :-\ | ||
5633 | +*/ | ||
5634 | +static int | ||
5635 | +acx111_s_set_tx_level(acx_device_t *adev, u8 level_dbm) | ||
5636 | +{ | ||
5637 | + struct acx111_ie_tx_level tx_level; | ||
5638 | + | ||
5639 | + /* my acx111 card has two power levels in its configoptions (== EEPROM): | ||
5640 | + * 1 (30mW) [15dBm] | ||
5641 | + * 2 (10mW) [10dBm] | ||
5642 | + * For now, just assume all other acx111 cards have the same. | ||
5643 | + * FIXME: Ideally we would query it here, but we first need a | ||
5644 | + * standard way to query individual configoptions easily. | ||
5645 | + * Well, now we have proper cfgopt txpower variables, but this still | ||
5646 | + * hasn't been done yet, since it also requires dBm <-> mW conversion here... */ | ||
5647 | + if (level_dbm <= 12) { | ||
5648 | + tx_level.level = 2; /* 10 dBm */ | ||
5649 | + adev->tx_level_dbm = 10; | ||
5650 | + } else { | ||
5651 | + tx_level.level = 1; /* 15 dBm */ | ||
5652 | + adev->tx_level_dbm = 15; | ||
5653 | + } | ||
5654 | + if (level_dbm != adev->tx_level_dbm) | ||
5655 | + log(L_INIT, "acx111 firmware has specific " | ||
5656 | + "power levels only: adjusted %d dBm to %d dBm!\n", | ||
5657 | + level_dbm, adev->tx_level_dbm); | ||
5658 | + | ||
5659 | + return acx_s_configure(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); | ||
5660 | +} | ||
5661 | + | ||
5662 | +static int | ||
5663 | +acx_s_set_tx_level(acx_device_t *adev, u8 level_dbm) | ||
5664 | +{ | ||
5665 | + if (IS_ACX111(adev)) { | ||
5666 | + return acx111_s_set_tx_level(adev, level_dbm); | ||
5667 | + } | ||
5668 | +#if defined (ACX_MEM) | ||
5669 | + return acx100mem_s_set_tx_level(adev, level_dbm); | ||
5670 | +#else | ||
5671 | + if (IS_PCI(adev)) { | ||
5672 | + return acx100pci_s_set_tx_level(adev, level_dbm); | ||
5673 | + } | ||
5674 | +#endif | ||
5675 | + return OK; | ||
5676 | +} | ||
5677 | + | ||
5678 | + | ||
5679 | +/*********************************************************************** | ||
5680 | +*/ | ||
5681 | +#ifdef UNUSED | ||
5682 | +/* Returns the current tx level (ACX111) */ | ||
5683 | +static u8 | ||
5684 | +acx111_s_get_tx_level(acx_device_t *adev) | ||
5685 | +{ | ||
5686 | + struct acx111_ie_tx_level tx_level; | ||
5687 | + | ||
5688 | + tx_level.level = 0; | ||
5689 | + acx_s_interrogate(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); | ||
5690 | + return tx_level.level; | ||
5691 | +} | ||
5692 | +#endif | ||
5693 | + | ||
5694 | + | ||
5695 | +/*********************************************************************** | ||
5696 | +** acx_l_rxmonitor | ||
5697 | +** Called from IRQ context only | ||
5698 | +*/ | ||
5699 | +static void | ||
5700 | +acx_l_rxmonitor(acx_device_t *adev, const rxbuffer_t *rxbuf) | ||
5701 | +{ | ||
5702 | + wlansniffrm_t *msg; | ||
5703 | + struct sk_buff *skb; | ||
5704 | + void *datap; | ||
5705 | + unsigned int skb_len; | ||
5706 | + int payload_offset; | ||
5707 | + | ||
5708 | + FN_ENTER; | ||
5709 | + | ||
5710 | + /* we are in big luck: the acx100 doesn't modify any of the fields */ | ||
5711 | + /* in the 802.11 frame. just pass this packet into the PF_PACKET */ | ||
5712 | + /* subsystem. yeah. */ | ||
5713 | + payload_offset = ((u8*)acx_get_wlan_hdr(adev, rxbuf) - (u8*)rxbuf); | ||
5714 | + skb_len = RXBUF_BYTES_USED(rxbuf) - payload_offset; | ||
5715 | + | ||
5716 | + /* sanity check */ | ||
5717 | + if (unlikely(skb_len > WLAN_A4FR_MAXLEN_WEP)) { | ||
5718 | + printk("%s: monitor mode panic: oversized frame!\n", | ||
5719 | + adev->ndev->name); | ||
5720 | + goto end; | ||
5721 | + } | ||
5722 | + | ||
5723 | + if (adev->ndev->type == ARPHRD_IEEE80211_PRISM) | ||
5724 | + skb_len += sizeof(*msg); | ||
5725 | + | ||
5726 | + /* allocate skb */ | ||
5727 | + skb = dev_alloc_skb(skb_len); | ||
5728 | + if (unlikely(!skb)) { | ||
5729 | + printk("%s: no memory for skb (%u bytes)\n", | ||
5730 | + adev->ndev->name, skb_len); | ||
5731 | + goto end; | ||
5732 | + } | ||
5733 | + | ||
5734 | + skb_put(skb, skb_len); | ||
5735 | + | ||
5736 | + if (adev->ndev->type == ARPHRD_IEEE80211) { | ||
5737 | + /* when in raw 802.11 mode, just copy frame as-is */ | ||
5738 | + datap = skb->data; | ||
5739 | + } else if (adev->ndev->type == ARPHRD_IEEE80211_PRISM) { | ||
5740 | + /* emulate prism header */ | ||
5741 | + msg = (wlansniffrm_t*)skb->data; | ||
5742 | + datap = msg + 1; | ||
5743 | + | ||
5744 | + msg->msgcode = WLANSNIFFFRM; | ||
5745 | + msg->msglen = sizeof(*msg); | ||
5746 | + strncpy(msg->devname, adev->ndev->name, sizeof(msg->devname)-1); | ||
5747 | + msg->devname[sizeof(msg->devname)-1] = '\0'; | ||
5748 | + | ||
5749 | + msg->hosttime.did = WLANSNIFFFRM_hosttime; | ||
5750 | + msg->hosttime.status = WLANITEM_STATUS_data_ok; | ||
5751 | + msg->hosttime.len = 4; | ||
5752 | + msg->hosttime.data = jiffies; | ||
5753 | + | ||
5754 | + msg->mactime.did = WLANSNIFFFRM_mactime; | ||
5755 | + msg->mactime.status = WLANITEM_STATUS_data_ok; | ||
5756 | + msg->mactime.len = 4; | ||
5757 | + msg->mactime.data = rxbuf->time; | ||
5758 | + | ||
5759 | + msg->channel.did = WLANSNIFFFRM_channel; | ||
5760 | + msg->channel.status = WLANITEM_STATUS_data_ok; | ||
5761 | + msg->channel.len = 4; | ||
5762 | + msg->channel.data = adev->channel; | ||
5763 | + | ||
5764 | + msg->rssi.did = WLANSNIFFFRM_rssi; | ||
5765 | + msg->rssi.status = WLANITEM_STATUS_no_value; | ||
5766 | + msg->rssi.len = 4; | ||
5767 | + msg->rssi.data = 0; | ||
5768 | + | ||
5769 | + msg->sq.did = WLANSNIFFFRM_sq; | ||
5770 | + msg->sq.status = WLANITEM_STATUS_no_value; | ||
5771 | + msg->sq.len = 4; | ||
5772 | + msg->sq.data = 0; | ||
5773 | + | ||
5774 | + msg->signal.did = WLANSNIFFFRM_signal; | ||
5775 | + msg->signal.status = WLANITEM_STATUS_data_ok; | ||
5776 | + msg->signal.len = 4; | ||
5777 | + msg->signal.data = rxbuf->phy_snr; | ||
5778 | + | ||
5779 | + msg->noise.did = WLANSNIFFFRM_noise; | ||
5780 | + msg->noise.status = WLANITEM_STATUS_data_ok; | ||
5781 | + msg->noise.len = 4; | ||
5782 | + msg->noise.data = rxbuf->phy_level; | ||
5783 | + | ||
5784 | + msg->rate.did = WLANSNIFFFRM_rate; | ||
5785 | + msg->rate.status = WLANITEM_STATUS_data_ok; | ||
5786 | + msg->rate.len = 4; | ||
5787 | + msg->rate.data = rxbuf->phy_plcp_signal / 5; | ||
5788 | + | ||
5789 | + msg->istx.did = WLANSNIFFFRM_istx; | ||
5790 | + msg->istx.status = WLANITEM_STATUS_data_ok; | ||
5791 | + msg->istx.len = 4; | ||
5792 | + msg->istx.data = 0; /* tx=0: it's not a tx packet */ | ||
5793 | + | ||
5794 | + skb_len -= sizeof(*msg); | ||
5795 | + | ||
5796 | + msg->frmlen.did = WLANSNIFFFRM_signal; | ||
5797 | + msg->frmlen.status = WLANITEM_STATUS_data_ok; | ||
5798 | + msg->frmlen.len = 4; | ||
5799 | + msg->frmlen.data = skb_len; | ||
5800 | + } else { | ||
5801 | + printk("acx: unsupported netdev type %d!\n", adev->ndev->type); | ||
5802 | + dev_kfree_skb(skb); | ||
5803 | + return; | ||
5804 | + } | ||
5805 | + | ||
5806 | + /* sanity check (keep it here) */ | ||
5807 | + if (unlikely((int)skb_len < 0)) { | ||
5808 | + printk("acx: skb_len=%d. Driver bug, please report\n", (int)skb_len); | ||
5809 | + dev_kfree_skb(skb); | ||
5810 | + return; | ||
5811 | + } | ||
5812 | + memcpy(datap, ((unsigned char*)rxbuf)+payload_offset, skb_len); | ||
5813 | + | ||
5814 | + skb->dev = adev->ndev; | ||
5815 | + skb->dev->last_rx = jiffies; | ||
5816 | + | ||
5817 | + skb_reset_mac_header(skb); | ||
5818 | + skb->ip_summed = CHECKSUM_NONE; | ||
5819 | + skb->pkt_type = PACKET_OTHERHOST; | ||
5820 | + skb->protocol = htons(ETH_P_80211_RAW); | ||
5821 | + netif_rx(skb); | ||
5822 | + | ||
5823 | + adev->stats.rx_packets++; | ||
5824 | + adev->stats.rx_bytes += skb->len; | ||
5825 | + | ||
5826 | +end: | ||
5827 | + FN_EXIT0; | ||
5828 | +} | ||
5829 | + | ||
5830 | + | ||
5831 | +/*********************************************************************** | ||
5832 | +** acx_l_rx_ieee802_11_frame | ||
5833 | +** | ||
5834 | +** Called from IRQ context only | ||
5835 | +*/ | ||
5836 | + | ||
5837 | +/* All these contortions are for saner dup logging | ||
5838 | +** | ||
5839 | +** We want: (a) to know about excessive dups | ||
5840 | +** (b) to not spam kernel log about occasional dups | ||
5841 | +** | ||
5842 | +** 1/64 threshold was chosen by running "ping -A" | ||
5843 | +** It gave "rx: 59 DUPs in 2878 packets" only with 4 parallel | ||
5844 | +** "ping -A" streams running. */ | ||
5845 | +/* 2005-10-11: bumped up to 1/8 | ||
5846 | +** subtract a $smallint from dup_count in order to | ||
5847 | +** avoid "2 DUPs in 19 packets" messages */ | ||
5848 | +static inline int | ||
5849 | +acx_l_handle_dup(acx_device_t *adev, u16 seq) | ||
5850 | +{ | ||
5851 | + if (adev->dup_count) { | ||
5852 | + adev->nondup_count++; | ||
5853 | + if (time_after(jiffies, adev->dup_msg_expiry)) { | ||
5854 | + /* Log only if more than 1 dup in 64 packets */ | ||
5855 | + if (adev->nondup_count/8 < adev->dup_count-5) { | ||
5856 | + printk(KERN_INFO "%s: rx: %d DUPs in " | ||
5857 | + "%d packets received in 10 secs\n", | ||
5858 | + adev->ndev->name, | ||
5859 | + adev->dup_count, | ||
5860 | + adev->nondup_count); | ||
5861 | + } | ||
5862 | + adev->dup_count = 0; | ||
5863 | + adev->nondup_count = 0; | ||
5864 | + } | ||
5865 | + } | ||
5866 | + if (unlikely(seq == adev->last_seq_ctrl)) { | ||
5867 | + if (!adev->dup_count++) | ||
5868 | + adev->dup_msg_expiry = jiffies + 10*HZ; | ||
5869 | + adev->stats.rx_errors++; | ||
5870 | + return 1; /* a dup */ | ||
5871 | + } | ||
5872 | + adev->last_seq_ctrl = seq; | ||
5873 | + return 0; | ||
5874 | +} | ||
5875 | + | ||
5876 | +static int | ||
5877 | +acx_l_rx_ieee802_11_frame(acx_device_t *adev, rxbuffer_t *rxbuf) | ||
5878 | +{ | ||
5879 | + unsigned int ftype, fstype; | ||
5880 | + const wlan_hdr_t *hdr; | ||
5881 | + int result = NOT_OK; | ||
5882 | + | ||
5883 | + FN_ENTER; | ||
5884 | + | ||
5885 | + hdr = acx_get_wlan_hdr(adev, rxbuf); | ||
5886 | + | ||
5887 | + /* see IEEE 802.11-1999.pdf chapter 7 "MAC frame formats" */ | ||
5888 | + if (unlikely((hdr->fc & WF_FC_PVERi) != 0)) { | ||
5889 | + printk_ratelimited(KERN_INFO "rx: unsupported 802.11 protocol\n"); | ||
5890 | + goto end; | ||
5891 | + } | ||
5892 | + | ||
5893 | + ftype = hdr->fc & WF_FC_FTYPEi; | ||
5894 | + fstype = hdr->fc & WF_FC_FSTYPEi; | ||
5895 | + | ||
5896 | + switch (ftype) { | ||
5897 | + /* check data frames first, for speed */ | ||
5898 | + case WF_FTYPE_DATAi: | ||
5899 | + switch (fstype) { | ||
5900 | + case WF_FSTYPE_DATAONLYi: | ||
5901 | + if (acx_l_handle_dup(adev, hdr->seq)) | ||
5902 | + break; /* a dup, simply discard it */ | ||
5903 | + | ||
5904 | + /* TODO: | ||
5905 | + if (WF_FC_FROMTODSi == (hdr->fc & WF_FC_FROMTODSi)) { | ||
5906 | + result = acx_l_process_data_frame_wds(adev, rxbuf); | ||
5907 | + break; | ||
5908 | + } | ||
5909 | + */ | ||
5910 | + | ||
5911 | + switch (adev->mode) { | ||
5912 | + case ACX_MODE_3_AP: | ||
5913 | + result = acx_l_process_data_frame_master(adev, rxbuf); | ||
5914 | + break; | ||
5915 | + case ACX_MODE_0_ADHOC: | ||
5916 | + case ACX_MODE_2_STA: | ||
5917 | + result = acx_l_process_data_frame_client(adev, rxbuf); | ||
5918 | + break; | ||
5919 | + } | ||
5920 | + case WF_FSTYPE_DATA_CFACKi: | ||
5921 | + case WF_FSTYPE_DATA_CFPOLLi: | ||
5922 | + case WF_FSTYPE_DATA_CFACK_CFPOLLi: | ||
5923 | + case WF_FSTYPE_CFPOLLi: | ||
5924 | + case WF_FSTYPE_CFACK_CFPOLLi: | ||
5925 | + /* see above. | ||
5926 | + acx_process_class_frame(adev, rxbuf, 3); */ | ||
5927 | + break; | ||
5928 | + case WF_FSTYPE_NULLi: | ||
5929 | + /* acx_l_process_NULL_frame(adev, rxbuf, 3); */ | ||
5930 | + break; | ||
5931 | + /* FIXME: same here, see above */ | ||
5932 | + case WF_FSTYPE_CFACKi: | ||
5933 | + default: | ||
5934 | + break; | ||
5935 | + } | ||
5936 | + break; | ||
5937 | + case WF_FTYPE_MGMTi: | ||
5938 | + result = acx_l_process_mgmt_frame(adev, rxbuf); | ||
5939 | + break; | ||
5940 | + case WF_FTYPE_CTLi: | ||
5941 | + if (fstype == WF_FSTYPE_PSPOLLi) | ||
5942 | + result = OK; | ||
5943 | + /* this call is irrelevant, since | ||
5944 | + * acx_process_class_frame is a stub, so return | ||
5945 | + * immediately instead. | ||
5946 | + * return acx_process_class_frame(adev, rxbuf, 3); */ | ||
5947 | + break; | ||
5948 | + default: | ||
5949 | + break; | ||
5950 | + } | ||
5951 | +end: | ||
5952 | + FN_EXIT1(result); | ||
5953 | + return result; | ||
5954 | +} | ||
5955 | + | ||
5956 | + | ||
5957 | +/*********************************************************************** | ||
5958 | +** acx_l_process_rxbuf | ||
5959 | +** | ||
5960 | +** NB: used by USB code also | ||
5961 | +*/ | ||
5962 | +void | ||
5963 | +acx_l_process_rxbuf(acx_device_t *adev, rxbuffer_t *rxbuf) | ||
5964 | +{ | ||
5965 | + struct wlan_hdr *hdr; | ||
5966 | + unsigned int qual; | ||
5967 | + int buf_len; | ||
5968 | + u16 fc; | ||
5969 | + | ||
5970 | + hdr = acx_get_wlan_hdr(adev, rxbuf); | ||
5971 | + fc = le16_to_cpu(hdr->fc); | ||
5972 | + /* length of frame from control field to first byte of FCS */ | ||
5973 | + buf_len = RXBUF_BYTES_RCVD(adev, rxbuf); | ||
5974 | + | ||
5975 | + if ( ((WF_FC_FSTYPE & fc) != WF_FSTYPE_BEACON) | ||
5976 | + || (acx_debug & L_XFER_BEACON) | ||
5977 | + ) { | ||
5978 | + log(L_XFER|L_DATA, "rx: %s " | ||
5979 | + "time:%u len:%u signal:%u SNR:%u macstat:%02X " | ||
5980 | + "phystat:%02X phyrate:%u status:%u\n", | ||
5981 | + acx_get_packet_type_string(fc), | ||
5982 | + le32_to_cpu(rxbuf->time), | ||
5983 | + buf_len, | ||
5984 | + acx_signal_to_winlevel(rxbuf->phy_level), | ||
5985 | + acx_signal_to_winlevel(rxbuf->phy_snr), | ||
5986 | + rxbuf->mac_status, | ||
5987 | + rxbuf->phy_stat_baseband, | ||
5988 | + rxbuf->phy_plcp_signal, | ||
5989 | + adev->status); | ||
5990 | + } | ||
5991 | + | ||
5992 | + if (unlikely(acx_debug & L_DATA)) { | ||
5993 | + printk("rx: 802.11 buf[%u]: ", buf_len); | ||
5994 | + acx_dump_bytes(hdr, buf_len); | ||
5995 | + } | ||
5996 | + | ||
5997 | + /* FIXME: should check for Rx errors (rxbuf->mac_status? | ||
5998 | + * discard broken packets - but NOT for monitor!) | ||
5999 | + * and update Rx packet statistics here */ | ||
6000 | + | ||
6001 | + if (unlikely(adev->mode == ACX_MODE_MONITOR)) { | ||
6002 | + acx_l_rxmonitor(adev, rxbuf); | ||
6003 | + } else if (likely(buf_len >= WLAN_HDR_A3_LEN)) { | ||
6004 | + acx_l_rx_ieee802_11_frame(adev, rxbuf); | ||
6005 | + } else { | ||
6006 | + log(L_DEBUG|L_XFER|L_DATA, | ||
6007 | + "rx: NOT receiving packet (%s): " | ||
6008 | + "size too small (%u)\n", | ||
6009 | + acx_get_packet_type_string(fc), | ||
6010 | + buf_len); | ||
6011 | + } | ||
6012 | + | ||
6013 | + /* Now check Rx quality level, AFTER processing packet. | ||
6014 | + * I tried to figure out how to map these levels to dBm | ||
6015 | + * values, but for the life of me I really didn't | ||
6016 | + * manage to get it. Either these values are not meant to | ||
6017 | + * be expressed in dBm, or it's some pretty complicated | ||
6018 | + * calculation. */ | ||
6019 | + | ||
6020 | +#ifdef FROM_SCAN_SOURCE_ONLY | ||
6021 | + /* only consider packets originating from the MAC | ||
6022 | + * address of the device that's managing our BSSID. | ||
6023 | + * Disable it for now, since it removes information (levels | ||
6024 | + * from different peers) and slows the Rx path. */ | ||
6025 | + if (adev->ap_client | ||
6026 | + && mac_is_equal(hdr->a2, adev->ap_client->address)) { | ||
6027 | +#endif | ||
6028 | + adev->wstats.qual.level = acx_signal_to_winlevel(rxbuf->phy_level); | ||
6029 | + adev->wstats.qual.noise = acx_signal_to_winlevel(rxbuf->phy_snr); | ||
6030 | +#ifndef OLD_QUALITY | ||
6031 | + qual = acx_signal_determine_quality(adev->wstats.qual.level, | ||
6032 | + adev->wstats.qual.noise); | ||
6033 | +#else | ||
6034 | + qual = (adev->wstats.qual.noise <= 100) ? | ||
6035 | + 100 - adev->wstats.qual.noise : 0; | ||
6036 | +#endif | ||
6037 | + adev->wstats.qual.qual = qual; | ||
6038 | + adev->wstats.qual.updated = 7; /* all 3 indicators updated */ | ||
6039 | +#ifdef FROM_SCAN_SOURCE_ONLY | ||
6040 | + } | ||
6041 | +#endif | ||
6042 | +} | ||
6043 | + | ||
6044 | + | ||
6045 | +/*********************************************************************** | ||
6046 | +** acx_l_handle_txrate_auto | ||
6047 | +** | ||
6048 | +** Theory of operation: | ||
6049 | +** client->rate_cap is a bitmask of rates client is capable of. | ||
6050 | +** client->rate_cfg is a bitmask of allowed (configured) rates. | ||
6051 | +** It is set as a result of iwconfig rate N [auto] | ||
6052 | +** or iwpriv set_rates "N,N,N N,N,N" commands. | ||
6053 | +** It can be fixed (e.g. 0x0080 == 18Mbit only), | ||
6054 | +** auto (0x00ff == 18Mbit or any lower value), | ||
6055 | +** and code handles any bitmask (0x1081 == try 54Mbit,18Mbit,1Mbit _only_). | ||
6056 | +** | ||
6057 | +** client->rate_cur is a value for rate111 field in tx descriptor. | ||
6058 | +** It is always set to txrate_cfg sans zero or more most significant | ||
6059 | +** bits. This routine handles selection of new rate_cur value depending on | ||
6060 | +** outcome of last tx event. | ||
6061 | +** | ||
6062 | +** client->rate_100 is a precalculated rate value for acx100 | ||
6063 | +** (we can do without it, but will need to calculate it on each tx). | ||
6064 | +** | ||
6065 | +** You cannot configure mixed usage of 5.5 and/or 11Mbit rate | ||
6066 | +** with PBCC and CCK modulation. Either both at CCK or both at PBCC. | ||
6067 | +** In theory you can implement it, but so far it is considered not worth doing. | ||
6068 | +** | ||
6069 | +** 22Mbit, of course, is PBCC always. */ | ||
6070 | + | ||
6071 | +/* maps acx100 tx descr rate field to acx111 one */ | ||
6072 | +static u16 | ||
6073 | +rate100to111(u8 r) | ||
6074 | +{ | ||
6075 | + switch (r) { | ||
6076 | + case RATE100_1: return RATE111_1; | ||
6077 | + case RATE100_2: return RATE111_2; | ||
6078 | + case RATE100_5: | ||
6079 | + case (RATE100_5 | RATE100_PBCC511): return RATE111_5; | ||
6080 | + case RATE100_11: | ||
6081 | + case (RATE100_11 | RATE100_PBCC511): return RATE111_11; | ||
6082 | + case RATE100_22: return RATE111_22; | ||
6083 | + default: | ||
6084 | + printk("acx: unexpected acx100 txrate: %u! " | ||
6085 | + "Please report\n", r); | ||
6086 | + return RATE111_1; | ||
6087 | + } | ||
6088 | +} | ||
6089 | + | ||
6090 | + | ||
6091 | +void | ||
6092 | +acx_l_handle_txrate_auto(acx_device_t *adev, struct client *txc, | ||
6093 | + u16 cur, u8 rate100, u16 rate111, | ||
6094 | + u8 error, int pkts_to_ignore) | ||
6095 | +{ | ||
6096 | + u16 sent_rate; | ||
6097 | + int slower_rate_was_used; | ||
6098 | + | ||
6099 | + /* vda: hmm. current code will do this: | ||
6100 | + ** 1. send packets at 11 Mbit, stepup++ | ||
6101 | + ** 2. will try to send at 22Mbit. hardware will see no ACK, | ||
6102 | + ** retries at 11Mbit, success. code notes that used rate | ||
6103 | + ** is lower. stepup = 0, fallback++ | ||
6104 | + ** 3. repeat step 2 fallback_count times. Fall back to | ||
6105 | + ** 11Mbit. go to step 1. | ||
6106 | + ** If stepup_count is large (say, 16) and fallback_count | ||
6107 | + ** is small (3), this wouldn't be too bad wrt throughput */ | ||
6108 | + | ||
6109 | + if (unlikely(!cur)) { | ||
6110 | + printk("acx: BUG! ratemask is empty\n"); | ||
6111 | + return; /* or else we may lock up the box */ | ||
6112 | + } | ||
6113 | + | ||
6114 | + /* do some preparations, i.e. calculate the one rate that was | ||
6115 | + * used to send this packet */ | ||
6116 | + if (IS_ACX111(adev)) { | ||
6117 | + sent_rate = 1 << highest_bit(rate111 & RATE111_ALL); | ||
6118 | + } else { | ||
6119 | + sent_rate = rate100to111(rate100); | ||
6120 | + } | ||
6121 | + /* sent_rate has only one bit set now, corresponding to tx rate | ||
6122 | + * which was used by hardware to tx this particular packet */ | ||
6123 | + | ||
6124 | + /* now do the actual auto rate management */ | ||
6125 | + log(L_XFER, "tx: %sclient=%p/"MACSTR" used=%04X cur=%04X cfg=%04X " | ||
6126 | + "__=%u/%u ^^=%u/%u\n", | ||
6127 | + (txc->ignore_count > 0) ? "[IGN] " : "", | ||
6128 | + txc, MAC(txc->address), sent_rate, cur, txc->rate_cfg, | ||
6129 | + txc->fallback_count, adev->fallback_threshold, | ||
6130 | + txc->stepup_count, adev->stepup_threshold | ||
6131 | + ); | ||
6132 | + | ||
6133 | + /* we need to ignore old packets already in the tx queue since | ||
6134 | + * they use older rate bytes configured before our last rate change, | ||
6135 | + * otherwise our mechanism will get confused by interpreting old data. | ||
6136 | + * Do it after logging above */ | ||
6137 | + if (txc->ignore_count) { | ||
6138 | + txc->ignore_count--; | ||
6139 | + return; | ||
6140 | + } | ||
6141 | + | ||
6142 | + /* true only if the only nonzero bit in sent_rate is | ||
6143 | + ** less significant than highest nonzero bit in cur */ | ||
6144 | + slower_rate_was_used = ( cur > ((sent_rate<<1)-1) ); | ||
6145 | + | ||
6146 | + if (slower_rate_was_used || error) { | ||
6147 | + txc->stepup_count = 0; | ||
6148 | + if (++txc->fallback_count <= adev->fallback_threshold) | ||
6149 | + return; | ||
6150 | + txc->fallback_count = 0; | ||
6151 | + | ||
6152 | + /* clear highest 1 bit in cur */ | ||
6153 | + sent_rate = RATE111_54; | ||
6154 | + while (!(cur & sent_rate)) sent_rate >>= 1; | ||
6155 | + CLEAR_BIT(cur, sent_rate); | ||
6156 | + if (!cur) /* we can't disable all rates! */ | ||
6157 | + cur = sent_rate; | ||
6158 | + log(L_XFER, "tx: falling back to ratemask %04X\n", cur); | ||
6159 | + | ||
6160 | + } else { /* there was neither lower rate nor error */ | ||
6161 | + txc->fallback_count = 0; | ||
6162 | + if (++txc->stepup_count <= adev->stepup_threshold) | ||
6163 | + return; | ||
6164 | + txc->stepup_count = 0; | ||
6165 | + | ||
6166 | + /* Sanitize. Sort of not needed, but I dont trust hw that much... | ||
6167 | + ** what if it can report bogus tx rates sometimes? */ | ||
6168 | + while (!(cur & sent_rate)) sent_rate >>= 1; | ||
6169 | + | ||
6170 | + /* try to find a higher sent_rate that isn't yet in our | ||
6171 | + * current set, but is an allowed cfg */ | ||
6172 | + while (1) { | ||
6173 | + sent_rate <<= 1; | ||
6174 | + if (sent_rate > txc->rate_cfg) | ||
6175 | + /* no higher rates allowed by config */ | ||
6176 | + return; | ||
6177 | + if (!(cur & sent_rate) && (txc->rate_cfg & sent_rate)) | ||
6178 | + /* found */ | ||
6179 | + break; | ||
6180 | + /* not found, try higher one */ | ||
6181 | + } | ||
6182 | + SET_BIT(cur, sent_rate); | ||
6183 | + log(L_XFER, "tx: stepping up to ratemask %04X\n", cur); | ||
6184 | + } | ||
6185 | + | ||
6186 | + txc->rate_cur = cur; | ||
6187 | + txc->ignore_count = pkts_to_ignore; | ||
6188 | + /* calculate acx100 style rate byte if needed */ | ||
6189 | + if (IS_ACX100(adev)) { | ||
6190 | + txc->rate_100 = acx_bitpos2rate100[highest_bit(cur)]; | ||
6191 | + } | ||
6192 | +} | ||
6193 | + | ||
6194 | + | ||
6195 | +/*********************************************************************** | ||
6196 | +** acx_i_start_xmit | ||
6197 | +** | ||
6198 | +** Called by network core. Can be called outside of process context. | ||
6199 | +*/ | ||
6200 | +int | ||
6201 | +acx_i_start_xmit(struct sk_buff *skb, struct net_device *ndev) | ||
6202 | +{ | ||
6203 | + acx_device_t *adev = ndev2adev(ndev); | ||
6204 | + tx_t *tx; | ||
6205 | + void *txbuf; | ||
6206 | + unsigned long flags; | ||
6207 | + int txresult = NOT_OK; | ||
6208 | + int len; | ||
6209 | + | ||
6210 | + FN_ENTER; | ||
6211 | + | ||
6212 | + if (unlikely(!skb)) { | ||
6213 | + /* indicate success */ | ||
6214 | + txresult = OK; | ||
6215 | + goto end_no_unlock; | ||
6216 | + } | ||
6217 | + if (unlikely(!adev)) { | ||
6218 | + goto end_no_unlock; | ||
6219 | + } | ||
6220 | + | ||
6221 | + acx_lock(adev, flags); | ||
6222 | + | ||
6223 | + if (unlikely(!(adev->dev_state_mask & ACX_STATE_IFACE_UP))) { | ||
6224 | + goto end; | ||
6225 | + } | ||
6226 | + if (unlikely(adev->mode == ACX_MODE_OFF)) { | ||
6227 | + goto end; | ||
6228 | + } | ||
6229 | + if (unlikely(acx_queue_stopped(ndev))) { | ||
6230 | + log(L_DEBUG, "%s: called when queue stopped\n", __func__); | ||
6231 | + goto end; | ||
6232 | + } | ||
6233 | + if (unlikely(ACX_STATUS_4_ASSOCIATED != adev->status)) { | ||
6234 | + log(L_XFER, "trying to xmit, but not associated yet: " | ||
6235 | + "aborting...\n"); | ||
6236 | + /* silently drop the packet, since we're not connected yet */ | ||
6237 | + txresult = OK; | ||
6238 | + /* ...but indicate an error nevertheless */ | ||
6239 | + adev->stats.tx_errors++; | ||
6240 | + goto end; | ||
6241 | + } | ||
6242 | + | ||
6243 | + tx = acx_l_alloc_tx(adev); | ||
6244 | + if (unlikely(!tx)) { | ||
6245 | +#ifndef ACX_MEM | ||
6246 | + /* | ||
6247 | + * generic slave interface has to make do with the tiny amount, around | ||
6248 | + * 7k, of transmit buffer space on the ACX itself. It is likely this will | ||
6249 | + * frequently be full. | ||
6250 | + */ | ||
6251 | + printk_ratelimited("%s: start_xmit: txdesc ring is full, " | ||
6252 | + "dropping tx\n", ndev->name); | ||
6253 | +#endif | ||
6254 | + txresult = NOT_OK; | ||
6255 | + goto end; | ||
6256 | + } | ||
6257 | + | ||
6258 | + txbuf = acx_l_get_txbuf(adev, tx); | ||
6259 | + if (unlikely(!txbuf)) { | ||
6260 | + /* Card was removed */ | ||
6261 | + txresult = NOT_OK; | ||
6262 | + acx_l_dealloc_tx(adev, tx); | ||
6263 | + goto end; | ||
6264 | + } | ||
6265 | + len = acx_ether_to_txbuf(adev, txbuf, skb); | ||
6266 | + if (unlikely(len < 0)) { | ||
6267 | + /* Error in packet conversion */ | ||
6268 | + txresult = NOT_OK; | ||
6269 | + acx_l_dealloc_tx(adev, tx); | ||
6270 | + goto end; | ||
6271 | + } | ||
6272 | + acx_l_tx_data(adev, tx, len); | ||
6273 | + ndev->trans_start = jiffies; | ||
6274 | + | ||
6275 | + txresult = OK; | ||
6276 | + adev->stats.tx_packets++; | ||
6277 | + adev->stats.tx_bytes += skb->len; | ||
6278 | + | ||
6279 | +end: | ||
6280 | + acx_unlock(adev, flags); | ||
6281 | + | ||
6282 | +end_no_unlock: | ||
6283 | + if ((txresult == OK) && skb) | ||
6284 | + dev_kfree_skb_any(skb); | ||
6285 | + | ||
6286 | + FN_EXIT1(txresult); | ||
6287 | + return txresult; | ||
6288 | +} | ||
6289 | + | ||
6290 | + | ||
6291 | +/*********************************************************************** | ||
6292 | +** acx_l_update_ratevector | ||
6293 | +** | ||
6294 | +** Updates adev->rate_supported[_len] according to rate_{basic,oper} | ||
6295 | +*/ | ||
6296 | +const u8 | ||
6297 | +acx_bitpos2ratebyte[] = { | ||
6298 | + DOT11RATEBYTE_1, | ||
6299 | + DOT11RATEBYTE_2, | ||
6300 | + DOT11RATEBYTE_5_5, | ||
6301 | + DOT11RATEBYTE_6_G, | ||
6302 | + DOT11RATEBYTE_9_G, | ||
6303 | + DOT11RATEBYTE_11, | ||
6304 | + DOT11RATEBYTE_12_G, | ||
6305 | + DOT11RATEBYTE_18_G, | ||
6306 | + DOT11RATEBYTE_22, | ||
6307 | + DOT11RATEBYTE_24_G, | ||
6308 | + DOT11RATEBYTE_36_G, | ||
6309 | + DOT11RATEBYTE_48_G, | ||
6310 | + DOT11RATEBYTE_54_G, | ||
6311 | +}; | ||
6312 | + | ||
6313 | +void | ||
6314 | +acx_l_update_ratevector(acx_device_t *adev) | ||
6315 | +{ | ||
6316 | + u16 bcfg = adev->rate_basic; | ||
6317 | + u16 ocfg = adev->rate_oper; | ||
6318 | + u8 *supp = adev->rate_supported; | ||
6319 | + const u8 *dot11 = acx_bitpos2ratebyte; | ||
6320 | + | ||
6321 | + FN_ENTER; | ||
6322 | + | ||
6323 | + while (ocfg) { | ||
6324 | + if (ocfg & 1) { | ||
6325 | + *supp = *dot11; | ||
6326 | + if (bcfg & 1) { | ||
6327 | + *supp |= 0x80; | ||
6328 | + } | ||
6329 | + supp++; | ||
6330 | + } | ||
6331 | + dot11++; | ||
6332 | + ocfg >>= 1; | ||
6333 | + bcfg >>= 1; | ||
6334 | + } | ||
6335 | + adev->rate_supported_len = supp - adev->rate_supported; | ||
6336 | + if (acx_debug & L_ASSOC) { | ||
6337 | + printk("new ratevector: "); | ||
6338 | + acx_dump_bytes(adev->rate_supported, adev->rate_supported_len); | ||
6339 | + } | ||
6340 | + FN_EXIT0; | ||
6341 | +} | ||
6342 | + | ||
6343 | + | ||
6344 | +/*********************************************************************** | ||
6345 | +** acx_l_sta_list_init | ||
6346 | +*/ | ||
6347 | +static void | ||
6348 | +acx_l_sta_list_init(acx_device_t *adev) | ||
6349 | +{ | ||
6350 | + FN_ENTER; | ||
6351 | + memset(adev->sta_hash_tab, 0, sizeof(adev->sta_hash_tab)); | ||
6352 | + memset(adev->sta_list, 0, sizeof(adev->sta_list)); | ||
6353 | + FN_EXIT0; | ||
6354 | +} | ||
6355 | + | ||
6356 | + | ||
6357 | +/*********************************************************************** | ||
6358 | +** acx_l_sta_list_get_from_hash | ||
6359 | +*/ | ||
6360 | +static inline client_t* | ||
6361 | +acx_l_sta_list_get_from_hash(acx_device_t *adev, const u8 *address) | ||
6362 | +{ | ||
6363 | + return adev->sta_hash_tab[address[5] % VEC_SIZE(adev->sta_hash_tab)]; | ||
6364 | +} | ||
6365 | + | ||
6366 | + | ||
6367 | +/*********************************************************************** | ||
6368 | +** acx_l_sta_list_get | ||
6369 | +*/ | ||
6370 | +client_t* | ||
6371 | +acx_l_sta_list_get(acx_device_t *adev, const u8 *address) | ||
6372 | +{ | ||
6373 | + client_t *client; | ||
6374 | + FN_ENTER; | ||
6375 | + client = acx_l_sta_list_get_from_hash(adev, address); | ||
6376 | + while (client) { | ||
6377 | + if (mac_is_equal(address, client->address)) { | ||
6378 | + client->mtime = jiffies; | ||
6379 | + break; | ||
6380 | + } | ||
6381 | + client = client->next; | ||
6382 | + } | ||
6383 | + FN_EXIT0; | ||
6384 | + return client; | ||
6385 | +} | ||
6386 | + | ||
6387 | + | ||
6388 | +/*********************************************************************** | ||
6389 | +** acx_l_sta_list_del | ||
6390 | +*/ | ||
6391 | +void | ||
6392 | +acx_l_sta_list_del(acx_device_t *adev, client_t *victim) | ||
6393 | +{ | ||
6394 | + client_t *client, *next; | ||
6395 | + | ||
6396 | + client = acx_l_sta_list_get_from_hash(adev, victim->address); | ||
6397 | + next = client; | ||
6398 | + /* tricky. next = client on first iteration only, | ||
6399 | + ** on all other iters next = client->next */ | ||
6400 | + while (next) { | ||
6401 | + if (next == victim) { | ||
6402 | + client->next = victim->next; | ||
6403 | + /* Overkill */ | ||
6404 | + memset(victim, 0, sizeof(*victim)); | ||
6405 | + break; | ||
6406 | + } | ||
6407 | + client = next; | ||
6408 | + next = client->next; | ||
6409 | + } | ||
6410 | +} | ||
6411 | + | ||
6412 | + | ||
6413 | +/*********************************************************************** | ||
6414 | +** acx_l_sta_list_alloc | ||
6415 | +** | ||
6416 | +** Never fails - will evict oldest client if needed | ||
6417 | +*/ | ||
6418 | +static client_t* | ||
6419 | +acx_l_sta_list_alloc(acx_device_t *adev) | ||
6420 | +{ | ||
6421 | + int i; | ||
6422 | + unsigned long age, oldest_age; | ||
6423 | + client_t *client, *oldest; | ||
6424 | + | ||
6425 | + FN_ENTER; | ||
6426 | + | ||
6427 | + oldest = &adev->sta_list[0]; | ||
6428 | + oldest_age = 0; | ||
6429 | + for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { | ||
6430 | + client = &adev->sta_list[i]; | ||
6431 | + | ||
6432 | + if (!client->used) { | ||
6433 | + goto found; | ||
6434 | + } else { | ||
6435 | + age = jiffies - client->mtime; | ||
6436 | + if (oldest_age < age) { | ||
6437 | + oldest_age = age; | ||
6438 | + oldest = client; | ||
6439 | + } | ||
6440 | + } | ||
6441 | + } | ||
6442 | + acx_l_sta_list_del(adev, oldest); | ||
6443 | + client = oldest; | ||
6444 | +found: | ||
6445 | + memset(client, 0, sizeof(*client)); | ||
6446 | + FN_EXIT0; | ||
6447 | + return client; | ||
6448 | +} | ||
6449 | + | ||
6450 | + | ||
6451 | +/*********************************************************************** | ||
6452 | +** acx_l_sta_list_add | ||
6453 | +** | ||
6454 | +** Never fails - will evict oldest client if needed | ||
6455 | +*/ | ||
6456 | +/* In case we will reimplement it differently... */ | ||
6457 | +#define STA_LIST_ADD_CAN_FAIL 0 | ||
6458 | + | ||
6459 | +static client_t* | ||
6460 | +acx_l_sta_list_add(acx_device_t *adev, const u8 *address) | ||
6461 | +{ | ||
6462 | + client_t *client; | ||
6463 | + int index; | ||
6464 | + | ||
6465 | + FN_ENTER; | ||
6466 | + | ||
6467 | + client = acx_l_sta_list_alloc(adev); | ||
6468 | + | ||
6469 | + client->mtime = jiffies; | ||
6470 | + MAC_COPY(client->address, address); | ||
6471 | + client->used = CLIENT_EXIST_1; | ||
6472 | + client->auth_alg = WLAN_AUTH_ALG_SHAREDKEY; | ||
6473 | + client->auth_step = 1; | ||
6474 | + /* give some tentative peer rate values | ||
6475 | + ** (needed because peer may do auth without probing us first, | ||
6476 | + ** thus we'll have no idea of peer's ratevector yet). | ||
6477 | + ** Will be overwritten by scanning or assoc code */ | ||
6478 | + client->rate_cap = adev->rate_basic; | ||
6479 | + client->rate_cfg = adev->rate_basic; | ||
6480 | + client->rate_cur = 1 << lowest_bit(adev->rate_basic); | ||
6481 | + | ||
6482 | + index = address[5] % VEC_SIZE(adev->sta_hash_tab); | ||
6483 | + client->next = adev->sta_hash_tab[index]; | ||
6484 | + adev->sta_hash_tab[index] = client; | ||
6485 | + | ||
6486 | + acxlog_mac(L_ASSOC, "sta_list_add: sta=", address, "\n"); | ||
6487 | + | ||
6488 | + FN_EXIT0; | ||
6489 | + return client; | ||
6490 | +} | ||
6491 | + | ||
6492 | + | ||
6493 | +/*********************************************************************** | ||
6494 | +** acx_l_sta_list_get_or_add | ||
6495 | +** | ||
6496 | +** Never fails - will evict oldest client if needed | ||
6497 | +*/ | ||
6498 | +static client_t* | ||
6499 | +acx_l_sta_list_get_or_add(acx_device_t *adev, const u8 *address) | ||
6500 | +{ | ||
6501 | + client_t *client = acx_l_sta_list_get(adev, address); | ||
6502 | + if (!client) | ||
6503 | + client = acx_l_sta_list_add(adev, address); | ||
6504 | + return client; | ||
6505 | +} | ||
6506 | + | ||
6507 | + | ||
6508 | +/*********************************************************************** | ||
6509 | +** acx_set_status | ||
6510 | +** | ||
6511 | +** This function is called in many atomic regions, must not sleep | ||
6512 | +** | ||
6513 | +** This function does not need locking UNLESS you call it | ||
6514 | +** as acx_set_status(ACX_STATUS_4_ASSOCIATED), bacause this can | ||
6515 | +** wake queue. This can race with stop_queue elsewhere. | ||
6516 | +** See acx_stop_queue comment. */ | ||
6517 | +void | ||
6518 | +acx_set_status(acx_device_t *adev, u16 new_status) | ||
6519 | +{ | ||
6520 | +#define QUEUE_OPEN_AFTER_ASSOC 1 /* this really seems to be needed now */ | ||
6521 | + u16 old_status = adev->status; | ||
6522 | + | ||
6523 | + FN_ENTER; | ||
6524 | + | ||
6525 | + log(L_ASSOC, "%s(%d):%s\n", | ||
6526 | + __func__, new_status, acx_get_status_name(new_status)); | ||
6527 | + | ||
6528 | + /* wireless_send_event never sleeps */ | ||
6529 | + if (ACX_STATUS_4_ASSOCIATED == new_status) { | ||
6530 | + union iwreq_data wrqu; | ||
6531 | + | ||
6532 | + wrqu.data.length = 0; | ||
6533 | + wrqu.data.flags = 0; | ||
6534 | + wireless_send_event(adev->ndev, SIOCGIWSCAN, &wrqu, NULL); | ||
6535 | + | ||
6536 | + wrqu.data.length = 0; | ||
6537 | + wrqu.data.flags = 0; | ||
6538 | + MAC_COPY(wrqu.ap_addr.sa_data, adev->bssid); | ||
6539 | + wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
6540 | + wireless_send_event(adev->ndev, SIOCGIWAP, &wrqu, NULL); | ||
6541 | + } else { | ||
6542 | + union iwreq_data wrqu; | ||
6543 | + | ||
6544 | + /* send event with empty BSSID to indicate we're not associated */ | ||
6545 | + MAC_ZERO(wrqu.ap_addr.sa_data); | ||
6546 | + wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
6547 | + wireless_send_event(adev->ndev, SIOCGIWAP, &wrqu, NULL); | ||
6548 | + } | ||
6549 | + | ||
6550 | + adev->status = new_status; | ||
6551 | + | ||
6552 | + switch (new_status) { | ||
6553 | + case ACX_STATUS_1_SCANNING: | ||
6554 | + adev->scan_retries = 0; | ||
6555 | + /* 1.0 s initial scan time */ | ||
6556 | + acx_set_timer(adev, 1000000); | ||
6557 | + break; | ||
6558 | + case ACX_STATUS_2_WAIT_AUTH: | ||
6559 | + case ACX_STATUS_3_AUTHENTICATED: | ||
6560 | + adev->auth_or_assoc_retries = 0; | ||
6561 | + acx_set_timer(adev, 1500000); /* 1.5 s */ | ||
6562 | + break; | ||
6563 | + } | ||
6564 | + | ||
6565 | +#if QUEUE_OPEN_AFTER_ASSOC | ||
6566 | + if (new_status == ACX_STATUS_4_ASSOCIATED) { | ||
6567 | + if (old_status < ACX_STATUS_4_ASSOCIATED) { | ||
6568 | + /* ah, we're newly associated now, | ||
6569 | + * so let's indicate carrier */ | ||
6570 | + acx_carrier_on(adev->ndev, "after association"); | ||
6571 | + acx_wake_queue(adev->ndev, "after association"); | ||
6572 | + } | ||
6573 | + } else { | ||
6574 | + /* not associated any more, so let's kill carrier */ | ||
6575 | + if (old_status >= ACX_STATUS_4_ASSOCIATED) { | ||
6576 | + acx_carrier_off(adev->ndev, "after losing association"); | ||
6577 | + acx_stop_queue(adev->ndev, "after losing association"); | ||
6578 | + } | ||
6579 | + } | ||
6580 | +#endif | ||
6581 | + FN_EXIT0; | ||
6582 | +} | ||
6583 | + | ||
6584 | + | ||
6585 | +/*********************************************************************** | ||
6586 | +** acx_i_timer | ||
6587 | +** | ||
6588 | +** Fires up periodically. Used to kick scan/auth/assoc if something goes wrong | ||
6589 | +*/ | ||
6590 | +void | ||
6591 | +acx_i_timer(unsigned long address) | ||
6592 | +{ | ||
6593 | + unsigned long flags; | ||
6594 | + acx_device_t *adev = (acx_device_t*)address; | ||
6595 | + | ||
6596 | + FN_ENTER; | ||
6597 | + | ||
6598 | + acx_lock(adev, flags); | ||
6599 | + | ||
6600 | + log(L_DEBUG|L_ASSOC, "%s: adev->status=%d (%s)\n", | ||
6601 | + __func__, adev->status, acx_get_status_name(adev->status)); | ||
6602 | + | ||
6603 | + switch (adev->status) { | ||
6604 | + case ACX_STATUS_1_SCANNING: | ||
6605 | + /* was set to 0 by set_status() */ | ||
6606 | + if (++adev->scan_retries < 7) { | ||
6607 | + acx_set_timer(adev, 1000000); | ||
6608 | + /* used to interrogate for scan status. | ||
6609 | + ** We rely on SCAN_COMPLETE IRQ instead */ | ||
6610 | + log(L_ASSOC, "continuing scan (%d sec)\n", | ||
6611 | + adev->scan_retries); | ||
6612 | + } else { | ||
6613 | + log(L_ASSOC, "stopping scan\n"); | ||
6614 | + /* send stop_scan cmd when we leave the interrupt context, | ||
6615 | + * and make a decision what to do next (COMPLETE_SCAN) */ | ||
6616 | + acx_schedule_task(adev, | ||
6617 | + ACX_AFTER_IRQ_CMD_STOP_SCAN + ACX_AFTER_IRQ_COMPLETE_SCAN); | ||
6618 | + } | ||
6619 | + break; | ||
6620 | + case ACX_STATUS_2_WAIT_AUTH: | ||
6621 | + /* was set to 0 by set_status() */ | ||
6622 | + if (++adev->auth_or_assoc_retries < 10) { | ||
6623 | + log(L_ASSOC, "resend authen1 request (attempt %d)\n", | ||
6624 | + adev->auth_or_assoc_retries + 1); | ||
6625 | + acx_l_transmit_authen1(adev); | ||
6626 | + } else { | ||
6627 | + /* time exceeded: fall back to scanning mode */ | ||
6628 | + log(L_ASSOC, | ||
6629 | + "authen1 request reply timeout, giving up\n"); | ||
6630 | + /* we are a STA, need to find AP anyhow */ | ||
6631 | + acx_set_status(adev, ACX_STATUS_1_SCANNING); | ||
6632 | + acx_schedule_task(adev, ACX_AFTER_IRQ_RESTART_SCAN); | ||
6633 | + } | ||
6634 | + /* used to be 1500000, but some other driver uses 2.5s */ | ||
6635 | + acx_set_timer(adev, 2500000); | ||
6636 | + break; | ||
6637 | + case ACX_STATUS_3_AUTHENTICATED: | ||
6638 | + /* was set to 0 by set_status() */ | ||
6639 | + if (++adev->auth_or_assoc_retries < 10) { | ||
6640 | + log(L_ASSOC, "resend assoc request (attempt %d)\n", | ||
6641 | + adev->auth_or_assoc_retries + 1); | ||
6642 | + acx_l_transmit_assoc_req(adev); | ||
6643 | + } else { | ||
6644 | + /* time exceeded: give up */ | ||
6645 | + log(L_ASSOC, | ||
6646 | + "association request reply timeout, giving up\n"); | ||
6647 | + /* we are a STA, need to find AP anyhow */ | ||
6648 | + acx_set_status(adev, ACX_STATUS_1_SCANNING); | ||
6649 | + acx_schedule_task(adev, ACX_AFTER_IRQ_RESTART_SCAN); | ||
6650 | + } | ||
6651 | + acx_set_timer(adev, 2500000); /* see above */ | ||
6652 | + break; | ||
6653 | + case ACX_STATUS_4_ASSOCIATED: | ||
6654 | + default: | ||
6655 | + break; | ||
6656 | + } | ||
6657 | + | ||
6658 | + acx_unlock(adev, flags); | ||
6659 | + | ||
6660 | + FN_EXIT0; | ||
6661 | +} | ||
6662 | + | ||
6663 | + | ||
6664 | +/*********************************************************************** | ||
6665 | +** acx_set_timer | ||
6666 | +** | ||
6667 | +** Sets the 802.11 state management timer's timeout. | ||
6668 | +*/ | ||
6669 | +void | ||
6670 | +acx_set_timer(acx_device_t *adev, int timeout_us) | ||
6671 | +{ | ||
6672 | + FN_ENTER; | ||
6673 | + | ||
6674 | + log(L_DEBUG|L_IRQ, "%s(%u ms)\n", __func__, timeout_us/1000); | ||
6675 | + if (!(adev->dev_state_mask & ACX_STATE_IFACE_UP)) { | ||
6676 | + printk("attempt to set the timer " | ||
6677 | + "when the card interface is not up!\n"); | ||
6678 | + goto end; | ||
6679 | + } | ||
6680 | + | ||
6681 | + /* first check if the timer was already initialized, THEN modify it */ | ||
6682 | + if (adev->mgmt_timer.function) { | ||
6683 | + mod_timer(&adev->mgmt_timer, | ||
6684 | + jiffies + (timeout_us * HZ / 1000000)); | ||
6685 | + } | ||
6686 | +end: | ||
6687 | + FN_EXIT0; | ||
6688 | +} | ||
6689 | + | ||
6690 | + | ||
6691 | +/*********************************************************************** | ||
6692 | +** acx_l_transmit_assocresp | ||
6693 | +** | ||
6694 | +** We are an AP here | ||
6695 | +*/ | ||
6696 | +static const u8 | ||
6697 | +dot11ratebyte[] = { | ||
6698 | + DOT11RATEBYTE_1, | ||
6699 | + DOT11RATEBYTE_2, | ||
6700 | + DOT11RATEBYTE_5_5, | ||
6701 | + DOT11RATEBYTE_6_G, | ||
6702 | + DOT11RATEBYTE_9_G, | ||
6703 | + DOT11RATEBYTE_11, | ||
6704 | + DOT11RATEBYTE_12_G, | ||
6705 | + DOT11RATEBYTE_18_G, | ||
6706 | + DOT11RATEBYTE_22, | ||
6707 | + DOT11RATEBYTE_24_G, | ||
6708 | + DOT11RATEBYTE_36_G, | ||
6709 | + DOT11RATEBYTE_48_G, | ||
6710 | + DOT11RATEBYTE_54_G, | ||
6711 | +}; | ||
6712 | + | ||
6713 | +static inline int | ||
6714 | +find_pos(const u8 *p, int size, u8 v) | ||
6715 | +{ | ||
6716 | + int i; | ||
6717 | + for (i = 0; i < size; i++) | ||
6718 | + if (p[i] == v) | ||
6719 | + return i; | ||
6720 | + /* printk a message about strange byte? */ | ||
6721 | + return 0; | ||
6722 | +} | ||
6723 | + | ||
6724 | +static void | ||
6725 | +add_bits_to_ratemasks(u8* ratevec, int len, u16* brate, u16* orate) | ||
6726 | +{ | ||
6727 | + while (len--) { | ||
6728 | + int n = 1 << find_pos(dot11ratebyte, | ||
6729 | + sizeof(dot11ratebyte), *ratevec & 0x7f); | ||
6730 | + if (*ratevec & 0x80) | ||
6731 | + *brate |= n; | ||
6732 | + *orate |= n; | ||
6733 | + ratevec++; | ||
6734 | + } | ||
6735 | +} | ||
6736 | + | ||
6737 | +static int | ||
6738 | +acx_l_transmit_assocresp(acx_device_t *adev, const wlan_fr_assocreq_t *req) | ||
6739 | +{ | ||
6740 | + struct tx *tx; | ||
6741 | + struct wlan_hdr_mgmt *head; | ||
6742 | + struct assocresp_frame_body *body; | ||
6743 | + u8 *p; | ||
6744 | + const u8 *da; | ||
6745 | + /* const u8 *sa; */ | ||
6746 | + const u8 *bssid; | ||
6747 | + client_t *clt; | ||
6748 | + | ||
6749 | + FN_ENTER; | ||
6750 | + | ||
6751 | + /* sa = req->hdr->a1; */ | ||
6752 | + da = req->hdr->a2; | ||
6753 | + bssid = req->hdr->a3; | ||
6754 | + | ||
6755 | + clt = acx_l_sta_list_get(adev, da); | ||
6756 | + if (!clt) | ||
6757 | + goto ok; | ||
6758 | + | ||
6759 | + /* Assoc without auth is a big no-no */ | ||
6760 | + /* Let's be liberal: if already assoc'ed STA sends assoc req again, | ||
6761 | + ** we won't be rude */ | ||
6762 | + if (clt->used != CLIENT_AUTHENTICATED_2 | ||
6763 | + && clt->used != CLIENT_ASSOCIATED_3) { | ||
6764 | + acx_l_transmit_deauthen(adev, da, WLAN_MGMT_REASON_CLASS2_NONAUTH); | ||
6765 | + goto bad; | ||
6766 | + } | ||
6767 | + | ||
6768 | + clt->used = CLIENT_ASSOCIATED_3; | ||
6769 | + | ||
6770 | + if (clt->aid == 0) | ||
6771 | + clt->aid = ++adev->aid; | ||
6772 | + clt->cap_info = ieee2host16(*(req->cap_info)); | ||
6773 | + | ||
6774 | + /* We cheat here a bit. We don't really care which rates are flagged | ||
6775 | + ** as basic by the client, so we stuff them in single ratemask */ | ||
6776 | + clt->rate_cap = 0; | ||
6777 | + if (req->supp_rates) | ||
6778 | + add_bits_to_ratemasks(req->supp_rates->rates, | ||
6779 | + req->supp_rates->len, &clt->rate_cap, &clt->rate_cap); | ||
6780 | + if (req->ext_rates) | ||
6781 | + add_bits_to_ratemasks(req->ext_rates->rates, | ||
6782 | + req->ext_rates->len, &clt->rate_cap, &clt->rate_cap); | ||
6783 | + /* We can check that client supports all basic rates, | ||
6784 | + ** and deny assoc if not. But let's be liberal, right? ;) */ | ||
6785 | + clt->rate_cfg = clt->rate_cap & adev->rate_oper; | ||
6786 | + if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(adev->rate_oper); | ||
6787 | + clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); | ||
6788 | + if (IS_ACX100(adev)) | ||
6789 | + clt->rate_100 = acx_bitpos2rate100[lowest_bit(clt->rate_cfg)]; | ||
6790 | + clt->fallback_count = clt->stepup_count = 0; | ||
6791 | + clt->ignore_count = 16; | ||
6792 | + | ||
6793 | + tx = acx_l_alloc_tx(adev); | ||
6794 | + if (!tx) | ||
6795 | + goto bad; | ||
6796 | + head = acx_l_get_txbuf(adev, tx); | ||
6797 | + if (!head) { | ||
6798 | + acx_l_dealloc_tx(adev, tx); | ||
6799 | + goto bad; | ||
6800 | + } | ||
6801 | + body = (void*)(head + 1); | ||
6802 | + | ||
6803 | + head->fc = WF_FSTYPE_ASSOCRESPi; | ||
6804 | + head->dur = req->hdr->dur; | ||
6805 | + MAC_COPY(head->da, da); | ||
6806 | + MAC_COPY(head->sa, adev->dev_addr); | ||
6807 | + MAC_COPY(head->bssid, bssid); | ||
6808 | + head->seq = req->hdr->seq; | ||
6809 | + | ||
6810 | + body->cap_info = host2ieee16(adev->capabilities); | ||
6811 | + body->status = host2ieee16(0); | ||
6812 | + body->aid = host2ieee16(clt->aid); | ||
6813 | + p = wlan_fill_ie_rates((u8*)&body->rates, adev->rate_supported_len, | ||
6814 | + adev->rate_supported); | ||
6815 | + p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, | ||
6816 | + adev->rate_supported); | ||
6817 | + | ||
6818 | + acx_l_tx_data(adev, tx, p - (u8*)head); | ||
6819 | +ok: | ||
6820 | + FN_EXIT1(OK); | ||
6821 | + return OK; | ||
6822 | +bad: | ||
6823 | + FN_EXIT1(NOT_OK); | ||
6824 | + return NOT_OK; | ||
6825 | +} | ||
6826 | + | ||
6827 | + | ||
6828 | +/*********************************************************************** | ||
6829 | +* acx_l_transmit_reassocresp | ||
6830 | + | ||
6831 | +You may be wondering, just like me, what the hell ReAuth is. | ||
6832 | +In practice it was seen sent by STA when STA feels like losing connection. | ||
6833 | + | ||
6834 | +[802.11] | ||
6835 | + | ||
6836 | +5.4.2.3 Reassociation | ||
6837 | + | ||
6838 | +Association is sufficient for no-transition message delivery between | ||
6839 | +IEEE 802.11 stations. Additional functionality is needed to support | ||
6840 | +BSS-transition mobility. The additional required functionality | ||
6841 | +is provided by the reassociation service. Reassociation is a DSS. | ||
6842 | +The reassociation service is invoked to 'move' a current association | ||
6843 | +from one AP to another. This keeps the DS informed of the current | ||
6844 | +mapping between AP and STA as the station moves from BSS to BSS within | ||
6845 | +an ESS. Reassociation also enables changing association attributes | ||
6846 | +of an established association while the STA remains associated with | ||
6847 | +the same AP. Reassociation is always initiated by the mobile STA. | ||
6848 | + | ||
6849 | +5.4.3.1 Authentication | ||
6850 | +... | ||
6851 | +A STA may be authenticated with many other STAs at any given instant. | ||
6852 | + | ||
6853 | +5.4.3.1.1 Preauthentication | ||
6854 | + | ||
6855 | +Because the authentication process could be time-consuming (depending | ||
6856 | +on the authentication protocol in use), the authentication service can | ||
6857 | +be invoked independently of the association service. Preauthentication | ||
6858 | +is typically done by a STA while it is already associated with an AP | ||
6859 | +(with which it previously authenticated). IEEE 802.11 does not require | ||
6860 | +that STAs preauthenticate with APs. However, authentication is required | ||
6861 | +before an association can be established. If the authentication is left | ||
6862 | +until reassociation time, this may impact the speed with which a STA can | ||
6863 | +reassociate between APs, limiting BSS-transition mobility performance. | ||
6864 | +The use of preauthentication takes the authentication service overhead | ||
6865 | +out of the time-critical reassociation process. | ||
6866 | + | ||
6867 | +5.7.3 Reassociation | ||
6868 | + | ||
6869 | +For a STA to reassociate, the reassociation service causes the following | ||
6870 | +message to occur: | ||
6871 | + | ||
6872 | + Reassociation request | ||
6873 | + | ||
6874 | +* Message type: Management | ||
6875 | +* Message subtype: Reassociation request | ||
6876 | +* Information items: | ||
6877 | + - IEEE address of the STA | ||
6878 | + - IEEE address of the AP with which the STA will reassociate | ||
6879 | + - IEEE address of the AP with which the STA is currently associated | ||
6880 | + - ESSID | ||
6881 | +* Direction of message: From STA to 'new' AP | ||
6882 | + | ||
6883 | +The address of the current AP is included for efficiency. The inclusion | ||
6884 | +of the current AP address facilitates MAC reassociation to be independent | ||
6885 | +of the DS implementation. | ||
6886 | + | ||
6887 | + Reassociation response | ||
6888 | +* Message type: Management | ||
6889 | +* Message subtype: Reassociation response | ||
6890 | +* Information items: | ||
6891 | + - Result of the requested reassociation. (success/failure) | ||
6892 | + - If the reassociation is successful, the response shall include the AID. | ||
6893 | +* Direction of message: From AP to STA | ||
6894 | + | ||
6895 | +7.2.3.6 Reassociation Request frame format | ||
6896 | + | ||
6897 | +The frame body of a management frame of subtype Reassociation Request | ||
6898 | +contains the information shown in Table 9. | ||
6899 | + | ||
6900 | +Table 9 Reassociation Request frame body | ||
6901 | +Order Information | ||
6902 | +1 Capability information | ||
6903 | +2 Listen interval | ||
6904 | +3 Current AP address | ||
6905 | +4 SSID | ||
6906 | +5 Supported rates | ||
6907 | + | ||
6908 | +7.2.3.7 Reassociation Response frame format | ||
6909 | + | ||
6910 | +The frame body of a management frame of subtype Reassociation Response | ||
6911 | +contains the information shown in Table 10. | ||
6912 | + | ||
6913 | +Table 10 Reassociation Response frame body | ||
6914 | +Order Information | ||
6915 | +1 Capability information | ||
6916 | +2 Status code | ||
6917 | +3 Association ID (AID) | ||
6918 | +4 Supported rates | ||
6919 | + | ||
6920 | +*/ | ||
6921 | +static int | ||
6922 | +acx_l_transmit_reassocresp(acx_device_t *adev, const wlan_fr_reassocreq_t *req) | ||
6923 | +{ | ||
6924 | + struct tx *tx; | ||
6925 | + struct wlan_hdr_mgmt *head; | ||
6926 | + struct reassocresp_frame_body *body; | ||
6927 | + u8 *p; | ||
6928 | + const u8 *da; | ||
6929 | + /* const u8 *sa; */ | ||
6930 | + const u8 *bssid; | ||
6931 | + client_t *clt; | ||
6932 | + | ||
6933 | + FN_ENTER; | ||
6934 | + | ||
6935 | + /* sa = req->hdr->a1; */ | ||
6936 | + da = req->hdr->a2; | ||
6937 | + bssid = req->hdr->a3; | ||
6938 | + | ||
6939 | + /* Must be already authenticated, so it must be in the list */ | ||
6940 | + clt = acx_l_sta_list_get(adev, da); | ||
6941 | + if (!clt) | ||
6942 | + goto ok; | ||
6943 | + | ||
6944 | + /* Assoc without auth is a big no-no */ | ||
6945 | + /* Already assoc'ed STAs sending ReAssoc req are ok per 802.11 */ | ||
6946 | + if (clt->used != CLIENT_AUTHENTICATED_2 | ||
6947 | + && clt->used != CLIENT_ASSOCIATED_3) { | ||
6948 | + acx_l_transmit_deauthen(adev, da, WLAN_MGMT_REASON_CLASS2_NONAUTH); | ||
6949 | + goto bad; | ||
6950 | + } | ||
6951 | + | ||
6952 | + clt->used = CLIENT_ASSOCIATED_3; | ||
6953 | + if (clt->aid == 0) { | ||
6954 | + clt->aid = ++adev->aid; | ||
6955 | + } | ||
6956 | + if (req->cap_info) | ||
6957 | + clt->cap_info = ieee2host16(*(req->cap_info)); | ||
6958 | + | ||
6959 | + /* We cheat here a bit. We don't really care which rates are flagged | ||
6960 | + ** as basic by the client, so we stuff them in single ratemask */ | ||
6961 | + clt->rate_cap = 0; | ||
6962 | + if (req->supp_rates) | ||
6963 | + add_bits_to_ratemasks(req->supp_rates->rates, | ||
6964 | + req->supp_rates->len, &clt->rate_cap, &clt->rate_cap); | ||
6965 | + if (req->ext_rates) | ||
6966 | + add_bits_to_ratemasks(req->ext_rates->rates, | ||
6967 | + req->ext_rates->len, &clt->rate_cap, &clt->rate_cap); | ||
6968 | + /* We can check that client supports all basic rates, | ||
6969 | + ** and deny assoc if not. But let's be liberal, right? ;) */ | ||
6970 | + clt->rate_cfg = clt->rate_cap & adev->rate_oper; | ||
6971 | + if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(adev->rate_oper); | ||
6972 | + clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); | ||
6973 | + if (IS_ACX100(adev)) | ||
6974 | + clt->rate_100 = acx_bitpos2rate100[lowest_bit(clt->rate_cfg)]; | ||
6975 | + | ||
6976 | + clt->fallback_count = clt->stepup_count = 0; | ||
6977 | + clt->ignore_count = 16; | ||
6978 | + | ||
6979 | + tx = acx_l_alloc_tx(adev); | ||
6980 | + if (!tx) | ||
6981 | + goto ok; | ||
6982 | + head = acx_l_get_txbuf(adev, tx); | ||
6983 | + if (!head) { | ||
6984 | + acx_l_dealloc_tx(adev, tx); | ||
6985 | + goto ok; | ||
6986 | + } | ||
6987 | + body = (void*)(head + 1); | ||
6988 | + | ||
6989 | + head->fc = WF_FSTYPE_REASSOCRESPi; | ||
6990 | + head->dur = req->hdr->dur; | ||
6991 | + MAC_COPY(head->da, da); | ||
6992 | + MAC_COPY(head->sa, adev->dev_addr); | ||
6993 | + MAC_COPY(head->bssid, bssid); | ||
6994 | + head->seq = req->hdr->seq; | ||
6995 | + | ||
6996 | + /* IEs: 1. caps */ | ||
6997 | + body->cap_info = host2ieee16(adev->capabilities); | ||
6998 | + /* 2. status code */ | ||
6999 | + body->status = host2ieee16(0); | ||
7000 | + /* 3. AID */ | ||
7001 | + body->aid = host2ieee16(clt->aid); | ||
7002 | + /* 4. supp rates */ | ||
7003 | + p = wlan_fill_ie_rates((u8*)&body->rates, adev->rate_supported_len, | ||
7004 | + adev->rate_supported); | ||
7005 | + /* 5. ext supp rates */ | ||
7006 | + p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, | ||
7007 | + adev->rate_supported); | ||
7008 | + | ||
7009 | + acx_l_tx_data(adev, tx, p - (u8*)head); | ||
7010 | +ok: | ||
7011 | + FN_EXIT1(OK); | ||
7012 | + return OK; | ||
7013 | +bad: | ||
7014 | + FN_EXIT1(NOT_OK); | ||
7015 | + return NOT_OK; | ||
7016 | +} | ||
7017 | + | ||
7018 | + | ||
7019 | +/*********************************************************************** | ||
7020 | +** acx_l_process_disassoc_from_sta | ||
7021 | +*/ | ||
7022 | +static void | ||
7023 | +acx_l_process_disassoc_from_sta(acx_device_t *adev, const wlan_fr_disassoc_t *req) | ||
7024 | +{ | ||
7025 | + const u8 *ta; | ||
7026 | + client_t *clt; | ||
7027 | + | ||
7028 | + FN_ENTER; | ||
7029 | + | ||
7030 | + ta = req->hdr->a2; | ||
7031 | + clt = acx_l_sta_list_get(adev, ta); | ||
7032 | + if (!clt) | ||
7033 | + goto end; | ||
7034 | + | ||
7035 | + if (clt->used != CLIENT_ASSOCIATED_3 | ||
7036 | + && clt->used != CLIENT_AUTHENTICATED_2) { | ||
7037 | + /* it's disassociating, but it's | ||
7038 | + ** not even authenticated! Let it know that */ | ||
7039 | + acxlog_mac(L_ASSOC|L_XFER, "peer ", ta, "has sent disassoc " | ||
7040 | + "req but it is not even auth'ed! sending deauth\n"); | ||
7041 | + acx_l_transmit_deauthen(adev, ta, | ||
7042 | + WLAN_MGMT_REASON_CLASS2_NONAUTH); | ||
7043 | + clt->used = CLIENT_EXIST_1; | ||
7044 | + } else { | ||
7045 | + /* mark it as auth'ed only */ | ||
7046 | + clt->used = CLIENT_AUTHENTICATED_2; | ||
7047 | + } | ||
7048 | +end: | ||
7049 | + FN_EXIT0; | ||
7050 | +} | ||
7051 | + | ||
7052 | + | ||
7053 | +/*********************************************************************** | ||
7054 | +** acx_l_process_deauthen_from_sta | ||
7055 | +*/ | ||
7056 | +static void | ||
7057 | +acx_l_process_deauth_from_sta(acx_device_t *adev, const wlan_fr_deauthen_t *req) | ||
7058 | +{ | ||
7059 | + const wlan_hdr_t *hdr; | ||
7060 | + client_t *client; | ||
7061 | + | ||
7062 | + FN_ENTER; | ||
7063 | + | ||
7064 | + hdr = req->hdr; | ||
7065 | + | ||
7066 | + if (acx_debug & L_ASSOC) { | ||
7067 | + acx_print_mac("got deauth from sta:", hdr->a2, " "); | ||
7068 | + acx_print_mac("a1:", hdr->a1, " "); | ||
7069 | + acx_print_mac("a3:", hdr->a3, " "); | ||
7070 | + acx_print_mac("adev->addr:", adev->dev_addr, " "); | ||
7071 | + acx_print_mac("adev->bssid:", adev->bssid, "\n"); | ||
7072 | + } | ||
7073 | + | ||
7074 | + if (!mac_is_equal(adev->dev_addr, hdr->a1)) { | ||
7075 | + goto end; | ||
7076 | + } | ||
7077 | + | ||
7078 | + client = acx_l_sta_list_get(adev, hdr->a2); | ||
7079 | + if (!client) { | ||
7080 | + goto end; | ||
7081 | + } | ||
7082 | + client->used = CLIENT_EXIST_1; | ||
7083 | +end: | ||
7084 | + FN_EXIT0; | ||
7085 | +} | ||
7086 | + | ||
7087 | + | ||
7088 | +/*********************************************************************** | ||
7089 | +** acx_l_process_disassoc_from_ap | ||
7090 | +*/ | ||
7091 | +static void | ||
7092 | +acx_l_process_disassoc_from_ap(acx_device_t *adev, const wlan_fr_disassoc_t *req) | ||
7093 | +{ | ||
7094 | + FN_ENTER; | ||
7095 | + | ||
7096 | + if (!adev->ap_client) { | ||
7097 | + /* Hrm, we aren't assoc'ed yet anyhow... */ | ||
7098 | + goto end; | ||
7099 | + } | ||
7100 | + | ||
7101 | + printk("%s: got disassoc frame with reason %d (%s)\n", | ||
7102 | + adev->ndev->name, *req->reason, | ||
7103 | + acx_wlan_reason_str(*req->reason)); | ||
7104 | + | ||
7105 | + if (mac_is_equal(adev->dev_addr, req->hdr->a1)) { | ||
7106 | + acx_l_transmit_deauthen(adev, adev->bssid, | ||
7107 | + WLAN_MGMT_REASON_DEAUTH_LEAVING); | ||
7108 | + SET_BIT(adev->set_mask, GETSET_RESCAN); | ||
7109 | + acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG); | ||
7110 | + } | ||
7111 | +end: | ||
7112 | + FN_EXIT0; | ||
7113 | +} | ||
7114 | + | ||
7115 | + | ||
7116 | +/*********************************************************************** | ||
7117 | +** acx_l_process_deauth_from_ap | ||
7118 | +*/ | ||
7119 | +static void | ||
7120 | +acx_l_process_deauth_from_ap(acx_device_t *adev, const wlan_fr_deauthen_t *req) | ||
7121 | +{ | ||
7122 | + FN_ENTER; | ||
7123 | + | ||
7124 | + if (!adev->ap_client) { | ||
7125 | + /* Hrm, we aren't assoc'ed yet anyhow... */ | ||
7126 | + goto end; | ||
7127 | + } | ||
7128 | + | ||
7129 | + printk("%s: got deauth frame with reason %d (%s)\n", | ||
7130 | + adev->ndev->name, *req->reason, | ||
7131 | + acx_wlan_reason_str(*req->reason)); | ||
7132 | + | ||
7133 | + /* Chk: is ta verified to be from our AP? */ | ||
7134 | + if (mac_is_equal(adev->dev_addr, req->hdr->a1)) { | ||
7135 | + log(L_DEBUG, "AP sent us deauth packet\n"); | ||
7136 | + SET_BIT(adev->set_mask, GETSET_RESCAN); | ||
7137 | + acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG); | ||
7138 | + } | ||
7139 | +end: | ||
7140 | + FN_EXIT0; | ||
7141 | +} | ||
7142 | + | ||
7143 | + | ||
7144 | +/*********************************************************************** | ||
7145 | +** acx_l_rx | ||
7146 | +** | ||
7147 | +** The end of the Rx path. Pulls data from a rxhostdesc into a socket | ||
7148 | +** buffer and feeds it to the network stack via netif_rx(). | ||
7149 | +*/ | ||
7150 | +static void | ||
7151 | +acx_l_rx(acx_device_t *adev, rxbuffer_t *rxbuf) | ||
7152 | +{ | ||
7153 | + FN_ENTER; | ||
7154 | + if (likely(adev->dev_state_mask & ACX_STATE_IFACE_UP)) { | ||
7155 | + struct sk_buff *skb; | ||
7156 | + skb = acx_rxbuf_to_ether(adev, rxbuf); | ||
7157 | + if (likely(skb)) { | ||
7158 | + netif_rx(skb); | ||
7159 | + adev->ndev->last_rx = jiffies; | ||
7160 | + adev->stats.rx_packets++; | ||
7161 | + adev->stats.rx_bytes += skb->len; | ||
7162 | + } | ||
7163 | + } | ||
7164 | + FN_EXIT0; | ||
7165 | +} | ||
7166 | + | ||
7167 | + | ||
7168 | +/*********************************************************************** | ||
7169 | +** acx_l_process_data_frame_master | ||
7170 | +*/ | ||
7171 | +static int | ||
7172 | +acx_l_process_data_frame_master(acx_device_t *adev, rxbuffer_t *rxbuf) | ||
7173 | +{ | ||
7174 | + struct wlan_hdr *hdr; | ||
7175 | + struct tx *tx; | ||
7176 | + void *txbuf; | ||
7177 | + int len; | ||
7178 | + int result = NOT_OK; | ||
7179 | + | ||
7180 | + FN_ENTER; | ||
7181 | + | ||
7182 | + hdr = acx_get_wlan_hdr(adev, rxbuf); | ||
7183 | + | ||
7184 | + switch (WF_FC_FROMTODSi & hdr->fc) { | ||
7185 | + case 0: | ||
7186 | + case WF_FC_FROMDSi: | ||
7187 | + log(L_DEBUG, "ap->sta or adhoc->adhoc data frame ignored\n"); | ||
7188 | + goto done; | ||
7189 | + case WF_FC_TODSi: | ||
7190 | + break; | ||
7191 | + default: /* WF_FC_FROMTODSi */ | ||
7192 | + log(L_DEBUG, "wds data frame ignored (TODO)\n"); | ||
7193 | + goto done; | ||
7194 | + } | ||
7195 | + | ||
7196 | + /* check if it is our BSSID, if not, leave */ | ||
7197 | + if (!mac_is_equal(adev->bssid, hdr->a1)) { | ||
7198 | + goto done; | ||
7199 | + } | ||
7200 | + | ||
7201 | + if (mac_is_equal(adev->dev_addr, hdr->a3)) { | ||
7202 | + /* this one is for us */ | ||
7203 | + acx_l_rx(adev, rxbuf); | ||
7204 | + } else { | ||
7205 | + if (mac_is_bcast(hdr->a3)) { | ||
7206 | + /* this one is bcast, rx it too */ | ||
7207 | + acx_l_rx(adev, rxbuf); | ||
7208 | + } | ||
7209 | + tx = acx_l_alloc_tx(adev); | ||
7210 | + if (!tx) { | ||
7211 | + goto fail; | ||
7212 | + } | ||
7213 | + /* repackage, tx, and hope it someday reaches its destination */ | ||
7214 | + /* order is important, we do it in-place */ | ||
7215 | + MAC_COPY(hdr->a1, hdr->a3); | ||
7216 | + MAC_COPY(hdr->a3, hdr->a2); | ||
7217 | + MAC_COPY(hdr->a2, adev->bssid); | ||
7218 | + /* To_DS = 0, From_DS = 1 */ | ||
7219 | + hdr->fc = WF_FC_FROMDSi + WF_FTYPE_DATAi; | ||
7220 | + | ||
7221 | + txbuf = acx_l_get_txbuf(adev, tx); | ||
7222 | + if (txbuf) { | ||
7223 | + len = RXBUF_BYTES_RCVD(adev, rxbuf); | ||
7224 | + memcpy(txbuf, hdr, len); | ||
7225 | + acx_l_tx_data(adev, tx, len); | ||
7226 | + } else { | ||
7227 | + acx_l_dealloc_tx(adev, tx); | ||
7228 | + } | ||
7229 | + } | ||
7230 | +done: | ||
7231 | + result = OK; | ||
7232 | +fail: | ||
7233 | + FN_EXIT1(result); | ||
7234 | + return result; | ||
7235 | +} | ||
7236 | + | ||
7237 | + | ||
7238 | +/*********************************************************************** | ||
7239 | +** acx_l_process_data_frame_client | ||
7240 | +*/ | ||
7241 | +static int | ||
7242 | +acx_l_process_data_frame_client(acx_device_t *adev, rxbuffer_t *rxbuf) | ||
7243 | +{ | ||
7244 | + const u8 *da, *bssid; | ||
7245 | + const wlan_hdr_t *hdr; | ||
7246 | + struct net_device *ndev = adev->ndev; | ||
7247 | + int result = NOT_OK; | ||
7248 | + | ||
7249 | + FN_ENTER; | ||
7250 | + | ||
7251 | + if (ACX_STATUS_4_ASSOCIATED != adev->status) | ||
7252 | + goto drop; | ||
7253 | + | ||
7254 | + hdr = acx_get_wlan_hdr(adev, rxbuf); | ||
7255 | + | ||
7256 | + switch (WF_FC_FROMTODSi & hdr->fc) { | ||
7257 | + case 0: | ||
7258 | + if (adev->mode != ACX_MODE_0_ADHOC) { | ||
7259 | + log(L_DEBUG, "adhoc->adhoc data frame ignored\n"); | ||
7260 | + goto drop; | ||
7261 | + } | ||
7262 | + bssid = hdr->a3; | ||
7263 | + break; | ||
7264 | + case WF_FC_FROMDSi: | ||
7265 | + if (adev->mode != ACX_MODE_2_STA) { | ||
7266 | + log(L_DEBUG, "ap->sta data frame ignored\n"); | ||
7267 | + goto drop; | ||
7268 | + } | ||
7269 | + bssid = hdr->a2; | ||
7270 | + break; | ||
7271 | + case WF_FC_TODSi: | ||
7272 | + log(L_DEBUG, "sta->ap data frame ignored\n"); | ||
7273 | + goto drop; | ||
7274 | + default: /* WF_FC_FROMTODSi: wds->wds */ | ||
7275 | + log(L_DEBUG, "wds data frame ignored (todo)\n"); | ||
7276 | + goto drop; | ||
7277 | + } | ||
7278 | + | ||
7279 | + da = hdr->a1; | ||
7280 | + | ||
7281 | + if (unlikely(acx_debug & L_DEBUG)) { | ||
7282 | + acx_print_mac("rx: da=", da, ""); | ||
7283 | + acx_print_mac(" bssid=", bssid, ""); | ||
7284 | + acx_print_mac(" adev->bssid=", adev->bssid, ""); | ||
7285 | + acx_print_mac(" adev->addr=", adev->dev_addr, "\n"); | ||
7286 | + } | ||
7287 | + | ||
7288 | + /* promiscuous mode --> receive all packets */ | ||
7289 | + if (unlikely(ndev->flags & IFF_PROMISC)) | ||
7290 | + goto process; | ||
7291 | + | ||
7292 | + /* FIRST, check if it is our BSSID */ | ||
7293 | + if (!mac_is_equal(adev->bssid, bssid)) { | ||
7294 | + /* is not our BSSID, so bail out */ | ||
7295 | + goto drop; | ||
7296 | + } | ||
7297 | + | ||
7298 | + /* then, check if it is our address */ | ||
7299 | + if (mac_is_equal(adev->dev_addr, da)) { | ||
7300 | + goto process; | ||
7301 | + } | ||
7302 | + | ||
7303 | + /* then, check if it is broadcast */ | ||
7304 | + if (mac_is_bcast(da)) { | ||
7305 | + goto process; | ||
7306 | + } | ||
7307 | + | ||
7308 | + if (mac_is_mcast(da)) { | ||
7309 | + /* unconditionally receive all multicasts */ | ||
7310 | + if (ndev->flags & IFF_ALLMULTI) | ||
7311 | + goto process; | ||
7312 | + | ||
7313 | + /* FIXME: need to check against the list of | ||
7314 | + * multicast addresses that are configured | ||
7315 | + * for the interface (ifconfig) */ | ||
7316 | + log(L_XFER, "FIXME: multicast packet, need to check " | ||
7317 | + "against a list of multicast addresses " | ||
7318 | + "(to be created!); accepting packet for now\n"); | ||
7319 | + /* for now, just accept it here */ | ||
7320 | + goto process; | ||
7321 | + } | ||
7322 | + | ||
7323 | + log(L_DEBUG, "rx: foreign packet, dropping\n"); | ||
7324 | + goto drop; | ||
7325 | +process: | ||
7326 | + /* receive packet */ | ||
7327 | + acx_l_rx(adev, rxbuf); | ||
7328 | + | ||
7329 | + result = OK; | ||
7330 | +drop: | ||
7331 | + FN_EXIT1(result); | ||
7332 | + return result; | ||
7333 | +} | ||
7334 | + | ||
7335 | + | ||
7336 | +/*********************************************************************** | ||
7337 | +** acx_l_process_mgmt_frame | ||
7338 | +** | ||
7339 | +** Theory of operation: mgmt packet gets parsed (to make it easy | ||
7340 | +** to access variable-sized IEs), results stored in 'parsed'. | ||
7341 | +** Then we react to the packet. | ||
7342 | +*/ | ||
7343 | +typedef union parsed_mgmt_req { | ||
7344 | + wlan_fr_mgmt_t mgmt; | ||
7345 | + wlan_fr_assocreq_t assocreq; | ||
7346 | + wlan_fr_reassocreq_t reassocreq; | ||
7347 | + wlan_fr_assocresp_t assocresp; | ||
7348 | + wlan_fr_reassocresp_t reassocresp; | ||
7349 | + wlan_fr_beacon_t beacon; | ||
7350 | + wlan_fr_disassoc_t disassoc; | ||
7351 | + wlan_fr_authen_t authen; | ||
7352 | + wlan_fr_deauthen_t deauthen; | ||
7353 | + wlan_fr_proberesp_t proberesp; | ||
7354 | +} parsed_mgmt_req_t; | ||
7355 | + | ||
7356 | +void BUG_excessive_stack_usage(void); | ||
7357 | + | ||
7358 | +static int | ||
7359 | +acx_l_process_mgmt_frame(acx_device_t *adev, rxbuffer_t *rxbuf) | ||
7360 | +{ | ||
7361 | + parsed_mgmt_req_t parsed; /* takes ~100 bytes of stack */ | ||
7362 | + wlan_hdr_t *hdr; | ||
7363 | + int adhoc, sta_scan, sta, ap; | ||
7364 | + int len; | ||
7365 | + | ||
7366 | + if (sizeof(parsed) > 256) | ||
7367 | + BUG_excessive_stack_usage(); | ||
7368 | + | ||
7369 | + FN_ENTER; | ||
7370 | + | ||
7371 | + hdr = acx_get_wlan_hdr(adev, rxbuf); | ||
7372 | + | ||
7373 | + /* Management frames never have these set */ | ||
7374 | + if (WF_FC_FROMTODSi & hdr->fc) { | ||
7375 | + FN_EXIT1(NOT_OK); | ||
7376 | + return NOT_OK; | ||
7377 | + } | ||
7378 | + | ||
7379 | + len = RXBUF_BYTES_RCVD(adev, rxbuf); | ||
7380 | + if (WF_FC_ISWEPi & hdr->fc) | ||
7381 | + len -= 0x10; | ||
7382 | + | ||
7383 | + adhoc = (adev->mode == ACX_MODE_0_ADHOC); | ||
7384 | + sta_scan = ((adev->mode == ACX_MODE_2_STA) | ||
7385 | + && (adev->status != ACX_STATUS_4_ASSOCIATED)); | ||
7386 | + sta = ((adev->mode == ACX_MODE_2_STA) | ||
7387 | + && (adev->status == ACX_STATUS_4_ASSOCIATED)); | ||
7388 | + ap = (adev->mode == ACX_MODE_3_AP); | ||
7389 | + | ||
7390 | + switch (WF_FC_FSTYPEi & hdr->fc) { | ||
7391 | + /* beacons first, for speed */ | ||
7392 | + case WF_FSTYPE_BEACONi: | ||
7393 | + memset(&parsed.beacon, 0, sizeof(parsed.beacon)); | ||
7394 | + parsed.beacon.hdr = hdr; | ||
7395 | + parsed.beacon.len = len; | ||
7396 | + if (acx_debug & L_DATA) { | ||
7397 | + printk("beacon len:%d fc:%04X dur:%04X seq:%04X", | ||
7398 | + len, hdr->fc, hdr->dur, hdr->seq); | ||
7399 | + acx_print_mac(" a1:", hdr->a1, ""); | ||
7400 | + acx_print_mac(" a2:", hdr->a2, ""); | ||
7401 | + acx_print_mac(" a3:", hdr->a3, "\n"); | ||
7402 | + } | ||
7403 | + wlan_mgmt_decode_beacon(&parsed.beacon); | ||
7404 | + /* beacon and probe response are very similar, so... */ | ||
7405 | + acx_l_process_probe_response(adev, &parsed.beacon, rxbuf); | ||
7406 | + break; | ||
7407 | + case WF_FSTYPE_ASSOCREQi: | ||
7408 | + if (!ap) | ||
7409 | + break; | ||
7410 | + memset(&parsed.assocreq, 0, sizeof(parsed.assocreq)); | ||
7411 | + parsed.assocreq.hdr = hdr; | ||
7412 | + parsed.assocreq.len = len; | ||
7413 | + wlan_mgmt_decode_assocreq(&parsed.assocreq); | ||
7414 | + if (mac_is_equal(hdr->a1, adev->bssid) | ||
7415 | + && mac_is_equal(hdr->a3, adev->bssid)) { | ||
7416 | + acx_l_transmit_assocresp(adev, &parsed.assocreq); | ||
7417 | + } | ||
7418 | + break; | ||
7419 | + case WF_FSTYPE_REASSOCREQi: | ||
7420 | + if (!ap) | ||
7421 | + break; | ||
7422 | + memset(&parsed.assocreq, 0, sizeof(parsed.assocreq)); | ||
7423 | + parsed.assocreq.hdr = hdr; | ||
7424 | + parsed.assocreq.len = len; | ||
7425 | + wlan_mgmt_decode_assocreq(&parsed.assocreq); | ||
7426 | + /* reassocreq and assocreq are equivalent */ | ||
7427 | + acx_l_transmit_reassocresp(adev, &parsed.reassocreq); | ||
7428 | + break; | ||
7429 | + case WF_FSTYPE_ASSOCRESPi: | ||
7430 | + if (!sta_scan) | ||
7431 | + break; | ||
7432 | + memset(&parsed.assocresp, 0, sizeof(parsed.assocresp)); | ||
7433 | + parsed.assocresp.hdr = hdr; | ||
7434 | + parsed.assocresp.len = len; | ||
7435 | + wlan_mgmt_decode_assocresp(&parsed.assocresp); | ||
7436 | + acx_l_process_assocresp(adev, &parsed.assocresp); | ||
7437 | + break; | ||
7438 | + case WF_FSTYPE_REASSOCRESPi: | ||
7439 | + if (!sta_scan) | ||
7440 | + break; | ||
7441 | + memset(&parsed.assocresp, 0, sizeof(parsed.assocresp)); | ||
7442 | + parsed.assocresp.hdr = hdr; | ||
7443 | + parsed.assocresp.len = len; | ||
7444 | + wlan_mgmt_decode_assocresp(&parsed.assocresp); | ||
7445 | + acx_l_process_reassocresp(adev, &parsed.reassocresp); | ||
7446 | + break; | ||
7447 | + case WF_FSTYPE_PROBEREQi: | ||
7448 | + if (ap || adhoc) { | ||
7449 | + /* FIXME: since we're supposed to be an AP, | ||
7450 | + ** we need to return a Probe Response packet. | ||
7451 | + ** Currently firmware is doing it for us, | ||
7452 | + ** but firmware is buggy! See comment elsewhere --vda */ | ||
7453 | + } | ||
7454 | + break; | ||
7455 | + case WF_FSTYPE_PROBERESPi: | ||
7456 | + memset(&parsed.proberesp, 0, sizeof(parsed.proberesp)); | ||
7457 | + parsed.proberesp.hdr = hdr; | ||
7458 | + parsed.proberesp.len = len; | ||
7459 | + wlan_mgmt_decode_proberesp(&parsed.proberesp); | ||
7460 | + acx_l_process_probe_response(adev, &parsed.proberesp, rxbuf); | ||
7461 | + break; | ||
7462 | + case 6: | ||
7463 | + case 7: | ||
7464 | + /* exit */ | ||
7465 | + break; | ||
7466 | + case WF_FSTYPE_ATIMi: | ||
7467 | + /* exit */ | ||
7468 | + break; | ||
7469 | + case WF_FSTYPE_DISASSOCi: | ||
7470 | + if (!sta && !ap) | ||
7471 | + break; | ||
7472 | + memset(&parsed.disassoc, 0, sizeof(parsed.disassoc)); | ||
7473 | + parsed.disassoc.hdr = hdr; | ||
7474 | + parsed.disassoc.len = len; | ||
7475 | + wlan_mgmt_decode_disassoc(&parsed.disassoc); | ||
7476 | + if (sta) | ||
7477 | + acx_l_process_disassoc_from_ap(adev, &parsed.disassoc); | ||
7478 | + else | ||
7479 | + acx_l_process_disassoc_from_sta(adev, &parsed.disassoc); | ||
7480 | + break; | ||
7481 | + case WF_FSTYPE_AUTHENi: | ||
7482 | + if (!sta_scan && !ap) | ||
7483 | + break; | ||
7484 | + memset(&parsed.authen, 0, sizeof(parsed.authen)); | ||
7485 | + parsed.authen.hdr = hdr; | ||
7486 | + parsed.authen.len = len; | ||
7487 | + wlan_mgmt_decode_authen(&parsed.authen); | ||
7488 | + acx_l_process_authen(adev, &parsed.authen); | ||
7489 | + break; | ||
7490 | + case WF_FSTYPE_DEAUTHENi: | ||
7491 | + if (!sta && !ap) | ||
7492 | + break; | ||
7493 | + memset(&parsed.deauthen, 0, sizeof(parsed.deauthen)); | ||
7494 | + parsed.deauthen.hdr = hdr; | ||
7495 | + parsed.deauthen.len = len; | ||
7496 | + wlan_mgmt_decode_deauthen(&parsed.deauthen); | ||
7497 | + if (sta) | ||
7498 | + acx_l_process_deauth_from_ap(adev, &parsed.deauthen); | ||
7499 | + else | ||
7500 | + acx_l_process_deauth_from_sta(adev, &parsed.deauthen); | ||
7501 | + break; | ||
7502 | + } | ||
7503 | + | ||
7504 | + FN_EXIT1(OK); | ||
7505 | + return OK; | ||
7506 | +} | ||
7507 | + | ||
7508 | + | ||
7509 | +#ifdef UNUSED | ||
7510 | +/*********************************************************************** | ||
7511 | +** acx_process_class_frame | ||
7512 | +** | ||
7513 | +** Called from IRQ context only | ||
7514 | +*/ | ||
7515 | +static int | ||
7516 | +acx_process_class_frame(acx_device_t *adev, rxbuffer_t *rxbuf, int vala) | ||
7517 | +{ | ||
7518 | + return OK; | ||
7519 | +} | ||
7520 | +#endif | ||
7521 | + | ||
7522 | + | ||
7523 | +/*********************************************************************** | ||
7524 | +** acx_l_process_NULL_frame | ||
7525 | +*/ | ||
7526 | +#ifdef BOGUS_ITS_NOT_A_NULL_FRAME_HANDLER_AT_ALL | ||
7527 | +static int | ||
7528 | +acx_l_process_NULL_frame(acx_device_t *adev, rxbuffer_t *rxbuf, int vala) | ||
7529 | +{ | ||
7530 | + const signed char *esi; | ||
7531 | + const u8 *ebx; | ||
7532 | + const wlan_hdr_t *hdr; | ||
7533 | + const client_t *client; | ||
7534 | + int result = NOT_OK; | ||
7535 | + | ||
7536 | + hdr = acx_get_wlan_hdr(adev, rxbuf); | ||
7537 | + | ||
7538 | + switch (WF_FC_FROMTODSi & hdr->fc) { | ||
7539 | + case 0: | ||
7540 | + esi = hdr->a1; | ||
7541 | + ebx = hdr->a2; | ||
7542 | + break; | ||
7543 | + case WF_FC_FROMDSi: | ||
7544 | + esi = hdr->a1; | ||
7545 | + ebx = hdr->a3; | ||
7546 | + break; | ||
7547 | + case WF_FC_TODSi: | ||
7548 | + esi = hdr->a1; | ||
7549 | + ebx = hdr->a2; | ||
7550 | + break; | ||
7551 | + default: /* WF_FC_FROMTODSi */ | ||
7552 | + esi = hdr->a1; /* added by me! --vda */ | ||
7553 | + ebx = hdr->a2; | ||
7554 | + } | ||
7555 | + | ||
7556 | + if (esi[0x0] < 0) { | ||
7557 | + result = OK; | ||
7558 | + goto done; | ||
7559 | + } | ||
7560 | + | ||
7561 | + client = acx_l_sta_list_get(adev, ebx); | ||
7562 | + if (client) | ||
7563 | + result = NOT_OK; | ||
7564 | + else { | ||
7565 | +#ifdef IS_IT_BROKEN | ||
7566 | + log(L_DEBUG|L_XFER, "<transmit_deauth 7>\n"); | ||
7567 | + acx_l_transmit_deauthen(adev, ebx, | ||
7568 | + WLAN_MGMT_REASON_CLASS2_NONAUTH); | ||
7569 | +#else | ||
7570 | + log(L_DEBUG, "received NULL frame from unknown client! " | ||
7571 | + "We really shouldn't send deauthen here, right?\n"); | ||
7572 | +#endif | ||
7573 | + result = OK; | ||
7574 | + } | ||
7575 | +done: | ||
7576 | + return result; | ||
7577 | +} | ||
7578 | +#endif | ||
7579 | + | ||
7580 | + | ||
7581 | +/*********************************************************************** | ||
7582 | +** acx_l_process_probe_response | ||
7583 | +*/ | ||
7584 | +static int | ||
7585 | +acx_l_process_probe_response(acx_device_t *adev, wlan_fr_proberesp_t *req, | ||
7586 | + const rxbuffer_t *rxbuf) | ||
7587 | +{ | ||
7588 | + struct client *bss; | ||
7589 | + wlan_hdr_t *hdr; | ||
7590 | + | ||
7591 | + FN_ENTER; | ||
7592 | + | ||
7593 | + hdr = req->hdr; | ||
7594 | + | ||
7595 | + if (mac_is_equal(hdr->a3, adev->dev_addr)) { | ||
7596 | + log(L_ASSOC, "huh, scan found our own MAC!?\n"); | ||
7597 | + goto ok; /* just skip this one silently */ | ||
7598 | + } | ||
7599 | + | ||
7600 | + bss = acx_l_sta_list_get_or_add(adev, hdr->a2); | ||
7601 | + | ||
7602 | + /* NB: be careful modifying bss data! It may be one | ||
7603 | + ** of the already known clients (like our AP if we are a STA) | ||
7604 | + ** Thus do not blindly modify e.g. current ratemask! */ | ||
7605 | + | ||
7606 | + if (STA_LIST_ADD_CAN_FAIL && !bss) { | ||
7607 | + /* uh oh, we found more sites/stations than we can handle with | ||
7608 | + * our current setup: pull the emergency brake and stop scanning! */ | ||
7609 | + acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_STOP_SCAN); | ||
7610 | + /* TODO: a nice comment what below call achieves --vda */ | ||
7611 | + acx_set_status(adev, ACX_STATUS_2_WAIT_AUTH); | ||
7612 | + goto ok; | ||
7613 | + } | ||
7614 | + /* NB: get_or_add already filled bss->address = hdr->a2 */ | ||
7615 | + MAC_COPY(bss->bssid, hdr->a3); | ||
7616 | + | ||
7617 | + /* copy the ESSID element */ | ||
7618 | + if (req->ssid && req->ssid->len <= IW_ESSID_MAX_SIZE) { | ||
7619 | + bss->essid_len = req->ssid->len; | ||
7620 | + memcpy(bss->essid, req->ssid->ssid, req->ssid->len); | ||
7621 | + bss->essid[req->ssid->len] = '\0'; | ||
7622 | + } else { | ||
7623 | + /* Either no ESSID IE or oversized one */ | ||
7624 | + printk("%s: received packet has bogus ESSID\n", | ||
7625 | + adev->ndev->name); | ||
7626 | + } | ||
7627 | + | ||
7628 | + if (req->ds_parms) | ||
7629 | + bss->channel = req->ds_parms->curr_ch; | ||
7630 | + if (req->cap_info) | ||
7631 | + bss->cap_info = ieee2host16(*req->cap_info); | ||
7632 | + | ||
7633 | + bss->sir = acx_signal_to_winlevel(rxbuf->phy_level); | ||
7634 | + bss->snr = acx_signal_to_winlevel(rxbuf->phy_snr); | ||
7635 | + | ||
7636 | + bss->rate_cap = 0; /* operational mask */ | ||
7637 | + bss->rate_bas = 0; /* basic mask */ | ||
7638 | + if (req->supp_rates) | ||
7639 | + add_bits_to_ratemasks(req->supp_rates->rates, | ||
7640 | + req->supp_rates->len, &bss->rate_bas, &bss->rate_cap); | ||
7641 | + if (req->ext_rates) | ||
7642 | + add_bits_to_ratemasks(req->ext_rates->rates, | ||
7643 | + req->ext_rates->len, &bss->rate_bas, &bss->rate_cap); | ||
7644 | + /* Fix up any possible bogosity - code elsewhere | ||
7645 | + * is not expecting empty masks */ | ||
7646 | + if (!bss->rate_cap) | ||
7647 | + bss->rate_cap = adev->rate_basic; | ||
7648 | + if (!bss->rate_bas) | ||
7649 | + bss->rate_bas = 1 << lowest_bit(bss->rate_cap); | ||
7650 | + if (!bss->rate_cur) | ||
7651 | + bss->rate_cur = 1 << lowest_bit(bss->rate_bas); | ||
7652 | + | ||
7653 | + /* People moan about this being too noisy at L_ASSOC */ | ||
7654 | + log(L_DEBUG, | ||
7655 | + "found %s: ESSID=\"%s\" ch=%d " | ||
7656 | + "BSSID="MACSTR" caps=0x%04X SIR=%d SNR=%d\n", | ||
7657 | + (bss->cap_info & WF_MGMT_CAP_IBSS) ? "Ad-Hoc peer" : "AP", | ||
7658 | + bss->essid, bss->channel, MAC(bss->bssid), bss->cap_info, | ||
7659 | + bss->sir, bss->snr); | ||
7660 | +ok: | ||
7661 | + FN_EXIT0; | ||
7662 | + return OK; | ||
7663 | +} | ||
7664 | + | ||
7665 | + | ||
7666 | +/*********************************************************************** | ||
7667 | +** acx_l_process_assocresp | ||
7668 | +*/ | ||
7669 | +static int | ||
7670 | +acx_l_process_assocresp(acx_device_t *adev, const wlan_fr_assocresp_t *req) | ||
7671 | +{ | ||
7672 | + const wlan_hdr_t *hdr; | ||
7673 | + int res = OK; | ||
7674 | + | ||
7675 | + FN_ENTER; | ||
7676 | + | ||
7677 | + hdr = req->hdr; | ||
7678 | + | ||
7679 | + if ((ACX_MODE_2_STA == adev->mode) | ||
7680 | + && mac_is_equal(adev->dev_addr, hdr->a1)) { | ||
7681 | + u16 st = ieee2host16(*(req->status)); | ||
7682 | + if (WLAN_MGMT_STATUS_SUCCESS == st) { | ||
7683 | + adev->aid = ieee2host16(*(req->aid)); | ||
7684 | + /* tell the card we are associated when | ||
7685 | + ** we are out of interrupt context */ | ||
7686 | + acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_ASSOCIATE); | ||
7687 | + } else { | ||
7688 | + | ||
7689 | + /* TODO: we shall delete peer from sta_list, and try | ||
7690 | + ** other candidates... */ | ||
7691 | + | ||
7692 | + printk("%s: association FAILED: peer sent " | ||
7693 | + "Status Code %d (%s)\n", | ||
7694 | + adev->ndev->name, st, get_status_string(st)); | ||
7695 | + res = NOT_OK; | ||
7696 | + } | ||
7697 | + } | ||
7698 | + | ||
7699 | + FN_EXIT1(res); | ||
7700 | + return res; | ||
7701 | +} | ||
7702 | + | ||
7703 | + | ||
7704 | +/*********************************************************************** | ||
7705 | +** acx_l_process_reassocresp | ||
7706 | +*/ | ||
7707 | +static int | ||
7708 | +acx_l_process_reassocresp(acx_device_t *adev, const wlan_fr_reassocresp_t *req) | ||
7709 | +{ | ||
7710 | + const wlan_hdr_t *hdr; | ||
7711 | + int result = NOT_OK; | ||
7712 | + u16 st; | ||
7713 | + | ||
7714 | + FN_ENTER; | ||
7715 | + | ||
7716 | + hdr = req->hdr; | ||
7717 | + | ||
7718 | + if (!mac_is_equal(adev->dev_addr, hdr->a1)) { | ||
7719 | + goto end; | ||
7720 | + } | ||
7721 | + st = ieee2host16(*(req->status)); | ||
7722 | + if (st == WLAN_MGMT_STATUS_SUCCESS) { | ||
7723 | + acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); | ||
7724 | + result = OK; | ||
7725 | + } else { | ||
7726 | + printk("%s: reassociation FAILED: peer sent " | ||
7727 | + "response code %d (%s)\n", | ||
7728 | + adev->ndev->name, st, get_status_string(st)); | ||
7729 | + } | ||
7730 | +end: | ||
7731 | + FN_EXIT1(result); | ||
7732 | + return result; | ||
7733 | +} | ||
7734 | + | ||
7735 | + | ||
7736 | +/*********************************************************************** | ||
7737 | +** acx_l_process_authen | ||
7738 | +** | ||
7739 | +** Called only in STA_SCAN or AP mode | ||
7740 | +*/ | ||
7741 | +static int | ||
7742 | +acx_l_process_authen(acx_device_t *adev, const wlan_fr_authen_t *req) | ||
7743 | +{ | ||
7744 | + const wlan_hdr_t *hdr; | ||
7745 | + client_t *clt; | ||
7746 | + wlan_ie_challenge_t *chal; | ||
7747 | + u16 alg, seq, status; | ||
7748 | + int ap, result; | ||
7749 | + | ||
7750 | + FN_ENTER; | ||
7751 | + | ||
7752 | + hdr = req->hdr; | ||
7753 | + | ||
7754 | + if (acx_debug & L_ASSOC) { | ||
7755 | + acx_print_mac("AUTHEN adev->addr=", adev->dev_addr, " "); | ||
7756 | + acx_print_mac("a1=", hdr->a1, " "); | ||
7757 | + acx_print_mac("a2=", hdr->a2, " "); | ||
7758 | + acx_print_mac("a3=", hdr->a3, " "); | ||
7759 | + acx_print_mac("adev->bssid=", adev->bssid, "\n"); | ||
7760 | + } | ||
7761 | + | ||
7762 | + if (!mac_is_equal(adev->dev_addr, hdr->a1) | ||
7763 | + || !mac_is_equal(adev->bssid, hdr->a3)) { | ||
7764 | + result = OK; | ||
7765 | + goto end; | ||
7766 | + } | ||
7767 | + | ||
7768 | + alg = ieee2host16(*(req->auth_alg)); | ||
7769 | + seq = ieee2host16(*(req->auth_seq)); | ||
7770 | + status = ieee2host16(*(req->status)); | ||
7771 | + | ||
7772 | + log(L_ASSOC, "auth algorithm %d, auth sequence %d, status %d\n", alg, seq, status); | ||
7773 | + | ||
7774 | + ap = (adev->mode == ACX_MODE_3_AP); | ||
7775 | + | ||
7776 | + if (adev->auth_alg <= 1) { | ||
7777 | + if (adev->auth_alg != alg) { | ||
7778 | + log(L_ASSOC, "auth algorithm mismatch: " | ||
7779 | + "our:%d peer:%d\n", adev->auth_alg, alg); | ||
7780 | + result = NOT_OK; | ||
7781 | + goto end; | ||
7782 | + } | ||
7783 | + } | ||
7784 | + if (ap) { | ||
7785 | + clt = acx_l_sta_list_get_or_add(adev, hdr->a2); | ||
7786 | + if (STA_LIST_ADD_CAN_FAIL && !clt) { | ||
7787 | + log(L_ASSOC, "could not allocate room for client\n"); | ||
7788 | + result = NOT_OK; | ||
7789 | + goto end; | ||
7790 | + } | ||
7791 | + } else { | ||
7792 | + clt = adev->ap_client; | ||
7793 | + if (!mac_is_equal(clt->address, hdr->a2)) { | ||
7794 | + printk("%s: malformed auth frame from AP?!\n", | ||
7795 | + adev->ndev->name); | ||
7796 | + result = NOT_OK; | ||
7797 | + goto end; | ||
7798 | + } | ||
7799 | + } | ||
7800 | + | ||
7801 | + /* now check which step in the authentication sequence we are | ||
7802 | + * currently in, and act accordingly */ | ||
7803 | + switch (seq) { | ||
7804 | + case 1: | ||
7805 | + if (!ap) | ||
7806 | + break; | ||
7807 | + acx_l_transmit_authen2(adev, req, clt); | ||
7808 | + break; | ||
7809 | + case 2: | ||
7810 | + if (ap) | ||
7811 | + break; | ||
7812 | + if (status == WLAN_MGMT_STATUS_SUCCESS) { | ||
7813 | + if (alg == WLAN_AUTH_ALG_OPENSYSTEM) { | ||
7814 | + acx_set_status(adev, ACX_STATUS_3_AUTHENTICATED); | ||
7815 | + acx_l_transmit_assoc_req(adev); | ||
7816 | + } else | ||
7817 | + if (alg == WLAN_AUTH_ALG_SHAREDKEY) { | ||
7818 | + acx_l_transmit_authen3(adev, req); | ||
7819 | + } | ||
7820 | + } else { | ||
7821 | + printk("%s: auth FAILED: peer sent " | ||
7822 | + "response code %d (%s), " | ||
7823 | + "still waiting for authentication\n", | ||
7824 | + adev->ndev->name, | ||
7825 | + status, get_status_string(status)); | ||
7826 | + acx_set_status(adev, ACX_STATUS_2_WAIT_AUTH); | ||
7827 | + } | ||
7828 | + break; | ||
7829 | + case 3: | ||
7830 | + if (!ap) | ||
7831 | + break; | ||
7832 | + if ((clt->auth_alg != WLAN_AUTH_ALG_SHAREDKEY) | ||
7833 | + || (alg != WLAN_AUTH_ALG_SHAREDKEY) | ||
7834 | + || (clt->auth_step != 2)) | ||
7835 | + break; | ||
7836 | + chal = req->challenge; | ||
7837 | + if (!chal | ||
7838 | + || memcmp(chal->challenge, clt->challenge_text, WLAN_CHALLENGE_LEN) | ||
7839 | + || (chal->eid != WLAN_EID_CHALLENGE) | ||
7840 | + || (chal->len != WLAN_CHALLENGE_LEN) | ||
7841 | + ) | ||
7842 | + break; | ||
7843 | + acx_l_transmit_authen4(adev, req); | ||
7844 | + MAC_COPY(clt->address, hdr->a2); | ||
7845 | + clt->used = CLIENT_AUTHENTICATED_2; | ||
7846 | + clt->auth_step = 4; | ||
7847 | + clt->seq = ieee2host16(hdr->seq); | ||
7848 | + break; | ||
7849 | + case 4: | ||
7850 | + if (ap) | ||
7851 | + break; | ||
7852 | + /* ok, we're through: we're authenticated. Woohoo!! */ | ||
7853 | + acx_set_status(adev, ACX_STATUS_3_AUTHENTICATED); | ||
7854 | + log(L_ASSOC, "Authenticated!\n"); | ||
7855 | + /* now that we're authenticated, request association */ | ||
7856 | + acx_l_transmit_assoc_req(adev); | ||
7857 | + break; | ||
7858 | + } | ||
7859 | + result = OK; | ||
7860 | +end: | ||
7861 | + FN_EXIT1(result); | ||
7862 | + return result; | ||
7863 | +} | ||
7864 | + | ||
7865 | + | ||
7866 | +/*********************************************************************** | ||
7867 | +** acx_gen_challenge | ||
7868 | +*/ | ||
7869 | +static inline void | ||
7870 | +acx_gen_challenge(wlan_ie_challenge_t* d) | ||
7871 | +{ | ||
7872 | + FN_ENTER; | ||
7873 | + d->eid = WLAN_EID_CHALLENGE; | ||
7874 | + d->len = WLAN_CHALLENGE_LEN; | ||
7875 | + get_random_bytes(d->challenge, WLAN_CHALLENGE_LEN); | ||
7876 | + FN_EXIT0; | ||
7877 | +} | ||
7878 | + | ||
7879 | + | ||
7880 | +/*********************************************************************** | ||
7881 | +** acx_l_transmit_deauthen | ||
7882 | +*/ | ||
7883 | +static int | ||
7884 | +acx_l_transmit_deauthen(acx_device_t *adev, const u8 *addr, u16 reason) | ||
7885 | +{ | ||
7886 | + struct tx *tx; | ||
7887 | + struct wlan_hdr_mgmt *head; | ||
7888 | + struct deauthen_frame_body *body; | ||
7889 | + | ||
7890 | + FN_ENTER; | ||
7891 | + | ||
7892 | + tx = acx_l_alloc_tx(adev); | ||
7893 | + if (!tx) | ||
7894 | + goto bad; | ||
7895 | + head = acx_l_get_txbuf(adev, tx); | ||
7896 | + if (!head) { | ||
7897 | + acx_l_dealloc_tx(adev, tx); | ||
7898 | + goto bad; | ||
7899 | + } | ||
7900 | + body = (void*)(head + 1); | ||
7901 | + | ||
7902 | + head->fc = (WF_FTYPE_MGMTi | WF_FSTYPE_DEAUTHENi); | ||
7903 | + head->dur = 0; | ||
7904 | + MAC_COPY(head->da, addr); | ||
7905 | + MAC_COPY(head->sa, adev->dev_addr); | ||
7906 | + MAC_COPY(head->bssid, adev->bssid); | ||
7907 | + head->seq = 0; | ||
7908 | + | ||
7909 | + log(L_DEBUG|L_ASSOC|L_XFER, | ||
7910 | + "sending deauthen to "MACSTR" for %d\n", | ||
7911 | + MAC(addr), reason); | ||
7912 | + | ||
7913 | + body->reason = host2ieee16(reason); | ||
7914 | + | ||
7915 | + /* body is fixed size here, but beware of cutting-and-pasting this - | ||
7916 | + ** do not use sizeof(*body) for variable sized mgmt packets! */ | ||
7917 | + acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + sizeof(*body)); | ||
7918 | + | ||
7919 | + FN_EXIT1(OK); | ||
7920 | + return OK; | ||
7921 | +bad: | ||
7922 | + FN_EXIT1(NOT_OK); | ||
7923 | + return NOT_OK; | ||
7924 | +} | ||
7925 | + | ||
7926 | + | ||
7927 | +/*********************************************************************** | ||
7928 | +** acx_l_transmit_authen1 | ||
7929 | +*/ | ||
7930 | +static int | ||
7931 | +acx_l_transmit_authen1(acx_device_t *adev) | ||
7932 | +{ | ||
7933 | + struct tx *tx; | ||
7934 | + struct wlan_hdr_mgmt *head; | ||
7935 | + struct auth_frame_body *body; | ||
7936 | + | ||
7937 | + FN_ENTER; | ||
7938 | + | ||
7939 | + log(L_ASSOC, "sending authentication1 request (auth algo %d), " | ||
7940 | + "awaiting response\n", adev->auth_alg); | ||
7941 | + | ||
7942 | + tx = acx_l_alloc_tx(adev); | ||
7943 | + if (!tx) | ||
7944 | + goto bad; | ||
7945 | + head = acx_l_get_txbuf(adev, tx); | ||
7946 | + if (!head) { | ||
7947 | + acx_l_dealloc_tx(adev, tx); | ||
7948 | + goto bad; | ||
7949 | + } | ||
7950 | + body = (void*)(head + 1); | ||
7951 | + | ||
7952 | + head->fc = WF_FSTYPE_AUTHENi; | ||
7953 | + /* duration should be 0 instead of 0x8000 to have | ||
7954 | + * the firmware calculate the value, right? */ | ||
7955 | + head->dur = 0; | ||
7956 | + MAC_COPY(head->da, adev->bssid); | ||
7957 | + MAC_COPY(head->sa, adev->dev_addr); | ||
7958 | + MAC_COPY(head->bssid, adev->bssid); | ||
7959 | + head->seq = 0; | ||
7960 | + | ||
7961 | + body->auth_alg = host2ieee16(adev->auth_alg); | ||
7962 | + body->auth_seq = host2ieee16(1); | ||
7963 | + body->status = host2ieee16(0); | ||
7964 | + | ||
7965 | + acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2); | ||
7966 | + | ||
7967 | + FN_EXIT1(OK); | ||
7968 | + return OK; | ||
7969 | +bad: | ||
7970 | + FN_EXIT1(NOT_OK); | ||
7971 | + return NOT_OK; | ||
7972 | +} | ||
7973 | + | ||
7974 | + | ||
7975 | +/*********************************************************************** | ||
7976 | +** acx_l_transmit_authen2 | ||
7977 | +*/ | ||
7978 | +static int | ||
7979 | +acx_l_transmit_authen2(acx_device_t *adev, const wlan_fr_authen_t *req, | ||
7980 | + client_t *clt) | ||
7981 | +{ | ||
7982 | + struct tx *tx; | ||
7983 | + struct wlan_hdr_mgmt *head; | ||
7984 | + struct auth_frame_body *body; | ||
7985 | + unsigned int packet_len; | ||
7986 | + | ||
7987 | + FN_ENTER; | ||
7988 | + | ||
7989 | + if (!clt) | ||
7990 | + goto ok; | ||
7991 | + | ||
7992 | + MAC_COPY(clt->address, req->hdr->a2); | ||
7993 | +#ifdef UNUSED | ||
7994 | + clt->ps = ((WF_FC_PWRMGTi & req->hdr->fc) != 0); | ||
7995 | +#endif | ||
7996 | + clt->auth_alg = ieee2host16(*(req->auth_alg)); | ||
7997 | + clt->auth_step = 2; | ||
7998 | + clt->seq = ieee2host16(req->hdr->seq); | ||
7999 | + | ||
8000 | + tx = acx_l_alloc_tx(adev); | ||
8001 | + if (!tx) | ||
8002 | + goto bad; | ||
8003 | + head = acx_l_get_txbuf(adev, tx); | ||
8004 | + if (!head) { | ||
8005 | + acx_l_dealloc_tx(adev, tx); | ||
8006 | + goto bad; | ||
8007 | + } | ||
8008 | + body = (void*)(head + 1); | ||
8009 | + | ||
8010 | + head->fc = WF_FSTYPE_AUTHENi; | ||
8011 | + head->dur = 0 /* req->hdr->dur */; | ||
8012 | + MAC_COPY(head->da, req->hdr->a2); | ||
8013 | + MAC_COPY(head->sa, adev->dev_addr); | ||
8014 | + MAC_COPY(head->bssid, req->hdr->a3); | ||
8015 | + head->seq = 0 /* req->hdr->seq */; | ||
8016 | + | ||
8017 | + /* already in IEEE format, no endianness conversion */ | ||
8018 | + body->auth_alg = *(req->auth_alg); | ||
8019 | + body->auth_seq = host2ieee16(2); | ||
8020 | + body->status = host2ieee16(0); | ||
8021 | + | ||
8022 | + packet_len = WLAN_HDR_A3_LEN + 2 + 2 + 2; | ||
8023 | + if (ieee2host16(*(req->auth_alg)) == WLAN_AUTH_ALG_OPENSYSTEM) { | ||
8024 | + clt->used = CLIENT_AUTHENTICATED_2; | ||
8025 | + } else { /* shared key */ | ||
8026 | + acx_gen_challenge(&body->challenge); | ||
8027 | + memcpy(&clt->challenge_text, body->challenge.challenge, WLAN_CHALLENGE_LEN); | ||
8028 | + packet_len += 2 + 2 + 2 + 1+1+WLAN_CHALLENGE_LEN; | ||
8029 | + } | ||
8030 | + | ||
8031 | + acxlog_mac(L_ASSOC|L_XFER, | ||
8032 | + "transmit_auth2: BSSID=", head->bssid, "\n"); | ||
8033 | + | ||
8034 | + acx_l_tx_data(adev, tx, packet_len); | ||
8035 | +ok: | ||
8036 | + FN_EXIT1(OK); | ||
8037 | + return OK; | ||
8038 | +bad: | ||
8039 | + FN_EXIT1(NOT_OK); | ||
8040 | + return NOT_OK; | ||
8041 | +} | ||
8042 | + | ||
8043 | + | ||
8044 | +/*********************************************************************** | ||
8045 | +** acx_l_transmit_authen3 | ||
8046 | +*/ | ||
8047 | +static int | ||
8048 | +acx_l_transmit_authen3(acx_device_t *adev, const wlan_fr_authen_t *req) | ||
8049 | +{ | ||
8050 | + struct tx *tx; | ||
8051 | + struct wlan_hdr_mgmt *head; | ||
8052 | + struct auth_frame_body *body; | ||
8053 | + unsigned int packet_len; | ||
8054 | + | ||
8055 | + FN_ENTER; | ||
8056 | + | ||
8057 | + tx = acx_l_alloc_tx(adev); | ||
8058 | + if (!tx) | ||
8059 | + goto ok; | ||
8060 | + head = acx_l_get_txbuf(adev, tx); | ||
8061 | + if (!head) { | ||
8062 | + acx_l_dealloc_tx(adev, tx); | ||
8063 | + goto ok; | ||
8064 | + } | ||
8065 | + body = (void*)(head + 1); | ||
8066 | + | ||
8067 | + /* add WF_FC_ISWEPi: auth step 3 needs to be encrypted */ | ||
8068 | + head->fc = WF_FC_ISWEPi + WF_FSTYPE_AUTHENi; | ||
8069 | + /* FIXME: is this needed?? authen4 does it... | ||
8070 | + * I think it's even wrong since we shouldn't re-use old | ||
8071 | + * values but instead let the firmware calculate proper ones | ||
8072 | + head->dur = req->hdr->dur; | ||
8073 | + head->seq = req->hdr->seq; | ||
8074 | + */ | ||
8075 | + MAC_COPY(head->da, adev->bssid); | ||
8076 | + MAC_COPY(head->sa, adev->dev_addr); | ||
8077 | + MAC_COPY(head->bssid, adev->bssid); | ||
8078 | + | ||
8079 | + /* already in IEEE format, no endianness conversion */ | ||
8080 | + body->auth_alg = *(req->auth_alg); | ||
8081 | + body->auth_seq = host2ieee16(3); | ||
8082 | + body->status = host2ieee16(0); | ||
8083 | + memcpy(&body->challenge, req->challenge, req->challenge->len + 2); | ||
8084 | + packet_len = WLAN_HDR_A3_LEN + 8 + req->challenge->len; | ||
8085 | + | ||
8086 | + log(L_ASSOC|L_XFER, "transmit_authen3!\n"); | ||
8087 | + | ||
8088 | + acx_l_tx_data(adev, tx, packet_len); | ||
8089 | +ok: | ||
8090 | + FN_EXIT1(OK); | ||
8091 | + return OK; | ||
8092 | +} | ||
8093 | + | ||
8094 | + | ||
8095 | +/*********************************************************************** | ||
8096 | +** acx_l_transmit_authen4 | ||
8097 | +*/ | ||
8098 | +static int | ||
8099 | +acx_l_transmit_authen4(acx_device_t *adev, const wlan_fr_authen_t *req) | ||
8100 | +{ | ||
8101 | + struct tx *tx; | ||
8102 | + struct wlan_hdr_mgmt *head; | ||
8103 | + struct auth_frame_body *body; | ||
8104 | + | ||
8105 | + FN_ENTER; | ||
8106 | + | ||
8107 | + tx = acx_l_alloc_tx(adev); | ||
8108 | + if (!tx) | ||
8109 | + goto ok; | ||
8110 | + head = acx_l_get_txbuf(adev, tx); | ||
8111 | + if (!head) { | ||
8112 | + acx_l_dealloc_tx(adev, tx); | ||
8113 | + goto ok; | ||
8114 | + } | ||
8115 | + body = (void*)(head + 1); | ||
8116 | + | ||
8117 | + head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */ | ||
8118 | + head->dur = 0 /* req->hdr->dur */; | ||
8119 | + MAC_COPY(head->da, req->hdr->a2); | ||
8120 | + MAC_COPY(head->sa, adev->dev_addr); | ||
8121 | + MAC_COPY(head->bssid, req->hdr->a3); | ||
8122 | + head->seq = 0 /* req->hdr->seq */; | ||
8123 | + | ||
8124 | + /* already in IEEE format, no endianness conversion */ | ||
8125 | + body->auth_alg = *(req->auth_alg); | ||
8126 | + body->auth_seq = host2ieee16(4); | ||
8127 | + body->status = host2ieee16(0); | ||
8128 | + | ||
8129 | + acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2); | ||
8130 | +ok: | ||
8131 | + FN_EXIT1(OK); | ||
8132 | + return OK; | ||
8133 | +} | ||
8134 | + | ||
8135 | + | ||
8136 | +/*********************************************************************** | ||
8137 | +** acx_l_transmit_assoc_req | ||
8138 | +** | ||
8139 | +** adev->ap_client is a current candidate AP here | ||
8140 | +*/ | ||
8141 | +static int | ||
8142 | +acx_l_transmit_assoc_req(acx_device_t *adev) | ||
8143 | +{ | ||
8144 | + struct tx *tx; | ||
8145 | + struct wlan_hdr_mgmt *head; | ||
8146 | + u8 *body, *p, *prate; | ||
8147 | + unsigned int packet_len; | ||
8148 | + u16 cap; | ||
8149 | + | ||
8150 | + FN_ENTER; | ||
8151 | + | ||
8152 | + log(L_ASSOC, "sending association request, " | ||
8153 | + "awaiting response. NOT ASSOCIATED YET\n"); | ||
8154 | + tx = acx_l_alloc_tx(adev); | ||
8155 | + if (!tx) | ||
8156 | + goto bad; | ||
8157 | + head = acx_l_get_txbuf(adev, tx); | ||
8158 | + if (!head) { | ||
8159 | + acx_l_dealloc_tx(adev, tx); | ||
8160 | + goto bad; | ||
8161 | + } | ||
8162 | + body = (void*)(head + 1); | ||
8163 | + | ||
8164 | + head->fc = WF_FSTYPE_ASSOCREQi; | ||
8165 | + head->dur = host2ieee16(0x8000); | ||
8166 | + MAC_COPY(head->da, adev->bssid); | ||
8167 | + MAC_COPY(head->sa, adev->dev_addr); | ||
8168 | + MAC_COPY(head->bssid, adev->bssid); | ||
8169 | + head->seq = 0; | ||
8170 | + | ||
8171 | + p = body; | ||
8172 | + /* now start filling the AssocReq frame body */ | ||
8173 | + | ||
8174 | + /* since this assoc request will most likely only get | ||
8175 | + * sent in the STA to AP case (and not when Ad-Hoc IBSS), | ||
8176 | + * the cap combination indicated here will thus be | ||
8177 | + * WF_MGMT_CAP_ESSi *always* (no IBSS ever) | ||
8178 | + * The specs are more than non-obvious on all that: | ||
8179 | + * | ||
8180 | + * 802.11 7.3.1.4 Capability Information field | ||
8181 | + ** APs set the ESS subfield to 1 and the IBSS subfield to 0 within | ||
8182 | + ** Beacon or Probe Response management frames. STAs within an IBSS | ||
8183 | + ** set the ESS subfield to 0 and the IBSS subfield to 1 in transmitted | ||
8184 | + ** Beacon or Probe Response management frames | ||
8185 | + ** | ||
8186 | + ** APs set the Privacy subfield to 1 within transmitted Beacon, | ||
8187 | + ** Probe Response, Association Response, and Reassociation Response | ||
8188 | + ** if WEP is required for all data type frames within the BSS. | ||
8189 | + ** STAs within an IBSS set the Privacy subfield to 1 in Beacon | ||
8190 | + ** or Probe Response management frames if WEP is required | ||
8191 | + ** for all data type frames within the IBSS */ | ||
8192 | + | ||
8193 | + /* note that returning 0 will be refused by several APs... | ||
8194 | + * (so this indicates that you're probably supposed to | ||
8195 | + * "confirm" the ESS mode) */ | ||
8196 | + cap = WF_MGMT_CAP_ESSi; | ||
8197 | + | ||
8198 | + /* this one used to be a check on wep_restricted, | ||
8199 | + * but more likely it's wep_enabled instead */ | ||
8200 | + if (adev->wep_enabled) | ||
8201 | + SET_BIT(cap, WF_MGMT_CAP_PRIVACYi); | ||
8202 | + | ||
8203 | + /* Probably we can just set these always, because our hw is | ||
8204 | + ** capable of shortpre and PBCC --vda */ | ||
8205 | + /* only ask for short preamble if the peer station supports it */ | ||
8206 | + if (adev->ap_client->cap_info & WF_MGMT_CAP_SHORT) | ||
8207 | + SET_BIT(cap, WF_MGMT_CAP_SHORTi); | ||
8208 | + /* only ask for PBCC support if the peer station supports it */ | ||
8209 | + if (adev->ap_client->cap_info & WF_MGMT_CAP_PBCC) | ||
8210 | + SET_BIT(cap, WF_MGMT_CAP_PBCCi); | ||
8211 | + | ||
8212 | + /* IEs: 1. caps */ | ||
8213 | + *(u16*)p = cap; p += 2; | ||
8214 | + /* 2. listen interval */ | ||
8215 | + *(u16*)p = host2ieee16(adev->listen_interval); p += 2; | ||
8216 | + /* 3. ESSID */ | ||
8217 | + p = wlan_fill_ie_ssid(p, | ||
8218 | + strlen(adev->essid_for_assoc), adev->essid_for_assoc); | ||
8219 | + /* 4. supp rates */ | ||
8220 | + prate = p; | ||
8221 | + p = wlan_fill_ie_rates(p, | ||
8222 | + adev->rate_supported_len, adev->rate_supported); | ||
8223 | + /* 5. ext supp rates */ | ||
8224 | + p = wlan_fill_ie_rates_ext(p, | ||
8225 | + adev->rate_supported_len, adev->rate_supported); | ||
8226 | + | ||
8227 | + if (acx_debug & L_DEBUG) { | ||
8228 | + printk("association: rates element\n"); | ||
8229 | + acx_dump_bytes(prate, p - prate); | ||
8230 | + } | ||
8231 | + | ||
8232 | + /* calculate lengths */ | ||
8233 | + packet_len = WLAN_HDR_A3_LEN + (p - body); | ||
8234 | + | ||
8235 | + log(L_ASSOC, "association: requesting caps 0x%04X, ESSID \"%s\"\n", | ||
8236 | + cap, adev->essid_for_assoc); | ||
8237 | + | ||
8238 | + acx_l_tx_data(adev, tx, packet_len); | ||
8239 | + FN_EXIT1(OK); | ||
8240 | + return OK; | ||
8241 | +bad: | ||
8242 | + FN_EXIT1(NOT_OK); | ||
8243 | + return NOT_OK; | ||
8244 | +} | ||
8245 | + | ||
8246 | + | ||
8247 | +/*********************************************************************** | ||
8248 | +** acx_l_transmit_disassoc | ||
8249 | +** | ||
8250 | +** FIXME: looks like incomplete implementation of a helper: | ||
8251 | +** acx_l_transmit_disassoc(adev, clt) - kick this client (we're an AP) | ||
8252 | +** acx_l_transmit_disassoc(adev, NULL) - leave BSSID (we're a STA) | ||
8253 | +*/ | ||
8254 | +#ifdef BROKEN | ||
8255 | +int | ||
8256 | +acx_l_transmit_disassoc(acx_device_t *adev, client_t *clt) | ||
8257 | +{ | ||
8258 | + struct tx *tx; | ||
8259 | + struct wlan_hdr_mgmt *head; | ||
8260 | + struct disassoc_frame_body *body; | ||
8261 | + | ||
8262 | + FN_ENTER; | ||
8263 | +/* if (clt != NULL) { */ | ||
8264 | + tx = acx_l_alloc_tx(adev); | ||
8265 | + if (!tx) | ||
8266 | + goto bad; | ||
8267 | + head = acx_l_get_txbuf(adev, tx); | ||
8268 | + if (!head) { | ||
8269 | + acx_l_dealloc_tx(adev, tx); | ||
8270 | + goto bad; | ||
8271 | + } | ||
8272 | + body = (void*)(head + 1); | ||
8273 | + | ||
8274 | +/* clt->used = CLIENT_AUTHENTICATED_2; - not (yet?) associated */ | ||
8275 | + | ||
8276 | + head->fc = WF_FSTYPE_DISASSOCi; | ||
8277 | + head->dur = 0; | ||
8278 | + /* huh? It muchly depends on whether we're STA or AP... | ||
8279 | + ** sta->ap: da=bssid, sa=own, bssid=bssid | ||
8280 | + ** ap->sta: da=sta, sa=bssid, bssid=bssid. FIXME! */ | ||
8281 | + MAC_COPY(head->da, adev->bssid); | ||
8282 | + MAC_COPY(head->sa, adev->dev_addr); | ||
8283 | + MAC_COPY(head->bssid, adev->dev_addr); | ||
8284 | + head->seq = 0; | ||
8285 | + | ||
8286 | + /* "Class 3 frame received from nonassociated station." */ | ||
8287 | + body->reason = host2ieee16(7); | ||
8288 | + | ||
8289 | + /* fixed size struct, ok to sizeof */ | ||
8290 | + acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + sizeof(*body)); | ||
8291 | +/* } */ | ||
8292 | + FN_EXIT1(OK); | ||
8293 | + return OK; | ||
8294 | +bad: | ||
8295 | + FN_EXIT1(NOT_OK); | ||
8296 | + return NOT_OK; | ||
8297 | +} | ||
8298 | +#endif | ||
8299 | + | ||
8300 | + | ||
8301 | +/*********************************************************************** | ||
8302 | +** acx_s_complete_scan | ||
8303 | +** | ||
8304 | +** Called either from after_interrupt_task() if: | ||
8305 | +** 1) there was Scan_Complete IRQ, or | ||
8306 | +** 2) scanning expired in timer() | ||
8307 | +** We need to decide which ESS or IBSS to join. | ||
8308 | +** Iterates thru adev->sta_list: | ||
8309 | +** if adev->ap is not bcast, will join only specified | ||
8310 | +** ESS or IBSS with this bssid | ||
8311 | +** checks peers' caps for ESS/IBSS bit | ||
8312 | +** checks peers' SSID, allows exact match or hidden SSID | ||
8313 | +** If station to join is chosen: | ||
8314 | +** points adev->ap_client to the chosen struct client | ||
8315 | +** sets adev->essid_for_assoc for future assoc attempt | ||
8316 | +** Auth/assoc is not yet performed | ||
8317 | +** Returns OK if there is no need to restart scan | ||
8318 | +*/ | ||
8319 | +int | ||
8320 | +acx_s_complete_scan(acx_device_t *adev) | ||
8321 | +{ | ||
8322 | + struct client *bss; | ||
8323 | + unsigned long flags; | ||
8324 | + u16 needed_cap; | ||
8325 | + int i; | ||
8326 | + int idx_found = -1; | ||
8327 | + int result = OK; | ||
8328 | + | ||
8329 | + FN_ENTER; | ||
8330 | + | ||
8331 | + switch (adev->mode) { | ||
8332 | + case ACX_MODE_0_ADHOC: | ||
8333 | + needed_cap = WF_MGMT_CAP_IBSS; /* 2, we require Ad-Hoc */ | ||
8334 | + break; | ||
8335 | + case ACX_MODE_2_STA: | ||
8336 | + needed_cap = WF_MGMT_CAP_ESS; /* 1, we require Managed */ | ||
8337 | + break; | ||
8338 | + default: | ||
8339 | + printk("acx: driver bug: mode=%d in complete_scan()\n", adev->mode); | ||
8340 | + dump_stack(); | ||
8341 | + goto end; | ||
8342 | + } | ||
8343 | + | ||
8344 | + acx_lock(adev, flags); | ||
8345 | + | ||
8346 | + /* TODO: sta_iterator hiding implementation would be nice here... */ | ||
8347 | + | ||
8348 | + for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { | ||
8349 | + bss = &adev->sta_list[i]; | ||
8350 | + if (!bss->used) continue; | ||
8351 | + | ||
8352 | + | ||
8353 | + log(L_ASSOC, "scan table: SSID=\"%s\" CH=%d SIR=%d SNR=%d\n", | ||
8354 | + bss->essid, bss->channel, bss->sir, bss->snr); | ||
8355 | + | ||
8356 | + if (!mac_is_bcast(adev->ap)) | ||
8357 | + if (!mac_is_equal(bss->bssid, adev->ap)) | ||
8358 | + continue; /* keep looking */ | ||
8359 | + | ||
8360 | + /* broken peer with no mode flags set? */ | ||
8361 | + if (unlikely(!(bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)))) { | ||
8362 | + printk("%s: strange peer "MACSTR" found with " | ||
8363 | + "neither ESS (AP) nor IBSS (Ad-Hoc) " | ||
8364 | + "capability - skipped\n", | ||
8365 | + adev->ndev->name, MAC(bss->address)); | ||
8366 | + continue; | ||
8367 | + } | ||
8368 | + log(L_ASSOC, "peer_cap 0x%04X, needed_cap 0x%04X\n", | ||
8369 | + bss->cap_info, needed_cap); | ||
8370 | + | ||
8371 | + /* does peer station support what we need? */ | ||
8372 | + if ((bss->cap_info & needed_cap) != needed_cap) | ||
8373 | + continue; /* keep looking */ | ||
8374 | + | ||
8375 | + /* strange peer with NO basic rates?! */ | ||
8376 | + if (unlikely(!bss->rate_bas)) { | ||
8377 | + printk("%s: strange peer "MACSTR" with empty rate set " | ||
8378 | + "- skipped\n", | ||
8379 | + adev->ndev->name, MAC(bss->address)); | ||
8380 | + continue; | ||
8381 | + } | ||
8382 | + | ||
8383 | + /* do we support all basic rates of this peer? */ | ||
8384 | + if ((bss->rate_bas & adev->rate_oper) != bss->rate_bas) { | ||
8385 | +/* we probably need to have all rates as operational rates, | ||
8386 | + even in case of an 11M-only configuration */ | ||
8387 | +#ifdef THIS_IS_TROUBLESOME | ||
8388 | + printk("%s: peer "MACSTR": incompatible basic rates " | ||
8389 | + "(AP requests 0x%04X, we have 0x%04X) " | ||
8390 | + "- skipped\n", | ||
8391 | + adev->ndev->name, MAC(bss->address), | ||
8392 | + bss->rate_bas, adev->rate_oper); | ||
8393 | + continue; | ||
8394 | +#else | ||
8395 | + printk("%s: peer "MACSTR": incompatible basic rates " | ||
8396 | + "(AP requests 0x%04X, we have 0x%04X). " | ||
8397 | + "Considering anyway...\n", | ||
8398 | + adev->ndev->name, MAC(bss->address), | ||
8399 | + bss->rate_bas, adev->rate_oper); | ||
8400 | +#endif | ||
8401 | + } | ||
8402 | + | ||
8403 | + if ( !(adev->reg_dom_chanmask & (1<<(bss->channel-1))) ) { | ||
8404 | + printk("%s: warning: peer "MACSTR" is on channel %d " | ||
8405 | + "outside of channel range of current " | ||
8406 | + "regulatory domain - couldn't join " | ||
8407 | + "even if other settings match. " | ||
8408 | + "You might want to adapt your config\n", | ||
8409 | + adev->ndev->name, MAC(bss->address), | ||
8410 | + bss->channel); | ||
8411 | + continue; /* keep looking */ | ||
8412 | + } | ||
8413 | + | ||
8414 | + if (!adev->essid_active || !strcmp(bss->essid, adev->essid)) { | ||
8415 | + log(L_ASSOC, | ||
8416 | + "found station with matching ESSID! ('%s' " | ||
8417 | + "station, '%s' config)\n", | ||
8418 | + bss->essid, | ||
8419 | + (adev->essid_active) ? adev->essid : "[any]"); | ||
8420 | + /* TODO: continue looking for peer with better SNR */ | ||
8421 | + bss->used = CLIENT_JOIN_CANDIDATE; | ||
8422 | + idx_found = i; | ||
8423 | + | ||
8424 | + /* stop searching if this station is | ||
8425 | + * on the current channel, otherwise | ||
8426 | + * keep looking for an even better match */ | ||
8427 | + if (bss->channel == adev->channel) | ||
8428 | + break; | ||
8429 | + } else | ||
8430 | + if (is_hidden_essid(bss->essid)) { | ||
8431 | + /* hmm, station with empty or single-space SSID: | ||
8432 | + * using hidden SSID broadcast? | ||
8433 | + */ | ||
8434 | + /* This behaviour is broken: which AP from zillion | ||
8435 | + ** of APs with hidden SSID you'd try? | ||
8436 | + ** We should use Probe requests to get Probe responses | ||
8437 | + ** and check for real SSID (are those never hidden?) */ | ||
8438 | + bss->used = CLIENT_JOIN_CANDIDATE; | ||
8439 | + if (idx_found == -1) | ||
8440 | + idx_found = i; | ||
8441 | + log(L_ASSOC, "found station with empty or " | ||
8442 | + "single-space (hidden) SSID, considering " | ||
8443 | + "for assoc attempt\n"); | ||
8444 | + /* ...and keep looking for better matches */ | ||
8445 | + } else { | ||
8446 | + log(L_ASSOC, "ESSID doesn't match! ('%s' " | ||
8447 | + "station, '%s' config)\n", | ||
8448 | + bss->essid, | ||
8449 | + (adev->essid_active) ? adev->essid : "[any]"); | ||
8450 | + } | ||
8451 | + } | ||
8452 | + | ||
8453 | + /* TODO: iterate thru join candidates instead */ | ||
8454 | + /* TODO: rescan if not associated within some timeout */ | ||
8455 | + if (idx_found != -1) { | ||
8456 | + char *essid_src; | ||
8457 | + size_t essid_len; | ||
8458 | + | ||
8459 | + bss = &adev->sta_list[idx_found]; | ||
8460 | + adev->ap_client = bss; | ||
8461 | + | ||
8462 | + if (is_hidden_essid(bss->essid)) { | ||
8463 | + /* if the ESSID of the station we found is empty | ||
8464 | + * (no broadcast), then use user-configured ESSID | ||
8465 | + * instead */ | ||
8466 | + essid_src = adev->essid; | ||
8467 | + essid_len = adev->essid_len; | ||
8468 | + } else { | ||
8469 | + essid_src = bss->essid; | ||
8470 | + essid_len = strlen(bss->essid); | ||
8471 | + } | ||
8472 | + | ||
8473 | + acx_update_capabilities(adev); | ||
8474 | + | ||
8475 | + memcpy(adev->essid_for_assoc, essid_src, essid_len); | ||
8476 | + adev->essid_for_assoc[essid_len] = '\0'; | ||
8477 | + adev->channel = bss->channel; | ||
8478 | + MAC_COPY(adev->bssid, bss->bssid); | ||
8479 | + | ||
8480 | + bss->rate_cfg = (bss->rate_cap & adev->rate_oper); | ||
8481 | + bss->rate_cur = 1 << lowest_bit(bss->rate_cfg); | ||
8482 | + bss->rate_100 = acx_rate111to100(bss->rate_cur); | ||
8483 | + | ||
8484 | + acxlog_mac(L_ASSOC, | ||
8485 | + "matching station found: ", adev->bssid, ", joining\n"); | ||
8486 | + | ||
8487 | + /* TODO: do we need to switch to the peer's channel first? */ | ||
8488 | + | ||
8489 | + if (ACX_MODE_0_ADHOC == adev->mode) { | ||
8490 | + acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); | ||
8491 | + } else { | ||
8492 | + acx_l_transmit_authen1(adev); | ||
8493 | + acx_set_status(adev, ACX_STATUS_2_WAIT_AUTH); | ||
8494 | + } | ||
8495 | + } else { /* idx_found == -1 */ | ||
8496 | + /* uh oh, no station found in range */ | ||
8497 | + if (ACX_MODE_0_ADHOC == adev->mode) { | ||
8498 | + printk("%s: no matching station found in range, " | ||
8499 | + "generating our own IBSS instead\n", | ||
8500 | + adev->ndev->name); | ||
8501 | + /* we do it the HostAP way: */ | ||
8502 | + MAC_COPY(adev->bssid, adev->dev_addr); | ||
8503 | + adev->bssid[0] |= 0x02; /* 'local assigned addr' bit */ | ||
8504 | + /* add IBSS bit to our caps... */ | ||
8505 | + acx_update_capabilities(adev); | ||
8506 | + acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); | ||
8507 | + /* In order to cmd_join be called below */ | ||
8508 | + idx_found = 0; | ||
8509 | + } else { | ||
8510 | + /* we shall scan again, AP can be | ||
8511 | + ** just temporarily powered off */ | ||
8512 | + log(L_ASSOC, | ||
8513 | + "no matching station found in range yet\n"); | ||
8514 | + acx_set_status(adev, ACX_STATUS_1_SCANNING); | ||
8515 | + result = NOT_OK; | ||
8516 | + } | ||
8517 | + } | ||
8518 | + | ||
8519 | + acx_unlock(adev, flags); | ||
8520 | + | ||
8521 | + if (idx_found != -1) { | ||
8522 | + if (ACX_MODE_0_ADHOC == adev->mode) { | ||
8523 | + /* need to update channel in beacon template */ | ||
8524 | + SET_BIT(adev->set_mask, SET_TEMPLATES); | ||
8525 | + if (ACX_STATE_IFACE_UP & adev->dev_state_mask) | ||
8526 | + acx_s_update_card_settings(adev); | ||
8527 | + } | ||
8528 | + /* Inform firmware on our decision to start or join BSS */ | ||
8529 | + acx_s_cmd_join_bssid(adev, adev->bssid); | ||
8530 | + } | ||
8531 | + | ||
8532 | +end: | ||
8533 | + FN_EXIT1(result); | ||
8534 | + return result; | ||
8535 | +} | ||
8536 | + | ||
8537 | + | ||
8538 | +/*********************************************************************** | ||
8539 | +** acx_s_read_fw | ||
8540 | +** | ||
8541 | +** Loads a firmware image | ||
8542 | +** | ||
8543 | +** Returns: | ||
8544 | +** 0 unable to load file | ||
8545 | +** pointer to firmware success | ||
8546 | +*/ | ||
8547 | +firmware_image_t* | ||
8548 | +acx_s_read_fw(struct device *dev, const char *file, u32 *size) | ||
8549 | +{ | ||
8550 | + firmware_image_t *res; | ||
8551 | + const struct firmware *fw_entry; | ||
8552 | + | ||
8553 | + res = NULL; | ||
8554 | + log(L_INIT, "requesting firmware image '%s'\n", file); | ||
8555 | + if (!request_firmware(&fw_entry, file, dev)) { | ||
8556 | + *size = 8; | ||
8557 | + if (fw_entry->size >= 8) | ||
8558 | + *size = 8 + le32_to_cpu(*(u32 *)(fw_entry->data + 4)); | ||
8559 | + if (fw_entry->size != *size) { | ||
8560 | + printk("acx: firmware size does not match " | ||
8561 | + "firmware header: %d != %d, " | ||
8562 | + "aborting fw upload\n", | ||
8563 | + (int) fw_entry->size, (int) *size); | ||
8564 | + goto release_ret; | ||
8565 | + } | ||
8566 | + res = vmalloc(*size); | ||
8567 | + if (!res) { | ||
8568 | + printk("acx: no memory for firmware " | ||
8569 | + "(%u bytes)\n", *size); | ||
8570 | + goto release_ret; | ||
8571 | + } | ||
8572 | + memcpy(res, fw_entry->data, fw_entry->size); | ||
8573 | +release_ret: | ||
8574 | + release_firmware(fw_entry); | ||
8575 | + return res; | ||
8576 | + } | ||
8577 | + printk("acx: firmware image '%s' was not provided. " | ||
8578 | + "Check your hotplug scripts\n", file); | ||
8579 | + | ||
8580 | + /* checksum will be verified in write_fw, so don't bother here */ | ||
8581 | + return res; | ||
8582 | +} | ||
8583 | + | ||
8584 | + | ||
8585 | +/*********************************************************************** | ||
8586 | +** acx_s_set_wepkey | ||
8587 | +*/ | ||
8588 | +static void | ||
8589 | +acx100_s_set_wepkey(acx_device_t *adev) | ||
8590 | +{ | ||
8591 | + ie_dot11WEPDefaultKey_t dk; | ||
8592 | + int i; | ||
8593 | + | ||
8594 | + for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) { | ||
8595 | + if (adev->wep_keys[i].size != 0) { | ||
8596 | + log(L_INIT, "setting WEP key: %d with " | ||
8597 | + "total size: %d\n", i, (int) adev->wep_keys[i].size); | ||
8598 | + dk.action = 1; | ||
8599 | + dk.keySize = adev->wep_keys[i].size; | ||
8600 | + dk.defaultKeyNum = i; | ||
8601 | + memcpy(dk.key, adev->wep_keys[i].key, dk.keySize); | ||
8602 | + acx_s_configure(adev, &dk, ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE); | ||
8603 | + } | ||
8604 | + } | ||
8605 | +} | ||
8606 | + | ||
8607 | +static void | ||
8608 | +acx111_s_set_wepkey(acx_device_t *adev) | ||
8609 | +{ | ||
8610 | + acx111WEPDefaultKey_t dk; | ||
8611 | + int i; | ||
8612 | + | ||
8613 | + for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) { | ||
8614 | + if (adev->wep_keys[i].size != 0) { | ||
8615 | + log(L_INIT, "setting WEP key: %d with " | ||
8616 | + "total size: %d\n", i, (int) adev->wep_keys[i].size); | ||
8617 | + memset(&dk, 0, sizeof(dk)); | ||
8618 | + dk.action = cpu_to_le16(1); /* "add key"; yes, that's a 16bit value */ | ||
8619 | + dk.keySize = adev->wep_keys[i].size; | ||
8620 | + | ||
8621 | + /* are these two lines necessary? */ | ||
8622 | + dk.type = 0; /* default WEP key */ | ||
8623 | + dk.index = 0; /* ignored when setting default key */ | ||
8624 | + | ||
8625 | + dk.defaultKeyNum = i; | ||
8626 | + memcpy(dk.key, adev->wep_keys[i].key, dk.keySize); | ||
8627 | + acx_s_issue_cmd(adev, ACX1xx_CMD_WEP_MGMT, &dk, sizeof(dk)); | ||
8628 | + } | ||
8629 | + } | ||
8630 | +} | ||
8631 | + | ||
8632 | +static void | ||
8633 | +acx_s_set_wepkey(acx_device_t *adev) | ||
8634 | +{ | ||
8635 | + if (IS_ACX111(adev)) | ||
8636 | + acx111_s_set_wepkey(adev); | ||
8637 | + else | ||
8638 | + acx100_s_set_wepkey(adev); | ||
8639 | +} | ||
8640 | + | ||
8641 | + | ||
8642 | +/*********************************************************************** | ||
8643 | +** acx100_s_init_wep | ||
8644 | +** | ||
8645 | +** FIXME: this should probably be moved into the new card settings | ||
8646 | +** management, but since we're also modifying the memory map layout here | ||
8647 | +** due to the WEP key space we want, we should take care... | ||
8648 | +*/ | ||
8649 | +static int | ||
8650 | +acx100_s_init_wep(acx_device_t *adev) | ||
8651 | +{ | ||
8652 | + acx100_ie_wep_options_t options; | ||
8653 | + ie_dot11WEPDefaultKeyID_t dk; | ||
8654 | + acx_ie_memmap_t pt; | ||
8655 | + int res = NOT_OK; | ||
8656 | + | ||
8657 | + FN_ENTER; | ||
8658 | + | ||
8659 | + if (OK != acx_s_interrogate(adev, &pt, ACX1xx_IE_MEMORY_MAP)) { | ||
8660 | + goto fail; | ||
8661 | + } | ||
8662 | + | ||
8663 | + log(L_DEBUG, "CodeEnd:%X\n", pt.CodeEnd); | ||
8664 | + | ||
8665 | + pt.WEPCacheStart = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4); | ||
8666 | + pt.WEPCacheEnd = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4); | ||
8667 | + | ||
8668 | + if (OK != acx_s_configure(adev, &pt, ACX1xx_IE_MEMORY_MAP)) { | ||
8669 | + goto fail; | ||
8670 | + } | ||
8671 | + | ||
8672 | + /* let's choose maximum setting: 4 default keys, plus 10 other keys: */ | ||
8673 | + options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10); | ||
8674 | + options.WEPOption = 0x00; | ||
8675 | + | ||
8676 | + log(L_ASSOC, "%s: writing WEP options\n", __func__); | ||
8677 | + acx_s_configure(adev, &options, ACX100_IE_WEP_OPTIONS); | ||
8678 | + | ||
8679 | + acx100_s_set_wepkey(adev); | ||
8680 | + | ||
8681 | + if (adev->wep_keys[adev->wep_current_index].size != 0) { | ||
8682 | + log(L_ASSOC, "setting active default WEP key number: %d\n", | ||
8683 | + adev->wep_current_index); | ||
8684 | + dk.KeyID = adev->wep_current_index; | ||
8685 | + acx_s_configure(adev, &dk, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); /* 0x1010 */ | ||
8686 | + } | ||
8687 | + /* FIXME!!! wep_key_struct is filled nowhere! But adev | ||
8688 | + * is initialized to 0, and we don't REALLY need those keys either */ | ||
8689 | +/* for (i = 0; i < 10; i++) { | ||
8690 | + if (adev->wep_key_struct[i].len != 0) { | ||
8691 | + MAC_COPY(wep_mgmt.MacAddr, adev->wep_key_struct[i].addr); | ||
8692 | + wep_mgmt.KeySize = cpu_to_le16(adev->wep_key_struct[i].len); | ||
8693 | + memcpy(&wep_mgmt.Key, adev->wep_key_struct[i].key, le16_to_cpu(wep_mgmt.KeySize)); | ||
8694 | + wep_mgmt.Action = cpu_to_le16(1); | ||
8695 | + log(L_ASSOC, "writing WEP key %d (len %d)\n", i, le16_to_cpu(wep_mgmt.KeySize)); | ||
8696 | + if (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_WEP_MGMT, &wep_mgmt, sizeof(wep_mgmt))) { | ||
8697 | + adev->wep_key_struct[i].index = i; | ||
8698 | + } | ||
8699 | + } | ||
8700 | + } | ||
8701 | +*/ | ||
8702 | + | ||
8703 | + /* now retrieve the updated WEPCacheEnd pointer... */ | ||
8704 | + if (OK != acx_s_interrogate(adev, &pt, ACX1xx_IE_MEMORY_MAP)) { | ||
8705 | + printk("%s: ACX1xx_IE_MEMORY_MAP read #2 FAILED\n", | ||
8706 | + adev->ndev->name); | ||
8707 | + goto fail; | ||
8708 | + } | ||
8709 | + /* ...and tell it to start allocating templates at that location */ | ||
8710 | + /* (no endianness conversion needed) */ | ||
8711 | + pt.PacketTemplateStart = pt.WEPCacheEnd; | ||
8712 | + | ||
8713 | + if (OK != acx_s_configure(adev, &pt, ACX1xx_IE_MEMORY_MAP)) { | ||
8714 | + printk("%s: ACX1xx_IE_MEMORY_MAP write #2 FAILED\n", | ||
8715 | + adev->ndev->name); | ||
8716 | + goto fail; | ||
8717 | + } | ||
8718 | + res = OK; | ||
8719 | + | ||
8720 | +fail: | ||
8721 | + FN_EXIT1(res); | ||
8722 | + return res; | ||
8723 | +} | ||
8724 | + | ||
8725 | + | ||
8726 | +static int | ||
8727 | +acx_s_init_max_template_generic(acx_device_t *adev, unsigned int len, unsigned int cmd) | ||
8728 | +{ | ||
8729 | + int res; | ||
8730 | + union { | ||
8731 | + acx_template_nullframe_t null; | ||
8732 | + acx_template_beacon_t b; | ||
8733 | + acx_template_tim_t tim; | ||
8734 | + acx_template_probereq_t preq; | ||
8735 | + acx_template_proberesp_t presp; | ||
8736 | + } templ; | ||
8737 | + | ||
8738 | + memset(&templ, 0, len); | ||
8739 | + templ.null.size = cpu_to_le16(len - 2); | ||
8740 | + res = acx_s_issue_cmd(adev, cmd, &templ, len); | ||
8741 | + return res; | ||
8742 | +} | ||
8743 | + | ||
8744 | +static inline int | ||
8745 | +acx_s_init_max_null_data_template(acx_device_t *adev) | ||
8746 | +{ | ||
8747 | + return acx_s_init_max_template_generic( | ||
8748 | + adev, sizeof(acx_template_nullframe_t), ACX1xx_CMD_CONFIG_NULL_DATA | ||
8749 | + ); | ||
8750 | +} | ||
8751 | + | ||
8752 | +static inline int | ||
8753 | +acx_s_init_max_beacon_template(acx_device_t *adev) | ||
8754 | +{ | ||
8755 | + return acx_s_init_max_template_generic( | ||
8756 | + adev, sizeof(acx_template_beacon_t), ACX1xx_CMD_CONFIG_BEACON | ||
8757 | + ); | ||
8758 | +} | ||
8759 | + | ||
8760 | +static inline int | ||
8761 | +acx_s_init_max_tim_template(acx_device_t *adev) | ||
8762 | +{ | ||
8763 | + return acx_s_init_max_template_generic( | ||
8764 | + adev, sizeof(acx_template_tim_t), ACX1xx_CMD_CONFIG_TIM | ||
8765 | + ); | ||
8766 | +} | ||
8767 | + | ||
8768 | +static inline int | ||
8769 | +acx_s_init_max_probe_response_template(acx_device_t *adev) | ||
8770 | +{ | ||
8771 | + return acx_s_init_max_template_generic( | ||
8772 | + adev, sizeof(acx_template_proberesp_t), ACX1xx_CMD_CONFIG_PROBE_RESPONSE | ||
8773 | + ); | ||
8774 | +} | ||
8775 | + | ||
8776 | +static inline int | ||
8777 | +acx_s_init_max_probe_request_template(acx_device_t *adev) | ||
8778 | +{ | ||
8779 | + return acx_s_init_max_template_generic( | ||
8780 | + adev, sizeof(acx_template_probereq_t), ACX1xx_CMD_CONFIG_PROBE_REQUEST | ||
8781 | + ); | ||
8782 | +} | ||
8783 | + | ||
8784 | +/*********************************************************************** | ||
8785 | +** acx_s_set_tim_template | ||
8786 | +** | ||
8787 | +** FIXME: In full blown driver we will regularly update partial virtual bitmap | ||
8788 | +** by calling this function | ||
8789 | +** (it can be done by irq handler on each DTIM irq or by timer...) | ||
8790 | + | ||
8791 | +[802.11 7.3.2.6] TIM information element: | ||
8792 | +- 1 EID | ||
8793 | +- 1 Length | ||
8794 | +1 1 DTIM Count | ||
8795 | + indicates how many beacons (including this) appear before next DTIM | ||
8796 | + (0=this one is a DTIM) | ||
8797 | +2 1 DTIM Period | ||
8798 | + number of beacons between successive DTIMs | ||
8799 | + (0=reserved, 1=all TIMs are DTIMs, 2=every other, etc) | ||
8800 | +3 1 Bitmap Control | ||
8801 | + bit0: Traffic Indicator bit associated with Assoc ID 0 (Bcast AID?) | ||
8802 | + set to 1 in TIM elements with a value of 0 in the DTIM Count field | ||
8803 | + when one or more broadcast or multicast frames are buffered at the AP. | ||
8804 | + bit1-7: Bitmap Offset (logically Bitmap_Offset = Bitmap_Control & 0xFE). | ||
8805 | +4 n Partial Virtual Bitmap | ||
8806 | + Visible part of traffic-indication bitmap. | ||
8807 | + Full bitmap consists of 2008 bits (251 octets) such that bit number N | ||
8808 | + (0<=N<=2007) in the bitmap corresponds to bit number (N mod 8) | ||
8809 | + in octet number N/8 where the low-order bit of each octet is bit0, | ||
8810 | + and the high order bit is bit7. | ||
8811 | + Each set bit in virtual bitmap corresponds to traffic buffered by AP | ||
8812 | + for a specific station (with corresponding AID?). | ||
8813 | + Partial Virtual Bitmap shows a part of bitmap which has non-zero. | ||
8814 | + Bitmap Offset is a number of skipped zero octets (see above). | ||
8815 | + 'Missing' octets at the tail are also assumed to be zero. | ||
8816 | + Example: Length=6, Bitmap_Offset=2, Partial_Virtual_Bitmap=55 55 55 | ||
8817 | + This means that traffic-indication bitmap is: | ||
8818 | + 00000000 00000000 01010101 01010101 01010101 00000000 00000000... | ||
8819 | + (is bit0 in the map is always 0 and real value is in Bitmap Control bit0?) | ||
8820 | +*/ | ||
8821 | +static int | ||
8822 | +acx_s_set_tim_template(acx_device_t *adev) | ||
8823 | +{ | ||
8824 | +/* For now, configure smallish test bitmap, all zero ("no pending data") */ | ||
8825 | + enum { bitmap_size = 5 }; | ||
8826 | + | ||
8827 | + acx_template_tim_t t; | ||
8828 | + int result; | ||
8829 | + | ||
8830 | + FN_ENTER; | ||
8831 | + | ||
8832 | + memset(&t, 0, sizeof(t)); | ||
8833 | + t.size = 5 + bitmap_size; /* eid+len+count+period+bmap_ctrl + bmap */ | ||
8834 | + t.tim_eid = WLAN_EID_TIM; | ||
8835 | + t.len = 3 + bitmap_size; /* count+period+bmap_ctrl + bmap */ | ||
8836 | + result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t)); | ||
8837 | + FN_EXIT1(result); | ||
8838 | + return result; | ||
8839 | +} | ||
8840 | + | ||
8841 | + | ||
8842 | +/*********************************************************************** | ||
8843 | +** acx_fill_beacon_or_proberesp_template | ||
8844 | +** | ||
8845 | +** For frame format info, please see 802.11-1999.pdf item 7.2.3.9 and below!! | ||
8846 | +** | ||
8847 | +** NB: we use the fact that | ||
8848 | +** struct acx_template_proberesp and struct acx_template_beacon are the same | ||
8849 | +** (well, almost...) | ||
8850 | +** | ||
8851 | +** [802.11] Beacon's body consist of these IEs: | ||
8852 | +** 1 Timestamp | ||
8853 | +** 2 Beacon interval | ||
8854 | +** 3 Capability information | ||
8855 | +** 4 SSID | ||
8856 | +** 5 Supported rates (up to 8 rates) | ||
8857 | +** 6 FH Parameter Set (frequency-hopping PHYs only) | ||
8858 | +** 7 DS Parameter Set (direct sequence PHYs only) | ||
8859 | +** 8 CF Parameter Set (only if PCF is supported) | ||
8860 | +** 9 IBSS Parameter Set (ad-hoc only) | ||
8861 | +** | ||
8862 | +** Beacon only: | ||
8863 | +** 10 TIM (AP only) (see 802.11 7.3.2.6) | ||
8864 | +** 11 Country Information (802.11d) | ||
8865 | +** 12 FH Parameters (802.11d) | ||
8866 | +** 13 FH Pattern Table (802.11d) | ||
8867 | +** ... (?!! did not yet find relevant PDF file... --vda) | ||
8868 | +** 19 ERP Information (extended rate PHYs) | ||
8869 | +** 20 Extended Supported Rates (if more than 8 rates) | ||
8870 | +** | ||
8871 | +** Proberesp only: | ||
8872 | +** 10 Country information (802.11d) | ||
8873 | +** 11 FH Parameters (802.11d) | ||
8874 | +** 12 FH Pattern Table (802.11d) | ||
8875 | +** 13-n Requested information elements (802.11d) | ||
8876 | +** ???? | ||
8877 | +** 18 ERP Information (extended rate PHYs) | ||
8878 | +** 19 Extended Supported Rates (if more than 8 rates) | ||
8879 | +*/ | ||
8880 | +static int | ||
8881 | +acx_fill_beacon_or_proberesp_template(acx_device_t *adev, | ||
8882 | + struct acx_template_beacon *templ, | ||
8883 | + u16 fc /* in host order! */) | ||
8884 | +{ | ||
8885 | + int len; | ||
8886 | + u8 *p; | ||
8887 | + | ||
8888 | + FN_ENTER; | ||
8889 | + | ||
8890 | + memset(templ, 0, sizeof(*templ)); | ||
8891 | + MAC_BCAST(templ->da); | ||
8892 | + MAC_COPY(templ->sa, adev->dev_addr); | ||
8893 | + MAC_COPY(templ->bssid, adev->bssid); | ||
8894 | + | ||
8895 | + templ->beacon_interval = cpu_to_le16(adev->beacon_interval); | ||
8896 | + acx_update_capabilities(adev); | ||
8897 | + templ->cap = cpu_to_le16(adev->capabilities); | ||
8898 | + | ||
8899 | + p = templ->variable; | ||
8900 | + p = wlan_fill_ie_ssid(p, adev->essid_len, adev->essid); | ||
8901 | + p = wlan_fill_ie_rates(p, adev->rate_supported_len, adev->rate_supported); | ||
8902 | + p = wlan_fill_ie_ds_parms(p, adev->channel); | ||
8903 | + /* NB: should go AFTER tim, but acx seem to keep tim last always */ | ||
8904 | + p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, adev->rate_supported); | ||
8905 | + | ||
8906 | + switch (adev->mode) { | ||
8907 | + case ACX_MODE_0_ADHOC: | ||
8908 | + /* ATIM window */ | ||
8909 | + p = wlan_fill_ie_ibss_parms(p, 0); break; | ||
8910 | + case ACX_MODE_3_AP: | ||
8911 | + /* TIM IE is set up as separate template */ | ||
8912 | + break; | ||
8913 | + } | ||
8914 | + | ||
8915 | + len = p - (u8*)templ; | ||
8916 | + templ->fc = cpu_to_le16(WF_FTYPE_MGMT | fc); | ||
8917 | + /* - 2: do not count 'u16 size' field */ | ||
8918 | + templ->size = cpu_to_le16(len - 2); | ||
8919 | + | ||
8920 | + FN_EXIT1(len); | ||
8921 | + return len; | ||
8922 | +} | ||
8923 | + | ||
8924 | + | ||
8925 | +#if POWER_SAVE_80211 | ||
8926 | +/*********************************************************************** | ||
8927 | +** acx_s_set_null_data_template | ||
8928 | +*/ | ||
8929 | +static int | ||
8930 | +acx_s_set_null_data_template(acx_device_t *adev) | ||
8931 | +{ | ||
8932 | + struct acx_template_nullframe b; | ||
8933 | + int result; | ||
8934 | + | ||
8935 | + FN_ENTER; | ||
8936 | + | ||
8937 | + /* memset(&b, 0, sizeof(b)); not needed, setting all members */ | ||
8938 | + | ||
8939 | + b.size = cpu_to_le16(sizeof(b) - 2); | ||
8940 | + b.hdr.fc = WF_FTYPE_MGMTi | WF_FSTYPE_NULLi; | ||
8941 | + b.hdr.dur = 0; | ||
8942 | + MAC_BCAST(b.hdr.a1); | ||
8943 | + MAC_COPY(b.hdr.a2, adev->dev_addr); | ||
8944 | + MAC_COPY(b.hdr.a3, adev->bssid); | ||
8945 | + b.hdr.seq = 0; | ||
8946 | + | ||
8947 | + result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_NULL_DATA, &b, sizeof(b)); | ||
8948 | + | ||
8949 | + FN_EXIT1(result); | ||
8950 | + return result; | ||
8951 | +} | ||
8952 | +#endif | ||
8953 | + | ||
8954 | + | ||
8955 | +/*********************************************************************** | ||
8956 | +** acx_s_set_beacon_template | ||
8957 | +*/ | ||
8958 | +static int | ||
8959 | +acx_s_set_beacon_template(acx_device_t *adev) | ||
8960 | +{ | ||
8961 | + struct acx_template_beacon bcn; | ||
8962 | + int len, result; | ||
8963 | + | ||
8964 | + FN_ENTER; | ||
8965 | + | ||
8966 | + len = acx_fill_beacon_or_proberesp_template(adev, &bcn, WF_FSTYPE_BEACON); | ||
8967 | + result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_BEACON, &bcn, len); | ||
8968 | + | ||
8969 | + FN_EXIT1(result); | ||
8970 | + return result; | ||
8971 | +} | ||
8972 | + | ||
8973 | + | ||
8974 | +/*********************************************************************** | ||
8975 | +** acx_s_set_probe_response_template | ||
8976 | +*/ | ||
8977 | +static int | ||
8978 | +acx_s_set_probe_response_template(acx_device_t *adev) | ||
8979 | +{ | ||
8980 | + struct acx_template_proberesp pr; | ||
8981 | + int len, result; | ||
8982 | + | ||
8983 | + FN_ENTER; | ||
8984 | + | ||
8985 | + len = acx_fill_beacon_or_proberesp_template(adev, &pr, WF_FSTYPE_PROBERESP); | ||
8986 | + result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, len); | ||
8987 | + | ||
8988 | + FN_EXIT1(result); | ||
8989 | + return result; | ||
8990 | +} | ||
8991 | + | ||
8992 | + | ||
8993 | +/*********************************************************************** | ||
8994 | +** acx_s_init_packet_templates() | ||
8995 | +** | ||
8996 | +** NOTE: order is very important here, to have a correct memory layout! | ||
8997 | +** init templates: max Probe Request (station mode), max NULL data, | ||
8998 | +** max Beacon, max TIM, max Probe Response. | ||
8999 | +*/ | ||
9000 | +static int | ||
9001 | +acx_s_init_packet_templates(acx_device_t *adev) | ||
9002 | +{ | ||
9003 | + acx_ie_memmap_t mm; /* ACX100 only */ | ||
9004 | + int result = NOT_OK; | ||
9005 | + | ||
9006 | + FN_ENTER; | ||
9007 | + | ||
9008 | + log(L_DEBUG|L_INIT, "initializing max packet templates\n"); | ||
9009 | + | ||
9010 | + if (OK != acx_s_init_max_probe_request_template(adev)) | ||
9011 | + goto failed; | ||
9012 | + | ||
9013 | + if (OK != acx_s_init_max_null_data_template(adev)) | ||
9014 | + goto failed; | ||
9015 | + | ||
9016 | + if (OK != acx_s_init_max_beacon_template(adev)) | ||
9017 | + goto failed; | ||
9018 | + | ||
9019 | + if (OK != acx_s_init_max_tim_template(adev)) | ||
9020 | + goto failed; | ||
9021 | + | ||
9022 | + if (OK != acx_s_init_max_probe_response_template(adev)) | ||
9023 | + goto failed; | ||
9024 | + | ||
9025 | + if (IS_ACX111(adev)) { | ||
9026 | + /* ACX111 doesn't need the memory map magic below, | ||
9027 | + * and the other templates will be set later (acx_start) */ | ||
9028 | + result = OK; | ||
9029 | + goto success; | ||
9030 | + } | ||
9031 | + | ||
9032 | + /* ACX100 will have its TIM template set, | ||
9033 | + * and we also need to update the memory map */ | ||
9034 | + | ||
9035 | + if (OK != acx_s_set_tim_template(adev)) | ||
9036 | + goto failed_acx100; | ||
9037 | + | ||
9038 | + log(L_DEBUG, "sizeof(memmap)=%d bytes\n", (int)sizeof(mm)); | ||
9039 | + | ||
9040 | + if (OK != acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP)) | ||
9041 | + goto failed_acx100; | ||
9042 | + | ||
9043 | + mm.QueueStart = cpu_to_le32(le32_to_cpu(mm.PacketTemplateEnd) + 4); | ||
9044 | + if (OK != acx_s_configure(adev, &mm, ACX1xx_IE_MEMORY_MAP)) | ||
9045 | + goto failed_acx100; | ||
9046 | + | ||
9047 | + result = OK; | ||
9048 | + goto success; | ||
9049 | + | ||
9050 | +failed_acx100: | ||
9051 | + log(L_DEBUG|L_INIT, | ||
9052 | + /* "cb=0x%X\n" */ | ||
9053 | + "ACXMemoryMap:\n" | ||
9054 | + ".CodeStart=0x%X\n" | ||
9055 | + ".CodeEnd=0x%X\n" | ||
9056 | + ".WEPCacheStart=0x%X\n" | ||
9057 | + ".WEPCacheEnd=0x%X\n" | ||
9058 | + ".PacketTemplateStart=0x%X\n" | ||
9059 | + ".PacketTemplateEnd=0x%X\n", | ||
9060 | + /* len, */ | ||
9061 | + le32_to_cpu(mm.CodeStart), | ||
9062 | + le32_to_cpu(mm.CodeEnd), | ||
9063 | + le32_to_cpu(mm.WEPCacheStart), | ||
9064 | + le32_to_cpu(mm.WEPCacheEnd), | ||
9065 | + le32_to_cpu(mm.PacketTemplateStart), | ||
9066 | + le32_to_cpu(mm.PacketTemplateEnd)); | ||
9067 | + | ||
9068 | +failed: | ||
9069 | + printk("%s: %s() FAILED\n", adev->ndev->name, __func__); | ||
9070 | + | ||
9071 | +success: | ||
9072 | + FN_EXIT1(result); | ||
9073 | + return result; | ||
9074 | +} | ||
9075 | + | ||
9076 | + | ||
9077 | +/*********************************************************************** | ||
9078 | +*/ | ||
9079 | +static int | ||
9080 | +acx_s_set_probe_request_template(acx_device_t *adev) | ||
9081 | +{ | ||
9082 | + struct acx_template_probereq probereq; | ||
9083 | + char *p; | ||
9084 | + int res; | ||
9085 | + int frame_len; | ||
9086 | + | ||
9087 | + FN_ENTER; | ||
9088 | + | ||
9089 | + memset(&probereq, 0, sizeof(probereq)); | ||
9090 | + | ||
9091 | + probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi; | ||
9092 | + MAC_BCAST(probereq.da); | ||
9093 | + MAC_COPY(probereq.sa, adev->dev_addr); | ||
9094 | + MAC_BCAST(probereq.bssid); | ||
9095 | + | ||
9096 | + p = probereq.variable; | ||
9097 | + p = wlan_fill_ie_ssid(p, adev->essid_len, adev->essid); | ||
9098 | + p = wlan_fill_ie_rates(p, adev->rate_supported_len, adev->rate_supported); | ||
9099 | + p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, adev->rate_supported); | ||
9100 | + frame_len = p - (char*)&probereq; | ||
9101 | + probereq.size = cpu_to_le16(frame_len - 2); | ||
9102 | + | ||
9103 | + res = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len); | ||
9104 | + FN_EXIT0; | ||
9105 | + return res; | ||
9106 | +} | ||
9107 | + | ||
9108 | + | ||
9109 | +/*********************************************************************** | ||
9110 | +** acx_s_init_mac | ||
9111 | +*/ | ||
9112 | +int | ||
9113 | +acx_s_init_mac(acx_device_t *adev) | ||
9114 | +{ | ||
9115 | + int result = NOT_OK; | ||
9116 | + | ||
9117 | + FN_ENTER; | ||
9118 | + | ||
9119 | + if (IS_ACX111(adev)) { | ||
9120 | + adev->ie_len = acx111_ie_len; | ||
9121 | + adev->ie_len_dot11 = acx111_ie_len_dot11; | ||
9122 | + } else { | ||
9123 | + adev->ie_len = acx100_ie_len; | ||
9124 | + adev->ie_len_dot11 = acx100_ie_len_dot11; | ||
9125 | + } | ||
9126 | + | ||
9127 | +#if defined (ACX_MEM) | ||
9128 | + adev->memblocksize = 256; /* 256 is default */ | ||
9129 | + /* try to load radio for both ACX100 and ACX111, since both | ||
9130 | + * chips have at least some firmware versions making use of an | ||
9131 | + * external radio module */ | ||
9132 | + acxmem_s_upload_radio(adev); | ||
9133 | +#else | ||
9134 | + if (IS_PCI(adev)) { | ||
9135 | + adev->memblocksize = 256; /* 256 is default */ | ||
9136 | + /* try to load radio for both ACX100 and ACX111, since both | ||
9137 | + * chips have at least some firmware versions making use of an | ||
9138 | + * external radio module */ | ||
9139 | + acxpci_s_upload_radio(adev); | ||
9140 | + } else { | ||
9141 | + adev->memblocksize = 128; | ||
9142 | + } | ||
9143 | +#endif | ||
9144 | + | ||
9145 | + if (IS_ACX111(adev)) { | ||
9146 | + /* for ACX111, the order is different from ACX100 | ||
9147 | + 1. init packet templates | ||
9148 | + 2. create station context and create dma regions | ||
9149 | + 3. init wep default keys | ||
9150 | + */ | ||
9151 | + if (OK != acx_s_init_packet_templates(adev)) | ||
9152 | + goto fail; | ||
9153 | + if (OK != acx111_s_create_dma_regions(adev)) { | ||
9154 | + printk("%s: acx111_create_dma_regions FAILED\n", | ||
9155 | + adev->ndev->name); | ||
9156 | + goto fail; | ||
9157 | + } | ||
9158 | + } else { | ||
9159 | + if (OK != acx100_s_init_wep(adev)) | ||
9160 | + goto fail; | ||
9161 | + if (OK != acx_s_init_packet_templates(adev)) | ||
9162 | + goto fail; | ||
9163 | + if (OK != acx100_s_create_dma_regions(adev)) { | ||
9164 | + printk("%s: acx100_create_dma_regions FAILED\n", | ||
9165 | + adev->ndev->name); | ||
9166 | + goto fail; | ||
9167 | + } | ||
9168 | + } | ||
9169 | + | ||
9170 | + MAC_COPY(adev->ndev->dev_addr, adev->dev_addr); | ||
9171 | + result = OK; | ||
9172 | + | ||
9173 | +fail: | ||
9174 | + if (result) | ||
9175 | + printk("acx: init_mac() FAILED\n"); | ||
9176 | + FN_EXIT1(result); | ||
9177 | + return result; | ||
9178 | +} | ||
9179 | + | ||
9180 | + | ||
9181 | +void | ||
9182 | +acx_s_set_sane_reg_domain(acx_device_t *adev, int do_set) | ||
9183 | +{ | ||
9184 | + unsigned mask; | ||
9185 | + | ||
9186 | + unsigned int i; | ||
9187 | + | ||
9188 | + for (i = 0; i < sizeof(acx_reg_domain_ids); i++) | ||
9189 | + if (acx_reg_domain_ids[i] == adev->reg_dom_id) | ||
9190 | + break; | ||
9191 | + | ||
9192 | + if (sizeof(acx_reg_domain_ids) == i) { | ||
9193 | + log(L_INIT, "Invalid or unsupported regulatory domain" | ||
9194 | + " 0x%02X specified, falling back to FCC (USA)!" | ||
9195 | + " Please report if this sounds fishy!\n", | ||
9196 | + adev->reg_dom_id); | ||
9197 | + i = 0; | ||
9198 | + adev->reg_dom_id = acx_reg_domain_ids[i]; | ||
9199 | + | ||
9200 | + /* since there was a mismatch, we need to force updating */ | ||
9201 | + do_set = 1; | ||
9202 | + } | ||
9203 | + | ||
9204 | + if (do_set) { | ||
9205 | + acx_ie_generic_t dom; | ||
9206 | + dom.m.bytes[0] = adev->reg_dom_id; | ||
9207 | + acx_s_configure(adev, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN); | ||
9208 | + } | ||
9209 | + | ||
9210 | + adev->reg_dom_chanmask = reg_domain_channel_masks[i]; | ||
9211 | + | ||
9212 | + mask = (1 << (adev->channel - 1)); | ||
9213 | + if (!(adev->reg_dom_chanmask & mask)) { | ||
9214 | + /* hmm, need to adjust our channel to reside within domain */ | ||
9215 | + mask = 1; | ||
9216 | + for (i = 1; i <= 14; i++) { | ||
9217 | + if (adev->reg_dom_chanmask & mask) { | ||
9218 | + printk("%s: adjusting selected channel from %d " | ||
9219 | + "to %d due to new regulatory domain\n", | ||
9220 | + adev->ndev->name, adev->channel, i); | ||
9221 | + adev->channel = i; | ||
9222 | + break; | ||
9223 | + } | ||
9224 | + mask <<= 1; | ||
9225 | + } | ||
9226 | + } | ||
9227 | +} | ||
9228 | + | ||
9229 | + | ||
9230 | +#if POWER_SAVE_80211 | ||
9231 | +static void | ||
9232 | +acx_s_update_80211_powersave_mode(acx_device_t *adev) | ||
9233 | +{ | ||
9234 | + /* merge both structs in a union to be able to have common code */ | ||
9235 | + union { | ||
9236 | + acx111_ie_powersave_t acx111; | ||
9237 | + acx100_ie_powersave_t acx100; | ||
9238 | + } pm; | ||
9239 | + | ||
9240 | + /* change 802.11 power save mode settings */ | ||
9241 | + log(L_INIT, "updating 802.11 power save mode settings: " | ||
9242 | + "wakeup_cfg 0x%02X, listen interval %u, " | ||
9243 | + "options 0x%02X, hangover period %u, " | ||
9244 | + "enhanced_ps_transition_time %u\n", | ||
9245 | + adev->ps_wakeup_cfg, adev->ps_listen_interval, | ||
9246 | + adev->ps_options, adev->ps_hangover_period, | ||
9247 | + adev->ps_enhanced_transition_time); | ||
9248 | + acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT); | ||
9249 | + log(L_INIT, "Previous PS mode settings: wakeup_cfg 0x%02X, " | ||
9250 | + "listen interval %u, options 0x%02X, " | ||
9251 | + "hangover period %u, " | ||
9252 | + "enhanced_ps_transition_time %u, beacon_rx_time %u\n", | ||
9253 | + pm.acx111.wakeup_cfg, | ||
9254 | + pm.acx111.listen_interval, | ||
9255 | + pm.acx111.options, | ||
9256 | + pm.acx111.hangover_period, | ||
9257 | + IS_ACX111(adev) ? | ||
9258 | + pm.acx111.enhanced_ps_transition_time | ||
9259 | + : pm.acx100.enhanced_ps_transition_time, | ||
9260 | + IS_ACX111(adev) ? | ||
9261 | + pm.acx111.beacon_rx_time | ||
9262 | + : (u32)-1 | ||
9263 | + ); | ||
9264 | + pm.acx111.wakeup_cfg = adev->ps_wakeup_cfg; | ||
9265 | + pm.acx111.listen_interval = adev->ps_listen_interval; | ||
9266 | + pm.acx111.options = adev->ps_options; | ||
9267 | + pm.acx111.hangover_period = adev->ps_hangover_period; | ||
9268 | + if (IS_ACX111(adev)) { | ||
9269 | + pm.acx111.beacon_rx_time = cpu_to_le32(adev->ps_beacon_rx_time); | ||
9270 | + pm.acx111.enhanced_ps_transition_time = cpu_to_le32(adev->ps_enhanced_transition_time); | ||
9271 | + } else { | ||
9272 | + pm.acx100.enhanced_ps_transition_time = cpu_to_le16(adev->ps_enhanced_transition_time); | ||
9273 | + } | ||
9274 | + acx_s_configure(adev, &pm, ACX1xx_IE_POWER_MGMT); | ||
9275 | + acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT); | ||
9276 | + log(L_INIT, "wakeup_cfg: 0x%02X\n", pm.acx111.wakeup_cfg); | ||
9277 | + acx_s_msleep(40); | ||
9278 | + acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT); | ||
9279 | + log(L_INIT, "wakeup_cfg: 0x%02X\n", pm.acx111.wakeup_cfg); | ||
9280 | + log(L_INIT, "power save mode change %s\n", | ||
9281 | + (pm.acx111.wakeup_cfg & PS_CFG_PENDING) ? "FAILED" : "was successful"); | ||
9282 | + /* FIXME: maybe verify via PS_CFG_PENDING bit here | ||
9283 | + * that power save mode change was successful. */ | ||
9284 | + /* FIXME: we shouldn't trigger a scan immediately after | ||
9285 | + * fiddling with power save mode (since the firmware is sending | ||
9286 | + * a NULL frame then). */ | ||
9287 | +} | ||
9288 | +#endif | ||
9289 | + | ||
9290 | + | ||
9291 | +/*********************************************************************** | ||
9292 | +** acx_s_update_card_settings | ||
9293 | +** | ||
9294 | +** Applies accumulated changes in various adev->xxxx members | ||
9295 | +** Called by ioctl commit handler, acx_start, acx_set_defaults, | ||
9296 | +** acx_s_after_interrupt_task (if IRQ_CMD_UPDATE_CARD_CFG), | ||
9297 | +*/ | ||
9298 | +static void | ||
9299 | +acx111_s_sens_radio_16_17(acx_device_t *adev) | ||
9300 | +{ | ||
9301 | + u32 feature1, feature2; | ||
9302 | + | ||
9303 | + if ((adev->sensitivity < 1) || (adev->sensitivity > 3)) { | ||
9304 | + printk("%s: invalid sensitivity setting (1..3), " | ||
9305 | + "setting to 1\n", adev->ndev->name); | ||
9306 | + adev->sensitivity = 1; | ||
9307 | + } | ||
9308 | + acx111_s_get_feature_config(adev, &feature1, &feature2); | ||
9309 | + CLEAR_BIT(feature1, FEATURE1_LOW_RX|FEATURE1_EXTRA_LOW_RX); | ||
9310 | + if (adev->sensitivity > 1) | ||
9311 | + SET_BIT(feature1, FEATURE1_LOW_RX); | ||
9312 | + if (adev->sensitivity > 2) | ||
9313 | + SET_BIT(feature1, FEATURE1_EXTRA_LOW_RX); | ||
9314 | + acx111_s_feature_set(adev, feature1, feature2); | ||
9315 | +} | ||
9316 | + | ||
9317 | + | ||
9318 | +void | ||
9319 | +acx_s_update_card_settings(acx_device_t *adev) | ||
9320 | +{ | ||
9321 | + unsigned long flags; | ||
9322 | + unsigned int start_scan = 0; | ||
9323 | + int i; | ||
9324 | + | ||
9325 | + FN_ENTER; | ||
9326 | + | ||
9327 | + log(L_INIT, "get_mask 0x%08X, set_mask 0x%08X\n", | ||
9328 | + adev->get_mask, adev->set_mask); | ||
9329 | + | ||
9330 | + /* Track dependencies betweed various settings */ | ||
9331 | + | ||
9332 | + if (adev->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_WEP)) { | ||
9333 | + log(L_INIT, "important setting has been changed. " | ||
9334 | + "Need to update packet templates, too\n"); | ||
9335 | + SET_BIT(adev->set_mask, SET_TEMPLATES); | ||
9336 | + } | ||
9337 | + if (adev->set_mask & GETSET_CHANNEL) { | ||
9338 | + /* This will actually tune RX/TX to the channel */ | ||
9339 | + SET_BIT(adev->set_mask, GETSET_RX|GETSET_TX); | ||
9340 | + switch (adev->mode) { | ||
9341 | + case ACX_MODE_0_ADHOC: | ||
9342 | + case ACX_MODE_3_AP: | ||
9343 | + /* Beacons contain channel# - update them */ | ||
9344 | + SET_BIT(adev->set_mask, SET_TEMPLATES); | ||
9345 | + } | ||
9346 | + switch (adev->mode) { | ||
9347 | + case ACX_MODE_0_ADHOC: | ||
9348 | + case ACX_MODE_2_STA: | ||
9349 | + start_scan = 1; | ||
9350 | + } | ||
9351 | + } | ||
9352 | + | ||
9353 | + /* Apply settings */ | ||
9354 | + | ||
9355 | +#ifdef WHY_SHOULD_WE_BOTHER /* imagine we were just powered off */ | ||
9356 | + /* send a disassoc request in case it's required */ | ||
9357 | + if (adev->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_CHANNEL|GETSET_WEP)) { | ||
9358 | + if (ACX_MODE_2_STA == adev->mode) { | ||
9359 | + if (ACX_STATUS_4_ASSOCIATED == adev->status) { | ||
9360 | + log(L_ASSOC, "we were ASSOCIATED - " | ||
9361 | + "sending disassoc request\n"); | ||
9362 | + acx_lock(adev, flags); | ||
9363 | + acx_l_transmit_disassoc(adev, NULL); | ||
9364 | + /* FIXME: deauth? */ | ||
9365 | + acx_unlock(adev, flags); | ||
9366 | + } | ||
9367 | + /* need to reset some other stuff as well */ | ||
9368 | + log(L_DEBUG, "resetting bssid\n"); | ||
9369 | + MAC_ZERO(adev->bssid); | ||
9370 | + SET_BIT(adev->set_mask, SET_TEMPLATES|SET_STA_LIST); | ||
9371 | + start_scan = 1; | ||
9372 | + } | ||
9373 | + } | ||
9374 | +#endif | ||
9375 | + | ||
9376 | + if (adev->get_mask & GETSET_STATION_ID) { | ||
9377 | + u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN]; | ||
9378 | + const u8 *paddr; | ||
9379 | + | ||
9380 | + acx_s_interrogate(adev, &stationID, ACX1xx_IE_DOT11_STATION_ID); | ||
9381 | + paddr = &stationID[4]; | ||
9382 | + for (i = 0; i < ETH_ALEN; i++) { | ||
9383 | + /* we copy the MAC address (reversed in | ||
9384 | + * the card) to the netdevice's MAC | ||
9385 | + * address, and on ifup it will be | ||
9386 | + * copied into iwadev->dev_addr */ | ||
9387 | + adev->ndev->dev_addr[ETH_ALEN - 1 - i] = paddr[i]; | ||
9388 | + } | ||
9389 | + CLEAR_BIT(adev->get_mask, GETSET_STATION_ID); | ||
9390 | + } | ||
9391 | + | ||
9392 | + if (adev->get_mask & GETSET_SENSITIVITY) { | ||
9393 | + if ((RADIO_RFMD_11 == adev->radio_type) | ||
9394 | + || (RADIO_MAXIM_0D == adev->radio_type) | ||
9395 | + || (RADIO_RALINK_15 == adev->radio_type)) { | ||
9396 | + acx_s_read_phy_reg(adev, 0x30, &adev->sensitivity); | ||
9397 | + } else { | ||
9398 | + log(L_INIT, "don't know how to get sensitivity " | ||
9399 | + "for radio type 0x%02X\n", adev->radio_type); | ||
9400 | + adev->sensitivity = 0; | ||
9401 | + } | ||
9402 | + log(L_INIT, "got sensitivity value %u\n", adev->sensitivity); | ||
9403 | + | ||
9404 | + CLEAR_BIT(adev->get_mask, GETSET_SENSITIVITY); | ||
9405 | + } | ||
9406 | + | ||
9407 | + if (adev->get_mask & GETSET_ANTENNA) { | ||
9408 | + u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN]; | ||
9409 | + | ||
9410 | + memset(antenna, 0, sizeof(antenna)); | ||
9411 | + acx_s_interrogate(adev, antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA); | ||
9412 | + adev->antenna = antenna[4]; | ||
9413 | + log(L_INIT, "got antenna value 0x%02X\n", adev->antenna); | ||
9414 | + CLEAR_BIT(adev->get_mask, GETSET_ANTENNA); | ||
9415 | + } | ||
9416 | + | ||
9417 | + if (adev->get_mask & GETSET_ED_THRESH) { | ||
9418 | + if (IS_ACX100(adev)) { | ||
9419 | + u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN]; | ||
9420 | + | ||
9421 | + memset(ed_threshold, 0, sizeof(ed_threshold)); | ||
9422 | + acx_s_interrogate(adev, ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD); | ||
9423 | + adev->ed_threshold = ed_threshold[4]; | ||
9424 | + } else { | ||
9425 | + log(L_INIT, "acx111 doesn't support ED\n"); | ||
9426 | + adev->ed_threshold = 0; | ||
9427 | + } | ||
9428 | + log(L_INIT, "got Energy Detect (ED) threshold %u\n", adev->ed_threshold); | ||
9429 | + CLEAR_BIT(adev->get_mask, GETSET_ED_THRESH); | ||
9430 | + } | ||
9431 | + | ||
9432 | + if (adev->get_mask & GETSET_CCA) { | ||
9433 | + if (IS_ACX100(adev)) { | ||
9434 | + u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN]; | ||
9435 | + | ||
9436 | + memset(cca, 0, sizeof(adev->cca)); | ||
9437 | + acx_s_interrogate(adev, cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE); | ||
9438 | + adev->cca = cca[4]; | ||
9439 | + } else { | ||
9440 | + log(L_INIT, "acx111 doesn't support CCA\n"); | ||
9441 | + adev->cca = 0; | ||
9442 | + } | ||
9443 | + log(L_INIT, "got Channel Clear Assessment (CCA) value %u\n", adev->cca); | ||
9444 | + CLEAR_BIT(adev->get_mask, GETSET_CCA); | ||
9445 | + } | ||
9446 | + | ||
9447 | + if (adev->get_mask & GETSET_REG_DOMAIN) { | ||
9448 | + acx_ie_generic_t dom; | ||
9449 | + | ||
9450 | + acx_s_interrogate(adev, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN); | ||
9451 | + adev->reg_dom_id = dom.m.bytes[0]; | ||
9452 | + acx_s_set_sane_reg_domain(adev, 0); | ||
9453 | + log(L_INIT, "got regulatory domain 0x%02X\n", adev->reg_dom_id); | ||
9454 | + CLEAR_BIT(adev->get_mask, GETSET_REG_DOMAIN); | ||
9455 | + } | ||
9456 | + | ||
9457 | + if (adev->set_mask & GETSET_STATION_ID) { | ||
9458 | + u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN]; | ||
9459 | + u8 *paddr; | ||
9460 | + | ||
9461 | + paddr = &stationID[4]; | ||
9462 | + memcpy(adev->dev_addr, adev->ndev->dev_addr, ETH_ALEN); | ||
9463 | + for (i = 0; i < ETH_ALEN; i++) { | ||
9464 | + /* copy the MAC address we obtained when we noticed | ||
9465 | + * that the ethernet iface's MAC changed | ||
9466 | + * to the card (reversed in | ||
9467 | + * the card!) */ | ||
9468 | + paddr[i] = adev->dev_addr[ETH_ALEN - 1 - i]; | ||
9469 | + } | ||
9470 | + acx_s_configure(adev, &stationID, ACX1xx_IE_DOT11_STATION_ID); | ||
9471 | + CLEAR_BIT(adev->set_mask, GETSET_STATION_ID); | ||
9472 | + } | ||
9473 | + | ||
9474 | + if (adev->set_mask & SET_TEMPLATES) { | ||
9475 | + log(L_INIT, "updating packet templates\n"); | ||
9476 | + switch (adev->mode) { | ||
9477 | + case ACX_MODE_2_STA: | ||
9478 | + acx_s_set_probe_request_template(adev); | ||
9479 | +#if POWER_SAVE_80211 | ||
9480 | + acx_s_set_null_data_template(adev); | ||
9481 | +#endif | ||
9482 | + break; | ||
9483 | + case ACX_MODE_0_ADHOC: | ||
9484 | + acx_s_set_probe_request_template(adev); | ||
9485 | +#if POWER_SAVE_80211 | ||
9486 | + /* maybe power save functionality is somehow possible | ||
9487 | + * for Ad-Hoc mode, too... FIXME: verify it somehow? firmware debug fields? */ | ||
9488 | + acx_s_set_null_data_template(adev); | ||
9489 | +#endif | ||
9490 | + /* fall through */ | ||
9491 | + case ACX_MODE_3_AP: | ||
9492 | + acx_s_set_beacon_template(adev); | ||
9493 | + acx_s_set_tim_template(adev); | ||
9494 | + /* BTW acx111 firmware would not send probe responses | ||
9495 | + ** if probe request does not have all basic rates flagged | ||
9496 | + ** by 0x80! Thus firmware does not conform to 802.11, | ||
9497 | + ** it should ignore 0x80 bit in ratevector from STA. | ||
9498 | + ** We can 'fix' it by not using this template and | ||
9499 | + ** sending probe responses by hand. TODO --vda */ | ||
9500 | + acx_s_set_probe_response_template(adev); | ||
9501 | + } | ||
9502 | + /* Needed if generated frames are to be emitted at different tx rate now */ | ||
9503 | + log(L_IRQ, "redoing cmd_join_bssid() after template cfg\n"); | ||
9504 | + acx_s_cmd_join_bssid(adev, adev->bssid); | ||
9505 | + CLEAR_BIT(adev->set_mask, SET_TEMPLATES); | ||
9506 | + } | ||
9507 | + if (adev->set_mask & SET_STA_LIST) { | ||
9508 | + acx_lock(adev, flags); | ||
9509 | + acx_l_sta_list_init(adev); | ||
9510 | + CLEAR_BIT(adev->set_mask, SET_STA_LIST); | ||
9511 | + acx_unlock(adev, flags); | ||
9512 | + } | ||
9513 | + if (adev->set_mask & SET_RATE_FALLBACK) { | ||
9514 | + u8 rate[4 + ACX1xx_IE_RATE_FALLBACK_LEN]; | ||
9515 | + | ||
9516 | + /* configure to not do fallbacks when not in auto rate mode */ | ||
9517 | + rate[4] = (adev->rate_auto) ? /* adev->txrate_fallback_retries */ 1 : 0; | ||
9518 | + log(L_INIT, "updating Tx fallback to %u retries\n", rate[4]); | ||
9519 | + acx_s_configure(adev, &rate, ACX1xx_IE_RATE_FALLBACK); | ||
9520 | + CLEAR_BIT(adev->set_mask, SET_RATE_FALLBACK); | ||
9521 | + } | ||
9522 | + if (adev->set_mask & GETSET_TXPOWER) { | ||
9523 | + log(L_INIT, "updating transmit power: %u dBm\n", | ||
9524 | + adev->tx_level_dbm); | ||
9525 | + acx_s_set_tx_level(adev, adev->tx_level_dbm); | ||
9526 | + CLEAR_BIT(adev->set_mask, GETSET_TXPOWER); | ||
9527 | + } | ||
9528 | + | ||
9529 | + if (adev->set_mask & GETSET_SENSITIVITY) { | ||
9530 | + log(L_INIT, "updating sensitivity value: %u\n", | ||
9531 | + adev->sensitivity); | ||
9532 | + switch (adev->radio_type) { | ||
9533 | + case RADIO_RFMD_11: | ||
9534 | + case RADIO_MAXIM_0D: | ||
9535 | + case RADIO_RALINK_15: | ||
9536 | + acx_s_write_phy_reg(adev, 0x30, adev->sensitivity); | ||
9537 | + break; | ||
9538 | + case RADIO_RADIA_16: | ||
9539 | + case RADIO_UNKNOWN_17: | ||
9540 | + acx111_s_sens_radio_16_17(adev); | ||
9541 | + break; | ||
9542 | + default: | ||
9543 | + log(L_INIT, "don't know how to modify sensitivity " | ||
9544 | + "for radio type 0x%02X\n", adev->radio_type); | ||
9545 | + } | ||
9546 | + CLEAR_BIT(adev->set_mask, GETSET_SENSITIVITY); | ||
9547 | + } | ||
9548 | + | ||
9549 | + if (adev->set_mask & GETSET_ANTENNA) { | ||
9550 | + /* antenna */ | ||
9551 | + u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN]; | ||
9552 | + | ||
9553 | + memset(antenna, 0, sizeof(antenna)); | ||
9554 | + antenna[4] = adev->antenna; | ||
9555 | + log(L_INIT, "updating antenna value: 0x%02X\n", | ||
9556 | + adev->antenna); | ||
9557 | + acx_s_configure(adev, &antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA); | ||
9558 | + CLEAR_BIT(adev->set_mask, GETSET_ANTENNA); | ||
9559 | + } | ||
9560 | + | ||
9561 | + if (adev->set_mask & GETSET_ED_THRESH) { | ||
9562 | + /* ed_threshold */ | ||
9563 | + log(L_INIT, "updating Energy Detect (ED) threshold: %u\n", | ||
9564 | + adev->ed_threshold); | ||
9565 | + if (IS_ACX100(adev)) { | ||
9566 | + u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN]; | ||
9567 | + | ||
9568 | + memset(ed_threshold, 0, sizeof(ed_threshold)); | ||
9569 | + ed_threshold[4] = adev->ed_threshold; | ||
9570 | + acx_s_configure(adev, &ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD); | ||
9571 | + } | ||
9572 | + else | ||
9573 | + log(L_INIT, "acx111 doesn't support ED!\n"); | ||
9574 | + CLEAR_BIT(adev->set_mask, GETSET_ED_THRESH); | ||
9575 | + } | ||
9576 | + | ||
9577 | + if (adev->set_mask & GETSET_CCA) { | ||
9578 | + /* CCA value */ | ||
9579 | + log(L_INIT, "updating Channel Clear Assessment " | ||
9580 | + "(CCA) value: 0x%02X\n", adev->cca); | ||
9581 | + if (IS_ACX100(adev)) { | ||
9582 | + u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN]; | ||
9583 | + | ||
9584 | + memset(cca, 0, sizeof(cca)); | ||
9585 | + cca[4] = adev->cca; | ||
9586 | + acx_s_configure(adev, &cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE); | ||
9587 | + } | ||
9588 | + else | ||
9589 | + log(L_INIT, "acx111 doesn't support CCA!\n"); | ||
9590 | + CLEAR_BIT(adev->set_mask, GETSET_CCA); | ||
9591 | + } | ||
9592 | + | ||
9593 | + if (adev->set_mask & GETSET_LED_POWER) { | ||
9594 | + /* Enable Tx */ | ||
9595 | + log(L_INIT, "updating power LED status: %u\n", adev->led_power); | ||
9596 | + | ||
9597 | + acx_lock(adev, flags); | ||
9598 | +#if defined (ACX_MEM) | ||
9599 | + acxmem_l_power_led(adev, adev->led_power); | ||
9600 | +#else | ||
9601 | + if (IS_PCI(adev)) | ||
9602 | + acxpci_l_power_led(adev, adev->led_power); | ||
9603 | +#endif | ||
9604 | + CLEAR_BIT(adev->set_mask, GETSET_LED_POWER); | ||
9605 | + acx_unlock(adev, flags); | ||
9606 | + } | ||
9607 | + | ||
9608 | + if (adev->set_mask & GETSET_POWER_80211) { | ||
9609 | +#if POWER_SAVE_80211 | ||
9610 | + acx_s_update_80211_powersave_mode(adev); | ||
9611 | +#endif | ||
9612 | + CLEAR_BIT(adev->set_mask, GETSET_POWER_80211); | ||
9613 | + } | ||
9614 | + | ||
9615 | + if (adev->set_mask & GETSET_CHANNEL) { | ||
9616 | + /* channel */ | ||
9617 | + log(L_INIT, "updating channel to: %u\n", adev->channel); | ||
9618 | + CLEAR_BIT(adev->set_mask, GETSET_CHANNEL); | ||
9619 | + } | ||
9620 | + | ||
9621 | + if (adev->set_mask & GETSET_TX) { | ||
9622 | + /* set Tx */ | ||
9623 | + log(L_INIT, "updating: %s Tx\n", | ||
9624 | + adev->tx_disabled ? "disable" : "enable"); | ||
9625 | + if (adev->tx_disabled) | ||
9626 | + acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0); | ||
9627 | + else | ||
9628 | + acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_TX, &adev->channel, 1); | ||
9629 | + CLEAR_BIT(adev->set_mask, GETSET_TX); | ||
9630 | + } | ||
9631 | + | ||
9632 | + if (adev->set_mask & GETSET_RX) { | ||
9633 | + /* Enable Rx */ | ||
9634 | + log(L_INIT, "updating: enable Rx on channel: %u\n", | ||
9635 | + adev->channel); | ||
9636 | + acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_RX, &adev->channel, 1); | ||
9637 | + CLEAR_BIT(adev->set_mask, GETSET_RX); | ||
9638 | + } | ||
9639 | + | ||
9640 | + if (adev->set_mask & GETSET_RETRY) { | ||
9641 | + u8 short_retry[4 + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN]; | ||
9642 | + u8 long_retry[4 + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN]; | ||
9643 | + | ||
9644 | + log(L_INIT, "updating short retry limit: %u, long retry limit: %u\n", | ||
9645 | + adev->short_retry, adev->long_retry); | ||
9646 | + short_retry[0x4] = adev->short_retry; | ||
9647 | + long_retry[0x4] = adev->long_retry; | ||
9648 | + acx_s_configure(adev, &short_retry, ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT); | ||
9649 | + acx_s_configure(adev, &long_retry, ACX1xx_IE_DOT11_LONG_RETRY_LIMIT); | ||
9650 | + CLEAR_BIT(adev->set_mask, GETSET_RETRY); | ||
9651 | + } | ||
9652 | + | ||
9653 | + if (adev->set_mask & SET_MSDU_LIFETIME) { | ||
9654 | + u8 xmt_msdu_lifetime[4 + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN]; | ||
9655 | + | ||
9656 | + log(L_INIT, "updating tx MSDU lifetime: %u\n", | ||
9657 | + adev->msdu_lifetime); | ||
9658 | + *(u32 *)&xmt_msdu_lifetime[4] = cpu_to_le32((u32)adev->msdu_lifetime); | ||
9659 | + acx_s_configure(adev, &xmt_msdu_lifetime, ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME); | ||
9660 | + CLEAR_BIT(adev->set_mask, SET_MSDU_LIFETIME); | ||
9661 | + } | ||
9662 | + | ||
9663 | + if (adev->set_mask & GETSET_REG_DOMAIN) { | ||
9664 | + log(L_INIT, "updating regulatory domain: 0x%02X\n", | ||
9665 | + adev->reg_dom_id); | ||
9666 | + acx_s_set_sane_reg_domain(adev, 1); | ||
9667 | + CLEAR_BIT(adev->set_mask, GETSET_REG_DOMAIN); | ||
9668 | + } | ||
9669 | + | ||
9670 | + if (adev->set_mask & GETSET_MODE) { | ||
9671 | + adev->ndev->type = (adev->mode == ACX_MODE_MONITOR) ? | ||
9672 | + adev->monitor_type : ARPHRD_ETHER; | ||
9673 | + | ||
9674 | + switch (adev->mode) { | ||
9675 | + case ACX_MODE_3_AP: | ||
9676 | + | ||
9677 | + acx_lock(adev, flags); | ||
9678 | + acx_l_sta_list_init(adev); | ||
9679 | + adev->aid = 0; | ||
9680 | + adev->ap_client = NULL; | ||
9681 | + MAC_COPY(adev->bssid, adev->dev_addr); | ||
9682 | + /* this basically says "we're connected" */ | ||
9683 | + acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); | ||
9684 | + acx_unlock(adev, flags); | ||
9685 | + | ||
9686 | + acx111_s_feature_off(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); | ||
9687 | + /* start sending beacons */ | ||
9688 | + acx_s_cmd_join_bssid(adev, adev->bssid); | ||
9689 | + break; | ||
9690 | + case ACX_MODE_MONITOR: | ||
9691 | + acx111_s_feature_on(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); | ||
9692 | + /* this stops beacons */ | ||
9693 | + acx_s_cmd_join_bssid(adev, adev->bssid); | ||
9694 | + /* this basically says "we're connected" */ | ||
9695 | + acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); | ||
9696 | + SET_BIT(adev->set_mask, SET_RXCONFIG|SET_WEP_OPTIONS); | ||
9697 | + break; | ||
9698 | + case ACX_MODE_0_ADHOC: | ||
9699 | + case ACX_MODE_2_STA: | ||
9700 | + acx111_s_feature_off(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); | ||
9701 | + | ||
9702 | + acx_lock(adev, flags); | ||
9703 | + adev->aid = 0; | ||
9704 | + adev->ap_client = NULL; | ||
9705 | + acx_unlock(adev, flags); | ||
9706 | + | ||
9707 | + /* we want to start looking for peer or AP */ | ||
9708 | + start_scan = 1; | ||
9709 | + break; | ||
9710 | + case ACX_MODE_OFF: | ||
9711 | + /* TODO: disable RX/TX, stop any scanning activity etc: */ | ||
9712 | + /* adev->tx_disabled = 1; */ | ||
9713 | + /* SET_BIT(adev->set_mask, GETSET_RX|GETSET_TX); */ | ||
9714 | + | ||
9715 | + /* This stops beacons (invalid macmode...) */ | ||
9716 | + acx_s_cmd_join_bssid(adev, adev->bssid); | ||
9717 | + acx_set_status(adev, ACX_STATUS_0_STOPPED); | ||
9718 | + break; | ||
9719 | + } | ||
9720 | + CLEAR_BIT(adev->set_mask, GETSET_MODE); | ||
9721 | + } | ||
9722 | + | ||
9723 | + if (adev->set_mask & SET_RXCONFIG) { | ||
9724 | + acx_s_initialize_rx_config(adev); | ||
9725 | + CLEAR_BIT(adev->set_mask, SET_RXCONFIG); | ||
9726 | + } | ||
9727 | + | ||
9728 | + if (adev->set_mask & GETSET_RESCAN) { | ||
9729 | + switch (adev->mode) { | ||
9730 | + case ACX_MODE_0_ADHOC: | ||
9731 | + case ACX_MODE_2_STA: | ||
9732 | + start_scan = 1; | ||
9733 | + break; | ||
9734 | + } | ||
9735 | + CLEAR_BIT(adev->set_mask, GETSET_RESCAN); | ||
9736 | + } | ||
9737 | + | ||
9738 | + if (adev->set_mask & GETSET_WEP) { | ||
9739 | + /* encode */ | ||
9740 | + | ||
9741 | + ie_dot11WEPDefaultKeyID_t dkey; | ||
9742 | +#ifdef DEBUG_WEP | ||
9743 | + struct { | ||
9744 | + u16 type; | ||
9745 | + u16 len; | ||
9746 | + u8 val; | ||
9747 | + } ACX_PACKED keyindic; | ||
9748 | +#endif | ||
9749 | + log(L_INIT, "updating WEP key settings\n"); | ||
9750 | + | ||
9751 | + acx_s_set_wepkey(adev); | ||
9752 | + | ||
9753 | + dkey.KeyID = adev->wep_current_index; | ||
9754 | + log(L_INIT, "setting WEP key %u as default\n", dkey.KeyID); | ||
9755 | + acx_s_configure(adev, &dkey, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); | ||
9756 | +#ifdef DEBUG_WEP | ||
9757 | + keyindic.val = 3; | ||
9758 | + acx_s_configure(adev, &keyindic, ACX111_IE_KEY_CHOOSE); | ||
9759 | +#endif | ||
9760 | + start_scan = 1; | ||
9761 | + CLEAR_BIT(adev->set_mask, GETSET_WEP); | ||
9762 | + } | ||
9763 | + | ||
9764 | + if (adev->set_mask & SET_WEP_OPTIONS) { | ||
9765 | + acx100_ie_wep_options_t options; | ||
9766 | + if (IS_ACX111(adev)) { | ||
9767 | + log(L_DEBUG, "setting WEP Options for acx111 is not supported\n"); | ||
9768 | + } else { | ||
9769 | + log(L_INIT, "setting WEP Options\n"); | ||
9770 | + acx100_s_init_wep(adev); | ||
9771 | +#if 0 | ||
9772 | + /* let's choose maximum setting: 4 default keys, | ||
9773 | + * plus 10 other keys: */ | ||
9774 | + options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10); | ||
9775 | + /* don't decrypt default key only, | ||
9776 | + * don't override decryption: */ | ||
9777 | + options.WEPOption = 0; | ||
9778 | + if (adev->mode == ACX_MODE_MONITOR) { | ||
9779 | + /* don't decrypt default key only, | ||
9780 | + * override decryption mechanism: */ | ||
9781 | + options.WEPOption = 2; | ||
9782 | + } | ||
9783 | + | ||
9784 | + acx_s_configure(adev, &options, ACX100_IE_WEP_OPTIONS); | ||
9785 | +#endif | ||
9786 | + } | ||
9787 | + CLEAR_BIT(adev->set_mask, SET_WEP_OPTIONS); | ||
9788 | + } | ||
9789 | + | ||
9790 | + /* Rescan was requested */ | ||
9791 | + if (start_scan) { | ||
9792 | + switch (adev->mode) { | ||
9793 | + case ACX_MODE_0_ADHOC: | ||
9794 | + case ACX_MODE_2_STA: | ||
9795 | + /* We can avoid clearing list if join code | ||
9796 | + ** will be a bit more clever about not picking | ||
9797 | + ** 'bad' AP over and over again */ | ||
9798 | + acx_lock(adev, flags); | ||
9799 | + adev->ap_client = NULL; | ||
9800 | + acx_l_sta_list_init(adev); | ||
9801 | + acx_set_status(adev, ACX_STATUS_1_SCANNING); | ||
9802 | + acx_unlock(adev, flags); | ||
9803 | + | ||
9804 | + acx_s_cmd_start_scan(adev); | ||
9805 | + } | ||
9806 | + } | ||
9807 | + | ||
9808 | + /* debug, rate, and nick don't need any handling */ | ||
9809 | + /* what about sniffing mode?? */ | ||
9810 | + | ||
9811 | + log(L_INIT, "get_mask 0x%08X, set_mask 0x%08X - after update\n", | ||
9812 | + adev->get_mask, adev->set_mask); | ||
9813 | + | ||
9814 | +/* end: */ | ||
9815 | + FN_EXIT0; | ||
9816 | +} | ||
9817 | + | ||
9818 | + | ||
9819 | +/*********************************************************************** | ||
9820 | +** acx_e_after_interrupt_task | ||
9821 | +*/ | ||
9822 | +static int | ||
9823 | +acx_s_recalib_radio(acx_device_t *adev) | ||
9824 | +{ | ||
9825 | + if (IS_ACX111(adev)) { | ||
9826 | + acx111_cmd_radiocalib_t cal; | ||
9827 | + | ||
9828 | + printk("%s: recalibrating radio\n", adev->ndev->name); | ||
9829 | + /* automatic recalibration, choose all methods: */ | ||
9830 | + cal.methods = cpu_to_le32(0x8000000f); | ||
9831 | + /* automatic recalibration every 60 seconds (value in TUs) | ||
9832 | + * I wonder what the firmware default here is? */ | ||
9833 | + cal.interval = cpu_to_le32(58594); | ||
9834 | + return acx_s_issue_cmd_timeo(adev, ACX111_CMD_RADIOCALIB, | ||
9835 | + &cal, sizeof(cal), CMD_TIMEOUT_MS(100)); | ||
9836 | + } else { | ||
9837 | + /* On ACX100, we need to recalibrate the radio | ||
9838 | + * by issuing a GETSET_TX|GETSET_RX */ | ||
9839 | + if (/* (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0)) && | ||
9840 | + (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0)) && */ | ||
9841 | + (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_TX, &adev->channel, 1)) && | ||
9842 | + (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_RX, &adev->channel, 1)) ) | ||
9843 | + return OK; | ||
9844 | + return NOT_OK; | ||
9845 | + } | ||
9846 | +} | ||
9847 | + | ||
9848 | +static void | ||
9849 | +acx_s_after_interrupt_recalib(acx_device_t *adev) | ||
9850 | +{ | ||
9851 | + int res; | ||
9852 | + | ||
9853 | + /* this helps with ACX100 at least; | ||
9854 | + * hopefully ACX111 also does a | ||
9855 | + * recalibration here */ | ||
9856 | + | ||
9857 | + /* clear flag beforehand, since we want to make sure | ||
9858 | + * it's cleared; then only set it again on specific circumstances */ | ||
9859 | + CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); | ||
9860 | + | ||
9861 | + /* better wait a bit between recalibrations to | ||
9862 | + * prevent overheating due to torturing the card | ||
9863 | + * into working too long despite high temperature | ||
9864 | + * (just a safety measure) */ | ||
9865 | + if (adev->recalib_time_last_success | ||
9866 | + && time_before(jiffies, adev->recalib_time_last_success | ||
9867 | + + RECALIB_PAUSE * 60 * HZ)) { | ||
9868 | + if (adev->recalib_msg_ratelimit <= 4) { | ||
9869 | + printk("%s: less than " STRING(RECALIB_PAUSE) | ||
9870 | + " minutes since last radio recalibration, " | ||
9871 | + "not recalibrating (maybe card is too hot?)\n", | ||
9872 | + adev->ndev->name); | ||
9873 | + adev->recalib_msg_ratelimit++; | ||
9874 | + if (adev->recalib_msg_ratelimit == 5) | ||
9875 | + printk("disabling above message until next recalib\n"); | ||
9876 | + } | ||
9877 | + return; | ||
9878 | + } | ||
9879 | + | ||
9880 | + adev->recalib_msg_ratelimit = 0; | ||
9881 | + | ||
9882 | + /* note that commands sometimes fail (card busy), | ||
9883 | + * so only clear flag if we were fully successful */ | ||
9884 | + res = acx_s_recalib_radio(adev); | ||
9885 | + if (res == OK) { | ||
9886 | + printk("%s: successfully recalibrated radio\n", | ||
9887 | + adev->ndev->name); | ||
9888 | + adev->recalib_time_last_success = jiffies; | ||
9889 | + adev->recalib_failure_count = 0; | ||
9890 | + } else { | ||
9891 | + /* failed: resubmit, but only limited | ||
9892 | + * amount of times within some time range | ||
9893 | + * to prevent endless loop */ | ||
9894 | + | ||
9895 | + adev->recalib_time_last_success = 0; /* we failed */ | ||
9896 | + | ||
9897 | + /* if some time passed between last | ||
9898 | + * attempts, then reset failure retry counter | ||
9899 | + * to be able to do next recalib attempt */ | ||
9900 | + if (time_after(jiffies, adev->recalib_time_last_attempt + 5*HZ)) | ||
9901 | + adev->recalib_failure_count = 0; | ||
9902 | + | ||
9903 | + if (adev->recalib_failure_count < 5) { | ||
9904 | + /* increment inside only, for speedup of outside path */ | ||
9905 | + adev->recalib_failure_count++; | ||
9906 | + adev->recalib_time_last_attempt = jiffies; | ||
9907 | + acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); | ||
9908 | + } | ||
9909 | + } | ||
9910 | +} | ||
9911 | + | ||
9912 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | ||
9913 | +static void | ||
9914 | +acx_e_after_interrupt_task(struct work_struct *work) | ||
9915 | +{ | ||
9916 | + acx_device_t *adev = container_of(work, acx_device_t, after_interrupt_task); | ||
9917 | +#else | ||
9918 | + static void | ||
9919 | + acx_e_after_interrupt_task(void *data) | ||
9920 | + { | ||
9921 | + struct net_device *ndev = (struct net_device*)data; | ||
9922 | + acx_device_t *adev = ndev2adev(ndev); | ||
9923 | +#endif | ||
9924 | + FN_ENTER; | ||
9925 | + | ||
9926 | + acx_sem_lock(adev); | ||
9927 | + | ||
9928 | + if (!adev->after_interrupt_jobs) | ||
9929 | + goto end; /* no jobs to do */ | ||
9930 | + | ||
9931 | +#if TX_CLEANUP_IN_SOFTIRQ | ||
9932 | + /* can happen only on PCI */ | ||
9933 | + if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_TX_CLEANUP) { | ||
9934 | + acx_lock(adev, flags); | ||
9935 | + acxpci_l_clean_txdesc(adev); | ||
9936 | + CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_TX_CLEANUP); | ||
9937 | + acx_unlock(adev, flags); | ||
9938 | + } | ||
9939 | +#endif | ||
9940 | + /* we see lotsa tx errors */ | ||
9941 | + if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_RADIO_RECALIB) { | ||
9942 | + acx_s_after_interrupt_recalib(adev); | ||
9943 | + } | ||
9944 | + | ||
9945 | + /* a poor interrupt code wanted to do update_card_settings() */ | ||
9946 | + if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_UPDATE_CARD_CFG) { | ||
9947 | + if (ACX_STATE_IFACE_UP & adev->dev_state_mask) | ||
9948 | + acx_s_update_card_settings(adev); | ||
9949 | + CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_UPDATE_CARD_CFG); | ||
9950 | + } | ||
9951 | + | ||
9952 | + /* 1) we detected that no Scan_Complete IRQ came from fw, or | ||
9953 | + ** 2) we found too many STAs */ | ||
9954 | + if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_STOP_SCAN) { | ||
9955 | + log(L_IRQ, "sending a stop scan cmd...\n"); | ||
9956 | + acx_s_issue_cmd(adev, ACX1xx_CMD_STOP_SCAN, NULL, 0); | ||
9957 | + /* HACK: set the IRQ bit, since we won't get a | ||
9958 | + * scan complete IRQ any more on ACX111 (works on ACX100!), | ||
9959 | + * since _we_, not a fw, have stopped the scan */ | ||
9960 | + SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE); | ||
9961 | + CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_STOP_SCAN); | ||
9962 | + } | ||
9963 | + | ||
9964 | + /* either fw sent Scan_Complete or we detected that | ||
9965 | + ** no Scan_Complete IRQ came from fw. Finish scanning, | ||
9966 | + ** pick join partner if any */ | ||
9967 | + if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_COMPLETE_SCAN) { | ||
9968 | + if (adev->status == ACX_STATUS_1_SCANNING) { | ||
9969 | + if (OK != acx_s_complete_scan(adev)) { | ||
9970 | + SET_BIT(adev->after_interrupt_jobs, | ||
9971 | + ACX_AFTER_IRQ_RESTART_SCAN); | ||
9972 | + } | ||
9973 | + } else { | ||
9974 | + /* + scan kills current join status - restore it | ||
9975 | + ** (do we need it for STA?) */ | ||
9976 | + /* + does it happen only with active scans? | ||
9977 | + ** active and passive scans? ALL scans including | ||
9978 | + ** background one? */ | ||
9979 | + /* + was not verified that everything is restored | ||
9980 | + ** (but at least we start to emit beacons again) */ | ||
9981 | + switch (adev->mode) { | ||
9982 | + case ACX_MODE_0_ADHOC: | ||
9983 | + case ACX_MODE_3_AP: | ||
9984 | + log(L_IRQ, "redoing cmd_join_bssid() after scan\n"); | ||
9985 | + acx_s_cmd_join_bssid(adev, adev->bssid); | ||
9986 | + } | ||
9987 | + } | ||
9988 | + CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_COMPLETE_SCAN); | ||
9989 | + } | ||
9990 | + | ||
9991 | + /* STA auth or assoc timed out, start over again */ | ||
9992 | + if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_RESTART_SCAN) { | ||
9993 | + log(L_IRQ, "sending a start_scan cmd...\n"); | ||
9994 | + acx_s_cmd_start_scan(adev); | ||
9995 | + CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_RESTART_SCAN); | ||
9996 | + } | ||
9997 | + | ||
9998 | + /* whee, we got positive assoc response! 8) */ | ||
9999 | + if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_ASSOCIATE) { | ||
10000 | + acx_ie_generic_t pdr; | ||
10001 | + /* tiny race window exists, checking that we still a STA */ | ||
10002 | + switch (adev->mode) { | ||
10003 | + case ACX_MODE_2_STA: | ||
10004 | + pdr.m.aid = cpu_to_le16(adev->aid); | ||
10005 | + acx_s_configure(adev, &pdr, ACX1xx_IE_ASSOC_ID); | ||
10006 | + acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); | ||
10007 | + log(L_ASSOC|L_DEBUG, "ASSOCIATED!\n"); | ||
10008 | + CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_ASSOCIATE); | ||
10009 | + } | ||
10010 | + } | ||
10011 | +end: | ||
10012 | + acx_sem_unlock(adev); | ||
10013 | + FN_EXIT0; | ||
10014 | +} | ||
10015 | + | ||
10016 | + | ||
10017 | +/*********************************************************************** | ||
10018 | +** acx_schedule_task | ||
10019 | +** | ||
10020 | +** Schedule the call of the after_interrupt method after leaving | ||
10021 | +** the interrupt context. | ||
10022 | +*/ | ||
10023 | +void | ||
10024 | +acx_schedule_task(acx_device_t *adev, unsigned int set_flag) | ||
10025 | +{ | ||
10026 | + SET_BIT(adev->after_interrupt_jobs, set_flag); | ||
10027 | + SCHEDULE_WORK(&adev->after_interrupt_task); | ||
10028 | +} | ||
10029 | + | ||
10030 | + | ||
10031 | +/*********************************************************************** | ||
10032 | +*/ | ||
10033 | +void | ||
10034 | +acx_init_task_scheduler(acx_device_t *adev) | ||
10035 | +{ | ||
10036 | + /* configure task scheduler */ | ||
10037 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | ||
10038 | + INIT_WORK(&adev->after_interrupt_task, acx_e_after_interrupt_task); | ||
10039 | +#else | ||
10040 | + INIT_WORK(&adev->after_interrupt_task, acx_e_after_interrupt_task, | ||
10041 | + adev->ndev); | ||
10042 | +#endif | ||
10043 | +} | ||
10044 | + | ||
10045 | + | ||
10046 | +/*********************************************************************** | ||
10047 | +** acx_s_start | ||
10048 | +*/ | ||
10049 | +void | ||
10050 | +acx_s_start(acx_device_t *adev) | ||
10051 | +{ | ||
10052 | + FN_ENTER; | ||
10053 | + | ||
10054 | + /* | ||
10055 | + * Ok, now we do everything that can possibly be done with ioctl | ||
10056 | + * calls to make sure that when it was called before the card | ||
10057 | + * was up we get the changes asked for | ||
10058 | + */ | ||
10059 | + | ||
10060 | + SET_BIT(adev->set_mask, SET_TEMPLATES|SET_STA_LIST|GETSET_WEP | ||
10061 | + |GETSET_TXPOWER|GETSET_ANTENNA|GETSET_ED_THRESH|GETSET_CCA | ||
10062 | + |GETSET_REG_DOMAIN|GETSET_MODE|GETSET_CHANNEL | ||
10063 | + |GETSET_TX|GETSET_RX|GETSET_STATION_ID); | ||
10064 | + | ||
10065 | + log(L_INIT, "updating initial settings on iface activation\n"); | ||
10066 | + acx_s_update_card_settings(adev); | ||
10067 | + | ||
10068 | + FN_EXIT0; | ||
10069 | +} | ||
10070 | + | ||
10071 | + | ||
10072 | +/*********************************************************************** | ||
10073 | +** acx_update_capabilities | ||
10074 | +*/ | ||
10075 | +void | ||
10076 | +acx_update_capabilities(acx_device_t *adev) | ||
10077 | +{ | ||
10078 | + u16 cap = 0; | ||
10079 | + | ||
10080 | + switch (adev->mode) { | ||
10081 | + case ACX_MODE_3_AP: | ||
10082 | + SET_BIT(cap, WF_MGMT_CAP_ESS); break; | ||
10083 | + case ACX_MODE_0_ADHOC: | ||
10084 | + SET_BIT(cap, WF_MGMT_CAP_IBSS); break; | ||
10085 | + /* other types of stations do not emit beacons */ | ||
10086 | + } | ||
10087 | + | ||
10088 | + if (adev->wep_restricted) { | ||
10089 | + SET_BIT(cap, WF_MGMT_CAP_PRIVACY); | ||
10090 | + } | ||
10091 | + if (adev->cfgopt_dot11ShortPreambleOption) { | ||
10092 | + SET_BIT(cap, WF_MGMT_CAP_SHORT); | ||
10093 | + } | ||
10094 | + if (adev->cfgopt_dot11PBCCOption) { | ||
10095 | + SET_BIT(cap, WF_MGMT_CAP_PBCC); | ||
10096 | + } | ||
10097 | + if (adev->cfgopt_dot11ChannelAgility) { | ||
10098 | + SET_BIT(cap, WF_MGMT_CAP_AGILITY); | ||
10099 | + } | ||
10100 | + log(L_DEBUG, "caps updated from 0x%04X to 0x%04X\n", | ||
10101 | + adev->capabilities, cap); | ||
10102 | + adev->capabilities = cap; | ||
10103 | +} | ||
10104 | + | ||
10105 | +/*********************************************************************** | ||
10106 | +** Common function to parse ALL configoption struct formats | ||
10107 | +** (ACX100 and ACX111; FIXME: how to make it work with ACX100 USB!?!?). | ||
10108 | +** FIXME: logging should be removed here and added to a /proc file instead | ||
10109 | +*/ | ||
10110 | +void | ||
10111 | +acx_s_parse_configoption(acx_device_t *adev, const acx111_ie_configoption_t *pcfg) | ||
10112 | +{ | ||
10113 | + const u8 *pEle; | ||
10114 | + int i; | ||
10115 | + int is_acx111 = IS_ACX111(adev); | ||
10116 | + | ||
10117 | + if (acx_debug & L_DEBUG) { | ||
10118 | + printk("configoption struct content:\n"); | ||
10119 | + acx_dump_bytes(pcfg, sizeof(*pcfg)); | ||
10120 | + } | ||
10121 | + | ||
10122 | + if (( is_acx111 && (adev->eeprom_version == 5)) | ||
10123 | + || (!is_acx111 && (adev->eeprom_version == 4)) | ||
10124 | + || (!is_acx111 && (adev->eeprom_version == 5))) { | ||
10125 | + /* these versions are known to be supported */ | ||
10126 | + } else { | ||
10127 | + printk("unknown chip and EEPROM version combination (%s, v%d), " | ||
10128 | + "don't know how to parse config options yet. " | ||
10129 | + "Please report\n", is_acx111 ? "ACX111" : "ACX100", | ||
10130 | + adev->eeprom_version); | ||
10131 | + return; | ||
10132 | + } | ||
10133 | + | ||
10134 | + /* first custom-parse the first part which has chip-specific layout */ | ||
10135 | + | ||
10136 | + pEle = (const u8 *) pcfg; | ||
10137 | + | ||
10138 | + pEle += 4; /* skip (type,len) header */ | ||
10139 | + | ||
10140 | + memcpy(adev->cfgopt_NVSv, pEle, sizeof(adev->cfgopt_NVSv)); | ||
10141 | + pEle += sizeof(adev->cfgopt_NVSv); | ||
10142 | + | ||
10143 | + if (is_acx111) { | ||
10144 | + adev->cfgopt_NVS_vendor_offs = le16_to_cpu(*(u16 *)pEle); | ||
10145 | + pEle += sizeof(adev->cfgopt_NVS_vendor_offs); | ||
10146 | + | ||
10147 | + adev->cfgopt_probe_delay = 200; /* good default value? */ | ||
10148 | + pEle += 2; /* FIXME: unknown, value 0x0001 */ | ||
10149 | + } else { | ||
10150 | + memcpy(adev->cfgopt_MAC, pEle, sizeof(adev->cfgopt_MAC)); | ||
10151 | + pEle += sizeof(adev->cfgopt_MAC); | ||
10152 | + | ||
10153 | + adev->cfgopt_probe_delay = le16_to_cpu(*(u16 *)pEle); | ||
10154 | + pEle += sizeof(adev->cfgopt_probe_delay); | ||
10155 | + if ((adev->cfgopt_probe_delay < 100) || (adev->cfgopt_probe_delay > 500)) { | ||
10156 | + printk("strange probe_delay value %d, " | ||
10157 | + "tweaking to 200\n", adev->cfgopt_probe_delay); | ||
10158 | + adev->cfgopt_probe_delay = 200; | ||
10159 | + } | ||
10160 | + } | ||
10161 | + | ||
10162 | + adev->cfgopt_eof_memory = le32_to_cpu(*(u32 *)pEle); | ||
10163 | + pEle += sizeof(adev->cfgopt_eof_memory); | ||
10164 | + | ||
10165 | + printk("NVS_vendor_offs:%04X probe_delay:%d eof_memory:%d\n", | ||
10166 | + adev->cfgopt_NVS_vendor_offs, | ||
10167 | + adev->cfgopt_probe_delay, | ||
10168 | + adev->cfgopt_eof_memory); | ||
10169 | + | ||
10170 | + adev->cfgopt_dot11CCAModes = *pEle++; | ||
10171 | + adev->cfgopt_dot11Diversity = *pEle++; | ||
10172 | + adev->cfgopt_dot11ShortPreambleOption = *pEle++; | ||
10173 | + adev->cfgopt_dot11PBCCOption = *pEle++; | ||
10174 | + adev->cfgopt_dot11ChannelAgility = *pEle++; | ||
10175 | + adev->cfgopt_dot11PhyType = *pEle++; | ||
10176 | + adev->cfgopt_dot11TempType = *pEle++; | ||
10177 | + printk("CCAModes:%02X Diversity:%02X ShortPreOpt:%02X " | ||
10178 | + "PBCC:%02X ChanAgil:%02X PHY:%02X Temp:%02X\n", | ||
10179 | + adev->cfgopt_dot11CCAModes, | ||
10180 | + adev->cfgopt_dot11Diversity, | ||
10181 | + adev->cfgopt_dot11ShortPreambleOption, | ||
10182 | + adev->cfgopt_dot11PBCCOption, | ||
10183 | + adev->cfgopt_dot11ChannelAgility, | ||
10184 | + adev->cfgopt_dot11PhyType, | ||
10185 | + adev->cfgopt_dot11TempType); | ||
10186 | + | ||
10187 | + /* then use common parsing for next part which has common layout */ | ||
10188 | + | ||
10189 | + pEle++; /* skip table_count (6) */ | ||
10190 | + | ||
10191 | + if (IS_MEM(adev) && IS_ACX100(adev)) | ||
10192 | + { | ||
10193 | + /* | ||
10194 | + * For iPaq hx4700 Generic Slave F/W 1.10.7.K. I'm not sure if these | ||
10195 | + * 4 extra bytes are before the dot11 things above or after, so I'm just | ||
10196 | + * going to guess after. If someone sees these aren't reasonable numbers, | ||
10197 | + * please fix this. | ||
10198 | + * The area from which the dot11 values above are read contains: | ||
10199 | + * 04 01 01 01 00 05 01 06 00 02 01 02 | ||
10200 | + * the 8 dot11 reads above take care of 8 of them, but which 8... | ||
10201 | + */ | ||
10202 | + pEle += 4; | ||
10203 | + } | ||
10204 | + | ||
10205 | + adev->cfgopt_antennas.type = pEle[0]; | ||
10206 | + adev->cfgopt_antennas.len = pEle[1]; | ||
10207 | + printk("AntennaID:%02X Len:%02X Data:", | ||
10208 | + adev->cfgopt_antennas.type, adev->cfgopt_antennas.len); | ||
10209 | + for (i = 0; i < pEle[1]; i++) { | ||
10210 | + adev->cfgopt_antennas.list[i] = pEle[i+2]; | ||
10211 | + printk("%02X ", pEle[i+2]); | ||
10212 | + } | ||
10213 | + printk("\n"); | ||
10214 | + | ||
10215 | + pEle += pEle[1] + 2; | ||
10216 | + adev->cfgopt_power_levels.type = pEle[0]; | ||
10217 | + adev->cfgopt_power_levels.len = pEle[1]; | ||
10218 | + printk("PowerLevelID:%02X Len:%02X Data:", | ||
10219 | + adev->cfgopt_power_levels.type, adev->cfgopt_power_levels.len); | ||
10220 | + for (i = 0; i < pEle[1]; i++) { | ||
10221 | + adev->cfgopt_power_levels.list[i] = le16_to_cpu(*(u16 *)&pEle[i*2+2]); | ||
10222 | + printk("%04X ", adev->cfgopt_power_levels.list[i]); | ||
10223 | + } | ||
10224 | + printk("\n"); | ||
10225 | + | ||
10226 | + pEle += pEle[1]*2 + 2; | ||
10227 | + adev->cfgopt_data_rates.type = pEle[0]; | ||
10228 | + adev->cfgopt_data_rates.len = pEle[1]; | ||
10229 | + printk("DataRatesID:%02X Len:%02X Data:", | ||
10230 | + adev->cfgopt_data_rates.type, adev->cfgopt_data_rates.len); | ||
10231 | + for (i = 0; i < pEle[1]; i++) { | ||
10232 | + adev->cfgopt_data_rates.list[i] = pEle[i+2]; | ||
10233 | + printk("%02X ", pEle[i+2]); | ||
10234 | + } | ||
10235 | + printk("\n"); | ||
10236 | + | ||
10237 | + pEle += pEle[1] + 2; | ||
10238 | + adev->cfgopt_domains.type = pEle[0]; | ||
10239 | + adev->cfgopt_domains.len = pEle[1]; | ||
10240 | + if (IS_MEM(adev) && IS_ACX100(adev)) | ||
10241 | + { | ||
10242 | + /* | ||
10243 | + * For iPaq hx4700 Generic Slave F/W 1.10.7.K. | ||
10244 | + * There's an extra byte between this structure and the next | ||
10245 | + * that is not accounted for with this structure's length. It's | ||
10246 | + * most likely a bug in the firmware, but we can fix it here | ||
10247 | + * by bumping the length of this field by 1. | ||
10248 | + */ | ||
10249 | + adev->cfgopt_domains.len++; | ||
10250 | + } | ||
10251 | + printk("DomainID:%02X Len:%02X Data:", | ||
10252 | + adev->cfgopt_domains.type, adev->cfgopt_domains.len); | ||
10253 | + for (i = 0; i < adev->cfgopt_domains.len; i++) { | ||
10254 | + adev->cfgopt_domains.list[i] = pEle[i+2]; | ||
10255 | + printk("%02X ", pEle[i+2]); | ||
10256 | + } | ||
10257 | + printk("\n"); | ||
10258 | + | ||
10259 | + pEle += adev->cfgopt_domains.len + 2; | ||
10260 | + | ||
10261 | + adev->cfgopt_product_id.type = pEle[0]; | ||
10262 | + adev->cfgopt_product_id.len = pEle[1]; | ||
10263 | + for (i = 0; i < pEle[1]; i++) { | ||
10264 | + adev->cfgopt_product_id.list[i] = pEle[i+2]; | ||
10265 | + } | ||
10266 | + printk("ProductID:%02X Len:%02X Data:%.*s\n", | ||
10267 | + adev->cfgopt_product_id.type, adev->cfgopt_product_id.len, | ||
10268 | + adev->cfgopt_product_id.len, (char *)adev->cfgopt_product_id.list); | ||
10269 | + | ||
10270 | + pEle += pEle[1] + 2; | ||
10271 | + adev->cfgopt_manufacturer.type = pEle[0]; | ||
10272 | + adev->cfgopt_manufacturer.len = pEle[1]; | ||
10273 | + for (i = 0; i < pEle[1]; i++) { | ||
10274 | + adev->cfgopt_manufacturer.list[i] = pEle[i+2]; | ||
10275 | + } | ||
10276 | + printk("ManufacturerID:%02X Len:%02X Data:%.*s\n", | ||
10277 | + adev->cfgopt_manufacturer.type, adev->cfgopt_manufacturer.len, | ||
10278 | + adev->cfgopt_manufacturer.len, (char *)adev->cfgopt_manufacturer.list); | ||
10279 | +/* | ||
10280 | + printk("EEPROM part:\n"); | ||
10281 | + for (i=0; i<58; i++) { | ||
10282 | + printk("%02X =======> 0x%02X\n", | ||
10283 | + i, (u8 *)adev->cfgopt_NVSv[i-2]); | ||
10284 | + } | ||
10285 | +*/ | ||
10286 | +} | ||
10287 | + | ||
10288 | + | ||
10289 | +/*********************************************************************** | ||
10290 | +*/ | ||
10291 | +static int __init | ||
10292 | +acx_e_init_module(void) | ||
10293 | +{ | ||
10294 | + int r1,r2,r3,r4; | ||
10295 | + | ||
10296 | + acx_struct_size_check(); | ||
10297 | + | ||
10298 | + printk("acx: this driver is still EXPERIMENTAL\n" | ||
10299 | + "acx: reading README file and/or Craig's HOWTO is " | ||
10300 | + "recommended, visit http://acx100.sf.net in case " | ||
10301 | + "of further questions/discussion\n"); | ||
10302 | + | ||
10303 | +#if defined(CONFIG_ACX_PCI) | ||
10304 | + r1 = acxpci_e_init_module(); | ||
10305 | +#else | ||
10306 | + r1 = -EINVAL; | ||
10307 | +#endif | ||
10308 | +#if defined(CONFIG_ACX_MEM) | ||
10309 | + r2 = acxmem_e_init_module(); | ||
10310 | +#else | ||
10311 | + r2 = -EINVAL; | ||
10312 | +#endif | ||
10313 | +#if defined(CONFIG_ACX_USB) | ||
10314 | + r3 = acxusb_e_init_module(); | ||
10315 | +#else | ||
10316 | + r3 = -EINVAL; | ||
10317 | +#endif | ||
10318 | +#if defined(CONFIG_ACX_CS) | ||
10319 | + r4 = acx_cs_init(); | ||
10320 | +#else | ||
10321 | + r4 = -EINVAL; | ||
10322 | +#endif | ||
10323 | + if (r2 && r1 && r3 && r4) { /* all failed! */ | ||
10324 | + if (r3 || r1) | ||
10325 | + return r3 ? r3 : r1; | ||
10326 | + else | ||
10327 | + return r2; | ||
10328 | + } | ||
10329 | + /* return success if at least one succeeded */ | ||
10330 | + return 0; | ||
10331 | + | ||
10332 | +} | ||
10333 | + | ||
10334 | +static void __exit | ||
10335 | +acx_e_cleanup_module(void) | ||
10336 | +{ | ||
10337 | +#if defined(CONFIG_ACX_PCI) | ||
10338 | + acxpci_e_cleanup_module(); | ||
10339 | +#endif | ||
10340 | +#if defined(CONFIG_ACX_MEM) | ||
10341 | + acxmem_e_cleanup_module(); | ||
10342 | +#endif | ||
10343 | +#if defined(CONFIG_ACX_USB) | ||
10344 | + acxusb_e_cleanup_module(); | ||
10345 | +#endif | ||
10346 | +#if defined(CONFIG_ACX_CS) | ||
10347 | + acx_cs_cleanup(); | ||
10348 | +#endif | ||
10349 | +} | ||
10350 | + | ||
10351 | +module_init(acx_e_init_module) | ||
10352 | +module_exit(acx_e_cleanup_module) | ||
10353 | Index: linux-2.6.22/drivers/net/wireless/acx/conv.c | ||
10354 | =================================================================== | ||
10355 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
10356 | +++ linux-2.6.22/drivers/net/wireless/acx/conv.c 2007-08-23 18:34:19.000000000 +0200 | ||
10357 | @@ -0,0 +1,504 @@ | ||
10358 | +/*********************************************************************** | ||
10359 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
10360 | +** | ||
10361 | +** The contents of this file are subject to the Mozilla Public | ||
10362 | +** License Version 1.1 (the "License"); you may not use this file | ||
10363 | +** except in compliance with the License. You may obtain a copy of | ||
10364 | +** the License at http://www.mozilla.org/MPL/ | ||
10365 | +** | ||
10366 | +** Software distributed under the License is distributed on an "AS | ||
10367 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
10368 | +** implied. See the License for the specific language governing | ||
10369 | +** rights and limitations under the License. | ||
10370 | +** | ||
10371 | +** Alternatively, the contents of this file may be used under the | ||
10372 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
10373 | +** case the provisions of the GPL are applicable instead of the | ||
10374 | +** above. If you wish to allow the use of your version of this file | ||
10375 | +** only under the terms of the GPL and not to allow others to use | ||
10376 | +** your version of this file under the MPL, indicate your decision | ||
10377 | +** by deleting the provisions above and replace them with the notice | ||
10378 | +** and other provisions required by the GPL. If you do not delete | ||
10379 | +** the provisions above, a recipient may use your version of this | ||
10380 | +** file under either the MPL or the GPL. | ||
10381 | +** --------------------------------------------------------------------- | ||
10382 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
10383 | +** made directly to: | ||
10384 | +** | ||
10385 | +** acx100-users@lists.sf.net | ||
10386 | +** http://acx100.sf.net | ||
10387 | +** --------------------------------------------------------------------- | ||
10388 | +*/ | ||
10389 | + | ||
10390 | +#include <linux/version.h> | ||
10391 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) | ||
10392 | +#include <linux/config.h> | ||
10393 | +#endif | ||
10394 | +#include <linux/skbuff.h> | ||
10395 | +#include <linux/if_arp.h> | ||
10396 | +#include <linux/etherdevice.h> | ||
10397 | +#include <linux/wireless.h> | ||
10398 | +#include <net/iw_handler.h> | ||
10399 | + | ||
10400 | +#include "acx.h" | ||
10401 | + | ||
10402 | + | ||
10403 | +/*********************************************************************** | ||
10404 | +** proto_is_stt | ||
10405 | +** | ||
10406 | +** Searches the 802.1h Selective Translation Table for a given | ||
10407 | +** protocol. | ||
10408 | +** | ||
10409 | +** prottype - protocol number (in host order) to search for. | ||
10410 | +** | ||
10411 | +** Returns: | ||
10412 | +** 1 - if the table is empty or a match is found. | ||
10413 | +** 0 - if the table is non-empty and a match is not found. | ||
10414 | +** | ||
10415 | +** Based largely on p80211conv.c of the linux-wlan-ng project | ||
10416 | +*/ | ||
10417 | +static inline int | ||
10418 | +proto_is_stt(unsigned int proto) | ||
10419 | +{ | ||
10420 | + /* Always return found for now. This is the behavior used by the */ | ||
10421 | + /* Zoom Win95 driver when 802.1h mode is selected */ | ||
10422 | + /* TODO: If necessary, add an actual search we'll probably | ||
10423 | + need this to match the CMAC's way of doing things. | ||
10424 | + Need to do some testing to confirm. | ||
10425 | + */ | ||
10426 | + | ||
10427 | + if (proto == 0x80f3) /* APPLETALK */ | ||
10428 | + return 1; | ||
10429 | + | ||
10430 | + return 0; | ||
10431 | +/* return ((prottype == ETH_P_AARP) || (prottype == ETH_P_IPX)); */ | ||
10432 | +} | ||
10433 | + | ||
10434 | +/* Helpers */ | ||
10435 | + | ||
10436 | +static inline void | ||
10437 | +store_llc_snap(struct wlan_llc *llc) | ||
10438 | +{ | ||
10439 | + llc->dsap = 0xaa; /* SNAP, see IEEE 802 */ | ||
10440 | + llc->ssap = 0xaa; | ||
10441 | + llc->ctl = 0x03; | ||
10442 | +} | ||
10443 | +static inline int | ||
10444 | +llc_is_snap(const struct wlan_llc *llc) | ||
10445 | +{ | ||
10446 | + return (llc->dsap == 0xaa) | ||
10447 | + && (llc->ssap == 0xaa) | ||
10448 | + && (llc->ctl == 0x03); | ||
10449 | +} | ||
10450 | +static inline void | ||
10451 | +store_oui_rfc1042(struct wlan_snap *snap) | ||
10452 | +{ | ||
10453 | + snap->oui[0] = 0; | ||
10454 | + snap->oui[1] = 0; | ||
10455 | + snap->oui[2] = 0; | ||
10456 | +} | ||
10457 | +static inline int | ||
10458 | +oui_is_rfc1042(const struct wlan_snap *snap) | ||
10459 | +{ | ||
10460 | + return (snap->oui[0] == 0) | ||
10461 | + && (snap->oui[1] == 0) | ||
10462 | + && (snap->oui[2] == 0); | ||
10463 | +} | ||
10464 | +static inline void | ||
10465 | +store_oui_8021h(struct wlan_snap *snap) | ||
10466 | +{ | ||
10467 | + snap->oui[0] = 0; | ||
10468 | + snap->oui[1] = 0; | ||
10469 | + snap->oui[2] = 0xf8; | ||
10470 | +} | ||
10471 | +static inline int | ||
10472 | +oui_is_8021h(const struct wlan_snap *snap) | ||
10473 | +{ | ||
10474 | + return (snap->oui[0] == 0) | ||
10475 | + && (snap->oui[1] == 0) | ||
10476 | + && (snap->oui[2] == 0xf8); | ||
10477 | +} | ||
10478 | + | ||
10479 | + | ||
10480 | +/*********************************************************************** | ||
10481 | +** acx_ether_to_txbuf | ||
10482 | +** | ||
10483 | +** Uses the contents of the ether frame to build the elements of | ||
10484 | +** the 802.11 frame. | ||
10485 | +** | ||
10486 | +** We don't actually set up the frame header here. That's the | ||
10487 | +** MAC's job. We're only handling conversion of DIXII or 802.3+LLC | ||
10488 | +** frames to something that works with 802.11. | ||
10489 | +** | ||
10490 | +** Based largely on p80211conv.c of the linux-wlan-ng project | ||
10491 | +*/ | ||
10492 | +int | ||
10493 | +acx_ether_to_txbuf(acx_device_t *adev, void *txbuf, const struct sk_buff *skb) | ||
10494 | +{ | ||
10495 | + struct wlan_hdr_a3 *w_hdr; | ||
10496 | + struct wlan_ethhdr *e_hdr; | ||
10497 | + struct wlan_llc *e_llc; | ||
10498 | + struct wlan_snap *e_snap; | ||
10499 | + const u8 *a1, *a3; | ||
10500 | + int header_len, payload_len = -1; | ||
10501 | + /* protocol type or data length, depending on whether | ||
10502 | + * DIX or 802.3 ethernet format */ | ||
10503 | + u16 proto; | ||
10504 | + u16 fc; | ||
10505 | + | ||
10506 | + FN_ENTER; | ||
10507 | + | ||
10508 | + if (unlikely(!skb->len)) { | ||
10509 | + log(L_DEBUG, "zero-length skb!\n"); | ||
10510 | + goto end; | ||
10511 | + } | ||
10512 | + | ||
10513 | + w_hdr = (struct wlan_hdr_a3*)txbuf; | ||
10514 | + | ||
10515 | + switch (adev->mode) { | ||
10516 | + case ACX_MODE_MONITOR: | ||
10517 | + /* NB: one day we might want to play with DESC_CTL2_FCS | ||
10518 | + ** Will need to stop doing "- WLAN_FCS_LEN" here then */ | ||
10519 | + if (unlikely(skb->len >= WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_FCS_LEN)) { | ||
10520 | + printk("%s: can't tx oversized frame (%d bytes)\n", | ||
10521 | + adev->ndev->name, skb->len); | ||
10522 | + goto end; | ||
10523 | + } | ||
10524 | + memcpy(w_hdr, skb->data, skb->len); | ||
10525 | + payload_len = skb->len; | ||
10526 | + goto end; | ||
10527 | + } | ||
10528 | + | ||
10529 | + /* step 1: classify ether frame, DIX or 802.3? */ | ||
10530 | + e_hdr = (wlan_ethhdr_t *)skb->data; | ||
10531 | + proto = ntohs(e_hdr->type); | ||
10532 | + if (proto <= 1500) { | ||
10533 | + log(L_DEBUG, "tx: 802.3 len: %d\n", skb->len); | ||
10534 | + /* codes <= 1500 reserved for 802.3 lengths */ | ||
10535 | + /* it's 802.3, pass ether payload unchanged, */ | ||
10536 | + /* trim off ethernet header and copy payload to txdesc */ | ||
10537 | + header_len = WLAN_HDR_A3_LEN; | ||
10538 | + } else { | ||
10539 | + /* it's DIXII, time for some conversion */ | ||
10540 | + /* Create 802.11 packet. Header also contains llc and snap. */ | ||
10541 | + | ||
10542 | + log(L_DEBUG, "tx: DIXII len: %d\n", skb->len); | ||
10543 | + | ||
10544 | + /* size of header is 802.11 header + llc + snap */ | ||
10545 | + header_len = WLAN_HDR_A3_LEN + sizeof(wlan_llc_t) + sizeof(wlan_snap_t); | ||
10546 | + /* llc is located behind the 802.11 header */ | ||
10547 | + e_llc = (wlan_llc_t*)(w_hdr + 1); | ||
10548 | + /* snap is located behind the llc */ | ||
10549 | + e_snap = (wlan_snap_t*)(e_llc + 1); | ||
10550 | + | ||
10551 | + /* setup the LLC header */ | ||
10552 | + store_llc_snap(e_llc); | ||
10553 | + | ||
10554 | + /* setup the SNAP header */ | ||
10555 | + e_snap->type = htons(proto); | ||
10556 | + if (proto_is_stt(proto)) { | ||
10557 | + store_oui_8021h(e_snap); | ||
10558 | + } else { | ||
10559 | + store_oui_rfc1042(e_snap); | ||
10560 | + } | ||
10561 | + } | ||
10562 | + /* trim off ethernet header and copy payload to txbuf */ | ||
10563 | + payload_len = skb->len - sizeof(wlan_ethhdr_t); | ||
10564 | + /* TODO: can we just let acx DMA payload from skb instead? */ | ||
10565 | + memcpy((u8*)txbuf + header_len, skb->data + sizeof(wlan_ethhdr_t), payload_len); | ||
10566 | + payload_len += header_len; | ||
10567 | + | ||
10568 | + /* Set up the 802.11 header */ | ||
10569 | + switch (adev->mode) { | ||
10570 | + case ACX_MODE_0_ADHOC: | ||
10571 | + fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi); | ||
10572 | + a1 = e_hdr->daddr; | ||
10573 | + a3 = adev->bssid; | ||
10574 | + break; | ||
10575 | + case ACX_MODE_2_STA: | ||
10576 | + fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_TODSi); | ||
10577 | + a1 = adev->bssid; | ||
10578 | + a3 = e_hdr->daddr; | ||
10579 | + break; | ||
10580 | + case ACX_MODE_3_AP: | ||
10581 | + fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_FROMDSi); | ||
10582 | + a1 = e_hdr->daddr; | ||
10583 | + a3 = e_hdr->saddr; | ||
10584 | + break; | ||
10585 | + default: | ||
10586 | + printk("%s: error - converting eth to wlan in unknown mode\n", | ||
10587 | + adev->ndev->name); | ||
10588 | + payload_len = -1; | ||
10589 | + goto end; | ||
10590 | + } | ||
10591 | + if (adev->wep_enabled) | ||
10592 | + SET_BIT(fc, WF_FC_ISWEPi); | ||
10593 | + | ||
10594 | + w_hdr->fc = fc; | ||
10595 | + w_hdr->dur = 0; | ||
10596 | + MAC_COPY(w_hdr->a1, a1); | ||
10597 | + MAC_COPY(w_hdr->a2, adev->dev_addr); | ||
10598 | + MAC_COPY(w_hdr->a3, a3); | ||
10599 | + w_hdr->seq = 0; | ||
10600 | + | ||
10601 | +#ifdef DEBUG_CONVERT | ||
10602 | + if (acx_debug & L_DATA) { | ||
10603 | + printk("original eth frame [%d]: ", skb->len); | ||
10604 | + acx_dump_bytes(skb->data, skb->len); | ||
10605 | + printk("802.11 frame [%d]: ", payload_len); | ||
10606 | + acx_dump_bytes(w_hdr, payload_len); | ||
10607 | + } | ||
10608 | +#endif | ||
10609 | + | ||
10610 | +end: | ||
10611 | + FN_EXIT1(payload_len); | ||
10612 | + return payload_len; | ||
10613 | +} | ||
10614 | + | ||
10615 | + | ||
10616 | +/*********************************************************************** | ||
10617 | +** acx_rxbuf_to_ether | ||
10618 | +** | ||
10619 | +** Uses the contents of a received 802.11 frame to build an ether | ||
10620 | +** frame. | ||
10621 | +** | ||
10622 | +** This function extracts the src and dest address from the 802.11 | ||
10623 | +** frame to use in the construction of the eth frame. | ||
10624 | +** | ||
10625 | +** Based largely on p80211conv.c of the linux-wlan-ng project | ||
10626 | +*/ | ||
10627 | +struct sk_buff* | ||
10628 | +acx_rxbuf_to_ether(acx_device_t *adev, rxbuffer_t *rxbuf) | ||
10629 | +{ | ||
10630 | + struct wlan_hdr *w_hdr; | ||
10631 | + struct wlan_ethhdr *e_hdr; | ||
10632 | + struct wlan_llc *e_llc; | ||
10633 | + struct wlan_snap *e_snap; | ||
10634 | + struct sk_buff *skb; | ||
10635 | + const u8 *daddr; | ||
10636 | + const u8 *saddr; | ||
10637 | + const u8 *e_payload; | ||
10638 | + int buflen, payload_length; | ||
10639 | + unsigned int payload_offset, mtu; | ||
10640 | + u16 fc; | ||
10641 | + | ||
10642 | + FN_ENTER; | ||
10643 | + | ||
10644 | + /* This looks complex because it must handle possible | ||
10645 | + ** phy header in rxbuff */ | ||
10646 | + w_hdr = acx_get_wlan_hdr(adev, rxbuf); | ||
10647 | + payload_offset = WLAN_HDR_A3_LEN; /* it is relative to w_hdr */ | ||
10648 | + payload_length = RXBUF_BYTES_USED(rxbuf) /* entire rxbuff... */ | ||
10649 | + - ((u8*)w_hdr - (u8*)rxbuf) /* minus space before 802.11 frame */ | ||
10650 | + - WLAN_HDR_A3_LEN; /* minus 802.11 header */ | ||
10651 | + | ||
10652 | + /* setup some vars for convenience */ | ||
10653 | + fc = w_hdr->fc; | ||
10654 | + switch (WF_FC_FROMTODSi & fc) { | ||
10655 | + case 0: | ||
10656 | + daddr = w_hdr->a1; | ||
10657 | + saddr = w_hdr->a2; | ||
10658 | + break; | ||
10659 | + case WF_FC_FROMDSi: | ||
10660 | + daddr = w_hdr->a1; | ||
10661 | + saddr = w_hdr->a3; | ||
10662 | + break; | ||
10663 | + case WF_FC_TODSi: | ||
10664 | + daddr = w_hdr->a3; | ||
10665 | + saddr = w_hdr->a2; | ||
10666 | + break; | ||
10667 | + default: /* WF_FC_FROMTODSi */ | ||
10668 | + payload_offset += (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); | ||
10669 | + payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); | ||
10670 | + daddr = w_hdr->a3; | ||
10671 | + saddr = w_hdr->a4; | ||
10672 | + } | ||
10673 | + | ||
10674 | + if ((WF_FC_ISWEPi & fc) && IS_ACX100(adev)) { | ||
10675 | + /* chop off the IV+ICV WEP header and footer */ | ||
10676 | + log(L_DATA|L_DEBUG, "rx: WEP packet, " | ||
10677 | + "chopping off IV and ICV\n"); | ||
10678 | + payload_offset += WLAN_WEP_IV_LEN; | ||
10679 | + payload_length -= WLAN_WEP_IV_LEN + WLAN_WEP_ICV_LEN; | ||
10680 | + } | ||
10681 | + | ||
10682 | + if (unlikely(payload_length < 0)) { | ||
10683 | + printk("%s: rx frame too short, ignored\n", adev->ndev->name); | ||
10684 | + goto ret_null; | ||
10685 | + } | ||
10686 | + | ||
10687 | + e_hdr = (wlan_ethhdr_t*) ((u8*) w_hdr + payload_offset); | ||
10688 | + e_llc = (wlan_llc_t*) e_hdr; | ||
10689 | + e_snap = (wlan_snap_t*) (e_llc + 1); | ||
10690 | + mtu = adev->ndev->mtu; | ||
10691 | + e_payload = (u8*) (e_snap + 1); | ||
10692 | + | ||
10693 | + log(L_DATA, "rx: payload_offset %d, payload_length %d\n", | ||
10694 | + payload_offset, payload_length); | ||
10695 | + log(L_XFER|L_DATA, | ||
10696 | + "rx: frame info: llc=%02X%02X%02X " | ||
10697 | + "snap.oui=%02X%02X%02X snap.type=%04X\n", | ||
10698 | + e_llc->dsap, e_llc->ssap, e_llc->ctl, | ||
10699 | + e_snap->oui[0], e_snap->oui[1], e_snap->oui[2], | ||
10700 | + ntohs(e_snap->type)); | ||
10701 | + | ||
10702 | + /* Test for the various encodings */ | ||
10703 | + if ((payload_length >= sizeof(wlan_ethhdr_t)) | ||
10704 | + && ((e_llc->dsap != 0xaa) || (e_llc->ssap != 0xaa)) | ||
10705 | + && ( (mac_is_equal(daddr, e_hdr->daddr)) | ||
10706 | + || (mac_is_equal(saddr, e_hdr->saddr)) | ||
10707 | + ) | ||
10708 | + ) { | ||
10709 | + /* 802.3 Encapsulated: */ | ||
10710 | + /* wlan frame body contains complete eth frame (header+body) */ | ||
10711 | + log(L_DEBUG|L_DATA, "rx: 802.3 ENCAP len=%d\n", payload_length); | ||
10712 | + | ||
10713 | + if (unlikely(payload_length > (mtu + ETH_HLEN))) { | ||
10714 | + printk("%s: rx: ENCAP frame too large (%d > %d)\n", | ||
10715 | + adev->ndev->name, | ||
10716 | + payload_length, mtu + ETH_HLEN); | ||
10717 | + goto ret_null; | ||
10718 | + } | ||
10719 | + | ||
10720 | + /* allocate space and setup host buffer */ | ||
10721 | + buflen = payload_length; | ||
10722 | + /* Attempt to align IP header (14 bytes eth header + 2 = 16) */ | ||
10723 | + skb = dev_alloc_skb(buflen + 2); | ||
10724 | + if (unlikely(!skb)) | ||
10725 | + goto no_skb; | ||
10726 | + skb_reserve(skb, 2); | ||
10727 | + skb_put(skb, buflen); /* make room */ | ||
10728 | + | ||
10729 | + /* now copy the data from the 80211 frame */ | ||
10730 | + memcpy(skb->data, e_hdr, payload_length); | ||
10731 | + | ||
10732 | + } else if ( (payload_length >= sizeof(wlan_llc_t)+sizeof(wlan_snap_t)) | ||
10733 | + && llc_is_snap(e_llc) ) { | ||
10734 | + /* wlan frame body contains: AA AA 03 ... (it's a SNAP) */ | ||
10735 | + | ||
10736 | + if ( !oui_is_rfc1042(e_snap) | ||
10737 | + || (proto_is_stt(ieee2host16(e_snap->type)) /* && (ethconv == WLAN_ETHCONV_8021h) */)) { | ||
10738 | + log(L_DEBUG|L_DATA, "rx: SNAP+RFC1042 len=%d\n", payload_length); | ||
10739 | + /* wlan frame body contains: AA AA 03 !(00 00 00) ... -or- */ | ||
10740 | + /* wlan frame body contains: AA AA 03 00 00 00 0x80f3 ... */ | ||
10741 | + /* build eth hdr, type = len, copy AA AA 03... as eth body */ | ||
10742 | + /* it's a SNAP + RFC1042 frame && protocol is in STT */ | ||
10743 | + | ||
10744 | + if (unlikely(payload_length > mtu)) { | ||
10745 | + printk("%s: rx: SNAP frame too large (%d > %d)\n", | ||
10746 | + adev->ndev->name, | ||
10747 | + payload_length, mtu); | ||
10748 | + goto ret_null; | ||
10749 | + } | ||
10750 | + | ||
10751 | + /* allocate space and setup host buffer */ | ||
10752 | + buflen = payload_length + ETH_HLEN; | ||
10753 | + skb = dev_alloc_skb(buflen + 2); | ||
10754 | + if (unlikely(!skb)) | ||
10755 | + goto no_skb; | ||
10756 | + skb_reserve(skb, 2); | ||
10757 | + skb_put(skb, buflen); /* make room */ | ||
10758 | + | ||
10759 | + /* create 802.3 header */ | ||
10760 | + e_hdr = (wlan_ethhdr_t*) skb->data; | ||
10761 | + MAC_COPY(e_hdr->daddr, daddr); | ||
10762 | + MAC_COPY(e_hdr->saddr, saddr); | ||
10763 | + e_hdr->type = htons(payload_length); | ||
10764 | + | ||
10765 | + /* Now copy the data from the 80211 frame. | ||
10766 | + Make room in front for the eth header, and keep the | ||
10767 | + llc and snap from the 802.11 payload */ | ||
10768 | + memcpy(skb->data + ETH_HLEN, | ||
10769 | + e_llc, payload_length); | ||
10770 | + | ||
10771 | + } else { | ||
10772 | + /* wlan frame body contains: AA AA 03 00 00 00 [type] [tail] */ | ||
10773 | + /* build eth hdr, type=[type], copy [tail] as eth body */ | ||
10774 | + log(L_DEBUG|L_DATA, "rx: 802.1h/RFC1042 len=%d\n", | ||
10775 | + payload_length); | ||
10776 | + /* it's an 802.1h frame (an RFC1042 && protocol is not in STT) */ | ||
10777 | + /* build a DIXII + RFC894 */ | ||
10778 | + | ||
10779 | + payload_length -= sizeof(wlan_llc_t) + sizeof(wlan_snap_t); | ||
10780 | + if (unlikely(payload_length > mtu)) { | ||
10781 | + printk("%s: rx: DIXII frame too large (%d > %d)\n", | ||
10782 | + adev->ndev->name, | ||
10783 | + payload_length, mtu); | ||
10784 | + goto ret_null; | ||
10785 | + } | ||
10786 | + | ||
10787 | + /* allocate space and setup host buffer */ | ||
10788 | + buflen = payload_length + ETH_HLEN; | ||
10789 | + skb = dev_alloc_skb(buflen + 2); | ||
10790 | + if (unlikely(!skb)) | ||
10791 | + goto no_skb; | ||
10792 | + skb_reserve(skb, 2); | ||
10793 | + skb_put(skb, buflen); /* make room */ | ||
10794 | + | ||
10795 | + /* create 802.3 header */ | ||
10796 | + e_hdr = (wlan_ethhdr_t *) skb->data; | ||
10797 | + MAC_COPY(e_hdr->daddr, daddr); | ||
10798 | + MAC_COPY(e_hdr->saddr, saddr); | ||
10799 | + e_hdr->type = e_snap->type; | ||
10800 | + | ||
10801 | + /* Now copy the data from the 80211 frame. | ||
10802 | + Make room in front for the eth header, and cut off the | ||
10803 | + llc and snap from the 802.11 payload */ | ||
10804 | + memcpy(skb->data + ETH_HLEN, | ||
10805 | + e_payload, payload_length); | ||
10806 | + } | ||
10807 | + | ||
10808 | + } else { | ||
10809 | + log(L_DEBUG|L_DATA, "rx: NON-ENCAP len=%d\n", payload_length); | ||
10810 | + /* build eth hdr, type=len, copy wlan body as eth body */ | ||
10811 | + /* any NON-ENCAP */ | ||
10812 | + /* it's a generic 80211+LLC or IPX 'Raw 802.3' */ | ||
10813 | + /* build an 802.3 frame */ | ||
10814 | + | ||
10815 | + if (unlikely(payload_length > mtu)) { | ||
10816 | + printk("%s: rx: OTHER frame too large (%d > %d)\n", | ||
10817 | + adev->ndev->name, payload_length, mtu); | ||
10818 | + goto ret_null; | ||
10819 | + } | ||
10820 | + | ||
10821 | + /* allocate space and setup host buffer */ | ||
10822 | + buflen = payload_length + ETH_HLEN; | ||
10823 | + skb = dev_alloc_skb(buflen + 2); | ||
10824 | + if (unlikely(!skb)) | ||
10825 | + goto no_skb; | ||
10826 | + skb_reserve(skb, 2); | ||
10827 | + skb_put(skb, buflen); /* make room */ | ||
10828 | + | ||
10829 | + /* set up the 802.3 header */ | ||
10830 | + e_hdr = (wlan_ethhdr_t *) skb->data; | ||
10831 | + MAC_COPY(e_hdr->daddr, daddr); | ||
10832 | + MAC_COPY(e_hdr->saddr, saddr); | ||
10833 | + e_hdr->type = htons(payload_length); | ||
10834 | + | ||
10835 | + /* now copy the data from the 80211 frame */ | ||
10836 | + memcpy(skb->data + ETH_HLEN, e_llc, payload_length); | ||
10837 | + } | ||
10838 | + | ||
10839 | + skb->dev = adev->ndev; | ||
10840 | + skb->protocol = eth_type_trans(skb, adev->ndev); | ||
10841 | + | ||
10842 | +#ifdef DEBUG_CONVERT | ||
10843 | + if (acx_debug & L_DATA) { | ||
10844 | + int len = RXBUF_BYTES_RCVD(adev, rxbuf); | ||
10845 | + printk("p802.11 frame [%d]: ", len); | ||
10846 | + acx_dump_bytes(w_hdr, len); | ||
10847 | + printk("eth frame [%d]: ", skb->len); | ||
10848 | + acx_dump_bytes(skb->data, skb->len); | ||
10849 | + } | ||
10850 | +#endif | ||
10851 | + | ||
10852 | + FN_EXIT0; | ||
10853 | + return skb; | ||
10854 | + | ||
10855 | +no_skb: | ||
10856 | + printk("%s: rx: no memory for skb (%d bytes)\n", | ||
10857 | + adev->ndev->name, buflen + 2); | ||
10858 | +ret_null: | ||
10859 | + FN_EXIT1((int)NULL); | ||
10860 | + return NULL; | ||
10861 | +} | ||
10862 | Index: linux-2.6.22/drivers/net/wireless/acx/cs.c | ||
10863 | =================================================================== | ||
10864 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
10865 | +++ linux-2.6.22/drivers/net/wireless/acx/cs.c 2007-08-23 18:34:19.000000000 +0200 | ||
10866 | @@ -0,0 +1,5703 @@ | ||
10867 | +/*********************************************************************** | ||
10868 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
10869 | +** | ||
10870 | +** The contents of this file are subject to the Mozilla Public | ||
10871 | +** License Version 1.1 (the "License"); you may not use this file | ||
10872 | +** except in compliance with the License. You may obtain a copy of | ||
10873 | +** the License at http://www.mozilla.org/MPL/ | ||
10874 | +** | ||
10875 | +** Software distributed under the License is distributed on an "AS | ||
10876 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
10877 | +** implied. See the License for the specific language governing | ||
10878 | +** rights and limitations under the License. | ||
10879 | +** | ||
10880 | +** Alternatively, the contents of this file may be used under the | ||
10881 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
10882 | +** case the provisions of the GPL are applicable instead of the | ||
10883 | +** above. If you wish to allow the use of your version of this file | ||
10884 | +** only under the terms of the GPL and not to allow others to use | ||
10885 | +** your version of this file under the MPL, indicate your decision | ||
10886 | +** by deleting the provisions above and replace them with the notice | ||
10887 | +** and other provisions required by the GPL. If you do not delete | ||
10888 | +** the provisions above, a recipient may use your version of this | ||
10889 | +** file under either the MPL or the GPL. | ||
10890 | +** --------------------------------------------------------------------- | ||
10891 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
10892 | +** made directly to: | ||
10893 | +** | ||
10894 | +** acx100-users@lists.sf.net | ||
10895 | +** http://acx100.sf.net | ||
10896 | +** --------------------------------------------------------------------- | ||
10897 | +** | ||
10898 | +** Slave memory interface support: | ||
10899 | +** | ||
10900 | +** Todd Blumer - SDG Systems | ||
10901 | +** Bill Reese - HP | ||
10902 | +** Eric McCorkle - Shadowsun | ||
10903 | +** | ||
10904 | +** CF support, (c) Fabrice Crohas, Paul Sokolovsky | ||
10905 | +*/ | ||
10906 | +#define ACX_MEM 1 | ||
10907 | + | ||
10908 | +/* | ||
10909 | + * non-zero makes it dump the ACX memory to the console then | ||
10910 | + * panic when you cat /proc/driver/acx_wlan0_diag | ||
10911 | + */ | ||
10912 | +#define DUMP_MEM_DEFINED 1 | ||
10913 | + | ||
10914 | +#define DUMP_MEM_DURING_DIAG 0 | ||
10915 | +#define DUMP_IF_SLOW 0 | ||
10916 | + | ||
10917 | +#define PATCH_AROUND_BAD_SPOTS 1 | ||
10918 | +#define HX4700_FIRMWARE_CHECKSUM 0x0036862e | ||
10919 | +#define HX4700_ALTERNATE_FIRMWARE_CHECKSUM 0x00368a75 | ||
10920 | + | ||
10921 | +#include <linux/version.h> | ||
10922 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) | ||
10923 | +#include <linux/config.h> | ||
10924 | +#endif | ||
10925 | + | ||
10926 | +/* Linux 2.6.18+ uses <linux/utsrelease.h> */ | ||
10927 | +#ifndef UTS_RELEASE | ||
10928 | +#include <linux/utsrelease.h> | ||
10929 | +#endif | ||
10930 | + | ||
10931 | +#include <linux/compiler.h> /* required for Lx 2.6.8 ?? */ | ||
10932 | +#include <linux/kernel.h> | ||
10933 | +#include <linux/module.h> | ||
10934 | +#include <linux/moduleparam.h> | ||
10935 | +#include <linux/sched.h> | ||
10936 | +#include <linux/types.h> | ||
10937 | +#include <linux/skbuff.h> | ||
10938 | +#include <linux/slab.h> | ||
10939 | +#include <linux/if_arp.h> | ||
10940 | +#include <linux/irq.h> | ||
10941 | +#include <linux/rtnetlink.h> | ||
10942 | +#include <linux/wireless.h> | ||
10943 | +#include <net/iw_handler.h> | ||
10944 | +#include <linux/netdevice.h> | ||
10945 | +#include <linux/ioport.h> | ||
10946 | +#include <linux/pci.h> | ||
10947 | +#include <linux/platform_device.h> | ||
10948 | +#include <linux/pm.h> | ||
10949 | +#include <linux/vmalloc.h> | ||
10950 | +#include <linux/delay.h> | ||
10951 | +#include <linux/workqueue.h> | ||
10952 | +#include <linux/inetdevice.h> | ||
10953 | + | ||
10954 | +#define PCMCIA_DEBUG 1 | ||
10955 | + | ||
10956 | +/* | ||
10957 | + All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If | ||
10958 | + you do not define PCMCIA_DEBUG at all, all the debug code will be | ||
10959 | + left out. If you compile with PCMCIA_DEBUG=0, the debug code will | ||
10960 | + be present but disabled -- but it can then be enabled for specific | ||
10961 | + modules at load time with a 'pc_debug=#' option to insmod. | ||
10962 | + | ||
10963 | +*/ | ||
10964 | +#include <pcmcia/cs_types.h> | ||
10965 | +#include <pcmcia/cs.h> | ||
10966 | +#include <pcmcia/cistpl.h> | ||
10967 | +#include <pcmcia/cisreg.h> | ||
10968 | +#include <pcmcia/ds.h> | ||
10969 | +#include "acx.h" | ||
10970 | +#include "acx_hw.h" | ||
10971 | + | ||
10972 | +#ifdef PCMCIA_DEBUG | ||
10973 | +static int pc_debug = PCMCIA_DEBUG; | ||
10974 | +module_param(pc_debug, int, 0); | ||
10975 | +static char *version = "$Revision: 1.10 $"; | ||
10976 | +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args); | ||
10977 | +#else | ||
10978 | +#define DEBUG(n, args...) | ||
10979 | +#endif | ||
10980 | + | ||
10981 | + | ||
10982 | +static win_req_t memwin; | ||
10983 | + | ||
10984 | +typedef struct local_info_t { | ||
10985 | + dev_node_t node; | ||
10986 | + struct net_device *ndev; | ||
10987 | +} local_info_t; | ||
10988 | + | ||
10989 | +static struct net_device *resume_ndev; | ||
10990 | + | ||
10991 | + | ||
10992 | +/*********************************************************************** | ||
10993 | +*/ | ||
10994 | + | ||
10995 | +#define CARD_EEPROM_ID_SIZE 6 | ||
10996 | + | ||
10997 | +#include <asm/io.h> | ||
10998 | + | ||
10999 | +#define REG_ACX_VENDOR_ID 0x900 | ||
11000 | +/* | ||
11001 | + * This is the vendor id on the HX4700, anyway | ||
11002 | + */ | ||
11003 | +#define ACX_VENDOR_ID 0x8400104c | ||
11004 | + | ||
11005 | +typedef enum { | ||
11006 | + ACX_SOFT_RESET = 0, | ||
11007 | + | ||
11008 | + ACX_SLV_REG_ADDR, | ||
11009 | + ACX_SLV_REG_DATA, | ||
11010 | + ACX_SLV_REG_ADATA, | ||
11011 | + | ||
11012 | + ACX_SLV_MEM_CP, | ||
11013 | + ACX_SLV_MEM_ADDR, | ||
11014 | + ACX_SLV_MEM_DATA, | ||
11015 | + ACX_SLV_MEM_CTL, | ||
11016 | +} acxreg_t; | ||
11017 | + | ||
11018 | +/*********************************************************************** | ||
11019 | +*/ | ||
11020 | +static void acxmem_i_tx_timeout(struct net_device *ndev); | ||
11021 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
11022 | +static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id); | ||
11023 | +#else | ||
11024 | +static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id, struct pt_regs *regs); | ||
11025 | +#endif | ||
11026 | +static void acxmem_i_set_multicast_list(struct net_device *ndev); | ||
11027 | + | ||
11028 | +static int acxmem_e_open(struct net_device *ndev); | ||
11029 | +static int acxmem_e_close(struct net_device *ndev); | ||
11030 | +static void acxmem_s_up(struct net_device *ndev); | ||
11031 | +static void acxmem_s_down(struct net_device *ndev); | ||
11032 | + | ||
11033 | +static void dump_acxmem (acx_device_t *adev, u32 start, int length); | ||
11034 | +static int acxmem_complete_hw_reset (acx_device_t *adev); | ||
11035 | +static void acxmem_s_delete_dma_regions(acx_device_t *adev); | ||
11036 | + | ||
11037 | +static int | ||
11038 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) | ||
11039 | +acxmem_e_suspend( struct net_device *ndev, pm_message_t state); | ||
11040 | +#else | ||
11041 | +acxmem_e_suspend( struct net_device *ndev, u32 state); | ||
11042 | +#endif | ||
11043 | +static void | ||
11044 | +fw_resumer(struct work_struct *notused); | ||
11045 | +//fw_resumer( void *data ); | ||
11046 | + | ||
11047 | +static int acx_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) | ||
11048 | +{ | ||
11049 | + struct net_device *ndev = ptr; | ||
11050 | + acx_device_t *adev = ndev2adev(ndev); | ||
11051 | + | ||
11052 | + /* | ||
11053 | + * Upper level ioctl() handlers send a NETDEV_CHANGEADDR if the MAC address changes. | ||
11054 | + */ | ||
11055 | + | ||
11056 | + if (NETDEV_CHANGEADDR == event) { | ||
11057 | + /* | ||
11058 | + * the upper layers put the new MAC address in ndev->dev_addr; we just copy | ||
11059 | + * it over and update the ACX with it. | ||
11060 | + */ | ||
11061 | + MAC_COPY(adev->dev_addr, adev->ndev->dev_addr); | ||
11062 | + adev->set_mask |= GETSET_STATION_ID; | ||
11063 | + acx_s_update_card_settings (adev); | ||
11064 | + } | ||
11065 | + | ||
11066 | + return 0; | ||
11067 | +} | ||
11068 | + | ||
11069 | +static struct notifier_block acx_netdev_notifier = { | ||
11070 | + .notifier_call = acx_netdev_event, | ||
11071 | +}; | ||
11072 | + | ||
11073 | +/*********************************************************************** | ||
11074 | +** Register access | ||
11075 | +*/ | ||
11076 | + | ||
11077 | +/* Pick one */ | ||
11078 | +/* #define INLINE_IO static */ | ||
11079 | +#define INLINE_IO static inline | ||
11080 | + | ||
11081 | +INLINE_IO u32 | ||
11082 | +read_id_register (acx_device_t *adev) | ||
11083 | +{ | ||
11084 | + writel (0x24, &adev->iobase[ACX_SLV_REG_ADDR]); | ||
11085 | + return readl (&adev->iobase[ACX_SLV_REG_DATA]); | ||
11086 | +} | ||
11087 | + | ||
11088 | +INLINE_IO u32 | ||
11089 | +read_reg32(acx_device_t *adev, unsigned int offset) | ||
11090 | +{ | ||
11091 | + u32 val; | ||
11092 | + u32 addr; | ||
11093 | + | ||
11094 | + if (offset > IO_ACX_ECPU_CTRL) | ||
11095 | + addr = offset; | ||
11096 | + else | ||
11097 | + addr = adev->io[offset]; | ||
11098 | + | ||
11099 | + if (addr < 0x20) { | ||
11100 | + return readl(((u8*)adev->iobase) + addr); | ||
11101 | + } | ||
11102 | + | ||
11103 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
11104 | + val = readl( &adev->iobase[ACX_SLV_REG_DATA] ); | ||
11105 | + | ||
11106 | + return val; | ||
11107 | +} | ||
11108 | + | ||
11109 | +INLINE_IO u16 | ||
11110 | +read_reg16(acx_device_t *adev, unsigned int offset) | ||
11111 | +{ | ||
11112 | + u16 lo; | ||
11113 | + u32 addr; | ||
11114 | + | ||
11115 | + if (offset > IO_ACX_ECPU_CTRL) | ||
11116 | + addr = offset; | ||
11117 | + else | ||
11118 | + addr = adev->io[offset]; | ||
11119 | + | ||
11120 | + if (addr < 0x20) { | ||
11121 | + return readw(((u8 *) adev->iobase) + addr); | ||
11122 | + } | ||
11123 | + | ||
11124 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
11125 | + lo = readw( (u16 *)&adev->iobase[ACX_SLV_REG_DATA] ); | ||
11126 | + | ||
11127 | + return lo; | ||
11128 | +} | ||
11129 | + | ||
11130 | +INLINE_IO u8 | ||
11131 | +read_reg8(acx_device_t *adev, unsigned int offset) | ||
11132 | +{ | ||
11133 | + u8 lo; | ||
11134 | + u32 addr; | ||
11135 | + | ||
11136 | + if (offset > IO_ACX_ECPU_CTRL) | ||
11137 | + addr = offset; | ||
11138 | + else | ||
11139 | + addr = adev->io[offset]; | ||
11140 | + | ||
11141 | + if (addr < 0x20) | ||
11142 | + return readb(((u8 *)adev->iobase) + addr); | ||
11143 | + | ||
11144 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
11145 | + lo = readw( (u8 *)&adev->iobase[ACX_SLV_REG_DATA] ); | ||
11146 | + | ||
11147 | + return (u8)lo; | ||
11148 | +} | ||
11149 | + | ||
11150 | +INLINE_IO void | ||
11151 | +write_reg32(acx_device_t *adev, unsigned int offset, u32 val) | ||
11152 | +{ | ||
11153 | + u32 addr; | ||
11154 | + | ||
11155 | + if (offset > IO_ACX_ECPU_CTRL) | ||
11156 | + addr = offset; | ||
11157 | + else | ||
11158 | + addr = adev->io[offset]; | ||
11159 | + | ||
11160 | + if (addr < 0x20) { | ||
11161 | + writel(val, ((u8*)adev->iobase) + addr); | ||
11162 | + return; | ||
11163 | + } | ||
11164 | + | ||
11165 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
11166 | + writel( val, &adev->iobase[ACX_SLV_REG_DATA] ); | ||
11167 | +} | ||
11168 | + | ||
11169 | +INLINE_IO void | ||
11170 | +write_reg16(acx_device_t *adev, unsigned int offset, u16 val) | ||
11171 | +{ | ||
11172 | + u32 addr; | ||
11173 | + | ||
11174 | + if (offset > IO_ACX_ECPU_CTRL) | ||
11175 | + addr = offset; | ||
11176 | + else | ||
11177 | + addr = adev->io[offset]; | ||
11178 | + | ||
11179 | + if (addr < 0x20) { | ||
11180 | + writew(val, ((u8 *)adev->iobase) + addr); | ||
11181 | + return; | ||
11182 | + } | ||
11183 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
11184 | + writew( val, (u16 *) &adev->iobase[ACX_SLV_REG_DATA] ); | ||
11185 | +} | ||
11186 | + | ||
11187 | +INLINE_IO void | ||
11188 | +write_reg8(acx_device_t *adev, unsigned int offset, u8 val) | ||
11189 | +{ | ||
11190 | + u32 addr; | ||
11191 | + | ||
11192 | + if (offset > IO_ACX_ECPU_CTRL) | ||
11193 | + addr = offset; | ||
11194 | + else | ||
11195 | + addr = adev->io[offset]; | ||
11196 | + | ||
11197 | + if (addr < 0x20) { | ||
11198 | + writeb(val, ((u8 *) adev->iobase) + addr); | ||
11199 | + return; | ||
11200 | + } | ||
11201 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
11202 | + writeb( val, (u8 *)&adev->iobase[ACX_SLV_REG_DATA] ); | ||
11203 | +} | ||
11204 | + | ||
11205 | +/* Handle PCI posting properly: | ||
11206 | + * Make sure that writes reach the adapter in case they require to be executed | ||
11207 | + * *before* the next write, by reading a random (and safely accessible) register. | ||
11208 | + * This call has to be made if there is no read following (which would flush the data | ||
11209 | + * to the adapter), yet the written data has to reach the adapter immediately. */ | ||
11210 | +INLINE_IO void | ||
11211 | +write_flush(acx_device_t *adev) | ||
11212 | +{ | ||
11213 | + /* readb(adev->iobase + adev->io[IO_ACX_INFO_MAILBOX_OFFS]); */ | ||
11214 | + /* faster version (accesses the first register, IO_ACX_SOFT_RESET, | ||
11215 | + * which should also be safe): */ | ||
11216 | + (void) readl(adev->iobase); | ||
11217 | +} | ||
11218 | + | ||
11219 | +INLINE_IO void | ||
11220 | +set_regbits (acx_device_t *adev, unsigned int offset, u32 bits) { | ||
11221 | + u32 tmp; | ||
11222 | + | ||
11223 | + tmp = read_reg32 (adev, offset); | ||
11224 | + tmp = tmp | bits; | ||
11225 | + write_reg32 (adev, offset, tmp); | ||
11226 | + write_flush (adev); | ||
11227 | +} | ||
11228 | + | ||
11229 | +INLINE_IO void | ||
11230 | +clear_regbits (acx_device_t *adev, unsigned int offset, u32 bits) { | ||
11231 | + u32 tmp; | ||
11232 | + | ||
11233 | + tmp = read_reg32 (adev, offset); | ||
11234 | + tmp = tmp & ~bits; | ||
11235 | + write_reg32 (adev, offset, tmp); | ||
11236 | + write_flush (adev); | ||
11237 | +} | ||
11238 | + | ||
11239 | +/* | ||
11240 | + * Copy from PXA memory to the ACX memory. This assumes both the PXA and ACX | ||
11241 | + * addresses are 32 bit aligned. Count is in bytes. | ||
11242 | + */ | ||
11243 | +INLINE_IO void | ||
11244 | +write_slavemem32 (acx_device_t *adev, u32 slave_address, u32 val) | ||
11245 | +{ | ||
11246 | + write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0); | ||
11247 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address); | ||
11248 | + udelay (10); | ||
11249 | + write_reg32 (adev, IO_ACX_SLV_MEM_DATA, val); | ||
11250 | +} | ||
11251 | + | ||
11252 | +INLINE_IO u32 | ||
11253 | +read_slavemem32 (acx_device_t *adev, u32 slave_address) | ||
11254 | +{ | ||
11255 | + u32 val; | ||
11256 | + | ||
11257 | + write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0); | ||
11258 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address); | ||
11259 | + udelay (10); | ||
11260 | + val = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); | ||
11261 | + | ||
11262 | + return val; | ||
11263 | +} | ||
11264 | + | ||
11265 | +INLINE_IO void | ||
11266 | +write_slavemem8 (acx_device_t *adev, u32 slave_address, u8 val) | ||
11267 | +{ | ||
11268 | + u32 data; | ||
11269 | + u32 base; | ||
11270 | + int offset; | ||
11271 | + | ||
11272 | + /* | ||
11273 | + * Get the word containing the target address and the byte offset in that word. | ||
11274 | + */ | ||
11275 | + base = slave_address & ~3; | ||
11276 | + offset = (slave_address & 3) * 8; | ||
11277 | + | ||
11278 | + data = read_slavemem32 (adev, base); | ||
11279 | + data &= ~(0xff << offset); | ||
11280 | + data |= val << offset; | ||
11281 | + write_slavemem32 (adev, base, data); | ||
11282 | +} | ||
11283 | + | ||
11284 | +INLINE_IO u8 | ||
11285 | +read_slavemem8 (acx_device_t *adev, u32 slave_address) | ||
11286 | +{ | ||
11287 | + u8 val; | ||
11288 | + u32 base; | ||
11289 | + u32 data; | ||
11290 | + int offset; | ||
11291 | + | ||
11292 | + base = slave_address & ~3; | ||
11293 | + offset = (slave_address & 3) * 8; | ||
11294 | + | ||
11295 | + data = read_slavemem32 (adev, base); | ||
11296 | + | ||
11297 | + val = (data >> offset) & 0xff; | ||
11298 | + | ||
11299 | + return val; | ||
11300 | +} | ||
11301 | + | ||
11302 | +/* | ||
11303 | + * doesn't split across word boundaries | ||
11304 | + */ | ||
11305 | +INLINE_IO void | ||
11306 | +write_slavemem16 (acx_device_t *adev, u32 slave_address, u16 val) | ||
11307 | +{ | ||
11308 | + u32 data; | ||
11309 | + u32 base; | ||
11310 | + int offset; | ||
11311 | + | ||
11312 | + /* | ||
11313 | + * Get the word containing the target address and the byte offset in that word. | ||
11314 | + */ | ||
11315 | + base = slave_address & ~3; | ||
11316 | + offset = (slave_address & 3) * 8; | ||
11317 | + | ||
11318 | + data = read_slavemem32 (adev, base); | ||
11319 | + data &= ~(0xffff << offset); | ||
11320 | + data |= val << offset; | ||
11321 | + write_slavemem32 (adev, base, data); | ||
11322 | +} | ||
11323 | + | ||
11324 | +/* | ||
11325 | + * doesn't split across word boundaries | ||
11326 | + */ | ||
11327 | +INLINE_IO u16 | ||
11328 | +read_slavemem16 (acx_device_t *adev, u32 slave_address) | ||
11329 | +{ | ||
11330 | + u16 val; | ||
11331 | + u32 base; | ||
11332 | + u32 data; | ||
11333 | + int offset; | ||
11334 | + | ||
11335 | + base = slave_address & ~3; | ||
11336 | + offset = (slave_address & 3) * 8; | ||
11337 | + | ||
11338 | + data = read_slavemem32 (adev, base); | ||
11339 | + | ||
11340 | + val = (data >> offset) & 0xffff; | ||
11341 | + | ||
11342 | + return val; | ||
11343 | +} | ||
11344 | + | ||
11345 | +/* | ||
11346 | + * Copy from slave memory | ||
11347 | + * | ||
11348 | + * TODO - rewrite using address autoincrement, handle partial words | ||
11349 | + */ | ||
11350 | +void | ||
11351 | +copy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count) { | ||
11352 | + u32 tmp = 0; | ||
11353 | + u8 *ptmp = (u8 *) &tmp; | ||
11354 | + | ||
11355 | + /* | ||
11356 | + * Right now I'm making the assumption that the destination is aligned, but | ||
11357 | + * I'd better check. | ||
11358 | + */ | ||
11359 | + if ((u32) destination & 3) { | ||
11360 | + printk ("acx copy_from_slavemem: warning! destination not word-aligned!\n"); | ||
11361 | + } | ||
11362 | + | ||
11363 | + while (count >= 4) { | ||
11364 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source); | ||
11365 | + udelay (10); | ||
11366 | + *((u32 *) destination) = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); | ||
11367 | + count -= 4; | ||
11368 | + source += 4; | ||
11369 | + destination += 4; | ||
11370 | + } | ||
11371 | + | ||
11372 | + /* | ||
11373 | + * If the word reads above didn't satisfy the count, read one more word | ||
11374 | + * and transfer a byte at a time until the request is satisfied. | ||
11375 | + */ | ||
11376 | + if (count) { | ||
11377 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source); | ||
11378 | + udelay (10); | ||
11379 | + tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); | ||
11380 | + while (count--) { | ||
11381 | + *destination++ = *ptmp++; | ||
11382 | + } | ||
11383 | + } | ||
11384 | +} | ||
11385 | + | ||
11386 | +/* | ||
11387 | + * Copy to slave memory | ||
11388 | + * | ||
11389 | + * TODO - rewrite using autoincrement, handle partial words | ||
11390 | + */ | ||
11391 | +void | ||
11392 | +copy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count) | ||
11393 | +{ | ||
11394 | + u32 tmp = 0; | ||
11395 | + u8* ptmp = (u8 *) &tmp; | ||
11396 | + static u8 src[512]; /* make static to avoid huge stack objects */ | ||
11397 | + | ||
11398 | + /* | ||
11399 | + * For now, make sure the source is word-aligned by copying it to a word-aligned | ||
11400 | + * buffer. Someday rewrite to avoid the extra copy. | ||
11401 | + */ | ||
11402 | + if (count > sizeof (src)) { | ||
11403 | + printk ("acx copy_to_slavemem: Warning! buffer overflow!\n"); | ||
11404 | + count = sizeof (src); | ||
11405 | + } | ||
11406 | + memcpy (src, source, count); | ||
11407 | + source = src; | ||
11408 | + | ||
11409 | + while (count >= 4) { | ||
11410 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); | ||
11411 | + udelay (10); | ||
11412 | + write_reg32 (adev, IO_ACX_SLV_MEM_DATA, *((u32 *) source)); | ||
11413 | + count -= 4; | ||
11414 | + source += 4; | ||
11415 | + destination += 4; | ||
11416 | + } | ||
11417 | + | ||
11418 | + /* | ||
11419 | + * If there are leftovers read the next word from the acx and merge in | ||
11420 | + * what they want to write. | ||
11421 | + */ | ||
11422 | + if (count) { | ||
11423 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); | ||
11424 | + udelay (10); | ||
11425 | + tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); | ||
11426 | + while (count--) { | ||
11427 | + *ptmp++ = *source++; | ||
11428 | + } | ||
11429 | + /* | ||
11430 | + * reset address in case we're currently in auto-increment mode | ||
11431 | + */ | ||
11432 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); | ||
11433 | + udelay (10); | ||
11434 | + write_reg32 (adev, IO_ACX_SLV_MEM_DATA, tmp); | ||
11435 | + udelay (10); | ||
11436 | + } | ||
11437 | + | ||
11438 | +} | ||
11439 | + | ||
11440 | +/* | ||
11441 | + * Block copy to slave buffers using memory block chain mode. Copies to the ACX | ||
11442 | + * transmit buffer structure with minimal intervention on our part. | ||
11443 | + * Interrupts should be disabled when calling this. | ||
11444 | + */ | ||
11445 | +void | ||
11446 | +chaincopy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count) | ||
11447 | +{ | ||
11448 | + u32 val; | ||
11449 | + u32 *data = (u32 *) source; | ||
11450 | + static u8 aligned_source[WLAN_A4FR_MAXLEN_WEP_FCS]; | ||
11451 | + | ||
11452 | + /* | ||
11453 | + * Warn if the pointers don't look right. Destination must fit in [23:5] with | ||
11454 | + * zero elsewhere and source should be 32 bit aligned. | ||
11455 | + * This should never happen since we're in control of both, but I want to know about | ||
11456 | + * it if it does. | ||
11457 | + */ | ||
11458 | + if ((destination & 0x00ffffe0) != destination) { | ||
11459 | + printk ("acx chaincopy: destination block 0x%04x not aligned!\n", destination); | ||
11460 | + } | ||
11461 | + if (count > sizeof aligned_source) { | ||
11462 | + printk( KERN_ERR "chaincopy_to_slavemem overflow!\n" ); | ||
11463 | + count = sizeof aligned_source; | ||
11464 | + } | ||
11465 | + if ((u32) source & 3) { | ||
11466 | + memcpy (aligned_source, source, count); | ||
11467 | + data = (u32 *) aligned_source; | ||
11468 | + } | ||
11469 | + | ||
11470 | + /* | ||
11471 | + * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment | ||
11472 | + * SLV_MEM_CTL[5:2] = offset to data portion = 1 word | ||
11473 | + */ | ||
11474 | + val = 2 << 16 | 1 << 2; | ||
11475 | + writel (val, &adev->iobase[ACX_SLV_MEM_CTL]); | ||
11476 | + | ||
11477 | + /* | ||
11478 | + * SLV_MEM_CP[23:5] = start of 1st block | ||
11479 | + * SLV_MEM_CP[3:2] = offset to memblkptr = 0 | ||
11480 | + */ | ||
11481 | + val = destination & 0x00ffffe0; | ||
11482 | + writel (val, &adev->iobase[ACX_SLV_MEM_CP]); | ||
11483 | + | ||
11484 | + /* | ||
11485 | + * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5] | ||
11486 | + */ | ||
11487 | + val = (destination & 0x00ffffe0) + (1<<2); | ||
11488 | + writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]); | ||
11489 | + | ||
11490 | + /* | ||
11491 | + * Write the data to the slave data register, rounding up to the end | ||
11492 | + * of the word containing the last byte (hence the > 0) | ||
11493 | + */ | ||
11494 | + while (count > 0) { | ||
11495 | + writel (*data++, &adev->iobase[ACX_SLV_MEM_DATA]); | ||
11496 | + count -= 4; | ||
11497 | + } | ||
11498 | +} | ||
11499 | + | ||
11500 | + | ||
11501 | +/* | ||
11502 | + * Block copy from slave buffers using memory block chain mode. Copies from the ACX | ||
11503 | + * receive buffer structures with minimal intervention on our part. | ||
11504 | + * Interrupts should be disabled when calling this. | ||
11505 | + */ | ||
11506 | +void | ||
11507 | +chaincopy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count) | ||
11508 | +{ | ||
11509 | + u32 val; | ||
11510 | + u32 *data = (u32 *) destination; | ||
11511 | + static u8 aligned_destination[WLAN_A4FR_MAXLEN_WEP_FCS]; | ||
11512 | + int saved_count = count; | ||
11513 | + | ||
11514 | + /* | ||
11515 | + * Warn if the pointers don't look right. Destination must fit in [23:5] with | ||
11516 | + * zero elsewhere and source should be 32 bit aligned. | ||
11517 | + * Turns out the network stack sends unaligned things, so fix them before | ||
11518 | + * copying to the ACX. | ||
11519 | + */ | ||
11520 | + if ((source & 0x00ffffe0) != source) { | ||
11521 | + printk ("acx chaincopy: source block 0x%04x not aligned!\n", source); | ||
11522 | + dump_acxmem (adev, 0, 0x10000); | ||
11523 | + } | ||
11524 | + if ((u32) destination & 3) { | ||
11525 | + //printk ("acx chaincopy: data destination not word aligned!\n"); | ||
11526 | + data = (u32 *) aligned_destination; | ||
11527 | + if (count > sizeof aligned_destination) { | ||
11528 | + printk( KERN_ERR "chaincopy_from_slavemem overflow!\n" ); | ||
11529 | + count = sizeof aligned_destination; | ||
11530 | + } | ||
11531 | + } | ||
11532 | + | ||
11533 | + /* | ||
11534 | + * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment | ||
11535 | + * SLV_MEM_CTL[5:2] = offset to data portion = 1 word | ||
11536 | + */ | ||
11537 | + val = (2 << 16) | (1 << 2); | ||
11538 | + writel (val, &adev->iobase[ACX_SLV_MEM_CTL]); | ||
11539 | + | ||
11540 | + /* | ||
11541 | + * SLV_MEM_CP[23:5] = start of 1st block | ||
11542 | + * SLV_MEM_CP[3:2] = offset to memblkptr = 0 | ||
11543 | + */ | ||
11544 | + val = source & 0x00ffffe0; | ||
11545 | + writel (val, &adev->iobase[ACX_SLV_MEM_CP]); | ||
11546 | + | ||
11547 | + /* | ||
11548 | + * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5] | ||
11549 | + */ | ||
11550 | + val = (source & 0x00ffffe0) + (1<<2); | ||
11551 | + writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]); | ||
11552 | + | ||
11553 | + /* | ||
11554 | + * Read the data from the slave data register, rounding up to the end | ||
11555 | + * of the word containing the last byte (hence the > 0) | ||
11556 | + */ | ||
11557 | + while (count > 0) { | ||
11558 | + *data++ = readl (&adev->iobase[ACX_SLV_MEM_DATA]); | ||
11559 | + count -= 4; | ||
11560 | + } | ||
11561 | + | ||
11562 | + /* | ||
11563 | + * If the destination wasn't aligned, we would have saved it in | ||
11564 | + * the aligned buffer, so copy it where it should go. | ||
11565 | + */ | ||
11566 | + if ((u32) destination & 3) { | ||
11567 | + memcpy (destination, aligned_destination, saved_count); | ||
11568 | + } | ||
11569 | +} | ||
11570 | + | ||
11571 | +char | ||
11572 | +printable (char c) | ||
11573 | +{ | ||
11574 | + return ((c >= 20) && (c < 127)) ? c : '.'; | ||
11575 | +} | ||
11576 | + | ||
11577 | +#if DUMP_MEM_DEFINED > 0 | ||
11578 | +static void | ||
11579 | +dump_acxmem (acx_device_t *adev, u32 start, int length) | ||
11580 | +{ | ||
11581 | + int i; | ||
11582 | + u8 buf[16]; | ||
11583 | + | ||
11584 | + while (length > 0) { | ||
11585 | + printk ("%04x ", start); | ||
11586 | + copy_from_slavemem (adev, buf, start, 16); | ||
11587 | + for (i = 0; (i < 16) && (i < length); i++) { | ||
11588 | + printk ("%02x ", buf[i]); | ||
11589 | + } | ||
11590 | + for (i = 0; (i < 16) && (i < length); i++) { | ||
11591 | + printk ("%c", printable (buf[i])); | ||
11592 | + } | ||
11593 | + printk ("\n"); | ||
11594 | + start += 16; | ||
11595 | + length -= 16; | ||
11596 | + } | ||
11597 | +} | ||
11598 | +#endif | ||
11599 | + | ||
11600 | +static void | ||
11601 | +enable_acx_irq(acx_device_t *adev); | ||
11602 | +static void | ||
11603 | +disable_acx_irq(acx_device_t *adev); | ||
11604 | + | ||
11605 | +/* | ||
11606 | + * Return an acx pointer to the next transmit data block. | ||
11607 | + */ | ||
11608 | +u32 | ||
11609 | +allocate_acx_txbuf_space (acx_device_t *adev, int count) { | ||
11610 | + u32 block, next, last_block; | ||
11611 | + int blocks_needed; | ||
11612 | + unsigned long flags; | ||
11613 | + | ||
11614 | + spin_lock_irqsave(&adev->txbuf_lock, flags); | ||
11615 | + /* | ||
11616 | + * Take 4 off the memory block size to account for the reserved word at the start of | ||
11617 | + * the block. | ||
11618 | + */ | ||
11619 | + blocks_needed = count / (adev->memblocksize - 4); | ||
11620 | + if (count % (adev->memblocksize - 4)) | ||
11621 | + blocks_needed++; | ||
11622 | + | ||
11623 | + if (blocks_needed <= adev->acx_txbuf_blocks_free) { | ||
11624 | + /* | ||
11625 | + * Take blocks at the head of the free list. | ||
11626 | + */ | ||
11627 | + last_block = block = adev->acx_txbuf_free; | ||
11628 | + | ||
11629 | + /* | ||
11630 | + * Follow block pointers through the requested number of blocks both to | ||
11631 | + * find the new head of the free list and to set the flags for the blocks | ||
11632 | + * appropriately. | ||
11633 | + */ | ||
11634 | + while (blocks_needed--) { | ||
11635 | + /* | ||
11636 | + * Keep track of the last block of the allocation | ||
11637 | + */ | ||
11638 | + last_block = adev->acx_txbuf_free; | ||
11639 | + | ||
11640 | + /* | ||
11641 | + * Make sure the end control flag is not set. | ||
11642 | + */ | ||
11643 | + next = read_slavemem32 (adev, adev->acx_txbuf_free) & 0x7ffff; | ||
11644 | + write_slavemem32 (adev, adev->acx_txbuf_free, next); | ||
11645 | + | ||
11646 | + /* | ||
11647 | + * Update the new head of the free list | ||
11648 | + */ | ||
11649 | + adev->acx_txbuf_free = next << 5; | ||
11650 | + adev->acx_txbuf_blocks_free--; | ||
11651 | + | ||
11652 | + } | ||
11653 | + | ||
11654 | + /* | ||
11655 | + * Flag the last block both by clearing out the next pointer | ||
11656 | + * and marking the control field. | ||
11657 | + */ | ||
11658 | + write_slavemem32 (adev, last_block, 0x02000000); | ||
11659 | + | ||
11660 | + /* | ||
11661 | + * If we're out of buffers make sure the free list pointer is NULL | ||
11662 | + */ | ||
11663 | + if (!adev->acx_txbuf_blocks_free) { | ||
11664 | + adev->acx_txbuf_free = 0; | ||
11665 | + } | ||
11666 | + } | ||
11667 | + else { | ||
11668 | + block = 0; | ||
11669 | + } | ||
11670 | + spin_unlock_irqrestore (&adev->txbuf_lock, flags); | ||
11671 | + return block; | ||
11672 | +} | ||
11673 | + | ||
11674 | +/* | ||
11675 | + * Return buffer space back to the pool by following the next pointers until we find | ||
11676 | + * the block marked as the end. Point the last block to the head of the free list, | ||
11677 | + * then update the head of the free list to point to the newly freed memory. | ||
11678 | + * This routine gets called in interrupt context, so it shouldn't block to protect | ||
11679 | + * the integrity of the linked list. The ISR already holds the lock. | ||
11680 | + */ | ||
11681 | +void | ||
11682 | +reclaim_acx_txbuf_space (acx_device_t *adev, u32 blockptr) { | ||
11683 | + u32 cur, last, next; | ||
11684 | + unsigned long flags; | ||
11685 | + | ||
11686 | + spin_lock_irqsave (&adev->txbuf_lock, flags); | ||
11687 | + if ((blockptr >= adev->acx_txbuf_start) && | ||
11688 | + (blockptr <= adev->acx_txbuf_start + | ||
11689 | + (adev->acx_txbuf_numblocks - 1) * adev->memblocksize)) { | ||
11690 | + cur = blockptr; | ||
11691 | + do { | ||
11692 | + last = cur; | ||
11693 | + next = read_slavemem32 (adev, cur); | ||
11694 | + | ||
11695 | + /* | ||
11696 | + * Advance to the next block in this allocation | ||
11697 | + */ | ||
11698 | + cur = (next & 0x7ffff) << 5; | ||
11699 | + | ||
11700 | + /* | ||
11701 | + * This block now counts as free. | ||
11702 | + */ | ||
11703 | + adev->acx_txbuf_blocks_free++; | ||
11704 | + } while (!(next & 0x02000000)); | ||
11705 | + | ||
11706 | + /* | ||
11707 | + * last now points to the last block of that allocation. Update the pointer | ||
11708 | + * in that block to point to the free list and reset the free list to the | ||
11709 | + * first block of the free call. If there were no free blocks, make sure | ||
11710 | + * the new end of the list marks itself as truly the end. | ||
11711 | + */ | ||
11712 | + if (adev->acx_txbuf_free) { | ||
11713 | + write_slavemem32 (adev, last, adev->acx_txbuf_free >> 5); | ||
11714 | + } | ||
11715 | + else { | ||
11716 | + write_slavemem32 (adev, last, 0x02000000); | ||
11717 | + } | ||
11718 | + adev->acx_txbuf_free = blockptr; | ||
11719 | + } | ||
11720 | + spin_unlock_irqrestore(&adev->txbuf_lock, flags); | ||
11721 | +} | ||
11722 | + | ||
11723 | +/* | ||
11724 | + * Initialize the pieces managing the transmit buffer pool on the ACX. The transmit | ||
11725 | + * buffer is a circular queue with one 32 bit word reserved at the beginning of each | ||
11726 | + * block. The upper 13 bits are a control field, of which only 0x02000000 has any | ||
11727 | + * meaning. The lower 19 bits are the address of the next block divided by 32. | ||
11728 | + */ | ||
11729 | +void | ||
11730 | +init_acx_txbuf (acx_device_t *adev) { | ||
11731 | + | ||
11732 | + /* | ||
11733 | + * acx100_s_init_memory_pools set up txbuf_start and txbuf_numblocks for us. | ||
11734 | + * All we need to do is reset the rest of the bookeeping. | ||
11735 | + */ | ||
11736 | + | ||
11737 | + adev->acx_txbuf_free = adev->acx_txbuf_start; | ||
11738 | + adev->acx_txbuf_blocks_free = adev->acx_txbuf_numblocks; | ||
11739 | + | ||
11740 | + /* | ||
11741 | + * Initialization leaves the last transmit pool block without a pointer back to | ||
11742 | + * the head of the list, but marked as the end of the list. That's how we want | ||
11743 | + * to see it, too, so leave it alone. This is only ever called after a firmware | ||
11744 | + * reset, so the ACX memory is in the state we want. | ||
11745 | + */ | ||
11746 | + | ||
11747 | +} | ||
11748 | + | ||
11749 | +INLINE_IO int | ||
11750 | +adev_present(acx_device_t *adev) | ||
11751 | +{ | ||
11752 | + /* fast version (accesses the first register, IO_ACX_SOFT_RESET, | ||
11753 | + * which should be safe): */ | ||
11754 | + return readl(adev->iobase) != 0xffffffff; | ||
11755 | +} | ||
11756 | + | ||
11757 | +/*********************************************************************** | ||
11758 | +*/ | ||
11759 | +static inline txdesc_t* | ||
11760 | +get_txdesc(acx_device_t *adev, int index) | ||
11761 | +{ | ||
11762 | + return (txdesc_t*) (((u8*)adev->txdesc_start) + index * adev->txdesc_size); | ||
11763 | +} | ||
11764 | + | ||
11765 | +static inline txdesc_t* | ||
11766 | +advance_txdesc(acx_device_t *adev, txdesc_t* txdesc, int inc) | ||
11767 | +{ | ||
11768 | + return (txdesc_t*) (((u8*)txdesc) + inc * adev->txdesc_size); | ||
11769 | +} | ||
11770 | + | ||
11771 | +static txhostdesc_t* | ||
11772 | +get_txhostdesc(acx_device_t *adev, txdesc_t* txdesc) | ||
11773 | +{ | ||
11774 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
11775 | + if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { | ||
11776 | + printk("bad txdesc ptr %p\n", txdesc); | ||
11777 | + return NULL; | ||
11778 | + } | ||
11779 | + index /= adev->txdesc_size; | ||
11780 | + if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { | ||
11781 | + printk("bad txdesc ptr %p\n", txdesc); | ||
11782 | + return NULL; | ||
11783 | + } | ||
11784 | + return &adev->txhostdesc_start[index*2]; | ||
11785 | +} | ||
11786 | + | ||
11787 | +static inline client_t* | ||
11788 | +get_txc(acx_device_t *adev, txdesc_t* txdesc) | ||
11789 | +{ | ||
11790 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
11791 | + if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { | ||
11792 | + printk("bad txdesc ptr %p\n", txdesc); | ||
11793 | + return NULL; | ||
11794 | + } | ||
11795 | + index /= adev->txdesc_size; | ||
11796 | + if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { | ||
11797 | + printk("bad txdesc ptr %p\n", txdesc); | ||
11798 | + return NULL; | ||
11799 | + } | ||
11800 | + return adev->txc[index]; | ||
11801 | +} | ||
11802 | + | ||
11803 | +static inline u16 | ||
11804 | +get_txr(acx_device_t *adev, txdesc_t* txdesc) | ||
11805 | +{ | ||
11806 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
11807 | + index /= adev->txdesc_size; | ||
11808 | + return adev->txr[index]; | ||
11809 | +} | ||
11810 | + | ||
11811 | +static inline void | ||
11812 | +put_txcr(acx_device_t *adev, txdesc_t* txdesc, client_t* c, u16 r111) | ||
11813 | +{ | ||
11814 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
11815 | + if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { | ||
11816 | + printk("bad txdesc ptr %p\n", txdesc); | ||
11817 | + return; | ||
11818 | + } | ||
11819 | + index /= adev->txdesc_size; | ||
11820 | + if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { | ||
11821 | + printk("bad txdesc ptr %p\n", txdesc); | ||
11822 | + return; | ||
11823 | + } | ||
11824 | + adev->txc[index] = c; | ||
11825 | + adev->txr[index] = r111; | ||
11826 | +} | ||
11827 | + | ||
11828 | + | ||
11829 | +/*********************************************************************** | ||
11830 | +** EEPROM and PHY read/write helpers | ||
11831 | +*/ | ||
11832 | +/*********************************************************************** | ||
11833 | +** acxmem_read_eeprom_byte | ||
11834 | +** | ||
11835 | +** Function called to read an octet in the EEPROM. | ||
11836 | +** | ||
11837 | +** This function is used by acxmem_e_probe to check if the | ||
11838 | +** connected card is a legal one or not. | ||
11839 | +** | ||
11840 | +** Arguments: | ||
11841 | +** adev ptr to acx_device structure | ||
11842 | +** addr address to read in the EEPROM | ||
11843 | +** charbuf ptr to a char. This is where the read octet | ||
11844 | +** will be stored | ||
11845 | +*/ | ||
11846 | +int | ||
11847 | +acxmem_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf) | ||
11848 | +{ | ||
11849 | + int result; | ||
11850 | + int count; | ||
11851 | + | ||
11852 | + write_reg32(adev, IO_ACX_EEPROM_CFG, 0); | ||
11853 | + write_reg32(adev, IO_ACX_EEPROM_ADDR, addr); | ||
11854 | + write_flush(adev); | ||
11855 | + write_reg32(adev, IO_ACX_EEPROM_CTL, 2); | ||
11856 | + | ||
11857 | + count = 0xffff; | ||
11858 | + while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { | ||
11859 | + /* scheduling away instead of CPU burning loop | ||
11860 | + * doesn't seem to work here at all: | ||
11861 | + * awful delay, sometimes also failure. | ||
11862 | + * Doesn't matter anyway (only small delay). */ | ||
11863 | + if (unlikely(!--count)) { | ||
11864 | + printk("%s: timeout waiting for EEPROM read\n", | ||
11865 | + adev->ndev->name); | ||
11866 | + result = NOT_OK; | ||
11867 | + goto fail; | ||
11868 | + } | ||
11869 | + cpu_relax(); | ||
11870 | + } | ||
11871 | + | ||
11872 | + *charbuf = read_reg8(adev, IO_ACX_EEPROM_DATA); | ||
11873 | + log(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf); | ||
11874 | + result = OK; | ||
11875 | + | ||
11876 | +fail: | ||
11877 | + return result; | ||
11878 | +} | ||
11879 | + | ||
11880 | + | ||
11881 | +/*********************************************************************** | ||
11882 | +** We don't lock hw accesses here since we never r/w eeprom in IRQ | ||
11883 | +** Note: this function sleeps only because of GFP_KERNEL alloc | ||
11884 | +*/ | ||
11885 | +#ifdef UNUSED | ||
11886 | +int | ||
11887 | +acxmem_s_write_eeprom(acx_device_t *adev, u32 addr, u32 len, const u8 *charbuf) | ||
11888 | +{ | ||
11889 | + u8 *data_verify = NULL; | ||
11890 | + unsigned long flags; | ||
11891 | + int count, i; | ||
11892 | + int result = NOT_OK; | ||
11893 | + u16 gpio_orig; | ||
11894 | + | ||
11895 | + printk("acx: WARNING! I would write to EEPROM now. " | ||
11896 | + "Since I really DON'T want to unless you know " | ||
11897 | + "what you're doing (THIS CODE WILL PROBABLY " | ||
11898 | + "NOT WORK YET!), I will abort that now. And " | ||
11899 | + "definitely make sure to make a " | ||
11900 | + "/proc/driver/acx_wlan0_eeprom backup copy first!!! " | ||
11901 | + "(the EEPROM content includes the PCI config header!! " | ||
11902 | + "If you kill important stuff, then you WILL " | ||
11903 | + "get in trouble and people DID get in trouble already)\n"); | ||
11904 | + return OK; | ||
11905 | + | ||
11906 | + FN_ENTER; | ||
11907 | + | ||
11908 | + data_verify = kmalloc(len, GFP_KERNEL); | ||
11909 | + if (!data_verify) { | ||
11910 | + goto end; | ||
11911 | + } | ||
11912 | + | ||
11913 | + /* first we need to enable the OE (EEPROM Output Enable) GPIO line | ||
11914 | + * to be able to write to the EEPROM. | ||
11915 | + * NOTE: an EEPROM writing success has been reported, | ||
11916 | + * but you probably have to modify GPIO_OUT, too, | ||
11917 | + * and you probably need to activate a different GPIO | ||
11918 | + * line instead! */ | ||
11919 | + gpio_orig = read_reg16(adev, IO_ACX_GPIO_OE); | ||
11920 | + write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig & ~1); | ||
11921 | + write_flush(adev); | ||
11922 | + | ||
11923 | + /* ok, now start writing the data out */ | ||
11924 | + for (i = 0; i < len; i++) { | ||
11925 | + write_reg32(adev, IO_ACX_EEPROM_CFG, 0); | ||
11926 | + write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); | ||
11927 | + write_reg32(adev, IO_ACX_EEPROM_DATA, *(charbuf + i)); | ||
11928 | + write_flush(adev); | ||
11929 | + write_reg32(adev, IO_ACX_EEPROM_CTL, 1); | ||
11930 | + | ||
11931 | + count = 0xffff; | ||
11932 | + while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { | ||
11933 | + if (unlikely(!--count)) { | ||
11934 | + printk("WARNING, DANGER!!! " | ||
11935 | + "Timeout waiting for EEPROM write\n"); | ||
11936 | + goto end; | ||
11937 | + } | ||
11938 | + cpu_relax(); | ||
11939 | + } | ||
11940 | + } | ||
11941 | + | ||
11942 | + /* disable EEPROM writing */ | ||
11943 | + write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig); | ||
11944 | + write_flush(adev); | ||
11945 | + | ||
11946 | + /* now start a verification run */ | ||
11947 | + for (i = 0; i < len; i++) { | ||
11948 | + write_reg32(adev, IO_ACX_EEPROM_CFG, 0); | ||
11949 | + write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); | ||
11950 | + write_flush(adev); | ||
11951 | + write_reg32(adev, IO_ACX_EEPROM_CTL, 2); | ||
11952 | + | ||
11953 | + count = 0xffff; | ||
11954 | + while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { | ||
11955 | + if (unlikely(!--count)) { | ||
11956 | + printk("timeout waiting for EEPROM read\n"); | ||
11957 | + goto end; | ||
11958 | + } | ||
11959 | + cpu_relax(); | ||
11960 | + } | ||
11961 | + | ||
11962 | + data_verify[i] = read_reg16(adev, IO_ACX_EEPROM_DATA); | ||
11963 | + } | ||
11964 | + | ||
11965 | + if (0 == memcmp(charbuf, data_verify, len)) | ||
11966 | + result = OK; /* read data matches, success */ | ||
11967 | + | ||
11968 | +end: | ||
11969 | + kfree(data_verify); | ||
11970 | + FN_EXIT1(result); | ||
11971 | + return result; | ||
11972 | +} | ||
11973 | +#endif /* UNUSED */ | ||
11974 | + | ||
11975 | + | ||
11976 | +/*********************************************************************** | ||
11977 | +** acxmem_s_read_phy_reg | ||
11978 | +** | ||
11979 | +** Messing with rx/tx disabling and enabling here | ||
11980 | +** (write_reg32(adev, IO_ACX_ENABLE, 0b000000xx)) kills traffic | ||
11981 | +*/ | ||
11982 | +int | ||
11983 | +acxmem_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf) | ||
11984 | +{ | ||
11985 | + int result = NOT_OK; | ||
11986 | + int count; | ||
11987 | + | ||
11988 | + FN_ENTER; | ||
11989 | + | ||
11990 | + write_reg32(adev, IO_ACX_PHY_ADDR, reg); | ||
11991 | + write_flush(adev); | ||
11992 | + write_reg32(adev, IO_ACX_PHY_CTL, 2); | ||
11993 | + | ||
11994 | + count = 0xffff; | ||
11995 | + while (read_reg32(adev, IO_ACX_PHY_CTL)) { | ||
11996 | + /* scheduling away instead of CPU burning loop | ||
11997 | + * doesn't seem to work here at all: | ||
11998 | + * awful delay, sometimes also failure. | ||
11999 | + * Doesn't matter anyway (only small delay). */ | ||
12000 | + if (unlikely(!--count)) { | ||
12001 | + printk("%s: timeout waiting for phy read\n", | ||
12002 | + adev->ndev->name); | ||
12003 | + *charbuf = 0; | ||
12004 | + goto fail; | ||
12005 | + } | ||
12006 | + cpu_relax(); | ||
12007 | + } | ||
12008 | + | ||
12009 | + log(L_DEBUG, "count was %u\n", count); | ||
12010 | + *charbuf = read_reg8(adev, IO_ACX_PHY_DATA); | ||
12011 | + | ||
12012 | + log(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg); | ||
12013 | + result = OK; | ||
12014 | + goto fail; /* silence compiler warning */ | ||
12015 | +fail: | ||
12016 | + FN_EXIT1(result); | ||
12017 | + return result; | ||
12018 | +} | ||
12019 | + | ||
12020 | + | ||
12021 | +/*********************************************************************** | ||
12022 | +*/ | ||
12023 | +int | ||
12024 | +acxmem_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value) | ||
12025 | +{ | ||
12026 | + int count; | ||
12027 | + FN_ENTER; | ||
12028 | + | ||
12029 | + /* mprusko said that 32bit accesses result in distorted sensitivity | ||
12030 | + * on his card. Unconfirmed, looks like it's not true (most likely since we | ||
12031 | + * now properly flush writes). */ | ||
12032 | + write_reg32(adev, IO_ACX_PHY_DATA, value); | ||
12033 | + write_reg32(adev, IO_ACX_PHY_ADDR, reg); | ||
12034 | + write_flush(adev); | ||
12035 | + write_reg32(adev, IO_ACX_PHY_CTL, 1); | ||
12036 | + write_flush(adev); | ||
12037 | + | ||
12038 | + count = 0xffff; | ||
12039 | + while (read_reg32(adev, IO_ACX_PHY_CTL)) { | ||
12040 | + /* scheduling away instead of CPU burning loop | ||
12041 | + * doesn't seem to work here at all: | ||
12042 | + * awful delay, sometimes also failure. | ||
12043 | + * Doesn't matter anyway (only small delay). */ | ||
12044 | + if (unlikely(!--count)) { | ||
12045 | + printk("%s: timeout waiting for phy read\n", | ||
12046 | + adev->ndev->name); | ||
12047 | + goto fail; | ||
12048 | + } | ||
12049 | + cpu_relax(); | ||
12050 | + } | ||
12051 | + | ||
12052 | + log(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg); | ||
12053 | + fail: | ||
12054 | + FN_EXIT1(OK); | ||
12055 | + return OK; | ||
12056 | +} | ||
12057 | + | ||
12058 | + | ||
12059 | +#define NO_AUTO_INCREMENT 1 | ||
12060 | + | ||
12061 | +/*********************************************************************** | ||
12062 | +** acxmem_s_write_fw | ||
12063 | +** | ||
12064 | +** Write the firmware image into the card. | ||
12065 | +** | ||
12066 | +** Arguments: | ||
12067 | +** adev wlan device structure | ||
12068 | +** fw_image firmware image. | ||
12069 | +** | ||
12070 | +** Returns: | ||
12071 | +** 1 firmware image corrupted | ||
12072 | +** 0 success | ||
12073 | +*/ | ||
12074 | +static int | ||
12075 | +acxmem_s_write_fw(acx_device_t *adev, const firmware_image_t *fw_image, u32 offset) | ||
12076 | +{ | ||
12077 | + int len, size, checkMismatch = -1; | ||
12078 | + u32 sum, v32, tmp, id; | ||
12079 | + /* we skip the first four bytes which contain the control sum */ | ||
12080 | + const u8 *p = (u8*)fw_image + 4; | ||
12081 | + | ||
12082 | + /* start the image checksum by adding the image size value */ | ||
12083 | + sum = p[0]+p[1]+p[2]+p[3]; | ||
12084 | + p += 4; | ||
12085 | + | ||
12086 | +#ifdef NOPE | ||
12087 | +#if NO_AUTO_INCREMENT | ||
12088 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ | ||
12089 | +#else | ||
12090 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ | ||
12091 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ | ||
12092 | + write_flush(adev); | ||
12093 | +#endif | ||
12094 | +#endif | ||
12095 | + len = 0; | ||
12096 | + size = le32_to_cpu(fw_image->size) & (~3); | ||
12097 | + | ||
12098 | + while (likely(len < size)) { | ||
12099 | + v32 = be32_to_cpu(*(u32*)p); | ||
12100 | + sum += p[0]+p[1]+p[2]+p[3]; | ||
12101 | + p += 4; | ||
12102 | + len += 4; | ||
12103 | + | ||
12104 | +#ifdef NOPE | ||
12105 | +#if NO_AUTO_INCREMENT | ||
12106 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); | ||
12107 | + write_flush(adev); | ||
12108 | +#endif | ||
12109 | + write_reg32(adev, IO_ACX_SLV_MEM_DATA, v32); | ||
12110 | + write_flush(adev); | ||
12111 | +#endif | ||
12112 | + write_slavemem32 (adev, offset + len - 4, v32); | ||
12113 | + | ||
12114 | + id = read_id_register (adev); | ||
12115 | + | ||
12116 | + /* | ||
12117 | + * check the data written | ||
12118 | + */ | ||
12119 | + tmp = read_slavemem32 (adev, offset + len - 4); | ||
12120 | + if (checkMismatch && (tmp != v32)) { | ||
12121 | + printk ("first data mismatch at 0x%08x good 0x%08x bad 0x%08x id 0x%08x\n", | ||
12122 | + offset + len - 4, v32, tmp, id); | ||
12123 | + checkMismatch = 0; | ||
12124 | + } | ||
12125 | + } | ||
12126 | + log(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n", | ||
12127 | + size, sum, le32_to_cpu(fw_image->chksum)); | ||
12128 | + | ||
12129 | + /* compare our checksum with the stored image checksum */ | ||
12130 | + return (sum != le32_to_cpu(fw_image->chksum)); | ||
12131 | +} | ||
12132 | + | ||
12133 | + | ||
12134 | +/*********************************************************************** | ||
12135 | +** acxmem_s_validate_fw | ||
12136 | +** | ||
12137 | +** Compare the firmware image given with | ||
12138 | +** the firmware image written into the card. | ||
12139 | +** | ||
12140 | +** Arguments: | ||
12141 | +** adev wlan device structure | ||
12142 | +** fw_image firmware image. | ||
12143 | +** | ||
12144 | +** Returns: | ||
12145 | +** NOT_OK firmware image corrupted or not correctly written | ||
12146 | +** OK success | ||
12147 | +*/ | ||
12148 | +static int | ||
12149 | +acxmem_s_validate_fw(acx_device_t *adev, const firmware_image_t *fw_image, | ||
12150 | + u32 offset) | ||
12151 | +{ | ||
12152 | + u32 sum, v32, w32; | ||
12153 | + int len, size; | ||
12154 | + int result = OK; | ||
12155 | + /* we skip the first four bytes which contain the control sum */ | ||
12156 | + const u8 *p = (u8*)fw_image + 4; | ||
12157 | + | ||
12158 | + /* start the image checksum by adding the image size value */ | ||
12159 | + sum = p[0]+p[1]+p[2]+p[3]; | ||
12160 | + p += 4; | ||
12161 | + | ||
12162 | + write_reg32(adev, IO_ACX_SLV_END_CTL, 0); | ||
12163 | + | ||
12164 | +#if NO_AUTO_INCREMENT | ||
12165 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ | ||
12166 | +#else | ||
12167 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ | ||
12168 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ | ||
12169 | +#endif | ||
12170 | + | ||
12171 | + len = 0; | ||
12172 | + size = le32_to_cpu(fw_image->size) & (~3); | ||
12173 | + | ||
12174 | + while (likely(len < size)) { | ||
12175 | + v32 = be32_to_cpu(*(u32*)p); | ||
12176 | + p += 4; | ||
12177 | + len += 4; | ||
12178 | + | ||
12179 | +#ifdef NOPE | ||
12180 | +#if NO_AUTO_INCREMENT | ||
12181 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); | ||
12182 | +#endif | ||
12183 | + udelay(10); | ||
12184 | + w32 = read_reg32(adev, IO_ACX_SLV_MEM_DATA); | ||
12185 | +#endif | ||
12186 | + w32 = read_slavemem32 (adev, offset + len - 4); | ||
12187 | + | ||
12188 | + if (unlikely(w32 != v32)) { | ||
12189 | + printk("acx: FATAL: firmware upload: " | ||
12190 | + "data parts at offset %d don't match\n(0x%08X vs. 0x%08X)!\n" | ||
12191 | + "I/O timing issues or defective memory, with DWL-xx0+? " | ||
12192 | + "ACX_IO_WIDTH=16 may help. Please report\n", | ||
12193 | + len, v32, w32); | ||
12194 | + result = NOT_OK; | ||
12195 | + break; | ||
12196 | + } | ||
12197 | + | ||
12198 | + sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24); | ||
12199 | + } | ||
12200 | + | ||
12201 | + /* sum control verification */ | ||
12202 | + if (result != NOT_OK) { | ||
12203 | + if (sum != le32_to_cpu(fw_image->chksum)) { | ||
12204 | + printk("acx: FATAL: firmware upload: " | ||
12205 | + "checksums don't match!\n"); | ||
12206 | + result = NOT_OK; | ||
12207 | + } | ||
12208 | + } | ||
12209 | + | ||
12210 | + return result; | ||
12211 | +} | ||
12212 | + | ||
12213 | + | ||
12214 | +/*********************************************************************** | ||
12215 | +** acxmem_s_upload_fw | ||
12216 | +** | ||
12217 | +** Called from acx_reset_dev | ||
12218 | +*/ | ||
12219 | +static int | ||
12220 | +acxmem_s_upload_fw(acx_device_t *adev) | ||
12221 | +{ | ||
12222 | + firmware_image_t *fw_image = NULL; | ||
12223 | + int res = NOT_OK; | ||
12224 | + int try; | ||
12225 | + u32 file_size; | ||
12226 | + char *filename = "WLANGEN.BIN"; | ||
12227 | +#ifdef PATCH_AROUND_BAD_SPOTS | ||
12228 | + u32 offset; | ||
12229 | + int i; | ||
12230 | + /* | ||
12231 | + * arm-linux-objdump -d patch.bin, or | ||
12232 | + * od -Ax -t x4 patch.bin after finding the bounds | ||
12233 | + * of the .text section with arm-linux-objdump -s patch.bin | ||
12234 | + */ | ||
12235 | + u32 patch[] = { | ||
12236 | + 0xe584c030, 0xe59fc008, | ||
12237 | + 0xe92d1000, 0xe59fc004, 0xe8bd8000, 0x0000080c, | ||
12238 | + 0x0000aa68, 0x605a2200, 0x2c0a689c, 0x2414d80a, | ||
12239 | + 0x2f00689f, 0x1c27d007, 0x06241e7c, 0x2f000e24, | ||
12240 | + 0xe000d1f6, 0x602e6018, 0x23036468, 0x480203db, | ||
12241 | + 0x60ca6003, 0xbdf0750a, 0xffff0808 | ||
12242 | + }; | ||
12243 | +#endif | ||
12244 | + | ||
12245 | + FN_ENTER; | ||
12246 | + /* No combined image; tell common we need the radio firmware, too */ | ||
12247 | + adev->need_radio_fw = 1; | ||
12248 | + | ||
12249 | + fw_image = acx_s_read_fw(adev->dev, filename, &file_size); | ||
12250 | + if (!fw_image) { | ||
12251 | + FN_EXIT1(NOT_OK); | ||
12252 | + return NOT_OK; | ||
12253 | + } | ||
12254 | + | ||
12255 | + for (try = 1; try <= 5; try++) { | ||
12256 | + res = acxmem_s_write_fw(adev, fw_image, 0); | ||
12257 | + log(L_DEBUG|L_INIT, "acx_write_fw (main): %d\n", res); | ||
12258 | + if (OK == res) { | ||
12259 | + res = acxmem_s_validate_fw(adev, fw_image, 0); | ||
12260 | + log(L_DEBUG|L_INIT, "acx_validate_fw " | ||
12261 | + "(main): %d\n", res); | ||
12262 | + } | ||
12263 | + | ||
12264 | + if (OK == res) { | ||
12265 | + SET_BIT(adev->dev_state_mask, ACX_STATE_FW_LOADED); | ||
12266 | + break; | ||
12267 | + } | ||
12268 | + printk("acx: firmware upload attempt #%d FAILED, " | ||
12269 | + "retrying...\n", try); | ||
12270 | + acx_s_msleep(1000); /* better wait for a while... */ | ||
12271 | + } | ||
12272 | + | ||
12273 | +#ifdef PATCH_AROUND_BAD_SPOTS | ||
12274 | + /* | ||
12275 | + * Only want to do this if the firmware is exactly what we expect for an | ||
12276 | + * iPaq 4700; otherwise, bad things would ensue. | ||
12277 | + */ | ||
12278 | + if ((HX4700_FIRMWARE_CHECKSUM == fw_image->chksum) || | ||
12279 | + (HX4700_ALTERNATE_FIRMWARE_CHECKSUM == fw_image->chksum)) { | ||
12280 | + /* | ||
12281 | + * Put the patch after the main firmware image. 0x950c contains | ||
12282 | + * the ACX's idea of the end of the firmware. Use that location to | ||
12283 | + * load ours (which depends on that location being 0xab58) then | ||
12284 | + * update that location to point to after ours. | ||
12285 | + */ | ||
12286 | + | ||
12287 | + offset = read_slavemem32 (adev, 0x950c); | ||
12288 | + | ||
12289 | + log (L_DEBUG, "acx: patching in at 0x%04x\n", offset); | ||
12290 | + | ||
12291 | + for (i = 0; i < sizeof(patch) / sizeof(patch[0]); i++) { | ||
12292 | + write_slavemem32 (adev, offset, patch[i]); | ||
12293 | + offset += sizeof(u32); | ||
12294 | + } | ||
12295 | + | ||
12296 | + /* | ||
12297 | + * Patch the instruction at 0x0804 to branch to our ARM patch at 0xab58 | ||
12298 | + */ | ||
12299 | + write_slavemem32 (adev, 0x0804, 0xea000000 + (0xab58-0x0804-8)/4); | ||
12300 | + | ||
12301 | + /* | ||
12302 | + * Patch the instructions at 0x1f40 to branch to our Thumb patch at 0xab74 | ||
12303 | + * | ||
12304 | + * 4a00 ldr r2, [pc, #0] | ||
12305 | + * 4710 bx r2 | ||
12306 | + * .data 0xab74+1 | ||
12307 | + */ | ||
12308 | + write_slavemem32 (adev, 0x1f40, 0x47104a00); | ||
12309 | + write_slavemem32 (adev, 0x1f44, 0x0000ab74+1); | ||
12310 | + | ||
12311 | + /* | ||
12312 | + * Bump the end of the firmware up to beyond our patch. | ||
12313 | + */ | ||
12314 | + write_slavemem32 (adev, 0x950c, offset); | ||
12315 | + | ||
12316 | + } | ||
12317 | +#endif | ||
12318 | + | ||
12319 | + vfree(fw_image); | ||
12320 | + | ||
12321 | + FN_EXIT1(res); | ||
12322 | + return res; | ||
12323 | +} | ||
12324 | + | ||
12325 | + | ||
12326 | +/*********************************************************************** | ||
12327 | +** acxmem_s_upload_radio | ||
12328 | +** | ||
12329 | +** Uploads the appropriate radio module firmware into the card. | ||
12330 | +*/ | ||
12331 | +int | ||
12332 | +acxmem_s_upload_radio(acx_device_t *adev) | ||
12333 | +{ | ||
12334 | + acx_ie_memmap_t mm; | ||
12335 | + firmware_image_t *radio_image; | ||
12336 | + acx_cmd_radioinit_t radioinit; | ||
12337 | + int res = NOT_OK; | ||
12338 | + int try; | ||
12339 | + u32 offset; | ||
12340 | + u32 size; | ||
12341 | + char filename[sizeof("RADIONN.BIN")]; | ||
12342 | + | ||
12343 | + if (!adev->need_radio_fw) return OK; | ||
12344 | + | ||
12345 | + FN_ENTER; | ||
12346 | + | ||
12347 | + acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); | ||
12348 | + offset = le32_to_cpu(mm.CodeEnd); | ||
12349 | + | ||
12350 | + snprintf(filename, sizeof(filename), "RADIO%02x.BIN", | ||
12351 | + adev->radio_type); | ||
12352 | + radio_image = acx_s_read_fw(adev->dev, filename, &size); | ||
12353 | + if (!radio_image) { | ||
12354 | + printk("acx: can't load radio module '%s'\n", filename); | ||
12355 | + goto fail; | ||
12356 | + } | ||
12357 | + | ||
12358 | + acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0); | ||
12359 | + | ||
12360 | + for (try = 1; try <= 5; try++) { | ||
12361 | + res = acxmem_s_write_fw(adev, radio_image, offset); | ||
12362 | + log(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res); | ||
12363 | + if (OK == res) { | ||
12364 | + res = acxmem_s_validate_fw(adev, radio_image, offset); | ||
12365 | + log(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res); | ||
12366 | + } | ||
12367 | + | ||
12368 | + if (OK == res) | ||
12369 | + break; | ||
12370 | + printk("acx: radio firmware upload attempt #%d FAILED, " | ||
12371 | + "retrying...\n", try); | ||
12372 | + acx_s_msleep(1000); /* better wait for a while... */ | ||
12373 | + } | ||
12374 | + | ||
12375 | + acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0); | ||
12376 | + radioinit.offset = cpu_to_le32(offset); | ||
12377 | + | ||
12378 | + /* no endian conversion needed, remains in card CPU area: */ | ||
12379 | + radioinit.len = radio_image->size; | ||
12380 | + | ||
12381 | + vfree(radio_image); | ||
12382 | + | ||
12383 | + if (OK != res) | ||
12384 | + goto fail; | ||
12385 | + | ||
12386 | + /* will take a moment so let's have a big timeout */ | ||
12387 | + acx_s_issue_cmd_timeo(adev, ACX1xx_CMD_RADIOINIT, | ||
12388 | + &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000)); | ||
12389 | + | ||
12390 | + res = acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); | ||
12391 | + | ||
12392 | +fail: | ||
12393 | + FN_EXIT1(res); | ||
12394 | + return res; | ||
12395 | +} | ||
12396 | + | ||
12397 | +/*********************************************************************** | ||
12398 | +** acxmem_l_reset_mac | ||
12399 | +** | ||
12400 | +** MAC will be reset | ||
12401 | +** Call context: reset_dev | ||
12402 | +*/ | ||
12403 | +static void | ||
12404 | +acxmem_l_reset_mac(acx_device_t *adev) | ||
12405 | +{ | ||
12406 | + int count; | ||
12407 | + FN_ENTER; | ||
12408 | + | ||
12409 | + /* halt eCPU */ | ||
12410 | + set_regbits (adev, IO_ACX_ECPU_CTRL, 0x1); | ||
12411 | + | ||
12412 | + /* now do soft reset of eCPU, set bit */ | ||
12413 | + set_regbits (adev, IO_ACX_SOFT_RESET, 0x1); | ||
12414 | + log(L_DEBUG, "%s: enable soft reset...\n", __func__); | ||
12415 | + | ||
12416 | + /* Windows driver sleeps here for a while with this sequence */ | ||
12417 | + for (count = 0; count < 200; count++) { | ||
12418 | + udelay (50); | ||
12419 | + } | ||
12420 | + | ||
12421 | + /* now clear bit again: deassert eCPU reset */ | ||
12422 | + log(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__); | ||
12423 | + clear_regbits (adev, IO_ACX_SOFT_RESET, 0x1); | ||
12424 | + | ||
12425 | + /* now start a burst read from initial EEPROM */ | ||
12426 | + set_regbits (adev, IO_ACX_EE_START, 0x1); | ||
12427 | + | ||
12428 | + /* | ||
12429 | + * Windows driver sleeps here for a while with this sequence | ||
12430 | + */ | ||
12431 | + for (count = 0; count < 200; count++) { | ||
12432 | + udelay (50); | ||
12433 | + } | ||
12434 | + | ||
12435 | + /* Windows driver writes 0x10000 to register 0x808 here */ | ||
12436 | + | ||
12437 | + write_reg32 (adev, 0x808, 0x10000); | ||
12438 | + | ||
12439 | + FN_EXIT0; | ||
12440 | +} | ||
12441 | + | ||
12442 | + | ||
12443 | +/*********************************************************************** | ||
12444 | +** acxmem_s_verify_init | ||
12445 | +*/ | ||
12446 | +static int | ||
12447 | +acxmem_s_verify_init(acx_device_t *adev) | ||
12448 | +{ | ||
12449 | + int result = NOT_OK; | ||
12450 | + unsigned long timeout; | ||
12451 | + | ||
12452 | + FN_ENTER; | ||
12453 | + | ||
12454 | + timeout = jiffies + 2*HZ; | ||
12455 | + for (;;) { | ||
12456 | + u32 irqstat = read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES); | ||
12457 | + if ((irqstat != 0xFFFFFFFF) && (irqstat & HOST_INT_FCS_THRESHOLD)) { | ||
12458 | + result = OK; | ||
12459 | + write_reg32(adev, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD); | ||
12460 | + break; | ||
12461 | + } | ||
12462 | + if (time_after(jiffies, timeout)) | ||
12463 | + break; | ||
12464 | + /* Init may take up to ~0.5 sec total */ | ||
12465 | + acx_s_msleep(50); | ||
12466 | + } | ||
12467 | + | ||
12468 | + FN_EXIT1(result); | ||
12469 | + return result; | ||
12470 | +} | ||
12471 | + | ||
12472 | + | ||
12473 | +/*********************************************************************** | ||
12474 | +** A few low-level helpers | ||
12475 | +** | ||
12476 | +** Note: these functions are not protected by lock | ||
12477 | +** and thus are never allowed to be called from IRQ. | ||
12478 | +** Also they must not race with fw upload which uses same hw regs | ||
12479 | +*/ | ||
12480 | + | ||
12481 | +/*********************************************************************** | ||
12482 | +** acxmem_write_cmd_type_status | ||
12483 | +*/ | ||
12484 | + | ||
12485 | +static inline void | ||
12486 | +acxmem_write_cmd_type_status(acx_device_t *adev, u16 type, u16 status) | ||
12487 | +{ | ||
12488 | + write_slavemem32 (adev, (u32) adev->cmd_area, type | (status << 16)); | ||
12489 | + write_flush(adev); | ||
12490 | +} | ||
12491 | + | ||
12492 | + | ||
12493 | +/*********************************************************************** | ||
12494 | +** acxmem_read_cmd_type_status | ||
12495 | +*/ | ||
12496 | +static u32 | ||
12497 | +acxmem_read_cmd_type_status(acx_device_t *adev) | ||
12498 | +{ | ||
12499 | + u32 cmd_type, cmd_status; | ||
12500 | + | ||
12501 | + cmd_type = read_slavemem32 (adev, (u32) adev->cmd_area); | ||
12502 | + | ||
12503 | + cmd_status = (cmd_type >> 16); | ||
12504 | + cmd_type = (u16)cmd_type; | ||
12505 | + | ||
12506 | + log(L_CTL, "cmd_type:%04X cmd_status:%04X [%s]\n", | ||
12507 | + cmd_type, cmd_status, | ||
12508 | + acx_cmd_status_str(cmd_status)); | ||
12509 | + | ||
12510 | + return cmd_status; | ||
12511 | +} | ||
12512 | + | ||
12513 | + | ||
12514 | +/*********************************************************************** | ||
12515 | +** acxmem_s_reset_dev | ||
12516 | +** | ||
12517 | +** Arguments: | ||
12518 | +** netdevice that contains the adev variable | ||
12519 | +** Returns: | ||
12520 | +** NOT_OK on fail | ||
12521 | +** OK on success | ||
12522 | +** Side effects: | ||
12523 | +** device is hard reset | ||
12524 | +** Call context: | ||
12525 | +** acxmem_e_probe | ||
12526 | +** Comment: | ||
12527 | +** This resets the device using low level hardware calls | ||
12528 | +** as well as uploads and verifies the firmware to the card | ||
12529 | +*/ | ||
12530 | + | ||
12531 | +static inline void | ||
12532 | +init_mboxes(acx_device_t *adev) | ||
12533 | +{ | ||
12534 | + u32 cmd_offs, info_offs; | ||
12535 | + | ||
12536 | + cmd_offs = read_reg32(adev, IO_ACX_CMD_MAILBOX_OFFS); | ||
12537 | + info_offs = read_reg32(adev, IO_ACX_INFO_MAILBOX_OFFS); | ||
12538 | + adev->cmd_area = (u8*) cmd_offs; | ||
12539 | + adev->info_area = (u8*) info_offs; | ||
12540 | + /* | ||
12541 | + log(L_DEBUG, "iobase2=%p\n" | ||
12542 | + */ | ||
12543 | + log( L_DEBUG, "cmd_mbox_offset=%X cmd_area=%p\n" | ||
12544 | + "info_mbox_offset=%X info_area=%p\n", | ||
12545 | + cmd_offs, adev->cmd_area, | ||
12546 | + info_offs, adev->info_area); | ||
12547 | +} | ||
12548 | + | ||
12549 | + | ||
12550 | +static inline void | ||
12551 | +read_eeprom_area(acx_device_t *adev) | ||
12552 | +{ | ||
12553 | +#if ACX_DEBUG > 1 | ||
12554 | + int offs; | ||
12555 | + u8 tmp; | ||
12556 | + | ||
12557 | + for (offs = 0x8c; offs < 0xb9; offs++) | ||
12558 | + acxmem_read_eeprom_byte(adev, offs, &tmp); | ||
12559 | +#endif | ||
12560 | +} | ||
12561 | + | ||
12562 | +static int | ||
12563 | +acxmem_s_reset_dev(acx_device_t *adev) | ||
12564 | +{ | ||
12565 | + const char* msg = ""; | ||
12566 | + unsigned long flags; | ||
12567 | + int result = NOT_OK; | ||
12568 | + u16 hardware_info; | ||
12569 | + u16 ecpu_ctrl; | ||
12570 | + int count; | ||
12571 | + u32 tmp; | ||
12572 | + | ||
12573 | + FN_ENTER; | ||
12574 | + /* | ||
12575 | + write_reg32 (adev, IO_ACX_SLV_MEM_CP, 0); | ||
12576 | + */ | ||
12577 | + /* reset the device to make sure the eCPU is stopped | ||
12578 | + * to upload the firmware correctly */ | ||
12579 | + | ||
12580 | + acx_lock(adev, flags); | ||
12581 | + | ||
12582 | + /* Windows driver does some funny things here */ | ||
12583 | + /* | ||
12584 | + * clear bit 0x200 in register 0x2A0 | ||
12585 | + */ | ||
12586 | + clear_regbits (adev, 0x2A0, 0x200); | ||
12587 | + | ||
12588 | + /* | ||
12589 | + * Set bit 0x200 in ACX_GPIO_OUT | ||
12590 | + */ | ||
12591 | + set_regbits (adev, IO_ACX_GPIO_OUT, 0x200); | ||
12592 | + | ||
12593 | + /* | ||
12594 | + * read register 0x900 until its value is 0x8400104C, sleeping | ||
12595 | + * in between reads if it's not immediate | ||
12596 | + */ | ||
12597 | + tmp = read_reg32 (adev, REG_ACX_VENDOR_ID); | ||
12598 | + count = 500; | ||
12599 | + while (count-- && (tmp != ACX_VENDOR_ID)) { | ||
12600 | + mdelay (10); | ||
12601 | + tmp = read_reg32 (adev, REG_ACX_VENDOR_ID); | ||
12602 | + } | ||
12603 | + | ||
12604 | + /* end what Windows driver does */ | ||
12605 | + | ||
12606 | + acxmem_l_reset_mac(adev); | ||
12607 | + | ||
12608 | + ecpu_ctrl = read_reg32(adev, IO_ACX_ECPU_CTRL) & 1; | ||
12609 | + if (!ecpu_ctrl) { | ||
12610 | + msg = "eCPU is already running. "; | ||
12611 | + goto end_unlock; | ||
12612 | + } | ||
12613 | + | ||
12614 | +#ifdef WE_DONT_NEED_THAT_DO_WE | ||
12615 | + if (read_reg16(adev, IO_ACX_SOR_CFG) & 2) { | ||
12616 | + /* eCPU most likely means "embedded CPU" */ | ||
12617 | + msg = "eCPU did not start after boot from flash. "; | ||
12618 | + goto end_unlock; | ||
12619 | + } | ||
12620 | + | ||
12621 | + /* check sense on reset flags */ | ||
12622 | + if (read_reg16(adev, IO_ACX_SOR_CFG) & 0x10) { | ||
12623 | + printk("%s: eCPU did not start after boot (SOR), " | ||
12624 | + "is this fatal?\n", adev->ndev->name); | ||
12625 | + } | ||
12626 | +#endif | ||
12627 | + /* scan, if any, is stopped now, setting corresponding IRQ bit */ | ||
12628 | + adev->irq_status |= HOST_INT_SCAN_COMPLETE; | ||
12629 | + | ||
12630 | + acx_unlock(adev, flags); | ||
12631 | + | ||
12632 | + /* need to know radio type before fw load */ | ||
12633 | + /* Need to wait for arrival of this information in a loop, | ||
12634 | + * most probably since eCPU runs some init code from EEPROM | ||
12635 | + * (started burst read in reset_mac()) which also | ||
12636 | + * sets the radio type ID */ | ||
12637 | + | ||
12638 | + count = 0xffff; | ||
12639 | + do { | ||
12640 | + hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION); | ||
12641 | + if (!--count) { | ||
12642 | + msg = "eCPU didn't indicate radio type"; | ||
12643 | + goto end_fail; | ||
12644 | + } | ||
12645 | + cpu_relax(); | ||
12646 | + } while (!(hardware_info & 0xff00)); /* radio type still zero? */ | ||
12647 | + printk("ACX radio type 0x%02x\n", (hardware_info >> 8) & 0xff); | ||
12648 | + /* printk("DEBUG: count %d\n", count); */ | ||
12649 | + adev->form_factor = hardware_info & 0xff; | ||
12650 | + adev->radio_type = hardware_info >> 8; | ||
12651 | + | ||
12652 | + /* load the firmware */ | ||
12653 | + if (OK != acxmem_s_upload_fw(adev)) | ||
12654 | + goto end_fail; | ||
12655 | + | ||
12656 | + /* acx_s_msleep(10); this one really shouldn't be required */ | ||
12657 | + | ||
12658 | + /* now start eCPU by clearing bit */ | ||
12659 | + clear_regbits (adev, IO_ACX_ECPU_CTRL, 0x1); | ||
12660 | + log(L_DEBUG, "booted eCPU up and waiting for completion...\n"); | ||
12661 | + | ||
12662 | + /* Windows driver clears bit 0x200 in register 0x2A0 here */ | ||
12663 | + clear_regbits (adev, 0x2A0, 0x200); | ||
12664 | + | ||
12665 | + /* Windows driver sets bit 0x200 in ACX_GPIO_OUT here */ | ||
12666 | + set_regbits (adev, IO_ACX_GPIO_OUT, 0x200); | ||
12667 | + /* wait for eCPU bootup */ | ||
12668 | + if (OK != acxmem_s_verify_init(adev)) { | ||
12669 | + msg = "timeout waiting for eCPU. "; | ||
12670 | + goto end_fail; | ||
12671 | + } | ||
12672 | + log(L_DEBUG, "eCPU has woken up, card is ready to be configured\n"); | ||
12673 | + init_mboxes(adev); | ||
12674 | + acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0); | ||
12675 | + | ||
12676 | + /* test that EEPROM is readable */ | ||
12677 | + read_eeprom_area(adev); | ||
12678 | + | ||
12679 | + result = OK; | ||
12680 | + goto end; | ||
12681 | + | ||
12682 | +/* Finish error message. Indicate which function failed */ | ||
12683 | +end_unlock: | ||
12684 | + acx_unlock(adev, flags); | ||
12685 | +end_fail: | ||
12686 | + printk("acx: %sreset_dev() FAILED\n", msg); | ||
12687 | +end: | ||
12688 | + FN_EXIT1(result); | ||
12689 | + return result; | ||
12690 | +} | ||
12691 | + | ||
12692 | + | ||
12693 | +/*********************************************************************** | ||
12694 | +** acxmem_s_issue_cmd_timeo | ||
12695 | +** | ||
12696 | +** Sends command to fw, extract result | ||
12697 | +** | ||
12698 | +** NB: we do _not_ take lock inside, so be sure to not touch anything | ||
12699 | +** which may interfere with IRQ handler operation | ||
12700 | +** | ||
12701 | +** TODO: busy wait is a bit silly, so: | ||
12702 | +** 1) stop doing many iters - go to sleep after first | ||
12703 | +** 2) go to waitqueue based approach: wait, not poll! | ||
12704 | +*/ | ||
12705 | +#undef FUNC | ||
12706 | +#define FUNC "issue_cmd" | ||
12707 | + | ||
12708 | +#if !ACX_DEBUG | ||
12709 | +int | ||
12710 | +acxmem_s_issue_cmd_timeo( | ||
12711 | + acx_device_t *adev, | ||
12712 | + unsigned int cmd, | ||
12713 | + void *buffer, | ||
12714 | + unsigned buflen, | ||
12715 | + unsigned cmd_timeout) | ||
12716 | +{ | ||
12717 | +#else | ||
12718 | +int | ||
12719 | +acxmem_s_issue_cmd_timeo_debug( | ||
12720 | + acx_device_t *adev, | ||
12721 | + unsigned cmd, | ||
12722 | + void *buffer, | ||
12723 | + unsigned buflen, | ||
12724 | + unsigned cmd_timeout, | ||
12725 | + const char* cmdstr) | ||
12726 | +{ | ||
12727 | + unsigned long start = jiffies; | ||
12728 | +#endif | ||
12729 | + const char *devname; | ||
12730 | + unsigned counter; | ||
12731 | + u16 irqtype; | ||
12732 | + int i, j; | ||
12733 | + u8 *p; | ||
12734 | + u16 cmd_status; | ||
12735 | + unsigned long timeout; | ||
12736 | + | ||
12737 | + FN_ENTER; | ||
12738 | + | ||
12739 | + devname = adev->ndev->name; | ||
12740 | + if (!devname || !devname[0] || devname[4]=='%') | ||
12741 | + devname = "acx"; | ||
12742 | + | ||
12743 | + log(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n", | ||
12744 | + cmdstr, buflen, cmd_timeout, | ||
12745 | + buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1); | ||
12746 | + | ||
12747 | + if (!(adev->dev_state_mask & ACX_STATE_FW_LOADED)) { | ||
12748 | + printk("%s: "FUNC"(): firmware is not loaded yet, " | ||
12749 | + "cannot execute commands!\n", devname); | ||
12750 | + goto bad; | ||
12751 | + } | ||
12752 | + | ||
12753 | + if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) { | ||
12754 | + printk("input buffer (len=%u):\n", buflen); | ||
12755 | + acx_dump_bytes(buffer, buflen); | ||
12756 | + } | ||
12757 | + | ||
12758 | + /* wait for firmware to become idle for our command submission */ | ||
12759 | + timeout = HZ/5; | ||
12760 | + counter = (timeout * 1000 / HZ) - 1; /* in ms */ | ||
12761 | + timeout += jiffies; | ||
12762 | + do { | ||
12763 | + cmd_status = acxmem_read_cmd_type_status(adev); | ||
12764 | + /* Test for IDLE state */ | ||
12765 | + if (!cmd_status) | ||
12766 | + break; | ||
12767 | + if (counter % 8 == 0) { | ||
12768 | + if (time_after(jiffies, timeout)) { | ||
12769 | + counter = 0; | ||
12770 | + break; | ||
12771 | + } | ||
12772 | + /* we waited 8 iterations, no luck. Sleep 8 ms */ | ||
12773 | + acx_s_msleep(8); | ||
12774 | + } | ||
12775 | + } while (likely(--counter)); | ||
12776 | + | ||
12777 | + if (!counter) { | ||
12778 | + /* the card doesn't get idle, we're in trouble */ | ||
12779 | + printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n", | ||
12780 | + devname, cmd_status); | ||
12781 | +#if DUMP_IF_SLOW > 0 | ||
12782 | + dump_acxmem (adev, 0, 0x10000); | ||
12783 | + panic ("not idle"); | ||
12784 | +#endif | ||
12785 | + goto bad; | ||
12786 | + } else if (counter < 190) { /* if waited >10ms... */ | ||
12787 | + log(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. " | ||
12788 | + "Please report\n", 199 - counter); | ||
12789 | + } | ||
12790 | + | ||
12791 | + /* now write the parameters of the command if needed */ | ||
12792 | + if (buffer && buflen) { | ||
12793 | + /* if it's an INTERROGATE command, just pass the length | ||
12794 | + * of parameters to read, as data */ | ||
12795 | +#if CMD_DISCOVERY | ||
12796 | + if (cmd == ACX1xx_CMD_INTERROGATE) | ||
12797 | + memset_io(adev->cmd_area + 4, 0xAA, buflen); | ||
12798 | +#endif | ||
12799 | + /* | ||
12800 | + * slave memory version | ||
12801 | + */ | ||
12802 | + copy_to_slavemem (adev, (u32) (adev->cmd_area + 4), buffer, | ||
12803 | + (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen); | ||
12804 | + } | ||
12805 | + /* now write the actual command type */ | ||
12806 | + acxmem_write_cmd_type_status(adev, cmd, 0); | ||
12807 | + | ||
12808 | + /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */ | ||
12809 | + adev->irq_status &= ~HOST_INT_CMD_COMPLETE; | ||
12810 | + | ||
12811 | + /* execute command */ | ||
12812 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_CMD); | ||
12813 | + write_flush(adev); | ||
12814 | + | ||
12815 | + /* wait for firmware to process command */ | ||
12816 | + | ||
12817 | + /* Ensure nonzero and not too large timeout. | ||
12818 | + ** Also converts e.g. 100->99, 200->199 | ||
12819 | + ** which is nice but not essential */ | ||
12820 | + cmd_timeout = (cmd_timeout-1) | 1; | ||
12821 | + if (unlikely(cmd_timeout > 1199)) | ||
12822 | + cmd_timeout = 1199; | ||
12823 | + | ||
12824 | + /* we schedule away sometimes (timeout can be large) */ | ||
12825 | + counter = cmd_timeout; | ||
12826 | + timeout = jiffies + cmd_timeout * HZ / 1000; | ||
12827 | + do { | ||
12828 | + if (!adev->irqs_active) { /* IRQ disabled: poll */ | ||
12829 | + irqtype = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES); | ||
12830 | + if (irqtype & HOST_INT_CMD_COMPLETE) { | ||
12831 | + write_reg16(adev, IO_ACX_IRQ_ACK, | ||
12832 | + HOST_INT_CMD_COMPLETE); | ||
12833 | + break; | ||
12834 | + } | ||
12835 | + } else { /* Wait when IRQ will set the bit */ | ||
12836 | + irqtype = adev->irq_status; | ||
12837 | + if (irqtype & HOST_INT_CMD_COMPLETE) | ||
12838 | + break; | ||
12839 | + } | ||
12840 | + | ||
12841 | + if (counter % 8 == 0) { | ||
12842 | + if (time_after(jiffies, timeout)) { | ||
12843 | + counter = 0; | ||
12844 | + break; | ||
12845 | + } | ||
12846 | + /* we waited 8 iterations, no luck. Sleep 8 ms */ | ||
12847 | + acx_s_msleep(8); | ||
12848 | + } | ||
12849 | + } while (likely(--counter)); | ||
12850 | + | ||
12851 | + /* save state for debugging */ | ||
12852 | + cmd_status = acxmem_read_cmd_type_status(adev); | ||
12853 | + | ||
12854 | + /* put the card in IDLE state */ | ||
12855 | + acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0); | ||
12856 | + | ||
12857 | + if (!counter) { /* timed out! */ | ||
12858 | + printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. " | ||
12859 | + "irq bits:0x%04X irq_status:0x%04X timeout:%dms " | ||
12860 | + "cmd_status:%d (%s)\n", | ||
12861 | + devname, (adev->irqs_active) ? "waiting" : "polling", | ||
12862 | + irqtype, adev->irq_status, cmd_timeout, | ||
12863 | + cmd_status, acx_cmd_status_str(cmd_status)); | ||
12864 | + printk("%s: "FUNC"(): device irq status 0x%04x\n", | ||
12865 | + devname, read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES)); | ||
12866 | + printk("%s: "FUNC"(): IO_ACX_IRQ_MASK 0x%04x IO_ACX_FEMR 0x%04x\n", | ||
12867 | + devname, | ||
12868 | + read_reg16 (adev, IO_ACX_IRQ_MASK), | ||
12869 | + read_reg16 (adev, IO_ACX_FEMR)); | ||
12870 | + if (read_reg16 (adev, IO_ACX_IRQ_MASK) == 0xffff) { | ||
12871 | + printk ("acxmem: firmware probably hosed - reloading\n"); | ||
12872 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) | ||
12873 | + { | ||
12874 | + pm_message_t state; | ||
12875 | + /* acxmem_e_suspend (resume_pdev, state); */ | ||
12876 | + acxmem_e_suspend (adev->ndev , state); | ||
12877 | + } | ||
12878 | +#else | ||
12879 | + acxmem_e_suspend (adev, 0); | ||
12880 | +#endif | ||
12881 | + { | ||
12882 | + resume_ndev = adev->ndev; | ||
12883 | + fw_resumer (NULL); | ||
12884 | + } | ||
12885 | + } | ||
12886 | + | ||
12887 | + goto bad; | ||
12888 | + } else if (cmd_timeout - counter > 30) { /* if waited >30ms... */ | ||
12889 | + log(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. " | ||
12890 | + "count:%d. Please report\n", | ||
12891 | + (adev->irqs_active) ? "waited" : "polled", | ||
12892 | + cmd_timeout - counter, counter); | ||
12893 | + } | ||
12894 | + | ||
12895 | + if (1 != cmd_status) { /* it is not a 'Success' */ | ||
12896 | + printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). " | ||
12897 | + "Took %dms of %d\n", | ||
12898 | + devname, cmd_status, acx_cmd_status_str(cmd_status), | ||
12899 | + cmd_timeout - counter, cmd_timeout); | ||
12900 | + /* zero out result buffer | ||
12901 | + * WARNING: this will trash stack in case of illegally large input | ||
12902 | + * length! */ | ||
12903 | + if (buflen > 388) { | ||
12904 | + /* | ||
12905 | + * 388 is maximum command length | ||
12906 | + */ | ||
12907 | + printk ("invalid length 0x%08x\n", buflen); | ||
12908 | + buflen = 388; | ||
12909 | + } | ||
12910 | + p = (u8 *) buffer; | ||
12911 | + for (i = 0; i < buflen; i+= 16) { | ||
12912 | + printk ("%04x:", i); | ||
12913 | + for (j = 0; (j < 16) && (i+j < buflen); j++) { | ||
12914 | + printk (" %02x", *p++); | ||
12915 | + } | ||
12916 | + printk ("\n"); | ||
12917 | + } | ||
12918 | + if (buffer && buflen) | ||
12919 | + memset(buffer, 0, buflen); | ||
12920 | + goto bad; | ||
12921 | + } | ||
12922 | + | ||
12923 | + /* read in result parameters if needed */ | ||
12924 | + if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) { | ||
12925 | + copy_from_slavemem (adev, buffer, (u32) (adev->cmd_area + 4), buflen); | ||
12926 | + if (acx_debug & L_DEBUG) { | ||
12927 | + printk("output buffer (len=%u): ", buflen); | ||
12928 | + acx_dump_bytes(buffer, buflen); | ||
12929 | + } | ||
12930 | + } | ||
12931 | + | ||
12932 | +/* ok: */ | ||
12933 | + log(L_CTL, FUNC"(%s): took %ld jiffies to complete\n", | ||
12934 | + cmdstr, jiffies - start); | ||
12935 | + FN_EXIT1(OK); | ||
12936 | + return OK; | ||
12937 | + | ||
12938 | +bad: | ||
12939 | + /* Give enough info so that callers can avoid | ||
12940 | + ** printing their own diagnostic messages */ | ||
12941 | +#if ACX_DEBUG | ||
12942 | + printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr); | ||
12943 | +#else | ||
12944 | + printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd); | ||
12945 | +#endif | ||
12946 | + dump_stack(); | ||
12947 | + FN_EXIT1(NOT_OK); | ||
12948 | + return NOT_OK; | ||
12949 | +} | ||
12950 | + | ||
12951 | + | ||
12952 | +/*********************************************************************** | ||
12953 | +*/ | ||
12954 | +#if defined(NONESSENTIAL_FEATURES) | ||
12955 | +typedef struct device_id { | ||
12956 | + unsigned char id[6]; | ||
12957 | + char *descr; | ||
12958 | + char *type; | ||
12959 | +} device_id_t; | ||
12960 | + | ||
12961 | +static const device_id_t | ||
12962 | +device_ids[] = | ||
12963 | +{ | ||
12964 | + { | ||
12965 | + {'G', 'l', 'o', 'b', 'a', 'l'}, | ||
12966 | + NULL, | ||
12967 | + NULL, | ||
12968 | + }, | ||
12969 | + { | ||
12970 | + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, | ||
12971 | + "uninitialized", | ||
12972 | + "SpeedStream SS1021 or Gigafast WF721-AEX" | ||
12973 | + }, | ||
12974 | + { | ||
12975 | + {0x80, 0x81, 0x82, 0x83, 0x84, 0x85}, | ||
12976 | + "non-standard", | ||
12977 | + "DrayTek Vigor 520" | ||
12978 | + }, | ||
12979 | + { | ||
12980 | + {'?', '?', '?', '?', '?', '?'}, | ||
12981 | + "non-standard", | ||
12982 | + "Level One WPC-0200" | ||
12983 | + }, | ||
12984 | + { | ||
12985 | + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | ||
12986 | + "empty", | ||
12987 | + "DWL-650+ variant" | ||
12988 | + } | ||
12989 | +}; | ||
12990 | + | ||
12991 | +static void | ||
12992 | +acx_show_card_eeprom_id(acx_device_t *adev) | ||
12993 | +{ | ||
12994 | + unsigned char buffer[CARD_EEPROM_ID_SIZE]; | ||
12995 | + int i; | ||
12996 | + | ||
12997 | + memset(&buffer, 0, CARD_EEPROM_ID_SIZE); | ||
12998 | + /* use direct EEPROM access */ | ||
12999 | + for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) { | ||
13000 | + if (OK != acxmem_read_eeprom_byte(adev, | ||
13001 | + ACX100_EEPROM_ID_OFFSET + i, | ||
13002 | + &buffer[i])) { | ||
13003 | + printk("acx: reading EEPROM FAILED\n"); | ||
13004 | + break; | ||
13005 | + } | ||
13006 | + } | ||
13007 | + | ||
13008 | + for (i = 0; i < VEC_SIZE(device_ids); i++) { | ||
13009 | + if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) { | ||
13010 | + if (device_ids[i].descr) { | ||
13011 | + printk("acx: EEPROM card ID string check " | ||
13012 | + "found %s card ID: is this %s?\n", | ||
13013 | + device_ids[i].descr, device_ids[i].type); | ||
13014 | + } | ||
13015 | + break; | ||
13016 | + } | ||
13017 | + } | ||
13018 | + if (i == VEC_SIZE(device_ids)) { | ||
13019 | + printk("acx: EEPROM card ID string check found " | ||
13020 | + "unknown card: expected 'Global', got '%.*s\'. " | ||
13021 | + "Please report\n", CARD_EEPROM_ID_SIZE, buffer); | ||
13022 | + } | ||
13023 | +} | ||
13024 | +#endif /* NONESSENTIAL_FEATURES */ | ||
13025 | + | ||
13026 | +/*********************************************************************** | ||
13027 | +** acxmem_free_desc_queues | ||
13028 | +** | ||
13029 | +** Releases the queues that have been allocated, the | ||
13030 | +** others have been initialised to NULL so this | ||
13031 | +** function can be used if only part of the queues were allocated. | ||
13032 | +*/ | ||
13033 | + | ||
13034 | +void | ||
13035 | +acxmem_free_desc_queues(acx_device_t *adev) | ||
13036 | +{ | ||
13037 | +#define ACX_FREE_QUEUE(size, ptr, phyaddr) \ | ||
13038 | + if (ptr) { \ | ||
13039 | + kfree(ptr); \ | ||
13040 | + ptr = NULL; \ | ||
13041 | + size = 0; \ | ||
13042 | + } | ||
13043 | + | ||
13044 | + FN_ENTER; | ||
13045 | + | ||
13046 | + ACX_FREE_QUEUE(adev->txhostdesc_area_size, adev->txhostdesc_start, adev->txhostdesc_startphy); | ||
13047 | + ACX_FREE_QUEUE(adev->txbuf_area_size, adev->txbuf_start, adev->txbuf_startphy); | ||
13048 | + | ||
13049 | + adev->txdesc_start = NULL; | ||
13050 | + | ||
13051 | + ACX_FREE_QUEUE(adev->rxhostdesc_area_size, adev->rxhostdesc_start, adev->rxhostdesc_startphy); | ||
13052 | + ACX_FREE_QUEUE(adev->rxbuf_area_size, adev->rxbuf_start, adev->rxbuf_startphy); | ||
13053 | + | ||
13054 | + adev->rxdesc_start = NULL; | ||
13055 | + | ||
13056 | + FN_EXIT0; | ||
13057 | +} | ||
13058 | + | ||
13059 | + | ||
13060 | +/*********************************************************************** | ||
13061 | +** acxmem_s_delete_dma_regions | ||
13062 | +*/ | ||
13063 | +static void | ||
13064 | +acxmem_s_delete_dma_regions(acx_device_t *adev) | ||
13065 | +{ | ||
13066 | + unsigned long flags; | ||
13067 | + | ||
13068 | + FN_ENTER; | ||
13069 | + /* disable radio Tx/Rx. Shouldn't we use the firmware commands | ||
13070 | + * here instead? Or are we that much down the road that it's no | ||
13071 | + * longer possible here? */ | ||
13072 | + /* | ||
13073 | + * slave memory interface really doesn't like this. | ||
13074 | + */ | ||
13075 | + /* | ||
13076 | + write_reg16(adev, IO_ACX_ENABLE, 0); | ||
13077 | + */ | ||
13078 | + | ||
13079 | + acx_s_msleep(100); | ||
13080 | + | ||
13081 | + acx_lock(adev, flags); | ||
13082 | + acxmem_free_desc_queues(adev); | ||
13083 | + acx_unlock(adev, flags); | ||
13084 | + | ||
13085 | + FN_EXIT0; | ||
13086 | +} | ||
13087 | + | ||
13088 | + | ||
13089 | +/*********************************************************************** | ||
13090 | +** acxmem_e_probe | ||
13091 | +** | ||
13092 | +** Probe routine called when a PCI device w/ matching ID is found. | ||
13093 | +** Here's the sequence: | ||
13094 | +** - Allocate the PCI resources. | ||
13095 | +** - Read the PCMCIA attribute memory to make sure we have a WLAN card | ||
13096 | +** - Reset the MAC | ||
13097 | +** - Initialize the dev and wlan data | ||
13098 | +** - Initialize the MAC | ||
13099 | +** | ||
13100 | +** pdev - ptr to pci device structure containing info about pci configuration | ||
13101 | +** id - ptr to the device id entry that matched this device | ||
13102 | +*/ | ||
13103 | +static const u16 | ||
13104 | +IO_ACX100[] = | ||
13105 | +{ | ||
13106 | + 0x0000, /* IO_ACX_SOFT_RESET */ | ||
13107 | + | ||
13108 | + 0x0014, /* IO_ACX_SLV_MEM_ADDR */ | ||
13109 | + 0x0018, /* IO_ACX_SLV_MEM_DATA */ | ||
13110 | + 0x001c, /* IO_ACX_SLV_MEM_CTL */ | ||
13111 | + 0x0020, /* IO_ACX_SLV_END_CTL */ | ||
13112 | + | ||
13113 | + 0x0034, /* IO_ACX_FEMR */ | ||
13114 | + | ||
13115 | + 0x007c, /* IO_ACX_INT_TRIG */ | ||
13116 | + 0x0098, /* IO_ACX_IRQ_MASK */ | ||
13117 | + 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */ | ||
13118 | + 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */ | ||
13119 | + 0x00ac, /* IO_ACX_IRQ_ACK */ | ||
13120 | + 0x00b0, /* IO_ACX_HINT_TRIG */ | ||
13121 | + | ||
13122 | + 0x0104, /* IO_ACX_ENABLE */ | ||
13123 | + | ||
13124 | + 0x0250, /* IO_ACX_EEPROM_CTL */ | ||
13125 | + 0x0254, /* IO_ACX_EEPROM_ADDR */ | ||
13126 | + 0x0258, /* IO_ACX_EEPROM_DATA */ | ||
13127 | + 0x025c, /* IO_ACX_EEPROM_CFG */ | ||
13128 | + | ||
13129 | + 0x0268, /* IO_ACX_PHY_ADDR */ | ||
13130 | + 0x026c, /* IO_ACX_PHY_DATA */ | ||
13131 | + 0x0270, /* IO_ACX_PHY_CTL */ | ||
13132 | + | ||
13133 | + 0x0290, /* IO_ACX_GPIO_OE */ | ||
13134 | + | ||
13135 | + 0x0298, /* IO_ACX_GPIO_OUT */ | ||
13136 | + | ||
13137 | + 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */ | ||
13138 | + 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */ | ||
13139 | + 0x02ac, /* IO_ACX_EEPROM_INFORMATION */ | ||
13140 | + | ||
13141 | + 0x02d0, /* IO_ACX_EE_START */ | ||
13142 | + 0x02d4, /* IO_ACX_SOR_CFG */ | ||
13143 | + 0x02d8 /* IO_ACX_ECPU_CTRL */ | ||
13144 | +}; | ||
13145 | + | ||
13146 | +static const u16 | ||
13147 | +IO_ACX111[] = | ||
13148 | +{ | ||
13149 | + 0x0000, /* IO_ACX_SOFT_RESET */ | ||
13150 | + | ||
13151 | + 0x0014, /* IO_ACX_SLV_MEM_ADDR */ | ||
13152 | + 0x0018, /* IO_ACX_SLV_MEM_DATA */ | ||
13153 | + 0x001c, /* IO_ACX_SLV_MEM_CTL */ | ||
13154 | + 0x0020, /* IO_ACX_SLV_MEM_CP */ | ||
13155 | + | ||
13156 | + 0x0034, /* IO_ACX_FEMR */ | ||
13157 | + | ||
13158 | + 0x00b4, /* IO_ACX_INT_TRIG */ | ||
13159 | + 0x00d4, /* IO_ACX_IRQ_MASK */ | ||
13160 | + /* we do mean NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */ | ||
13161 | + 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */ | ||
13162 | + 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */ | ||
13163 | + 0x00e8, /* IO_ACX_IRQ_ACK */ | ||
13164 | + 0x00ec, /* IO_ACX_HINT_TRIG */ | ||
13165 | + | ||
13166 | + 0x01d0, /* IO_ACX_ENABLE */ | ||
13167 | + | ||
13168 | + 0x0338, /* IO_ACX_EEPROM_CTL */ | ||
13169 | + 0x033c, /* IO_ACX_EEPROM_ADDR */ | ||
13170 | + 0x0340, /* IO_ACX_EEPROM_DATA */ | ||
13171 | + 0x0344, /* IO_ACX_EEPROM_CFG */ | ||
13172 | + | ||
13173 | + 0x0350, /* IO_ACX_PHY_ADDR */ | ||
13174 | + 0x0354, /* IO_ACX_PHY_DATA */ | ||
13175 | + 0x0358, /* IO_ACX_PHY_CTL */ | ||
13176 | + | ||
13177 | + 0x0374, /* IO_ACX_GPIO_OE */ | ||
13178 | + | ||
13179 | + 0x037c, /* IO_ACX_GPIO_OUT */ | ||
13180 | + | ||
13181 | + 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */ | ||
13182 | + 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */ | ||
13183 | + 0x0390, /* IO_ACX_EEPROM_INFORMATION */ | ||
13184 | + | ||
13185 | + 0x0100, /* IO_ACX_EE_START */ | ||
13186 | + 0x0104, /* IO_ACX_SOR_CFG */ | ||
13187 | + 0x0108, /* IO_ACX_ECPU_CTRL */ | ||
13188 | +}; | ||
13189 | + | ||
13190 | +static void | ||
13191 | +dummy_netdev_init(struct net_device *ndev) {} | ||
13192 | + | ||
13193 | +/* | ||
13194 | + * Most of the acx specific pieces of hardware reset. | ||
13195 | + */ | ||
13196 | +static int | ||
13197 | +acxmem_complete_hw_reset (acx_device_t *adev) | ||
13198 | +{ | ||
13199 | + acx111_ie_configoption_t co; | ||
13200 | + | ||
13201 | + /* NB: read_reg() reads may return bogus data before reset_dev(), | ||
13202 | + * since the firmware which directly controls large parts of the I/O | ||
13203 | + * registers isn't initialized yet. | ||
13204 | + * acx100 seems to be more affected than acx111 */ | ||
13205 | + if (OK != acxmem_s_reset_dev (adev)) | ||
13206 | + return -1; | ||
13207 | + | ||
13208 | + if (IS_ACX100(adev)) { | ||
13209 | + /* ACX100: configopt struct in cmd mailbox - directly after reset */ | ||
13210 | + copy_from_slavemem (adev, (u8*) &co, (u32) adev->cmd_area, sizeof (co)); | ||
13211 | + } | ||
13212 | + | ||
13213 | + if (OK != acx_s_init_mac(adev)) | ||
13214 | + return -3; | ||
13215 | + | ||
13216 | + if (IS_ACX111(adev)) { | ||
13217 | + /* ACX111: configopt struct needs to be queried after full init */ | ||
13218 | + acx_s_interrogate(adev, &co, ACX111_IE_CONFIG_OPTIONS); | ||
13219 | + } | ||
13220 | + | ||
13221 | + /* | ||
13222 | + * Set up transmit buffer administration | ||
13223 | + */ | ||
13224 | + init_acx_txbuf (adev); | ||
13225 | + | ||
13226 | + /* | ||
13227 | + * Windows driver writes 0x01000000 to register 0x288, RADIO_CTL, if the form factor | ||
13228 | + * is 3. It also write protects the EEPROM by writing 1<<9 to GPIO_OUT | ||
13229 | + */ | ||
13230 | + if (adev->form_factor == 3) { | ||
13231 | + set_regbits (adev, 0x288, 0x01000000); | ||
13232 | + set_regbits (adev, 0x298, 1<<9); | ||
13233 | + } | ||
13234 | + | ||
13235 | +/* TODO: merge them into one function, they are called just once and are the same for pci & usb */ | ||
13236 | + if (OK != acxmem_read_eeprom_byte(adev, 0x05, &adev->eeprom_version)) | ||
13237 | + return -2; | ||
13238 | + | ||
13239 | + acx_s_parse_configoption(adev, &co); | ||
13240 | + acx_s_get_firmware_version(adev); /* needs to be after acx_s_init_mac() */ | ||
13241 | + acx_display_hardware_details(adev); | ||
13242 | + | ||
13243 | + return 0; | ||
13244 | +} | ||
13245 | + | ||
13246 | +static int acx_init_netdev(struct net_device *ndev, struct device *dev, int base_addr, int addr_size, int irq) | ||
13247 | +{ | ||
13248 | + const char *chip_name; | ||
13249 | + int result = -EIO; | ||
13250 | + int err; | ||
13251 | + u8 chip_type; | ||
13252 | + acx_device_t *adev = NULL; | ||
13253 | + | ||
13254 | + FN_ENTER; | ||
13255 | + | ||
13256 | + /* FIXME: prism54 calls pci_set_mwi() here, | ||
13257 | + * should we do/support the same? */ | ||
13258 | + | ||
13259 | + /* chiptype is u8 but id->driver_data is ulong | ||
13260 | + ** Works for now (possible values are 1 and 2) */ | ||
13261 | + chip_type = CHIPTYPE_ACX100; | ||
13262 | + /* acx100 and acx111 have different PCI memory regions */ | ||
13263 | + if (chip_type == CHIPTYPE_ACX100) { | ||
13264 | + chip_name = "ACX100"; | ||
13265 | + } else if (chip_type == CHIPTYPE_ACX111) { | ||
13266 | + chip_name = "ACX111"; | ||
13267 | + } else { | ||
13268 | + printk("acx: unknown chip type 0x%04X\n", chip_type); | ||
13269 | + goto fail_unknown_chiptype; | ||
13270 | + } | ||
13271 | + | ||
13272 | + printk("acx: found %s-based wireless network card\n", chip_name); | ||
13273 | + log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug); | ||
13274 | + | ||
13275 | + | ||
13276 | + dev_set_drvdata(dev, ndev); | ||
13277 | + | ||
13278 | + ether_setup(ndev); | ||
13279 | + | ||
13280 | + ndev->irq = irq; | ||
13281 | + | ||
13282 | + ndev->base_addr = base_addr; | ||
13283 | +printk (KERN_INFO "memwinbase=%lx memwinsize=%u\n",memwin.Base,memwin.Size); | ||
13284 | + if (addr_size == 0 || ndev->irq == 0) | ||
13285 | + goto fail_hw_params; | ||
13286 | + ndev->open = &acxmem_e_open; | ||
13287 | + ndev->stop = &acxmem_e_close; | ||
13288 | + //pdev->dev.release = &acxmem_e_release; | ||
13289 | + ndev->hard_start_xmit = &acx_i_start_xmit; | ||
13290 | + ndev->get_stats = &acx_e_get_stats; | ||
13291 | +#if IW_HANDLER_VERSION <= 5 | ||
13292 | + ndev->get_wireless_stats = &acx_e_get_wireless_stats; | ||
13293 | +#endif | ||
13294 | + ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; | ||
13295 | + ndev->set_multicast_list = &acxmem_i_set_multicast_list; | ||
13296 | + ndev->tx_timeout = &acxmem_i_tx_timeout; | ||
13297 | + ndev->change_mtu = &acx_e_change_mtu; | ||
13298 | + ndev->watchdog_timeo = 4 * HZ; | ||
13299 | + | ||
13300 | + adev = ndev2adev(ndev); | ||
13301 | + spin_lock_init(&adev->lock); /* initial state: unlocked */ | ||
13302 | + spin_lock_init(&adev->txbuf_lock); | ||
13303 | + /* We do not start with downed sem: we want PARANOID_LOCKING to work */ | ||
13304 | + sema_init(&adev->sem, 1); /* initial state: 1 (upped) */ | ||
13305 | + /* since nobody can see new netdev yet, we can as well | ||
13306 | + ** just _presume_ that we're under sem (instead of actually taking it): */ | ||
13307 | + /* acx_sem_lock(adev); */ | ||
13308 | + adev->dev = dev; | ||
13309 | + adev->ndev = ndev; | ||
13310 | + adev->dev_type = DEVTYPE_MEM; | ||
13311 | + adev->chip_type = chip_type; | ||
13312 | + adev->chip_name = chip_name; | ||
13313 | + adev->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111; | ||
13314 | + adev->membase = (volatile u32 *) ndev->base_addr; | ||
13315 | + adev->iobase = (volatile u32 *) ioremap_nocache (ndev->base_addr, addr_size); | ||
13316 | + /* to find crashes due to weird driver access | ||
13317 | + * to unconfigured interface (ifup) */ | ||
13318 | + adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead; | ||
13319 | + | ||
13320 | +#if defined(NONESSENTIAL_FEATURES) | ||
13321 | + acx_show_card_eeprom_id(adev); | ||
13322 | +#endif /* NONESSENTIAL_FEATURES */ | ||
13323 | + | ||
13324 | +#ifdef SET_MODULE_OWNER | ||
13325 | + SET_MODULE_OWNER(ndev); | ||
13326 | +#endif | ||
13327 | + // need to fix that @@ | ||
13328 | + SET_NETDEV_DEV(ndev, dev); | ||
13329 | + | ||
13330 | + log(L_IRQ|L_INIT, "using IRQ %d\n", ndev->irq); | ||
13331 | + | ||
13332 | + /* ok, pci setup is finished, now start initializing the card */ | ||
13333 | + | ||
13334 | + if (OK != acxmem_complete_hw_reset (adev)) | ||
13335 | + goto fail_reset; | ||
13336 | + | ||
13337 | + /* | ||
13338 | + * Set up default things for most of the card settings. | ||
13339 | + */ | ||
13340 | + acx_s_set_defaults(adev); | ||
13341 | + | ||
13342 | + /* Register the card, AFTER everything else has been set up, | ||
13343 | + * since otherwise an ioctl could step on our feet due to | ||
13344 | + * firmware operations happening in parallel or uninitialized data */ | ||
13345 | + err = register_netdev(ndev); | ||
13346 | + if (OK != err) { | ||
13347 | + printk("acx: register_netdev() FAILED: %d\n", err); | ||
13348 | + goto fail_register_netdev; | ||
13349 | + } | ||
13350 | + | ||
13351 | + acx_proc_register_entries(ndev); | ||
13352 | + | ||
13353 | + /* Now we have our device, so make sure the kernel doesn't try | ||
13354 | + * to send packets even though we're not associated to a network yet */ | ||
13355 | + acx_stop_queue(ndev, "on probe"); | ||
13356 | + acx_carrier_off(ndev, "on probe"); | ||
13357 | + | ||
13358 | + /* | ||
13359 | + * Set up a default monitor type so that poor combinations of initialization | ||
13360 | + * sequences in monitor mode don't end up destroying the hardware type. | ||
13361 | + */ | ||
13362 | + adev->monitor_type = ARPHRD_ETHER; | ||
13363 | + | ||
13364 | + /* | ||
13365 | + * Register to receive inetaddr notifier changes. This will allow us to | ||
13366 | + * catch if the user changes the MAC address of the interface. | ||
13367 | + */ | ||
13368 | + register_netdevice_notifier(&acx_netdev_notifier); | ||
13369 | + | ||
13370 | + /* after register_netdev() userspace may start working with dev | ||
13371 | + * (in particular, on other CPUs), we only need to up the sem */ | ||
13372 | + /* acx_sem_unlock(adev); */ | ||
13373 | + | ||
13374 | + printk("acx "ACX_RELEASE": net device %s, driver compiled " | ||
13375 | + "against wireless extensions %d and Linux %s\n", | ||
13376 | + ndev->name, WIRELESS_EXT, UTS_RELEASE); | ||
13377 | + | ||
13378 | +#if CMD_DISCOVERY | ||
13379 | + great_inquisitor(adev); | ||
13380 | +#endif | ||
13381 | + | ||
13382 | + result = OK; | ||
13383 | + goto done; | ||
13384 | + | ||
13385 | + /* error paths: undo everything in reverse order... */ | ||
13386 | + | ||
13387 | +fail_register_netdev: | ||
13388 | + | ||
13389 | + acxmem_s_delete_dma_regions(adev); | ||
13390 | + | ||
13391 | +fail_reset: | ||
13392 | +fail_hw_params: | ||
13393 | + free_netdev(ndev); | ||
13394 | +fail_unknown_chiptype: | ||
13395 | + | ||
13396 | + | ||
13397 | +done: | ||
13398 | + FN_EXIT1(result); | ||
13399 | + return result; | ||
13400 | +} | ||
13401 | + | ||
13402 | + | ||
13403 | +/*********************************************************************** | ||
13404 | +** acxmem_e_remove | ||
13405 | +** | ||
13406 | +** Shut device down (if not hot unplugged) | ||
13407 | +** and deallocate PCI resources for the acx chip. | ||
13408 | +** | ||
13409 | +** pdev - ptr to PCI device structure containing info about pci configuration | ||
13410 | +*/ | ||
13411 | +static int __devexit | ||
13412 | +acxmem_e_remove(struct pcmcia_device *link) | ||
13413 | +{ | ||
13414 | + struct net_device *ndev; | ||
13415 | + acx_device_t *adev; | ||
13416 | + unsigned long flags; | ||
13417 | + | ||
13418 | + FN_ENTER; | ||
13419 | + | ||
13420 | + ndev = ((local_info_t*)link->priv)->ndev; | ||
13421 | + if (!ndev) { | ||
13422 | + log(L_DEBUG, "%s: card is unused. Skipping any release code\n", | ||
13423 | + __func__); | ||
13424 | + goto end; | ||
13425 | + } | ||
13426 | + | ||
13427 | + adev = ndev2adev(ndev); | ||
13428 | + | ||
13429 | + /* If device wasn't hot unplugged... */ | ||
13430 | + if (adev_present(adev)) { | ||
13431 | + | ||
13432 | + acx_sem_lock(adev); | ||
13433 | + | ||
13434 | + /* disable both Tx and Rx to shut radio down properly */ | ||
13435 | + acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0); | ||
13436 | + acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0); | ||
13437 | + | ||
13438 | +#ifdef REDUNDANT | ||
13439 | + /* put the eCPU to sleep to save power | ||
13440 | + * Halting is not possible currently, | ||
13441 | + * since not supported by all firmware versions */ | ||
13442 | + acx_s_issue_cmd(adev, ACX100_CMD_SLEEP, NULL, 0); | ||
13443 | +#endif | ||
13444 | + acx_lock(adev, flags); | ||
13445 | + | ||
13446 | + /* disable power LED to save power :-) */ | ||
13447 | + log(L_INIT, "switching off power LED to save power\n"); | ||
13448 | + acxmem_l_power_led(adev, 0); | ||
13449 | + | ||
13450 | + /* stop our eCPU */ | ||
13451 | + if (IS_ACX111(adev)) { | ||
13452 | + /* FIXME: does this actually keep halting the eCPU? | ||
13453 | + * I don't think so... | ||
13454 | + */ | ||
13455 | + acxmem_l_reset_mac(adev); | ||
13456 | + } else { | ||
13457 | + u16 temp; | ||
13458 | + | ||
13459 | + /* halt eCPU */ | ||
13460 | + temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1; | ||
13461 | + write_reg16(adev, IO_ACX_ECPU_CTRL, temp); | ||
13462 | + write_flush(adev); | ||
13463 | + } | ||
13464 | + | ||
13465 | + acx_unlock(adev, flags); | ||
13466 | + | ||
13467 | + acx_sem_unlock(adev); | ||
13468 | + } | ||
13469 | + | ||
13470 | + | ||
13471 | + /* | ||
13472 | + * Unregister the notifier chain | ||
13473 | + */ | ||
13474 | + unregister_netdevice_notifier(&acx_netdev_notifier); | ||
13475 | + | ||
13476 | + /* unregister the device to not let the kernel | ||
13477 | + * (e.g. ioctls) access a half-deconfigured device | ||
13478 | + * NB: this will cause acxmem_e_close() to be called, | ||
13479 | + * thus we shouldn't call it under sem! */ | ||
13480 | + log(L_INIT, "removing device %s\n", ndev->name); | ||
13481 | + unregister_netdev(ndev); | ||
13482 | + | ||
13483 | + /* unregister_netdev ensures that no references to us left. | ||
13484 | + * For paranoid reasons we continue to follow the rules */ | ||
13485 | + acx_sem_lock(adev); | ||
13486 | + | ||
13487 | + if (adev->dev_state_mask & ACX_STATE_IFACE_UP) { | ||
13488 | + acxmem_s_down(ndev); | ||
13489 | + CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
13490 | + } | ||
13491 | + | ||
13492 | + acx_proc_unregister_entries(ndev); | ||
13493 | + | ||
13494 | + acxmem_s_delete_dma_regions(adev); | ||
13495 | + | ||
13496 | + /* finally, clean up PCI bus state */ | ||
13497 | + if (adev->iobase) iounmap((void *)adev->iobase); | ||
13498 | + | ||
13499 | + acx_sem_unlock(adev); | ||
13500 | + | ||
13501 | + /* Free netdev (quite late, | ||
13502 | + * since otherwise we might get caught off-guard | ||
13503 | + * by a netdev timeout handler execution | ||
13504 | + * expecting to see a working dev...) */ | ||
13505 | + free_netdev(ndev); | ||
13506 | + | ||
13507 | + printk ("e_remove done\n"); | ||
13508 | +end: | ||
13509 | + FN_EXIT0; | ||
13510 | + | ||
13511 | + return 0; | ||
13512 | +} | ||
13513 | + | ||
13514 | + | ||
13515 | +/*********************************************************************** | ||
13516 | +** TODO: PM code needs to be fixed / debugged / tested. | ||
13517 | +*/ | ||
13518 | +#ifdef CONFIG_PM | ||
13519 | +static int | ||
13520 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) | ||
13521 | +acxmem_e_suspend( struct net_device *ndev, pm_message_t state) | ||
13522 | +#else | ||
13523 | +acxmem_e_suspend( struct net_device *ndev, u32 state) | ||
13524 | +#endif | ||
13525 | +{ | ||
13526 | + FN_ENTER; | ||
13527 | + acx_device_t *adev; | ||
13528 | + printk("acx: suspend handler is experimental!\n"); | ||
13529 | + printk("sus: dev %p\n", ndev); | ||
13530 | + | ||
13531 | + if (!netif_running(ndev)) | ||
13532 | + goto end; | ||
13533 | + // @@ need to get it from link or something like that | ||
13534 | + adev = ndev2adev(ndev); | ||
13535 | + printk("sus: adev %p\n", adev); | ||
13536 | + | ||
13537 | + acx_sem_lock(adev); | ||
13538 | + | ||
13539 | + netif_device_detach(adev->ndev); /* this one cannot sleep */ | ||
13540 | + acxmem_s_down(adev->ndev); | ||
13541 | + /* down() does not set it to 0xffff, but here we really want that */ | ||
13542 | + write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); | ||
13543 | + write_reg16(adev, IO_ACX_FEMR, 0x0); | ||
13544 | + acxmem_s_delete_dma_regions(adev); | ||
13545 | + | ||
13546 | + /* | ||
13547 | + * Turn the ACX chip off. | ||
13548 | + */ | ||
13549 | + | ||
13550 | + acx_sem_unlock(adev); | ||
13551 | +end: | ||
13552 | + FN_EXIT0; | ||
13553 | + return OK; | ||
13554 | +} | ||
13555 | + | ||
13556 | + | ||
13557 | +static void | ||
13558 | +fw_resumer(struct work_struct *notused) | ||
13559 | +{ | ||
13560 | + acx_device_t *adev; | ||
13561 | + struct net_device *ndev = resume_ndev; | ||
13562 | + | ||
13563 | + printk("acx: resume handler is experimental!\n"); | ||
13564 | + printk("rsm: got dev %p\n", ndev); | ||
13565 | + | ||
13566 | + if (!netif_running(ndev)) | ||
13567 | + return; | ||
13568 | + | ||
13569 | + adev = ndev2adev(ndev); | ||
13570 | + printk("rsm: got adev %p\n", adev); | ||
13571 | + | ||
13572 | + acx_sem_lock(adev); | ||
13573 | + | ||
13574 | + /* | ||
13575 | + * Turn on the ACX. | ||
13576 | + */ | ||
13577 | + | ||
13578 | + acxmem_complete_hw_reset (adev); | ||
13579 | + | ||
13580 | + /* | ||
13581 | + * done by acx_s_set_defaults for initial startup | ||
13582 | + */ | ||
13583 | + acxmem_set_interrupt_mask(adev); | ||
13584 | + | ||
13585 | + printk ("rsm: bringing up interface\n"); | ||
13586 | + SET_BIT (adev->set_mask, GETSET_ALL); | ||
13587 | + acxmem_s_up(ndev); | ||
13588 | + printk("rsm: acx up done\n"); | ||
13589 | + | ||
13590 | + /* now even reload all card parameters as they were before suspend, | ||
13591 | + * and possibly be back in the network again already :-) | ||
13592 | + */ | ||
13593 | + /* - most settings updated in acxmem_s_up() | ||
13594 | + if (ACX_STATE_IFACE_UP & adev->dev_state_mask) { | ||
13595 | + adev->set_mask = GETSET_ALL; | ||
13596 | + acx_s_update_card_settings(adev); | ||
13597 | + printk("rsm: settings updated\n"); | ||
13598 | + } | ||
13599 | + */ | ||
13600 | + netif_device_attach(ndev); | ||
13601 | + printk("rsm: device attached\n"); | ||
13602 | + | ||
13603 | + acx_sem_unlock(adev); | ||
13604 | +} | ||
13605 | + | ||
13606 | +DECLARE_WORK( fw_resume_work, fw_resumer ); | ||
13607 | + | ||
13608 | +static int | ||
13609 | +acxmem_e_resume(struct pcmcia_device *link) | ||
13610 | +{ | ||
13611 | + FN_ENTER; | ||
13612 | + | ||
13613 | + //resume_pdev = pdev; | ||
13614 | + schedule_work( &fw_resume_work ); | ||
13615 | + | ||
13616 | + FN_EXIT0; | ||
13617 | + return OK; | ||
13618 | +} | ||
13619 | +#endif /* CONFIG_PM */ | ||
13620 | + | ||
13621 | + | ||
13622 | +/*********************************************************************** | ||
13623 | +** acxmem_s_up | ||
13624 | +** | ||
13625 | +** This function is called by acxmem_e_open (when ifconfig sets the device as up) | ||
13626 | +** | ||
13627 | +** Side effects: | ||
13628 | +** - Enables on-card interrupt requests | ||
13629 | +** - calls acx_s_start | ||
13630 | +*/ | ||
13631 | + | ||
13632 | +static void | ||
13633 | +enable_acx_irq(acx_device_t *adev) | ||
13634 | +{ | ||
13635 | + FN_ENTER; | ||
13636 | + write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask); | ||
13637 | + write_reg16(adev, IO_ACX_FEMR, 0x8000); | ||
13638 | + adev->irqs_active = 1; | ||
13639 | + FN_EXIT0; | ||
13640 | +} | ||
13641 | + | ||
13642 | +static void | ||
13643 | +acxmem_s_up(struct net_device *ndev) | ||
13644 | +{ | ||
13645 | + acx_device_t *adev = ndev2adev(ndev); | ||
13646 | + unsigned long flags; | ||
13647 | + | ||
13648 | + FN_ENTER; | ||
13649 | + | ||
13650 | + acx_lock(adev, flags); | ||
13651 | + enable_acx_irq(adev); | ||
13652 | + acx_unlock(adev, flags); | ||
13653 | + | ||
13654 | + /* acx fw < 1.9.3.e has a hardware timer, and older drivers | ||
13655 | + ** used to use it. But we don't do that anymore, our OS | ||
13656 | + ** has reliable software timers */ | ||
13657 | + init_timer(&adev->mgmt_timer); | ||
13658 | + adev->mgmt_timer.function = acx_i_timer; | ||
13659 | + adev->mgmt_timer.data = (unsigned long)adev; | ||
13660 | + | ||
13661 | + /* Need to set ACX_STATE_IFACE_UP first, or else | ||
13662 | + ** timer won't be started by acx_set_status() */ | ||
13663 | + SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
13664 | + switch (adev->mode) { | ||
13665 | + case ACX_MODE_0_ADHOC: | ||
13666 | + case ACX_MODE_2_STA: | ||
13667 | + /* actual scan cmd will happen in start() */ | ||
13668 | + acx_set_status(adev, ACX_STATUS_1_SCANNING); break; | ||
13669 | + case ACX_MODE_3_AP: | ||
13670 | + case ACX_MODE_MONITOR: | ||
13671 | + acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); break; | ||
13672 | + } | ||
13673 | + | ||
13674 | + acx_s_start(adev); | ||
13675 | + | ||
13676 | + FN_EXIT0; | ||
13677 | +} | ||
13678 | + | ||
13679 | + | ||
13680 | +/*********************************************************************** | ||
13681 | +** acxmem_s_down | ||
13682 | +** | ||
13683 | +** This disables the netdevice | ||
13684 | +** | ||
13685 | +** Side effects: | ||
13686 | +** - disables on-card interrupt request | ||
13687 | +*/ | ||
13688 | + | ||
13689 | +static void | ||
13690 | +disable_acx_irq(acx_device_t *adev) | ||
13691 | +{ | ||
13692 | + FN_ENTER; | ||
13693 | + | ||
13694 | + /* I guess mask is not 0xffff because acx100 won't signal | ||
13695 | + ** cmd completion then (needed for ifup). | ||
13696 | + ** Someone with acx100 please confirm */ | ||
13697 | + write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask_off); | ||
13698 | + write_reg16(adev, IO_ACX_FEMR, 0x0); | ||
13699 | + adev->irqs_active = 0; | ||
13700 | + FN_EXIT0; | ||
13701 | +} | ||
13702 | + | ||
13703 | +static void | ||
13704 | +acxmem_s_down(struct net_device *ndev) | ||
13705 | +{ | ||
13706 | + acx_device_t *adev = ndev2adev(ndev); | ||
13707 | + unsigned long flags; | ||
13708 | + | ||
13709 | + FN_ENTER; | ||
13710 | + | ||
13711 | + /* Disable IRQs first, so that IRQs cannot race with us */ | ||
13712 | + /* then wait until interrupts have finished executing on other CPUs */ | ||
13713 | + acx_lock(adev, flags); | ||
13714 | + disable_acx_irq(adev); | ||
13715 | + synchronize_irq(adev->pdev->irq); | ||
13716 | + acx_unlock(adev, flags); | ||
13717 | + | ||
13718 | + /* we really don't want to have an asynchronous tasklet disturb us | ||
13719 | + ** after something vital for its job has been shut down, so | ||
13720 | + ** end all remaining work now. | ||
13721 | + ** | ||
13722 | + ** NB: carrier_off (done by set_status below) would lead to | ||
13723 | + ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK(). | ||
13724 | + ** That's why we do FLUSH first. | ||
13725 | + ** | ||
13726 | + ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK() | ||
13727 | + ** waits for acx_e_after_interrupt_task to complete if it is running | ||
13728 | + ** on another CPU, but acx_e_after_interrupt_task | ||
13729 | + ** will sleep on sem forever, because it is taken by us! | ||
13730 | + ** Work around that by temporary sem unlock. | ||
13731 | + ** This will fail miserably if we'll be hit by concurrent | ||
13732 | + ** iwconfig or something in between. TODO! */ | ||
13733 | + acx_sem_unlock(adev); | ||
13734 | + FLUSH_SCHEDULED_WORK(); | ||
13735 | + acx_sem_lock(adev); | ||
13736 | + | ||
13737 | + /* This is possible: | ||
13738 | + ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task -> | ||
13739 | + ** -> set_status(ASSOCIATED) -> wake_queue() | ||
13740 | + ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK | ||
13741 | + ** lock/unlock is just paranoia, maybe not needed */ | ||
13742 | + acx_lock(adev, flags); | ||
13743 | + acx_stop_queue(ndev, "on ifdown"); | ||
13744 | + acx_set_status(adev, ACX_STATUS_0_STOPPED); | ||
13745 | + acx_unlock(adev, flags); | ||
13746 | + | ||
13747 | + /* kernel/timer.c says it's illegal to del_timer_sync() | ||
13748 | + ** a timer which restarts itself. We guarantee this cannot | ||
13749 | + ** ever happen because acx_i_timer() never does this if | ||
13750 | + ** status is ACX_STATUS_0_STOPPED */ | ||
13751 | + del_timer_sync(&adev->mgmt_timer); | ||
13752 | + | ||
13753 | + FN_EXIT0; | ||
13754 | +} | ||
13755 | + | ||
13756 | + | ||
13757 | +/*********************************************************************** | ||
13758 | +** acxmem_e_open | ||
13759 | +** | ||
13760 | +** Called as a result of SIOCSIFFLAGS ioctl changing the flags bit IFF_UP | ||
13761 | +** from clear to set. In other words: ifconfig up. | ||
13762 | +** | ||
13763 | +** Returns: | ||
13764 | +** 0 success | ||
13765 | +** >0 f/w reported error | ||
13766 | +** <0 driver reported error | ||
13767 | +*/ | ||
13768 | +static int | ||
13769 | +acxmem_e_open(struct net_device *ndev) | ||
13770 | +{ | ||
13771 | + acx_device_t *adev = ndev2adev(ndev); | ||
13772 | + int result = OK; | ||
13773 | + | ||
13774 | + FN_ENTER; | ||
13775 | + | ||
13776 | + acx_sem_lock(adev); | ||
13777 | + | ||
13778 | + acx_init_task_scheduler(adev); | ||
13779 | + | ||
13780 | +/* TODO: pci_set_power_state(pdev, PCI_D0); ? */ | ||
13781 | + | ||
13782 | +#if 0 | ||
13783 | + /* request shared IRQ handler */ | ||
13784 | + if (request_irq(ndev->irq, acxmem_i_interrupt, SA_INTERRUPT, ndev->name, ndev)) { | ||
13785 | + printk("%s: request_irq FAILED\n", ndev->name); | ||
13786 | + result = -EAGAIN; | ||
13787 | + goto done; | ||
13788 | + } | ||
13789 | + set_irq_type (ndev->irq, IRQT_FALLING); | ||
13790 | + log(L_DEBUG|L_IRQ, "request_irq %d successful\n", ndev->irq); | ||
13791 | +#endif | ||
13792 | + | ||
13793 | + /* ifup device */ | ||
13794 | + acxmem_s_up(ndev); | ||
13795 | + | ||
13796 | + /* We don't currently have to do anything else. | ||
13797 | + * The setup of the MAC should be subsequently completed via | ||
13798 | + * the mlme commands. | ||
13799 | + * Higher layers know we're ready from dev->start==1 and | ||
13800 | + * dev->tbusy==0. Our rx path knows to pass up received/ | ||
13801 | + * frames because of dev->flags&IFF_UP is true. | ||
13802 | + */ | ||
13803 | +done: | ||
13804 | + acx_sem_unlock(adev); | ||
13805 | + | ||
13806 | + FN_EXIT1(result); | ||
13807 | + return result; | ||
13808 | +} | ||
13809 | + | ||
13810 | + | ||
13811 | +/*********************************************************************** | ||
13812 | +** acxmem_e_close | ||
13813 | +** | ||
13814 | +** Called as a result of SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP | ||
13815 | +** from set to clear. I.e. called by "ifconfig DEV down" | ||
13816 | +** | ||
13817 | +** Returns: | ||
13818 | +** 0 success | ||
13819 | +** >0 f/w reported error | ||
13820 | +** <0 driver reported error | ||
13821 | +*/ | ||
13822 | +static int | ||
13823 | +acxmem_e_close(struct net_device *ndev) | ||
13824 | +{ | ||
13825 | + acx_device_t *adev = ndev2adev(ndev); | ||
13826 | + | ||
13827 | + FN_ENTER; | ||
13828 | + | ||
13829 | + acx_sem_lock(adev); | ||
13830 | + | ||
13831 | + /* ifdown device */ | ||
13832 | + CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
13833 | + if (netif_device_present(ndev)) { | ||
13834 | + acxmem_s_down(ndev); | ||
13835 | + } | ||
13836 | + | ||
13837 | + /* disable all IRQs, release shared IRQ handler */ | ||
13838 | + write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); | ||
13839 | + write_reg16(adev, IO_ACX_FEMR, 0x0); | ||
13840 | + free_irq(ndev->irq, ndev); | ||
13841 | + | ||
13842 | +/* TODO: pci_set_power_state(pdev, PCI_D3hot); ? */ | ||
13843 | + | ||
13844 | + /* We currently don't have to do anything else. | ||
13845 | + * Higher layers know we're not ready from dev->start==0 and | ||
13846 | + * dev->tbusy==1. Our rx path knows to not pass up received | ||
13847 | + * frames because of dev->flags&IFF_UP is false. | ||
13848 | + */ | ||
13849 | + acx_sem_unlock(adev); | ||
13850 | + | ||
13851 | + log(L_INIT, "closed device\n"); | ||
13852 | + FN_EXIT0; | ||
13853 | + return OK; | ||
13854 | +} | ||
13855 | + | ||
13856 | + | ||
13857 | +/*********************************************************************** | ||
13858 | +** acxmem_i_tx_timeout | ||
13859 | +** | ||
13860 | +** Called from network core. Must not sleep! | ||
13861 | +*/ | ||
13862 | +static void | ||
13863 | +acxmem_i_tx_timeout(struct net_device *ndev) | ||
13864 | +{ | ||
13865 | + acx_device_t *adev = ndev2adev(ndev); | ||
13866 | + unsigned long flags; | ||
13867 | + unsigned int tx_num_cleaned; | ||
13868 | + | ||
13869 | + FN_ENTER; | ||
13870 | + | ||
13871 | + acx_lock(adev, flags); | ||
13872 | + | ||
13873 | + /* clean processed tx descs, they may have been completely full */ | ||
13874 | + tx_num_cleaned = acxmem_l_clean_txdesc(adev); | ||
13875 | + | ||
13876 | + /* nothing cleaned, yet (almost) no free buffers available? | ||
13877 | + * --> clean all tx descs, no matter which status!! | ||
13878 | + * Note that I strongly suspect that doing emergency cleaning | ||
13879 | + * may confuse the firmware. This is a last ditch effort to get | ||
13880 | + * ANYTHING to work again... | ||
13881 | + * | ||
13882 | + * TODO: it's best to simply reset & reinit hw from scratch... | ||
13883 | + */ | ||
13884 | + if ((adev->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) { | ||
13885 | + printk("%s: FAILED to free any of the many full tx buffers. " | ||
13886 | + "Switching to emergency freeing. " | ||
13887 | + "Please report!\n", ndev->name); | ||
13888 | + acxmem_l_clean_txdesc_emergency(adev); | ||
13889 | + } | ||
13890 | + | ||
13891 | + if (acx_queue_stopped(ndev) && (ACX_STATUS_4_ASSOCIATED == adev->status)) | ||
13892 | + acx_wake_queue(ndev, "after tx timeout"); | ||
13893 | + | ||
13894 | + /* stall may have happened due to radio drift, so recalib radio */ | ||
13895 | + acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); | ||
13896 | + | ||
13897 | + /* do unimportant work last */ | ||
13898 | + printk("%s: tx timeout!\n", ndev->name); | ||
13899 | + adev->stats.tx_errors++; | ||
13900 | + | ||
13901 | + acx_unlock(adev, flags); | ||
13902 | + | ||
13903 | + FN_EXIT0; | ||
13904 | +} | ||
13905 | + | ||
13906 | + | ||
13907 | +/*********************************************************************** | ||
13908 | +** acxmem_i_set_multicast_list | ||
13909 | +** FIXME: most likely needs refinement | ||
13910 | +*/ | ||
13911 | +static void | ||
13912 | +acxmem_i_set_multicast_list(struct net_device *ndev) | ||
13913 | +{ | ||
13914 | + acx_device_t *adev = ndev2adev(ndev); | ||
13915 | + unsigned long flags; | ||
13916 | + | ||
13917 | + FN_ENTER; | ||
13918 | + | ||
13919 | + acx_lock(adev, flags); | ||
13920 | + | ||
13921 | + /* firmwares don't have allmulti capability, | ||
13922 | + * so just use promiscuous mode instead in this case. */ | ||
13923 | + if (ndev->flags & (IFF_PROMISC|IFF_ALLMULTI)) { | ||
13924 | + SET_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); | ||
13925 | + CLEAR_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); | ||
13926 | + SET_BIT(adev->set_mask, SET_RXCONFIG); | ||
13927 | + /* let kernel know in case *we* needed to set promiscuous */ | ||
13928 | + ndev->flags |= (IFF_PROMISC|IFF_ALLMULTI); | ||
13929 | + } else { | ||
13930 | + CLEAR_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); | ||
13931 | + SET_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); | ||
13932 | + SET_BIT(adev->set_mask, SET_RXCONFIG); | ||
13933 | + ndev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI); | ||
13934 | + } | ||
13935 | + | ||
13936 | + /* cannot update card settings directly here, atomic context */ | ||
13937 | + acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG); | ||
13938 | + | ||
13939 | + acx_unlock(adev, flags); | ||
13940 | + | ||
13941 | + FN_EXIT0; | ||
13942 | +} | ||
13943 | + | ||
13944 | + | ||
13945 | +/*************************************************************** | ||
13946 | +** acxmem_l_process_rxdesc | ||
13947 | +** | ||
13948 | +** Called directly and only from the IRQ handler | ||
13949 | +*/ | ||
13950 | + | ||
13951 | +#if !ACX_DEBUG | ||
13952 | +static inline void log_rxbuffer(const acx_device_t *adev) {} | ||
13953 | +#else | ||
13954 | +static void | ||
13955 | +log_rxbuffer(const acx_device_t *adev) | ||
13956 | +{ | ||
13957 | + register const struct rxhostdesc *rxhostdesc; | ||
13958 | + int i; | ||
13959 | + /* no FN_ENTER here, we don't want that */ | ||
13960 | + | ||
13961 | + rxhostdesc = adev->rxhostdesc_start; | ||
13962 | + if (unlikely(!rxhostdesc)) return; | ||
13963 | + for (i = 0; i < RX_CNT; i++) { | ||
13964 | + if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) | ||
13965 | + && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) | ||
13966 | + printk("rx: buf %d full\n", i); | ||
13967 | + rxhostdesc++; | ||
13968 | + } | ||
13969 | +} | ||
13970 | +#endif | ||
13971 | + | ||
13972 | +static void | ||
13973 | +acxmem_l_process_rxdesc(acx_device_t *adev) | ||
13974 | +{ | ||
13975 | + register rxhostdesc_t *hostdesc; | ||
13976 | + register rxdesc_t *rxdesc; | ||
13977 | + unsigned count, tail; | ||
13978 | + u32 addr; | ||
13979 | + u8 Ctl_8; | ||
13980 | + | ||
13981 | + FN_ENTER; | ||
13982 | + | ||
13983 | + if (unlikely(acx_debug & L_BUFR)) | ||
13984 | + log_rxbuffer(adev); | ||
13985 | + | ||
13986 | + /* First, have a loop to determine the first descriptor that's | ||
13987 | + * full, just in case there's a mismatch between our current | ||
13988 | + * rx_tail and the full descriptor we're supposed to handle. */ | ||
13989 | + tail = adev->rx_tail; | ||
13990 | + count = RX_CNT; | ||
13991 | + while (1) { | ||
13992 | + hostdesc = &adev->rxhostdesc_start[tail]; | ||
13993 | + rxdesc = &adev->rxdesc_start[tail]; | ||
13994 | + /* advance tail regardless of outcome of the below test */ | ||
13995 | + tail = (tail + 1) % RX_CNT; | ||
13996 | + | ||
13997 | + /* | ||
13998 | + * Unlike the PCI interface, where the ACX can write directly to | ||
13999 | + * the host descriptors, on the slave memory interface we have to | ||
14000 | + * pull these. All we really need to do is check the Ctl_8 field | ||
14001 | + * in the rx descriptor on the ACX, which should be 0x11000000 if | ||
14002 | + * we should process it. | ||
14003 | + */ | ||
14004 | + Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); | ||
14005 | + if ((Ctl_8 & DESC_CTL_HOSTOWN) && | ||
14006 | + (Ctl_8 & DESC_CTL_ACXDONE)) | ||
14007 | + break; /* found it! */ | ||
14008 | + | ||
14009 | + if (unlikely(!--count)) /* hmm, no luck: all descs empty, bail out */ | ||
14010 | + goto end; | ||
14011 | + } | ||
14012 | + | ||
14013 | + /* now process descriptors, starting with the first we figured out */ | ||
14014 | + while (1) { | ||
14015 | + log(L_BUFR, "rx: tail=%u Ctl_8=%02X\n", tail, Ctl_8); | ||
14016 | + /* | ||
14017 | + * If the ACX has CTL_RECLAIM set on this descriptor there | ||
14018 | + * is no buffer associated; it just wants us to tell it to | ||
14019 | + * reclaim the memory. | ||
14020 | + */ | ||
14021 | + if (!(Ctl_8 & DESC_CTL_RECLAIM)) { | ||
14022 | + | ||
14023 | + /* | ||
14024 | + * slave interface - pull data now | ||
14025 | + */ | ||
14026 | + hostdesc->length = read_slavemem16 (adev, (u32) &(rxdesc->total_length)); | ||
14027 | + | ||
14028 | + /* | ||
14029 | + * hostdesc->data is an rxbuffer_t, which includes header information, | ||
14030 | + * but the length in the data packet doesn't. The header information | ||
14031 | + * takes up an additional 12 bytes, so add that to the length we copy. | ||
14032 | + */ | ||
14033 | + addr = read_slavemem32 (adev, (u32) &(rxdesc->ACXMemPtr)); | ||
14034 | + if (addr) { | ||
14035 | + /* | ||
14036 | + * How can &(rxdesc->ACXMemPtr) above ever be zero? Looks like we | ||
14037 | + * get that now and then - try to trap it for debug. | ||
14038 | + */ | ||
14039 | + if (addr & 0xffff0000) { | ||
14040 | + printk("rxdesc 0x%08x\n", (u32) rxdesc); | ||
14041 | + dump_acxmem (adev, 0, 0x10000); | ||
14042 | + panic ("Bad access!"); | ||
14043 | + } | ||
14044 | + chaincopy_from_slavemem (adev, (u8 *) hostdesc->data, addr, | ||
14045 | + hostdesc->length + | ||
14046 | + (u32) &((rxbuffer_t *)0)->hdr_a3); | ||
14047 | + acx_l_process_rxbuf(adev, hostdesc->data); | ||
14048 | + } | ||
14049 | + } | ||
14050 | + else { | ||
14051 | + printk ("rx reclaim only!\n"); | ||
14052 | + } | ||
14053 | + | ||
14054 | + hostdesc->Status = 0; | ||
14055 | + | ||
14056 | + /* | ||
14057 | + * Let the ACX know we're done. | ||
14058 | + */ | ||
14059 | + CLEAR_BIT (Ctl_8, DESC_CTL_HOSTOWN); | ||
14060 | + SET_BIT (Ctl_8, DESC_CTL_HOSTDONE); | ||
14061 | + SET_BIT (Ctl_8, DESC_CTL_RECLAIM); | ||
14062 | + write_slavemem8 (adev, (u32) &rxdesc->Ctl_8, Ctl_8); | ||
14063 | + | ||
14064 | + /* | ||
14065 | + * Now tell the ACX we've finished with the receive buffer so | ||
14066 | + * it can finish the reclaim. | ||
14067 | + */ | ||
14068 | + write_reg16 (adev, IO_ACX_INT_TRIG, INT_TRIG_RXPRC); | ||
14069 | + | ||
14070 | + /* ok, descriptor is handled, now check the next descriptor */ | ||
14071 | + hostdesc = &adev->rxhostdesc_start[tail]; | ||
14072 | + rxdesc = &adev->rxdesc_start[tail]; | ||
14073 | + | ||
14074 | + Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); | ||
14075 | + | ||
14076 | + /* if next descriptor is empty, then bail out */ | ||
14077 | + if (!(Ctl_8 & DESC_CTL_HOSTOWN) || !(Ctl_8 & DESC_CTL_ACXDONE)) | ||
14078 | + break; | ||
14079 | + | ||
14080 | + tail = (tail + 1) % RX_CNT; | ||
14081 | + } | ||
14082 | +end: | ||
14083 | + adev->rx_tail = tail; | ||
14084 | + FN_EXIT0; | ||
14085 | +} | ||
14086 | + | ||
14087 | + | ||
14088 | +/*********************************************************************** | ||
14089 | +** acxmem_i_interrupt | ||
14090 | +** | ||
14091 | +** IRQ handler (atomic context, must not sleep, blah, blah) | ||
14092 | +*/ | ||
14093 | + | ||
14094 | +/* scan is complete. all frames now on the receive queue are valid */ | ||
14095 | +#define INFO_SCAN_COMPLETE 0x0001 | ||
14096 | +#define INFO_WEP_KEY_NOT_FOUND 0x0002 | ||
14097 | +/* hw has been reset as the result of a watchdog timer timeout */ | ||
14098 | +#define INFO_WATCH_DOG_RESET 0x0003 | ||
14099 | +/* failed to send out NULL frame from PS mode notification to AP */ | ||
14100 | +/* recommended action: try entering 802.11 PS mode again */ | ||
14101 | +#define INFO_PS_FAIL 0x0004 | ||
14102 | +/* encryption/decryption process on a packet failed */ | ||
14103 | +#define INFO_IV_ICV_FAILURE 0x0005 | ||
14104 | + | ||
14105 | +/* Info mailbox format: | ||
14106 | +2 bytes: type | ||
14107 | +2 bytes: status | ||
14108 | +more bytes may follow | ||
14109 | + rumors say about status: | ||
14110 | + 0x0000 info available (set by hw) | ||
14111 | + 0x0001 information received (must be set by host) | ||
14112 | + 0x1000 info available, mailbox overflowed (messages lost) (set by hw) | ||
14113 | + but in practice we've seen: | ||
14114 | + 0x9000 when we did not set status to 0x0001 on prev message | ||
14115 | + 0x1001 when we did set it | ||
14116 | + 0x0000 was never seen | ||
14117 | + conclusion: this is really a bitfield: | ||
14118 | + 0x1000 is 'info available' bit | ||
14119 | + 'mailbox overflowed' bit is 0x8000, not 0x1000 | ||
14120 | + value of 0x0000 probably means that there are no messages at all | ||
14121 | + P.S. I dunno how in hell hw is supposed to notice that messages are lost - | ||
14122 | + it does NOT clear bit 0x0001, and this bit will probably stay forever set | ||
14123 | + after we set it once. Let's hope this will be fixed in firmware someday | ||
14124 | +*/ | ||
14125 | + | ||
14126 | +static void | ||
14127 | +handle_info_irq(acx_device_t *adev) | ||
14128 | +{ | ||
14129 | +#if ACX_DEBUG | ||
14130 | + static const char * const info_type_msg[] = { | ||
14131 | + "(unknown)", | ||
14132 | + "scan complete", | ||
14133 | + "WEP key not found", | ||
14134 | + "internal watchdog reset was done", | ||
14135 | + "failed to send powersave (NULL frame) notification to AP", | ||
14136 | + "encrypt/decrypt on a packet has failed", | ||
14137 | + "TKIP tx keys disabled", | ||
14138 | + "TKIP rx keys disabled", | ||
14139 | + "TKIP rx: key ID not found", | ||
14140 | + "???", | ||
14141 | + "???", | ||
14142 | + "???", | ||
14143 | + "???", | ||
14144 | + "???", | ||
14145 | + "???", | ||
14146 | + "???", | ||
14147 | + "TKIP IV value exceeds thresh" | ||
14148 | + }; | ||
14149 | +#endif | ||
14150 | + u32 info_type, info_status; | ||
14151 | + | ||
14152 | + info_type = read_slavemem32 (adev, (u32) adev->info_area); | ||
14153 | + | ||
14154 | + info_status = (info_type >> 16); | ||
14155 | + info_type = (u16)info_type; | ||
14156 | + | ||
14157 | + /* inform fw that we have read this info message */ | ||
14158 | + write_slavemem32(adev, (u32) adev->info_area, info_type | 0x00010000); | ||
14159 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_INFOACK); | ||
14160 | + write_flush(adev); | ||
14161 | + | ||
14162 | + log(L_CTL, "info_type:%04X info_status:%04X\n", | ||
14163 | + info_type, info_status); | ||
14164 | + | ||
14165 | + log(L_IRQ, "got Info IRQ: status %04X type %04X: %s\n", | ||
14166 | + info_status, info_type, | ||
14167 | + info_type_msg[(info_type >= VEC_SIZE(info_type_msg)) ? | ||
14168 | + 0 : info_type] | ||
14169 | + ); | ||
14170 | +} | ||
14171 | + | ||
14172 | + | ||
14173 | +static void | ||
14174 | +log_unusual_irq(u16 irqtype) { | ||
14175 | + /* | ||
14176 | + if (!printk_ratelimit()) | ||
14177 | + return; | ||
14178 | + */ | ||
14179 | + | ||
14180 | + printk("acx: got"); | ||
14181 | + if (irqtype & HOST_INT_TX_XFER) { | ||
14182 | + printk(" Tx_Xfer"); | ||
14183 | + } | ||
14184 | + if (irqtype & HOST_INT_RX_COMPLETE) { | ||
14185 | + printk(" Rx_Complete"); | ||
14186 | + } | ||
14187 | + if (irqtype & HOST_INT_DTIM) { | ||
14188 | + printk(" DTIM"); | ||
14189 | + } | ||
14190 | + if (irqtype & HOST_INT_BEACON) { | ||
14191 | + printk(" Beacon"); | ||
14192 | + } | ||
14193 | + if (irqtype & HOST_INT_TIMER) { | ||
14194 | + log(L_IRQ, " Timer"); | ||
14195 | + } | ||
14196 | + if (irqtype & HOST_INT_KEY_NOT_FOUND) { | ||
14197 | + printk(" Key_Not_Found"); | ||
14198 | + } | ||
14199 | + if (irqtype & HOST_INT_IV_ICV_FAILURE) { | ||
14200 | + printk(" IV_ICV_Failure (crypto)"); | ||
14201 | + } | ||
14202 | + /* HOST_INT_CMD_COMPLETE */ | ||
14203 | + /* HOST_INT_INFO */ | ||
14204 | + if (irqtype & HOST_INT_OVERFLOW) { | ||
14205 | + printk(" Overflow"); | ||
14206 | + } | ||
14207 | + if (irqtype & HOST_INT_PROCESS_ERROR) { | ||
14208 | + printk(" Process_Error"); | ||
14209 | + } | ||
14210 | + /* HOST_INT_SCAN_COMPLETE */ | ||
14211 | + if (irqtype & HOST_INT_FCS_THRESHOLD) { | ||
14212 | + printk(" FCS_Threshold"); | ||
14213 | + } | ||
14214 | + if (irqtype & HOST_INT_UNKNOWN) { | ||
14215 | + printk(" Unknown"); | ||
14216 | + } | ||
14217 | + printk(" IRQ(s)\n"); | ||
14218 | +} | ||
14219 | + | ||
14220 | + | ||
14221 | +static void | ||
14222 | +update_link_quality_led(acx_device_t *adev) | ||
14223 | +{ | ||
14224 | + int qual; | ||
14225 | + | ||
14226 | + qual = acx_signal_determine_quality(adev->wstats.qual.level, adev->wstats.qual.noise); | ||
14227 | + if (qual > adev->brange_max_quality) | ||
14228 | + qual = adev->brange_max_quality; | ||
14229 | + | ||
14230 | + if (time_after(jiffies, adev->brange_time_last_state_change + | ||
14231 | + (HZ/2 - HZ/2 * (unsigned long)qual / adev->brange_max_quality ) )) { | ||
14232 | + acxmem_l_power_led(adev, (adev->brange_last_state == 0)); | ||
14233 | + adev->brange_last_state ^= 1; /* toggle */ | ||
14234 | + adev->brange_time_last_state_change = jiffies; | ||
14235 | + } | ||
14236 | +} | ||
14237 | + | ||
14238 | + | ||
14239 | +#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */ | ||
14240 | + | ||
14241 | +static irqreturn_t | ||
14242 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
14243 | +acxmem_i_interrupt(int irq, void *dev_id) | ||
14244 | +#else | ||
14245 | +acxmwm_i_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
14246 | +#endif | ||
14247 | +{ | ||
14248 | + acx_device_t *adev; | ||
14249 | + unsigned long flags; | ||
14250 | + unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY; | ||
14251 | + register u16 irqtype; | ||
14252 | + u16 unmasked; | ||
14253 | + | ||
14254 | + adev = ndev2adev((struct net_device*)dev_id); | ||
14255 | + | ||
14256 | + /* LOCKING: can just spin_lock() since IRQs are disabled anyway. | ||
14257 | + * I am paranoid */ | ||
14258 | + acx_lock(adev, flags); | ||
14259 | + | ||
14260 | + unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); | ||
14261 | + if (unlikely(0xffff == unmasked)) { | ||
14262 | + /* 0xffff value hints at missing hardware, | ||
14263 | + * so don't do anything. | ||
14264 | + * Not very clean, but other drivers do the same... */ | ||
14265 | + log(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n"); | ||
14266 | + goto none; | ||
14267 | + } | ||
14268 | + | ||
14269 | + /* We will check only "interesting" IRQ types */ | ||
14270 | + irqtype = unmasked & ~adev->irq_mask; | ||
14271 | + if (!irqtype) { | ||
14272 | + /* We are on a shared IRQ line and it wasn't our IRQ */ | ||
14273 | + log(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n", | ||
14274 | + unmasked, adev->irq_mask); | ||
14275 | + goto none; | ||
14276 | + } | ||
14277 | + | ||
14278 | + /* Done here because IRQ_NONEs taking three lines of log | ||
14279 | + ** drive me crazy */ | ||
14280 | + FN_ENTER; | ||
14281 | + | ||
14282 | +#define IRQ_ITERATE 1 | ||
14283 | +#if IRQ_ITERATE | ||
14284 | +if (jiffies != adev->irq_last_jiffies) { | ||
14285 | + adev->irq_loops_this_jiffy = 0; | ||
14286 | + adev->irq_last_jiffies = jiffies; | ||
14287 | +} | ||
14288 | + | ||
14289 | +/* safety condition; we'll normally abort loop below | ||
14290 | + * in case no IRQ type occurred */ | ||
14291 | +while (likely(--irqcount)) { | ||
14292 | +#endif | ||
14293 | + /* ACK all IRQs ASAP */ | ||
14294 | + write_reg16(adev, IO_ACX_IRQ_ACK, 0xffff); | ||
14295 | + | ||
14296 | + log(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n", | ||
14297 | + unmasked, adev->irq_mask, irqtype); | ||
14298 | + | ||
14299 | + /* Handle most important IRQ types first */ | ||
14300 | + if (irqtype & HOST_INT_RX_DATA) { | ||
14301 | + log(L_IRQ, "got Rx_Data IRQ\n"); | ||
14302 | + acxmem_l_process_rxdesc(adev); | ||
14303 | + } | ||
14304 | + if (irqtype & HOST_INT_TX_COMPLETE) { | ||
14305 | + log(L_IRQ, "got Tx_Complete IRQ\n"); | ||
14306 | + /* don't clean up on each Tx complete, wait a bit | ||
14307 | + * unless we're going towards full, in which case | ||
14308 | + * we do it immediately, too (otherwise we might lockup | ||
14309 | + * with a full Tx buffer if we go into | ||
14310 | + * acxmem_l_clean_txdesc() at a time when we won't wakeup | ||
14311 | + * the net queue in there for some reason...) */ | ||
14312 | + if (adev->tx_free <= TX_START_CLEAN) { | ||
14313 | +#if TX_CLEANUP_IN_SOFTIRQ | ||
14314 | + acx_schedule_task(adev, ACX_AFTER_IRQ_TX_CLEANUP); | ||
14315 | +#else | ||
14316 | + acxmem_l_clean_txdesc(adev); | ||
14317 | +#endif | ||
14318 | + } | ||
14319 | + } | ||
14320 | + | ||
14321 | + /* Less frequent ones */ | ||
14322 | + if (irqtype & (0 | ||
14323 | + | HOST_INT_CMD_COMPLETE | ||
14324 | + | HOST_INT_INFO | ||
14325 | + | HOST_INT_SCAN_COMPLETE | ||
14326 | + )) { | ||
14327 | + if (irqtype & HOST_INT_CMD_COMPLETE) { | ||
14328 | + log(L_IRQ, "got Command_Complete IRQ\n"); | ||
14329 | + /* save the state for the running issue_cmd() */ | ||
14330 | + SET_BIT(adev->irq_status, HOST_INT_CMD_COMPLETE); | ||
14331 | + } | ||
14332 | + if (irqtype & HOST_INT_INFO) { | ||
14333 | + handle_info_irq(adev); | ||
14334 | + } | ||
14335 | + if (irqtype & HOST_INT_SCAN_COMPLETE) { | ||
14336 | + log(L_IRQ, "got Scan_Complete IRQ\n"); | ||
14337 | + /* need to do that in process context */ | ||
14338 | + acx_schedule_task(adev, ACX_AFTER_IRQ_COMPLETE_SCAN); | ||
14339 | + /* remember that fw is not scanning anymore */ | ||
14340 | + SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE); | ||
14341 | + } | ||
14342 | + } | ||
14343 | + | ||
14344 | + /* These we just log, but either they happen rarely | ||
14345 | + * or we keep them masked out */ | ||
14346 | + if (irqtype & (0 | ||
14347 | + /* | HOST_INT_RX_DATA */ | ||
14348 | + /* | HOST_INT_TX_COMPLETE */ | ||
14349 | + | HOST_INT_TX_XFER | ||
14350 | + | HOST_INT_RX_COMPLETE | ||
14351 | + | HOST_INT_DTIM | ||
14352 | + | HOST_INT_BEACON | ||
14353 | + | HOST_INT_TIMER | ||
14354 | + | HOST_INT_KEY_NOT_FOUND | ||
14355 | + | HOST_INT_IV_ICV_FAILURE | ||
14356 | + /* | HOST_INT_CMD_COMPLETE */ | ||
14357 | + /* | HOST_INT_INFO */ | ||
14358 | + | HOST_INT_OVERFLOW | ||
14359 | + | HOST_INT_PROCESS_ERROR | ||
14360 | + /* | HOST_INT_SCAN_COMPLETE */ | ||
14361 | + | HOST_INT_FCS_THRESHOLD | ||
14362 | + | HOST_INT_UNKNOWN | ||
14363 | + )) { | ||
14364 | + log_unusual_irq(irqtype); | ||
14365 | + } | ||
14366 | + | ||
14367 | +#if IRQ_ITERATE | ||
14368 | + unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); | ||
14369 | + irqtype = unmasked & ~adev->irq_mask; | ||
14370 | + /* Bail out if no new IRQ bits or if all are masked out */ | ||
14371 | + if (!irqtype) | ||
14372 | + break; | ||
14373 | + | ||
14374 | + if (unlikely(++adev->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) { | ||
14375 | + printk(KERN_ERR "acx: too many interrupts per jiffy!\n"); | ||
14376 | + /* Looks like card floods us with IRQs! Try to stop that */ | ||
14377 | + write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); | ||
14378 | + /* This will short-circuit all future attempts to handle IRQ. | ||
14379 | + * We cant do much more... */ | ||
14380 | + adev->irq_mask = 0; | ||
14381 | + break; | ||
14382 | + } | ||
14383 | +} | ||
14384 | +#endif | ||
14385 | + /* Routine to perform blink with range */ | ||
14386 | + if (unlikely(adev->led_power == 2)) | ||
14387 | + update_link_quality_led(adev); | ||
14388 | + | ||
14389 | +/* handled: */ | ||
14390 | + /* write_flush(adev); - not needed, last op was read anyway */ | ||
14391 | + acx_unlock(adev, flags); | ||
14392 | + FN_EXIT0; | ||
14393 | + return IRQ_HANDLED; | ||
14394 | + | ||
14395 | +none: | ||
14396 | + acx_unlock(adev, flags); | ||
14397 | + return IRQ_NONE; | ||
14398 | +} | ||
14399 | + | ||
14400 | + | ||
14401 | +/*********************************************************************** | ||
14402 | +** acxmem_l_power_led | ||
14403 | +*/ | ||
14404 | +void | ||
14405 | +acxmem_l_power_led(acx_device_t *adev, int enable) | ||
14406 | +{ | ||
14407 | + u16 gpio_pled = IS_ACX111(adev) ? 0x0040 : 0x0800; | ||
14408 | + | ||
14409 | + /* A hack. Not moving message rate limiting to adev->xxx | ||
14410 | + * (it's only a debug message after all) */ | ||
14411 | + static int rate_limit = 0; | ||
14412 | + | ||
14413 | + if (rate_limit++ < 3) | ||
14414 | + log(L_IOCTL, "Please report in case toggling the power " | ||
14415 | + "LED doesn't work for your card!\n"); | ||
14416 | + if (enable) | ||
14417 | + write_reg16(adev, IO_ACX_GPIO_OUT, | ||
14418 | + read_reg16(adev, IO_ACX_GPIO_OUT) & ~gpio_pled); | ||
14419 | + else | ||
14420 | + write_reg16(adev, IO_ACX_GPIO_OUT, | ||
14421 | + read_reg16(adev, IO_ACX_GPIO_OUT) | gpio_pled); | ||
14422 | +} | ||
14423 | + | ||
14424 | + | ||
14425 | +/*********************************************************************** | ||
14426 | +** Ioctls | ||
14427 | +*/ | ||
14428 | + | ||
14429 | +/*********************************************************************** | ||
14430 | +*/ | ||
14431 | +int | ||
14432 | +acx111pci_ioctl_info( | ||
14433 | + struct net_device *ndev, | ||
14434 | + struct iw_request_info *info, | ||
14435 | + struct iw_param *vwrq, | ||
14436 | + char *extra) | ||
14437 | +{ | ||
14438 | +#if ACX_DEBUG > 1 | ||
14439 | + acx_device_t *adev = ndev2adev(ndev); | ||
14440 | + rxdesc_t *rxdesc; | ||
14441 | + txdesc_t *txdesc; | ||
14442 | + rxhostdesc_t *rxhostdesc; | ||
14443 | + txhostdesc_t *txhostdesc; | ||
14444 | + struct acx111_ie_memoryconfig memconf; | ||
14445 | + struct acx111_ie_queueconfig queueconf; | ||
14446 | + unsigned long flags; | ||
14447 | + int i; | ||
14448 | + char memmap[0x34]; | ||
14449 | + char rxconfig[0x8]; | ||
14450 | + char fcserror[0x8]; | ||
14451 | + char ratefallback[0x5]; | ||
14452 | + | ||
14453 | + if ( !(acx_debug & (L_IOCTL|L_DEBUG)) ) | ||
14454 | + return OK; | ||
14455 | + /* using printk() since we checked debug flag already */ | ||
14456 | + | ||
14457 | + acx_sem_lock(adev); | ||
14458 | + | ||
14459 | + if (!IS_ACX111(adev)) { | ||
14460 | + printk("acx111-specific function called " | ||
14461 | + "with non-acx111 chip, aborting\n"); | ||
14462 | + goto end_ok; | ||
14463 | + } | ||
14464 | + | ||
14465 | + /* get Acx111 Memory Configuration */ | ||
14466 | + memset(&memconf, 0, sizeof(memconf)); | ||
14467 | + /* BTW, fails with 12 (Write only) error code. | ||
14468 | + ** Retained for easy testing of issue_cmd error handling :) */ | ||
14469 | + printk ("Interrogating queue config\n"); | ||
14470 | + acx_s_interrogate(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG); | ||
14471 | + printk ("done with queue config\n"); | ||
14472 | + | ||
14473 | + /* get Acx111 Queue Configuration */ | ||
14474 | + memset(&queueconf, 0, sizeof(queueconf)); | ||
14475 | + printk ("Interrogating mem config options\n"); | ||
14476 | + acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS); | ||
14477 | + printk ("done with mem config options\n"); | ||
14478 | + | ||
14479 | + /* get Acx111 Memory Map */ | ||
14480 | + memset(memmap, 0, sizeof(memmap)); | ||
14481 | + printk ("Interrogating mem map\n"); | ||
14482 | + acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP); | ||
14483 | + printk ("done with mem map\n"); | ||
14484 | + | ||
14485 | + /* get Acx111 Rx Config */ | ||
14486 | + memset(rxconfig, 0, sizeof(rxconfig)); | ||
14487 | + printk ("Interrogating rxconfig\n"); | ||
14488 | + acx_s_interrogate(adev, &rxconfig, ACX1xx_IE_RXCONFIG); | ||
14489 | + printk ("done with queue rxconfig\n"); | ||
14490 | + | ||
14491 | + /* get Acx111 fcs error count */ | ||
14492 | + memset(fcserror, 0, sizeof(fcserror)); | ||
14493 | + printk ("Interrogating fcs err count\n"); | ||
14494 | + acx_s_interrogate(adev, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT); | ||
14495 | + printk ("done with err count\n"); | ||
14496 | + | ||
14497 | + /* get Acx111 rate fallback */ | ||
14498 | + memset(ratefallback, 0, sizeof(ratefallback)); | ||
14499 | + printk ("Interrogating rate fallback\n"); | ||
14500 | + acx_s_interrogate(adev, &ratefallback, ACX1xx_IE_RATE_FALLBACK); | ||
14501 | + printk ("done with rate fallback\n"); | ||
14502 | + | ||
14503 | + /* force occurrence of a beacon interrupt */ | ||
14504 | + /* TODO: comment why is this necessary */ | ||
14505 | + write_reg16(adev, IO_ACX_HINT_TRIG, HOST_INT_BEACON); | ||
14506 | + | ||
14507 | + /* dump Acx111 Mem Configuration */ | ||
14508 | + printk("dump mem config:\n" | ||
14509 | + "data read: %d, struct size: %d\n" | ||
14510 | + "Number of stations: %1X\n" | ||
14511 | + "Memory block size: %1X\n" | ||
14512 | + "tx/rx memory block allocation: %1X\n" | ||
14513 | + "count rx: %X / tx: %X queues\n" | ||
14514 | + "options %1X\n" | ||
14515 | + "fragmentation %1X\n" | ||
14516 | + "Rx Queue 1 Count Descriptors: %X\n" | ||
14517 | + "Rx Queue 1 Host Memory Start: %X\n" | ||
14518 | + "Tx Queue 1 Count Descriptors: %X\n" | ||
14519 | + "Tx Queue 1 Attributes: %X\n", | ||
14520 | + memconf.len, (int) sizeof(memconf), | ||
14521 | + memconf.no_of_stations, | ||
14522 | + memconf.memory_block_size, | ||
14523 | + memconf.tx_rx_memory_block_allocation, | ||
14524 | + memconf.count_rx_queues, memconf.count_tx_queues, | ||
14525 | + memconf.options, | ||
14526 | + memconf.fragmentation, | ||
14527 | + memconf.rx_queue1_count_descs, | ||
14528 | + acx2cpu(memconf.rx_queue1_host_rx_start), | ||
14529 | + memconf.tx_queue1_count_descs, | ||
14530 | + memconf.tx_queue1_attributes); | ||
14531 | + | ||
14532 | + /* dump Acx111 Queue Configuration */ | ||
14533 | + printk("dump queue head:\n" | ||
14534 | + "data read: %d, struct size: %d\n" | ||
14535 | + "tx_memory_block_address (from card): %X\n" | ||
14536 | + "rx_memory_block_address (from card): %X\n" | ||
14537 | + "rx1_queue address (from card): %X\n" | ||
14538 | + "tx1_queue address (from card): %X\n" | ||
14539 | + "tx1_queue attributes (from card): %X\n", | ||
14540 | + queueconf.len, (int) sizeof(queueconf), | ||
14541 | + queueconf.tx_memory_block_address, | ||
14542 | + queueconf.rx_memory_block_address, | ||
14543 | + queueconf.rx1_queue_address, | ||
14544 | + queueconf.tx1_queue_address, | ||
14545 | + queueconf.tx1_attributes); | ||
14546 | + | ||
14547 | + /* dump Acx111 Mem Map */ | ||
14548 | + printk("dump mem map:\n" | ||
14549 | + "data read: %d, struct size: %d\n" | ||
14550 | + "Code start: %X\n" | ||
14551 | + "Code end: %X\n" | ||
14552 | + "WEP default key start: %X\n" | ||
14553 | + "WEP default key end: %X\n" | ||
14554 | + "STA table start: %X\n" | ||
14555 | + "STA table end: %X\n" | ||
14556 | + "Packet template start: %X\n" | ||
14557 | + "Packet template end: %X\n" | ||
14558 | + "Queue memory start: %X\n" | ||
14559 | + "Queue memory end: %X\n" | ||
14560 | + "Packet memory pool start: %X\n" | ||
14561 | + "Packet memory pool end: %X\n" | ||
14562 | + "iobase: %p\n" | ||
14563 | + "iobase2: %p\n", | ||
14564 | + *((u16 *)&memmap[0x02]), (int) sizeof(memmap), | ||
14565 | + *((u32 *)&memmap[0x04]), | ||
14566 | + *((u32 *)&memmap[0x08]), | ||
14567 | + *((u32 *)&memmap[0x0C]), | ||
14568 | + *((u32 *)&memmap[0x10]), | ||
14569 | + *((u32 *)&memmap[0x14]), | ||
14570 | + *((u32 *)&memmap[0x18]), | ||
14571 | + *((u32 *)&memmap[0x1C]), | ||
14572 | + *((u32 *)&memmap[0x20]), | ||
14573 | + *((u32 *)&memmap[0x24]), | ||
14574 | + *((u32 *)&memmap[0x28]), | ||
14575 | + *((u32 *)&memmap[0x2C]), | ||
14576 | + *((u32 *)&memmap[0x30]), | ||
14577 | + adev->iobase, | ||
14578 | + adev->iobase2); | ||
14579 | + | ||
14580 | + /* dump Acx111 Rx Config */ | ||
14581 | + printk("dump rx config:\n" | ||
14582 | + "data read: %d, struct size: %d\n" | ||
14583 | + "rx config: %X\n" | ||
14584 | + "rx filter config: %X\n", | ||
14585 | + *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig), | ||
14586 | + *((u16 *)&rxconfig[0x04]), | ||
14587 | + *((u16 *)&rxconfig[0x06])); | ||
14588 | + | ||
14589 | + /* dump Acx111 fcs error */ | ||
14590 | + printk("dump fcserror:\n" | ||
14591 | + "data read: %d, struct size: %d\n" | ||
14592 | + "fcserrors: %X\n", | ||
14593 | + *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror), | ||
14594 | + *((u32 *)&fcserror[0x04])); | ||
14595 | + | ||
14596 | + /* dump Acx111 rate fallback */ | ||
14597 | + printk("dump rate fallback:\n" | ||
14598 | + "data read: %d, struct size: %d\n" | ||
14599 | + "ratefallback: %X\n", | ||
14600 | + *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback), | ||
14601 | + *((u8 *)&ratefallback[0x04])); | ||
14602 | + | ||
14603 | + /* protect against IRQ */ | ||
14604 | + acx_lock(adev, flags); | ||
14605 | + | ||
14606 | + /* dump acx111 internal rx descriptor ring buffer */ | ||
14607 | + rxdesc = adev->rxdesc_start; | ||
14608 | + | ||
14609 | + /* loop over complete receive pool */ | ||
14610 | + if (rxdesc) for (i = 0; i < RX_CNT; i++) { | ||
14611 | + printk("\ndump internal rxdesc %d:\n" | ||
14612 | + "mem pos %p\n" | ||
14613 | + "next 0x%X\n" | ||
14614 | + "acx mem pointer (dynamic) 0x%X\n" | ||
14615 | + "CTL (dynamic) 0x%X\n" | ||
14616 | + "Rate (dynamic) 0x%X\n" | ||
14617 | + "RxStatus (dynamic) 0x%X\n" | ||
14618 | + "Mod/Pre (dynamic) 0x%X\n", | ||
14619 | + i, | ||
14620 | + rxdesc, | ||
14621 | + acx2cpu(rxdesc->pNextDesc), | ||
14622 | + acx2cpu(rxdesc->ACXMemPtr), | ||
14623 | + rxdesc->Ctl_8, | ||
14624 | + rxdesc->rate, | ||
14625 | + rxdesc->error, | ||
14626 | + rxdesc->SNR); | ||
14627 | + rxdesc++; | ||
14628 | + } | ||
14629 | + | ||
14630 | + /* dump host rx descriptor ring buffer */ | ||
14631 | + | ||
14632 | + rxhostdesc = adev->rxhostdesc_start; | ||
14633 | + | ||
14634 | + /* loop over complete receive pool */ | ||
14635 | + if (rxhostdesc) for (i = 0; i < RX_CNT; i++) { | ||
14636 | + printk("\ndump host rxdesc %d:\n" | ||
14637 | + "mem pos %p\n" | ||
14638 | + "buffer mem pos 0x%X\n" | ||
14639 | + "buffer mem offset 0x%X\n" | ||
14640 | + "CTL 0x%X\n" | ||
14641 | + "Length 0x%X\n" | ||
14642 | + "next 0x%X\n" | ||
14643 | + "Status 0x%X\n", | ||
14644 | + i, | ||
14645 | + rxhostdesc, | ||
14646 | + acx2cpu(rxhostdesc->data_phy), | ||
14647 | + rxhostdesc->data_offset, | ||
14648 | + le16_to_cpu(rxhostdesc->Ctl_16), | ||
14649 | + le16_to_cpu(rxhostdesc->length), | ||
14650 | + acx2cpu(rxhostdesc->desc_phy_next), | ||
14651 | + rxhostdesc->Status); | ||
14652 | + rxhostdesc++; | ||
14653 | + } | ||
14654 | + | ||
14655 | + /* dump acx111 internal tx descriptor ring buffer */ | ||
14656 | + txdesc = adev->txdesc_start; | ||
14657 | + | ||
14658 | + /* loop over complete transmit pool */ | ||
14659 | + if (txdesc) for (i = 0; i < TX_CNT; i++) { | ||
14660 | + printk("\ndump internal txdesc %d:\n" | ||
14661 | + "size 0x%X\n" | ||
14662 | + "mem pos %p\n" | ||
14663 | + "next 0x%X\n" | ||
14664 | + "acx mem pointer (dynamic) 0x%X\n" | ||
14665 | + "host mem pointer (dynamic) 0x%X\n" | ||
14666 | + "length (dynamic) 0x%X\n" | ||
14667 | + "CTL (dynamic) 0x%X\n" | ||
14668 | + "CTL2 (dynamic) 0x%X\n" | ||
14669 | + "Status (dynamic) 0x%X\n" | ||
14670 | + "Rate (dynamic) 0x%X\n", | ||
14671 | + i, | ||
14672 | + (int) sizeof(struct txdesc), | ||
14673 | + txdesc, | ||
14674 | + acx2cpu(txdesc->pNextDesc), | ||
14675 | + acx2cpu(txdesc->AcxMemPtr), | ||
14676 | + acx2cpu(txdesc->HostMemPtr), | ||
14677 | + le16_to_cpu(txdesc->total_length), | ||
14678 | + txdesc->Ctl_8, | ||
14679 | + txdesc->Ctl2_8, txdesc->error, | ||
14680 | + txdesc->u.r1.rate); | ||
14681 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
14682 | + } | ||
14683 | + | ||
14684 | + /* dump host tx descriptor ring buffer */ | ||
14685 | + | ||
14686 | + txhostdesc = adev->txhostdesc_start; | ||
14687 | + | ||
14688 | + /* loop over complete host send pool */ | ||
14689 | + if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) { | ||
14690 | + printk("\ndump host txdesc %d:\n" | ||
14691 | + "mem pos %p\n" | ||
14692 | + "buffer mem pos 0x%X\n" | ||
14693 | + "buffer mem offset 0x%X\n" | ||
14694 | + "CTL 0x%X\n" | ||
14695 | + "Length 0x%X\n" | ||
14696 | + "next 0x%X\n" | ||
14697 | + "Status 0x%X\n", | ||
14698 | + i, | ||
14699 | + txhostdesc, | ||
14700 | + acx2cpu(txhostdesc->data_phy), | ||
14701 | + txhostdesc->data_offset, | ||
14702 | + le16_to_cpu(txhostdesc->Ctl_16), | ||
14703 | + le16_to_cpu(txhostdesc->length), | ||
14704 | + acx2cpu(txhostdesc->desc_phy_next), | ||
14705 | + le32_to_cpu(txhostdesc->Status)); | ||
14706 | + txhostdesc++; | ||
14707 | + } | ||
14708 | + | ||
14709 | + /* write_reg16(adev, 0xb4, 0x4); */ | ||
14710 | + | ||
14711 | + acx_unlock(adev, flags); | ||
14712 | +end_ok: | ||
14713 | + | ||
14714 | + acx_sem_unlock(adev); | ||
14715 | +#endif /* ACX_DEBUG */ | ||
14716 | + return OK; | ||
14717 | +} | ||
14718 | + | ||
14719 | + | ||
14720 | +/*********************************************************************** | ||
14721 | +*/ | ||
14722 | +int | ||
14723 | +acx100mem_ioctl_set_phy_amp_bias( | ||
14724 | + struct net_device *ndev, | ||
14725 | + struct iw_request_info *info, | ||
14726 | + struct iw_param *vwrq, | ||
14727 | + char *extra) | ||
14728 | +{ | ||
14729 | + acx_device_t *adev = ndev2adev(ndev); | ||
14730 | + unsigned long flags; | ||
14731 | + u16 gpio_old; | ||
14732 | + | ||
14733 | + if (!IS_ACX100(adev)) { | ||
14734 | + /* WARNING!!! | ||
14735 | + * Removing this check *might* damage | ||
14736 | + * hardware, since we're tweaking GPIOs here after all!!! | ||
14737 | + * You've been warned... | ||
14738 | + * WARNING!!! */ | ||
14739 | + printk("acx: sorry, setting bias level for non-acx100 " | ||
14740 | + "is not supported yet\n"); | ||
14741 | + return OK; | ||
14742 | + } | ||
14743 | + | ||
14744 | + if (*extra > 7) { | ||
14745 | + printk("acx: invalid bias parameter, range is 0-7\n"); | ||
14746 | + return -EINVAL; | ||
14747 | + } | ||
14748 | + | ||
14749 | + acx_sem_lock(adev); | ||
14750 | + | ||
14751 | + /* Need to lock accesses to [IO_ACX_GPIO_OUT]: | ||
14752 | + * IRQ handler uses it to update LED */ | ||
14753 | + acx_lock(adev, flags); | ||
14754 | + gpio_old = read_reg16(adev, IO_ACX_GPIO_OUT); | ||
14755 | + write_reg16(adev, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8)); | ||
14756 | + acx_unlock(adev, flags); | ||
14757 | + | ||
14758 | + log(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old); | ||
14759 | + printk("%s: PHY power amplifier bias: old:%d, new:%d\n", | ||
14760 | + ndev->name, | ||
14761 | + (gpio_old & 0x0700) >> 8, (unsigned char)*extra); | ||
14762 | + | ||
14763 | + acx_sem_unlock(adev); | ||
14764 | + | ||
14765 | + return OK; | ||
14766 | +} | ||
14767 | + | ||
14768 | +/*************************************************************** | ||
14769 | +** acxmem_l_alloc_tx | ||
14770 | +** Actually returns a txdesc_t* ptr | ||
14771 | +** | ||
14772 | +** FIXME: in case of fragments, should allocate multiple descrs | ||
14773 | +** after figuring out how many we need and whether we still have | ||
14774 | +** sufficiently many. | ||
14775 | +*/ | ||
14776 | +tx_t* | ||
14777 | +acxmem_l_alloc_tx(acx_device_t *adev) | ||
14778 | +{ | ||
14779 | + struct txdesc *txdesc; | ||
14780 | + unsigned head; | ||
14781 | + u8 ctl8; | ||
14782 | + static int txattempts = 0; | ||
14783 | + | ||
14784 | + FN_ENTER; | ||
14785 | + | ||
14786 | + if (unlikely(!adev->tx_free)) { | ||
14787 | + printk("acx: BUG: no free txdesc left\n"); | ||
14788 | + /* | ||
14789 | + * Probably the ACX ignored a transmit attempt and now there's a packet | ||
14790 | + * sitting in the queue we think should be transmitting but the ACX doesn't | ||
14791 | + * know about. | ||
14792 | + * On the first pass, send the ACX a TxProc interrupt to try moving | ||
14793 | + * things along, and if that doesn't work (ie, we get called again) completely | ||
14794 | + * flush the transmit queue. | ||
14795 | + */ | ||
14796 | + if (txattempts < 10) { | ||
14797 | + txattempts++; | ||
14798 | + printk ("acx: trying to wake up ACX\n"); | ||
14799 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); | ||
14800 | + write_flush(adev); } | ||
14801 | + else { | ||
14802 | + txattempts = 0; | ||
14803 | + printk ("acx: flushing transmit queue.\n"); | ||
14804 | + acxmem_l_clean_txdesc_emergency (adev); | ||
14805 | + } | ||
14806 | + txdesc = NULL; | ||
14807 | + goto end; | ||
14808 | + } | ||
14809 | + | ||
14810 | + /* | ||
14811 | + * Make a quick check to see if there is transmit buffer space on | ||
14812 | + * the ACX. This can't guarantee there is enough space for the packet | ||
14813 | + * since we don't yet know how big it is, but it will prevent at least some | ||
14814 | + * annoyances. | ||
14815 | + */ | ||
14816 | + if (!adev->acx_txbuf_blocks_free) { | ||
14817 | + txdesc = NULL; | ||
14818 | + goto end; | ||
14819 | + } | ||
14820 | + | ||
14821 | + head = adev->tx_head; | ||
14822 | + /* | ||
14823 | + * txdesc points to ACX memory | ||
14824 | + */ | ||
14825 | + txdesc = get_txdesc(adev, head); | ||
14826 | + ctl8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); | ||
14827 | + | ||
14828 | + /* | ||
14829 | + * If we don't own the buffer (HOSTOWN) it is certainly not free; however, | ||
14830 | + * we may have previously thought we had enough memory to send | ||
14831 | + * a packet, allocated the buffer then gave up when we found not enough | ||
14832 | + * transmit buffer space on the ACX. In that case, HOSTOWN and | ||
14833 | + * ACXDONE will both be set. | ||
14834 | + */ | ||
14835 | + if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_HOSTOWN))) { | ||
14836 | + /* whoops, descr at current index is not free, so probably | ||
14837 | + * ring buffer already full */ | ||
14838 | + printk("acx: BUG: tx_head:%d Ctl8:0x%02X - failed to find " | ||
14839 | + "free txdesc\n", head, ctl8); | ||
14840 | + txdesc = NULL; | ||
14841 | + goto end; | ||
14842 | + } | ||
14843 | + | ||
14844 | + /* Needed in case txdesc won't be eventually submitted for tx */ | ||
14845 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_ACXDONE_HOSTOWN); | ||
14846 | + | ||
14847 | + adev->tx_free--; | ||
14848 | + log(L_BUFT, "tx: got desc %u, %u remain\n", | ||
14849 | + head, adev->tx_free); | ||
14850 | + /* Keep a few free descs between head and tail of tx ring. | ||
14851 | + ** It is not absolutely needed, just feels safer */ | ||
14852 | + if (adev->tx_free < TX_STOP_QUEUE) { | ||
14853 | + log(L_BUF, "stop queue (%u tx desc left)\n", | ||
14854 | + adev->tx_free); | ||
14855 | + acx_stop_queue(adev->ndev, NULL); | ||
14856 | + } | ||
14857 | + | ||
14858 | + /* returning current descriptor, so advance to next free one */ | ||
14859 | + adev->tx_head = (head + 1) % TX_CNT; | ||
14860 | +end: | ||
14861 | + FN_EXIT0; | ||
14862 | + | ||
14863 | + return (tx_t*)txdesc; | ||
14864 | +} | ||
14865 | + | ||
14866 | + | ||
14867 | +/*************************************************************** | ||
14868 | +** acxmem_l_dealloc_tx | ||
14869 | +** Clears out a previously allocatedvoid acxmem_l_dealloc_tx(tx_t *tx_opaque); | ||
14870 | + transmit descriptor. The ACX | ||
14871 | +** can get confused if we skip transmit descriptors in the queue, | ||
14872 | +** so when we don't need a descriptor return it to its original | ||
14873 | +** state and move the queue head pointer back. | ||
14874 | +** | ||
14875 | +*/ | ||
14876 | +void | ||
14877 | +acxmem_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque) | ||
14878 | +{ | ||
14879 | + /* | ||
14880 | + * txdesc is the address of the descriptor on the ACX. | ||
14881 | + */ | ||
14882 | + txdesc_t *txdesc = (txdesc_t*)tx_opaque; | ||
14883 | + txdesc_t tmptxdesc; | ||
14884 | + int index; | ||
14885 | + | ||
14886 | + memset (&tmptxdesc, 0, sizeof(tmptxdesc)); | ||
14887 | + tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG; | ||
14888 | + tmptxdesc.u.r1.rate = 0x0a; | ||
14889 | + | ||
14890 | + /* | ||
14891 | + * Clear out all of the transmit descriptor except for the next pointer | ||
14892 | + */ | ||
14893 | + copy_to_slavemem (adev, (u32) &(txdesc->HostMemPtr), | ||
14894 | + (u8 *) &(tmptxdesc.HostMemPtr), | ||
14895 | + sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc)); | ||
14896 | + | ||
14897 | + /* | ||
14898 | + * This is only called immediately after we've allocated, so we should | ||
14899 | + * be able to set the head back to this descriptor. | ||
14900 | + */ | ||
14901 | + index = ((u8*) txdesc - (u8*)adev->txdesc_start) / adev->txdesc_size; | ||
14902 | + printk ("acx_dealloc: moving head from %d to %d\n", adev->tx_head, index); | ||
14903 | + adev->tx_head = index; | ||
14904 | +} | ||
14905 | + | ||
14906 | + | ||
14907 | +/*********************************************************************** | ||
14908 | +*/ | ||
14909 | +void* | ||
14910 | +acxmem_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque) | ||
14911 | +{ | ||
14912 | + return get_txhostdesc(adev, (txdesc_t*)tx_opaque)->data; | ||
14913 | +} | ||
14914 | + | ||
14915 | + | ||
14916 | +/*********************************************************************** | ||
14917 | +** acxmem_l_tx_data | ||
14918 | +** | ||
14919 | +** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). | ||
14920 | +** Can be called from acx_i_start_xmit (data frames from net core). | ||
14921 | +** | ||
14922 | +** FIXME: in case of fragments, should loop over the number of | ||
14923 | +** pre-allocated tx descrs, properly setting up transfer data and | ||
14924 | +** CTL_xxx flags according to fragment number. | ||
14925 | +*/ | ||
14926 | +void | ||
14927 | +acxmem_update_queue_indicator (acx_device_t *adev, int txqueue) | ||
14928 | +{ | ||
14929 | +#ifdef USING_MORE_THAN_ONE_TRANSMIT_QUEUE | ||
14930 | + u32 indicator; | ||
14931 | + unsigned long flags; | ||
14932 | + int count; | ||
14933 | + | ||
14934 | + /* | ||
14935 | + * Can't handle an interrupt while we're fiddling with the ACX's lock, | ||
14936 | + * according to TI. The ACX is supposed to hold fw_lock for at most | ||
14937 | + * 500ns. | ||
14938 | + */ | ||
14939 | + local_irq_save (flags); | ||
14940 | + | ||
14941 | + /* | ||
14942 | + * Wait for ACX to release the lock (at most 500ns). | ||
14943 | + */ | ||
14944 | + count = 0; | ||
14945 | + while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock)) | ||
14946 | + && (count++ < 50)) { | ||
14947 | + ndelay (10); | ||
14948 | + } | ||
14949 | + if (count < 50) { | ||
14950 | + | ||
14951 | + /* | ||
14952 | + * Take out the host lock - anything non-zero will work, so don't worry about | ||
14953 | + * be/le | ||
14954 | + */ | ||
14955 | + write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 1); | ||
14956 | + | ||
14957 | + /* | ||
14958 | + * Avoid a race condition | ||
14959 | + */ | ||
14960 | + count = 0; | ||
14961 | + while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock)) | ||
14962 | + && (count++ < 50)) { | ||
14963 | + ndelay (10); | ||
14964 | + } | ||
14965 | + | ||
14966 | + if (count < 50) { | ||
14967 | + /* | ||
14968 | + * Mark the queue active | ||
14969 | + */ | ||
14970 | + indicator = read_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator)); | ||
14971 | + indicator |= cpu_to_le32 (1 << txqueue); | ||
14972 | + write_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator), indicator); | ||
14973 | + } | ||
14974 | + | ||
14975 | + /* | ||
14976 | + * Release the host lock | ||
14977 | + */ | ||
14978 | + write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 0); | ||
14979 | + | ||
14980 | + } | ||
14981 | + | ||
14982 | + /* | ||
14983 | + * Restore interrupts | ||
14984 | + */ | ||
14985 | + local_irq_restore (flags); | ||
14986 | +#endif | ||
14987 | +} | ||
14988 | + | ||
14989 | +void | ||
14990 | +acxmem_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int len) | ||
14991 | +{ | ||
14992 | + /* | ||
14993 | + * txdesc is the address on the ACX | ||
14994 | + */ | ||
14995 | + txdesc_t *txdesc = (txdesc_t*)tx_opaque; | ||
14996 | + txhostdesc_t *hostdesc1, *hostdesc2; | ||
14997 | + client_t *clt; | ||
14998 | + u16 rate_cur; | ||
14999 | + u8 Ctl_8, Ctl2_8; | ||
15000 | + u32 addr; | ||
15001 | + | ||
15002 | + FN_ENTER; | ||
15003 | + /* fw doesn't tx such packets anyhow */ | ||
15004 | + if (unlikely(len < WLAN_HDR_A3_LEN)) | ||
15005 | + goto end; | ||
15006 | + | ||
15007 | + hostdesc1 = get_txhostdesc(adev, txdesc); | ||
15008 | + /* modify flag status in separate variable to be able to write it back | ||
15009 | + * in one big swoop later (also in order to have less device memory | ||
15010 | + * accesses) */ | ||
15011 | + Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); | ||
15012 | + Ctl2_8 = 0; /* really need to init it to 0, not txdesc->Ctl2_8, it seems */ | ||
15013 | + | ||
15014 | + hostdesc2 = hostdesc1 + 1; | ||
15015 | + | ||
15016 | + /* DON'T simply set Ctl field to 0 here globally, | ||
15017 | + * it needs to maintain a consistent flag status (those are state flags!!), | ||
15018 | + * otherwise it may lead to severe disruption. Only set or reset particular | ||
15019 | + * flags at the exact moment this is needed... */ | ||
15020 | + | ||
15021 | + /* let chip do RTS/CTS handshaking before sending | ||
15022 | + * in case packet size exceeds threshold */ | ||
15023 | + if (len > adev->rts_threshold) | ||
15024 | + SET_BIT(Ctl2_8, DESC_CTL2_RTS); | ||
15025 | + else | ||
15026 | + CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS); | ||
15027 | + | ||
15028 | + switch (adev->mode) { | ||
15029 | + case ACX_MODE_0_ADHOC: | ||
15030 | + case ACX_MODE_3_AP: | ||
15031 | + clt = acx_l_sta_list_get(adev, ((wlan_hdr_t*)hostdesc1->data)->a1); | ||
15032 | + break; | ||
15033 | + case ACX_MODE_2_STA: | ||
15034 | + clt = adev->ap_client; | ||
15035 | + break; | ||
15036 | +#if 0 | ||
15037 | +/* testing was done on acx111: */ | ||
15038 | + case ACX_MODE_MONITOR: | ||
15039 | + SET_BIT(Ctl2_8, 0 | ||
15040 | +/* sends CTS to self before packet */ | ||
15041 | + + DESC_CTL2_SEQ /* don't increase sequence field */ | ||
15042 | +/* not working (looks like good fcs is still added) */ | ||
15043 | + + DESC_CTL2_FCS /* don't add the FCS */ | ||
15044 | +/* not tested */ | ||
15045 | + + DESC_CTL2_MORE_FRAG | ||
15046 | +/* not tested */ | ||
15047 | + + DESC_CTL2_RETRY /* don't increase retry field */ | ||
15048 | +/* not tested */ | ||
15049 | + + DESC_CTL2_POWER /* don't increase power mgmt. field */ | ||
15050 | +/* no effect */ | ||
15051 | + + DESC_CTL2_WEP /* encrypt this frame */ | ||
15052 | +/* not tested */ | ||
15053 | + + DESC_CTL2_DUR /* don't increase duration field */ | ||
15054 | + ); | ||
15055 | + /* fallthrough */ | ||
15056 | +#endif | ||
15057 | + default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ | ||
15058 | + clt = NULL; | ||
15059 | + break; | ||
15060 | + } | ||
15061 | + | ||
15062 | + rate_cur = clt ? clt->rate_cur : adev->rate_bcast; | ||
15063 | + if (unlikely(!rate_cur)) { | ||
15064 | + printk("acx: driver bug! bad ratemask\n"); | ||
15065 | + goto end; | ||
15066 | + } | ||
15067 | + | ||
15068 | + /* used in tx cleanup routine for auto rate and accounting: */ | ||
15069 | + put_txcr(adev, txdesc, clt, rate_cur); | ||
15070 | + | ||
15071 | + write_slavemem16 (adev, (u32) &(txdesc->total_length), cpu_to_le16(len)); | ||
15072 | + hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN); | ||
15073 | + if (IS_ACX111(adev)) { | ||
15074 | + /* note that if !txdesc->do_auto, txrate->cur | ||
15075 | + ** has only one nonzero bit */ | ||
15076 | + txdesc->u.r2.rate111 = cpu_to_le16( | ||
15077 | + rate_cur | ||
15078 | + /* WARNING: I was never able to make it work with prism54 AP. | ||
15079 | + ** It was falling down to 1Mbit where shortpre is not applicable, | ||
15080 | + ** and not working at all at "5,11 basic rates only" setting. | ||
15081 | + ** I even didn't see tx packets in radio packet capture. | ||
15082 | + ** Disabled for now --vda */ | ||
15083 | + /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */ | ||
15084 | + ); | ||
15085 | +#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS | ||
15086 | + /* should add this to rate111 above as necessary */ | ||
15087 | + | (clt->pbcc511 ? RATE111_PBCC511 : 0) | ||
15088 | +#endif | ||
15089 | + hostdesc1->length = cpu_to_le16(len); | ||
15090 | + } else { /* ACX100 */ | ||
15091 | + u8 rate_100 = clt ? clt->rate_100 : adev->rate_bcast100; | ||
15092 | + write_slavemem8 (adev, (u32) &(txdesc->u.r1.rate), rate_100); | ||
15093 | +#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS | ||
15094 | + if (clt->pbcc511) { | ||
15095 | + if (n == RATE100_5 || n == RATE100_11) | ||
15096 | + n |= RATE100_PBCC511; | ||
15097 | + } | ||
15098 | + | ||
15099 | + if (clt->shortpre && (clt->cur != RATE111_1)) | ||
15100 | + SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */ | ||
15101 | +#endif | ||
15102 | + /* set autodma and reclaim and 1st mpdu */ | ||
15103 | + SET_BIT(Ctl_8, DESC_CTL_FIRSTFRAG); | ||
15104 | + | ||
15105 | +#if ACX_FRAGMENTATION | ||
15106 | + /* SET_BIT(Ctl2_8, DESC_CTL2_MORE_FRAG); cannot set it unconditionally, needs to be set for all non-last fragments */ | ||
15107 | +#endif | ||
15108 | + hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN); | ||
15109 | + | ||
15110 | + /* | ||
15111 | + * Since we're not using autodma copy the packet data to the acx now. | ||
15112 | + * Even host descriptors point to the packet header, and the odd indexed | ||
15113 | + * descriptor following points to the packet data. | ||
15114 | + * | ||
15115 | + * The first step is to find free memory in the ACX transmit buffers. | ||
15116 | + * They don't necessarily map one to one with the transmit queue entries, | ||
15117 | + * so search through them starting just after the last one used. | ||
15118 | + */ | ||
15119 | + addr = allocate_acx_txbuf_space (adev, len); | ||
15120 | + if (addr) { | ||
15121 | + chaincopy_to_slavemem (adev, addr, hostdesc1->data, len); | ||
15122 | + } | ||
15123 | + else { | ||
15124 | + /* | ||
15125 | + * Bummer. We thought we might have enough room in the transmit | ||
15126 | + * buffers to send this packet, but it turns out we don't. alloc_tx | ||
15127 | + * has already marked this transmit descriptor as HOSTOWN and ACXDONE, | ||
15128 | + * which means the ACX will hang when it gets to this descriptor unless | ||
15129 | + * we do something about it. Having a bubble in the transmit queue just | ||
15130 | + * doesn't seem to work, so we have to reset this transmit queue entry's | ||
15131 | + * state to its original value and back up our head pointer to point | ||
15132 | + * back to this entry. | ||
15133 | + */ | ||
15134 | + hostdesc1->length = 0; | ||
15135 | + hostdesc2->length = 0; | ||
15136 | + write_slavemem16 (adev, (u32) &(txdesc->total_length), 0); | ||
15137 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG); | ||
15138 | + adev->tx_head = ((u8*) txdesc - (u8*) adev->txdesc_start) / adev->txdesc_size; | ||
15139 | + goto end; | ||
15140 | + } | ||
15141 | + /* | ||
15142 | + * Tell the ACX where the packet is. | ||
15143 | + */ | ||
15144 | + write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), addr); | ||
15145 | + | ||
15146 | + } | ||
15147 | + /* don't need to clean ack/rts statistics here, already | ||
15148 | + * done on descr cleanup */ | ||
15149 | + | ||
15150 | + /* clears HOSTOWN and ACXDONE bits, thus telling that the descriptors | ||
15151 | + * are now owned by the acx100; do this as LAST operation */ | ||
15152 | + CLEAR_BIT(Ctl_8, DESC_CTL_ACXDONE_HOSTOWN); | ||
15153 | + /* flush writes before we release hostdesc to the adapter here */ | ||
15154 | + //wmb(); | ||
15155 | + | ||
15156 | + /* write back modified flags */ | ||
15157 | + /* | ||
15158 | + * At this point Ctl_8 should just be FIRSTFRAG | ||
15159 | + */ | ||
15160 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl2_8),Ctl2_8); | ||
15161 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), Ctl_8); | ||
15162 | + /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */ | ||
15163 | + | ||
15164 | + /* | ||
15165 | + * Update the queue indicator to say there's data on the first queue. | ||
15166 | + */ | ||
15167 | + acxmem_update_queue_indicator (adev, 0); | ||
15168 | + | ||
15169 | + /* flush writes before we tell the adapter that it's its turn now */ | ||
15170 | + mmiowb(); | ||
15171 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); | ||
15172 | + write_flush(adev); | ||
15173 | + | ||
15174 | + /* log the packet content AFTER sending it, | ||
15175 | + * in order to not delay sending any further than absolutely needed | ||
15176 | + * Do separate logs for acx100/111 to have human-readable rates */ | ||
15177 | + if (unlikely(acx_debug & (L_XFER|L_DATA))) { | ||
15178 | + u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc; | ||
15179 | + if (IS_ACX111(adev)) | ||
15180 | + printk("tx: pkt (%s): len %d " | ||
15181 | + "rate %04X%s status %u\n", | ||
15182 | + acx_get_packet_type_string(le16_to_cpu(fc)), len, | ||
15183 | + le16_to_cpu(txdesc->u.r2.rate111), | ||
15184 | + (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "", | ||
15185 | + adev->status); | ||
15186 | + else | ||
15187 | + printk("tx: pkt (%s): len %d rate %03u%s status %u\n", | ||
15188 | + acx_get_packet_type_string(fc), len, | ||
15189 | + read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate)), | ||
15190 | + (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "", | ||
15191 | + adev->status); | ||
15192 | + | ||
15193 | + if (acx_debug & L_DATA) { | ||
15194 | + printk("tx: 802.11 [%d]: ", len); | ||
15195 | + acx_dump_bytes(hostdesc1->data, len); | ||
15196 | + } | ||
15197 | + } | ||
15198 | +end: | ||
15199 | + FN_EXIT0; | ||
15200 | +} | ||
15201 | + | ||
15202 | + | ||
15203 | +/*********************************************************************** | ||
15204 | +** acxmem_l_clean_txdesc | ||
15205 | +** | ||
15206 | +** This function resets the txdescs' status when the ACX100 | ||
15207 | +** signals the TX done IRQ (txdescs have been processed), starting with | ||
15208 | +** the pool index of the descriptor which we would use next, | ||
15209 | +** in order to make sure that we can be as fast as possible | ||
15210 | +** in filling new txdescs. | ||
15211 | +** Everytime we get called we know where the next packet to be cleaned is. | ||
15212 | +*/ | ||
15213 | + | ||
15214 | +#if !ACX_DEBUG | ||
15215 | +static inline void log_txbuffer(const acx_device_t *adev) {} | ||
15216 | +#else | ||
15217 | +static void | ||
15218 | +log_txbuffer(acx_device_t *adev) | ||
15219 | +{ | ||
15220 | + txdesc_t *txdesc; | ||
15221 | + int i; | ||
15222 | + u8 Ctl_8; | ||
15223 | + | ||
15224 | + /* no FN_ENTER here, we don't want that */ | ||
15225 | + /* no locks here, since it's entirely non-critical code */ | ||
15226 | + txdesc = adev->txdesc_start; | ||
15227 | + if (unlikely(!txdesc)) return; | ||
15228 | + printk("tx: desc->Ctl8's:"); | ||
15229 | + for (i = 0; i < TX_CNT; i++) { | ||
15230 | + Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); | ||
15231 | + printk(" %02X", Ctl_8); | ||
15232 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
15233 | + } | ||
15234 | + printk("\n"); | ||
15235 | +} | ||
15236 | +#endif | ||
15237 | + | ||
15238 | + | ||
15239 | +static void | ||
15240 | +handle_tx_error(acx_device_t *adev, u8 error, unsigned int finger) | ||
15241 | +{ | ||
15242 | + const char *err = "unknown error"; | ||
15243 | + | ||
15244 | + /* hmm, should we handle this as a mask | ||
15245 | + * of *several* bits? | ||
15246 | + * For now I think only caring about | ||
15247 | + * individual bits is ok... */ | ||
15248 | + switch (error) { | ||
15249 | + case 0x01: | ||
15250 | + err = "no Tx due to error in other fragment"; | ||
15251 | + adev->wstats.discard.fragment++; | ||
15252 | + break; | ||
15253 | + case 0x02: | ||
15254 | + err = "Tx aborted"; | ||
15255 | + adev->stats.tx_aborted_errors++; | ||
15256 | + break; | ||
15257 | + case 0x04: | ||
15258 | + err = "Tx desc wrong parameters"; | ||
15259 | + adev->wstats.discard.misc++; | ||
15260 | + break; | ||
15261 | + case 0x08: | ||
15262 | + err = "WEP key not found"; | ||
15263 | + adev->wstats.discard.misc++; | ||
15264 | + break; | ||
15265 | + case 0x10: | ||
15266 | + err = "MSDU lifetime timeout? - try changing " | ||
15267 | + "'iwconfig retry lifetime XXX'"; | ||
15268 | + adev->wstats.discard.misc++; | ||
15269 | + break; | ||
15270 | + case 0x20: | ||
15271 | + err = "excessive Tx retries due to either distance " | ||
15272 | + "too high or unable to Tx or Tx frame error - " | ||
15273 | + "try changing 'iwconfig txpower XXX' or " | ||
15274 | + "'sens'itivity or 'retry'"; | ||
15275 | + adev->wstats.discard.retries++; | ||
15276 | + /* Tx error 0x20 also seems to occur on | ||
15277 | + * overheating, so I'm not sure whether we | ||
15278 | + * actually want to do aggressive radio recalibration, | ||
15279 | + * since people maybe won't notice then that their hardware | ||
15280 | + * is slowly getting cooked... | ||
15281 | + * Or is it still a safe long distance from utter | ||
15282 | + * radio non-functionality despite many radio recalibs | ||
15283 | + * to final destructive overheating of the hardware? | ||
15284 | + * In this case we really should do recalib here... | ||
15285 | + * I guess the only way to find out is to do a | ||
15286 | + * potentially fatal self-experiment :-\ | ||
15287 | + * Or maybe only recalib in case we're using Tx | ||
15288 | + * rate auto (on errors switching to lower speed | ||
15289 | + * --> less heat?) or 802.11 power save mode? | ||
15290 | + * | ||
15291 | + * ok, just do it. */ | ||
15292 | + if (++adev->retry_errors_msg_ratelimit % 4 == 0) { | ||
15293 | + if (adev->retry_errors_msg_ratelimit <= 20) { | ||
15294 | + printk("%s: several excessive Tx " | ||
15295 | + "retry errors occurred, attempting " | ||
15296 | + "to recalibrate radio. Radio " | ||
15297 | + "drift might be caused by increasing " | ||
15298 | + "card temperature, please check the card " | ||
15299 | + "before it's too late!\n", | ||
15300 | + adev->ndev->name); | ||
15301 | + if (adev->retry_errors_msg_ratelimit == 20) | ||
15302 | + printk("disabling above message\n"); | ||
15303 | + } | ||
15304 | + | ||
15305 | + acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); | ||
15306 | + } | ||
15307 | + break; | ||
15308 | + case 0x40: | ||
15309 | + err = "Tx buffer overflow"; | ||
15310 | + adev->stats.tx_fifo_errors++; | ||
15311 | + break; | ||
15312 | + case 0x80: | ||
15313 | + err = "DMA error"; | ||
15314 | + adev->wstats.discard.misc++; | ||
15315 | + break; | ||
15316 | + } | ||
15317 | + adev->stats.tx_errors++; | ||
15318 | + if (adev->stats.tx_errors <= 20) | ||
15319 | + printk("%s: tx error 0x%02X, buf %02u! (%s)\n", | ||
15320 | + adev->ndev->name, error, finger, err); | ||
15321 | + else | ||
15322 | + printk("%s: tx error 0x%02X, buf %02u!\n", | ||
15323 | + adev->ndev->name, error, finger); | ||
15324 | +} | ||
15325 | + | ||
15326 | + | ||
15327 | +unsigned int | ||
15328 | +acxmem_l_clean_txdesc(acx_device_t *adev) | ||
15329 | +{ | ||
15330 | + txdesc_t *txdesc; | ||
15331 | + unsigned finger; | ||
15332 | + int num_cleaned; | ||
15333 | + u16 r111; | ||
15334 | + u8 error, ack_failures, rts_failures, rts_ok, r100, Ctl_8; | ||
15335 | + u32 acxmem; | ||
15336 | + txdesc_t tmptxdesc; | ||
15337 | + | ||
15338 | + FN_ENTER; | ||
15339 | + | ||
15340 | + /* | ||
15341 | + * Set up a template descriptor for re-initialization. The only | ||
15342 | + * things that get set are Ctl_8 and the rate, and the rate defaults | ||
15343 | + * to 1Mbps. | ||
15344 | + */ | ||
15345 | + memset (&tmptxdesc, 0, sizeof (tmptxdesc)); | ||
15346 | + tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG; | ||
15347 | + tmptxdesc.u.r1.rate = 0x0a; | ||
15348 | + | ||
15349 | + if (unlikely(acx_debug & L_DEBUG)) | ||
15350 | + log_txbuffer(adev); | ||
15351 | + | ||
15352 | + log(L_BUFT, "tx: cleaning up bufs from %u\n", adev->tx_tail); | ||
15353 | + | ||
15354 | + /* We know first descr which is not free yet. We advance it as far | ||
15355 | + ** as we see correct bits set in following descs (if next desc | ||
15356 | + ** is NOT free, we shouldn't advance at all). We know that in | ||
15357 | + ** front of tx_tail may be "holes" with isolated free descs. | ||
15358 | + ** We will catch up when all intermediate descs will be freed also */ | ||
15359 | + | ||
15360 | + finger = adev->tx_tail; | ||
15361 | + num_cleaned = 0; | ||
15362 | + while (likely(finger != adev->tx_head)) { | ||
15363 | + txdesc = get_txdesc(adev, finger); | ||
15364 | + | ||
15365 | + /* If we allocated txdesc on tx path but then decided | ||
15366 | + ** to NOT use it, then it will be left as a free "bubble" | ||
15367 | + ** in the "allocated for tx" part of the ring. | ||
15368 | + ** We may meet it on the next ring pass here. */ | ||
15369 | + | ||
15370 | + /* stop if not marked as "tx finished" and "host owned" */ | ||
15371 | + Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); | ||
15372 | + if ((Ctl_8 & DESC_CTL_ACXDONE_HOSTOWN) | ||
15373 | + != DESC_CTL_ACXDONE_HOSTOWN) { | ||
15374 | + if (unlikely(!num_cleaned)) { /* maybe remove completely */ | ||
15375 | + log(L_BUFT, "clean_txdesc: tail isn't free. " | ||
15376 | + "tail:%d head:%d\n", | ||
15377 | + adev->tx_tail, adev->tx_head); | ||
15378 | + } | ||
15379 | + break; | ||
15380 | + } | ||
15381 | + | ||
15382 | + /* remember desc values... */ | ||
15383 | + error = read_slavemem8 (adev, (u32) &(txdesc->error)); | ||
15384 | + ack_failures = read_slavemem8 (adev, (u32) &(txdesc->ack_failures)); | ||
15385 | + rts_failures = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures)); | ||
15386 | + rts_ok = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok)); | ||
15387 | + r100 = read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate)); | ||
15388 | + r111 = le16_to_cpu(read_slavemem16 (adev, (u32) &(txdesc->u.r2.rate111))); | ||
15389 | + | ||
15390 | + /* need to check for certain error conditions before we | ||
15391 | + * clean the descriptor: we still need valid descr data here */ | ||
15392 | + if (unlikely(0x30 & error)) { | ||
15393 | + /* only send IWEVTXDROP in case of retry or lifetime exceeded; | ||
15394 | + * all other errors mean we screwed up locally */ | ||
15395 | + union iwreq_data wrqu; | ||
15396 | + wlan_hdr_t *hdr; | ||
15397 | + txhostdesc_t *hostdesc; | ||
15398 | + | ||
15399 | + hostdesc = get_txhostdesc(adev, txdesc); | ||
15400 | + hdr = (wlan_hdr_t *)hostdesc->data; | ||
15401 | + MAC_COPY(wrqu.addr.sa_data, hdr->a1); | ||
15402 | + wireless_send_event(adev->ndev, IWEVTXDROP, &wrqu, NULL); | ||
15403 | + } | ||
15404 | + | ||
15405 | + /* | ||
15406 | + * Free up the transmit data buffers | ||
15407 | + */ | ||
15408 | + acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); | ||
15409 | + if (acxmem) { | ||
15410 | + reclaim_acx_txbuf_space (adev, acxmem); | ||
15411 | + } | ||
15412 | + | ||
15413 | + /* ...and free the desc by clearing all the fields | ||
15414 | + except the next pointer */ | ||
15415 | + copy_to_slavemem (adev, | ||
15416 | + (u32) &(txdesc->HostMemPtr), | ||
15417 | + (u8 *) &(tmptxdesc.HostMemPtr), | ||
15418 | + sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc) | ||
15419 | + ); | ||
15420 | + | ||
15421 | + adev->tx_free++; | ||
15422 | + num_cleaned++; | ||
15423 | + | ||
15424 | + if ((adev->tx_free >= TX_START_QUEUE) | ||
15425 | + && (adev->status == ACX_STATUS_4_ASSOCIATED) | ||
15426 | + && (acx_queue_stopped(adev->ndev)) | ||
15427 | + ) { | ||
15428 | + log(L_BUF, "tx: wake queue (avail. Tx desc %u)\n", | ||
15429 | + adev->tx_free); | ||
15430 | + acx_wake_queue(adev->ndev, NULL); | ||
15431 | + } | ||
15432 | + | ||
15433 | + /* do error checking, rate handling and logging | ||
15434 | + * AFTER having done the work, it's faster */ | ||
15435 | + | ||
15436 | + /* do rate handling */ | ||
15437 | + if (adev->rate_auto) { | ||
15438 | + struct client *clt = get_txc(adev, txdesc); | ||
15439 | + if (clt) { | ||
15440 | + u16 cur = get_txr(adev, txdesc); | ||
15441 | + if (clt->rate_cur == cur) { | ||
15442 | + acx_l_handle_txrate_auto(adev, clt, | ||
15443 | + cur, /* intended rate */ | ||
15444 | + r100, r111, /* actually used rate */ | ||
15445 | + (error & 0x30), /* was there an error? */ | ||
15446 | + TX_CNT + TX_CLEAN_BACKLOG - adev->tx_free); | ||
15447 | + } | ||
15448 | + } | ||
15449 | + } | ||
15450 | + | ||
15451 | + if (unlikely(error)) | ||
15452 | + handle_tx_error(adev, error, finger); | ||
15453 | + | ||
15454 | + if (IS_ACX111(adev)) | ||
15455 | + log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n", | ||
15456 | + finger, ack_failures, rts_failures, rts_ok, r111); | ||
15457 | + else | ||
15458 | + log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n", | ||
15459 | + finger, ack_failures, rts_failures, rts_ok, r100); | ||
15460 | + | ||
15461 | + /* update pointer for descr to be cleaned next */ | ||
15462 | + finger = (finger + 1) % TX_CNT; | ||
15463 | + } | ||
15464 | + | ||
15465 | + /* remember last position */ | ||
15466 | + adev->tx_tail = finger; | ||
15467 | +/* end: */ | ||
15468 | + FN_EXIT1(num_cleaned); | ||
15469 | + return num_cleaned; | ||
15470 | +} | ||
15471 | + | ||
15472 | +/* clean *all* Tx descriptors, and regardless of their previous state. | ||
15473 | + * Used for brute-force reset handling. */ | ||
15474 | +void | ||
15475 | +acxmem_l_clean_txdesc_emergency(acx_device_t *adev) | ||
15476 | +{ | ||
15477 | + txdesc_t *txdesc; | ||
15478 | + int i; | ||
15479 | + u32 acxmem; | ||
15480 | + | ||
15481 | + FN_ENTER; | ||
15482 | + | ||
15483 | + for (i = 0; i < TX_CNT; i++) { | ||
15484 | + txdesc = get_txdesc(adev, i); | ||
15485 | + | ||
15486 | + /* free it */ | ||
15487 | + write_slavemem8 (adev, (u32) &(txdesc->ack_failures), 0); | ||
15488 | + write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures), 0); | ||
15489 | + write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok), 0); | ||
15490 | + write_slavemem8 (adev, (u32) &(txdesc->error), 0); | ||
15491 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN); | ||
15492 | + | ||
15493 | + /* | ||
15494 | + * Clean up the memory allocated on the ACX for this transmit descriptor. | ||
15495 | + */ | ||
15496 | + acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); | ||
15497 | + if (acxmem) { | ||
15498 | + reclaim_acx_txbuf_space (adev, acxmem); | ||
15499 | + } | ||
15500 | + | ||
15501 | + write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), 0); | ||
15502 | + } | ||
15503 | + | ||
15504 | + adev->tx_free = TX_CNT; | ||
15505 | + | ||
15506 | + FN_EXIT0; | ||
15507 | +} | ||
15508 | + | ||
15509 | + | ||
15510 | +/*********************************************************************** | ||
15511 | +** acxmem_s_create_tx_host_desc_queue | ||
15512 | +*/ | ||
15513 | + | ||
15514 | +static void* | ||
15515 | +allocate(acx_device_t *adev, size_t size, dma_addr_t *phy, const char *msg) | ||
15516 | +{ | ||
15517 | + void *ptr; | ||
15518 | + ptr = kmalloc (size, GFP_KERNEL); | ||
15519 | + /* | ||
15520 | + * The ACX can't use the physical address, so we'll have to fake it | ||
15521 | + * later and it might be handy to have the virtual address. | ||
15522 | + */ | ||
15523 | + *phy = (dma_addr_t) NULL; | ||
15524 | + | ||
15525 | + if (ptr) { | ||
15526 | + log(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n", | ||
15527 | + msg, (int)size, ptr, (unsigned long long)*phy); | ||
15528 | + memset(ptr, 0, size); | ||
15529 | + return ptr; | ||
15530 | + } | ||
15531 | + printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n", | ||
15532 | + msg, (int)size); | ||
15533 | + return NULL; | ||
15534 | +} | ||
15535 | + | ||
15536 | + | ||
15537 | +/* | ||
15538 | + * In the generic slave memory access mode, most of the stuff in | ||
15539 | + * the txhostdesc_t is unused. It's only here because the rest of | ||
15540 | + * the ACX driver expects it to be since the PCI version uses indirect | ||
15541 | + * host memory organization with DMA. Since we're not using DMA the | ||
15542 | + * only use we have for the host descriptors is to store the packets | ||
15543 | + * on the way out. | ||
15544 | + */ | ||
15545 | +static int | ||
15546 | +acxmem_s_create_tx_host_desc_queue(acx_device_t *adev) | ||
15547 | +{ | ||
15548 | + txhostdesc_t *hostdesc; | ||
15549 | + u8 *txbuf; | ||
15550 | + int i; | ||
15551 | + | ||
15552 | + FN_ENTER; | ||
15553 | + | ||
15554 | + /* allocate TX buffer */ | ||
15555 | + adev->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS; | ||
15556 | + | ||
15557 | + adev->txbuf_start = allocate(adev, adev->txbuf_area_size, | ||
15558 | + &adev->txbuf_startphy, "txbuf_start"); | ||
15559 | + if (!adev->txbuf_start) | ||
15560 | + goto fail; | ||
15561 | + | ||
15562 | + /* allocate the TX host descriptor queue pool */ | ||
15563 | + adev->txhostdesc_area_size = TX_CNT * 2*sizeof(*hostdesc); | ||
15564 | + | ||
15565 | + adev->txhostdesc_start = allocate(adev, adev->txhostdesc_area_size, | ||
15566 | + &adev->txhostdesc_startphy, "txhostdesc_start"); | ||
15567 | + if (!adev->txhostdesc_start) | ||
15568 | + goto fail; | ||
15569 | + | ||
15570 | + /* check for proper alignment of TX host descriptor pool */ | ||
15571 | + if ((long) adev->txhostdesc_start & 3) { | ||
15572 | + printk("acx: driver bug: dma alloc returns unaligned address\n"); | ||
15573 | + goto fail; | ||
15574 | + } | ||
15575 | + | ||
15576 | + hostdesc = adev->txhostdesc_start; | ||
15577 | + txbuf = adev->txbuf_start; | ||
15578 | + | ||
15579 | +#if 0 | ||
15580 | +/* Each tx buffer is accessed by hardware via | ||
15581 | +** txdesc -> txhostdesc(s) -> txbuffer(s). | ||
15582 | +** We use only one txhostdesc per txdesc, but it looks like | ||
15583 | +** acx111 is buggy: it accesses second txhostdesc | ||
15584 | +** (via hostdesc.desc_phy_next field) even if | ||
15585 | +** txdesc->length == hostdesc->length and thus | ||
15586 | +** entire packet was placed into first txhostdesc. | ||
15587 | +** Due to this bug acx111 hangs unless second txhostdesc | ||
15588 | +** has le16_to_cpu(hostdesc.length) = 3 (or larger) | ||
15589 | +** Storing NULL into hostdesc.desc_phy_next | ||
15590 | +** doesn't seem to help. | ||
15591 | +** | ||
15592 | +** Update: although it worked on Xterasys XN-2522g | ||
15593 | +** with len=3 trick, WG311v2 is even more bogus, doesn't work. | ||
15594 | +** Keeping this code (#ifdef'ed out) for documentational purposes. | ||
15595 | +*/ | ||
15596 | + for (i = 0; i < TX_CNT*2; i++) { | ||
15597 | + hostdesc_phy += sizeof(*hostdesc); | ||
15598 | + if (!(i & 1)) { | ||
15599 | + hostdesc->data_phy = cpu2acx(txbuf_phy); | ||
15600 | + /* hostdesc->data_offset = ... */ | ||
15601 | + /* hostdesc->reserved = ... */ | ||
15602 | + hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); | ||
15603 | + /* hostdesc->length = ... */ | ||
15604 | + hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); | ||
15605 | + hostdesc->pNext = ptr2acx(NULL); | ||
15606 | + /* hostdesc->Status = ... */ | ||
15607 | + /* below: non-hardware fields */ | ||
15608 | + hostdesc->data = txbuf; | ||
15609 | + | ||
15610 | + txbuf += WLAN_A4FR_MAXLEN_WEP_FCS; | ||
15611 | + txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS; | ||
15612 | + } else { | ||
15613 | + /* hostdesc->data_phy = ... */ | ||
15614 | + /* hostdesc->data_offset = ... */ | ||
15615 | + /* hostdesc->reserved = ... */ | ||
15616 | + /* hostdesc->Ctl_16 = ... */ | ||
15617 | + hostdesc->length = cpu_to_le16(3); /* bug workaround */ | ||
15618 | + /* hostdesc->desc_phy_next = ... */ | ||
15619 | + /* hostdesc->pNext = ... */ | ||
15620 | + /* hostdesc->Status = ... */ | ||
15621 | + /* below: non-hardware fields */ | ||
15622 | + /* hostdesc->data = ... */ | ||
15623 | + } | ||
15624 | + hostdesc++; | ||
15625 | + } | ||
15626 | +#endif | ||
15627 | +/* We initialize two hostdescs so that they point to adjacent | ||
15628 | +** memory areas. Thus txbuf is really just a contiguous memory area */ | ||
15629 | + for (i = 0; i < TX_CNT*2; i++) { | ||
15630 | + /* ->data is a non-hardware field: */ | ||
15631 | + hostdesc->data = txbuf; | ||
15632 | + | ||
15633 | + if (!(i & 1)) { | ||
15634 | + txbuf += WLAN_HDR_A3_LEN; | ||
15635 | + } else { | ||
15636 | + txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN; | ||
15637 | + } | ||
15638 | + hostdesc++; | ||
15639 | + } | ||
15640 | + hostdesc--; | ||
15641 | + | ||
15642 | + FN_EXIT1(OK); | ||
15643 | + return OK; | ||
15644 | +fail: | ||
15645 | + printk("acx: create_tx_host_desc_queue FAILED\n"); | ||
15646 | + /* dealloc will be done by free function on error case */ | ||
15647 | + FN_EXIT1(NOT_OK); | ||
15648 | + return NOT_OK; | ||
15649 | +} | ||
15650 | + | ||
15651 | + | ||
15652 | +/*************************************************************** | ||
15653 | +** acxmem_s_create_rx_host_desc_queue | ||
15654 | +*/ | ||
15655 | +/* the whole size of a data buffer (header plus data body) | ||
15656 | + * plus 32 bytes safety offset at the end */ | ||
15657 | +#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32) | ||
15658 | + | ||
15659 | +static int | ||
15660 | +acxmem_s_create_rx_host_desc_queue(acx_device_t *adev) | ||
15661 | +{ | ||
15662 | + rxhostdesc_t *hostdesc; | ||
15663 | + rxbuffer_t *rxbuf; | ||
15664 | + int i; | ||
15665 | + | ||
15666 | + FN_ENTER; | ||
15667 | + | ||
15668 | + /* allocate the RX host descriptor queue pool */ | ||
15669 | + adev->rxhostdesc_area_size = RX_CNT * sizeof(*hostdesc); | ||
15670 | + | ||
15671 | + adev->rxhostdesc_start = allocate(adev, adev->rxhostdesc_area_size, | ||
15672 | + &adev->rxhostdesc_startphy, "rxhostdesc_start"); | ||
15673 | + if (!adev->rxhostdesc_start) | ||
15674 | + goto fail; | ||
15675 | + | ||
15676 | + /* check for proper alignment of RX host descriptor pool */ | ||
15677 | + if ((long) adev->rxhostdesc_start & 3) { | ||
15678 | + printk("acx: driver bug: dma alloc returns unaligned address\n"); | ||
15679 | + goto fail; | ||
15680 | + } | ||
15681 | + | ||
15682 | + /* allocate Rx buffer pool which will be used by the acx | ||
15683 | + * to store the whole content of the received frames in it */ | ||
15684 | + adev->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE; | ||
15685 | + | ||
15686 | + adev->rxbuf_start = allocate(adev, adev->rxbuf_area_size, | ||
15687 | + &adev->rxbuf_startphy, "rxbuf_start"); | ||
15688 | + if (!adev->rxbuf_start) | ||
15689 | + goto fail; | ||
15690 | + | ||
15691 | + rxbuf = adev->rxbuf_start; | ||
15692 | + hostdesc = adev->rxhostdesc_start; | ||
15693 | + | ||
15694 | + /* don't make any popular C programming pointer arithmetic mistakes | ||
15695 | + * here, otherwise I'll kill you... | ||
15696 | + * (and don't dare asking me why I'm warning you about that...) */ | ||
15697 | + for (i = 0; i < RX_CNT; i++) { | ||
15698 | + hostdesc->data = rxbuf; | ||
15699 | + hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE); | ||
15700 | + rxbuf++; | ||
15701 | + hostdesc++; | ||
15702 | + } | ||
15703 | + hostdesc--; | ||
15704 | + FN_EXIT1(OK); | ||
15705 | + return OK; | ||
15706 | +fail: | ||
15707 | + printk("acx: create_rx_host_desc_queue FAILED\n"); | ||
15708 | + /* dealloc will be done by free function on error case */ | ||
15709 | + FN_EXIT1(NOT_OK); | ||
15710 | + return NOT_OK; | ||
15711 | +} | ||
15712 | + | ||
15713 | + | ||
15714 | +/*************************************************************** | ||
15715 | +** acxmem_s_create_hostdesc_queues | ||
15716 | +*/ | ||
15717 | +int | ||
15718 | +acxmem_s_create_hostdesc_queues(acx_device_t *adev) | ||
15719 | +{ | ||
15720 | + int result; | ||
15721 | + result = acxmem_s_create_tx_host_desc_queue(adev); | ||
15722 | + if (OK != result) return result; | ||
15723 | + result = acxmem_s_create_rx_host_desc_queue(adev); | ||
15724 | + return result; | ||
15725 | +} | ||
15726 | + | ||
15727 | + | ||
15728 | +/*************************************************************** | ||
15729 | +** acxmem_create_tx_desc_queue | ||
15730 | +*/ | ||
15731 | +static void | ||
15732 | +acxmem_create_tx_desc_queue(acx_device_t *adev, u32 tx_queue_start) | ||
15733 | +{ | ||
15734 | + txdesc_t *txdesc; | ||
15735 | + u32 clr; | ||
15736 | + int i; | ||
15737 | + | ||
15738 | + FN_ENTER; | ||
15739 | + | ||
15740 | + if (IS_ACX100(adev)) | ||
15741 | + adev->txdesc_size = sizeof(*txdesc); | ||
15742 | + else | ||
15743 | + /* the acx111 txdesc is 4 bytes larger */ | ||
15744 | + adev->txdesc_size = sizeof(*txdesc) + 4; | ||
15745 | + | ||
15746 | + /* | ||
15747 | + * This refers to an ACX address, not one of ours | ||
15748 | + */ | ||
15749 | + adev->txdesc_start = (txdesc_t *) tx_queue_start; | ||
15750 | + | ||
15751 | + log(L_DEBUG, "adev->txdesc_start=%p\n", | ||
15752 | + adev->txdesc_start); | ||
15753 | + | ||
15754 | + adev->tx_free = TX_CNT; | ||
15755 | + /* done by memset: adev->tx_head = 0; */ | ||
15756 | + /* done by memset: adev->tx_tail = 0; */ | ||
15757 | + txdesc = adev->txdesc_start; | ||
15758 | + | ||
15759 | + if (IS_ACX111(adev)) { | ||
15760 | + /* ACX111 has a preinitialized Tx buffer! */ | ||
15761 | + /* loop over whole send pool */ | ||
15762 | + /* FIXME: do we have to do the hostmemptr stuff here?? */ | ||
15763 | + for (i = 0; i < TX_CNT; i++) { | ||
15764 | + txdesc->Ctl_8 = DESC_CTL_HOSTOWN; | ||
15765 | + /* reserve two (hdr desc and payload desc) */ | ||
15766 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
15767 | + } | ||
15768 | + } else { | ||
15769 | + /* ACX100 Tx buffer needs to be initialized by us */ | ||
15770 | + /* clear whole send pool. sizeof is safe here (we are acx100) */ | ||
15771 | + | ||
15772 | + /* | ||
15773 | + * adev->txdesc_start refers to device memory, so we can't write | ||
15774 | + * directly to it. | ||
15775 | + */ | ||
15776 | + clr = (u32) adev->txdesc_start; | ||
15777 | + while (clr < (u32) adev->txdesc_start + (TX_CNT * sizeof(*txdesc))) { | ||
15778 | + write_slavemem32 (adev, clr, 0); | ||
15779 | + clr += 4; | ||
15780 | + } | ||
15781 | + | ||
15782 | + /* loop over whole send pool */ | ||
15783 | + for (i = 0; i < TX_CNT; i++) { | ||
15784 | + log(L_DEBUG, "configure card tx descriptor: 0x%p, " | ||
15785 | + "size: 0x%X\n", txdesc, adev->txdesc_size); | ||
15786 | + | ||
15787 | + /* initialise ctl */ | ||
15788 | + /* | ||
15789 | + * No auto DMA here | ||
15790 | + */ | ||
15791 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), | ||
15792 | + (u8) (DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG)); | ||
15793 | + /* done by memset(0): txdesc->Ctl2_8 = 0; */ | ||
15794 | + | ||
15795 | + /* point to next txdesc */ | ||
15796 | + write_slavemem32 (adev, (u32) &(txdesc->pNextDesc), | ||
15797 | + (u32) cpu_to_le32 ((u8 *) txdesc + adev->txdesc_size)); | ||
15798 | + | ||
15799 | + /* go to the next one */ | ||
15800 | + /* ++ is safe here (we are acx100) */ | ||
15801 | + txdesc++; | ||
15802 | + } | ||
15803 | + /* go back to the last one */ | ||
15804 | + txdesc--; | ||
15805 | + /* and point to the first making it a ring buffer */ | ||
15806 | + write_slavemem32 (adev, (u32) &(txdesc->pNextDesc), | ||
15807 | + (u32) cpu_to_le32 (tx_queue_start)); | ||
15808 | + } | ||
15809 | + FN_EXIT0; | ||
15810 | +} | ||
15811 | + | ||
15812 | + | ||
15813 | +/*************************************************************** | ||
15814 | +** acxmem_create_rx_desc_queue | ||
15815 | +*/ | ||
15816 | +static void | ||
15817 | +acxmem_create_rx_desc_queue(acx_device_t *adev, u32 rx_queue_start) | ||
15818 | +{ | ||
15819 | + rxdesc_t *rxdesc; | ||
15820 | + u32 mem_offs; | ||
15821 | + int i; | ||
15822 | + | ||
15823 | + FN_ENTER; | ||
15824 | + | ||
15825 | + /* done by memset: adev->rx_tail = 0; */ | ||
15826 | + | ||
15827 | + /* ACX111 doesn't need any further config: preconfigures itself. | ||
15828 | + * Simply print ring buffer for debugging */ | ||
15829 | + if (IS_ACX111(adev)) { | ||
15830 | + /* rxdesc_start already set here */ | ||
15831 | + | ||
15832 | + adev->rxdesc_start = (rxdesc_t *) rx_queue_start; | ||
15833 | + | ||
15834 | + rxdesc = adev->rxdesc_start; | ||
15835 | + for (i = 0; i < RX_CNT; i++) { | ||
15836 | + log(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc); | ||
15837 | + rxdesc = adev->rxdesc_start = (rxdesc_t *) | ||
15838 | + acx2cpu(rxdesc->pNextDesc); | ||
15839 | + } | ||
15840 | + } else { | ||
15841 | + /* we didn't pre-calculate rxdesc_start in case of ACX100 */ | ||
15842 | + /* rxdesc_start should be right AFTER Tx pool */ | ||
15843 | + adev->rxdesc_start = (rxdesc_t *) | ||
15844 | + ((u8 *) adev->txdesc_start + (TX_CNT * sizeof(txdesc_t))); | ||
15845 | + /* NB: sizeof(txdesc_t) above is valid because we know | ||
15846 | + ** we are in if (acx100) block. Beware of cut-n-pasting elsewhere! | ||
15847 | + ** acx111's txdesc is larger! */ | ||
15848 | + | ||
15849 | + mem_offs = (u32) adev->rxdesc_start; | ||
15850 | + while (mem_offs < (u32) adev->rxdesc_start + (RX_CNT * sizeof (*rxdesc))) { | ||
15851 | + write_slavemem32 (adev, mem_offs, 0); | ||
15852 | + mem_offs += 4; | ||
15853 | + } | ||
15854 | + | ||
15855 | + /* loop over whole receive pool */ | ||
15856 | + rxdesc = adev->rxdesc_start; | ||
15857 | + for (i = 0; i < RX_CNT; i++) { | ||
15858 | + log(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc); | ||
15859 | + /* point to next rxdesc */ | ||
15860 | + write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc), | ||
15861 | + (u32) cpu_to_le32 ((u8 *) rxdesc + sizeof(*rxdesc))); | ||
15862 | + /* go to the next one */ | ||
15863 | + rxdesc++; | ||
15864 | + } | ||
15865 | + /* go to the last one */ | ||
15866 | + rxdesc--; | ||
15867 | + | ||
15868 | + /* and point to the first making it a ring buffer */ | ||
15869 | + write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc), | ||
15870 | + (u32) cpu_to_le32 (rx_queue_start)); | ||
15871 | + } | ||
15872 | + FN_EXIT0; | ||
15873 | +} | ||
15874 | + | ||
15875 | + | ||
15876 | +/*************************************************************** | ||
15877 | +** acxmem_create_desc_queues | ||
15878 | +*/ | ||
15879 | +void | ||
15880 | +acxmem_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start) | ||
15881 | +{ | ||
15882 | + u32 *p; | ||
15883 | + int i; | ||
15884 | + | ||
15885 | + acxmem_create_tx_desc_queue(adev, tx_queue_start); | ||
15886 | + acxmem_create_rx_desc_queue(adev, rx_queue_start); | ||
15887 | + p = (u32 *) adev->acx_queue_indicator; | ||
15888 | + for (i = 0; i < 4; i++) { | ||
15889 | + write_slavemem32 (adev, (u32) p, 0); | ||
15890 | + p++; | ||
15891 | + } | ||
15892 | +} | ||
15893 | + | ||
15894 | + | ||
15895 | +/*************************************************************** | ||
15896 | +** acxmem_s_proc_diag_output | ||
15897 | +*/ | ||
15898 | +char* | ||
15899 | +acxmem_s_proc_diag_output(char *p, acx_device_t *adev) | ||
15900 | +{ | ||
15901 | + const char *rtl, *thd, *ttl; | ||
15902 | + txdesc_t *txdesc; | ||
15903 | + u8 Ctl_8; | ||
15904 | + rxdesc_t *rxdesc; | ||
15905 | + int i; | ||
15906 | + u32 tmp; | ||
15907 | + txdesc_t txd; | ||
15908 | + u8 buf[0x200]; | ||
15909 | + int j, k; | ||
15910 | + | ||
15911 | + FN_ENTER; | ||
15912 | + | ||
15913 | +#if DUMP_MEM_DURING_DIAG > 0 | ||
15914 | + dump_acxmem (adev, 0, 0x10000); | ||
15915 | + panic ("dump finished"); | ||
15916 | +#endif | ||
15917 | + | ||
15918 | + p += sprintf(p, "** Rx buf **\n"); | ||
15919 | + rxdesc = adev->rxdesc_start; | ||
15920 | + if (rxdesc) for (i = 0; i < RX_CNT; i++) { | ||
15921 | + rtl = (i == adev->rx_tail) ? " [tail]" : ""; | ||
15922 | + Ctl_8 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); | ||
15923 | + if (Ctl_8 & DESC_CTL_HOSTOWN) | ||
15924 | + p += sprintf(p, "%02u (%02x) FULL%s\n", i, Ctl_8, rtl); | ||
15925 | + else | ||
15926 | + p += sprintf(p, "%02u (%02x) empty%s\n", i, Ctl_8, rtl); | ||
15927 | + rxdesc++; | ||
15928 | + } | ||
15929 | + p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", adev->tx_free, | ||
15930 | + acx_queue_stopped(adev->ndev) ? "STOPPED" : "running"); | ||
15931 | + | ||
15932 | + p += sprintf(p, "** Tx buf %d blocks total, %d available, free list head %04x\n", | ||
15933 | + adev->acx_txbuf_numblocks, adev->acx_txbuf_blocks_free, adev->acx_txbuf_free); | ||
15934 | + txdesc = adev->txdesc_start; | ||
15935 | + if (txdesc) { | ||
15936 | + for (i = 0; i < TX_CNT; i++) { | ||
15937 | + thd = (i == adev->tx_head) ? " [head]" : ""; | ||
15938 | + ttl = (i == adev->tx_tail) ? " [tail]" : ""; | ||
15939 | + copy_from_slavemem (adev, (u8 *) &txd, (u32) txdesc, sizeof (txd)); | ||
15940 | + Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); | ||
15941 | + if (Ctl_8 & DESC_CTL_ACXDONE) | ||
15942 | + p += sprintf(p, "%02u ready to free (%02X)%s%s", i, Ctl_8, thd, ttl); | ||
15943 | + else if (Ctl_8 & DESC_CTL_HOSTOWN) | ||
15944 | + p += sprintf(p, "%02u available (%02X)%s%s", i, Ctl_8, thd, ttl); | ||
15945 | + else | ||
15946 | + p += sprintf(p, "%02u busy (%02X)%s%s", i, Ctl_8, thd, ttl); | ||
15947 | + tmp = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); | ||
15948 | + if (tmp) { | ||
15949 | + p += sprintf (p, " %04x", tmp); | ||
15950 | + while ((tmp = read_slavemem32 (adev, (u32) tmp)) != 0x02000000) { | ||
15951 | + tmp <<= 5; | ||
15952 | + p += sprintf (p, " %04x", tmp); | ||
15953 | + } | ||
15954 | + } | ||
15955 | + p += sprintf (p, "\n"); | ||
15956 | + p += sprintf (p, " %04x: %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %02x %02x %02x %02x\n" | ||
15957 | + "%02x %02x %02x %02x %04x\n", | ||
15958 | + (u32) txdesc, | ||
15959 | + txd.pNextDesc.v, txd.HostMemPtr.v, txd.AcxMemPtr.v, txd.tx_time, | ||
15960 | + txd.total_length, txd.Reserved, | ||
15961 | + txd.dummy[0], txd.dummy[1], txd.dummy[2], txd.dummy[3], | ||
15962 | + txd.Ctl_8, txd.Ctl2_8, txd.error, txd.ack_failures, | ||
15963 | + txd.u.rts.rts_failures, txd.u.rts.rts_ok, txd.u.r1.rate, txd.u.r1.queue_ctrl, | ||
15964 | + txd.queue_info | ||
15965 | + ); | ||
15966 | + if (txd.AcxMemPtr.v) { | ||
15967 | + copy_from_slavemem (adev, buf, txd.AcxMemPtr.v, sizeof (buf)); | ||
15968 | + for (j = 0; (j < txd.total_length) && (j<(sizeof(buf)-4)); j+=16) { | ||
15969 | + p += sprintf (p, " "); | ||
15970 | + for (k = 0; (k < 16) && (j+k < txd.total_length); k++) { | ||
15971 | + p += sprintf (p, " %02x", buf[j+k+4]); | ||
15972 | + } | ||
15973 | + p += sprintf (p, "\n"); | ||
15974 | + } | ||
15975 | + } | ||
15976 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
15977 | + } | ||
15978 | + } | ||
15979 | + | ||
15980 | + p += sprintf(p, | ||
15981 | + "\n" | ||
15982 | + "** Generic slave data **\n" | ||
15983 | + "irq_mask 0x%04x irq_status 0x%04x irq on acx 0x%04x\n" | ||
15984 | + "txbuf_start 0x%p, txbuf_area_size %u\n" | ||
15985 | + "txdesc_size %u, txdesc_start 0x%p\n" | ||
15986 | + "txhostdesc_start 0x%p, txhostdesc_area_size %u\n" | ||
15987 | + "txbuf start 0x%04x, txbuf size %d\n" | ||
15988 | + "rxdesc_start 0x%p\n" | ||
15989 | + "rxhostdesc_start 0x%p, rxhostdesc_area_size %u\n" | ||
15990 | + "rxbuf_start 0x%p, rxbuf_area_size %u\n", | ||
15991 | + adev->irq_mask, adev->irq_status, read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES), | ||
15992 | + adev->txbuf_start, adev->txbuf_area_size, | ||
15993 | + adev->txdesc_size, adev->txdesc_start, | ||
15994 | + adev->txhostdesc_start, adev->txhostdesc_area_size, | ||
15995 | + adev->acx_txbuf_start, adev->acx_txbuf_numblocks * adev->memblocksize, | ||
15996 | + adev->rxdesc_start, | ||
15997 | + adev->rxhostdesc_start, adev->rxhostdesc_area_size, | ||
15998 | + adev->rxbuf_start, adev->rxbuf_area_size); | ||
15999 | + FN_EXIT0; | ||
16000 | + return p; | ||
16001 | +} | ||
16002 | + | ||
16003 | + | ||
16004 | +/*********************************************************************** | ||
16005 | +*/ | ||
16006 | +int | ||
16007 | +acxmem_proc_eeprom_output(char *buf, acx_device_t *adev) | ||
16008 | +{ | ||
16009 | + char *p = buf; | ||
16010 | + int i; | ||
16011 | + | ||
16012 | + FN_ENTER; | ||
16013 | + | ||
16014 | + for (i = 0; i < 0x400; i++) { | ||
16015 | + acxmem_read_eeprom_byte(adev, i, p++); | ||
16016 | + } | ||
16017 | + | ||
16018 | + FN_EXIT1(p - buf); | ||
16019 | + return p - buf; | ||
16020 | +} | ||
16021 | + | ||
16022 | + | ||
16023 | +/*********************************************************************** | ||
16024 | +*/ | ||
16025 | +void | ||
16026 | +acxmem_set_interrupt_mask(acx_device_t *adev) | ||
16027 | +{ | ||
16028 | + if (IS_ACX111(adev)) { | ||
16029 | + adev->irq_mask = (u16) ~(0 | ||
16030 | + | HOST_INT_RX_DATA | ||
16031 | + | HOST_INT_TX_COMPLETE | ||
16032 | + /* | HOST_INT_TX_XFER */ | ||
16033 | + /* | HOST_INT_RX_COMPLETE */ | ||
16034 | + /* | HOST_INT_DTIM */ | ||
16035 | + /* | HOST_INT_BEACON */ | ||
16036 | + /* | HOST_INT_TIMER */ | ||
16037 | + /* | HOST_INT_KEY_NOT_FOUND */ | ||
16038 | + | HOST_INT_IV_ICV_FAILURE | ||
16039 | + | HOST_INT_CMD_COMPLETE | ||
16040 | + | HOST_INT_INFO | ||
16041 | + | HOST_INT_OVERFLOW | ||
16042 | + /* | HOST_INT_PROCESS_ERROR */ | ||
16043 | + | HOST_INT_SCAN_COMPLETE | ||
16044 | + | HOST_INT_FCS_THRESHOLD | ||
16045 | + | HOST_INT_UNKNOWN | ||
16046 | + ); | ||
16047 | + /* Or else acx100 won't signal cmd completion, right? */ | ||
16048 | + adev->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */ | ||
16049 | + } else { | ||
16050 | + adev->irq_mask = (u16) ~(0 | ||
16051 | + | HOST_INT_RX_DATA | ||
16052 | + | HOST_INT_TX_COMPLETE | ||
16053 | + /* | HOST_INT_TX_XFER */ | ||
16054 | + /* | HOST_INT_RX_COMPLETE */ | ||
16055 | + /* | HOST_INT_DTIM */ | ||
16056 | + /* | HOST_INT_BEACON */ | ||
16057 | + /* | HOST_INT_TIMER */ | ||
16058 | + /* | HOST_INT_KEY_NOT_FOUND */ | ||
16059 | + /* | HOST_INT_IV_ICV_FAILURE */ | ||
16060 | + | HOST_INT_CMD_COMPLETE | ||
16061 | + | HOST_INT_INFO | ||
16062 | + /* | HOST_INT_OVERFLOW */ | ||
16063 | + /* | HOST_INT_PROCESS_ERROR */ | ||
16064 | + | HOST_INT_SCAN_COMPLETE | ||
16065 | + /* | HOST_INT_FCS_THRESHOLD */ | ||
16066 | + /* | HOST_INT_BEACON_MISSED */ | ||
16067 | + ); | ||
16068 | + adev->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */ | ||
16069 | + } | ||
16070 | +} | ||
16071 | + | ||
16072 | + | ||
16073 | +/*********************************************************************** | ||
16074 | +*/ | ||
16075 | +int | ||
16076 | +acx100mem_s_set_tx_level(acx_device_t *adev, u8 level_dbm) | ||
16077 | +{ | ||
16078 | + struct acx111_ie_tx_level tx_level; | ||
16079 | + | ||
16080 | + /* since it can be assumed that at least the Maxim radio has a | ||
16081 | + * maximum power output of 20dBm and since it also can be | ||
16082 | + * assumed that these values drive the DAC responsible for | ||
16083 | + * setting the linear Tx level, I'd guess that these values | ||
16084 | + * should be the corresponding linear values for a dBm value, | ||
16085 | + * in other words: calculate the values from that formula: | ||
16086 | + * Y [dBm] = 10 * log (X [mW]) | ||
16087 | + * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm) | ||
16088 | + * and you're done... | ||
16089 | + * Hopefully that's ok, but you never know if we're actually | ||
16090 | + * right... (especially since Windows XP doesn't seem to show | ||
16091 | + * actual Tx dBm values :-P) */ | ||
16092 | + | ||
16093 | + /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the | ||
16094 | + * values are EXACTLY mW!!! Not sure about RFMD and others, | ||
16095 | + * though... */ | ||
16096 | + static const u8 dbm2val_maxim[21] = { | ||
16097 | + 63, 63, 63, 62, | ||
16098 | + 61, 61, 60, 60, | ||
16099 | + 59, 58, 57, 55, | ||
16100 | + 53, 50, 47, 43, | ||
16101 | + 38, 31, 23, 13, | ||
16102 | + 0 | ||
16103 | + }; | ||
16104 | + static const u8 dbm2val_rfmd[21] = { | ||
16105 | + 0, 0, 0, 1, | ||
16106 | + 2, 2, 3, 3, | ||
16107 | + 4, 5, 6, 8, | ||
16108 | + 10, 13, 16, 20, | ||
16109 | + 25, 32, 41, 50, | ||
16110 | + 63 | ||
16111 | + }; | ||
16112 | + const u8 *table; | ||
16113 | + | ||
16114 | + switch (adev->radio_type) { | ||
16115 | + case RADIO_MAXIM_0D: | ||
16116 | + table = &dbm2val_maxim[0]; | ||
16117 | + break; | ||
16118 | + case RADIO_RFMD_11: | ||
16119 | + case RADIO_RALINK_15: | ||
16120 | + table = &dbm2val_rfmd[0]; | ||
16121 | + break; | ||
16122 | + default: | ||
16123 | + printk("%s: unknown/unsupported radio type, " | ||
16124 | + "cannot modify tx power level yet!\n", | ||
16125 | + adev->ndev->name); | ||
16126 | + return NOT_OK; | ||
16127 | + } | ||
16128 | + /* | ||
16129 | + * The hx4700 EEPROM, at least, only supports 1 power setting. The configure | ||
16130 | + * routine matches the PA bias with the gain, so just use its default value. | ||
16131 | + * The values are: 0x2b for the gain and 0x03 for the PA bias. The firmware | ||
16132 | + * writes the gain level to the Tx gain control DAC and the PA bias to the Maxim | ||
16133 | + * radio's PA bias register. The firmware limits itself to 0 - 64 when writing to the | ||
16134 | + * gain control DAC. | ||
16135 | + * | ||
16136 | + * Physically between the ACX and the radio, higher Tx gain control DAC values result | ||
16137 | + * in less power output; 0 volts to the Maxim radio results in the highest output power | ||
16138 | + * level, which I'm assuming matches up with 0 in the Tx Gain DAC register. | ||
16139 | + * | ||
16140 | + * Although there is only the 1 power setting, one of the radio firmware functions adjusts | ||
16141 | + * the transmit power level up and down. That function is called by the ACX FIQ handler | ||
16142 | + * under certain conditions. | ||
16143 | + */ | ||
16144 | + tx_level.level = 1; | ||
16145 | + //return acx_s_configure(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); | ||
16146 | + | ||
16147 | + printk("%s: changing radio power level to %u dBm (%u)\n", | ||
16148 | + adev->ndev->name, level_dbm, table[level_dbm]); | ||
16149 | + acxmem_s_write_phy_reg(adev, 0x11, table[level_dbm]); | ||
16150 | + | ||
16151 | + return 0; | ||
16152 | +} | ||
16153 | + | ||
16154 | +void acxmem_e_release(struct device *dev) { | ||
16155 | +} | ||
16156 | + | ||
16157 | +/*********************************************************************** | ||
16158 | +** acx_cs part | ||
16159 | +** | ||
16160 | +** called by pcmcia card service | ||
16161 | +*/ | ||
16162 | + | ||
16163 | +/* | ||
16164 | + The event() function is this driver's Card Services event handler. | ||
16165 | + It will be called by Card Services when an appropriate card status | ||
16166 | + event is received. The config() and release() entry points are | ||
16167 | + used to configure or release a socket, in response to card | ||
16168 | + insertion and ejection events. They are invoked from the acx_cs | ||
16169 | + event handler. | ||
16170 | +*/ | ||
16171 | + | ||
16172 | +static int acx_cs_config(struct pcmcia_device *link); | ||
16173 | +static void acx_cs_release(struct pcmcia_device *link); | ||
16174 | + | ||
16175 | +/* | ||
16176 | + The attach() and detach() entry points are used to create and destroy | ||
16177 | + "instances" of the driver, where each instance represents everything | ||
16178 | + needed to manage one actual PCMCIA card. | ||
16179 | +*/ | ||
16180 | + | ||
16181 | +static void acx_cs_detach(struct pcmcia_device *p_dev); | ||
16182 | + | ||
16183 | +/* | ||
16184 | + You'll also need to prototype all the functions that will actually | ||
16185 | + be used to talk to your device. See 'pcmem_cs' for a good example | ||
16186 | + of a fully self-sufficient driver; the other drivers rely more or | ||
16187 | + less on other parts of the kernel. | ||
16188 | +*/ | ||
16189 | + | ||
16190 | +/* | ||
16191 | + A linked list of "instances" of the acxnet device. Each actual | ||
16192 | + PCMCIA card corresponds to one device instance, and is described | ||
16193 | + by one struct pcmcia_device structure (defined in ds.h). | ||
16194 | + | ||
16195 | + You may not want to use a linked list for this -- for example, the | ||
16196 | + memory card driver uses an array of struct pcmcia_device pointers, where minor | ||
16197 | + device numbers are used to derive the corresponding array index. | ||
16198 | +*/ | ||
16199 | + | ||
16200 | +/* | ||
16201 | + A driver needs to provide a dev_node_t structure for each device | ||
16202 | + on a card. In some cases, there is only one device per card (for | ||
16203 | + example, ethernet cards, modems). In other cases, there may be | ||
16204 | + many actual or logical devices (SCSI adapters, memory cards with | ||
16205 | + multiple partitions). The dev_node_t structures need to be kept | ||
16206 | + in a linked list starting at the 'dev' field of a struct pcmcia_device | ||
16207 | + structure. We allocate them in the card's private data structure, | ||
16208 | + because they generally shouldn't be allocated dynamically. | ||
16209 | + | ||
16210 | + In this case, we also provide a flag to indicate if a device is | ||
16211 | + "stopped" due to a power management event, or card ejection. The | ||
16212 | + device IO routines can use a flag like this to throttle IO to a | ||
16213 | + card that is not ready to accept it. | ||
16214 | +*/ | ||
16215 | + | ||
16216 | + | ||
16217 | +/*====================================================================== | ||
16218 | + | ||
16219 | + acx_attach() creates an "instance" of the driver, allocating | ||
16220 | + local data structures for one device. The device is registered | ||
16221 | + with Card Services. | ||
16222 | + | ||
16223 | + The dev_link structure is initialized, but we don't actually | ||
16224 | + configure the card at this point -- we wait until we receive a | ||
16225 | + card insertion event. | ||
16226 | + | ||
16227 | + ======================================================================*/ | ||
16228 | + | ||
16229 | +static int acx_cs_probe(struct pcmcia_device *link) | ||
16230 | +{ | ||
16231 | + local_info_t *local; | ||
16232 | + struct net_device *ndev; | ||
16233 | + | ||
16234 | + DEBUG(0, "acx_attach()\n"); | ||
16235 | + | ||
16236 | + ndev = alloc_netdev(sizeof(acx_device_t), "wlan%d", dummy_netdev_init); | ||
16237 | + if (!ndev) { | ||
16238 | + printk("acx: no memory for netdevice struct\n"); | ||
16239 | + return -ENOMEM; | ||
16240 | + } | ||
16241 | + | ||
16242 | + /* Interrupt setup */ | ||
16243 | + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; | ||
16244 | + link->irq.IRQInfo1 = IRQ_LEVEL_ID; | ||
16245 | + link->irq.Handler = acxmem_i_interrupt; | ||
16246 | + link->irq.Instance = ndev; | ||
16247 | + | ||
16248 | + /* | ||
16249 | + General socket configuration defaults can go here. In this | ||
16250 | + client, we assume very little, and rely on the CIS for almost | ||
16251 | + everything. In most clients, many details (i.e., number, sizes, | ||
16252 | + and attributes of IO windows) are fixed by the nature of the | ||
16253 | + device, and can be hard-wired here. | ||
16254 | + */ | ||
16255 | + link->conf.Attributes = CONF_ENABLE_IRQ; | ||
16256 | + link->conf.IntType = INT_MEMORY_AND_IO; | ||
16257 | + link->conf.Present = PRESENT_OPTION | PRESENT_COPY; | ||
16258 | + | ||
16259 | + /* Allocate space for private device-specific data */ | ||
16260 | + local = kzalloc(sizeof(local_info_t), GFP_KERNEL); | ||
16261 | + if (!local) { | ||
16262 | + printk(KERN_ERR "acx_cs: no memory for new device\n"); | ||
16263 | + return -ENOMEM; | ||
16264 | + } | ||
16265 | + local->ndev = ndev; | ||
16266 | + | ||
16267 | + link->priv = local; | ||
16268 | + | ||
16269 | + return acx_cs_config(link); | ||
16270 | +} /* acx_attach */ | ||
16271 | + | ||
16272 | +/*====================================================================== | ||
16273 | + | ||
16274 | + This deletes a driver "instance". The device is de-registered | ||
16275 | + with Card Services. If it has been released, all local data | ||
16276 | + structures are freed. Otherwise, the structures will be freed | ||
16277 | + when the device is released. | ||
16278 | + | ||
16279 | + ======================================================================*/ | ||
16280 | + | ||
16281 | +static void acx_cs_detach(struct pcmcia_device *link) | ||
16282 | +{ | ||
16283 | + DEBUG(0, "acx_detach(0x%p)\n", link); | ||
16284 | + | ||
16285 | + | ||
16286 | + if ( ((local_info_t*)link->priv)->ndev ) { | ||
16287 | + acxmem_e_close( ((local_info_t*)link->priv)->ndev ); | ||
16288 | + } | ||
16289 | + | ||
16290 | + acx_cs_release(link); | ||
16291 | + | ||
16292 | + ((local_info_t*)link->priv)->ndev = NULL; | ||
16293 | + | ||
16294 | + kfree(link->priv); | ||
16295 | +} /* acx_detach */ | ||
16296 | + | ||
16297 | +/*====================================================================== | ||
16298 | + | ||
16299 | + acx_config() is scheduled to run after a CARD_INSERTION event | ||
16300 | + is received, to configure the PCMCIA socket, and to make the | ||
16301 | + device available to the system. | ||
16302 | + | ||
16303 | + ======================================================================*/ | ||
16304 | + | ||
16305 | +#define CS_CHECK(fn, ret) \ | ||
16306 | +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) | ||
16307 | + | ||
16308 | +static int acx_cs_config(struct pcmcia_device *link) | ||
16309 | +{ | ||
16310 | + tuple_t tuple; | ||
16311 | + cisparse_t parse; | ||
16312 | + local_info_t *local = link->priv; | ||
16313 | + int last_fn, last_ret; | ||
16314 | + u_char buf[64]; | ||
16315 | + win_req_t req; | ||
16316 | + memreq_t map; | ||
16317 | +// int i; | ||
16318 | +// acx_device_t *adev; | ||
16319 | + | ||
16320 | +// adev = (acx_device_t *)link->priv; | ||
16321 | + | ||
16322 | + DEBUG(0, "acx_cs_config(0x%p)\n", link); | ||
16323 | + | ||
16324 | + /* | ||
16325 | + In this loop, we scan the CIS for configuration table entries, | ||
16326 | + each of which describes a valid card configuration, including | ||
16327 | + voltage, IO window, memory window, and interrupt settings. | ||
16328 | + | ||
16329 | + We make no assumptions about the card to be configured: we use | ||
16330 | + just the information available in the CIS. In an ideal world, | ||
16331 | + this would work for any PCMCIA card, but it requires a complete | ||
16332 | + and accurate CIS. In practice, a driver usually "knows" most of | ||
16333 | + these things without consulting the CIS, and most client drivers | ||
16334 | + will only use the CIS to fill in implementation-defined details. | ||
16335 | + */ | ||
16336 | + tuple.Attributes = 0; | ||
16337 | + tuple.TupleData = (cisdata_t *)buf; | ||
16338 | + tuple.TupleDataMax = sizeof(buf); | ||
16339 | + tuple.TupleOffset = 0; | ||
16340 | + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; | ||
16341 | + | ||
16342 | + /* don't trust the CIS on this; Linksys got it wrong */ | ||
16343 | + //link->conf.Present = 0x63; | ||
16344 | + | ||
16345 | + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); | ||
16346 | + while (1) { | ||
16347 | + cistpl_cftable_entry_t dflt = { 0 }; | ||
16348 | + cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); | ||
16349 | + if (pcmcia_get_tuple_data(link, &tuple) != 0 || | ||
16350 | + pcmcia_parse_tuple(link, &tuple, &parse) != 0) | ||
16351 | + goto next_entry; | ||
16352 | + | ||
16353 | + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; | ||
16354 | + if (cfg->index == 0) goto next_entry; | ||
16355 | + link->conf.ConfigIndex = cfg->index; | ||
16356 | + | ||
16357 | + /* Does this card need audio output? */ | ||
16358 | + if (cfg->flags & CISTPL_CFTABLE_AUDIO) { | ||
16359 | + link->conf.Attributes |= CONF_ENABLE_SPKR; | ||
16360 | + link->conf.Status = CCSR_AUDIO_ENA; | ||
16361 | + } | ||
16362 | + | ||
16363 | + /* Use power settings for Vcc and Vpp if present */ | ||
16364 | + /* Note that the CIS values need to be rescaled */ | ||
16365 | + if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM)) | ||
16366 | + link->conf.Vpp = | ||
16367 | + cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; | ||
16368 | + else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM)) | ||
16369 | + link->conf.Vpp = | ||
16370 | + dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; | ||
16371 | + | ||
16372 | + /* Do we need to allocate an interrupt? */ | ||
16373 | + if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) | ||
16374 | + link->conf.Attributes |= CONF_ENABLE_IRQ; | ||
16375 | + if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) { | ||
16376 | + cistpl_mem_t *mem = | ||
16377 | + (cfg->mem.nwin) ? &cfg->mem : &dflt.mem; | ||
16378 | +// req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_AM|WIN_ENABLE|WIN_USE_WAIT; | ||
16379 | + req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE|WIN_USE_WAIT; | ||
16380 | + req.Base = mem->win[0].host_addr; | ||
16381 | + req.Size = mem->win[0].len; | ||
16382 | + req.Size=0x1000; | ||
16383 | + req.AccessSpeed = 0; | ||
16384 | + if (pcmcia_request_window(&link, &req, &link->win) != 0) | ||
16385 | + goto next_entry; | ||
16386 | + map.Page = 0; map.CardOffset = mem->win[0].card_addr; | ||
16387 | + if (pcmcia_map_mem_page(link->win, &map) != 0) | ||
16388 | + goto next_entry; | ||
16389 | + else | ||
16390 | + printk(KERN_INFO "MEMORY WINDOW FOUND!!!\n"); | ||
16391 | + } | ||
16392 | + /* If we got this far, we're cool! */ | ||
16393 | + break; | ||
16394 | + | ||
16395 | + next_entry: | ||
16396 | + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); | ||
16397 | + } | ||
16398 | + | ||
16399 | + if (link->conf.Attributes & CONF_ENABLE_IRQ) { | ||
16400 | + printk(KERN_INFO "requesting Irq...\n"); | ||
16401 | + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); | ||
16402 | + } | ||
16403 | + | ||
16404 | + /* | ||
16405 | + This actually configures the PCMCIA socket -- setting up | ||
16406 | + the I/O windows and the interrupt mapping, and putting the | ||
16407 | + card and host interface into "Memory and IO" mode. | ||
16408 | + */ | ||
16409 | + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); | ||
16410 | + DEBUG(0,"RequestConfiguration OK\n"); | ||
16411 | + | ||
16412 | + | ||
16413 | + memwin.Base=req.Base; | ||
16414 | + memwin.Size=req.Size; | ||
16415 | + | ||
16416 | + acx_init_netdev(local->ndev, &link->dev, memwin.Base, memwin.Size, link->irq.AssignedIRQ); | ||
16417 | + | ||
16418 | +#if 1 | ||
16419 | + /* | ||
16420 | + At this point, the dev_node_t structure(s) need to be | ||
16421 | + initialized and arranged in a linked list at link->dev_node. | ||
16422 | + */ | ||
16423 | + strcpy(local->node.dev_name, local->ndev->name ); | ||
16424 | + local->node.major = local->node.minor = 0; | ||
16425 | + link->dev_node = &local->node; | ||
16426 | + | ||
16427 | + /* Finally, report what we've done */ | ||
16428 | + printk(KERN_INFO "%s: index 0x%02x: ", | ||
16429 | + local->ndev->name, link->conf.ConfigIndex); | ||
16430 | +#endif | ||
16431 | + if (link->conf.Attributes & CONF_ENABLE_IRQ) | ||
16432 | + printk("irq %d", link->irq.AssignedIRQ); | ||
16433 | + if (link->io.NumPorts1) | ||
16434 | + printk(", io 0x%04x-0x%04x", link->io.BasePort1, | ||
16435 | + link->io.BasePort1+link->io.NumPorts1-1); | ||
16436 | + if (link->io.NumPorts2) | ||
16437 | + printk(" & 0x%04x-0x%04x", link->io.BasePort2, | ||
16438 | + link->io.BasePort2+link->io.NumPorts2-1); | ||
16439 | + if (link->win) | ||
16440 | + printk(", mem 0x%06lx-0x%06lx\n", req.Base, | ||
16441 | + req.Base+req.Size-1); | ||
16442 | + return 0; | ||
16443 | + | ||
16444 | + cs_failed: | ||
16445 | + cs_error(link, last_fn, last_ret); | ||
16446 | + acx_cs_release(link); | ||
16447 | + return -ENODEV; | ||
16448 | +} /* acx_config */ | ||
16449 | + | ||
16450 | +/*====================================================================== | ||
16451 | + | ||
16452 | + After a card is removed, acx_release() will unregister the | ||
16453 | + device, and release the PCMCIA configuration. If the device is | ||
16454 | + still open, this will be postponed until it is closed. | ||
16455 | + | ||
16456 | + ======================================================================*/ | ||
16457 | + | ||
16458 | +static void acx_cs_release(struct pcmcia_device *link) | ||
16459 | +{ | ||
16460 | + DEBUG(0, "acx_release(0x%p)\n", link); | ||
16461 | + acxmem_e_remove(link); | ||
16462 | + pcmcia_disable_device(link); | ||
16463 | +} | ||
16464 | + | ||
16465 | +static int acx_cs_suspend(struct pcmcia_device *link) | ||
16466 | +{ | ||
16467 | + local_info_t *local = link->priv; | ||
16468 | + | ||
16469 | + pm_message_t state; | ||
16470 | + acxmem_e_suspend ( local->ndev, state); | ||
16471 | + /* Already done in suspend | ||
16472 | + * netif_device_detach(local->ndev); */ | ||
16473 | + | ||
16474 | + return 0; | ||
16475 | +} | ||
16476 | + | ||
16477 | +static int acx_cs_resume(struct pcmcia_device *link) | ||
16478 | +{ | ||
16479 | + local_info_t *local = link->priv; | ||
16480 | + | ||
16481 | + FN_ENTER; | ||
16482 | + resume_ndev = local->ndev; | ||
16483 | + | ||
16484 | + schedule_work( &fw_resume_work ); | ||
16485 | + | ||
16486 | + /* Already done in suspend | ||
16487 | + if (link->open) { | ||
16488 | + // do we need reset for ACX, if so what function nane is ? | ||
16489 | + //reset_acx_card(local->eth_dev); | ||
16490 | + netif_device_attach(local->ndev); | ||
16491 | + } */ | ||
16492 | + | ||
16493 | + FN_EXIT0; | ||
16494 | + return 0; | ||
16495 | +} | ||
16496 | + | ||
16497 | +static struct pcmcia_device_id acx_ids[] = { | ||
16498 | + PCMCIA_DEVICE_MANF_CARD(0x0097, 0x8402), | ||
16499 | + PCMCIA_DEVICE_MANF_CARD(0x0250, 0xb001), | ||
16500 | + PCMCIA_DEVICE_NULL, | ||
16501 | +}; | ||
16502 | +MODULE_DEVICE_TABLE(pcmcia, acx_ids); | ||
16503 | + | ||
16504 | +static struct pcmcia_driver acx_driver = { | ||
16505 | + .owner = THIS_MODULE, | ||
16506 | + .drv = { | ||
16507 | + .name = "acx_cs", | ||
16508 | + }, | ||
16509 | + .probe = acx_cs_probe, | ||
16510 | + .remove = acx_cs_detach, | ||
16511 | + .id_table = acx_ids, | ||
16512 | + .suspend = acx_cs_suspend, | ||
16513 | + .resume = acx_cs_resume, | ||
16514 | +}; | ||
16515 | + | ||
16516 | +int acx_cs_init(void) | ||
16517 | +{ | ||
16518 | + /* return success if at least one succeeded */ | ||
16519 | + DEBUG(0, "acxcs_init()\n"); | ||
16520 | + return pcmcia_register_driver(&acx_driver); | ||
16521 | +} | ||
16522 | + | ||
16523 | +void acx_cs_cleanup(void) | ||
16524 | +{ | ||
16525 | + pcmcia_unregister_driver(&acx_driver); | ||
16526 | +} | ||
16527 | + | ||
16528 | +/* | ||
16529 | + This program is free software; you can redistribute it and/or | ||
16530 | + modify it under the terms of the GNU General Public License | ||
16531 | + as published by the Free Software Foundation; either version 2 | ||
16532 | + of the License, or (at your option) any later version. | ||
16533 | + | ||
16534 | + This program is distributed in the hope that it will be useful, | ||
16535 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16536 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16537 | + GNU General Public License for more details. | ||
16538 | + | ||
16539 | + In addition: | ||
16540 | + | ||
16541 | + Redistribution and use in source and binary forms, with or without | ||
16542 | + modification, are permitted provided that the following conditions | ||
16543 | + are met: | ||
16544 | + | ||
16545 | + 1. Redistributions of source code must retain the above copyright | ||
16546 | + notice, this list of conditions and the following disclaimer. | ||
16547 | + 2. Redistributions in binary form must reproduce the above copyright | ||
16548 | + notice, this list of conditions and the following disclaimer in the | ||
16549 | + documentation and/or other materials provided with the distribution. | ||
16550 | + 3. The name of the author may not be used to endorse or promote | ||
16551 | + products derived from this software without specific prior written | ||
16552 | + permission. | ||
16553 | + | ||
16554 | + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
16555 | + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
16556 | + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
16557 | + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | ||
16558 | + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
16559 | + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
16560 | + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
16561 | + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
16562 | + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | ||
16563 | + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
16564 | + POSSIBILITY OF SUCH DAMAGE. | ||
16565 | +*/ | ||
16566 | + | ||
16567 | +MODULE_DESCRIPTION( "ACX Cardbus Driver" ); | ||
16568 | +MODULE_LICENSE( "GPL" ); | ||
16569 | + | ||
16570 | Index: linux-2.6.22/drivers/net/wireless/acx/htcsable_acx.c | ||
16571 | =================================================================== | ||
16572 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
16573 | +++ linux-2.6.22/drivers/net/wireless/acx/htcsable_acx.c 2007-08-23 18:34:19.000000000 +0200 | ||
16574 | @@ -0,0 +1,118 @@ | ||
16575 | +/* | ||
16576 | + * WLAN (TI TNETW1100B) support in the HTC Sable | ||
16577 | + * | ||
16578 | + * Copyright (c) 2006 SDG Systems, LLC | ||
16579 | + * | ||
16580 | + * This file is subject to the terms and conditions of the GNU General Public | ||
16581 | + * License. See the file COPYING in the main directory of this archive for | ||
16582 | + * more details. | ||
16583 | + * | ||
16584 | + * 28-March-2006 Todd Blumer <todd@sdgsystems.com> | ||
16585 | + */ | ||
16586 | + | ||
16587 | + | ||
16588 | +#include <linux/kernel.h> | ||
16589 | +#include <linux/platform_device.h> | ||
16590 | +#include <linux/delay.h> | ||
16591 | + | ||
16592 | +#include <asm/hardware.h> | ||
16593 | + | ||
16594 | +#include <asm/arch/pxa-regs.h> | ||
16595 | +#include <linux/mfd/asic3_base.h> | ||
16596 | +#include <asm/arch/htcsable-gpio.h> | ||
16597 | +#include <asm/arch/htcsable-asic.h> | ||
16598 | +#include <asm/io.h> | ||
16599 | + | ||
16600 | +#include "acx_hw.h" | ||
16601 | + | ||
16602 | +#define WLAN_BASE PXA_CS2_PHYS | ||
16603 | + | ||
16604 | +/* | ||
16605 | +off: b15 c8 d3 | ||
16606 | +on: d3 c8 b5 b5- | ||
16607 | +*/ | ||
16608 | + | ||
16609 | +#define GPIO_NR_HTCSABLE_ACX111 111 | ||
16610 | + | ||
16611 | +static int | ||
16612 | +htcsable_wlan_stop( void ); | ||
16613 | + | ||
16614 | +static int | ||
16615 | +htcsable_wlan_start( void ) | ||
16616 | +{ | ||
16617 | + printk( "htcsable_wlan_start\n" ); | ||
16618 | + | ||
16619 | + /*asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_RESET, 0);*/ | ||
16620 | + asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_PWR_3, 1<<GPIOC_ACX_PWR_3); /* related to acx */ | ||
16621 | + SET_HTCSABLE_GPIO(ACX111, 1); | ||
16622 | + asic3_set_gpio_out_b(&htcsable_asic3.dev, 1<<GPIOB_ACX_PWR_1, 1<<GPIOB_ACX_PWR_1); | ||
16623 | + asic3_set_gpio_out_d(&htcsable_asic3.dev, 1<<GPIOD_ACX_PWR_2, 1<<GPIOD_ACX_PWR_2); | ||
16624 | + mdelay(260); | ||
16625 | + asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_RESET, 1<<GPIOC_ACX_RESET); | ||
16626 | + | ||
16627 | + return 0; | ||
16628 | +} | ||
16629 | + | ||
16630 | +static int | ||
16631 | +htcsable_wlan_stop( void ) | ||
16632 | +{ | ||
16633 | + printk( "htcsable_wlan_stop\n" ); | ||
16634 | + asic3_set_gpio_out_b(&htcsable_asic3.dev, 1<<GPIOB_ACX_PWR_1, 0); | ||
16635 | + asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_RESET, 0); | ||
16636 | + asic3_set_gpio_out_d(&htcsable_asic3.dev, 1<<GPIOD_ACX_PWR_2, 0); | ||
16637 | + SET_HTCSABLE_GPIO(ACX111, 0); /* not necessary to power down this one? */ | ||
16638 | + asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_PWR_3, 0); /* not necessary to power down this one? */ | ||
16639 | + | ||
16640 | + return 0; | ||
16641 | +} | ||
16642 | + | ||
16643 | +static struct resource acx_resources[] = { | ||
16644 | + [0] = { | ||
16645 | + .start = WLAN_BASE, | ||
16646 | + .end = WLAN_BASE + 0x20, | ||
16647 | + .flags = IORESOURCE_MEM, | ||
16648 | + }, | ||
16649 | + [1] = { | ||
16650 | +// .start = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N, | ||
16651 | +// .end = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N, | ||
16652 | + .flags = IORESOURCE_IRQ, | ||
16653 | + }, | ||
16654 | +}; | ||
16655 | + | ||
16656 | +static struct acx_hardware_data acx_data = { | ||
16657 | + .start_hw = htcsable_wlan_start, | ||
16658 | + .stop_hw = htcsable_wlan_stop, | ||
16659 | +}; | ||
16660 | + | ||
16661 | +static struct platform_device acx_device = { | ||
16662 | + .name = "acx-mem", | ||
16663 | + .dev = { | ||
16664 | + .platform_data = &acx_data, | ||
16665 | + }, | ||
16666 | + .num_resources = ARRAY_SIZE( acx_resources ), | ||
16667 | + .resource = acx_resources, | ||
16668 | +}; | ||
16669 | + | ||
16670 | +static int __init | ||
16671 | +htcsable_wlan_init( void ) | ||
16672 | +{ | ||
16673 | + printk( "htcsable_wlan_init: acx-mem platform_device_register\n" ); | ||
16674 | + acx_device.resource[1].start = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOB_IRQ_BASE+GPIOB_ACX_IRQ_N; | ||
16675 | + acx_device.resource[1].end = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOB_IRQ_BASE+GPIOB_ACX_IRQ_N; | ||
16676 | + return platform_device_register( &acx_device ); | ||
16677 | +} | ||
16678 | + | ||
16679 | + | ||
16680 | +static void __exit | ||
16681 | +htcsable_wlan_exit( void ) | ||
16682 | +{ | ||
16683 | + platform_device_unregister( &acx_device ); | ||
16684 | +} | ||
16685 | + | ||
16686 | +module_init( htcsable_wlan_init ); | ||
16687 | +module_exit( htcsable_wlan_exit ); | ||
16688 | + | ||
16689 | +MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" ); | ||
16690 | +MODULE_DESCRIPTION( "WLAN driver for HTC Sable" ); | ||
16691 | +MODULE_LICENSE( "GPL" ); | ||
16692 | + | ||
16693 | Index: linux-2.6.22/drivers/net/wireless/acx/htcuniversal_acx.c | ||
16694 | =================================================================== | ||
16695 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
16696 | +++ linux-2.6.22/drivers/net/wireless/acx/htcuniversal_acx.c 2007-08-23 18:34:19.000000000 +0200 | ||
16697 | @@ -0,0 +1,108 @@ | ||
16698 | +/* | ||
16699 | + * WLAN (TI TNETW1100B) support in the HTC Universal | ||
16700 | + * | ||
16701 | + * Copyright (c) 2006 SDG Systems, LLC | ||
16702 | + * | ||
16703 | + * This file is subject to the terms and conditions of the GNU General Public | ||
16704 | + * License. See the file COPYING in the main directory of this archive for | ||
16705 | + * more details. | ||
16706 | + * | ||
16707 | + * 28-March-2006 Todd Blumer <todd@sdgsystems.com> | ||
16708 | + */ | ||
16709 | + | ||
16710 | + | ||
16711 | +#include <linux/kernel.h> | ||
16712 | +#include <linux/platform_device.h> | ||
16713 | +#include <linux/delay.h> | ||
16714 | + | ||
16715 | +#include <asm/hardware.h> | ||
16716 | + | ||
16717 | +#include <asm/arch/pxa-regs.h> | ||
16718 | +#include <linux/soc/asic3_base.h> | ||
16719 | +#include <asm/arch/htcuniversal-gpio.h> | ||
16720 | +#include <asm/arch/htcuniversal-asic.h> | ||
16721 | +#include <asm/io.h> | ||
16722 | + | ||
16723 | +#include "acx_hw.h" | ||
16724 | + | ||
16725 | +#define WLAN_BASE PXA_CS2_PHYS | ||
16726 | + | ||
16727 | + | ||
16728 | +static int | ||
16729 | +htcuniversal_wlan_start( void ) | ||
16730 | +{ | ||
16731 | + htcuniversal_egpio_enable(1<<EGPIO6_WIFI_ON); | ||
16732 | + asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_PWR1_ON, 1<<GPIOC_WIFI_PWR1_ON); | ||
16733 | + asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR3_ON, 1<<GPIOD_WIFI_PWR3_ON); | ||
16734 | + asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR2_ON, 1<<GPIOD_WIFI_PWR2_ON); | ||
16735 | + mdelay(100); | ||
16736 | + | ||
16737 | + asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_RESET, 0); | ||
16738 | + mdelay(100); | ||
16739 | + asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_RESET, 1<<GPIOC_WIFI_RESET); | ||
16740 | + mdelay(100); | ||
16741 | + return 0; | ||
16742 | +} | ||
16743 | + | ||
16744 | +static int | ||
16745 | +htcuniversal_wlan_stop( void ) | ||
16746 | +{ | ||
16747 | + asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_RESET, 0); | ||
16748 | + | ||
16749 | + htcuniversal_egpio_disable(1<<EGPIO6_WIFI_ON); | ||
16750 | + asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_PWR1_ON, 0); | ||
16751 | + asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR2_ON, 0); | ||
16752 | + asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR3_ON, 0); | ||
16753 | + return 0; | ||
16754 | +} | ||
16755 | + | ||
16756 | +static struct resource acx_resources[] = { | ||
16757 | + [0] = { | ||
16758 | + .start = WLAN_BASE, | ||
16759 | + .end = WLAN_BASE + 0x20, | ||
16760 | + .flags = IORESOURCE_MEM, | ||
16761 | + }, | ||
16762 | + [1] = { | ||
16763 | +// .start = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N, | ||
16764 | +// .end = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N, | ||
16765 | + .flags = IORESOURCE_IRQ, | ||
16766 | + }, | ||
16767 | +}; | ||
16768 | + | ||
16769 | +static struct acx_hardware_data acx_data = { | ||
16770 | + .start_hw = htcuniversal_wlan_start, | ||
16771 | + .stop_hw = htcuniversal_wlan_stop, | ||
16772 | +}; | ||
16773 | + | ||
16774 | +static struct platform_device acx_device = { | ||
16775 | + .name = "acx-mem", | ||
16776 | + .dev = { | ||
16777 | + .platform_data = &acx_data, | ||
16778 | + }, | ||
16779 | + .num_resources = ARRAY_SIZE( acx_resources ), | ||
16780 | + .resource = acx_resources, | ||
16781 | +}; | ||
16782 | + | ||
16783 | +static int __init | ||
16784 | +htcuniversal_wlan_init( void ) | ||
16785 | +{ | ||
16786 | + printk( "htcuniversal_wlan_init: acx-mem platform_device_register\n" ); | ||
16787 | + acx_device.resource[1].start = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N; | ||
16788 | + acx_device.resource[1].end = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N; | ||
16789 | + return platform_device_register( &acx_device ); | ||
16790 | +} | ||
16791 | + | ||
16792 | + | ||
16793 | +static void __exit | ||
16794 | +htcuniversal_wlan_exit( void ) | ||
16795 | +{ | ||
16796 | + platform_device_unregister( &acx_device ); | ||
16797 | +} | ||
16798 | + | ||
16799 | +module_init( htcuniversal_wlan_init ); | ||
16800 | +module_exit( htcuniversal_wlan_exit ); | ||
16801 | + | ||
16802 | +MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" ); | ||
16803 | +MODULE_DESCRIPTION( "WLAN driver for HTC Universal" ); | ||
16804 | +MODULE_LICENSE( "GPL" ); | ||
16805 | + | ||
16806 | Index: linux-2.6.22/drivers/net/wireless/acx/hx4700_acx.c | ||
16807 | =================================================================== | ||
16808 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
16809 | +++ linux-2.6.22/drivers/net/wireless/acx/hx4700_acx.c 2007-08-23 18:34:19.000000000 +0200 | ||
16810 | @@ -0,0 +1,108 @@ | ||
16811 | +/* | ||
16812 | + * WLAN (TI TNETW1100B) support in the hx470x. | ||
16813 | + * | ||
16814 | + * Copyright (c) 2006 SDG Systems, LLC | ||
16815 | + * | ||
16816 | + * This file is subject to the terms and conditions of the GNU General Public | ||
16817 | + * License. See the file COPYING in the main directory of this archive for | ||
16818 | + * more details. | ||
16819 | + * | ||
16820 | + * 28-March-2006 Todd Blumer <todd@sdgsystems.com> | ||
16821 | + */ | ||
16822 | + | ||
16823 | + | ||
16824 | +#include <linux/kernel.h> | ||
16825 | +#include <linux/platform_device.h> | ||
16826 | +#include <linux/delay.h> | ||
16827 | +#include <linux/leds.h> | ||
16828 | + | ||
16829 | +#include <asm/hardware.h> | ||
16830 | + | ||
16831 | +#include <asm/arch/pxa-regs.h> | ||
16832 | +#include <asm/arch/hx4700-gpio.h> | ||
16833 | +#include <asm/arch/hx4700-core.h> | ||
16834 | +#include <asm/io.h> | ||
16835 | + | ||
16836 | +#include "acx_hw.h" | ||
16837 | + | ||
16838 | +#define WLAN_OFFSET 0x1000000 | ||
16839 | +#define WLAN_BASE (PXA_CS5_PHYS+WLAN_OFFSET) | ||
16840 | + | ||
16841 | + | ||
16842 | +static int | ||
16843 | +hx4700_wlan_start( void ) | ||
16844 | +{ | ||
16845 | + SET_HX4700_GPIO( WLAN_RESET_N, 0 ); | ||
16846 | + mdelay(5); | ||
16847 | + hx4700_egpio_enable( EGPIO0_VCC_3V3_EN ); | ||
16848 | + mdelay(100); | ||
16849 | + hx4700_egpio_enable( EGPIO7_VCC_3V3_WL_EN ); | ||
16850 | + mdelay(150); | ||
16851 | + hx4700_egpio_enable( EGPIO1_WL_VREG_EN | EGPIO2_VCC_2V1_WL_EN | | ||
16852 | + EGPIO6_WL1V8_EN ); | ||
16853 | + mdelay(10); | ||
16854 | + SET_HX4700_GPIO( WLAN_RESET_N, 1 ); | ||
16855 | + mdelay(50); | ||
16856 | + led_trigger_event_shared(hx4700_radio_trig, LED_FULL); | ||
16857 | + return 0; | ||
16858 | +} | ||
16859 | + | ||
16860 | +static int | ||
16861 | +hx4700_wlan_stop( void ) | ||
16862 | +{ | ||
16863 | + hx4700_egpio_disable( EGPIO0_VCC_3V3_EN | EGPIO1_WL_VREG_EN | | ||
16864 | + EGPIO7_VCC_3V3_WL_EN | EGPIO2_VCC_2V1_WL_EN | | ||
16865 | + EGPIO6_WL1V8_EN ); | ||
16866 | + SET_HX4700_GPIO( WLAN_RESET_N, 0 ); | ||
16867 | + led_trigger_event_shared(hx4700_radio_trig, LED_OFF); | ||
16868 | + return 0; | ||
16869 | +} | ||
16870 | + | ||
16871 | +static struct resource acx_resources[] = { | ||
16872 | + [0] = { | ||
16873 | + .start = WLAN_BASE, | ||
16874 | + .end = WLAN_BASE + 0x20, | ||
16875 | + .flags = IORESOURCE_MEM, | ||
16876 | + }, | ||
16877 | + [1] = { | ||
16878 | + .start = HX4700_IRQ(WLAN_IRQ_N), | ||
16879 | + .end = HX4700_IRQ(WLAN_IRQ_N), | ||
16880 | + .flags = IORESOURCE_IRQ, | ||
16881 | + }, | ||
16882 | +}; | ||
16883 | + | ||
16884 | +static struct acx_hardware_data acx_data = { | ||
16885 | + .start_hw = hx4700_wlan_start, | ||
16886 | + .stop_hw = hx4700_wlan_stop, | ||
16887 | +}; | ||
16888 | + | ||
16889 | +static struct platform_device acx_device = { | ||
16890 | + .name = "acx-mem", | ||
16891 | + .dev = { | ||
16892 | + .platform_data = &acx_data, | ||
16893 | + }, | ||
16894 | + .num_resources = ARRAY_SIZE( acx_resources ), | ||
16895 | + .resource = acx_resources, | ||
16896 | +}; | ||
16897 | + | ||
16898 | +static int __init | ||
16899 | +hx4700_wlan_init( void ) | ||
16900 | +{ | ||
16901 | + printk( "hx4700_wlan_init: acx-mem platform_device_register\n" ); | ||
16902 | + return platform_device_register( &acx_device ); | ||
16903 | +} | ||
16904 | + | ||
16905 | + | ||
16906 | +static void __exit | ||
16907 | +hx4700_wlan_exit( void ) | ||
16908 | +{ | ||
16909 | + platform_device_unregister( &acx_device ); | ||
16910 | +} | ||
16911 | + | ||
16912 | +module_init( hx4700_wlan_init ); | ||
16913 | +module_exit( hx4700_wlan_exit ); | ||
16914 | + | ||
16915 | +MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" ); | ||
16916 | +MODULE_DESCRIPTION( "WLAN driver for iPAQ hx4700" ); | ||
16917 | +MODULE_LICENSE( "GPL" ); | ||
16918 | + | ||
16919 | Index: linux-2.6.22/drivers/net/wireless/acx/ioctl.c | ||
16920 | =================================================================== | ||
16921 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
16922 | +++ linux-2.6.22/drivers/net/wireless/acx/ioctl.c 2007-08-23 18:34:19.000000000 +0200 | ||
16923 | @@ -0,0 +1,2748 @@ | ||
16924 | +/*********************************************************************** | ||
16925 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
16926 | +** | ||
16927 | +** The contents of this file are subject to the Mozilla Public | ||
16928 | +** License Version 1.1 (the "License"); you may not use this file | ||
16929 | +** except in compliance with the License. You may obtain a copy of | ||
16930 | +** the License at http://www.mozilla.org/MPL/ | ||
16931 | +** | ||
16932 | +** Software distributed under the License is distributed on an "AS | ||
16933 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
16934 | +** implied. See the License for the specific language governing | ||
16935 | +** rights and limitations under the License. | ||
16936 | +** | ||
16937 | +** Alternatively, the contents of this file may be used under the | ||
16938 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
16939 | +** case the provisions of the GPL are applicable instead of the | ||
16940 | +** above. If you wish to allow the use of your version of this file | ||
16941 | +** only under the terms of the GPL and not to allow others to use | ||
16942 | +** your version of this file under the MPL, indicate your decision | ||
16943 | +** by deleting the provisions above and replace them with the notice | ||
16944 | +** and other provisions required by the GPL. If you do not delete | ||
16945 | +** the provisions above, a recipient may use your version of this | ||
16946 | +** file under either the MPL or the GPL. | ||
16947 | +** --------------------------------------------------------------------- | ||
16948 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
16949 | +** made directly to: | ||
16950 | +** | ||
16951 | +** acx100-users@lists.sf.net | ||
16952 | +** http://acx100.sf.net | ||
16953 | +** --------------------------------------------------------------------- | ||
16954 | +*/ | ||
16955 | + | ||
16956 | +#include <linux/version.h> | ||
16957 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) | ||
16958 | +#include <linux/config.h> | ||
16959 | +#endif | ||
16960 | +#include <linux/kernel.h> | ||
16961 | +#include <linux/types.h> | ||
16962 | +#include <asm/io.h> | ||
16963 | +/* #include <asm/uaccess.h> */ /* required for 2.4.x kernels; verify_write() */ | ||
16964 | +#include <linux/if_arp.h> | ||
16965 | +#include <linux/wireless.h> | ||
16966 | +#include <net/iw_handler.h> | ||
16967 | + | ||
16968 | +#include "acx.h" | ||
16969 | + | ||
16970 | + | ||
16971 | +/*********************************************************************** | ||
16972 | +*/ | ||
16973 | + | ||
16974 | +/* channel frequencies | ||
16975 | + * TODO: Currently, every other 802.11 driver keeps its own copy of this. In | ||
16976 | + * the long run this should be integrated into ieee802_11.h or wireless.h or | ||
16977 | + * whatever IEEE802.11x framework evolves */ | ||
16978 | +static const u16 acx_channel_freq[] = { | ||
16979 | + 2412, 2417, 2422, 2427, 2432, 2437, 2442, | ||
16980 | + 2447, 2452, 2457, 2462, 2467, 2472, 2484, | ||
16981 | +}; | ||
16982 | + | ||
16983 | + | ||
16984 | +/*********************************************************************** | ||
16985 | +** acx_ioctl_commit | ||
16986 | +*/ | ||
16987 | +static int | ||
16988 | +acx_ioctl_commit(struct net_device *ndev, | ||
16989 | + struct iw_request_info *info, | ||
16990 | + union iwreq_data *wrqu, | ||
16991 | + char *extra) | ||
16992 | +{ | ||
16993 | + acx_device_t *adev = ndev2adev(ndev); | ||
16994 | + | ||
16995 | + FN_ENTER; | ||
16996 | + | ||
16997 | + acx_sem_lock(adev); | ||
16998 | + if (ACX_STATE_IFACE_UP & adev->dev_state_mask) | ||
16999 | + acx_s_update_card_settings(adev); | ||
17000 | + acx_sem_unlock(adev); | ||
17001 | + | ||
17002 | + FN_EXIT0; | ||
17003 | + return OK; | ||
17004 | +} | ||
17005 | + | ||
17006 | + | ||
17007 | +/*********************************************************************** | ||
17008 | +*/ | ||
17009 | +static int | ||
17010 | +acx_ioctl_get_name( | ||
17011 | + struct net_device *ndev, | ||
17012 | + struct iw_request_info *info, | ||
17013 | + union iwreq_data *wrqu, | ||
17014 | + char *extra) | ||
17015 | +{ | ||
17016 | + acx_device_t *adev = ndev2adev(ndev); | ||
17017 | + static const char * const names[] = { "IEEE 802.11b+/g+", "IEEE 802.11b+" }; | ||
17018 | + | ||
17019 | + strcpy(wrqu->name, names[IS_ACX111(adev) ? 0 : 1]); | ||
17020 | + | ||
17021 | + return OK; | ||
17022 | +} | ||
17023 | + | ||
17024 | + | ||
17025 | +/*********************************************************************** | ||
17026 | +** acx_ioctl_set_freq | ||
17027 | +*/ | ||
17028 | +static int | ||
17029 | +acx_ioctl_set_freq( | ||
17030 | + struct net_device *ndev, | ||
17031 | + struct iw_request_info *info, | ||
17032 | + union iwreq_data *wrqu, | ||
17033 | + char *extra) | ||
17034 | +{ | ||
17035 | + acx_device_t *adev = ndev2adev(ndev); | ||
17036 | + int channel = -1; | ||
17037 | + unsigned int mult = 1; | ||
17038 | + int result; | ||
17039 | + | ||
17040 | + FN_ENTER; | ||
17041 | + | ||
17042 | + if (wrqu->freq.e == 0 && wrqu->freq.m <= 1000) { | ||
17043 | + /* Setting by channel number */ | ||
17044 | + channel = wrqu->freq.m; | ||
17045 | + } else { | ||
17046 | + /* If setting by frequency, convert to a channel */ | ||
17047 | + int i; | ||
17048 | + | ||
17049 | + for (i = 0; i < (6 - wrqu->freq.e); i++) | ||
17050 | + mult *= 10; | ||
17051 | + | ||
17052 | + for (i = 1; i <= 14; i++) | ||
17053 | + if (wrqu->freq.m == acx_channel_freq[i - 1] * mult) | ||
17054 | + channel = i; | ||
17055 | + } | ||
17056 | + | ||
17057 | + if (channel > 14) { | ||
17058 | + result = -EINVAL; | ||
17059 | + goto end; | ||
17060 | + } | ||
17061 | + | ||
17062 | + acx_sem_lock(adev); | ||
17063 | + | ||
17064 | + adev->channel = channel; | ||
17065 | + /* hmm, the following code part is strange, but this is how | ||
17066 | + * it was being done before... */ | ||
17067 | + log(L_IOCTL, "Changing to channel %d\n", channel); | ||
17068 | + SET_BIT(adev->set_mask, GETSET_CHANNEL); | ||
17069 | + | ||
17070 | + result = -EINPROGRESS; /* need to call commit handler */ | ||
17071 | + | ||
17072 | + acx_sem_unlock(adev); | ||
17073 | +end: | ||
17074 | + FN_EXIT1(result); | ||
17075 | + return result; | ||
17076 | +} | ||
17077 | + | ||
17078 | + | ||
17079 | +/*********************************************************************** | ||
17080 | +*/ | ||
17081 | +static inline int | ||
17082 | +acx_ioctl_get_freq( | ||
17083 | + struct net_device *ndev, | ||
17084 | + struct iw_request_info *info, | ||
17085 | + union iwreq_data *wrqu, | ||
17086 | + char *extra) | ||
17087 | +{ | ||
17088 | + acx_device_t *adev = ndev2adev(ndev); | ||
17089 | + wrqu->freq.e = 0; | ||
17090 | + wrqu->freq.m = adev->channel; | ||
17091 | + return OK; | ||
17092 | +} | ||
17093 | + | ||
17094 | + | ||
17095 | +/*********************************************************************** | ||
17096 | +** acx_ioctl_set_mode | ||
17097 | +*/ | ||
17098 | +static int | ||
17099 | +acx_ioctl_set_mode( | ||
17100 | + struct net_device *ndev, | ||
17101 | + struct iw_request_info *info, | ||
17102 | + union iwreq_data *wrqu, | ||
17103 | + char *extra) | ||
17104 | +{ | ||
17105 | + acx_device_t *adev = ndev2adev(ndev); | ||
17106 | + int result; | ||
17107 | + | ||
17108 | + FN_ENTER; | ||
17109 | + | ||
17110 | + acx_sem_lock(adev); | ||
17111 | + | ||
17112 | + switch (wrqu->mode) { | ||
17113 | + case IW_MODE_AUTO: | ||
17114 | + adev->mode = ACX_MODE_OFF; | ||
17115 | + break; | ||
17116 | + case IW_MODE_MONITOR: | ||
17117 | + adev->mode = ACX_MODE_MONITOR; | ||
17118 | + break; | ||
17119 | + case IW_MODE_ADHOC: | ||
17120 | + adev->mode = ACX_MODE_0_ADHOC; | ||
17121 | + break; | ||
17122 | + case IW_MODE_INFRA: | ||
17123 | + adev->mode = ACX_MODE_2_STA; | ||
17124 | + break; | ||
17125 | + case IW_MODE_MASTER: | ||
17126 | + printk("acx: master mode (HostAP) is very, very " | ||
17127 | + "experimental! It might work partially, but " | ||
17128 | + "better get prepared for nasty surprises " | ||
17129 | + "at any time\n"); | ||
17130 | + adev->mode = ACX_MODE_3_AP; | ||
17131 | + break; | ||
17132 | + case IW_MODE_REPEAT: | ||
17133 | + case IW_MODE_SECOND: | ||
17134 | + default: | ||
17135 | + result = -EOPNOTSUPP; | ||
17136 | + goto end_unlock; | ||
17137 | + } | ||
17138 | + | ||
17139 | + log(L_ASSOC, "new adev->mode=%d\n", adev->mode); | ||
17140 | + SET_BIT(adev->set_mask, GETSET_MODE); | ||
17141 | + result = -EINPROGRESS; | ||
17142 | + | ||
17143 | +end_unlock: | ||
17144 | + acx_sem_unlock(adev); | ||
17145 | + | ||
17146 | + FN_EXIT1(result); | ||
17147 | + return result; | ||
17148 | +} | ||
17149 | + | ||
17150 | + | ||
17151 | +/*********************************************************************** | ||
17152 | +*/ | ||
17153 | +static int | ||
17154 | +acx_ioctl_get_mode( | ||
17155 | + struct net_device *ndev, | ||
17156 | + struct iw_request_info *info, | ||
17157 | + union iwreq_data *wrqu, | ||
17158 | + char *extra) | ||
17159 | +{ | ||
17160 | + acx_device_t *adev = ndev2adev(ndev); | ||
17161 | + int result = 0; | ||
17162 | + | ||
17163 | + switch (adev->mode) { | ||
17164 | + case ACX_MODE_OFF: | ||
17165 | + wrqu->mode = IW_MODE_AUTO; break; | ||
17166 | + case ACX_MODE_MONITOR: | ||
17167 | + wrqu->mode = IW_MODE_MONITOR; break; | ||
17168 | + case ACX_MODE_0_ADHOC: | ||
17169 | + wrqu->mode = IW_MODE_ADHOC; break; | ||
17170 | + case ACX_MODE_2_STA: | ||
17171 | + wrqu->mode = IW_MODE_INFRA; break; | ||
17172 | + case ACX_MODE_3_AP: | ||
17173 | + wrqu->mode = IW_MODE_MASTER; break; | ||
17174 | + default: | ||
17175 | + result = -EOPNOTSUPP; | ||
17176 | + } | ||
17177 | + return result; | ||
17178 | +} | ||
17179 | + | ||
17180 | + | ||
17181 | +/*********************************************************************** | ||
17182 | +*/ | ||
17183 | +static int | ||
17184 | +acx_ioctl_set_sens( | ||
17185 | + struct net_device *ndev, | ||
17186 | + struct iw_request_info *info, | ||
17187 | + union iwreq_data *wrqu, | ||
17188 | + char *extra) | ||
17189 | +{ | ||
17190 | + struct iw_param *vwrq = &wrqu->sens; | ||
17191 | + acx_device_t *adev = ndev2adev(ndev); | ||
17192 | + | ||
17193 | + acx_sem_lock(adev); | ||
17194 | + | ||
17195 | + adev->sensitivity = (1 == vwrq->disabled) ? 0 : vwrq->value; | ||
17196 | + SET_BIT(adev->set_mask, GETSET_SENSITIVITY); | ||
17197 | + | ||
17198 | + acx_sem_unlock(adev); | ||
17199 | + | ||
17200 | + return -EINPROGRESS; | ||
17201 | +} | ||
17202 | + | ||
17203 | + | ||
17204 | +/*********************************************************************** | ||
17205 | +*/ | ||
17206 | +static int | ||
17207 | +acx_ioctl_get_sens( | ||
17208 | + struct net_device *ndev, | ||
17209 | + struct iw_request_info *info, | ||
17210 | + union iwreq_data *wrqu, | ||
17211 | + char *extra) | ||
17212 | +{ | ||
17213 | + struct iw_param *vwrq = &wrqu->sens; | ||
17214 | + acx_device_t *adev = ndev2adev(ndev); | ||
17215 | + | ||
17216 | + if (IS_USB(adev)) | ||
17217 | + /* setting the PHY reg via fw cmd doesn't work yet */ | ||
17218 | + return -EOPNOTSUPP; | ||
17219 | + | ||
17220 | + /* acx_sem_lock(adev); */ | ||
17221 | + | ||
17222 | + vwrq->value = adev->sensitivity; | ||
17223 | + vwrq->disabled = (vwrq->value == 0); | ||
17224 | + vwrq->fixed = 1; | ||
17225 | + | ||
17226 | + /* acx_sem_unlock(adev); */ | ||
17227 | + | ||
17228 | + return OK; | ||
17229 | +} | ||
17230 | + | ||
17231 | + | ||
17232 | +/*********************************************************************** | ||
17233 | +** acx_ioctl_set_ap | ||
17234 | +** | ||
17235 | +** Sets the MAC address of the AP to associate with | ||
17236 | +*/ | ||
17237 | +static int | ||
17238 | +acx_ioctl_set_ap( | ||
17239 | + struct net_device *ndev, | ||
17240 | + struct iw_request_info *info, | ||
17241 | + union iwreq_data *wrqu, | ||
17242 | + char *extra) | ||
17243 | +{ | ||
17244 | + struct sockaddr *awrq = &wrqu->ap_addr; | ||
17245 | + acx_device_t *adev = ndev2adev(ndev); | ||
17246 | + int result = 0; | ||
17247 | + const u8 *ap; | ||
17248 | + | ||
17249 | + FN_ENTER; | ||
17250 | + if (NULL == awrq) { | ||
17251 | + result = -EFAULT; | ||
17252 | + goto end; | ||
17253 | + } | ||
17254 | + if (ARPHRD_ETHER != awrq->sa_family) { | ||
17255 | + result = -EINVAL; | ||
17256 | + goto end; | ||
17257 | + } | ||
17258 | + | ||
17259 | + ap = awrq->sa_data; | ||
17260 | + acxlog_mac(L_IOCTL, "set AP=", ap, "\n"); | ||
17261 | + | ||
17262 | + MAC_COPY(adev->ap, ap); | ||
17263 | + | ||
17264 | + /* We want to start rescan in managed or ad-hoc mode, | ||
17265 | + ** otherwise just set adev->ap. | ||
17266 | + ** "iwconfig <if> ap <mac> mode managed": we must be able | ||
17267 | + ** to set ap _first_ and _then_ set mode */ | ||
17268 | + switch (adev->mode) { | ||
17269 | + case ACX_MODE_0_ADHOC: | ||
17270 | + case ACX_MODE_2_STA: | ||
17271 | + /* FIXME: if there is a convention on what zero AP means, | ||
17272 | + ** please add a comment about that. I don't know of any --vda */ | ||
17273 | + if (mac_is_zero(ap)) { | ||
17274 | + /* "off" == 00:00:00:00:00:00 */ | ||
17275 | + MAC_BCAST(adev->ap); | ||
17276 | + log(L_IOCTL, "Not reassociating\n"); | ||
17277 | + } else { | ||
17278 | + log(L_IOCTL, "Forcing reassociation\n"); | ||
17279 | + SET_BIT(adev->set_mask, GETSET_RESCAN); | ||
17280 | + } | ||
17281 | + break; | ||
17282 | + } | ||
17283 | + result = -EINPROGRESS; | ||
17284 | +end: | ||
17285 | + FN_EXIT1(result); | ||
17286 | + return result; | ||
17287 | +} | ||
17288 | + | ||
17289 | + | ||
17290 | +/*********************************************************************** | ||
17291 | +*/ | ||
17292 | +static int | ||
17293 | +acx_ioctl_get_ap( | ||
17294 | + struct net_device *ndev, | ||
17295 | + struct iw_request_info *info, | ||
17296 | + union iwreq_data *wrqu, | ||
17297 | + char *extra) | ||
17298 | +{ | ||
17299 | + struct sockaddr *awrq = &wrqu->ap_addr; | ||
17300 | + acx_device_t *adev = ndev2adev(ndev); | ||
17301 | + | ||
17302 | + if (ACX_STATUS_4_ASSOCIATED == adev->status) { | ||
17303 | + /* as seen in Aironet driver, airo.c */ | ||
17304 | + MAC_COPY(awrq->sa_data, adev->bssid); | ||
17305 | + } else { | ||
17306 | + MAC_ZERO(awrq->sa_data); | ||
17307 | + } | ||
17308 | + awrq->sa_family = ARPHRD_ETHER; | ||
17309 | + return OK; | ||
17310 | +} | ||
17311 | + | ||
17312 | + | ||
17313 | +/*********************************************************************** | ||
17314 | +** acx_ioctl_get_aplist | ||
17315 | +** | ||
17316 | +** Deprecated in favor of iwscan. | ||
17317 | +** We simply return the list of currently available stations in range, | ||
17318 | +** don't do a new scan. | ||
17319 | +*/ | ||
17320 | +static int | ||
17321 | +acx_ioctl_get_aplist( | ||
17322 | + struct net_device *ndev, | ||
17323 | + struct iw_request_info *info, | ||
17324 | + union iwreq_data *wrqu, | ||
17325 | + char *extra) | ||
17326 | +{ | ||
17327 | + struct iw_point *dwrq = &wrqu->data; | ||
17328 | + acx_device_t *adev = ndev2adev(ndev); | ||
17329 | + struct sockaddr *address = (struct sockaddr *) extra; | ||
17330 | + struct iw_quality qual[IW_MAX_AP]; | ||
17331 | + int i, cur; | ||
17332 | + int result = OK; | ||
17333 | + | ||
17334 | + FN_ENTER; | ||
17335 | + | ||
17336 | + /* we have AP list only in STA mode */ | ||
17337 | + if (ACX_MODE_2_STA != adev->mode) { | ||
17338 | + result = -EOPNOTSUPP; | ||
17339 | + goto end; | ||
17340 | + } | ||
17341 | + | ||
17342 | + cur = 0; | ||
17343 | + for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { | ||
17344 | + struct client *bss = &adev->sta_list[i]; | ||
17345 | + if (!bss->used) continue; | ||
17346 | + MAC_COPY(address[cur].sa_data, bss->bssid); | ||
17347 | + address[cur].sa_family = ARPHRD_ETHER; | ||
17348 | + qual[cur].level = bss->sir; | ||
17349 | + qual[cur].noise = bss->snr; | ||
17350 | +#ifndef OLD_QUALITY | ||
17351 | + qual[cur].qual = acx_signal_determine_quality(qual[cur].level, | ||
17352 | + qual[cur].noise); | ||
17353 | +#else | ||
17354 | + qual[cur].qual = (qual[cur].noise <= 100) ? | ||
17355 | + 100 - qual[cur].noise : 0; | ||
17356 | +#endif | ||
17357 | + /* no scan: level/noise/qual not updated: */ | ||
17358 | + qual[cur].updated = 0; | ||
17359 | + cur++; | ||
17360 | + } | ||
17361 | + if (cur) { | ||
17362 | + dwrq->flags = 1; | ||
17363 | + memcpy(extra + sizeof(struct sockaddr)*cur, &qual, | ||
17364 | + sizeof(struct iw_quality)*cur); | ||
17365 | + } | ||
17366 | + dwrq->length = cur; | ||
17367 | +end: | ||
17368 | + FN_EXIT1(result); | ||
17369 | + return result; | ||
17370 | +} | ||
17371 | + | ||
17372 | + | ||
17373 | +/*********************************************************************** | ||
17374 | +*/ | ||
17375 | +static int | ||
17376 | +acx_ioctl_set_scan( | ||
17377 | + struct net_device *ndev, | ||
17378 | + struct iw_request_info *info, | ||
17379 | + union iwreq_data *wrqu, | ||
17380 | + char *extra) | ||
17381 | +{ | ||
17382 | + acx_device_t *adev = ndev2adev(ndev); | ||
17383 | + int result; | ||
17384 | + | ||
17385 | + FN_ENTER; | ||
17386 | + | ||
17387 | + acx_sem_lock(adev); | ||
17388 | + | ||
17389 | + /* don't start scan if device is not up yet */ | ||
17390 | + if (!(adev->dev_state_mask & ACX_STATE_IFACE_UP)) { | ||
17391 | + result = -EAGAIN; | ||
17392 | + goto end_unlock; | ||
17393 | + } | ||
17394 | + | ||
17395 | + /* This is NOT a rescan for new AP! | ||
17396 | + ** Do not use SET_BIT(GETSET_RESCAN); */ | ||
17397 | + acx_s_cmd_start_scan(adev); | ||
17398 | + result = OK; | ||
17399 | + | ||
17400 | +end_unlock: | ||
17401 | + acx_sem_unlock(adev); | ||
17402 | +/* end: */ | ||
17403 | + FN_EXIT1(result); | ||
17404 | + return result; | ||
17405 | +} | ||
17406 | + | ||
17407 | + | ||
17408 | +/*********************************************************************** | ||
17409 | +** acx_s_scan_add_station | ||
17410 | +*/ | ||
17411 | +/* helper. not sure whether it's really a _s_leeping fn */ | ||
17412 | +static char* | ||
17413 | +acx_s_scan_add_station( | ||
17414 | + acx_device_t *adev, | ||
17415 | + char *ptr, | ||
17416 | + char *end_buf, | ||
17417 | + struct client *bss) | ||
17418 | +{ | ||
17419 | + struct iw_event iwe; | ||
17420 | + char *ptr_rate; | ||
17421 | + | ||
17422 | + FN_ENTER; | ||
17423 | + | ||
17424 | + /* MAC address has to be added first */ | ||
17425 | + iwe.cmd = SIOCGIWAP; | ||
17426 | + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
17427 | + MAC_COPY(iwe.u.ap_addr.sa_data, bss->bssid); | ||
17428 | + acxlog_mac(L_IOCTL, "scan, station address: ", bss->bssid, "\n"); | ||
17429 | + ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_ADDR_LEN); | ||
17430 | + | ||
17431 | + /* Add ESSID */ | ||
17432 | + iwe.cmd = SIOCGIWESSID; | ||
17433 | + iwe.u.data.length = bss->essid_len; | ||
17434 | + iwe.u.data.flags = 1; | ||
17435 | + log(L_IOCTL, "scan, essid: %s\n", bss->essid); | ||
17436 | + ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid); | ||
17437 | + | ||
17438 | + /* Add mode */ | ||
17439 | + iwe.cmd = SIOCGIWMODE; | ||
17440 | + if (bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)) { | ||
17441 | + if (bss->cap_info & WF_MGMT_CAP_ESS) | ||
17442 | + iwe.u.mode = IW_MODE_MASTER; | ||
17443 | + else | ||
17444 | + iwe.u.mode = IW_MODE_ADHOC; | ||
17445 | + log(L_IOCTL, "scan, mode: %d\n", iwe.u.mode); | ||
17446 | + ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_UINT_LEN); | ||
17447 | + } | ||
17448 | + | ||
17449 | + /* Add frequency */ | ||
17450 | + iwe.cmd = SIOCGIWFREQ; | ||
17451 | + iwe.u.freq.m = acx_channel_freq[bss->channel - 1] * 100000; | ||
17452 | + iwe.u.freq.e = 1; | ||
17453 | + log(L_IOCTL, "scan, frequency: %d\n", iwe.u.freq.m); | ||
17454 | + ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_FREQ_LEN); | ||
17455 | + | ||
17456 | + /* Add link quality */ | ||
17457 | + iwe.cmd = IWEVQUAL; | ||
17458 | + /* FIXME: these values should be expressed in dBm, but we don't know | ||
17459 | + * how to calibrate it yet */ | ||
17460 | + iwe.u.qual.level = bss->sir; | ||
17461 | + iwe.u.qual.noise = bss->snr; | ||
17462 | +#ifndef OLD_QUALITY | ||
17463 | + iwe.u.qual.qual = acx_signal_determine_quality(iwe.u.qual.level, | ||
17464 | + iwe.u.qual.noise); | ||
17465 | +#else | ||
17466 | + iwe.u.qual.qual = (iwe.u.qual.noise <= 100) ? | ||
17467 | + 100 - iwe.u.qual.noise : 0; | ||
17468 | +#endif | ||
17469 | + iwe.u.qual.updated = 7; | ||
17470 | + log(L_IOCTL, "scan, link quality: %d/%d/%d\n", | ||
17471 | + iwe.u.qual.level, iwe.u.qual.noise, iwe.u.qual.qual); | ||
17472 | + ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_QUAL_LEN); | ||
17473 | + | ||
17474 | + /* Add encryption */ | ||
17475 | + iwe.cmd = SIOCGIWENCODE; | ||
17476 | + if (bss->cap_info & WF_MGMT_CAP_PRIVACY) | ||
17477 | + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
17478 | + else | ||
17479 | + iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
17480 | + iwe.u.data.length = 0; | ||
17481 | + log(L_IOCTL, "scan, encryption flags: %X\n", iwe.u.data.flags); | ||
17482 | + ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid); | ||
17483 | + | ||
17484 | + /* add rates */ | ||
17485 | + iwe.cmd = SIOCGIWRATE; | ||
17486 | + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
17487 | + ptr_rate = ptr + IW_EV_LCP_LEN; | ||
17488 | + | ||
17489 | + { | ||
17490 | + u16 rate = bss->rate_cap; | ||
17491 | + const u8* p = acx_bitpos2ratebyte; | ||
17492 | + while (rate) { | ||
17493 | + if (rate & 1) { | ||
17494 | + iwe.u.bitrate.value = *p * 500000; /* units of 500kb/s */ | ||
17495 | + log(L_IOCTL, "scan, rate: %d\n", iwe.u.bitrate.value); | ||
17496 | + ptr_rate = iwe_stream_add_value(ptr, ptr_rate, end_buf, | ||
17497 | + &iwe, IW_EV_PARAM_LEN); | ||
17498 | + } | ||
17499 | + rate >>= 1; | ||
17500 | + p++; | ||
17501 | + }} | ||
17502 | + | ||
17503 | + if ((ptr_rate - ptr) > (ptrdiff_t)IW_EV_LCP_LEN) | ||
17504 | + ptr = ptr_rate; | ||
17505 | + | ||
17506 | + /* drop remaining station data items for now */ | ||
17507 | + | ||
17508 | + FN_EXIT0; | ||
17509 | + return ptr; | ||
17510 | +} | ||
17511 | + | ||
17512 | + | ||
17513 | +/*********************************************************************** | ||
17514 | + * acx_ioctl_get_scan | ||
17515 | + */ | ||
17516 | +static int | ||
17517 | +acx_ioctl_get_scan( | ||
17518 | + struct net_device *ndev, | ||
17519 | + struct iw_request_info *info, | ||
17520 | + union iwreq_data *wrqu, | ||
17521 | + char *extra) | ||
17522 | +{ | ||
17523 | + struct iw_point *dwrq = &wrqu->data; | ||
17524 | + acx_device_t *adev = ndev2adev(ndev); | ||
17525 | + char *ptr = extra; | ||
17526 | + int i; | ||
17527 | + int result = OK; | ||
17528 | + | ||
17529 | + FN_ENTER; | ||
17530 | + | ||
17531 | + acx_sem_lock(adev); | ||
17532 | + | ||
17533 | + /* no scan available if device is not up yet */ | ||
17534 | + if (!(adev->dev_state_mask & ACX_STATE_IFACE_UP)) { | ||
17535 | + log(L_IOCTL, "iface not up yet\n"); | ||
17536 | + result = -EAGAIN; | ||
17537 | + goto end_unlock; | ||
17538 | + } | ||
17539 | + | ||
17540 | +#ifdef ENODATA_TO_BE_USED_AFTER_SCAN_ERROR_ONLY | ||
17541 | + if (adev->bss_table_count == 0) { | ||
17542 | + /* no stations found */ | ||
17543 | + result = -ENODATA; | ||
17544 | + goto end_unlock; | ||
17545 | + } | ||
17546 | +#endif | ||
17547 | + | ||
17548 | + for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { | ||
17549 | + struct client *bss = &adev->sta_list[i]; | ||
17550 | + if (!bss->used) continue; | ||
17551 | + ptr = acx_s_scan_add_station(adev, ptr, | ||
17552 | + extra + IW_SCAN_MAX_DATA, bss); | ||
17553 | + } | ||
17554 | + dwrq->length = ptr - extra; | ||
17555 | + dwrq->flags = 0; | ||
17556 | + | ||
17557 | +end_unlock: | ||
17558 | + acx_sem_unlock(adev); | ||
17559 | +/* end: */ | ||
17560 | + FN_EXIT1(result); | ||
17561 | + return result; | ||
17562 | +} | ||
17563 | + | ||
17564 | + | ||
17565 | +/*********************************************************************** | ||
17566 | +** acx_ioctl_set_essid | ||
17567 | +*/ | ||
17568 | +static int | ||
17569 | +acx_ioctl_set_essid( | ||
17570 | + struct net_device *ndev, | ||
17571 | + struct iw_request_info *info, | ||
17572 | + union iwreq_data *wrqu, | ||
17573 | + char *extra) | ||
17574 | +{ | ||
17575 | + struct iw_point *dwrq = &wrqu->essid; | ||
17576 | + acx_device_t *adev = ndev2adev(ndev); | ||
17577 | + int len = dwrq->length; | ||
17578 | + int result; | ||
17579 | + | ||
17580 | + FN_ENTER; | ||
17581 | + | ||
17582 | + if (len < 0) { | ||
17583 | + result = -EINVAL; | ||
17584 | + goto end; | ||
17585 | + } | ||
17586 | + | ||
17587 | + log(L_IOCTL, "set ESSID '%*s', length %d, flags 0x%04X\n", | ||
17588 | + len, extra, len, dwrq->flags); | ||
17589 | + | ||
17590 | +#if WIRELESS_EXT >= 21 | ||
17591 | + /* WE 21 gives real ESSID strlen, not +1 (trailing zero): | ||
17592 | + * see LKML "[patch] drivers/net/wireless: correct reported ssid lengths" */ | ||
17593 | + len += 1; | ||
17594 | +#endif | ||
17595 | + | ||
17596 | + acx_sem_lock(adev); | ||
17597 | + | ||
17598 | + /* ESSID disabled? */ | ||
17599 | + if (0 == dwrq->flags) { | ||
17600 | + adev->essid_active = 0; | ||
17601 | + | ||
17602 | + } else { | ||
17603 | + if (len > IW_ESSID_MAX_SIZE) { | ||
17604 | + result = -E2BIG; | ||
17605 | + goto end_unlock; | ||
17606 | + } | ||
17607 | + | ||
17608 | + if (len >= sizeof(adev->essid)) | ||
17609 | + len = sizeof(adev->essid) - 1; | ||
17610 | + memcpy(adev->essid, extra, len); | ||
17611 | + adev->essid[len] = '\0'; | ||
17612 | + /* Paranoia: just in case there is a '\0'... */ | ||
17613 | + adev->essid_len = strlen(adev->essid); | ||
17614 | + adev->essid_active = 1; | ||
17615 | + } | ||
17616 | + | ||
17617 | + SET_BIT(adev->set_mask, GETSET_RESCAN); | ||
17618 | + | ||
17619 | + result = -EINPROGRESS; | ||
17620 | + | ||
17621 | +end_unlock: | ||
17622 | + acx_sem_unlock(adev); | ||
17623 | +end: | ||
17624 | + FN_EXIT1(result); | ||
17625 | + return result; | ||
17626 | +} | ||
17627 | + | ||
17628 | + | ||
17629 | +/*********************************************************************** | ||
17630 | +*/ | ||
17631 | +static int | ||
17632 | +acx_ioctl_get_essid( | ||
17633 | + struct net_device *ndev, | ||
17634 | + struct iw_request_info *info, | ||
17635 | + union iwreq_data *wrqu, | ||
17636 | + char *extra) | ||
17637 | +{ | ||
17638 | + struct iw_point *dwrq = &wrqu->essid; | ||
17639 | + acx_device_t *adev = ndev2adev(ndev); | ||
17640 | + | ||
17641 | + dwrq->flags = adev->essid_active; | ||
17642 | + if (adev->essid_active) { | ||
17643 | + memcpy(extra, adev->essid, adev->essid_len); | ||
17644 | + extra[adev->essid_len] = '\0'; | ||
17645 | + dwrq->length = adev->essid_len + 1; | ||
17646 | + dwrq->flags = 1; | ||
17647 | + } | ||
17648 | + return OK; | ||
17649 | +} | ||
17650 | + | ||
17651 | + | ||
17652 | +/*********************************************************************** | ||
17653 | +** acx_l_update_client_rates | ||
17654 | +*/ | ||
17655 | +static void | ||
17656 | +acx_l_update_client_rates(acx_device_t *adev, u16 rate) | ||
17657 | +{ | ||
17658 | + int i; | ||
17659 | + for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { | ||
17660 | + client_t *clt = &adev->sta_list[i]; | ||
17661 | + if (!clt->used) continue; | ||
17662 | + clt->rate_cfg = (clt->rate_cap & rate); | ||
17663 | + if (!clt->rate_cfg) { | ||
17664 | + /* no compatible rates left: kick client */ | ||
17665 | + acxlog_mac(L_ASSOC, "client ",clt->address," kicked: " | ||
17666 | + "rates are not compatible anymore\n"); | ||
17667 | + acx_l_sta_list_del(adev, clt); | ||
17668 | + continue; | ||
17669 | + } | ||
17670 | + clt->rate_cur &= clt->rate_cfg; | ||
17671 | + if (!clt->rate_cur) { | ||
17672 | + /* current rate become invalid, choose a valid one */ | ||
17673 | + clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); | ||
17674 | + } | ||
17675 | + if (IS_ACX100(adev)) | ||
17676 | + clt->rate_100 = acx_bitpos2rate100[highest_bit(clt->rate_cur)]; | ||
17677 | + clt->fallback_count = clt->stepup_count = 0; | ||
17678 | + clt->ignore_count = 16; | ||
17679 | + } | ||
17680 | + switch (adev->mode) { | ||
17681 | + case ACX_MODE_2_STA: | ||
17682 | + if (adev->ap_client && !adev->ap_client->used) { | ||
17683 | + /* Owwww... we kicked our AP!! :) */ | ||
17684 | + SET_BIT(adev->set_mask, GETSET_RESCAN); | ||
17685 | + } | ||
17686 | + } | ||
17687 | +} | ||
17688 | + | ||
17689 | + | ||
17690 | +/*********************************************************************** | ||
17691 | +*/ | ||
17692 | +/* maps bits from acx111 rate to rate in Mbits */ | ||
17693 | +static const unsigned int | ||
17694 | +acx111_rate_tbl[] = { | ||
17695 | + 1000000, /* 0 */ | ||
17696 | + 2000000, /* 1 */ | ||
17697 | + 5500000, /* 2 */ | ||
17698 | + 6000000, /* 3 */ | ||
17699 | + 9000000, /* 4 */ | ||
17700 | + 11000000, /* 5 */ | ||
17701 | + 12000000, /* 6 */ | ||
17702 | + 18000000, /* 7 */ | ||
17703 | + 22000000, /* 8 */ | ||
17704 | + 24000000, /* 9 */ | ||
17705 | + 36000000, /* 10 */ | ||
17706 | + 48000000, /* 11 */ | ||
17707 | + 54000000, /* 12 */ | ||
17708 | + 500000, /* 13, should not happen */ | ||
17709 | + 500000, /* 14, should not happen */ | ||
17710 | + 500000, /* 15, should not happen */ | ||
17711 | +}; | ||
17712 | + | ||
17713 | +/*********************************************************************** | ||
17714 | + * acx_ioctl_set_rate | ||
17715 | + */ | ||
17716 | +static int | ||
17717 | +acx_ioctl_set_rate( | ||
17718 | + struct net_device *ndev, | ||
17719 | + struct iw_request_info *info, | ||
17720 | + union iwreq_data *wrqu, | ||
17721 | + char *extra) | ||
17722 | +{ | ||
17723 | + struct iw_param *vwrq = &wrqu->param; | ||
17724 | + acx_device_t *adev = ndev2adev(ndev); | ||
17725 | + u16 txrate_cfg = 1; | ||
17726 | + unsigned long flags; | ||
17727 | + int autorate; | ||
17728 | + int result = -EINVAL; | ||
17729 | + | ||
17730 | + FN_ENTER; | ||
17731 | + log(L_IOCTL, "rate %d fixed 0x%X disabled 0x%X flags 0x%X\n", | ||
17732 | + vwrq->value, vwrq->fixed, vwrq->disabled, vwrq->flags); | ||
17733 | + | ||
17734 | + if ((0 == vwrq->fixed) || (1 == vwrq->fixed)) { | ||
17735 | + int i = VEC_SIZE(acx111_rate_tbl)-1; | ||
17736 | + if (vwrq->value == -1) | ||
17737 | + /* "iwconfig rate auto" --> choose highest */ | ||
17738 | + vwrq->value = IS_ACX100(adev) ? 22000000 : 54000000; | ||
17739 | + while (i >= 0) { | ||
17740 | + if (vwrq->value == acx111_rate_tbl[i]) { | ||
17741 | + txrate_cfg <<= i; | ||
17742 | + i = 0; | ||
17743 | + break; | ||
17744 | + } | ||
17745 | + i--; | ||
17746 | + } | ||
17747 | + if (i == -1) { /* no matching rate */ | ||
17748 | + result = -EINVAL; | ||
17749 | + goto end; | ||
17750 | + } | ||
17751 | + } else { /* rate N, N<1000 (driver specific): we don't use this */ | ||
17752 | + result = -EOPNOTSUPP; | ||
17753 | + goto end; | ||
17754 | + } | ||
17755 | + /* now: only one bit is set in txrate_cfg, corresponding to | ||
17756 | + ** indicated rate */ | ||
17757 | + | ||
17758 | + autorate = (vwrq->fixed == 0) && (RATE111_1 != txrate_cfg); | ||
17759 | + if (autorate) { | ||
17760 | + /* convert 00100000 -> 00111111 */ | ||
17761 | + txrate_cfg = (txrate_cfg<<1)-1; | ||
17762 | + } | ||
17763 | + | ||
17764 | + if (IS_ACX100(adev)) { | ||
17765 | + txrate_cfg &= RATE111_ACX100_COMPAT; | ||
17766 | + if (!txrate_cfg) { | ||
17767 | + result = -ENOTSUPP; /* rate is not supported by acx100 */ | ||
17768 | + goto end; | ||
17769 | + } | ||
17770 | + } | ||
17771 | + | ||
17772 | + acx_sem_lock(adev); | ||
17773 | + acx_lock(adev, flags); | ||
17774 | + | ||
17775 | + adev->rate_auto = autorate; | ||
17776 | + adev->rate_oper = txrate_cfg; | ||
17777 | + adev->rate_basic = txrate_cfg; | ||
17778 | + /* only do that in auto mode, non-auto will be able to use | ||
17779 | + * one specific Tx rate only anyway */ | ||
17780 | + if (autorate) { | ||
17781 | + /* only use 802.11b base rates, for standard 802.11b H/W | ||
17782 | + * compatibility */ | ||
17783 | + adev->rate_basic &= RATE111_80211B_COMPAT; | ||
17784 | + } | ||
17785 | + adev->rate_bcast = 1 << lowest_bit(txrate_cfg); | ||
17786 | + if (IS_ACX100(adev)) | ||
17787 | + adev->rate_bcast100 = acx_rate111to100(adev->rate_bcast); | ||
17788 | + acx_l_update_ratevector(adev); | ||
17789 | + acx_l_update_client_rates(adev, txrate_cfg); | ||
17790 | + | ||
17791 | + /* Do/don't do tx rate fallback; beacon contents and rate */ | ||
17792 | + SET_BIT(adev->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES); | ||
17793 | + result = -EINPROGRESS; | ||
17794 | + | ||
17795 | + acx_unlock(adev, flags); | ||
17796 | + acx_sem_unlock(adev); | ||
17797 | +end: | ||
17798 | + FN_EXIT1(result); | ||
17799 | + return result; | ||
17800 | +} | ||
17801 | + | ||
17802 | + | ||
17803 | +/*********************************************************************** | ||
17804 | +** acx_ioctl_get_rate | ||
17805 | +*/ | ||
17806 | +static int | ||
17807 | +acx_ioctl_get_rate( | ||
17808 | + struct net_device *ndev, | ||
17809 | + struct iw_request_info *info, | ||
17810 | + union iwreq_data *wrqu, | ||
17811 | + char *extra) | ||
17812 | +{ | ||
17813 | + struct iw_param *vwrq = &wrqu->param; | ||
17814 | + acx_device_t *adev = ndev2adev(ndev); | ||
17815 | + unsigned long flags; | ||
17816 | + u16 rate; | ||
17817 | + | ||
17818 | + acx_lock(adev, flags); | ||
17819 | + rate = adev->rate_oper; | ||
17820 | + if (adev->ap_client) | ||
17821 | + rate = adev->ap_client->rate_cur; | ||
17822 | + vwrq->value = acx111_rate_tbl[highest_bit(rate)]; | ||
17823 | + vwrq->fixed = !adev->rate_auto; | ||
17824 | + vwrq->disabled = 0; | ||
17825 | + acx_unlock(adev, flags); | ||
17826 | + | ||
17827 | + return OK; | ||
17828 | +} | ||
17829 | + | ||
17830 | +static int | ||
17831 | +acx_ioctl_set_rts( | ||
17832 | + struct net_device *ndev, | ||
17833 | + struct iw_request_info *info, | ||
17834 | + union iwreq_data *wrqu, | ||
17835 | + char *extra) | ||
17836 | +{ | ||
17837 | + struct iw_param *vwrq = &wrqu->rts; | ||
17838 | + acx_device_t *adev = ndev2adev(ndev); | ||
17839 | + int val = vwrq->value; | ||
17840 | + | ||
17841 | + if (vwrq->disabled) | ||
17842 | + val = 2312; | ||
17843 | + if ((val < 0) || (val > 2312)) | ||
17844 | + return -EINVAL; | ||
17845 | + | ||
17846 | + adev->rts_threshold = val; | ||
17847 | + return OK; | ||
17848 | +} | ||
17849 | + | ||
17850 | +static inline int | ||
17851 | +acx_ioctl_get_rts( | ||
17852 | + struct net_device *ndev, | ||
17853 | + struct iw_request_info *info, | ||
17854 | + union iwreq_data *wrqu, | ||
17855 | + char *extra) | ||
17856 | +{ | ||
17857 | + struct iw_param *vwrq = &wrqu->rts; | ||
17858 | + acx_device_t *adev = ndev2adev(ndev); | ||
17859 | + | ||
17860 | + vwrq->value = adev->rts_threshold; | ||
17861 | + vwrq->disabled = (vwrq->value >= 2312); | ||
17862 | + vwrq->fixed = 1; | ||
17863 | + return OK; | ||
17864 | +} | ||
17865 | + | ||
17866 | + | ||
17867 | +#if ACX_FRAGMENTATION | ||
17868 | +static int | ||
17869 | +acx_ioctl_set_frag( | ||
17870 | + struct net_device *ndev, | ||
17871 | + struct iw_request_info *info, | ||
17872 | + struct iw_param *vwrq, | ||
17873 | + char *extra) | ||
17874 | +{ | ||
17875 | + acx_device_t *adev = ndev2adev(ndev); | ||
17876 | + int val = vwrq->value; | ||
17877 | + | ||
17878 | + if (vwrq->disabled) | ||
17879 | + val = 32767; | ||
17880 | + else | ||
17881 | + if ((val < 256) || (val > 2347)) | ||
17882 | + return -EINVAL; | ||
17883 | + | ||
17884 | + adev->frag_threshold = val; | ||
17885 | + return OK; | ||
17886 | +} | ||
17887 | + | ||
17888 | +static inline int | ||
17889 | +acx_ioctl_get_frag( | ||
17890 | + struct net_device *ndev, | ||
17891 | + struct iw_request_info *info, | ||
17892 | + union iwreq_data *wrqu, | ||
17893 | + char *extra) | ||
17894 | +{ | ||
17895 | + struct iw_param *vwrq = &wrqu->frag; | ||
17896 | + acx_device_t *adev = ndev2adev(ndev); | ||
17897 | + | ||
17898 | + vwrq->value = adev->frag_threshold; | ||
17899 | + vwrq->disabled = (vwrq->value >= 2347); | ||
17900 | + vwrq->fixed = 1; | ||
17901 | + return OK; | ||
17902 | +} | ||
17903 | +#endif | ||
17904 | + | ||
17905 | + | ||
17906 | +/*********************************************************************** | ||
17907 | +** acx_ioctl_set_encode | ||
17908 | +*/ | ||
17909 | +static int | ||
17910 | +acx_ioctl_set_encode( | ||
17911 | + struct net_device *ndev, | ||
17912 | + struct iw_request_info *info, | ||
17913 | + union iwreq_data *wrqu, | ||
17914 | + char *extra) | ||
17915 | +{ | ||
17916 | + struct iw_point *dwrq = &wrqu->encoding; | ||
17917 | + acx_device_t *adev = ndev2adev(ndev); | ||
17918 | + int index; | ||
17919 | + int result; | ||
17920 | + | ||
17921 | + FN_ENTER; | ||
17922 | + | ||
17923 | + log(L_IOCTL, "set encoding flags=0x%04X, size=%d, key: %s\n", | ||
17924 | + dwrq->flags, dwrq->length, extra ? "set" : "No key"); | ||
17925 | + | ||
17926 | + acx_sem_lock(adev); | ||
17927 | + | ||
17928 | + index = (dwrq->flags & IW_ENCODE_INDEX) - 1; | ||
17929 | + | ||
17930 | + if (dwrq->length > 0) { | ||
17931 | + /* if index is 0 or invalid, use default key */ | ||
17932 | + if ((index < 0) || (index > 3)) | ||
17933 | + index = (int)adev->wep_current_index; | ||
17934 | + | ||
17935 | + if (0 == (dwrq->flags & IW_ENCODE_NOKEY)) { | ||
17936 | + if (dwrq->length > 29) | ||
17937 | + dwrq->length = 29; /* restrict it */ | ||
17938 | + | ||
17939 | + if (dwrq->length > 13) { | ||
17940 | + /* 29*8 == 232, WEP256 */ | ||
17941 | + adev->wep_keys[index].size = 29; | ||
17942 | + } else if (dwrq->length > 5) { | ||
17943 | + /* 13*8 == 104bit, WEP128 */ | ||
17944 | + adev->wep_keys[index].size = 13; | ||
17945 | + } else if (dwrq->length > 0) { | ||
17946 | + /* 5*8 == 40bit, WEP64 */ | ||
17947 | + adev->wep_keys[index].size = 5; | ||
17948 | + } else { | ||
17949 | + /* disable key */ | ||
17950 | + adev->wep_keys[index].size = 0; | ||
17951 | + } | ||
17952 | + | ||
17953 | + memset(adev->wep_keys[index].key, 0, | ||
17954 | + sizeof(adev->wep_keys[index].key)); | ||
17955 | + memcpy(adev->wep_keys[index].key, extra, dwrq->length); | ||
17956 | + } | ||
17957 | + } else { | ||
17958 | + /* set transmit key */ | ||
17959 | + if ((index >= 0) && (index <= 3)) | ||
17960 | + adev->wep_current_index = index; | ||
17961 | + else if (0 == (dwrq->flags & IW_ENCODE_MODE)) { | ||
17962 | + /* complain if we were not just setting | ||
17963 | + * the key mode */ | ||
17964 | + result = -EINVAL; | ||
17965 | + goto end_unlock; | ||
17966 | + } | ||
17967 | + } | ||
17968 | + | ||
17969 | + adev->wep_enabled = !(dwrq->flags & IW_ENCODE_DISABLED); | ||
17970 | + | ||
17971 | + if (dwrq->flags & IW_ENCODE_OPEN) { | ||
17972 | + adev->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM; | ||
17973 | + adev->wep_restricted = 0; | ||
17974 | + | ||
17975 | + } else if (dwrq->flags & IW_ENCODE_RESTRICTED) { | ||
17976 | + adev->auth_alg = WLAN_AUTH_ALG_SHAREDKEY; | ||
17977 | + adev->wep_restricted = 1; | ||
17978 | + } | ||
17979 | + | ||
17980 | + /* set flag to make sure the card WEP settings get updated */ | ||
17981 | + SET_BIT(adev->set_mask, GETSET_WEP); | ||
17982 | + | ||
17983 | + log(L_IOCTL, "len=%d, key at 0x%p, flags=0x%X\n", | ||
17984 | + dwrq->length, extra, dwrq->flags); | ||
17985 | + | ||
17986 | + for (index = 0; index <= 3; index++) { | ||
17987 | + if (adev->wep_keys[index].size) { | ||
17988 | + log(L_IOCTL, "index=%d, size=%d, key at 0x%p\n", | ||
17989 | + adev->wep_keys[index].index, | ||
17990 | + (int) adev->wep_keys[index].size, | ||
17991 | + adev->wep_keys[index].key); | ||
17992 | + } | ||
17993 | + } | ||
17994 | + result = -EINPROGRESS; | ||
17995 | + | ||
17996 | +end_unlock: | ||
17997 | + acx_sem_unlock(adev); | ||
17998 | + | ||
17999 | + FN_EXIT1(result); | ||
18000 | + return result; | ||
18001 | +} | ||
18002 | + | ||
18003 | + | ||
18004 | +/*********************************************************************** | ||
18005 | +** acx_ioctl_get_encode | ||
18006 | +*/ | ||
18007 | +static int | ||
18008 | +acx_ioctl_get_encode( | ||
18009 | + struct net_device *ndev, | ||
18010 | + struct iw_request_info *info, | ||
18011 | + union iwreq_data *wrqu, | ||
18012 | + char *extra) | ||
18013 | +{ | ||
18014 | + struct iw_point *dwrq = &wrqu->encoding; | ||
18015 | + acx_device_t *adev = ndev2adev(ndev); | ||
18016 | + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; | ||
18017 | + | ||
18018 | + FN_ENTER; | ||
18019 | + | ||
18020 | + if (adev->wep_enabled == 0) { | ||
18021 | + dwrq->flags = IW_ENCODE_DISABLED; | ||
18022 | + } else { | ||
18023 | + if ((index < 0) || (index > 3)) | ||
18024 | + index = (int)adev->wep_current_index; | ||
18025 | + | ||
18026 | + dwrq->flags = (adev->wep_restricted == 1) ? | ||
18027 | + IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN; | ||
18028 | + dwrq->length = adev->wep_keys[index].size; | ||
18029 | + | ||
18030 | + memcpy(extra, adev->wep_keys[index].key, | ||
18031 | + adev->wep_keys[index].size); | ||
18032 | + } | ||
18033 | + | ||
18034 | + /* set the current index */ | ||
18035 | + SET_BIT(dwrq->flags, index + 1); | ||
18036 | + | ||
18037 | + log(L_IOCTL, "len=%d, key=%p, flags=0x%X\n", | ||
18038 | + dwrq->length, dwrq->pointer, | ||
18039 | + dwrq->flags); | ||
18040 | + | ||
18041 | + FN_EXIT1(OK); | ||
18042 | + return OK; | ||
18043 | +} | ||
18044 | + | ||
18045 | + | ||
18046 | +/*********************************************************************** | ||
18047 | +*/ | ||
18048 | +static int | ||
18049 | +acx_ioctl_set_power( | ||
18050 | + struct net_device *ndev, | ||
18051 | + struct iw_request_info *info, | ||
18052 | + union iwreq_data *wrqu, | ||
18053 | + char *extra) | ||
18054 | +{ | ||
18055 | + struct iw_param *vwrq = &wrqu->power; | ||
18056 | + acx_device_t *adev = ndev2adev(ndev); | ||
18057 | + int result = -EINPROGRESS; | ||
18058 | + | ||
18059 | + FN_ENTER; | ||
18060 | + | ||
18061 | + log(L_IOCTL, "set 802.11 powersave flags=0x%04X\n", vwrq->flags); | ||
18062 | + | ||
18063 | + acx_sem_lock(adev); | ||
18064 | + | ||
18065 | + if (vwrq->disabled) { | ||
18066 | + CLEAR_BIT(adev->ps_wakeup_cfg, PS_CFG_ENABLE); | ||
18067 | + SET_BIT(adev->set_mask, GETSET_POWER_80211); | ||
18068 | + goto end; | ||
18069 | + } | ||
18070 | + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { | ||
18071 | + u16 ps_timeout = (vwrq->value * 1024) / 1000; | ||
18072 | + | ||
18073 | + if (ps_timeout > 255) | ||
18074 | + ps_timeout = 255; | ||
18075 | + log(L_IOCTL, "setting PS timeout value to %d time units " | ||
18076 | + "due to %dus\n", ps_timeout, vwrq->value); | ||
18077 | + adev->ps_hangover_period = ps_timeout; | ||
18078 | + } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { | ||
18079 | + u16 ps_periods = vwrq->value / 1000000; | ||
18080 | + | ||
18081 | + if (ps_periods > 255) | ||
18082 | + ps_periods = 255; | ||
18083 | + log(L_IOCTL, "setting PS period value to %d periods " | ||
18084 | + "due to %dus\n", ps_periods, vwrq->value); | ||
18085 | + adev->ps_listen_interval = ps_periods; | ||
18086 | + CLEAR_BIT(adev->ps_wakeup_cfg, PS_CFG_WAKEUP_MODE_MASK); | ||
18087 | + SET_BIT(adev->ps_wakeup_cfg, PS_CFG_WAKEUP_EACH_ITVL); | ||
18088 | + } | ||
18089 | + | ||
18090 | + switch (vwrq->flags & IW_POWER_MODE) { | ||
18091 | + /* FIXME: are we doing the right thing here? */ | ||
18092 | + case IW_POWER_UNICAST_R: | ||
18093 | + CLEAR_BIT(adev->ps_options, PS_OPT_STILL_RCV_BCASTS); | ||
18094 | + break; | ||
18095 | + case IW_POWER_MULTICAST_R: | ||
18096 | + SET_BIT(adev->ps_options, PS_OPT_STILL_RCV_BCASTS); | ||
18097 | + break; | ||
18098 | + case IW_POWER_ALL_R: | ||
18099 | + SET_BIT(adev->ps_options, PS_OPT_STILL_RCV_BCASTS); | ||
18100 | + break; | ||
18101 | + case IW_POWER_ON: | ||
18102 | + break; | ||
18103 | + default: | ||
18104 | + log(L_IOCTL, "unknown PS mode\n"); | ||
18105 | + result = -EINVAL; | ||
18106 | + goto end; | ||
18107 | + } | ||
18108 | + | ||
18109 | + SET_BIT(adev->ps_wakeup_cfg, PS_CFG_ENABLE); | ||
18110 | + SET_BIT(adev->set_mask, GETSET_POWER_80211); | ||
18111 | +end: | ||
18112 | + acx_sem_unlock(adev); | ||
18113 | + | ||
18114 | + FN_EXIT1(result); | ||
18115 | + return result; | ||
18116 | +} | ||
18117 | + | ||
18118 | + | ||
18119 | +/*********************************************************************** | ||
18120 | +*/ | ||
18121 | +static int | ||
18122 | +acx_ioctl_get_power( | ||
18123 | + struct net_device *ndev, | ||
18124 | + struct iw_request_info *info, | ||
18125 | + union iwreq_data *wrqu, | ||
18126 | + char *extra) | ||
18127 | +{ | ||
18128 | + struct iw_param *vwrq = &wrqu->power; | ||
18129 | + acx_device_t *adev = ndev2adev(ndev); | ||
18130 | + | ||
18131 | + FN_ENTER; | ||
18132 | + | ||
18133 | + log(L_IOCTL, "Get 802.11 Power Save flags = 0x%04X\n", vwrq->flags); | ||
18134 | + vwrq->disabled = ((adev->ps_wakeup_cfg & PS_CFG_ENABLE) == 0); | ||
18135 | + if (vwrq->disabled) | ||
18136 | + goto end; | ||
18137 | + | ||
18138 | + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { | ||
18139 | + vwrq->value = adev->ps_hangover_period * 1000 / 1024; | ||
18140 | + vwrq->flags = IW_POWER_TIMEOUT; | ||
18141 | + } else { | ||
18142 | + vwrq->value = adev->ps_listen_interval * 1000000; | ||
18143 | + vwrq->flags = IW_POWER_PERIOD|IW_POWER_RELATIVE; | ||
18144 | + } | ||
18145 | + if (adev->ps_options & PS_OPT_STILL_RCV_BCASTS) | ||
18146 | + SET_BIT(vwrq->flags, IW_POWER_ALL_R); | ||
18147 | + else | ||
18148 | + SET_BIT(vwrq->flags, IW_POWER_UNICAST_R); | ||
18149 | +end: | ||
18150 | + FN_EXIT1(OK); | ||
18151 | + return OK; | ||
18152 | +} | ||
18153 | + | ||
18154 | + | ||
18155 | +/*********************************************************************** | ||
18156 | +** acx_ioctl_get_txpow | ||
18157 | +*/ | ||
18158 | +static inline int | ||
18159 | +acx_ioctl_get_txpow( | ||
18160 | + struct net_device *ndev, | ||
18161 | + struct iw_request_info *info, | ||
18162 | + union iwreq_data *wrqu, | ||
18163 | + char *extra) | ||
18164 | +{ | ||
18165 | + struct iw_param *vwrq = &wrqu->power; | ||
18166 | + acx_device_t *adev = ndev2adev(ndev); | ||
18167 | + | ||
18168 | + FN_ENTER; | ||
18169 | + | ||
18170 | + vwrq->flags = IW_TXPOW_DBM; | ||
18171 | + vwrq->disabled = 0; | ||
18172 | + vwrq->fixed = 1; | ||
18173 | + vwrq->value = adev->tx_level_dbm; | ||
18174 | + | ||
18175 | + log(L_IOCTL, "get txpower:%d dBm\n", adev->tx_level_dbm); | ||
18176 | + | ||
18177 | + FN_EXIT1(OK); | ||
18178 | + return OK; | ||
18179 | +} | ||
18180 | + | ||
18181 | + | ||
18182 | +/*********************************************************************** | ||
18183 | +** acx_ioctl_set_txpow | ||
18184 | +*/ | ||
18185 | +static int | ||
18186 | +acx_ioctl_set_txpow( | ||
18187 | + struct net_device *ndev, | ||
18188 | + struct iw_request_info *info, | ||
18189 | + union iwreq_data *wrqu, | ||
18190 | + char *extra) | ||
18191 | +{ | ||
18192 | + struct iw_param *vwrq = &wrqu->power; | ||
18193 | + acx_device_t *adev = ndev2adev(ndev); | ||
18194 | + int result; | ||
18195 | + | ||
18196 | + FN_ENTER; | ||
18197 | + | ||
18198 | + log(L_IOCTL, "set txpower:%d, disabled:%d, flags:0x%04X\n", | ||
18199 | + vwrq->value, vwrq->disabled, vwrq->flags); | ||
18200 | + | ||
18201 | + acx_sem_lock(adev); | ||
18202 | + | ||
18203 | + if (vwrq->disabled != adev->tx_disabled) { | ||
18204 | + SET_BIT(adev->set_mask, GETSET_TX); | ||
18205 | + } | ||
18206 | + | ||
18207 | + adev->tx_disabled = vwrq->disabled; | ||
18208 | + if (vwrq->value == -1) { | ||
18209 | + if (vwrq->disabled) { | ||
18210 | + adev->tx_level_dbm = 0; | ||
18211 | + log(L_IOCTL, "disable radio tx\n"); | ||
18212 | + } else { | ||
18213 | + /* adev->tx_level_auto = 1; */ | ||
18214 | + log(L_IOCTL, "set tx power auto (NIY)\n"); | ||
18215 | + } | ||
18216 | + } else { | ||
18217 | + adev->tx_level_dbm = vwrq->value <= 20 ? vwrq->value : 20; | ||
18218 | + /* adev->tx_level_auto = 0; */ | ||
18219 | + log(L_IOCTL, "set txpower=%d dBm\n", adev->tx_level_dbm); | ||
18220 | + } | ||
18221 | + SET_BIT(adev->set_mask, GETSET_TXPOWER); | ||
18222 | + | ||
18223 | + result = -EINPROGRESS; | ||
18224 | + | ||
18225 | + acx_sem_unlock(adev); | ||
18226 | + | ||
18227 | + FN_EXIT1(result); | ||
18228 | + return result; | ||
18229 | +} | ||
18230 | + | ||
18231 | + | ||
18232 | +/*********************************************************************** | ||
18233 | +** acx_ioctl_get_range | ||
18234 | +*/ | ||
18235 | +static int | ||
18236 | +acx_ioctl_get_range( | ||
18237 | + struct net_device *ndev, | ||
18238 | + struct iw_request_info *info, | ||
18239 | + union iwreq_data *wrqu, | ||
18240 | + char *extra) | ||
18241 | +{ | ||
18242 | + struct iw_point *dwrq = &wrqu->data; | ||
18243 | + struct iw_range *range = (struct iw_range *)extra; | ||
18244 | + acx_device_t *adev = ndev2adev(ndev); | ||
18245 | + int i,n; | ||
18246 | + | ||
18247 | + FN_ENTER; | ||
18248 | + | ||
18249 | + if (!dwrq->pointer) | ||
18250 | + goto end; | ||
18251 | + | ||
18252 | + dwrq->length = sizeof(struct iw_range); | ||
18253 | + memset(range, 0, sizeof(struct iw_range)); | ||
18254 | + n = 0; | ||
18255 | + for (i = 1; i <= 14; i++) { | ||
18256 | + if (adev->reg_dom_chanmask & (1 << (i - 1))) { | ||
18257 | + range->freq[n].i = i; | ||
18258 | + range->freq[n].m = acx_channel_freq[i - 1] * 100000; | ||
18259 | + range->freq[n].e = 1; /* units are MHz */ | ||
18260 | + n++; | ||
18261 | + } | ||
18262 | + } | ||
18263 | + range->num_channels = n; | ||
18264 | + range->num_frequency = n; | ||
18265 | + | ||
18266 | + range->min_rts = 0; | ||
18267 | + range->max_rts = 2312; | ||
18268 | + | ||
18269 | +#if ACX_FRAGMENTATION | ||
18270 | + range->min_frag = 256; | ||
18271 | + range->max_frag = 2312; | ||
18272 | +#endif | ||
18273 | + | ||
18274 | + range->encoding_size[0] = 5; | ||
18275 | + range->encoding_size[1] = 13; | ||
18276 | + range->encoding_size[2] = 29; | ||
18277 | + range->num_encoding_sizes = 3; | ||
18278 | + range->max_encoding_tokens = 4; | ||
18279 | + | ||
18280 | + range->min_pmp = 0; | ||
18281 | + range->max_pmp = 5000000; | ||
18282 | + range->min_pmt = 0; | ||
18283 | + range->max_pmt = 65535 * 1000; | ||
18284 | + range->pmp_flags = IW_POWER_PERIOD; | ||
18285 | + range->pmt_flags = IW_POWER_TIMEOUT; | ||
18286 | + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; | ||
18287 | + | ||
18288 | + if (IS_ACX100(adev)) { /* ACX100 has direct radio programming - arbitrary levels, so offer a lot */ | ||
18289 | + for (i = 0; i <= IW_MAX_TXPOWER - 1; i++) | ||
18290 | + range->txpower[i] = 20 * i / (IW_MAX_TXPOWER - 1); | ||
18291 | + range->num_txpower = IW_MAX_TXPOWER; | ||
18292 | + range->txpower_capa = IW_TXPOW_DBM; | ||
18293 | + } | ||
18294 | + else { | ||
18295 | + int count = min(IW_MAX_TXPOWER, (int)adev->cfgopt_power_levels.len); | ||
18296 | + for (i = 0; i <= count; i++) | ||
18297 | + range->txpower[i] = adev->cfgopt_power_levels.list[i]; | ||
18298 | + range->num_txpower = count; | ||
18299 | + /* this list is given in mW */ | ||
18300 | + range->txpower_capa = IW_TXPOW_MWATT; | ||
18301 | + } | ||
18302 | + | ||
18303 | + range->we_version_compiled = WIRELESS_EXT; | ||
18304 | + range->we_version_source = 0x9; | ||
18305 | + | ||
18306 | + range->retry_capa = IW_RETRY_LIMIT; | ||
18307 | + range->retry_flags = IW_RETRY_LIMIT; | ||
18308 | + range->min_retry = 1; | ||
18309 | + range->max_retry = 255; | ||
18310 | + | ||
18311 | + range->r_time_flags = IW_RETRY_LIFETIME; | ||
18312 | + range->min_r_time = 0; | ||
18313 | + /* FIXME: lifetime ranges and orders of magnitude are strange?? */ | ||
18314 | + range->max_r_time = 65535; | ||
18315 | + | ||
18316 | + if (IS_USB(adev)) | ||
18317 | + range->sensitivity = 0; | ||
18318 | + else if (IS_ACX111(adev)) | ||
18319 | + range->sensitivity = 3; | ||
18320 | + else | ||
18321 | + range->sensitivity = 255; | ||
18322 | + | ||
18323 | + for (i=0; i < adev->rate_supported_len; i++) { | ||
18324 | + range->bitrate[i] = (adev->rate_supported[i] & ~0x80) * 500000; | ||
18325 | + /* never happens, but keep it, to be safe: */ | ||
18326 | + if (range->bitrate[i] == 0) | ||
18327 | + break; | ||
18328 | + } | ||
18329 | + range->num_bitrates = i; | ||
18330 | + | ||
18331 | + range->max_qual.qual = 100; | ||
18332 | + range->max_qual.level = 100; | ||
18333 | + range->max_qual.noise = 100; | ||
18334 | + /* TODO: better values */ | ||
18335 | + range->avg_qual.qual = 90; | ||
18336 | + range->avg_qual.level = 80; | ||
18337 | + range->avg_qual.noise = 2; | ||
18338 | + | ||
18339 | +end: | ||
18340 | + FN_EXIT1(OK); | ||
18341 | + return OK; | ||
18342 | +} | ||
18343 | + | ||
18344 | + | ||
18345 | +/*********************************************************************** | ||
18346 | +** Private functions | ||
18347 | +*/ | ||
18348 | + | ||
18349 | +/*********************************************************************** | ||
18350 | +** acx_ioctl_get_nick | ||
18351 | +*/ | ||
18352 | +static inline int | ||
18353 | +acx_ioctl_get_nick( | ||
18354 | + struct net_device *ndev, | ||
18355 | + struct iw_request_info *info, | ||
18356 | + union iwreq_data *wrqu, | ||
18357 | + char *extra) | ||
18358 | +{ | ||
18359 | + struct iw_point *dwrq = &wrqu->data; | ||
18360 | + acx_device_t *adev = ndev2adev(ndev); | ||
18361 | + | ||
18362 | + strcpy(extra, adev->nick); | ||
18363 | + dwrq->length = strlen(extra) + 1; | ||
18364 | + | ||
18365 | + return OK; | ||
18366 | +} | ||
18367 | + | ||
18368 | + | ||
18369 | +/*********************************************************************** | ||
18370 | +** acx_ioctl_set_nick | ||
18371 | +*/ | ||
18372 | +static int | ||
18373 | +acx_ioctl_set_nick( | ||
18374 | + struct net_device *ndev, | ||
18375 | + struct iw_request_info *info, | ||
18376 | + union iwreq_data *wrqu, | ||
18377 | + char *extra) | ||
18378 | +{ | ||
18379 | + struct iw_point *dwrq = &wrqu->data; | ||
18380 | + acx_device_t *adev = ndev2adev(ndev); | ||
18381 | + int result; | ||
18382 | + | ||
18383 | + FN_ENTER; | ||
18384 | + | ||
18385 | + acx_sem_lock(adev); | ||
18386 | + | ||
18387 | + if (dwrq->length > IW_ESSID_MAX_SIZE + 1) { | ||
18388 | + result = -E2BIG; | ||
18389 | + goto end_unlock; | ||
18390 | + } | ||
18391 | + | ||
18392 | + /* extra includes trailing \0, so it's ok */ | ||
18393 | + strcpy(adev->nick, extra); | ||
18394 | + result = OK; | ||
18395 | + | ||
18396 | +end_unlock: | ||
18397 | + acx_sem_unlock(adev); | ||
18398 | + | ||
18399 | + FN_EXIT1(result); | ||
18400 | + return result; | ||
18401 | +} | ||
18402 | + | ||
18403 | + | ||
18404 | +/*********************************************************************** | ||
18405 | +** acx_ioctl_get_retry | ||
18406 | +*/ | ||
18407 | +static int | ||
18408 | +acx_ioctl_get_retry( | ||
18409 | + struct net_device *ndev, | ||
18410 | + struct iw_request_info *info, | ||
18411 | + union iwreq_data *wrqu, | ||
18412 | + char *extra) | ||
18413 | +{ | ||
18414 | + struct iw_param *vwrq = &wrqu->retry; | ||
18415 | + acx_device_t *adev = ndev2adev(ndev); | ||
18416 | + unsigned int type = vwrq->flags & IW_RETRY_TYPE; | ||
18417 | + unsigned int modifier = vwrq->flags & IW_RETRY_MODIFIER; | ||
18418 | + int result; | ||
18419 | + | ||
18420 | + FN_ENTER; | ||
18421 | + | ||
18422 | + acx_sem_lock(adev); | ||
18423 | + | ||
18424 | + /* return the short retry number by default */ | ||
18425 | + if (type == IW_RETRY_LIFETIME) { | ||
18426 | + vwrq->flags = IW_RETRY_LIFETIME; | ||
18427 | + vwrq->value = adev->msdu_lifetime; | ||
18428 | + } else if (modifier == IW_RETRY_MAX) { | ||
18429 | + vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; | ||
18430 | + vwrq->value = adev->long_retry; | ||
18431 | + } else { | ||
18432 | + vwrq->flags = IW_RETRY_LIMIT; | ||
18433 | + if (adev->long_retry != adev->short_retry) | ||
18434 | + SET_BIT(vwrq->flags, IW_RETRY_MIN); | ||
18435 | + vwrq->value = adev->short_retry; | ||
18436 | + } | ||
18437 | + | ||
18438 | + /* can't be disabled */ | ||
18439 | + vwrq->disabled = (u8)0; | ||
18440 | + result = OK; | ||
18441 | + | ||
18442 | + acx_sem_unlock(adev); | ||
18443 | + | ||
18444 | + FN_EXIT1(result); | ||
18445 | + return result; | ||
18446 | +} | ||
18447 | + | ||
18448 | + | ||
18449 | +/*********************************************************************** | ||
18450 | +** acx_ioctl_set_retry | ||
18451 | +*/ | ||
18452 | +static int | ||
18453 | +acx_ioctl_set_retry( | ||
18454 | + struct net_device *ndev, | ||
18455 | + struct iw_request_info *info, | ||
18456 | + union iwreq_data *wrqu, | ||
18457 | + char *extra) | ||
18458 | +{ | ||
18459 | + struct iw_param *vwrq = &wrqu->retry; | ||
18460 | + acx_device_t *adev = ndev2adev(ndev); | ||
18461 | + int result; | ||
18462 | + | ||
18463 | + FN_ENTER; | ||
18464 | + | ||
18465 | + if (!vwrq) { | ||
18466 | + result = -EFAULT; | ||
18467 | + goto end; | ||
18468 | + } | ||
18469 | + if (vwrq->disabled) { | ||
18470 | + result = -EINVAL; | ||
18471 | + goto end; | ||
18472 | + } | ||
18473 | + | ||
18474 | + acx_sem_lock(adev); | ||
18475 | + | ||
18476 | + result = -EINVAL; | ||
18477 | + if (IW_RETRY_LIMIT == (vwrq->flags & IW_RETRY_TYPE)) { | ||
18478 | + printk("old retry limits: short %d long %d\n", | ||
18479 | + adev->short_retry, adev->long_retry); | ||
18480 | + if (vwrq->flags & IW_RETRY_MAX) { | ||
18481 | + adev->long_retry = vwrq->value; | ||
18482 | + } else if (vwrq->flags & IW_RETRY_MIN) { | ||
18483 | + adev->short_retry = vwrq->value; | ||
18484 | + } else { | ||
18485 | + /* no modifier: set both */ | ||
18486 | + adev->long_retry = vwrq->value; | ||
18487 | + adev->short_retry = vwrq->value; | ||
18488 | + } | ||
18489 | + printk("new retry limits: short %d long %d\n", | ||
18490 | + adev->short_retry, adev->long_retry); | ||
18491 | + SET_BIT(adev->set_mask, GETSET_RETRY); | ||
18492 | + result = -EINPROGRESS; | ||
18493 | + } | ||
18494 | + else if (vwrq->flags & IW_RETRY_LIFETIME) { | ||
18495 | + adev->msdu_lifetime = vwrq->value; | ||
18496 | + printk("new MSDU lifetime: %d\n", adev->msdu_lifetime); | ||
18497 | + SET_BIT(adev->set_mask, SET_MSDU_LIFETIME); | ||
18498 | + result = -EINPROGRESS; | ||
18499 | + } | ||
18500 | + | ||
18501 | + acx_sem_unlock(adev); | ||
18502 | +end: | ||
18503 | + FN_EXIT1(result); | ||
18504 | + return result; | ||
18505 | +} | ||
18506 | + | ||
18507 | + | ||
18508 | +/************************ private ioctls ******************************/ | ||
18509 | + | ||
18510 | + | ||
18511 | +/*********************************************************************** | ||
18512 | +** acx_ioctl_set_debug | ||
18513 | +*/ | ||
18514 | +#if ACX_DEBUG | ||
18515 | +static int | ||
18516 | +acx_ioctl_set_debug( | ||
18517 | + struct net_device *ndev, | ||
18518 | + struct iw_request_info *info, | ||
18519 | + union iwreq_data *wrqu, | ||
18520 | + char *extra) | ||
18521 | +{ | ||
18522 | + unsigned int debug_new = *((unsigned int *)extra); | ||
18523 | + int result = -EINVAL; | ||
18524 | + | ||
18525 | + log(L_ANY, "setting debug from %04X to %04X\n", acx_debug, debug_new); | ||
18526 | + acx_debug = debug_new; | ||
18527 | + | ||
18528 | + result = OK; | ||
18529 | + return result; | ||
18530 | + | ||
18531 | +} | ||
18532 | +#endif | ||
18533 | + | ||
18534 | + | ||
18535 | +/*********************************************************************** | ||
18536 | +** acx_ioctl_list_reg_domain | ||
18537 | +*/ | ||
18538 | +static int | ||
18539 | +acx_ioctl_list_reg_domain( | ||
18540 | + struct net_device *ndev, | ||
18541 | + struct iw_request_info *info, | ||
18542 | + union iwreq_data *wrqu, | ||
18543 | + char *extra) | ||
18544 | +{ | ||
18545 | + int i = 1; | ||
18546 | + const char * const *entry = acx_reg_domain_strings; | ||
18547 | + | ||
18548 | + printk("dom# chan# domain/country\n"); | ||
18549 | + while (*entry) | ||
18550 | + printk("%4d %s\n", i++, *entry++); | ||
18551 | + return OK; | ||
18552 | +} | ||
18553 | + | ||
18554 | + | ||
18555 | +/*********************************************************************** | ||
18556 | +** acx_ioctl_set_reg_domain | ||
18557 | +*/ | ||
18558 | +static int | ||
18559 | +acx_ioctl_set_reg_domain( | ||
18560 | + struct net_device *ndev, | ||
18561 | + struct iw_request_info *info, | ||
18562 | + union iwreq_data *wrqu, | ||
18563 | + char *extra) | ||
18564 | +{ | ||
18565 | + acx_device_t *adev = ndev2adev(ndev); | ||
18566 | + int result; | ||
18567 | + | ||
18568 | + FN_ENTER; | ||
18569 | + | ||
18570 | + if ((*extra < 1) || ((size_t)*extra > acx_reg_domain_ids_len)) { | ||
18571 | + result = -EINVAL; | ||
18572 | + goto end; | ||
18573 | + } | ||
18574 | + | ||
18575 | + acx_sem_lock(adev); | ||
18576 | + | ||
18577 | + adev->reg_dom_id = acx_reg_domain_ids[*extra - 1]; | ||
18578 | + SET_BIT(adev->set_mask, GETSET_REG_DOMAIN); | ||
18579 | + | ||
18580 | + result = -EINPROGRESS; | ||
18581 | + | ||
18582 | + acx_sem_unlock(adev); | ||
18583 | +end: | ||
18584 | + FN_EXIT1(result); | ||
18585 | + return result; | ||
18586 | +} | ||
18587 | + | ||
18588 | + | ||
18589 | +/*********************************************************************** | ||
18590 | +** acx_ioctl_get_reg_domain | ||
18591 | +*/ | ||
18592 | +static int | ||
18593 | +acx_ioctl_get_reg_domain( | ||
18594 | + struct net_device *ndev, | ||
18595 | + struct iw_request_info *info, | ||
18596 | + union iwreq_data *wrqu, | ||
18597 | + char *extra) | ||
18598 | +{ | ||
18599 | + acx_device_t *adev = ndev2adev(ndev); | ||
18600 | + int dom,i; | ||
18601 | + | ||
18602 | + /* no locking */ | ||
18603 | + dom = adev->reg_dom_id; | ||
18604 | + | ||
18605 | + for (i = 1; i <= acx_reg_domain_ids_len; i++) { | ||
18606 | + if (acx_reg_domain_ids[i-1] == dom) { | ||
18607 | + log(L_IOCTL, "regulatory domain is currently set " | ||
18608 | + "to %d (0x%X): %s\n", i, dom, | ||
18609 | + acx_reg_domain_strings[i-1]); | ||
18610 | + *extra = i; | ||
18611 | + break; | ||
18612 | + } | ||
18613 | + } | ||
18614 | + | ||
18615 | + return OK; | ||
18616 | +} | ||
18617 | + | ||
18618 | + | ||
18619 | +/*********************************************************************** | ||
18620 | +** acx_ioctl_set_short_preamble | ||
18621 | +*/ | ||
18622 | +static const char * const | ||
18623 | +preamble_modes[] = { | ||
18624 | + "off", | ||
18625 | + "on", | ||
18626 | + "auto (peer capability dependent)", | ||
18627 | + "unknown mode, error" | ||
18628 | +}; | ||
18629 | + | ||
18630 | +static int | ||
18631 | +acx_ioctl_set_short_preamble( | ||
18632 | + struct net_device *ndev, | ||
18633 | + struct iw_request_info *info, | ||
18634 | + union iwreq_data *wrqu, | ||
18635 | + char *extra) | ||
18636 | +{ | ||
18637 | + acx_device_t *adev = ndev2adev(ndev); | ||
18638 | + int i; | ||
18639 | + int result; | ||
18640 | + | ||
18641 | + FN_ENTER; | ||
18642 | + | ||
18643 | + if ((unsigned char)*extra > 2) { | ||
18644 | + result = -EINVAL; | ||
18645 | + goto end; | ||
18646 | + } | ||
18647 | + | ||
18648 | + acx_sem_lock(adev); | ||
18649 | + | ||
18650 | + adev->preamble_mode = (u8)*extra; | ||
18651 | + switch (adev->preamble_mode) { | ||
18652 | + case 0: /* long */ | ||
18653 | + adev->preamble_cur = 0; | ||
18654 | + break; | ||
18655 | + case 1: | ||
18656 | + /* short, kick incapable peers */ | ||
18657 | + adev->preamble_cur = 1; | ||
18658 | + for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { | ||
18659 | + client_t *clt = &adev->sta_list[i]; | ||
18660 | + if (!clt->used) continue; | ||
18661 | + if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) { | ||
18662 | + clt->used = CLIENT_EMPTY_SLOT_0; | ||
18663 | + } | ||
18664 | + } | ||
18665 | + switch (adev->mode) { | ||
18666 | + case ACX_MODE_2_STA: | ||
18667 | + if (adev->ap_client && !adev->ap_client->used) { | ||
18668 | + /* We kicked our AP :) */ | ||
18669 | + SET_BIT(adev->set_mask, GETSET_RESCAN); | ||
18670 | + } | ||
18671 | + } | ||
18672 | + break; | ||
18673 | + case 2: /* auto. short only if all peers are short-capable */ | ||
18674 | + adev->preamble_cur = 1; | ||
18675 | + for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { | ||
18676 | + client_t *clt = &adev->sta_list[i]; | ||
18677 | + if (!clt->used) continue; | ||
18678 | + if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) { | ||
18679 | + adev->preamble_cur = 0; | ||
18680 | + break; | ||
18681 | + } | ||
18682 | + } | ||
18683 | + break; | ||
18684 | + } | ||
18685 | + printk("new short preamble setting: configured %s, active %s\n", | ||
18686 | + preamble_modes[adev->preamble_mode], | ||
18687 | + preamble_modes[adev->preamble_cur]); | ||
18688 | + result = OK; | ||
18689 | + | ||
18690 | + acx_sem_unlock(adev); | ||
18691 | +end: | ||
18692 | + FN_EXIT1(result); | ||
18693 | + return result; | ||
18694 | +} | ||
18695 | + | ||
18696 | + | ||
18697 | +/*********************************************************************** | ||
18698 | +** acx_ioctl_get_short_preamble | ||
18699 | +*/ | ||
18700 | +static int | ||
18701 | +acx_ioctl_get_short_preamble( | ||
18702 | + struct net_device *ndev, | ||
18703 | + struct iw_request_info *info, | ||
18704 | + union iwreq_data *wrqu, | ||
18705 | + char *extra) | ||
18706 | +{ | ||
18707 | + acx_device_t *adev = ndev2adev(ndev); | ||
18708 | + | ||
18709 | + acx_sem_lock(adev); | ||
18710 | + | ||
18711 | + printk("current short preamble setting: configured %s, active %s\n", | ||
18712 | + preamble_modes[adev->preamble_mode], | ||
18713 | + preamble_modes[adev->preamble_cur]); | ||
18714 | + | ||
18715 | + *extra = (char)adev->preamble_mode; | ||
18716 | + | ||
18717 | + acx_sem_unlock(adev); | ||
18718 | + | ||
18719 | + return OK; | ||
18720 | +} | ||
18721 | + | ||
18722 | + | ||
18723 | +/*********************************************************************** | ||
18724 | +** acx_ioctl_set_antenna | ||
18725 | +** | ||
18726 | +** TX and RX antenna can be set separately but this function good | ||
18727 | +** for testing 0-4 bits | ||
18728 | +*/ | ||
18729 | +static int | ||
18730 | +acx_ioctl_set_antenna( | ||
18731 | + struct net_device *ndev, | ||
18732 | + struct iw_request_info *info, | ||
18733 | + union iwreq_data *wrqu, | ||
18734 | + char *extra) | ||
18735 | +{ | ||
18736 | + acx_device_t *adev = ndev2adev(ndev); | ||
18737 | + | ||
18738 | + acx_sem_lock(adev); | ||
18739 | + | ||
18740 | + printk("old antenna value: 0x%02X (COMBINED bit mask)\n" | ||
18741 | + "Rx antenna selection:\n" | ||
18742 | + "0x00 ant. 1\n" | ||
18743 | + "0x40 ant. 2\n" | ||
18744 | + "0x80 full diversity\n" | ||
18745 | + "0xc0 partial diversity\n" | ||
18746 | + "0x0f dwell time mask (in units of us)\n" | ||
18747 | + "Tx antenna selection:\n" | ||
18748 | + "0x00 ant. 2\n" /* yep, those ARE reversed! */ | ||
18749 | + "0x20 ant. 1\n" | ||
18750 | + "new antenna value: 0x%02X\n", | ||
18751 | + adev->antenna, (u8)*extra); | ||
18752 | + | ||
18753 | + adev->antenna = (u8)*extra; | ||
18754 | + SET_BIT(adev->set_mask, GETSET_ANTENNA); | ||
18755 | + | ||
18756 | + acx_sem_unlock(adev); | ||
18757 | + | ||
18758 | + return -EINPROGRESS; | ||
18759 | +} | ||
18760 | + | ||
18761 | + | ||
18762 | +/*********************************************************************** | ||
18763 | +** acx_ioctl_get_antenna | ||
18764 | +*/ | ||
18765 | +static int | ||
18766 | +acx_ioctl_get_antenna( | ||
18767 | + struct net_device *ndev, | ||
18768 | + struct iw_request_info *info, | ||
18769 | + union iwreq_data *wrqu, | ||
18770 | + char *extra) | ||
18771 | +{ | ||
18772 | + acx_device_t *adev = ndev2adev(ndev); | ||
18773 | + | ||
18774 | + /* no locking. it's pointless to lock a single load */ | ||
18775 | + printk("current antenna value: 0x%02X (COMBINED bit mask)\n" | ||
18776 | + "Rx antenna selection:\n" | ||
18777 | + "0x00 ant. 1\n" | ||
18778 | + "0x40 ant. 2\n" | ||
18779 | + "0x80 full diversity\n" | ||
18780 | + "0xc0 partial diversity\n" | ||
18781 | + "Tx antenna selection:\n" | ||
18782 | + "0x00 ant. 2\n" /* yep, those ARE reversed! */ | ||
18783 | + "0x20 ant. 1\n", adev->antenna); | ||
18784 | + | ||
18785 | + return 0; | ||
18786 | +} | ||
18787 | + | ||
18788 | + | ||
18789 | +/*********************************************************************** | ||
18790 | +** acx_ioctl_set_rx_antenna | ||
18791 | +** | ||
18792 | +** 0 = antenna1; 1 = antenna2; 2 = full diversity; 3 = partial diversity | ||
18793 | +** Could anybody test which antenna is the external one? | ||
18794 | +*/ | ||
18795 | +static int | ||
18796 | +acx_ioctl_set_rx_antenna( | ||
18797 | + struct net_device *ndev, | ||
18798 | + struct iw_request_info *info, | ||
18799 | + union iwreq_data *wrqu, | ||
18800 | + char *extra) | ||
18801 | +{ | ||
18802 | + acx_device_t *adev = ndev2adev(ndev); | ||
18803 | + int result; | ||
18804 | + | ||
18805 | + FN_ENTER; | ||
18806 | + | ||
18807 | + if (*extra > 3) { | ||
18808 | + result = -EINVAL; | ||
18809 | + goto end; | ||
18810 | + } | ||
18811 | + | ||
18812 | + printk("old antenna value: 0x%02X\n", adev->antenna); | ||
18813 | + | ||
18814 | + acx_sem_lock(adev); | ||
18815 | + | ||
18816 | + adev->antenna &= 0x3f; | ||
18817 | + SET_BIT(adev->antenna, (*extra << 6)); | ||
18818 | + SET_BIT(adev->set_mask, GETSET_ANTENNA); | ||
18819 | + printk("new antenna value: 0x%02X\n", adev->antenna); | ||
18820 | + result = -EINPROGRESS; | ||
18821 | + | ||
18822 | + acx_sem_unlock(adev); | ||
18823 | +end: | ||
18824 | + FN_EXIT1(result); | ||
18825 | + return result; | ||
18826 | +} | ||
18827 | + | ||
18828 | + | ||
18829 | +/*********************************************************************** | ||
18830 | +** acx_ioctl_set_tx_antenna | ||
18831 | +** | ||
18832 | +** Arguments: 0 == antenna2; 1 == antenna1; | ||
18833 | +** Could anybody test which antenna is the external one? | ||
18834 | +*/ | ||
18835 | +static int | ||
18836 | +acx_ioctl_set_tx_antenna( | ||
18837 | + struct net_device *ndev, | ||
18838 | + struct iw_request_info *info, | ||
18839 | + union iwreq_data *wrqu, | ||
18840 | + char *extra) | ||
18841 | +{ | ||
18842 | + acx_device_t *adev = ndev2adev(ndev); | ||
18843 | + int result; | ||
18844 | + | ||
18845 | + FN_ENTER; | ||
18846 | + | ||
18847 | + if (*extra > 1) { | ||
18848 | + result = -EINVAL; | ||
18849 | + goto end; | ||
18850 | + } | ||
18851 | + | ||
18852 | + printk("old antenna value: 0x%02X\n", adev->antenna); | ||
18853 | + | ||
18854 | + acx_sem_lock(adev); | ||
18855 | + | ||
18856 | + adev->antenna &= ~0x30; | ||
18857 | + SET_BIT(adev->antenna, ((*extra & 0x01) << 5)); | ||
18858 | + SET_BIT(adev->set_mask, GETSET_ANTENNA); | ||
18859 | + printk("new antenna value: 0x%02X\n", adev->antenna); | ||
18860 | + result = -EINPROGRESS; | ||
18861 | + | ||
18862 | + acx_sem_unlock(adev); | ||
18863 | +end: | ||
18864 | + FN_EXIT1(result); | ||
18865 | + return result; | ||
18866 | +} | ||
18867 | + | ||
18868 | + | ||
18869 | +/*********************************************************************** | ||
18870 | +** acx_ioctl_wlansniff | ||
18871 | +** | ||
18872 | +** can we just remove this in favor of monitor mode? --vda | ||
18873 | +*/ | ||
18874 | +static int | ||
18875 | +acx_ioctl_wlansniff( | ||
18876 | + struct net_device *ndev, | ||
18877 | + struct iw_request_info *info, | ||
18878 | + union iwreq_data *wrqu, | ||
18879 | + char *extra) | ||
18880 | +{ | ||
18881 | + acx_device_t *adev = ndev2adev(ndev); | ||
18882 | + unsigned int *params = (unsigned int*)extra; | ||
18883 | + unsigned int enable = (unsigned int)(params[0] > 0); | ||
18884 | + int result; | ||
18885 | + | ||
18886 | + FN_ENTER; | ||
18887 | + | ||
18888 | + acx_sem_lock(adev); | ||
18889 | + | ||
18890 | + /* not using printk() here, since it distorts kismet display | ||
18891 | + * when printk messages activated */ | ||
18892 | + log(L_IOCTL, "setting monitor to: 0x%02X\n", params[0]); | ||
18893 | + | ||
18894 | + switch (params[0]) { | ||
18895 | + case 0: | ||
18896 | + /* no monitor mode. hmm, should we simply ignore it | ||
18897 | + * or go back to enabling adev->netdev->type ARPHRD_ETHER? */ | ||
18898 | + break; | ||
18899 | + case 1: | ||
18900 | + adev->monitor_type = ARPHRD_IEEE80211_PRISM; | ||
18901 | + break; | ||
18902 | + case 2: | ||
18903 | + adev->monitor_type = ARPHRD_IEEE80211; | ||
18904 | + break; | ||
18905 | + } | ||
18906 | + | ||
18907 | + if (params[0]) { | ||
18908 | + adev->mode = ACX_MODE_MONITOR; | ||
18909 | + SET_BIT(adev->set_mask, GETSET_MODE); | ||
18910 | + } | ||
18911 | + | ||
18912 | + if (enable) { | ||
18913 | + adev->channel = params[1]; | ||
18914 | + SET_BIT(adev->set_mask, GETSET_RX); | ||
18915 | + } | ||
18916 | + result = -EINPROGRESS; | ||
18917 | + | ||
18918 | + acx_sem_unlock(adev); | ||
18919 | + | ||
18920 | + FN_EXIT1(result); | ||
18921 | + return result; | ||
18922 | +} | ||
18923 | + | ||
18924 | + | ||
18925 | +/*********************************************************************** | ||
18926 | +** acx_ioctl_unknown11 | ||
18927 | +** FIXME: looks like some sort of "iwpriv kick_sta MAC" but it's broken | ||
18928 | +*/ | ||
18929 | +static int | ||
18930 | +acx_ioctl_unknown11( | ||
18931 | + struct net_device *ndev, | ||
18932 | + struct iw_request_info *info, | ||
18933 | + union iwreq_data *wrqu, | ||
18934 | + char *extra) | ||
18935 | +{ | ||
18936 | +#ifdef BROKEN | ||
18937 | + struct iw_param *vwrq = &wrqu->param; | ||
18938 | + acx_device_t *adev = ndev2adev(ndev); | ||
18939 | + unsigned long flags; | ||
18940 | + client_t client; | ||
18941 | + int result; | ||
18942 | + | ||
18943 | + acx_sem_lock(adev); | ||
18944 | + acx_lock(adev, flags); | ||
18945 | + | ||
18946 | + acx_l_transmit_disassoc(adev, &client); | ||
18947 | + result = OK; | ||
18948 | + | ||
18949 | + acx_unlock(adev, flags); | ||
18950 | + acx_sem_unlock(adev); | ||
18951 | + | ||
18952 | + return result; | ||
18953 | +#endif | ||
18954 | + return -EINVAL; | ||
18955 | +} | ||
18956 | + | ||
18957 | + | ||
18958 | +/*********************************************************************** | ||
18959 | +** debug helper function to be able to debug various issues relatively easily | ||
18960 | +*/ | ||
18961 | +static int | ||
18962 | +acx_ioctl_dbg_set_masks( | ||
18963 | + struct net_device *ndev, | ||
18964 | + struct iw_request_info *info, | ||
18965 | + union iwreq_data *wrqu, | ||
18966 | + char *extra) | ||
18967 | +{ | ||
18968 | + acx_device_t *adev = ndev2adev(ndev); | ||
18969 | + const unsigned int *params = (unsigned int*)extra; | ||
18970 | + int result; | ||
18971 | + | ||
18972 | + acx_sem_lock(adev); | ||
18973 | + | ||
18974 | + log(L_IOCTL, "setting flags in settings mask: " | ||
18975 | + "get_mask %08X set_mask %08X\n" | ||
18976 | + "before: get_mask %08X set_mask %08X\n", | ||
18977 | + params[0], params[1], | ||
18978 | + adev->get_mask, adev->set_mask); | ||
18979 | + SET_BIT(adev->get_mask, params[0]); | ||
18980 | + SET_BIT(adev->set_mask, params[1]); | ||
18981 | + log(L_IOCTL, "after: get_mask %08X set_mask %08X\n", | ||
18982 | + adev->get_mask, adev->set_mask); | ||
18983 | + result = -EINPROGRESS; /* immediately call commit handler */ | ||
18984 | + | ||
18985 | + acx_sem_unlock(adev); | ||
18986 | + | ||
18987 | + return result; | ||
18988 | +} | ||
18989 | + | ||
18990 | + | ||
18991 | +/*********************************************************************** | ||
18992 | +* acx_ioctl_set_rates | ||
18993 | +* | ||
18994 | +* This ioctl takes string parameter. Examples: | ||
18995 | +* iwpriv wlan0 SetRates "1,2" | ||
18996 | +* use 1 and 2 Mbit rates, both are in basic rate set | ||
18997 | +* iwpriv wlan0 SetRates "1,2 5,11" | ||
18998 | +* use 1,2,5.5,11 Mbit rates. 1 and 2 are basic | ||
18999 | +* iwpriv wlan0 SetRates "1,2 5c,11c" | ||
19000 | +* same ('c' means 'CCK modulation' and it is a default for 5 and 11) | ||
19001 | +* iwpriv wlan0 SetRates "1,2 5p,11p" | ||
19002 | +* use 1,2,5.5,11 Mbit, 1,2 are basic. 5 and 11 are using PBCC | ||
19003 | +* iwpriv wlan0 SetRates "1,2,5,11 22p" | ||
19004 | +* use 1,2,5.5,11,22 Mbit. 1,2,5.5 and 11 are basic. 22 is using PBCC | ||
19005 | +* (this is the maximum acx100 can do (modulo x4 mode)) | ||
19006 | +* iwpriv wlan0 SetRates "1,2,5,11 22" | ||
19007 | +* same. 802.11 defines only PBCC modulation | ||
19008 | +* for 22 and 33 Mbit rates, so there is no ambiguity | ||
19009 | +* iwpriv wlan0 SetRates "1,2,5,11 6o,9o,12o,18o,24o,36o,48o,54o" | ||
19010 | +* 1,2,5.5 and 11 are basic. 11g OFDM rates are enabled but | ||
19011 | +* they are not in basic rate set. 22 Mbit is disabled. | ||
19012 | +* iwpriv wlan0 SetRates "1,2,5,11 6,9,12,18,24,36,48,54" | ||
19013 | +* same. OFDM is default for 11g rates except 22 and 33 Mbit, | ||
19014 | +* thus 'o' is optional | ||
19015 | +* iwpriv wlan0 SetRates "1,2,5,11 6d,9d,12d,18d,24d,36d,48d,54d" | ||
19016 | +* 1,2,5.5 and 11 are basic. 11g CCK-OFDM rates are enabled | ||
19017 | +* (acx111 does not support CCK-OFDM, driver will reject this cmd) | ||
19018 | +* iwpriv wlan0 SetRates "6,9,12 18,24,36,48,54" | ||
19019 | +* 6,9,12 are basic, rest of 11g rates is enabled. Using OFDM | ||
19020 | +*/ | ||
19021 | +#include "setrate.c" | ||
19022 | + | ||
19023 | +/* disallow: 33Mbit (unsupported by hw) */ | ||
19024 | +/* disallow: CCKOFDM (unsupported by hw) */ | ||
19025 | +static int | ||
19026 | +acx111_supported(int mbit, int modulation, void *opaque) | ||
19027 | +{ | ||
19028 | + if (mbit==33) return -ENOTSUPP; | ||
19029 | + if (modulation==DOT11_MOD_CCKOFDM) return -ENOTSUPP; | ||
19030 | + return OK; | ||
19031 | +} | ||
19032 | + | ||
19033 | +static const u16 | ||
19034 | +acx111mask[] = { | ||
19035 | + [DOT11_RATE_1 ] = RATE111_1 , | ||
19036 | + [DOT11_RATE_2 ] = RATE111_2 , | ||
19037 | + [DOT11_RATE_5 ] = RATE111_5 , | ||
19038 | + [DOT11_RATE_11] = RATE111_11, | ||
19039 | + [DOT11_RATE_22] = RATE111_22, | ||
19040 | + /* [DOT11_RATE_33] = */ | ||
19041 | + [DOT11_RATE_6 ] = RATE111_6 , | ||
19042 | + [DOT11_RATE_9 ] = RATE111_9 , | ||
19043 | + [DOT11_RATE_12] = RATE111_12, | ||
19044 | + [DOT11_RATE_18] = RATE111_18, | ||
19045 | + [DOT11_RATE_24] = RATE111_24, | ||
19046 | + [DOT11_RATE_36] = RATE111_36, | ||
19047 | + [DOT11_RATE_48] = RATE111_48, | ||
19048 | + [DOT11_RATE_54] = RATE111_54, | ||
19049 | +}; | ||
19050 | + | ||
19051 | +static u32 | ||
19052 | +acx111_gen_mask(int mbit, int modulation, void *opaque) | ||
19053 | +{ | ||
19054 | + /* lower 16 bits show selected 1, 2, CCK and OFDM rates */ | ||
19055 | + /* upper 16 bits show selected PBCC rates */ | ||
19056 | + u32 m = acx111mask[rate_mbit2enum(mbit)]; | ||
19057 | + if (modulation==DOT11_MOD_PBCC) | ||
19058 | + return m<<16; | ||
19059 | + return m; | ||
19060 | +} | ||
19061 | + | ||
19062 | +static int | ||
19063 | +verify_rate(u32 rate, int chip_type) | ||
19064 | +{ | ||
19065 | + /* never happens. be paranoid */ | ||
19066 | + if (!rate) return -EINVAL; | ||
19067 | + | ||
19068 | + /* disallow: mixing PBCC and CCK at 5 and 11Mbit | ||
19069 | + ** (can be supported, but needs complicated handling in tx code) */ | ||
19070 | + if (( rate & ((RATE111_11+RATE111_5)<<16) ) | ||
19071 | + && ( rate & (RATE111_11+RATE111_5) ) | ||
19072 | + ) { | ||
19073 | + return -ENOTSUPP; | ||
19074 | + } | ||
19075 | + if (CHIPTYPE_ACX100 == chip_type) { | ||
19076 | + if ( rate & ~(RATE111_ACX100_COMPAT+(RATE111_ACX100_COMPAT<<16)) ) | ||
19077 | + return -ENOTSUPP; | ||
19078 | + } | ||
19079 | + return 0; | ||
19080 | +} | ||
19081 | + | ||
19082 | +static int | ||
19083 | +acx_ioctl_set_rates(struct net_device *ndev, | ||
19084 | + struct iw_request_info *info, | ||
19085 | + union iwreq_data *wrqu, | ||
19086 | + char *extra) | ||
19087 | +{ | ||
19088 | + acx_device_t *adev = ndev2adev(ndev); | ||
19089 | + unsigned long flags; | ||
19090 | + int result; | ||
19091 | + u32 brate = 0, orate = 0; /* basic, operational rate set */ | ||
19092 | + | ||
19093 | + FN_ENTER; | ||
19094 | + | ||
19095 | + log(L_IOCTL, "set_rates %s\n", extra); | ||
19096 | + result = fill_ratemasks(extra, &brate, &orate, | ||
19097 | + acx111_supported, acx111_gen_mask, 0); | ||
19098 | + if (result) goto end; | ||
19099 | + SET_BIT(orate, brate); | ||
19100 | + log(L_IOCTL, "brate %08X orate %08X\n", brate, orate); | ||
19101 | + | ||
19102 | + result = verify_rate(brate, adev->chip_type); | ||
19103 | + if (result) goto end; | ||
19104 | + result = verify_rate(orate, adev->chip_type); | ||
19105 | + if (result) goto end; | ||
19106 | + | ||
19107 | + acx_sem_lock(adev); | ||
19108 | + acx_lock(adev, flags); | ||
19109 | + | ||
19110 | + adev->rate_basic = brate; | ||
19111 | + adev->rate_oper = orate; | ||
19112 | + /* TODO: ideally, we shall monitor highest basic rate | ||
19113 | + ** which was successfully sent to every peer | ||
19114 | + ** (say, last we checked, everybody could hear 5.5 Mbits) | ||
19115 | + ** and use that for bcasts when we want to reach all peers. | ||
19116 | + ** For beacons, we probably shall use lowest basic rate | ||
19117 | + ** because we want to reach all *potential* new peers too */ | ||
19118 | + adev->rate_bcast = 1 << lowest_bit(brate); | ||
19119 | + if (IS_ACX100(adev)) | ||
19120 | + adev->rate_bcast100 = acx_rate111to100(adev->rate_bcast); | ||
19121 | + adev->rate_auto = !has_only_one_bit(orate); | ||
19122 | + acx_l_update_client_rates(adev, orate); | ||
19123 | + /* TODO: get rid of ratevector, build it only when needed */ | ||
19124 | + acx_l_update_ratevector(adev); | ||
19125 | + | ||
19126 | + /* Do/don't do tx rate fallback; beacon contents and rate */ | ||
19127 | + SET_BIT(adev->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES); | ||
19128 | + result = -EINPROGRESS; | ||
19129 | + | ||
19130 | + acx_unlock(adev, flags); | ||
19131 | + acx_sem_unlock(adev); | ||
19132 | +end: | ||
19133 | + FN_EXIT1(result); | ||
19134 | + return result; | ||
19135 | +} | ||
19136 | + | ||
19137 | + | ||
19138 | +/*********************************************************************** | ||
19139 | +** acx_ioctl_get_phy_chan_busy_percentage | ||
19140 | +*/ | ||
19141 | +static int | ||
19142 | +acx_ioctl_get_phy_chan_busy_percentage( | ||
19143 | + struct net_device *ndev, | ||
19144 | + struct iw_request_info *info, | ||
19145 | + union iwreq_data *wrqu, | ||
19146 | + char *extra) | ||
19147 | +{ | ||
19148 | + acx_device_t *adev = ndev2adev(ndev); | ||
19149 | + struct { | ||
19150 | + u16 type; | ||
19151 | + u16 len; | ||
19152 | + u32 busytime; | ||
19153 | + u32 totaltime; | ||
19154 | + } ACX_PACKED usage; | ||
19155 | + int result; | ||
19156 | + | ||
19157 | + acx_sem_lock(adev); | ||
19158 | + | ||
19159 | + if (OK != acx_s_interrogate(adev, &usage, ACX1xx_IE_MEDIUM_USAGE)) { | ||
19160 | + result = NOT_OK; | ||
19161 | + goto end_unlock; | ||
19162 | + } | ||
19163 | + | ||
19164 | + usage.busytime = le32_to_cpu(usage.busytime); | ||
19165 | + usage.totaltime = le32_to_cpu(usage.totaltime); | ||
19166 | + | ||
19167 | + /* yes, this is supposed to be "Medium" (singular of media), | ||
19168 | + not "average"! OK, reword the message to make it obvious... */ | ||
19169 | + printk("%s: busy percentage of medium (since last invocation): %d%% " | ||
19170 | + "(%u of %u microseconds)\n", | ||
19171 | + ndev->name, | ||
19172 | + usage.busytime / ((usage.totaltime / 100) + 1), | ||
19173 | + usage.busytime, usage.totaltime); | ||
19174 | + | ||
19175 | + result = OK; | ||
19176 | + | ||
19177 | +end_unlock: | ||
19178 | + acx_sem_unlock(adev); | ||
19179 | + | ||
19180 | + return result; | ||
19181 | +} | ||
19182 | + | ||
19183 | + | ||
19184 | +/*********************************************************************** | ||
19185 | +** acx_ioctl_set_ed_threshold | ||
19186 | +*/ | ||
19187 | +static inline int | ||
19188 | +acx_ioctl_set_ed_threshold( | ||
19189 | + struct net_device *ndev, | ||
19190 | + struct iw_request_info *info, | ||
19191 | + union iwreq_data *wrqu, | ||
19192 | + char *extra) | ||
19193 | +{ | ||
19194 | + acx_device_t *adev = ndev2adev(ndev); | ||
19195 | + | ||
19196 | + acx_sem_lock(adev); | ||
19197 | + | ||
19198 | + printk("old ED threshold value: %d\n", adev->ed_threshold); | ||
19199 | + adev->ed_threshold = (unsigned char)*extra; | ||
19200 | + printk("new ED threshold value: %d\n", (unsigned char)*extra); | ||
19201 | + SET_BIT(adev->set_mask, GETSET_ED_THRESH); | ||
19202 | + | ||
19203 | + acx_sem_unlock(adev); | ||
19204 | + | ||
19205 | + return -EINPROGRESS; | ||
19206 | +} | ||
19207 | + | ||
19208 | + | ||
19209 | +/*********************************************************************** | ||
19210 | +** acx_ioctl_set_cca | ||
19211 | +*/ | ||
19212 | +static inline int | ||
19213 | +acx_ioctl_set_cca( | ||
19214 | + struct net_device *ndev, | ||
19215 | + struct iw_request_info *info, | ||
19216 | + union iwreq_data *wrqu, | ||
19217 | + char *extra) | ||
19218 | +{ | ||
19219 | + acx_device_t *adev = ndev2adev(ndev); | ||
19220 | + int result; | ||
19221 | + | ||
19222 | + acx_sem_lock(adev); | ||
19223 | + | ||
19224 | + printk("old CCA value: 0x%02X\n", adev->cca); | ||
19225 | + adev->cca = (unsigned char)*extra; | ||
19226 | + printk("new CCA value: 0x%02X\n", (unsigned char)*extra); | ||
19227 | + SET_BIT(adev->set_mask, GETSET_CCA); | ||
19228 | + result = -EINPROGRESS; | ||
19229 | + | ||
19230 | + acx_sem_unlock(adev); | ||
19231 | + | ||
19232 | + return result; | ||
19233 | +} | ||
19234 | + | ||
19235 | + | ||
19236 | +/*********************************************************************** | ||
19237 | +*/ | ||
19238 | +static const char * const | ||
19239 | +scan_modes[] = { "active", "passive", "background" }; | ||
19240 | + | ||
19241 | +static void | ||
19242 | +acx_print_scan_params(acx_device_t *adev, const char* head) | ||
19243 | +{ | ||
19244 | + printk("%s: %smode %d (%s), min chan time %dTU, " | ||
19245 | + "max chan time %dTU, max scan rate byte: %d\n", | ||
19246 | + adev->ndev->name, head, | ||
19247 | + adev->scan_mode, scan_modes[adev->scan_mode], | ||
19248 | + adev->scan_probe_delay, adev->scan_duration, adev->scan_rate); | ||
19249 | +} | ||
19250 | + | ||
19251 | +static int | ||
19252 | +acx_ioctl_set_scan_params( | ||
19253 | + struct net_device *ndev, | ||
19254 | + struct iw_request_info *info, | ||
19255 | + union iwreq_data *wrqu, | ||
19256 | + char *extra) | ||
19257 | +{ | ||
19258 | + acx_device_t *adev = ndev2adev(ndev); | ||
19259 | + int result; | ||
19260 | + const int *params = (int *)extra; | ||
19261 | + | ||
19262 | + acx_sem_lock(adev); | ||
19263 | + | ||
19264 | + acx_print_scan_params(adev, "old scan parameters: "); | ||
19265 | + if ((params[0] != -1) && (params[0] >= 0) && (params[0] <= 2)) | ||
19266 | + adev->scan_mode = params[0]; | ||
19267 | + if (params[1] != -1) | ||
19268 | + adev->scan_probe_delay = params[1]; | ||
19269 | + if (params[2] != -1) | ||
19270 | + adev->scan_duration = params[2]; | ||
19271 | + if ((params[3] != -1) && (params[3] <= 255)) | ||
19272 | + adev->scan_rate = params[3]; | ||
19273 | + acx_print_scan_params(adev, "new scan parameters: "); | ||
19274 | + SET_BIT(adev->set_mask, GETSET_RESCAN); | ||
19275 | + result = -EINPROGRESS; | ||
19276 | + | ||
19277 | + acx_sem_unlock(adev); | ||
19278 | + | ||
19279 | + return result; | ||
19280 | +} | ||
19281 | + | ||
19282 | +static int | ||
19283 | +acx_ioctl_get_scan_params( | ||
19284 | + struct net_device *ndev, | ||
19285 | + struct iw_request_info *info, | ||
19286 | + union iwreq_data *wrqu, | ||
19287 | + char *extra) | ||
19288 | +{ | ||
19289 | + acx_device_t *adev = ndev2adev(ndev); | ||
19290 | + int result; | ||
19291 | + int *params = (int *)extra; | ||
19292 | + | ||
19293 | + acx_sem_lock(adev); | ||
19294 | + | ||
19295 | + acx_print_scan_params(adev, "current scan parameters: "); | ||
19296 | + params[0] = adev->scan_mode; | ||
19297 | + params[1] = adev->scan_probe_delay; | ||
19298 | + params[2] = adev->scan_duration; | ||
19299 | + params[3] = adev->scan_rate; | ||
19300 | + result = OK; | ||
19301 | + | ||
19302 | + acx_sem_unlock(adev); | ||
19303 | + | ||
19304 | + return result; | ||
19305 | +} | ||
19306 | + | ||
19307 | + | ||
19308 | +/*********************************************************************** | ||
19309 | +*/ | ||
19310 | +static int | ||
19311 | +acx100_ioctl_set_led_power( | ||
19312 | + struct net_device *ndev, | ||
19313 | + struct iw_request_info *info, | ||
19314 | + union iwreq_data *wrqu, | ||
19315 | + char *extra) | ||
19316 | +{ | ||
19317 | + static const char * const led_modes[] = { "off", "on", "LinkQuality" }; | ||
19318 | + | ||
19319 | + acx_device_t *adev = ndev2adev(ndev); | ||
19320 | + int result; | ||
19321 | + | ||
19322 | + acx_sem_lock(adev); | ||
19323 | + | ||
19324 | + printk("%s: power LED status: old %d (%s), ", | ||
19325 | + ndev->name, | ||
19326 | + adev->led_power, | ||
19327 | + led_modes[adev->led_power]); | ||
19328 | + adev->led_power = extra[0]; | ||
19329 | + if (adev->led_power > 2) adev->led_power = 2; | ||
19330 | + printk("new %d (%s)\n", | ||
19331 | + adev->led_power, | ||
19332 | + led_modes[adev->led_power]); | ||
19333 | + | ||
19334 | + if (adev->led_power == 2) { | ||
19335 | + printk("%s: max link quality setting: old %d, ", | ||
19336 | + ndev->name, adev->brange_max_quality); | ||
19337 | + if (extra[1]) | ||
19338 | + adev->brange_max_quality = extra[1]; | ||
19339 | + printk("new %d\n", adev->brange_max_quality); | ||
19340 | + } | ||
19341 | + | ||
19342 | + SET_BIT(adev->set_mask, GETSET_LED_POWER); | ||
19343 | + | ||
19344 | + result = -EINPROGRESS; | ||
19345 | + | ||
19346 | + acx_sem_unlock(adev); | ||
19347 | + | ||
19348 | + return result; | ||
19349 | +} | ||
19350 | + | ||
19351 | + | ||
19352 | +/*********************************************************************** | ||
19353 | +*/ | ||
19354 | +static inline int | ||
19355 | +acx100_ioctl_get_led_power( | ||
19356 | + struct net_device *ndev, | ||
19357 | + struct iw_request_info *info, | ||
19358 | + union iwreq_data *wrqu, | ||
19359 | + char *extra) | ||
19360 | +{ | ||
19361 | + acx_device_t *adev = ndev2adev(ndev); | ||
19362 | + | ||
19363 | + acx_sem_lock(adev); | ||
19364 | + | ||
19365 | + extra[0] = adev->led_power; | ||
19366 | + if (adev->led_power == 2) | ||
19367 | + extra[1] = adev->brange_max_quality; | ||
19368 | + else | ||
19369 | + extra[1] = -1; | ||
19370 | + | ||
19371 | + acx_sem_unlock(adev); | ||
19372 | + | ||
19373 | + return OK; | ||
19374 | +} | ||
19375 | + | ||
19376 | + | ||
19377 | +/*********************************************************************** | ||
19378 | +*/ | ||
19379 | +static int | ||
19380 | +acx111_ioctl_info( | ||
19381 | + struct net_device *ndev, | ||
19382 | + struct iw_request_info *info, | ||
19383 | + union iwreq_data *wrqu, | ||
19384 | + char *extra) | ||
19385 | +{ | ||
19386 | + struct iw_param *vwrq = &wrqu->param; | ||
19387 | + if (!IS_PCI(ndev2adev(ndev))) | ||
19388 | + return OK; | ||
19389 | + return acx111pci_ioctl_info(ndev, info, vwrq, extra); | ||
19390 | +} | ||
19391 | + | ||
19392 | + | ||
19393 | +/*********************************************************************** | ||
19394 | +*/ | ||
19395 | +static int | ||
19396 | +acx100_ioctl_set_phy_amp_bias( | ||
19397 | + struct net_device *ndev, | ||
19398 | + struct iw_request_info *info, | ||
19399 | + union iwreq_data *wrqu, | ||
19400 | + char *extra) | ||
19401 | +{ | ||
19402 | + struct iw_param *vwrq = &wrqu->param; | ||
19403 | + if (IS_USB(ndev2adev(ndev))) { | ||
19404 | + printk("acx: set_phy_amp_bias() is not supported on USB\n"); | ||
19405 | + return OK; | ||
19406 | + } | ||
19407 | +#ifdef ACX_MEM | ||
19408 | + return acx100mem_ioctl_set_phy_amp_bias(ndev, info, vwrq, extra); | ||
19409 | +#else | ||
19410 | + return acx100pci_ioctl_set_phy_amp_bias(ndev, info, vwrq, extra); | ||
19411 | +#endif | ||
19412 | +} | ||
19413 | + | ||
19414 | + | ||
19415 | +/*********************************************************************** | ||
19416 | +*/ | ||
19417 | +static const iw_handler acx_ioctl_handler[] = | ||
19418 | +{ | ||
19419 | + acx_ioctl_commit, /* SIOCSIWCOMMIT */ | ||
19420 | + acx_ioctl_get_name, /* SIOCGIWNAME */ | ||
19421 | + NULL, /* SIOCSIWNWID */ | ||
19422 | + NULL, /* SIOCGIWNWID */ | ||
19423 | + acx_ioctl_set_freq, /* SIOCSIWFREQ */ | ||
19424 | + acx_ioctl_get_freq, /* SIOCGIWFREQ */ | ||
19425 | + acx_ioctl_set_mode, /* SIOCSIWMODE */ | ||
19426 | + acx_ioctl_get_mode, /* SIOCGIWMODE */ | ||
19427 | + acx_ioctl_set_sens, /* SIOCSIWSENS */ | ||
19428 | + acx_ioctl_get_sens, /* SIOCGIWSENS */ | ||
19429 | + NULL, /* SIOCSIWRANGE */ | ||
19430 | + acx_ioctl_get_range, /* SIOCGIWRANGE */ | ||
19431 | + NULL, /* SIOCSIWPRIV */ | ||
19432 | + NULL, /* SIOCGIWPRIV */ | ||
19433 | + NULL, /* SIOCSIWSTATS */ | ||
19434 | + NULL, /* SIOCGIWSTATS */ | ||
19435 | +#if IW_HANDLER_VERSION > 4 | ||
19436 | + iw_handler_set_spy, /* SIOCSIWSPY */ | ||
19437 | + iw_handler_get_spy, /* SIOCGIWSPY */ | ||
19438 | + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ | ||
19439 | + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ | ||
19440 | +#else /* IW_HANDLER_VERSION > 4 */ | ||
19441 | +#ifdef WIRELESS_SPY | ||
19442 | + NULL /* acx_ioctl_set_spy FIXME */, /* SIOCSIWSPY */ | ||
19443 | + NULL /* acx_ioctl_get_spy */, /* SIOCGIWSPY */ | ||
19444 | +#else /* WSPY */ | ||
19445 | + NULL, /* SIOCSIWSPY */ | ||
19446 | + NULL, /* SIOCGIWSPY */ | ||
19447 | +#endif /* WSPY */ | ||
19448 | + NULL, /* [nothing] */ | ||
19449 | + NULL, /* [nothing] */ | ||
19450 | +#endif /* IW_HANDLER_VERSION > 4 */ | ||
19451 | + acx_ioctl_set_ap, /* SIOCSIWAP */ | ||
19452 | + acx_ioctl_get_ap, /* SIOCGIWAP */ | ||
19453 | + NULL, /* [nothing] */ | ||
19454 | + acx_ioctl_get_aplist, /* SIOCGIWAPLIST */ | ||
19455 | + acx_ioctl_set_scan, /* SIOCSIWSCAN */ | ||
19456 | + acx_ioctl_get_scan, /* SIOCGIWSCAN */ | ||
19457 | + acx_ioctl_set_essid, /* SIOCSIWESSID */ | ||
19458 | + acx_ioctl_get_essid, /* SIOCGIWESSID */ | ||
19459 | + acx_ioctl_set_nick, /* SIOCSIWNICKN */ | ||
19460 | + acx_ioctl_get_nick, /* SIOCGIWNICKN */ | ||
19461 | + NULL, /* [nothing] */ | ||
19462 | + NULL, /* [nothing] */ | ||
19463 | + acx_ioctl_set_rate, /* SIOCSIWRATE */ | ||
19464 | + acx_ioctl_get_rate, /* SIOCGIWRATE */ | ||
19465 | + acx_ioctl_set_rts, /* SIOCSIWRTS */ | ||
19466 | + acx_ioctl_get_rts, /* SIOCGIWRTS */ | ||
19467 | +#if ACX_FRAGMENTATION | ||
19468 | + acx_ioctl_set_frag, /* SIOCSIWFRAG */ | ||
19469 | + acx_ioctl_get_frag, /* SIOCGIWFRAG */ | ||
19470 | +#else | ||
19471 | + NULL, /* SIOCSIWFRAG */ | ||
19472 | + NULL, /* SIOCGIWFRAG */ | ||
19473 | +#endif | ||
19474 | + acx_ioctl_set_txpow, /* SIOCSIWTXPOW */ | ||
19475 | + acx_ioctl_get_txpow, /* SIOCGIWTXPOW */ | ||
19476 | + acx_ioctl_set_retry, /* SIOCSIWRETRY */ | ||
19477 | + acx_ioctl_get_retry, /* SIOCGIWRETRY */ | ||
19478 | + acx_ioctl_set_encode, /* SIOCSIWENCODE */ | ||
19479 | + acx_ioctl_get_encode, /* SIOCGIWENCODE */ | ||
19480 | + acx_ioctl_set_power, /* SIOCSIWPOWER */ | ||
19481 | + acx_ioctl_get_power, /* SIOCGIWPOWER */ | ||
19482 | +}; | ||
19483 | + | ||
19484 | + | ||
19485 | +/*********************************************************************** | ||
19486 | +*/ | ||
19487 | + | ||
19488 | +/* if you plan to reorder something, make sure to reorder all other places | ||
19489 | + * accordingly! */ | ||
19490 | +/* SET/GET convention: SETs must have even position, GETs odd */ | ||
19491 | +#define ACX100_IOCTL SIOCIWFIRSTPRIV | ||
19492 | +enum { | ||
19493 | + ACX100_IOCTL_DEBUG = ACX100_IOCTL, | ||
19494 | + ACX100_IOCTL_GET__________UNUSED1, | ||
19495 | + ACX100_IOCTL_SET_PLED, | ||
19496 | + ACX100_IOCTL_GET_PLED, | ||
19497 | + ACX100_IOCTL_SET_RATES, | ||
19498 | + ACX100_IOCTL_LIST_DOM, | ||
19499 | + ACX100_IOCTL_SET_DOM, | ||
19500 | + ACX100_IOCTL_GET_DOM, | ||
19501 | + ACX100_IOCTL_SET_SCAN_PARAMS, | ||
19502 | + ACX100_IOCTL_GET_SCAN_PARAMS, | ||
19503 | + ACX100_IOCTL_SET_PREAMB, | ||
19504 | + ACX100_IOCTL_GET_PREAMB, | ||
19505 | + ACX100_IOCTL_SET_ANT, | ||
19506 | + ACX100_IOCTL_GET_ANT, | ||
19507 | + ACX100_IOCTL_RX_ANT, | ||
19508 | + ACX100_IOCTL_TX_ANT, | ||
19509 | + ACX100_IOCTL_SET_PHY_AMP_BIAS, | ||
19510 | + ACX100_IOCTL_GET_PHY_CHAN_BUSY, | ||
19511 | + ACX100_IOCTL_SET_ED, | ||
19512 | + ACX100_IOCTL_GET__________UNUSED3, | ||
19513 | + ACX100_IOCTL_SET_CCA, | ||
19514 | + ACX100_IOCTL_GET__________UNUSED4, | ||
19515 | + ACX100_IOCTL_MONITOR, | ||
19516 | + ACX100_IOCTL_TEST, | ||
19517 | + ACX100_IOCTL_DBG_SET_MASKS, | ||
19518 | + ACX111_IOCTL_INFO, | ||
19519 | + ACX100_IOCTL_DBG_SET_IO, | ||
19520 | + ACX100_IOCTL_DBG_GET_IO | ||
19521 | +}; | ||
19522 | + | ||
19523 | + | ||
19524 | +static const iw_handler acx_ioctl_private_handler[] = | ||
19525 | +{ | ||
19526 | +#if ACX_DEBUG | ||
19527 | +[ACX100_IOCTL_DEBUG - ACX100_IOCTL] = acx_ioctl_set_debug, | ||
19528 | +#endif | ||
19529 | +[ACX100_IOCTL_SET_PLED - ACX100_IOCTL] = acx100_ioctl_set_led_power, | ||
19530 | +[ACX100_IOCTL_GET_PLED - ACX100_IOCTL] = acx100_ioctl_get_led_power, | ||
19531 | +[ACX100_IOCTL_SET_RATES - ACX100_IOCTL] = acx_ioctl_set_rates, | ||
19532 | +[ACX100_IOCTL_LIST_DOM - ACX100_IOCTL] = acx_ioctl_list_reg_domain, | ||
19533 | +[ACX100_IOCTL_SET_DOM - ACX100_IOCTL] = acx_ioctl_set_reg_domain, | ||
19534 | +[ACX100_IOCTL_GET_DOM - ACX100_IOCTL] = acx_ioctl_get_reg_domain, | ||
19535 | +[ACX100_IOCTL_SET_SCAN_PARAMS - ACX100_IOCTL] = acx_ioctl_set_scan_params, | ||
19536 | +[ACX100_IOCTL_GET_SCAN_PARAMS - ACX100_IOCTL] = acx_ioctl_get_scan_params, | ||
19537 | +[ACX100_IOCTL_SET_PREAMB - ACX100_IOCTL] = acx_ioctl_set_short_preamble, | ||
19538 | +[ACX100_IOCTL_GET_PREAMB - ACX100_IOCTL] = acx_ioctl_get_short_preamble, | ||
19539 | +[ACX100_IOCTL_SET_ANT - ACX100_IOCTL] = acx_ioctl_set_antenna, | ||
19540 | +[ACX100_IOCTL_GET_ANT - ACX100_IOCTL] = acx_ioctl_get_antenna, | ||
19541 | +[ACX100_IOCTL_RX_ANT - ACX100_IOCTL] = acx_ioctl_set_rx_antenna, | ||
19542 | +[ACX100_IOCTL_TX_ANT - ACX100_IOCTL] = acx_ioctl_set_tx_antenna, | ||
19543 | +[ACX100_IOCTL_SET_PHY_AMP_BIAS - ACX100_IOCTL] = acx100_ioctl_set_phy_amp_bias, | ||
19544 | +[ACX100_IOCTL_GET_PHY_CHAN_BUSY - ACX100_IOCTL] = acx_ioctl_get_phy_chan_busy_percentage, | ||
19545 | +[ACX100_IOCTL_SET_ED - ACX100_IOCTL] = acx_ioctl_set_ed_threshold, | ||
19546 | +[ACX100_IOCTL_SET_CCA - ACX100_IOCTL] = acx_ioctl_set_cca, | ||
19547 | +[ACX100_IOCTL_MONITOR - ACX100_IOCTL] = acx_ioctl_wlansniff, | ||
19548 | +[ACX100_IOCTL_TEST - ACX100_IOCTL] = acx_ioctl_unknown11, | ||
19549 | +[ACX100_IOCTL_DBG_SET_MASKS - ACX100_IOCTL] = acx_ioctl_dbg_set_masks, | ||
19550 | +[ACX111_IOCTL_INFO - ACX100_IOCTL] = acx111_ioctl_info, | ||
19551 | +}; | ||
19552 | + | ||
19553 | + | ||
19554 | +static const struct iw_priv_args acx_ioctl_private_args[] = { | ||
19555 | +#if ACX_DEBUG | ||
19556 | +{ cmd : ACX100_IOCTL_DEBUG, | ||
19557 | + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
19558 | + get_args : 0, | ||
19559 | + name : "SetDebug" }, | ||
19560 | +#endif | ||
19561 | +{ cmd : ACX100_IOCTL_SET_PLED, | ||
19562 | + set_args : IW_PRIV_TYPE_BYTE | 2, | ||
19563 | + get_args : 0, | ||
19564 | + name : "SetLEDPower" }, | ||
19565 | +{ cmd : ACX100_IOCTL_GET_PLED, | ||
19566 | + set_args : 0, | ||
19567 | + get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2, | ||
19568 | + name : "GetLEDPower" }, | ||
19569 | +{ cmd : ACX100_IOCTL_SET_RATES, | ||
19570 | + set_args : IW_PRIV_TYPE_CHAR | 256, | ||
19571 | + get_args : 0, | ||
19572 | + name : "SetRates" }, | ||
19573 | +{ cmd : ACX100_IOCTL_LIST_DOM, | ||
19574 | + set_args : 0, | ||
19575 | + get_args : 0, | ||
19576 | + name : "ListRegDomain" }, | ||
19577 | +{ cmd : ACX100_IOCTL_SET_DOM, | ||
19578 | + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | ||
19579 | + get_args : 0, | ||
19580 | + name : "SetRegDomain" }, | ||
19581 | +{ cmd : ACX100_IOCTL_GET_DOM, | ||
19582 | + set_args : 0, | ||
19583 | + get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | ||
19584 | + name : "GetRegDomain" }, | ||
19585 | +{ cmd : ACX100_IOCTL_SET_SCAN_PARAMS, | ||
19586 | + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, | ||
19587 | + get_args : 0, | ||
19588 | + name : "SetScanParams" }, | ||
19589 | +{ cmd : ACX100_IOCTL_GET_SCAN_PARAMS, | ||
19590 | + set_args : 0, | ||
19591 | + get_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, | ||
19592 | + name : "GetScanParams" }, | ||
19593 | +{ cmd : ACX100_IOCTL_SET_PREAMB, | ||
19594 | + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | ||
19595 | + get_args : 0, | ||
19596 | + name : "SetSPreamble" }, | ||
19597 | +{ cmd : ACX100_IOCTL_GET_PREAMB, | ||
19598 | + set_args : 0, | ||
19599 | + get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | ||
19600 | + name : "GetSPreamble" }, | ||
19601 | +{ cmd : ACX100_IOCTL_SET_ANT, | ||
19602 | + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | ||
19603 | + get_args : 0, | ||
19604 | + name : "SetAntenna" }, | ||
19605 | +{ cmd : ACX100_IOCTL_GET_ANT, | ||
19606 | + set_args : 0, | ||
19607 | + get_args : 0, | ||
19608 | + name : "GetAntenna" }, | ||
19609 | +{ cmd : ACX100_IOCTL_RX_ANT, | ||
19610 | + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | ||
19611 | + get_args : 0, | ||
19612 | + name : "SetRxAnt" }, | ||
19613 | +{ cmd : ACX100_IOCTL_TX_ANT, | ||
19614 | + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | ||
19615 | + get_args : 0, | ||
19616 | + name : "SetTxAnt" }, | ||
19617 | +{ cmd : ACX100_IOCTL_SET_PHY_AMP_BIAS, | ||
19618 | + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | ||
19619 | + get_args : 0, | ||
19620 | + name : "SetPhyAmpBias"}, | ||
19621 | +{ cmd : ACX100_IOCTL_GET_PHY_CHAN_BUSY, | ||
19622 | + set_args : 0, | ||
19623 | + get_args : 0, | ||
19624 | + name : "GetPhyChanBusy" }, | ||
19625 | +{ cmd : ACX100_IOCTL_SET_ED, | ||
19626 | + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
19627 | + get_args : 0, | ||
19628 | + name : "SetED" }, | ||
19629 | +{ cmd : ACX100_IOCTL_SET_CCA, | ||
19630 | + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | ||
19631 | + get_args : 0, | ||
19632 | + name : "SetCCA" }, | ||
19633 | +{ cmd : ACX100_IOCTL_MONITOR, | ||
19634 | + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, | ||
19635 | + get_args : 0, | ||
19636 | + name : "monitor" }, | ||
19637 | +{ cmd : ACX100_IOCTL_TEST, | ||
19638 | + set_args : 0, | ||
19639 | + get_args : 0, | ||
19640 | + name : "Test" }, | ||
19641 | +{ cmd : ACX100_IOCTL_DBG_SET_MASKS, | ||
19642 | + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, | ||
19643 | + get_args : 0, | ||
19644 | + name : "DbgSetMasks" }, | ||
19645 | +{ cmd : ACX111_IOCTL_INFO, | ||
19646 | + set_args : 0, | ||
19647 | + get_args : 0, | ||
19648 | + name : "GetAcx111Info" }, | ||
19649 | +{ cmd : ACX100_IOCTL_DBG_SET_IO, | ||
19650 | + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, | ||
19651 | + get_args : 0, | ||
19652 | + name : "DbgSetIO" }, | ||
19653 | +{ cmd : ACX100_IOCTL_DBG_GET_IO, | ||
19654 | + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, | ||
19655 | + get_args : 0, | ||
19656 | + name : "DbgGetIO" }, | ||
19657 | +}; | ||
19658 | + | ||
19659 | + | ||
19660 | +const struct iw_handler_def acx_ioctl_handler_def = | ||
19661 | +{ | ||
19662 | + .num_standard = VEC_SIZE(acx_ioctl_handler), | ||
19663 | + .num_private = VEC_SIZE(acx_ioctl_private_handler), | ||
19664 | + .num_private_args = VEC_SIZE(acx_ioctl_private_args), | ||
19665 | + .standard = (iw_handler *) acx_ioctl_handler, | ||
19666 | + .private = (iw_handler *) acx_ioctl_private_handler, | ||
19667 | + .private_args = (struct iw_priv_args *) acx_ioctl_private_args, | ||
19668 | +#if IW_HANDLER_VERSION > 5 | ||
19669 | + .get_wireless_stats = acx_e_get_wireless_stats | ||
19670 | +#endif /* IW > 5 */ | ||
19671 | +}; | ||
19672 | Index: linux-2.6.22/drivers/net/wireless/acx/Kconfig | ||
19673 | =================================================================== | ||
19674 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
19675 | +++ linux-2.6.22/drivers/net/wireless/acx/Kconfig 2007-08-23 18:34:19.000000000 +0200 | ||
19676 | @@ -0,0 +1,113 @@ | ||
19677 | +config ACX | ||
19678 | + tristate "TI acx100/acx111 802.11b/g wireless chipsets" | ||
19679 | + depends on NET_RADIO && EXPERIMENTAL | ||
19680 | + select FW_LOADER | ||
19681 | + ---help--- | ||
19682 | + A driver for 802.11b/g wireless cards based on | ||
19683 | + Texas Instruments acx100 and acx111 chipsets. | ||
19684 | + | ||
19685 | + This driver supports Host AP mode that allows | ||
19686 | + your computer to act as an IEEE 802.11 access point. | ||
19687 | + This driver is new and experimental. | ||
19688 | + | ||
19689 | + Texas Instruments did not take part in development of this driver | ||
19690 | + in any way, shape or form. | ||
19691 | + | ||
19692 | + The driver can be compiled as a module and will be named "acx". | ||
19693 | + | ||
19694 | +config ACX_PCI | ||
19695 | + bool "TI acx100/acx111 802.11b/g PCI" | ||
19696 | + depends on ACX && PCI | ||
19697 | + ---help--- | ||
19698 | + Include PCI and CardBus support in acx. | ||
19699 | + | ||
19700 | + acx chipsets need their firmware loaded at startup. | ||
19701 | + You will need to provide a firmware image via hotplug. | ||
19702 | + | ||
19703 | + Firmware may be in a form of single image 40-100kb in size | ||
19704 | + (a 'combined' firmware) or two images - main image | ||
19705 | + (again 40-100kb) and radio image (~10kb or less). | ||
19706 | + | ||
19707 | + Firmware images are requested from hotplug using following names: | ||
19708 | + | ||
19709 | + tiacx100 - main firmware image for acx100 chipset | ||
19710 | + tiacx100rNN - radio acx100 firmware for radio type NN | ||
19711 | + tiacx100cNN - combined acx100 firmware for radio type NN | ||
19712 | + tiacx111 - main acx111 firmware | ||
19713 | + tiacx111rNN - radio acx111 firmware for radio type NN | ||
19714 | + tiacx111cNN - combined acx111 firmware for radio type NN | ||
19715 | + | ||
19716 | + Driver will attempt to load combined image first. | ||
19717 | + If no such image is found, it will try to load main image | ||
19718 | + and radio image instead. | ||
19719 | + | ||
19720 | + Firmware files are not covered by GPL and are not distributed | ||
19721 | + with this driver for legal reasons. | ||
19722 | + | ||
19723 | +config ACX_USB | ||
19724 | + bool "TI acx100/acx111 802.11b/g USB" | ||
19725 | + depends on ACX && (USB=y || USB=ACX) | ||
19726 | + ---help--- | ||
19727 | + Include USB support in acx. | ||
19728 | + | ||
19729 | + There is only one currently known device in this category, | ||
19730 | + D-Link DWL-120+, but newer devices seem to be on the horizon. | ||
19731 | + | ||
19732 | + acx chipsets need their firmware loaded at startup. | ||
19733 | + You will need to provide a firmware image via hotplug. | ||
19734 | + | ||
19735 | + Firmware for USB device is requested from hotplug | ||
19736 | + by the 'tiacx100usb' name. | ||
19737 | + | ||
19738 | + Firmware files are not covered by GPL and are not distributed | ||
19739 | + with this driver for legal reasons. | ||
19740 | + | ||
19741 | +config ACX_MEM | ||
19742 | + bool "TI acx100/acx111 802.11b/g memory mapped slave 16 interface" | ||
19743 | + depends on ACX | ||
19744 | + ---help--- | ||
19745 | + acx chipsets need their firmware loaded at startup. | ||
19746 | + You will need to provide a firmware image via hotplug. | ||
19747 | + | ||
19748 | + Firmware for USB device is requested from hotplug | ||
19749 | + by the 'tiacx100usb' name. | ||
19750 | + | ||
19751 | + Firmware files are not covered by GPL and are not distributed | ||
19752 | + with this driver for legal reasons. | ||
19753 | + | ||
19754 | +config ACX_CS | ||
19755 | + bool "TI acx100/acx111 802.11b/g cardbus interface" | ||
19756 | + depends on ACX | ||
19757 | + ---help--- | ||
19758 | + acx chipsets need their firmware loaded at startup. | ||
19759 | + You will need to provide a firmware image via hotplug. | ||
19760 | + | ||
19761 | + This driver is based on memory mapped driver. | ||
19762 | + | ||
19763 | + Firmware files are not covered by GPL and are not distributed | ||
19764 | + with this driver for legal reasons. | ||
19765 | + | ||
19766 | +config ACX_HX4700 | ||
19767 | + tristate "ACX support for the iPAQ hx4700 using ACX_MEM" | ||
19768 | + depends on HX4700_CORE && ACX_MEM | ||
19769 | + ---help--- | ||
19770 | + Include memory interface support in acx for the iPAQ hx4700. | ||
19771 | + | ||
19772 | +config ACX_HTCUNIVERSAL | ||
19773 | + tristate "ACX support for the HTC Universal using ACX_MEM" | ||
19774 | + depends on HTCUNIVERSAL_CORE && HTC_ASIC3 && ACX_MEM | ||
19775 | + ---help--- | ||
19776 | + Include memory interface support in acx for the HTC Universal. | ||
19777 | + | ||
19778 | +config ACX_HTCSABLE | ||
19779 | + tristate "ACX support for the HTC Sable (IPAQ hw6915) using ACX_MEM" | ||
19780 | + depends on MACH_HW6900 && HTC_ASIC3 && ACX_MEM | ||
19781 | + ---help--- | ||
19782 | + Include memory interface support in acx for the HTC Sable (IPAQ hw6915). | ||
19783 | + | ||
19784 | +config ACX_RX3000 | ||
19785 | + tristate "ACX support for the iPAQ RX3000 using ACX_MEM" | ||
19786 | + depends on MACH_RX3715 && ACX_MEM && LEDS_ASIC3 | ||
19787 | + ---help--- | ||
19788 | + Include memory interface support in acx for the IPAQ RX3000. | ||
19789 | + | ||
19790 | Index: linux-2.6.22/drivers/net/wireless/acx/Makefile | ||
19791 | =================================================================== | ||
19792 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
19793 | +++ linux-2.6.22/drivers/net/wireless/acx/Makefile 2007-08-23 18:34:19.000000000 +0200 | ||
19794 | @@ -0,0 +1,21 @@ | ||
19795 | +#obj-m += acx.o | ||
19796 | + | ||
19797 | +#acx-obj-y += pci.o | ||
19798 | +#acx-obj-y += usb.o | ||
19799 | + | ||
19800 | +#acx-objs := wlan.o conv.o ioctl.o common.o $(acx-obj-y) | ||
19801 | + | ||
19802 | +# Use this if you have proper Kconfig integration: | ||
19803 | + | ||
19804 | +obj-$(CONFIG_ACX) += acx.o | ||
19805 | +obj-$(CONFIG_ACX_HX4700) += hx4700_acx.o | ||
19806 | +obj-$(CONFIG_ACX_HTCUNIVERSAL) += htcuniversal_acx.o | ||
19807 | +obj-$(CONFIG_ACX_HTCSABLE) += htcsable_acx.o | ||
19808 | +obj-$(CONFIG_ACX_RX3000) += rx3000_acx.o | ||
19809 | +# | ||
19810 | +acx-obj-$(CONFIG_ACX_PCI) += pci.o | ||
19811 | +acx-obj-$(CONFIG_ACX_USB) += usb.o | ||
19812 | +acx-obj-$(CONFIG_ACX_MEM) += mem.o | ||
19813 | +acx-obj-$(CONFIG_ACX_CS) += cs.o | ||
19814 | +# | ||
19815 | +acx-objs := wlan.o conv.o ioctl.o common.o $(acx-obj-y) | ||
19816 | Index: linux-2.6.22/drivers/net/wireless/acx/mem.c | ||
19817 | =================================================================== | ||
19818 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
19819 | +++ linux-2.6.22/drivers/net/wireless/acx/mem.c 2007-08-23 18:34:19.000000000 +0200 | ||
19820 | @@ -0,0 +1,5363 @@ | ||
19821 | +/*********************************************************************** | ||
19822 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
19823 | +** | ||
19824 | +** The contents of this file are subject to the Mozilla Public | ||
19825 | +** License Version 1.1 (the "License"); you may not use this file | ||
19826 | +** except in compliance with the License. You may obtain a copy of | ||
19827 | +** the License at http://www.mozilla.org/MPL/ | ||
19828 | +** | ||
19829 | +** Software distributed under the License is distributed on an "AS | ||
19830 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
19831 | +** implied. See the License for the specific language governing | ||
19832 | +** rights and limitations under the License. | ||
19833 | +** | ||
19834 | +** Alternatively, the contents of this file may be used under the | ||
19835 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
19836 | +** case the provisions of the GPL are applicable instead of the | ||
19837 | +** above. If you wish to allow the use of your version of this file | ||
19838 | +** only under the terms of the GPL and not to allow others to use | ||
19839 | +** your version of this file under the MPL, indicate your decision | ||
19840 | +** by deleting the provisions above and replace them with the notice | ||
19841 | +** and other provisions required by the GPL. If you do not delete | ||
19842 | +** the provisions above, a recipient may use your version of this | ||
19843 | +** file under either the MPL or the GPL. | ||
19844 | +** --------------------------------------------------------------------- | ||
19845 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
19846 | +** made directly to: | ||
19847 | +** | ||
19848 | +** acx100-users@lists.sf.net | ||
19849 | +** http://acx100.sf.net | ||
19850 | +** --------------------------------------------------------------------- | ||
19851 | +** | ||
19852 | +** Slave memory interface support: | ||
19853 | +** | ||
19854 | +** Todd Blumer - SDG Systems | ||
19855 | +** Bill Reese - HP | ||
19856 | +** Eric McCorkle - Shadowsun | ||
19857 | +*/ | ||
19858 | +#define ACX_MEM 1 | ||
19859 | + | ||
19860 | +/* | ||
19861 | + * non-zero makes it dump the ACX memory to the console then | ||
19862 | + * panic when you cat /proc/driver/acx_wlan0_diag | ||
19863 | + */ | ||
19864 | +#define DUMP_MEM_DEFINED 1 | ||
19865 | + | ||
19866 | +#define DUMP_MEM_DURING_DIAG 0 | ||
19867 | +#define DUMP_IF_SLOW 0 | ||
19868 | + | ||
19869 | +#define PATCH_AROUND_BAD_SPOTS 1 | ||
19870 | +#define HX4700_FIRMWARE_CHECKSUM 0x0036862e | ||
19871 | +#define HX4700_ALTERNATE_FIRMWARE_CHECKSUM 0x00368a75 | ||
19872 | + | ||
19873 | +#include <linux/version.h> | ||
19874 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) | ||
19875 | +#include <linux/config.h> | ||
19876 | +#endif | ||
19877 | + | ||
19878 | +/* Linux 2.6.18+ uses <linux/utsrelease.h> */ | ||
19879 | +#ifndef UTS_RELEASE | ||
19880 | +#include <linux/utsrelease.h> | ||
19881 | +#endif | ||
19882 | + | ||
19883 | +#include <linux/compiler.h> /* required for Lx 2.6.8 ?? */ | ||
19884 | +#include <linux/kernel.h> | ||
19885 | +#include <linux/module.h> | ||
19886 | +#include <linux/moduleparam.h> | ||
19887 | +#include <linux/sched.h> | ||
19888 | +#include <linux/types.h> | ||
19889 | +#include <linux/skbuff.h> | ||
19890 | +#include <linux/slab.h> | ||
19891 | +#include <linux/if_arp.h> | ||
19892 | +#include <linux/irq.h> | ||
19893 | +#include <linux/rtnetlink.h> | ||
19894 | +#include <linux/wireless.h> | ||
19895 | +#include <net/iw_handler.h> | ||
19896 | +#include <linux/netdevice.h> | ||
19897 | +#include <linux/ioport.h> | ||
19898 | +#include <linux/pci.h> | ||
19899 | +#include <linux/platform_device.h> | ||
19900 | +#include <linux/pm.h> | ||
19901 | +#include <linux/vmalloc.h> | ||
19902 | +#include <linux/delay.h> | ||
19903 | +#include <linux/workqueue.h> | ||
19904 | +#include <linux/inetdevice.h> | ||
19905 | + | ||
19906 | +#include "acx.h" | ||
19907 | +#include "acx_hw.h" | ||
19908 | + | ||
19909 | +/*********************************************************************** | ||
19910 | +*/ | ||
19911 | + | ||
19912 | +#define CARD_EEPROM_ID_SIZE 6 | ||
19913 | + | ||
19914 | +#include <asm/io.h> | ||
19915 | + | ||
19916 | +#define REG_ACX_VENDOR_ID 0x900 | ||
19917 | +/* | ||
19918 | + * This is the vendor id on the HX4700, anyway | ||
19919 | + */ | ||
19920 | +#define ACX_VENDOR_ID 0x8400104c | ||
19921 | + | ||
19922 | +typedef enum { | ||
19923 | + ACX_SOFT_RESET = 0, | ||
19924 | + | ||
19925 | + ACX_SLV_REG_ADDR, | ||
19926 | + ACX_SLV_REG_DATA, | ||
19927 | + ACX_SLV_REG_ADATA, | ||
19928 | + | ||
19929 | + ACX_SLV_MEM_CP, | ||
19930 | + ACX_SLV_MEM_ADDR, | ||
19931 | + ACX_SLV_MEM_DATA, | ||
19932 | + ACX_SLV_MEM_CTL, | ||
19933 | +} acxreg_t; | ||
19934 | + | ||
19935 | +/*********************************************************************** | ||
19936 | +*/ | ||
19937 | +static void acxmem_i_tx_timeout(struct net_device *ndev); | ||
19938 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
19939 | +static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id); | ||
19940 | +#else | ||
19941 | +static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id, struct pt_regs *regs); | ||
19942 | +#endif | ||
19943 | +static void acxmem_i_set_multicast_list(struct net_device *ndev); | ||
19944 | + | ||
19945 | +static int acxmem_e_open(struct net_device *ndev); | ||
19946 | +static int acxmem_e_close(struct net_device *ndev); | ||
19947 | +static void acxmem_s_up(struct net_device *ndev); | ||
19948 | +static void acxmem_s_down(struct net_device *ndev); | ||
19949 | + | ||
19950 | +static void dump_acxmem (acx_device_t *adev, u32 start, int length); | ||
19951 | +static int acxmem_complete_hw_reset (acx_device_t *adev); | ||
19952 | +static void acxmem_s_delete_dma_regions(acx_device_t *adev); | ||
19953 | + | ||
19954 | +static struct platform_device *resume_pdev; | ||
19955 | + | ||
19956 | +static int | ||
19957 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) | ||
19958 | +acxmem_e_suspend(struct platform_device *pdev, pm_message_t state); | ||
19959 | +#else | ||
19960 | +acxmem_e_suspend(struct device *pdev, u32 state); | ||
19961 | +#endif | ||
19962 | +static void | ||
19963 | +fw_resumer(struct work_struct *notused); | ||
19964 | +//fw_resumer( void *data ); | ||
19965 | + | ||
19966 | +static int acx_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) | ||
19967 | +{ | ||
19968 | + struct net_device *ndev = ptr; | ||
19969 | + acx_device_t *adev = ndev2adev(ndev); | ||
19970 | + | ||
19971 | + /* | ||
19972 | + * Upper level ioctl() handlers send a NETDEV_CHANGEADDR if the MAC address changes. | ||
19973 | + */ | ||
19974 | + | ||
19975 | + if (NETDEV_CHANGEADDR == event) { | ||
19976 | + /* | ||
19977 | + * the upper layers put the new MAC address in ndev->dev_addr; we just copy | ||
19978 | + * it over and update the ACX with it. | ||
19979 | + */ | ||
19980 | + MAC_COPY(adev->dev_addr, adev->ndev->dev_addr); | ||
19981 | + adev->set_mask |= GETSET_STATION_ID; | ||
19982 | + acx_s_update_card_settings (adev); | ||
19983 | + } | ||
19984 | + | ||
19985 | + return 0; | ||
19986 | +} | ||
19987 | + | ||
19988 | +static struct notifier_block acx_netdev_notifier = { | ||
19989 | + .notifier_call = acx_netdev_event, | ||
19990 | +}; | ||
19991 | + | ||
19992 | +/*********************************************************************** | ||
19993 | +** Register access | ||
19994 | +*/ | ||
19995 | + | ||
19996 | +/* Pick one */ | ||
19997 | +/* #define INLINE_IO static */ | ||
19998 | +#define INLINE_IO static inline | ||
19999 | + | ||
20000 | +INLINE_IO u32 | ||
20001 | +read_id_register (acx_device_t *adev) | ||
20002 | +{ | ||
20003 | + writel (0x24, &adev->iobase[ACX_SLV_REG_ADDR]); | ||
20004 | + return readl (&adev->iobase[ACX_SLV_REG_DATA]); | ||
20005 | +} | ||
20006 | + | ||
20007 | +INLINE_IO u32 | ||
20008 | +read_reg32(acx_device_t *adev, unsigned int offset) | ||
20009 | +{ | ||
20010 | + u32 val; | ||
20011 | + u32 addr; | ||
20012 | + | ||
20013 | + if (offset > IO_ACX_ECPU_CTRL) | ||
20014 | + addr = offset; | ||
20015 | + else | ||
20016 | + addr = adev->io[offset]; | ||
20017 | + | ||
20018 | + if (addr < 0x20) { | ||
20019 | + return readl(((u8*)adev->iobase) + addr); | ||
20020 | + } | ||
20021 | + | ||
20022 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
20023 | + val = readl( &adev->iobase[ACX_SLV_REG_DATA] ); | ||
20024 | + | ||
20025 | + return val; | ||
20026 | +} | ||
20027 | + | ||
20028 | +INLINE_IO u16 | ||
20029 | +read_reg16(acx_device_t *adev, unsigned int offset) | ||
20030 | +{ | ||
20031 | + u16 lo; | ||
20032 | + u32 addr; | ||
20033 | + | ||
20034 | + if (offset > IO_ACX_ECPU_CTRL) | ||
20035 | + addr = offset; | ||
20036 | + else | ||
20037 | + addr = adev->io[offset]; | ||
20038 | + | ||
20039 | + if (addr < 0x20) { | ||
20040 | + return readw(((u8 *) adev->iobase) + addr); | ||
20041 | + } | ||
20042 | + | ||
20043 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
20044 | + lo = readw( (u16 *)&adev->iobase[ACX_SLV_REG_DATA] ); | ||
20045 | + | ||
20046 | + return lo; | ||
20047 | +} | ||
20048 | + | ||
20049 | +INLINE_IO u8 | ||
20050 | +read_reg8(acx_device_t *adev, unsigned int offset) | ||
20051 | +{ | ||
20052 | + u8 lo; | ||
20053 | + u32 addr; | ||
20054 | + | ||
20055 | + if (offset > IO_ACX_ECPU_CTRL) | ||
20056 | + addr = offset; | ||
20057 | + else | ||
20058 | + addr = adev->io[offset]; | ||
20059 | + | ||
20060 | + if (addr < 0x20) | ||
20061 | + return readb(((u8 *)adev->iobase) + addr); | ||
20062 | + | ||
20063 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
20064 | + lo = readw( (u8 *)&adev->iobase[ACX_SLV_REG_DATA] ); | ||
20065 | + | ||
20066 | + return (u8)lo; | ||
20067 | +} | ||
20068 | + | ||
20069 | +INLINE_IO void | ||
20070 | +write_reg32(acx_device_t *adev, unsigned int offset, u32 val) | ||
20071 | +{ | ||
20072 | + u32 addr; | ||
20073 | + | ||
20074 | + if (offset > IO_ACX_ECPU_CTRL) | ||
20075 | + addr = offset; | ||
20076 | + else | ||
20077 | + addr = adev->io[offset]; | ||
20078 | + | ||
20079 | + if (addr < 0x20) { | ||
20080 | + writel(val, ((u8*)adev->iobase) + addr); | ||
20081 | + return; | ||
20082 | + } | ||
20083 | + | ||
20084 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
20085 | + writel( val, &adev->iobase[ACX_SLV_REG_DATA] ); | ||
20086 | +} | ||
20087 | + | ||
20088 | +INLINE_IO void | ||
20089 | +write_reg16(acx_device_t *adev, unsigned int offset, u16 val) | ||
20090 | +{ | ||
20091 | + u32 addr; | ||
20092 | + | ||
20093 | + if (offset > IO_ACX_ECPU_CTRL) | ||
20094 | + addr = offset; | ||
20095 | + else | ||
20096 | + addr = adev->io[offset]; | ||
20097 | + | ||
20098 | + if (addr < 0x20) { | ||
20099 | + writew(val, ((u8 *)adev->iobase) + addr); | ||
20100 | + return; | ||
20101 | + } | ||
20102 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
20103 | + writew( val, (u16 *) &adev->iobase[ACX_SLV_REG_DATA] ); | ||
20104 | +} | ||
20105 | + | ||
20106 | +INLINE_IO void | ||
20107 | +write_reg8(acx_device_t *adev, unsigned int offset, u8 val) | ||
20108 | +{ | ||
20109 | + u32 addr; | ||
20110 | + | ||
20111 | + if (offset > IO_ACX_ECPU_CTRL) | ||
20112 | + addr = offset; | ||
20113 | + else | ||
20114 | + addr = adev->io[offset]; | ||
20115 | + | ||
20116 | + if (addr < 0x20) { | ||
20117 | + writeb(val, ((u8 *) adev->iobase) + addr); | ||
20118 | + return; | ||
20119 | + } | ||
20120 | + writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); | ||
20121 | + writeb( val, (u8 *)&adev->iobase[ACX_SLV_REG_DATA] ); | ||
20122 | +} | ||
20123 | + | ||
20124 | +/* Handle PCI posting properly: | ||
20125 | + * Make sure that writes reach the adapter in case they require to be executed | ||
20126 | + * *before* the next write, by reading a random (and safely accessible) register. | ||
20127 | + * This call has to be made if there is no read following (which would flush the data | ||
20128 | + * to the adapter), yet the written data has to reach the adapter immediately. */ | ||
20129 | +INLINE_IO void | ||
20130 | +write_flush(acx_device_t *adev) | ||
20131 | +{ | ||
20132 | + /* readb(adev->iobase + adev->io[IO_ACX_INFO_MAILBOX_OFFS]); */ | ||
20133 | + /* faster version (accesses the first register, IO_ACX_SOFT_RESET, | ||
20134 | + * which should also be safe): */ | ||
20135 | + (void) readl(adev->iobase); | ||
20136 | +} | ||
20137 | + | ||
20138 | +INLINE_IO void | ||
20139 | +set_regbits (acx_device_t *adev, unsigned int offset, u32 bits) { | ||
20140 | + u32 tmp; | ||
20141 | + | ||
20142 | + tmp = read_reg32 (adev, offset); | ||
20143 | + tmp = tmp | bits; | ||
20144 | + write_reg32 (adev, offset, tmp); | ||
20145 | + write_flush (adev); | ||
20146 | +} | ||
20147 | + | ||
20148 | +INLINE_IO void | ||
20149 | +clear_regbits (acx_device_t *adev, unsigned int offset, u32 bits) { | ||
20150 | + u32 tmp; | ||
20151 | + | ||
20152 | + tmp = read_reg32 (adev, offset); | ||
20153 | + tmp = tmp & ~bits; | ||
20154 | + write_reg32 (adev, offset, tmp); | ||
20155 | + write_flush (adev); | ||
20156 | +} | ||
20157 | + | ||
20158 | +/* | ||
20159 | + * Copy from PXA memory to the ACX memory. This assumes both the PXA and ACX | ||
20160 | + * addresses are 32 bit aligned. Count is in bytes. | ||
20161 | + */ | ||
20162 | +INLINE_IO void | ||
20163 | +write_slavemem32 (acx_device_t *adev, u32 slave_address, u32 val) | ||
20164 | +{ | ||
20165 | + write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0); | ||
20166 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address); | ||
20167 | + udelay (10); | ||
20168 | + write_reg32 (adev, IO_ACX_SLV_MEM_DATA, val); | ||
20169 | +} | ||
20170 | + | ||
20171 | +INLINE_IO u32 | ||
20172 | +read_slavemem32 (acx_device_t *adev, u32 slave_address) | ||
20173 | +{ | ||
20174 | + u32 val; | ||
20175 | + | ||
20176 | + write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0); | ||
20177 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address); | ||
20178 | + udelay (10); | ||
20179 | + val = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); | ||
20180 | + | ||
20181 | + return val; | ||
20182 | +} | ||
20183 | + | ||
20184 | +INLINE_IO void | ||
20185 | +write_slavemem8 (acx_device_t *adev, u32 slave_address, u8 val) | ||
20186 | +{ | ||
20187 | + u32 data; | ||
20188 | + u32 base; | ||
20189 | + int offset; | ||
20190 | + | ||
20191 | + /* | ||
20192 | + * Get the word containing the target address and the byte offset in that word. | ||
20193 | + */ | ||
20194 | + base = slave_address & ~3; | ||
20195 | + offset = (slave_address & 3) * 8; | ||
20196 | + | ||
20197 | + data = read_slavemem32 (adev, base); | ||
20198 | + data &= ~(0xff << offset); | ||
20199 | + data |= val << offset; | ||
20200 | + write_slavemem32 (adev, base, data); | ||
20201 | +} | ||
20202 | + | ||
20203 | +INLINE_IO u8 | ||
20204 | +read_slavemem8 (acx_device_t *adev, u32 slave_address) | ||
20205 | +{ | ||
20206 | + u8 val; | ||
20207 | + u32 base; | ||
20208 | + u32 data; | ||
20209 | + int offset; | ||
20210 | + | ||
20211 | + base = slave_address & ~3; | ||
20212 | + offset = (slave_address & 3) * 8; | ||
20213 | + | ||
20214 | + data = read_slavemem32 (adev, base); | ||
20215 | + | ||
20216 | + val = (data >> offset) & 0xff; | ||
20217 | + | ||
20218 | + return val; | ||
20219 | +} | ||
20220 | + | ||
20221 | +/* | ||
20222 | + * doesn't split across word boundaries | ||
20223 | + */ | ||
20224 | +INLINE_IO void | ||
20225 | +write_slavemem16 (acx_device_t *adev, u32 slave_address, u16 val) | ||
20226 | +{ | ||
20227 | + u32 data; | ||
20228 | + u32 base; | ||
20229 | + int offset; | ||
20230 | + | ||
20231 | + /* | ||
20232 | + * Get the word containing the target address and the byte offset in that word. | ||
20233 | + */ | ||
20234 | + base = slave_address & ~3; | ||
20235 | + offset = (slave_address & 3) * 8; | ||
20236 | + | ||
20237 | + data = read_slavemem32 (adev, base); | ||
20238 | + data &= ~(0xffff << offset); | ||
20239 | + data |= val << offset; | ||
20240 | + write_slavemem32 (adev, base, data); | ||
20241 | +} | ||
20242 | + | ||
20243 | +/* | ||
20244 | + * doesn't split across word boundaries | ||
20245 | + */ | ||
20246 | +INLINE_IO u16 | ||
20247 | +read_slavemem16 (acx_device_t *adev, u32 slave_address) | ||
20248 | +{ | ||
20249 | + u16 val; | ||
20250 | + u32 base; | ||
20251 | + u32 data; | ||
20252 | + int offset; | ||
20253 | + | ||
20254 | + base = slave_address & ~3; | ||
20255 | + offset = (slave_address & 3) * 8; | ||
20256 | + | ||
20257 | + data = read_slavemem32 (adev, base); | ||
20258 | + | ||
20259 | + val = (data >> offset) & 0xffff; | ||
20260 | + | ||
20261 | + return val; | ||
20262 | +} | ||
20263 | + | ||
20264 | +/* | ||
20265 | + * Copy from slave memory | ||
20266 | + * | ||
20267 | + * TODO - rewrite using address autoincrement, handle partial words | ||
20268 | + */ | ||
20269 | +void | ||
20270 | +copy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count) { | ||
20271 | + u32 tmp = 0; | ||
20272 | + u8 *ptmp = (u8 *) &tmp; | ||
20273 | + | ||
20274 | + /* | ||
20275 | + * Right now I'm making the assumption that the destination is aligned, but | ||
20276 | + * I'd better check. | ||
20277 | + */ | ||
20278 | + if ((u32) destination & 3) { | ||
20279 | + printk ("acx copy_from_slavemem: warning! destination not word-aligned!\n"); | ||
20280 | + } | ||
20281 | + | ||
20282 | + while (count >= 4) { | ||
20283 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source); | ||
20284 | + udelay (10); | ||
20285 | + *((u32 *) destination) = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); | ||
20286 | + count -= 4; | ||
20287 | + source += 4; | ||
20288 | + destination += 4; | ||
20289 | + } | ||
20290 | + | ||
20291 | + /* | ||
20292 | + * If the word reads above didn't satisfy the count, read one more word | ||
20293 | + * and transfer a byte at a time until the request is satisfied. | ||
20294 | + */ | ||
20295 | + if (count) { | ||
20296 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source); | ||
20297 | + udelay (10); | ||
20298 | + tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); | ||
20299 | + while (count--) { | ||
20300 | + *destination++ = *ptmp++; | ||
20301 | + } | ||
20302 | + } | ||
20303 | +} | ||
20304 | + | ||
20305 | +/* | ||
20306 | + * Copy to slave memory | ||
20307 | + * | ||
20308 | + * TODO - rewrite using autoincrement, handle partial words | ||
20309 | + */ | ||
20310 | +void | ||
20311 | +copy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count) | ||
20312 | +{ | ||
20313 | + u32 tmp = 0; | ||
20314 | + u8* ptmp = (u8 *) &tmp; | ||
20315 | + static u8 src[512]; /* make static to avoid huge stack objects */ | ||
20316 | + | ||
20317 | + /* | ||
20318 | + * For now, make sure the source is word-aligned by copying it to a word-aligned | ||
20319 | + * buffer. Someday rewrite to avoid the extra copy. | ||
20320 | + */ | ||
20321 | + if (count > sizeof (src)) { | ||
20322 | + printk ("acx copy_to_slavemem: Warning! buffer overflow!\n"); | ||
20323 | + count = sizeof (src); | ||
20324 | + } | ||
20325 | + memcpy (src, source, count); | ||
20326 | + source = src; | ||
20327 | + | ||
20328 | + while (count >= 4) { | ||
20329 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); | ||
20330 | + udelay (10); | ||
20331 | + write_reg32 (adev, IO_ACX_SLV_MEM_DATA, *((u32 *) source)); | ||
20332 | + count -= 4; | ||
20333 | + source += 4; | ||
20334 | + destination += 4; | ||
20335 | + } | ||
20336 | + | ||
20337 | + /* | ||
20338 | + * If there are leftovers read the next word from the acx and merge in | ||
20339 | + * what they want to write. | ||
20340 | + */ | ||
20341 | + if (count) { | ||
20342 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); | ||
20343 | + udelay (10); | ||
20344 | + tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); | ||
20345 | + while (count--) { | ||
20346 | + *ptmp++ = *source++; | ||
20347 | + } | ||
20348 | + /* | ||
20349 | + * reset address in case we're currently in auto-increment mode | ||
20350 | + */ | ||
20351 | + write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); | ||
20352 | + udelay (10); | ||
20353 | + write_reg32 (adev, IO_ACX_SLV_MEM_DATA, tmp); | ||
20354 | + udelay (10); | ||
20355 | + } | ||
20356 | + | ||
20357 | +} | ||
20358 | + | ||
20359 | +/* | ||
20360 | + * Block copy to slave buffers using memory block chain mode. Copies to the ACX | ||
20361 | + * transmit buffer structure with minimal intervention on our part. | ||
20362 | + * Interrupts should be disabled when calling this. | ||
20363 | + */ | ||
20364 | +void | ||
20365 | +chaincopy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count) | ||
20366 | +{ | ||
20367 | + u32 val; | ||
20368 | + u32 *data = (u32 *) source; | ||
20369 | + static u8 aligned_source[WLAN_A4FR_MAXLEN_WEP_FCS]; | ||
20370 | + | ||
20371 | + /* | ||
20372 | + * Warn if the pointers don't look right. Destination must fit in [23:5] with | ||
20373 | + * zero elsewhere and source should be 32 bit aligned. | ||
20374 | + * This should never happen since we're in control of both, but I want to know about | ||
20375 | + * it if it does. | ||
20376 | + */ | ||
20377 | + if ((destination & 0x00ffffe0) != destination) { | ||
20378 | + printk ("acx chaincopy: destination block 0x%04x not aligned!\n", destination); | ||
20379 | + } | ||
20380 | + if (count > sizeof aligned_source) { | ||
20381 | + printk( KERN_ERR "chaincopy_to_slavemem overflow!\n" ); | ||
20382 | + count = sizeof aligned_source; | ||
20383 | + } | ||
20384 | + if ((u32) source & 3) { | ||
20385 | + memcpy (aligned_source, source, count); | ||
20386 | + data = (u32 *) aligned_source; | ||
20387 | + } | ||
20388 | + | ||
20389 | + /* | ||
20390 | + * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment | ||
20391 | + * SLV_MEM_CTL[5:2] = offset to data portion = 1 word | ||
20392 | + */ | ||
20393 | + val = 2 << 16 | 1 << 2; | ||
20394 | + writel (val, &adev->iobase[ACX_SLV_MEM_CTL]); | ||
20395 | + | ||
20396 | + /* | ||
20397 | + * SLV_MEM_CP[23:5] = start of 1st block | ||
20398 | + * SLV_MEM_CP[3:2] = offset to memblkptr = 0 | ||
20399 | + */ | ||
20400 | + val = destination & 0x00ffffe0; | ||
20401 | + writel (val, &adev->iobase[ACX_SLV_MEM_CP]); | ||
20402 | + | ||
20403 | + /* | ||
20404 | + * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5] | ||
20405 | + */ | ||
20406 | + val = (destination & 0x00ffffe0) + (1<<2); | ||
20407 | + writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]); | ||
20408 | + | ||
20409 | + /* | ||
20410 | + * Write the data to the slave data register, rounding up to the end | ||
20411 | + * of the word containing the last byte (hence the > 0) | ||
20412 | + */ | ||
20413 | + while (count > 0) { | ||
20414 | + writel (*data++, &adev->iobase[ACX_SLV_MEM_DATA]); | ||
20415 | + count -= 4; | ||
20416 | + } | ||
20417 | +} | ||
20418 | + | ||
20419 | + | ||
20420 | +/* | ||
20421 | + * Block copy from slave buffers using memory block chain mode. Copies from the ACX | ||
20422 | + * receive buffer structures with minimal intervention on our part. | ||
20423 | + * Interrupts should be disabled when calling this. | ||
20424 | + */ | ||
20425 | +void | ||
20426 | +chaincopy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count) | ||
20427 | +{ | ||
20428 | + u32 val; | ||
20429 | + u32 *data = (u32 *) destination; | ||
20430 | + static u8 aligned_destination[WLAN_A4FR_MAXLEN_WEP_FCS]; | ||
20431 | + int saved_count = count; | ||
20432 | + | ||
20433 | + /* | ||
20434 | + * Warn if the pointers don't look right. Destination must fit in [23:5] with | ||
20435 | + * zero elsewhere and source should be 32 bit aligned. | ||
20436 | + * Turns out the network stack sends unaligned things, so fix them before | ||
20437 | + * copying to the ACX. | ||
20438 | + */ | ||
20439 | + if ((source & 0x00ffffe0) != source) { | ||
20440 | + printk ("acx chaincopy: source block 0x%04x not aligned!\n", source); | ||
20441 | + dump_acxmem (adev, 0, 0x10000); | ||
20442 | + } | ||
20443 | + if ((u32) destination & 3) { | ||
20444 | + //printk ("acx chaincopy: data destination not word aligned!\n"); | ||
20445 | + data = (u32 *) aligned_destination; | ||
20446 | + if (count > sizeof aligned_destination) { | ||
20447 | + printk( KERN_ERR "chaincopy_from_slavemem overflow!\n" ); | ||
20448 | + count = sizeof aligned_destination; | ||
20449 | + } | ||
20450 | + } | ||
20451 | + | ||
20452 | + /* | ||
20453 | + * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment | ||
20454 | + * SLV_MEM_CTL[5:2] = offset to data portion = 1 word | ||
20455 | + */ | ||
20456 | + val = (2 << 16) | (1 << 2); | ||
20457 | + writel (val, &adev->iobase[ACX_SLV_MEM_CTL]); | ||
20458 | + | ||
20459 | + /* | ||
20460 | + * SLV_MEM_CP[23:5] = start of 1st block | ||
20461 | + * SLV_MEM_CP[3:2] = offset to memblkptr = 0 | ||
20462 | + */ | ||
20463 | + val = source & 0x00ffffe0; | ||
20464 | + writel (val, &adev->iobase[ACX_SLV_MEM_CP]); | ||
20465 | + | ||
20466 | + /* | ||
20467 | + * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5] | ||
20468 | + */ | ||
20469 | + val = (source & 0x00ffffe0) + (1<<2); | ||
20470 | + writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]); | ||
20471 | + | ||
20472 | + /* | ||
20473 | + * Read the data from the slave data register, rounding up to the end | ||
20474 | + * of the word containing the last byte (hence the > 0) | ||
20475 | + */ | ||
20476 | + while (count > 0) { | ||
20477 | + *data++ = readl (&adev->iobase[ACX_SLV_MEM_DATA]); | ||
20478 | + count -= 4; | ||
20479 | + } | ||
20480 | + | ||
20481 | + /* | ||
20482 | + * If the destination wasn't aligned, we would have saved it in | ||
20483 | + * the aligned buffer, so copy it where it should go. | ||
20484 | + */ | ||
20485 | + if ((u32) destination & 3) { | ||
20486 | + memcpy (destination, aligned_destination, saved_count); | ||
20487 | + } | ||
20488 | +} | ||
20489 | + | ||
20490 | +char | ||
20491 | +printable (char c) | ||
20492 | +{ | ||
20493 | + return ((c >= 20) && (c < 127)) ? c : '.'; | ||
20494 | +} | ||
20495 | + | ||
20496 | +#if DUMP_MEM_DEFINED > 0 | ||
20497 | +static void | ||
20498 | +dump_acxmem (acx_device_t *adev, u32 start, int length) | ||
20499 | +{ | ||
20500 | + int i; | ||
20501 | + u8 buf[16]; | ||
20502 | + | ||
20503 | + while (length > 0) { | ||
20504 | + printk ("%04x ", start); | ||
20505 | + copy_from_slavemem (adev, buf, start, 16); | ||
20506 | + for (i = 0; (i < 16) && (i < length); i++) { | ||
20507 | + printk ("%02x ", buf[i]); | ||
20508 | + } | ||
20509 | + for (i = 0; (i < 16) && (i < length); i++) { | ||
20510 | + printk ("%c", printable (buf[i])); | ||
20511 | + } | ||
20512 | + printk ("\n"); | ||
20513 | + start += 16; | ||
20514 | + length -= 16; | ||
20515 | + } | ||
20516 | +} | ||
20517 | +#endif | ||
20518 | + | ||
20519 | +static void | ||
20520 | +enable_acx_irq(acx_device_t *adev); | ||
20521 | +static void | ||
20522 | +disable_acx_irq(acx_device_t *adev); | ||
20523 | + | ||
20524 | +/* | ||
20525 | + * Return an acx pointer to the next transmit data block. | ||
20526 | + */ | ||
20527 | +u32 | ||
20528 | +allocate_acx_txbuf_space (acx_device_t *adev, int count) { | ||
20529 | + u32 block, next, last_block; | ||
20530 | + int blocks_needed; | ||
20531 | + unsigned long flags; | ||
20532 | + | ||
20533 | + spin_lock_irqsave(&adev->txbuf_lock, flags); | ||
20534 | + /* | ||
20535 | + * Take 4 off the memory block size to account for the reserved word at the start of | ||
20536 | + * the block. | ||
20537 | + */ | ||
20538 | + blocks_needed = count / (adev->memblocksize - 4); | ||
20539 | + if (count % (adev->memblocksize - 4)) | ||
20540 | + blocks_needed++; | ||
20541 | + | ||
20542 | + if (blocks_needed <= adev->acx_txbuf_blocks_free) { | ||
20543 | + /* | ||
20544 | + * Take blocks at the head of the free list. | ||
20545 | + */ | ||
20546 | + last_block = block = adev->acx_txbuf_free; | ||
20547 | + | ||
20548 | + /* | ||
20549 | + * Follow block pointers through the requested number of blocks both to | ||
20550 | + * find the new head of the free list and to set the flags for the blocks | ||
20551 | + * appropriately. | ||
20552 | + */ | ||
20553 | + while (blocks_needed--) { | ||
20554 | + /* | ||
20555 | + * Keep track of the last block of the allocation | ||
20556 | + */ | ||
20557 | + last_block = adev->acx_txbuf_free; | ||
20558 | + | ||
20559 | + /* | ||
20560 | + * Make sure the end control flag is not set. | ||
20561 | + */ | ||
20562 | + next = read_slavemem32 (adev, adev->acx_txbuf_free) & 0x7ffff; | ||
20563 | + write_slavemem32 (adev, adev->acx_txbuf_free, next); | ||
20564 | + | ||
20565 | + /* | ||
20566 | + * Update the new head of the free list | ||
20567 | + */ | ||
20568 | + adev->acx_txbuf_free = next << 5; | ||
20569 | + adev->acx_txbuf_blocks_free--; | ||
20570 | + | ||
20571 | + } | ||
20572 | + | ||
20573 | + /* | ||
20574 | + * Flag the last block both by clearing out the next pointer | ||
20575 | + * and marking the control field. | ||
20576 | + */ | ||
20577 | + write_slavemem32 (adev, last_block, 0x02000000); | ||
20578 | + | ||
20579 | + /* | ||
20580 | + * If we're out of buffers make sure the free list pointer is NULL | ||
20581 | + */ | ||
20582 | + if (!adev->acx_txbuf_blocks_free) { | ||
20583 | + adev->acx_txbuf_free = 0; | ||
20584 | + } | ||
20585 | + } | ||
20586 | + else { | ||
20587 | + block = 0; | ||
20588 | + } | ||
20589 | + spin_unlock_irqrestore (&adev->txbuf_lock, flags); | ||
20590 | + return block; | ||
20591 | +} | ||
20592 | + | ||
20593 | +/* | ||
20594 | + * Return buffer space back to the pool by following the next pointers until we find | ||
20595 | + * the block marked as the end. Point the last block to the head of the free list, | ||
20596 | + * then update the head of the free list to point to the newly freed memory. | ||
20597 | + * This routine gets called in interrupt context, so it shouldn't block to protect | ||
20598 | + * the integrity of the linked list. The ISR already holds the lock. | ||
20599 | + */ | ||
20600 | +void | ||
20601 | +reclaim_acx_txbuf_space (acx_device_t *adev, u32 blockptr) { | ||
20602 | + u32 cur, last, next; | ||
20603 | + unsigned long flags; | ||
20604 | + | ||
20605 | + spin_lock_irqsave (&adev->txbuf_lock, flags); | ||
20606 | + if ((blockptr >= adev->acx_txbuf_start) && | ||
20607 | + (blockptr <= adev->acx_txbuf_start + | ||
20608 | + (adev->acx_txbuf_numblocks - 1) * adev->memblocksize)) { | ||
20609 | + cur = blockptr; | ||
20610 | + do { | ||
20611 | + last = cur; | ||
20612 | + next = read_slavemem32 (adev, cur); | ||
20613 | + | ||
20614 | + /* | ||
20615 | + * Advance to the next block in this allocation | ||
20616 | + */ | ||
20617 | + cur = (next & 0x7ffff) << 5; | ||
20618 | + | ||
20619 | + /* | ||
20620 | + * This block now counts as free. | ||
20621 | + */ | ||
20622 | + adev->acx_txbuf_blocks_free++; | ||
20623 | + } while (!(next & 0x02000000)); | ||
20624 | + | ||
20625 | + /* | ||
20626 | + * last now points to the last block of that allocation. Update the pointer | ||
20627 | + * in that block to point to the free list and reset the free list to the | ||
20628 | + * first block of the free call. If there were no free blocks, make sure | ||
20629 | + * the new end of the list marks itself as truly the end. | ||
20630 | + */ | ||
20631 | + if (adev->acx_txbuf_free) { | ||
20632 | + write_slavemem32 (adev, last, adev->acx_txbuf_free >> 5); | ||
20633 | + } | ||
20634 | + else { | ||
20635 | + write_slavemem32 (adev, last, 0x02000000); | ||
20636 | + } | ||
20637 | + adev->acx_txbuf_free = blockptr; | ||
20638 | + } | ||
20639 | + spin_unlock_irqrestore(&adev->txbuf_lock, flags); | ||
20640 | +} | ||
20641 | + | ||
20642 | +/* | ||
20643 | + * Initialize the pieces managing the transmit buffer pool on the ACX. The transmit | ||
20644 | + * buffer is a circular queue with one 32 bit word reserved at the beginning of each | ||
20645 | + * block. The upper 13 bits are a control field, of which only 0x02000000 has any | ||
20646 | + * meaning. The lower 19 bits are the address of the next block divided by 32. | ||
20647 | + */ | ||
20648 | +void | ||
20649 | +init_acx_txbuf (acx_device_t *adev) { | ||
20650 | + | ||
20651 | + /* | ||
20652 | + * acx100_s_init_memory_pools set up txbuf_start and txbuf_numblocks for us. | ||
20653 | + * All we need to do is reset the rest of the bookeeping. | ||
20654 | + */ | ||
20655 | + | ||
20656 | + adev->acx_txbuf_free = adev->acx_txbuf_start; | ||
20657 | + adev->acx_txbuf_blocks_free = adev->acx_txbuf_numblocks; | ||
20658 | + | ||
20659 | + /* | ||
20660 | + * Initialization leaves the last transmit pool block without a pointer back to | ||
20661 | + * the head of the list, but marked as the end of the list. That's how we want | ||
20662 | + * to see it, too, so leave it alone. This is only ever called after a firmware | ||
20663 | + * reset, so the ACX memory is in the state we want. | ||
20664 | + */ | ||
20665 | + | ||
20666 | +} | ||
20667 | + | ||
20668 | +INLINE_IO int | ||
20669 | +adev_present(acx_device_t *adev) | ||
20670 | +{ | ||
20671 | + /* fast version (accesses the first register, IO_ACX_SOFT_RESET, | ||
20672 | + * which should be safe): */ | ||
20673 | + return readl(adev->iobase) != 0xffffffff; | ||
20674 | +} | ||
20675 | + | ||
20676 | +/*********************************************************************** | ||
20677 | +*/ | ||
20678 | +static inline txdesc_t* | ||
20679 | +get_txdesc(acx_device_t *adev, int index) | ||
20680 | +{ | ||
20681 | + return (txdesc_t*) (((u8*)adev->txdesc_start) + index * adev->txdesc_size); | ||
20682 | +} | ||
20683 | + | ||
20684 | +static inline txdesc_t* | ||
20685 | +advance_txdesc(acx_device_t *adev, txdesc_t* txdesc, int inc) | ||
20686 | +{ | ||
20687 | + return (txdesc_t*) (((u8*)txdesc) + inc * adev->txdesc_size); | ||
20688 | +} | ||
20689 | + | ||
20690 | +static txhostdesc_t* | ||
20691 | +get_txhostdesc(acx_device_t *adev, txdesc_t* txdesc) | ||
20692 | +{ | ||
20693 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
20694 | + if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { | ||
20695 | + printk("bad txdesc ptr %p\n", txdesc); | ||
20696 | + return NULL; | ||
20697 | + } | ||
20698 | + index /= adev->txdesc_size; | ||
20699 | + if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { | ||
20700 | + printk("bad txdesc ptr %p\n", txdesc); | ||
20701 | + return NULL; | ||
20702 | + } | ||
20703 | + return &adev->txhostdesc_start[index*2]; | ||
20704 | +} | ||
20705 | + | ||
20706 | +static inline client_t* | ||
20707 | +get_txc(acx_device_t *adev, txdesc_t* txdesc) | ||
20708 | +{ | ||
20709 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
20710 | + if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { | ||
20711 | + printk("bad txdesc ptr %p\n", txdesc); | ||
20712 | + return NULL; | ||
20713 | + } | ||
20714 | + index /= adev->txdesc_size; | ||
20715 | + if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { | ||
20716 | + printk("bad txdesc ptr %p\n", txdesc); | ||
20717 | + return NULL; | ||
20718 | + } | ||
20719 | + return adev->txc[index]; | ||
20720 | +} | ||
20721 | + | ||
20722 | +static inline u16 | ||
20723 | +get_txr(acx_device_t *adev, txdesc_t* txdesc) | ||
20724 | +{ | ||
20725 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
20726 | + index /= adev->txdesc_size; | ||
20727 | + return adev->txr[index]; | ||
20728 | +} | ||
20729 | + | ||
20730 | +static inline void | ||
20731 | +put_txcr(acx_device_t *adev, txdesc_t* txdesc, client_t* c, u16 r111) | ||
20732 | +{ | ||
20733 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
20734 | + if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { | ||
20735 | + printk("bad txdesc ptr %p\n", txdesc); | ||
20736 | + return; | ||
20737 | + } | ||
20738 | + index /= adev->txdesc_size; | ||
20739 | + if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { | ||
20740 | + printk("bad txdesc ptr %p\n", txdesc); | ||
20741 | + return; | ||
20742 | + } | ||
20743 | + adev->txc[index] = c; | ||
20744 | + adev->txr[index] = r111; | ||
20745 | +} | ||
20746 | + | ||
20747 | + | ||
20748 | +/*********************************************************************** | ||
20749 | +** EEPROM and PHY read/write helpers | ||
20750 | +*/ | ||
20751 | +/*********************************************************************** | ||
20752 | +** acxmem_read_eeprom_byte | ||
20753 | +** | ||
20754 | +** Function called to read an octet in the EEPROM. | ||
20755 | +** | ||
20756 | +** This function is used by acxmem_e_probe to check if the | ||
20757 | +** connected card is a legal one or not. | ||
20758 | +** | ||
20759 | +** Arguments: | ||
20760 | +** adev ptr to acx_device structure | ||
20761 | +** addr address to read in the EEPROM | ||
20762 | +** charbuf ptr to a char. This is where the read octet | ||
20763 | +** will be stored | ||
20764 | +*/ | ||
20765 | +int | ||
20766 | +acxmem_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf) | ||
20767 | +{ | ||
20768 | + int result; | ||
20769 | + int count; | ||
20770 | + | ||
20771 | + write_reg32(adev, IO_ACX_EEPROM_CFG, 0); | ||
20772 | + write_reg32(adev, IO_ACX_EEPROM_ADDR, addr); | ||
20773 | + write_flush(adev); | ||
20774 | + write_reg32(adev, IO_ACX_EEPROM_CTL, 2); | ||
20775 | + | ||
20776 | + count = 0xffff; | ||
20777 | + while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { | ||
20778 | + /* scheduling away instead of CPU burning loop | ||
20779 | + * doesn't seem to work here at all: | ||
20780 | + * awful delay, sometimes also failure. | ||
20781 | + * Doesn't matter anyway (only small delay). */ | ||
20782 | + if (unlikely(!--count)) { | ||
20783 | + printk("%s: timeout waiting for EEPROM read\n", | ||
20784 | + adev->ndev->name); | ||
20785 | + result = NOT_OK; | ||
20786 | + goto fail; | ||
20787 | + } | ||
20788 | + cpu_relax(); | ||
20789 | + } | ||
20790 | + | ||
20791 | + *charbuf = read_reg8(adev, IO_ACX_EEPROM_DATA); | ||
20792 | + log(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf); | ||
20793 | + result = OK; | ||
20794 | + | ||
20795 | +fail: | ||
20796 | + return result; | ||
20797 | +} | ||
20798 | + | ||
20799 | + | ||
20800 | +/*********************************************************************** | ||
20801 | +** We don't lock hw accesses here since we never r/w eeprom in IRQ | ||
20802 | +** Note: this function sleeps only because of GFP_KERNEL alloc | ||
20803 | +*/ | ||
20804 | +#ifdef UNUSED | ||
20805 | +int | ||
20806 | +acxmem_s_write_eeprom(acx_device_t *adev, u32 addr, u32 len, const u8 *charbuf) | ||
20807 | +{ | ||
20808 | + u8 *data_verify = NULL; | ||
20809 | + unsigned long flags; | ||
20810 | + int count, i; | ||
20811 | + int result = NOT_OK; | ||
20812 | + u16 gpio_orig; | ||
20813 | + | ||
20814 | + printk("acx: WARNING! I would write to EEPROM now. " | ||
20815 | + "Since I really DON'T want to unless you know " | ||
20816 | + "what you're doing (THIS CODE WILL PROBABLY " | ||
20817 | + "NOT WORK YET!), I will abort that now. And " | ||
20818 | + "definitely make sure to make a " | ||
20819 | + "/proc/driver/acx_wlan0_eeprom backup copy first!!! " | ||
20820 | + "(the EEPROM content includes the PCI config header!! " | ||
20821 | + "If you kill important stuff, then you WILL " | ||
20822 | + "get in trouble and people DID get in trouble already)\n"); | ||
20823 | + return OK; | ||
20824 | + | ||
20825 | + FN_ENTER; | ||
20826 | + | ||
20827 | + data_verify = kmalloc(len, GFP_KERNEL); | ||
20828 | + if (!data_verify) { | ||
20829 | + goto end; | ||
20830 | + } | ||
20831 | + | ||
20832 | + /* first we need to enable the OE (EEPROM Output Enable) GPIO line | ||
20833 | + * to be able to write to the EEPROM. | ||
20834 | + * NOTE: an EEPROM writing success has been reported, | ||
20835 | + * but you probably have to modify GPIO_OUT, too, | ||
20836 | + * and you probably need to activate a different GPIO | ||
20837 | + * line instead! */ | ||
20838 | + gpio_orig = read_reg16(adev, IO_ACX_GPIO_OE); | ||
20839 | + write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig & ~1); | ||
20840 | + write_flush(adev); | ||
20841 | + | ||
20842 | + /* ok, now start writing the data out */ | ||
20843 | + for (i = 0; i < len; i++) { | ||
20844 | + write_reg32(adev, IO_ACX_EEPROM_CFG, 0); | ||
20845 | + write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); | ||
20846 | + write_reg32(adev, IO_ACX_EEPROM_DATA, *(charbuf + i)); | ||
20847 | + write_flush(adev); | ||
20848 | + write_reg32(adev, IO_ACX_EEPROM_CTL, 1); | ||
20849 | + | ||
20850 | + count = 0xffff; | ||
20851 | + while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { | ||
20852 | + if (unlikely(!--count)) { | ||
20853 | + printk("WARNING, DANGER!!! " | ||
20854 | + "Timeout waiting for EEPROM write\n"); | ||
20855 | + goto end; | ||
20856 | + } | ||
20857 | + cpu_relax(); | ||
20858 | + } | ||
20859 | + } | ||
20860 | + | ||
20861 | + /* disable EEPROM writing */ | ||
20862 | + write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig); | ||
20863 | + write_flush(adev); | ||
20864 | + | ||
20865 | + /* now start a verification run */ | ||
20866 | + for (i = 0; i < len; i++) { | ||
20867 | + write_reg32(adev, IO_ACX_EEPROM_CFG, 0); | ||
20868 | + write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); | ||
20869 | + write_flush(adev); | ||
20870 | + write_reg32(adev, IO_ACX_EEPROM_CTL, 2); | ||
20871 | + | ||
20872 | + count = 0xffff; | ||
20873 | + while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { | ||
20874 | + if (unlikely(!--count)) { | ||
20875 | + printk("timeout waiting for EEPROM read\n"); | ||
20876 | + goto end; | ||
20877 | + } | ||
20878 | + cpu_relax(); | ||
20879 | + } | ||
20880 | + | ||
20881 | + data_verify[i] = read_reg16(adev, IO_ACX_EEPROM_DATA); | ||
20882 | + } | ||
20883 | + | ||
20884 | + if (0 == memcmp(charbuf, data_verify, len)) | ||
20885 | + result = OK; /* read data matches, success */ | ||
20886 | + | ||
20887 | +end: | ||
20888 | + kfree(data_verify); | ||
20889 | + FN_EXIT1(result); | ||
20890 | + return result; | ||
20891 | +} | ||
20892 | +#endif /* UNUSED */ | ||
20893 | + | ||
20894 | + | ||
20895 | +/*********************************************************************** | ||
20896 | +** acxmem_s_read_phy_reg | ||
20897 | +** | ||
20898 | +** Messing with rx/tx disabling and enabling here | ||
20899 | +** (write_reg32(adev, IO_ACX_ENABLE, 0b000000xx)) kills traffic | ||
20900 | +*/ | ||
20901 | +int | ||
20902 | +acxmem_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf) | ||
20903 | +{ | ||
20904 | + int result = NOT_OK; | ||
20905 | + int count; | ||
20906 | + | ||
20907 | + FN_ENTER; | ||
20908 | + | ||
20909 | + write_reg32(adev, IO_ACX_PHY_ADDR, reg); | ||
20910 | + write_flush(adev); | ||
20911 | + write_reg32(adev, IO_ACX_PHY_CTL, 2); | ||
20912 | + | ||
20913 | + count = 0xffff; | ||
20914 | + while (read_reg32(adev, IO_ACX_PHY_CTL)) { | ||
20915 | + /* scheduling away instead of CPU burning loop | ||
20916 | + * doesn't seem to work here at all: | ||
20917 | + * awful delay, sometimes also failure. | ||
20918 | + * Doesn't matter anyway (only small delay). */ | ||
20919 | + if (unlikely(!--count)) { | ||
20920 | + printk("%s: timeout waiting for phy read\n", | ||
20921 | + adev->ndev->name); | ||
20922 | + *charbuf = 0; | ||
20923 | + goto fail; | ||
20924 | + } | ||
20925 | + cpu_relax(); | ||
20926 | + } | ||
20927 | + | ||
20928 | + log(L_DEBUG, "count was %u\n", count); | ||
20929 | + *charbuf = read_reg8(adev, IO_ACX_PHY_DATA); | ||
20930 | + | ||
20931 | + log(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg); | ||
20932 | + result = OK; | ||
20933 | + goto fail; /* silence compiler warning */ | ||
20934 | +fail: | ||
20935 | + FN_EXIT1(result); | ||
20936 | + return result; | ||
20937 | +} | ||
20938 | + | ||
20939 | + | ||
20940 | +/*********************************************************************** | ||
20941 | +*/ | ||
20942 | +int | ||
20943 | +acxmem_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value) | ||
20944 | +{ | ||
20945 | + int count; | ||
20946 | + FN_ENTER; | ||
20947 | + | ||
20948 | + /* mprusko said that 32bit accesses result in distorted sensitivity | ||
20949 | + * on his card. Unconfirmed, looks like it's not true (most likely since we | ||
20950 | + * now properly flush writes). */ | ||
20951 | + write_reg32(adev, IO_ACX_PHY_DATA, value); | ||
20952 | + write_reg32(adev, IO_ACX_PHY_ADDR, reg); | ||
20953 | + write_flush(adev); | ||
20954 | + write_reg32(adev, IO_ACX_PHY_CTL, 1); | ||
20955 | + write_flush(adev); | ||
20956 | + | ||
20957 | + count = 0xffff; | ||
20958 | + while (read_reg32(adev, IO_ACX_PHY_CTL)) { | ||
20959 | + /* scheduling away instead of CPU burning loop | ||
20960 | + * doesn't seem to work here at all: | ||
20961 | + * awful delay, sometimes also failure. | ||
20962 | + * Doesn't matter anyway (only small delay). */ | ||
20963 | + if (unlikely(!--count)) { | ||
20964 | + printk("%s: timeout waiting for phy read\n", | ||
20965 | + adev->ndev->name); | ||
20966 | + goto fail; | ||
20967 | + } | ||
20968 | + cpu_relax(); | ||
20969 | + } | ||
20970 | + | ||
20971 | + log(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg); | ||
20972 | + fail: | ||
20973 | + FN_EXIT1(OK); | ||
20974 | + return OK; | ||
20975 | +} | ||
20976 | + | ||
20977 | + | ||
20978 | +#define NO_AUTO_INCREMENT 1 | ||
20979 | + | ||
20980 | +/*********************************************************************** | ||
20981 | +** acxmem_s_write_fw | ||
20982 | +** | ||
20983 | +** Write the firmware image into the card. | ||
20984 | +** | ||
20985 | +** Arguments: | ||
20986 | +** adev wlan device structure | ||
20987 | +** fw_image firmware image. | ||
20988 | +** | ||
20989 | +** Returns: | ||
20990 | +** 1 firmware image corrupted | ||
20991 | +** 0 success | ||
20992 | +*/ | ||
20993 | +static int | ||
20994 | +acxmem_s_write_fw(acx_device_t *adev, const firmware_image_t *fw_image, u32 offset) | ||
20995 | +{ | ||
20996 | + int len, size, checkMismatch = -1; | ||
20997 | + u32 sum, v32, tmp, id; | ||
20998 | + /* we skip the first four bytes which contain the control sum */ | ||
20999 | + const u8 *p = (u8*)fw_image + 4; | ||
21000 | + | ||
21001 | + /* start the image checksum by adding the image size value */ | ||
21002 | + sum = p[0]+p[1]+p[2]+p[3]; | ||
21003 | + p += 4; | ||
21004 | + | ||
21005 | +#ifdef NOPE | ||
21006 | +#if NO_AUTO_INCREMENT | ||
21007 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ | ||
21008 | +#else | ||
21009 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ | ||
21010 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ | ||
21011 | + write_flush(adev); | ||
21012 | +#endif | ||
21013 | +#endif | ||
21014 | + len = 0; | ||
21015 | + size = le32_to_cpu(fw_image->size) & (~3); | ||
21016 | + | ||
21017 | + while (likely(len < size)) { | ||
21018 | + v32 = be32_to_cpu(*(u32*)p); | ||
21019 | + sum += p[0]+p[1]+p[2]+p[3]; | ||
21020 | + p += 4; | ||
21021 | + len += 4; | ||
21022 | + | ||
21023 | +#ifdef NOPE | ||
21024 | +#if NO_AUTO_INCREMENT | ||
21025 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); | ||
21026 | + write_flush(adev); | ||
21027 | +#endif | ||
21028 | + write_reg32(adev, IO_ACX_SLV_MEM_DATA, v32); | ||
21029 | + write_flush(adev); | ||
21030 | +#endif | ||
21031 | + write_slavemem32 (adev, offset + len - 4, v32); | ||
21032 | + | ||
21033 | + id = read_id_register (adev); | ||
21034 | + | ||
21035 | + /* | ||
21036 | + * check the data written | ||
21037 | + */ | ||
21038 | + tmp = read_slavemem32 (adev, offset + len - 4); | ||
21039 | + if (checkMismatch && (tmp != v32)) { | ||
21040 | + printk ("first data mismatch at 0x%08x good 0x%08x bad 0x%08x id 0x%08x\n", | ||
21041 | + offset + len - 4, v32, tmp, id); | ||
21042 | + checkMismatch = 0; | ||
21043 | + } | ||
21044 | + } | ||
21045 | + log(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n", | ||
21046 | + size, sum, le32_to_cpu(fw_image->chksum)); | ||
21047 | + | ||
21048 | + /* compare our checksum with the stored image checksum */ | ||
21049 | + return (sum != le32_to_cpu(fw_image->chksum)); | ||
21050 | +} | ||
21051 | + | ||
21052 | + | ||
21053 | +/*********************************************************************** | ||
21054 | +** acxmem_s_validate_fw | ||
21055 | +** | ||
21056 | +** Compare the firmware image given with | ||
21057 | +** the firmware image written into the card. | ||
21058 | +** | ||
21059 | +** Arguments: | ||
21060 | +** adev wlan device structure | ||
21061 | +** fw_image firmware image. | ||
21062 | +** | ||
21063 | +** Returns: | ||
21064 | +** NOT_OK firmware image corrupted or not correctly written | ||
21065 | +** OK success | ||
21066 | +*/ | ||
21067 | +static int | ||
21068 | +acxmem_s_validate_fw(acx_device_t *adev, const firmware_image_t *fw_image, | ||
21069 | + u32 offset) | ||
21070 | +{ | ||
21071 | + u32 sum, v32, w32; | ||
21072 | + int len, size; | ||
21073 | + int result = OK; | ||
21074 | + /* we skip the first four bytes which contain the control sum */ | ||
21075 | + const u8 *p = (u8*)fw_image + 4; | ||
21076 | + | ||
21077 | + /* start the image checksum by adding the image size value */ | ||
21078 | + sum = p[0]+p[1]+p[2]+p[3]; | ||
21079 | + p += 4; | ||
21080 | + | ||
21081 | + write_reg32(adev, IO_ACX_SLV_END_CTL, 0); | ||
21082 | + | ||
21083 | +#if NO_AUTO_INCREMENT | ||
21084 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ | ||
21085 | +#else | ||
21086 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ | ||
21087 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ | ||
21088 | +#endif | ||
21089 | + | ||
21090 | + len = 0; | ||
21091 | + size = le32_to_cpu(fw_image->size) & (~3); | ||
21092 | + | ||
21093 | + while (likely(len < size)) { | ||
21094 | + v32 = be32_to_cpu(*(u32*)p); | ||
21095 | + p += 4; | ||
21096 | + len += 4; | ||
21097 | + | ||
21098 | +#ifdef NOPE | ||
21099 | +#if NO_AUTO_INCREMENT | ||
21100 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); | ||
21101 | +#endif | ||
21102 | + udelay(10); | ||
21103 | + w32 = read_reg32(adev, IO_ACX_SLV_MEM_DATA); | ||
21104 | +#endif | ||
21105 | + w32 = read_slavemem32 (adev, offset + len - 4); | ||
21106 | + | ||
21107 | + if (unlikely(w32 != v32)) { | ||
21108 | + printk("acx: FATAL: firmware upload: " | ||
21109 | + "data parts at offset %d don't match\n(0x%08X vs. 0x%08X)!\n" | ||
21110 | + "I/O timing issues or defective memory, with DWL-xx0+? " | ||
21111 | + "ACX_IO_WIDTH=16 may help. Please report\n", | ||
21112 | + len, v32, w32); | ||
21113 | + result = NOT_OK; | ||
21114 | + break; | ||
21115 | + } | ||
21116 | + | ||
21117 | + sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24); | ||
21118 | + } | ||
21119 | + | ||
21120 | + /* sum control verification */ | ||
21121 | + if (result != NOT_OK) { | ||
21122 | + if (sum != le32_to_cpu(fw_image->chksum)) { | ||
21123 | + printk("acx: FATAL: firmware upload: " | ||
21124 | + "checksums don't match!\n"); | ||
21125 | + result = NOT_OK; | ||
21126 | + } | ||
21127 | + } | ||
21128 | + | ||
21129 | + return result; | ||
21130 | +} | ||
21131 | + | ||
21132 | + | ||
21133 | +/*********************************************************************** | ||
21134 | +** acxmem_s_upload_fw | ||
21135 | +** | ||
21136 | +** Called from acx_reset_dev | ||
21137 | +*/ | ||
21138 | +static int | ||
21139 | +acxmem_s_upload_fw(acx_device_t *adev) | ||
21140 | +{ | ||
21141 | + firmware_image_t *fw_image = NULL; | ||
21142 | + int res = NOT_OK; | ||
21143 | + int try; | ||
21144 | + u32 file_size; | ||
21145 | + char *filename = "WLANGEN.BIN"; | ||
21146 | +#ifdef PATCH_AROUND_BAD_SPOTS | ||
21147 | + u32 offset; | ||
21148 | + int i; | ||
21149 | + /* | ||
21150 | + * arm-linux-objdump -d patch.bin, or | ||
21151 | + * od -Ax -t x4 patch.bin after finding the bounds | ||
21152 | + * of the .text section with arm-linux-objdump -s patch.bin | ||
21153 | + */ | ||
21154 | + u32 patch[] = { | ||
21155 | + 0xe584c030, 0xe59fc008, | ||
21156 | + 0xe92d1000, 0xe59fc004, 0xe8bd8000, 0x0000080c, | ||
21157 | + 0x0000aa68, 0x605a2200, 0x2c0a689c, 0x2414d80a, | ||
21158 | + 0x2f00689f, 0x1c27d007, 0x06241e7c, 0x2f000e24, | ||
21159 | + 0xe000d1f6, 0x602e6018, 0x23036468, 0x480203db, | ||
21160 | + 0x60ca6003, 0xbdf0750a, 0xffff0808 | ||
21161 | + }; | ||
21162 | +#endif | ||
21163 | + | ||
21164 | + FN_ENTER; | ||
21165 | + /* No combined image; tell common we need the radio firmware, too */ | ||
21166 | + adev->need_radio_fw = 1; | ||
21167 | + | ||
21168 | + fw_image = acx_s_read_fw(adev->dev, filename, &file_size); | ||
21169 | + if (!fw_image) { | ||
21170 | + FN_EXIT1(NOT_OK); | ||
21171 | + return NOT_OK; | ||
21172 | + } | ||
21173 | + | ||
21174 | + for (try = 1; try <= 5; try++) { | ||
21175 | + res = acxmem_s_write_fw(adev, fw_image, 0); | ||
21176 | + log(L_DEBUG|L_INIT, "acx_write_fw (main): %d\n", res); | ||
21177 | + if (OK == res) { | ||
21178 | + res = acxmem_s_validate_fw(adev, fw_image, 0); | ||
21179 | + log(L_DEBUG|L_INIT, "acx_validate_fw " | ||
21180 | + "(main): %d\n", res); | ||
21181 | + } | ||
21182 | + | ||
21183 | + if (OK == res) { | ||
21184 | + SET_BIT(adev->dev_state_mask, ACX_STATE_FW_LOADED); | ||
21185 | + break; | ||
21186 | + } | ||
21187 | + printk("acx: firmware upload attempt #%d FAILED, " | ||
21188 | + "retrying...\n", try); | ||
21189 | + acx_s_msleep(1000); /* better wait for a while... */ | ||
21190 | + } | ||
21191 | + | ||
21192 | +#ifdef PATCH_AROUND_BAD_SPOTS | ||
21193 | + /* | ||
21194 | + * Only want to do this if the firmware is exactly what we expect for an | ||
21195 | + * iPaq 4700; otherwise, bad things would ensue. | ||
21196 | + */ | ||
21197 | + if ((HX4700_FIRMWARE_CHECKSUM == fw_image->chksum) || | ||
21198 | + (HX4700_ALTERNATE_FIRMWARE_CHECKSUM == fw_image->chksum)) { | ||
21199 | + /* | ||
21200 | + * Put the patch after the main firmware image. 0x950c contains | ||
21201 | + * the ACX's idea of the end of the firmware. Use that location to | ||
21202 | + * load ours (which depends on that location being 0xab58) then | ||
21203 | + * update that location to point to after ours. | ||
21204 | + */ | ||
21205 | + | ||
21206 | + offset = read_slavemem32 (adev, 0x950c); | ||
21207 | + | ||
21208 | + log (L_DEBUG, "acx: patching in at 0x%04x\n", offset); | ||
21209 | + | ||
21210 | + for (i = 0; i < sizeof(patch) / sizeof(patch[0]); i++) { | ||
21211 | + write_slavemem32 (adev, offset, patch[i]); | ||
21212 | + offset += sizeof(u32); | ||
21213 | + } | ||
21214 | + | ||
21215 | + /* | ||
21216 | + * Patch the instruction at 0x0804 to branch to our ARM patch at 0xab58 | ||
21217 | + */ | ||
21218 | + write_slavemem32 (adev, 0x0804, 0xea000000 + (0xab58-0x0804-8)/4); | ||
21219 | + | ||
21220 | + /* | ||
21221 | + * Patch the instructions at 0x1f40 to branch to our Thumb patch at 0xab74 | ||
21222 | + * | ||
21223 | + * 4a00 ldr r2, [pc, #0] | ||
21224 | + * 4710 bx r2 | ||
21225 | + * .data 0xab74+1 | ||
21226 | + */ | ||
21227 | + write_slavemem32 (adev, 0x1f40, 0x47104a00); | ||
21228 | + write_slavemem32 (adev, 0x1f44, 0x0000ab74+1); | ||
21229 | + | ||
21230 | + /* | ||
21231 | + * Bump the end of the firmware up to beyond our patch. | ||
21232 | + */ | ||
21233 | + write_slavemem32 (adev, 0x950c, offset); | ||
21234 | + | ||
21235 | + } | ||
21236 | +#endif | ||
21237 | + | ||
21238 | + vfree(fw_image); | ||
21239 | + | ||
21240 | + FN_EXIT1(res); | ||
21241 | + return res; | ||
21242 | +} | ||
21243 | + | ||
21244 | + | ||
21245 | +/*********************************************************************** | ||
21246 | +** acxmem_s_upload_radio | ||
21247 | +** | ||
21248 | +** Uploads the appropriate radio module firmware into the card. | ||
21249 | +*/ | ||
21250 | +int | ||
21251 | +acxmem_s_upload_radio(acx_device_t *adev) | ||
21252 | +{ | ||
21253 | + acx_ie_memmap_t mm; | ||
21254 | + firmware_image_t *radio_image; | ||
21255 | + acx_cmd_radioinit_t radioinit; | ||
21256 | + int res = NOT_OK; | ||
21257 | + int try; | ||
21258 | + u32 offset; | ||
21259 | + u32 size; | ||
21260 | + char filename[sizeof("RADIONN.BIN")]; | ||
21261 | + | ||
21262 | + if (!adev->need_radio_fw) return OK; | ||
21263 | + | ||
21264 | + FN_ENTER; | ||
21265 | + | ||
21266 | + acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); | ||
21267 | + offset = le32_to_cpu(mm.CodeEnd); | ||
21268 | + | ||
21269 | + snprintf(filename, sizeof(filename), "RADIO%02x.BIN", | ||
21270 | + adev->radio_type); | ||
21271 | + radio_image = acx_s_read_fw(adev->dev, filename, &size); | ||
21272 | + if (!radio_image) { | ||
21273 | + printk("acx: can't load radio module '%s'\n", filename); | ||
21274 | + goto fail; | ||
21275 | + } | ||
21276 | + | ||
21277 | + acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0); | ||
21278 | + | ||
21279 | + for (try = 1; try <= 5; try++) { | ||
21280 | + res = acxmem_s_write_fw(adev, radio_image, offset); | ||
21281 | + log(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res); | ||
21282 | + if (OK == res) { | ||
21283 | + res = acxmem_s_validate_fw(adev, radio_image, offset); | ||
21284 | + log(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res); | ||
21285 | + } | ||
21286 | + | ||
21287 | + if (OK == res) | ||
21288 | + break; | ||
21289 | + printk("acx: radio firmware upload attempt #%d FAILED, " | ||
21290 | + "retrying...\n", try); | ||
21291 | + acx_s_msleep(1000); /* better wait for a while... */ | ||
21292 | + } | ||
21293 | + | ||
21294 | + acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0); | ||
21295 | + radioinit.offset = cpu_to_le32(offset); | ||
21296 | + | ||
21297 | + /* no endian conversion needed, remains in card CPU area: */ | ||
21298 | + radioinit.len = radio_image->size; | ||
21299 | + | ||
21300 | + vfree(radio_image); | ||
21301 | + | ||
21302 | + if (OK != res) | ||
21303 | + goto fail; | ||
21304 | + | ||
21305 | + /* will take a moment so let's have a big timeout */ | ||
21306 | + acx_s_issue_cmd_timeo(adev, ACX1xx_CMD_RADIOINIT, | ||
21307 | + &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000)); | ||
21308 | + | ||
21309 | + res = acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); | ||
21310 | + | ||
21311 | +fail: | ||
21312 | + FN_EXIT1(res); | ||
21313 | + return res; | ||
21314 | +} | ||
21315 | + | ||
21316 | +/*********************************************************************** | ||
21317 | +** acxmem_l_reset_mac | ||
21318 | +** | ||
21319 | +** MAC will be reset | ||
21320 | +** Call context: reset_dev | ||
21321 | +*/ | ||
21322 | +static void | ||
21323 | +acxmem_l_reset_mac(acx_device_t *adev) | ||
21324 | +{ | ||
21325 | + int count; | ||
21326 | + FN_ENTER; | ||
21327 | + | ||
21328 | + /* halt eCPU */ | ||
21329 | + set_regbits (adev, IO_ACX_ECPU_CTRL, 0x1); | ||
21330 | + | ||
21331 | + /* now do soft reset of eCPU, set bit */ | ||
21332 | + set_regbits (adev, IO_ACX_SOFT_RESET, 0x1); | ||
21333 | + log(L_DEBUG, "%s: enable soft reset...\n", __func__); | ||
21334 | + | ||
21335 | + /* Windows driver sleeps here for a while with this sequence */ | ||
21336 | + for (count = 0; count < 200; count++) { | ||
21337 | + udelay (50); | ||
21338 | + } | ||
21339 | + | ||
21340 | + /* now clear bit again: deassert eCPU reset */ | ||
21341 | + log(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__); | ||
21342 | + clear_regbits (adev, IO_ACX_SOFT_RESET, 0x1); | ||
21343 | + | ||
21344 | + /* now start a burst read from initial EEPROM */ | ||
21345 | + set_regbits (adev, IO_ACX_EE_START, 0x1); | ||
21346 | + | ||
21347 | + /* | ||
21348 | + * Windows driver sleeps here for a while with this sequence | ||
21349 | + */ | ||
21350 | + for (count = 0; count < 200; count++) { | ||
21351 | + udelay (50); | ||
21352 | + } | ||
21353 | + | ||
21354 | + /* Windows driver writes 0x10000 to register 0x808 here */ | ||
21355 | + | ||
21356 | + write_reg32 (adev, 0x808, 0x10000); | ||
21357 | + | ||
21358 | + FN_EXIT0; | ||
21359 | +} | ||
21360 | + | ||
21361 | + | ||
21362 | +/*********************************************************************** | ||
21363 | +** acxmem_s_verify_init | ||
21364 | +*/ | ||
21365 | +static int | ||
21366 | +acxmem_s_verify_init(acx_device_t *adev) | ||
21367 | +{ | ||
21368 | + int result = NOT_OK; | ||
21369 | + unsigned long timeout; | ||
21370 | + | ||
21371 | + FN_ENTER; | ||
21372 | + | ||
21373 | + timeout = jiffies + 2*HZ; | ||
21374 | + for (;;) { | ||
21375 | + u32 irqstat = read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES); | ||
21376 | + if ((irqstat != 0xFFFFFFFF) && (irqstat & HOST_INT_FCS_THRESHOLD)) { | ||
21377 | + result = OK; | ||
21378 | + write_reg32(adev, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD); | ||
21379 | + break; | ||
21380 | + } | ||
21381 | + if (time_after(jiffies, timeout)) | ||
21382 | + break; | ||
21383 | + /* Init may take up to ~0.5 sec total */ | ||
21384 | + acx_s_msleep(50); | ||
21385 | + } | ||
21386 | + | ||
21387 | + FN_EXIT1(result); | ||
21388 | + return result; | ||
21389 | +} | ||
21390 | + | ||
21391 | + | ||
21392 | +/*********************************************************************** | ||
21393 | +** A few low-level helpers | ||
21394 | +** | ||
21395 | +** Note: these functions are not protected by lock | ||
21396 | +** and thus are never allowed to be called from IRQ. | ||
21397 | +** Also they must not race with fw upload which uses same hw regs | ||
21398 | +*/ | ||
21399 | + | ||
21400 | +/*********************************************************************** | ||
21401 | +** acxmem_write_cmd_type_status | ||
21402 | +*/ | ||
21403 | + | ||
21404 | +static inline void | ||
21405 | +acxmem_write_cmd_type_status(acx_device_t *adev, u16 type, u16 status) | ||
21406 | +{ | ||
21407 | + write_slavemem32 (adev, (u32) adev->cmd_area, type | (status << 16)); | ||
21408 | + write_flush(adev); | ||
21409 | +} | ||
21410 | + | ||
21411 | + | ||
21412 | +/*********************************************************************** | ||
21413 | +** acxmem_read_cmd_type_status | ||
21414 | +*/ | ||
21415 | +static u32 | ||
21416 | +acxmem_read_cmd_type_status(acx_device_t *adev) | ||
21417 | +{ | ||
21418 | + u32 cmd_type, cmd_status; | ||
21419 | + | ||
21420 | + cmd_type = read_slavemem32 (adev, (u32) adev->cmd_area); | ||
21421 | + | ||
21422 | + cmd_status = (cmd_type >> 16); | ||
21423 | + cmd_type = (u16)cmd_type; | ||
21424 | + | ||
21425 | + log(L_CTL, "cmd_type:%04X cmd_status:%04X [%s]\n", | ||
21426 | + cmd_type, cmd_status, | ||
21427 | + acx_cmd_status_str(cmd_status)); | ||
21428 | + | ||
21429 | + return cmd_status; | ||
21430 | +} | ||
21431 | + | ||
21432 | + | ||
21433 | +/*********************************************************************** | ||
21434 | +** acxmem_s_reset_dev | ||
21435 | +** | ||
21436 | +** Arguments: | ||
21437 | +** netdevice that contains the adev variable | ||
21438 | +** Returns: | ||
21439 | +** NOT_OK on fail | ||
21440 | +** OK on success | ||
21441 | +** Side effects: | ||
21442 | +** device is hard reset | ||
21443 | +** Call context: | ||
21444 | +** acxmem_e_probe | ||
21445 | +** Comment: | ||
21446 | +** This resets the device using low level hardware calls | ||
21447 | +** as well as uploads and verifies the firmware to the card | ||
21448 | +*/ | ||
21449 | + | ||
21450 | +static inline void | ||
21451 | +init_mboxes(acx_device_t *adev) | ||
21452 | +{ | ||
21453 | + u32 cmd_offs, info_offs; | ||
21454 | + | ||
21455 | + cmd_offs = read_reg32(adev, IO_ACX_CMD_MAILBOX_OFFS); | ||
21456 | + info_offs = read_reg32(adev, IO_ACX_INFO_MAILBOX_OFFS); | ||
21457 | + adev->cmd_area = (u8*) cmd_offs; | ||
21458 | + adev->info_area = (u8*) info_offs; | ||
21459 | + /* | ||
21460 | + log(L_DEBUG, "iobase2=%p\n" | ||
21461 | + */ | ||
21462 | + log( L_DEBUG, "cmd_mbox_offset=%X cmd_area=%p\n" | ||
21463 | + "info_mbox_offset=%X info_area=%p\n", | ||
21464 | + cmd_offs, adev->cmd_area, | ||
21465 | + info_offs, adev->info_area); | ||
21466 | +} | ||
21467 | + | ||
21468 | + | ||
21469 | +static inline void | ||
21470 | +read_eeprom_area(acx_device_t *adev) | ||
21471 | +{ | ||
21472 | +#if ACX_DEBUG > 1 | ||
21473 | + int offs; | ||
21474 | + u8 tmp; | ||
21475 | + | ||
21476 | + for (offs = 0x8c; offs < 0xb9; offs++) | ||
21477 | + acxmem_read_eeprom_byte(adev, offs, &tmp); | ||
21478 | +#endif | ||
21479 | +} | ||
21480 | + | ||
21481 | +static int | ||
21482 | +acxmem_s_reset_dev(acx_device_t *adev) | ||
21483 | +{ | ||
21484 | + const char* msg = ""; | ||
21485 | + unsigned long flags; | ||
21486 | + int result = NOT_OK; | ||
21487 | + u16 hardware_info; | ||
21488 | + u16 ecpu_ctrl; | ||
21489 | + int count; | ||
21490 | + u32 tmp; | ||
21491 | + | ||
21492 | + FN_ENTER; | ||
21493 | + /* | ||
21494 | + write_reg32 (adev, IO_ACX_SLV_MEM_CP, 0); | ||
21495 | + */ | ||
21496 | + /* reset the device to make sure the eCPU is stopped | ||
21497 | + * to upload the firmware correctly */ | ||
21498 | + | ||
21499 | + acx_lock(adev, flags); | ||
21500 | + | ||
21501 | + /* Windows driver does some funny things here */ | ||
21502 | + /* | ||
21503 | + * clear bit 0x200 in register 0x2A0 | ||
21504 | + */ | ||
21505 | + clear_regbits (adev, 0x2A0, 0x200); | ||
21506 | + | ||
21507 | + /* | ||
21508 | + * Set bit 0x200 in ACX_GPIO_OUT | ||
21509 | + */ | ||
21510 | + set_regbits (adev, IO_ACX_GPIO_OUT, 0x200); | ||
21511 | + | ||
21512 | + /* | ||
21513 | + * read register 0x900 until its value is 0x8400104C, sleeping | ||
21514 | + * in between reads if it's not immediate | ||
21515 | + */ | ||
21516 | + tmp = read_reg32 (adev, REG_ACX_VENDOR_ID); | ||
21517 | + count = 500; | ||
21518 | + while (count-- && (tmp != ACX_VENDOR_ID)) { | ||
21519 | + mdelay (10); | ||
21520 | + tmp = read_reg32 (adev, REG_ACX_VENDOR_ID); | ||
21521 | + } | ||
21522 | + | ||
21523 | + /* end what Windows driver does */ | ||
21524 | + | ||
21525 | + acxmem_l_reset_mac(adev); | ||
21526 | + | ||
21527 | + ecpu_ctrl = read_reg32(adev, IO_ACX_ECPU_CTRL) & 1; | ||
21528 | + if (!ecpu_ctrl) { | ||
21529 | + msg = "eCPU is already running. "; | ||
21530 | + goto end_unlock; | ||
21531 | + } | ||
21532 | + | ||
21533 | +#ifdef WE_DONT_NEED_THAT_DO_WE | ||
21534 | + if (read_reg16(adev, IO_ACX_SOR_CFG) & 2) { | ||
21535 | + /* eCPU most likely means "embedded CPU" */ | ||
21536 | + msg = "eCPU did not start after boot from flash. "; | ||
21537 | + goto end_unlock; | ||
21538 | + } | ||
21539 | + | ||
21540 | + /* check sense on reset flags */ | ||
21541 | + if (read_reg16(adev, IO_ACX_SOR_CFG) & 0x10) { | ||
21542 | + printk("%s: eCPU did not start after boot (SOR), " | ||
21543 | + "is this fatal?\n", adev->ndev->name); | ||
21544 | + } | ||
21545 | +#endif | ||
21546 | + /* scan, if any, is stopped now, setting corresponding IRQ bit */ | ||
21547 | + adev->irq_status |= HOST_INT_SCAN_COMPLETE; | ||
21548 | + | ||
21549 | + acx_unlock(adev, flags); | ||
21550 | + | ||
21551 | + /* need to know radio type before fw load */ | ||
21552 | + /* Need to wait for arrival of this information in a loop, | ||
21553 | + * most probably since eCPU runs some init code from EEPROM | ||
21554 | + * (started burst read in reset_mac()) which also | ||
21555 | + * sets the radio type ID */ | ||
21556 | + | ||
21557 | + count = 0xffff; | ||
21558 | + do { | ||
21559 | + hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION); | ||
21560 | + if (!--count) { | ||
21561 | + msg = "eCPU didn't indicate radio type"; | ||
21562 | + goto end_fail; | ||
21563 | + } | ||
21564 | + cpu_relax(); | ||
21565 | + } while (!(hardware_info & 0xff00)); /* radio type still zero? */ | ||
21566 | + printk("ACX radio type 0x%02x\n", (hardware_info >> 8) & 0xff); | ||
21567 | + /* printk("DEBUG: count %d\n", count); */ | ||
21568 | + adev->form_factor = hardware_info & 0xff; | ||
21569 | + adev->radio_type = hardware_info >> 8; | ||
21570 | + | ||
21571 | + /* load the firmware */ | ||
21572 | + if (OK != acxmem_s_upload_fw(adev)) | ||
21573 | + goto end_fail; | ||
21574 | + | ||
21575 | + /* acx_s_msleep(10); this one really shouldn't be required */ | ||
21576 | + | ||
21577 | + /* now start eCPU by clearing bit */ | ||
21578 | + clear_regbits (adev, IO_ACX_ECPU_CTRL, 0x1); | ||
21579 | + log(L_DEBUG, "booted eCPU up and waiting for completion...\n"); | ||
21580 | + | ||
21581 | + /* Windows driver clears bit 0x200 in register 0x2A0 here */ | ||
21582 | + clear_regbits (adev, 0x2A0, 0x200); | ||
21583 | + | ||
21584 | + /* Windows driver sets bit 0x200 in ACX_GPIO_OUT here */ | ||
21585 | + set_regbits (adev, IO_ACX_GPIO_OUT, 0x200); | ||
21586 | + /* wait for eCPU bootup */ | ||
21587 | + if (OK != acxmem_s_verify_init(adev)) { | ||
21588 | + msg = "timeout waiting for eCPU. "; | ||
21589 | + goto end_fail; | ||
21590 | + } | ||
21591 | + log(L_DEBUG, "eCPU has woken up, card is ready to be configured\n"); | ||
21592 | + init_mboxes(adev); | ||
21593 | + acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0); | ||
21594 | + | ||
21595 | + /* test that EEPROM is readable */ | ||
21596 | + read_eeprom_area(adev); | ||
21597 | + | ||
21598 | + result = OK; | ||
21599 | + goto end; | ||
21600 | + | ||
21601 | +/* Finish error message. Indicate which function failed */ | ||
21602 | +end_unlock: | ||
21603 | + acx_unlock(adev, flags); | ||
21604 | +end_fail: | ||
21605 | + printk("acx: %sreset_dev() FAILED\n", msg); | ||
21606 | +end: | ||
21607 | + FN_EXIT1(result); | ||
21608 | + return result; | ||
21609 | +} | ||
21610 | + | ||
21611 | + | ||
21612 | +/*********************************************************************** | ||
21613 | +** acxmem_s_issue_cmd_timeo | ||
21614 | +** | ||
21615 | +** Sends command to fw, extract result | ||
21616 | +** | ||
21617 | +** NB: we do _not_ take lock inside, so be sure to not touch anything | ||
21618 | +** which may interfere with IRQ handler operation | ||
21619 | +** | ||
21620 | +** TODO: busy wait is a bit silly, so: | ||
21621 | +** 1) stop doing many iters - go to sleep after first | ||
21622 | +** 2) go to waitqueue based approach: wait, not poll! | ||
21623 | +*/ | ||
21624 | +#undef FUNC | ||
21625 | +#define FUNC "issue_cmd" | ||
21626 | + | ||
21627 | +#if !ACX_DEBUG | ||
21628 | +int | ||
21629 | +acxmem_s_issue_cmd_timeo( | ||
21630 | + acx_device_t *adev, | ||
21631 | + unsigned int cmd, | ||
21632 | + void *buffer, | ||
21633 | + unsigned buflen, | ||
21634 | + unsigned cmd_timeout) | ||
21635 | +{ | ||
21636 | +#else | ||
21637 | +int | ||
21638 | +acxmem_s_issue_cmd_timeo_debug( | ||
21639 | + acx_device_t *adev, | ||
21640 | + unsigned cmd, | ||
21641 | + void *buffer, | ||
21642 | + unsigned buflen, | ||
21643 | + unsigned cmd_timeout, | ||
21644 | + const char* cmdstr) | ||
21645 | +{ | ||
21646 | + unsigned long start = jiffies; | ||
21647 | +#endif | ||
21648 | + const char *devname; | ||
21649 | + unsigned counter; | ||
21650 | + u16 irqtype; | ||
21651 | + int i, j; | ||
21652 | + u8 *p; | ||
21653 | + u16 cmd_status; | ||
21654 | + unsigned long timeout; | ||
21655 | + | ||
21656 | + FN_ENTER; | ||
21657 | + | ||
21658 | + devname = adev->ndev->name; | ||
21659 | + if (!devname || !devname[0] || devname[4]=='%') | ||
21660 | + devname = "acx"; | ||
21661 | + | ||
21662 | + log(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n", | ||
21663 | + cmdstr, buflen, cmd_timeout, | ||
21664 | + buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1); | ||
21665 | + | ||
21666 | + if (!(adev->dev_state_mask & ACX_STATE_FW_LOADED)) { | ||
21667 | + printk("%s: "FUNC"(): firmware is not loaded yet, " | ||
21668 | + "cannot execute commands!\n", devname); | ||
21669 | + goto bad; | ||
21670 | + } | ||
21671 | + | ||
21672 | + if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) { | ||
21673 | + printk("input buffer (len=%u):\n", buflen); | ||
21674 | + acx_dump_bytes(buffer, buflen); | ||
21675 | + } | ||
21676 | + | ||
21677 | + /* wait for firmware to become idle for our command submission */ | ||
21678 | + timeout = HZ/5; | ||
21679 | + counter = (timeout * 1000 / HZ) - 1; /* in ms */ | ||
21680 | + timeout += jiffies; | ||
21681 | + do { | ||
21682 | + cmd_status = acxmem_read_cmd_type_status(adev); | ||
21683 | + /* Test for IDLE state */ | ||
21684 | + if (!cmd_status) | ||
21685 | + break; | ||
21686 | + if (counter % 8 == 0) { | ||
21687 | + if (time_after(jiffies, timeout)) { | ||
21688 | + counter = 0; | ||
21689 | + break; | ||
21690 | + } | ||
21691 | + /* we waited 8 iterations, no luck. Sleep 8 ms */ | ||
21692 | + acx_s_msleep(8); | ||
21693 | + } | ||
21694 | + } while (likely(--counter)); | ||
21695 | + | ||
21696 | + if (!counter) { | ||
21697 | + /* the card doesn't get idle, we're in trouble */ | ||
21698 | + printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n", | ||
21699 | + devname, cmd_status); | ||
21700 | +#if DUMP_IF_SLOW > 0 | ||
21701 | + dump_acxmem (adev, 0, 0x10000); | ||
21702 | + panic ("not idle"); | ||
21703 | +#endif | ||
21704 | + goto bad; | ||
21705 | + } else if (counter < 190) { /* if waited >10ms... */ | ||
21706 | + log(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. " | ||
21707 | + "Please report\n", 199 - counter); | ||
21708 | + } | ||
21709 | + | ||
21710 | + /* now write the parameters of the command if needed */ | ||
21711 | + if (buffer && buflen) { | ||
21712 | + /* if it's an INTERROGATE command, just pass the length | ||
21713 | + * of parameters to read, as data */ | ||
21714 | +#if CMD_DISCOVERY | ||
21715 | + if (cmd == ACX1xx_CMD_INTERROGATE) | ||
21716 | + memset_io(adev->cmd_area + 4, 0xAA, buflen); | ||
21717 | +#endif | ||
21718 | + /* | ||
21719 | + * slave memory version | ||
21720 | + */ | ||
21721 | + copy_to_slavemem (adev, (u32) (adev->cmd_area + 4), buffer, | ||
21722 | + (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen); | ||
21723 | + } | ||
21724 | + /* now write the actual command type */ | ||
21725 | + acxmem_write_cmd_type_status(adev, cmd, 0); | ||
21726 | + | ||
21727 | + /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */ | ||
21728 | + adev->irq_status &= ~HOST_INT_CMD_COMPLETE; | ||
21729 | + | ||
21730 | + /* execute command */ | ||
21731 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_CMD); | ||
21732 | + write_flush(adev); | ||
21733 | + | ||
21734 | + /* wait for firmware to process command */ | ||
21735 | + | ||
21736 | + /* Ensure nonzero and not too large timeout. | ||
21737 | + ** Also converts e.g. 100->99, 200->199 | ||
21738 | + ** which is nice but not essential */ | ||
21739 | + cmd_timeout = (cmd_timeout-1) | 1; | ||
21740 | + if (unlikely(cmd_timeout > 1199)) | ||
21741 | + cmd_timeout = 1199; | ||
21742 | + | ||
21743 | + /* we schedule away sometimes (timeout can be large) */ | ||
21744 | + counter = cmd_timeout; | ||
21745 | + timeout = jiffies + cmd_timeout * HZ / 1000; | ||
21746 | + do { | ||
21747 | + if (!adev->irqs_active) { /* IRQ disabled: poll */ | ||
21748 | + irqtype = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES); | ||
21749 | + if (irqtype & HOST_INT_CMD_COMPLETE) { | ||
21750 | + write_reg16(adev, IO_ACX_IRQ_ACK, | ||
21751 | + HOST_INT_CMD_COMPLETE); | ||
21752 | + break; | ||
21753 | + } | ||
21754 | + } else { /* Wait when IRQ will set the bit */ | ||
21755 | + irqtype = adev->irq_status; | ||
21756 | + if (irqtype & HOST_INT_CMD_COMPLETE) | ||
21757 | + break; | ||
21758 | + } | ||
21759 | + | ||
21760 | + if (counter % 8 == 0) { | ||
21761 | + if (time_after(jiffies, timeout)) { | ||
21762 | + counter = 0; | ||
21763 | + break; | ||
21764 | + } | ||
21765 | + /* we waited 8 iterations, no luck. Sleep 8 ms */ | ||
21766 | + acx_s_msleep(8); | ||
21767 | + } | ||
21768 | + } while (likely(--counter)); | ||
21769 | + | ||
21770 | + /* save state for debugging */ | ||
21771 | + cmd_status = acxmem_read_cmd_type_status(adev); | ||
21772 | + | ||
21773 | + /* put the card in IDLE state */ | ||
21774 | + acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0); | ||
21775 | + | ||
21776 | + if (!counter) { /* timed out! */ | ||
21777 | + printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. " | ||
21778 | + "irq bits:0x%04X irq_status:0x%04X timeout:%dms " | ||
21779 | + "cmd_status:%d (%s)\n", | ||
21780 | + devname, (adev->irqs_active) ? "waiting" : "polling", | ||
21781 | + irqtype, adev->irq_status, cmd_timeout, | ||
21782 | + cmd_status, acx_cmd_status_str(cmd_status)); | ||
21783 | + printk("%s: "FUNC"(): device irq status 0x%04x\n", | ||
21784 | + devname, read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES)); | ||
21785 | + printk("%s: "FUNC"(): IO_ACX_IRQ_MASK 0x%04x IO_ACX_FEMR 0x%04x\n", | ||
21786 | + devname, | ||
21787 | + read_reg16 (adev, IO_ACX_IRQ_MASK), | ||
21788 | + read_reg16 (adev, IO_ACX_FEMR)); | ||
21789 | + if (read_reg16 (adev, IO_ACX_IRQ_MASK) == 0xffff) { | ||
21790 | + printk ("acxmem: firmware probably hosed - reloading\n"); | ||
21791 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) | ||
21792 | + { | ||
21793 | + pm_message_t state; | ||
21794 | + acxmem_e_suspend (resume_pdev, state); | ||
21795 | + } | ||
21796 | +#else | ||
21797 | + acxmem_e_suspend (adev->dev, 0); | ||
21798 | +#endif | ||
21799 | + { | ||
21800 | + struct work_struct *notused; | ||
21801 | + fw_resumer (notused); | ||
21802 | + } | ||
21803 | + } | ||
21804 | + | ||
21805 | + goto bad; | ||
21806 | + } else if (cmd_timeout - counter > 30) { /* if waited >30ms... */ | ||
21807 | + log(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. " | ||
21808 | + "count:%d. Please report\n", | ||
21809 | + (adev->irqs_active) ? "waited" : "polled", | ||
21810 | + cmd_timeout - counter, counter); | ||
21811 | + } | ||
21812 | + | ||
21813 | + if (1 != cmd_status) { /* it is not a 'Success' */ | ||
21814 | + printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). " | ||
21815 | + "Took %dms of %d\n", | ||
21816 | + devname, cmd_status, acx_cmd_status_str(cmd_status), | ||
21817 | + cmd_timeout - counter, cmd_timeout); | ||
21818 | + /* zero out result buffer | ||
21819 | + * WARNING: this will trash stack in case of illegally large input | ||
21820 | + * length! */ | ||
21821 | + if (buflen > 388) { | ||
21822 | + /* | ||
21823 | + * 388 is maximum command length | ||
21824 | + */ | ||
21825 | + printk ("invalid length 0x%08x\n", buflen); | ||
21826 | + buflen = 388; | ||
21827 | + } | ||
21828 | + p = (u8 *) buffer; | ||
21829 | + for (i = 0; i < buflen; i+= 16) { | ||
21830 | + printk ("%04x:", i); | ||
21831 | + for (j = 0; (j < 16) && (i+j < buflen); j++) { | ||
21832 | + printk (" %02x", *p++); | ||
21833 | + } | ||
21834 | + printk ("\n"); | ||
21835 | + } | ||
21836 | + | ||
21837 | + if (buffer && buflen) | ||
21838 | + memset(buffer, 0, buflen); | ||
21839 | + goto bad; | ||
21840 | + } | ||
21841 | + | ||
21842 | + /* read in result parameters if needed */ | ||
21843 | + if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) { | ||
21844 | + copy_from_slavemem (adev, buffer, (u32) (adev->cmd_area + 4), buflen); | ||
21845 | + if (acx_debug & L_DEBUG) { | ||
21846 | + printk("output buffer (len=%u): ", buflen); | ||
21847 | + acx_dump_bytes(buffer, buflen); | ||
21848 | + } | ||
21849 | + } | ||
21850 | + | ||
21851 | +/* ok: */ | ||
21852 | + log(L_CTL, FUNC"(%s): took %ld jiffies to complete\n", | ||
21853 | + cmdstr, jiffies - start); | ||
21854 | + FN_EXIT1(OK); | ||
21855 | + return OK; | ||
21856 | + | ||
21857 | +bad: | ||
21858 | + /* Give enough info so that callers can avoid | ||
21859 | + ** printing their own diagnostic messages */ | ||
21860 | +#if ACX_DEBUG | ||
21861 | + printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr); | ||
21862 | +#else | ||
21863 | + printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd); | ||
21864 | +#endif | ||
21865 | + dump_stack(); | ||
21866 | + FN_EXIT1(NOT_OK); | ||
21867 | + return NOT_OK; | ||
21868 | +} | ||
21869 | + | ||
21870 | + | ||
21871 | +/*********************************************************************** | ||
21872 | +*/ | ||
21873 | +#if defined(NONESSENTIAL_FEATURES) | ||
21874 | +typedef struct device_id { | ||
21875 | + unsigned char id[6]; | ||
21876 | + char *descr; | ||
21877 | + char *type; | ||
21878 | +} device_id_t; | ||
21879 | + | ||
21880 | +static const device_id_t | ||
21881 | +device_ids[] = | ||
21882 | +{ | ||
21883 | + { | ||
21884 | + {'G', 'l', 'o', 'b', 'a', 'l'}, | ||
21885 | + NULL, | ||
21886 | + NULL, | ||
21887 | + }, | ||
21888 | + { | ||
21889 | + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, | ||
21890 | + "uninitialized", | ||
21891 | + "SpeedStream SS1021 or Gigafast WF721-AEX" | ||
21892 | + }, | ||
21893 | + { | ||
21894 | + {0x80, 0x81, 0x82, 0x83, 0x84, 0x85}, | ||
21895 | + "non-standard", | ||
21896 | + "DrayTek Vigor 520" | ||
21897 | + }, | ||
21898 | + { | ||
21899 | + {'?', '?', '?', '?', '?', '?'}, | ||
21900 | + "non-standard", | ||
21901 | + "Level One WPC-0200" | ||
21902 | + }, | ||
21903 | + { | ||
21904 | + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | ||
21905 | + "empty", | ||
21906 | + "DWL-650+ variant" | ||
21907 | + } | ||
21908 | +}; | ||
21909 | + | ||
21910 | +static void | ||
21911 | +acx_show_card_eeprom_id(acx_device_t *adev) | ||
21912 | +{ | ||
21913 | + unsigned char buffer[CARD_EEPROM_ID_SIZE]; | ||
21914 | + int i; | ||
21915 | + | ||
21916 | + memset(&buffer, 0, CARD_EEPROM_ID_SIZE); | ||
21917 | + /* use direct EEPROM access */ | ||
21918 | + for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) { | ||
21919 | + if (OK != acxmem_read_eeprom_byte(adev, | ||
21920 | + ACX100_EEPROM_ID_OFFSET + i, | ||
21921 | + &buffer[i])) { | ||
21922 | + printk("acx: reading EEPROM FAILED\n"); | ||
21923 | + break; | ||
21924 | + } | ||
21925 | + } | ||
21926 | + | ||
21927 | + for (i = 0; i < VEC_SIZE(device_ids); i++) { | ||
21928 | + if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) { | ||
21929 | + if (device_ids[i].descr) { | ||
21930 | + printk("acx: EEPROM card ID string check " | ||
21931 | + "found %s card ID: is this %s?\n", | ||
21932 | + device_ids[i].descr, device_ids[i].type); | ||
21933 | + } | ||
21934 | + break; | ||
21935 | + } | ||
21936 | + } | ||
21937 | + if (i == VEC_SIZE(device_ids)) { | ||
21938 | + printk("acx: EEPROM card ID string check found " | ||
21939 | + "unknown card: expected 'Global', got '%.*s\'. " | ||
21940 | + "Please report\n", CARD_EEPROM_ID_SIZE, buffer); | ||
21941 | + } | ||
21942 | +} | ||
21943 | +#endif /* NONESSENTIAL_FEATURES */ | ||
21944 | + | ||
21945 | +/*********************************************************************** | ||
21946 | +** acxmem_free_desc_queues | ||
21947 | +** | ||
21948 | +** Releases the queues that have been allocated, the | ||
21949 | +** others have been initialised to NULL so this | ||
21950 | +** function can be used if only part of the queues were allocated. | ||
21951 | +*/ | ||
21952 | + | ||
21953 | +void | ||
21954 | +acxmem_free_desc_queues(acx_device_t *adev) | ||
21955 | +{ | ||
21956 | +#define ACX_FREE_QUEUE(size, ptr, phyaddr) \ | ||
21957 | + if (ptr) { \ | ||
21958 | + kfree(ptr); \ | ||
21959 | + ptr = NULL; \ | ||
21960 | + size = 0; \ | ||
21961 | + } | ||
21962 | + | ||
21963 | + FN_ENTER; | ||
21964 | + | ||
21965 | + ACX_FREE_QUEUE(adev->txhostdesc_area_size, adev->txhostdesc_start, adev->txhostdesc_startphy); | ||
21966 | + ACX_FREE_QUEUE(adev->txbuf_area_size, adev->txbuf_start, adev->txbuf_startphy); | ||
21967 | + | ||
21968 | + adev->txdesc_start = NULL; | ||
21969 | + | ||
21970 | + ACX_FREE_QUEUE(adev->rxhostdesc_area_size, adev->rxhostdesc_start, adev->rxhostdesc_startphy); | ||
21971 | + ACX_FREE_QUEUE(adev->rxbuf_area_size, adev->rxbuf_start, adev->rxbuf_startphy); | ||
21972 | + | ||
21973 | + adev->rxdesc_start = NULL; | ||
21974 | + | ||
21975 | + FN_EXIT0; | ||
21976 | +} | ||
21977 | + | ||
21978 | + | ||
21979 | +/*********************************************************************** | ||
21980 | +** acxmem_s_delete_dma_regions | ||
21981 | +*/ | ||
21982 | +static void | ||
21983 | +acxmem_s_delete_dma_regions(acx_device_t *adev) | ||
21984 | +{ | ||
21985 | + unsigned long flags; | ||
21986 | + | ||
21987 | + FN_ENTER; | ||
21988 | + /* disable radio Tx/Rx. Shouldn't we use the firmware commands | ||
21989 | + * here instead? Or are we that much down the road that it's no | ||
21990 | + * longer possible here? */ | ||
21991 | + /* | ||
21992 | + * slave memory interface really doesn't like this. | ||
21993 | + */ | ||
21994 | + /* | ||
21995 | + write_reg16(adev, IO_ACX_ENABLE, 0); | ||
21996 | + */ | ||
21997 | + | ||
21998 | + acx_s_msleep(100); | ||
21999 | + | ||
22000 | + acx_lock(adev, flags); | ||
22001 | + acxmem_free_desc_queues(adev); | ||
22002 | + acx_unlock(adev, flags); | ||
22003 | + | ||
22004 | + FN_EXIT0; | ||
22005 | +} | ||
22006 | + | ||
22007 | + | ||
22008 | +/*********************************************************************** | ||
22009 | +** acxmem_e_probe | ||
22010 | +** | ||
22011 | +** Probe routine called when a PCI device w/ matching ID is found. | ||
22012 | +** Here's the sequence: | ||
22013 | +** - Allocate the PCI resources. | ||
22014 | +** - Read the PCMCIA attribute memory to make sure we have a WLAN card | ||
22015 | +** - Reset the MAC | ||
22016 | +** - Initialize the dev and wlan data | ||
22017 | +** - Initialize the MAC | ||
22018 | +** | ||
22019 | +** pdev - ptr to pci device structure containing info about pci configuration | ||
22020 | +** id - ptr to the device id entry that matched this device | ||
22021 | +*/ | ||
22022 | +static const u16 | ||
22023 | +IO_ACX100[] = | ||
22024 | +{ | ||
22025 | + 0x0000, /* IO_ACX_SOFT_RESET */ | ||
22026 | + | ||
22027 | + 0x0014, /* IO_ACX_SLV_MEM_ADDR */ | ||
22028 | + 0x0018, /* IO_ACX_SLV_MEM_DATA */ | ||
22029 | + 0x001c, /* IO_ACX_SLV_MEM_CTL */ | ||
22030 | + 0x0020, /* IO_ACX_SLV_END_CTL */ | ||
22031 | + | ||
22032 | + 0x0034, /* IO_ACX_FEMR */ | ||
22033 | + | ||
22034 | + 0x007c, /* IO_ACX_INT_TRIG */ | ||
22035 | + 0x0098, /* IO_ACX_IRQ_MASK */ | ||
22036 | + 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */ | ||
22037 | + 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */ | ||
22038 | + 0x00ac, /* IO_ACX_IRQ_ACK */ | ||
22039 | + 0x00b0, /* IO_ACX_HINT_TRIG */ | ||
22040 | + | ||
22041 | + 0x0104, /* IO_ACX_ENABLE */ | ||
22042 | + | ||
22043 | + 0x0250, /* IO_ACX_EEPROM_CTL */ | ||
22044 | + 0x0254, /* IO_ACX_EEPROM_ADDR */ | ||
22045 | + 0x0258, /* IO_ACX_EEPROM_DATA */ | ||
22046 | + 0x025c, /* IO_ACX_EEPROM_CFG */ | ||
22047 | + | ||
22048 | + 0x0268, /* IO_ACX_PHY_ADDR */ | ||
22049 | + 0x026c, /* IO_ACX_PHY_DATA */ | ||
22050 | + 0x0270, /* IO_ACX_PHY_CTL */ | ||
22051 | + | ||
22052 | + 0x0290, /* IO_ACX_GPIO_OE */ | ||
22053 | + | ||
22054 | + 0x0298, /* IO_ACX_GPIO_OUT */ | ||
22055 | + | ||
22056 | + 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */ | ||
22057 | + 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */ | ||
22058 | + 0x02ac, /* IO_ACX_EEPROM_INFORMATION */ | ||
22059 | + | ||
22060 | + 0x02d0, /* IO_ACX_EE_START */ | ||
22061 | + 0x02d4, /* IO_ACX_SOR_CFG */ | ||
22062 | + 0x02d8 /* IO_ACX_ECPU_CTRL */ | ||
22063 | +}; | ||
22064 | + | ||
22065 | +static const u16 | ||
22066 | +IO_ACX111[] = | ||
22067 | +{ | ||
22068 | + 0x0000, /* IO_ACX_SOFT_RESET */ | ||
22069 | + | ||
22070 | + 0x0014, /* IO_ACX_SLV_MEM_ADDR */ | ||
22071 | + 0x0018, /* IO_ACX_SLV_MEM_DATA */ | ||
22072 | + 0x001c, /* IO_ACX_SLV_MEM_CTL */ | ||
22073 | + 0x0020, /* IO_ACX_SLV_MEM_CP */ | ||
22074 | + | ||
22075 | + 0x0034, /* IO_ACX_FEMR */ | ||
22076 | + | ||
22077 | + 0x00b4, /* IO_ACX_INT_TRIG */ | ||
22078 | + 0x00d4, /* IO_ACX_IRQ_MASK */ | ||
22079 | + /* we do mean NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */ | ||
22080 | + 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */ | ||
22081 | + 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */ | ||
22082 | + 0x00e8, /* IO_ACX_IRQ_ACK */ | ||
22083 | + 0x00ec, /* IO_ACX_HINT_TRIG */ | ||
22084 | + | ||
22085 | + 0x01d0, /* IO_ACX_ENABLE */ | ||
22086 | + | ||
22087 | + 0x0338, /* IO_ACX_EEPROM_CTL */ | ||
22088 | + 0x033c, /* IO_ACX_EEPROM_ADDR */ | ||
22089 | + 0x0340, /* IO_ACX_EEPROM_DATA */ | ||
22090 | + 0x0344, /* IO_ACX_EEPROM_CFG */ | ||
22091 | + | ||
22092 | + 0x0350, /* IO_ACX_PHY_ADDR */ | ||
22093 | + 0x0354, /* IO_ACX_PHY_DATA */ | ||
22094 | + 0x0358, /* IO_ACX_PHY_CTL */ | ||
22095 | + | ||
22096 | + 0x0374, /* IO_ACX_GPIO_OE */ | ||
22097 | + | ||
22098 | + 0x037c, /* IO_ACX_GPIO_OUT */ | ||
22099 | + | ||
22100 | + 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */ | ||
22101 | + 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */ | ||
22102 | + 0x0390, /* IO_ACX_EEPROM_INFORMATION */ | ||
22103 | + | ||
22104 | + 0x0100, /* IO_ACX_EE_START */ | ||
22105 | + 0x0104, /* IO_ACX_SOR_CFG */ | ||
22106 | + 0x0108, /* IO_ACX_ECPU_CTRL */ | ||
22107 | +}; | ||
22108 | + | ||
22109 | +static void | ||
22110 | +dummy_netdev_init(struct net_device *ndev) {} | ||
22111 | + | ||
22112 | +/* | ||
22113 | + * Most of the acx specific pieces of hardware reset. | ||
22114 | + */ | ||
22115 | +static int | ||
22116 | +acxmem_complete_hw_reset (acx_device_t *adev) | ||
22117 | +{ | ||
22118 | + acx111_ie_configoption_t co; | ||
22119 | + | ||
22120 | + /* NB: read_reg() reads may return bogus data before reset_dev(), | ||
22121 | + * since the firmware which directly controls large parts of the I/O | ||
22122 | + * registers isn't initialized yet. | ||
22123 | + * acx100 seems to be more affected than acx111 */ | ||
22124 | + if (OK != acxmem_s_reset_dev (adev)) | ||
22125 | + return -1; | ||
22126 | + | ||
22127 | + if (IS_ACX100(adev)) { | ||
22128 | + /* ACX100: configopt struct in cmd mailbox - directly after reset */ | ||
22129 | + copy_from_slavemem (adev, (u8*) &co, (u32) adev->cmd_area, sizeof (co)); | ||
22130 | + } | ||
22131 | + | ||
22132 | + if (OK != acx_s_init_mac(adev)) | ||
22133 | + return -3; | ||
22134 | + | ||
22135 | + if (IS_ACX111(adev)) { | ||
22136 | + /* ACX111: configopt struct needs to be queried after full init */ | ||
22137 | + acx_s_interrogate(adev, &co, ACX111_IE_CONFIG_OPTIONS); | ||
22138 | + } | ||
22139 | + | ||
22140 | + /* | ||
22141 | + * Set up transmit buffer administration | ||
22142 | + */ | ||
22143 | + init_acx_txbuf (adev); | ||
22144 | + | ||
22145 | + /* | ||
22146 | + * Windows driver writes 0x01000000 to register 0x288, RADIO_CTL, if the form factor | ||
22147 | + * is 3. It also write protects the EEPROM by writing 1<<9 to GPIO_OUT | ||
22148 | + */ | ||
22149 | + if (adev->form_factor == 3) { | ||
22150 | + set_regbits (adev, 0x288, 0x01000000); | ||
22151 | + set_regbits (adev, 0x298, 1<<9); | ||
22152 | + } | ||
22153 | + | ||
22154 | +/* TODO: merge them into one function, they are called just once and are the same for pci & usb */ | ||
22155 | + if (OK != acxmem_read_eeprom_byte(adev, 0x05, &adev->eeprom_version)) | ||
22156 | + return -2; | ||
22157 | + | ||
22158 | + acx_s_parse_configoption(adev, &co); | ||
22159 | + acx_s_get_firmware_version(adev); /* needs to be after acx_s_init_mac() */ | ||
22160 | + acx_display_hardware_details(adev); | ||
22161 | + | ||
22162 | + return 0; | ||
22163 | +} | ||
22164 | + | ||
22165 | +static int __devinit | ||
22166 | +acxmem_e_probe(struct platform_device *pdev) | ||
22167 | +{ | ||
22168 | + struct acx_hardware_data *hwdata = pdev->dev.platform_data; | ||
22169 | + acx_device_t *adev = NULL; | ||
22170 | + struct net_device *ndev = NULL; | ||
22171 | + const char *chip_name; | ||
22172 | + int result = -EIO; | ||
22173 | + int err; | ||
22174 | + int i; | ||
22175 | + unsigned long addr_size=0; | ||
22176 | + u8 chip_type; | ||
22177 | + | ||
22178 | + FN_ENTER; | ||
22179 | + (void) hwdata->start_hw(); | ||
22180 | + | ||
22181 | + /* FIXME: prism54 calls pci_set_mwi() here, | ||
22182 | + * should we do/support the same? */ | ||
22183 | + | ||
22184 | + /* chiptype is u8 but id->driver_data is ulong | ||
22185 | + ** Works for now (possible values are 1 and 2) */ | ||
22186 | + chip_type = CHIPTYPE_ACX100; | ||
22187 | + /* acx100 and acx111 have different PCI memory regions */ | ||
22188 | + if (chip_type == CHIPTYPE_ACX100) { | ||
22189 | + chip_name = "ACX100"; | ||
22190 | + } else if (chip_type == CHIPTYPE_ACX111) { | ||
22191 | + chip_name = "ACX111"; | ||
22192 | + } else { | ||
22193 | + printk("acx: unknown chip type 0x%04X\n", chip_type); | ||
22194 | + goto fail_unknown_chiptype; | ||
22195 | + } | ||
22196 | + | ||
22197 | + printk("acx: found %s-based wireless network card\n", chip_name); | ||
22198 | + log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug); | ||
22199 | + | ||
22200 | + ndev = alloc_netdev(sizeof(*adev), "wlan%d", dummy_netdev_init); | ||
22201 | + /* (NB: memsets to 0 entire area) */ | ||
22202 | + if (!ndev) { | ||
22203 | + printk("acx: no memory for netdevice struct\n"); | ||
22204 | + goto fail_alloc_netdev; | ||
22205 | + } | ||
22206 | + | ||
22207 | + platform_set_drvdata (pdev, ndev); | ||
22208 | + | ||
22209 | + ether_setup(ndev); | ||
22210 | + | ||
22211 | + /* | ||
22212 | + * use platform_data resources that were provided | ||
22213 | + */ | ||
22214 | + ndev->irq = 0; | ||
22215 | + for (i=0; i<pdev->num_resources; i++) { | ||
22216 | + if (pdev->resource[i].flags == IORESOURCE_IRQ) { | ||
22217 | + ndev->irq = pdev->resource[i].start; | ||
22218 | + } | ||
22219 | + else if (pdev->resource[i].flags == IORESOURCE_MEM) { | ||
22220 | + ndev->base_addr = pdev->resource[i].start; | ||
22221 | + addr_size = pdev->resource[i].end - pdev->resource[i].start; | ||
22222 | + } | ||
22223 | + } | ||
22224 | + if (addr_size == 0 || ndev->irq == 0) | ||
22225 | + goto fail_hw_params; | ||
22226 | + ndev->open = &acxmem_e_open; | ||
22227 | + ndev->stop = &acxmem_e_close; | ||
22228 | + pdev->dev.release = &acxmem_e_release; | ||
22229 | + ndev->hard_start_xmit = &acx_i_start_xmit; | ||
22230 | + ndev->get_stats = &acx_e_get_stats; | ||
22231 | +#if IW_HANDLER_VERSION <= 5 | ||
22232 | + ndev->get_wireless_stats = &acx_e_get_wireless_stats; | ||
22233 | +#endif | ||
22234 | + ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; | ||
22235 | + ndev->set_multicast_list = &acxmem_i_set_multicast_list; | ||
22236 | + ndev->tx_timeout = &acxmem_i_tx_timeout; | ||
22237 | + ndev->change_mtu = &acx_e_change_mtu; | ||
22238 | + ndev->watchdog_timeo = 4 * HZ; | ||
22239 | + | ||
22240 | + adev = ndev2adev(ndev); | ||
22241 | + spin_lock_init(&adev->lock); /* initial state: unlocked */ | ||
22242 | + spin_lock_init(&adev->txbuf_lock); | ||
22243 | + /* We do not start with downed sem: we want PARANOID_LOCKING to work */ | ||
22244 | + sema_init(&adev->sem, 1); /* initial state: 1 (upped) */ | ||
22245 | + /* since nobody can see new netdev yet, we can as well | ||
22246 | + ** just _presume_ that we're under sem (instead of actually taking it): */ | ||
22247 | + /* acx_sem_lock(adev); */ | ||
22248 | + adev->dev = &pdev->dev; | ||
22249 | + adev->ndev = ndev; | ||
22250 | + adev->dev_type = DEVTYPE_MEM; | ||
22251 | + adev->chip_type = chip_type; | ||
22252 | + adev->chip_name = chip_name; | ||
22253 | + adev->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111; | ||
22254 | + adev->membase = (volatile u32 *) ndev->base_addr; | ||
22255 | + adev->iobase = (volatile u32 *) ioremap_nocache (ndev->base_addr, addr_size); | ||
22256 | + /* to find crashes due to weird driver access | ||
22257 | + * to unconfigured interface (ifup) */ | ||
22258 | + adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead; | ||
22259 | + | ||
22260 | +#if defined(NONESSENTIAL_FEATURES) | ||
22261 | + acx_show_card_eeprom_id(adev); | ||
22262 | +#endif /* NONESSENTIAL_FEATURES */ | ||
22263 | + | ||
22264 | +#ifdef SET_MODULE_OWNER | ||
22265 | + SET_MODULE_OWNER(ndev); | ||
22266 | +#endif | ||
22267 | + SET_NETDEV_DEV(ndev, &pdev->dev); | ||
22268 | + | ||
22269 | + log(L_IRQ|L_INIT, "using IRQ %d\n", ndev->irq); | ||
22270 | + | ||
22271 | + /* ok, pci setup is finished, now start initializing the card */ | ||
22272 | + | ||
22273 | + if (OK != acxmem_complete_hw_reset (adev)) | ||
22274 | + goto fail_reset; | ||
22275 | + | ||
22276 | + /* | ||
22277 | + * Set up default things for most of the card settings. | ||
22278 | + */ | ||
22279 | + acx_s_set_defaults(adev); | ||
22280 | + | ||
22281 | + /* Register the card, AFTER everything else has been set up, | ||
22282 | + * since otherwise an ioctl could step on our feet due to | ||
22283 | + * firmware operations happening in parallel or uninitialized data */ | ||
22284 | + err = register_netdev(ndev); | ||
22285 | + if (OK != err) { | ||
22286 | + printk("acx: register_netdev() FAILED: %d\n", err); | ||
22287 | + goto fail_register_netdev; | ||
22288 | + } | ||
22289 | + | ||
22290 | + acx_proc_register_entries(ndev); | ||
22291 | + | ||
22292 | + /* Now we have our device, so make sure the kernel doesn't try | ||
22293 | + * to send packets even though we're not associated to a network yet */ | ||
22294 | + acx_stop_queue(ndev, "on probe"); | ||
22295 | + acx_carrier_off(ndev, "on probe"); | ||
22296 | + | ||
22297 | + /* | ||
22298 | + * Set up a default monitor type so that poor combinations of initialization | ||
22299 | + * sequences in monitor mode don't end up destroying the hardware type. | ||
22300 | + */ | ||
22301 | + adev->monitor_type = ARPHRD_ETHER; | ||
22302 | + | ||
22303 | + /* | ||
22304 | + * Register to receive inetaddr notifier changes. This will allow us to | ||
22305 | + * catch if the user changes the MAC address of the interface. | ||
22306 | + */ | ||
22307 | + register_netdevice_notifier(&acx_netdev_notifier); | ||
22308 | + | ||
22309 | + /* after register_netdev() userspace may start working with dev | ||
22310 | + * (in particular, on other CPUs), we only need to up the sem */ | ||
22311 | + /* acx_sem_unlock(adev); */ | ||
22312 | + | ||
22313 | + printk("acx "ACX_RELEASE": net device %s, driver compiled " | ||
22314 | + "against wireless extensions %d and Linux %s\n", | ||
22315 | + ndev->name, WIRELESS_EXT, UTS_RELEASE); | ||
22316 | + | ||
22317 | +#if CMD_DISCOVERY | ||
22318 | + great_inquisitor(adev); | ||
22319 | +#endif | ||
22320 | + | ||
22321 | + result = OK; | ||
22322 | + goto done; | ||
22323 | + | ||
22324 | + /* error paths: undo everything in reverse order... */ | ||
22325 | + | ||
22326 | +fail_register_netdev: | ||
22327 | + | ||
22328 | + acxmem_s_delete_dma_regions(adev); | ||
22329 | + | ||
22330 | +fail_reset: | ||
22331 | +fail_hw_params: | ||
22332 | + free_netdev(ndev); | ||
22333 | +fail_alloc_netdev: | ||
22334 | +fail_unknown_chiptype: | ||
22335 | + | ||
22336 | + | ||
22337 | +done: | ||
22338 | + FN_EXIT1(result); | ||
22339 | + return result; | ||
22340 | +} | ||
22341 | + | ||
22342 | + | ||
22343 | +/*********************************************************************** | ||
22344 | +** acxmem_e_remove | ||
22345 | +** | ||
22346 | +** Shut device down (if not hot unplugged) | ||
22347 | +** and deallocate PCI resources for the acx chip. | ||
22348 | +** | ||
22349 | +** pdev - ptr to PCI device structure containing info about pci configuration | ||
22350 | +*/ | ||
22351 | +static int __devexit | ||
22352 | +acxmem_e_remove(struct platform_device *pdev) | ||
22353 | +{ | ||
22354 | + struct acx_hardware_data *hwdata = pdev->dev.platform_data; | ||
22355 | + struct net_device *ndev; | ||
22356 | + acx_device_t *adev; | ||
22357 | + unsigned long flags; | ||
22358 | + | ||
22359 | + FN_ENTER; | ||
22360 | + | ||
22361 | + ndev = (struct net_device*) platform_get_drvdata(pdev); | ||
22362 | + if (!ndev) { | ||
22363 | + log(L_DEBUG, "%s: card is unused. Skipping any release code\n", | ||
22364 | + __func__); | ||
22365 | + goto end; | ||
22366 | + } | ||
22367 | + | ||
22368 | + adev = ndev2adev(ndev); | ||
22369 | + | ||
22370 | + /* If device wasn't hot unplugged... */ | ||
22371 | + if (adev_present(adev)) { | ||
22372 | + | ||
22373 | + acx_sem_lock(adev); | ||
22374 | + | ||
22375 | + /* disable both Tx and Rx to shut radio down properly */ | ||
22376 | + acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0); | ||
22377 | + acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0); | ||
22378 | + | ||
22379 | +#ifdef REDUNDANT | ||
22380 | + /* put the eCPU to sleep to save power | ||
22381 | + * Halting is not possible currently, | ||
22382 | + * since not supported by all firmware versions */ | ||
22383 | + acx_s_issue_cmd(adev, ACX100_CMD_SLEEP, NULL, 0); | ||
22384 | +#endif | ||
22385 | + acx_lock(adev, flags); | ||
22386 | + | ||
22387 | + /* disable power LED to save power :-) */ | ||
22388 | + log(L_INIT, "switching off power LED to save power\n"); | ||
22389 | + acxmem_l_power_led(adev, 0); | ||
22390 | + | ||
22391 | + /* stop our eCPU */ | ||
22392 | + if (IS_ACX111(adev)) { | ||
22393 | + /* FIXME: does this actually keep halting the eCPU? | ||
22394 | + * I don't think so... | ||
22395 | + */ | ||
22396 | + acxmem_l_reset_mac(adev); | ||
22397 | + } else { | ||
22398 | + u16 temp; | ||
22399 | + | ||
22400 | + /* halt eCPU */ | ||
22401 | + temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1; | ||
22402 | + write_reg16(adev, IO_ACX_ECPU_CTRL, temp); | ||
22403 | + write_flush(adev); | ||
22404 | + } | ||
22405 | + | ||
22406 | + acx_unlock(adev, flags); | ||
22407 | + | ||
22408 | + acx_sem_unlock(adev); | ||
22409 | + } | ||
22410 | + | ||
22411 | + | ||
22412 | + /* | ||
22413 | + * Unregister the notifier chain | ||
22414 | + */ | ||
22415 | + unregister_netdevice_notifier(&acx_netdev_notifier); | ||
22416 | + | ||
22417 | + /* unregister the device to not let the kernel | ||
22418 | + * (e.g. ioctls) access a half-deconfigured device | ||
22419 | + * NB: this will cause acxmem_e_close() to be called, | ||
22420 | + * thus we shouldn't call it under sem! */ | ||
22421 | + log(L_INIT, "removing device %s\n", ndev->name); | ||
22422 | + unregister_netdev(ndev); | ||
22423 | + | ||
22424 | + /* unregister_netdev ensures that no references to us left. | ||
22425 | + * For paranoid reasons we continue to follow the rules */ | ||
22426 | + acx_sem_lock(adev); | ||
22427 | + | ||
22428 | + if (adev->dev_state_mask & ACX_STATE_IFACE_UP) { | ||
22429 | + acxmem_s_down(ndev); | ||
22430 | + CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
22431 | + } | ||
22432 | + | ||
22433 | + acx_proc_unregister_entries(ndev); | ||
22434 | + | ||
22435 | + acxmem_s_delete_dma_regions(adev); | ||
22436 | + | ||
22437 | + /* finally, clean up PCI bus state */ | ||
22438 | + if (adev->iobase) iounmap((void *)adev->iobase); | ||
22439 | + | ||
22440 | + acx_sem_unlock(adev); | ||
22441 | + | ||
22442 | + /* Free netdev (quite late, | ||
22443 | + * since otherwise we might get caught off-guard | ||
22444 | + * by a netdev timeout handler execution | ||
22445 | + * expecting to see a working dev...) */ | ||
22446 | + free_netdev(ndev); | ||
22447 | + | ||
22448 | + (void) hwdata->stop_hw(); | ||
22449 | + | ||
22450 | + printk ("e_remove done\n"); | ||
22451 | +end: | ||
22452 | + FN_EXIT0; | ||
22453 | + | ||
22454 | + return 0; | ||
22455 | +} | ||
22456 | + | ||
22457 | + | ||
22458 | +/*********************************************************************** | ||
22459 | +** TODO: PM code needs to be fixed / debugged / tested. | ||
22460 | +*/ | ||
22461 | +#ifdef CONFIG_PM | ||
22462 | +static int | ||
22463 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) | ||
22464 | +acxmem_e_suspend(struct platform_device *pdev, pm_message_t state) | ||
22465 | +#else | ||
22466 | +acxmem_e_suspend(struct device *pdev, u32 state) | ||
22467 | +#endif | ||
22468 | +{ | ||
22469 | + struct net_device *ndev = platform_get_drvdata(pdev); | ||
22470 | + acx_device_t *adev; | ||
22471 | + struct acx_hardware_data *hwdata; | ||
22472 | + | ||
22473 | + FN_ENTER; | ||
22474 | + printk("acx: suspend handler is experimental!\n"); | ||
22475 | + printk("sus: dev %p\n", ndev); | ||
22476 | + | ||
22477 | + if (!netif_running(ndev)) | ||
22478 | + goto end; | ||
22479 | + | ||
22480 | + adev = ndev2adev(ndev); | ||
22481 | + printk("sus: adev %p\n", adev); | ||
22482 | + | ||
22483 | + hwdata = adev->dev->platform_data; | ||
22484 | + | ||
22485 | + acx_sem_lock(adev); | ||
22486 | + | ||
22487 | + netif_device_detach(ndev); /* this one cannot sleep */ | ||
22488 | + acxmem_s_down(ndev); | ||
22489 | + /* down() does not set it to 0xffff, but here we really want that */ | ||
22490 | + write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); | ||
22491 | + write_reg16(adev, IO_ACX_FEMR, 0x0); | ||
22492 | + acxmem_s_delete_dma_regions(adev); | ||
22493 | + | ||
22494 | + /* | ||
22495 | + * Turn the ACX chip off. | ||
22496 | + */ | ||
22497 | + hwdata->stop_hw(); | ||
22498 | + | ||
22499 | + acx_sem_unlock(adev); | ||
22500 | +end: | ||
22501 | + FN_EXIT0; | ||
22502 | + return OK; | ||
22503 | +} | ||
22504 | + | ||
22505 | + | ||
22506 | + | ||
22507 | +static void | ||
22508 | +fw_resumer(struct work_struct *notused) | ||
22509 | +{ | ||
22510 | + struct platform_device *pdev = resume_pdev; | ||
22511 | + struct net_device *ndev = platform_get_drvdata(pdev); | ||
22512 | + acx_device_t *adev; | ||
22513 | + struct acx_hardware_data *hwdata; | ||
22514 | + | ||
22515 | + printk("acx: resume handler is experimental!\n"); | ||
22516 | + printk("rsm: got dev %p\n", ndev); | ||
22517 | + | ||
22518 | + if (!netif_running(ndev)) | ||
22519 | + return; | ||
22520 | + | ||
22521 | + adev = ndev2adev(ndev); | ||
22522 | + printk("rsm: got adev %p\n", adev); | ||
22523 | + | ||
22524 | + acx_sem_lock(adev); | ||
22525 | + | ||
22526 | + hwdata = adev->dev->platform_data; | ||
22527 | + | ||
22528 | + /* | ||
22529 | + * Turn on the ACX. | ||
22530 | + */ | ||
22531 | + hwdata->start_hw(); | ||
22532 | + | ||
22533 | + acxmem_complete_hw_reset (adev); | ||
22534 | + | ||
22535 | + /* | ||
22536 | + * done by acx_s_set_defaults for initial startup | ||
22537 | + */ | ||
22538 | + acxmem_set_interrupt_mask(adev); | ||
22539 | + | ||
22540 | + printk ("rsm: bringing up interface\n"); | ||
22541 | + SET_BIT (adev->set_mask, GETSET_ALL); | ||
22542 | + acxmem_s_up(ndev); | ||
22543 | + printk("rsm: acx up done\n"); | ||
22544 | + | ||
22545 | + /* now even reload all card parameters as they were before suspend, | ||
22546 | + * and possibly be back in the network again already :-) | ||
22547 | + */ | ||
22548 | + /* - most settings updated in acxmem_s_up() | ||
22549 | + if (ACX_STATE_IFACE_UP & adev->dev_state_mask) { | ||
22550 | + adev->set_mask = GETSET_ALL; | ||
22551 | + acx_s_update_card_settings(adev); | ||
22552 | + printk("rsm: settings updated\n"); | ||
22553 | + } | ||
22554 | + */ | ||
22555 | + netif_device_attach(ndev); | ||
22556 | + printk("rsm: device attached\n"); | ||
22557 | + | ||
22558 | + acx_sem_unlock(adev); | ||
22559 | +} | ||
22560 | + | ||
22561 | +DECLARE_WORK( fw_resume_work, fw_resumer ); | ||
22562 | + | ||
22563 | +static int | ||
22564 | +acxmem_e_resume(struct platform_device *pdev) | ||
22565 | +{ | ||
22566 | + FN_ENTER; | ||
22567 | + | ||
22568 | + resume_pdev = pdev; | ||
22569 | + schedule_work( &fw_resume_work ); | ||
22570 | + | ||
22571 | + FN_EXIT0; | ||
22572 | + return OK; | ||
22573 | +} | ||
22574 | +#endif /* CONFIG_PM */ | ||
22575 | + | ||
22576 | + | ||
22577 | +/*********************************************************************** | ||
22578 | +** acxmem_s_up | ||
22579 | +** | ||
22580 | +** This function is called by acxmem_e_open (when ifconfig sets the device as up) | ||
22581 | +** | ||
22582 | +** Side effects: | ||
22583 | +** - Enables on-card interrupt requests | ||
22584 | +** - calls acx_s_start | ||
22585 | +*/ | ||
22586 | + | ||
22587 | +static void | ||
22588 | +enable_acx_irq(acx_device_t *adev) | ||
22589 | +{ | ||
22590 | + FN_ENTER; | ||
22591 | + write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask); | ||
22592 | + write_reg16(adev, IO_ACX_FEMR, 0x8000); | ||
22593 | + adev->irqs_active = 1; | ||
22594 | + FN_EXIT0; | ||
22595 | +} | ||
22596 | + | ||
22597 | +static void | ||
22598 | +acxmem_s_up(struct net_device *ndev) | ||
22599 | +{ | ||
22600 | + acx_device_t *adev = ndev2adev(ndev); | ||
22601 | + unsigned long flags; | ||
22602 | + | ||
22603 | + FN_ENTER; | ||
22604 | + | ||
22605 | + acx_lock(adev, flags); | ||
22606 | + enable_acx_irq(adev); | ||
22607 | + acx_unlock(adev, flags); | ||
22608 | + | ||
22609 | + /* acx fw < 1.9.3.e has a hardware timer, and older drivers | ||
22610 | + ** used to use it. But we don't do that anymore, our OS | ||
22611 | + ** has reliable software timers */ | ||
22612 | + init_timer(&adev->mgmt_timer); | ||
22613 | + adev->mgmt_timer.function = acx_i_timer; | ||
22614 | + adev->mgmt_timer.data = (unsigned long)adev; | ||
22615 | + | ||
22616 | + /* Need to set ACX_STATE_IFACE_UP first, or else | ||
22617 | + ** timer won't be started by acx_set_status() */ | ||
22618 | + SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
22619 | + switch (adev->mode) { | ||
22620 | + case ACX_MODE_0_ADHOC: | ||
22621 | + case ACX_MODE_2_STA: | ||
22622 | + /* actual scan cmd will happen in start() */ | ||
22623 | + acx_set_status(adev, ACX_STATUS_1_SCANNING); break; | ||
22624 | + case ACX_MODE_3_AP: | ||
22625 | + case ACX_MODE_MONITOR: | ||
22626 | + acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); break; | ||
22627 | + } | ||
22628 | + | ||
22629 | + acx_s_start(adev); | ||
22630 | + | ||
22631 | + FN_EXIT0; | ||
22632 | +} | ||
22633 | + | ||
22634 | + | ||
22635 | +/*********************************************************************** | ||
22636 | +** acxmem_s_down | ||
22637 | +** | ||
22638 | +** This disables the netdevice | ||
22639 | +** | ||
22640 | +** Side effects: | ||
22641 | +** - disables on-card interrupt request | ||
22642 | +*/ | ||
22643 | + | ||
22644 | +static void | ||
22645 | +disable_acx_irq(acx_device_t *adev) | ||
22646 | +{ | ||
22647 | + FN_ENTER; | ||
22648 | + | ||
22649 | + /* I guess mask is not 0xffff because acx100 won't signal | ||
22650 | + ** cmd completion then (needed for ifup). | ||
22651 | + ** Someone with acx100 please confirm */ | ||
22652 | + write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask_off); | ||
22653 | + write_reg16(adev, IO_ACX_FEMR, 0x0); | ||
22654 | + adev->irqs_active = 0; | ||
22655 | + FN_EXIT0; | ||
22656 | +} | ||
22657 | + | ||
22658 | +static void | ||
22659 | +acxmem_s_down(struct net_device *ndev) | ||
22660 | +{ | ||
22661 | + acx_device_t *adev = ndev2adev(ndev); | ||
22662 | + unsigned long flags; | ||
22663 | + | ||
22664 | + FN_ENTER; | ||
22665 | + | ||
22666 | + /* Disable IRQs first, so that IRQs cannot race with us */ | ||
22667 | + /* then wait until interrupts have finished executing on other CPUs */ | ||
22668 | + acx_lock(adev, flags); | ||
22669 | + disable_acx_irq(adev); | ||
22670 | + synchronize_irq(adev->pdev->irq); | ||
22671 | + acx_unlock(adev, flags); | ||
22672 | + | ||
22673 | + /* we really don't want to have an asynchronous tasklet disturb us | ||
22674 | + ** after something vital for its job has been shut down, so | ||
22675 | + ** end all remaining work now. | ||
22676 | + ** | ||
22677 | + ** NB: carrier_off (done by set_status below) would lead to | ||
22678 | + ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK(). | ||
22679 | + ** That's why we do FLUSH first. | ||
22680 | + ** | ||
22681 | + ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK() | ||
22682 | + ** waits for acx_e_after_interrupt_task to complete if it is running | ||
22683 | + ** on another CPU, but acx_e_after_interrupt_task | ||
22684 | + ** will sleep on sem forever, because it is taken by us! | ||
22685 | + ** Work around that by temporary sem unlock. | ||
22686 | + ** This will fail miserably if we'll be hit by concurrent | ||
22687 | + ** iwconfig or something in between. TODO! */ | ||
22688 | + acx_sem_unlock(adev); | ||
22689 | + FLUSH_SCHEDULED_WORK(); | ||
22690 | + acx_sem_lock(adev); | ||
22691 | + | ||
22692 | + /* This is possible: | ||
22693 | + ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task -> | ||
22694 | + ** -> set_status(ASSOCIATED) -> wake_queue() | ||
22695 | + ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK | ||
22696 | + ** lock/unlock is just paranoia, maybe not needed */ | ||
22697 | + acx_lock(adev, flags); | ||
22698 | + acx_stop_queue(ndev, "on ifdown"); | ||
22699 | + acx_set_status(adev, ACX_STATUS_0_STOPPED); | ||
22700 | + acx_unlock(adev, flags); | ||
22701 | + | ||
22702 | + /* kernel/timer.c says it's illegal to del_timer_sync() | ||
22703 | + ** a timer which restarts itself. We guarantee this cannot | ||
22704 | + ** ever happen because acx_i_timer() never does this if | ||
22705 | + ** status is ACX_STATUS_0_STOPPED */ | ||
22706 | + del_timer_sync(&adev->mgmt_timer); | ||
22707 | + | ||
22708 | + FN_EXIT0; | ||
22709 | +} | ||
22710 | + | ||
22711 | + | ||
22712 | +/*********************************************************************** | ||
22713 | +** acxmem_e_open | ||
22714 | +** | ||
22715 | +** Called as a result of SIOCSIFFLAGS ioctl changing the flags bit IFF_UP | ||
22716 | +** from clear to set. In other words: ifconfig up. | ||
22717 | +** | ||
22718 | +** Returns: | ||
22719 | +** 0 success | ||
22720 | +** >0 f/w reported error | ||
22721 | +** <0 driver reported error | ||
22722 | +*/ | ||
22723 | +static int | ||
22724 | +acxmem_e_open(struct net_device *ndev) | ||
22725 | +{ | ||
22726 | + acx_device_t *adev = ndev2adev(ndev); | ||
22727 | + int result = OK; | ||
22728 | + | ||
22729 | + FN_ENTER; | ||
22730 | + | ||
22731 | + acx_sem_lock(adev); | ||
22732 | + | ||
22733 | + acx_init_task_scheduler(adev); | ||
22734 | + | ||
22735 | +/* TODO: pci_set_power_state(pdev, PCI_D0); ? */ | ||
22736 | + | ||
22737 | + /* request shared IRQ handler */ | ||
22738 | + if (request_irq(ndev->irq, acxmem_i_interrupt, SA_INTERRUPT, ndev->name, ndev)) { | ||
22739 | + printk("%s: request_irq FAILED\n", ndev->name); | ||
22740 | + result = -EAGAIN; | ||
22741 | + goto done; | ||
22742 | + } | ||
22743 | + set_irq_type (ndev->irq, IRQT_FALLING); | ||
22744 | + log(L_DEBUG|L_IRQ, "request_irq %d successful\n", ndev->irq); | ||
22745 | + | ||
22746 | + /* ifup device */ | ||
22747 | + acxmem_s_up(ndev); | ||
22748 | + | ||
22749 | + /* We don't currently have to do anything else. | ||
22750 | + * The setup of the MAC should be subsequently completed via | ||
22751 | + * the mlme commands. | ||
22752 | + * Higher layers know we're ready from dev->start==1 and | ||
22753 | + * dev->tbusy==0. Our rx path knows to pass up received/ | ||
22754 | + * frames because of dev->flags&IFF_UP is true. | ||
22755 | + */ | ||
22756 | +done: | ||
22757 | + acx_sem_unlock(adev); | ||
22758 | + | ||
22759 | + FN_EXIT1(result); | ||
22760 | + return result; | ||
22761 | +} | ||
22762 | + | ||
22763 | + | ||
22764 | +/*********************************************************************** | ||
22765 | +** acxmem_e_close | ||
22766 | +** | ||
22767 | +** Called as a result of SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP | ||
22768 | +** from set to clear. I.e. called by "ifconfig DEV down" | ||
22769 | +** | ||
22770 | +** Returns: | ||
22771 | +** 0 success | ||
22772 | +** >0 f/w reported error | ||
22773 | +** <0 driver reported error | ||
22774 | +*/ | ||
22775 | +static int | ||
22776 | +acxmem_e_close(struct net_device *ndev) | ||
22777 | +{ | ||
22778 | + acx_device_t *adev = ndev2adev(ndev); | ||
22779 | + | ||
22780 | + FN_ENTER; | ||
22781 | + | ||
22782 | + acx_sem_lock(adev); | ||
22783 | + | ||
22784 | + /* ifdown device */ | ||
22785 | + CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
22786 | + if (netif_device_present(ndev)) { | ||
22787 | + acxmem_s_down(ndev); | ||
22788 | + } | ||
22789 | + | ||
22790 | + /* disable all IRQs, release shared IRQ handler */ | ||
22791 | + write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); | ||
22792 | + write_reg16(adev, IO_ACX_FEMR, 0x0); | ||
22793 | + free_irq(ndev->irq, ndev); | ||
22794 | + | ||
22795 | +/* TODO: pci_set_power_state(pdev, PCI_D3hot); ? */ | ||
22796 | + | ||
22797 | + /* We currently don't have to do anything else. | ||
22798 | + * Higher layers know we're not ready from dev->start==0 and | ||
22799 | + * dev->tbusy==1. Our rx path knows to not pass up received | ||
22800 | + * frames because of dev->flags&IFF_UP is false. | ||
22801 | + */ | ||
22802 | + acx_sem_unlock(adev); | ||
22803 | + | ||
22804 | + log(L_INIT, "closed device\n"); | ||
22805 | + FN_EXIT0; | ||
22806 | + return OK; | ||
22807 | +} | ||
22808 | + | ||
22809 | + | ||
22810 | +/*********************************************************************** | ||
22811 | +** acxmem_i_tx_timeout | ||
22812 | +** | ||
22813 | +** Called from network core. Must not sleep! | ||
22814 | +*/ | ||
22815 | +static void | ||
22816 | +acxmem_i_tx_timeout(struct net_device *ndev) | ||
22817 | +{ | ||
22818 | + acx_device_t *adev = ndev2adev(ndev); | ||
22819 | + unsigned long flags; | ||
22820 | + unsigned int tx_num_cleaned; | ||
22821 | + | ||
22822 | + FN_ENTER; | ||
22823 | + | ||
22824 | + acx_lock(adev, flags); | ||
22825 | + | ||
22826 | + /* clean processed tx descs, they may have been completely full */ | ||
22827 | + tx_num_cleaned = acxmem_l_clean_txdesc(adev); | ||
22828 | + | ||
22829 | + /* nothing cleaned, yet (almost) no free buffers available? | ||
22830 | + * --> clean all tx descs, no matter which status!! | ||
22831 | + * Note that I strongly suspect that doing emergency cleaning | ||
22832 | + * may confuse the firmware. This is a last ditch effort to get | ||
22833 | + * ANYTHING to work again... | ||
22834 | + * | ||
22835 | + * TODO: it's best to simply reset & reinit hw from scratch... | ||
22836 | + */ | ||
22837 | + if ((adev->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) { | ||
22838 | + printk("%s: FAILED to free any of the many full tx buffers. " | ||
22839 | + "Switching to emergency freeing. " | ||
22840 | + "Please report!\n", ndev->name); | ||
22841 | + acxmem_l_clean_txdesc_emergency(adev); | ||
22842 | + } | ||
22843 | + | ||
22844 | + if (acx_queue_stopped(ndev) && (ACX_STATUS_4_ASSOCIATED == adev->status)) | ||
22845 | + acx_wake_queue(ndev, "after tx timeout"); | ||
22846 | + | ||
22847 | + /* stall may have happened due to radio drift, so recalib radio */ | ||
22848 | + acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); | ||
22849 | + | ||
22850 | + /* do unimportant work last */ | ||
22851 | + printk("%s: tx timeout!\n", ndev->name); | ||
22852 | + adev->stats.tx_errors++; | ||
22853 | + | ||
22854 | + acx_unlock(adev, flags); | ||
22855 | + | ||
22856 | + FN_EXIT0; | ||
22857 | +} | ||
22858 | + | ||
22859 | + | ||
22860 | +/*********************************************************************** | ||
22861 | +** acxmem_i_set_multicast_list | ||
22862 | +** FIXME: most likely needs refinement | ||
22863 | +*/ | ||
22864 | +static void | ||
22865 | +acxmem_i_set_multicast_list(struct net_device *ndev) | ||
22866 | +{ | ||
22867 | + acx_device_t *adev = ndev2adev(ndev); | ||
22868 | + unsigned long flags; | ||
22869 | + | ||
22870 | + FN_ENTER; | ||
22871 | + | ||
22872 | + acx_lock(adev, flags); | ||
22873 | + | ||
22874 | + /* firmwares don't have allmulti capability, | ||
22875 | + * so just use promiscuous mode instead in this case. */ | ||
22876 | + if (ndev->flags & (IFF_PROMISC|IFF_ALLMULTI)) { | ||
22877 | + SET_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); | ||
22878 | + CLEAR_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); | ||
22879 | + SET_BIT(adev->set_mask, SET_RXCONFIG); | ||
22880 | + /* let kernel know in case *we* needed to set promiscuous */ | ||
22881 | + ndev->flags |= (IFF_PROMISC|IFF_ALLMULTI); | ||
22882 | + } else { | ||
22883 | + CLEAR_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); | ||
22884 | + SET_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); | ||
22885 | + SET_BIT(adev->set_mask, SET_RXCONFIG); | ||
22886 | + ndev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI); | ||
22887 | + } | ||
22888 | + | ||
22889 | + /* cannot update card settings directly here, atomic context */ | ||
22890 | + acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG); | ||
22891 | + | ||
22892 | + acx_unlock(adev, flags); | ||
22893 | + | ||
22894 | + FN_EXIT0; | ||
22895 | +} | ||
22896 | + | ||
22897 | + | ||
22898 | +/*************************************************************** | ||
22899 | +** acxmem_l_process_rxdesc | ||
22900 | +** | ||
22901 | +** Called directly and only from the IRQ handler | ||
22902 | +*/ | ||
22903 | + | ||
22904 | +#if !ACX_DEBUG | ||
22905 | +static inline void log_rxbuffer(const acx_device_t *adev) {} | ||
22906 | +#else | ||
22907 | +static void | ||
22908 | +log_rxbuffer(const acx_device_t *adev) | ||
22909 | +{ | ||
22910 | + register const struct rxhostdesc *rxhostdesc; | ||
22911 | + int i; | ||
22912 | + /* no FN_ENTER here, we don't want that */ | ||
22913 | + | ||
22914 | + rxhostdesc = adev->rxhostdesc_start; | ||
22915 | + if (unlikely(!rxhostdesc)) return; | ||
22916 | + for (i = 0; i < RX_CNT; i++) { | ||
22917 | + if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) | ||
22918 | + && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) | ||
22919 | + printk("rx: buf %d full\n", i); | ||
22920 | + rxhostdesc++; | ||
22921 | + } | ||
22922 | +} | ||
22923 | +#endif | ||
22924 | + | ||
22925 | +static void | ||
22926 | +acxmem_l_process_rxdesc(acx_device_t *adev) | ||
22927 | +{ | ||
22928 | + register rxhostdesc_t *hostdesc; | ||
22929 | + register rxdesc_t *rxdesc; | ||
22930 | + unsigned count, tail; | ||
22931 | + u32 addr; | ||
22932 | + u8 Ctl_8; | ||
22933 | + | ||
22934 | + FN_ENTER; | ||
22935 | + | ||
22936 | + if (unlikely(acx_debug & L_BUFR)) | ||
22937 | + log_rxbuffer(adev); | ||
22938 | + | ||
22939 | + /* First, have a loop to determine the first descriptor that's | ||
22940 | + * full, just in case there's a mismatch between our current | ||
22941 | + * rx_tail and the full descriptor we're supposed to handle. */ | ||
22942 | + tail = adev->rx_tail; | ||
22943 | + count = RX_CNT; | ||
22944 | + while (1) { | ||
22945 | + hostdesc = &adev->rxhostdesc_start[tail]; | ||
22946 | + rxdesc = &adev->rxdesc_start[tail]; | ||
22947 | + /* advance tail regardless of outcome of the below test */ | ||
22948 | + tail = (tail + 1) % RX_CNT; | ||
22949 | + | ||
22950 | + /* | ||
22951 | + * Unlike the PCI interface, where the ACX can write directly to | ||
22952 | + * the host descriptors, on the slave memory interface we have to | ||
22953 | + * pull these. All we really need to do is check the Ctl_8 field | ||
22954 | + * in the rx descriptor on the ACX, which should be 0x11000000 if | ||
22955 | + * we should process it. | ||
22956 | + */ | ||
22957 | + Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); | ||
22958 | + if ((Ctl_8 & DESC_CTL_HOSTOWN) && | ||
22959 | + (Ctl_8 & DESC_CTL_ACXDONE)) | ||
22960 | + break; /* found it! */ | ||
22961 | + | ||
22962 | + if (unlikely(!--count)) /* hmm, no luck: all descs empty, bail out */ | ||
22963 | + goto end; | ||
22964 | + } | ||
22965 | + | ||
22966 | + /* now process descriptors, starting with the first we figured out */ | ||
22967 | + while (1) { | ||
22968 | + log(L_BUFR, "rx: tail=%u Ctl_8=%02X\n", tail, Ctl_8); | ||
22969 | + /* | ||
22970 | + * If the ACX has CTL_RECLAIM set on this descriptor there | ||
22971 | + * is no buffer associated; it just wants us to tell it to | ||
22972 | + * reclaim the memory. | ||
22973 | + */ | ||
22974 | + if (!(Ctl_8 & DESC_CTL_RECLAIM)) { | ||
22975 | + | ||
22976 | + /* | ||
22977 | + * slave interface - pull data now | ||
22978 | + */ | ||
22979 | + hostdesc->length = read_slavemem16 (adev, (u32) &(rxdesc->total_length)); | ||
22980 | + | ||
22981 | + /* | ||
22982 | + * hostdesc->data is an rxbuffer_t, which includes header information, | ||
22983 | + * but the length in the data packet doesn't. The header information | ||
22984 | + * takes up an additional 12 bytes, so add that to the length we copy. | ||
22985 | + */ | ||
22986 | + addr = read_slavemem32 (adev, (u32) &(rxdesc->ACXMemPtr)); | ||
22987 | + if (addr) { | ||
22988 | + /* | ||
22989 | + * How can &(rxdesc->ACXMemPtr) above ever be zero? Looks like we | ||
22990 | + * get that now and then - try to trap it for debug. | ||
22991 | + */ | ||
22992 | + if (addr & 0xffff0000) { | ||
22993 | + printk("rxdesc 0x%08x\n", (u32) rxdesc); | ||
22994 | + dump_acxmem (adev, 0, 0x10000); | ||
22995 | + panic ("Bad access!"); | ||
22996 | + } | ||
22997 | + chaincopy_from_slavemem (adev, (u8 *) hostdesc->data, addr, | ||
22998 | + hostdesc->length + | ||
22999 | + (u32) &((rxbuffer_t *)0)->hdr_a3); | ||
23000 | + acx_l_process_rxbuf(adev, hostdesc->data); | ||
23001 | + } | ||
23002 | + } | ||
23003 | + else { | ||
23004 | + printk ("rx reclaim only!\n"); | ||
23005 | + } | ||
23006 | + | ||
23007 | + hostdesc->Status = 0; | ||
23008 | + | ||
23009 | + /* | ||
23010 | + * Let the ACX know we're done. | ||
23011 | + */ | ||
23012 | + CLEAR_BIT (Ctl_8, DESC_CTL_HOSTOWN); | ||
23013 | + SET_BIT (Ctl_8, DESC_CTL_HOSTDONE); | ||
23014 | + SET_BIT (Ctl_8, DESC_CTL_RECLAIM); | ||
23015 | + write_slavemem8 (adev, (u32) &rxdesc->Ctl_8, Ctl_8); | ||
23016 | + | ||
23017 | + /* | ||
23018 | + * Now tell the ACX we've finished with the receive buffer so | ||
23019 | + * it can finish the reclaim. | ||
23020 | + */ | ||
23021 | + write_reg16 (adev, IO_ACX_INT_TRIG, INT_TRIG_RXPRC); | ||
23022 | + | ||
23023 | + /* ok, descriptor is handled, now check the next descriptor */ | ||
23024 | + hostdesc = &adev->rxhostdesc_start[tail]; | ||
23025 | + rxdesc = &adev->rxdesc_start[tail]; | ||
23026 | + | ||
23027 | + Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); | ||
23028 | + | ||
23029 | + /* if next descriptor is empty, then bail out */ | ||
23030 | + if (!(Ctl_8 & DESC_CTL_HOSTOWN) || !(Ctl_8 & DESC_CTL_ACXDONE)) | ||
23031 | + break; | ||
23032 | + | ||
23033 | + tail = (tail + 1) % RX_CNT; | ||
23034 | + } | ||
23035 | +end: | ||
23036 | + adev->rx_tail = tail; | ||
23037 | + FN_EXIT0; | ||
23038 | +} | ||
23039 | + | ||
23040 | + | ||
23041 | +/*********************************************************************** | ||
23042 | +** acxmem_i_interrupt | ||
23043 | +** | ||
23044 | +** IRQ handler (atomic context, must not sleep, blah, blah) | ||
23045 | +*/ | ||
23046 | + | ||
23047 | +/* scan is complete. all frames now on the receive queue are valid */ | ||
23048 | +#define INFO_SCAN_COMPLETE 0x0001 | ||
23049 | +#define INFO_WEP_KEY_NOT_FOUND 0x0002 | ||
23050 | +/* hw has been reset as the result of a watchdog timer timeout */ | ||
23051 | +#define INFO_WATCH_DOG_RESET 0x0003 | ||
23052 | +/* failed to send out NULL frame from PS mode notification to AP */ | ||
23053 | +/* recommended action: try entering 802.11 PS mode again */ | ||
23054 | +#define INFO_PS_FAIL 0x0004 | ||
23055 | +/* encryption/decryption process on a packet failed */ | ||
23056 | +#define INFO_IV_ICV_FAILURE 0x0005 | ||
23057 | + | ||
23058 | +/* Info mailbox format: | ||
23059 | +2 bytes: type | ||
23060 | +2 bytes: status | ||
23061 | +more bytes may follow | ||
23062 | + rumors say about status: | ||
23063 | + 0x0000 info available (set by hw) | ||
23064 | + 0x0001 information received (must be set by host) | ||
23065 | + 0x1000 info available, mailbox overflowed (messages lost) (set by hw) | ||
23066 | + but in practice we've seen: | ||
23067 | + 0x9000 when we did not set status to 0x0001 on prev message | ||
23068 | + 0x1001 when we did set it | ||
23069 | + 0x0000 was never seen | ||
23070 | + conclusion: this is really a bitfield: | ||
23071 | + 0x1000 is 'info available' bit | ||
23072 | + 'mailbox overflowed' bit is 0x8000, not 0x1000 | ||
23073 | + value of 0x0000 probably means that there are no messages at all | ||
23074 | + P.S. I dunno how in hell hw is supposed to notice that messages are lost - | ||
23075 | + it does NOT clear bit 0x0001, and this bit will probably stay forever set | ||
23076 | + after we set it once. Let's hope this will be fixed in firmware someday | ||
23077 | +*/ | ||
23078 | + | ||
23079 | +static void | ||
23080 | +handle_info_irq(acx_device_t *adev) | ||
23081 | +{ | ||
23082 | +#if ACX_DEBUG | ||
23083 | + static const char * const info_type_msg[] = { | ||
23084 | + "(unknown)", | ||
23085 | + "scan complete", | ||
23086 | + "WEP key not found", | ||
23087 | + "internal watchdog reset was done", | ||
23088 | + "failed to send powersave (NULL frame) notification to AP", | ||
23089 | + "encrypt/decrypt on a packet has failed", | ||
23090 | + "TKIP tx keys disabled", | ||
23091 | + "TKIP rx keys disabled", | ||
23092 | + "TKIP rx: key ID not found", | ||
23093 | + "???", | ||
23094 | + "???", | ||
23095 | + "???", | ||
23096 | + "???", | ||
23097 | + "???", | ||
23098 | + "???", | ||
23099 | + "???", | ||
23100 | + "TKIP IV value exceeds thresh" | ||
23101 | + }; | ||
23102 | +#endif | ||
23103 | + u32 info_type, info_status; | ||
23104 | + | ||
23105 | + info_type = read_slavemem32 (adev, (u32) adev->info_area); | ||
23106 | + | ||
23107 | + info_status = (info_type >> 16); | ||
23108 | + info_type = (u16)info_type; | ||
23109 | + | ||
23110 | + /* inform fw that we have read this info message */ | ||
23111 | + write_slavemem32(adev, (u32) adev->info_area, info_type | 0x00010000); | ||
23112 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_INFOACK); | ||
23113 | + write_flush(adev); | ||
23114 | + | ||
23115 | + log(L_CTL, "info_type:%04X info_status:%04X\n", | ||
23116 | + info_type, info_status); | ||
23117 | + | ||
23118 | + log(L_IRQ, "got Info IRQ: status %04X type %04X: %s\n", | ||
23119 | + info_status, info_type, | ||
23120 | + info_type_msg[(info_type >= VEC_SIZE(info_type_msg)) ? | ||
23121 | + 0 : info_type] | ||
23122 | + ); | ||
23123 | +} | ||
23124 | + | ||
23125 | + | ||
23126 | +static void | ||
23127 | +log_unusual_irq(u16 irqtype) { | ||
23128 | + /* | ||
23129 | + if (!printk_ratelimit()) | ||
23130 | + return; | ||
23131 | + */ | ||
23132 | + | ||
23133 | + printk("acx: got"); | ||
23134 | + if (irqtype & HOST_INT_TX_XFER) { | ||
23135 | + printk(" Tx_Xfer"); | ||
23136 | + } | ||
23137 | + if (irqtype & HOST_INT_RX_COMPLETE) { | ||
23138 | + printk(" Rx_Complete"); | ||
23139 | + } | ||
23140 | + if (irqtype & HOST_INT_DTIM) { | ||
23141 | + printk(" DTIM"); | ||
23142 | + } | ||
23143 | + if (irqtype & HOST_INT_BEACON) { | ||
23144 | + printk(" Beacon"); | ||
23145 | + } | ||
23146 | + if (irqtype & HOST_INT_TIMER) { | ||
23147 | + log(L_IRQ, " Timer"); | ||
23148 | + } | ||
23149 | + if (irqtype & HOST_INT_KEY_NOT_FOUND) { | ||
23150 | + printk(" Key_Not_Found"); | ||
23151 | + } | ||
23152 | + if (irqtype & HOST_INT_IV_ICV_FAILURE) { | ||
23153 | + printk(" IV_ICV_Failure (crypto)"); | ||
23154 | + } | ||
23155 | + /* HOST_INT_CMD_COMPLETE */ | ||
23156 | + /* HOST_INT_INFO */ | ||
23157 | + if (irqtype & HOST_INT_OVERFLOW) { | ||
23158 | + printk(" Overflow"); | ||
23159 | + } | ||
23160 | + if (irqtype & HOST_INT_PROCESS_ERROR) { | ||
23161 | + printk(" Process_Error"); | ||
23162 | + } | ||
23163 | + /* HOST_INT_SCAN_COMPLETE */ | ||
23164 | + if (irqtype & HOST_INT_FCS_THRESHOLD) { | ||
23165 | + printk(" FCS_Threshold"); | ||
23166 | + } | ||
23167 | + if (irqtype & HOST_INT_UNKNOWN) { | ||
23168 | + printk(" Unknown"); | ||
23169 | + } | ||
23170 | + printk(" IRQ(s)\n"); | ||
23171 | +} | ||
23172 | + | ||
23173 | + | ||
23174 | +static void | ||
23175 | +update_link_quality_led(acx_device_t *adev) | ||
23176 | +{ | ||
23177 | + int qual; | ||
23178 | + | ||
23179 | + qual = acx_signal_determine_quality(adev->wstats.qual.level, adev->wstats.qual.noise); | ||
23180 | + if (qual > adev->brange_max_quality) | ||
23181 | + qual = adev->brange_max_quality; | ||
23182 | + | ||
23183 | + if (time_after(jiffies, adev->brange_time_last_state_change + | ||
23184 | + (HZ/2 - HZ/2 * (unsigned long)qual / adev->brange_max_quality ) )) { | ||
23185 | + acxmem_l_power_led(adev, (adev->brange_last_state == 0)); | ||
23186 | + adev->brange_last_state ^= 1; /* toggle */ | ||
23187 | + adev->brange_time_last_state_change = jiffies; | ||
23188 | + } | ||
23189 | +} | ||
23190 | + | ||
23191 | + | ||
23192 | +#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */ | ||
23193 | + | ||
23194 | +static irqreturn_t | ||
23195 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
23196 | +acxmem_i_interrupt(int irq, void *dev_id) | ||
23197 | +#else | ||
23198 | +acxmwm_i_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
23199 | +#endif | ||
23200 | +{ | ||
23201 | + acx_device_t *adev; | ||
23202 | + unsigned long flags; | ||
23203 | + unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY; | ||
23204 | + register u16 irqtype; | ||
23205 | + u16 unmasked; | ||
23206 | + | ||
23207 | + adev = ndev2adev((struct net_device*)dev_id); | ||
23208 | + | ||
23209 | + /* LOCKING: can just spin_lock() since IRQs are disabled anyway. | ||
23210 | + * I am paranoid */ | ||
23211 | + acx_lock(adev, flags); | ||
23212 | + | ||
23213 | + unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); | ||
23214 | + if (unlikely(0xffff == unmasked)) { | ||
23215 | + /* 0xffff value hints at missing hardware, | ||
23216 | + * so don't do anything. | ||
23217 | + * Not very clean, but other drivers do the same... */ | ||
23218 | + log(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n"); | ||
23219 | + goto none; | ||
23220 | + } | ||
23221 | + | ||
23222 | + /* We will check only "interesting" IRQ types */ | ||
23223 | + irqtype = unmasked & ~adev->irq_mask; | ||
23224 | + if (!irqtype) { | ||
23225 | + /* We are on a shared IRQ line and it wasn't our IRQ */ | ||
23226 | + log(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n", | ||
23227 | + unmasked, adev->irq_mask); | ||
23228 | + goto none; | ||
23229 | + } | ||
23230 | + | ||
23231 | + /* Done here because IRQ_NONEs taking three lines of log | ||
23232 | + ** drive me crazy */ | ||
23233 | + FN_ENTER; | ||
23234 | + | ||
23235 | +#define IRQ_ITERATE 1 | ||
23236 | +#if IRQ_ITERATE | ||
23237 | +if (jiffies != adev->irq_last_jiffies) { | ||
23238 | + adev->irq_loops_this_jiffy = 0; | ||
23239 | + adev->irq_last_jiffies = jiffies; | ||
23240 | +} | ||
23241 | + | ||
23242 | +/* safety condition; we'll normally abort loop below | ||
23243 | + * in case no IRQ type occurred */ | ||
23244 | +while (likely(--irqcount)) { | ||
23245 | +#endif | ||
23246 | + /* ACK all IRQs ASAP */ | ||
23247 | + write_reg16(adev, IO_ACX_IRQ_ACK, 0xffff); | ||
23248 | + | ||
23249 | + log(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n", | ||
23250 | + unmasked, adev->irq_mask, irqtype); | ||
23251 | + | ||
23252 | + /* Handle most important IRQ types first */ | ||
23253 | + if (irqtype & HOST_INT_RX_DATA) { | ||
23254 | + log(L_IRQ, "got Rx_Data IRQ\n"); | ||
23255 | + acxmem_l_process_rxdesc(adev); | ||
23256 | + } | ||
23257 | + if (irqtype & HOST_INT_TX_COMPLETE) { | ||
23258 | + log(L_IRQ, "got Tx_Complete IRQ\n"); | ||
23259 | + /* don't clean up on each Tx complete, wait a bit | ||
23260 | + * unless we're going towards full, in which case | ||
23261 | + * we do it immediately, too (otherwise we might lockup | ||
23262 | + * with a full Tx buffer if we go into | ||
23263 | + * acxmem_l_clean_txdesc() at a time when we won't wakeup | ||
23264 | + * the net queue in there for some reason...) */ | ||
23265 | + if (adev->tx_free <= TX_START_CLEAN) { | ||
23266 | +#if TX_CLEANUP_IN_SOFTIRQ | ||
23267 | + acx_schedule_task(adev, ACX_AFTER_IRQ_TX_CLEANUP); | ||
23268 | +#else | ||
23269 | + acxmem_l_clean_txdesc(adev); | ||
23270 | +#endif | ||
23271 | + } | ||
23272 | + } | ||
23273 | + | ||
23274 | + /* Less frequent ones */ | ||
23275 | + if (irqtype & (0 | ||
23276 | + | HOST_INT_CMD_COMPLETE | ||
23277 | + | HOST_INT_INFO | ||
23278 | + | HOST_INT_SCAN_COMPLETE | ||
23279 | + )) { | ||
23280 | + if (irqtype & HOST_INT_CMD_COMPLETE) { | ||
23281 | + log(L_IRQ, "got Command_Complete IRQ\n"); | ||
23282 | + /* save the state for the running issue_cmd() */ | ||
23283 | + SET_BIT(adev->irq_status, HOST_INT_CMD_COMPLETE); | ||
23284 | + } | ||
23285 | + if (irqtype & HOST_INT_INFO) { | ||
23286 | + handle_info_irq(adev); | ||
23287 | + } | ||
23288 | + if (irqtype & HOST_INT_SCAN_COMPLETE) { | ||
23289 | + log(L_IRQ, "got Scan_Complete IRQ\n"); | ||
23290 | + /* need to do that in process context */ | ||
23291 | + acx_schedule_task(adev, ACX_AFTER_IRQ_COMPLETE_SCAN); | ||
23292 | + /* remember that fw is not scanning anymore */ | ||
23293 | + SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE); | ||
23294 | + } | ||
23295 | + } | ||
23296 | + | ||
23297 | + /* These we just log, but either they happen rarely | ||
23298 | + * or we keep them masked out */ | ||
23299 | + if (irqtype & (0 | ||
23300 | + /* | HOST_INT_RX_DATA */ | ||
23301 | + /* | HOST_INT_TX_COMPLETE */ | ||
23302 | + | HOST_INT_TX_XFER | ||
23303 | + | HOST_INT_RX_COMPLETE | ||
23304 | + | HOST_INT_DTIM | ||
23305 | + | HOST_INT_BEACON | ||
23306 | + | HOST_INT_TIMER | ||
23307 | + | HOST_INT_KEY_NOT_FOUND | ||
23308 | + | HOST_INT_IV_ICV_FAILURE | ||
23309 | + /* | HOST_INT_CMD_COMPLETE */ | ||
23310 | + /* | HOST_INT_INFO */ | ||
23311 | + | HOST_INT_OVERFLOW | ||
23312 | + | HOST_INT_PROCESS_ERROR | ||
23313 | + /* | HOST_INT_SCAN_COMPLETE */ | ||
23314 | + | HOST_INT_FCS_THRESHOLD | ||
23315 | + | HOST_INT_UNKNOWN | ||
23316 | + )) { | ||
23317 | + log_unusual_irq(irqtype); | ||
23318 | + } | ||
23319 | + | ||
23320 | +#if IRQ_ITERATE | ||
23321 | + unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); | ||
23322 | + irqtype = unmasked & ~adev->irq_mask; | ||
23323 | + /* Bail out if no new IRQ bits or if all are masked out */ | ||
23324 | + if (!irqtype) | ||
23325 | + break; | ||
23326 | + | ||
23327 | + if (unlikely(++adev->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) { | ||
23328 | + printk(KERN_ERR "acx: too many interrupts per jiffy!\n"); | ||
23329 | + /* Looks like card floods us with IRQs! Try to stop that */ | ||
23330 | + write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); | ||
23331 | + /* This will short-circuit all future attempts to handle IRQ. | ||
23332 | + * We cant do much more... */ | ||
23333 | + adev->irq_mask = 0; | ||
23334 | + break; | ||
23335 | + } | ||
23336 | +} | ||
23337 | +#endif | ||
23338 | + /* Routine to perform blink with range */ | ||
23339 | + if (unlikely(adev->led_power == 2)) | ||
23340 | + update_link_quality_led(adev); | ||
23341 | + | ||
23342 | +/* handled: */ | ||
23343 | + /* write_flush(adev); - not needed, last op was read anyway */ | ||
23344 | + acx_unlock(adev, flags); | ||
23345 | + FN_EXIT0; | ||
23346 | + return IRQ_HANDLED; | ||
23347 | + | ||
23348 | +none: | ||
23349 | + acx_unlock(adev, flags); | ||
23350 | + return IRQ_NONE; | ||
23351 | +} | ||
23352 | + | ||
23353 | + | ||
23354 | +/*********************************************************************** | ||
23355 | +** acxmem_l_power_led | ||
23356 | +*/ | ||
23357 | +void | ||
23358 | +acxmem_l_power_led(acx_device_t *adev, int enable) | ||
23359 | +{ | ||
23360 | + u16 gpio_pled = IS_ACX111(adev) ? 0x0040 : 0x0800; | ||
23361 | + | ||
23362 | + /* A hack. Not moving message rate limiting to adev->xxx | ||
23363 | + * (it's only a debug message after all) */ | ||
23364 | + static int rate_limit = 0; | ||
23365 | + | ||
23366 | + if (rate_limit++ < 3) | ||
23367 | + log(L_IOCTL, "Please report in case toggling the power " | ||
23368 | + "LED doesn't work for your card!\n"); | ||
23369 | + if (enable) | ||
23370 | + write_reg16(adev, IO_ACX_GPIO_OUT, | ||
23371 | + read_reg16(adev, IO_ACX_GPIO_OUT) & ~gpio_pled); | ||
23372 | + else | ||
23373 | + write_reg16(adev, IO_ACX_GPIO_OUT, | ||
23374 | + read_reg16(adev, IO_ACX_GPIO_OUT) | gpio_pled); | ||
23375 | +} | ||
23376 | + | ||
23377 | + | ||
23378 | +/*********************************************************************** | ||
23379 | +** Ioctls | ||
23380 | +*/ | ||
23381 | + | ||
23382 | +/*********************************************************************** | ||
23383 | +*/ | ||
23384 | +int | ||
23385 | +acx111pci_ioctl_info( | ||
23386 | + struct net_device *ndev, | ||
23387 | + struct iw_request_info *info, | ||
23388 | + struct iw_param *vwrq, | ||
23389 | + char *extra) | ||
23390 | +{ | ||
23391 | +#if ACX_DEBUG > 1 | ||
23392 | + acx_device_t *adev = ndev2adev(ndev); | ||
23393 | + rxdesc_t *rxdesc; | ||
23394 | + txdesc_t *txdesc; | ||
23395 | + rxhostdesc_t *rxhostdesc; | ||
23396 | + txhostdesc_t *txhostdesc; | ||
23397 | + struct acx111_ie_memoryconfig memconf; | ||
23398 | + struct acx111_ie_queueconfig queueconf; | ||
23399 | + unsigned long flags; | ||
23400 | + int i; | ||
23401 | + char memmap[0x34]; | ||
23402 | + char rxconfig[0x8]; | ||
23403 | + char fcserror[0x8]; | ||
23404 | + char ratefallback[0x5]; | ||
23405 | + | ||
23406 | + if ( !(acx_debug & (L_IOCTL|L_DEBUG)) ) | ||
23407 | + return OK; | ||
23408 | + /* using printk() since we checked debug flag already */ | ||
23409 | + | ||
23410 | + acx_sem_lock(adev); | ||
23411 | + | ||
23412 | + if (!IS_ACX111(adev)) { | ||
23413 | + printk("acx111-specific function called " | ||
23414 | + "with non-acx111 chip, aborting\n"); | ||
23415 | + goto end_ok; | ||
23416 | + } | ||
23417 | + | ||
23418 | + /* get Acx111 Memory Configuration */ | ||
23419 | + memset(&memconf, 0, sizeof(memconf)); | ||
23420 | + /* BTW, fails with 12 (Write only) error code. | ||
23421 | + ** Retained for easy testing of issue_cmd error handling :) */ | ||
23422 | + printk ("Interrogating queue config\n"); | ||
23423 | + acx_s_interrogate(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG); | ||
23424 | + printk ("done with queue config\n"); | ||
23425 | + | ||
23426 | + /* get Acx111 Queue Configuration */ | ||
23427 | + memset(&queueconf, 0, sizeof(queueconf)); | ||
23428 | + printk ("Interrogating mem config options\n"); | ||
23429 | + acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS); | ||
23430 | + printk ("done with mem config options\n"); | ||
23431 | + | ||
23432 | + /* get Acx111 Memory Map */ | ||
23433 | + memset(memmap, 0, sizeof(memmap)); | ||
23434 | + printk ("Interrogating mem map\n"); | ||
23435 | + acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP); | ||
23436 | + printk ("done with mem map\n"); | ||
23437 | + | ||
23438 | + /* get Acx111 Rx Config */ | ||
23439 | + memset(rxconfig, 0, sizeof(rxconfig)); | ||
23440 | + printk ("Interrogating rxconfig\n"); | ||
23441 | + acx_s_interrogate(adev, &rxconfig, ACX1xx_IE_RXCONFIG); | ||
23442 | + printk ("done with queue rxconfig\n"); | ||
23443 | + | ||
23444 | + /* get Acx111 fcs error count */ | ||
23445 | + memset(fcserror, 0, sizeof(fcserror)); | ||
23446 | + printk ("Interrogating fcs err count\n"); | ||
23447 | + acx_s_interrogate(adev, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT); | ||
23448 | + printk ("done with err count\n"); | ||
23449 | + | ||
23450 | + /* get Acx111 rate fallback */ | ||
23451 | + memset(ratefallback, 0, sizeof(ratefallback)); | ||
23452 | + printk ("Interrogating rate fallback\n"); | ||
23453 | + acx_s_interrogate(adev, &ratefallback, ACX1xx_IE_RATE_FALLBACK); | ||
23454 | + printk ("done with rate fallback\n"); | ||
23455 | + | ||
23456 | + /* force occurrence of a beacon interrupt */ | ||
23457 | + /* TODO: comment why is this necessary */ | ||
23458 | + write_reg16(adev, IO_ACX_HINT_TRIG, HOST_INT_BEACON); | ||
23459 | + | ||
23460 | + /* dump Acx111 Mem Configuration */ | ||
23461 | + printk("dump mem config:\n" | ||
23462 | + "data read: %d, struct size: %d\n" | ||
23463 | + "Number of stations: %1X\n" | ||
23464 | + "Memory block size: %1X\n" | ||
23465 | + "tx/rx memory block allocation: %1X\n" | ||
23466 | + "count rx: %X / tx: %X queues\n" | ||
23467 | + "options %1X\n" | ||
23468 | + "fragmentation %1X\n" | ||
23469 | + "Rx Queue 1 Count Descriptors: %X\n" | ||
23470 | + "Rx Queue 1 Host Memory Start: %X\n" | ||
23471 | + "Tx Queue 1 Count Descriptors: %X\n" | ||
23472 | + "Tx Queue 1 Attributes: %X\n", | ||
23473 | + memconf.len, (int) sizeof(memconf), | ||
23474 | + memconf.no_of_stations, | ||
23475 | + memconf.memory_block_size, | ||
23476 | + memconf.tx_rx_memory_block_allocation, | ||
23477 | + memconf.count_rx_queues, memconf.count_tx_queues, | ||
23478 | + memconf.options, | ||
23479 | + memconf.fragmentation, | ||
23480 | + memconf.rx_queue1_count_descs, | ||
23481 | + acx2cpu(memconf.rx_queue1_host_rx_start), | ||
23482 | + memconf.tx_queue1_count_descs, | ||
23483 | + memconf.tx_queue1_attributes); | ||
23484 | + | ||
23485 | + /* dump Acx111 Queue Configuration */ | ||
23486 | + printk("dump queue head:\n" | ||
23487 | + "data read: %d, struct size: %d\n" | ||
23488 | + "tx_memory_block_address (from card): %X\n" | ||
23489 | + "rx_memory_block_address (from card): %X\n" | ||
23490 | + "rx1_queue address (from card): %X\n" | ||
23491 | + "tx1_queue address (from card): %X\n" | ||
23492 | + "tx1_queue attributes (from card): %X\n", | ||
23493 | + queueconf.len, (int) sizeof(queueconf), | ||
23494 | + queueconf.tx_memory_block_address, | ||
23495 | + queueconf.rx_memory_block_address, | ||
23496 | + queueconf.rx1_queue_address, | ||
23497 | + queueconf.tx1_queue_address, | ||
23498 | + queueconf.tx1_attributes); | ||
23499 | + | ||
23500 | + /* dump Acx111 Mem Map */ | ||
23501 | + printk("dump mem map:\n" | ||
23502 | + "data read: %d, struct size: %d\n" | ||
23503 | + "Code start: %X\n" | ||
23504 | + "Code end: %X\n" | ||
23505 | + "WEP default key start: %X\n" | ||
23506 | + "WEP default key end: %X\n" | ||
23507 | + "STA table start: %X\n" | ||
23508 | + "STA table end: %X\n" | ||
23509 | + "Packet template start: %X\n" | ||
23510 | + "Packet template end: %X\n" | ||
23511 | + "Queue memory start: %X\n" | ||
23512 | + "Queue memory end: %X\n" | ||
23513 | + "Packet memory pool start: %X\n" | ||
23514 | + "Packet memory pool end: %X\n" | ||
23515 | + "iobase: %p\n" | ||
23516 | + "iobase2: %p\n", | ||
23517 | + *((u16 *)&memmap[0x02]), (int) sizeof(memmap), | ||
23518 | + *((u32 *)&memmap[0x04]), | ||
23519 | + *((u32 *)&memmap[0x08]), | ||
23520 | + *((u32 *)&memmap[0x0C]), | ||
23521 | + *((u32 *)&memmap[0x10]), | ||
23522 | + *((u32 *)&memmap[0x14]), | ||
23523 | + *((u32 *)&memmap[0x18]), | ||
23524 | + *((u32 *)&memmap[0x1C]), | ||
23525 | + *((u32 *)&memmap[0x20]), | ||
23526 | + *((u32 *)&memmap[0x24]), | ||
23527 | + *((u32 *)&memmap[0x28]), | ||
23528 | + *((u32 *)&memmap[0x2C]), | ||
23529 | + *((u32 *)&memmap[0x30]), | ||
23530 | + adev->iobase, | ||
23531 | + adev->iobase2); | ||
23532 | + | ||
23533 | + /* dump Acx111 Rx Config */ | ||
23534 | + printk("dump rx config:\n" | ||
23535 | + "data read: %d, struct size: %d\n" | ||
23536 | + "rx config: %X\n" | ||
23537 | + "rx filter config: %X\n", | ||
23538 | + *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig), | ||
23539 | + *((u16 *)&rxconfig[0x04]), | ||
23540 | + *((u16 *)&rxconfig[0x06])); | ||
23541 | + | ||
23542 | + /* dump Acx111 fcs error */ | ||
23543 | + printk("dump fcserror:\n" | ||
23544 | + "data read: %d, struct size: %d\n" | ||
23545 | + "fcserrors: %X\n", | ||
23546 | + *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror), | ||
23547 | + *((u32 *)&fcserror[0x04])); | ||
23548 | + | ||
23549 | + /* dump Acx111 rate fallback */ | ||
23550 | + printk("dump rate fallback:\n" | ||
23551 | + "data read: %d, struct size: %d\n" | ||
23552 | + "ratefallback: %X\n", | ||
23553 | + *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback), | ||
23554 | + *((u8 *)&ratefallback[0x04])); | ||
23555 | + | ||
23556 | + /* protect against IRQ */ | ||
23557 | + acx_lock(adev, flags); | ||
23558 | + | ||
23559 | + /* dump acx111 internal rx descriptor ring buffer */ | ||
23560 | + rxdesc = adev->rxdesc_start; | ||
23561 | + | ||
23562 | + /* loop over complete receive pool */ | ||
23563 | + if (rxdesc) for (i = 0; i < RX_CNT; i++) { | ||
23564 | + printk("\ndump internal rxdesc %d:\n" | ||
23565 | + "mem pos %p\n" | ||
23566 | + "next 0x%X\n" | ||
23567 | + "acx mem pointer (dynamic) 0x%X\n" | ||
23568 | + "CTL (dynamic) 0x%X\n" | ||
23569 | + "Rate (dynamic) 0x%X\n" | ||
23570 | + "RxStatus (dynamic) 0x%X\n" | ||
23571 | + "Mod/Pre (dynamic) 0x%X\n", | ||
23572 | + i, | ||
23573 | + rxdesc, | ||
23574 | + acx2cpu(rxdesc->pNextDesc), | ||
23575 | + acx2cpu(rxdesc->ACXMemPtr), | ||
23576 | + rxdesc->Ctl_8, | ||
23577 | + rxdesc->rate, | ||
23578 | + rxdesc->error, | ||
23579 | + rxdesc->SNR); | ||
23580 | + rxdesc++; | ||
23581 | + } | ||
23582 | + | ||
23583 | + /* dump host rx descriptor ring buffer */ | ||
23584 | + | ||
23585 | + rxhostdesc = adev->rxhostdesc_start; | ||
23586 | + | ||
23587 | + /* loop over complete receive pool */ | ||
23588 | + if (rxhostdesc) for (i = 0; i < RX_CNT; i++) { | ||
23589 | + printk("\ndump host rxdesc %d:\n" | ||
23590 | + "mem pos %p\n" | ||
23591 | + "buffer mem pos 0x%X\n" | ||
23592 | + "buffer mem offset 0x%X\n" | ||
23593 | + "CTL 0x%X\n" | ||
23594 | + "Length 0x%X\n" | ||
23595 | + "next 0x%X\n" | ||
23596 | + "Status 0x%X\n", | ||
23597 | + i, | ||
23598 | + rxhostdesc, | ||
23599 | + acx2cpu(rxhostdesc->data_phy), | ||
23600 | + rxhostdesc->data_offset, | ||
23601 | + le16_to_cpu(rxhostdesc->Ctl_16), | ||
23602 | + le16_to_cpu(rxhostdesc->length), | ||
23603 | + acx2cpu(rxhostdesc->desc_phy_next), | ||
23604 | + rxhostdesc->Status); | ||
23605 | + rxhostdesc++; | ||
23606 | + } | ||
23607 | + | ||
23608 | + /* dump acx111 internal tx descriptor ring buffer */ | ||
23609 | + txdesc = adev->txdesc_start; | ||
23610 | + | ||
23611 | + /* loop over complete transmit pool */ | ||
23612 | + if (txdesc) for (i = 0; i < TX_CNT; i++) { | ||
23613 | + printk("\ndump internal txdesc %d:\n" | ||
23614 | + "size 0x%X\n" | ||
23615 | + "mem pos %p\n" | ||
23616 | + "next 0x%X\n" | ||
23617 | + "acx mem pointer (dynamic) 0x%X\n" | ||
23618 | + "host mem pointer (dynamic) 0x%X\n" | ||
23619 | + "length (dynamic) 0x%X\n" | ||
23620 | + "CTL (dynamic) 0x%X\n" | ||
23621 | + "CTL2 (dynamic) 0x%X\n" | ||
23622 | + "Status (dynamic) 0x%X\n" | ||
23623 | + "Rate (dynamic) 0x%X\n", | ||
23624 | + i, | ||
23625 | + (int) sizeof(struct txdesc), | ||
23626 | + txdesc, | ||
23627 | + acx2cpu(txdesc->pNextDesc), | ||
23628 | + acx2cpu(txdesc->AcxMemPtr), | ||
23629 | + acx2cpu(txdesc->HostMemPtr), | ||
23630 | + le16_to_cpu(txdesc->total_length), | ||
23631 | + txdesc->Ctl_8, | ||
23632 | + txdesc->Ctl2_8, txdesc->error, | ||
23633 | + txdesc->u.r1.rate); | ||
23634 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
23635 | + } | ||
23636 | + | ||
23637 | + /* dump host tx descriptor ring buffer */ | ||
23638 | + | ||
23639 | + txhostdesc = adev->txhostdesc_start; | ||
23640 | + | ||
23641 | + /* loop over complete host send pool */ | ||
23642 | + if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) { | ||
23643 | + printk("\ndump host txdesc %d:\n" | ||
23644 | + "mem pos %p\n" | ||
23645 | + "buffer mem pos 0x%X\n" | ||
23646 | + "buffer mem offset 0x%X\n" | ||
23647 | + "CTL 0x%X\n" | ||
23648 | + "Length 0x%X\n" | ||
23649 | + "next 0x%X\n" | ||
23650 | + "Status 0x%X\n", | ||
23651 | + i, | ||
23652 | + txhostdesc, | ||
23653 | + acx2cpu(txhostdesc->data_phy), | ||
23654 | + txhostdesc->data_offset, | ||
23655 | + le16_to_cpu(txhostdesc->Ctl_16), | ||
23656 | + le16_to_cpu(txhostdesc->length), | ||
23657 | + acx2cpu(txhostdesc->desc_phy_next), | ||
23658 | + le32_to_cpu(txhostdesc->Status)); | ||
23659 | + txhostdesc++; | ||
23660 | + } | ||
23661 | + | ||
23662 | + /* write_reg16(adev, 0xb4, 0x4); */ | ||
23663 | + | ||
23664 | + acx_unlock(adev, flags); | ||
23665 | +end_ok: | ||
23666 | + | ||
23667 | + acx_sem_unlock(adev); | ||
23668 | +#endif /* ACX_DEBUG */ | ||
23669 | + return OK; | ||
23670 | +} | ||
23671 | + | ||
23672 | + | ||
23673 | +/*********************************************************************** | ||
23674 | +*/ | ||
23675 | +int | ||
23676 | +acx100mem_ioctl_set_phy_amp_bias( | ||
23677 | + struct net_device *ndev, | ||
23678 | + struct iw_request_info *info, | ||
23679 | + struct iw_param *vwrq, | ||
23680 | + char *extra) | ||
23681 | +{ | ||
23682 | + acx_device_t *adev = ndev2adev(ndev); | ||
23683 | + unsigned long flags; | ||
23684 | + u16 gpio_old; | ||
23685 | + | ||
23686 | + if (!IS_ACX100(adev)) { | ||
23687 | + /* WARNING!!! | ||
23688 | + * Removing this check *might* damage | ||
23689 | + * hardware, since we're tweaking GPIOs here after all!!! | ||
23690 | + * You've been warned... | ||
23691 | + * WARNING!!! */ | ||
23692 | + printk("acx: sorry, setting bias level for non-acx100 " | ||
23693 | + "is not supported yet\n"); | ||
23694 | + return OK; | ||
23695 | + } | ||
23696 | + | ||
23697 | + if (*extra > 7) { | ||
23698 | + printk("acx: invalid bias parameter, range is 0-7\n"); | ||
23699 | + return -EINVAL; | ||
23700 | + } | ||
23701 | + | ||
23702 | + acx_sem_lock(adev); | ||
23703 | + | ||
23704 | + /* Need to lock accesses to [IO_ACX_GPIO_OUT]: | ||
23705 | + * IRQ handler uses it to update LED */ | ||
23706 | + acx_lock(adev, flags); | ||
23707 | + gpio_old = read_reg16(adev, IO_ACX_GPIO_OUT); | ||
23708 | + write_reg16(adev, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8)); | ||
23709 | + acx_unlock(adev, flags); | ||
23710 | + | ||
23711 | + log(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old); | ||
23712 | + printk("%s: PHY power amplifier bias: old:%d, new:%d\n", | ||
23713 | + ndev->name, | ||
23714 | + (gpio_old & 0x0700) >> 8, (unsigned char)*extra); | ||
23715 | + | ||
23716 | + acx_sem_unlock(adev); | ||
23717 | + | ||
23718 | + return OK; | ||
23719 | +} | ||
23720 | + | ||
23721 | +/*************************************************************** | ||
23722 | +** acxmem_l_alloc_tx | ||
23723 | +** Actually returns a txdesc_t* ptr | ||
23724 | +** | ||
23725 | +** FIXME: in case of fragments, should allocate multiple descrs | ||
23726 | +** after figuring out how many we need and whether we still have | ||
23727 | +** sufficiently many. | ||
23728 | +*/ | ||
23729 | +tx_t* | ||
23730 | +acxmem_l_alloc_tx(acx_device_t *adev) | ||
23731 | +{ | ||
23732 | + struct txdesc *txdesc; | ||
23733 | + unsigned head; | ||
23734 | + u8 ctl8; | ||
23735 | + static int txattempts = 0; | ||
23736 | + | ||
23737 | + FN_ENTER; | ||
23738 | + | ||
23739 | + if (unlikely(!adev->tx_free)) { | ||
23740 | + printk("acx: BUG: no free txdesc left\n"); | ||
23741 | + /* | ||
23742 | + * Probably the ACX ignored a transmit attempt and now there's a packet | ||
23743 | + * sitting in the queue we think should be transmitting but the ACX doesn't | ||
23744 | + * know about. | ||
23745 | + * On the first pass, send the ACX a TxProc interrupt to try moving | ||
23746 | + * things along, and if that doesn't work (ie, we get called again) completely | ||
23747 | + * flush the transmit queue. | ||
23748 | + */ | ||
23749 | + if (txattempts < 10) { | ||
23750 | + txattempts++; | ||
23751 | + printk ("acx: trying to wake up ACX\n"); | ||
23752 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); | ||
23753 | + write_flush(adev); } | ||
23754 | + else { | ||
23755 | + txattempts = 0; | ||
23756 | + printk ("acx: flushing transmit queue.\n"); | ||
23757 | + acxmem_l_clean_txdesc_emergency (adev); | ||
23758 | + } | ||
23759 | + txdesc = NULL; | ||
23760 | + goto end; | ||
23761 | + } | ||
23762 | + | ||
23763 | + /* | ||
23764 | + * Make a quick check to see if there is transmit buffer space on | ||
23765 | + * the ACX. This can't guarantee there is enough space for the packet | ||
23766 | + * since we don't yet know how big it is, but it will prevent at least some | ||
23767 | + * annoyances. | ||
23768 | + */ | ||
23769 | + if (!adev->acx_txbuf_blocks_free) { | ||
23770 | + txdesc = NULL; | ||
23771 | + goto end; | ||
23772 | + } | ||
23773 | + | ||
23774 | + head = adev->tx_head; | ||
23775 | + /* | ||
23776 | + * txdesc points to ACX memory | ||
23777 | + */ | ||
23778 | + txdesc = get_txdesc(adev, head); | ||
23779 | + ctl8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); | ||
23780 | + | ||
23781 | + /* | ||
23782 | + * If we don't own the buffer (HOSTOWN) it is certainly not free; however, | ||
23783 | + * we may have previously thought we had enough memory to send | ||
23784 | + * a packet, allocated the buffer then gave up when we found not enough | ||
23785 | + * transmit buffer space on the ACX. In that case, HOSTOWN and | ||
23786 | + * ACXDONE will both be set. | ||
23787 | + */ | ||
23788 | + if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_HOSTOWN))) { | ||
23789 | + /* whoops, descr at current index is not free, so probably | ||
23790 | + * ring buffer already full */ | ||
23791 | + printk("acx: BUG: tx_head:%d Ctl8:0x%02X - failed to find " | ||
23792 | + "free txdesc\n", head, ctl8); | ||
23793 | + txdesc = NULL; | ||
23794 | + goto end; | ||
23795 | + } | ||
23796 | + | ||
23797 | + /* Needed in case txdesc won't be eventually submitted for tx */ | ||
23798 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_ACXDONE_HOSTOWN); | ||
23799 | + | ||
23800 | + adev->tx_free--; | ||
23801 | + log(L_BUFT, "tx: got desc %u, %u remain\n", | ||
23802 | + head, adev->tx_free); | ||
23803 | + /* Keep a few free descs between head and tail of tx ring. | ||
23804 | + ** It is not absolutely needed, just feels safer */ | ||
23805 | + if (adev->tx_free < TX_STOP_QUEUE) { | ||
23806 | + log(L_BUF, "stop queue (%u tx desc left)\n", | ||
23807 | + adev->tx_free); | ||
23808 | + acx_stop_queue(adev->ndev, NULL); | ||
23809 | + } | ||
23810 | + | ||
23811 | + /* returning current descriptor, so advance to next free one */ | ||
23812 | + adev->tx_head = (head + 1) % TX_CNT; | ||
23813 | +end: | ||
23814 | + FN_EXIT0; | ||
23815 | + | ||
23816 | + return (tx_t*)txdesc; | ||
23817 | +} | ||
23818 | + | ||
23819 | + | ||
23820 | +/*************************************************************** | ||
23821 | +** acxmem_l_dealloc_tx | ||
23822 | +** Clears out a previously allocatedvoid acxmem_l_dealloc_tx(tx_t *tx_opaque); | ||
23823 | + transmit descriptor. The ACX | ||
23824 | +** can get confused if we skip transmit descriptors in the queue, | ||
23825 | +** so when we don't need a descriptor return it to its original | ||
23826 | +** state and move the queue head pointer back. | ||
23827 | +** | ||
23828 | +*/ | ||
23829 | +void | ||
23830 | +acxmem_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque) | ||
23831 | +{ | ||
23832 | + /* | ||
23833 | + * txdesc is the address of the descriptor on the ACX. | ||
23834 | + */ | ||
23835 | + txdesc_t *txdesc = (txdesc_t*)tx_opaque; | ||
23836 | + txdesc_t tmptxdesc; | ||
23837 | + int index; | ||
23838 | + | ||
23839 | + memset (&tmptxdesc, 0, sizeof(tmptxdesc)); | ||
23840 | + tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG; | ||
23841 | + tmptxdesc.u.r1.rate = 0x0a; | ||
23842 | + | ||
23843 | + /* | ||
23844 | + * Clear out all of the transmit descriptor except for the next pointer | ||
23845 | + */ | ||
23846 | + copy_to_slavemem (adev, (u32) &(txdesc->HostMemPtr), | ||
23847 | + (u8 *) &(tmptxdesc.HostMemPtr), | ||
23848 | + sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc)); | ||
23849 | + | ||
23850 | + /* | ||
23851 | + * This is only called immediately after we've allocated, so we should | ||
23852 | + * be able to set the head back to this descriptor. | ||
23853 | + */ | ||
23854 | + index = ((u8*) txdesc - (u8*)adev->txdesc_start) / adev->txdesc_size; | ||
23855 | + printk ("acx_dealloc: moving head from %d to %d\n", adev->tx_head, index); | ||
23856 | + adev->tx_head = index; | ||
23857 | +} | ||
23858 | + | ||
23859 | + | ||
23860 | +/*********************************************************************** | ||
23861 | +*/ | ||
23862 | +void* | ||
23863 | +acxmem_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque) | ||
23864 | +{ | ||
23865 | + return get_txhostdesc(adev, (txdesc_t*)tx_opaque)->data; | ||
23866 | +} | ||
23867 | + | ||
23868 | + | ||
23869 | +/*********************************************************************** | ||
23870 | +** acxmem_l_tx_data | ||
23871 | +** | ||
23872 | +** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). | ||
23873 | +** Can be called from acx_i_start_xmit (data frames from net core). | ||
23874 | +** | ||
23875 | +** FIXME: in case of fragments, should loop over the number of | ||
23876 | +** pre-allocated tx descrs, properly setting up transfer data and | ||
23877 | +** CTL_xxx flags according to fragment number. | ||
23878 | +*/ | ||
23879 | +void | ||
23880 | +acxmem_update_queue_indicator (acx_device_t *adev, int txqueue) | ||
23881 | +{ | ||
23882 | +#ifdef USING_MORE_THAN_ONE_TRANSMIT_QUEUE | ||
23883 | + u32 indicator; | ||
23884 | + unsigned long flags; | ||
23885 | + int count; | ||
23886 | + | ||
23887 | + /* | ||
23888 | + * Can't handle an interrupt while we're fiddling with the ACX's lock, | ||
23889 | + * according to TI. The ACX is supposed to hold fw_lock for at most | ||
23890 | + * 500ns. | ||
23891 | + */ | ||
23892 | + local_irq_save (flags); | ||
23893 | + | ||
23894 | + /* | ||
23895 | + * Wait for ACX to release the lock (at most 500ns). | ||
23896 | + */ | ||
23897 | + count = 0; | ||
23898 | + while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock)) | ||
23899 | + && (count++ < 50)) { | ||
23900 | + ndelay (10); | ||
23901 | + } | ||
23902 | + if (count < 50) { | ||
23903 | + | ||
23904 | + /* | ||
23905 | + * Take out the host lock - anything non-zero will work, so don't worry about | ||
23906 | + * be/le | ||
23907 | + */ | ||
23908 | + write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 1); | ||
23909 | + | ||
23910 | + /* | ||
23911 | + * Avoid a race condition | ||
23912 | + */ | ||
23913 | + count = 0; | ||
23914 | + while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock)) | ||
23915 | + && (count++ < 50)) { | ||
23916 | + ndelay (10); | ||
23917 | + } | ||
23918 | + | ||
23919 | + if (count < 50) { | ||
23920 | + /* | ||
23921 | + * Mark the queue active | ||
23922 | + */ | ||
23923 | + indicator = read_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator)); | ||
23924 | + indicator |= cpu_to_le32 (1 << txqueue); | ||
23925 | + write_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator), indicator); | ||
23926 | + } | ||
23927 | + | ||
23928 | + /* | ||
23929 | + * Release the host lock | ||
23930 | + */ | ||
23931 | + write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 0); | ||
23932 | + | ||
23933 | + } | ||
23934 | + | ||
23935 | + /* | ||
23936 | + * Restore interrupts | ||
23937 | + */ | ||
23938 | + local_irq_restore (flags); | ||
23939 | +#endif | ||
23940 | +} | ||
23941 | + | ||
23942 | +void | ||
23943 | +acxmem_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int len) | ||
23944 | +{ | ||
23945 | + /* | ||
23946 | + * txdesc is the address on the ACX | ||
23947 | + */ | ||
23948 | + txdesc_t *txdesc = (txdesc_t*)tx_opaque; | ||
23949 | + txhostdesc_t *hostdesc1, *hostdesc2; | ||
23950 | + client_t *clt; | ||
23951 | + u16 rate_cur; | ||
23952 | + u8 Ctl_8, Ctl2_8; | ||
23953 | + u32 addr; | ||
23954 | + | ||
23955 | + FN_ENTER; | ||
23956 | + /* fw doesn't tx such packets anyhow */ | ||
23957 | + if (unlikely(len < WLAN_HDR_A3_LEN)) | ||
23958 | + goto end; | ||
23959 | + | ||
23960 | + hostdesc1 = get_txhostdesc(adev, txdesc); | ||
23961 | + /* modify flag status in separate variable to be able to write it back | ||
23962 | + * in one big swoop later (also in order to have less device memory | ||
23963 | + * accesses) */ | ||
23964 | + Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); | ||
23965 | + Ctl2_8 = 0; /* really need to init it to 0, not txdesc->Ctl2_8, it seems */ | ||
23966 | + | ||
23967 | + hostdesc2 = hostdesc1 + 1; | ||
23968 | + | ||
23969 | + /* DON'T simply set Ctl field to 0 here globally, | ||
23970 | + * it needs to maintain a consistent flag status (those are state flags!!), | ||
23971 | + * otherwise it may lead to severe disruption. Only set or reset particular | ||
23972 | + * flags at the exact moment this is needed... */ | ||
23973 | + | ||
23974 | + /* let chip do RTS/CTS handshaking before sending | ||
23975 | + * in case packet size exceeds threshold */ | ||
23976 | + if (len > adev->rts_threshold) | ||
23977 | + SET_BIT(Ctl2_8, DESC_CTL2_RTS); | ||
23978 | + else | ||
23979 | + CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS); | ||
23980 | + | ||
23981 | + switch (adev->mode) { | ||
23982 | + case ACX_MODE_0_ADHOC: | ||
23983 | + case ACX_MODE_3_AP: | ||
23984 | + clt = acx_l_sta_list_get(adev, ((wlan_hdr_t*)hostdesc1->data)->a1); | ||
23985 | + break; | ||
23986 | + case ACX_MODE_2_STA: | ||
23987 | + clt = adev->ap_client; | ||
23988 | + break; | ||
23989 | +#if 0 | ||
23990 | +/* testing was done on acx111: */ | ||
23991 | + case ACX_MODE_MONITOR: | ||
23992 | + SET_BIT(Ctl2_8, 0 | ||
23993 | +/* sends CTS to self before packet */ | ||
23994 | + + DESC_CTL2_SEQ /* don't increase sequence field */ | ||
23995 | +/* not working (looks like good fcs is still added) */ | ||
23996 | + + DESC_CTL2_FCS /* don't add the FCS */ | ||
23997 | +/* not tested */ | ||
23998 | + + DESC_CTL2_MORE_FRAG | ||
23999 | +/* not tested */ | ||
24000 | + + DESC_CTL2_RETRY /* don't increase retry field */ | ||
24001 | +/* not tested */ | ||
24002 | + + DESC_CTL2_POWER /* don't increase power mgmt. field */ | ||
24003 | +/* no effect */ | ||
24004 | + + DESC_CTL2_WEP /* encrypt this frame */ | ||
24005 | +/* not tested */ | ||
24006 | + + DESC_CTL2_DUR /* don't increase duration field */ | ||
24007 | + ); | ||
24008 | + /* fallthrough */ | ||
24009 | +#endif | ||
24010 | + default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ | ||
24011 | + clt = NULL; | ||
24012 | + break; | ||
24013 | + } | ||
24014 | + | ||
24015 | + rate_cur = clt ? clt->rate_cur : adev->rate_bcast; | ||
24016 | + if (unlikely(!rate_cur)) { | ||
24017 | + printk("acx: driver bug! bad ratemask\n"); | ||
24018 | + goto end; | ||
24019 | + } | ||
24020 | + | ||
24021 | + /* used in tx cleanup routine for auto rate and accounting: */ | ||
24022 | + put_txcr(adev, txdesc, clt, rate_cur); | ||
24023 | + | ||
24024 | + write_slavemem16 (adev, (u32) &(txdesc->total_length), cpu_to_le16(len)); | ||
24025 | + hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN); | ||
24026 | + if (IS_ACX111(adev)) { | ||
24027 | + /* note that if !txdesc->do_auto, txrate->cur | ||
24028 | + ** has only one nonzero bit */ | ||
24029 | + txdesc->u.r2.rate111 = cpu_to_le16( | ||
24030 | + rate_cur | ||
24031 | + /* WARNING: I was never able to make it work with prism54 AP. | ||
24032 | + ** It was falling down to 1Mbit where shortpre is not applicable, | ||
24033 | + ** and not working at all at "5,11 basic rates only" setting. | ||
24034 | + ** I even didn't see tx packets in radio packet capture. | ||
24035 | + ** Disabled for now --vda */ | ||
24036 | + /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */ | ||
24037 | + ); | ||
24038 | +#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS | ||
24039 | + /* should add this to rate111 above as necessary */ | ||
24040 | + | (clt->pbcc511 ? RATE111_PBCC511 : 0) | ||
24041 | +#endif | ||
24042 | + hostdesc1->length = cpu_to_le16(len); | ||
24043 | + } else { /* ACX100 */ | ||
24044 | + u8 rate_100 = clt ? clt->rate_100 : adev->rate_bcast100; | ||
24045 | + write_slavemem8 (adev, (u32) &(txdesc->u.r1.rate), rate_100); | ||
24046 | +#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS | ||
24047 | + if (clt->pbcc511) { | ||
24048 | + if (n == RATE100_5 || n == RATE100_11) | ||
24049 | + n |= RATE100_PBCC511; | ||
24050 | + } | ||
24051 | + | ||
24052 | + if (clt->shortpre && (clt->cur != RATE111_1)) | ||
24053 | + SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */ | ||
24054 | +#endif | ||
24055 | + /* set autodma and reclaim and 1st mpdu */ | ||
24056 | + SET_BIT(Ctl_8, DESC_CTL_FIRSTFRAG); | ||
24057 | + | ||
24058 | +#if ACX_FRAGMENTATION | ||
24059 | + /* SET_BIT(Ctl2_8, DESC_CTL2_MORE_FRAG); cannot set it unconditionally, needs to be set for all non-last fragments */ | ||
24060 | +#endif | ||
24061 | + hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN); | ||
24062 | + | ||
24063 | + /* | ||
24064 | + * Since we're not using autodma copy the packet data to the acx now. | ||
24065 | + * Even host descriptors point to the packet header, and the odd indexed | ||
24066 | + * descriptor following points to the packet data. | ||
24067 | + * | ||
24068 | + * The first step is to find free memory in the ACX transmit buffers. | ||
24069 | + * They don't necessarily map one to one with the transmit queue entries, | ||
24070 | + * so search through them starting just after the last one used. | ||
24071 | + */ | ||
24072 | + addr = allocate_acx_txbuf_space (adev, len); | ||
24073 | + if (addr) { | ||
24074 | + chaincopy_to_slavemem (adev, addr, hostdesc1->data, len); | ||
24075 | + } | ||
24076 | + else { | ||
24077 | + /* | ||
24078 | + * Bummer. We thought we might have enough room in the transmit | ||
24079 | + * buffers to send this packet, but it turns out we don't. alloc_tx | ||
24080 | + * has already marked this transmit descriptor as HOSTOWN and ACXDONE, | ||
24081 | + * which means the ACX will hang when it gets to this descriptor unless | ||
24082 | + * we do something about it. Having a bubble in the transmit queue just | ||
24083 | + * doesn't seem to work, so we have to reset this transmit queue entry's | ||
24084 | + * state to its original value and back up our head pointer to point | ||
24085 | + * back to this entry. | ||
24086 | + */ | ||
24087 | + hostdesc1->length = 0; | ||
24088 | + hostdesc2->length = 0; | ||
24089 | + write_slavemem16 (adev, (u32) &(txdesc->total_length), 0); | ||
24090 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG); | ||
24091 | + adev->tx_head = ((u8*) txdesc - (u8*) adev->txdesc_start) / adev->txdesc_size; | ||
24092 | + goto end; | ||
24093 | + } | ||
24094 | + /* | ||
24095 | + * Tell the ACX where the packet is. | ||
24096 | + */ | ||
24097 | + write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), addr); | ||
24098 | + | ||
24099 | + } | ||
24100 | + /* don't need to clean ack/rts statistics here, already | ||
24101 | + * done on descr cleanup */ | ||
24102 | + | ||
24103 | + /* clears HOSTOWN and ACXDONE bits, thus telling that the descriptors | ||
24104 | + * are now owned by the acx100; do this as LAST operation */ | ||
24105 | + CLEAR_BIT(Ctl_8, DESC_CTL_ACXDONE_HOSTOWN); | ||
24106 | + /* flush writes before we release hostdesc to the adapter here */ | ||
24107 | + //wmb(); | ||
24108 | + | ||
24109 | + /* write back modified flags */ | ||
24110 | + /* | ||
24111 | + * At this point Ctl_8 should just be FIRSTFRAG | ||
24112 | + */ | ||
24113 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl2_8),Ctl2_8); | ||
24114 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), Ctl_8); | ||
24115 | + /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */ | ||
24116 | + | ||
24117 | + /* | ||
24118 | + * Update the queue indicator to say there's data on the first queue. | ||
24119 | + */ | ||
24120 | + acxmem_update_queue_indicator (adev, 0); | ||
24121 | + | ||
24122 | + /* flush writes before we tell the adapter that it's its turn now */ | ||
24123 | + mmiowb(); | ||
24124 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); | ||
24125 | + write_flush(adev); | ||
24126 | + | ||
24127 | + /* log the packet content AFTER sending it, | ||
24128 | + * in order to not delay sending any further than absolutely needed | ||
24129 | + * Do separate logs for acx100/111 to have human-readable rates */ | ||
24130 | + if (unlikely(acx_debug & (L_XFER|L_DATA))) { | ||
24131 | + u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc; | ||
24132 | + if (IS_ACX111(adev)) | ||
24133 | + printk("tx: pkt (%s): len %d " | ||
24134 | + "rate %04X%s status %u\n", | ||
24135 | + acx_get_packet_type_string(le16_to_cpu(fc)), len, | ||
24136 | + le16_to_cpu(txdesc->u.r2.rate111), | ||
24137 | + (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "", | ||
24138 | + adev->status); | ||
24139 | + else | ||
24140 | + printk("tx: pkt (%s): len %d rate %03u%s status %u\n", | ||
24141 | + acx_get_packet_type_string(fc), len, | ||
24142 | + read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate)), | ||
24143 | + (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "", | ||
24144 | + adev->status); | ||
24145 | + | ||
24146 | + if (acx_debug & L_DATA) { | ||
24147 | + printk("tx: 802.11 [%d]: ", len); | ||
24148 | + acx_dump_bytes(hostdesc1->data, len); | ||
24149 | + } | ||
24150 | + } | ||
24151 | +end: | ||
24152 | + FN_EXIT0; | ||
24153 | +} | ||
24154 | + | ||
24155 | + | ||
24156 | +/*********************************************************************** | ||
24157 | +** acxmem_l_clean_txdesc | ||
24158 | +** | ||
24159 | +** This function resets the txdescs' status when the ACX100 | ||
24160 | +** signals the TX done IRQ (txdescs have been processed), starting with | ||
24161 | +** the pool index of the descriptor which we would use next, | ||
24162 | +** in order to make sure that we can be as fast as possible | ||
24163 | +** in filling new txdescs. | ||
24164 | +** Everytime we get called we know where the next packet to be cleaned is. | ||
24165 | +*/ | ||
24166 | + | ||
24167 | +#if !ACX_DEBUG | ||
24168 | +static inline void log_txbuffer(const acx_device_t *adev) {} | ||
24169 | +#else | ||
24170 | +static void | ||
24171 | +log_txbuffer(acx_device_t *adev) | ||
24172 | +{ | ||
24173 | + txdesc_t *txdesc; | ||
24174 | + int i; | ||
24175 | + u8 Ctl_8; | ||
24176 | + | ||
24177 | + /* no FN_ENTER here, we don't want that */ | ||
24178 | + /* no locks here, since it's entirely non-critical code */ | ||
24179 | + txdesc = adev->txdesc_start; | ||
24180 | + if (unlikely(!txdesc)) return; | ||
24181 | + printk("tx: desc->Ctl8's:"); | ||
24182 | + for (i = 0; i < TX_CNT; i++) { | ||
24183 | + Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); | ||
24184 | + printk(" %02X", Ctl_8); | ||
24185 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
24186 | + } | ||
24187 | + printk("\n"); | ||
24188 | +} | ||
24189 | +#endif | ||
24190 | + | ||
24191 | + | ||
24192 | +static void | ||
24193 | +handle_tx_error(acx_device_t *adev, u8 error, unsigned int finger) | ||
24194 | +{ | ||
24195 | + const char *err = "unknown error"; | ||
24196 | + | ||
24197 | + /* hmm, should we handle this as a mask | ||
24198 | + * of *several* bits? | ||
24199 | + * For now I think only caring about | ||
24200 | + * individual bits is ok... */ | ||
24201 | + switch (error) { | ||
24202 | + case 0x01: | ||
24203 | + err = "no Tx due to error in other fragment"; | ||
24204 | + adev->wstats.discard.fragment++; | ||
24205 | + break; | ||
24206 | + case 0x02: | ||
24207 | + err = "Tx aborted"; | ||
24208 | + adev->stats.tx_aborted_errors++; | ||
24209 | + break; | ||
24210 | + case 0x04: | ||
24211 | + err = "Tx desc wrong parameters"; | ||
24212 | + adev->wstats.discard.misc++; | ||
24213 | + break; | ||
24214 | + case 0x08: | ||
24215 | + err = "WEP key not found"; | ||
24216 | + adev->wstats.discard.misc++; | ||
24217 | + break; | ||
24218 | + case 0x10: | ||
24219 | + err = "MSDU lifetime timeout? - try changing " | ||
24220 | + "'iwconfig retry lifetime XXX'"; | ||
24221 | + adev->wstats.discard.misc++; | ||
24222 | + break; | ||
24223 | + case 0x20: | ||
24224 | + err = "excessive Tx retries due to either distance " | ||
24225 | + "too high or unable to Tx or Tx frame error - " | ||
24226 | + "try changing 'iwconfig txpower XXX' or " | ||
24227 | + "'sens'itivity or 'retry'"; | ||
24228 | + adev->wstats.discard.retries++; | ||
24229 | + /* Tx error 0x20 also seems to occur on | ||
24230 | + * overheating, so I'm not sure whether we | ||
24231 | + * actually want to do aggressive radio recalibration, | ||
24232 | + * since people maybe won't notice then that their hardware | ||
24233 | + * is slowly getting cooked... | ||
24234 | + * Or is it still a safe long distance from utter | ||
24235 | + * radio non-functionality despite many radio recalibs | ||
24236 | + * to final destructive overheating of the hardware? | ||
24237 | + * In this case we really should do recalib here... | ||
24238 | + * I guess the only way to find out is to do a | ||
24239 | + * potentially fatal self-experiment :-\ | ||
24240 | + * Or maybe only recalib in case we're using Tx | ||
24241 | + * rate auto (on errors switching to lower speed | ||
24242 | + * --> less heat?) or 802.11 power save mode? | ||
24243 | + * | ||
24244 | + * ok, just do it. */ | ||
24245 | + if (++adev->retry_errors_msg_ratelimit % 4 == 0) { | ||
24246 | + if (adev->retry_errors_msg_ratelimit <= 20) { | ||
24247 | + printk("%s: several excessive Tx " | ||
24248 | + "retry errors occurred, attempting " | ||
24249 | + "to recalibrate radio. Radio " | ||
24250 | + "drift might be caused by increasing " | ||
24251 | + "card temperature, please check the card " | ||
24252 | + "before it's too late!\n", | ||
24253 | + adev->ndev->name); | ||
24254 | + if (adev->retry_errors_msg_ratelimit == 20) | ||
24255 | + printk("disabling above message\n"); | ||
24256 | + } | ||
24257 | + | ||
24258 | + acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); | ||
24259 | + } | ||
24260 | + break; | ||
24261 | + case 0x40: | ||
24262 | + err = "Tx buffer overflow"; | ||
24263 | + adev->stats.tx_fifo_errors++; | ||
24264 | + break; | ||
24265 | + case 0x80: | ||
24266 | + err = "DMA error"; | ||
24267 | + adev->wstats.discard.misc++; | ||
24268 | + break; | ||
24269 | + } | ||
24270 | + adev->stats.tx_errors++; | ||
24271 | + if (adev->stats.tx_errors <= 20) | ||
24272 | + printk("%s: tx error 0x%02X, buf %02u! (%s)\n", | ||
24273 | + adev->ndev->name, error, finger, err); | ||
24274 | + else | ||
24275 | + printk("%s: tx error 0x%02X, buf %02u!\n", | ||
24276 | + adev->ndev->name, error, finger); | ||
24277 | +} | ||
24278 | + | ||
24279 | + | ||
24280 | +unsigned int | ||
24281 | +acxmem_l_clean_txdesc(acx_device_t *adev) | ||
24282 | +{ | ||
24283 | + txdesc_t *txdesc; | ||
24284 | + unsigned finger; | ||
24285 | + int num_cleaned; | ||
24286 | + u16 r111; | ||
24287 | + u8 error, ack_failures, rts_failures, rts_ok, r100, Ctl_8; | ||
24288 | + u32 acxmem; | ||
24289 | + txdesc_t tmptxdesc; | ||
24290 | + | ||
24291 | + FN_ENTER; | ||
24292 | + | ||
24293 | + /* | ||
24294 | + * Set up a template descriptor for re-initialization. The only | ||
24295 | + * things that get set are Ctl_8 and the rate, and the rate defaults | ||
24296 | + * to 1Mbps. | ||
24297 | + */ | ||
24298 | + memset (&tmptxdesc, 0, sizeof (tmptxdesc)); | ||
24299 | + tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG; | ||
24300 | + tmptxdesc.u.r1.rate = 0x0a; | ||
24301 | + | ||
24302 | + if (unlikely(acx_debug & L_DEBUG)) | ||
24303 | + log_txbuffer(adev); | ||
24304 | + | ||
24305 | + log(L_BUFT, "tx: cleaning up bufs from %u\n", adev->tx_tail); | ||
24306 | + | ||
24307 | + /* We know first descr which is not free yet. We advance it as far | ||
24308 | + ** as we see correct bits set in following descs (if next desc | ||
24309 | + ** is NOT free, we shouldn't advance at all). We know that in | ||
24310 | + ** front of tx_tail may be "holes" with isolated free descs. | ||
24311 | + ** We will catch up when all intermediate descs will be freed also */ | ||
24312 | + | ||
24313 | + finger = adev->tx_tail; | ||
24314 | + num_cleaned = 0; | ||
24315 | + while (likely(finger != adev->tx_head)) { | ||
24316 | + txdesc = get_txdesc(adev, finger); | ||
24317 | + | ||
24318 | + /* If we allocated txdesc on tx path but then decided | ||
24319 | + ** to NOT use it, then it will be left as a free "bubble" | ||
24320 | + ** in the "allocated for tx" part of the ring. | ||
24321 | + ** We may meet it on the next ring pass here. */ | ||
24322 | + | ||
24323 | + /* stop if not marked as "tx finished" and "host owned" */ | ||
24324 | + Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); | ||
24325 | + if ((Ctl_8 & DESC_CTL_ACXDONE_HOSTOWN) | ||
24326 | + != DESC_CTL_ACXDONE_HOSTOWN) { | ||
24327 | + if (unlikely(!num_cleaned)) { /* maybe remove completely */ | ||
24328 | + log(L_BUFT, "clean_txdesc: tail isn't free. " | ||
24329 | + "tail:%d head:%d\n", | ||
24330 | + adev->tx_tail, adev->tx_head); | ||
24331 | + } | ||
24332 | + break; | ||
24333 | + } | ||
24334 | + | ||
24335 | + /* remember desc values... */ | ||
24336 | + error = read_slavemem8 (adev, (u32) &(txdesc->error)); | ||
24337 | + ack_failures = read_slavemem8 (adev, (u32) &(txdesc->ack_failures)); | ||
24338 | + rts_failures = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures)); | ||
24339 | + rts_ok = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok)); | ||
24340 | + r100 = read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate)); | ||
24341 | + r111 = le16_to_cpu(read_slavemem16 (adev, (u32) &(txdesc->u.r2.rate111))); | ||
24342 | + | ||
24343 | + /* need to check for certain error conditions before we | ||
24344 | + * clean the descriptor: we still need valid descr data here */ | ||
24345 | + if (unlikely(0x30 & error)) { | ||
24346 | + /* only send IWEVTXDROP in case of retry or lifetime exceeded; | ||
24347 | + * all other errors mean we screwed up locally */ | ||
24348 | + union iwreq_data wrqu; | ||
24349 | + wlan_hdr_t *hdr; | ||
24350 | + txhostdesc_t *hostdesc; | ||
24351 | + | ||
24352 | + hostdesc = get_txhostdesc(adev, txdesc); | ||
24353 | + hdr = (wlan_hdr_t *)hostdesc->data; | ||
24354 | + MAC_COPY(wrqu.addr.sa_data, hdr->a1); | ||
24355 | + wireless_send_event(adev->ndev, IWEVTXDROP, &wrqu, NULL); | ||
24356 | + } | ||
24357 | + | ||
24358 | + /* | ||
24359 | + * Free up the transmit data buffers | ||
24360 | + */ | ||
24361 | + acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); | ||
24362 | + if (acxmem) { | ||
24363 | + reclaim_acx_txbuf_space (adev, acxmem); | ||
24364 | + } | ||
24365 | + | ||
24366 | + /* ...and free the desc by clearing all the fields | ||
24367 | + except the next pointer */ | ||
24368 | + copy_to_slavemem (adev, | ||
24369 | + (u32) &(txdesc->HostMemPtr), | ||
24370 | + (u8 *) &(tmptxdesc.HostMemPtr), | ||
24371 | + sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc) | ||
24372 | + ); | ||
24373 | + | ||
24374 | + adev->tx_free++; | ||
24375 | + num_cleaned++; | ||
24376 | + | ||
24377 | + if ((adev->tx_free >= TX_START_QUEUE) | ||
24378 | + && (adev->status == ACX_STATUS_4_ASSOCIATED) | ||
24379 | + && (acx_queue_stopped(adev->ndev)) | ||
24380 | + ) { | ||
24381 | + log(L_BUF, "tx: wake queue (avail. Tx desc %u)\n", | ||
24382 | + adev->tx_free); | ||
24383 | + acx_wake_queue(adev->ndev, NULL); | ||
24384 | + } | ||
24385 | + | ||
24386 | + /* do error checking, rate handling and logging | ||
24387 | + * AFTER having done the work, it's faster */ | ||
24388 | + | ||
24389 | + /* do rate handling */ | ||
24390 | + if (adev->rate_auto) { | ||
24391 | + struct client *clt = get_txc(adev, txdesc); | ||
24392 | + if (clt) { | ||
24393 | + u16 cur = get_txr(adev, txdesc); | ||
24394 | + if (clt->rate_cur == cur) { | ||
24395 | + acx_l_handle_txrate_auto(adev, clt, | ||
24396 | + cur, /* intended rate */ | ||
24397 | + r100, r111, /* actually used rate */ | ||
24398 | + (error & 0x30), /* was there an error? */ | ||
24399 | + TX_CNT + TX_CLEAN_BACKLOG - adev->tx_free); | ||
24400 | + } | ||
24401 | + } | ||
24402 | + } | ||
24403 | + | ||
24404 | + if (unlikely(error)) | ||
24405 | + handle_tx_error(adev, error, finger); | ||
24406 | + | ||
24407 | + if (IS_ACX111(adev)) | ||
24408 | + log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n", | ||
24409 | + finger, ack_failures, rts_failures, rts_ok, r111); | ||
24410 | + else | ||
24411 | + log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n", | ||
24412 | + finger, ack_failures, rts_failures, rts_ok, r100); | ||
24413 | + | ||
24414 | + /* update pointer for descr to be cleaned next */ | ||
24415 | + finger = (finger + 1) % TX_CNT; | ||
24416 | + } | ||
24417 | + | ||
24418 | + /* remember last position */ | ||
24419 | + adev->tx_tail = finger; | ||
24420 | +/* end: */ | ||
24421 | + FN_EXIT1(num_cleaned); | ||
24422 | + return num_cleaned; | ||
24423 | +} | ||
24424 | + | ||
24425 | +/* clean *all* Tx descriptors, and regardless of their previous state. | ||
24426 | + * Used for brute-force reset handling. */ | ||
24427 | +void | ||
24428 | +acxmem_l_clean_txdesc_emergency(acx_device_t *adev) | ||
24429 | +{ | ||
24430 | + txdesc_t *txdesc; | ||
24431 | + int i; | ||
24432 | + u32 acxmem; | ||
24433 | + | ||
24434 | + FN_ENTER; | ||
24435 | + | ||
24436 | + for (i = 0; i < TX_CNT; i++) { | ||
24437 | + txdesc = get_txdesc(adev, i); | ||
24438 | + | ||
24439 | + /* free it */ | ||
24440 | + write_slavemem8 (adev, (u32) &(txdesc->ack_failures), 0); | ||
24441 | + write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures), 0); | ||
24442 | + write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok), 0); | ||
24443 | + write_slavemem8 (adev, (u32) &(txdesc->error), 0); | ||
24444 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN); | ||
24445 | + | ||
24446 | + /* | ||
24447 | + * Clean up the memory allocated on the ACX for this transmit descriptor. | ||
24448 | + */ | ||
24449 | + acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); | ||
24450 | + if (acxmem) { | ||
24451 | + reclaim_acx_txbuf_space (adev, acxmem); | ||
24452 | + } | ||
24453 | + | ||
24454 | + write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), 0); | ||
24455 | + } | ||
24456 | + | ||
24457 | + adev->tx_free = TX_CNT; | ||
24458 | + | ||
24459 | + FN_EXIT0; | ||
24460 | +} | ||
24461 | + | ||
24462 | + | ||
24463 | +/*********************************************************************** | ||
24464 | +** acxmem_s_create_tx_host_desc_queue | ||
24465 | +*/ | ||
24466 | + | ||
24467 | +static void* | ||
24468 | +allocate(acx_device_t *adev, size_t size, dma_addr_t *phy, const char *msg) | ||
24469 | +{ | ||
24470 | + void *ptr; | ||
24471 | + ptr = kmalloc (size, GFP_KERNEL); | ||
24472 | + /* | ||
24473 | + * The ACX can't use the physical address, so we'll have to fake it | ||
24474 | + * later and it might be handy to have the virtual address. | ||
24475 | + */ | ||
24476 | + *phy = (dma_addr_t) NULL; | ||
24477 | + | ||
24478 | + if (ptr) { | ||
24479 | + log(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n", | ||
24480 | + msg, (int)size, ptr, (unsigned long long)*phy); | ||
24481 | + memset(ptr, 0, size); | ||
24482 | + return ptr; | ||
24483 | + } | ||
24484 | + printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n", | ||
24485 | + msg, (int)size); | ||
24486 | + return NULL; | ||
24487 | +} | ||
24488 | + | ||
24489 | + | ||
24490 | +/* | ||
24491 | + * In the generic slave memory access mode, most of the stuff in | ||
24492 | + * the txhostdesc_t is unused. It's only here because the rest of | ||
24493 | + * the ACX driver expects it to be since the PCI version uses indirect | ||
24494 | + * host memory organization with DMA. Since we're not using DMA the | ||
24495 | + * only use we have for the host descriptors is to store the packets | ||
24496 | + * on the way out. | ||
24497 | + */ | ||
24498 | +static int | ||
24499 | +acxmem_s_create_tx_host_desc_queue(acx_device_t *adev) | ||
24500 | +{ | ||
24501 | + txhostdesc_t *hostdesc; | ||
24502 | + u8 *txbuf; | ||
24503 | + int i; | ||
24504 | + | ||
24505 | + FN_ENTER; | ||
24506 | + | ||
24507 | + /* allocate TX buffer */ | ||
24508 | + adev->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS; | ||
24509 | + | ||
24510 | + adev->txbuf_start = allocate(adev, adev->txbuf_area_size, | ||
24511 | + &adev->txbuf_startphy, "txbuf_start"); | ||
24512 | + if (!adev->txbuf_start) | ||
24513 | + goto fail; | ||
24514 | + | ||
24515 | + /* allocate the TX host descriptor queue pool */ | ||
24516 | + adev->txhostdesc_area_size = TX_CNT * 2*sizeof(*hostdesc); | ||
24517 | + | ||
24518 | + adev->txhostdesc_start = allocate(adev, adev->txhostdesc_area_size, | ||
24519 | + &adev->txhostdesc_startphy, "txhostdesc_start"); | ||
24520 | + if (!adev->txhostdesc_start) | ||
24521 | + goto fail; | ||
24522 | + | ||
24523 | + /* check for proper alignment of TX host descriptor pool */ | ||
24524 | + if ((long) adev->txhostdesc_start & 3) { | ||
24525 | + printk("acx: driver bug: dma alloc returns unaligned address\n"); | ||
24526 | + goto fail; | ||
24527 | + } | ||
24528 | + | ||
24529 | + hostdesc = adev->txhostdesc_start; | ||
24530 | + txbuf = adev->txbuf_start; | ||
24531 | + | ||
24532 | +#if 0 | ||
24533 | +/* Each tx buffer is accessed by hardware via | ||
24534 | +** txdesc -> txhostdesc(s) -> txbuffer(s). | ||
24535 | +** We use only one txhostdesc per txdesc, but it looks like | ||
24536 | +** acx111 is buggy: it accesses second txhostdesc | ||
24537 | +** (via hostdesc.desc_phy_next field) even if | ||
24538 | +** txdesc->length == hostdesc->length and thus | ||
24539 | +** entire packet was placed into first txhostdesc. | ||
24540 | +** Due to this bug acx111 hangs unless second txhostdesc | ||
24541 | +** has le16_to_cpu(hostdesc.length) = 3 (or larger) | ||
24542 | +** Storing NULL into hostdesc.desc_phy_next | ||
24543 | +** doesn't seem to help. | ||
24544 | +** | ||
24545 | +** Update: although it worked on Xterasys XN-2522g | ||
24546 | +** with len=3 trick, WG311v2 is even more bogus, doesn't work. | ||
24547 | +** Keeping this code (#ifdef'ed out) for documentational purposes. | ||
24548 | +*/ | ||
24549 | + for (i = 0; i < TX_CNT*2; i++) { | ||
24550 | + hostdesc_phy += sizeof(*hostdesc); | ||
24551 | + if (!(i & 1)) { | ||
24552 | + hostdesc->data_phy = cpu2acx(txbuf_phy); | ||
24553 | + /* hostdesc->data_offset = ... */ | ||
24554 | + /* hostdesc->reserved = ... */ | ||
24555 | + hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); | ||
24556 | + /* hostdesc->length = ... */ | ||
24557 | + hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); | ||
24558 | + hostdesc->pNext = ptr2acx(NULL); | ||
24559 | + /* hostdesc->Status = ... */ | ||
24560 | + /* below: non-hardware fields */ | ||
24561 | + hostdesc->data = txbuf; | ||
24562 | + | ||
24563 | + txbuf += WLAN_A4FR_MAXLEN_WEP_FCS; | ||
24564 | + txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS; | ||
24565 | + } else { | ||
24566 | + /* hostdesc->data_phy = ... */ | ||
24567 | + /* hostdesc->data_offset = ... */ | ||
24568 | + /* hostdesc->reserved = ... */ | ||
24569 | + /* hostdesc->Ctl_16 = ... */ | ||
24570 | + hostdesc->length = cpu_to_le16(3); /* bug workaround */ | ||
24571 | + /* hostdesc->desc_phy_next = ... */ | ||
24572 | + /* hostdesc->pNext = ... */ | ||
24573 | + /* hostdesc->Status = ... */ | ||
24574 | + /* below: non-hardware fields */ | ||
24575 | + /* hostdesc->data = ... */ | ||
24576 | + } | ||
24577 | + hostdesc++; | ||
24578 | + } | ||
24579 | +#endif | ||
24580 | +/* We initialize two hostdescs so that they point to adjacent | ||
24581 | +** memory areas. Thus txbuf is really just a contiguous memory area */ | ||
24582 | + for (i = 0; i < TX_CNT*2; i++) { | ||
24583 | + /* ->data is a non-hardware field: */ | ||
24584 | + hostdesc->data = txbuf; | ||
24585 | + | ||
24586 | + if (!(i & 1)) { | ||
24587 | + txbuf += WLAN_HDR_A3_LEN; | ||
24588 | + } else { | ||
24589 | + txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN; | ||
24590 | + } | ||
24591 | + hostdesc++; | ||
24592 | + } | ||
24593 | + hostdesc--; | ||
24594 | + | ||
24595 | + FN_EXIT1(OK); | ||
24596 | + return OK; | ||
24597 | +fail: | ||
24598 | + printk("acx: create_tx_host_desc_queue FAILED\n"); | ||
24599 | + /* dealloc will be done by free function on error case */ | ||
24600 | + FN_EXIT1(NOT_OK); | ||
24601 | + return NOT_OK; | ||
24602 | +} | ||
24603 | + | ||
24604 | + | ||
24605 | +/*************************************************************** | ||
24606 | +** acxmem_s_create_rx_host_desc_queue | ||
24607 | +*/ | ||
24608 | +/* the whole size of a data buffer (header plus data body) | ||
24609 | + * plus 32 bytes safety offset at the end */ | ||
24610 | +#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32) | ||
24611 | + | ||
24612 | +static int | ||
24613 | +acxmem_s_create_rx_host_desc_queue(acx_device_t *adev) | ||
24614 | +{ | ||
24615 | + rxhostdesc_t *hostdesc; | ||
24616 | + rxbuffer_t *rxbuf; | ||
24617 | + int i; | ||
24618 | + | ||
24619 | + FN_ENTER; | ||
24620 | + | ||
24621 | + /* allocate the RX host descriptor queue pool */ | ||
24622 | + adev->rxhostdesc_area_size = RX_CNT * sizeof(*hostdesc); | ||
24623 | + | ||
24624 | + adev->rxhostdesc_start = allocate(adev, adev->rxhostdesc_area_size, | ||
24625 | + &adev->rxhostdesc_startphy, "rxhostdesc_start"); | ||
24626 | + if (!adev->rxhostdesc_start) | ||
24627 | + goto fail; | ||
24628 | + | ||
24629 | + /* check for proper alignment of RX host descriptor pool */ | ||
24630 | + if ((long) adev->rxhostdesc_start & 3) { | ||
24631 | + printk("acx: driver bug: dma alloc returns unaligned address\n"); | ||
24632 | + goto fail; | ||
24633 | + } | ||
24634 | + | ||
24635 | + /* allocate Rx buffer pool which will be used by the acx | ||
24636 | + * to store the whole content of the received frames in it */ | ||
24637 | + adev->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE; | ||
24638 | + | ||
24639 | + adev->rxbuf_start = allocate(adev, adev->rxbuf_area_size, | ||
24640 | + &adev->rxbuf_startphy, "rxbuf_start"); | ||
24641 | + if (!adev->rxbuf_start) | ||
24642 | + goto fail; | ||
24643 | + | ||
24644 | + rxbuf = adev->rxbuf_start; | ||
24645 | + hostdesc = adev->rxhostdesc_start; | ||
24646 | + | ||
24647 | + /* don't make any popular C programming pointer arithmetic mistakes | ||
24648 | + * here, otherwise I'll kill you... | ||
24649 | + * (and don't dare asking me why I'm warning you about that...) */ | ||
24650 | + for (i = 0; i < RX_CNT; i++) { | ||
24651 | + hostdesc->data = rxbuf; | ||
24652 | + hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE); | ||
24653 | + rxbuf++; | ||
24654 | + hostdesc++; | ||
24655 | + } | ||
24656 | + hostdesc--; | ||
24657 | + FN_EXIT1(OK); | ||
24658 | + return OK; | ||
24659 | +fail: | ||
24660 | + printk("acx: create_rx_host_desc_queue FAILED\n"); | ||
24661 | + /* dealloc will be done by free function on error case */ | ||
24662 | + FN_EXIT1(NOT_OK); | ||
24663 | + return NOT_OK; | ||
24664 | +} | ||
24665 | + | ||
24666 | + | ||
24667 | +/*************************************************************** | ||
24668 | +** acxmem_s_create_hostdesc_queues | ||
24669 | +*/ | ||
24670 | +int | ||
24671 | +acxmem_s_create_hostdesc_queues(acx_device_t *adev) | ||
24672 | +{ | ||
24673 | + int result; | ||
24674 | + result = acxmem_s_create_tx_host_desc_queue(adev); | ||
24675 | + if (OK != result) return result; | ||
24676 | + result = acxmem_s_create_rx_host_desc_queue(adev); | ||
24677 | + return result; | ||
24678 | +} | ||
24679 | + | ||
24680 | + | ||
24681 | +/*************************************************************** | ||
24682 | +** acxmem_create_tx_desc_queue | ||
24683 | +*/ | ||
24684 | +static void | ||
24685 | +acxmem_create_tx_desc_queue(acx_device_t *adev, u32 tx_queue_start) | ||
24686 | +{ | ||
24687 | + txdesc_t *txdesc; | ||
24688 | + u32 clr; | ||
24689 | + int i; | ||
24690 | + | ||
24691 | + FN_ENTER; | ||
24692 | + | ||
24693 | + if (IS_ACX100(adev)) | ||
24694 | + adev->txdesc_size = sizeof(*txdesc); | ||
24695 | + else | ||
24696 | + /* the acx111 txdesc is 4 bytes larger */ | ||
24697 | + adev->txdesc_size = sizeof(*txdesc) + 4; | ||
24698 | + | ||
24699 | + /* | ||
24700 | + * This refers to an ACX address, not one of ours | ||
24701 | + */ | ||
24702 | + adev->txdesc_start = (txdesc_t *) tx_queue_start; | ||
24703 | + | ||
24704 | + log(L_DEBUG, "adev->txdesc_start=%p\n", | ||
24705 | + adev->txdesc_start); | ||
24706 | + | ||
24707 | + adev->tx_free = TX_CNT; | ||
24708 | + /* done by memset: adev->tx_head = 0; */ | ||
24709 | + /* done by memset: adev->tx_tail = 0; */ | ||
24710 | + txdesc = adev->txdesc_start; | ||
24711 | + | ||
24712 | + if (IS_ACX111(adev)) { | ||
24713 | + /* ACX111 has a preinitialized Tx buffer! */ | ||
24714 | + /* loop over whole send pool */ | ||
24715 | + /* FIXME: do we have to do the hostmemptr stuff here?? */ | ||
24716 | + for (i = 0; i < TX_CNT; i++) { | ||
24717 | + txdesc->Ctl_8 = DESC_CTL_HOSTOWN; | ||
24718 | + /* reserve two (hdr desc and payload desc) */ | ||
24719 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
24720 | + } | ||
24721 | + } else { | ||
24722 | + /* ACX100 Tx buffer needs to be initialized by us */ | ||
24723 | + /* clear whole send pool. sizeof is safe here (we are acx100) */ | ||
24724 | + | ||
24725 | + /* | ||
24726 | + * adev->txdesc_start refers to device memory, so we can't write | ||
24727 | + * directly to it. | ||
24728 | + */ | ||
24729 | + clr = (u32) adev->txdesc_start; | ||
24730 | + while (clr < (u32) adev->txdesc_start + (TX_CNT * sizeof(*txdesc))) { | ||
24731 | + write_slavemem32 (adev, clr, 0); | ||
24732 | + clr += 4; | ||
24733 | + } | ||
24734 | + | ||
24735 | + /* loop over whole send pool */ | ||
24736 | + for (i = 0; i < TX_CNT; i++) { | ||
24737 | + log(L_DEBUG, "configure card tx descriptor: 0x%p, " | ||
24738 | + "size: 0x%X\n", txdesc, adev->txdesc_size); | ||
24739 | + | ||
24740 | + /* initialise ctl */ | ||
24741 | + /* | ||
24742 | + * No auto DMA here | ||
24743 | + */ | ||
24744 | + write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), | ||
24745 | + (u8) (DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG)); | ||
24746 | + /* done by memset(0): txdesc->Ctl2_8 = 0; */ | ||
24747 | + | ||
24748 | + /* point to next txdesc */ | ||
24749 | + write_slavemem32 (adev, (u32) &(txdesc->pNextDesc), | ||
24750 | + (u32) cpu_to_le32 ((u8 *) txdesc + adev->txdesc_size)); | ||
24751 | + | ||
24752 | + /* go to the next one */ | ||
24753 | + /* ++ is safe here (we are acx100) */ | ||
24754 | + txdesc++; | ||
24755 | + } | ||
24756 | + /* go back to the last one */ | ||
24757 | + txdesc--; | ||
24758 | + /* and point to the first making it a ring buffer */ | ||
24759 | + write_slavemem32 (adev, (u32) &(txdesc->pNextDesc), | ||
24760 | + (u32) cpu_to_le32 (tx_queue_start)); | ||
24761 | + } | ||
24762 | + FN_EXIT0; | ||
24763 | +} | ||
24764 | + | ||
24765 | + | ||
24766 | +/*************************************************************** | ||
24767 | +** acxmem_create_rx_desc_queue | ||
24768 | +*/ | ||
24769 | +static void | ||
24770 | +acxmem_create_rx_desc_queue(acx_device_t *adev, u32 rx_queue_start) | ||
24771 | +{ | ||
24772 | + rxdesc_t *rxdesc; | ||
24773 | + u32 mem_offs; | ||
24774 | + int i; | ||
24775 | + | ||
24776 | + FN_ENTER; | ||
24777 | + | ||
24778 | + /* done by memset: adev->rx_tail = 0; */ | ||
24779 | + | ||
24780 | + /* ACX111 doesn't need any further config: preconfigures itself. | ||
24781 | + * Simply print ring buffer for debugging */ | ||
24782 | + if (IS_ACX111(adev)) { | ||
24783 | + /* rxdesc_start already set here */ | ||
24784 | + | ||
24785 | + adev->rxdesc_start = (rxdesc_t *) rx_queue_start; | ||
24786 | + | ||
24787 | + rxdesc = adev->rxdesc_start; | ||
24788 | + for (i = 0; i < RX_CNT; i++) { | ||
24789 | + log(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc); | ||
24790 | + rxdesc = adev->rxdesc_start = (rxdesc_t *) | ||
24791 | + acx2cpu(rxdesc->pNextDesc); | ||
24792 | + } | ||
24793 | + } else { | ||
24794 | + /* we didn't pre-calculate rxdesc_start in case of ACX100 */ | ||
24795 | + /* rxdesc_start should be right AFTER Tx pool */ | ||
24796 | + adev->rxdesc_start = (rxdesc_t *) | ||
24797 | + ((u8 *) adev->txdesc_start + (TX_CNT * sizeof(txdesc_t))); | ||
24798 | + /* NB: sizeof(txdesc_t) above is valid because we know | ||
24799 | + ** we are in if (acx100) block. Beware of cut-n-pasting elsewhere! | ||
24800 | + ** acx111's txdesc is larger! */ | ||
24801 | + | ||
24802 | + mem_offs = (u32) adev->rxdesc_start; | ||
24803 | + while (mem_offs < (u32) adev->rxdesc_start + (RX_CNT * sizeof (*rxdesc))) { | ||
24804 | + write_slavemem32 (adev, mem_offs, 0); | ||
24805 | + mem_offs += 4; | ||
24806 | + } | ||
24807 | + | ||
24808 | + /* loop over whole receive pool */ | ||
24809 | + rxdesc = adev->rxdesc_start; | ||
24810 | + for (i = 0; i < RX_CNT; i++) { | ||
24811 | + log(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc); | ||
24812 | + /* point to next rxdesc */ | ||
24813 | + write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc), | ||
24814 | + (u32) cpu_to_le32 ((u8 *) rxdesc + sizeof(*rxdesc))); | ||
24815 | + /* go to the next one */ | ||
24816 | + rxdesc++; | ||
24817 | + } | ||
24818 | + /* go to the last one */ | ||
24819 | + rxdesc--; | ||
24820 | + | ||
24821 | + /* and point to the first making it a ring buffer */ | ||
24822 | + write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc), | ||
24823 | + (u32) cpu_to_le32 (rx_queue_start)); | ||
24824 | + } | ||
24825 | + FN_EXIT0; | ||
24826 | +} | ||
24827 | + | ||
24828 | + | ||
24829 | +/*************************************************************** | ||
24830 | +** acxmem_create_desc_queues | ||
24831 | +*/ | ||
24832 | +void | ||
24833 | +acxmem_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start) | ||
24834 | +{ | ||
24835 | + u32 *p; | ||
24836 | + int i; | ||
24837 | + | ||
24838 | + acxmem_create_tx_desc_queue(adev, tx_queue_start); | ||
24839 | + acxmem_create_rx_desc_queue(adev, rx_queue_start); | ||
24840 | + p = (u32 *) adev->acx_queue_indicator; | ||
24841 | + for (i = 0; i < 4; i++) { | ||
24842 | + write_slavemem32 (adev, (u32) p, 0); | ||
24843 | + p++; | ||
24844 | + } | ||
24845 | +} | ||
24846 | + | ||
24847 | + | ||
24848 | +/*************************************************************** | ||
24849 | +** acxmem_s_proc_diag_output | ||
24850 | +*/ | ||
24851 | +char* | ||
24852 | +acxmem_s_proc_diag_output(char *p, acx_device_t *adev) | ||
24853 | +{ | ||
24854 | + const char *rtl, *thd, *ttl; | ||
24855 | + txdesc_t *txdesc; | ||
24856 | + u8 Ctl_8; | ||
24857 | + rxdesc_t *rxdesc; | ||
24858 | + int i; | ||
24859 | + u32 tmp; | ||
24860 | + txdesc_t txd; | ||
24861 | + u8 buf[0x200]; | ||
24862 | + int j, k; | ||
24863 | + | ||
24864 | + FN_ENTER; | ||
24865 | + | ||
24866 | +#if DUMP_MEM_DURING_DIAG > 0 | ||
24867 | + dump_acxmem (adev, 0, 0x10000); | ||
24868 | + panic ("dump finished"); | ||
24869 | +#endif | ||
24870 | + | ||
24871 | + p += sprintf(p, "** Rx buf **\n"); | ||
24872 | + rxdesc = adev->rxdesc_start; | ||
24873 | + if (rxdesc) for (i = 0; i < RX_CNT; i++) { | ||
24874 | + rtl = (i == adev->rx_tail) ? " [tail]" : ""; | ||
24875 | + Ctl_8 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); | ||
24876 | + if (Ctl_8 & DESC_CTL_HOSTOWN) | ||
24877 | + p += sprintf(p, "%02u (%02x) FULL%s\n", i, Ctl_8, rtl); | ||
24878 | + else | ||
24879 | + p += sprintf(p, "%02u (%02x) empty%s\n", i, Ctl_8, rtl); | ||
24880 | + rxdesc++; | ||
24881 | + } | ||
24882 | + p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", adev->tx_free, | ||
24883 | + acx_queue_stopped(adev->ndev) ? "STOPPED" : "running"); | ||
24884 | + | ||
24885 | + p += sprintf(p, "** Tx buf %d blocks total, %d available, free list head %04x\n", | ||
24886 | + adev->acx_txbuf_numblocks, adev->acx_txbuf_blocks_free, adev->acx_txbuf_free); | ||
24887 | + txdesc = adev->txdesc_start; | ||
24888 | + if (txdesc) { | ||
24889 | + for (i = 0; i < TX_CNT; i++) { | ||
24890 | + thd = (i == adev->tx_head) ? " [head]" : ""; | ||
24891 | + ttl = (i == adev->tx_tail) ? " [tail]" : ""; | ||
24892 | + copy_from_slavemem (adev, (u8 *) &txd, (u32) txdesc, sizeof (txd)); | ||
24893 | + Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); | ||
24894 | + if (Ctl_8 & DESC_CTL_ACXDONE) | ||
24895 | + p += sprintf(p, "%02u ready to free (%02X)%s%s", i, Ctl_8, thd, ttl); | ||
24896 | + else if (Ctl_8 & DESC_CTL_HOSTOWN) | ||
24897 | + p += sprintf(p, "%02u available (%02X)%s%s", i, Ctl_8, thd, ttl); | ||
24898 | + else | ||
24899 | + p += sprintf(p, "%02u busy (%02X)%s%s", i, Ctl_8, thd, ttl); | ||
24900 | + tmp = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); | ||
24901 | + if (tmp) { | ||
24902 | + p += sprintf (p, " %04x", tmp); | ||
24903 | + while ((tmp = read_slavemem32 (adev, (u32) tmp)) != 0x02000000) { | ||
24904 | + tmp <<= 5; | ||
24905 | + p += sprintf (p, " %04x", tmp); | ||
24906 | + } | ||
24907 | + } | ||
24908 | + p += sprintf (p, "\n"); | ||
24909 | + p += sprintf (p, " %04x: %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %02x %02x %02x %02x\n" | ||
24910 | + "%02x %02x %02x %02x %04x\n", | ||
24911 | + (u32) txdesc, | ||
24912 | + txd.pNextDesc.v, txd.HostMemPtr.v, txd.AcxMemPtr.v, txd.tx_time, | ||
24913 | + txd.total_length, txd.Reserved, | ||
24914 | + txd.dummy[0], txd.dummy[1], txd.dummy[2], txd.dummy[3], | ||
24915 | + txd.Ctl_8, txd.Ctl2_8, txd.error, txd.ack_failures, | ||
24916 | + txd.u.rts.rts_failures, txd.u.rts.rts_ok, txd.u.r1.rate, txd.u.r1.queue_ctrl, | ||
24917 | + txd.queue_info | ||
24918 | + ); | ||
24919 | + if (txd.AcxMemPtr.v) { | ||
24920 | + copy_from_slavemem (adev, buf, txd.AcxMemPtr.v, sizeof (buf)); | ||
24921 | + for (j = 0; (j < txd.total_length) && (j<(sizeof(buf)-4)); j+=16) { | ||
24922 | + p += sprintf (p, " "); | ||
24923 | + for (k = 0; (k < 16) && (j+k < txd.total_length); k++) { | ||
24924 | + p += sprintf (p, " %02x", buf[j+k+4]); | ||
24925 | + } | ||
24926 | + p += sprintf (p, "\n"); | ||
24927 | + } | ||
24928 | + } | ||
24929 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
24930 | + } | ||
24931 | + } | ||
24932 | + | ||
24933 | + p += sprintf(p, | ||
24934 | + "\n" | ||
24935 | + "** Generic slave data **\n" | ||
24936 | + "irq_mask 0x%04x irq_status 0x%04x irq on acx 0x%04x\n" | ||
24937 | + "txbuf_start 0x%p, txbuf_area_size %u\n" | ||
24938 | + "txdesc_size %u, txdesc_start 0x%p\n" | ||
24939 | + "txhostdesc_start 0x%p, txhostdesc_area_size %u\n" | ||
24940 | + "txbuf start 0x%04x, txbuf size %d\n" | ||
24941 | + "rxdesc_start 0x%p\n" | ||
24942 | + "rxhostdesc_start 0x%p, rxhostdesc_area_size %u\n" | ||
24943 | + "rxbuf_start 0x%p, rxbuf_area_size %u\n", | ||
24944 | + adev->irq_mask, adev->irq_status, read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES), | ||
24945 | + adev->txbuf_start, adev->txbuf_area_size, | ||
24946 | + adev->txdesc_size, adev->txdesc_start, | ||
24947 | + adev->txhostdesc_start, adev->txhostdesc_area_size, | ||
24948 | + adev->acx_txbuf_start, adev->acx_txbuf_numblocks * adev->memblocksize, | ||
24949 | + adev->rxdesc_start, | ||
24950 | + adev->rxhostdesc_start, adev->rxhostdesc_area_size, | ||
24951 | + adev->rxbuf_start, adev->rxbuf_area_size); | ||
24952 | + FN_EXIT0; | ||
24953 | + return p; | ||
24954 | +} | ||
24955 | + | ||
24956 | + | ||
24957 | +/*********************************************************************** | ||
24958 | +*/ | ||
24959 | +int | ||
24960 | +acxmem_proc_eeprom_output(char *buf, acx_device_t *adev) | ||
24961 | +{ | ||
24962 | + char *p = buf; | ||
24963 | + int i; | ||
24964 | + | ||
24965 | + FN_ENTER; | ||
24966 | + | ||
24967 | + for (i = 0; i < 0x400; i++) { | ||
24968 | + acxmem_read_eeprom_byte(adev, i, p++); | ||
24969 | + } | ||
24970 | + | ||
24971 | + FN_EXIT1(p - buf); | ||
24972 | + return p - buf; | ||
24973 | +} | ||
24974 | + | ||
24975 | + | ||
24976 | +/*********************************************************************** | ||
24977 | +*/ | ||
24978 | +void | ||
24979 | +acxmem_set_interrupt_mask(acx_device_t *adev) | ||
24980 | +{ | ||
24981 | + if (IS_ACX111(adev)) { | ||
24982 | + adev->irq_mask = (u16) ~(0 | ||
24983 | + | HOST_INT_RX_DATA | ||
24984 | + | HOST_INT_TX_COMPLETE | ||
24985 | + /* | HOST_INT_TX_XFER */ | ||
24986 | + /* | HOST_INT_RX_COMPLETE */ | ||
24987 | + /* | HOST_INT_DTIM */ | ||
24988 | + /* | HOST_INT_BEACON */ | ||
24989 | + /* | HOST_INT_TIMER */ | ||
24990 | + /* | HOST_INT_KEY_NOT_FOUND */ | ||
24991 | + | HOST_INT_IV_ICV_FAILURE | ||
24992 | + | HOST_INT_CMD_COMPLETE | ||
24993 | + | HOST_INT_INFO | ||
24994 | + | HOST_INT_OVERFLOW | ||
24995 | + /* | HOST_INT_PROCESS_ERROR */ | ||
24996 | + | HOST_INT_SCAN_COMPLETE | ||
24997 | + | HOST_INT_FCS_THRESHOLD | ||
24998 | + | HOST_INT_UNKNOWN | ||
24999 | + ); | ||
25000 | + /* Or else acx100 won't signal cmd completion, right? */ | ||
25001 | + adev->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */ | ||
25002 | + } else { | ||
25003 | + adev->irq_mask = (u16) ~(0 | ||
25004 | + | HOST_INT_RX_DATA | ||
25005 | + | HOST_INT_TX_COMPLETE | ||
25006 | + /* | HOST_INT_TX_XFER */ | ||
25007 | + /* | HOST_INT_RX_COMPLETE */ | ||
25008 | + /* | HOST_INT_DTIM */ | ||
25009 | + /* | HOST_INT_BEACON */ | ||
25010 | + /* | HOST_INT_TIMER */ | ||
25011 | + /* | HOST_INT_KEY_NOT_FOUND */ | ||
25012 | + /* | HOST_INT_IV_ICV_FAILURE */ | ||
25013 | + | HOST_INT_CMD_COMPLETE | ||
25014 | + | HOST_INT_INFO | ||
25015 | + /* | HOST_INT_OVERFLOW */ | ||
25016 | + /* | HOST_INT_PROCESS_ERROR */ | ||
25017 | + | HOST_INT_SCAN_COMPLETE | ||
25018 | + /* | HOST_INT_FCS_THRESHOLD */ | ||
25019 | + /* | HOST_INT_BEACON_MISSED */ | ||
25020 | + ); | ||
25021 | + adev->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */ | ||
25022 | + } | ||
25023 | +} | ||
25024 | + | ||
25025 | + | ||
25026 | +/*********************************************************************** | ||
25027 | +*/ | ||
25028 | +int | ||
25029 | +acx100mem_s_set_tx_level(acx_device_t *adev, u8 level_dbm) | ||
25030 | +{ | ||
25031 | + struct acx111_ie_tx_level tx_level; | ||
25032 | + | ||
25033 | + /* since it can be assumed that at least the Maxim radio has a | ||
25034 | + * maximum power output of 20dBm and since it also can be | ||
25035 | + * assumed that these values drive the DAC responsible for | ||
25036 | + * setting the linear Tx level, I'd guess that these values | ||
25037 | + * should be the corresponding linear values for a dBm value, | ||
25038 | + * in other words: calculate the values from that formula: | ||
25039 | + * Y [dBm] = 10 * log (X [mW]) | ||
25040 | + * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm) | ||
25041 | + * and you're done... | ||
25042 | + * Hopefully that's ok, but you never know if we're actually | ||
25043 | + * right... (especially since Windows XP doesn't seem to show | ||
25044 | + * actual Tx dBm values :-P) */ | ||
25045 | + | ||
25046 | + /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the | ||
25047 | + * values are EXACTLY mW!!! Not sure about RFMD and others, | ||
25048 | + * though... */ | ||
25049 | + static const u8 dbm2val_maxim[21] = { | ||
25050 | + 63, 63, 63, 62, | ||
25051 | + 61, 61, 60, 60, | ||
25052 | + 59, 58, 57, 55, | ||
25053 | + 53, 50, 47, 43, | ||
25054 | + 38, 31, 23, 13, | ||
25055 | + 0 | ||
25056 | + }; | ||
25057 | + static const u8 dbm2val_rfmd[21] = { | ||
25058 | + 0, 0, 0, 1, | ||
25059 | + 2, 2, 3, 3, | ||
25060 | + 4, 5, 6, 8, | ||
25061 | + 10, 13, 16, 20, | ||
25062 | + 25, 32, 41, 50, | ||
25063 | + 63 | ||
25064 | + }; | ||
25065 | + const u8 *table; | ||
25066 | + | ||
25067 | + switch (adev->radio_type) { | ||
25068 | + case RADIO_MAXIM_0D: | ||
25069 | + table = &dbm2val_maxim[0]; | ||
25070 | + break; | ||
25071 | + case RADIO_RFMD_11: | ||
25072 | + case RADIO_RALINK_15: | ||
25073 | + table = &dbm2val_rfmd[0]; | ||
25074 | + break; | ||
25075 | + default: | ||
25076 | + printk("%s: unknown/unsupported radio type, " | ||
25077 | + "cannot modify tx power level yet!\n", | ||
25078 | + adev->ndev->name); | ||
25079 | + return NOT_OK; | ||
25080 | + } | ||
25081 | + /* | ||
25082 | + * The hx4700 EEPROM, at least, only supports 1 power setting. The configure | ||
25083 | + * routine matches the PA bias with the gain, so just use its default value. | ||
25084 | + * The values are: 0x2b for the gain and 0x03 for the PA bias. The firmware | ||
25085 | + * writes the gain level to the Tx gain control DAC and the PA bias to the Maxim | ||
25086 | + * radio's PA bias register. The firmware limits itself to 0 - 64 when writing to the | ||
25087 | + * gain control DAC. | ||
25088 | + * | ||
25089 | + * Physically between the ACX and the radio, higher Tx gain control DAC values result | ||
25090 | + * in less power output; 0 volts to the Maxim radio results in the highest output power | ||
25091 | + * level, which I'm assuming matches up with 0 in the Tx Gain DAC register. | ||
25092 | + * | ||
25093 | + * Although there is only the 1 power setting, one of the radio firmware functions adjusts | ||
25094 | + * the transmit power level up and down. That function is called by the ACX FIQ handler | ||
25095 | + * under certain conditions. | ||
25096 | + */ | ||
25097 | + tx_level.level = 1; | ||
25098 | + //return acx_s_configure(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); | ||
25099 | + | ||
25100 | + printk("%s: changing radio power level to %u dBm (%u)\n", | ||
25101 | + adev->ndev->name, level_dbm, table[level_dbm]); | ||
25102 | + acxmem_s_write_phy_reg(adev, 0x11, table[level_dbm]); | ||
25103 | + | ||
25104 | + return 0; | ||
25105 | +} | ||
25106 | + | ||
25107 | + | ||
25108 | +static struct platform_driver | ||
25109 | +acxmem_drv_id = { | ||
25110 | + .driver = { | ||
25111 | + .name = "acx-mem", | ||
25112 | + }, | ||
25113 | + .probe = acxmem_e_probe, | ||
25114 | + .remove = __devexit_p(acxmem_e_remove), | ||
25115 | +#ifdef CONFIG_PM | ||
25116 | + .suspend = acxmem_e_suspend, | ||
25117 | + .resume = acxmem_e_resume | ||
25118 | +#endif /* CONFIG_PM */ | ||
25119 | +}; | ||
25120 | + | ||
25121 | + | ||
25122 | +/*********************************************************************** | ||
25123 | +** acxmem_e_init_module | ||
25124 | +** | ||
25125 | +** Module initialization routine, called once at module load time | ||
25126 | +*/ | ||
25127 | +int __init | ||
25128 | +acxmem_e_init_module(void) | ||
25129 | +{ | ||
25130 | + int res; | ||
25131 | + | ||
25132 | + FN_ENTER; | ||
25133 | + | ||
25134 | +#if (ACX_IO_WIDTH==32) | ||
25135 | + printk("acx: compiled to use 32bit I/O access. " | ||
25136 | + "I/O timing issues might occur, such as " | ||
25137 | + "non-working firmware upload. Report them\n"); | ||
25138 | +#else | ||
25139 | + printk("acx: compiled to use 16bit I/O access only " | ||
25140 | + "(compatibility mode)\n"); | ||
25141 | +#endif | ||
25142 | + | ||
25143 | +#ifdef __LITTLE_ENDIAN | ||
25144 | +#define ENDIANNESS_STRING "running on a little-endian CPU\n" | ||
25145 | +#else | ||
25146 | +#define ENDIANNESS_STRING "running on a BIG-ENDIAN CPU\n" | ||
25147 | +#endif | ||
25148 | + log(L_INIT, | ||
25149 | + ENDIANNESS_STRING | ||
25150 | + "PCI module " ACX_RELEASE " initialized, " | ||
25151 | + "waiting for cards to probe...\n" | ||
25152 | + ); | ||
25153 | + | ||
25154 | + res = platform_driver_register (&acxmem_drv_id); | ||
25155 | + FN_EXIT1(res); | ||
25156 | + return res; | ||
25157 | +} | ||
25158 | + | ||
25159 | + | ||
25160 | +/*********************************************************************** | ||
25161 | +** acxmem_e_cleanup_module | ||
25162 | +** | ||
25163 | +** Called at module unload time. This is our last chance to | ||
25164 | +** clean up after ourselves. | ||
25165 | +*/ | ||
25166 | +void __exit | ||
25167 | +acxmem_e_cleanup_module(void) | ||
25168 | +{ | ||
25169 | + FN_ENTER; | ||
25170 | + | ||
25171 | + printk ("cleanup_module\n"); | ||
25172 | + platform_driver_unregister( &acxmem_drv_id ); | ||
25173 | + | ||
25174 | + FN_EXIT0; | ||
25175 | +} | ||
25176 | + | ||
25177 | +void acxmem_e_release(struct device *dev) { | ||
25178 | +} | ||
25179 | + | ||
25180 | +MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" ); | ||
25181 | +MODULE_DESCRIPTION( "ACX Slave Memory Driver" ); | ||
25182 | +MODULE_LICENSE( "GPL" ); | ||
25183 | + | ||
25184 | Index: linux-2.6.22/drivers/net/wireless/acx/pci.c | ||
25185 | =================================================================== | ||
25186 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
25187 | +++ linux-2.6.22/drivers/net/wireless/acx/pci.c 2007-08-23 18:34:19.000000000 +0200 | ||
25188 | @@ -0,0 +1,4234 @@ | ||
25189 | +/*********************************************************************** | ||
25190 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
25191 | +** | ||
25192 | +** The contents of this file are subject to the Mozilla Public | ||
25193 | +** License Version 1.1 (the "License"); you may not use this file | ||
25194 | +** except in compliance with the License. You may obtain a copy of | ||
25195 | +** the License at http://www.mozilla.org/MPL/ | ||
25196 | +** | ||
25197 | +** Software distributed under the License is distributed on an "AS | ||
25198 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
25199 | +** implied. See the License for the specific language governing | ||
25200 | +** rights and limitations under the License. | ||
25201 | +** | ||
25202 | +** Alternatively, the contents of this file may be used under the | ||
25203 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
25204 | +** case the provisions of the GPL are applicable instead of the | ||
25205 | +** above. If you wish to allow the use of your version of this file | ||
25206 | +** only under the terms of the GPL and not to allow others to use | ||
25207 | +** your version of this file under the MPL, indicate your decision | ||
25208 | +** by deleting the provisions above and replace them with the notice | ||
25209 | +** and other provisions required by the GPL. If you do not delete | ||
25210 | +** the provisions above, a recipient may use your version of this | ||
25211 | +** file under either the MPL or the GPL. | ||
25212 | +** --------------------------------------------------------------------- | ||
25213 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
25214 | +** made directly to: | ||
25215 | +** | ||
25216 | +** acx100-users@lists.sf.net | ||
25217 | +** http://acx100.sf.net | ||
25218 | +** --------------------------------------------------------------------- | ||
25219 | +*/ | ||
25220 | +#define ACX_PCI 1 | ||
25221 | + | ||
25222 | +#include <linux/version.h> | ||
25223 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) | ||
25224 | +#include <linux/config.h> | ||
25225 | +#endif | ||
25226 | + | ||
25227 | +/* Linux 2.6.18+ uses <linux/utsrelease.h> */ | ||
25228 | +#ifndef UTS_RELEASE | ||
25229 | +#include <linux/utsrelease.h> | ||
25230 | +#endif | ||
25231 | + | ||
25232 | +#include <linux/compiler.h> /* required for Lx 2.6.8 ?? */ | ||
25233 | +#include <linux/kernel.h> | ||
25234 | +#include <linux/module.h> | ||
25235 | +#include <linux/moduleparam.h> | ||
25236 | +#include <linux/sched.h> | ||
25237 | +#include <linux/types.h> | ||
25238 | +#include <linux/skbuff.h> | ||
25239 | +#include <linux/slab.h> | ||
25240 | +#include <linux/if_arp.h> | ||
25241 | +#include <linux/rtnetlink.h> | ||
25242 | +#include <linux/wireless.h> | ||
25243 | +#include <net/iw_handler.h> | ||
25244 | +#include <linux/netdevice.h> | ||
25245 | +#include <linux/ioport.h> | ||
25246 | +#include <linux/pci.h> | ||
25247 | +#include <linux/pm.h> | ||
25248 | +#include <linux/vmalloc.h> | ||
25249 | +#include <linux/dma-mapping.h> | ||
25250 | + | ||
25251 | +#include "acx.h" | ||
25252 | + | ||
25253 | + | ||
25254 | +/*********************************************************************** | ||
25255 | +*/ | ||
25256 | +#define PCI_TYPE (PCI_USES_MEM | PCI_ADDR0 | PCI_NO_ACPI_WAKE) | ||
25257 | +#define PCI_ACX100_REGION1 0x01 | ||
25258 | +#define PCI_ACX100_REGION1_SIZE 0x1000 /* Memory size - 4K bytes */ | ||
25259 | +#define PCI_ACX100_REGION2 0x02 | ||
25260 | +#define PCI_ACX100_REGION2_SIZE 0x10000 /* Memory size - 64K bytes */ | ||
25261 | + | ||
25262 | +#define PCI_ACX111_REGION1 0x00 | ||
25263 | +#define PCI_ACX111_REGION1_SIZE 0x2000 /* Memory size - 8K bytes */ | ||
25264 | +#define PCI_ACX111_REGION2 0x01 | ||
25265 | +#define PCI_ACX111_REGION2_SIZE 0x20000 /* Memory size - 128K bytes */ | ||
25266 | + | ||
25267 | +/* Texas Instruments Vendor ID */ | ||
25268 | +#define PCI_VENDOR_ID_TI 0x104c | ||
25269 | + | ||
25270 | +/* ACX100 22Mb/s WLAN controller */ | ||
25271 | +#define PCI_DEVICE_ID_TI_TNETW1100A 0x8400 | ||
25272 | +#define PCI_DEVICE_ID_TI_TNETW1100B 0x8401 | ||
25273 | + | ||
25274 | +/* ACX111 54Mb/s WLAN controller */ | ||
25275 | +#define PCI_DEVICE_ID_TI_TNETW1130 0x9066 | ||
25276 | + | ||
25277 | +/* PCI Class & Sub-Class code, Network-'Other controller' */ | ||
25278 | +#define PCI_CLASS_NETWORK_OTHERS 0x0280 | ||
25279 | + | ||
25280 | +#define CARD_EEPROM_ID_SIZE 6 | ||
25281 | + | ||
25282 | +#ifndef PCI_D0 | ||
25283 | +/* From include/linux/pci.h */ | ||
25284 | +#define PCI_D0 0 | ||
25285 | +#define PCI_D1 1 | ||
25286 | +#define PCI_D2 2 | ||
25287 | +#define PCI_D3hot 3 | ||
25288 | +#define PCI_D3cold 4 | ||
25289 | +#define PCI_UNKNOWN 5 | ||
25290 | +#define PCI_POWER_ERROR -1 | ||
25291 | +#endif | ||
25292 | + | ||
25293 | + | ||
25294 | +/*********************************************************************** | ||
25295 | +*/ | ||
25296 | +static void acxpci_i_tx_timeout(struct net_device *ndev); | ||
25297 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
25298 | +static irqreturn_t acxpci_i_interrupt(int irq, void *dev_id); | ||
25299 | +#else | ||
25300 | +static irqreturn_t acxpci_i_interrupt(int irq, void *dev_id, struct pt_regs *regs); | ||
25301 | +#endif | ||
25302 | +static void acxpci_i_set_multicast_list(struct net_device *ndev); | ||
25303 | + | ||
25304 | +static int acxpci_e_open(struct net_device *ndev); | ||
25305 | +static int acxpci_e_close(struct net_device *ndev); | ||
25306 | +static void acxpci_s_up(struct net_device *ndev); | ||
25307 | +static void acxpci_s_down(struct net_device *ndev); | ||
25308 | + | ||
25309 | + | ||
25310 | +/*********************************************************************** | ||
25311 | +** Register access | ||
25312 | +*/ | ||
25313 | + | ||
25314 | +/* Pick one */ | ||
25315 | +/* #define INLINE_IO static */ | ||
25316 | +#define INLINE_IO static inline | ||
25317 | + | ||
25318 | +INLINE_IO u32 | ||
25319 | +read_reg32(acx_device_t *adev, unsigned int offset) | ||
25320 | +{ | ||
25321 | +#if ACX_IO_WIDTH == 32 | ||
25322 | + return readl((u8 *)adev->iobase + adev->io[offset]); | ||
25323 | +#else | ||
25324 | + return readw((u8 *)adev->iobase + adev->io[offset]) | ||
25325 | + + (readw((u8 *)adev->iobase + adev->io[offset] + 2) << 16); | ||
25326 | +#endif | ||
25327 | +} | ||
25328 | + | ||
25329 | +INLINE_IO u16 | ||
25330 | +read_reg16(acx_device_t *adev, unsigned int offset) | ||
25331 | +{ | ||
25332 | + return readw((u8 *)adev->iobase + adev->io[offset]); | ||
25333 | +} | ||
25334 | + | ||
25335 | +INLINE_IO u8 | ||
25336 | +read_reg8(acx_device_t *adev, unsigned int offset) | ||
25337 | +{ | ||
25338 | + return readb((u8 *)adev->iobase + adev->io[offset]); | ||
25339 | +} | ||
25340 | + | ||
25341 | +INLINE_IO void | ||
25342 | +write_reg32(acx_device_t *adev, unsigned int offset, u32 val) | ||
25343 | +{ | ||
25344 | +#if ACX_IO_WIDTH == 32 | ||
25345 | + writel(val, (u8 *)adev->iobase + adev->io[offset]); | ||
25346 | +#else | ||
25347 | + writew(val & 0xffff, (u8 *)adev->iobase + adev->io[offset]); | ||
25348 | + writew(val >> 16, (u8 *)adev->iobase + adev->io[offset] + 2); | ||
25349 | +#endif | ||
25350 | +} | ||
25351 | + | ||
25352 | +INLINE_IO void | ||
25353 | +write_reg16(acx_device_t *adev, unsigned int offset, u16 val) | ||
25354 | +{ | ||
25355 | + writew(val, (u8 *)adev->iobase + adev->io[offset]); | ||
25356 | +} | ||
25357 | + | ||
25358 | +INLINE_IO void | ||
25359 | +write_reg8(acx_device_t *adev, unsigned int offset, u8 val) | ||
25360 | +{ | ||
25361 | + writeb(val, (u8 *)adev->iobase + adev->io[offset]); | ||
25362 | +} | ||
25363 | + | ||
25364 | +/* Handle PCI posting properly: | ||
25365 | + * Make sure that writes reach the adapter in case they require to be executed | ||
25366 | + * *before* the next write, by reading a random (and safely accessible) register. | ||
25367 | + * This call has to be made if there is no read following (which would flush the data | ||
25368 | + * to the adapter), yet the written data has to reach the adapter immediately. */ | ||
25369 | +INLINE_IO void | ||
25370 | +write_flush(acx_device_t *adev) | ||
25371 | +{ | ||
25372 | + /* readb(adev->iobase + adev->io[IO_ACX_INFO_MAILBOX_OFFS]); */ | ||
25373 | + /* faster version (accesses the first register, IO_ACX_SOFT_RESET, | ||
25374 | + * which should also be safe): */ | ||
25375 | + readb(adev->iobase); | ||
25376 | +} | ||
25377 | + | ||
25378 | +INLINE_IO int | ||
25379 | +adev_present(acx_device_t *adev) | ||
25380 | +{ | ||
25381 | + /* fast version (accesses the first register, IO_ACX_SOFT_RESET, | ||
25382 | + * which should be safe): */ | ||
25383 | + return readl(adev->iobase) != 0xffffffff; | ||
25384 | +} | ||
25385 | + | ||
25386 | + | ||
25387 | +/*********************************************************************** | ||
25388 | +*/ | ||
25389 | +static inline txdesc_t* | ||
25390 | +get_txdesc(acx_device_t *adev, int index) | ||
25391 | +{ | ||
25392 | + return (txdesc_t*) (((u8*)adev->txdesc_start) + index * adev->txdesc_size); | ||
25393 | +} | ||
25394 | + | ||
25395 | +static inline txdesc_t* | ||
25396 | +advance_txdesc(acx_device_t *adev, txdesc_t* txdesc, int inc) | ||
25397 | +{ | ||
25398 | + return (txdesc_t*) (((u8*)txdesc) + inc * adev->txdesc_size); | ||
25399 | +} | ||
25400 | + | ||
25401 | +static txhostdesc_t* | ||
25402 | +get_txhostdesc(acx_device_t *adev, txdesc_t* txdesc) | ||
25403 | +{ | ||
25404 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
25405 | + if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { | ||
25406 | + printk("bad txdesc ptr %p\n", txdesc); | ||
25407 | + return NULL; | ||
25408 | + } | ||
25409 | + index /= adev->txdesc_size; | ||
25410 | + if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { | ||
25411 | + printk("bad txdesc ptr %p\n", txdesc); | ||
25412 | + return NULL; | ||
25413 | + } | ||
25414 | + return &adev->txhostdesc_start[index*2]; | ||
25415 | +} | ||
25416 | + | ||
25417 | +static inline client_t* | ||
25418 | +get_txc(acx_device_t *adev, txdesc_t* txdesc) | ||
25419 | +{ | ||
25420 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
25421 | + if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { | ||
25422 | + printk("bad txdesc ptr %p\n", txdesc); | ||
25423 | + return NULL; | ||
25424 | + } | ||
25425 | + index /= adev->txdesc_size; | ||
25426 | + if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { | ||
25427 | + printk("bad txdesc ptr %p\n", txdesc); | ||
25428 | + return NULL; | ||
25429 | + } | ||
25430 | + return adev->txc[index]; | ||
25431 | +} | ||
25432 | + | ||
25433 | +static inline u16 | ||
25434 | +get_txr(acx_device_t *adev, txdesc_t* txdesc) | ||
25435 | +{ | ||
25436 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
25437 | + index /= adev->txdesc_size; | ||
25438 | + return adev->txr[index]; | ||
25439 | +} | ||
25440 | + | ||
25441 | +static inline void | ||
25442 | +put_txcr(acx_device_t *adev, txdesc_t* txdesc, client_t* c, u16 r111) | ||
25443 | +{ | ||
25444 | + int index = (u8*)txdesc - (u8*)adev->txdesc_start; | ||
25445 | + if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { | ||
25446 | + printk("bad txdesc ptr %p\n", txdesc); | ||
25447 | + return; | ||
25448 | + } | ||
25449 | + index /= adev->txdesc_size; | ||
25450 | + if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { | ||
25451 | + printk("bad txdesc ptr %p\n", txdesc); | ||
25452 | + return; | ||
25453 | + } | ||
25454 | + adev->txc[index] = c; | ||
25455 | + adev->txr[index] = r111; | ||
25456 | +} | ||
25457 | + | ||
25458 | + | ||
25459 | +/*********************************************************************** | ||
25460 | +** EEPROM and PHY read/write helpers | ||
25461 | +*/ | ||
25462 | +/*********************************************************************** | ||
25463 | +** acxpci_read_eeprom_byte | ||
25464 | +** | ||
25465 | +** Function called to read an octet in the EEPROM. | ||
25466 | +** | ||
25467 | +** This function is used by acxpci_e_probe to check if the | ||
25468 | +** connected card is a legal one or not. | ||
25469 | +** | ||
25470 | +** Arguments: | ||
25471 | +** adev ptr to acx_device structure | ||
25472 | +** addr address to read in the EEPROM | ||
25473 | +** charbuf ptr to a char. This is where the read octet | ||
25474 | +** will be stored | ||
25475 | +*/ | ||
25476 | +int | ||
25477 | +acxpci_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf) | ||
25478 | +{ | ||
25479 | + int result; | ||
25480 | + int count; | ||
25481 | + | ||
25482 | + write_reg32(adev, IO_ACX_EEPROM_CFG, 0); | ||
25483 | + write_reg32(adev, IO_ACX_EEPROM_ADDR, addr); | ||
25484 | + write_flush(adev); | ||
25485 | + write_reg32(adev, IO_ACX_EEPROM_CTL, 2); | ||
25486 | + | ||
25487 | + count = 0xffff; | ||
25488 | + while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { | ||
25489 | + /* scheduling away instead of CPU burning loop | ||
25490 | + * doesn't seem to work here at all: | ||
25491 | + * awful delay, sometimes also failure. | ||
25492 | + * Doesn't matter anyway (only small delay). */ | ||
25493 | + if (unlikely(!--count)) { | ||
25494 | + printk("%s: timeout waiting for EEPROM read\n", | ||
25495 | + adev->ndev->name); | ||
25496 | + result = NOT_OK; | ||
25497 | + goto fail; | ||
25498 | + } | ||
25499 | + cpu_relax(); | ||
25500 | + } | ||
25501 | + | ||
25502 | + *charbuf = read_reg8(adev, IO_ACX_EEPROM_DATA); | ||
25503 | + log(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf); | ||
25504 | + result = OK; | ||
25505 | + | ||
25506 | +fail: | ||
25507 | + return result; | ||
25508 | +} | ||
25509 | + | ||
25510 | + | ||
25511 | +/*********************************************************************** | ||
25512 | +** We don't lock hw accesses here since we never r/w eeprom in IRQ | ||
25513 | +** Note: this function sleeps only because of GFP_KERNEL alloc | ||
25514 | +*/ | ||
25515 | +#ifdef UNUSED | ||
25516 | +int | ||
25517 | +acxpci_s_write_eeprom(acx_device_t *adev, u32 addr, u32 len, const u8 *charbuf) | ||
25518 | +{ | ||
25519 | + u8 *data_verify = NULL; | ||
25520 | + unsigned long flags; | ||
25521 | + int count, i; | ||
25522 | + int result = NOT_OK; | ||
25523 | + u16 gpio_orig; | ||
25524 | + | ||
25525 | + printk("acx: WARNING! I would write to EEPROM now. " | ||
25526 | + "Since I really DON'T want to unless you know " | ||
25527 | + "what you're doing (THIS CODE WILL PROBABLY " | ||
25528 | + "NOT WORK YET!), I will abort that now. And " | ||
25529 | + "definitely make sure to make a " | ||
25530 | + "/proc/driver/acx_wlan0_eeprom backup copy first!!! " | ||
25531 | + "(the EEPROM content includes the PCI config header!! " | ||
25532 | + "If you kill important stuff, then you WILL " | ||
25533 | + "get in trouble and people DID get in trouble already)\n"); | ||
25534 | + return OK; | ||
25535 | + | ||
25536 | + FN_ENTER; | ||
25537 | + | ||
25538 | + data_verify = kmalloc(len, GFP_KERNEL); | ||
25539 | + if (!data_verify) { | ||
25540 | + goto end; | ||
25541 | + } | ||
25542 | + | ||
25543 | + /* first we need to enable the OE (EEPROM Output Enable) GPIO line | ||
25544 | + * to be able to write to the EEPROM. | ||
25545 | + * NOTE: an EEPROM writing success has been reported, | ||
25546 | + * but you probably have to modify GPIO_OUT, too, | ||
25547 | + * and you probably need to activate a different GPIO | ||
25548 | + * line instead! */ | ||
25549 | + gpio_orig = read_reg16(adev, IO_ACX_GPIO_OE); | ||
25550 | + write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig & ~1); | ||
25551 | + write_flush(adev); | ||
25552 | + | ||
25553 | + /* ok, now start writing the data out */ | ||
25554 | + for (i = 0; i < len; i++) { | ||
25555 | + write_reg32(adev, IO_ACX_EEPROM_CFG, 0); | ||
25556 | + write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); | ||
25557 | + write_reg32(adev, IO_ACX_EEPROM_DATA, *(charbuf + i)); | ||
25558 | + write_flush(adev); | ||
25559 | + write_reg32(adev, IO_ACX_EEPROM_CTL, 1); | ||
25560 | + | ||
25561 | + count = 0xffff; | ||
25562 | + while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { | ||
25563 | + if (unlikely(!--count)) { | ||
25564 | + printk("WARNING, DANGER!!! " | ||
25565 | + "Timeout waiting for EEPROM write\n"); | ||
25566 | + goto end; | ||
25567 | + } | ||
25568 | + cpu_relax(); | ||
25569 | + } | ||
25570 | + } | ||
25571 | + | ||
25572 | + /* disable EEPROM writing */ | ||
25573 | + write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig); | ||
25574 | + write_flush(adev); | ||
25575 | + | ||
25576 | + /* now start a verification run */ | ||
25577 | + for (i = 0; i < len; i++) { | ||
25578 | + write_reg32(adev, IO_ACX_EEPROM_CFG, 0); | ||
25579 | + write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); | ||
25580 | + write_flush(adev); | ||
25581 | + write_reg32(adev, IO_ACX_EEPROM_CTL, 2); | ||
25582 | + | ||
25583 | + count = 0xffff; | ||
25584 | + while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { | ||
25585 | + if (unlikely(!--count)) { | ||
25586 | + printk("timeout waiting for EEPROM read\n"); | ||
25587 | + goto end; | ||
25588 | + } | ||
25589 | + cpu_relax(); | ||
25590 | + } | ||
25591 | + | ||
25592 | + data_verify[i] = read_reg16(adev, IO_ACX_EEPROM_DATA); | ||
25593 | + } | ||
25594 | + | ||
25595 | + if (0 == memcmp(charbuf, data_verify, len)) | ||
25596 | + result = OK; /* read data matches, success */ | ||
25597 | + | ||
25598 | +end: | ||
25599 | + kfree(data_verify); | ||
25600 | + FN_EXIT1(result); | ||
25601 | + return result; | ||
25602 | +} | ||
25603 | +#endif /* UNUSED */ | ||
25604 | + | ||
25605 | + | ||
25606 | +/*********************************************************************** | ||
25607 | +** acxpci_s_read_phy_reg | ||
25608 | +** | ||
25609 | +** Messing with rx/tx disabling and enabling here | ||
25610 | +** (write_reg32(adev, IO_ACX_ENABLE, 0b000000xx)) kills traffic | ||
25611 | +*/ | ||
25612 | +int | ||
25613 | +acxpci_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf) | ||
25614 | +{ | ||
25615 | + int result = NOT_OK; | ||
25616 | + int count; | ||
25617 | + | ||
25618 | + FN_ENTER; | ||
25619 | + | ||
25620 | + write_reg32(adev, IO_ACX_PHY_ADDR, reg); | ||
25621 | + write_flush(adev); | ||
25622 | + write_reg32(adev, IO_ACX_PHY_CTL, 2); | ||
25623 | + | ||
25624 | + count = 0xffff; | ||
25625 | + while (read_reg32(adev, IO_ACX_PHY_CTL)) { | ||
25626 | + /* scheduling away instead of CPU burning loop | ||
25627 | + * doesn't seem to work here at all: | ||
25628 | + * awful delay, sometimes also failure. | ||
25629 | + * Doesn't matter anyway (only small delay). */ | ||
25630 | + if (unlikely(!--count)) { | ||
25631 | + printk("%s: timeout waiting for phy read\n", | ||
25632 | + adev->ndev->name); | ||
25633 | + *charbuf = 0; | ||
25634 | + goto fail; | ||
25635 | + } | ||
25636 | + cpu_relax(); | ||
25637 | + } | ||
25638 | + | ||
25639 | + log(L_DEBUG, "count was %u\n", count); | ||
25640 | + *charbuf = read_reg8(adev, IO_ACX_PHY_DATA); | ||
25641 | + | ||
25642 | + log(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg); | ||
25643 | + result = OK; | ||
25644 | + goto fail; /* silence compiler warning */ | ||
25645 | +fail: | ||
25646 | + FN_EXIT1(result); | ||
25647 | + return result; | ||
25648 | +} | ||
25649 | + | ||
25650 | + | ||
25651 | +/*********************************************************************** | ||
25652 | +*/ | ||
25653 | +int | ||
25654 | +acxpci_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value) | ||
25655 | +{ | ||
25656 | + FN_ENTER; | ||
25657 | + | ||
25658 | + /* mprusko said that 32bit accesses result in distorted sensitivity | ||
25659 | + * on his card. Unconfirmed, looks like it's not true (most likely since we | ||
25660 | + * now properly flush writes). */ | ||
25661 | + write_reg32(adev, IO_ACX_PHY_DATA, value); | ||
25662 | + write_reg32(adev, IO_ACX_PHY_ADDR, reg); | ||
25663 | + write_flush(adev); | ||
25664 | + write_reg32(adev, IO_ACX_PHY_CTL, 1); | ||
25665 | + write_flush(adev); | ||
25666 | + log(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg); | ||
25667 | + | ||
25668 | + FN_EXIT1(OK); | ||
25669 | + return OK; | ||
25670 | +} | ||
25671 | + | ||
25672 | + | ||
25673 | +#define NO_AUTO_INCREMENT 1 | ||
25674 | + | ||
25675 | +/*********************************************************************** | ||
25676 | +** acxpci_s_write_fw | ||
25677 | +** | ||
25678 | +** Write the firmware image into the card. | ||
25679 | +** | ||
25680 | +** Arguments: | ||
25681 | +** adev wlan device structure | ||
25682 | +** fw_image firmware image. | ||
25683 | +** | ||
25684 | +** Returns: | ||
25685 | +** 1 firmware image corrupted | ||
25686 | +** 0 success | ||
25687 | +*/ | ||
25688 | +static int | ||
25689 | +acxpci_s_write_fw(acx_device_t *adev, const firmware_image_t *fw_image, u32 offset) | ||
25690 | +{ | ||
25691 | + int len, size; | ||
25692 | + u32 sum, v32; | ||
25693 | + /* we skip the first four bytes which contain the control sum */ | ||
25694 | + const u8 *p = (u8*)fw_image + 4; | ||
25695 | + | ||
25696 | + /* start the image checksum by adding the image size value */ | ||
25697 | + sum = p[0]+p[1]+p[2]+p[3]; | ||
25698 | + p += 4; | ||
25699 | + | ||
25700 | + write_reg32(adev, IO_ACX_SLV_END_CTL, 0); | ||
25701 | + | ||
25702 | +#if NO_AUTO_INCREMENT | ||
25703 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ | ||
25704 | +#else | ||
25705 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ | ||
25706 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ | ||
25707 | + write_flush(adev); | ||
25708 | +#endif | ||
25709 | + | ||
25710 | + len = 0; | ||
25711 | + size = le32_to_cpu(fw_image->size) & (~3); | ||
25712 | + | ||
25713 | + while (likely(len < size)) { | ||
25714 | + v32 = be32_to_cpu(*(u32*)p); | ||
25715 | + sum += p[0]+p[1]+p[2]+p[3]; | ||
25716 | + p += 4; | ||
25717 | + len += 4; | ||
25718 | + | ||
25719 | +#if NO_AUTO_INCREMENT | ||
25720 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); | ||
25721 | + write_flush(adev); | ||
25722 | +#endif | ||
25723 | + write_reg32(adev, IO_ACX_SLV_MEM_DATA, v32); | ||
25724 | + } | ||
25725 | + | ||
25726 | + log(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n", | ||
25727 | + size, sum, le32_to_cpu(fw_image->chksum)); | ||
25728 | + | ||
25729 | + /* compare our checksum with the stored image checksum */ | ||
25730 | + return (sum != le32_to_cpu(fw_image->chksum)); | ||
25731 | +} | ||
25732 | + | ||
25733 | + | ||
25734 | +/*********************************************************************** | ||
25735 | +** acxpci_s_validate_fw | ||
25736 | +** | ||
25737 | +** Compare the firmware image given with | ||
25738 | +** the firmware image written into the card. | ||
25739 | +** | ||
25740 | +** Arguments: | ||
25741 | +** adev wlan device structure | ||
25742 | +** fw_image firmware image. | ||
25743 | +** | ||
25744 | +** Returns: | ||
25745 | +** NOT_OK firmware image corrupted or not correctly written | ||
25746 | +** OK success | ||
25747 | +*/ | ||
25748 | +static int | ||
25749 | +acxpci_s_validate_fw(acx_device_t *adev, const firmware_image_t *fw_image, | ||
25750 | + u32 offset) | ||
25751 | +{ | ||
25752 | + u32 sum, v32, w32; | ||
25753 | + int len, size; | ||
25754 | + int result = OK; | ||
25755 | + /* we skip the first four bytes which contain the control sum */ | ||
25756 | + const u8 *p = (u8*)fw_image + 4; | ||
25757 | + | ||
25758 | + /* start the image checksum by adding the image size value */ | ||
25759 | + sum = p[0]+p[1]+p[2]+p[3]; | ||
25760 | + p += 4; | ||
25761 | + | ||
25762 | + write_reg32(adev, IO_ACX_SLV_END_CTL, 0); | ||
25763 | + | ||
25764 | +#if NO_AUTO_INCREMENT | ||
25765 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ | ||
25766 | +#else | ||
25767 | + write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ | ||
25768 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ | ||
25769 | +#endif | ||
25770 | + | ||
25771 | + len = 0; | ||
25772 | + size = le32_to_cpu(fw_image->size) & (~3); | ||
25773 | + | ||
25774 | + while (likely(len < size)) { | ||
25775 | + v32 = be32_to_cpu(*(u32*)p); | ||
25776 | + p += 4; | ||
25777 | + len += 4; | ||
25778 | + | ||
25779 | +#if NO_AUTO_INCREMENT | ||
25780 | + write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); | ||
25781 | +#endif | ||
25782 | + w32 = read_reg32(adev, IO_ACX_SLV_MEM_DATA); | ||
25783 | + | ||
25784 | + if (unlikely(w32 != v32)) { | ||
25785 | + printk("acx: FATAL: firmware upload: " | ||
25786 | + "data parts at offset %d don't match (0x%08X vs. 0x%08X)! " | ||
25787 | + "I/O timing issues or defective memory, with DWL-xx0+? " | ||
25788 | + "ACX_IO_WIDTH=16 may help. Please report\n", | ||
25789 | + len, v32, w32); | ||
25790 | + result = NOT_OK; | ||
25791 | + break; | ||
25792 | + } | ||
25793 | + | ||
25794 | + sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24); | ||
25795 | + } | ||
25796 | + | ||
25797 | + /* sum control verification */ | ||
25798 | + if (result != NOT_OK) { | ||
25799 | + if (sum != le32_to_cpu(fw_image->chksum)) { | ||
25800 | + printk("acx: FATAL: firmware upload: " | ||
25801 | + "checksums don't match!\n"); | ||
25802 | + result = NOT_OK; | ||
25803 | + } | ||
25804 | + } | ||
25805 | + | ||
25806 | + return result; | ||
25807 | +} | ||
25808 | + | ||
25809 | + | ||
25810 | +/*********************************************************************** | ||
25811 | +** acxpci_s_upload_fw | ||
25812 | +** | ||
25813 | +** Called from acx_reset_dev | ||
25814 | +*/ | ||
25815 | +static int | ||
25816 | +acxpci_s_upload_fw(acx_device_t *adev) | ||
25817 | +{ | ||
25818 | + firmware_image_t *fw_image = NULL; | ||
25819 | + int res = NOT_OK; | ||
25820 | + int try; | ||
25821 | + u32 file_size; | ||
25822 | + char filename[sizeof("tiacx1NNcNN")]; | ||
25823 | + | ||
25824 | + FN_ENTER; | ||
25825 | + | ||
25826 | + /* print exact chipset and radio ID to make sure people really get a clue on which files exactly they are supposed to provide, | ||
25827 | + * since firmware loading is the biggest enduser PITA with these chipsets. | ||
25828 | + * Not printing radio ID in 0xHEX in order to not confuse them into wrong file naming */ | ||
25829 | + printk( "acx: need to load firmware for acx1%02d chipset with radio ID %02x, please provide via firmware hotplug:\n" | ||
25830 | + "acx: either one file only (<c>ombined firmware image file, radio-specific) or two files (radio-less base image file *plus* separate <r>adio-specific extension file)\n", | ||
25831 | + IS_ACX111(adev)*11, adev->radio_type); | ||
25832 | + | ||
25833 | + /* Try combined, then main image */ | ||
25834 | + adev->need_radio_fw = 0; | ||
25835 | + snprintf(filename, sizeof(filename), "tiacx1%02dc%02X", | ||
25836 | + IS_ACX111(adev)*11, adev->radio_type); | ||
25837 | + | ||
25838 | + fw_image = acx_s_read_fw(&adev->pdev->dev, filename, &file_size); | ||
25839 | + if (!fw_image) { | ||
25840 | + adev->need_radio_fw = 1; | ||
25841 | + filename[sizeof("tiacx1NN")-1] = '\0'; | ||
25842 | + fw_image = acx_s_read_fw(&adev->pdev->dev, filename, &file_size); | ||
25843 | + if (!fw_image) { | ||
25844 | + FN_EXIT1(NOT_OK); | ||
25845 | + return NOT_OK; | ||
25846 | + } | ||
25847 | + } | ||
25848 | + | ||
25849 | + for (try = 1; try <= 5; try++) { | ||
25850 | + res = acxpci_s_write_fw(adev, fw_image, 0); | ||
25851 | + log(L_DEBUG|L_INIT, "acx_write_fw (main/combined): %d\n", res); | ||
25852 | + if (OK == res) { | ||
25853 | + res = acxpci_s_validate_fw(adev, fw_image, 0); | ||
25854 | + log(L_DEBUG|L_INIT, "acx_validate_fw " | ||
25855 | + "(main/combined): %d\n", res); | ||
25856 | + } | ||
25857 | + | ||
25858 | + if (OK == res) { | ||
25859 | + SET_BIT(adev->dev_state_mask, ACX_STATE_FW_LOADED); | ||
25860 | + break; | ||
25861 | + } | ||
25862 | + printk("acx: firmware upload attempt #%d FAILED, " | ||
25863 | + "retrying...\n", try); | ||
25864 | + acx_s_msleep(1000); /* better wait for a while... */ | ||
25865 | + } | ||
25866 | + | ||
25867 | + vfree(fw_image); | ||
25868 | + | ||
25869 | + FN_EXIT1(res); | ||
25870 | + return res; | ||
25871 | +} | ||
25872 | + | ||
25873 | + | ||
25874 | +/*********************************************************************** | ||
25875 | +** acxpci_s_upload_radio | ||
25876 | +** | ||
25877 | +** Uploads the appropriate radio module firmware into the card. | ||
25878 | +*/ | ||
25879 | +int | ||
25880 | +acxpci_s_upload_radio(acx_device_t *adev) | ||
25881 | +{ | ||
25882 | + acx_ie_memmap_t mm; | ||
25883 | + firmware_image_t *radio_image; | ||
25884 | + acx_cmd_radioinit_t radioinit; | ||
25885 | + int res = NOT_OK; | ||
25886 | + int try; | ||
25887 | + u32 offset; | ||
25888 | + u32 size; | ||
25889 | + char filename[sizeof("tiacx1NNrNN")]; | ||
25890 | + | ||
25891 | + if (!adev->need_radio_fw) return OK; | ||
25892 | + | ||
25893 | + FN_ENTER; | ||
25894 | + | ||
25895 | + acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); | ||
25896 | + offset = le32_to_cpu(mm.CodeEnd); | ||
25897 | + | ||
25898 | + snprintf(filename, sizeof(filename), "tiacx1%02dr%02X", | ||
25899 | + IS_ACX111(adev)*11, | ||
25900 | + adev->radio_type); | ||
25901 | + radio_image = acx_s_read_fw(&adev->pdev->dev, filename, &size); | ||
25902 | + if (!radio_image) { | ||
25903 | + printk("acx: can't load radio module '%s'\n", filename); | ||
25904 | + goto fail; | ||
25905 | + } | ||
25906 | + | ||
25907 | + acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0); | ||
25908 | + | ||
25909 | + for (try = 1; try <= 5; try++) { | ||
25910 | + res = acxpci_s_write_fw(adev, radio_image, offset); | ||
25911 | + log(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res); | ||
25912 | + if (OK == res) { | ||
25913 | + res = acxpci_s_validate_fw(adev, radio_image, offset); | ||
25914 | + log(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res); | ||
25915 | + } | ||
25916 | + | ||
25917 | + if (OK == res) | ||
25918 | + break; | ||
25919 | + printk("acx: radio firmware upload attempt #%d FAILED, " | ||
25920 | + "retrying...\n", try); | ||
25921 | + acx_s_msleep(1000); /* better wait for a while... */ | ||
25922 | + } | ||
25923 | + | ||
25924 | + acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0); | ||
25925 | + radioinit.offset = cpu_to_le32(offset); | ||
25926 | + /* no endian conversion needed, remains in card CPU area: */ | ||
25927 | + radioinit.len = radio_image->size; | ||
25928 | + | ||
25929 | + vfree(radio_image); | ||
25930 | + | ||
25931 | + if (OK != res) | ||
25932 | + goto fail; | ||
25933 | + | ||
25934 | + /* will take a moment so let's have a big timeout */ | ||
25935 | + acx_s_issue_cmd_timeo(adev, ACX1xx_CMD_RADIOINIT, | ||
25936 | + &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000)); | ||
25937 | + | ||
25938 | + res = acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); | ||
25939 | +fail: | ||
25940 | + FN_EXIT1(res); | ||
25941 | + return res; | ||
25942 | +} | ||
25943 | + | ||
25944 | + | ||
25945 | +/*********************************************************************** | ||
25946 | +** acxpci_l_reset_mac | ||
25947 | +** | ||
25948 | +** MAC will be reset | ||
25949 | +** Call context: reset_dev | ||
25950 | +*/ | ||
25951 | +static void | ||
25952 | +acxpci_l_reset_mac(acx_device_t *adev) | ||
25953 | +{ | ||
25954 | + u16 temp; | ||
25955 | + | ||
25956 | + FN_ENTER; | ||
25957 | + | ||
25958 | + /* halt eCPU */ | ||
25959 | + temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1; | ||
25960 | + write_reg16(adev, IO_ACX_ECPU_CTRL, temp); | ||
25961 | + | ||
25962 | + /* now do soft reset of eCPU, set bit */ | ||
25963 | + temp = read_reg16(adev, IO_ACX_SOFT_RESET) | 0x1; | ||
25964 | + log(L_DEBUG, "%s: enable soft reset...\n", __func__); | ||
25965 | + write_reg16(adev, IO_ACX_SOFT_RESET, temp); | ||
25966 | + write_flush(adev); | ||
25967 | + | ||
25968 | + /* now clear bit again: deassert eCPU reset */ | ||
25969 | + log(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__); | ||
25970 | + write_reg16(adev, IO_ACX_SOFT_RESET, temp & ~0x1); | ||
25971 | + | ||
25972 | + /* now start a burst read from initial EEPROM */ | ||
25973 | + temp = read_reg16(adev, IO_ACX_EE_START) | 0x1; | ||
25974 | + write_reg16(adev, IO_ACX_EE_START, temp); | ||
25975 | + write_flush(adev); | ||
25976 | + | ||
25977 | + FN_EXIT0; | ||
25978 | +} | ||
25979 | + | ||
25980 | + | ||
25981 | +/*********************************************************************** | ||
25982 | +** acxpci_s_verify_init | ||
25983 | +*/ | ||
25984 | +static int | ||
25985 | +acxpci_s_verify_init(acx_device_t *adev) | ||
25986 | +{ | ||
25987 | + int result = NOT_OK; | ||
25988 | + unsigned long timeout; | ||
25989 | + | ||
25990 | + FN_ENTER; | ||
25991 | + | ||
25992 | + timeout = jiffies + 2*HZ; | ||
25993 | + for (;;) { | ||
25994 | + u16 irqstat = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES); | ||
25995 | + if (irqstat & HOST_INT_FCS_THRESHOLD) { | ||
25996 | + result = OK; | ||
25997 | + write_reg16(adev, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD); | ||
25998 | + break; | ||
25999 | + } | ||
26000 | + if (time_after(jiffies, timeout)) | ||
26001 | + break; | ||
26002 | + /* Init may take up to ~0.5 sec total */ | ||
26003 | + acx_s_msleep(50); | ||
26004 | + } | ||
26005 | + | ||
26006 | + FN_EXIT1(result); | ||
26007 | + return result; | ||
26008 | +} | ||
26009 | + | ||
26010 | + | ||
26011 | +/*********************************************************************** | ||
26012 | +** A few low-level helpers | ||
26013 | +** | ||
26014 | +** Note: these functions are not protected by lock | ||
26015 | +** and thus are never allowed to be called from IRQ. | ||
26016 | +** Also they must not race with fw upload which uses same hw regs | ||
26017 | +*/ | ||
26018 | + | ||
26019 | +/*********************************************************************** | ||
26020 | +** acxpci_write_cmd_type_status | ||
26021 | +*/ | ||
26022 | + | ||
26023 | +static inline void | ||
26024 | +acxpci_write_cmd_type_status(acx_device_t *adev, u16 type, u16 status) | ||
26025 | +{ | ||
26026 | + writel(type | (status << 16), adev->cmd_area); | ||
26027 | + write_flush(adev); | ||
26028 | +} | ||
26029 | + | ||
26030 | + | ||
26031 | +/*********************************************************************** | ||
26032 | +** acxpci_read_cmd_type_status | ||
26033 | +*/ | ||
26034 | +static u32 | ||
26035 | +acxpci_read_cmd_type_status(acx_device_t *adev) | ||
26036 | +{ | ||
26037 | + u32 cmd_type, cmd_status; | ||
26038 | + | ||
26039 | + cmd_type = readl(adev->cmd_area); | ||
26040 | + cmd_status = (cmd_type >> 16); | ||
26041 | + cmd_type = (u16)cmd_type; | ||
26042 | + | ||
26043 | + log(L_CTL, "cmd_type:%04X cmd_status:%04X [%s]\n", | ||
26044 | + cmd_type, cmd_status, | ||
26045 | + acx_cmd_status_str(cmd_status)); | ||
26046 | + | ||
26047 | + return cmd_status; | ||
26048 | +} | ||
26049 | + | ||
26050 | + | ||
26051 | +/*********************************************************************** | ||
26052 | +** acxpci_s_reset_dev | ||
26053 | +** | ||
26054 | +** Arguments: | ||
26055 | +** netdevice that contains the adev variable | ||
26056 | +** Returns: | ||
26057 | +** NOT_OK on fail | ||
26058 | +** OK on success | ||
26059 | +** Side effects: | ||
26060 | +** device is hard reset | ||
26061 | +** Call context: | ||
26062 | +** acxpci_e_probe | ||
26063 | +** Comment: | ||
26064 | +** This resets the device using low level hardware calls | ||
26065 | +** as well as uploads and verifies the firmware to the card | ||
26066 | +*/ | ||
26067 | + | ||
26068 | +static inline void | ||
26069 | +init_mboxes(acx_device_t *adev) | ||
26070 | +{ | ||
26071 | + u32 cmd_offs, info_offs; | ||
26072 | + | ||
26073 | + cmd_offs = read_reg32(adev, IO_ACX_CMD_MAILBOX_OFFS); | ||
26074 | + info_offs = read_reg32(adev, IO_ACX_INFO_MAILBOX_OFFS); | ||
26075 | + adev->cmd_area = (u8 *)adev->iobase2 + cmd_offs; | ||
26076 | + adev->info_area = (u8 *)adev->iobase2 + info_offs; | ||
26077 | + log(L_DEBUG, "iobase2=%p\n" | ||
26078 | + "cmd_mbox_offset=%X cmd_area=%p\n" | ||
26079 | + "info_mbox_offset=%X info_area=%p\n", | ||
26080 | + adev->iobase2, | ||
26081 | + cmd_offs, adev->cmd_area, | ||
26082 | + info_offs, adev->info_area); | ||
26083 | +} | ||
26084 | + | ||
26085 | + | ||
26086 | +static inline void | ||
26087 | +read_eeprom_area(acx_device_t *adev) | ||
26088 | +{ | ||
26089 | +#if ACX_DEBUG > 1 | ||
26090 | + int offs; | ||
26091 | + u8 tmp; | ||
26092 | + | ||
26093 | + for (offs = 0x8c; offs < 0xb9; offs++) | ||
26094 | + acxpci_read_eeprom_byte(adev, offs, &tmp); | ||
26095 | +#endif | ||
26096 | +} | ||
26097 | + | ||
26098 | + | ||
26099 | +static int | ||
26100 | +acxpci_s_reset_dev(acx_device_t *adev) | ||
26101 | +{ | ||
26102 | + const char* msg = ""; | ||
26103 | + unsigned long flags; | ||
26104 | + int result = NOT_OK; | ||
26105 | + u16 hardware_info; | ||
26106 | + u16 ecpu_ctrl; | ||
26107 | + int count; | ||
26108 | + | ||
26109 | + FN_ENTER; | ||
26110 | + | ||
26111 | + /* reset the device to make sure the eCPU is stopped | ||
26112 | + * to upload the firmware correctly */ | ||
26113 | + | ||
26114 | + acx_lock(adev, flags); | ||
26115 | + | ||
26116 | + acxpci_l_reset_mac(adev); | ||
26117 | + | ||
26118 | + ecpu_ctrl = read_reg16(adev, IO_ACX_ECPU_CTRL) & 1; | ||
26119 | + if (!ecpu_ctrl) { | ||
26120 | + msg = "eCPU is already running. "; | ||
26121 | + goto end_unlock; | ||
26122 | + } | ||
26123 | + | ||
26124 | +#ifdef WE_DONT_NEED_THAT_DO_WE | ||
26125 | + if (read_reg16(adev, IO_ACX_SOR_CFG) & 2) { | ||
26126 | + /* eCPU most likely means "embedded CPU" */ | ||
26127 | + msg = "eCPU did not start after boot from flash. "; | ||
26128 | + goto end_unlock; | ||
26129 | + } | ||
26130 | + | ||
26131 | + /* check sense on reset flags */ | ||
26132 | + if (read_reg16(adev, IO_ACX_SOR_CFG) & 0x10) { | ||
26133 | + printk("%s: eCPU did not start after boot (SOR), " | ||
26134 | + "is this fatal?\n", adev->ndev->name); | ||
26135 | + } | ||
26136 | +#endif | ||
26137 | + /* scan, if any, is stopped now, setting corresponding IRQ bit */ | ||
26138 | + adev->irq_status |= HOST_INT_SCAN_COMPLETE; | ||
26139 | + | ||
26140 | + acx_unlock(adev, flags); | ||
26141 | + | ||
26142 | + /* need to know radio type before fw load */ | ||
26143 | + /* Need to wait for arrival of this information in a loop, | ||
26144 | + * most probably since eCPU runs some init code from EEPROM | ||
26145 | + * (started burst read in reset_mac()) which also | ||
26146 | + * sets the radio type ID */ | ||
26147 | + | ||
26148 | + count = 0xffff; | ||
26149 | + do { | ||
26150 | + hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION); | ||
26151 | + if (!--count) { | ||
26152 | + msg = "eCPU didn't indicate radio type"; | ||
26153 | + goto end_fail; | ||
26154 | + } | ||
26155 | + cpu_relax(); | ||
26156 | + } while (!(hardware_info & 0xff00)); /* radio type still zero? */ | ||
26157 | + | ||
26158 | + /* printk("DEBUG: count %d\n", count); */ | ||
26159 | + adev->form_factor = hardware_info & 0xff; | ||
26160 | + adev->radio_type = hardware_info >> 8; | ||
26161 | + | ||
26162 | + /* load the firmware */ | ||
26163 | + if (OK != acxpci_s_upload_fw(adev)) | ||
26164 | + goto end_fail; | ||
26165 | + | ||
26166 | + /* acx_s_msleep(10); this one really shouldn't be required */ | ||
26167 | + | ||
26168 | + /* now start eCPU by clearing bit */ | ||
26169 | + write_reg16(adev, IO_ACX_ECPU_CTRL, ecpu_ctrl & ~0x1); | ||
26170 | + log(L_DEBUG, "booted eCPU up and waiting for completion...\n"); | ||
26171 | + | ||
26172 | + /* wait for eCPU bootup */ | ||
26173 | + if (OK != acxpci_s_verify_init(adev)) { | ||
26174 | + msg = "timeout waiting for eCPU. "; | ||
26175 | + goto end_fail; | ||
26176 | + } | ||
26177 | + log(L_DEBUG, "eCPU has woken up, card is ready to be configured\n"); | ||
26178 | + | ||
26179 | + init_mboxes(adev); | ||
26180 | + acxpci_write_cmd_type_status(adev, 0, 0); | ||
26181 | + | ||
26182 | + /* test that EEPROM is readable */ | ||
26183 | + read_eeprom_area(adev); | ||
26184 | + | ||
26185 | + result = OK; | ||
26186 | + goto end; | ||
26187 | + | ||
26188 | +/* Finish error message. Indicate which function failed */ | ||
26189 | +end_unlock: | ||
26190 | + acx_unlock(adev, flags); | ||
26191 | +end_fail: | ||
26192 | + printk("acx: %sreset_dev() FAILED\n", msg); | ||
26193 | +end: | ||
26194 | + FN_EXIT1(result); | ||
26195 | + return result; | ||
26196 | +} | ||
26197 | + | ||
26198 | + | ||
26199 | +/*********************************************************************** | ||
26200 | +** acxpci_s_issue_cmd_timeo | ||
26201 | +** | ||
26202 | +** Sends command to fw, extract result | ||
26203 | +** | ||
26204 | +** NB: we do _not_ take lock inside, so be sure to not touch anything | ||
26205 | +** which may interfere with IRQ handler operation | ||
26206 | +** | ||
26207 | +** TODO: busy wait is a bit silly, so: | ||
26208 | +** 1) stop doing many iters - go to sleep after first | ||
26209 | +** 2) go to waitqueue based approach: wait, not poll! | ||
26210 | +*/ | ||
26211 | +#undef FUNC | ||
26212 | +#define FUNC "issue_cmd" | ||
26213 | + | ||
26214 | +#if !ACX_DEBUG | ||
26215 | +int | ||
26216 | +acxpci_s_issue_cmd_timeo( | ||
26217 | + acx_device_t *adev, | ||
26218 | + unsigned int cmd, | ||
26219 | + void *buffer, | ||
26220 | + unsigned buflen, | ||
26221 | + unsigned cmd_timeout) | ||
26222 | +{ | ||
26223 | +#else | ||
26224 | +int | ||
26225 | +acxpci_s_issue_cmd_timeo_debug( | ||
26226 | + acx_device_t *adev, | ||
26227 | + unsigned cmd, | ||
26228 | + void *buffer, | ||
26229 | + unsigned buflen, | ||
26230 | + unsigned cmd_timeout, | ||
26231 | + const char* cmdstr) | ||
26232 | +{ | ||
26233 | + unsigned long start = jiffies; | ||
26234 | +#endif | ||
26235 | + const char *devname; | ||
26236 | + unsigned counter; | ||
26237 | + u16 irqtype; | ||
26238 | + u16 cmd_status; | ||
26239 | + unsigned long timeout; | ||
26240 | + | ||
26241 | + FN_ENTER; | ||
26242 | + | ||
26243 | + devname = adev->ndev->name; | ||
26244 | + if (!devname || !devname[0] || devname[4]=='%') | ||
26245 | + devname = "acx"; | ||
26246 | + | ||
26247 | + log(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n", | ||
26248 | + cmdstr, buflen, cmd_timeout, | ||
26249 | + buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1); | ||
26250 | + | ||
26251 | + if (!(adev->dev_state_mask & ACX_STATE_FW_LOADED)) { | ||
26252 | + printk("%s: "FUNC"(): firmware is not loaded yet, " | ||
26253 | + "cannot execute commands!\n", devname); | ||
26254 | + goto bad; | ||
26255 | + } | ||
26256 | + | ||
26257 | + if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) { | ||
26258 | + printk("input buffer (len=%u):\n", buflen); | ||
26259 | + acx_dump_bytes(buffer, buflen); | ||
26260 | + } | ||
26261 | + | ||
26262 | + /* wait for firmware to become idle for our command submission */ | ||
26263 | + timeout = HZ/5; | ||
26264 | + counter = (timeout * 1000 / HZ) - 1; /* in ms */ | ||
26265 | + timeout += jiffies; | ||
26266 | + do { | ||
26267 | + cmd_status = acxpci_read_cmd_type_status(adev); | ||
26268 | + /* Test for IDLE state */ | ||
26269 | + if (!cmd_status) | ||
26270 | + break; | ||
26271 | + if (counter % 8 == 0) { | ||
26272 | + if (time_after(jiffies, timeout)) { | ||
26273 | + counter = 0; | ||
26274 | + break; | ||
26275 | + } | ||
26276 | + /* we waited 8 iterations, no luck. Sleep 8 ms */ | ||
26277 | + acx_s_msleep(8); | ||
26278 | + } | ||
26279 | + } while (likely(--counter)); | ||
26280 | + | ||
26281 | + if (!counter) { | ||
26282 | + /* the card doesn't get idle, we're in trouble */ | ||
26283 | + printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n", | ||
26284 | + devname, cmd_status); | ||
26285 | + goto bad; | ||
26286 | + } else if (counter < 190) { /* if waited >10ms... */ | ||
26287 | + log(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. " | ||
26288 | + "Please report\n", 199 - counter); | ||
26289 | + } | ||
26290 | + | ||
26291 | + /* now write the parameters of the command if needed */ | ||
26292 | + if (buffer && buflen) { | ||
26293 | + /* if it's an INTERROGATE command, just pass the length | ||
26294 | + * of parameters to read, as data */ | ||
26295 | +#if CMD_DISCOVERY | ||
26296 | + if (cmd == ACX1xx_CMD_INTERROGATE) | ||
26297 | + memset_io(adev->cmd_area + 4, 0xAA, buflen); | ||
26298 | +#endif | ||
26299 | + /* adev->cmd_area points to PCI device's memory, not to RAM! */ | ||
26300 | + memcpy_toio(adev->cmd_area + 4, buffer, | ||
26301 | + (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen); | ||
26302 | + } | ||
26303 | + /* now write the actual command type */ | ||
26304 | + acxpci_write_cmd_type_status(adev, cmd, 0); | ||
26305 | + /* execute command */ | ||
26306 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_CMD); | ||
26307 | + write_flush(adev); | ||
26308 | + | ||
26309 | + /* wait for firmware to process command */ | ||
26310 | + | ||
26311 | + /* Ensure nonzero and not too large timeout. | ||
26312 | + ** Also converts e.g. 100->99, 200->199 | ||
26313 | + ** which is nice but not essential */ | ||
26314 | + cmd_timeout = (cmd_timeout-1) | 1; | ||
26315 | + if (unlikely(cmd_timeout > 1199)) | ||
26316 | + cmd_timeout = 1199; | ||
26317 | + /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */ | ||
26318 | + adev->irq_status &= ~HOST_INT_CMD_COMPLETE; | ||
26319 | + | ||
26320 | + /* we schedule away sometimes (timeout can be large) */ | ||
26321 | + counter = cmd_timeout; | ||
26322 | + timeout = jiffies + cmd_timeout * HZ / 1000; | ||
26323 | + do { | ||
26324 | + if (!adev->irqs_active) { /* IRQ disabled: poll */ | ||
26325 | + irqtype = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES); | ||
26326 | + if (irqtype & HOST_INT_CMD_COMPLETE) { | ||
26327 | + write_reg16(adev, IO_ACX_IRQ_ACK, | ||
26328 | + HOST_INT_CMD_COMPLETE); | ||
26329 | + break; | ||
26330 | + } | ||
26331 | + } else { /* Wait when IRQ will set the bit */ | ||
26332 | + irqtype = adev->irq_status; | ||
26333 | + if (irqtype & HOST_INT_CMD_COMPLETE) | ||
26334 | + break; | ||
26335 | + } | ||
26336 | + | ||
26337 | + if (counter % 8 == 0) { | ||
26338 | + if (time_after(jiffies, timeout)) { | ||
26339 | + counter = 0; | ||
26340 | + break; | ||
26341 | + } | ||
26342 | + /* we waited 8 iterations, no luck. Sleep 8 ms */ | ||
26343 | + acx_s_msleep(8); | ||
26344 | + } | ||
26345 | + } while (likely(--counter)); | ||
26346 | + | ||
26347 | + /* save state for debugging */ | ||
26348 | + cmd_status = acxpci_read_cmd_type_status(adev); | ||
26349 | + | ||
26350 | + /* put the card in IDLE state */ | ||
26351 | + acxpci_write_cmd_type_status(adev, 0, 0); | ||
26352 | + | ||
26353 | + if (!counter) { /* timed out! */ | ||
26354 | + printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. " | ||
26355 | + "irq bits:0x%04X irq_status:0x%04X timeout:%dms " | ||
26356 | + "cmd_status:%d (%s)\n", | ||
26357 | + devname, (adev->irqs_active) ? "waiting" : "polling", | ||
26358 | + irqtype, adev->irq_status, cmd_timeout, | ||
26359 | + cmd_status, acx_cmd_status_str(cmd_status)); | ||
26360 | + goto bad; | ||
26361 | + } else if (cmd_timeout - counter > 30) { /* if waited >30ms... */ | ||
26362 | + log(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. " | ||
26363 | + "count:%d. Please report\n", | ||
26364 | + (adev->irqs_active) ? "waited" : "polled", | ||
26365 | + cmd_timeout - counter, counter); | ||
26366 | + } | ||
26367 | + | ||
26368 | + if (1 != cmd_status) { /* it is not a 'Success' */ | ||
26369 | + printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). " | ||
26370 | + "Took %dms of %d\n", | ||
26371 | + devname, cmd_status, acx_cmd_status_str(cmd_status), | ||
26372 | + cmd_timeout - counter, cmd_timeout); | ||
26373 | + /* zero out result buffer | ||
26374 | + * WARNING: this will trash stack in case of illegally large input | ||
26375 | + * length! */ | ||
26376 | + if (buffer && buflen) | ||
26377 | + memset(buffer, 0, buflen); | ||
26378 | + goto bad; | ||
26379 | + } | ||
26380 | + | ||
26381 | + /* read in result parameters if needed */ | ||
26382 | + if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) { | ||
26383 | + /* adev->cmd_area points to PCI device's memory, not to RAM! */ | ||
26384 | + memcpy_fromio(buffer, adev->cmd_area + 4, buflen); | ||
26385 | + if (acx_debug & L_DEBUG) { | ||
26386 | + printk("output buffer (len=%u): ", buflen); | ||
26387 | + acx_dump_bytes(buffer, buflen); | ||
26388 | + } | ||
26389 | + } | ||
26390 | +/* ok: */ | ||
26391 | + log(L_CTL, FUNC"(%s): took %ld jiffies to complete\n", | ||
26392 | + cmdstr, jiffies - start); | ||
26393 | + FN_EXIT1(OK); | ||
26394 | + return OK; | ||
26395 | + | ||
26396 | +bad: | ||
26397 | + /* Give enough info so that callers can avoid | ||
26398 | + ** printing their own diagnostic messages */ | ||
26399 | +#if ACX_DEBUG | ||
26400 | + printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr); | ||
26401 | +#else | ||
26402 | + printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd); | ||
26403 | +#endif | ||
26404 | + dump_stack(); | ||
26405 | + FN_EXIT1(NOT_OK); | ||
26406 | + return NOT_OK; | ||
26407 | +} | ||
26408 | + | ||
26409 | + | ||
26410 | +/*********************************************************************** | ||
26411 | +*/ | ||
26412 | +#ifdef NONESSENTIAL_FEATURES | ||
26413 | +typedef struct device_id { | ||
26414 | + unsigned char id[6]; | ||
26415 | + char *descr; | ||
26416 | + char *type; | ||
26417 | +} device_id_t; | ||
26418 | + | ||
26419 | +static const device_id_t | ||
26420 | +device_ids[] = | ||
26421 | +{ | ||
26422 | + { | ||
26423 | + {'G', 'l', 'o', 'b', 'a', 'l'}, | ||
26424 | + NULL, | ||
26425 | + NULL, | ||
26426 | + }, | ||
26427 | + { | ||
26428 | + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, | ||
26429 | + "uninitialized", | ||
26430 | + "SpeedStream SS1021 or Gigafast WF721-AEX" | ||
26431 | + }, | ||
26432 | + { | ||
26433 | + {0x80, 0x81, 0x82, 0x83, 0x84, 0x85}, | ||
26434 | + "non-standard", | ||
26435 | + "DrayTek Vigor 520" | ||
26436 | + }, | ||
26437 | + { | ||
26438 | + {'?', '?', '?', '?', '?', '?'}, | ||
26439 | + "non-standard", | ||
26440 | + "Level One WPC-0200" | ||
26441 | + }, | ||
26442 | + { | ||
26443 | + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | ||
26444 | + "empty", | ||
26445 | + "DWL-650+ variant" | ||
26446 | + } | ||
26447 | +}; | ||
26448 | + | ||
26449 | +static void | ||
26450 | +acx_show_card_eeprom_id(acx_device_t *adev) | ||
26451 | +{ | ||
26452 | + unsigned char buffer[CARD_EEPROM_ID_SIZE]; | ||
26453 | + int i; | ||
26454 | + | ||
26455 | + memset(&buffer, 0, CARD_EEPROM_ID_SIZE); | ||
26456 | + /* use direct EEPROM access */ | ||
26457 | + for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) { | ||
26458 | + if (OK != acxpci_read_eeprom_byte(adev, | ||
26459 | + ACX100_EEPROM_ID_OFFSET + i, | ||
26460 | + &buffer[i])) { | ||
26461 | + printk("acx: reading EEPROM FAILED\n"); | ||
26462 | + break; | ||
26463 | + } | ||
26464 | + } | ||
26465 | + | ||
26466 | + for (i = 0; i < VEC_SIZE(device_ids); i++) { | ||
26467 | + if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) { | ||
26468 | + if (device_ids[i].descr) { | ||
26469 | + printk("acx: EEPROM card ID string check " | ||
26470 | + "found %s card ID: is this %s?\n", | ||
26471 | + device_ids[i].descr, device_ids[i].type); | ||
26472 | + } | ||
26473 | + break; | ||
26474 | + } | ||
26475 | + } | ||
26476 | + if (i == VEC_SIZE(device_ids)) { | ||
26477 | + printk("acx: EEPROM card ID string check found " | ||
26478 | + "unknown card: expected 'Global', got '%.*s\'. " | ||
26479 | + "Please report\n", CARD_EEPROM_ID_SIZE, buffer); | ||
26480 | + } | ||
26481 | +} | ||
26482 | +#endif /* NONESSENTIAL_FEATURES */ | ||
26483 | + | ||
26484 | + | ||
26485 | +/*********************************************************************** | ||
26486 | +** acxpci_free_desc_queues | ||
26487 | +** | ||
26488 | +** Releases the queues that have been allocated, the | ||
26489 | +** others have been initialised to NULL so this | ||
26490 | +** function can be used if only part of the queues were allocated. | ||
26491 | +*/ | ||
26492 | + | ||
26493 | +static inline void | ||
26494 | +free_coherent(struct pci_dev *hwdev, size_t size, | ||
26495 | + void *vaddr, dma_addr_t dma_handle) | ||
26496 | +{ | ||
26497 | + dma_free_coherent(hwdev == NULL ? NULL : &hwdev->dev, | ||
26498 | + size, vaddr, dma_handle); | ||
26499 | +} | ||
26500 | + | ||
26501 | +void | ||
26502 | +acxpci_free_desc_queues(acx_device_t *adev) | ||
26503 | +{ | ||
26504 | +#define ACX_FREE_QUEUE(size, ptr, phyaddr) \ | ||
26505 | + if (ptr) { \ | ||
26506 | + free_coherent(0, size, ptr, phyaddr); \ | ||
26507 | + ptr = NULL; \ | ||
26508 | + size = 0; \ | ||
26509 | + } | ||
26510 | + | ||
26511 | + FN_ENTER; | ||
26512 | + | ||
26513 | + ACX_FREE_QUEUE(adev->txhostdesc_area_size, adev->txhostdesc_start, adev->txhostdesc_startphy); | ||
26514 | + ACX_FREE_QUEUE(adev->txbuf_area_size, adev->txbuf_start, adev->txbuf_startphy); | ||
26515 | + | ||
26516 | + adev->txdesc_start = NULL; | ||
26517 | + | ||
26518 | + ACX_FREE_QUEUE(adev->rxhostdesc_area_size, adev->rxhostdesc_start, adev->rxhostdesc_startphy); | ||
26519 | + ACX_FREE_QUEUE(adev->rxbuf_area_size, adev->rxbuf_start, adev->rxbuf_startphy); | ||
26520 | + | ||
26521 | + adev->rxdesc_start = NULL; | ||
26522 | + | ||
26523 | + FN_EXIT0; | ||
26524 | +} | ||
26525 | + | ||
26526 | + | ||
26527 | +/*********************************************************************** | ||
26528 | +** acxpci_s_delete_dma_regions | ||
26529 | +*/ | ||
26530 | +static void | ||
26531 | +acxpci_s_delete_dma_regions(acx_device_t *adev) | ||
26532 | +{ | ||
26533 | + unsigned long flags; | ||
26534 | + | ||
26535 | + FN_ENTER; | ||
26536 | + /* disable radio Tx/Rx. Shouldn't we use the firmware commands | ||
26537 | + * here instead? Or are we that much down the road that it's no | ||
26538 | + * longer possible here? */ | ||
26539 | + write_reg16(adev, IO_ACX_ENABLE, 0); | ||
26540 | + | ||
26541 | + acx_s_msleep(100); | ||
26542 | + | ||
26543 | + acx_lock(adev, flags); | ||
26544 | + acxpci_free_desc_queues(adev); | ||
26545 | + acx_unlock(adev, flags); | ||
26546 | + | ||
26547 | + FN_EXIT0; | ||
26548 | +} | ||
26549 | + | ||
26550 | + | ||
26551 | +/*********************************************************************** | ||
26552 | +** acxpci_e_probe | ||
26553 | +** | ||
26554 | +** Probe routine called when a PCI device w/ matching ID is found. | ||
26555 | +** Here's the sequence: | ||
26556 | +** - Allocate the PCI resources. | ||
26557 | +** - Read the PCMCIA attribute memory to make sure we have a WLAN card | ||
26558 | +** - Reset the MAC | ||
26559 | +** - Initialize the dev and wlan data | ||
26560 | +** - Initialize the MAC | ||
26561 | +** | ||
26562 | +** pdev - ptr to pci device structure containing info about pci configuration | ||
26563 | +** id - ptr to the device id entry that matched this device | ||
26564 | +*/ | ||
26565 | +static const u16 | ||
26566 | +IO_ACX100[] = | ||
26567 | +{ | ||
26568 | + 0x0000, /* IO_ACX_SOFT_RESET */ | ||
26569 | + | ||
26570 | + 0x0014, /* IO_ACX_SLV_MEM_ADDR */ | ||
26571 | + 0x0018, /* IO_ACX_SLV_MEM_DATA */ | ||
26572 | + 0x001c, /* IO_ACX_SLV_MEM_CTL */ | ||
26573 | + 0x0020, /* IO_ACX_SLV_END_CTL */ | ||
26574 | + | ||
26575 | + 0x0034, /* IO_ACX_FEMR */ | ||
26576 | + | ||
26577 | + 0x007c, /* IO_ACX_INT_TRIG */ | ||
26578 | + 0x0098, /* IO_ACX_IRQ_MASK */ | ||
26579 | + 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */ | ||
26580 | + 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */ | ||
26581 | + 0x00ac, /* IO_ACX_IRQ_ACK */ | ||
26582 | + 0x00b0, /* IO_ACX_HINT_TRIG */ | ||
26583 | + | ||
26584 | + 0x0104, /* IO_ACX_ENABLE */ | ||
26585 | + | ||
26586 | + 0x0250, /* IO_ACX_EEPROM_CTL */ | ||
26587 | + 0x0254, /* IO_ACX_EEPROM_ADDR */ | ||
26588 | + 0x0258, /* IO_ACX_EEPROM_DATA */ | ||
26589 | + 0x025c, /* IO_ACX_EEPROM_CFG */ | ||
26590 | + | ||
26591 | + 0x0268, /* IO_ACX_PHY_ADDR */ | ||
26592 | + 0x026c, /* IO_ACX_PHY_DATA */ | ||
26593 | + 0x0270, /* IO_ACX_PHY_CTL */ | ||
26594 | + | ||
26595 | + 0x0290, /* IO_ACX_GPIO_OE */ | ||
26596 | + | ||
26597 | + 0x0298, /* IO_ACX_GPIO_OUT */ | ||
26598 | + | ||
26599 | + 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */ | ||
26600 | + 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */ | ||
26601 | + 0x02ac, /* IO_ACX_EEPROM_INFORMATION */ | ||
26602 | + | ||
26603 | + 0x02d0, /* IO_ACX_EE_START */ | ||
26604 | + 0x02d4, /* IO_ACX_SOR_CFG */ | ||
26605 | + 0x02d8 /* IO_ACX_ECPU_CTRL */ | ||
26606 | +}; | ||
26607 | + | ||
26608 | +static const u16 | ||
26609 | +IO_ACX111[] = | ||
26610 | +{ | ||
26611 | + 0x0000, /* IO_ACX_SOFT_RESET */ | ||
26612 | + | ||
26613 | + 0x0014, /* IO_ACX_SLV_MEM_ADDR */ | ||
26614 | + 0x0018, /* IO_ACX_SLV_MEM_DATA */ | ||
26615 | + 0x001c, /* IO_ACX_SLV_MEM_CTL */ | ||
26616 | + 0x0020, /* IO_ACX_SLV_END_CTL */ | ||
26617 | + | ||
26618 | + 0x0034, /* IO_ACX_FEMR */ | ||
26619 | + | ||
26620 | + 0x00b4, /* IO_ACX_INT_TRIG */ | ||
26621 | + 0x00d4, /* IO_ACX_IRQ_MASK */ | ||
26622 | + /* we do mean NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */ | ||
26623 | + 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */ | ||
26624 | + 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */ | ||
26625 | + 0x00e8, /* IO_ACX_IRQ_ACK */ | ||
26626 | + 0x00ec, /* IO_ACX_HINT_TRIG */ | ||
26627 | + | ||
26628 | + 0x01d0, /* IO_ACX_ENABLE */ | ||
26629 | + | ||
26630 | + 0x0338, /* IO_ACX_EEPROM_CTL */ | ||
26631 | + 0x033c, /* IO_ACX_EEPROM_ADDR */ | ||
26632 | + 0x0340, /* IO_ACX_EEPROM_DATA */ | ||
26633 | + 0x0344, /* IO_ACX_EEPROM_CFG */ | ||
26634 | + | ||
26635 | + 0x0350, /* IO_ACX_PHY_ADDR */ | ||
26636 | + 0x0354, /* IO_ACX_PHY_DATA */ | ||
26637 | + 0x0358, /* IO_ACX_PHY_CTL */ | ||
26638 | + | ||
26639 | + 0x0374, /* IO_ACX_GPIO_OE */ | ||
26640 | + | ||
26641 | + 0x037c, /* IO_ACX_GPIO_OUT */ | ||
26642 | + | ||
26643 | + 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */ | ||
26644 | + 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */ | ||
26645 | + 0x0390, /* IO_ACX_EEPROM_INFORMATION */ | ||
26646 | + | ||
26647 | + 0x0100, /* IO_ACX_EE_START */ | ||
26648 | + 0x0104, /* IO_ACX_SOR_CFG */ | ||
26649 | + 0x0108, /* IO_ACX_ECPU_CTRL */ | ||
26650 | +}; | ||
26651 | + | ||
26652 | +static void | ||
26653 | +dummy_netdev_init(struct net_device *ndev) {} | ||
26654 | + | ||
26655 | +static int __devinit | ||
26656 | +acxpci_e_probe(struct pci_dev *pdev, const struct pci_device_id *id) | ||
26657 | +{ | ||
26658 | + acx111_ie_configoption_t co; | ||
26659 | + unsigned long mem_region1 = 0; | ||
26660 | + unsigned long mem_region2 = 0; | ||
26661 | + unsigned long mem_region1_size; | ||
26662 | + unsigned long mem_region2_size; | ||
26663 | + unsigned long phymem1; | ||
26664 | + unsigned long phymem2; | ||
26665 | + void *mem1 = NULL; | ||
26666 | + void *mem2 = NULL; | ||
26667 | + acx_device_t *adev = NULL; | ||
26668 | + struct net_device *ndev = NULL; | ||
26669 | + const char *chip_name; | ||
26670 | + int result = -EIO; | ||
26671 | + int err; | ||
26672 | + u8 chip_type; | ||
26673 | + | ||
26674 | + FN_ENTER; | ||
26675 | + | ||
26676 | + /* Enable the PCI device */ | ||
26677 | + if (pci_enable_device(pdev)) { | ||
26678 | + printk("acx: pci_enable_device() FAILED\n"); | ||
26679 | + result = -ENODEV; | ||
26680 | + goto fail_pci_enable_device; | ||
26681 | + } | ||
26682 | + | ||
26683 | + /* enable busmastering (required for CardBus) */ | ||
26684 | + pci_set_master(pdev); | ||
26685 | + | ||
26686 | + /* FIXME: prism54 calls pci_set_mwi() here, | ||
26687 | + * should we do/support the same? */ | ||
26688 | + | ||
26689 | + /* chiptype is u8 but id->driver_data is ulong | ||
26690 | + ** Works for now (possible values are 1 and 2) */ | ||
26691 | + chip_type = (u8)id->driver_data; | ||
26692 | + /* acx100 and acx111 have different PCI memory regions */ | ||
26693 | + if (chip_type == CHIPTYPE_ACX100) { | ||
26694 | + chip_name = "ACX100"; | ||
26695 | + mem_region1 = PCI_ACX100_REGION1; | ||
26696 | + mem_region1_size = PCI_ACX100_REGION1_SIZE; | ||
26697 | + | ||
26698 | + mem_region2 = PCI_ACX100_REGION2; | ||
26699 | + mem_region2_size = PCI_ACX100_REGION2_SIZE; | ||
26700 | + } else if (chip_type == CHIPTYPE_ACX111) { | ||
26701 | + chip_name = "ACX111"; | ||
26702 | + mem_region1 = PCI_ACX111_REGION1; | ||
26703 | + mem_region1_size = PCI_ACX111_REGION1_SIZE; | ||
26704 | + | ||
26705 | + mem_region2 = PCI_ACX111_REGION2; | ||
26706 | + mem_region2_size = PCI_ACX111_REGION2_SIZE; | ||
26707 | + } else { | ||
26708 | + printk("acx: unknown chip type 0x%04X\n", chip_type); | ||
26709 | + goto fail_unknown_chiptype; | ||
26710 | + } | ||
26711 | + | ||
26712 | + /* Figure out our resources */ | ||
26713 | + phymem1 = pci_resource_start(pdev, mem_region1); | ||
26714 | + phymem2 = pci_resource_start(pdev, mem_region2); | ||
26715 | + if (!request_mem_region(phymem1, pci_resource_len(pdev, mem_region1), "acx_1")) { | ||
26716 | + printk("acx: cannot reserve PCI memory region 1 (are you sure " | ||
26717 | + "you have CardBus support in kernel?)\n"); | ||
26718 | + goto fail_request_mem_region1; | ||
26719 | + } | ||
26720 | + if (!request_mem_region(phymem2, pci_resource_len(pdev, mem_region2), "acx_2")) { | ||
26721 | + printk("acx: cannot reserve PCI memory region 2\n"); | ||
26722 | + goto fail_request_mem_region2; | ||
26723 | + } | ||
26724 | + | ||
26725 | + /* this used to be ioremap(), but ioremap_nocache() | ||
26726 | + * is much less risky, right? (and slower?) | ||
26727 | + * FIXME: we may want to go back to cached variant if it's | ||
26728 | + * certain that our code really properly handles | ||
26729 | + * cached operation (memory barriers, volatile?, ...) | ||
26730 | + * (but always keep this comment here regardless!) | ||
26731 | + * Possibly make this a driver config setting? */ | ||
26732 | + | ||
26733 | + mem1 = ioremap_nocache(phymem1, mem_region1_size); | ||
26734 | + if (!mem1) { | ||
26735 | + printk("acx: ioremap() FAILED\n"); | ||
26736 | + goto fail_ioremap1; | ||
26737 | + } | ||
26738 | + mem2 = ioremap_nocache(phymem2, mem_region2_size); | ||
26739 | + if (!mem2) { | ||
26740 | + printk("acx: ioremap() #2 FAILED\n"); | ||
26741 | + goto fail_ioremap2; | ||
26742 | + } | ||
26743 | + | ||
26744 | + printk("acx: found %s-based wireless network card at %s, irq:%d, " | ||
26745 | + "phymem1:0x%lX, phymem2:0x%lX, mem1:0x%p, mem1_size:%ld, " | ||
26746 | + "mem2:0x%p, mem2_size:%ld\n", | ||
26747 | + chip_name, pci_name(pdev), pdev->irq, phymem1, phymem2, | ||
26748 | + mem1, mem_region1_size, | ||
26749 | + mem2, mem_region2_size); | ||
26750 | + log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug); | ||
26751 | + | ||
26752 | + if (0 == pdev->irq) { | ||
26753 | + printk("acx: can't use IRQ 0\n"); | ||
26754 | + goto fail_irq; | ||
26755 | + } | ||
26756 | + | ||
26757 | + ndev = alloc_netdev(sizeof(*adev), "wlan%d", dummy_netdev_init); | ||
26758 | + /* (NB: memsets to 0 entire area) */ | ||
26759 | + if (!ndev) { | ||
26760 | + printk("acx: no memory for netdevice struct\n"); | ||
26761 | + goto fail_alloc_netdev; | ||
26762 | + } | ||
26763 | + | ||
26764 | + ether_setup(ndev); | ||
26765 | + ndev->open = &acxpci_e_open; | ||
26766 | + ndev->stop = &acxpci_e_close; | ||
26767 | + ndev->hard_start_xmit = &acx_i_start_xmit; | ||
26768 | + ndev->get_stats = &acx_e_get_stats; | ||
26769 | +#if IW_HANDLER_VERSION <= 5 | ||
26770 | + ndev->get_wireless_stats = &acx_e_get_wireless_stats; | ||
26771 | +#endif | ||
26772 | + ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; | ||
26773 | + ndev->set_multicast_list = &acxpci_i_set_multicast_list; | ||
26774 | + ndev->tx_timeout = &acxpci_i_tx_timeout; | ||
26775 | + ndev->change_mtu = &acx_e_change_mtu; | ||
26776 | + ndev->watchdog_timeo = 4 * HZ; | ||
26777 | + ndev->irq = pdev->irq; | ||
26778 | + ndev->base_addr = pci_resource_start(pdev, 0); | ||
26779 | + | ||
26780 | + adev = ndev2adev(ndev); | ||
26781 | + spin_lock_init(&adev->lock); /* initial state: unlocked */ | ||
26782 | + /* We do not start with downed sem: we want PARANOID_LOCKING to work */ | ||
26783 | + sema_init(&adev->sem, 1); /* initial state: 1 (upped) */ | ||
26784 | + /* since nobody can see new netdev yet, we can as well | ||
26785 | + ** just _presume_ that we're under sem (instead of actually taking it): */ | ||
26786 | + /* acx_sem_lock(adev); */ | ||
26787 | + adev->pdev = pdev; | ||
26788 | + adev->ndev = ndev; | ||
26789 | + adev->dev_type = DEVTYPE_PCI; | ||
26790 | + adev->chip_type = chip_type; | ||
26791 | + adev->chip_name = chip_name; | ||
26792 | + adev->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111; | ||
26793 | + adev->membase = phymem1; | ||
26794 | + adev->iobase = mem1; | ||
26795 | + adev->membase2 = phymem2; | ||
26796 | + adev->iobase2 = mem2; | ||
26797 | + /* to find crashes due to weird driver access | ||
26798 | + * to unconfigured interface (ifup) */ | ||
26799 | + adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead; | ||
26800 | + | ||
26801 | +#ifdef NONESSENTIAL_FEATURES | ||
26802 | + acx_show_card_eeprom_id(adev); | ||
26803 | +#endif /* NONESSENTIAL_FEATURES */ | ||
26804 | + | ||
26805 | +#ifdef SET_MODULE_OWNER | ||
26806 | + SET_MODULE_OWNER(ndev); | ||
26807 | +#endif | ||
26808 | + SET_NETDEV_DEV(ndev, &pdev->dev); | ||
26809 | + | ||
26810 | + log(L_IRQ|L_INIT, "using IRQ %d\n", pdev->irq); | ||
26811 | + | ||
26812 | + /* need to be able to restore PCI state after a suspend */ | ||
26813 | + pci_save_state(pdev); | ||
26814 | + pci_set_drvdata(pdev, ndev); | ||
26815 | + | ||
26816 | + /* ok, pci setup is finished, now start initializing the card */ | ||
26817 | + | ||
26818 | + /* NB: read_reg() reads may return bogus data before reset_dev(), | ||
26819 | + * since the firmware which directly controls large parts of the I/O | ||
26820 | + * registers isn't initialized yet. | ||
26821 | + * acx100 seems to be more affected than acx111 */ | ||
26822 | + if (OK != acxpci_s_reset_dev(adev)) | ||
26823 | + goto fail_reset; | ||
26824 | + | ||
26825 | + if (IS_ACX100(adev)) { | ||
26826 | + /* ACX100: configopt struct in cmd mailbox - directly after reset */ | ||
26827 | + memcpy_fromio(&co, adev->cmd_area, sizeof(co)); | ||
26828 | + } | ||
26829 | + | ||
26830 | + if (OK != acx_s_init_mac(adev)) | ||
26831 | + goto fail_init_mac; | ||
26832 | + | ||
26833 | + if (IS_ACX111(adev)) { | ||
26834 | + /* ACX111: configopt struct needs to be queried after full init */ | ||
26835 | + acx_s_interrogate(adev, &co, ACX111_IE_CONFIG_OPTIONS); | ||
26836 | + } | ||
26837 | + | ||
26838 | +/* TODO: merge them into one function, they are called just once and are the same for pci & usb */ | ||
26839 | + if (OK != acxpci_read_eeprom_byte(adev, 0x05, &adev->eeprom_version)) | ||
26840 | + goto fail_read_eeprom_version; | ||
26841 | + | ||
26842 | + acx_s_parse_configoption(adev, &co); | ||
26843 | + acx_s_set_defaults(adev); | ||
26844 | + acx_s_get_firmware_version(adev); /* needs to be after acx_s_init_mac() */ | ||
26845 | + acx_display_hardware_details(adev); | ||
26846 | + | ||
26847 | + /* Register the card, AFTER everything else has been set up, | ||
26848 | + * since otherwise an ioctl could step on our feet due to | ||
26849 | + * firmware operations happening in parallel or uninitialized data */ | ||
26850 | + err = register_netdev(ndev); | ||
26851 | + if (OK != err) { | ||
26852 | + printk("acx: register_netdev() FAILED: %d\n", err); | ||
26853 | + goto fail_register_netdev; | ||
26854 | + } | ||
26855 | + | ||
26856 | + acx_proc_register_entries(ndev); | ||
26857 | + | ||
26858 | + /* Now we have our device, so make sure the kernel doesn't try | ||
26859 | + * to send packets even though we're not associated to a network yet */ | ||
26860 | + acx_stop_queue(ndev, "on probe"); | ||
26861 | + acx_carrier_off(ndev, "on probe"); | ||
26862 | + | ||
26863 | + /* after register_netdev() userspace may start working with dev | ||
26864 | + * (in particular, on other CPUs), we only need to up the sem */ | ||
26865 | + /* acx_sem_unlock(adev); */ | ||
26866 | + | ||
26867 | + printk("acx "ACX_RELEASE": net device %s, driver compiled " | ||
26868 | + "against wireless extensions %d and Linux %s\n", | ||
26869 | + ndev->name, WIRELESS_EXT, UTS_RELEASE); | ||
26870 | + | ||
26871 | +#if CMD_DISCOVERY | ||
26872 | + great_inquisitor(adev); | ||
26873 | +#endif | ||
26874 | + | ||
26875 | + result = OK; | ||
26876 | + goto done; | ||
26877 | + | ||
26878 | + /* error paths: undo everything in reverse order... */ | ||
26879 | + | ||
26880 | +fail_register_netdev: | ||
26881 | + | ||
26882 | + acxpci_s_delete_dma_regions(adev); | ||
26883 | + pci_set_drvdata(pdev, NULL); | ||
26884 | + | ||
26885 | +fail_init_mac: | ||
26886 | +fail_read_eeprom_version: | ||
26887 | +fail_reset: | ||
26888 | + | ||
26889 | + free_netdev(ndev); | ||
26890 | +fail_alloc_netdev: | ||
26891 | +fail_irq: | ||
26892 | + | ||
26893 | + iounmap(mem2); | ||
26894 | +fail_ioremap2: | ||
26895 | + | ||
26896 | + iounmap(mem1); | ||
26897 | +fail_ioremap1: | ||
26898 | + | ||
26899 | + release_mem_region(pci_resource_start(pdev, mem_region2), | ||
26900 | + pci_resource_len(pdev, mem_region2)); | ||
26901 | +fail_request_mem_region2: | ||
26902 | + | ||
26903 | + release_mem_region(pci_resource_start(pdev, mem_region1), | ||
26904 | + pci_resource_len(pdev, mem_region1)); | ||
26905 | +fail_request_mem_region1: | ||
26906 | +fail_unknown_chiptype: | ||
26907 | + | ||
26908 | + pci_disable_device(pdev); | ||
26909 | +fail_pci_enable_device: | ||
26910 | + | ||
26911 | + pci_set_power_state(pdev, PCI_D3hot); | ||
26912 | + | ||
26913 | +done: | ||
26914 | + FN_EXIT1(result); | ||
26915 | + return result; | ||
26916 | +} | ||
26917 | + | ||
26918 | + | ||
26919 | +/*********************************************************************** | ||
26920 | +** acxpci_e_remove | ||
26921 | +** | ||
26922 | +** Shut device down (if not hot unplugged) | ||
26923 | +** and deallocate PCI resources for the acx chip. | ||
26924 | +** | ||
26925 | +** pdev - ptr to PCI device structure containing info about pci configuration | ||
26926 | +*/ | ||
26927 | +static void __devexit | ||
26928 | +acxpci_e_remove(struct pci_dev *pdev) | ||
26929 | +{ | ||
26930 | + struct net_device *ndev; | ||
26931 | + acx_device_t *adev; | ||
26932 | + unsigned long mem_region1, mem_region2; | ||
26933 | + unsigned long flags; | ||
26934 | + | ||
26935 | + FN_ENTER; | ||
26936 | + | ||
26937 | + ndev = (struct net_device*) pci_get_drvdata(pdev); | ||
26938 | + if (!ndev) { | ||
26939 | + log(L_DEBUG, "%s: card is unused. Skipping any release code\n", | ||
26940 | + __func__); | ||
26941 | + goto end; | ||
26942 | + } | ||
26943 | + | ||
26944 | + adev = ndev2adev(ndev); | ||
26945 | + | ||
26946 | + /* If device wasn't hot unplugged... */ | ||
26947 | + if (adev_present(adev)) { | ||
26948 | + | ||
26949 | + acx_sem_lock(adev); | ||
26950 | + | ||
26951 | + /* disable both Tx and Rx to shut radio down properly */ | ||
26952 | + acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0); | ||
26953 | + acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0); | ||
26954 | + | ||
26955 | +#ifdef REDUNDANT | ||
26956 | + /* put the eCPU to sleep to save power | ||
26957 | + * Halting is not possible currently, | ||
26958 | + * since not supported by all firmware versions */ | ||
26959 | + acx_s_issue_cmd(adev, ACX100_CMD_SLEEP, NULL, 0); | ||
26960 | +#endif | ||
26961 | + acx_lock(adev, flags); | ||
26962 | + /* disable power LED to save power :-) */ | ||
26963 | + log(L_INIT, "switching off power LED to save power\n"); | ||
26964 | + acxpci_l_power_led(adev, 0); | ||
26965 | + /* stop our eCPU */ | ||
26966 | + if (IS_ACX111(adev)) { | ||
26967 | + /* FIXME: does this actually keep halting the eCPU? | ||
26968 | + * I don't think so... | ||
26969 | + */ | ||
26970 | + acxpci_l_reset_mac(adev); | ||
26971 | + } else { | ||
26972 | + u16 temp; | ||
26973 | + /* halt eCPU */ | ||
26974 | + temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1; | ||
26975 | + write_reg16(adev, IO_ACX_ECPU_CTRL, temp); | ||
26976 | + write_flush(adev); | ||
26977 | + } | ||
26978 | + acx_unlock(adev, flags); | ||
26979 | + | ||
26980 | + acx_sem_unlock(adev); | ||
26981 | + } | ||
26982 | + | ||
26983 | + /* unregister the device to not let the kernel | ||
26984 | + * (e.g. ioctls) access a half-deconfigured device | ||
26985 | + * NB: this will cause acxpci_e_close() to be called, | ||
26986 | + * thus we shouldn't call it under sem! */ | ||
26987 | + log(L_INIT, "removing device %s\n", ndev->name); | ||
26988 | + unregister_netdev(ndev); | ||
26989 | + | ||
26990 | + /* unregister_netdev ensures that no references to us left. | ||
26991 | + * For paranoid reasons we continue to follow the rules */ | ||
26992 | + acx_sem_lock(adev); | ||
26993 | + | ||
26994 | + if (adev->dev_state_mask & ACX_STATE_IFACE_UP) { | ||
26995 | + acxpci_s_down(ndev); | ||
26996 | + CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
26997 | + } | ||
26998 | + | ||
26999 | + acx_proc_unregister_entries(ndev); | ||
27000 | + | ||
27001 | + if (IS_ACX100(adev)) { | ||
27002 | + mem_region1 = PCI_ACX100_REGION1; | ||
27003 | + mem_region2 = PCI_ACX100_REGION2; | ||
27004 | + } else { | ||
27005 | + mem_region1 = PCI_ACX111_REGION1; | ||
27006 | + mem_region2 = PCI_ACX111_REGION2; | ||
27007 | + } | ||
27008 | + | ||
27009 | + /* finally, clean up PCI bus state */ | ||
27010 | + acxpci_s_delete_dma_regions(adev); | ||
27011 | + if (adev->iobase) iounmap(adev->iobase); | ||
27012 | + if (adev->iobase2) iounmap(adev->iobase2); | ||
27013 | + release_mem_region(pci_resource_start(pdev, mem_region1), | ||
27014 | + pci_resource_len(pdev, mem_region1)); | ||
27015 | + release_mem_region(pci_resource_start(pdev, mem_region2), | ||
27016 | + pci_resource_len(pdev, mem_region2)); | ||
27017 | + pci_disable_device(pdev); | ||
27018 | + | ||
27019 | + /* remove dev registration */ | ||
27020 | + pci_set_drvdata(pdev, NULL); | ||
27021 | + | ||
27022 | + acx_sem_unlock(adev); | ||
27023 | + | ||
27024 | + /* Free netdev (quite late, | ||
27025 | + * since otherwise we might get caught off-guard | ||
27026 | + * by a netdev timeout handler execution | ||
27027 | + * expecting to see a working dev...) */ | ||
27028 | + free_netdev(ndev); | ||
27029 | + | ||
27030 | + /* put device into ACPI D3 mode (shutdown) */ | ||
27031 | + pci_set_power_state(pdev, PCI_D3hot); | ||
27032 | + | ||
27033 | +end: | ||
27034 | + FN_EXIT0; | ||
27035 | +} | ||
27036 | + | ||
27037 | + | ||
27038 | +/*********************************************************************** | ||
27039 | +** TODO: PM code needs to be fixed / debugged / tested. | ||
27040 | +*/ | ||
27041 | +#ifdef CONFIG_PM | ||
27042 | +static int | ||
27043 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) | ||
27044 | +acxpci_e_suspend(struct pci_dev *pdev, pm_message_t state) | ||
27045 | +#else | ||
27046 | +acxpci_e_suspend(struct pci_dev *pdev, u32 state) | ||
27047 | +#endif | ||
27048 | +{ | ||
27049 | + struct net_device *ndev = pci_get_drvdata(pdev); | ||
27050 | + acx_device_t *adev; | ||
27051 | + | ||
27052 | + FN_ENTER; | ||
27053 | + printk("acx: suspend handler is experimental!\n"); | ||
27054 | + printk("sus: dev %p\n", ndev); | ||
27055 | + | ||
27056 | + if (!netif_running(ndev)) | ||
27057 | + goto end; | ||
27058 | + | ||
27059 | + adev = ndev2adev(ndev); | ||
27060 | + printk("sus: adev %p\n", adev); | ||
27061 | + | ||
27062 | + acx_sem_lock(adev); | ||
27063 | + | ||
27064 | + netif_device_detach(ndev); /* this one cannot sleep */ | ||
27065 | + acxpci_s_down(ndev); | ||
27066 | + /* down() does not set it to 0xffff, but here we really want that */ | ||
27067 | + write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); | ||
27068 | + write_reg16(adev, IO_ACX_FEMR, 0x0); | ||
27069 | + acxpci_s_delete_dma_regions(adev); | ||
27070 | + pci_save_state(pdev); | ||
27071 | + pci_set_power_state(pdev, PCI_D3hot); | ||
27072 | + | ||
27073 | + acx_sem_unlock(adev); | ||
27074 | +end: | ||
27075 | + FN_EXIT0; | ||
27076 | + return OK; | ||
27077 | +} | ||
27078 | + | ||
27079 | + | ||
27080 | +static int | ||
27081 | +acxpci_e_resume(struct pci_dev *pdev) | ||
27082 | +{ | ||
27083 | + struct net_device *ndev = pci_get_drvdata(pdev); | ||
27084 | + acx_device_t *adev; | ||
27085 | + | ||
27086 | + FN_ENTER; | ||
27087 | + | ||
27088 | + printk("acx: resume handler is experimental!\n"); | ||
27089 | + printk("rsm: got dev %p\n", ndev); | ||
27090 | + | ||
27091 | + if (!netif_running(ndev)) | ||
27092 | + goto end; | ||
27093 | + | ||
27094 | + adev = ndev2adev(ndev); | ||
27095 | + printk("rsm: got adev %p\n", adev); | ||
27096 | + | ||
27097 | + acx_sem_lock(adev); | ||
27098 | + | ||
27099 | + pci_set_power_state(pdev, PCI_D0); | ||
27100 | + printk("rsm: power state PCI_D0 set\n"); | ||
27101 | + pci_restore_state(pdev); | ||
27102 | + printk("rsm: PCI state restored\n"); | ||
27103 | + | ||
27104 | + if (OK != acxpci_s_reset_dev(adev)) | ||
27105 | + goto end_unlock; | ||
27106 | + printk("rsm: device reset done\n"); | ||
27107 | + if (OK != acx_s_init_mac(adev)) | ||
27108 | + goto end_unlock; | ||
27109 | + printk("rsm: init MAC done\n"); | ||
27110 | + | ||
27111 | + acxpci_s_up(ndev); | ||
27112 | + printk("rsm: acx up done\n"); | ||
27113 | + | ||
27114 | + /* now even reload all card parameters as they were before suspend, | ||
27115 | + * and possibly be back in the network again already :-) */ | ||
27116 | + if (ACX_STATE_IFACE_UP & adev->dev_state_mask) { | ||
27117 | + adev->set_mask = GETSET_ALL; | ||
27118 | + acx_s_update_card_settings(adev); | ||
27119 | + printk("rsm: settings updated\n"); | ||
27120 | + } | ||
27121 | + netif_device_attach(ndev); | ||
27122 | + printk("rsm: device attached\n"); | ||
27123 | + | ||
27124 | +end_unlock: | ||
27125 | + acx_sem_unlock(adev); | ||
27126 | +end: | ||
27127 | + /* we need to return OK here anyway, right? */ | ||
27128 | + FN_EXIT0; | ||
27129 | + return OK; | ||
27130 | +} | ||
27131 | +#endif /* CONFIG_PM */ | ||
27132 | + | ||
27133 | + | ||
27134 | +/*********************************************************************** | ||
27135 | +** acxpci_s_up | ||
27136 | +** | ||
27137 | +** This function is called by acxpci_e_open (when ifconfig sets the device as up) | ||
27138 | +** | ||
27139 | +** Side effects: | ||
27140 | +** - Enables on-card interrupt requests | ||
27141 | +** - calls acx_s_start | ||
27142 | +*/ | ||
27143 | + | ||
27144 | +static void | ||
27145 | +enable_acx_irq(acx_device_t *adev) | ||
27146 | +{ | ||
27147 | + FN_ENTER; | ||
27148 | + write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask); | ||
27149 | + write_reg16(adev, IO_ACX_FEMR, 0x8000); | ||
27150 | + adev->irqs_active = 1; | ||
27151 | + FN_EXIT0; | ||
27152 | +} | ||
27153 | + | ||
27154 | +static void | ||
27155 | +acxpci_s_up(struct net_device *ndev) | ||
27156 | +{ | ||
27157 | + acx_device_t *adev = ndev2adev(ndev); | ||
27158 | + unsigned long flags; | ||
27159 | + | ||
27160 | + FN_ENTER; | ||
27161 | + | ||
27162 | + acx_lock(adev, flags); | ||
27163 | + enable_acx_irq(adev); | ||
27164 | + acx_unlock(adev, flags); | ||
27165 | + | ||
27166 | + /* acx fw < 1.9.3.e has a hardware timer, and older drivers | ||
27167 | + ** used to use it. But we don't do that anymore, our OS | ||
27168 | + ** has reliable software timers */ | ||
27169 | + init_timer(&adev->mgmt_timer); | ||
27170 | + adev->mgmt_timer.function = acx_i_timer; | ||
27171 | + adev->mgmt_timer.data = (unsigned long)adev; | ||
27172 | + | ||
27173 | + /* Need to set ACX_STATE_IFACE_UP first, or else | ||
27174 | + ** timer won't be started by acx_set_status() */ | ||
27175 | + SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
27176 | + switch (adev->mode) { | ||
27177 | + case ACX_MODE_0_ADHOC: | ||
27178 | + case ACX_MODE_2_STA: | ||
27179 | + /* actual scan cmd will happen in start() */ | ||
27180 | + acx_set_status(adev, ACX_STATUS_1_SCANNING); break; | ||
27181 | + case ACX_MODE_3_AP: | ||
27182 | + case ACX_MODE_MONITOR: | ||
27183 | + acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); break; | ||
27184 | + } | ||
27185 | + | ||
27186 | + acx_s_start(adev); | ||
27187 | + | ||
27188 | + FN_EXIT0; | ||
27189 | +} | ||
27190 | + | ||
27191 | + | ||
27192 | +/*********************************************************************** | ||
27193 | +** acxpci_s_down | ||
27194 | +** | ||
27195 | +** NB: device may be already hot unplugged if called from acxpci_e_remove() | ||
27196 | +** | ||
27197 | +** Disables on-card interrupt request, stops softirq and timer, stops queue, | ||
27198 | +** sets status == STOPPED | ||
27199 | +*/ | ||
27200 | + | ||
27201 | +static void | ||
27202 | +disable_acx_irq(acx_device_t *adev) | ||
27203 | +{ | ||
27204 | + FN_ENTER; | ||
27205 | + | ||
27206 | + /* I guess mask is not 0xffff because acx100 won't signal | ||
27207 | + ** cmd completion then (needed for ifup). | ||
27208 | + ** Someone with acx100 please confirm */ | ||
27209 | + write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask_off); | ||
27210 | + write_reg16(adev, IO_ACX_FEMR, 0x0); | ||
27211 | + adev->irqs_active = 0; | ||
27212 | + FN_EXIT0; | ||
27213 | +} | ||
27214 | + | ||
27215 | +static void | ||
27216 | +acxpci_s_down(struct net_device *ndev) | ||
27217 | +{ | ||
27218 | + acx_device_t *adev = ndev2adev(ndev); | ||
27219 | + unsigned long flags; | ||
27220 | + | ||
27221 | + FN_ENTER; | ||
27222 | + | ||
27223 | + /* Disable IRQs first, so that IRQs cannot race with us */ | ||
27224 | + /* then wait until interrupts have finished executing on other CPUs */ | ||
27225 | + acx_lock(adev, flags); | ||
27226 | + disable_acx_irq(adev); | ||
27227 | + synchronize_irq(adev->pdev->irq); | ||
27228 | + acx_unlock(adev, flags); | ||
27229 | + | ||
27230 | + /* we really don't want to have an asynchronous tasklet disturb us | ||
27231 | + ** after something vital for its job has been shut down, so | ||
27232 | + ** end all remaining work now. | ||
27233 | + ** | ||
27234 | + ** NB: carrier_off (done by set_status below) would lead to | ||
27235 | + ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK(). | ||
27236 | + ** That's why we do FLUSH first. | ||
27237 | + ** | ||
27238 | + ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK() | ||
27239 | + ** waits for acx_e_after_interrupt_task to complete if it is running | ||
27240 | + ** on another CPU, but acx_e_after_interrupt_task | ||
27241 | + ** will sleep on sem forever, because it is taken by us! | ||
27242 | + ** Work around that by temporary sem unlock. | ||
27243 | + ** This will fail miserably if we'll be hit by concurrent | ||
27244 | + ** iwconfig or something in between. TODO! */ | ||
27245 | + acx_sem_unlock(adev); | ||
27246 | + FLUSH_SCHEDULED_WORK(); | ||
27247 | + acx_sem_lock(adev); | ||
27248 | + | ||
27249 | + /* This is possible: | ||
27250 | + ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task -> | ||
27251 | + ** -> set_status(ASSOCIATED) -> wake_queue() | ||
27252 | + ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK | ||
27253 | + ** lock/unlock is just paranoia, maybe not needed */ | ||
27254 | + acx_lock(adev, flags); | ||
27255 | + acx_stop_queue(ndev, "on ifdown"); | ||
27256 | + acx_set_status(adev, ACX_STATUS_0_STOPPED); | ||
27257 | + acx_unlock(adev, flags); | ||
27258 | + | ||
27259 | + /* kernel/timer.c says it's illegal to del_timer_sync() | ||
27260 | + ** a timer which restarts itself. We guarantee this cannot | ||
27261 | + ** ever happen because acx_i_timer() never does this if | ||
27262 | + ** status is ACX_STATUS_0_STOPPED */ | ||
27263 | + del_timer_sync(&adev->mgmt_timer); | ||
27264 | + | ||
27265 | + FN_EXIT0; | ||
27266 | +} | ||
27267 | + | ||
27268 | + | ||
27269 | +/*********************************************************************** | ||
27270 | +** acxpci_e_open | ||
27271 | +** | ||
27272 | +** Called as a result of SIOCSIFFLAGS ioctl changing the flags bit IFF_UP | ||
27273 | +** from clear to set. In other words: ifconfig up. | ||
27274 | +** | ||
27275 | +** Returns: | ||
27276 | +** 0 success | ||
27277 | +** >0 f/w reported error | ||
27278 | +** <0 driver reported error | ||
27279 | +*/ | ||
27280 | +static int | ||
27281 | +acxpci_e_open(struct net_device *ndev) | ||
27282 | +{ | ||
27283 | + acx_device_t *adev = ndev2adev(ndev); | ||
27284 | + int result = OK; | ||
27285 | + | ||
27286 | + FN_ENTER; | ||
27287 | + | ||
27288 | + acx_sem_lock(adev); | ||
27289 | + | ||
27290 | + acx_init_task_scheduler(adev); | ||
27291 | + | ||
27292 | +/* TODO: pci_set_power_state(pdev, PCI_D0); ? */ | ||
27293 | + | ||
27294 | + /* request shared IRQ handler */ | ||
27295 | + if (request_irq(ndev->irq, acxpci_i_interrupt, SA_SHIRQ, ndev->name, ndev)) { | ||
27296 | + printk("%s: request_irq FAILED\n", ndev->name); | ||
27297 | + result = -EAGAIN; | ||
27298 | + goto done; | ||
27299 | + } | ||
27300 | + log(L_DEBUG|L_IRQ, "request_irq %d successful\n", ndev->irq); | ||
27301 | + | ||
27302 | + /* ifup device */ | ||
27303 | + acxpci_s_up(ndev); | ||
27304 | + | ||
27305 | + /* We don't currently have to do anything else. | ||
27306 | + * The setup of the MAC should be subsequently completed via | ||
27307 | + * the mlme commands. | ||
27308 | + * Higher layers know we're ready from dev->start==1 and | ||
27309 | + * dev->tbusy==0. Our rx path knows to pass up received/ | ||
27310 | + * frames because of dev->flags&IFF_UP is true. | ||
27311 | + */ | ||
27312 | +done: | ||
27313 | + acx_sem_unlock(adev); | ||
27314 | + | ||
27315 | + FN_EXIT1(result); | ||
27316 | + return result; | ||
27317 | +} | ||
27318 | + | ||
27319 | + | ||
27320 | +/*********************************************************************** | ||
27321 | +** acxpci_e_close | ||
27322 | +** | ||
27323 | +** Called as a result of SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP | ||
27324 | +** from set to clear. I.e. called by "ifconfig DEV down" | ||
27325 | +** | ||
27326 | +** Returns: | ||
27327 | +** 0 success | ||
27328 | +** >0 f/w reported error | ||
27329 | +** <0 driver reported error | ||
27330 | +*/ | ||
27331 | +static int | ||
27332 | +acxpci_e_close(struct net_device *ndev) | ||
27333 | +{ | ||
27334 | + acx_device_t *adev = ndev2adev(ndev); | ||
27335 | + | ||
27336 | + FN_ENTER; | ||
27337 | + | ||
27338 | + acx_sem_lock(adev); | ||
27339 | + | ||
27340 | + /* ifdown device */ | ||
27341 | + CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
27342 | + if (netif_device_present(ndev)) { | ||
27343 | + acxpci_s_down(ndev); | ||
27344 | + } | ||
27345 | + | ||
27346 | + /* disable all IRQs, release shared IRQ handler */ | ||
27347 | + write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); | ||
27348 | + write_reg16(adev, IO_ACX_FEMR, 0x0); | ||
27349 | + free_irq(ndev->irq, ndev); | ||
27350 | + | ||
27351 | +/* TODO: pci_set_power_state(pdev, PCI_D3hot); ? */ | ||
27352 | + | ||
27353 | + /* We currently don't have to do anything else. | ||
27354 | + * Higher layers know we're not ready from dev->start==0 and | ||
27355 | + * dev->tbusy==1. Our rx path knows to not pass up received | ||
27356 | + * frames because of dev->flags&IFF_UP is false. | ||
27357 | + */ | ||
27358 | + acx_sem_unlock(adev); | ||
27359 | + | ||
27360 | + log(L_INIT, "closed device\n"); | ||
27361 | + FN_EXIT0; | ||
27362 | + return OK; | ||
27363 | +} | ||
27364 | + | ||
27365 | + | ||
27366 | +/*********************************************************************** | ||
27367 | +** acxpci_i_tx_timeout | ||
27368 | +** | ||
27369 | +** Called from network core. Must not sleep! | ||
27370 | +*/ | ||
27371 | +static void | ||
27372 | +acxpci_i_tx_timeout(struct net_device *ndev) | ||
27373 | +{ | ||
27374 | + acx_device_t *adev = ndev2adev(ndev); | ||
27375 | + unsigned long flags; | ||
27376 | + unsigned int tx_num_cleaned; | ||
27377 | + | ||
27378 | + FN_ENTER; | ||
27379 | + | ||
27380 | + acx_lock(adev, flags); | ||
27381 | + | ||
27382 | + /* clean processed tx descs, they may have been completely full */ | ||
27383 | + tx_num_cleaned = acxpci_l_clean_txdesc(adev); | ||
27384 | + | ||
27385 | + /* nothing cleaned, yet (almost) no free buffers available? | ||
27386 | + * --> clean all tx descs, no matter which status!! | ||
27387 | + * Note that I strongly suspect that doing emergency cleaning | ||
27388 | + * may confuse the firmware. This is a last ditch effort to get | ||
27389 | + * ANYTHING to work again... | ||
27390 | + * | ||
27391 | + * TODO: it's best to simply reset & reinit hw from scratch... | ||
27392 | + */ | ||
27393 | + if ((adev->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) { | ||
27394 | + printk("%s: FAILED to free any of the many full tx buffers. " | ||
27395 | + "Switching to emergency freeing. " | ||
27396 | + "Please report!\n", ndev->name); | ||
27397 | + acxpci_l_clean_txdesc_emergency(adev); | ||
27398 | + } | ||
27399 | + | ||
27400 | + if (acx_queue_stopped(ndev) && (ACX_STATUS_4_ASSOCIATED == adev->status)) | ||
27401 | + acx_wake_queue(ndev, "after tx timeout"); | ||
27402 | + | ||
27403 | + /* stall may have happened due to radio drift, so recalib radio */ | ||
27404 | + acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); | ||
27405 | + | ||
27406 | + /* do unimportant work last */ | ||
27407 | + printk("%s: tx timeout!\n", ndev->name); | ||
27408 | + adev->stats.tx_errors++; | ||
27409 | + | ||
27410 | + acx_unlock(adev, flags); | ||
27411 | + | ||
27412 | + FN_EXIT0; | ||
27413 | +} | ||
27414 | + | ||
27415 | + | ||
27416 | +/*********************************************************************** | ||
27417 | +** acxpci_i_set_multicast_list | ||
27418 | +** FIXME: most likely needs refinement | ||
27419 | +*/ | ||
27420 | +static void | ||
27421 | +acxpci_i_set_multicast_list(struct net_device *ndev) | ||
27422 | +{ | ||
27423 | + acx_device_t *adev = ndev2adev(ndev); | ||
27424 | + unsigned long flags; | ||
27425 | + | ||
27426 | + FN_ENTER; | ||
27427 | + | ||
27428 | + acx_lock(adev, flags); | ||
27429 | + | ||
27430 | + /* firmwares don't have allmulti capability, | ||
27431 | + * so just use promiscuous mode instead in this case. */ | ||
27432 | + if (ndev->flags & (IFF_PROMISC|IFF_ALLMULTI)) { | ||
27433 | + SET_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); | ||
27434 | + CLEAR_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); | ||
27435 | + SET_BIT(adev->set_mask, SET_RXCONFIG); | ||
27436 | + /* let kernel know in case *we* needed to set promiscuous */ | ||
27437 | + ndev->flags |= (IFF_PROMISC|IFF_ALLMULTI); | ||
27438 | + } else { | ||
27439 | + CLEAR_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); | ||
27440 | + SET_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); | ||
27441 | + SET_BIT(adev->set_mask, SET_RXCONFIG); | ||
27442 | + ndev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI); | ||
27443 | + } | ||
27444 | + | ||
27445 | + /* cannot update card settings directly here, atomic context */ | ||
27446 | + acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG); | ||
27447 | + | ||
27448 | + acx_unlock(adev, flags); | ||
27449 | + | ||
27450 | + FN_EXIT0; | ||
27451 | +} | ||
27452 | + | ||
27453 | + | ||
27454 | +/*************************************************************** | ||
27455 | +** acxpci_l_process_rxdesc | ||
27456 | +** | ||
27457 | +** Called directly and only from the IRQ handler | ||
27458 | +*/ | ||
27459 | + | ||
27460 | +#if !ACX_DEBUG | ||
27461 | +static inline void log_rxbuffer(const acx_device_t *adev) {} | ||
27462 | +#else | ||
27463 | +static void | ||
27464 | +log_rxbuffer(const acx_device_t *adev) | ||
27465 | +{ | ||
27466 | + register const struct rxhostdesc *rxhostdesc; | ||
27467 | + int i; | ||
27468 | + /* no FN_ENTER here, we don't want that */ | ||
27469 | + | ||
27470 | + rxhostdesc = adev->rxhostdesc_start; | ||
27471 | + if (unlikely(!rxhostdesc)) return; | ||
27472 | + for (i = 0; i < RX_CNT; i++) { | ||
27473 | + if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) | ||
27474 | + && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) | ||
27475 | + printk("rx: buf %d full\n", i); | ||
27476 | + rxhostdesc++; | ||
27477 | + } | ||
27478 | +} | ||
27479 | +#endif | ||
27480 | + | ||
27481 | +static void | ||
27482 | +acxpci_l_process_rxdesc(acx_device_t *adev) | ||
27483 | +{ | ||
27484 | + register rxhostdesc_t *hostdesc; | ||
27485 | + unsigned count, tail; | ||
27486 | + | ||
27487 | + FN_ENTER; | ||
27488 | + | ||
27489 | + if (unlikely(acx_debug & L_BUFR)) | ||
27490 | + log_rxbuffer(adev); | ||
27491 | + | ||
27492 | + /* First, have a loop to determine the first descriptor that's | ||
27493 | + * full, just in case there's a mismatch between our current | ||
27494 | + * rx_tail and the full descriptor we're supposed to handle. */ | ||
27495 | + tail = adev->rx_tail; | ||
27496 | + count = RX_CNT; | ||
27497 | + while (1) { | ||
27498 | + hostdesc = &adev->rxhostdesc_start[tail]; | ||
27499 | + /* advance tail regardless of outcome of the below test */ | ||
27500 | + tail = (tail + 1) % RX_CNT; | ||
27501 | + | ||
27502 | + if ((hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) | ||
27503 | + && (hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) | ||
27504 | + break; /* found it! */ | ||
27505 | + | ||
27506 | + if (unlikely(!--count)) /* hmm, no luck: all descs empty, bail out */ | ||
27507 | + goto end; | ||
27508 | + } | ||
27509 | + | ||
27510 | + /* now process descriptors, starting with the first we figured out */ | ||
27511 | + while (1) { | ||
27512 | + log(L_BUFR, "rx: tail=%u Ctl_16=%04X Status=%08X\n", | ||
27513 | + tail, hostdesc->Ctl_16, hostdesc->Status); | ||
27514 | + | ||
27515 | + acx_l_process_rxbuf(adev, hostdesc->data); | ||
27516 | + | ||
27517 | + hostdesc->Status = 0; | ||
27518 | + /* flush all writes before adapter sees CTL_HOSTOWN change */ | ||
27519 | + wmb(); | ||
27520 | + /* Host no longer owns this, needs to be LAST */ | ||
27521 | + CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); | ||
27522 | + | ||
27523 | + /* ok, descriptor is handled, now check the next descriptor */ | ||
27524 | + hostdesc = &adev->rxhostdesc_start[tail]; | ||
27525 | + | ||
27526 | + /* if next descriptor is empty, then bail out */ | ||
27527 | + if (!(hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) | ||
27528 | + || !(hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) | ||
27529 | + break; | ||
27530 | + | ||
27531 | + tail = (tail + 1) % RX_CNT; | ||
27532 | + } | ||
27533 | +end: | ||
27534 | + adev->rx_tail = tail; | ||
27535 | + FN_EXIT0; | ||
27536 | +} | ||
27537 | + | ||
27538 | + | ||
27539 | +/*********************************************************************** | ||
27540 | +** acxpci_i_interrupt | ||
27541 | +** | ||
27542 | +** IRQ handler (atomic context, must not sleep, blah, blah) | ||
27543 | +*/ | ||
27544 | + | ||
27545 | +/* scan is complete. all frames now on the receive queue are valid */ | ||
27546 | +#define INFO_SCAN_COMPLETE 0x0001 | ||
27547 | +#define INFO_WEP_KEY_NOT_FOUND 0x0002 | ||
27548 | +/* hw has been reset as the result of a watchdog timer timeout */ | ||
27549 | +#define INFO_WATCH_DOG_RESET 0x0003 | ||
27550 | +/* failed to send out NULL frame from PS mode notification to AP */ | ||
27551 | +/* recommended action: try entering 802.11 PS mode again */ | ||
27552 | +#define INFO_PS_FAIL 0x0004 | ||
27553 | +/* encryption/decryption process on a packet failed */ | ||
27554 | +#define INFO_IV_ICV_FAILURE 0x0005 | ||
27555 | + | ||
27556 | +/* Info mailbox format: | ||
27557 | +2 bytes: type | ||
27558 | +2 bytes: status | ||
27559 | +more bytes may follow | ||
27560 | + rumors say about status: | ||
27561 | + 0x0000 info available (set by hw) | ||
27562 | + 0x0001 information received (must be set by host) | ||
27563 | + 0x1000 info available, mailbox overflowed (messages lost) (set by hw) | ||
27564 | + but in practice we've seen: | ||
27565 | + 0x9000 when we did not set status to 0x0001 on prev message | ||
27566 | + 0x1001 when we did set it | ||
27567 | + 0x0000 was never seen | ||
27568 | + conclusion: this is really a bitfield: | ||
27569 | + 0x1000 is 'info available' bit | ||
27570 | + 'mailbox overflowed' bit is 0x8000, not 0x1000 | ||
27571 | + value of 0x0000 probably means that there are no messages at all | ||
27572 | + P.S. I dunno how in hell hw is supposed to notice that messages are lost - | ||
27573 | + it does NOT clear bit 0x0001, and this bit will probably stay forever set | ||
27574 | + after we set it once. Let's hope this will be fixed in firmware someday | ||
27575 | +*/ | ||
27576 | + | ||
27577 | +static void | ||
27578 | +handle_info_irq(acx_device_t *adev) | ||
27579 | +{ | ||
27580 | +#if ACX_DEBUG | ||
27581 | + static const char * const info_type_msg[] = { | ||
27582 | + "(unknown)", | ||
27583 | + "scan complete", | ||
27584 | + "WEP key not found", | ||
27585 | + "internal watchdog reset was done", | ||
27586 | + "failed to send powersave (NULL frame) notification to AP", | ||
27587 | + "encrypt/decrypt on a packet has failed", | ||
27588 | + "TKIP tx keys disabled", | ||
27589 | + "TKIP rx keys disabled", | ||
27590 | + "TKIP rx: key ID not found", | ||
27591 | + "???", | ||
27592 | + "???", | ||
27593 | + "???", | ||
27594 | + "???", | ||
27595 | + "???", | ||
27596 | + "???", | ||
27597 | + "???", | ||
27598 | + "TKIP IV value exceeds thresh" | ||
27599 | + }; | ||
27600 | +#endif | ||
27601 | + u32 info_type, info_status; | ||
27602 | + | ||
27603 | + info_type = readl(adev->info_area); | ||
27604 | + info_status = (info_type >> 16); | ||
27605 | + info_type = (u16)info_type; | ||
27606 | + | ||
27607 | + /* inform fw that we have read this info message */ | ||
27608 | + writel(info_type | 0x00010000, adev->info_area); | ||
27609 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_INFOACK); | ||
27610 | + write_flush(adev); | ||
27611 | + | ||
27612 | + log(L_CTL, "info_type:%04X info_status:%04X\n", | ||
27613 | + info_type, info_status); | ||
27614 | + | ||
27615 | + log(L_IRQ, "got Info IRQ: status %04X type %04X: %s\n", | ||
27616 | + info_status, info_type, | ||
27617 | + info_type_msg[(info_type >= VEC_SIZE(info_type_msg)) ? | ||
27618 | + 0 : info_type] | ||
27619 | + ); | ||
27620 | +} | ||
27621 | + | ||
27622 | + | ||
27623 | +static void | ||
27624 | +log_unusual_irq(u16 irqtype) { | ||
27625 | + /* | ||
27626 | + if (!printk_ratelimit()) | ||
27627 | + return; | ||
27628 | + */ | ||
27629 | + | ||
27630 | + printk("acx: got"); | ||
27631 | + if (irqtype & HOST_INT_RX_DATA) { | ||
27632 | + printk(" Rx_Data"); | ||
27633 | + } | ||
27634 | + /* HOST_INT_TX_COMPLETE */ | ||
27635 | + if (irqtype & HOST_INT_TX_XFER) { | ||
27636 | + printk(" Tx_Xfer"); | ||
27637 | + } | ||
27638 | + /* HOST_INT_RX_COMPLETE */ | ||
27639 | + if (irqtype & HOST_INT_DTIM) { | ||
27640 | + printk(" DTIM"); | ||
27641 | + } | ||
27642 | + if (irqtype & HOST_INT_BEACON) { | ||
27643 | + printk(" Beacon"); | ||
27644 | + } | ||
27645 | + if (irqtype & HOST_INT_TIMER) { | ||
27646 | + log(L_IRQ, " Timer"); | ||
27647 | + } | ||
27648 | + if (irqtype & HOST_INT_KEY_NOT_FOUND) { | ||
27649 | + printk(" Key_Not_Found"); | ||
27650 | + } | ||
27651 | + if (irqtype & HOST_INT_IV_ICV_FAILURE) { | ||
27652 | + printk(" IV_ICV_Failure (crypto)"); | ||
27653 | + } | ||
27654 | + /* HOST_INT_CMD_COMPLETE */ | ||
27655 | + /* HOST_INT_INFO */ | ||
27656 | + if (irqtype & HOST_INT_OVERFLOW) { | ||
27657 | + printk(" Overflow"); | ||
27658 | + } | ||
27659 | + if (irqtype & HOST_INT_PROCESS_ERROR) { | ||
27660 | + printk(" Process_Error"); | ||
27661 | + } | ||
27662 | + /* HOST_INT_SCAN_COMPLETE */ | ||
27663 | + if (irqtype & HOST_INT_FCS_THRESHOLD) { | ||
27664 | + printk(" FCS_Threshold"); | ||
27665 | + } | ||
27666 | + if (irqtype & HOST_INT_UNKNOWN) { | ||
27667 | + printk(" Unknown"); | ||
27668 | + } | ||
27669 | + printk(" IRQ(s)\n"); | ||
27670 | +} | ||
27671 | + | ||
27672 | + | ||
27673 | +static void | ||
27674 | +update_link_quality_led(acx_device_t *adev) | ||
27675 | +{ | ||
27676 | + int qual; | ||
27677 | + | ||
27678 | + qual = acx_signal_determine_quality(adev->wstats.qual.level, adev->wstats.qual.noise); | ||
27679 | + if (qual > adev->brange_max_quality) | ||
27680 | + qual = adev->brange_max_quality; | ||
27681 | + | ||
27682 | + if (time_after(jiffies, adev->brange_time_last_state_change + | ||
27683 | + (HZ/2 - HZ/2 * (unsigned long)qual / adev->brange_max_quality ) )) { | ||
27684 | + acxpci_l_power_led(adev, (adev->brange_last_state == 0)); | ||
27685 | + adev->brange_last_state ^= 1; /* toggle */ | ||
27686 | + adev->brange_time_last_state_change = jiffies; | ||
27687 | + } | ||
27688 | +} | ||
27689 | + | ||
27690 | + | ||
27691 | +#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */ | ||
27692 | + | ||
27693 | +static irqreturn_t | ||
27694 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
27695 | +acxpci_i_interrupt(int irq, void *dev_id) | ||
27696 | +#else | ||
27697 | +acxpci_i_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
27698 | +#endif | ||
27699 | +{ | ||
27700 | + acx_device_t *adev; | ||
27701 | + unsigned long flags; | ||
27702 | + unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY; | ||
27703 | + register u16 irqtype; | ||
27704 | + u16 unmasked; | ||
27705 | + | ||
27706 | + adev = ndev2adev((struct net_device*)dev_id); | ||
27707 | + | ||
27708 | + /* LOCKING: can just spin_lock() since IRQs are disabled anyway. | ||
27709 | + * I am paranoid */ | ||
27710 | + acx_lock(adev, flags); | ||
27711 | + | ||
27712 | + unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); | ||
27713 | + if (unlikely(0xffff == unmasked)) { | ||
27714 | + /* 0xffff value hints at missing hardware, | ||
27715 | + * so don't do anything. | ||
27716 | + * Not very clean, but other drivers do the same... */ | ||
27717 | + log(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n"); | ||
27718 | + goto none; | ||
27719 | + } | ||
27720 | + | ||
27721 | + /* We will check only "interesting" IRQ types */ | ||
27722 | + irqtype = unmasked & ~adev->irq_mask; | ||
27723 | + if (!irqtype) { | ||
27724 | + /* We are on a shared IRQ line and it wasn't our IRQ */ | ||
27725 | + log(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n", | ||
27726 | + unmasked, adev->irq_mask); | ||
27727 | + goto none; | ||
27728 | + } | ||
27729 | + | ||
27730 | + /* Done here because IRQ_NONEs taking three lines of log | ||
27731 | + ** drive me crazy */ | ||
27732 | + FN_ENTER; | ||
27733 | + | ||
27734 | +#define IRQ_ITERATE 1 | ||
27735 | +#if IRQ_ITERATE | ||
27736 | +if (jiffies != adev->irq_last_jiffies) { | ||
27737 | + adev->irq_loops_this_jiffy = 0; | ||
27738 | + adev->irq_last_jiffies = jiffies; | ||
27739 | +} | ||
27740 | + | ||
27741 | +/* safety condition; we'll normally abort loop below | ||
27742 | + * in case no IRQ type occurred */ | ||
27743 | +while (likely(--irqcount)) { | ||
27744 | +#endif | ||
27745 | + /* ACK all IRQs ASAP */ | ||
27746 | + write_reg16(adev, IO_ACX_IRQ_ACK, 0xffff); | ||
27747 | + | ||
27748 | + log(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n", | ||
27749 | + unmasked, adev->irq_mask, irqtype); | ||
27750 | + | ||
27751 | + /* Handle most important IRQ types first */ | ||
27752 | + if (irqtype & HOST_INT_RX_COMPLETE) { | ||
27753 | + log(L_IRQ, "got Rx_Complete IRQ\n"); | ||
27754 | + acxpci_l_process_rxdesc(adev); | ||
27755 | + } | ||
27756 | + if (irqtype & HOST_INT_TX_COMPLETE) { | ||
27757 | + log(L_IRQ, "got Tx_Complete IRQ\n"); | ||
27758 | + /* don't clean up on each Tx complete, wait a bit | ||
27759 | + * unless we're going towards full, in which case | ||
27760 | + * we do it immediately, too (otherwise we might lockup | ||
27761 | + * with a full Tx buffer if we go into | ||
27762 | + * acxpci_l_clean_txdesc() at a time when we won't wakeup | ||
27763 | + * the net queue in there for some reason...) */ | ||
27764 | + if (adev->tx_free <= TX_START_CLEAN) { | ||
27765 | +#if TX_CLEANUP_IN_SOFTIRQ | ||
27766 | + acx_schedule_task(adev, ACX_AFTER_IRQ_TX_CLEANUP); | ||
27767 | +#else | ||
27768 | + acxpci_l_clean_txdesc(adev); | ||
27769 | +#endif | ||
27770 | + } | ||
27771 | + } | ||
27772 | + | ||
27773 | + /* Less frequent ones */ | ||
27774 | + if (irqtype & (0 | ||
27775 | + | HOST_INT_CMD_COMPLETE | ||
27776 | + | HOST_INT_INFO | ||
27777 | + | HOST_INT_SCAN_COMPLETE | ||
27778 | + )) { | ||
27779 | + if (irqtype & HOST_INT_CMD_COMPLETE) { | ||
27780 | + log(L_IRQ, "got Command_Complete IRQ\n"); | ||
27781 | + /* save the state for the running issue_cmd() */ | ||
27782 | + SET_BIT(adev->irq_status, HOST_INT_CMD_COMPLETE); | ||
27783 | + } | ||
27784 | + if (irqtype & HOST_INT_INFO) { | ||
27785 | + handle_info_irq(adev); | ||
27786 | + } | ||
27787 | + if (irqtype & HOST_INT_SCAN_COMPLETE) { | ||
27788 | + log(L_IRQ, "got Scan_Complete IRQ\n"); | ||
27789 | + /* need to do that in process context */ | ||
27790 | + acx_schedule_task(adev, ACX_AFTER_IRQ_COMPLETE_SCAN); | ||
27791 | + /* remember that fw is not scanning anymore */ | ||
27792 | + SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE); | ||
27793 | + } | ||
27794 | + } | ||
27795 | + | ||
27796 | + /* These we just log, but either they happen rarely | ||
27797 | + * or we keep them masked out */ | ||
27798 | + if (irqtype & (0 | ||
27799 | + | HOST_INT_RX_DATA | ||
27800 | + /* | HOST_INT_TX_COMPLETE */ | ||
27801 | + | HOST_INT_TX_XFER | ||
27802 | + /* | HOST_INT_RX_COMPLETE */ | ||
27803 | + | HOST_INT_DTIM | ||
27804 | + | HOST_INT_BEACON | ||
27805 | + | HOST_INT_TIMER | ||
27806 | + | HOST_INT_KEY_NOT_FOUND | ||
27807 | + | HOST_INT_IV_ICV_FAILURE | ||
27808 | + /* | HOST_INT_CMD_COMPLETE */ | ||
27809 | + /* | HOST_INT_INFO */ | ||
27810 | + | HOST_INT_OVERFLOW | ||
27811 | + | HOST_INT_PROCESS_ERROR | ||
27812 | + /* | HOST_INT_SCAN_COMPLETE */ | ||
27813 | + | HOST_INT_FCS_THRESHOLD | ||
27814 | + | HOST_INT_UNKNOWN | ||
27815 | + )) { | ||
27816 | + log_unusual_irq(irqtype); | ||
27817 | + } | ||
27818 | + | ||
27819 | +#if IRQ_ITERATE | ||
27820 | + unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); | ||
27821 | + irqtype = unmasked & ~adev->irq_mask; | ||
27822 | + /* Bail out if no new IRQ bits or if all are masked out */ | ||
27823 | + if (!irqtype) | ||
27824 | + break; | ||
27825 | + | ||
27826 | + if (unlikely(++adev->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) { | ||
27827 | + printk(KERN_ERR "acx: too many interrupts per jiffy!\n"); | ||
27828 | + /* Looks like card floods us with IRQs! Try to stop that */ | ||
27829 | + write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); | ||
27830 | + /* This will short-circuit all future attempts to handle IRQ. | ||
27831 | + * We cant do much more... */ | ||
27832 | + adev->irq_mask = 0; | ||
27833 | + break; | ||
27834 | + } | ||
27835 | +} | ||
27836 | +#endif | ||
27837 | + /* Routine to perform blink with range */ | ||
27838 | + if (unlikely(adev->led_power == 2)) | ||
27839 | + update_link_quality_led(adev); | ||
27840 | + | ||
27841 | +/* handled: */ | ||
27842 | + /* write_flush(adev); - not needed, last op was read anyway */ | ||
27843 | + acx_unlock(adev, flags); | ||
27844 | + FN_EXIT0; | ||
27845 | + return IRQ_HANDLED; | ||
27846 | + | ||
27847 | +none: | ||
27848 | + acx_unlock(adev, flags); | ||
27849 | + return IRQ_NONE; | ||
27850 | +} | ||
27851 | + | ||
27852 | + | ||
27853 | +/*********************************************************************** | ||
27854 | +** acxpci_l_power_led | ||
27855 | +*/ | ||
27856 | +void | ||
27857 | +acxpci_l_power_led(acx_device_t *adev, int enable) | ||
27858 | +{ | ||
27859 | + u16 gpio_pled = IS_ACX111(adev) ? 0x0040 : 0x0800; | ||
27860 | + | ||
27861 | + /* A hack. Not moving message rate limiting to adev->xxx | ||
27862 | + * (it's only a debug message after all) */ | ||
27863 | + static int rate_limit = 0; | ||
27864 | + | ||
27865 | + if (rate_limit++ < 3) | ||
27866 | + log(L_IOCTL, "Please report in case toggling the power " | ||
27867 | + "LED doesn't work for your card!\n"); | ||
27868 | + if (enable) | ||
27869 | + write_reg16(adev, IO_ACX_GPIO_OUT, | ||
27870 | + read_reg16(adev, IO_ACX_GPIO_OUT) & ~gpio_pled); | ||
27871 | + else | ||
27872 | + write_reg16(adev, IO_ACX_GPIO_OUT, | ||
27873 | + read_reg16(adev, IO_ACX_GPIO_OUT) | gpio_pled); | ||
27874 | +} | ||
27875 | + | ||
27876 | + | ||
27877 | +/*********************************************************************** | ||
27878 | +** Ioctls | ||
27879 | +*/ | ||
27880 | + | ||
27881 | +/*********************************************************************** | ||
27882 | +*/ | ||
27883 | +int | ||
27884 | +acx111pci_ioctl_info( | ||
27885 | + struct net_device *ndev, | ||
27886 | + struct iw_request_info *info, | ||
27887 | + struct iw_param *vwrq, | ||
27888 | + char *extra) | ||
27889 | +{ | ||
27890 | +#if ACX_DEBUG > 1 | ||
27891 | + acx_device_t *adev = ndev2adev(ndev); | ||
27892 | + rxdesc_t *rxdesc; | ||
27893 | + txdesc_t *txdesc; | ||
27894 | + rxhostdesc_t *rxhostdesc; | ||
27895 | + txhostdesc_t *txhostdesc; | ||
27896 | + struct acx111_ie_memoryconfig memconf; | ||
27897 | + struct acx111_ie_queueconfig queueconf; | ||
27898 | + unsigned long flags; | ||
27899 | + int i; | ||
27900 | + char memmap[0x34]; | ||
27901 | + char rxconfig[0x8]; | ||
27902 | + char fcserror[0x8]; | ||
27903 | + char ratefallback[0x5]; | ||
27904 | + | ||
27905 | + if ( !(acx_debug & (L_IOCTL|L_DEBUG)) ) | ||
27906 | + return OK; | ||
27907 | + /* using printk() since we checked debug flag already */ | ||
27908 | + | ||
27909 | + acx_sem_lock(adev); | ||
27910 | + | ||
27911 | + if (!IS_ACX111(adev)) { | ||
27912 | + printk("acx111-specific function called " | ||
27913 | + "with non-acx111 chip, aborting\n"); | ||
27914 | + goto end_ok; | ||
27915 | + } | ||
27916 | + | ||
27917 | + /* get Acx111 Memory Configuration */ | ||
27918 | + memset(&memconf, 0, sizeof(memconf)); | ||
27919 | + /* BTW, fails with 12 (Write only) error code. | ||
27920 | + ** Retained for easy testing of issue_cmd error handling :) */ | ||
27921 | + acx_s_interrogate(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG); | ||
27922 | + | ||
27923 | + /* get Acx111 Queue Configuration */ | ||
27924 | + memset(&queueconf, 0, sizeof(queueconf)); | ||
27925 | + acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS); | ||
27926 | + | ||
27927 | + /* get Acx111 Memory Map */ | ||
27928 | + memset(memmap, 0, sizeof(memmap)); | ||
27929 | + acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP); | ||
27930 | + | ||
27931 | + /* get Acx111 Rx Config */ | ||
27932 | + memset(rxconfig, 0, sizeof(rxconfig)); | ||
27933 | + acx_s_interrogate(adev, &rxconfig, ACX1xx_IE_RXCONFIG); | ||
27934 | + | ||
27935 | + /* get Acx111 fcs error count */ | ||
27936 | + memset(fcserror, 0, sizeof(fcserror)); | ||
27937 | + acx_s_interrogate(adev, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT); | ||
27938 | + | ||
27939 | + /* get Acx111 rate fallback */ | ||
27940 | + memset(ratefallback, 0, sizeof(ratefallback)); | ||
27941 | + acx_s_interrogate(adev, &ratefallback, ACX1xx_IE_RATE_FALLBACK); | ||
27942 | + | ||
27943 | + /* force occurrence of a beacon interrupt */ | ||
27944 | + /* TODO: comment why is this necessary */ | ||
27945 | + write_reg16(adev, IO_ACX_HINT_TRIG, HOST_INT_BEACON); | ||
27946 | + | ||
27947 | + /* dump Acx111 Mem Configuration */ | ||
27948 | + printk("dump mem config:\n" | ||
27949 | + "data read: %d, struct size: %d\n" | ||
27950 | + "Number of stations: %1X\n" | ||
27951 | + "Memory block size: %1X\n" | ||
27952 | + "tx/rx memory block allocation: %1X\n" | ||
27953 | + "count rx: %X / tx: %X queues\n" | ||
27954 | + "options %1X\n" | ||
27955 | + "fragmentation %1X\n" | ||
27956 | + "Rx Queue 1 Count Descriptors: %X\n" | ||
27957 | + "Rx Queue 1 Host Memory Start: %X\n" | ||
27958 | + "Tx Queue 1 Count Descriptors: %X\n" | ||
27959 | + "Tx Queue 1 Attributes: %X\n", | ||
27960 | + memconf.len, (int) sizeof(memconf), | ||
27961 | + memconf.no_of_stations, | ||
27962 | + memconf.memory_block_size, | ||
27963 | + memconf.tx_rx_memory_block_allocation, | ||
27964 | + memconf.count_rx_queues, memconf.count_tx_queues, | ||
27965 | + memconf.options, | ||
27966 | + memconf.fragmentation, | ||
27967 | + memconf.rx_queue1_count_descs, | ||
27968 | + acx2cpu(memconf.rx_queue1_host_rx_start), | ||
27969 | + memconf.tx_queue1_count_descs, | ||
27970 | + memconf.tx_queue1_attributes); | ||
27971 | + | ||
27972 | + /* dump Acx111 Queue Configuration */ | ||
27973 | + printk("dump queue head:\n" | ||
27974 | + "data read: %d, struct size: %d\n" | ||
27975 | + "tx_memory_block_address (from card): %X\n" | ||
27976 | + "rx_memory_block_address (from card): %X\n" | ||
27977 | + "rx1_queue address (from card): %X\n" | ||
27978 | + "tx1_queue address (from card): %X\n" | ||
27979 | + "tx1_queue attributes (from card): %X\n", | ||
27980 | + queueconf.len, (int) sizeof(queueconf), | ||
27981 | + queueconf.tx_memory_block_address, | ||
27982 | + queueconf.rx_memory_block_address, | ||
27983 | + queueconf.rx1_queue_address, | ||
27984 | + queueconf.tx1_queue_address, | ||
27985 | + queueconf.tx1_attributes); | ||
27986 | + | ||
27987 | + /* dump Acx111 Mem Map */ | ||
27988 | + printk("dump mem map:\n" | ||
27989 | + "data read: %d, struct size: %d\n" | ||
27990 | + "Code start: %X\n" | ||
27991 | + "Code end: %X\n" | ||
27992 | + "WEP default key start: %X\n" | ||
27993 | + "WEP default key end: %X\n" | ||
27994 | + "STA table start: %X\n" | ||
27995 | + "STA table end: %X\n" | ||
27996 | + "Packet template start: %X\n" | ||
27997 | + "Packet template end: %X\n" | ||
27998 | + "Queue memory start: %X\n" | ||
27999 | + "Queue memory end: %X\n" | ||
28000 | + "Packet memory pool start: %X\n" | ||
28001 | + "Packet memory pool end: %X\n" | ||
28002 | + "iobase: %p\n" | ||
28003 | + "iobase2: %p\n", | ||
28004 | + *((u16 *)&memmap[0x02]), (int) sizeof(memmap), | ||
28005 | + *((u32 *)&memmap[0x04]), | ||
28006 | + *((u32 *)&memmap[0x08]), | ||
28007 | + *((u32 *)&memmap[0x0C]), | ||
28008 | + *((u32 *)&memmap[0x10]), | ||
28009 | + *((u32 *)&memmap[0x14]), | ||
28010 | + *((u32 *)&memmap[0x18]), | ||
28011 | + *((u32 *)&memmap[0x1C]), | ||
28012 | + *((u32 *)&memmap[0x20]), | ||
28013 | + *((u32 *)&memmap[0x24]), | ||
28014 | + *((u32 *)&memmap[0x28]), | ||
28015 | + *((u32 *)&memmap[0x2C]), | ||
28016 | + *((u32 *)&memmap[0x30]), | ||
28017 | + adev->iobase, | ||
28018 | + adev->iobase2); | ||
28019 | + | ||
28020 | + /* dump Acx111 Rx Config */ | ||
28021 | + printk("dump rx config:\n" | ||
28022 | + "data read: %d, struct size: %d\n" | ||
28023 | + "rx config: %X\n" | ||
28024 | + "rx filter config: %X\n", | ||
28025 | + *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig), | ||
28026 | + *((u16 *)&rxconfig[0x04]), | ||
28027 | + *((u16 *)&rxconfig[0x06])); | ||
28028 | + | ||
28029 | + /* dump Acx111 fcs error */ | ||
28030 | + printk("dump fcserror:\n" | ||
28031 | + "data read: %d, struct size: %d\n" | ||
28032 | + "fcserrors: %X\n", | ||
28033 | + *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror), | ||
28034 | + *((u32 *)&fcserror[0x04])); | ||
28035 | + | ||
28036 | + /* dump Acx111 rate fallback */ | ||
28037 | + printk("dump rate fallback:\n" | ||
28038 | + "data read: %d, struct size: %d\n" | ||
28039 | + "ratefallback: %X\n", | ||
28040 | + *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback), | ||
28041 | + *((u8 *)&ratefallback[0x04])); | ||
28042 | + | ||
28043 | + /* protect against IRQ */ | ||
28044 | + acx_lock(adev, flags); | ||
28045 | + | ||
28046 | + /* dump acx111 internal rx descriptor ring buffer */ | ||
28047 | + rxdesc = adev->rxdesc_start; | ||
28048 | + | ||
28049 | + /* loop over complete receive pool */ | ||
28050 | + if (rxdesc) for (i = 0; i < RX_CNT; i++) { | ||
28051 | + printk("\ndump internal rxdesc %d:\n" | ||
28052 | + "mem pos %p\n" | ||
28053 | + "next 0x%X\n" | ||
28054 | + "acx mem pointer (dynamic) 0x%X\n" | ||
28055 | + "CTL (dynamic) 0x%X\n" | ||
28056 | + "Rate (dynamic) 0x%X\n" | ||
28057 | + "RxStatus (dynamic) 0x%X\n" | ||
28058 | + "Mod/Pre (dynamic) 0x%X\n", | ||
28059 | + i, | ||
28060 | + rxdesc, | ||
28061 | + acx2cpu(rxdesc->pNextDesc), | ||
28062 | + acx2cpu(rxdesc->ACXMemPtr), | ||
28063 | + rxdesc->Ctl_8, | ||
28064 | + rxdesc->rate, | ||
28065 | + rxdesc->error, | ||
28066 | + rxdesc->SNR); | ||
28067 | + rxdesc++; | ||
28068 | + } | ||
28069 | + | ||
28070 | + /* dump host rx descriptor ring buffer */ | ||
28071 | + | ||
28072 | + rxhostdesc = adev->rxhostdesc_start; | ||
28073 | + | ||
28074 | + /* loop over complete receive pool */ | ||
28075 | + if (rxhostdesc) for (i = 0; i < RX_CNT; i++) { | ||
28076 | + printk("\ndump host rxdesc %d:\n" | ||
28077 | + "mem pos %p\n" | ||
28078 | + "buffer mem pos 0x%X\n" | ||
28079 | + "buffer mem offset 0x%X\n" | ||
28080 | + "CTL 0x%X\n" | ||
28081 | + "Length 0x%X\n" | ||
28082 | + "next 0x%X\n" | ||
28083 | + "Status 0x%X\n", | ||
28084 | + i, | ||
28085 | + rxhostdesc, | ||
28086 | + acx2cpu(rxhostdesc->data_phy), | ||
28087 | + rxhostdesc->data_offset, | ||
28088 | + le16_to_cpu(rxhostdesc->Ctl_16), | ||
28089 | + le16_to_cpu(rxhostdesc->length), | ||
28090 | + acx2cpu(rxhostdesc->desc_phy_next), | ||
28091 | + rxhostdesc->Status); | ||
28092 | + rxhostdesc++; | ||
28093 | + } | ||
28094 | + | ||
28095 | + /* dump acx111 internal tx descriptor ring buffer */ | ||
28096 | + txdesc = adev->txdesc_start; | ||
28097 | + | ||
28098 | + /* loop over complete transmit pool */ | ||
28099 | + if (txdesc) for (i = 0; i < TX_CNT; i++) { | ||
28100 | + printk("\ndump internal txdesc %d:\n" | ||
28101 | + "size 0x%X\n" | ||
28102 | + "mem pos %p\n" | ||
28103 | + "next 0x%X\n" | ||
28104 | + "acx mem pointer (dynamic) 0x%X\n" | ||
28105 | + "host mem pointer (dynamic) 0x%X\n" | ||
28106 | + "length (dynamic) 0x%X\n" | ||
28107 | + "CTL (dynamic) 0x%X\n" | ||
28108 | + "CTL2 (dynamic) 0x%X\n" | ||
28109 | + "Status (dynamic) 0x%X\n" | ||
28110 | + "Rate (dynamic) 0x%X\n", | ||
28111 | + i, | ||
28112 | + (int) sizeof(struct txdesc), | ||
28113 | + txdesc, | ||
28114 | + acx2cpu(txdesc->pNextDesc), | ||
28115 | + acx2cpu(txdesc->AcxMemPtr), | ||
28116 | + acx2cpu(txdesc->HostMemPtr), | ||
28117 | + le16_to_cpu(txdesc->total_length), | ||
28118 | + txdesc->Ctl_8, | ||
28119 | + txdesc->Ctl2_8, txdesc->error, | ||
28120 | + txdesc->u.r1.rate); | ||
28121 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
28122 | + } | ||
28123 | + | ||
28124 | + /* dump host tx descriptor ring buffer */ | ||
28125 | + | ||
28126 | + txhostdesc = adev->txhostdesc_start; | ||
28127 | + | ||
28128 | + /* loop over complete host send pool */ | ||
28129 | + if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) { | ||
28130 | + printk("\ndump host txdesc %d:\n" | ||
28131 | + "mem pos %p\n" | ||
28132 | + "buffer mem pos 0x%X\n" | ||
28133 | + "buffer mem offset 0x%X\n" | ||
28134 | + "CTL 0x%X\n" | ||
28135 | + "Length 0x%X\n" | ||
28136 | + "next 0x%X\n" | ||
28137 | + "Status 0x%X\n", | ||
28138 | + i, | ||
28139 | + txhostdesc, | ||
28140 | + acx2cpu(txhostdesc->data_phy), | ||
28141 | + txhostdesc->data_offset, | ||
28142 | + le16_to_cpu(txhostdesc->Ctl_16), | ||
28143 | + le16_to_cpu(txhostdesc->length), | ||
28144 | + acx2cpu(txhostdesc->desc_phy_next), | ||
28145 | + le32_to_cpu(txhostdesc->Status)); | ||
28146 | + txhostdesc++; | ||
28147 | + } | ||
28148 | + | ||
28149 | + /* write_reg16(adev, 0xb4, 0x4); */ | ||
28150 | + | ||
28151 | + acx_unlock(adev, flags); | ||
28152 | +end_ok: | ||
28153 | + | ||
28154 | + acx_sem_unlock(adev); | ||
28155 | +#endif /* ACX_DEBUG */ | ||
28156 | + return OK; | ||
28157 | +} | ||
28158 | + | ||
28159 | + | ||
28160 | +/*********************************************************************** | ||
28161 | +*/ | ||
28162 | +int | ||
28163 | +acx100pci_ioctl_set_phy_amp_bias( | ||
28164 | + struct net_device *ndev, | ||
28165 | + struct iw_request_info *info, | ||
28166 | + struct iw_param *vwrq, | ||
28167 | + char *extra) | ||
28168 | +{ | ||
28169 | + acx_device_t *adev = ndev2adev(ndev); | ||
28170 | + unsigned long flags; | ||
28171 | + u16 gpio_old; | ||
28172 | + | ||
28173 | + if (!IS_ACX100(adev)) { | ||
28174 | + /* WARNING!!! | ||
28175 | + * Removing this check *might* damage | ||
28176 | + * hardware, since we're tweaking GPIOs here after all!!! | ||
28177 | + * You've been warned... | ||
28178 | + * WARNING!!! */ | ||
28179 | + printk("acx: sorry, setting bias level for non-acx100 " | ||
28180 | + "is not supported yet\n"); | ||
28181 | + return OK; | ||
28182 | + } | ||
28183 | + | ||
28184 | + if (*extra > 7) { | ||
28185 | + printk("acx: invalid bias parameter, range is 0-7\n"); | ||
28186 | + return -EINVAL; | ||
28187 | + } | ||
28188 | + | ||
28189 | + acx_sem_lock(adev); | ||
28190 | + | ||
28191 | + /* Need to lock accesses to [IO_ACX_GPIO_OUT]: | ||
28192 | + * IRQ handler uses it to update LED */ | ||
28193 | + acx_lock(adev, flags); | ||
28194 | + gpio_old = read_reg16(adev, IO_ACX_GPIO_OUT); | ||
28195 | + write_reg16(adev, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8)); | ||
28196 | + acx_unlock(adev, flags); | ||
28197 | + | ||
28198 | + log(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old); | ||
28199 | + printk("%s: PHY power amplifier bias: old:%d, new:%d\n", | ||
28200 | + ndev->name, | ||
28201 | + (gpio_old & 0x0700) >> 8, (unsigned char)*extra); | ||
28202 | + | ||
28203 | + acx_sem_unlock(adev); | ||
28204 | + | ||
28205 | + return OK; | ||
28206 | +} | ||
28207 | + | ||
28208 | + | ||
28209 | +/*************************************************************** | ||
28210 | +** acxpci_l_alloc_tx | ||
28211 | +** Actually returns a txdesc_t* ptr | ||
28212 | +** | ||
28213 | +** FIXME: in case of fragments, should allocate multiple descrs | ||
28214 | +** after figuring out how many we need and whether we still have | ||
28215 | +** sufficiently many. | ||
28216 | +*/ | ||
28217 | +tx_t* | ||
28218 | +acxpci_l_alloc_tx(acx_device_t *adev) | ||
28219 | +{ | ||
28220 | + struct txdesc *txdesc; | ||
28221 | + unsigned head; | ||
28222 | + u8 ctl8; | ||
28223 | + | ||
28224 | + FN_ENTER; | ||
28225 | + | ||
28226 | + if (unlikely(!adev->tx_free)) { | ||
28227 | + printk("acx: BUG: no free txdesc left\n"); | ||
28228 | + txdesc = NULL; | ||
28229 | + goto end; | ||
28230 | + } | ||
28231 | + | ||
28232 | + head = adev->tx_head; | ||
28233 | + txdesc = get_txdesc(adev, head); | ||
28234 | + ctl8 = txdesc->Ctl_8; | ||
28235 | + | ||
28236 | + /* 2005-10-11: there were several bug reports on this happening | ||
28237 | + ** but now cause seems to be understood & fixed */ | ||
28238 | + if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_ACXDONE_HOSTOWN))) { | ||
28239 | + /* whoops, descr at current index is not free, so probably | ||
28240 | + * ring buffer already full */ | ||
28241 | + printk("acx: BUG: tx_head:%d Ctl8:0x%02X - failed to find " | ||
28242 | + "free txdesc\n", head, ctl8); | ||
28243 | + txdesc = NULL; | ||
28244 | + goto end; | ||
28245 | + } | ||
28246 | + | ||
28247 | + /* Needed in case txdesc won't be eventually submitted for tx */ | ||
28248 | + txdesc->Ctl_8 = DESC_CTL_ACXDONE_HOSTOWN; | ||
28249 | + | ||
28250 | + adev->tx_free--; | ||
28251 | + log(L_BUFT, "tx: got desc %u, %u remain\n", | ||
28252 | + head, adev->tx_free); | ||
28253 | + /* Keep a few free descs between head and tail of tx ring. | ||
28254 | + ** It is not absolutely needed, just feels safer */ | ||
28255 | + if (adev->tx_free < TX_STOP_QUEUE) { | ||
28256 | + log(L_BUF, "stop queue (%u tx desc left)\n", | ||
28257 | + adev->tx_free); | ||
28258 | + acx_stop_queue(adev->ndev, NULL); | ||
28259 | + } | ||
28260 | + | ||
28261 | + /* returning current descriptor, so advance to next free one */ | ||
28262 | + adev->tx_head = (head + 1) % TX_CNT; | ||
28263 | +end: | ||
28264 | + FN_EXIT0; | ||
28265 | + | ||
28266 | + return (tx_t*)txdesc; | ||
28267 | +} | ||
28268 | + | ||
28269 | + | ||
28270 | +/*********************************************************************** | ||
28271 | +*/ | ||
28272 | +void* | ||
28273 | +acxpci_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque) | ||
28274 | +{ | ||
28275 | + return get_txhostdesc(adev, (txdesc_t*)tx_opaque)->data; | ||
28276 | +} | ||
28277 | + | ||
28278 | + | ||
28279 | +/*********************************************************************** | ||
28280 | +** acxpci_l_tx_data | ||
28281 | +** | ||
28282 | +** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). | ||
28283 | +** Can be called from acx_i_start_xmit (data frames from net core). | ||
28284 | +** | ||
28285 | +** FIXME: in case of fragments, should loop over the number of | ||
28286 | +** pre-allocated tx descrs, properly setting up transfer data and | ||
28287 | +** CTL_xxx flags according to fragment number. | ||
28288 | +*/ | ||
28289 | +void | ||
28290 | +acxpci_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int len) | ||
28291 | +{ | ||
28292 | + txdesc_t *txdesc = (txdesc_t*)tx_opaque; | ||
28293 | + txhostdesc_t *hostdesc1, *hostdesc2; | ||
28294 | + client_t *clt; | ||
28295 | + u16 rate_cur; | ||
28296 | + u8 Ctl_8, Ctl2_8; | ||
28297 | + | ||
28298 | + FN_ENTER; | ||
28299 | + | ||
28300 | + /* fw doesn't tx such packets anyhow */ | ||
28301 | + if (unlikely(len < WLAN_HDR_A3_LEN)) | ||
28302 | + goto end; | ||
28303 | + | ||
28304 | + hostdesc1 = get_txhostdesc(adev, txdesc); | ||
28305 | + /* modify flag status in separate variable to be able to write it back | ||
28306 | + * in one big swoop later (also in order to have less device memory | ||
28307 | + * accesses) */ | ||
28308 | + Ctl_8 = txdesc->Ctl_8; | ||
28309 | + Ctl2_8 = 0; /* really need to init it to 0, not txdesc->Ctl2_8, it seems */ | ||
28310 | + | ||
28311 | + hostdesc2 = hostdesc1 + 1; | ||
28312 | + | ||
28313 | + /* DON'T simply set Ctl field to 0 here globally, | ||
28314 | + * it needs to maintain a consistent flag status (those are state flags!!), | ||
28315 | + * otherwise it may lead to severe disruption. Only set or reset particular | ||
28316 | + * flags at the exact moment this is needed... */ | ||
28317 | + | ||
28318 | + /* let chip do RTS/CTS handshaking before sending | ||
28319 | + * in case packet size exceeds threshold */ | ||
28320 | + if (len > adev->rts_threshold) | ||
28321 | + SET_BIT(Ctl2_8, DESC_CTL2_RTS); | ||
28322 | + else | ||
28323 | + CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS); | ||
28324 | + | ||
28325 | + switch (adev->mode) { | ||
28326 | + case ACX_MODE_0_ADHOC: | ||
28327 | + case ACX_MODE_3_AP: | ||
28328 | + clt = acx_l_sta_list_get(adev, ((wlan_hdr_t*)hostdesc1->data)->a1); | ||
28329 | + break; | ||
28330 | + case ACX_MODE_2_STA: | ||
28331 | + clt = adev->ap_client; | ||
28332 | + break; | ||
28333 | +#if 0 | ||
28334 | +/* testing was done on acx111: */ | ||
28335 | + case ACX_MODE_MONITOR: | ||
28336 | + SET_BIT(Ctl2_8, 0 | ||
28337 | +/* sends CTS to self before packet */ | ||
28338 | + + DESC_CTL2_SEQ /* don't increase sequence field */ | ||
28339 | +/* not working (looks like good fcs is still added) */ | ||
28340 | + + DESC_CTL2_FCS /* don't add the FCS */ | ||
28341 | +/* not tested */ | ||
28342 | + + DESC_CTL2_MORE_FRAG | ||
28343 | +/* not tested */ | ||
28344 | + + DESC_CTL2_RETRY /* don't increase retry field */ | ||
28345 | +/* not tested */ | ||
28346 | + + DESC_CTL2_POWER /* don't increase power mgmt. field */ | ||
28347 | +/* no effect */ | ||
28348 | + + DESC_CTL2_WEP /* encrypt this frame */ | ||
28349 | +/* not tested */ | ||
28350 | + + DESC_CTL2_DUR /* don't increase duration field */ | ||
28351 | + ); | ||
28352 | + /* fallthrough */ | ||
28353 | +#endif | ||
28354 | + default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ | ||
28355 | + clt = NULL; | ||
28356 | + break; | ||
28357 | + } | ||
28358 | + | ||
28359 | + rate_cur = clt ? clt->rate_cur : adev->rate_bcast; | ||
28360 | + if (unlikely(!rate_cur)) { | ||
28361 | + printk("acx: driver bug! bad ratemask\n"); | ||
28362 | + goto end; | ||
28363 | + } | ||
28364 | + | ||
28365 | + /* used in tx cleanup routine for auto rate and accounting: */ | ||
28366 | + put_txcr(adev, txdesc, clt, rate_cur); | ||
28367 | + | ||
28368 | + txdesc->total_length = cpu_to_le16(len); | ||
28369 | + hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN); | ||
28370 | + if (IS_ACX111(adev)) { | ||
28371 | + /* note that if !txdesc->do_auto, txrate->cur | ||
28372 | + ** has only one nonzero bit */ | ||
28373 | + txdesc->u.r2.rate111 = cpu_to_le16( | ||
28374 | + rate_cur | ||
28375 | + /* WARNING: I was never able to make it work with prism54 AP. | ||
28376 | + ** It was falling down to 1Mbit where shortpre is not applicable, | ||
28377 | + ** and not working at all at "5,11 basic rates only" setting. | ||
28378 | + ** I even didn't see tx packets in radio packet capture. | ||
28379 | + ** Disabled for now --vda */ | ||
28380 | + /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */ | ||
28381 | + ); | ||
28382 | +#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS | ||
28383 | + /* should add this to rate111 above as necessary */ | ||
28384 | + | (clt->pbcc511 ? RATE111_PBCC511 : 0) | ||
28385 | +#endif | ||
28386 | + hostdesc1->length = cpu_to_le16(len); | ||
28387 | + } else { /* ACX100 */ | ||
28388 | + u8 rate_100 = clt ? clt->rate_100 : adev->rate_bcast100; | ||
28389 | + txdesc->u.r1.rate = rate_100; | ||
28390 | +#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS | ||
28391 | + if (clt->pbcc511) { | ||
28392 | + if (n == RATE100_5 || n == RATE100_11) | ||
28393 | + n |= RATE100_PBCC511; | ||
28394 | + } | ||
28395 | + | ||
28396 | + if (clt->shortpre && (clt->cur != RATE111_1)) | ||
28397 | + SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */ | ||
28398 | +#endif | ||
28399 | + /* set autodma and reclaim and 1st mpdu */ | ||
28400 | + SET_BIT(Ctl_8, DESC_CTL_AUTODMA | DESC_CTL_RECLAIM | DESC_CTL_FIRSTFRAG); | ||
28401 | +#if ACX_FRAGMENTATION | ||
28402 | + /* SET_BIT(Ctl2_8, DESC_CTL2_MORE_FRAG); cannot set it unconditionally, needs to be set for all non-last fragments */ | ||
28403 | +#endif | ||
28404 | + hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN); | ||
28405 | + } | ||
28406 | + /* don't need to clean ack/rts statistics here, already | ||
28407 | + * done on descr cleanup */ | ||
28408 | + | ||
28409 | + /* clears HOSTOWN and ACXDONE bits, thus telling that the descriptors | ||
28410 | + * are now owned by the acx100; do this as LAST operation */ | ||
28411 | + CLEAR_BIT(Ctl_8, DESC_CTL_ACXDONE_HOSTOWN); | ||
28412 | + /* flush writes before we release hostdesc to the adapter here */ | ||
28413 | + wmb(); | ||
28414 | + CLEAR_BIT(hostdesc1->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); | ||
28415 | + CLEAR_BIT(hostdesc2->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); | ||
28416 | + | ||
28417 | + /* write back modified flags */ | ||
28418 | + txdesc->Ctl2_8 = Ctl2_8; | ||
28419 | + txdesc->Ctl_8 = Ctl_8; | ||
28420 | + /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */ | ||
28421 | + | ||
28422 | + /* flush writes before we tell the adapter that it's its turn now */ | ||
28423 | + mmiowb(); | ||
28424 | + write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); | ||
28425 | + write_flush(adev); | ||
28426 | + | ||
28427 | + /* log the packet content AFTER sending it, | ||
28428 | + * in order to not delay sending any further than absolutely needed | ||
28429 | + * Do separate logs for acx100/111 to have human-readable rates */ | ||
28430 | + if (unlikely(acx_debug & (L_XFER|L_DATA))) { | ||
28431 | + u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc; | ||
28432 | + if (IS_ACX111(adev)) | ||
28433 | + printk("tx: pkt (%s): len %d " | ||
28434 | + "rate %04X%s status %u\n", | ||
28435 | + acx_get_packet_type_string(le16_to_cpu(fc)), len, | ||
28436 | + le16_to_cpu(txdesc->u.r2.rate111), | ||
28437 | + (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "", | ||
28438 | + adev->status); | ||
28439 | + else | ||
28440 | + printk("tx: pkt (%s): len %d rate %03u%s status %u\n", | ||
28441 | + acx_get_packet_type_string(fc), len, | ||
28442 | + txdesc->u.r1.rate, | ||
28443 | + (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "", | ||
28444 | + adev->status); | ||
28445 | + | ||
28446 | + if (acx_debug & L_DATA) { | ||
28447 | + printk("tx: 802.11 [%d]: ", len); | ||
28448 | + acx_dump_bytes(hostdesc1->data, len); | ||
28449 | + } | ||
28450 | + } | ||
28451 | +end: | ||
28452 | + FN_EXIT0; | ||
28453 | +} | ||
28454 | + | ||
28455 | + | ||
28456 | +/*********************************************************************** | ||
28457 | +** acxpci_l_clean_txdesc | ||
28458 | +** | ||
28459 | +** This function resets the txdescs' status when the ACX100 | ||
28460 | +** signals the TX done IRQ (txdescs have been processed), starting with | ||
28461 | +** the pool index of the descriptor which we would use next, | ||
28462 | +** in order to make sure that we can be as fast as possible | ||
28463 | +** in filling new txdescs. | ||
28464 | +** Everytime we get called we know where the next packet to be cleaned is. | ||
28465 | +*/ | ||
28466 | + | ||
28467 | +#if !ACX_DEBUG | ||
28468 | +static inline void log_txbuffer(const acx_device_t *adev) {} | ||
28469 | +#else | ||
28470 | +static void | ||
28471 | +log_txbuffer(acx_device_t *adev) | ||
28472 | +{ | ||
28473 | + txdesc_t *txdesc; | ||
28474 | + int i; | ||
28475 | + | ||
28476 | + /* no FN_ENTER here, we don't want that */ | ||
28477 | + /* no locks here, since it's entirely non-critical code */ | ||
28478 | + txdesc = adev->txdesc_start; | ||
28479 | + if (unlikely(!txdesc)) return; | ||
28480 | + printk("tx: desc->Ctl8's:"); | ||
28481 | + for (i = 0; i < TX_CNT; i++) { | ||
28482 | + printk(" %02X", txdesc->Ctl_8); | ||
28483 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
28484 | + } | ||
28485 | + printk("\n"); | ||
28486 | +} | ||
28487 | +#endif | ||
28488 | + | ||
28489 | + | ||
28490 | +static void | ||
28491 | +handle_tx_error(acx_device_t *adev, u8 error, unsigned int finger) | ||
28492 | +{ | ||
28493 | + const char *err = "unknown error"; | ||
28494 | + | ||
28495 | + /* hmm, should we handle this as a mask | ||
28496 | + * of *several* bits? | ||
28497 | + * For now I think only caring about | ||
28498 | + * individual bits is ok... */ | ||
28499 | + switch (error) { | ||
28500 | + case 0x01: | ||
28501 | + err = "no Tx due to error in other fragment"; | ||
28502 | + adev->wstats.discard.fragment++; | ||
28503 | + break; | ||
28504 | + case 0x02: | ||
28505 | + err = "Tx aborted"; | ||
28506 | + adev->stats.tx_aborted_errors++; | ||
28507 | + break; | ||
28508 | + case 0x04: | ||
28509 | + err = "Tx desc wrong parameters"; | ||
28510 | + adev->wstats.discard.misc++; | ||
28511 | + break; | ||
28512 | + case 0x08: | ||
28513 | + err = "WEP key not found"; | ||
28514 | + adev->wstats.discard.misc++; | ||
28515 | + break; | ||
28516 | + case 0x10: | ||
28517 | + err = "MSDU lifetime timeout? - try changing " | ||
28518 | + "'iwconfig retry lifetime XXX'"; | ||
28519 | + adev->wstats.discard.misc++; | ||
28520 | + break; | ||
28521 | + case 0x20: | ||
28522 | + err = "excessive Tx retries due to either distance " | ||
28523 | + "too high or unable to Tx or Tx frame error - " | ||
28524 | + "try changing 'iwconfig txpower XXX' or " | ||
28525 | + "'sens'itivity or 'retry'"; | ||
28526 | + adev->wstats.discard.retries++; | ||
28527 | + /* Tx error 0x20 also seems to occur on | ||
28528 | + * overheating, so I'm not sure whether we | ||
28529 | + * actually want to do aggressive radio recalibration, | ||
28530 | + * since people maybe won't notice then that their hardware | ||
28531 | + * is slowly getting cooked... | ||
28532 | + * Or is it still a safe long distance from utter | ||
28533 | + * radio non-functionality despite many radio recalibs | ||
28534 | + * to final destructive overheating of the hardware? | ||
28535 | + * In this case we really should do recalib here... | ||
28536 | + * I guess the only way to find out is to do a | ||
28537 | + * potentially fatal self-experiment :-\ | ||
28538 | + * Or maybe only recalib in case we're using Tx | ||
28539 | + * rate auto (on errors switching to lower speed | ||
28540 | + * --> less heat?) or 802.11 power save mode? | ||
28541 | + * | ||
28542 | + * ok, just do it. */ | ||
28543 | + if (++adev->retry_errors_msg_ratelimit % 4 == 0) { | ||
28544 | + if (adev->retry_errors_msg_ratelimit <= 20) { | ||
28545 | + printk("%s: several excessive Tx " | ||
28546 | + "retry errors occurred, attempting " | ||
28547 | + "to recalibrate radio. Radio " | ||
28548 | + "drift might be caused by increasing " | ||
28549 | + "card temperature, please check the card " | ||
28550 | + "before it's too late!\n", | ||
28551 | + adev->ndev->name); | ||
28552 | + if (adev->retry_errors_msg_ratelimit == 20) | ||
28553 | + printk("disabling above message\n"); | ||
28554 | + } | ||
28555 | + | ||
28556 | + acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); | ||
28557 | + } | ||
28558 | + break; | ||
28559 | + case 0x40: | ||
28560 | + err = "Tx buffer overflow"; | ||
28561 | + adev->stats.tx_fifo_errors++; | ||
28562 | + break; | ||
28563 | + case 0x80: | ||
28564 | + /* possibly ACPI C-state powersaving related!!! | ||
28565 | + * (DMA timeout due to excessively high wakeup | ||
28566 | + * latency after C-state activation!?) | ||
28567 | + * Disable C-State powersaving and try again, | ||
28568 | + * then PLEASE REPORT, I'm VERY interested in | ||
28569 | + * whether my theory is correct that this is | ||
28570 | + * actually the problem here. | ||
28571 | + * In that case, use new Linux idle wakeup latency | ||
28572 | + * requirements kernel API to prevent this issue. */ | ||
28573 | + err = "DMA error"; | ||
28574 | + adev->wstats.discard.misc++; | ||
28575 | + break; | ||
28576 | + } | ||
28577 | + adev->stats.tx_errors++; | ||
28578 | + if (adev->stats.tx_errors <= 20) | ||
28579 | + printk("%s: tx error 0x%02X, buf %02u! (%s)\n", | ||
28580 | + adev->ndev->name, error, finger, err); | ||
28581 | + else | ||
28582 | + printk("%s: tx error 0x%02X, buf %02u!\n", | ||
28583 | + adev->ndev->name, error, finger); | ||
28584 | +} | ||
28585 | + | ||
28586 | + | ||
28587 | +unsigned int | ||
28588 | +acxpci_l_clean_txdesc(acx_device_t *adev) | ||
28589 | +{ | ||
28590 | + txdesc_t *txdesc; | ||
28591 | + unsigned finger; | ||
28592 | + int num_cleaned; | ||
28593 | + u16 r111; | ||
28594 | + u8 error, ack_failures, rts_failures, rts_ok, r100; | ||
28595 | + | ||
28596 | + FN_ENTER; | ||
28597 | + | ||
28598 | + if (unlikely(acx_debug & L_DEBUG)) | ||
28599 | + log_txbuffer(adev); | ||
28600 | + | ||
28601 | + log(L_BUFT, "tx: cleaning up bufs from %u\n", adev->tx_tail); | ||
28602 | + | ||
28603 | + /* We know first descr which is not free yet. We advance it as far | ||
28604 | + ** as we see correct bits set in following descs (if next desc | ||
28605 | + ** is NOT free, we shouldn't advance at all). We know that in | ||
28606 | + ** front of tx_tail may be "holes" with isolated free descs. | ||
28607 | + ** We will catch up when all intermediate descs will be freed also */ | ||
28608 | + | ||
28609 | + finger = adev->tx_tail; | ||
28610 | + num_cleaned = 0; | ||
28611 | + while (likely(finger != adev->tx_head)) { | ||
28612 | + txdesc = get_txdesc(adev, finger); | ||
28613 | + | ||
28614 | + /* If we allocated txdesc on tx path but then decided | ||
28615 | + ** to NOT use it, then it will be left as a free "bubble" | ||
28616 | + ** in the "allocated for tx" part of the ring. | ||
28617 | + ** We may meet it on the next ring pass here. */ | ||
28618 | + | ||
28619 | + /* stop if not marked as "tx finished" and "host owned" */ | ||
28620 | + if ((txdesc->Ctl_8 & DESC_CTL_ACXDONE_HOSTOWN) | ||
28621 | + != DESC_CTL_ACXDONE_HOSTOWN) { | ||
28622 | + if (unlikely(!num_cleaned)) { /* maybe remove completely */ | ||
28623 | + log(L_BUFT, "clean_txdesc: tail isn't free. " | ||
28624 | + "tail:%d head:%d\n", | ||
28625 | + adev->tx_tail, adev->tx_head); | ||
28626 | + } | ||
28627 | + break; | ||
28628 | + } | ||
28629 | + | ||
28630 | + /* remember desc values... */ | ||
28631 | + error = txdesc->error; | ||
28632 | + ack_failures = txdesc->ack_failures; | ||
28633 | + rts_failures = txdesc->rts_failures; | ||
28634 | + rts_ok = txdesc->rts_ok; | ||
28635 | + r100 = txdesc->u.r1.rate; | ||
28636 | + r111 = le16_to_cpu(txdesc->u.r2.rate111); | ||
28637 | + | ||
28638 | + /* need to check for certain error conditions before we | ||
28639 | + * clean the descriptor: we still need valid descr data here */ | ||
28640 | + if (unlikely(0x30 & error)) { | ||
28641 | + /* only send IWEVTXDROP in case of retry or lifetime exceeded; | ||
28642 | + * all other errors mean we screwed up locally */ | ||
28643 | + union iwreq_data wrqu; | ||
28644 | + wlan_hdr_t *hdr; | ||
28645 | + txhostdesc_t *hostdesc; | ||
28646 | + | ||
28647 | + hostdesc = get_txhostdesc(adev, txdesc); | ||
28648 | + hdr = (wlan_hdr_t *)hostdesc->data; | ||
28649 | + MAC_COPY(wrqu.addr.sa_data, hdr->a1); | ||
28650 | + wireless_send_event(adev->ndev, IWEVTXDROP, &wrqu, NULL); | ||
28651 | + } | ||
28652 | + | ||
28653 | + /* ...and free the desc */ | ||
28654 | + txdesc->error = 0; | ||
28655 | + txdesc->ack_failures = 0; | ||
28656 | + txdesc->rts_failures = 0; | ||
28657 | + txdesc->rts_ok = 0; | ||
28658 | + /* signal host owning it LAST, since ACX already knows that this | ||
28659 | + ** descriptor is finished since it set Ctl_8 accordingly. */ | ||
28660 | + txdesc->Ctl_8 = DESC_CTL_HOSTOWN; | ||
28661 | + | ||
28662 | + adev->tx_free++; | ||
28663 | + num_cleaned++; | ||
28664 | + | ||
28665 | + if ((adev->tx_free >= TX_START_QUEUE) | ||
28666 | + && (adev->status == ACX_STATUS_4_ASSOCIATED) | ||
28667 | + && (acx_queue_stopped(adev->ndev)) | ||
28668 | + ) { | ||
28669 | + log(L_BUF, "tx: wake queue (avail. Tx desc %u)\n", | ||
28670 | + adev->tx_free); | ||
28671 | + acx_wake_queue(adev->ndev, NULL); | ||
28672 | + } | ||
28673 | + | ||
28674 | + /* do error checking, rate handling and logging | ||
28675 | + * AFTER having done the work, it's faster */ | ||
28676 | + | ||
28677 | + /* do rate handling */ | ||
28678 | + if (adev->rate_auto) { | ||
28679 | + struct client *clt = get_txc(adev, txdesc); | ||
28680 | + if (clt) { | ||
28681 | + u16 cur = get_txr(adev, txdesc); | ||
28682 | + if (clt->rate_cur == cur) { | ||
28683 | + acx_l_handle_txrate_auto(adev, clt, | ||
28684 | + cur, /* intended rate */ | ||
28685 | + r100, r111, /* actually used rate */ | ||
28686 | + (error & 0x30), /* was there an error? */ | ||
28687 | + TX_CNT + TX_CLEAN_BACKLOG - adev->tx_free); | ||
28688 | + } | ||
28689 | + } | ||
28690 | + } | ||
28691 | + | ||
28692 | + if (unlikely(error)) | ||
28693 | + handle_tx_error(adev, error, finger); | ||
28694 | + | ||
28695 | + if (IS_ACX111(adev)) | ||
28696 | + log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n", | ||
28697 | + finger, ack_failures, rts_failures, rts_ok, r111); | ||
28698 | + else | ||
28699 | + log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n", | ||
28700 | + finger, ack_failures, rts_failures, rts_ok, r100); | ||
28701 | + | ||
28702 | + /* update pointer for descr to be cleaned next */ | ||
28703 | + finger = (finger + 1) % TX_CNT; | ||
28704 | + } | ||
28705 | + | ||
28706 | + /* remember last position */ | ||
28707 | + adev->tx_tail = finger; | ||
28708 | +/* end: */ | ||
28709 | + FN_EXIT1(num_cleaned); | ||
28710 | + return num_cleaned; | ||
28711 | +} | ||
28712 | + | ||
28713 | +/* clean *all* Tx descriptors, and regardless of their previous state. | ||
28714 | + * Used for brute-force reset handling. */ | ||
28715 | +void | ||
28716 | +acxpci_l_clean_txdesc_emergency(acx_device_t *adev) | ||
28717 | +{ | ||
28718 | + txdesc_t *txdesc; | ||
28719 | + int i; | ||
28720 | + | ||
28721 | + FN_ENTER; | ||
28722 | + | ||
28723 | + for (i = 0; i < TX_CNT; i++) { | ||
28724 | + txdesc = get_txdesc(adev, i); | ||
28725 | + | ||
28726 | + /* free it */ | ||
28727 | + txdesc->ack_failures = 0; | ||
28728 | + txdesc->rts_failures = 0; | ||
28729 | + txdesc->rts_ok = 0; | ||
28730 | + txdesc->error = 0; | ||
28731 | + txdesc->Ctl_8 = DESC_CTL_HOSTOWN; | ||
28732 | + } | ||
28733 | + | ||
28734 | + adev->tx_free = TX_CNT; | ||
28735 | + | ||
28736 | + FN_EXIT0; | ||
28737 | +} | ||
28738 | + | ||
28739 | + | ||
28740 | +/*********************************************************************** | ||
28741 | +** acxpci_s_create_tx_host_desc_queue | ||
28742 | +*/ | ||
28743 | + | ||
28744 | +static void* | ||
28745 | +allocate(acx_device_t *adev, size_t size, dma_addr_t *phy, const char *msg) | ||
28746 | +{ | ||
28747 | + void *ptr; | ||
28748 | + | ||
28749 | + ptr = dma_alloc_coherent(adev->pdev ? &adev->pdev->dev : NULL, | ||
28750 | + size, phy, GFP_KERNEL); | ||
28751 | + | ||
28752 | + if (ptr) { | ||
28753 | + log(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n", | ||
28754 | + msg, (int)size, ptr, (unsigned long long)*phy); | ||
28755 | + memset(ptr, 0, size); | ||
28756 | + return ptr; | ||
28757 | + } | ||
28758 | + printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n", | ||
28759 | + msg, (int)size); | ||
28760 | + return NULL; | ||
28761 | +} | ||
28762 | + | ||
28763 | + | ||
28764 | +static int | ||
28765 | +acxpci_s_create_tx_host_desc_queue(acx_device_t *adev) | ||
28766 | +{ | ||
28767 | + txhostdesc_t *hostdesc; | ||
28768 | + u8 *txbuf; | ||
28769 | + dma_addr_t hostdesc_phy; | ||
28770 | + dma_addr_t txbuf_phy; | ||
28771 | + int i; | ||
28772 | + | ||
28773 | + FN_ENTER; | ||
28774 | + | ||
28775 | + /* allocate TX buffer */ | ||
28776 | + adev->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS; | ||
28777 | + adev->txbuf_start = allocate(adev, adev->txbuf_area_size, | ||
28778 | + &adev->txbuf_startphy, "txbuf_start"); | ||
28779 | + if (!adev->txbuf_start) | ||
28780 | + goto fail; | ||
28781 | + | ||
28782 | + /* allocate the TX host descriptor queue pool */ | ||
28783 | + adev->txhostdesc_area_size = TX_CNT * 2*sizeof(*hostdesc); | ||
28784 | + adev->txhostdesc_start = allocate(adev, adev->txhostdesc_area_size, | ||
28785 | + &adev->txhostdesc_startphy, "txhostdesc_start"); | ||
28786 | + if (!adev->txhostdesc_start) | ||
28787 | + goto fail; | ||
28788 | + /* check for proper alignment of TX host descriptor pool */ | ||
28789 | + if ((long) adev->txhostdesc_start & 3) { | ||
28790 | + printk("acx: driver bug: dma alloc returns unaligned address\n"); | ||
28791 | + goto fail; | ||
28792 | + } | ||
28793 | + | ||
28794 | + hostdesc = adev->txhostdesc_start; | ||
28795 | + hostdesc_phy = adev->txhostdesc_startphy; | ||
28796 | + txbuf = adev->txbuf_start; | ||
28797 | + txbuf_phy = adev->txbuf_startphy; | ||
28798 | + | ||
28799 | +#if 0 | ||
28800 | +/* Each tx buffer is accessed by hardware via | ||
28801 | +** txdesc -> txhostdesc(s) -> txbuffer(s). | ||
28802 | +** We use only one txhostdesc per txdesc, but it looks like | ||
28803 | +** acx111 is buggy: it accesses second txhostdesc | ||
28804 | +** (via hostdesc.desc_phy_next field) even if | ||
28805 | +** txdesc->length == hostdesc->length and thus | ||
28806 | +** entire packet was placed into first txhostdesc. | ||
28807 | +** Due to this bug acx111 hangs unless second txhostdesc | ||
28808 | +** has le16_to_cpu(hostdesc.length) = 3 (or larger) | ||
28809 | +** Storing NULL into hostdesc.desc_phy_next | ||
28810 | +** doesn't seem to help. | ||
28811 | +** | ||
28812 | +** Update: although it worked on Xterasys XN-2522g | ||
28813 | +** with len=3 trick, WG311v2 is even more bogus, doesn't work. | ||
28814 | +** Keeping this code (#ifdef'ed out) for documentational purposes. | ||
28815 | +*/ | ||
28816 | + for (i = 0; i < TX_CNT*2; i++) { | ||
28817 | + hostdesc_phy += sizeof(*hostdesc); | ||
28818 | + if (!(i & 1)) { | ||
28819 | + hostdesc->data_phy = cpu2acx(txbuf_phy); | ||
28820 | + /* hostdesc->data_offset = ... */ | ||
28821 | + /* hostdesc->reserved = ... */ | ||
28822 | + hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); | ||
28823 | + /* hostdesc->length = ... */ | ||
28824 | + hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); | ||
28825 | + hostdesc->pNext = ptr2acx(NULL); | ||
28826 | + /* hostdesc->Status = ... */ | ||
28827 | + /* below: non-hardware fields */ | ||
28828 | + hostdesc->data = txbuf; | ||
28829 | + | ||
28830 | + txbuf += WLAN_A4FR_MAXLEN_WEP_FCS; | ||
28831 | + txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS; | ||
28832 | + } else { | ||
28833 | + /* hostdesc->data_phy = ... */ | ||
28834 | + /* hostdesc->data_offset = ... */ | ||
28835 | + /* hostdesc->reserved = ... */ | ||
28836 | + /* hostdesc->Ctl_16 = ... */ | ||
28837 | + hostdesc->length = cpu_to_le16(3); /* bug workaround */ | ||
28838 | + /* hostdesc->desc_phy_next = ... */ | ||
28839 | + /* hostdesc->pNext = ... */ | ||
28840 | + /* hostdesc->Status = ... */ | ||
28841 | + /* below: non-hardware fields */ | ||
28842 | + /* hostdesc->data = ... */ | ||
28843 | + } | ||
28844 | + hostdesc++; | ||
28845 | + } | ||
28846 | +#endif | ||
28847 | +/* We initialize two hostdescs so that they point to adjacent | ||
28848 | +** memory areas. Thus txbuf is really just a contiguous memory area */ | ||
28849 | + for (i = 0; i < TX_CNT*2; i++) { | ||
28850 | + hostdesc_phy += sizeof(*hostdesc); | ||
28851 | + | ||
28852 | + hostdesc->data_phy = cpu2acx(txbuf_phy); | ||
28853 | + /* done by memset(0): hostdesc->data_offset = 0; */ | ||
28854 | + /* hostdesc->reserved = ... */ | ||
28855 | + hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); | ||
28856 | + /* hostdesc->length = ... */ | ||
28857 | + hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); | ||
28858 | + /* done by memset(0): hostdesc->pNext = ptr2acx(NULL); */ | ||
28859 | + /* hostdesc->Status = ... */ | ||
28860 | + /* ->data is a non-hardware field: */ | ||
28861 | + hostdesc->data = txbuf; | ||
28862 | + | ||
28863 | + if (!(i & 1)) { | ||
28864 | + txbuf += WLAN_HDR_A3_LEN; | ||
28865 | + txbuf_phy += WLAN_HDR_A3_LEN; | ||
28866 | + } else { | ||
28867 | + txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN; | ||
28868 | + txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN; | ||
28869 | + } | ||
28870 | + hostdesc++; | ||
28871 | + } | ||
28872 | + hostdesc--; | ||
28873 | + hostdesc->desc_phy_next = cpu2acx(adev->txhostdesc_startphy); | ||
28874 | + | ||
28875 | + FN_EXIT1(OK); | ||
28876 | + return OK; | ||
28877 | +fail: | ||
28878 | + printk("acx: create_tx_host_desc_queue FAILED\n"); | ||
28879 | + /* dealloc will be done by free function on error case */ | ||
28880 | + FN_EXIT1(NOT_OK); | ||
28881 | + return NOT_OK; | ||
28882 | +} | ||
28883 | + | ||
28884 | + | ||
28885 | +/*************************************************************** | ||
28886 | +** acxpci_s_create_rx_host_desc_queue | ||
28887 | +*/ | ||
28888 | +/* the whole size of a data buffer (header plus data body) | ||
28889 | + * plus 32 bytes safety offset at the end */ | ||
28890 | +#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32) | ||
28891 | + | ||
28892 | +static int | ||
28893 | +acxpci_s_create_rx_host_desc_queue(acx_device_t *adev) | ||
28894 | +{ | ||
28895 | + rxhostdesc_t *hostdesc; | ||
28896 | + rxbuffer_t *rxbuf; | ||
28897 | + dma_addr_t hostdesc_phy; | ||
28898 | + dma_addr_t rxbuf_phy; | ||
28899 | + int i; | ||
28900 | + | ||
28901 | + FN_ENTER; | ||
28902 | + | ||
28903 | + /* allocate the RX host descriptor queue pool */ | ||
28904 | + adev->rxhostdesc_area_size = RX_CNT * sizeof(*hostdesc); | ||
28905 | + adev->rxhostdesc_start = allocate(adev, adev->rxhostdesc_area_size, | ||
28906 | + &adev->rxhostdesc_startphy, "rxhostdesc_start"); | ||
28907 | + if (!adev->rxhostdesc_start) | ||
28908 | + goto fail; | ||
28909 | + /* check for proper alignment of RX host descriptor pool */ | ||
28910 | + if ((long) adev->rxhostdesc_start & 3) { | ||
28911 | + printk("acx: driver bug: dma alloc returns unaligned address\n"); | ||
28912 | + goto fail; | ||
28913 | + } | ||
28914 | + | ||
28915 | + /* allocate Rx buffer pool which will be used by the acx | ||
28916 | + * to store the whole content of the received frames in it */ | ||
28917 | + adev->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE; | ||
28918 | + adev->rxbuf_start = allocate(adev, adev->rxbuf_area_size, | ||
28919 | + &adev->rxbuf_startphy, "rxbuf_start"); | ||
28920 | + if (!adev->rxbuf_start) | ||
28921 | + goto fail; | ||
28922 | + | ||
28923 | + rxbuf = adev->rxbuf_start; | ||
28924 | + rxbuf_phy = adev->rxbuf_startphy; | ||
28925 | + hostdesc = adev->rxhostdesc_start; | ||
28926 | + hostdesc_phy = adev->rxhostdesc_startphy; | ||
28927 | + | ||
28928 | + /* don't make any popular C programming pointer arithmetic mistakes | ||
28929 | + * here, otherwise I'll kill you... | ||
28930 | + * (and don't dare asking me why I'm warning you about that...) */ | ||
28931 | + for (i = 0; i < RX_CNT; i++) { | ||
28932 | + hostdesc->data = rxbuf; | ||
28933 | + hostdesc->data_phy = cpu2acx(rxbuf_phy); | ||
28934 | + hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE); | ||
28935 | + CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); | ||
28936 | + rxbuf++; | ||
28937 | + rxbuf_phy += sizeof(*rxbuf); | ||
28938 | + hostdesc_phy += sizeof(*hostdesc); | ||
28939 | + hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); | ||
28940 | + hostdesc++; | ||
28941 | + } | ||
28942 | + hostdesc--; | ||
28943 | + hostdesc->desc_phy_next = cpu2acx(adev->rxhostdesc_startphy); | ||
28944 | + FN_EXIT1(OK); | ||
28945 | + return OK; | ||
28946 | +fail: | ||
28947 | + printk("acx: create_rx_host_desc_queue FAILED\n"); | ||
28948 | + /* dealloc will be done by free function on error case */ | ||
28949 | + FN_EXIT1(NOT_OK); | ||
28950 | + return NOT_OK; | ||
28951 | +} | ||
28952 | + | ||
28953 | + | ||
28954 | +/*************************************************************** | ||
28955 | +** acxpci_s_create_hostdesc_queues | ||
28956 | +*/ | ||
28957 | +int | ||
28958 | +acxpci_s_create_hostdesc_queues(acx_device_t *adev) | ||
28959 | +{ | ||
28960 | + int result; | ||
28961 | + result = acxpci_s_create_tx_host_desc_queue(adev); | ||
28962 | + if (OK != result) return result; | ||
28963 | + result = acxpci_s_create_rx_host_desc_queue(adev); | ||
28964 | + return result; | ||
28965 | +} | ||
28966 | + | ||
28967 | + | ||
28968 | +/*************************************************************** | ||
28969 | +** acxpci_create_tx_desc_queue | ||
28970 | +*/ | ||
28971 | +static void | ||
28972 | +acxpci_create_tx_desc_queue(acx_device_t *adev, u32 tx_queue_start) | ||
28973 | +{ | ||
28974 | + txdesc_t *txdesc; | ||
28975 | + txhostdesc_t *hostdesc; | ||
28976 | + dma_addr_t hostmemptr; | ||
28977 | + u32 mem_offs; | ||
28978 | + int i; | ||
28979 | + | ||
28980 | + FN_ENTER; | ||
28981 | + | ||
28982 | + if (IS_ACX100(adev)) | ||
28983 | + adev->txdesc_size = sizeof(*txdesc); | ||
28984 | + else | ||
28985 | + /* the acx111 txdesc is 4 bytes larger */ | ||
28986 | + adev->txdesc_size = sizeof(*txdesc) + 4; | ||
28987 | + | ||
28988 | + adev->txdesc_start = (txdesc_t *) (adev->iobase2 + tx_queue_start); | ||
28989 | + | ||
28990 | + log(L_DEBUG, "adev->iobase2=%p\n" | ||
28991 | + "tx_queue_start=%08X\n" | ||
28992 | + "adev->txdesc_start=%p\n", | ||
28993 | + adev->iobase2, | ||
28994 | + tx_queue_start, | ||
28995 | + adev->txdesc_start); | ||
28996 | + | ||
28997 | + adev->tx_free = TX_CNT; | ||
28998 | + /* done by memset: adev->tx_head = 0; */ | ||
28999 | + /* done by memset: adev->tx_tail = 0; */ | ||
29000 | + txdesc = adev->txdesc_start; | ||
29001 | + mem_offs = tx_queue_start; | ||
29002 | + hostmemptr = adev->txhostdesc_startphy; | ||
29003 | + hostdesc = adev->txhostdesc_start; | ||
29004 | + | ||
29005 | + if (IS_ACX111(adev)) { | ||
29006 | + /* ACX111 has a preinitialized Tx buffer! */ | ||
29007 | + /* loop over whole send pool */ | ||
29008 | + /* FIXME: do we have to do the hostmemptr stuff here?? */ | ||
29009 | + for (i = 0; i < TX_CNT; i++) { | ||
29010 | + txdesc->HostMemPtr = ptr2acx(hostmemptr); | ||
29011 | + txdesc->Ctl_8 = DESC_CTL_HOSTOWN; | ||
29012 | + /* reserve two (hdr desc and payload desc) */ | ||
29013 | + hostdesc += 2; | ||
29014 | + hostmemptr += 2 * sizeof(*hostdesc); | ||
29015 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
29016 | + } | ||
29017 | + } else { | ||
29018 | + /* ACX100 Tx buffer needs to be initialized by us */ | ||
29019 | + /* clear whole send pool. sizeof is safe here (we are acx100) */ | ||
29020 | + memset(adev->txdesc_start, 0, TX_CNT * sizeof(*txdesc)); | ||
29021 | + | ||
29022 | + /* loop over whole send pool */ | ||
29023 | + for (i = 0; i < TX_CNT; i++) { | ||
29024 | + log(L_DEBUG, "configure card tx descriptor: 0x%p, " | ||
29025 | + "size: 0x%X\n", txdesc, adev->txdesc_size); | ||
29026 | + | ||
29027 | + /* pointer to hostdesc memory */ | ||
29028 | + txdesc->HostMemPtr = ptr2acx(hostmemptr); | ||
29029 | + /* initialise ctl */ | ||
29030 | + txdesc->Ctl_8 = ( DESC_CTL_HOSTOWN | DESC_CTL_RECLAIM | ||
29031 | + | DESC_CTL_AUTODMA | DESC_CTL_FIRSTFRAG); | ||
29032 | + /* done by memset(0): txdesc->Ctl2_8 = 0; */ | ||
29033 | + /* point to next txdesc */ | ||
29034 | + txdesc->pNextDesc = cpu2acx(mem_offs + adev->txdesc_size); | ||
29035 | + /* reserve two (hdr desc and payload desc) */ | ||
29036 | + hostdesc += 2; | ||
29037 | + hostmemptr += 2 * sizeof(*hostdesc); | ||
29038 | + /* go to the next one */ | ||
29039 | + mem_offs += adev->txdesc_size; | ||
29040 | + /* ++ is safe here (we are acx100) */ | ||
29041 | + txdesc++; | ||
29042 | + } | ||
29043 | + /* go back to the last one */ | ||
29044 | + txdesc--; | ||
29045 | + /* and point to the first making it a ring buffer */ | ||
29046 | + txdesc->pNextDesc = cpu2acx(tx_queue_start); | ||
29047 | + } | ||
29048 | + FN_EXIT0; | ||
29049 | +} | ||
29050 | + | ||
29051 | + | ||
29052 | +/*************************************************************** | ||
29053 | +** acxpci_create_rx_desc_queue | ||
29054 | +*/ | ||
29055 | +static void | ||
29056 | +acxpci_create_rx_desc_queue(acx_device_t *adev, u32 rx_queue_start) | ||
29057 | +{ | ||
29058 | + rxdesc_t *rxdesc; | ||
29059 | + u32 mem_offs; | ||
29060 | + int i; | ||
29061 | + | ||
29062 | + FN_ENTER; | ||
29063 | + | ||
29064 | + /* done by memset: adev->rx_tail = 0; */ | ||
29065 | + | ||
29066 | + /* ACX111 doesn't need any further config: preconfigures itself. | ||
29067 | + * Simply print ring buffer for debugging */ | ||
29068 | + if (IS_ACX111(adev)) { | ||
29069 | + /* rxdesc_start already set here */ | ||
29070 | + | ||
29071 | + adev->rxdesc_start = (rxdesc_t *) ((u8 *)adev->iobase2 + rx_queue_start); | ||
29072 | + | ||
29073 | + rxdesc = adev->rxdesc_start; | ||
29074 | + for (i = 0; i < RX_CNT; i++) { | ||
29075 | + log(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc); | ||
29076 | + rxdesc = adev->rxdesc_start = (rxdesc_t *) | ||
29077 | + (adev->iobase2 + acx2cpu(rxdesc->pNextDesc)); | ||
29078 | + } | ||
29079 | + } else { | ||
29080 | + /* we didn't pre-calculate rxdesc_start in case of ACX100 */ | ||
29081 | + /* rxdesc_start should be right AFTER Tx pool */ | ||
29082 | + adev->rxdesc_start = (rxdesc_t *) | ||
29083 | + ((u8 *) adev->txdesc_start + (TX_CNT * sizeof(txdesc_t))); | ||
29084 | + /* NB: sizeof(txdesc_t) above is valid because we know | ||
29085 | + ** we are in if (acx100) block. Beware of cut-n-pasting elsewhere! | ||
29086 | + ** acx111's txdesc is larger! */ | ||
29087 | + | ||
29088 | + memset(adev->rxdesc_start, 0, RX_CNT * sizeof(*rxdesc)); | ||
29089 | + | ||
29090 | + /* loop over whole receive pool */ | ||
29091 | + rxdesc = adev->rxdesc_start; | ||
29092 | + mem_offs = rx_queue_start; | ||
29093 | + for (i = 0; i < RX_CNT; i++) { | ||
29094 | + log(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc); | ||
29095 | + rxdesc->Ctl_8 = DESC_CTL_RECLAIM | DESC_CTL_AUTODMA; | ||
29096 | + /* point to next rxdesc */ | ||
29097 | + rxdesc->pNextDesc = cpu2acx(mem_offs + sizeof(*rxdesc)); | ||
29098 | + /* go to the next one */ | ||
29099 | + mem_offs += sizeof(*rxdesc); | ||
29100 | + rxdesc++; | ||
29101 | + } | ||
29102 | + /* go to the last one */ | ||
29103 | + rxdesc--; | ||
29104 | + | ||
29105 | + /* and point to the first making it a ring buffer */ | ||
29106 | + rxdesc->pNextDesc = cpu2acx(rx_queue_start); | ||
29107 | + } | ||
29108 | + FN_EXIT0; | ||
29109 | +} | ||
29110 | + | ||
29111 | + | ||
29112 | +/*************************************************************** | ||
29113 | +** acxpci_create_desc_queues | ||
29114 | +*/ | ||
29115 | +void | ||
29116 | +acxpci_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start) | ||
29117 | +{ | ||
29118 | + acxpci_create_tx_desc_queue(adev, tx_queue_start); | ||
29119 | + acxpci_create_rx_desc_queue(adev, rx_queue_start); | ||
29120 | +} | ||
29121 | + | ||
29122 | + | ||
29123 | +/*************************************************************** | ||
29124 | +** acxpci_s_proc_diag_output | ||
29125 | +*/ | ||
29126 | +char* | ||
29127 | +acxpci_s_proc_diag_output(char *p, acx_device_t *adev) | ||
29128 | +{ | ||
29129 | + const char *rtl, *thd, *ttl; | ||
29130 | + rxhostdesc_t *rxhostdesc; | ||
29131 | + txdesc_t *txdesc; | ||
29132 | + int i; | ||
29133 | + | ||
29134 | + FN_ENTER; | ||
29135 | + | ||
29136 | + p += sprintf(p, "** Rx buf **\n"); | ||
29137 | + rxhostdesc = adev->rxhostdesc_start; | ||
29138 | + if (rxhostdesc) for (i = 0; i < RX_CNT; i++) { | ||
29139 | + rtl = (i == adev->rx_tail) ? " [tail]" : ""; | ||
29140 | + if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) | ||
29141 | + && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)) ) | ||
29142 | + p += sprintf(p, "%02u FULL%s\n", i, rtl); | ||
29143 | + else | ||
29144 | + p += sprintf(p, "%02u empty%s\n", i, rtl); | ||
29145 | + rxhostdesc++; | ||
29146 | + } | ||
29147 | + p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", adev->tx_free, | ||
29148 | + acx_queue_stopped(adev->ndev) ? "STOPPED" : "running"); | ||
29149 | + txdesc = adev->txdesc_start; | ||
29150 | + if (txdesc) for (i = 0; i < TX_CNT; i++) { | ||
29151 | + thd = (i == adev->tx_head) ? " [head]" : ""; | ||
29152 | + ttl = (i == adev->tx_tail) ? " [tail]" : ""; | ||
29153 | + if (txdesc->Ctl_8 & DESC_CTL_ACXDONE) | ||
29154 | + p += sprintf(p, "%02u free (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl); | ||
29155 | + else | ||
29156 | + p += sprintf(p, "%02u tx (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl); | ||
29157 | + txdesc = advance_txdesc(adev, txdesc, 1); | ||
29158 | + } | ||
29159 | + p += sprintf(p, | ||
29160 | + "\n" | ||
29161 | + "** PCI data **\n" | ||
29162 | + "txbuf_start %p, txbuf_area_size %u, txbuf_startphy %08llx\n" | ||
29163 | + "txdesc_size %u, txdesc_start %p\n" | ||
29164 | + "txhostdesc_start %p, txhostdesc_area_size %u, txhostdesc_startphy %08llx\n" | ||
29165 | + "rxdesc_start %p\n" | ||
29166 | + "rxhostdesc_start %p, rxhostdesc_area_size %u, rxhostdesc_startphy %08llx\n" | ||
29167 | + "rxbuf_start %p, rxbuf_area_size %u, rxbuf_startphy %08llx\n", | ||
29168 | + adev->txbuf_start, adev->txbuf_area_size, | ||
29169 | + (unsigned long long)adev->txbuf_startphy, | ||
29170 | + adev->txdesc_size, adev->txdesc_start, | ||
29171 | + adev->txhostdesc_start, adev->txhostdesc_area_size, | ||
29172 | + (unsigned long long)adev->txhostdesc_startphy, | ||
29173 | + adev->rxdesc_start, | ||
29174 | + adev->rxhostdesc_start, adev->rxhostdesc_area_size, | ||
29175 | + (unsigned long long)adev->rxhostdesc_startphy, | ||
29176 | + adev->rxbuf_start, adev->rxbuf_area_size, | ||
29177 | + (unsigned long long)adev->rxbuf_startphy); | ||
29178 | + | ||
29179 | + FN_EXIT0; | ||
29180 | + return p; | ||
29181 | +} | ||
29182 | + | ||
29183 | + | ||
29184 | +/*********************************************************************** | ||
29185 | +*/ | ||
29186 | +int | ||
29187 | +acxpci_proc_eeprom_output(char *buf, acx_device_t *adev) | ||
29188 | +{ | ||
29189 | + char *p = buf; | ||
29190 | + int i; | ||
29191 | + | ||
29192 | + FN_ENTER; | ||
29193 | + | ||
29194 | + for (i = 0; i < 0x400; i++) { | ||
29195 | + acxpci_read_eeprom_byte(adev, i, p++); | ||
29196 | + } | ||
29197 | + | ||
29198 | + FN_EXIT1(p - buf); | ||
29199 | + return p - buf; | ||
29200 | +} | ||
29201 | + | ||
29202 | + | ||
29203 | +/*********************************************************************** | ||
29204 | +*/ | ||
29205 | +void | ||
29206 | +acxpci_set_interrupt_mask(acx_device_t *adev) | ||
29207 | +{ | ||
29208 | + if (IS_ACX111(adev)) { | ||
29209 | + adev->irq_mask = (u16) ~(0 | ||
29210 | + /* | HOST_INT_RX_DATA */ | ||
29211 | + | HOST_INT_TX_COMPLETE | ||
29212 | + /* | HOST_INT_TX_XFER */ | ||
29213 | + | HOST_INT_RX_COMPLETE | ||
29214 | + /* | HOST_INT_DTIM */ | ||
29215 | + /* | HOST_INT_BEACON */ | ||
29216 | + /* | HOST_INT_TIMER */ | ||
29217 | + /* | HOST_INT_KEY_NOT_FOUND */ | ||
29218 | + | HOST_INT_IV_ICV_FAILURE | ||
29219 | + | HOST_INT_CMD_COMPLETE | ||
29220 | + | HOST_INT_INFO | ||
29221 | + /* | HOST_INT_OVERFLOW */ | ||
29222 | + /* | HOST_INT_PROCESS_ERROR */ | ||
29223 | + | HOST_INT_SCAN_COMPLETE | ||
29224 | + | HOST_INT_FCS_THRESHOLD | ||
29225 | + /* | HOST_INT_UNKNOWN */ | ||
29226 | + ); | ||
29227 | + /* Or else acx100 won't signal cmd completion, right? */ | ||
29228 | + adev->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */ | ||
29229 | + } else { | ||
29230 | + adev->irq_mask = (u16) ~(0 | ||
29231 | + /* | HOST_INT_RX_DATA */ | ||
29232 | + | HOST_INT_TX_COMPLETE | ||
29233 | + /* | HOST_INT_TX_XFER */ | ||
29234 | + | HOST_INT_RX_COMPLETE | ||
29235 | + /* | HOST_INT_DTIM */ | ||
29236 | + /* | HOST_INT_BEACON */ | ||
29237 | + /* | HOST_INT_TIMER */ | ||
29238 | + /* | HOST_INT_KEY_NOT_FOUND */ | ||
29239 | + /* | HOST_INT_IV_ICV_FAILURE */ | ||
29240 | + | HOST_INT_CMD_COMPLETE | ||
29241 | + | HOST_INT_INFO | ||
29242 | + /* | HOST_INT_OVERFLOW */ | ||
29243 | + /* | HOST_INT_PROCESS_ERROR */ | ||
29244 | + | HOST_INT_SCAN_COMPLETE | ||
29245 | + /* | HOST_INT_FCS_THRESHOLD */ | ||
29246 | + /* | HOST_INT_UNKNOWN */ | ||
29247 | + ); | ||
29248 | + adev->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */ | ||
29249 | + } | ||
29250 | +} | ||
29251 | + | ||
29252 | + | ||
29253 | +/*********************************************************************** | ||
29254 | +*/ | ||
29255 | +int | ||
29256 | +acx100pci_s_set_tx_level(acx_device_t *adev, u8 level_dbm) | ||
29257 | +{ | ||
29258 | + /* since it can be assumed that at least the Maxim radio has a | ||
29259 | + * maximum power output of 20dBm and since it also can be | ||
29260 | + * assumed that these values drive the DAC responsible for | ||
29261 | + * setting the linear Tx level, I'd guess that these values | ||
29262 | + * should be the corresponding linear values for a dBm value, | ||
29263 | + * in other words: calculate the values from that formula: | ||
29264 | + * Y [dBm] = 10 * log (X [mW]) | ||
29265 | + * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm) | ||
29266 | + * and you're done... | ||
29267 | + * Hopefully that's ok, but you never know if we're actually | ||
29268 | + * right... (especially since Windows XP doesn't seem to show | ||
29269 | + * actual Tx dBm values :-P) */ | ||
29270 | + | ||
29271 | + /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the | ||
29272 | + * values are EXACTLY mW!!! Not sure about RFMD and others, | ||
29273 | + * though... */ | ||
29274 | + static const u8 dbm2val_maxim[21] = { | ||
29275 | + 63, 63, 63, 62, | ||
29276 | + 61, 61, 60, 60, | ||
29277 | + 59, 58, 57, 55, | ||
29278 | + 53, 50, 47, 43, | ||
29279 | + 38, 31, 23, 13, | ||
29280 | + 0 | ||
29281 | + }; | ||
29282 | + static const u8 dbm2val_rfmd[21] = { | ||
29283 | + 0, 0, 0, 1, | ||
29284 | + 2, 2, 3, 3, | ||
29285 | + 4, 5, 6, 8, | ||
29286 | + 10, 13, 16, 20, | ||
29287 | + 25, 32, 41, 50, | ||
29288 | + 63 | ||
29289 | + }; | ||
29290 | + const u8 *table; | ||
29291 | + | ||
29292 | + switch (adev->radio_type) { | ||
29293 | + case RADIO_MAXIM_0D: | ||
29294 | + table = &dbm2val_maxim[0]; | ||
29295 | + break; | ||
29296 | + case RADIO_RFMD_11: | ||
29297 | + case RADIO_RALINK_15: | ||
29298 | + table = &dbm2val_rfmd[0]; | ||
29299 | + break; | ||
29300 | + default: | ||
29301 | + printk("%s: unknown/unsupported radio type, " | ||
29302 | + "cannot modify tx power level yet!\n", | ||
29303 | + adev->ndev->name); | ||
29304 | + return NOT_OK; | ||
29305 | + } | ||
29306 | + printk("%s: changing radio power level to %u dBm (%u)\n", | ||
29307 | + adev->ndev->name, level_dbm, table[level_dbm]); | ||
29308 | + acxpci_s_write_phy_reg(adev, 0x11, table[level_dbm]); | ||
29309 | + return OK; | ||
29310 | +} | ||
29311 | + | ||
29312 | + | ||
29313 | +/*********************************************************************** | ||
29314 | +** Data for init_module/cleanup_module | ||
29315 | +*/ | ||
29316 | +static const struct pci_device_id | ||
29317 | +acxpci_id_tbl[] __devinitdata = { | ||
29318 | + { | ||
29319 | + .vendor = PCI_VENDOR_ID_TI, | ||
29320 | + .device = PCI_DEVICE_ID_TI_TNETW1100A, | ||
29321 | + .subvendor = PCI_ANY_ID, | ||
29322 | + .subdevice = PCI_ANY_ID, | ||
29323 | + .driver_data = CHIPTYPE_ACX100, | ||
29324 | + }, | ||
29325 | + { | ||
29326 | + .vendor = PCI_VENDOR_ID_TI, | ||
29327 | + .device = PCI_DEVICE_ID_TI_TNETW1100B, | ||
29328 | + .subvendor = PCI_ANY_ID, | ||
29329 | + .subdevice = PCI_ANY_ID, | ||
29330 | + .driver_data = CHIPTYPE_ACX100, | ||
29331 | + }, | ||
29332 | + { | ||
29333 | + .vendor = PCI_VENDOR_ID_TI, | ||
29334 | + .device = PCI_DEVICE_ID_TI_TNETW1130, | ||
29335 | + .subvendor = PCI_ANY_ID, | ||
29336 | + .subdevice = PCI_ANY_ID, | ||
29337 | + .driver_data = CHIPTYPE_ACX111, | ||
29338 | + }, | ||
29339 | + { | ||
29340 | + .vendor = 0, | ||
29341 | + .device = 0, | ||
29342 | + .subvendor = 0, | ||
29343 | + .subdevice = 0, | ||
29344 | + .driver_data = 0, | ||
29345 | + } | ||
29346 | +}; | ||
29347 | + | ||
29348 | +MODULE_DEVICE_TABLE(pci, acxpci_id_tbl); | ||
29349 | + | ||
29350 | +/* FIXME: checks should be removed once driver is included in the kernel */ | ||
29351 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11) | ||
29352 | +/* pci_name() got introduced at start of 2.6.x, | ||
29353 | + * got mandatory (slot_name member removed) in 2.6.11-bk1 */ | ||
29354 | +#define pci_name(x) x->slot_name | ||
29355 | +#endif | ||
29356 | + | ||
29357 | +static struct pci_driver | ||
29358 | +acxpci_drv_id = { | ||
29359 | + .name = "acx_pci", | ||
29360 | + .id_table = acxpci_id_tbl, | ||
29361 | + .probe = acxpci_e_probe, | ||
29362 | + .remove = __devexit_p(acxpci_e_remove), | ||
29363 | +#ifdef CONFIG_PM | ||
29364 | + .suspend = acxpci_e_suspend, | ||
29365 | + .resume = acxpci_e_resume | ||
29366 | +#endif /* CONFIG_PM */ | ||
29367 | +}; | ||
29368 | + | ||
29369 | + | ||
29370 | +/*********************************************************************** | ||
29371 | +** acxpci_e_init_module | ||
29372 | +** | ||
29373 | +** Module initialization routine, called once at module load time | ||
29374 | +*/ | ||
29375 | +int __init | ||
29376 | +acxpci_e_init_module(void) | ||
29377 | +{ | ||
29378 | + int res; | ||
29379 | + | ||
29380 | + FN_ENTER; | ||
29381 | + | ||
29382 | +#if (ACX_IO_WIDTH==32) | ||
29383 | + printk("acx: compiled to use 32bit I/O access. " | ||
29384 | + "I/O timing issues might occur, such as " | ||
29385 | + "non-working firmware upload. Report them\n"); | ||
29386 | +#else | ||
29387 | + printk("acx: compiled to use 16bit I/O access only " | ||
29388 | + "(compatibility mode)\n"); | ||
29389 | +#endif | ||
29390 | + | ||
29391 | +#ifdef __LITTLE_ENDIAN | ||
29392 | +#define ENDIANNESS_STRING "running on a little-endian CPU\n" | ||
29393 | +#else | ||
29394 | +#define ENDIANNESS_STRING "running on a BIG-ENDIAN CPU\n" | ||
29395 | +#endif | ||
29396 | + log(L_INIT, | ||
29397 | + ENDIANNESS_STRING | ||
29398 | + "PCI module " ACX_RELEASE " initialized, " | ||
29399 | + "waiting for cards to probe...\n" | ||
29400 | + ); | ||
29401 | + | ||
29402 | + res = pci_register_driver(&acxpci_drv_id); | ||
29403 | + FN_EXIT1(res); | ||
29404 | + return res; | ||
29405 | +} | ||
29406 | + | ||
29407 | + | ||
29408 | +/*********************************************************************** | ||
29409 | +** acxpci_e_cleanup_module | ||
29410 | +** | ||
29411 | +** Called at module unload time. This is our last chance to | ||
29412 | +** clean up after ourselves. | ||
29413 | +*/ | ||
29414 | +void __exit | ||
29415 | +acxpci_e_cleanup_module(void) | ||
29416 | +{ | ||
29417 | + FN_ENTER; | ||
29418 | + | ||
29419 | + pci_unregister_driver(&acxpci_drv_id); | ||
29420 | + | ||
29421 | + FN_EXIT0; | ||
29422 | +} | ||
29423 | Index: linux-2.6.22/drivers/net/wireless/acx/rx3000_acx.c | ||
29424 | =================================================================== | ||
29425 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
29426 | +++ linux-2.6.22/drivers/net/wireless/acx/rx3000_acx.c 2007-08-23 18:34:19.000000000 +0200 | ||
29427 | @@ -0,0 +1,110 @@ | ||
29428 | +/* | ||
29429 | + * WLAN (TI TNETW1100B) support in the HP iPAQ RX3000 | ||
29430 | + * | ||
29431 | + * Copyright (c) 2006 SDG Systems, LLC | ||
29432 | + * Copyright (c) 2006 Roman Moravcik | ||
29433 | + * | ||
29434 | + * This file is subject to the terms and conditions of the GNU General Public | ||
29435 | + * License. See the file COPYING in the main directory of this archive for | ||
29436 | + * more details. | ||
29437 | + * | ||
29438 | + * Based on hx4700_acx.c | ||
29439 | + */ | ||
29440 | + | ||
29441 | + | ||
29442 | +#include <linux/kernel.h> | ||
29443 | +#include <linux/platform_device.h> | ||
29444 | +#include <linux/delay.h> | ||
29445 | +#include <linux/dpm.h> | ||
29446 | +#include <linux/leds.h> | ||
29447 | + | ||
29448 | +#include <asm/hardware.h> | ||
29449 | + | ||
29450 | +#include <asm/arch/regs-gpio.h> | ||
29451 | +#include <linux/mfd/asic3_base.h> | ||
29452 | +#include <asm/arch/rx3000.h> | ||
29453 | +#include <asm/arch/rx3000-asic3.h> | ||
29454 | +#include <asm/io.h> | ||
29455 | + | ||
29456 | +#include "acx_hw.h" | ||
29457 | + | ||
29458 | +extern struct platform_device s3c_device_asic3; | ||
29459 | + | ||
29460 | +static int rx3000_wlan_start(void) | ||
29461 | +{ | ||
29462 | + DPM_DEBUG("rx3000_acx: Turning on\n"); | ||
29463 | + asic3_set_gpio_out_b(&s3c_device_asic3.dev, ASIC3_GPB3, ASIC3_GPB3); | ||
29464 | + mdelay(20); | ||
29465 | + asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC13, ASIC3_GPC13); | ||
29466 | + mdelay(20); | ||
29467 | + asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC11, ASIC3_GPC11); | ||
29468 | + mdelay(100); | ||
29469 | + asic3_set_gpio_out_b(&s3c_device_asic3.dev, ASIC3_GPB3, ASIC3_GPB3); | ||
29470 | + mdelay(20); | ||
29471 | + s3c2410_gpio_cfgpin(S3C2410_GPA15, S3C2410_GPA15_nGCS4); | ||
29472 | + mdelay(100); | ||
29473 | + s3c2410_gpio_setpin(S3C2410_GPA11, 0); | ||
29474 | + mdelay(50); | ||
29475 | + s3c2410_gpio_setpin(S3C2410_GPA11, 1); | ||
29476 | + led_trigger_event_shared(rx3000_radio_trig, LED_FULL); | ||
29477 | + return 0; | ||
29478 | +} | ||
29479 | + | ||
29480 | +static int rx3000_wlan_stop(void) | ||
29481 | +{ | ||
29482 | + DPM_DEBUG("rx3000_acx: Turning off\n"); | ||
29483 | + s3c2410_gpio_setpin(S3C2410_GPA15, 1); | ||
29484 | + s3c2410_gpio_cfgpin(S3C2410_GPA15, S3C2410_GPA15_OUT); | ||
29485 | + asic3_set_gpio_out_b(&s3c_device_asic3.dev, ASIC3_GPB3, 0); | ||
29486 | + asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC13, 0); | ||
29487 | + asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC11, 0); | ||
29488 | + led_trigger_event_shared(rx3000_radio_trig, LED_OFF); | ||
29489 | + return 0; | ||
29490 | +} | ||
29491 | + | ||
29492 | +static struct resource acx_resources[] = { | ||
29493 | + [0] = { | ||
29494 | + .start = RX3000_PA_WLAN, | ||
29495 | + .end = RX3000_PA_WLAN + 0x20, | ||
29496 | + .flags = IORESOURCE_MEM, | ||
29497 | + }, | ||
29498 | + [1] = { | ||
29499 | + .start = IRQ_EINT16, | ||
29500 | + .end = IRQ_EINT16, | ||
29501 | + .flags = IORESOURCE_IRQ, | ||
29502 | + }, | ||
29503 | +}; | ||
29504 | + | ||
29505 | +static struct acx_hardware_data acx_data = { | ||
29506 | + .start_hw = rx3000_wlan_start, | ||
29507 | + .stop_hw = rx3000_wlan_stop, | ||
29508 | +}; | ||
29509 | + | ||
29510 | +static struct platform_device acx_device = { | ||
29511 | + .name = "acx-mem", | ||
29512 | + .dev = { | ||
29513 | + .platform_data = &acx_data, | ||
29514 | + }, | ||
29515 | + .num_resources = ARRAY_SIZE(acx_resources), | ||
29516 | + .resource = acx_resources, | ||
29517 | +}; | ||
29518 | + | ||
29519 | +static int __init rx3000_wlan_init(void) | ||
29520 | +{ | ||
29521 | + printk("rx3000_wlan_init: acx-mem platform_device_register\n"); | ||
29522 | + return platform_device_register(&acx_device); | ||
29523 | +} | ||
29524 | + | ||
29525 | + | ||
29526 | +static void __exit rx3000_wlan_exit(void) | ||
29527 | +{ | ||
29528 | + platform_device_unregister(&acx_device); | ||
29529 | +} | ||
29530 | + | ||
29531 | +module_init(rx3000_wlan_init); | ||
29532 | +module_exit(rx3000_wlan_exit); | ||
29533 | + | ||
29534 | +MODULE_AUTHOR("Todd Blumer <todd@sdgsystems.com>, Roman Moravcik <roman.moravcik@gmail.com>"); | ||
29535 | +MODULE_DESCRIPTION("WLAN driver for HP iPAQ RX3000"); | ||
29536 | +MODULE_LICENSE("GPL"); | ||
29537 | + | ||
29538 | Index: linux-2.6.22/drivers/net/wireless/acx/setrate.c | ||
29539 | =================================================================== | ||
29540 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
29541 | +++ linux-2.6.22/drivers/net/wireless/acx/setrate.c 2007-08-23 18:34:19.000000000 +0200 | ||
29542 | @@ -0,0 +1,213 @@ | ||
29543 | +/* TODO: stop #including, move into wireless.c | ||
29544 | + * until then, keep in sync copies in prism54/ and acx/ dirs | ||
29545 | + * code+data size: less than 1k */ | ||
29546 | + | ||
29547 | +enum { | ||
29548 | + DOT11_RATE_1, | ||
29549 | + DOT11_RATE_2, | ||
29550 | + DOT11_RATE_5, | ||
29551 | + DOT11_RATE_11, | ||
29552 | + DOT11_RATE_22, | ||
29553 | + DOT11_RATE_33, | ||
29554 | + DOT11_RATE_6, | ||
29555 | + DOT11_RATE_9, | ||
29556 | + DOT11_RATE_12, | ||
29557 | + DOT11_RATE_18, | ||
29558 | + DOT11_RATE_24, | ||
29559 | + DOT11_RATE_36, | ||
29560 | + DOT11_RATE_48, | ||
29561 | + DOT11_RATE_54 | ||
29562 | +}; | ||
29563 | +enum { | ||
29564 | + DOT11_MOD_DBPSK, | ||
29565 | + DOT11_MOD_DQPSK, | ||
29566 | + DOT11_MOD_CCK, | ||
29567 | + DOT11_MOD_OFDM, | ||
29568 | + DOT11_MOD_CCKOFDM, | ||
29569 | + DOT11_MOD_PBCC | ||
29570 | +}; | ||
29571 | +static const u8 ratelist[] = { 1,2,5,11,22,33,6,9,12,18,24,36,48,54 }; | ||
29572 | +static const u8 dot11ratebyte[] = { 1*2,2*2,11,11*2,22*2,33*2,6*2,9*2,12*2,18*2,24*2,36*2,48*2,54*2 }; | ||
29573 | +static const u8 default_modulation[] = { | ||
29574 | + DOT11_MOD_DBPSK, | ||
29575 | + DOT11_MOD_DQPSK, | ||
29576 | + DOT11_MOD_CCK, | ||
29577 | + DOT11_MOD_CCK, | ||
29578 | + DOT11_MOD_PBCC, | ||
29579 | + DOT11_MOD_PBCC, | ||
29580 | + DOT11_MOD_OFDM, | ||
29581 | + DOT11_MOD_OFDM, | ||
29582 | + DOT11_MOD_OFDM, | ||
29583 | + DOT11_MOD_OFDM, | ||
29584 | + DOT11_MOD_OFDM, | ||
29585 | + DOT11_MOD_OFDM, | ||
29586 | + DOT11_MOD_OFDM, | ||
29587 | + DOT11_MOD_OFDM | ||
29588 | +}; | ||
29589 | + | ||
29590 | +static /* TODO: remove 'static' when moved to wireless.c */ | ||
29591 | +int | ||
29592 | +rate_mbit2enum(int n) { | ||
29593 | + int i=0; | ||
29594 | + while(i<sizeof(ratelist)) { | ||
29595 | + if(n==ratelist[i]) return i; | ||
29596 | + i++; | ||
29597 | + } | ||
29598 | + return -EINVAL; | ||
29599 | +} | ||
29600 | + | ||
29601 | +static int | ||
29602 | +get_modulation(int r_enum, char suffix) { | ||
29603 | + if(suffix==',' || suffix==' ' || suffix=='\0') { | ||
29604 | + /* could shorten default_mod by 8 bytes: | ||
29605 | + if(r_enum>=DOT11_RATE_6) return DOT11_MOD_OFDM; */ | ||
29606 | + return default_modulation[r_enum]; | ||
29607 | + } | ||
29608 | + if(suffix=='c') { | ||
29609 | + if(r_enum<DOT11_RATE_5 || r_enum>DOT11_RATE_11) return -EINVAL; | ||
29610 | + return DOT11_MOD_CCK; | ||
29611 | + } | ||
29612 | + if(suffix=='p') { | ||
29613 | + if(r_enum<DOT11_RATE_5 || r_enum>DOT11_RATE_33) return -EINVAL; | ||
29614 | + return DOT11_MOD_PBCC; | ||
29615 | + } | ||
29616 | + if(suffix=='o') { | ||
29617 | + if(r_enum<DOT11_RATE_6) return -EINVAL; | ||
29618 | + return DOT11_MOD_OFDM; | ||
29619 | + } | ||
29620 | + if(suffix=='d') { | ||
29621 | + if(r_enum<DOT11_RATE_6) return -EINVAL; | ||
29622 | + return DOT11_MOD_CCKOFDM; | ||
29623 | + } | ||
29624 | + return -EINVAL; | ||
29625 | +} | ||
29626 | + | ||
29627 | +#ifdef UNUSED | ||
29628 | +static int | ||
29629 | +fill_ratevector(const char **pstr, u8 *vector, int size, | ||
29630 | + int (*supported)(int mbit, int mod, void *opaque), void *opaque, int or_mask) | ||
29631 | +{ | ||
29632 | + unsigned long rate_mbit; | ||
29633 | + int rate_enum,mod; | ||
29634 | + const char *str = *pstr; | ||
29635 | + char c; | ||
29636 | + | ||
29637 | + do { | ||
29638 | + rate_mbit = simple_strtoul(str, (char**)&str, 10); | ||
29639 | + if(rate_mbit>INT_MAX) return -EINVAL; | ||
29640 | + | ||
29641 | + rate_enum = rate_mbit2enum(rate_mbit); | ||
29642 | + if(rate_enum<0) return rate_enum; | ||
29643 | + | ||
29644 | + c = *str; | ||
29645 | + mod = get_modulation(rate_enum, c); | ||
29646 | + if(mod<0) return mod; | ||
29647 | + | ||
29648 | + if(c>='a' && c<='z') c = *++str; | ||
29649 | + if(c!=',' && c!=' ' && c!='\0') return -EINVAL; | ||
29650 | + | ||
29651 | + if(supported) { | ||
29652 | + int r = supported(rate_mbit, mod, opaque); | ||
29653 | + if(r) return r; | ||
29654 | + } | ||
29655 | + | ||
29656 | + *vector++ = dot11ratebyte[rate_enum] | or_mask; | ||
29657 | + | ||
29658 | + size--; | ||
29659 | + str++; | ||
29660 | + } while(size>0 && c==','); | ||
29661 | + | ||
29662 | + if(size<1) return -E2BIG; | ||
29663 | + *vector=0; /* TODO: sort, remove dups? */ | ||
29664 | + | ||
29665 | + *pstr = str-1; | ||
29666 | + return 0; | ||
29667 | +} | ||
29668 | + | ||
29669 | +static /* TODO: remove 'static' when moved to wireless.c */ | ||
29670 | +int | ||
29671 | +fill_ratevectors(const char *str, u8 *brate, u8 *orate, int size, | ||
29672 | + int (*supported)(int mbit, int mod, void *opaque), void *opaque) | ||
29673 | +{ | ||
29674 | + int r; | ||
29675 | + | ||
29676 | + r = fill_ratevector(&str, brate, size, supported, opaque, 0x80); | ||
29677 | + if(r) return r; | ||
29678 | + | ||
29679 | + orate[0] = 0; | ||
29680 | + if(*str==' ') { | ||
29681 | + str++; | ||
29682 | + r = fill_ratevector(&str, orate, size, supported, opaque, 0); | ||
29683 | + if(r) return r; | ||
29684 | + /* TODO: sanitize, e.g. remove/error on rates already in basic rate set? */ | ||
29685 | + } | ||
29686 | + if(*str) | ||
29687 | + return -EINVAL; | ||
29688 | + | ||
29689 | + return 0; | ||
29690 | +} | ||
29691 | +#endif | ||
29692 | + | ||
29693 | +/* TODO: use u64 masks? */ | ||
29694 | + | ||
29695 | +static int | ||
29696 | +fill_ratemask(const char **pstr, u32* mask, | ||
29697 | + int (*supported)(int mbit, int mod,void *opaque), | ||
29698 | + u32 (*gen_mask)(int mbit, int mod,void *opaque), | ||
29699 | + void *opaque) | ||
29700 | +{ | ||
29701 | + unsigned long rate_mbit; | ||
29702 | + int rate_enum,mod; | ||
29703 | + u32 m = 0; | ||
29704 | + const char *str = *pstr; | ||
29705 | + char c; | ||
29706 | + | ||
29707 | + do { | ||
29708 | + rate_mbit = simple_strtoul(str, (char**)&str, 10); | ||
29709 | + if(rate_mbit>INT_MAX) return -EINVAL; | ||
29710 | + | ||
29711 | + rate_enum = rate_mbit2enum(rate_mbit); | ||
29712 | + if(rate_enum<0) return rate_enum; | ||
29713 | + | ||
29714 | + c = *str; | ||
29715 | + mod = get_modulation(rate_enum, c); | ||
29716 | + if(mod<0) return mod; | ||
29717 | + | ||
29718 | + if(c>='a' && c<='z') c = *++str; | ||
29719 | + if(c!=',' && c!=' ' && c!='\0') return -EINVAL; | ||
29720 | + | ||
29721 | + if(supported) { | ||
29722 | + int r = supported(rate_mbit, mod, opaque); | ||
29723 | + if(r) return r; | ||
29724 | + } | ||
29725 | + | ||
29726 | + m |= gen_mask(rate_mbit, mod, opaque); | ||
29727 | + str++; | ||
29728 | + } while(c==','); | ||
29729 | + | ||
29730 | + *pstr = str-1; | ||
29731 | + *mask |= m; | ||
29732 | + return 0; | ||
29733 | +} | ||
29734 | + | ||
29735 | +static /* TODO: remove 'static' when moved to wireless.c */ | ||
29736 | +int | ||
29737 | +fill_ratemasks(const char *str, u32 *bmask, u32 *omask, | ||
29738 | + int (*supported)(int mbit, int mod,void *opaque), | ||
29739 | + u32 (*gen_mask)(int mbit, int mod,void *opaque), | ||
29740 | + void *opaque) | ||
29741 | +{ | ||
29742 | + int r; | ||
29743 | + | ||
29744 | + r = fill_ratemask(&str, bmask, supported, gen_mask, opaque); | ||
29745 | + if(r) return r; | ||
29746 | + | ||
29747 | + if(*str==' ') { | ||
29748 | + str++; | ||
29749 | + r = fill_ratemask(&str, omask, supported, gen_mask, opaque); | ||
29750 | + if(r) return r; | ||
29751 | + } | ||
29752 | + if(*str) | ||
29753 | + return -EINVAL; | ||
29754 | + return 0; | ||
29755 | +} | ||
29756 | Index: linux-2.6.22/drivers/net/wireless/acx/usb.c | ||
29757 | =================================================================== | ||
29758 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
29759 | +++ linux-2.6.22/drivers/net/wireless/acx/usb.c 2007-08-23 18:34:19.000000000 +0200 | ||
29760 | @@ -0,0 +1,1922 @@ | ||
29761 | +/*********************************************************************** | ||
29762 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
29763 | +** | ||
29764 | +** The contents of this file are subject to the Mozilla Public | ||
29765 | +** License Version 1.1 (the "License"); you may not use this file | ||
29766 | +** except in compliance with the License. You may obtain a copy of | ||
29767 | +** the License at http://www.mozilla.org/MPL/ | ||
29768 | +** | ||
29769 | +** Software distributed under the License is distributed on an "AS | ||
29770 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
29771 | +** implied. See the License for the specific language governing | ||
29772 | +** rights and limitations under the License. | ||
29773 | +** | ||
29774 | +** Alternatively, the contents of this file may be used under the | ||
29775 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
29776 | +** case the provisions of the GPL are applicable instead of the | ||
29777 | +** above. If you wish to allow the use of your version of this file | ||
29778 | +** only under the terms of the GPL and not to allow others to use | ||
29779 | +** your version of this file under the MPL, indicate your decision | ||
29780 | +** by deleting the provisions above and replace them with the notice | ||
29781 | +** and other provisions required by the GPL. If you do not delete | ||
29782 | +** the provisions above, a recipient may use your version of this | ||
29783 | +** file under either the MPL or the GPL. | ||
29784 | +** --------------------------------------------------------------------- | ||
29785 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
29786 | +** made directly to: | ||
29787 | +** | ||
29788 | +** acx100-users@lists.sf.net | ||
29789 | +** http://acx100.sf.net | ||
29790 | +** --------------------------------------------------------------------- | ||
29791 | +*/ | ||
29792 | + | ||
29793 | +/*********************************************************************** | ||
29794 | +** USB support for TI ACX100 based devices. Many parts are taken from | ||
29795 | +** the PCI driver. | ||
29796 | +** | ||
29797 | +** Authors: | ||
29798 | +** Martin Wawro <martin.wawro AT uni-dortmund.de> | ||
29799 | +** Andreas Mohr <andi AT lisas.de> | ||
29800 | +** | ||
29801 | +** LOCKING | ||
29802 | +** callback functions called by USB core are running in interrupt context | ||
29803 | +** and thus have names with _i_. | ||
29804 | +*/ | ||
29805 | +#define ACX_USB 1 | ||
29806 | + | ||
29807 | +#include <linux/version.h> | ||
29808 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) | ||
29809 | +#include <linux/config.h> | ||
29810 | +#endif | ||
29811 | +#include <linux/types.h> | ||
29812 | +#include <linux/module.h> | ||
29813 | +#include <linux/moduleparam.h> | ||
29814 | +#include <linux/kernel.h> | ||
29815 | +#include <linux/usb.h> | ||
29816 | +#include <linux/netdevice.h> | ||
29817 | +#include <linux/rtnetlink.h> | ||
29818 | +#include <linux/etherdevice.h> | ||
29819 | +#include <linux/wireless.h> | ||
29820 | +#include <net/iw_handler.h> | ||
29821 | +#include <linux/vmalloc.h> | ||
29822 | + | ||
29823 | +#include "acx.h" | ||
29824 | + | ||
29825 | + | ||
29826 | +/*********************************************************************** | ||
29827 | +*/ | ||
29828 | +/* number of endpoints of an interface */ | ||
29829 | +#define NUM_EP(intf) (intf)->altsetting[0].desc.bNumEndpoints | ||
29830 | +#define EP(intf, nr) (intf)->altsetting[0].endpoint[(nr)].desc | ||
29831 | +#define GET_DEV(udev) usb_get_dev((udev)) | ||
29832 | +#define PUT_DEV(udev) usb_put_dev((udev)) | ||
29833 | +#define SET_NETDEV_OWNER(ndev, owner) /* not needed anymore ??? */ | ||
29834 | + | ||
29835 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) | ||
29836 | +/* removed in 2.6.14. We will use fake value for now */ | ||
29837 | +#define URB_ASYNC_UNLINK 0 | ||
29838 | +#endif | ||
29839 | + | ||
29840 | + | ||
29841 | +/*********************************************************************** | ||
29842 | +*/ | ||
29843 | +/* ACX100 (TNETW1100) USB device: D-Link DWL-120+ */ | ||
29844 | +#define ACX100_VENDOR_ID 0x2001 | ||
29845 | +#define ACX100_PRODUCT_ID_UNBOOTED 0x3B01 | ||
29846 | +#define ACX100_PRODUCT_ID_BOOTED 0x3B00 | ||
29847 | + | ||
29848 | +/* TNETW1450 USB devices */ | ||
29849 | +#define VENDOR_ID_DLINK 0x07b8 /* D-Link Corp. */ | ||
29850 | +#define PRODUCT_ID_WUG2400 0xb21a /* AboCom WUG2400 or SafeCom SWLUT-54125 */ | ||
29851 | +#define VENDOR_ID_AVM_GMBH 0x057c | ||
29852 | +#define PRODUCT_ID_AVM_WLAN_USB 0x5601 | ||
29853 | +#define PRODUCT_ID_AVM_WLAN_USB_si 0x6201 /* "self install" named Version: driver kills kernel on inbound scans from fritz box ??? */ | ||
29854 | +#define VENDOR_ID_ZCOM 0x0cde | ||
29855 | +#define PRODUCT_ID_ZCOM_XG750 0x0017 /* not tested yet */ | ||
29856 | +#define VENDOR_ID_TI 0x0451 | ||
29857 | +#define PRODUCT_ID_TI_UNKNOWN 0x60c5 /* not tested yet */ | ||
29858 | + | ||
29859 | +#define ACX_USB_CTRL_TIMEOUT 5500 /* steps in ms */ | ||
29860 | + | ||
29861 | +/* Buffer size for fw upload, same for both ACX100 USB and TNETW1450 */ | ||
29862 | +#define USB_RWMEM_MAXLEN 2048 | ||
29863 | + | ||
29864 | +/* The number of bulk URBs to use */ | ||
29865 | +#define ACX_TX_URB_CNT 8 | ||
29866 | +#define ACX_RX_URB_CNT 2 | ||
29867 | + | ||
29868 | +/* Should be sent to the bulkout endpoint */ | ||
29869 | +#define ACX_USB_REQ_UPLOAD_FW 0x10 | ||
29870 | +#define ACX_USB_REQ_ACK_CS 0x11 | ||
29871 | +#define ACX_USB_REQ_CMD 0x12 | ||
29872 | + | ||
29873 | +/*********************************************************************** | ||
29874 | +** Prototypes | ||
29875 | +*/ | ||
29876 | +static int acxusb_e_probe(struct usb_interface *, const struct usb_device_id *); | ||
29877 | +static void acxusb_e_disconnect(struct usb_interface *); | ||
29878 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
29879 | +static void acxusb_i_complete_tx(struct urb *); | ||
29880 | +static void acxusb_i_complete_rx(struct urb *); | ||
29881 | +#else | ||
29882 | +static void acxusb_i_complete_tx(struct urb *, struct pt_regs *); | ||
29883 | +static void acxusb_i_complete_rx(struct urb *, struct pt_regs *); | ||
29884 | +#endif | ||
29885 | +static int acxusb_e_open(struct net_device *); | ||
29886 | +static int acxusb_e_close(struct net_device *); | ||
29887 | +static void acxusb_i_set_rx_mode(struct net_device *); | ||
29888 | +static int acxusb_boot(struct usb_device *, int is_tnetw1450, int *radio_type); | ||
29889 | + | ||
29890 | +static void acxusb_l_poll_rx(acx_device_t *adev, usb_rx_t* rx); | ||
29891 | + | ||
29892 | +static void acxusb_i_tx_timeout(struct net_device *); | ||
29893 | + | ||
29894 | +/* static void dump_device(struct usb_device *); */ | ||
29895 | +/* static void dump_device_descriptor(struct usb_device_descriptor *); */ | ||
29896 | +/* static void dump_config_descriptor(struct usb_config_descriptor *); */ | ||
29897 | + | ||
29898 | +/*********************************************************************** | ||
29899 | +** Module Data | ||
29900 | +*/ | ||
29901 | +#define TXBUFSIZE sizeof(usb_txbuffer_t) | ||
29902 | +/* | ||
29903 | + * Now, this is just plain lying, but the device insists in giving us | ||
29904 | + * huge packets. We supply extra space after rxbuffer. Need to understand | ||
29905 | + * it better... | ||
29906 | + */ | ||
29907 | +#define RXBUFSIZE (sizeof(rxbuffer_t) + \ | ||
29908 | + (sizeof(usb_rx_t) - sizeof(struct usb_rx_plain))) | ||
29909 | + | ||
29910 | +static const struct usb_device_id | ||
29911 | +acxusb_ids[] = { | ||
29912 | + { USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_BOOTED) }, | ||
29913 | + { USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_UNBOOTED) }, | ||
29914 | + { USB_DEVICE(VENDOR_ID_DLINK, PRODUCT_ID_WUG2400) }, | ||
29915 | + { USB_DEVICE(VENDOR_ID_AVM_GMBH, PRODUCT_ID_AVM_WLAN_USB) }, | ||
29916 | + { USB_DEVICE(VENDOR_ID_AVM_GMBH, PRODUCT_ID_AVM_WLAN_USB_si) }, | ||
29917 | + { USB_DEVICE(VENDOR_ID_ZCOM, PRODUCT_ID_ZCOM_XG750) }, | ||
29918 | + { USB_DEVICE(VENDOR_ID_TI, PRODUCT_ID_TI_UNKNOWN) }, | ||
29919 | + {} | ||
29920 | +}; | ||
29921 | + | ||
29922 | +MODULE_DEVICE_TABLE(usb, acxusb_ids); | ||
29923 | + | ||
29924 | +/* USB driver data structure as required by the kernel's USB core */ | ||
29925 | +static struct usb_driver | ||
29926 | +acxusb_driver = { | ||
29927 | + .name = "acx_usb", | ||
29928 | + .probe = acxusb_e_probe, | ||
29929 | + .disconnect = acxusb_e_disconnect, | ||
29930 | + .id_table = acxusb_ids | ||
29931 | +}; | ||
29932 | + | ||
29933 | + | ||
29934 | +/*********************************************************************** | ||
29935 | +** USB helper | ||
29936 | +** | ||
29937 | +** ldd3 ch13 says: | ||
29938 | +** When the function is usb_kill_urb, the urb lifecycle is stopped. This | ||
29939 | +** function is usually used when the device is disconnected from the system, | ||
29940 | +** in the disconnect callback. For some drivers, the usb_unlink_urb function | ||
29941 | +** should be used to tell the USB core to stop an urb. This function does not | ||
29942 | +** wait for the urb to be fully stopped before returning to the caller. | ||
29943 | +** This is useful for stoppingthe urb while in an interrupt handler or when | ||
29944 | +** a spinlock is held, as waiting for a urb to fully stop requires the ability | ||
29945 | +** for the USB core to put the calling process to sleep. This function requires | ||
29946 | +** that the URB_ASYNC_UNLINK flag value be set in the urb that is being asked | ||
29947 | +** to be stopped in order to work properly. | ||
29948 | +** | ||
29949 | +** (URB_ASYNC_UNLINK is obsolete, usb_unlink_urb will always be | ||
29950 | +** asynchronous while usb_kill_urb is synchronous and should be called | ||
29951 | +** directly (drivers/usb/core/urb.c)) | ||
29952 | +** | ||
29953 | +** In light of this, timeout is just for paranoid reasons... | ||
29954 | +* | ||
29955 | +* Actually, it's useful for debugging. If we reach timeout, we're doing | ||
29956 | +* something wrong with the urbs. | ||
29957 | +*/ | ||
29958 | +static void | ||
29959 | +acxusb_unlink_urb(struct urb* urb) | ||
29960 | +{ | ||
29961 | + if (!urb) | ||
29962 | + return; | ||
29963 | + | ||
29964 | + if (urb->status == -EINPROGRESS) { | ||
29965 | + int timeout = 10; | ||
29966 | + | ||
29967 | + usb_unlink_urb(urb); | ||
29968 | + while (--timeout && urb->status == -EINPROGRESS) { | ||
29969 | + mdelay(1); | ||
29970 | + } | ||
29971 | + if (!timeout) { | ||
29972 | + printk("acx_usb: urb unlink timeout!\n"); | ||
29973 | + } | ||
29974 | + } | ||
29975 | +} | ||
29976 | + | ||
29977 | + | ||
29978 | +/*********************************************************************** | ||
29979 | +** EEPROM and PHY read/write helpers | ||
29980 | +*/ | ||
29981 | +/*********************************************************************** | ||
29982 | +** acxusb_s_read_phy_reg | ||
29983 | +*/ | ||
29984 | +int | ||
29985 | +acxusb_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf) | ||
29986 | +{ | ||
29987 | + /* mem_read_write_t mem; */ | ||
29988 | + | ||
29989 | + FN_ENTER; | ||
29990 | + | ||
29991 | + printk("%s doesn't seem to work yet, disabled.\n", __func__); | ||
29992 | + | ||
29993 | + /* | ||
29994 | + mem.addr = cpu_to_le16(reg); | ||
29995 | + mem.type = cpu_to_le16(0x82); | ||
29996 | + mem.len = cpu_to_le32(4); | ||
29997 | + acx_s_issue_cmd(adev, ACX1xx_CMD_MEM_READ, &mem, sizeof(mem)); | ||
29998 | + *charbuf = mem.data; | ||
29999 | + log(L_DEBUG, "read radio PHY[0x%04X]=0x%02X\n", reg, *charbuf); | ||
30000 | + */ | ||
30001 | + | ||
30002 | + FN_EXIT1(OK); | ||
30003 | + return OK; | ||
30004 | +} | ||
30005 | + | ||
30006 | + | ||
30007 | +/*********************************************************************** | ||
30008 | +*/ | ||
30009 | +int | ||
30010 | +acxusb_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value) | ||
30011 | +{ | ||
30012 | + mem_read_write_t mem; | ||
30013 | + | ||
30014 | + FN_ENTER; | ||
30015 | + | ||
30016 | + mem.addr = cpu_to_le16(reg); | ||
30017 | + mem.type = cpu_to_le16(0x82); | ||
30018 | + mem.len = cpu_to_le32(4); | ||
30019 | + mem.data = value; | ||
30020 | + acx_s_issue_cmd(adev, ACX1xx_CMD_MEM_WRITE, &mem, sizeof(mem)); | ||
30021 | + log(L_DEBUG, "write radio PHY[0x%04X]=0x%02X\n", reg, value); | ||
30022 | + | ||
30023 | + FN_EXIT1(OK); | ||
30024 | + return OK; | ||
30025 | +} | ||
30026 | + | ||
30027 | + | ||
30028 | +/*********************************************************************** | ||
30029 | +** acxusb_s_issue_cmd_timeo | ||
30030 | +** Excecutes a command in the command mailbox | ||
30031 | +** | ||
30032 | +** buffer = a pointer to the data. | ||
30033 | +** The data must not include 4 byte command header | ||
30034 | +*/ | ||
30035 | + | ||
30036 | +/* TODO: ideally we shall always know how much we need | ||
30037 | +** and this shall be 0 */ | ||
30038 | +#define BOGUS_SAFETY_PADDING 0x40 | ||
30039 | + | ||
30040 | +#undef FUNC | ||
30041 | +#define FUNC "issue_cmd" | ||
30042 | + | ||
30043 | +#if !ACX_DEBUG | ||
30044 | +int | ||
30045 | +acxusb_s_issue_cmd_timeo( | ||
30046 | + acx_device_t *adev, | ||
30047 | + unsigned cmd, | ||
30048 | + void *buffer, | ||
30049 | + unsigned buflen, | ||
30050 | + unsigned timeout) | ||
30051 | +{ | ||
30052 | +#else | ||
30053 | +int | ||
30054 | +acxusb_s_issue_cmd_timeo_debug( | ||
30055 | + acx_device_t *adev, | ||
30056 | + unsigned cmd, | ||
30057 | + void *buffer, | ||
30058 | + unsigned buflen, | ||
30059 | + unsigned timeout, | ||
30060 | + const char* cmdstr) | ||
30061 | +{ | ||
30062 | +#endif | ||
30063 | + /* USB ignores timeout param */ | ||
30064 | + | ||
30065 | + struct usb_device *usbdev; | ||
30066 | + struct { | ||
30067 | + u16 cmd; | ||
30068 | + u16 status; | ||
30069 | + u8 data[1]; | ||
30070 | + } ACX_PACKED *loc; | ||
30071 | + const char *devname; | ||
30072 | + int acklen, blocklen, inpipe, outpipe; | ||
30073 | + int cmd_status; | ||
30074 | + int result; | ||
30075 | + | ||
30076 | + FN_ENTER; | ||
30077 | + | ||
30078 | + devname = adev->ndev->name; | ||
30079 | + /* no "wlan%%d: ..." please */ | ||
30080 | + if (!devname || !devname[0] || devname[4]=='%') | ||
30081 | + devname = "acx"; | ||
30082 | + | ||
30083 | + log(L_CTL, FUNC"(cmd:%s,buflen:%u,type:0x%04X)\n", | ||
30084 | + cmdstr, buflen, | ||
30085 | + buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1); | ||
30086 | + | ||
30087 | + loc = kmalloc(buflen + 4 + BOGUS_SAFETY_PADDING, GFP_KERNEL); | ||
30088 | + if (!loc) { | ||
30089 | + printk("%s: "FUNC"(): no memory for data buffer\n", devname); | ||
30090 | + goto bad; | ||
30091 | + } | ||
30092 | + | ||
30093 | + /* get context from acx_device */ | ||
30094 | + usbdev = adev->usbdev; | ||
30095 | + | ||
30096 | + /* check which kind of command was issued */ | ||
30097 | + loc->cmd = cpu_to_le16(cmd); | ||
30098 | + loc->status = 0; | ||
30099 | + | ||
30100 | +/* NB: buflen == frmlen + 4 | ||
30101 | +** | ||
30102 | +** Interrogate: write 8 bytes: (cmd,status,rid,frmlen), then | ||
30103 | +** read (cmd,status,rid,frmlen,data[frmlen]) back | ||
30104 | +** | ||
30105 | +** Configure: write (cmd,status,rid,frmlen,data[frmlen]) | ||
30106 | +** | ||
30107 | +** Possibly bogus special handling of ACX1xx_IE_SCAN_STATUS removed | ||
30108 | +*/ | ||
30109 | + | ||
30110 | + /* now write the parameters of the command if needed */ | ||
30111 | + acklen = buflen + 4 + BOGUS_SAFETY_PADDING; | ||
30112 | + blocklen = buflen; | ||
30113 | + if (buffer && buflen) { | ||
30114 | + /* if it's an INTERROGATE command, just pass the length | ||
30115 | + * of parameters to read, as data */ | ||
30116 | + if (cmd == ACX1xx_CMD_INTERROGATE) { | ||
30117 | + blocklen = 4; | ||
30118 | + acklen = buflen + 4; | ||
30119 | + } | ||
30120 | + memcpy(loc->data, buffer, blocklen); | ||
30121 | + } | ||
30122 | + blocklen += 4; /* account for cmd,status */ | ||
30123 | + | ||
30124 | + /* obtain the I/O pipes */ | ||
30125 | + outpipe = usb_sndctrlpipe(usbdev, 0); | ||
30126 | + inpipe = usb_rcvctrlpipe(usbdev, 0); | ||
30127 | + log(L_CTL, "ctrl inpipe=0x%X outpipe=0x%X\n", inpipe, outpipe); | ||
30128 | + log(L_CTL, "sending USB control msg (out) (blocklen=%d)\n", blocklen); | ||
30129 | + if (acx_debug & L_DATA) | ||
30130 | + acx_dump_bytes(loc, blocklen); | ||
30131 | + | ||
30132 | + result = usb_control_msg(usbdev, outpipe, | ||
30133 | + ACX_USB_REQ_CMD, /* request */ | ||
30134 | + USB_TYPE_VENDOR|USB_DIR_OUT, /* requesttype */ | ||
30135 | + 0, /* value */ | ||
30136 | + 0, /* index */ | ||
30137 | + loc, /* dataptr */ | ||
30138 | + blocklen, /* size */ | ||
30139 | + ACX_USB_CTRL_TIMEOUT /* timeout in ms */ | ||
30140 | + ); | ||
30141 | + | ||
30142 | + if (result == -ENODEV) { | ||
30143 | + log(L_CTL, "no device present (unplug?)\n"); | ||
30144 | + goto good; | ||
30145 | + } | ||
30146 | + | ||
30147 | + log(L_CTL, "wrote %d bytes\n", result); | ||
30148 | + if (result < 0) { | ||
30149 | + goto bad; | ||
30150 | + } | ||
30151 | + | ||
30152 | + /* check for device acknowledge */ | ||
30153 | + log(L_CTL, "sending USB control msg (in) (acklen=%d)\n", acklen); | ||
30154 | + loc->status = 0; /* delete old status flag -> set to IDLE */ | ||
30155 | + /* shall we zero out the rest? */ | ||
30156 | + result = usb_control_msg(usbdev, inpipe, | ||
30157 | + ACX_USB_REQ_CMD, /* request */ | ||
30158 | + USB_TYPE_VENDOR|USB_DIR_IN, /* requesttype */ | ||
30159 | + 0, /* value */ | ||
30160 | + 0, /* index */ | ||
30161 | + loc, /* dataptr */ | ||
30162 | + acklen, /* size */ | ||
30163 | + ACX_USB_CTRL_TIMEOUT /* timeout in ms */ | ||
30164 | + ); | ||
30165 | + if (result < 0) { | ||
30166 | + printk("%s: "FUNC"(): USB read error %d\n", devname, result); | ||
30167 | + goto bad; | ||
30168 | + } | ||
30169 | + if (acx_debug & L_CTL) { | ||
30170 | + printk("read %d bytes: ", result); | ||
30171 | + acx_dump_bytes(loc, result); | ||
30172 | + } | ||
30173 | + | ||
30174 | +/* | ||
30175 | + check for result==buflen+4? Was seen: | ||
30176 | + | ||
30177 | +interrogate(type:ACX100_IE_DOT11_ED_THRESHOLD,len:4) | ||
30178 | +issue_cmd(cmd:ACX1xx_CMD_INTERROGATE,buflen:8,type:4111) | ||
30179 | +ctrl inpipe=0x80000280 outpipe=0x80000200 | ||
30180 | +sending USB control msg (out) (blocklen=8) | ||
30181 | +01 00 00 00 0F 10 04 00 | ||
30182 | +wrote 8 bytes | ||
30183 | +sending USB control msg (in) (acklen=12) sizeof(loc->data | ||
30184 | +read 4 bytes <==== MUST BE 12!! | ||
30185 | +*/ | ||
30186 | + | ||
30187 | + cmd_status = le16_to_cpu(loc->status); | ||
30188 | + if (cmd_status != 1) { | ||
30189 | + printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s)\n", | ||
30190 | + devname, cmd_status, acx_cmd_status_str(cmd_status)); | ||
30191 | + /* TODO: goto bad; ? */ | ||
30192 | + } | ||
30193 | + if ((cmd == ACX1xx_CMD_INTERROGATE) && buffer && buflen) { | ||
30194 | + memcpy(buffer, loc->data, buflen); | ||
30195 | + log(L_CTL, "response frame: cmd=0x%04X status=%d\n", | ||
30196 | + le16_to_cpu(loc->cmd), | ||
30197 | + cmd_status); | ||
30198 | + } | ||
30199 | +good: | ||
30200 | + kfree(loc); | ||
30201 | + FN_EXIT1(OK); | ||
30202 | + return OK; | ||
30203 | +bad: | ||
30204 | + /* Give enough info so that callers can avoid | ||
30205 | + ** printing their own diagnostic messages */ | ||
30206 | +#if ACX_DEBUG | ||
30207 | + printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr); | ||
30208 | +#else | ||
30209 | + printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd); | ||
30210 | +#endif | ||
30211 | + dump_stack(); | ||
30212 | + kfree(loc); | ||
30213 | + FN_EXIT1(NOT_OK); | ||
30214 | + return NOT_OK; | ||
30215 | +} | ||
30216 | + | ||
30217 | + | ||
30218 | +/*********************************************************************** | ||
30219 | +** acxusb_boot() | ||
30220 | +** Inputs: | ||
30221 | +** usbdev -> Pointer to kernel's usb_device structure | ||
30222 | +** | ||
30223 | +** Returns: | ||
30224 | +** (int) Errorcode or 0 on success | ||
30225 | +** | ||
30226 | +** This function triggers the loading of the firmware image from harddisk | ||
30227 | +** and then uploads the firmware to the USB device. After uploading the | ||
30228 | +** firmware and transmitting the checksum, the device resets and appears | ||
30229 | +** as a new device on the USB bus (the device we can finally deal with) | ||
30230 | +*/ | ||
30231 | +static inline int | ||
30232 | +acxusb_fw_needs_padding(firmware_image_t *fw_image, unsigned int usb_maxlen) | ||
30233 | +{ | ||
30234 | + unsigned int num_xfers = ((fw_image->size - 1) / usb_maxlen) + 1; | ||
30235 | + | ||
30236 | + return ((num_xfers % 2) == 0); | ||
30237 | +} | ||
30238 | + | ||
30239 | +static int | ||
30240 | +acxusb_boot(struct usb_device *usbdev, int is_tnetw1450, int *radio_type) | ||
30241 | +{ | ||
30242 | + char filename[sizeof("tiacx1NNusbcRR")]; | ||
30243 | + | ||
30244 | + firmware_image_t *fw_image = NULL; | ||
30245 | + char *usbbuf; | ||
30246 | + unsigned int offset; | ||
30247 | + unsigned int blk_len, inpipe, outpipe; | ||
30248 | + u32 num_processed; | ||
30249 | + u32 img_checksum, sum; | ||
30250 | + u32 file_size; | ||
30251 | + int result = -EIO; | ||
30252 | + int i; | ||
30253 | + | ||
30254 | + FN_ENTER; | ||
30255 | + | ||
30256 | + /* dump_device(usbdev); */ | ||
30257 | + | ||
30258 | + usbbuf = kmalloc(USB_RWMEM_MAXLEN, GFP_KERNEL); | ||
30259 | + if (!usbbuf) { | ||
30260 | + printk(KERN_ERR "acx: no memory for USB transfer buffer (%d bytes)\n", USB_RWMEM_MAXLEN); | ||
30261 | + result = -ENOMEM; | ||
30262 | + goto end; | ||
30263 | + } | ||
30264 | + if (is_tnetw1450) { | ||
30265 | + /* Obtain the I/O pipes */ | ||
30266 | + outpipe = usb_sndbulkpipe(usbdev, 1); | ||
30267 | + inpipe = usb_rcvbulkpipe(usbdev, 2); | ||
30268 | + | ||
30269 | + printk(KERN_DEBUG "wait for device ready\n"); | ||
30270 | + for (i = 0; i <= 2; i++) { | ||
30271 | + result = usb_bulk_msg(usbdev, inpipe, | ||
30272 | + usbbuf, | ||
30273 | + USB_RWMEM_MAXLEN, | ||
30274 | + &num_processed, | ||
30275 | + 2000 | ||
30276 | + ); | ||
30277 | + | ||
30278 | + if ((*(u32 *)&usbbuf[4] == 0x40000001) | ||
30279 | + && (*(u16 *)&usbbuf[2] == 0x1) | ||
30280 | + && ((*(u16 *)usbbuf & 0x3fff) == 0) | ||
30281 | + && ((*(u16 *)usbbuf & 0xc000) == 0xc000)) | ||
30282 | + break; | ||
30283 | + msleep(10); | ||
30284 | + } | ||
30285 | + if (i == 2) | ||
30286 | + goto fw_end; | ||
30287 | + | ||
30288 | + *radio_type = usbbuf[8]; | ||
30289 | + } else { | ||
30290 | + /* Obtain the I/O pipes */ | ||
30291 | + outpipe = usb_sndctrlpipe(usbdev, 0); | ||
30292 | + inpipe = usb_rcvctrlpipe(usbdev, 0); | ||
30293 | + | ||
30294 | + /* FIXME: shouldn't be hardcoded */ | ||
30295 | + *radio_type = RADIO_MAXIM_0D; | ||
30296 | + } | ||
30297 | + | ||
30298 | + snprintf(filename, sizeof(filename), "tiacx1%02dusbc%02X", | ||
30299 | + is_tnetw1450 * 11, *radio_type); | ||
30300 | + | ||
30301 | + fw_image = acx_s_read_fw(&usbdev->dev, filename, &file_size); | ||
30302 | + if (!fw_image) { | ||
30303 | + result = -EIO; | ||
30304 | + goto end; | ||
30305 | + } | ||
30306 | + log(L_INIT, "firmware size: %d bytes\n", file_size); | ||
30307 | + | ||
30308 | + img_checksum = le32_to_cpu(fw_image->chksum); | ||
30309 | + | ||
30310 | + if (is_tnetw1450) { | ||
30311 | + u8 cmdbuf[20]; | ||
30312 | + const u8 *p; | ||
30313 | + u8 need_padding; | ||
30314 | + u32 tmplen, val; | ||
30315 | + | ||
30316 | + memset(cmdbuf, 0, 16); | ||
30317 | + | ||
30318 | + need_padding = acxusb_fw_needs_padding(fw_image, USB_RWMEM_MAXLEN); | ||
30319 | + tmplen = need_padding ? file_size-4 : file_size-8; | ||
30320 | + *(u16 *)&cmdbuf[0] = 0xc000; | ||
30321 | + *(u16 *)&cmdbuf[2] = 0x000b; | ||
30322 | + *(u32 *)&cmdbuf[4] = tmplen; | ||
30323 | + *(u32 *)&cmdbuf[8] = file_size-8; | ||
30324 | + *(u32 *)&cmdbuf[12] = img_checksum; | ||
30325 | + | ||
30326 | + result = usb_bulk_msg(usbdev, outpipe, cmdbuf, 16, &num_processed, HZ); | ||
30327 | + if (result < 0) | ||
30328 | + goto fw_end; | ||
30329 | + | ||
30330 | + p = (const u8 *)&fw_image->size; | ||
30331 | + | ||
30332 | + /* first calculate checksum for image size part */ | ||
30333 | + sum = p[0]+p[1]+p[2]+p[3]; | ||
30334 | + p += 4; | ||
30335 | + | ||
30336 | + /* now continue checksum for firmware data part */ | ||
30337 | + tmplen = le32_to_cpu(fw_image->size); | ||
30338 | + for (i = 0; i < tmplen /* image size */; i++) { | ||
30339 | + sum += *p++; | ||
30340 | + } | ||
30341 | + | ||
30342 | + if (sum != le32_to_cpu(fw_image->chksum)) { | ||
30343 | + printk("acx: FATAL: firmware upload: " | ||
30344 | + "checksums don't match! " | ||
30345 | + "(0x%08x vs. 0x%08x)\n", | ||
30346 | + sum, fw_image->chksum); | ||
30347 | + goto fw_end; | ||
30348 | + } | ||
30349 | + | ||
30350 | + offset = 8; | ||
30351 | + while (offset < file_size) { | ||
30352 | + blk_len = file_size - offset; | ||
30353 | + if (blk_len > USB_RWMEM_MAXLEN) { | ||
30354 | + blk_len = USB_RWMEM_MAXLEN; | ||
30355 | + } | ||
30356 | + | ||
30357 | + log(L_INIT, "uploading firmware (%d bytes, offset=%d)\n", | ||
30358 | + blk_len, offset); | ||
30359 | + memcpy(usbbuf, ((u8 *)fw_image) + offset, blk_len); | ||
30360 | + | ||
30361 | + p = usbbuf; | ||
30362 | + for (i = 0; i < blk_len; i += 4) { | ||
30363 | + *(u32 *)p = be32_to_cpu(*(u32 *)p); | ||
30364 | + p += 4; | ||
30365 | + } | ||
30366 | + | ||
30367 | + result = usb_bulk_msg(usbdev, outpipe, usbbuf, blk_len, &num_processed, HZ); | ||
30368 | + if ((result < 0) || (num_processed != blk_len)) | ||
30369 | + goto fw_end; | ||
30370 | + offset += blk_len; | ||
30371 | + } | ||
30372 | + if (need_padding) { | ||
30373 | + printk(KERN_DEBUG "send padding\n"); | ||
30374 | + memset(usbbuf, 0, 4); | ||
30375 | + result = usb_bulk_msg(usbdev, outpipe, usbbuf, 4, &num_processed, HZ); | ||
30376 | + if ((result < 0) || (num_processed != 4)) | ||
30377 | + goto fw_end; | ||
30378 | + } | ||
30379 | + printk(KERN_DEBUG "read firmware upload result\n"); | ||
30380 | + memset(cmdbuf, 0, 20); /* additional memset */ | ||
30381 | + result = usb_bulk_msg(usbdev, inpipe, cmdbuf, 20, &num_processed, 2000); | ||
30382 | + if (result < 0) | ||
30383 | + goto fw_end; | ||
30384 | + if (*(u32 *)&cmdbuf[4] == 0x40000003) | ||
30385 | + goto fw_end; | ||
30386 | + if (*(u32 *)&cmdbuf[4]) | ||
30387 | + goto fw_end; | ||
30388 | + if (*(u16 *)&cmdbuf[16] != 1) | ||
30389 | + goto fw_end; | ||
30390 | + | ||
30391 | + val = *(u32 *)&cmdbuf[0]; | ||
30392 | + if ((val & 0x3fff) | ||
30393 | + || ((val & 0xc000) != 0xc000)) | ||
30394 | + goto fw_end; | ||
30395 | + | ||
30396 | + val = *(u32 *)&cmdbuf[8]; | ||
30397 | + if (val & 2) { | ||
30398 | + result = usb_bulk_msg(usbdev, inpipe, cmdbuf, 20, &num_processed, 2000); | ||
30399 | + if (result < 0) | ||
30400 | + goto fw_end; | ||
30401 | + val = *(u32 *)&cmdbuf[8]; | ||
30402 | + } | ||
30403 | + /* yup, no "else" here! */ | ||
30404 | + if (val & 1) { | ||
30405 | + memset(usbbuf, 0, 4); | ||
30406 | + result = usb_bulk_msg(usbdev, outpipe, usbbuf, 4, &num_processed, HZ); | ||
30407 | + if ((result < 0) || (!num_processed)) | ||
30408 | + goto fw_end; | ||
30409 | + } | ||
30410 | + | ||
30411 | + printk("TNETW1450 firmware upload successful!\n"); | ||
30412 | + result = 0; | ||
30413 | + goto end; | ||
30414 | +fw_end: | ||
30415 | + result = -EIO; | ||
30416 | + goto end; | ||
30417 | + } else { | ||
30418 | + /* ACX100 USB */ | ||
30419 | + | ||
30420 | + /* now upload the firmware, slice the data into blocks */ | ||
30421 | + offset = 8; | ||
30422 | + while (offset < file_size) { | ||
30423 | + blk_len = file_size - offset; | ||
30424 | + if (blk_len > USB_RWMEM_MAXLEN) { | ||
30425 | + blk_len = USB_RWMEM_MAXLEN; | ||
30426 | + } | ||
30427 | + log(L_INIT, "uploading firmware (%d bytes, offset=%d)\n", | ||
30428 | + blk_len, offset); | ||
30429 | + memcpy(usbbuf, ((u8 *)fw_image) + offset, blk_len); | ||
30430 | + result = usb_control_msg(usbdev, outpipe, | ||
30431 | + ACX_USB_REQ_UPLOAD_FW, | ||
30432 | + USB_TYPE_VENDOR|USB_DIR_OUT, | ||
30433 | + (file_size - 8) & 0xffff, /* value */ | ||
30434 | + (file_size - 8) >> 16, /* index */ | ||
30435 | + usbbuf, /* dataptr */ | ||
30436 | + blk_len, /* size */ | ||
30437 | + 3000 /* timeout in ms */ | ||
30438 | + ); | ||
30439 | + offset += blk_len; | ||
30440 | + if (result < 0) { | ||
30441 | + printk(KERN_ERR "acx: error %d during upload " | ||
30442 | + "of firmware, aborting\n", result); | ||
30443 | + goto end; | ||
30444 | + } | ||
30445 | + } | ||
30446 | + | ||
30447 | + /* finally, send the checksum and reboot the device */ | ||
30448 | + /* does this trigger the reboot? */ | ||
30449 | + result = usb_control_msg(usbdev, outpipe, | ||
30450 | + ACX_USB_REQ_UPLOAD_FW, | ||
30451 | + USB_TYPE_VENDOR|USB_DIR_OUT, | ||
30452 | + img_checksum & 0xffff, /* value */ | ||
30453 | + img_checksum >> 16, /* index */ | ||
30454 | + NULL, /* dataptr */ | ||
30455 | + 0, /* size */ | ||
30456 | + 3000 /* timeout in ms */ | ||
30457 | + ); | ||
30458 | + if (result < 0) { | ||
30459 | + printk(KERN_ERR "acx: error %d during tx of checksum, " | ||
30460 | + "aborting\n", result); | ||
30461 | + goto end; | ||
30462 | + } | ||
30463 | + result = usb_control_msg(usbdev, inpipe, | ||
30464 | + ACX_USB_REQ_ACK_CS, | ||
30465 | + USB_TYPE_VENDOR|USB_DIR_IN, | ||
30466 | + img_checksum & 0xffff, /* value */ | ||
30467 | + img_checksum >> 16, /* index */ | ||
30468 | + usbbuf, /* dataptr */ | ||
30469 | + 8, /* size */ | ||
30470 | + 3000 /* timeout in ms */ | ||
30471 | + ); | ||
30472 | + if (result < 0) { | ||
30473 | + printk(KERN_ERR "acx: error %d during ACK of checksum, " | ||
30474 | + "aborting\n", result); | ||
30475 | + goto end; | ||
30476 | + } | ||
30477 | + if (*usbbuf != 0x10) { | ||
30478 | + printk(KERN_ERR "acx: invalid checksum?\n"); | ||
30479 | + result = -EINVAL; | ||
30480 | + goto end; | ||
30481 | + } | ||
30482 | + result = 0; | ||
30483 | + } | ||
30484 | + | ||
30485 | +end: | ||
30486 | + vfree(fw_image); | ||
30487 | + kfree(usbbuf); | ||
30488 | + | ||
30489 | + FN_EXIT1(result); | ||
30490 | + return result; | ||
30491 | +} | ||
30492 | + | ||
30493 | + | ||
30494 | +/* FIXME: maybe merge it with usual eeprom reading, into common code? */ | ||
30495 | +static void | ||
30496 | +acxusb_s_read_eeprom_version(acx_device_t *adev) | ||
30497 | +{ | ||
30498 | + u8 eeprom_ver[0x8]; | ||
30499 | + | ||
30500 | + memset(eeprom_ver, 0, sizeof(eeprom_ver)); | ||
30501 | + acx_s_interrogate(adev, &eeprom_ver, ACX1FF_IE_EEPROM_VER); | ||
30502 | + | ||
30503 | + /* FIXME: which one of those values to take? */ | ||
30504 | + adev->eeprom_version = eeprom_ver[5]; | ||
30505 | +} | ||
30506 | + | ||
30507 | + | ||
30508 | +/* | ||
30509 | + * temporary helper function to at least fill important cfgopt members with | ||
30510 | + * useful replacement values until we figure out how one manages to fetch | ||
30511 | + * the configoption struct in the USB device case... | ||
30512 | + */ | ||
30513 | +static int | ||
30514 | +acxusb_s_fill_configoption(acx_device_t *adev) | ||
30515 | +{ | ||
30516 | + adev->cfgopt_probe_delay = 200; | ||
30517 | + adev->cfgopt_dot11CCAModes = 4; | ||
30518 | + adev->cfgopt_dot11Diversity = 1; | ||
30519 | + adev->cfgopt_dot11ShortPreambleOption = 1; | ||
30520 | + adev->cfgopt_dot11PBCCOption = 1; | ||
30521 | + adev->cfgopt_dot11ChannelAgility = 0; | ||
30522 | + adev->cfgopt_dot11PhyType = 5; | ||
30523 | + adev->cfgopt_dot11TempType = 1; | ||
30524 | + return OK; | ||
30525 | +} | ||
30526 | + | ||
30527 | + | ||
30528 | +/*********************************************************************** | ||
30529 | +** acxusb_e_probe() | ||
30530 | +** | ||
30531 | +** This function is invoked by the kernel's USB core whenever a new device is | ||
30532 | +** attached to the system or the module is loaded. It is presented a usb_device | ||
30533 | +** structure from which information regarding the device is obtained and evaluated. | ||
30534 | +** In case this driver is able to handle one of the offered devices, it returns | ||
30535 | +** a non-null pointer to a driver context and thereby claims the device. | ||
30536 | +*/ | ||
30537 | + | ||
30538 | +static void | ||
30539 | +dummy_netdev_init(struct net_device *ndev) {} | ||
30540 | + | ||
30541 | +static int | ||
30542 | +acxusb_e_probe(struct usb_interface *intf, const struct usb_device_id *devID) | ||
30543 | +{ | ||
30544 | + struct usb_device *usbdev = interface_to_usbdev(intf); | ||
30545 | + acx_device_t *adev = NULL; | ||
30546 | + struct net_device *ndev = NULL; | ||
30547 | + struct usb_config_descriptor *config; | ||
30548 | + struct usb_endpoint_descriptor *epdesc; | ||
30549 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) | ||
30550 | + struct usb_host_endpoint *ep; | ||
30551 | +#endif | ||
30552 | + struct usb_interface_descriptor *ifdesc; | ||
30553 | + const char* msg; | ||
30554 | + int numconfigs, numfaces, numep; | ||
30555 | + int result = OK; | ||
30556 | + int i; | ||
30557 | + int radio_type; | ||
30558 | + /* this one needs to be more precise in case there appears a TNETW1450 from the same vendor */ | ||
30559 | + int is_tnetw1450 = (usbdev->descriptor.idVendor != ACX100_VENDOR_ID); | ||
30560 | + | ||
30561 | + FN_ENTER; | ||
30562 | + | ||
30563 | + if (is_tnetw1450) { | ||
30564 | + /* Boot the device (i.e. upload the firmware) */ | ||
30565 | + acxusb_boot(usbdev, is_tnetw1450, &radio_type); | ||
30566 | + | ||
30567 | + /* TNETW1450-based cards will continue right away with | ||
30568 | + * the same USB ID after booting */ | ||
30569 | + } else { | ||
30570 | + /* First check if this is the "unbooted" hardware */ | ||
30571 | + if (usbdev->descriptor.idProduct == ACX100_PRODUCT_ID_UNBOOTED) { | ||
30572 | + | ||
30573 | + /* Boot the device (i.e. upload the firmware) */ | ||
30574 | + acxusb_boot(usbdev, is_tnetw1450, &radio_type); | ||
30575 | + | ||
30576 | + /* DWL-120+ will first boot the firmware, | ||
30577 | + * then later have a *separate* probe() run | ||
30578 | + * since its USB ID will have changed after | ||
30579 | + * firmware boot! | ||
30580 | + * Since the first probe() run has no | ||
30581 | + * other purpose than booting the firmware, | ||
30582 | + * simply return immediately. | ||
30583 | + */ | ||
30584 | + log(L_INIT, "finished booting, returning from probe()\n"); | ||
30585 | + result = OK; /* success */ | ||
30586 | + goto end; | ||
30587 | + } | ||
30588 | + else | ||
30589 | + /* device not unbooted, but invalid USB ID!? */ | ||
30590 | + if (usbdev->descriptor.idProduct != ACX100_PRODUCT_ID_BOOTED) | ||
30591 | + goto end_nodev; | ||
30592 | + } | ||
30593 | + | ||
30594 | +/* Ok, so it's our device and it has already booted */ | ||
30595 | + | ||
30596 | + /* Allocate memory for a network device */ | ||
30597 | + | ||
30598 | + ndev = alloc_netdev(sizeof(*adev), "wlan%d", dummy_netdev_init); | ||
30599 | + /* (NB: memsets to 0 entire area) */ | ||
30600 | + if (!ndev) { | ||
30601 | + msg = "acx: no memory for netdev\n"; | ||
30602 | + goto end_nomem; | ||
30603 | + } | ||
30604 | + | ||
30605 | + /* Register the callbacks for the network device functions */ | ||
30606 | + | ||
30607 | + ether_setup(ndev); | ||
30608 | + ndev->open = &acxusb_e_open; | ||
30609 | + ndev->stop = &acxusb_e_close; | ||
30610 | + ndev->hard_start_xmit = (void *)&acx_i_start_xmit; | ||
30611 | + ndev->get_stats = (void *)&acx_e_get_stats; | ||
30612 | +#if IW_HANDLER_VERSION <= 5 | ||
30613 | + ndev->get_wireless_stats = (void *)&acx_e_get_wireless_stats; | ||
30614 | +#endif | ||
30615 | + ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; | ||
30616 | + ndev->set_multicast_list = (void *)&acxusb_i_set_rx_mode; | ||
30617 | +#ifdef HAVE_TX_TIMEOUT | ||
30618 | + ndev->tx_timeout = &acxusb_i_tx_timeout; | ||
30619 | + ndev->watchdog_timeo = 4 * HZ; | ||
30620 | +#endif | ||
30621 | + ndev->change_mtu = &acx_e_change_mtu; | ||
30622 | + SET_MODULE_OWNER(ndev); | ||
30623 | + | ||
30624 | + /* Setup private driver context */ | ||
30625 | + | ||
30626 | + adev = ndev2adev(ndev); | ||
30627 | + adev->ndev = ndev; | ||
30628 | + | ||
30629 | + adev->dev_type = DEVTYPE_USB; | ||
30630 | + adev->radio_type = radio_type; | ||
30631 | + if (is_tnetw1450) { | ||
30632 | + /* well, actually it's a TNETW1450, but since it | ||
30633 | + * seems to be sufficiently similar to TNETW1130, | ||
30634 | + * I don't want to change large amounts of code now */ | ||
30635 | + adev->chip_type = CHIPTYPE_ACX111; | ||
30636 | + } else { | ||
30637 | + adev->chip_type = CHIPTYPE_ACX100; | ||
30638 | + } | ||
30639 | + | ||
30640 | + adev->usbdev = usbdev; | ||
30641 | + spin_lock_init(&adev->lock); /* initial state: unlocked */ | ||
30642 | + sema_init(&adev->sem, 1); /* initial state: 1 (upped) */ | ||
30643 | + | ||
30644 | + /* Check that this is really the hardware we know about. | ||
30645 | + ** If not sure, at least notify the user that he | ||
30646 | + ** may be in trouble... | ||
30647 | + */ | ||
30648 | + numconfigs = (int)usbdev->descriptor.bNumConfigurations; | ||
30649 | + if (numconfigs != 1) | ||
30650 | + printk("acx: number of configurations is %d, " | ||
30651 | + "this driver only knows how to handle 1, " | ||
30652 | + "be prepared for surprises\n", numconfigs); | ||
30653 | + | ||
30654 | + config = &usbdev->config->desc; | ||
30655 | + numfaces = config->bNumInterfaces; | ||
30656 | + if (numfaces != 1) | ||
30657 | + printk("acx: number of interfaces is %d, " | ||
30658 | + "this driver only knows how to handle 1, " | ||
30659 | + "be prepared for surprises\n", numfaces); | ||
30660 | + | ||
30661 | + ifdesc = &intf->altsetting->desc; | ||
30662 | + numep = ifdesc->bNumEndpoints; | ||
30663 | + log(L_DEBUG, "# of endpoints: %d\n", numep); | ||
30664 | + | ||
30665 | + if (is_tnetw1450) { | ||
30666 | + adev->bulkoutep = 1; | ||
30667 | + adev->bulkinep = 2; | ||
30668 | + } else { | ||
30669 | + /* obtain information about the endpoint | ||
30670 | + ** addresses, begin with some default values | ||
30671 | + */ | ||
30672 | + adev->bulkoutep = 1; | ||
30673 | + adev->bulkinep = 1; | ||
30674 | + for (i = 0; i < numep; i++) { | ||
30675 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) | ||
30676 | + ep = usbdev->ep_in[i]; | ||
30677 | + if (!ep) | ||
30678 | + continue; | ||
30679 | + epdesc = &ep->desc; | ||
30680 | +#else | ||
30681 | + epdesc = usb_epnum_to_ep_desc(usbdev, i); | ||
30682 | + if (!epdesc) | ||
30683 | + continue; | ||
30684 | +#endif | ||
30685 | + if (epdesc->bmAttributes & USB_ENDPOINT_XFER_BULK) { | ||
30686 | + if (epdesc->bEndpointAddress & 0x80) | ||
30687 | + adev->bulkinep = epdesc->bEndpointAddress & 0xF; | ||
30688 | + else | ||
30689 | + adev->bulkoutep = epdesc->bEndpointAddress & 0xF; | ||
30690 | + } | ||
30691 | + } | ||
30692 | + } | ||
30693 | + log(L_DEBUG, "bulkout ep: 0x%X\n", adev->bulkoutep); | ||
30694 | + log(L_DEBUG, "bulkin ep: 0x%X\n", adev->bulkinep); | ||
30695 | + | ||
30696 | + /* already done by memset: adev->rxtruncsize = 0; */ | ||
30697 | + log(L_DEBUG, "TXBUFSIZE=%d RXBUFSIZE=%d\n", | ||
30698 | + (int) TXBUFSIZE, (int) RXBUFSIZE); | ||
30699 | + | ||
30700 | + /* Allocate the RX/TX containers. */ | ||
30701 | + adev->usb_tx = kmalloc(sizeof(usb_tx_t) * ACX_TX_URB_CNT, GFP_KERNEL); | ||
30702 | + if (!adev->usb_tx) { | ||
30703 | + msg = "acx: no memory for tx container"; | ||
30704 | + goto end_nomem; | ||
30705 | + } | ||
30706 | + adev->usb_rx = kmalloc(sizeof(usb_rx_t) * ACX_RX_URB_CNT, GFP_KERNEL); | ||
30707 | + if (!adev->usb_rx) { | ||
30708 | + msg = "acx: no memory for rx container"; | ||
30709 | + goto end_nomem; | ||
30710 | + } | ||
30711 | + | ||
30712 | + /* Setup URBs for bulk-in/out messages */ | ||
30713 | + for (i = 0; i < ACX_RX_URB_CNT; i++) { | ||
30714 | + adev->usb_rx[i].urb = usb_alloc_urb(0, GFP_KERNEL); | ||
30715 | + if (!adev->usb_rx[i].urb) { | ||
30716 | + msg = "acx: no memory for input URB\n"; | ||
30717 | + goto end_nomem; | ||
30718 | + } | ||
30719 | + adev->usb_rx[i].urb->status = 0; | ||
30720 | + adev->usb_rx[i].adev = adev; | ||
30721 | + adev->usb_rx[i].busy = 0; | ||
30722 | + } | ||
30723 | + | ||
30724 | + for (i = 0; i< ACX_TX_URB_CNT; i++) { | ||
30725 | + adev->usb_tx[i].urb = usb_alloc_urb(0, GFP_KERNEL); | ||
30726 | + if (!adev->usb_tx[i].urb) { | ||
30727 | + msg = "acx: no memory for output URB\n"; | ||
30728 | + goto end_nomem; | ||
30729 | + } | ||
30730 | + adev->usb_tx[i].urb->status = 0; | ||
30731 | + adev->usb_tx[i].adev = adev; | ||
30732 | + adev->usb_tx[i].busy = 0; | ||
30733 | + } | ||
30734 | + adev->tx_free = ACX_TX_URB_CNT; | ||
30735 | + | ||
30736 | + usb_set_intfdata(intf, adev); | ||
30737 | + SET_NETDEV_DEV(ndev, &intf->dev); | ||
30738 | + | ||
30739 | + /* TODO: move all of fw cmds to open()? But then we won't know our MAC addr | ||
30740 | + until ifup (it's available via reading ACX1xx_IE_DOT11_STATION_ID)... */ | ||
30741 | + | ||
30742 | + /* put acx out of sleep mode and initialize it */ | ||
30743 | + acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0); | ||
30744 | + | ||
30745 | + result = acx_s_init_mac(adev); | ||
30746 | + if (result) | ||
30747 | + goto end; | ||
30748 | + | ||
30749 | + /* TODO: see similar code in pci.c */ | ||
30750 | + acxusb_s_read_eeprom_version(adev); | ||
30751 | + acxusb_s_fill_configoption(adev); | ||
30752 | + acx_s_set_defaults(adev); | ||
30753 | + acx_s_get_firmware_version(adev); | ||
30754 | + acx_display_hardware_details(adev); | ||
30755 | + | ||
30756 | + /* Register the network device */ | ||
30757 | + log(L_INIT, "registering network device\n"); | ||
30758 | + result = register_netdev(ndev); | ||
30759 | + if (result) { | ||
30760 | + msg = "acx: failed to register USB network device " | ||
30761 | + "(error %d)\n"; | ||
30762 | + goto end_nomem; | ||
30763 | + } | ||
30764 | + | ||
30765 | + acx_proc_register_entries(ndev); | ||
30766 | + | ||
30767 | + acx_stop_queue(ndev, "on probe"); | ||
30768 | + acx_carrier_off(ndev, "on probe"); | ||
30769 | + | ||
30770 | + printk("acx: USB module " ACX_RELEASE " loaded successfully\n"); | ||
30771 | + | ||
30772 | +#if CMD_DISCOVERY | ||
30773 | + great_inquisitor(adev); | ||
30774 | +#endif | ||
30775 | + | ||
30776 | + /* Everything went OK, we are happy now */ | ||
30777 | + result = OK; | ||
30778 | + goto end; | ||
30779 | + | ||
30780 | +end_nomem: | ||
30781 | + printk(msg, result); | ||
30782 | + | ||
30783 | + if (ndev) { | ||
30784 | + if (adev->usb_rx) { | ||
30785 | + for (i = 0; i < ACX_RX_URB_CNT; i++) | ||
30786 | + usb_free_urb(adev->usb_rx[i].urb); | ||
30787 | + kfree(adev->usb_rx); | ||
30788 | + } | ||
30789 | + if (adev->usb_tx) { | ||
30790 | + for (i = 0; i < ACX_TX_URB_CNT; i++) | ||
30791 | + usb_free_urb(adev->usb_tx[i].urb); | ||
30792 | + kfree(adev->usb_tx); | ||
30793 | + } | ||
30794 | + free_netdev(ndev); | ||
30795 | + } | ||
30796 | + | ||
30797 | + result = -ENOMEM; | ||
30798 | + goto end; | ||
30799 | + | ||
30800 | +end_nodev: | ||
30801 | + /* no device we could handle, return error. */ | ||
30802 | + result = -EIO; | ||
30803 | + | ||
30804 | +end: | ||
30805 | + FN_EXIT1(result); | ||
30806 | + return result; | ||
30807 | +} | ||
30808 | + | ||
30809 | + | ||
30810 | +/*********************************************************************** | ||
30811 | +** acxusb_e_disconnect() | ||
30812 | +** | ||
30813 | +** This function is invoked whenever the user pulls the plug from the USB | ||
30814 | +** device or the module is removed from the kernel. In these cases, the | ||
30815 | +** network devices have to be taken down and all allocated memory has | ||
30816 | +** to be freed. | ||
30817 | +*/ | ||
30818 | +static void | ||
30819 | +acxusb_e_disconnect(struct usb_interface *intf) | ||
30820 | +{ | ||
30821 | + acx_device_t *adev = usb_get_intfdata(intf); | ||
30822 | + unsigned long flags; | ||
30823 | + int i; | ||
30824 | + | ||
30825 | + FN_ENTER; | ||
30826 | + | ||
30827 | + /* No WLAN device... no sense */ | ||
30828 | + if (!adev) | ||
30829 | + goto end; | ||
30830 | + | ||
30831 | + /* Unregister network device | ||
30832 | + * | ||
30833 | + * If the interface is up, unregister_netdev() will take | ||
30834 | + * care of calling our close() function, which takes | ||
30835 | + * care of unlinking the urbs, sending the device to | ||
30836 | + * sleep, etc... | ||
30837 | + * This can't be called with sem or lock held because | ||
30838 | + * _close() will try to grab it as well if it's called, | ||
30839 | + * deadlocking the machine. | ||
30840 | + */ | ||
30841 | + unregister_netdev(adev->ndev); | ||
30842 | + | ||
30843 | + acx_sem_lock(adev); | ||
30844 | + acx_lock(adev, flags); | ||
30845 | + /* This device exists no more */ | ||
30846 | + usb_set_intfdata(intf, NULL); | ||
30847 | + acx_proc_unregister_entries(adev->ndev); | ||
30848 | + | ||
30849 | + /* | ||
30850 | + * Here we only free them. _close() took care of | ||
30851 | + * unlinking them. | ||
30852 | + */ | ||
30853 | + for (i = 0; i < ACX_RX_URB_CNT; ++i) { | ||
30854 | + usb_free_urb(adev->usb_rx[i].urb); | ||
30855 | + } | ||
30856 | + for (i = 0; i< ACX_TX_URB_CNT; ++i) { | ||
30857 | + usb_free_urb(adev->usb_tx[i].urb); | ||
30858 | + } | ||
30859 | + | ||
30860 | + /* Freeing containers */ | ||
30861 | + kfree(adev->usb_rx); | ||
30862 | + kfree(adev->usb_tx); | ||
30863 | + | ||
30864 | + acx_unlock(adev, flags); | ||
30865 | + acx_sem_unlock(adev); | ||
30866 | + | ||
30867 | + free_netdev(adev->ndev); | ||
30868 | +end: | ||
30869 | + FN_EXIT0; | ||
30870 | +} | ||
30871 | + | ||
30872 | + | ||
30873 | +/*********************************************************************** | ||
30874 | +** acxusb_e_open() | ||
30875 | +** This function is called when the user sets up the network interface. | ||
30876 | +** It initializes a management timer, sets up the USB card and starts | ||
30877 | +** the network tx queue and USB receive. | ||
30878 | +*/ | ||
30879 | +static int | ||
30880 | +acxusb_e_open(struct net_device *ndev) | ||
30881 | +{ | ||
30882 | + acx_device_t *adev = ndev2adev(ndev); | ||
30883 | + unsigned long flags; | ||
30884 | + int i; | ||
30885 | + | ||
30886 | + FN_ENTER; | ||
30887 | + | ||
30888 | + acx_sem_lock(adev); | ||
30889 | + | ||
30890 | + /* put the ACX100 out of sleep mode */ | ||
30891 | + acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0); | ||
30892 | + | ||
30893 | + acx_init_task_scheduler(adev); | ||
30894 | + | ||
30895 | + init_timer(&adev->mgmt_timer); | ||
30896 | + adev->mgmt_timer.function = acx_i_timer; | ||
30897 | + adev->mgmt_timer.data = (unsigned long)adev; | ||
30898 | + | ||
30899 | + /* acx_s_start needs it */ | ||
30900 | + SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
30901 | + acx_s_start(adev); | ||
30902 | + | ||
30903 | + /* don't acx_start_queue() here, we need to associate first */ | ||
30904 | + | ||
30905 | + acx_lock(adev, flags); | ||
30906 | + for (i = 0; i < ACX_RX_URB_CNT; i++) { | ||
30907 | + adev->usb_rx[i].urb->status = 0; | ||
30908 | + } | ||
30909 | + | ||
30910 | + acxusb_l_poll_rx(adev, &adev->usb_rx[0]); | ||
30911 | + | ||
30912 | + acx_unlock(adev, flags); | ||
30913 | + | ||
30914 | + acx_sem_unlock(adev); | ||
30915 | + | ||
30916 | + FN_EXIT0; | ||
30917 | + return 0; | ||
30918 | +} | ||
30919 | + | ||
30920 | + | ||
30921 | +/*********************************************************************** | ||
30922 | +** acxusb_e_close() | ||
30923 | +** | ||
30924 | +** This function stops the network functionality of the interface (invoked | ||
30925 | +** when the user calls ifconfig <wlan> down). The tx queue is halted and | ||
30926 | +** the device is marked as down. In case there were any pending USB bulk | ||
30927 | +** transfers, these are unlinked (asynchronously). The module in-use count | ||
30928 | +** is also decreased in this function. | ||
30929 | +*/ | ||
30930 | +static int | ||
30931 | +acxusb_e_close(struct net_device *ndev) | ||
30932 | +{ | ||
30933 | + acx_device_t *adev = ndev2adev(ndev); | ||
30934 | + unsigned long flags; | ||
30935 | + int i; | ||
30936 | + | ||
30937 | + FN_ENTER; | ||
30938 | + | ||
30939 | +#ifdef WE_STILL_DONT_CARE_ABOUT_IT | ||
30940 | + /* Transmit a disassociate frame */ | ||
30941 | + lock | ||
30942 | + acx_l_transmit_disassoc(adev, &client); | ||
30943 | + unlock | ||
30944 | +#endif | ||
30945 | + | ||
30946 | + acx_sem_lock(adev); | ||
30947 | + | ||
30948 | + CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); | ||
30949 | + | ||
30950 | +/* Code below is remarkably similar to acxpci_s_down(). Maybe we can merge them? */ | ||
30951 | + | ||
30952 | + /* Make sure we don't get any more rx requests */ | ||
30953 | + acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0); | ||
30954 | + acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0); | ||
30955 | + | ||
30956 | + /* | ||
30957 | + * We must do FLUSH *without* holding sem to avoid a deadlock. | ||
30958 | + * See pci.c:acxpci_s_down() for deails. | ||
30959 | + */ | ||
30960 | + acx_sem_unlock(adev); | ||
30961 | + FLUSH_SCHEDULED_WORK(); | ||
30962 | + acx_sem_lock(adev); | ||
30963 | + | ||
30964 | + /* Power down the device */ | ||
30965 | + acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0); | ||
30966 | + | ||
30967 | + /* Stop the transmit queue, mark the device as DOWN */ | ||
30968 | + acx_lock(adev, flags); | ||
30969 | + acx_stop_queue(ndev, "on ifdown"); | ||
30970 | + acx_set_status(adev, ACX_STATUS_0_STOPPED); | ||
30971 | + /* stop pending rx/tx urb transfers */ | ||
30972 | + for (i = 0; i < ACX_TX_URB_CNT; i++) { | ||
30973 | + acxusb_unlink_urb(adev->usb_tx[i].urb); | ||
30974 | + adev->usb_tx[i].busy = 0; | ||
30975 | + } | ||
30976 | + for (i = 0; i < ACX_RX_URB_CNT; i++) { | ||
30977 | + acxusb_unlink_urb(adev->usb_rx[i].urb); | ||
30978 | + adev->usb_rx[i].busy = 0; | ||
30979 | + } | ||
30980 | + adev->tx_free = ACX_TX_URB_CNT; | ||
30981 | + acx_unlock(adev, flags); | ||
30982 | + | ||
30983 | + /* Must do this outside of lock */ | ||
30984 | + del_timer_sync(&adev->mgmt_timer); | ||
30985 | + | ||
30986 | + acx_sem_unlock(adev); | ||
30987 | + | ||
30988 | + FN_EXIT0; | ||
30989 | + return 0; | ||
30990 | +} | ||
30991 | + | ||
30992 | + | ||
30993 | +/*********************************************************************** | ||
30994 | +** acxusb_l_poll_rx | ||
30995 | +** This function (re)initiates a bulk-in USB transfer on a given urb | ||
30996 | +*/ | ||
30997 | +static void | ||
30998 | +acxusb_l_poll_rx(acx_device_t *adev, usb_rx_t* rx) | ||
30999 | +{ | ||
31000 | + struct usb_device *usbdev; | ||
31001 | + struct urb *rxurb; | ||
31002 | + int errcode, rxnum; | ||
31003 | + unsigned int inpipe; | ||
31004 | + | ||
31005 | + FN_ENTER; | ||
31006 | + | ||
31007 | + rxurb = rx->urb; | ||
31008 | + usbdev = adev->usbdev; | ||
31009 | + | ||
31010 | + rxnum = rx - adev->usb_rx; | ||
31011 | + | ||
31012 | + inpipe = usb_rcvbulkpipe(usbdev, adev->bulkinep); | ||
31013 | + if (unlikely(rxurb->status == -EINPROGRESS)) { | ||
31014 | + printk(KERN_ERR "acx: error, rx triggered while rx urb in progress\n"); | ||
31015 | + /* FIXME: this is nasty, receive is being cancelled by this code | ||
31016 | + * on the other hand, this should not happen anyway... | ||
31017 | + */ | ||
31018 | + usb_unlink_urb(rxurb); | ||
31019 | + } else | ||
31020 | + if (unlikely(rxurb->status == -ECONNRESET)) { | ||
31021 | + log(L_USBRXTX, "acx_usb: _poll_rx: connection reset\n"); | ||
31022 | + goto end; | ||
31023 | + } | ||
31024 | + rxurb->actual_length = 0; | ||
31025 | + usb_fill_bulk_urb(rxurb, usbdev, inpipe, | ||
31026 | + &rx->bulkin, /* dataptr */ | ||
31027 | + RXBUFSIZE, /* size */ | ||
31028 | + acxusb_i_complete_rx, /* handler */ | ||
31029 | + rx /* handler param */ | ||
31030 | + ); | ||
31031 | + rxurb->transfer_flags = URB_ASYNC_UNLINK; | ||
31032 | + | ||
31033 | + /* ATOMIC: we may be called from complete_rx() usb callback */ | ||
31034 | + errcode = usb_submit_urb(rxurb, GFP_ATOMIC); | ||
31035 | + /* FIXME: evaluate the error code! */ | ||
31036 | + log(L_USBRXTX, "SUBMIT RX (%d) inpipe=0x%X size=%d errcode=%d\n", | ||
31037 | + rxnum, inpipe, (int) RXBUFSIZE, errcode); | ||
31038 | +end: | ||
31039 | + FN_EXIT0; | ||
31040 | +} | ||
31041 | + | ||
31042 | + | ||
31043 | +/*********************************************************************** | ||
31044 | +** acxusb_i_complete_rx() | ||
31045 | +** Inputs: | ||
31046 | +** urb -> pointer to USB request block | ||
31047 | +** regs -> pointer to register-buffer for syscalls (see asm/ptrace.h) | ||
31048 | +** | ||
31049 | +** This function is invoked by USB subsystem whenever a bulk receive | ||
31050 | +** request returns. | ||
31051 | +** The received data is then committed to the network stack and the next | ||
31052 | +** USB receive is triggered. | ||
31053 | +*/ | ||
31054 | +static void | ||
31055 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
31056 | +acxusb_i_complete_rx(struct urb *urb) | ||
31057 | +#else | ||
31058 | +acxusb_i_complete_rx(struct urb *urb, struct pt_regs *regs) | ||
31059 | +#endif | ||
31060 | +{ | ||
31061 | + acx_device_t *adev; | ||
31062 | + rxbuffer_t *ptr; | ||
31063 | + rxbuffer_t *inbuf; | ||
31064 | + usb_rx_t *rx; | ||
31065 | + unsigned long flags; | ||
31066 | + int size, remsize, packetsize, rxnum; | ||
31067 | + | ||
31068 | + FN_ENTER; | ||
31069 | + | ||
31070 | + BUG_ON(!urb->context); | ||
31071 | + | ||
31072 | + rx = (usb_rx_t *)urb->context; | ||
31073 | + adev = rx->adev; | ||
31074 | + | ||
31075 | + acx_lock(adev, flags); | ||
31076 | + | ||
31077 | + /* | ||
31078 | + * Happens on disconnect or close. Don't play with the urb. | ||
31079 | + * Don't resubmit it. It will get unlinked by close() | ||
31080 | + */ | ||
31081 | + if (unlikely(!(adev->dev_state_mask & ACX_STATE_IFACE_UP))) { | ||
31082 | + log(L_USBRXTX, "rx: device is down, not doing anything\n"); | ||
31083 | + goto end_unlock; | ||
31084 | + } | ||
31085 | + | ||
31086 | + inbuf = &rx->bulkin; | ||
31087 | + size = urb->actual_length; | ||
31088 | + remsize = size; | ||
31089 | + rxnum = rx - adev->usb_rx; | ||
31090 | + | ||
31091 | + log(L_USBRXTX, "RETURN RX (%d) status=%d size=%d\n", | ||
31092 | + rxnum, urb->status, size); | ||
31093 | + | ||
31094 | + /* Send the URB that's waiting. */ | ||
31095 | + log(L_USBRXTX, "rxnum=%d, sending=%d\n", rxnum, rxnum^1); | ||
31096 | + acxusb_l_poll_rx(adev, &adev->usb_rx[rxnum^1]); | ||
31097 | + | ||
31098 | + if (unlikely(size > sizeof(rxbuffer_t))) | ||
31099 | + printk("acx_usb: rx too large: %d, please report\n", size); | ||
31100 | + | ||
31101 | + /* check if the transfer was aborted */ | ||
31102 | + switch (urb->status) { | ||
31103 | + case 0: /* No error */ | ||
31104 | + break; | ||
31105 | + case -EOVERFLOW: | ||
31106 | + printk(KERN_ERR "acx: rx data overrun\n"); | ||
31107 | + adev->rxtruncsize = 0; /* Not valid anymore. */ | ||
31108 | + goto end_unlock; | ||
31109 | + case -ECONNRESET: | ||
31110 | + adev->rxtruncsize = 0; | ||
31111 | + goto end_unlock; | ||
31112 | + case -ESHUTDOWN: /* rmmod */ | ||
31113 | + adev->rxtruncsize = 0; | ||
31114 | + goto end_unlock; | ||
31115 | + default: | ||
31116 | + adev->rxtruncsize = 0; | ||
31117 | + adev->stats.rx_errors++; | ||
31118 | + printk("acx: rx error (urb status=%d)\n", urb->status); | ||
31119 | + goto end_unlock; | ||
31120 | + } | ||
31121 | + | ||
31122 | + if (unlikely(!size)) | ||
31123 | + printk("acx: warning, encountered zerolength rx packet\n"); | ||
31124 | + | ||
31125 | + if (urb->transfer_buffer != inbuf) | ||
31126 | + goto end_unlock; | ||
31127 | + | ||
31128 | + /* check if previous frame was truncated | ||
31129 | + ** FIXME: this code can only handle truncation | ||
31130 | + ** of consecutive packets! | ||
31131 | + */ | ||
31132 | + ptr = inbuf; | ||
31133 | + if (adev->rxtruncsize) { | ||
31134 | + int tail_size; | ||
31135 | + | ||
31136 | + ptr = &adev->rxtruncbuf; | ||
31137 | + packetsize = RXBUF_BYTES_USED(ptr); | ||
31138 | + if (acx_debug & L_USBRXTX) { | ||
31139 | + printk("handling truncated frame (truncsize=%d size=%d " | ||
31140 | + "packetsize(from trunc)=%d)\n", | ||
31141 | + adev->rxtruncsize, size, packetsize); | ||
31142 | + acx_dump_bytes(ptr, RXBUF_HDRSIZE); | ||
31143 | + acx_dump_bytes(inbuf, RXBUF_HDRSIZE); | ||
31144 | + } | ||
31145 | + | ||
31146 | + /* bytes needed for rxtruncbuf completion: */ | ||
31147 | + tail_size = packetsize - adev->rxtruncsize; | ||
31148 | + | ||
31149 | + if (size < tail_size) { | ||
31150 | + /* there is not enough data to complete this packet, | ||
31151 | + ** simply append the stuff to the truncation buffer | ||
31152 | + */ | ||
31153 | + memcpy(((char *)ptr) + adev->rxtruncsize, inbuf, size); | ||
31154 | + adev->rxtruncsize += size; | ||
31155 | + remsize = 0; | ||
31156 | + } else { | ||
31157 | + /* ok, this data completes the previously | ||
31158 | + ** truncated packet. copy it into a descriptor | ||
31159 | + ** and give it to the rest of the stack */ | ||
31160 | + | ||
31161 | + /* append tail to previously truncated part | ||
31162 | + ** NB: adev->rxtruncbuf (pointed to by ptr) can't | ||
31163 | + ** overflow because this is already checked before | ||
31164 | + ** truncation buffer was filled. See below, | ||
31165 | + ** "if (packetsize > sizeof(rxbuffer_t))..." code */ | ||
31166 | + memcpy(((char *)ptr) + adev->rxtruncsize, inbuf, tail_size); | ||
31167 | + | ||
31168 | + if (acx_debug & L_USBRXTX) { | ||
31169 | + printk("full trailing packet + 12 bytes:\n"); | ||
31170 | + acx_dump_bytes(inbuf, tail_size + RXBUF_HDRSIZE); | ||
31171 | + } | ||
31172 | + acx_l_process_rxbuf(adev, ptr); | ||
31173 | + adev->rxtruncsize = 0; | ||
31174 | + ptr = (rxbuffer_t *) (((char *)inbuf) + tail_size); | ||
31175 | + remsize -= tail_size; | ||
31176 | + } | ||
31177 | + log(L_USBRXTX, "post-merge size=%d remsize=%d\n", | ||
31178 | + size, remsize); | ||
31179 | + } | ||
31180 | + | ||
31181 | + /* size = USB data block size | ||
31182 | + ** remsize = unprocessed USB bytes left | ||
31183 | + ** ptr = current pos in USB data block | ||
31184 | + */ | ||
31185 | + while (remsize) { | ||
31186 | + if (remsize < RXBUF_HDRSIZE) { | ||
31187 | + printk("acx: truncated rx header (%d bytes)!\n", | ||
31188 | + remsize); | ||
31189 | + if (ACX_DEBUG) | ||
31190 | + acx_dump_bytes(ptr, remsize); | ||
31191 | + break; | ||
31192 | + } | ||
31193 | + | ||
31194 | + packetsize = RXBUF_BYTES_USED(ptr); | ||
31195 | + log(L_USBRXTX, "packet with packetsize=%d\n", packetsize); | ||
31196 | + | ||
31197 | + if (RXBUF_IS_TXSTAT(ptr)) { | ||
31198 | + /* do rate handling */ | ||
31199 | + usb_txstatus_t *stat = (void*)ptr; | ||
31200 | + u16 client_no = (u16)stat->hostdata; | ||
31201 | + | ||
31202 | + log(L_USBRXTX, "tx: stat: mac_cnt_rcvd:%04X " | ||
31203 | + "queue_index:%02X mac_status:%02X hostdata:%08X " | ||
31204 | + "rate:%u ack_failures:%02X rts_failures:%02X " | ||
31205 | + "rts_ok:%02X\n", | ||
31206 | + stat->mac_cnt_rcvd, | ||
31207 | + stat->queue_index, stat->mac_status, stat->hostdata, | ||
31208 | + stat->rate, stat->ack_failures, stat->rts_failures, | ||
31209 | + stat->rts_ok); | ||
31210 | + | ||
31211 | + if (adev->rate_auto && client_no < VEC_SIZE(adev->sta_list)) { | ||
31212 | + client_t *clt = &adev->sta_list[client_no]; | ||
31213 | + u16 cur = stat->hostdata >> 16; | ||
31214 | + | ||
31215 | + if (clt && clt->rate_cur == cur) { | ||
31216 | + acx_l_handle_txrate_auto(adev, clt, | ||
31217 | + cur, /* intended rate */ | ||
31218 | + stat->rate, 0, /* actually used rate */ | ||
31219 | + stat->mac_status, /* error? */ | ||
31220 | + ACX_TX_URB_CNT - adev->tx_free); | ||
31221 | + } | ||
31222 | + } | ||
31223 | + goto next; | ||
31224 | + } | ||
31225 | + | ||
31226 | + if (packetsize > sizeof(rxbuffer_t)) { | ||
31227 | + printk("acx: packet exceeds max wlan " | ||
31228 | + "frame size (%d > %d). size=%d\n", | ||
31229 | + packetsize, (int) sizeof(rxbuffer_t), size); | ||
31230 | + if (ACX_DEBUG) | ||
31231 | + acx_dump_bytes(ptr, 16); | ||
31232 | + /* FIXME: put some real error-handling in here! */ | ||
31233 | + break; | ||
31234 | + } | ||
31235 | + | ||
31236 | + if (packetsize > remsize) { | ||
31237 | + /* frame truncation handling */ | ||
31238 | + if (acx_debug & L_USBRXTX) { | ||
31239 | + printk("need to truncate packet, " | ||
31240 | + "packetsize=%d remsize=%d " | ||
31241 | + "size=%d bytes:", | ||
31242 | + packetsize, remsize, size); | ||
31243 | + acx_dump_bytes(ptr, RXBUF_HDRSIZE); | ||
31244 | + } | ||
31245 | + memcpy(&adev->rxtruncbuf, ptr, remsize); | ||
31246 | + adev->rxtruncsize = remsize; | ||
31247 | + break; | ||
31248 | + } | ||
31249 | + | ||
31250 | + /* packetsize <= remsize */ | ||
31251 | + /* now handle the received data */ | ||
31252 | + acx_l_process_rxbuf(adev, ptr); | ||
31253 | +next: | ||
31254 | + ptr = (rxbuffer_t *)(((char *)ptr) + packetsize); | ||
31255 | + remsize -= packetsize; | ||
31256 | + if ((acx_debug & L_USBRXTX) && remsize) { | ||
31257 | + printk("more than one packet in buffer, " | ||
31258 | + "second packet hdr:"); | ||
31259 | + acx_dump_bytes(ptr, RXBUF_HDRSIZE); | ||
31260 | + } | ||
31261 | + } | ||
31262 | + | ||
31263 | +end_unlock: | ||
31264 | + acx_unlock(adev, flags); | ||
31265 | +/* end: */ | ||
31266 | + FN_EXIT0; | ||
31267 | +} | ||
31268 | + | ||
31269 | + | ||
31270 | +/*********************************************************************** | ||
31271 | +** acxusb_i_complete_tx() | ||
31272 | +** Inputs: | ||
31273 | +** urb -> pointer to USB request block | ||
31274 | +** regs -> pointer to register-buffer for syscalls (see asm/ptrace.h) | ||
31275 | +** | ||
31276 | +** This function is invoked upon termination of a USB transfer. | ||
31277 | +*/ | ||
31278 | +static void | ||
31279 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
31280 | +acxusb_i_complete_tx(struct urb *urb) | ||
31281 | +#else | ||
31282 | +acxusb_i_complete_tx(struct urb *urb, struct pt_regs *regs) | ||
31283 | +#endif | ||
31284 | +{ | ||
31285 | + acx_device_t *adev; | ||
31286 | + usb_tx_t *tx; | ||
31287 | + unsigned long flags; | ||
31288 | + int txnum; | ||
31289 | + | ||
31290 | + FN_ENTER; | ||
31291 | + | ||
31292 | + BUG_ON(!urb->context); | ||
31293 | + | ||
31294 | + tx = (usb_tx_t *)urb->context; | ||
31295 | + adev = tx->adev; | ||
31296 | + | ||
31297 | + txnum = tx - adev->usb_tx; | ||
31298 | + | ||
31299 | + acx_lock(adev, flags); | ||
31300 | + | ||
31301 | + /* | ||
31302 | + * If the iface isn't up, we don't have any right | ||
31303 | + * to play with them. The urb may get unlinked. | ||
31304 | + */ | ||
31305 | + if (unlikely(!(adev->dev_state_mask & ACX_STATE_IFACE_UP))) { | ||
31306 | + log(L_USBRXTX, "tx: device is down, not doing anything\n"); | ||
31307 | + goto end_unlock; | ||
31308 | + } | ||
31309 | + | ||
31310 | + log(L_USBRXTX, "RETURN TX (%d): status=%d size=%d\n", | ||
31311 | + txnum, urb->status, urb->actual_length); | ||
31312 | + | ||
31313 | + /* handle USB transfer errors */ | ||
31314 | + switch (urb->status) { | ||
31315 | + case 0: /* No error */ | ||
31316 | + break; | ||
31317 | + case -ESHUTDOWN: | ||
31318 | + goto end_unlock; | ||
31319 | + break; | ||
31320 | + case -ECONNRESET: | ||
31321 | + goto end_unlock; | ||
31322 | + break; | ||
31323 | + /* FIXME: real error-handling code here please */ | ||
31324 | + default: | ||
31325 | + printk(KERN_ERR "acx: tx error, urb status=%d\n", urb->status); | ||
31326 | + /* FIXME: real error-handling code here please */ | ||
31327 | + } | ||
31328 | + | ||
31329 | + /* free the URB and check for more data */ | ||
31330 | + tx->busy = 0; | ||
31331 | + adev->tx_free++; | ||
31332 | + if ((adev->tx_free >= TX_START_QUEUE) | ||
31333 | + && (adev->status == ACX_STATUS_4_ASSOCIATED) | ||
31334 | + && (acx_queue_stopped(adev->ndev)) | ||
31335 | + ) { | ||
31336 | + log(L_BUF, "tx: wake queue (%u free txbufs)\n", | ||
31337 | + adev->tx_free); | ||
31338 | + acx_wake_queue(adev->ndev, NULL); | ||
31339 | + } | ||
31340 | + | ||
31341 | +end_unlock: | ||
31342 | + acx_unlock(adev, flags); | ||
31343 | +/* end: */ | ||
31344 | + FN_EXIT0; | ||
31345 | +} | ||
31346 | + | ||
31347 | + | ||
31348 | +/*************************************************************** | ||
31349 | +** acxusb_l_alloc_tx | ||
31350 | +** Actually returns a usb_tx_t* ptr | ||
31351 | +*/ | ||
31352 | +tx_t* | ||
31353 | +acxusb_l_alloc_tx(acx_device_t *adev) | ||
31354 | +{ | ||
31355 | + usb_tx_t *tx; | ||
31356 | + unsigned head; | ||
31357 | + | ||
31358 | + FN_ENTER; | ||
31359 | + | ||
31360 | + head = adev->tx_head; | ||
31361 | + do { | ||
31362 | + head = (head + 1) % ACX_TX_URB_CNT; | ||
31363 | + if (!adev->usb_tx[head].busy) { | ||
31364 | + log(L_USBRXTX, "allocated tx %d\n", head); | ||
31365 | + tx = &adev->usb_tx[head]; | ||
31366 | + tx->busy = 1; | ||
31367 | + adev->tx_free--; | ||
31368 | + /* Keep a few free descs between head and tail of tx ring. | ||
31369 | + ** It is not absolutely needed, just feels safer */ | ||
31370 | + if (adev->tx_free < TX_STOP_QUEUE) { | ||
31371 | + log(L_BUF, "tx: stop queue " | ||
31372 | + "(%u free txbufs)\n", adev->tx_free); | ||
31373 | + acx_stop_queue(adev->ndev, NULL); | ||
31374 | + } | ||
31375 | + goto end; | ||
31376 | + } | ||
31377 | + } while (likely(head!=adev->tx_head)); | ||
31378 | + tx = NULL; | ||
31379 | + printk_ratelimited("acx: tx buffers full\n"); | ||
31380 | +end: | ||
31381 | + adev->tx_head = head; | ||
31382 | + FN_EXIT0; | ||
31383 | + return (tx_t*)tx; | ||
31384 | +} | ||
31385 | + | ||
31386 | + | ||
31387 | +/*************************************************************** | ||
31388 | +** Used if alloc_tx()'ed buffer needs to be cancelled without doing tx | ||
31389 | +*/ | ||
31390 | +void | ||
31391 | +acxusb_l_dealloc_tx(tx_t *tx_opaque) | ||
31392 | +{ | ||
31393 | + usb_tx_t* tx = (usb_tx_t*)tx_opaque; | ||
31394 | + tx->busy = 0; | ||
31395 | +} | ||
31396 | + | ||
31397 | + | ||
31398 | +/*************************************************************** | ||
31399 | +*/ | ||
31400 | +void* | ||
31401 | +acxusb_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque) | ||
31402 | +{ | ||
31403 | + usb_tx_t* tx = (usb_tx_t*)tx_opaque; | ||
31404 | + return &tx->bulkout.data; | ||
31405 | +} | ||
31406 | + | ||
31407 | + | ||
31408 | +/*************************************************************** | ||
31409 | +** acxusb_l_tx_data | ||
31410 | +** | ||
31411 | +** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). | ||
31412 | +** Can be called from acx_i_start_xmit (data frames from net core). | ||
31413 | +*/ | ||
31414 | +void | ||
31415 | +acxusb_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int wlanpkt_len) | ||
31416 | +{ | ||
31417 | + struct usb_device *usbdev; | ||
31418 | + struct urb* txurb; | ||
31419 | + usb_tx_t* tx; | ||
31420 | + usb_txbuffer_t* txbuf; | ||
31421 | + client_t *clt; | ||
31422 | + wlan_hdr_t* whdr; | ||
31423 | + unsigned int outpipe; | ||
31424 | + int ucode, txnum; | ||
31425 | + | ||
31426 | + FN_ENTER; | ||
31427 | + | ||
31428 | + tx = ((usb_tx_t *)tx_opaque); | ||
31429 | + txurb = tx->urb; | ||
31430 | + txbuf = &tx->bulkout; | ||
31431 | + whdr = (wlan_hdr_t *)txbuf->data; | ||
31432 | + txnum = tx - adev->usb_tx; | ||
31433 | + | ||
31434 | + log(L_DEBUG, "using buf#%d free=%d len=%d\n", | ||
31435 | + txnum, adev->tx_free, wlanpkt_len); | ||
31436 | + | ||
31437 | + switch (adev->mode) { | ||
31438 | + case ACX_MODE_0_ADHOC: | ||
31439 | + case ACX_MODE_3_AP: | ||
31440 | + clt = acx_l_sta_list_get(adev, whdr->a1); | ||
31441 | + break; | ||
31442 | + case ACX_MODE_2_STA: | ||
31443 | + clt = adev->ap_client; | ||
31444 | + break; | ||
31445 | + default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ | ||
31446 | + clt = NULL; | ||
31447 | + break; | ||
31448 | + } | ||
31449 | + | ||
31450 | + if (unlikely(clt && !clt->rate_cur)) { | ||
31451 | + printk("acx: driver bug! bad ratemask\n"); | ||
31452 | + goto end; | ||
31453 | + } | ||
31454 | + | ||
31455 | + /* fill the USB transfer header */ | ||
31456 | + txbuf->desc = cpu_to_le16(USB_TXBUF_TXDESC); | ||
31457 | + txbuf->mpdu_len = cpu_to_le16(wlanpkt_len); | ||
31458 | + txbuf->queue_index = 1; | ||
31459 | + if (clt) { | ||
31460 | + txbuf->rate = clt->rate_100; | ||
31461 | + txbuf->hostdata = (clt - adev->sta_list) | (clt->rate_cur << 16); | ||
31462 | + } else { | ||
31463 | + txbuf->rate = adev->rate_bcast100; | ||
31464 | + txbuf->hostdata = ((u16)-1) | (adev->rate_bcast << 16); | ||
31465 | + } | ||
31466 | + txbuf->ctrl1 = DESC_CTL_FIRSTFRAG; | ||
31467 | + if (1 == adev->preamble_cur) | ||
31468 | + SET_BIT(txbuf->ctrl1, DESC_CTL_SHORT_PREAMBLE); | ||
31469 | + txbuf->ctrl2 = 0; | ||
31470 | + txbuf->data_len = cpu_to_le16(wlanpkt_len); | ||
31471 | + | ||
31472 | + if (unlikely(acx_debug & L_DATA)) { | ||
31473 | + printk("dump of bulk out urb:\n"); | ||
31474 | + acx_dump_bytes(txbuf, wlanpkt_len + USB_TXBUF_HDRSIZE); | ||
31475 | + } | ||
31476 | + | ||
31477 | + if (unlikely(txurb->status == -EINPROGRESS)) { | ||
31478 | + printk("acx: trying to submit tx urb while already in progress\n"); | ||
31479 | + } | ||
31480 | + | ||
31481 | + /* now schedule the USB transfer */ | ||
31482 | + usbdev = adev->usbdev; | ||
31483 | + outpipe = usb_sndbulkpipe(usbdev, adev->bulkoutep); | ||
31484 | + | ||
31485 | + usb_fill_bulk_urb(txurb, usbdev, outpipe, | ||
31486 | + txbuf, /* dataptr */ | ||
31487 | + wlanpkt_len + USB_TXBUF_HDRSIZE, /* size */ | ||
31488 | + acxusb_i_complete_tx, /* handler */ | ||
31489 | + tx /* handler param */ | ||
31490 | + ); | ||
31491 | + | ||
31492 | + txurb->transfer_flags = URB_ASYNC_UNLINK|URB_ZERO_PACKET; | ||
31493 | + ucode = usb_submit_urb(txurb, GFP_ATOMIC); | ||
31494 | + log(L_USBRXTX, "SUBMIT TX (%d): outpipe=0x%X buf=%p txsize=%d " | ||
31495 | + "rate=%u errcode=%d\n", txnum, outpipe, txbuf, | ||
31496 | + wlanpkt_len + USB_TXBUF_HDRSIZE, txbuf->rate, ucode); | ||
31497 | + | ||
31498 | + if (unlikely(ucode)) { | ||
31499 | + printk(KERN_ERR "acx: submit_urb() error=%d txsize=%d\n", | ||
31500 | + ucode, wlanpkt_len + USB_TXBUF_HDRSIZE); | ||
31501 | + | ||
31502 | + /* on error, just mark the frame as done and update | ||
31503 | + ** the statistics | ||
31504 | + */ | ||
31505 | + adev->stats.tx_errors++; | ||
31506 | + tx->busy = 0; | ||
31507 | + adev->tx_free++; | ||
31508 | + /* needed? if (adev->tx_free > TX_START_QUEUE) acx_wake_queue(...) */ | ||
31509 | + } | ||
31510 | +end: | ||
31511 | + FN_EXIT0; | ||
31512 | +} | ||
31513 | + | ||
31514 | + | ||
31515 | +/*********************************************************************** | ||
31516 | +*/ | ||
31517 | +static void | ||
31518 | +acxusb_i_set_rx_mode(struct net_device *ndev) | ||
31519 | +{ | ||
31520 | +} | ||
31521 | + | ||
31522 | + | ||
31523 | +/*********************************************************************** | ||
31524 | +*/ | ||
31525 | +#ifdef HAVE_TX_TIMEOUT | ||
31526 | +static void | ||
31527 | +acxusb_i_tx_timeout(struct net_device *ndev) | ||
31528 | +{ | ||
31529 | + acx_device_t *adev = ndev2adev(ndev); | ||
31530 | + unsigned long flags; | ||
31531 | + int i; | ||
31532 | + | ||
31533 | + FN_ENTER; | ||
31534 | + | ||
31535 | + acx_lock(adev, flags); | ||
31536 | + /* unlink the URBs */ | ||
31537 | + for (i = 0; i < ACX_TX_URB_CNT; i++) { | ||
31538 | + acxusb_unlink_urb(adev->usb_tx[i].urb); | ||
31539 | + adev->usb_tx[i].busy = 0; | ||
31540 | + } | ||
31541 | + adev->tx_free = ACX_TX_URB_CNT; | ||
31542 | + /* TODO: stats update */ | ||
31543 | + acx_unlock(adev, flags); | ||
31544 | + | ||
31545 | + FN_EXIT0; | ||
31546 | +} | ||
31547 | +#endif | ||
31548 | + | ||
31549 | + | ||
31550 | +/*********************************************************************** | ||
31551 | +** init_module() | ||
31552 | +** | ||
31553 | +** This function is invoked upon loading of the kernel module. | ||
31554 | +** It registers itself at the kernel's USB subsystem. | ||
31555 | +** | ||
31556 | +** Returns: Errorcode on failure, 0 on success | ||
31557 | +*/ | ||
31558 | +int __init | ||
31559 | +acxusb_e_init_module(void) | ||
31560 | +{ | ||
31561 | + log(L_INIT, "USB module " ACX_RELEASE " initialized, " | ||
31562 | + "probing for devices...\n"); | ||
31563 | + return usb_register(&acxusb_driver); | ||
31564 | +} | ||
31565 | + | ||
31566 | + | ||
31567 | + | ||
31568 | +/*********************************************************************** | ||
31569 | +** cleanup_module() | ||
31570 | +** | ||
31571 | +** This function is invoked as last step of the module unloading. It simply | ||
31572 | +** deregisters this module at the kernel's USB subsystem. | ||
31573 | +*/ | ||
31574 | +void __exit | ||
31575 | +acxusb_e_cleanup_module() | ||
31576 | +{ | ||
31577 | + usb_deregister(&acxusb_driver); | ||
31578 | +} | ||
31579 | + | ||
31580 | + | ||
31581 | +/*********************************************************************** | ||
31582 | +** DEBUG STUFF | ||
31583 | +*/ | ||
31584 | +#if ACX_DEBUG | ||
31585 | + | ||
31586 | +#ifdef UNUSED | ||
31587 | +static void | ||
31588 | +dump_device(struct usb_device *usbdev) | ||
31589 | +{ | ||
31590 | + int i; | ||
31591 | + struct usb_config_descriptor *cd; | ||
31592 | + | ||
31593 | + printk("acx device dump:\n"); | ||
31594 | + printk(" devnum: %d\n", usbdev->devnum); | ||
31595 | + printk(" speed: %d\n", usbdev->speed); | ||
31596 | + printk(" tt: 0x%X\n", (unsigned int)(usbdev->tt)); | ||
31597 | + printk(" ttport: %d\n", (unsigned int)(usbdev->ttport)); | ||
31598 | + printk(" toggle[0]: 0x%X toggle[1]: 0x%X\n", (unsigned int)(usbdev->toggle[0]), (unsigned int)(usbdev->toggle[1])); | ||
31599 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) | ||
31600 | + /* This saw a change after 2.6.10 */ | ||
31601 | + printk(" ep_in wMaxPacketSize: "); | ||
31602 | + for (i = 0; i < 16; ++i) | ||
31603 | + if (usbdev->ep_in[i] != NULL) | ||
31604 | + printk("%d:%d ", i, usbdev->ep_in[i]->desc.wMaxPacketSize); | ||
31605 | + printk("\n"); | ||
31606 | + printk(" ep_out wMaxPacketSize: "); | ||
31607 | + for (i = 0; i < VEC_SIZE(usbdev->ep_out); ++i) | ||
31608 | + if (usbdev->ep_out[i] != NULL) | ||
31609 | + printk("%d:%d ", i, usbdev->ep_out[i]->desc.wMaxPacketSize); | ||
31610 | + printk("\n"); | ||
31611 | +#else | ||
31612 | + printk(" epmaxpacketin: "); | ||
31613 | + for (i = 0; i < 16; i++) | ||
31614 | + printk("%d ", usbdev->epmaxpacketin[i]); | ||
31615 | + printk("\n"); | ||
31616 | + printk(" epmaxpacketout: "); | ||
31617 | + for (i = 0; i < 16; i++) | ||
31618 | + printk("%d ", usbdev->epmaxpacketout[i]); | ||
31619 | + printk("\n"); | ||
31620 | +#endif | ||
31621 | + printk(" parent: 0x%X\n", (unsigned int)usbdev->parent); | ||
31622 | + printk(" bus: 0x%X\n", (unsigned int)usbdev->bus); | ||
31623 | +#ifdef NO_DATATYPE | ||
31624 | + printk(" configs: "); | ||
31625 | + for (i = 0; i < usbdev->descriptor.bNumConfigurations; i++) | ||
31626 | + printk("0x%X ", usbdev->config[i]); | ||
31627 | + printk("\n"); | ||
31628 | +#endif | ||
31629 | + printk(" actconfig: %p\n", usbdev->actconfig); | ||
31630 | + dump_device_descriptor(&usbdev->descriptor); | ||
31631 | + | ||
31632 | + cd = &usbdev->config->desc; | ||
31633 | + dump_config_descriptor(cd); | ||
31634 | +} | ||
31635 | + | ||
31636 | + | ||
31637 | +/*********************************************************************** | ||
31638 | +*/ | ||
31639 | +static void | ||
31640 | +dump_config_descriptor(struct usb_config_descriptor *cd) | ||
31641 | +{ | ||
31642 | + printk("Configuration Descriptor:\n"); | ||
31643 | + if (!cd) { | ||
31644 | + printk("NULL\n"); | ||
31645 | + return; | ||
31646 | + } | ||
31647 | + printk(" bLength: %d (0x%X)\n", cd->bLength, cd->bLength); | ||
31648 | + printk(" bDescriptorType: %d (0x%X)\n", cd->bDescriptorType, cd->bDescriptorType); | ||
31649 | + printk(" bNumInterfaces: %d (0x%X)\n", cd->bNumInterfaces, cd->bNumInterfaces); | ||
31650 | + printk(" bConfigurationValue: %d (0x%X)\n", cd->bConfigurationValue, cd->bConfigurationValue); | ||
31651 | + printk(" iConfiguration: %d (0x%X)\n", cd->iConfiguration, cd->iConfiguration); | ||
31652 | + printk(" bmAttributes: %d (0x%X)\n", cd->bmAttributes, cd->bmAttributes); | ||
31653 | + /* printk(" MaxPower: %d (0x%X)\n", cd->bMaxPower, cd->bMaxPower); */ | ||
31654 | +} | ||
31655 | + | ||
31656 | + | ||
31657 | +static void | ||
31658 | +dump_device_descriptor(struct usb_device_descriptor *dd) | ||
31659 | +{ | ||
31660 | + printk("Device Descriptor:\n"); | ||
31661 | + if (!dd) { | ||
31662 | + printk("NULL\n"); | ||
31663 | + return; | ||
31664 | + } | ||
31665 | + printk(" bLength: %d (0x%X)\n", dd->bLength, dd->bLength); | ||
31666 | + printk(" bDescriptortype: %d (0x%X)\n", dd->bDescriptorType, dd->bDescriptorType); | ||
31667 | + printk(" bcdUSB: %d (0x%X)\n", dd->bcdUSB, dd->bcdUSB); | ||
31668 | + printk(" bDeviceClass: %d (0x%X)\n", dd->bDeviceClass, dd->bDeviceClass); | ||
31669 | + printk(" bDeviceSubClass: %d (0x%X)\n", dd->bDeviceSubClass, dd->bDeviceSubClass); | ||
31670 | + printk(" bDeviceProtocol: %d (0x%X)\n", dd->bDeviceProtocol, dd->bDeviceProtocol); | ||
31671 | + printk(" bMaxPacketSize0: %d (0x%X)\n", dd->bMaxPacketSize0, dd->bMaxPacketSize0); | ||
31672 | + printk(" idVendor: %d (0x%X)\n", dd->idVendor, dd->idVendor); | ||
31673 | + printk(" idProduct: %d (0x%X)\n", dd->idProduct, dd->idProduct); | ||
31674 | + printk(" bcdDevice: %d (0x%X)\n", dd->bcdDevice, dd->bcdDevice); | ||
31675 | + printk(" iManufacturer: %d (0x%X)\n", dd->iManufacturer, dd->iManufacturer); | ||
31676 | + printk(" iProduct: %d (0x%X)\n", dd->iProduct, dd->iProduct); | ||
31677 | + printk(" iSerialNumber: %d (0x%X)\n", dd->iSerialNumber, dd->iSerialNumber); | ||
31678 | + printk(" bNumConfigurations: %d (0x%X)\n", dd->bNumConfigurations, dd->bNumConfigurations); | ||
31679 | +} | ||
31680 | +#endif /* UNUSED */ | ||
31681 | + | ||
31682 | +#endif /* ACX_DEBUG */ | ||
31683 | Index: linux-2.6.22/drivers/net/wireless/acx/wlan.c | ||
31684 | =================================================================== | ||
31685 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
31686 | +++ linux-2.6.22/drivers/net/wireless/acx/wlan.c 2007-08-23 18:34:19.000000000 +0200 | ||
31687 | @@ -0,0 +1,424 @@ | ||
31688 | +/*********************************************************************** | ||
31689 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
31690 | +** | ||
31691 | +** The contents of this file are subject to the Mozilla Public | ||
31692 | +** License Version 1.1 (the "License"); you may not use this file | ||
31693 | +** except in compliance with the License. You may obtain a copy of | ||
31694 | +** the License at http://www.mozilla.org/MPL/ | ||
31695 | +** | ||
31696 | +** Software distributed under the License is distributed on an "AS | ||
31697 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
31698 | +** implied. See the License for the specific language governing | ||
31699 | +** rights and limitations under the License. | ||
31700 | +** | ||
31701 | +** Alternatively, the contents of this file may be used under the | ||
31702 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
31703 | +** case the provisions of the GPL are applicable instead of the | ||
31704 | +** above. If you wish to allow the use of your version of this file | ||
31705 | +** only under the terms of the GPL and not to allow others to use | ||
31706 | +** your version of this file under the MPL, indicate your decision | ||
31707 | +** by deleting the provisions above and replace them with the notice | ||
31708 | +** and other provisions required by the GPL. If you do not delete | ||
31709 | +** the provisions above, a recipient may use your version of this | ||
31710 | +** file under either the MPL or the GPL. | ||
31711 | +** --------------------------------------------------------------------- | ||
31712 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
31713 | +** made directly to: | ||
31714 | +** | ||
31715 | +** acx100-users@lists.sf.net | ||
31716 | +** http://acx100.sf.net | ||
31717 | +** --------------------------------------------------------------------- | ||
31718 | +*/ | ||
31719 | + | ||
31720 | +/*********************************************************************** | ||
31721 | +** This code is based on elements which are | ||
31722 | +** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. | ||
31723 | +** info@linux-wlan.com | ||
31724 | +** http://www.linux-wlan.com | ||
31725 | +*/ | ||
31726 | + | ||
31727 | +#include <linux/version.h> | ||
31728 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) | ||
31729 | +#include <linux/config.h> | ||
31730 | +#endif | ||
31731 | +#include <linux/types.h> | ||
31732 | +#include <linux/if_arp.h> | ||
31733 | +#include <linux/wireless.h> | ||
31734 | +#include <net/iw_handler.h> | ||
31735 | + | ||
31736 | +#include "acx.h" | ||
31737 | + | ||
31738 | + | ||
31739 | +/*********************************************************************** | ||
31740 | +*/ | ||
31741 | +#define LOG_BAD_EID(hdr,len,ie_ptr) acx_log_bad_eid(hdr, len, ((wlan_ie_t*)ie_ptr)) | ||
31742 | + | ||
31743 | +#define IE_EID(ie_ptr) (((wlan_ie_t*)(ie_ptr))->eid) | ||
31744 | +#define IE_LEN(ie_ptr) (((wlan_ie_t*)(ie_ptr))->len) | ||
31745 | +#define OFFSET(hdr,off) (WLAN_HDR_A3_DATAP(hdr) + (off)) | ||
31746 | + | ||
31747 | + | ||
31748 | +/*********************************************************************** | ||
31749 | +** wlan_mgmt_decode_XXX | ||
31750 | +** | ||
31751 | +** Given a complete frame in f->hdr, sets the pointers in f to | ||
31752 | +** the areas that correspond to the parts of the frame. | ||
31753 | +** | ||
31754 | +** Assumptions: | ||
31755 | +** 1) f->len and f->hdr are already set | ||
31756 | +** 2) f->len is the length of the MAC header + data, the FCS | ||
31757 | +** is NOT included | ||
31758 | +** 3) all members except len and hdr are zero | ||
31759 | +** Arguments: | ||
31760 | +** f frame structure | ||
31761 | +** | ||
31762 | +** Returns: | ||
31763 | +** nothing | ||
31764 | +** | ||
31765 | +** Side effects: | ||
31766 | +** frame structure members are pointing at their | ||
31767 | +** respective portions of the frame buffer. | ||
31768 | +*/ | ||
31769 | +void | ||
31770 | +wlan_mgmt_decode_beacon(wlan_fr_beacon_t * f) | ||
31771 | +{ | ||
31772 | + u8 *ie_ptr; | ||
31773 | + u8 *end = (u8*)f->hdr + f->len; | ||
31774 | + | ||
31775 | + f->type = WLAN_FSTYPE_BEACON; | ||
31776 | + | ||
31777 | + /*-- Fixed Fields ----*/ | ||
31778 | + f->ts = (u64 *) OFFSET(f->hdr, WLAN_BEACON_OFF_TS); | ||
31779 | + f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_BCN_INT); | ||
31780 | + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_CAPINFO); | ||
31781 | + | ||
31782 | + /*-- Information elements */ | ||
31783 | + ie_ptr = OFFSET(f->hdr, WLAN_BEACON_OFF_SSID); | ||
31784 | + while (ie_ptr < end) { | ||
31785 | + switch (IE_EID(ie_ptr)) { | ||
31786 | + case WLAN_EID_SSID: | ||
31787 | + f->ssid = (wlan_ie_ssid_t *) ie_ptr; | ||
31788 | + break; | ||
31789 | + case WLAN_EID_SUPP_RATES: | ||
31790 | + f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; | ||
31791 | + break; | ||
31792 | + case WLAN_EID_EXT_RATES: | ||
31793 | + f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; | ||
31794 | + break; | ||
31795 | + case WLAN_EID_FH_PARMS: | ||
31796 | + f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr; | ||
31797 | + break; | ||
31798 | + case WLAN_EID_DS_PARMS: | ||
31799 | + f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr; | ||
31800 | + break; | ||
31801 | + case WLAN_EID_CF_PARMS: | ||
31802 | + f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr; | ||
31803 | + break; | ||
31804 | + case WLAN_EID_IBSS_PARMS: | ||
31805 | + f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr; | ||
31806 | + break; | ||
31807 | + case WLAN_EID_TIM: | ||
31808 | + f->tim = (wlan_ie_tim_t *) ie_ptr; | ||
31809 | + break; | ||
31810 | + case WLAN_EID_ERP_INFO: | ||
31811 | + f->erp = (wlan_ie_erp_t *) ie_ptr; | ||
31812 | + break; | ||
31813 | + | ||
31814 | + case WLAN_EID_COUNTRY: | ||
31815 | + /* was seen: 07 06 47 42 20 01 0D 14 */ | ||
31816 | + case WLAN_EID_PWR_CONSTRAINT: | ||
31817 | + /* was seen by Ashwin Mansinghka <ashwin_man@yahoo.com> from | ||
31818 | + Atheros-based PCI card in AP mode using madwifi drivers: */ | ||
31819 | + /* 20 01 00 */ | ||
31820 | + case WLAN_EID_NONERP: | ||
31821 | + /* was seen from WRT54GS with OpenWrt: 2F 01 07 */ | ||
31822 | + case WLAN_EID_UNKNOWN128: | ||
31823 | + /* was seen by Jacek Jablonski <conexion2000@gmail.com> from Orinoco AP */ | ||
31824 | + /* 80 06 00 60 1D 2C 3B 00 */ | ||
31825 | + case WLAN_EID_UNKNOWN133: | ||
31826 | + /* was seen by David Bronaugh <dbronaugh@linuxboxen.org> from ???? */ | ||
31827 | + /* 85 1E 00 00 84 12 07 00 FF 00 11 00 61 70 63 31 */ | ||
31828 | + /* 63 73 72 30 34 32 00 00 00 00 00 00 00 00 00 25 */ | ||
31829 | + case WLAN_EID_UNKNOWN223: | ||
31830 | + /* was seen by Carlos Martin <carlosmn@gmail.com> from ???? */ | ||
31831 | + /* DF 20 01 1E 04 00 00 00 06 63 09 02 FF 0F 30 30 */ | ||
31832 | + /* 30 42 36 42 33 34 30 39 46 31 00 00 00 00 00 00 00 00 */ | ||
31833 | + case WLAN_EID_GENERIC: | ||
31834 | + /* WPA: hostap code: | ||
31835 | + if (pos[1] >= 4 && | ||
31836 | + pos[2] == 0x00 && pos[3] == 0x50 && | ||
31837 | + pos[4] == 0xf2 && pos[5] == 1) { | ||
31838 | + wpa = pos; | ||
31839 | + wpa_len = pos[1] + 2; | ||
31840 | + } | ||
31841 | + TI x4 mode: seen DD 04 08 00 28 00 | ||
31842 | + (08 00 28 is TI's OUI) | ||
31843 | + last byte is probably 0/1 - disabled/enabled | ||
31844 | + */ | ||
31845 | + case WLAN_EID_RSN: | ||
31846 | + /* hostap does something with it: | ||
31847 | + rsn = pos; | ||
31848 | + rsn_len = pos[1] + 2; | ||
31849 | + */ | ||
31850 | + break; | ||
31851 | + | ||
31852 | + default: | ||
31853 | + LOG_BAD_EID(f->hdr, f->len, ie_ptr); | ||
31854 | + break; | ||
31855 | + } | ||
31856 | + ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); | ||
31857 | + } | ||
31858 | +} | ||
31859 | + | ||
31860 | + | ||
31861 | +#ifdef UNUSED | ||
31862 | +void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t * f) | ||
31863 | +{ | ||
31864 | + f->type = WLAN_FSTYPE_ATIM; | ||
31865 | + /*-- Fixed Fields ----*/ | ||
31866 | + /*-- Information elements */ | ||
31867 | +} | ||
31868 | +#endif /* UNUSED */ | ||
31869 | + | ||
31870 | +void | ||
31871 | +wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t * f) | ||
31872 | +{ | ||
31873 | + f->type = WLAN_FSTYPE_DISASSOC; | ||
31874 | + | ||
31875 | + /*-- Fixed Fields ----*/ | ||
31876 | + f->reason = (u16 *) OFFSET(f->hdr, WLAN_DISASSOC_OFF_REASON); | ||
31877 | + | ||
31878 | + /*-- Information elements */ | ||
31879 | +} | ||
31880 | + | ||
31881 | + | ||
31882 | +void | ||
31883 | +wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t * f) | ||
31884 | +{ | ||
31885 | + u8 *ie_ptr; | ||
31886 | + u8 *end = (u8*)f->hdr + f->len; | ||
31887 | + | ||
31888 | + | ||
31889 | + f->type = WLAN_FSTYPE_ASSOCREQ; | ||
31890 | + | ||
31891 | + /*-- Fixed Fields ----*/ | ||
31892 | + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_CAP_INFO); | ||
31893 | + f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_LISTEN_INT); | ||
31894 | + | ||
31895 | + /*-- Information elements */ | ||
31896 | + ie_ptr = OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_SSID); | ||
31897 | + while (ie_ptr < end) { | ||
31898 | + switch (IE_EID(ie_ptr)) { | ||
31899 | + case WLAN_EID_SSID: | ||
31900 | + f->ssid = (wlan_ie_ssid_t *) ie_ptr; | ||
31901 | + break; | ||
31902 | + case WLAN_EID_SUPP_RATES: | ||
31903 | + f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; | ||
31904 | + break; | ||
31905 | + case WLAN_EID_EXT_RATES: | ||
31906 | + f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; | ||
31907 | + break; | ||
31908 | + default: | ||
31909 | + LOG_BAD_EID(f->hdr, f->len, ie_ptr); | ||
31910 | + break; | ||
31911 | + } | ||
31912 | + ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); | ||
31913 | + } | ||
31914 | +} | ||
31915 | + | ||
31916 | + | ||
31917 | +void | ||
31918 | +wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t * f) | ||
31919 | +{ | ||
31920 | + f->type = WLAN_FSTYPE_ASSOCRESP; | ||
31921 | + | ||
31922 | + /*-- Fixed Fields ----*/ | ||
31923 | + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_CAP_INFO); | ||
31924 | + f->status = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_STATUS); | ||
31925 | + f->aid = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_AID); | ||
31926 | + | ||
31927 | + /*-- Information elements */ | ||
31928 | + f->supp_rates = (wlan_ie_supp_rates_t *) | ||
31929 | + OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_SUPP_RATES); | ||
31930 | +} | ||
31931 | + | ||
31932 | + | ||
31933 | +#ifdef UNUSED | ||
31934 | +void | ||
31935 | +wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t * f) | ||
31936 | +{ | ||
31937 | + u8 *ie_ptr; | ||
31938 | + u8 *end = (u8*)f->hdr + f->len; | ||
31939 | + | ||
31940 | + f->type = WLAN_FSTYPE_REASSOCREQ; | ||
31941 | + | ||
31942 | + /*-- Fixed Fields ----*/ | ||
31943 | + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CAP_INFO); | ||
31944 | + f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_LISTEN_INT); | ||
31945 | + f->curr_ap = (u8 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CURR_AP); | ||
31946 | + | ||
31947 | + /*-- Information elements */ | ||
31948 | + ie_ptr = OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_SSID); | ||
31949 | + while (ie_ptr < end) { | ||
31950 | + switch (IE_EID(ie_ptr)) { | ||
31951 | + case WLAN_EID_SSID: | ||
31952 | + f->ssid = (wlan_ie_ssid_t *) ie_ptr; | ||
31953 | + break; | ||
31954 | + case WLAN_EID_SUPP_RATES: | ||
31955 | + f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; | ||
31956 | + break; | ||
31957 | + case WLAN_EID_EXT_RATES: | ||
31958 | + f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; | ||
31959 | + break; | ||
31960 | + default: | ||
31961 | + LOG_BAD_EID(f->hdr, f->len, ie_ptr); | ||
31962 | + break; | ||
31963 | + } | ||
31964 | + ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); | ||
31965 | + } | ||
31966 | +} | ||
31967 | + | ||
31968 | + | ||
31969 | +void | ||
31970 | +wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t * f) | ||
31971 | +{ | ||
31972 | + f->type = WLAN_FSTYPE_REASSOCRESP; | ||
31973 | + | ||
31974 | + /*-- Fixed Fields ----*/ | ||
31975 | + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_CAP_INFO); | ||
31976 | + f->status = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_STATUS); | ||
31977 | + f->aid = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_AID); | ||
31978 | + | ||
31979 | + /*-- Information elements */ | ||
31980 | + f->supp_rates = (wlan_ie_supp_rates_t *) | ||
31981 | + OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_SUPP_RATES); | ||
31982 | +} | ||
31983 | + | ||
31984 | + | ||
31985 | +void | ||
31986 | +wlan_mgmt_decode_probereq(wlan_fr_probereq_t * f) | ||
31987 | +{ | ||
31988 | + u8 *ie_ptr; | ||
31989 | + u8 *end = (u8*)f->hdr + f->len; | ||
31990 | + | ||
31991 | + f->type = WLAN_FSTYPE_PROBEREQ; | ||
31992 | + | ||
31993 | + /*-- Fixed Fields ----*/ | ||
31994 | + | ||
31995 | + /*-- Information elements */ | ||
31996 | + ie_ptr = OFFSET(f->hdr, WLAN_PROBEREQ_OFF_SSID); | ||
31997 | + while (ie_ptr < end) { | ||
31998 | + switch (IE_EID(ie_ptr)) { | ||
31999 | + case WLAN_EID_SSID: | ||
32000 | + f->ssid = (wlan_ie_ssid_t *) ie_ptr; | ||
32001 | + break; | ||
32002 | + case WLAN_EID_SUPP_RATES: | ||
32003 | + f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; | ||
32004 | + break; | ||
32005 | + case WLAN_EID_EXT_RATES: | ||
32006 | + f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; | ||
32007 | + break; | ||
32008 | + default: | ||
32009 | + LOG_BAD_EID(f->hdr, f->len, ie_ptr); | ||
32010 | + break; | ||
32011 | + } | ||
32012 | + ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); | ||
32013 | + } | ||
32014 | +} | ||
32015 | +#endif /* UNUSED */ | ||
32016 | + | ||
32017 | + | ||
32018 | +/* TODO: decoding of beacon and proberesp can be merged (similar structure) */ | ||
32019 | +void | ||
32020 | +wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t * f) | ||
32021 | +{ | ||
32022 | + u8 *ie_ptr; | ||
32023 | + u8 *end = (u8*)f->hdr + f->len; | ||
32024 | + | ||
32025 | + f->type = WLAN_FSTYPE_PROBERESP; | ||
32026 | + | ||
32027 | + /*-- Fixed Fields ----*/ | ||
32028 | + f->ts = (u64 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_TS); | ||
32029 | + f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_BCN_INT); | ||
32030 | + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_CAP_INFO); | ||
32031 | + | ||
32032 | + /*-- Information elements */ | ||
32033 | + ie_ptr = OFFSET(f->hdr, WLAN_PROBERESP_OFF_SSID); | ||
32034 | + while (ie_ptr < end) { | ||
32035 | + switch (IE_EID(ie_ptr)) { | ||
32036 | + case WLAN_EID_SSID: | ||
32037 | + f->ssid = (wlan_ie_ssid_t *) ie_ptr; | ||
32038 | + break; | ||
32039 | + case WLAN_EID_SUPP_RATES: | ||
32040 | + f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; | ||
32041 | + break; | ||
32042 | + case WLAN_EID_EXT_RATES: | ||
32043 | + f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; | ||
32044 | + break; | ||
32045 | + case WLAN_EID_FH_PARMS: | ||
32046 | + f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr; | ||
32047 | + break; | ||
32048 | + case WLAN_EID_DS_PARMS: | ||
32049 | + f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr; | ||
32050 | + break; | ||
32051 | + case WLAN_EID_CF_PARMS: | ||
32052 | + f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr; | ||
32053 | + break; | ||
32054 | + case WLAN_EID_IBSS_PARMS: | ||
32055 | + f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr; | ||
32056 | + break; | ||
32057 | +#ifdef DONT_DO_IT_ADD_REAL_HANDLING_INSTEAD | ||
32058 | + case WLAN_EID_COUNTRY: | ||
32059 | + break; | ||
32060 | + ... | ||
32061 | +#endif | ||
32062 | +#ifdef SENT_HERE_BY_OPENWRT | ||
32063 | + /* should those be trapped or handled?? */ | ||
32064 | + case WLAN_EID_ERP_INFO: | ||
32065 | + break; | ||
32066 | + case WLAN_EID_NONERP: | ||
32067 | + break; | ||
32068 | + case WLAN_EID_GENERIC: | ||
32069 | + break; | ||
32070 | +#endif | ||
32071 | + default: | ||
32072 | + LOG_BAD_EID(f->hdr, f->len, ie_ptr); | ||
32073 | + break; | ||
32074 | + } | ||
32075 | + | ||
32076 | + ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); | ||
32077 | + } | ||
32078 | +} | ||
32079 | + | ||
32080 | + | ||
32081 | +void | ||
32082 | +wlan_mgmt_decode_authen(wlan_fr_authen_t * f) | ||
32083 | +{ | ||
32084 | + u8 *ie_ptr; | ||
32085 | + u8 *end = (u8*)f->hdr + f->len; | ||
32086 | + | ||
32087 | + f->type = WLAN_FSTYPE_AUTHEN; | ||
32088 | + | ||
32089 | + /*-- Fixed Fields ----*/ | ||
32090 | + f->auth_alg = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_ALG); | ||
32091 | + f->auth_seq = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_SEQ); | ||
32092 | + f->status = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_STATUS); | ||
32093 | + | ||
32094 | + /*-- Information elements */ | ||
32095 | + ie_ptr = OFFSET(f->hdr, WLAN_AUTHEN_OFF_CHALLENGE); | ||
32096 | + if ((ie_ptr < end) && (IE_EID(ie_ptr) == WLAN_EID_CHALLENGE)) { | ||
32097 | + f->challenge = (wlan_ie_challenge_t *) ie_ptr; | ||
32098 | + } | ||
32099 | +} | ||
32100 | + | ||
32101 | + | ||
32102 | +void | ||
32103 | +wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t * f) | ||
32104 | +{ | ||
32105 | + f->type = WLAN_FSTYPE_DEAUTHEN; | ||
32106 | + | ||
32107 | + /*-- Fixed Fields ----*/ | ||
32108 | + f->reason = (u16 *) OFFSET(f->hdr, WLAN_DEAUTHEN_OFF_REASON); | ||
32109 | + | ||
32110 | + /*-- Information elements */ | ||
32111 | +} | ||
32112 | Index: linux-2.6.22/drivers/net/wireless/acx/wlan_compat.h | ||
32113 | =================================================================== | ||
32114 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
32115 | +++ linux-2.6.22/drivers/net/wireless/acx/wlan_compat.h 2007-08-23 18:34:19.000000000 +0200 | ||
32116 | @@ -0,0 +1,260 @@ | ||
32117 | +/*********************************************************************** | ||
32118 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
32119 | +** | ||
32120 | +** The contents of this file are subject to the Mozilla Public | ||
32121 | +** License Version 1.1 (the "License"); you may not use this file | ||
32122 | +** except in compliance with the License. You may obtain a copy of | ||
32123 | +** the License at http://www.mozilla.org/MPL/ | ||
32124 | +** | ||
32125 | +** Software distributed under the License is distributed on an "AS | ||
32126 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
32127 | +** implied. See the License for the specific language governing | ||
32128 | +** rights and limitations under the License. | ||
32129 | +** | ||
32130 | +** Alternatively, the contents of this file may be used under the | ||
32131 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
32132 | +** case the provisions of the GPL are applicable instead of the | ||
32133 | +** above. If you wish to allow the use of your version of this file | ||
32134 | +** only under the terms of the GPL and not to allow others to use | ||
32135 | +** your version of this file under the MPL, indicate your decision | ||
32136 | +** by deleting the provisions above and replace them with the notice | ||
32137 | +** and other provisions required by the GPL. If you do not delete | ||
32138 | +** the provisions above, a recipient may use your version of this | ||
32139 | +** file under either the MPL or the GPL. | ||
32140 | +** --------------------------------------------------------------------- | ||
32141 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
32142 | +** made directly to: | ||
32143 | +** | ||
32144 | +** acx100-users@lists.sf.net | ||
32145 | +** http://acx100.sf.net | ||
32146 | +** --------------------------------------------------------------------- | ||
32147 | +*/ | ||
32148 | + | ||
32149 | +/*********************************************************************** | ||
32150 | +** This code is based on elements which are | ||
32151 | +** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. | ||
32152 | +** info@linux-wlan.com | ||
32153 | +** http://www.linux-wlan.com | ||
32154 | +*/ | ||
32155 | + | ||
32156 | +/*=============================================================*/ | ||
32157 | +/*------ Establish Platform Identity --------------------------*/ | ||
32158 | +/*=============================================================*/ | ||
32159 | +/* Key macros: */ | ||
32160 | +/* WLAN_CPU_FAMILY */ | ||
32161 | +#define WLAN_Ix86 1 | ||
32162 | +#define WLAN_PPC 2 | ||
32163 | +#define WLAN_Ix96 3 | ||
32164 | +#define WLAN_ARM 4 | ||
32165 | +#define WLAN_ALPHA 5 | ||
32166 | +#define WLAN_MIPS 6 | ||
32167 | +#define WLAN_HPPA 7 | ||
32168 | +#define WLAN_SPARC 8 | ||
32169 | +#define WLAN_SH 9 | ||
32170 | +#define WLAN_x86_64 10 | ||
32171 | +/* WLAN_CPU_CORE */ | ||
32172 | +#define WLAN_I386CORE 1 | ||
32173 | +#define WLAN_PPCCORE 2 | ||
32174 | +#define WLAN_I296 3 | ||
32175 | +#define WLAN_ARMCORE 4 | ||
32176 | +#define WLAN_ALPHACORE 5 | ||
32177 | +#define WLAN_MIPSCORE 6 | ||
32178 | +#define WLAN_HPPACORE 7 | ||
32179 | +/* WLAN_CPU_PART */ | ||
32180 | +#define WLAN_I386PART 1 | ||
32181 | +#define WLAN_MPC860 2 | ||
32182 | +#define WLAN_MPC823 3 | ||
32183 | +#define WLAN_I296SA 4 | ||
32184 | +#define WLAN_PPCPART 5 | ||
32185 | +#define WLAN_ARMPART 6 | ||
32186 | +#define WLAN_ALPHAPART 7 | ||
32187 | +#define WLAN_MIPSPART 8 | ||
32188 | +#define WLAN_HPPAPART 9 | ||
32189 | +/* WLAN_SYSARCH */ | ||
32190 | +#define WLAN_PCAT 1 | ||
32191 | +#define WLAN_MBX 2 | ||
32192 | +#define WLAN_RPX 3 | ||
32193 | +#define WLAN_LWARCH 4 | ||
32194 | +#define WLAN_PMAC 5 | ||
32195 | +#define WLAN_SKIFF 6 | ||
32196 | +#define WLAN_BITSY 7 | ||
32197 | +#define WLAN_ALPHAARCH 7 | ||
32198 | +#define WLAN_MIPSARCH 9 | ||
32199 | +#define WLAN_HPPAARCH 10 | ||
32200 | +/* WLAN_HOSTIF (generally set on the command line, not detected) */ | ||
32201 | +#define WLAN_PCMCIA 1 | ||
32202 | +#define WLAN_ISA 2 | ||
32203 | +#define WLAN_PCI 3 | ||
32204 | +#define WLAN_USB 4 | ||
32205 | +#define WLAN_PLX 5 | ||
32206 | + | ||
32207 | +/* Note: the PLX HOSTIF above refers to some vendors implementations for */ | ||
32208 | +/* PCI. It's a PLX chip that is a PCI to PCMCIA adapter, but it */ | ||
32209 | +/* isn't a real PCMCIA host interface adapter providing all the */ | ||
32210 | +/* card&socket services. */ | ||
32211 | + | ||
32212 | +#ifdef __powerpc__ | ||
32213 | +#ifndef __ppc__ | ||
32214 | +#define __ppc__ | ||
32215 | +#endif | ||
32216 | +#endif | ||
32217 | + | ||
32218 | +#if (defined(CONFIG_PPC) || defined(CONFIG_8xx)) | ||
32219 | +#ifndef __ppc__ | ||
32220 | +#define __ppc__ | ||
32221 | +#endif | ||
32222 | +#endif | ||
32223 | + | ||
32224 | +#if defined(__x86_64__) | ||
32225 | + #define WLAN_CPU_FAMILY WLAN_x86_64 | ||
32226 | + #define WLAN_SYSARCH WLAN_PCAT | ||
32227 | +#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) | ||
32228 | + #define WLAN_CPU_FAMILY WLAN_Ix86 | ||
32229 | + #define WLAN_CPU_CORE WLAN_I386CORE | ||
32230 | + #define WLAN_CPU_PART WLAN_I386PART | ||
32231 | + #define WLAN_SYSARCH WLAN_PCAT | ||
32232 | +#elif defined(__ppc__) | ||
32233 | + #define WLAN_CPU_FAMILY WLAN_PPC | ||
32234 | + #define WLAN_CPU_CORE WLAN_PPCCORE | ||
32235 | + #if defined(CONFIG_MBX) | ||
32236 | + #define WLAN_CPU_PART WLAN_MPC860 | ||
32237 | + #define WLAN_SYSARCH WLAN_MBX | ||
32238 | + #elif defined(CONFIG_RPXLITE) | ||
32239 | + #define WLAN_CPU_PART WLAN_MPC823 | ||
32240 | + #define WLAN_SYSARCH WLAN_RPX | ||
32241 | + #elif defined(CONFIG_RPXCLASSIC) | ||
32242 | + #define WLAN_CPU_PART WLAN_MPC860 | ||
32243 | + #define WLAN_SYSARCH WLAN_RPX | ||
32244 | + #else | ||
32245 | + #define WLAN_CPU_PART WLAN_PPCPART | ||
32246 | + #define WLAN_SYSARCH WLAN_PMAC | ||
32247 | + #endif | ||
32248 | +#elif defined(__arm__) | ||
32249 | + #define WLAN_CPU_FAMILY WLAN_ARM | ||
32250 | + #define WLAN_CPU_CORE WLAN_ARMCORE | ||
32251 | + #define WLAN_CPU_PART WLAN_ARM_PART | ||
32252 | + #define WLAN_SYSARCH WLAN_SKIFF | ||
32253 | +#elif defined(__alpha__) | ||
32254 | + #define WLAN_CPU_FAMILY WLAN_ALPHA | ||
32255 | + #define WLAN_CPU_CORE WLAN_ALPHACORE | ||
32256 | + #define WLAN_CPU_PART WLAN_ALPHAPART | ||
32257 | + #define WLAN_SYSARCH WLAN_ALPHAARCH | ||
32258 | +#elif defined(__mips__) | ||
32259 | + #define WLAN_CPU_FAMILY WLAN_MIPS | ||
32260 | + #define WLAN_CPU_CORE WLAN_MIPSCORE | ||
32261 | + #define WLAN_CPU_PART WLAN_MIPSPART | ||
32262 | + #define WLAN_SYSARCH WLAN_MIPSARCH | ||
32263 | +#elif defined(__hppa__) | ||
32264 | + #define WLAN_CPU_FAMILY WLAN_HPPA | ||
32265 | + #define WLAN_CPU_CORE WLAN_HPPACORE | ||
32266 | + #define WLAN_CPU_PART WLAN_HPPAPART | ||
32267 | + #define WLAN_SYSARCH WLAN_HPPAARCH | ||
32268 | +#elif defined(__sparc__) | ||
32269 | + #define WLAN_CPU_FAMILY WLAN_SPARC | ||
32270 | + #define WLAN_SYSARCH WLAN_SPARC | ||
32271 | +#elif defined(__sh__) | ||
32272 | + #define WLAN_CPU_FAMILY WLAN_SH | ||
32273 | + #define WLAN_SYSARCH WLAN_SHARCH | ||
32274 | + #ifndef __LITTLE_ENDIAN__ | ||
32275 | + #define __LITTLE_ENDIAN__ | ||
32276 | + #endif | ||
32277 | +#else | ||
32278 | + #error "No CPU identified!" | ||
32279 | +#endif | ||
32280 | + | ||
32281 | +/* | ||
32282 | + Some big endian machines implicitly do all I/O in little endian mode. | ||
32283 | + | ||
32284 | + In particular: | ||
32285 | + Linux/PPC on PowerMacs (PCI) | ||
32286 | + Arm/Intel Xscale (PCI) | ||
32287 | + | ||
32288 | + This may also affect PLX boards and other BE &| PPC platforms; | ||
32289 | + as new ones are discovered, add them below. | ||
32290 | +*/ | ||
32291 | + | ||
32292 | +#if ((WLAN_SYSARCH == WLAN_SKIFF) || (WLAN_SYSARCH == WLAN_PMAC)) | ||
32293 | +#define REVERSE_ENDIAN | ||
32294 | +#endif | ||
32295 | + | ||
32296 | +/*=============================================================*/ | ||
32297 | +/*------ Hardware Portability Macros --------------------------*/ | ||
32298 | +/*=============================================================*/ | ||
32299 | +#if (WLAN_CPU_FAMILY == WLAN_PPC) | ||
32300 | +#define wlan_inw(a) in_be16((unsigned short *)((a)+_IO_BASE)) | ||
32301 | +#define wlan_inw_le16_to_cpu(a) inw((a)) | ||
32302 | +#define wlan_outw(v,a) out_be16((unsigned short *)((a)+_IO_BASE), (v)) | ||
32303 | +#define wlan_outw_cpu_to_le16(v,a) outw((v),(a)) | ||
32304 | +#else | ||
32305 | +#define wlan_inw(a) inw((a)) | ||
32306 | +#define wlan_inw_le16_to_cpu(a) __cpu_to_le16(inw((a))) | ||
32307 | +#define wlan_outw(v,a) outw((v),(a)) | ||
32308 | +#define wlan_outw_cpu_to_le16(v,a) outw(__cpu_to_le16((v)),(a)) | ||
32309 | +#endif | ||
32310 | + | ||
32311 | +/*=============================================================*/ | ||
32312 | +/*------ Bit settings -----------------------------------------*/ | ||
32313 | +/*=============================================================*/ | ||
32314 | +#define ieee2host16(n) __le16_to_cpu(n) | ||
32315 | +#define ieee2host32(n) __le32_to_cpu(n) | ||
32316 | +#define host2ieee16(n) __cpu_to_le16(n) | ||
32317 | +#define host2ieee32(n) __cpu_to_le32(n) | ||
32318 | + | ||
32319 | +/* for constants */ | ||
32320 | +#ifdef __LITTLE_ENDIAN | ||
32321 | + #define IEEE16(a,n) a = n, a##i = n, | ||
32322 | +#else | ||
32323 | + #ifdef __BIG_ENDIAN | ||
32324 | + /* shifts would produce gcc warnings. Oh well... */ | ||
32325 | + #define IEEE16(a,n) a = n, a##i = ((n&0xff)*256 + ((n&0xff00)/256)), | ||
32326 | + #else | ||
32327 | + #error give me endianness or give me death | ||
32328 | + #endif | ||
32329 | +#endif | ||
32330 | + | ||
32331 | +/*=============================================================*/ | ||
32332 | +/*------ Compiler Portability Macros --------------------------*/ | ||
32333 | +/*=============================================================*/ | ||
32334 | +#define WLAN_PACKED __attribute__ ((packed)) | ||
32335 | + | ||
32336 | +/* Interrupt handler backwards compatibility stuff */ | ||
32337 | +#ifndef IRQ_NONE | ||
32338 | +#define IRQ_NONE | ||
32339 | +#define IRQ_HANDLED | ||
32340 | +typedef void irqreturn_t; | ||
32341 | +#endif | ||
32342 | + | ||
32343 | +#ifndef ARPHRD_IEEE80211_PRISM | ||
32344 | +#define ARPHRD_IEEE80211_PRISM 802 | ||
32345 | +#endif | ||
32346 | + | ||
32347 | +#define ETH_P_80211_RAW (ETH_P_ECONET + 1) | ||
32348 | + | ||
32349 | +/*============================================================================* | ||
32350 | + * Constants * | ||
32351 | + *============================================================================*/ | ||
32352 | +#define WLAN_IEEE_OUI_LEN 3 | ||
32353 | + | ||
32354 | +/*============================================================================* | ||
32355 | + * Types * | ||
32356 | + *============================================================================*/ | ||
32357 | + | ||
32358 | +/* local ether header type */ | ||
32359 | +typedef struct wlan_ethhdr { | ||
32360 | + u8 daddr[ETH_ALEN]; | ||
32361 | + u8 saddr[ETH_ALEN]; | ||
32362 | + u16 type; | ||
32363 | +} WLAN_PACKED wlan_ethhdr_t; | ||
32364 | + | ||
32365 | +/* local llc header type */ | ||
32366 | +typedef struct wlan_llc { | ||
32367 | + u8 dsap; | ||
32368 | + u8 ssap; | ||
32369 | + u8 ctl; | ||
32370 | +} WLAN_PACKED wlan_llc_t; | ||
32371 | + | ||
32372 | +/* local snap header type */ | ||
32373 | +typedef struct wlan_snap { | ||
32374 | + u8 oui[WLAN_IEEE_OUI_LEN]; | ||
32375 | + u16 type; | ||
32376 | +} WLAN_PACKED wlan_snap_t; | ||
32377 | Index: linux-2.6.22/drivers/net/wireless/acx/wlan_hdr.h | ||
32378 | =================================================================== | ||
32379 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
32380 | +++ linux-2.6.22/drivers/net/wireless/acx/wlan_hdr.h 2007-08-23 18:34:19.000000000 +0200 | ||
32381 | @@ -0,0 +1,497 @@ | ||
32382 | +/*********************************************************************** | ||
32383 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
32384 | +** | ||
32385 | +** The contents of this file are subject to the Mozilla Public | ||
32386 | +** License Version 1.1 (the "License"); you may not use this file | ||
32387 | +** except in compliance with the License. You may obtain a copy of | ||
32388 | +** the License at http://www.mozilla.org/MPL/ | ||
32389 | +** | ||
32390 | +** Software distributed under the License is distributed on an "AS | ||
32391 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
32392 | +** implied. See the License for the specific language governing | ||
32393 | +** rights and limitations under the License. | ||
32394 | +** | ||
32395 | +** Alternatively, the contents of this file may be used under the | ||
32396 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
32397 | +** case the provisions of the GPL are applicable instead of the | ||
32398 | +** above. If you wish to allow the use of your version of this file | ||
32399 | +** only under the terms of the GPL and not to allow others to use | ||
32400 | +** your version of this file under the MPL, indicate your decision | ||
32401 | +** by deleting the provisions above and replace them with the notice | ||
32402 | +** and other provisions required by the GPL. If you do not delete | ||
32403 | +** the provisions above, a recipient may use your version of this | ||
32404 | +** file under either the MPL or the GPL. | ||
32405 | +** --------------------------------------------------------------------- | ||
32406 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
32407 | +** made directly to: | ||
32408 | +** | ||
32409 | +** acx100-users@lists.sf.net | ||
32410 | +** http://acx100.sf.net | ||
32411 | +** --------------------------------------------------------------------- | ||
32412 | +*/ | ||
32413 | + | ||
32414 | +/*********************************************************************** | ||
32415 | +** This code is based on elements which are | ||
32416 | +** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. | ||
32417 | +** info@linux-wlan.com | ||
32418 | +** http://www.linux-wlan.com | ||
32419 | +*/ | ||
32420 | + | ||
32421 | +/* mini-doc | ||
32422 | + | ||
32423 | +Here are all 11b/11g/11a rates and modulations: | ||
32424 | + | ||
32425 | + 11b 11g 11a | ||
32426 | + --- --- --- | ||
32427 | + 1 |B |B | | ||
32428 | + 2 |Q |Q | | ||
32429 | + 5.5|Cp |C p| | ||
32430 | + 6 | |Od |O | ||
32431 | + 9 | |od |o | ||
32432 | +11 |Cp |C p| | ||
32433 | +12 | |Od |O | ||
32434 | +18 | |od |o | ||
32435 | +22 | | p| | ||
32436 | +24 | |Od |O | ||
32437 | +33 | | p| | ||
32438 | +36 | |od |o | ||
32439 | +48 | |od |o | ||
32440 | +54 | |od |o | ||
32441 | + | ||
32442 | +Mandatory: | ||
32443 | + B - DBPSK (Differential Binary Phase Shift Keying) | ||
32444 | + Q - DQPSK (Differential Quaternary Phase Shift Keying) | ||
32445 | + C - CCK (Complementary Code Keying, a form of DSSS | ||
32446 | + (Direct Sequence Spread Spectrum) modulation) | ||
32447 | + O - OFDM (Orthogonal Frequency Division Multiplexing) | ||
32448 | +Optional: | ||
32449 | + o - OFDM | ||
32450 | + d - CCK-OFDM (also known as DSSS-OFDM) | ||
32451 | + p - PBCC (Packet Binary Convolutional Coding) | ||
32452 | + | ||
32453 | +The term CCK-OFDM may be used interchangeably with DSSS-OFDM | ||
32454 | +(the IEEE 802.11g-2003 standard uses the latter terminology). | ||
32455 | +In the CCK-OFDM, the PLCP header of the frame uses the CCK form of DSSS, | ||
32456 | +while the PLCP payload (the MAC frame) is modulated using OFDM. | ||
32457 | + | ||
32458 | +Basically, you must use CCK-OFDM if you have mixed 11b/11g environment, | ||
32459 | +or else (pure OFDM) 11b equipment may not realize that AP | ||
32460 | +is sending a packet and start sending its own one. | ||
32461 | +Sadly, looks like acx111 does not support CCK-OFDM, only pure OFDM. | ||
32462 | + | ||
32463 | +Re PBCC: avoid using it. It makes sense only if you have | ||
32464 | +TI "11b+" hardware. You _must_ use PBCC in order to reach 22Mbps on it. | ||
32465 | + | ||
32466 | +Preambles: | ||
32467 | + | ||
32468 | +Long preamble (at 1Mbit rate, takes 144 us): | ||
32469 | + 16 bytes ones | ||
32470 | + 2 bytes 0xF3A0 (lsb sent first) | ||
32471 | +PLCP header follows (at 1Mbit also): | ||
32472 | + 1 byte Signal: speed, in 0.1Mbit units, except for: | ||
32473 | + 33Mbit: 33 (instead of 330 - doesn't fit in octet) | ||
32474 | + all CCK-OFDM rates: 30 | ||
32475 | + 1 byte Service | ||
32476 | + 0,1,4: reserved | ||
32477 | + 2: 1=locked clock | ||
32478 | + 3: 1=PBCC | ||
32479 | + 5: Length Extension (PBCC 22,33Mbit (11g only)) <- | ||
32480 | + 6: Length Extension (PBCC 22,33Mbit (11g only)) <- BLACK MAGIC HERE | ||
32481 | + 7: Length Extension <- | ||
32482 | + 2 bytes Length (time needed to tx this frame) | ||
32483 | + a) 5.5 Mbit/s CCK | ||
32484 | + Length = octets*8/5.5, rounded up to integer | ||
32485 | + b) 11 Mbit/s CCK | ||
32486 | + Length = octets*8/11, rounded up to integer | ||
32487 | + Service bit 7: | ||
32488 | + 0 = rounding took less than 8/11 | ||
32489 | + 1 = rounding took more than or equal to 8/11 | ||
32490 | + c) 5.5 Mbit/s PBCC | ||
32491 | + Length = (octets+1)*8/5.5, rounded up to integer | ||
32492 | + d) 11 Mbit/s PBCC | ||
32493 | + Length = (octets+1)*8/11, rounded up to integer | ||
32494 | + Service bit 7: | ||
32495 | + 0 = rounding took less than 8/11 | ||
32496 | + 1 = rounding took more than or equal to 8/11 | ||
32497 | + e) 22 Mbit/s PBCC | ||
32498 | + Length = (octets+1)*8/22, rounded up to integer | ||
32499 | + Service bits 6,7: | ||
32500 | + 00 = rounding took less than 8/22ths | ||
32501 | + 01 = rounding took 8/22...15/22ths | ||
32502 | + 10 = rounding took 16/22ths or more. | ||
32503 | + f) 33 Mbit/s PBCC | ||
32504 | + Length = (octets+1)*8/33, rounded up to integer | ||
32505 | + Service bits 5,6,7: | ||
32506 | + 000 rounding took less than 8/33 | ||
32507 | + 001 rounding took 8/33...15/33 | ||
32508 | + 010 rounding took 16/33...23/33 | ||
32509 | + 011 rounding took 24/33...31/33 | ||
32510 | + 100 rounding took 32/33 or more | ||
32511 | + 2 bytes CRC | ||
32512 | + | ||
32513 | +PSDU follows (up to 2346 bytes at selected rate) | ||
32514 | + | ||
32515 | +While Signal value alone is not enough to determine rate and modulation, | ||
32516 | +Signal+Service is always sufficient. | ||
32517 | + | ||
32518 | +Short preamble (at 1Mbit rate, takes 72 us): | ||
32519 | + 7 bytes zeroes | ||
32520 | + 2 bytes 0x05CF (lsb sent first) | ||
32521 | +PLCP header follows *at 2Mbit/s*. Format is the same as in long preamble. | ||
32522 | +PSDU follows (up to 2346 bytes at selected rate) | ||
32523 | + | ||
32524 | +OFDM preamble is completely different, uses OFDM | ||
32525 | +modulation from the start and thus easily identifiable. | ||
32526 | +Not shown here. | ||
32527 | +*/ | ||
32528 | + | ||
32529 | + | ||
32530 | +/*********************************************************************** | ||
32531 | +** Constants | ||
32532 | +*/ | ||
32533 | + | ||
32534 | +#define WLAN_HDR_A3_LEN 24 | ||
32535 | +#define WLAN_HDR_A4_LEN 30 | ||
32536 | +/* IV structure: | ||
32537 | +** 3 bytes: Initialization Vector (24 bits) | ||
32538 | +** 1 byte: 0..5: padding, must be 0; 6..7: key selector (0-3) | ||
32539 | +*/ | ||
32540 | +#define WLAN_WEP_IV_LEN 4 | ||
32541 | +/* 802.11 says 2312 but looks like 2312 is a max size of _WEPed data_ */ | ||
32542 | +#define WLAN_DATA_MAXLEN 2304 | ||
32543 | +#define WLAN_WEP_ICV_LEN 4 | ||
32544 | +#define WLAN_FCS_LEN 4 | ||
32545 | +#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN) | ||
32546 | +#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN) | ||
32547 | +#define WLAN_A3FR_MAXLEN_FCS (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + 4) | ||
32548 | +#define WLAN_A4FR_MAXLEN_FCS (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + 4) | ||
32549 | +#define WLAN_A3FR_MAXLEN_WEP (WLAN_A3FR_MAXLEN + 8) | ||
32550 | +#define WLAN_A4FR_MAXLEN_WEP (WLAN_A4FR_MAXLEN + 8) | ||
32551 | +#define WLAN_A3FR_MAXLEN_WEP_FCS (WLAN_A3FR_MAXLEN_FCS + 8) | ||
32552 | +#define WLAN_A4FR_MAXLEN_WEP_FCS (WLAN_A4FR_MAXLEN_FCS + 8) | ||
32553 | + | ||
32554 | +#define WLAN_BSS_TS_LEN 8 | ||
32555 | +#define WLAN_SSID_MAXLEN 32 | ||
32556 | +#define WLAN_BEACON_FR_MAXLEN (WLAN_HDR_A3_LEN + 334) | ||
32557 | +#define WLAN_ATIM_FR_MAXLEN (WLAN_HDR_A3_LEN + 0) | ||
32558 | +#define WLAN_DISASSOC_FR_MAXLEN (WLAN_HDR_A3_LEN + 2) | ||
32559 | +#define WLAN_ASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 48) | ||
32560 | +#define WLAN_ASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16) | ||
32561 | +#define WLAN_REASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 54) | ||
32562 | +#define WLAN_REASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16) | ||
32563 | +#define WLAN_PROBEREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 44) | ||
32564 | +#define WLAN_PROBERESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 78) | ||
32565 | +#define WLAN_AUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 261) | ||
32566 | +#define WLAN_DEAUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 2) | ||
32567 | +#define WLAN_CHALLENGE_IE_LEN 130 | ||
32568 | +#define WLAN_CHALLENGE_LEN 128 | ||
32569 | +#define WLAN_WEP_MAXKEYLEN 13 | ||
32570 | +#define WLAN_WEP_NKEYS 4 | ||
32571 | + | ||
32572 | +/*--- Frame Control Field -------------------------------------*/ | ||
32573 | +/* Frame Types */ | ||
32574 | +#define WLAN_FTYPE_MGMT 0x00 | ||
32575 | +#define WLAN_FTYPE_CTL 0x01 | ||
32576 | +#define WLAN_FTYPE_DATA 0x02 | ||
32577 | + | ||
32578 | +/* Frame subtypes */ | ||
32579 | +/* Management */ | ||
32580 | +#define WLAN_FSTYPE_ASSOCREQ 0x00 | ||
32581 | +#define WLAN_FSTYPE_ASSOCRESP 0x01 | ||
32582 | +#define WLAN_FSTYPE_REASSOCREQ 0x02 | ||
32583 | +#define WLAN_FSTYPE_REASSOCRESP 0x03 | ||
32584 | +#define WLAN_FSTYPE_PROBEREQ 0x04 | ||
32585 | +#define WLAN_FSTYPE_PROBERESP 0x05 | ||
32586 | +#define WLAN_FSTYPE_BEACON 0x08 | ||
32587 | +#define WLAN_FSTYPE_ATIM 0x09 | ||
32588 | +#define WLAN_FSTYPE_DISASSOC 0x0a | ||
32589 | +#define WLAN_FSTYPE_AUTHEN 0x0b | ||
32590 | +#define WLAN_FSTYPE_DEAUTHEN 0x0c | ||
32591 | + | ||
32592 | +/* Control */ | ||
32593 | +#define WLAN_FSTYPE_PSPOLL 0x0a | ||
32594 | +#define WLAN_FSTYPE_RTS 0x0b | ||
32595 | +#define WLAN_FSTYPE_CTS 0x0c | ||
32596 | +#define WLAN_FSTYPE_ACK 0x0d | ||
32597 | +#define WLAN_FSTYPE_CFEND 0x0e | ||
32598 | +#define WLAN_FSTYPE_CFENDCFACK 0x0f | ||
32599 | + | ||
32600 | +/* Data */ | ||
32601 | +#define WLAN_FSTYPE_DATAONLY 0x00 | ||
32602 | +#define WLAN_FSTYPE_DATA_CFACK 0x01 | ||
32603 | +#define WLAN_FSTYPE_DATA_CFPOLL 0x02 | ||
32604 | +#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03 | ||
32605 | +#define WLAN_FSTYPE_NULL 0x04 | ||
32606 | +#define WLAN_FSTYPE_CFACK 0x05 | ||
32607 | +#define WLAN_FSTYPE_CFPOLL 0x06 | ||
32608 | +#define WLAN_FSTYPE_CFACK_CFPOLL 0x07 | ||
32609 | + | ||
32610 | +/*--- FC Constants v. 2.0 ------------------------------------*/ | ||
32611 | +/* Each constant is defined twice: WF_CONST is in host */ | ||
32612 | +/* byteorder, WF_CONSTi is in ieee byteorder. */ | ||
32613 | +/* Usage: */ | ||
32614 | +/* printf("the frame subtype is %X", WF_FC_FTYPEi & rx.fc); */ | ||
32615 | +/* tx.fc = WF_FTYPE_CTLi | WF_FSTYPE_RTSi; */ | ||
32616 | +/*------------------------------------------------------------*/ | ||
32617 | + | ||
32618 | +enum { | ||
32619 | +/*--- Frame Control Field -------------------------------------*/ | ||
32620 | +/* Protocol version: always 0 for current 802.11 standards */ | ||
32621 | +IEEE16(WF_FC_PVER, 0x0003) | ||
32622 | +IEEE16(WF_FC_FTYPE, 0x000c) | ||
32623 | +IEEE16(WF_FC_FSTYPE, 0x00f0) | ||
32624 | +IEEE16(WF_FC_TODS, 0x0100) | ||
32625 | +IEEE16(WF_FC_FROMDS, 0x0200) | ||
32626 | +IEEE16(WF_FC_FROMTODS, 0x0300) | ||
32627 | +IEEE16(WF_FC_MOREFRAG, 0x0400) | ||
32628 | +IEEE16(WF_FC_RETRY, 0x0800) | ||
32629 | +/* Indicates PS mode in which STA will be after successful completion | ||
32630 | +** of current frame exchange sequence. Always 0 for AP frames */ | ||
32631 | +IEEE16(WF_FC_PWRMGT, 0x1000) | ||
32632 | +/* What MoreData=1 means: | ||
32633 | +** From AP to STA in PS mode: don't sleep yet, I have more frames for you | ||
32634 | +** From Contention-Free (CF) Pollable STA in response to a CF-Poll: | ||
32635 | +** STA has buffered frames for transmission in response to next CF-Poll | ||
32636 | +** Bcast/mcast frames transmitted from AP: | ||
32637 | +** when additional bcast/mcast frames remain to be transmitted by AP | ||
32638 | +** during this beacon interval | ||
32639 | +** In all other cases MoreData=0 */ | ||
32640 | +IEEE16(WF_FC_MOREDATA, 0x2000) | ||
32641 | +IEEE16(WF_FC_ISWEP, 0x4000) | ||
32642 | +IEEE16(WF_FC_ORDER, 0x8000) | ||
32643 | + | ||
32644 | +/* Frame Types */ | ||
32645 | +IEEE16(WF_FTYPE_MGMT, 0x00) | ||
32646 | +IEEE16(WF_FTYPE_CTL, 0x04) | ||
32647 | +IEEE16(WF_FTYPE_DATA, 0x08) | ||
32648 | + | ||
32649 | +/* Frame subtypes */ | ||
32650 | +/* Management */ | ||
32651 | +IEEE16(WF_FSTYPE_ASSOCREQ, 0x00) | ||
32652 | +IEEE16(WF_FSTYPE_ASSOCRESP, 0x10) | ||
32653 | +IEEE16(WF_FSTYPE_REASSOCREQ, 0x20) | ||
32654 | +IEEE16(WF_FSTYPE_REASSOCRESP, 0x30) | ||
32655 | +IEEE16(WF_FSTYPE_PROBEREQ, 0x40) | ||
32656 | +IEEE16(WF_FSTYPE_PROBERESP, 0x50) | ||
32657 | +IEEE16(WF_FSTYPE_BEACON, 0x80) | ||
32658 | +IEEE16(WF_FSTYPE_ATIM, 0x90) | ||
32659 | +IEEE16(WF_FSTYPE_DISASSOC, 0xa0) | ||
32660 | +IEEE16(WF_FSTYPE_AUTHEN, 0xb0) | ||
32661 | +IEEE16(WF_FSTYPE_DEAUTHEN, 0xc0) | ||
32662 | + | ||
32663 | +/* Control */ | ||
32664 | +IEEE16(WF_FSTYPE_PSPOLL, 0xa0) | ||
32665 | +IEEE16(WF_FSTYPE_RTS, 0xb0) | ||
32666 | +IEEE16(WF_FSTYPE_CTS, 0xc0) | ||
32667 | +IEEE16(WF_FSTYPE_ACK, 0xd0) | ||
32668 | +IEEE16(WF_FSTYPE_CFEND, 0xe0) | ||
32669 | +IEEE16(WF_FSTYPE_CFENDCFACK, 0xf0) | ||
32670 | + | ||
32671 | +/* Data */ | ||
32672 | +IEEE16(WF_FSTYPE_DATAONLY, 0x00) | ||
32673 | +IEEE16(WF_FSTYPE_DATA_CFACK, 0x10) | ||
32674 | +IEEE16(WF_FSTYPE_DATA_CFPOLL, 0x20) | ||
32675 | +IEEE16(WF_FSTYPE_DATA_CFACK_CFPOLL, 0x30) | ||
32676 | +IEEE16(WF_FSTYPE_NULL, 0x40) | ||
32677 | +IEEE16(WF_FSTYPE_CFACK, 0x50) | ||
32678 | +IEEE16(WF_FSTYPE_CFPOLL, 0x60) | ||
32679 | +IEEE16(WF_FSTYPE_CFACK_CFPOLL, 0x70) | ||
32680 | +}; | ||
32681 | + | ||
32682 | + | ||
32683 | +/*********************************************************************** | ||
32684 | +** Macros | ||
32685 | +*/ | ||
32686 | + | ||
32687 | +/*--- Duration Macros ----------------------------------------*/ | ||
32688 | +/* Macros to get/set the bitfields of the Duration Field */ | ||
32689 | +/* - the duration value is only valid when bit15 is zero */ | ||
32690 | +/* - the firmware handles these values, so I'm not going */ | ||
32691 | +/* to use these macros right now. */ | ||
32692 | +/*------------------------------------------------------------*/ | ||
32693 | + | ||
32694 | +/*--- Sequence Control Macros -------------------------------*/ | ||
32695 | +/* Macros to get/set the bitfields of the Sequence Control */ | ||
32696 | +/* Field. */ | ||
32697 | +/*------------------------------------------------------------*/ | ||
32698 | +#define WLAN_GET_SEQ_FRGNUM(n) ((u16)(n) & 0x000f) | ||
32699 | +#define WLAN_GET_SEQ_SEQNUM(n) (((u16)(n) & 0xfff0) >> 4) | ||
32700 | + | ||
32701 | +/*--- Data ptr macro -----------------------------------------*/ | ||
32702 | +/* Creates a u8* to the data portion of a frame */ | ||
32703 | +/* Assumes you're passing in a ptr to the beginning of the hdr*/ | ||
32704 | +/*------------------------------------------------------------*/ | ||
32705 | +#define WLAN_HDR_A3_DATAP(p) (((u8*)(p)) + WLAN_HDR_A3_LEN) | ||
32706 | +#define WLAN_HDR_A4_DATAP(p) (((u8*)(p)) + WLAN_HDR_A4_LEN) | ||
32707 | + | ||
32708 | + | ||
32709 | +/*********************************************************************** | ||
32710 | +** Types | ||
32711 | +*/ | ||
32712 | + | ||
32713 | +/* 802.11 header type | ||
32714 | +** | ||
32715 | +** Note the following: | ||
32716 | +** a1 *always* is receiver's mac or bcast/mcast | ||
32717 | +** a2 *always* is transmitter's mac, if a2 exists | ||
32718 | +** seq: [0:3] frag#, [4:15] seq# - used for dup detection | ||
32719 | +** (dups from retries have same seq#) */ | ||
32720 | +typedef struct wlan_hdr { | ||
32721 | + u16 fc; | ||
32722 | + u16 dur; | ||
32723 | + u8 a1[ETH_ALEN]; | ||
32724 | + u8 a2[ETH_ALEN]; | ||
32725 | + u8 a3[ETH_ALEN]; | ||
32726 | + u16 seq; | ||
32727 | + u8 a4[ETH_ALEN]; | ||
32728 | +} WLAN_PACKED wlan_hdr_t; | ||
32729 | + | ||
32730 | +/* Separate structs for use if frame type is known */ | ||
32731 | +typedef struct wlan_hdr_a3 { | ||
32732 | + u16 fc; | ||
32733 | + u16 dur; | ||
32734 | + u8 a1[ETH_ALEN]; | ||
32735 | + u8 a2[ETH_ALEN]; | ||
32736 | + u8 a3[ETH_ALEN]; | ||
32737 | + u16 seq; | ||
32738 | +} WLAN_PACKED wlan_hdr_a3_t; | ||
32739 | + | ||
32740 | +typedef struct wlan_hdr_mgmt { | ||
32741 | + u16 fc; | ||
32742 | + u16 dur; | ||
32743 | + u8 da[ETH_ALEN]; | ||
32744 | + u8 sa[ETH_ALEN]; | ||
32745 | + u8 bssid[ETH_ALEN]; | ||
32746 | + u16 seq; | ||
32747 | +} WLAN_PACKED wlan_hdr_mgmt_t; | ||
32748 | + | ||
32749 | +#ifdef NOT_NEEDED_YET | ||
32750 | +typedef struct { /* ad-hoc peer->peer (to/from DS = 0/0) */ | ||
32751 | + u16 fc; | ||
32752 | + u16 dur; | ||
32753 | + u8 da[ETH_ALEN]; | ||
32754 | + u8 sa[ETH_ALEN]; | ||
32755 | + u8 bssid[ETH_ALEN]; | ||
32756 | + u16 seq; | ||
32757 | +} WLAN_PACKED ibss; | ||
32758 | +typedef struct { /* ap->sta (to/from DS = 0/1) */ | ||
32759 | + u16 fc; | ||
32760 | + u16 dur; | ||
32761 | + u8 da[ETH_ALEN]; | ||
32762 | + u8 bssid[ETH_ALEN]; | ||
32763 | + u8 sa[ETH_ALEN]; | ||
32764 | + u16 seq; | ||
32765 | +} WLAN_PACKED fromap; | ||
32766 | +typedef struct { /* sta->ap (to/from DS = 1/0) */ | ||
32767 | + u16 fc; | ||
32768 | + u16 dur; | ||
32769 | + u8 bssid[ETH_ALEN]; | ||
32770 | + u8 sa[ETH_ALEN]; | ||
32771 | + u8 da[ETH_ALEN]; | ||
32772 | + u16 seq; | ||
32773 | +} WLAN_PACKED toap; | ||
32774 | +typedef struct { /* wds->wds (to/from DS = 1/1), the only 4addr pkt */ | ||
32775 | + u16 fc; | ||
32776 | + u16 dur; | ||
32777 | + u8 ra[ETH_ALEN]; | ||
32778 | + u8 ta[ETH_ALEN]; | ||
32779 | + u8 da[ETH_ALEN]; | ||
32780 | + u16 seq; | ||
32781 | + u8 sa[ETH_ALEN]; | ||
32782 | +} WLAN_PACKED wds; | ||
32783 | +typedef struct { /* all management packets */ | ||
32784 | + u16 fc; | ||
32785 | + u16 dur; | ||
32786 | + u8 da[ETH_ALEN]; | ||
32787 | + u8 sa[ETH_ALEN]; | ||
32788 | + u8 bssid[ETH_ALEN]; | ||
32789 | + u16 seq; | ||
32790 | +} WLAN_PACKED mgmt; | ||
32791 | +typedef struct { /* has no body, just a FCS */ | ||
32792 | + u16 fc; | ||
32793 | + u16 dur; | ||
32794 | + u8 ra[ETH_ALEN]; | ||
32795 | + u8 ta[ETH_ALEN]; | ||
32796 | +} WLAN_PACKED rts; | ||
32797 | +typedef struct { /* has no body, just a FCS */ | ||
32798 | + u16 fc; | ||
32799 | + u16 dur; | ||
32800 | + u8 ra[ETH_ALEN]; | ||
32801 | +} WLAN_PACKED cts; | ||
32802 | +typedef struct { /* has no body, just a FCS */ | ||
32803 | + u16 fc; | ||
32804 | + u16 dur; | ||
32805 | + u8 ra[ETH_ALEN]; | ||
32806 | +} WLAN_PACKED ack; | ||
32807 | +typedef struct { /* has no body, just a FCS */ | ||
32808 | + u16 fc; | ||
32809 | + /* NB: this one holds Assoc ID in dur field: */ | ||
32810 | + u16 aid; | ||
32811 | + u8 bssid[ETH_ALEN]; | ||
32812 | + u8 ta[ETH_ALEN]; | ||
32813 | +} WLAN_PACKED pspoll; | ||
32814 | +typedef struct { /* has no body, just a FCS */ | ||
32815 | + u16 fc; | ||
32816 | + u16 dur; | ||
32817 | + u8 ra[ETH_ALEN]; | ||
32818 | + u8 bssid[ETH_ALEN]; | ||
32819 | +} WLAN_PACKED cfend; | ||
32820 | +typedef struct { /* has no body, just a FCS */ | ||
32821 | + u16 fc; | ||
32822 | + u16 dur; | ||
32823 | + u8 ra[ETH_ALEN]; | ||
32824 | + u8 bssid[ETH_ALEN]; | ||
32825 | +} WLAN_PACKED cfendcfack; | ||
32826 | +#endif | ||
32827 | + | ||
32828 | +/* Prism header emulation (monitor mode) */ | ||
32829 | +typedef struct wlanitem_u32 { | ||
32830 | + u32 did; | ||
32831 | + u16 status; | ||
32832 | + u16 len; | ||
32833 | + u32 data; | ||
32834 | +} WLAN_PACKED wlanitem_u32_t; | ||
32835 | +#define WLANITEM_STATUS_data_ok 0 | ||
32836 | +#define WLANITEM_STATUS_no_value 1 | ||
32837 | +#define WLANITEM_STATUS_invalid_itemname 2 | ||
32838 | +#define WLANITEM_STATUS_invalid_itemdata 3 | ||
32839 | +#define WLANITEM_STATUS_missing_itemdata 4 | ||
32840 | +#define WLANITEM_STATUS_incomplete_itemdata 5 | ||
32841 | +#define WLANITEM_STATUS_invalid_msg_did 6 | ||
32842 | +#define WLANITEM_STATUS_invalid_mib_did 7 | ||
32843 | +#define WLANITEM_STATUS_missing_conv_func 8 | ||
32844 | +#define WLANITEM_STATUS_string_too_long 9 | ||
32845 | +#define WLANITEM_STATUS_data_out_of_range 10 | ||
32846 | +#define WLANITEM_STATUS_string_too_short 11 | ||
32847 | +#define WLANITEM_STATUS_missing_valid_func 12 | ||
32848 | +#define WLANITEM_STATUS_unknown 13 | ||
32849 | +#define WLANITEM_STATUS_invalid_did 14 | ||
32850 | +#define WLANITEM_STATUS_missing_print_func 15 | ||
32851 | + | ||
32852 | +#define WLAN_DEVNAMELEN_MAX 16 | ||
32853 | +typedef struct wlansniffrm { | ||
32854 | + u32 msgcode; | ||
32855 | + u32 msglen; | ||
32856 | + u8 devname[WLAN_DEVNAMELEN_MAX]; | ||
32857 | + wlanitem_u32_t hosttime; | ||
32858 | + wlanitem_u32_t mactime; | ||
32859 | + wlanitem_u32_t channel; | ||
32860 | + wlanitem_u32_t rssi; | ||
32861 | + wlanitem_u32_t sq; | ||
32862 | + wlanitem_u32_t signal; | ||
32863 | + wlanitem_u32_t noise; | ||
32864 | + wlanitem_u32_t rate; | ||
32865 | + wlanitem_u32_t istx; /* tx? 0:no 1:yes */ | ||
32866 | + wlanitem_u32_t frmlen; | ||
32867 | +} WLAN_PACKED wlansniffrm_t; | ||
32868 | +#define WLANSNIFFFRM 0x0041 | ||
32869 | +#define WLANSNIFFFRM_hosttime 0x1041 | ||
32870 | +#define WLANSNIFFFRM_mactime 0x2041 | ||
32871 | +#define WLANSNIFFFRM_channel 0x3041 | ||
32872 | +#define WLANSNIFFFRM_rssi 0x4041 | ||
32873 | +#define WLANSNIFFFRM_sq 0x5041 | ||
32874 | +#define WLANSNIFFFRM_signal 0x6041 | ||
32875 | +#define WLANSNIFFFRM_noise 0x7041 | ||
32876 | +#define WLANSNIFFFRM_rate 0x8041 | ||
32877 | +#define WLANSNIFFFRM_istx 0x9041 | ||
32878 | +#define WLANSNIFFFRM_frmlen 0xA041 | ||
32879 | Index: linux-2.6.22/drivers/net/wireless/acx/wlan_mgmt.h | ||
32880 | =================================================================== | ||
32881 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | ||
32882 | +++ linux-2.6.22/drivers/net/wireless/acx/wlan_mgmt.h 2007-08-23 18:34:19.000000000 +0200 | ||
32883 | @@ -0,0 +1,582 @@ | ||
32884 | +/*********************************************************************** | ||
32885 | +** Copyright (C) 2003 ACX100 Open Source Project | ||
32886 | +** | ||
32887 | +** The contents of this file are subject to the Mozilla Public | ||
32888 | +** License Version 1.1 (the "License"); you may not use this file | ||
32889 | +** except in compliance with the License. You may obtain a copy of | ||
32890 | +** the License at http://www.mozilla.org/MPL/ | ||
32891 | +** | ||
32892 | +** Software distributed under the License is distributed on an "AS | ||
32893 | +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
32894 | +** implied. See the License for the specific language governing | ||
32895 | +** rights and limitations under the License. | ||
32896 | +** | ||
32897 | +** Alternatively, the contents of this file may be used under the | ||
32898 | +** terms of the GNU Public License version 2 (the "GPL"), in which | ||
32899 | +** case the provisions of the GPL are applicable instead of the | ||
32900 | +** above. If you wish to allow the use of your version of this file | ||
32901 | +** only under the terms of the GPL and not to allow others to use | ||
32902 | +** your version of this file under the MPL, indicate your decision | ||
32903 | +** by deleting the provisions above and replace them with the notice | ||
32904 | +** and other provisions required by the GPL. If you do not delete | ||
32905 | +** the provisions above, a recipient may use your version of this | ||
32906 | +** file under either the MPL or the GPL. | ||
32907 | +** --------------------------------------------------------------------- | ||
32908 | +** Inquiries regarding the ACX100 Open Source Project can be | ||
32909 | +** made directly to: | ||
32910 | +** | ||
32911 | +** acx100-users@lists.sf.net | ||
32912 | +** http://acx100.sf.net | ||
32913 | +** --------------------------------------------------------------------- | ||
32914 | +*/ | ||
32915 | + | ||
32916 | +/*********************************************************************** | ||
32917 | +** This code is based on elements which are | ||
32918 | +** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. | ||
32919 | +** info@linux-wlan.com | ||
32920 | +** http://www.linux-wlan.com | ||
32921 | +*/ | ||
32922 | + | ||
32923 | +/*********************************************************************** | ||
32924 | +** Constants | ||
32925 | +*/ | ||
32926 | + | ||
32927 | +/*-- Information Element IDs --------------------*/ | ||
32928 | +#define WLAN_EID_SSID 0 | ||
32929 | +#define WLAN_EID_SUPP_RATES 1 | ||
32930 | +#define WLAN_EID_FH_PARMS 2 | ||
32931 | +#define WLAN_EID_DS_PARMS 3 | ||
32932 | +#define WLAN_EID_CF_PARMS 4 | ||
32933 | +#define WLAN_EID_TIM 5 | ||
32934 | +#define WLAN_EID_IBSS_PARMS 6 | ||
32935 | +#define WLAN_EID_COUNTRY 7 /* 802.11d */ | ||
32936 | +#define WLAN_EID_FH_HOP_PARMS 8 /* 802.11d */ | ||
32937 | +#define WLAN_EID_FH_TABLE 9 /* 802.11d */ | ||
32938 | +#define WLAN_EID_REQUEST 10 /* 802.11d */ | ||
32939 | +/*-- values 11-15 reserved --*/ | ||
32940 | +#define WLAN_EID_CHALLENGE 16 | ||
32941 | +/*-- values 17-31 reserved for challenge text extension --*/ | ||
32942 | +#define WLAN_EID_PWR_CONSTRAINT 32 /* 11h PowerConstraint */ | ||
32943 | +#define WLAN_EID_ERP_INFO 42 /* was seen from WRT54GS with OpenWrt */ | ||
32944 | +#define WLAN_EID_NONERP 47 /* was seen from WRT54GS with OpenWrt */ | ||
32945 | +#define WLAN_EID_RSN 48 | ||
32946 | +#define WLAN_EID_EXT_RATES 50 | ||
32947 | +#define WLAN_EID_UNKNOWN128 128 | ||
32948 | +#define WLAN_EID_UNKNOWN133 133 | ||
32949 | +#define WLAN_EID_GENERIC 221 /* was seen from WRT54GS with OpenWrt */ | ||
32950 | +#define WLAN_EID_UNKNOWN223 223 | ||
32951 | + | ||
32952 | +#if 0 | ||
32953 | +#define WLAN_EID_PWR_CAP 33 /* 11h PowerCapability */ | ||
32954 | +#define WLAN_EID_TPC_REQUEST 34 /* 11h TPC Request */ | ||
32955 | +#define WLAN_EID_TPC_REPORT 35 /* 11h TPC Report */ | ||
32956 | +#define WLAN_EID_SUPP_CHANNELS 36 /* 11h Supported Channels */ | ||
32957 | +#define WLAN_EID_CHANNEL_SWITCH 37 /* 11h ChannelSwitch */ | ||
32958 | +#define WLAN_EID_MEASURE_REQUEST 38 /* 11h MeasurementRequest */ | ||
32959 | +#define WLAN_EID_MEASURE_REPORT 39 /* 11h MeasurementReport */ | ||
32960 | +#define WLAN_EID_QUIET_ID 40 /* 11h Quiet */ | ||
32961 | +#define WLAN_EID_IBSS_DFS_ID 41 /* 11h IBSS_DFS */ | ||
32962 | +#endif | ||
32963 | + | ||
32964 | +/*-- Reason Codes -------------------------------*/ | ||
32965 | +#define WLAN_MGMT_REASON_RSVD 0 | ||
32966 | +#define WLAN_MGMT_REASON_UNSPEC 1 | ||
32967 | +#define WLAN_MGMT_REASON_PRIOR_AUTH_INVALID 2 | ||
32968 | +#define WLAN_MGMT_REASON_DEAUTH_LEAVING 3 | ||
32969 | +#define WLAN_MGMT_REASON_DISASSOC_INACTIVE 4 | ||
32970 | +#define WLAN_MGMT_REASON_DISASSOC_AP_BUSY 5 | ||
32971 | +#define WLAN_MGMT_REASON_CLASS2_NONAUTH 6 | ||
32972 | +#define WLAN_MGMT_REASON_CLASS3_NONASSOC 7 | ||
32973 | +#define WLAN_MGMT_REASON_DISASSOC_STA_HASLEFT 8 | ||
32974 | +#define WLAN_MGMT_REASON_CANT_ASSOC_NONAUTH 9 | ||
32975 | + | ||
32976 | +/*-- Status Codes -------------------------------*/ | ||
32977 | +#define WLAN_MGMT_STATUS_SUCCESS 0 | ||
32978 | +#define WLAN_MGMT_STATUS_UNSPEC_FAILURE 1 | ||
32979 | +#define WLAN_MGMT_STATUS_CAPS_UNSUPPORTED 10 | ||
32980 | +#define WLAN_MGMT_STATUS_REASSOC_NO_ASSOC 11 | ||
32981 | +#define WLAN_MGMT_STATUS_ASSOC_DENIED_UNSPEC 12 | ||
32982 | +#define WLAN_MGMT_STATUS_UNSUPPORTED_AUTHALG 13 | ||
32983 | +#define WLAN_MGMT_STATUS_RX_AUTH_NOSEQ 14 | ||
32984 | +#define WLAN_MGMT_STATUS_CHALLENGE_FAIL 15 | ||
32985 | +#define WLAN_MGMT_STATUS_AUTH_TIMEOUT 16 | ||
32986 | +#define WLAN_MGMT_STATUS_ASSOC_DENIED_BUSY 17 | ||
32987 | +#define WLAN_MGMT_STATUS_ASSOC_DENIED_RATES 18 | ||
32988 | +/* p80211b additions */ | ||
32989 | +#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOSHORT 19 | ||
32990 | +#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOPBCC 20 | ||
32991 | +#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOAGILITY 21 | ||
32992 | + | ||
32993 | +/*-- Auth Algorithm Field ---------------------------*/ | ||
32994 | +#define WLAN_AUTH_ALG_OPENSYSTEM 0 | ||
32995 | +#define WLAN_AUTH_ALG_SHAREDKEY 1 | ||
32996 | + | ||
32997 | +/*-- Management Frame Field Offsets -------------*/ | ||
32998 | +/* Note: Not all fields are listed because of variable lengths */ | ||
32999 | +/* Note: These offsets are from the start of the frame data */ | ||
33000 | + | ||
33001 | +#define WLAN_BEACON_OFF_TS 0 | ||
33002 | +#define WLAN_BEACON_OFF_BCN_INT 8 | ||
33003 | +#define WLAN_BEACON_OFF_CAPINFO 10 | ||
33004 | +#define WLAN_BEACON_OFF_SSID 12 | ||
33005 | + | ||
33006 | +#define WLAN_DISASSOC_OFF_REASON 0 | ||
33007 | + | ||
33008 | +#define WLAN_ASSOCREQ_OFF_CAP_INFO 0 | ||
33009 | +#define WLAN_ASSOCREQ_OFF_LISTEN_INT 2 | ||
33010 | +#define WLAN_ASSOCREQ_OFF_SSID 4 | ||
33011 | + | ||
33012 | +#define WLAN_ASSOCRESP_OFF_CAP_INFO 0 | ||
33013 | +#define WLAN_ASSOCRESP_OFF_STATUS 2 | ||
33014 | +#define WLAN_ASSOCRESP_OFF_AID 4 | ||
33015 | +#define WLAN_ASSOCRESP_OFF_SUPP_RATES 6 | ||
33016 | + | ||
33017 | +#define WLAN_REASSOCREQ_OFF_CAP_INFO 0 | ||
33018 | +#define WLAN_REASSOCREQ_OFF_LISTEN_INT 2 | ||
33019 | +#define WLAN_REASSOCREQ_OFF_CURR_AP 4 | ||
33020 | +#define WLAN_REASSOCREQ_OFF_SSID 10 | ||
33021 | + | ||
33022 | +#define WLAN_REASSOCRESP_OFF_CAP_INFO 0 | ||
33023 | +#define WLAN_REASSOCRESP_OFF_STATUS 2 | ||
33024 | +#define WLAN_REASSOCRESP_OFF_AID 4 | ||
33025 | +#define WLAN_REASSOCRESP_OFF_SUPP_RATES 6 | ||
33026 | + | ||
33027 | +#define WLAN_PROBEREQ_OFF_SSID 0 | ||
33028 | + | ||
33029 | +#define WLAN_PROBERESP_OFF_TS 0 | ||
33030 | +#define WLAN_PROBERESP_OFF_BCN_INT 8 | ||
33031 | +#define WLAN_PROBERESP_OFF_CAP_INFO 10 | ||
33032 | +#define WLAN_PROBERESP_OFF_SSID 12 | ||
33033 | + | ||
33034 | +#define WLAN_AUTHEN_OFF_AUTH_ALG 0 | ||
33035 | +#define WLAN_AUTHEN_OFF_AUTH_SEQ 2 | ||
33036 | +#define WLAN_AUTHEN_OFF_STATUS 4 | ||
33037 | +#define WLAN_AUTHEN_OFF_CHALLENGE 6 | ||
33038 | + | ||
33039 | +#define WLAN_DEAUTHEN_OFF_REASON 0 | ||
33040 | + | ||
33041 | +enum { | ||
33042 | +IEEE16(WF_MGMT_CAP_ESS, 0x0001) | ||
33043 | +IEEE16(WF_MGMT_CAP_IBSS, 0x0002) | ||
33044 | +/* In (re)assoc request frames by STA: | ||
33045 | +** Pollable=0, PollReq=0: STA is not CF-Pollable | ||
33046 | +** 0 1: STA is CF-Pollable, not requesting to be placed on the CF-Polling list | ||
33047 | +** 1 0: STA is CF-Pollable, requesting to be placed on the CF-Polling list | ||
33048 | +** 1 1: STA is CF-Pollable, requesting never to be polled | ||
33049 | +** In beacon, proberesp, (re)assoc resp frames by AP: | ||
33050 | +** 0 0: No point coordinator at AP | ||
33051 | +** 0 1: Point coordinator at AP for delivery only (no polling) | ||
33052 | +** 1 0: Point coordinator at AP for delivery and polling | ||
33053 | +** 1 1: Reserved */ | ||
33054 | +IEEE16(WF_MGMT_CAP_CFPOLLABLE, 0x0004) | ||
33055 | +IEEE16(WF_MGMT_CAP_CFPOLLREQ, 0x0008) | ||
33056 | +/* 1=non-WEP data frames are disallowed */ | ||
33057 | +IEEE16(WF_MGMT_CAP_PRIVACY, 0x0010) | ||
33058 | +/* In beacon, proberesp, (re)assocresp by AP/AdHoc: | ||
33059 | +** 1=use of shortpre is allowed ("I can receive shortpre") */ | ||
33060 | +IEEE16(WF_MGMT_CAP_SHORT, 0x0020) | ||
33061 | +IEEE16(WF_MGMT_CAP_PBCC, 0x0040) | ||
33062 | +IEEE16(WF_MGMT_CAP_AGILITY, 0x0080) | ||
33063 | +/* In (re)assoc request frames by STA: | ||
33064 | +** 1=short slot time implemented and enabled | ||
33065 | +** NB: AP shall use long slot time beginning at the next Beacon after assoc | ||
33066 | +** of STA with this bit set to 0 | ||
33067 | +** In beacon, proberesp, (re)assoc resp frames by AP: | ||
33068 | +** currently used slot time value: 0/1 - long/short */ | ||
33069 | +IEEE16(WF_MGMT_CAP_SHORTSLOT, 0x0400) | ||
33070 | +/* In (re)assoc request frames by STA: 1=CCK-OFDM is implemented and enabled | ||
33071 | +** In beacon, proberesp, (re)assoc resp frames by AP/AdHoc: | ||
33072 | +** 1=CCK-OFDM is allowed */ | ||
33073 | +IEEE16(WF_MGMT_CAP_CCKOFDM, 0x2000) | ||
33074 | +}; | ||
33075 | + | ||
33076 | + | ||
33077 | +/*********************************************************************** | ||
33078 | +** Types | ||
33079 | +*/ | ||
33080 | + | ||
33081 | +/* Information Element types */ | ||
33082 | + | ||
33083 | +/* prototype structure, all IEs start with these members */ | ||
33084 | +typedef struct wlan_ie { | ||
33085 | + u8 eid; | ||
33086 | + u8 len; | ||
33087 | +} WLAN_PACKED wlan_ie_t; | ||
33088 | + | ||
33089 | +/*-- Service Set Identity (SSID) -----------------*/ | ||
33090 | +typedef struct wlan_ie_ssid { | ||
33091 | + u8 eid; | ||
33092 | + u8 len; | ||
33093 | + u8 ssid[1]; /* may be zero */ | ||
33094 | +} WLAN_PACKED wlan_ie_ssid_t; | ||
33095 | + | ||
33096 | +/*-- Supported Rates -----------------------------*/ | ||
33097 | +typedef struct wlan_ie_supp_rates { | ||
33098 | + u8 eid; | ||
33099 | + u8 len; | ||
33100 | + u8 rates[1]; /* had better be at LEAST one! */ | ||
33101 | +} WLAN_PACKED wlan_ie_supp_rates_t; | ||
33102 | + | ||
33103 | +/*-- FH Parameter Set ----------------------------*/ | ||
33104 | +typedef struct wlan_ie_fh_parms { | ||
33105 | + u8 eid; | ||
33106 | + u8 len; | ||
33107 | + u16 dwell; | ||
33108 | + u8 hopset; | ||
33109 | + u8 hoppattern; | ||
33110 | + u8 hopindex; | ||
33111 | +} WLAN_PACKED wlan_ie_fh_parms_t; | ||
33112 | + | ||
33113 | +/*-- DS Parameter Set ----------------------------*/ | ||
33114 | +typedef struct wlan_ie_ds_parms { | ||
33115 | + u8 eid; | ||
33116 | + u8 len; | ||
33117 | + u8 curr_ch; | ||
33118 | +} WLAN_PACKED wlan_ie_ds_parms_t; | ||
33119 | + | ||
33120 | +/*-- CF Parameter Set ----------------------------*/ | ||
33121 | +typedef struct wlan_ie_cf_parms { | ||
33122 | + u8 eid; | ||
33123 | + u8 len; | ||
33124 | + u8 cfp_cnt; | ||
33125 | + u8 cfp_period; | ||
33126 | + u16 cfp_maxdur; | ||
33127 | + u16 cfp_durremaining; | ||
33128 | +} WLAN_PACKED wlan_ie_cf_parms_t; | ||
33129 | + | ||
33130 | +/*-- TIM ------------------------------------------*/ | ||
33131 | +typedef struct wlan_ie_tim { | ||
33132 | + u8 eid; | ||
33133 | + u8 len; | ||
33134 | + u8 dtim_cnt; | ||
33135 | + u8 dtim_period; | ||
33136 | + u8 bitmap_ctl; | ||
33137 | + u8 virt_bm[1]; | ||
33138 | +} WLAN_PACKED wlan_ie_tim_t; | ||
33139 | + | ||
33140 | +/*-- IBSS Parameter Set ---------------------------*/ | ||
33141 | +typedef struct wlan_ie_ibss_parms { | ||
33142 | + u8 eid; | ||
33143 | + u8 len; | ||
33144 | + u16 atim_win; | ||
33145 | +} WLAN_PACKED wlan_ie_ibss_parms_t; | ||
33146 | + | ||
33147 | +/*-- Challenge Text ------------------------------*/ | ||
33148 | +typedef struct wlan_ie_challenge { | ||
33149 | + u8 eid; | ||
33150 | + u8 len; | ||
33151 | + u8 challenge[1]; | ||
33152 | +} WLAN_PACKED wlan_ie_challenge_t; | ||
33153 | + | ||
33154 | +/*-- ERP (42) -------------------------------------*/ | ||
33155 | +typedef struct wlan_ie_erp { | ||
33156 | + u8 eid; | ||
33157 | + u8 len; | ||
33158 | + /* bit 0:Non ERP present | ||
33159 | + ** 1:Use Protection | ||
33160 | + ** 2:Barker Preamble mode | ||
33161 | + ** 3-7:reserved */ | ||
33162 | + u8 erp; | ||
33163 | +} WLAN_PACKED wlan_ie_erp_t; | ||
33164 | + | ||
33165 | +/* Types for parsing mgmt frames */ | ||
33166 | + | ||
33167 | +/* prototype structure, all mgmt frame types will start with these members */ | ||
33168 | +typedef struct wlan_fr_mgmt { | ||
33169 | + u16 type; | ||
33170 | + u16 len; /* DOES NOT include FCS */ | ||
33171 | + wlan_hdr_t *hdr; | ||
33172 | + /* used for target specific data, skb in Linux */ | ||
33173 | + /*-- fixed fields -----------*/ | ||
33174 | + /*-- info elements ----------*/ | ||
33175 | +} WLAN_PACKED wlan_fr_mgmt_t; | ||
33176 | + | ||
33177 | +/*-- Beacon ---------------------------------------*/ | ||
33178 | +typedef struct wlan_fr_beacon { | ||
33179 | + u16 type; | ||
33180 | + u16 len; | ||
33181 | + wlan_hdr_t *hdr; | ||
33182 | + /*-- fixed fields -----------*/ | ||
33183 | + u64 *ts; | ||
33184 | + u16 *bcn_int; | ||
33185 | + u16 *cap_info; | ||
33186 | + /*-- info elements ----------*/ | ||
33187 | + wlan_ie_ssid_t *ssid; | ||
33188 | + wlan_ie_supp_rates_t *supp_rates; | ||
33189 | + wlan_ie_supp_rates_t *ext_rates; | ||
33190 | + wlan_ie_fh_parms_t *fh_parms; | ||
33191 | + wlan_ie_ds_parms_t *ds_parms; | ||
33192 | + wlan_ie_cf_parms_t *cf_parms; | ||
33193 | + wlan_ie_ibss_parms_t *ibss_parms; | ||
33194 | + wlan_ie_tim_t *tim; /* in beacon only, not proberesp */ | ||
33195 | + wlan_ie_erp_t *erp; /* in beacon only, not proberesp */ | ||
33196 | +} wlan_fr_beacon_t; | ||
33197 | +#define wlan_fr_proberesp wlan_fr_beacon | ||
33198 | +#define wlan_fr_proberesp_t wlan_fr_beacon_t | ||
33199 | + | ||
33200 | +/*-- IBSS ATIM ------------------------------------*/ | ||
33201 | +typedef struct wlan_fr_ibssatim { | ||
33202 | + u16 type; | ||
33203 | + u16 len; | ||
33204 | + wlan_hdr_t *hdr; | ||
33205 | + /*-- fixed fields -----------*/ | ||
33206 | + /*-- info elements ----------*/ | ||
33207 | + /* this frame type has a null body */ | ||
33208 | +} wlan_fr_ibssatim_t; | ||
33209 | + | ||
33210 | +/*-- Disassociation -------------------------------*/ | ||
33211 | +typedef struct wlan_fr_disassoc { | ||
33212 | + u16 type; | ||
33213 | + u16 len; | ||
33214 | + wlan_hdr_t *hdr; | ||
33215 | + /*-- fixed fields -----------*/ | ||
33216 | + u16 *reason; | ||
33217 | + /*-- info elements ----------*/ | ||
33218 | +} wlan_fr_disassoc_t; | ||
33219 | + | ||
33220 | +/*-- Association Request --------------------------*/ | ||
33221 | +typedef struct wlan_fr_assocreq { | ||
33222 | + u16 type; | ||
33223 | + u16 len; | ||
33224 | + wlan_hdr_t *hdr; | ||
33225 | + /*-- fixed fields -----------*/ | ||
33226 | + u16 *cap_info; | ||
33227 | + u16 *listen_int; | ||
33228 | + /*-- info elements ----------*/ | ||
33229 | + wlan_ie_ssid_t *ssid; | ||
33230 | + wlan_ie_supp_rates_t *supp_rates; | ||
33231 | + wlan_ie_supp_rates_t *ext_rates; | ||
33232 | +} wlan_fr_assocreq_t; | ||
33233 | + | ||
33234 | +/*-- Association Response -------------------------*/ | ||
33235 | +typedef struct wlan_fr_assocresp { | ||
33236 | + u16 type; | ||
33237 | + u16 len; | ||
33238 | + wlan_hdr_t *hdr; | ||
33239 | + /*-- fixed fields -----------*/ | ||
33240 | + u16 *cap_info; | ||
33241 | + u16 *status; | ||
33242 | + u16 *aid; | ||
33243 | + /*-- info elements ----------*/ | ||
33244 | + wlan_ie_supp_rates_t *supp_rates; | ||
33245 | + wlan_ie_supp_rates_t *ext_rates; | ||
33246 | +} wlan_fr_assocresp_t; | ||
33247 | + | ||
33248 | +/*-- Reassociation Request ------------------------*/ | ||
33249 | +typedef struct wlan_fr_reassocreq { | ||
33250 | + u16 type; | ||
33251 | + u16 len; | ||
33252 | + wlan_hdr_t *hdr; | ||
33253 | + /*-- fixed fields -----------*/ | ||
33254 | + u16 *cap_info; | ||
33255 | + u16 *listen_int; | ||
33256 | + u8 *curr_ap; | ||
33257 | + /*-- info elements ----------*/ | ||
33258 | + wlan_ie_ssid_t *ssid; | ||
33259 | + wlan_ie_supp_rates_t *supp_rates; | ||
33260 | + wlan_ie_supp_rates_t *ext_rates; | ||
33261 | +} wlan_fr_reassocreq_t; | ||
33262 | + | ||
33263 | +/*-- Reassociation Response -----------------------*/ | ||
33264 | +typedef struct wlan_fr_reassocresp { | ||
33265 | + u16 type; | ||
33266 | + u16 len; | ||
33267 | + wlan_hdr_t *hdr; | ||
33268 | + /*-- fixed fields -----------*/ | ||
33269 | + u16 *cap_info; | ||
33270 | + u16 *status; | ||
33271 | + u16 *aid; | ||
33272 | + /*-- info elements ----------*/ | ||
33273 | + wlan_ie_supp_rates_t *supp_rates; | ||
33274 | + wlan_ie_supp_rates_t *ext_rates; | ||
33275 | +} wlan_fr_reassocresp_t; | ||
33276 | + | ||
33277 | +/*-- Probe Request --------------------------------*/ | ||
33278 | +typedef struct wlan_fr_probereq { | ||
33279 | + u16 type; | ||
33280 | + u16 len; | ||
33281 | + wlan_hdr_t *hdr; | ||
33282 | + /*-- fixed fields -----------*/ | ||
33283 | + /*-- info elements ----------*/ | ||
33284 | + wlan_ie_ssid_t *ssid; | ||
33285 | + wlan_ie_supp_rates_t *supp_rates; | ||
33286 | + wlan_ie_supp_rates_t *ext_rates; | ||
33287 | +} wlan_fr_probereq_t; | ||
33288 | + | ||
33289 | +/*-- Authentication -------------------------------*/ | ||
33290 | +typedef struct wlan_fr_authen { | ||
33291 | + u16 type; | ||
33292 | + u16 len; | ||
33293 | + wlan_hdr_t *hdr; | ||
33294 | + /*-- fixed fields -----------*/ | ||
33295 | + u16 *auth_alg; | ||
33296 | + u16 *auth_seq; | ||
33297 | + u16 *status; | ||
33298 | + /*-- info elements ----------*/ | ||
33299 | + wlan_ie_challenge_t *challenge; | ||
33300 | +} wlan_fr_authen_t; | ||
33301 | + | ||
33302 | +/*-- Deauthenication -----------------------------*/ | ||
33303 | +typedef struct wlan_fr_deauthen { | ||
33304 | + u16 type; | ||
33305 | + u16 len; | ||
33306 | + wlan_hdr_t *hdr; | ||
33307 | + /*-- fixed fields -----------*/ | ||
33308 | + u16 *reason; | ||
33309 | + /*-- info elements ----------*/ | ||
33310 | +} wlan_fr_deauthen_t; | ||
33311 | + | ||
33312 | +/* Types for building mgmt frames */ | ||
33313 | + | ||
33314 | +/* Warning. Several types used in below structs are | ||
33315 | +** in fact variable length. Use structs with such fields with caution */ | ||
33316 | +typedef struct auth_frame_body { | ||
33317 | + u16 auth_alg; | ||
33318 | + u16 auth_seq; | ||
33319 | + u16 status; | ||
33320 | + wlan_ie_challenge_t challenge; | ||
33321 | +} WLAN_PACKED auth_frame_body_t; | ||
33322 | + | ||
33323 | +typedef struct assocresp_frame_body { | ||
33324 | + u16 cap_info; | ||
33325 | + u16 status; | ||
33326 | + u16 aid; | ||
33327 | + wlan_ie_supp_rates_t rates; | ||
33328 | +} WLAN_PACKED assocresp_frame_body_t; | ||
33329 | + | ||
33330 | +typedef struct reassocreq_frame_body { | ||
33331 | + u16 cap_info; | ||
33332 | + u16 listen_int; | ||
33333 | + u8 current_ap[ETH_ALEN]; | ||
33334 | + wlan_ie_ssid_t ssid; | ||
33335 | +/* access to this one is disabled since ssid_t is variable length: */ | ||
33336 | + /* wlan_ie_supp_rates_t rates; */ | ||
33337 | +} WLAN_PACKED reassocreq_frame_body_t; | ||
33338 | + | ||
33339 | +typedef struct reassocresp_frame_body { | ||
33340 | + u16 cap_info; | ||
33341 | + u16 status; | ||
33342 | + u16 aid; | ||
33343 | + wlan_ie_supp_rates_t rates; | ||
33344 | +} WLAN_PACKED reassocresp_frame_body_t; | ||
33345 | + | ||
33346 | +typedef struct deauthen_frame_body { | ||
33347 | + u16 reason; | ||
33348 | +} WLAN_PACKED deauthen_frame_body_t; | ||
33349 | + | ||
33350 | +typedef struct disassoc_frame_body { | ||
33351 | + u16 reason; | ||
33352 | +} WLAN_PACKED disassoc_frame_body_t; | ||
33353 | + | ||
33354 | +typedef struct probereq_frame_body { | ||
33355 | + wlan_ie_ssid_t ssid; | ||
33356 | + wlan_ie_supp_rates_t rates; | ||
33357 | +} WLAN_PACKED probereq_frame_body_t; | ||
33358 | + | ||
33359 | +typedef struct proberesp_frame_body { | ||
33360 | + u8 timestamp[8]; | ||
33361 | + u16 beacon_int; | ||
33362 | + u16 cap_info; | ||
33363 | + wlan_ie_ssid_t ssid; | ||
33364 | +/* access to these is disabled since ssid_t is variable length: */ | ||
33365 | + /* wlan_ie_supp_rates_t rates; */ | ||
33366 | + /* fhps_t fhps; */ | ||
33367 | + /* dsps_t dsps; */ | ||
33368 | + /* cfps_t cfps; */ | ||
33369 | +} WLAN_PACKED proberesp_frame_body_t; | ||
33370 | + | ||
33371 | + | ||
33372 | +/*********************************************************************** | ||
33373 | +** Functions | ||
33374 | +*/ | ||
33375 | + | ||
33376 | +/* Helpers for parsing mgmt frames */ | ||
33377 | +void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t *f); | ||
33378 | +void wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t *f); | ||
33379 | +void wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t *f); | ||
33380 | +void wlan_mgmt_decode_authen(wlan_fr_authen_t *f); | ||
33381 | +void wlan_mgmt_decode_beacon(wlan_fr_beacon_t *f); | ||
33382 | +void wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t *f); | ||
33383 | +void wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t *f); | ||
33384 | +void wlan_mgmt_decode_probereq(wlan_fr_probereq_t *f); | ||
33385 | +void wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t *f); | ||
33386 | +void wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t *f); | ||
33387 | +void wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t *f); | ||
33388 | + | ||
33389 | +/* Helpers for building mgmt frames */ | ||
33390 | +static inline u8* | ||
33391 | +wlan_fill_ie_ssid(u8 *p, int len, const char *ssid) | ||
33392 | +{ | ||
33393 | + struct wlan_ie_ssid *ie = (void*)p; | ||
33394 | + ie->eid = WLAN_EID_SSID; | ||
33395 | + ie->len = len; | ||
33396 | + memcpy(ie->ssid, ssid, len); | ||
33397 | + return p + len + 2; | ||
33398 | +} | ||
33399 | +/* This controls whether we create 802.11g 'ext supported rates' IEs | ||
33400 | +** or just create overlong 'supported rates' IEs instead | ||
33401 | +** (non-11g compliant) */ | ||
33402 | +#define WE_OBEY_802_11G 1 | ||
33403 | +static inline u8* | ||
33404 | +wlan_fill_ie_rates(u8 *p, int len, const u8 *rates) | ||
33405 | +{ | ||
33406 | + struct wlan_ie_supp_rates *ie = (void*)p; | ||
33407 | +#if WE_OBEY_802_11G | ||
33408 | + if (len > 8 ) len = 8; | ||
33409 | +#endif | ||
33410 | + /* supported rates (1 to 8 octets) */ | ||
33411 | + ie->eid = WLAN_EID_SUPP_RATES; | ||
33412 | + ie->len = len; | ||
33413 | + memcpy(ie->rates, rates, len); | ||
33414 | + return p + len + 2; | ||
33415 | +} | ||
33416 | +/* This one wouldn't create an IE at all if not needed */ | ||
33417 | +static inline u8* | ||
33418 | +wlan_fill_ie_rates_ext(u8 *p, int len, const u8 *rates) | ||
33419 | +{ | ||
33420 | + struct wlan_ie_supp_rates *ie = (void*)p; | ||
33421 | +#if !WE_OBEY_802_11G | ||
33422 | + return p; | ||
33423 | +#endif | ||
33424 | + len -= 8; | ||
33425 | + if (len <= 0) return p; | ||
33426 | + /* ext supported rates */ | ||
33427 | + ie->eid = WLAN_EID_EXT_RATES; | ||
33428 | + ie->len = len; | ||
33429 | + memcpy(ie->rates, rates+8, len); | ||
33430 | + return p + len + 2; | ||
33431 | +} | ||
33432 | +static inline u8* | ||
33433 | +wlan_fill_ie_ds_parms(u8 *p, int channel) | ||
33434 | +{ | ||
33435 | + struct wlan_ie_ds_parms *ie = (void*)p; | ||
33436 | + ie->eid = WLAN_EID_DS_PARMS; | ||
33437 | + ie->len = 1; | ||
33438 | + ie->curr_ch = channel; | ||
33439 | + return p + sizeof(*ie); | ||
33440 | +} | ||
33441 | +static inline u8* | ||
33442 | +wlan_fill_ie_ibss_parms(u8 *p, int atim_win) | ||
33443 | +{ | ||
33444 | + struct wlan_ie_ibss_parms *ie = (void*)p; | ||
33445 | + ie->eid = WLAN_EID_IBSS_PARMS; | ||
33446 | + ie->len = 2; | ||
33447 | + ie->atim_win = atim_win; | ||
33448 | + return p + sizeof(*ie); | ||
33449 | +} | ||
33450 | +static inline u8* | ||
33451 | +wlan_fill_ie_tim(u8 *p, int rem, int period, int bcast, | ||
33452 | + int ofs, int len, const u8 *vbm) | ||
33453 | +{ | ||
33454 | + struct wlan_ie_tim *ie = (void*)p; | ||
33455 | + ie->eid = WLAN_EID_TIM; | ||
33456 | + ie->len = len + 3; | ||
33457 | + ie->dtim_cnt = rem; | ||
33458 | + ie->dtim_period = period; | ||
33459 | + ie->bitmap_ctl = ofs | (bcast!=0); | ||
33460 | + if (vbm) | ||
33461 | + memcpy(ie->virt_bm, vbm, len); /* min 1 byte */ | ||
33462 | + else | ||
33463 | + ie->virt_bm[0] = 0; | ||
33464 | + return p + len + 3 + 2; | ||
33465 | +} | ||
33466 | Index: linux-2.6.22/drivers/net/wireless/Kconfig | ||
33467 | =================================================================== | ||
33468 | --- linux-2.6.22.orig/drivers/net/wireless/Kconfig 2007-07-09 01:32:17.000000000 +0200 | ||
33469 | +++ linux-2.6.22/drivers/net/wireless/Kconfig 2007-08-23 18:34:19.000000000 +0200 | ||
33470 | @@ -5,6 +5,36 @@ | ||
33471 | menu "Wireless LAN" | ||
33472 | depends on !S390 | ||
33473 | |||
33474 | +config NET_RADIO | ||
33475 | + bool "Wireless LAN drivers (non-hamradio) & Wireless Extensions" | ||
33476 | + select WIRELESS_EXT | ||
33477 | + ---help--- | ||
33478 | + Support for wireless LANs and everything having to do with radio, | ||
33479 | + but not with amateur radio or FM broadcasting. | ||
33480 | + | ||
33481 | + Saying Y here also enables the Wireless Extensions (creates | ||
33482 | + /proc/net/wireless and enables iwconfig access). The Wireless | ||
33483 | + Extension is a generic API allowing a driver to expose to the user | ||
33484 | + space configuration and statistics specific to common Wireless LANs. | ||
33485 | + The beauty of it is that a single set of tool can support all the | ||
33486 | + variations of Wireless LANs, regardless of their type (as long as | ||
33487 | + the driver supports Wireless Extension). Another advantage is that | ||
33488 | + these parameters may be changed on the fly without restarting the | ||
33489 | + driver (or Linux). If you wish to use Wireless Extensions with | ||
33490 | + wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch | ||
33491 | + the tools from | ||
33492 | + <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>. | ||
33493 | + | ||
33494 | +config NET_WIRELESS_RTNETLINK | ||
33495 | + bool "Wireless Extension API over RtNetlink" | ||
33496 | + depends on NET_RADIO | ||
33497 | + ---help--- | ||
33498 | + Support the Wireless Extension API over the RtNetlink socket | ||
33499 | + in addition to the traditional ioctl interface (selected above). | ||
33500 | + | ||
33501 | + For now, few tools use this facility, but it might grow in the | ||
33502 | + future. The only downside is that it adds 4.5 kB to your kernel. | ||
33503 | + | ||
33504 | config WLAN_PRE80211 | ||
33505 | bool "Wireless LAN (pre-802.11)" | ||
33506 | depends on NETDEVICES | ||
33507 | @@ -549,5 +579,6 @@ | ||
33508 | source "drivers/net/wireless/hostap/Kconfig" | ||
33509 | source "drivers/net/wireless/bcm43xx/Kconfig" | ||
33510 | source "drivers/net/wireless/zd1211rw/Kconfig" | ||
33511 | +source "drivers/net/wireless/acx/Kconfig" | ||
33512 | |||
33513 | endmenu | ||
33514 | Index: linux-2.6.22/drivers/net/wireless/Makefile | ||
33515 | =================================================================== | ||
33516 | --- linux-2.6.22.orig/drivers/net/wireless/Makefile 2007-07-09 01:32:17.000000000 +0200 | ||
33517 | +++ linux-2.6.22/drivers/net/wireless/Makefile 2007-08-23 18:34:19.000000000 +0200 | ||
33518 | @@ -34,6 +34,8 @@ | ||
33519 | |||
33520 | obj-$(CONFIG_PRISM54) += prism54/ | ||
33521 | |||
33522 | +obj-$(CONFIG_ACX) += acx/ | ||
33523 | + | ||
33524 | obj-$(CONFIG_HOSTAP) += hostap/ | ||
33525 | obj-$(CONFIG_BCM43XX) += bcm43xx/ | ||
33526 | obj-$(CONFIG_ZD1211RW) += zd1211rw/ | ||
diff --git a/meta/packages/linux/linux-rp_2.6.22.bb b/meta/packages/linux/linux-rp_2.6.22.bb index bd67d677c4..0bacce86fa 100644 --- a/meta/packages/linux/linux-rp_2.6.22.bb +++ b/meta/packages/linux/linux-rp_2.6.22.bb | |||
@@ -109,4 +109,6 @@ SRC_URI_append_tosa = "\ | |||
109 | file://wm97xx-lcdnoise-r0.patch;patch=1 " | 109 | file://wm97xx-lcdnoise-r0.patch;patch=1 " |
110 | # ${DOSRC}/tosa-asoc-r1.patch;patch=1 " | 110 | # ${DOSRC}/tosa-asoc-r1.patch;patch=1 " |
111 | 111 | ||
112 | SRC_URI_append_htcuniversal ="file://htcuni-acx.patch;patch=1;status=external" | ||
113 | |||
112 | S = "${WORKDIR}/linux-2.6.22" | 114 | S = "${WORKDIR}/linux-2.6.22" |