diff options
| -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" |
