acx100.sourceforge.net (Andreas Mohr ) -> -> Denis Vlasenko -> Jeff Garzik -> me Signed-off-by: Andrew Morton --- drivers/net/wireless/Kconfig | 2 drivers/net/wireless/Makefile | 2 drivers/net/wireless/tiacx/Changelog | 136 drivers/net/wireless/tiacx/Kconfig | 59 drivers/net/wireless/tiacx/Makefile | 6 drivers/net/wireless/tiacx/acx.h | 6 drivers/net/wireless/tiacx/acx_config.h | 40 drivers/net/wireless/tiacx/acx_func.h | 603 +++ drivers/net/wireless/tiacx/acx_inline.h | 119 drivers/net/wireless/tiacx/acx_struct.h | 1803 +++++++++++ drivers/net/wireless/tiacx/conv.c | 523 +++ drivers/net/wireless/tiacx/helper.c | 4304 +++++++++++++++++++++++++++ drivers/net/wireless/tiacx/helper2.c | 2422 +++++++++++++++ drivers/net/wireless/tiacx/ioctl.c | 3033 +++++++++++++++++++ drivers/net/wireless/tiacx/pci.c | 4777 +++++++++++++++++++++++++++++++ drivers/net/wireless/tiacx/pci_helper.c | 2 drivers/net/wireless/tiacx/setrate.c | 213 + drivers/net/wireless/tiacx/usb.c | 1967 ++++++++++++ drivers/net/wireless/tiacx/usb_helper.c | 2 drivers/net/wireless/tiacx/wlan.c | 391 ++ drivers/net/wireless/tiacx/wlan_compat.h | 331 ++ drivers/net/wireless/tiacx/wlan_hdr.h | 498 +++ drivers/net/wireless/tiacx/wlan_mgmt.h | 580 +++ 23 files changed, 21818 insertions(+), 1 deletion(-) diff -puN drivers/net/wireless/Kconfig~acx1xx-wireless-driver drivers/net/wireless/Kconfig --- devel/drivers/net/wireless/Kconfig~acx1xx-wireless-driver 2005-09-07 20:10:30.000000000 -0700 +++ devel-akpm/drivers/net/wireless/Kconfig 2005-09-07 20:10:30.000000000 -0700 @@ -477,6 +477,8 @@ config PRISM54 source "drivers/net/wireless/hostap/Kconfig" +source "drivers/net/wireless/tiacx/Kconfig" + # yes, this works even when no drivers are selected config NET_WIRELESS bool diff -puN drivers/net/wireless/Makefile~acx1xx-wireless-driver drivers/net/wireless/Makefile --- devel/drivers/net/wireless/Makefile~acx1xx-wireless-driver 2005-09-07 20:10:30.000000000 -0700 +++ devel-akpm/drivers/net/wireless/Makefile 2005-09-07 20:10:30.000000000 -0700 @@ -33,7 +33,7 @@ obj-$(CONFIG_PCI_ATMEL) += atmel obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o obj-$(CONFIG_PRISM54) += prism54/ - +obj-$(CONFIG_TIACX) += tiacx/ obj-$(CONFIG_HOSTAP) += hostap/ # 16-bit wireless PCMCIA client drivers diff -puN /dev/null drivers/net/wireless/tiacx/acx_config.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/acx_config.h 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,40 @@ +#define WLAN_RELEASE "v0.3.0" + +/* set to 0 if you don't want any debugging code to be compiled in */ +/* set to 1 if you want some debugging */ +/* set to 2 if you want extensive debug log */ +#define ACX_DEBUG 1 + +/* assume 32bit I/O width + * (16bit is also compatible with Compact Flash) */ +#define ACX_IO_WIDTH 32 + +/* Set this to 1 if you want monitor mode to use + * phy header. Currently it is not useful anyway since we + * don't know what useful info (if any) is in phy header. + * If you want faster/smaller code, say 0 here */ +#define WANT_PHY_HDR 0 + +/* whether to do Tx descriptor cleanup in softirq (i.e. not in IRQ + * handler) or not. Note that doing it later does slightly increase + * system load, so still do that stuff in the IRQ handler for now, + * even if that probably means worse latency */ +#define TX_CLEANUP_IN_SOFTIRQ 0 + +/* set to 1 if you want to have 1 driver per card instead of 1 single driver + * managing all cards (of a particular bus type) in your system + * Useful e.g. if you need to reinitialize single cards from time to time + * LINUX 2.4.X ONLY!! (pci_for_each_dev()) Feel free to implement 2.6.x + * compatibility... */ +#define SEPARATE_DRIVER_INSTANCES 0 + +/* Undefine if you want out-of-line acx_r/w_regNN, + * define to "static inline" if you want them inlined */ +#define INLINE_IO static inline + +/* Locking: */ +/* very talkative */ +/* #define PARANOID_LOCKING 1 */ +/* normal (use when bug-free) */ +#define DO_LOCKING 1 +/* else locking is disabled! */ diff -puN /dev/null drivers/net/wireless/tiacx/acx_func.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/acx_func.h 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,603 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + + +/*********************************************************************** +** LOGGING +** +** - Avoid SHOUTING needlessly. Avoid excessive verbosity. +** Gradually remove messages which are old debugging aids. +** +** - Use printk() for messages which are to be always logged. +** Supply either 'acx:' or ':' prefix so that user +** can figure out who's speaking among other kernel chatter. +** acx: is for general issues (e.g. "acx: no firmware image!") +** while : is related to a particular device +** (think about multi-card setup). Double check that message +** is not confusing to the average user. +** +** - use printk KERN_xxx level only if message is not a WARNING +** but is INFO, ERR etc. +** +** - Use printk_ratelimited() for messages which may flood +** (e.g. "rx DUP pkt!"). +** +** - Use acxlog() for messages which may be omitted (and they +** _will_ be omitted in non-debug builds). Note that +** message levels may be disabled at compile-time selectively, +** thus select them wisely. Example: L_DEBUG is the lowest +** (most likely to be compiled out) -> use for less important stuff. +** +** - Do not print important stuff with acxlog(), or else people +** will never build non-debug driver. +** +** Style: +** hex: capital letters, zero filled (e.g. 0x02AC) +** str: dont start from capitals, no trailing periods ("tx: queue is stopped") +*/ +#if ACX_DEBUG > 1 + +void log_fn_enter(const char *funcname); +void log_fn_exit(const char *funcname); +void log_fn_exit_v(const char *funcname, int v); + +#define FN_ENTER \ + do { \ + if (unlikely(acx_debug & L_FUNC)) { \ + log_fn_enter(__func__); \ + } \ + } while (0) + +#define FN_EXIT1(v) \ + do { \ + if (unlikely(acx_debug & L_FUNC)) { \ + log_fn_exit_v(__func__, v); \ + } \ + } while (0) +#define FN_EXIT0 \ + do { \ + if (unlikely(acx_debug & L_FUNC)) { \ + log_fn_exit(__func__); \ + } \ + } while (0) + +#else + +#define FN_ENTER +#define FN_EXIT1(v) +#define FN_EXIT0 + +#endif /* ACX_DEBUG > 1 */ + + +#if ACX_DEBUG + +#define acxlog(chan, args...) \ + do { \ + if (acx_debug & (chan)) \ + printk(args); \ + } while (0) +#define printk_ratelimited(args...) printk(args) + +#else /* Non-debug build: */ + +#define acxlog(chan, args...) +/* Standard way of log flood prevention */ +#define printk_ratelimited(args...) \ +do { \ + if (printk_ratelimit()) \ + printk(args); \ +} while (0) + +#endif /* ACX_DEBUG */ + + +/* MAC logging code is big (relatively) */ +void acx_print_mac(const char *head, const u8 *mac, const char *tail); + +/* Optimized out to nothing in non-debug build */ +static inline void +acxlog_mac(int level, const char *head, const u8 *mac, const char *tail) +{ + if (acx_debug & level) { + acx_print_mac(head, mac, tail); + } +} + + +/*********************************************************************** +** Low-level io routines +*/ +#include "acx_inline.h" + + +/*********************************************************************** +** MAC address helpers +*/ +static inline void +MAC_COPY(u8 *mac, const u8 *src) +{ + *(u32*)mac = *(u32*)src; + ((u16*)mac)[2] = ((u16*)src)[2]; + /* kernel's memcpy will do the same: memcpy(dst, src, ETH_ALEN); */ +} + +static inline void +MAC_FILL(u8 *mac, u8 val) +{ + memset(mac, val, ETH_ALEN); +} + +static inline void +MAC_BCAST(u8 *mac) +{ + ((u16*)mac)[2] = *(u32*)mac = -1; +} + +static inline void +MAC_ZERO(u8 *mac) +{ + ((u16*)mac)[2] = *(u32*)mac = 0; +} + +static inline int +mac_is_equal(const u8 *a, const u8 *b) +{ + /* can't beat this */ + return memcmp(a, b, ETH_ALEN) == 0; +} + +static inline int +mac_is_bcast(const u8 *mac) +{ + /* AND together 4 first bytes with sign-entended 2 last bytes + ** Only bcast address gives 0xffffffff. +1 gives 0 */ + return ( *(s32*)mac & ((s16*)mac)[2] ) + 1 == 0; +} + +static inline int +mac_is_zero(const u8 *mac) +{ + return ( *(u32*)mac | ((u16*)mac)[2] ) == 0; +} + +static inline int +mac_is_directed(const u8 *mac) +{ + return (mac[0] & 1)==0; +} + +static inline int +mac_is_mcast(const u8 *mac) +{ + return (mac[0] & 1) && !mac_is_bcast(mac); +} + +#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X" +#define MAC(bytevector) \ + ((unsigned char *)bytevector)[0], \ + ((unsigned char *)bytevector)[1], \ + ((unsigned char *)bytevector)[2], \ + ((unsigned char *)bytevector)[3], \ + ((unsigned char *)bytevector)[4], \ + ((unsigned char *)bytevector)[5] + + +/*********************************************************************** +** Random helpers +*/ +#define TO_STRING(x) #x +#define STRING(x) TO_STRING(x) + +#define CLEAR_BIT(val, mask) ((val) &= ~(mask)) +#define SET_BIT(val, mask) ((val) |= (mask)) + +/* undefined if v==0 */ +static inline unsigned int +lowest_bit(u16 v) +{ + unsigned int n = 0; + while (!(v & 0xf)) { v>>=4; n+=4; } + while (!(v & 1)) { v>>=1; n++; } + return n; +} + +/* undefined if v==0 */ +static inline unsigned int +highest_bit(u16 v) +{ + unsigned int n = 0; + while (v>0xf) { v>>=4; n+=4; } + while (v>1) { v>>=1; n++; } + return n; +} + +/* undefined if v==0 */ +static inline int +has_only_one_bit(u16 v) +{ + return ((v-1) ^ v) >= v; +} + + +/*********************************************************************** +** LOCKING +** We have priv->sem and priv->lock. +** +** We employ following naming convention in order to get locking right: +** +** acx_e_xxxx - external entry points called from process context. +** It is okay to sleep. priv->sem is to be taken on entry. +** acx_i_xxxx - external entry points possibly called from atomic context. +** Sleeping is not allowed (and thus down(sem) is not legal!) +** acx_s_xxxx - potentially sleeping functions. Do not ever call under lock! +** acx_l_xxxx - functions which expect lock to be already taken. +** rest - non-sleeping functions which do not require locking +** but may be run inder lock +** +** Theory of operation: +** +** All process-context entry points (_e_ functions) take sem +** immediately. IRQ handler and other 'atomic-context' entry points +** (_i_ functions) take lock immediately on entry, but dont take sem +** because that might sleep. +** +** Thus *all* code is either protected by sem or lock, or both. +** +** Code which must not run concurrently with IRQ takes lock +** (since sem is already taken it runs under sem+lock then). +** Such code is marked with _l_. +** +** This results in the following rules of thumb useful in code review: +** +** + If a function calls _s_ fn, it must be an _s_ itself. +** + You can call _l_ fn only (a) from another _l_ fn +** or (b) from _s_, _e_ or _i_ fn by taking lock, calling _l_, +** and dropping lock. +** + All IRQ code runs under lock. +** + Any _s_ fn is running under sem. +** + Code under sem can race only with IRQ code. +** + Code under sem+lock cannot race with anything. +*/ + +/* These functions *must* be inline or they will break horribly on SPARC, due + * to its weird semantics for save/restore flags. extern inline should prevent + * the kernel from linking or module from loading if they are not inlined. */ + +#if defined(PARANOID_LOCKING) /* Lock debugging */ + +void acx_lock_debug(wlandevice_t *priv, const char* where); +void acx_unlock_debug(wlandevice_t *priv, const char* where); +void acx_down_debug(wlandevice_t *priv, const char* where); +void acx_up_debug(wlandevice_t *priv, const char* where); +void acx_lock_unhold(void); +void acx_sem_unhold(void); + +extern inline void +acx_lock_helper(wlandevice_t *priv, unsigned long *fp, const char* where) +{ + acx_lock_debug(priv, where); + spin_lock_irqsave(&priv->lock, *fp); +} +extern inline void +acx_unlock_helper(wlandevice_t *priv, unsigned long *fp, const char* where) +{ + acx_unlock_debug(priv, where); + spin_unlock_irqrestore(&priv->lock, *fp); +} +extern inline void +acx_down_helper(wlandevice_t *priv, const char* where) +{ + acx_down_debug(priv, where); +} +extern inline void +acx_up_helper(wlandevice_t *priv, const char* where) +{ + acx_up_debug(priv, where); +} +#define acx_lock(priv, flags) acx_lock_helper(priv, &(flags), __FILE__ ":" STRING(__LINE__)) +#define acx_unlock(priv, flags) acx_unlock_helper(priv, &(flags), __FILE__ ":" STRING(__LINE__)) +#define acx_sem_lock(priv) acx_down_helper(priv, __FILE__ ":" STRING(__LINE__)) +#define acx_sem_unlock(priv) acx_up_helper(priv, __FILE__ ":" STRING(__LINE__)) + +#elif defined(DO_LOCKING) + +#define acx_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) +#define acx_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) +#define acx_sem_lock(priv) down(&priv->sem) +#define acx_sem_unlock(priv) up(&priv->sem) +#define acx_lock_unhold() ((void)0) +#define acx_sem_unhold() ((void)0) + +#else /* no locking! :( */ + +#define acx_lock(priv, flags) ((void)0) +#define acx_unlock(priv, flags) ((void)0) +#define acx_sem_lock(priv) ((void)0) +#define acx_sem_unlock(priv) ((void)0) +#define acx_lock_unhold() ((void)0) +#define acx_sem_unhold() ((void)0) + +#endif + + +/*********************************************************************** +** transitional define (before we go towards a real netdev_priv() layout) +** DON'T erroneously use a netdev_priv() instead - it's different for now! +** +** BTW, the new netdev_priv() is available in >= 2.4.27, >= 2.6.3 +*/ +#define acx_netdev_priv(dev) (void *)((dev)->priv) + +static inline void +acx_stop_queue(netdevice_t *dev, const char *msg) +{ + netif_stop_queue(dev); + if (msg) + acxlog(L_BUFT, "tx: stop queue %s\n", msg); +} + +static inline int +acx_queue_stopped(netdevice_t *dev) +{ + return netif_queue_stopped(dev); +} + +static inline void +acx_start_queue(netdevice_t *dev, const char *msg) +{ + netif_start_queue(dev); + if (msg) + acxlog(L_BUFT, "tx: start queue %s\n", msg); +} + +static inline void +acx_wake_queue(netdevice_t *dev, const char *msg) +{ + netif_wake_queue(dev); + if (msg) + acxlog(L_BUFT, "tx: wake queue %s\n", msg); +} + +static inline void +acx_carrier_off(netdevice_t *dev, const char *msg) +{ + netif_carrier_off(dev); + if (msg) + acxlog(L_BUFT, "tx: carrier off %s\n", msg); +} + +static inline void +acx_carrier_on(netdevice_t *dev, const char *msg) +{ + netif_carrier_on(dev); + if (msg) + acxlog(L_BUFT, "tx: carrier on %s\n", msg); +} + + +/*********************************************************************** +** Communication with firmware +*/ +/* in 1/100 of ms */ +#define CMD_TIMEOUT_MS(n) ((n)*100) +#define ACX_CMD_TIMEOUT_DEFAULT CMD_TIMEOUT_MS(50) + +#if ACX_DEBUG + +/* We want to log cmd names */ + +int acx_s_issue_cmd_timeo_debug(wlandevice_t *priv, unsigned cmd, + void *pcmdparam, unsigned paramlen, unsigned timeout, + const char* cmdstr); +#define acx_s_issue_cmd(priv,cmd,param,len) \ + acx_s_issue_cmd_timeo_debug(priv,cmd,param,len, \ + ACX_CMD_TIMEOUT_DEFAULT,#cmd) +#define acx_s_issue_cmd_timeo(priv,cmd,param,len,timeo) \ + acx_s_issue_cmd_timeo_debug(priv,cmd,param,len, \ + timeo,#cmd) +int acx_s_configure_debug(wlandevice_t *priv, void *pdr, + int type, const char* str); +#define acx_s_configure(priv,pdr,type) \ + acx_s_configure_debug(priv,pdr,type,#type) +int acx_s_interrogate_debug(wlandevice_t *priv, void *pdr, + int type, const char* str); +#define acx_s_interrogate(priv,pdr,type) \ + acx_s_interrogate_debug(priv,pdr,type,#type) + +#else + +int acx_s_issue_cmd_timeo(wlandevice_t *priv, unsigned cmd, + void *pcmdparam, unsigned paramlen, unsigned timeout); +static inline int +acx_s_issue_cmd(wlandevice_t *priv, unsigned cmd, void *param, unsigned len) +{ + return acx_s_issue_cmd_timeo(priv, cmd, param, len, + ACX_CMD_TIMEOUT_DEFAULT); +} +int acx_s_configure(wlandevice_t *priv, void *pdr, int type); +int acx_s_interrogate(wlandevice_t *priv, void *pdr, int type); + +#endif + +void acx_s_cmd_join_bssid(wlandevice_t *priv, const u8 *bssid); +void acx_s_cmd_start_scan(wlandevice_t *priv); +int acx111_s_get_feature_config(wlandevice_t *priv, + u32 *feature_options, u32 *data_flow_options); +int acx111_s_set_feature_config(wlandevice_t *priv, + u32 feature_options, u32 data_flow_options, + unsigned int mode /* 0 == remove, 1 == add, 2 == set */); +static inline int +acx111_s_feature_off(wlandevice_t *priv, u32 f, u32 d) +{ + return acx111_s_set_feature_config(priv, f, d, 0); +} +static inline int +acx111_s_feature_on(wlandevice_t *priv, u32 f, u32 d) +{ + return acx111_s_set_feature_config(priv, f, d, 1); +} +static inline int +acx111_s_feature_set(wlandevice_t *priv, u32 f, u32 d) +{ + return acx111_s_set_feature_config(priv, f, d, 2); +} + + +/*********************************************************************** +** Ioctls +*/ +int acx_ioctl_dbg_get_io( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra); +int acx_ioctl_dbg_set_io( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra); +int acx111_ioctl_info( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra); +int acx100_ioctl_set_phy_amp_bias( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra); + + +/*********************************************************************** +** Unsorted yet :) +*/ +void acx_s_msleep(int ms); +int acx_s_init_mac(netdevice_t *dev); +void acx_set_reg_domain(wlandevice_t *priv, unsigned char reg_dom_id); +void acx_set_timer(wlandevice_t *priv, u32 time); +void acx_update_capabilities(wlandevice_t *priv); +int acx_read_eeprom_offset(wlandevice_t *priv, u32 addr, u8 *charbuf); +int acx_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf); +int acx_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value); +void acx_s_start(wlandevice_t *priv); +#if USE_FW_LOADER_26 +firmware_image_t *acx_s_read_fw(struct device *dev, const char *file, u32 *size); +#else +firmware_image_t *acx_s_read_fw(const char *file, u32 *size); +#define acx_s_read_fw(dev, file, size) acx_s_read_fw(file, size) +#endif +void acx_s_initialize_rx_config(wlandevice_t *priv); +void acx_s_update_card_settings(wlandevice_t *priv, int get_all, int set_all); +void acx_init_task_scheduler(wlandevice_t *priv); +void acx_schedule_after_interrupt_task(wlandevice_t *priv, unsigned int set_flag); +int acx_s_upload_radio(wlandevice_t *priv); +void acx_read_configoption(wlandevice_t *priv); +int acx_proc_register_entries(const struct net_device *dev); +int acx_proc_unregister_entries(const struct net_device *dev); +void acx_l_update_ratevector(wlandevice_t *priv); + +int acx_s_recalib_radio(wlandevice_t *priv); +int acx_e_ioctl_old(netdevice_t *dev, struct ifreq *ifr, int cmd); + +void acx_l_sta_list_init(wlandevice_t *priv); +client_t *acx_l_sta_list_get(wlandevice_t *priv, const u8 *address); +void acx_l_sta_list_del(wlandevice_t *priv, client_t *clt); + +void acx_set_status(wlandevice_t *priv, u16 status); +int acx_l_rx_ieee802_11_frame(wlandevice_t *priv, rxbuffer_t *rxbuf); +int acx_l_transmit_disassoc(wlandevice_t *priv, client_t *clt); +void acx_i_timer(unsigned long a); +int acx_s_complete_scan(wlandevice_t *priv); + +const char* acx_get_status_name(u16 status); + +static inline wlan_hdr_t* +acx_get_wlan_hdr(wlandevice_t *priv, const rxbuffer_t *rxbuf) +{ + if (!(priv->rx_config_1 & RX_CFG1_INCLUDE_PHY_HDR)) + return (wlan_hdr_t*)&rxbuf->hdr_a3; + + /* take into account phy header in front of packet */ + if (CHIPTYPE_ACX111 == priv->chip_type) + return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + 8); + + return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + 4); +} + +struct sk_buff *acx_rxbuf_to_ether(struct wlandevice *priv, rxbuffer_t *rxbuf); + +void acx_l_power_led(wlandevice_t *priv, int enable); + +int acx100_s_create_dma_regions(wlandevice_t *priv); +int acx111_s_create_dma_regions(wlandevice_t *priv); +unsigned int acx_l_clean_tx_desc(wlandevice_t *priv); +void acx_l_clean_tx_desc_emergency(wlandevice_t *priv); + +u8 acx_signal_to_winlevel(u8 rawlevel); +u8 acx_signal_determine_quality(u8 signal, u8 noise); + +void acx_l_process_rxbuf(wlandevice_t *priv, rxbuffer_t *rxbuf); +void acx_l_process_rx_desc(wlandevice_t *priv); + +tx_t* acx_l_alloc_tx(wlandevice_t *priv); +void* acx_l_get_txbuf(tx_t *tx_opaque); +/* Misnamed. submit_prepared_tx() */ +void acx_l_dma_tx_data(wlandevice_t *priv, tx_t *tx_opaque, int len); + +void acx_dump_bytes(const void *, int); +void acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr); + +u8 acx_rate111to100(u16); +void acx_l_rx(wlandevice_t *priv, rxbuffer_t *rxbuf); + +void acx100usb_l_tx_data(wlandevice_t *priv, struct txdesc *desc); +int acx_s_set_defaults(wlandevice_t *priv); +void acx_init_mboxes(wlandevice_t *priv); + +int acx_l_ether_to_txbuf(wlandevice_t *priv, void *txbuf, const struct sk_buff *skb); + +#if !ACX_DEBUG +static inline const char* acx_get_packet_type_string(u16 fc) { return ""; } +#else +const char* acx_get_packet_type_string(u16 fc); +#endif + +int acx_i_start_xmit(struct sk_buff *skb, netdevice_t *dev); +void acx_free_desc_queues(TIWLAN_DC *pDc); + +#ifdef ACX_PCI +int acx_s_create_tx_host_desc_queue(TIWLAN_DC *pDc); +int acx_s_create_rx_host_desc_queue(TIWLAN_DC *pDc); +void acx_s_create_tx_desc_queue(TIWLAN_DC *pDc); +void acx_create_rx_desc_queue(TIWLAN_DC *pDc); +#endif diff -puN /dev/null drivers/net/wireless/tiacx/acx.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/acx.h 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,6 @@ +#include "acx_config.h" +#include "wlan_compat.h" +#include "wlan_hdr.h" +#include "wlan_mgmt.h" +#include "acx_struct.h" +#include "acx_func.h" diff -puN /dev/null drivers/net/wireless/tiacx/acx_inline.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/acx_inline.h 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,119 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +/*********************************************************************** +** This file expects INLINE_IO to be: +** a) #defined to 'static inline': will emit inlined functions (for .h file); +** or +** b) #defined to '': will emit non-inlined functions (for .c file); +** or +** c) not #defined at all: emit prototypes only +*/ +#ifdef ACX_PCI + +#ifndef INLINE_IO + +u32 acx_read_reg32(wlandevice_t *priv, unsigned int offset); +u16 acx_read_reg16(wlandevice_t *priv, unsigned int offset); +u8 acx_read_reg8(wlandevice_t *priv, unsigned int offset); +void acx_write_reg32(wlandevice_t *priv, unsigned int offset, u32 val); +void acx_write_reg16(wlandevice_t *priv, unsigned int offset, u16 val); +void acx_write_reg8(wlandevice_t *priv, unsigned int offset, u8 val); +void acx_write_flush(wlandevice_t *priv); + +#else + +#include /* readl() etc. */ + +INLINE_IO u32 +acx_read_reg32(wlandevice_t *priv, unsigned int offset) +{ +#if ACX_IO_WIDTH == 32 + return readl((u8 *)priv->iobase + priv->io[offset]); +#else + return readw((u8 *)priv->iobase + priv->io[offset]) + + (readw((u8 *)priv->iobase + priv->io[offset] + 2) << 16); +#endif +} + +INLINE_IO u16 +acx_read_reg16(wlandevice_t *priv, unsigned int offset) +{ + return readw((u8 *)priv->iobase + priv->io[offset]); +} + +INLINE_IO u8 +acx_read_reg8(wlandevice_t *priv, unsigned int offset) +{ + return readb((u8 *)priv->iobase + priv->io[offset]); +} + +INLINE_IO void +acx_write_reg32(wlandevice_t *priv, unsigned int offset, u32 val) +{ +#if ACX_IO_WIDTH == 32 + writel(val, (u8 *)priv->iobase + priv->io[offset]); +#else + writew(val & 0xffff, (u8 *)priv->iobase + priv->io[offset]); + writew(val >> 16, (u8 *)priv->iobase + priv->io[offset] + 2); +#endif +} + +INLINE_IO void +acx_write_reg16(wlandevice_t *priv, unsigned int offset, u16 val) +{ + writew(val, (u8 *)priv->iobase + priv->io[offset]); +} + +INLINE_IO void +acx_write_reg8(wlandevice_t *priv, unsigned int offset, u8 val) +{ + writeb(val, (u8 *)priv->iobase + priv->io[offset]); +} + +/* Handle PCI posting properly: + * Make sure that writes reach the adapter in case they require to be executed + * *before* the next write, by reading a random (and safely accessible) register. + * This call has to be made if there is no read following (which would flush the data + * to the adapter), yet the written data has to reach the adapter immediately. */ +INLINE_IO void +acx_write_flush(wlandevice_t *priv) +{ + /* readb(priv->iobase + priv->io[IO_ACX_INFO_MAILBOX_OFFS]); */ + /* faster version (accesses the first register, IO_ACX_SOFT_RESET, + * which should also be safe): */ + readb(priv->iobase); +} + +#endif + +#endif /* CONFIG_TIACX_PCI */ diff -puN /dev/null drivers/net/wireless/tiacx/acx_struct.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/acx_struct.h 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,1803 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +/*********************************************************************** +** Forward declarations of types +*/ +typedef struct tx tx_t; +typedef struct wlandevice wlandevice_t; +typedef struct client client_t; +typedef struct rxdesc rxdesc_t; +typedef struct txdesc txdesc_t; +typedef struct rxhostdesc rxhostdesc_t; +typedef struct txhostdesc txhostdesc_t; +typedef struct TIWLAN_DC TIWLAN_DC; + + +/*********************************************************************** +** Debug / log functionality +*/ +enum { + L_LOCK = (ACX_DEBUG>1)*0x0001, /* locking debug log */ + L_INIT = (ACX_DEBUG>0)*0x0002, /* special card initialization logging */ + L_IRQ = (ACX_DEBUG>0)*0x0004, /* interrupt stuff */ + L_ASSOC = (ACX_DEBUG>0)*0x0008, /* assocation (network join) and station log */ + L_FUNC = (ACX_DEBUG>1)*0x0020, /* logging of function enter / leave */ + L_XFER = (ACX_DEBUG>1)*0x0080, /* logging of transfers and mgmt */ + L_DATA = (ACX_DEBUG>1)*0x0100, /* logging of transfer data */ + L_DEBUG = (ACX_DEBUG>1)*0x0200, /* log of debug info */ + L_IOCTL = (ACX_DEBUG>0)*0x0400, /* log ioctl calls */ + L_CTL = (ACX_DEBUG>1)*0x0800, /* log of low-level ctl commands */ + L_BUFR = (ACX_DEBUG>1)*0x1000, /* debug rx buffer mgmt (ring buffer etc.) */ + L_XFER_BEACON = (ACX_DEBUG>1)*0x2000, /* also log beacon packets */ + L_BUFT = (ACX_DEBUG>1)*0x4000, /* debug tx buffer mgmt (ring buffer etc.) */ + L_USBRXTX = (ACX_DEBUG>0)*0x8000, /* debug USB rx/tx operations */ + L_BUF = L_BUFR + L_BUFT, + L_ANY = 0xffff +}; + +#if ACX_DEBUG +extern unsigned int acx_debug; +#else +enum { acx_debug = 0 }; +#endif + + +/*============================================================================* + * Random helpers * + *============================================================================*/ +#define ACX_PACKED __WLAN_ATTRIB_PACK__ + +#define VEC_SIZE(a) (sizeof(a)/sizeof(a[0])) + +/* Use worker_queues for 2.5/2.6 Kernels and queue tasks for 2.4 Kernels + (used for the 'bottom half' of the interrupt routine) */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,41) +#include +/* #define NEWER_KERNELS_ONLY 1 */ +#define USE_WORKER_TASKS +#define WORK_STRUCT struct work_struct +#define SCHEDULE_WORK schedule_work +#define FLUSH_SCHEDULED_WORK flush_scheduled_work + +#else +#include +#define USE_QUEUE_TASKS +#define WORK_STRUCT struct tq_struct +#define SCHEDULE_WORK schedule_task +#define INIT_WORK(work, func, ndev) \ + do { \ + (work)->routine = (func); \ + (work)->data = (ndev); \ + } while (0) +#define FLUSH_SCHEDULED_WORK flush_scheduled_tasks + +#endif + + +/*============================================================================* + * Constants * + *============================================================================*/ +#define OK 0 +#define NOT_OK 1 + +/* The supported chip models (taken from pci_device_id.driver_data) */ +#define CHIPTYPE_ACX100 1 +#define CHIPTYPE_ACX111 2 + +/* Driver defaults */ +#define DEFAULT_DTIM_INTERVAL 10 +/* used to be 2048, but FreeBSD driver changed it to 4096 to work properly +** in noisy wlans */ +#define DEFAULT_MSDU_LIFETIME 4096 +#define DEFAULT_RTS_THRESHOLD 2312 /* max. size: disable RTS mechanism */ +#define DEFAULT_BEACON_INTERVAL 100 + +#define ACX100_BAP_DATALEN_MAX 4096 +#define ACX100_RID_GUESSING_MAXLEN 2048 /* I'm not really sure */ +#define ACX100_RIDDATA_MAXLEN ACX100_RID_GUESSING_MAXLEN + +/* Support Constants */ +/* Radio type names, found in Win98 driver's TIACXLN.INF */ +#define RADIO_MAXIM_0D 0x0d +#define RADIO_RFMD_11 0x11 +#define RADIO_RALINK_15 0x15 +/* used in ACX111 cards (WG311v2, WL-121, ...): */ +#define RADIO_RADIA_16 0x16 +/* most likely *sometimes* used in ACX111 cards: */ +#define RADIO_UNKNOWN_17 0x17 +/* FwRad19.bin was found in a Safecom driver; must be an ACX111 radio: */ +#define RADIO_UNKNOWN_19 0x19 + +/* Controller Commands */ +/* can be found in table cmdTable in firmware "Rev. 1.5.0" (FW150) */ +#define ACX1xx_CMD_RESET 0x00 +#define ACX1xx_CMD_INTERROGATE 0x01 +#define ACX1xx_CMD_CONFIGURE 0x02 +#define ACX1xx_CMD_ENABLE_RX 0x03 +#define ACX1xx_CMD_ENABLE_TX 0x04 +#define ACX1xx_CMD_DISABLE_RX 0x05 +#define ACX1xx_CMD_DISABLE_TX 0x06 +#define ACX1xx_CMD_FLUSH_QUEUE 0x07 +#define ACX1xx_CMD_SCAN 0x08 +#define ACX1xx_CMD_STOP_SCAN 0x09 +#define ACX1xx_CMD_CONFIG_TIM 0x0a +#define ACX1xx_CMD_JOIN 0x0b +#define ACX1xx_CMD_WEP_MGMT 0x0c +#if OLD_FIRMWARE_VERSIONS +#define ACX100_CMD_HALT 0x0e /* mapped to unknownCMD in FW150 */ +#else +#define ACX1xx_CMD_MEM_READ 0x0d +#define ACX1xx_CMD_MEM_WRITE 0x0e +#endif +#define ACX1xx_CMD_SLEEP 0x0f +#define ACX1xx_CMD_WAKE 0x10 +#define ACX1xx_CMD_UNKNOWN_11 0x11 /* mapped to unknownCMD in FW150 */ +#define ACX100_CMD_INIT_MEMORY 0x12 +#define ACX1xx_CMD_CONFIG_BEACON 0x13 +#define ACX1xx_CMD_CONFIG_PROBE_RESPONSE 0x14 +#define ACX1xx_CMD_CONFIG_NULL_DATA 0x15 +#define ACX1xx_CMD_CONFIG_PROBE_REQUEST 0x16 +#define ACX1xx_CMD_TEST 0x17 +#define ACX1xx_CMD_RADIOINIT 0x18 +#define ACX111_CMD_RADIOCALIB 0x19 + +/* 'After Interrupt' Commands */ +#define ACX_AFTER_IRQ_CMD_STOP_SCAN 0x01 +#define ACX_AFTER_IRQ_CMD_ASSOCIATE 0x02 +#define ACX_AFTER_IRQ_CMD_RADIO_RECALIB 0x04 +#define ACX_AFTER_IRQ_UPDATE_CARD_CFG 0x08 +#define ACX_AFTER_IRQ_TX_CLEANUP 0x10 +#define ACX_AFTER_IRQ_COMPLETE_SCAN 0x20 +#define ACX_AFTER_IRQ_RESTART_SCAN 0x40 + + +/*============================================================================* + * Record ID Constants * + *============================================================================*/ + +/* Information Elements: Network Parameters, Static Configuration Entities */ +/* these are handled by real_cfgtable in firmware "Rev 1.5.0" (FW150) */ +#define ACX1xx_IE_UNKNOWN_00 0x0000 /* mapped to cfgInvalid in FW150 */ +#define ACX100_IE_ACX_TIMER 0x0001 +#define ACX1xx_IE_POWER_MGMT 0x0002 +#define ACX1xx_IE_QUEUE_CONFIG 0x0003 +#define ACX100_IE_BLOCK_SIZE 0x0004 +#define ACX1xx_IE_MEMORY_CONFIG_OPTIONS 0x0005 +#define ACX1xx_IE_RATE_FALLBACK 0x0006 +#define ACX100_IE_WEP_OPTIONS 0x0007 +#define ACX111_IE_RADIO_BAND 0x0007 +#define ACX1xx_IE_MEMORY_MAP 0x0008 /* huh? */ +#define ACX100_IE_SSID 0x0008 /* huh? */ +#define ACX1xx_IE_SCAN_STATUS 0x0009 /* mapped to cfgInvalid in FW150 */ +#define ACX1xx_IE_ASSOC_ID 0x000a +#define ACX1xx_IE_UNKNOWN_0B 0x000b /* mapped to cfgInvalid in FW150 */ +#define ACX100_IE_UNKNOWN_0C 0x000c /* very small implementation in FW150! */ +#define ACX111_IE_CONFIG_OPTIONS 0x000c +#define ACX1xx_IE_FWREV 0x000d +#define ACX1xx_IE_FCS_ERROR_COUNT 0x000e +#define ACX1xx_IE_MEDIUM_USAGE 0x000f +#define ACX1xx_IE_RXCONFIG 0x0010 +#define ACX100_IE_UNKNOWN_11 0x0011 /* NONBINARY: large implementation in FW150! link quality readings or so? */ +#define ACX111_IE_QUEUE_THRESH 0x0011 +#define ACX100_IE_UNKNOWN_12 0x0012 /* NONBINARY: VERY large implementation in FW150!! */ +#define ACX111_IE_BSS_POWER_SAVE 0x0012 +#define ACX1xx_IE_FIRMWARE_STATISTICS 0x0013 +#define ACX1xx_IE_FEATURE_CONFIG 0x0015 +#define ACX111_IE_KEY_CHOOSE 0x0016 /* for rekeying */ +#define ACX1xx_IE_DOT11_STATION_ID 0x1001 +#define ACX100_IE_DOT11_UNKNOWN_1002 0x1002 /* mapped to cfgInvalid in FW150 */ +#define ACX111_IE_DOT11_FRAG_THRESH 0x1002 /* mapped to cfgInvalid in FW150 */ +#define ACX100_IE_DOT11_BEACON_PERIOD 0x1003 /* mapped to cfgInvalid in FW150 */ +#define ACX1xx_IE_DOT11_DTIM_PERIOD 0x1004 /* mapped to cfgInvalid in FW150 */ +#define ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT 0x1005 +#define ACX1xx_IE_DOT11_LONG_RETRY_LIMIT 0x1006 +#define ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE 0x1007 /* configure default keys */ +#define ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME 0x1008 +#define ACX1xx_IE_DOT11_GROUP_ADDR 0x1009 +#define ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN 0x100a +#define ACX1xx_IE_DOT11_CURRENT_ANTENNA 0x100b +#define ACX1xx_IE_DOT11_UNKNOWN_100C 0x100c /* mapped to cfgInvalid in FW150 */ +#define ACX1xx_IE_DOT11_TX_POWER_LEVEL 0x100d +#define ACX1xx_IE_DOT11_CURRENT_CCA_MODE 0x100e +#define ACX100_IE_DOT11_ED_THRESHOLD 0x100f +#define ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET 0x1010 /* set default key ID */ +#define ACX100_IE_DOT11_UNKNOWN_1011 0x1011 /* mapped to cfgInvalid in FW150 */ +#define ACX100_IE_DOT11_UNKNOWN_1012 0x1012 /* mapped to cfgInvalid in FW150 */ +#define ACX100_IE_DOT11_UNKNOWN_1013 0x1013 /* mapped to cfgInvalid in FW150 */ + +/*--- Configuration RID Lengths: Network Params, Static Config Entities -----*/ +/* This is the length of JUST the DATA part of the RID (does not include the + * len or code fields) */ + +/* TODO: fill in the rest of these */ +#define ACX100_IE_ACX_TIMER_LEN 0x10 +#define ACX1xx_IE_POWER_MGMT_LEN 0x06 +#define ACX1xx_IE_QUEUE_CONFIG_LEN 0x1c +#define ACX100_IE_BLOCK_SIZE_LEN 0x02 +#define ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN 0x14 +#define ACX1xx_IE_RATE_FALLBACK_LEN 0x01 +#define ACX100_IE_WEP_OPTIONS_LEN 0x03 +#define ACX1xx_IE_MEMORY_MAP_LEN 0x28 +#define ACX100_IE_SSID_LEN 0x20 +#define ACX1xx_IE_SCAN_STATUS_LEN 0x04 +#define ACX1xx_IE_ASSOC_ID_LEN 0x02 +#define ACX1xx_IE_CONFIG_OPTIONS_LEN 0x14c +#define ACX1xx_IE_FWREV_LEN 0x18 +#define ACX1xx_IE_FCS_ERROR_COUNT_LEN 0x04 +#define ACX1xx_IE_MEDIUM_USAGE_LEN 0x08 +#define ACX1xx_IE_RXCONFIG_LEN 0x04 +#define ACX1xx_IE_FIRMWARE_STATISTICS_LEN 0x9c +#define ACX1xx_IE_FEATURE_CONFIG_LEN 0x08 +#define ACX111_IE_KEY_CHOOSE_LEN 0x04 /* really 4?? */ +#define ACX1xx_IE_DOT11_STATION_ID_LEN 0x06 +#define ACX100_IE_DOT11_BEACON_PERIOD_LEN 0x02 +#define ACX1xx_IE_DOT11_DTIM_PERIOD_LEN 0x01 +#define ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN 0x01 +#define ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN 0x01 +#define ACX100_IE_DOT11_WEP_DEFAULT_KEY_LEN 0x20 +#define ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN 0x04 +#define ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN 0x02 + +#ifdef ACX_USB +#define ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN 0x02 +#else +#define ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN 0x01 +#endif + +#define ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN 0x01 + +#ifdef ACX_USB +#define ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN 0x02 +#else +#define ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN 0x01 +#endif + +//USB doesn't return anything - len==0?! +#define ACX100_IE_DOT11_ED_THRESHOLD_LEN 0x04 + +#define ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN 0x01 + + +/*============================================================================* + * Information Frames Structures * + *============================================================================*/ + +/* Used in beacon frames and the like */ +#define DOT11RATEBYTE_1 (1*2) +#define DOT11RATEBYTE_2 (2*2) +#define DOT11RATEBYTE_5_5 (5*2+1) +#define DOT11RATEBYTE_11 (11*2) +#define DOT11RATEBYTE_22 (22*2) +#define DOT11RATEBYTE_6_G (6*2) +#define DOT11RATEBYTE_9_G (9*2) +#define DOT11RATEBYTE_12_G (12*2) +#define DOT11RATEBYTE_18_G (18*2) +#define DOT11RATEBYTE_24_G (24*2) +#define DOT11RATEBYTE_36_G (36*2) +#define DOT11RATEBYTE_48_G (48*2) +#define DOT11RATEBYTE_54_G (54*2) +#define DOT11RATEBYTE_BASIC 0x80 /* flags rates included in basic rate set */ + + +/*********************************************************************** +** rxbuffer_t +** +** This is the format of rx data returned by acx +*/ + +/* I've hoped it's a 802.11 PHY header, but no... + * so far, I've seen on acx111: + * 0000 3a00 0000 0000 IBBS Beacons + * 0000 3c00 0000 0000 ESS Beacons + * 0000 2700 0000 0000 Probe requests + * --vda + */ +typedef struct phy_hdr { + u8 unknown[4] ACX_PACKED; + u8 acx111_unknown[4] ACX_PACKED; +} phy_hdr_t; + +/* seems to be a bit similar to hfa384x_rx_frame. + * These fields are still not quite obvious, though. + * Some seem to have different meanings... */ + +#define RXBUF_HDRSIZE 12 +#define PHY_HDR(rxbuf) ((phy_hdr_t*)&rxbuf->hdr_a3) +#define RXBUF_BYTES_RCVD(rxbuf) (le16_to_cpu(rxbuf->mac_cnt_rcvd) & 0xfff) +#define RXBUF_BYTES_USED(rxbuf) \ + ((le16_to_cpu(rxbuf->mac_cnt_rcvd) & 0xfff) + RXBUF_HDRSIZE) +/* +mac_cnt_rcvd: + 12 bits: length of frame from control field to last byte of FCS + 4 bits: reserved + +mac_cnt_mblks: + 6 bits: number of memory block used to store frame in adapter memory + 1 bit: Traffic Indicator bit in TIM of received Beacon was set + +mac_status: 1 byte (bitmap): + 7 Matching BSSID + 6 Matching SSID + 5 BDCST Address 1 field is a broadcast + 4 VBM received beacon frame has more than one set bit (?!) + 3 TIM Set bit representing this station is set in TIM of received beacon + 2 GROUP Address 1 is a multicast + 1 ADDR1 Address 1 matches our MAC + 0 FCSGD FSC is good + +phy_stat_baseband: 1 byte (bitmap): + 7 Preamble frame had a long preamble + 6 PLCP Error CRC16 error in PLCP header + 5 Unsup_Mod unsupported modulation + 4 Selected Antenna antenna 1 was used to receive this frame + 3 PBCC/CCK frame used: 1=PBCC, 0=CCK modulation + 2 OFDM frame used OFDM modulation + 1 TI Protection protection frame was detected + 0 Reserved + +phy_plcp_signal: 1 byte: + Receive PLCP Signal field from the Baseband Processor + +phy_level: 1 byte: + receive AGC gain level (can be used to measure receive signal strength) + +phy_snr: 1 byte: + estimated noise power of equalized receive signal + at input of FEC decoder (can be used to measure receive signal quality) + +time: 4 bytes: + timestamp sampled from either the Access Manager TSF counter + or free-running microsecond counter when the MAC receives + first byte of PLCP header. +*/ + +typedef struct rxbuffer { + u16 mac_cnt_rcvd ACX_PACKED; /* only 12 bits are len! (0xfff) */ + u8 mac_cnt_mblks ACX_PACKED; + u8 mac_status ACX_PACKED; + u8 phy_stat_baseband ACX_PACKED; /* bit 0x80: used LNA (Low-Noise Amplifier) */ + u8 phy_plcp_signal ACX_PACKED; + u8 phy_level ACX_PACKED; /* PHY stat */ + u8 phy_snr ACX_PACKED; /* PHY stat */ + u32 time ACX_PACKED; /* timestamp upon MAC rcv first byte */ +/* 4-byte (acx100) or 8-byte (acx111) phy header will be here +** if RX_CFG1_INCLUDE_PHY_HDR is in effect: +** phy_hdr_t phy */ + wlan_hdr_a3_t hdr_a3 ACX_PACKED; + /* maximally sized data part of wlan packet */ + u8 data_a3[WLAN_A4FR_MAXLEN_WEP - WLAN_HDR_A3_LEN] ACX_PACKED; + /* can add hdr/data_a4 if needed */ +} rxbuffer_t; + + +/*--- Firmware statistics ----------------------------------------------------*/ +typedef struct fw_stats { + u32 val0x0 ACX_PACKED; /* hdr; */ + u32 tx_desc_of ACX_PACKED; + u32 rx_oom ACX_PACKED; + u32 rx_hdr_of ACX_PACKED; + u32 rx_hdr_use_next ACX_PACKED; + u32 rx_dropped_frame ACX_PACKED; + u32 rx_frame_ptr_err ACX_PACKED; + u32 rx_xfr_hint_trig ACX_PACKED; + + u32 rx_dma_req ACX_PACKED; + u32 rx_dma_err ACX_PACKED; + u32 tx_dma_req ACX_PACKED; + u32 tx_dma_err ACX_PACKED; + + u32 cmd_cplt ACX_PACKED; + u32 fiq ACX_PACKED; + u32 rx_hdrs ACX_PACKED; + u32 rx_cmplt ACX_PACKED; + u32 rx_mem_of ACX_PACKED; + u32 rx_rdys ACX_PACKED; + u32 irqs ACX_PACKED; + u32 acx_trans_procs ACX_PACKED; + u32 decrypt_done ACX_PACKED; + u32 dma_0_done ACX_PACKED; + u32 dma_1_done ACX_PACKED; + u32 tx_exch_complet ACX_PACKED; + u32 commands ACX_PACKED; + u32 acx_rx_procs ACX_PACKED; + u32 hw_pm_mode_changes ACX_PACKED; + u32 host_acks ACX_PACKED; + u32 pci_pm ACX_PACKED; + u32 acm_wakeups ACX_PACKED; + + u32 wep_key_count ACX_PACKED; + u32 wep_default_key_count ACX_PACKED; + u32 dot11_def_key_mib ACX_PACKED; + u32 wep_key_not_found ACX_PACKED; + u32 wep_decrypt_fail ACX_PACKED; +} fw_stats_t; + +/* Firmware version struct */ + +typedef struct fw_ver { + u16 cmd ACX_PACKED; + u16 size ACX_PACKED; + char fw_id[20] ACX_PACKED; + u32 hw_id ACX_PACKED; +} fw_ver_t; + +#define FW_ID_SIZE 20 + + +/*--- WEP stuff --------------------------------------------------------------*/ +#define DOT11_MAX_DEFAULT_WEP_KEYS 4 +#define MAX_KEYLEN 32 + +#define HOSTWEP_DEFAULTKEY_MASK (BIT1 | BIT0) +#define HOSTWEP_DECRYPT BIT4 +#define HOSTWEP_ENCRYPT BIT5 +#define HOSTWEP_PRIVACYINVOKED BIT6 +#define HOSTWEP_EXCLUDEUNENCRYPTED BIT7 + +/* non-firmware struct, no packing necessary */ +typedef struct wep_key { + size_t size; /* most often used member first */ + u8 index; + u8 key[29]; + u16 strange_filler; +} wep_key_t; /* size = 264 bytes (33*8) */ +/* FIXME: We don't have size 264! Or is there 2 bytes beyond the key + * (strange_filler)? */ + +/* non-firmware struct, no packing necessary */ +typedef struct key_struct { + u8 addr[ETH_ALEN]; /* 0x00 */ + u16 filler1; /* 0x06 */ + u32 filler2; /* 0x08 */ + u32 index; /* 0x0c */ + u16 len; /* 0x10 */ + u8 key[29]; /* 0x12; is this long enough??? */ +} key_struct_t; /* size = 276. FIXME: where is the remaining space?? */ + + +/*--- Client (peer) info -----------------------------------------------------*/ +/* priv->sta_list[] is used for: +** accumulating and processing of scan results +** keeping client info in AP mode +** keeping AP info in STA mode (AP is the only one 'client') +** keeping peer info in ad-hoc mode +** non-firmware struct --> no packing necessary */ +enum { + CLIENT_EMPTY_SLOT_0 = 0, + CLIENT_EXIST_1 = 1, + CLIENT_AUTHENTICATED_2 = 2, + CLIENT_ASSOCIATED_3 = 3, + CLIENT_JOIN_CANDIDATE = 4 +}; +struct client { + struct client* next; + unsigned long mtime; /* last time we heard it, in jiffies */ + size_t essid_len; /* length of ESSID (without '\0') */ + u32 sir; /* Standard IR */ + u32 snr; /* Signal to Noise Ratio */ + u16 aid; /* association ID */ + u16 seq; /* from client's auth req */ + u16 auth_alg; /* from client's auth req */ + u16 cap_info; /* from client's assoc req */ + u16 rate_cap; /* what client supports (all rates) */ + u16 rate_bas; /* what client supports (basic rates) */ + u16 rate_cfg; /* what is allowed (by iwconfig etc) */ + u16 rate_cur; /* currently used rate mask */ + u8 rate_100; /* currently used rate byte (acx100 only) */ + u8 used; /* misnamed, more like 'status' */ + u8 address[ETH_ALEN]; + u8 bssid[ETH_ALEN]; /* ad-hoc hosts can have bssid != mac */ + u8 channel; + u8 auth_step; + u8 ignore_count; + u8 fallback_count; + u8 stepup_count; + char essid[IW_ESSID_MAX_SIZE + 1]; /* ESSID and trailing '\0' */ +/* FIXME: this one is too damn big */ + char challenge_text[WLAN_CHALLENGE_LEN]; +}; + + +/*============================================================================* + * Hardware structures * + *============================================================================*/ + +/* An opaque typesafe helper type + * + * Some hardware fields are actually pointers, + * but they have to remain u32, since using ptr instead + * (8 bytes on 64bit systems!) would disrupt the fixed descriptor + * format the acx firmware expects in the non-user area. + * Since we cannot cram an 8 byte ptr into 4 bytes, we need to + * enforce that pointed to data remains in low memory + * (address value needs to fit in 4 bytes) on 64bit systems. + * + * This is easy to get wrong, thus we are using a small struct + * and special macros to access it. Macros will check for + * attempts to overflow an acx_ptr with value > 0xffffffff. + * + * Attempts to use acx_ptr without macros result in compile-time errors */ + +typedef struct { + u32 v; +} acx_ptr; + +#if ACX_DEBUG +#define CHECK32(n) BUG_ON(sizeof(n)>4 && (long)(n)>0xffffff00) +#else +#define CHECK32(n) ((void)0) +#endif + +/* acx_ptr <-> integer conversion */ +#define cpu2acx(n) ({ CHECK32(n); ((acx_ptr){ .v = cpu_to_le32(n) }); }) +#define acx2cpu(a) (le32_to_cpu(a.v)) + +/* acx_ptr <-> pointer conversion */ +#define ptr2acx(p) ({ CHECK32(p); ((acx_ptr){ .v = cpu_to_le32((u32)(long)(p)) }); }) +#define acx2ptr(a) ((void*)le32_to_cpu(a.v)) + +/* Values for rate field (acx100 only) */ +#define RATE100_1 10 +#define RATE100_2 20 +#define RATE100_5 55 +#define RATE100_11 110 +#define RATE100_22 220 +/* This bit denotes use of PBCC: +** (PBCC encoding is usable with 11 and 22 Mbps speeds only) */ +#define RATE100_PBCC511 0x80 + +/* Bit values for rate111 field */ +#define RATE111_1 0x0001 /* DBPSK */ +#define RATE111_2 0x0002 /* DQPSK */ +#define RATE111_5 0x0004 /* CCK or PBCC */ +#define RATE111_6 0x0008 /* CCK-OFDM or OFDM */ +#define RATE111_9 0x0010 /* CCK-OFDM or OFDM */ +#define RATE111_11 0x0020 /* CCK or PBCC */ +#define RATE111_12 0x0040 /* CCK-OFDM or OFDM */ +#define RATE111_18 0x0080 /* CCK-OFDM or OFDM */ +#define RATE111_22 0x0100 /* PBCC */ +#define RATE111_24 0x0200 /* CCK-OFDM or OFDM */ +#define RATE111_36 0x0400 /* CCK-OFDM or OFDM */ +#define RATE111_48 0x0800 /* CCK-OFDM or OFDM */ +#define RATE111_54 0x1000 /* CCK-OFDM or OFDM */ +#define RATE111_RESERVED 0x2000 +#define RATE111_PBCC511 0x4000 /* PBCC mod at 5.5 or 11Mbit (else CCK) */ +#define RATE111_SHORTPRE 0x8000 /* short preamble */ +/* Special 'try everything' value */ +#define RATE111_ALL 0x1fff +/* These bits denote acx100 compatible settings */ +#define RATE111_ACX100_COMPAT 0x0127 +/* These bits denote 802.11b compatible settings */ +#define RATE111_80211B_COMPAT 0x0027 + +/* Descriptor Ctl field bits + * init value is 0x8e, "idle" value is 0x82 (in idle tx descs) + */ +#define DESC_CTL_SHORT_PREAMBLE 0x01 /* preamble type: 0 = long; 1 = short */ +#define DESC_CTL_FIRSTFRAG 0x02 /* this is the 1st frag of the frame */ +#define DESC_CTL_AUTODMA 0x04 +#define DESC_CTL_RECLAIM 0x08 /* ready to reuse */ +#define DESC_CTL_HOSTDONE 0x20 /* host has finished processing */ +#define DESC_CTL_ACXDONE 0x40 /* acx has finished processing */ +/* host owns the desc [has to be released last, AFTER modifying all other desc fields!] */ +#define DESC_CTL_HOSTOWN 0x80 + +#define DESC_CTL_INIT (DESC_CTL_HOSTOWN | DESC_CTL_RECLAIM | \ + DESC_CTL_AUTODMA | DESC_CTL_FIRSTFRAG) +#define DESC_CTL_DONE (DESC_CTL_ACXDONE | DESC_CTL_HOSTOWN) + +#define DESC_CTL_HOSTOWN_STR "80" +#define DESC_CTL_DONE_STR "C0" + +/* NB: some bits may be interesting for Monitor mode tx (aka Raw tx): */ +#define DESC_CTL2_SEQ 0x01 /* don't increase sequence field */ +#define DESC_CTL2_FCS 0x02 /* don't add the FCS */ +#define DESC_CTL2_MORE_FRAG 0x04 +#define DESC_CTL2_RETRY 0x08 /* don't increase retry field */ +#define DESC_CTL2_POWER 0x10 /* don't increase power mgmt. field */ +#define DESC_CTL2_RTS 0x20 /* do RTS/CTS magic before sending */ +#define DESC_CTL2_WEP 0x40 /* encrypt this frame */ +#define DESC_CTL2_DUR 0x80 /* don't increase duration field */ + +/*************************************************************** +** PCI structures +*/ +/* IRQ Constants +** (outside of "#ifdef PCI" because USB (mis)uses HOST_INT_SCAN_COMPLETE */ +#define HOST_INT_RX_DATA 0x0001 +#define HOST_INT_TX_COMPLETE 0x0002 +#define HOST_INT_TX_XFER 0x0004 +#define HOST_INT_RX_COMPLETE 0x0008 +#define HOST_INT_DTIM 0x0010 +#define HOST_INT_BEACON 0x0020 +#define HOST_INT_TIMER 0x0040 +#define HOST_INT_KEY_NOT_FOUND 0x0080 +#define HOST_INT_IV_ICV_FAILURE 0x0100 +#define HOST_INT_CMD_COMPLETE 0x0200 +#define HOST_INT_INFO 0x0400 +#define HOST_INT_OVERFLOW 0x0800 +#define HOST_INT_PROCESS_ERROR 0x1000 +#define HOST_INT_SCAN_COMPLETE 0x2000 +#define HOST_INT_FCS_THRESHOLD 0x4000 +#define HOST_INT_UNKNOWN 0x8000 + +#ifdef ACX_PCI + +/* Register I/O offsets */ +#define ACX100_EEPROM_ID_OFFSET 0x380 + +/* please add further ACX hardware register definitions only when + it turns out you need them in the driver, and please try to use + firmware functionality instead, since using direct I/O access instead + of letting the firmware do it might confuse the firmware's state + machine */ + +/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION +** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */ +enum { + IO_ACX_SOFT_RESET = 0, + + IO_ACX_SLV_MEM_ADDR, + IO_ACX_SLV_MEM_DATA, + IO_ACX_SLV_MEM_CTL, + IO_ACX_SLV_END_CTL, + + IO_ACX_FEMR, /* Function Event Mask */ + + IO_ACX_INT_TRIG, + IO_ACX_IRQ_MASK, + IO_ACX_IRQ_STATUS_NON_DES, + IO_ACX_IRQ_STATUS_CLEAR, /* CLEAR = clear on read */ + IO_ACX_IRQ_ACK, + IO_ACX_HINT_TRIG, + + IO_ACX_ENABLE, + + IO_ACX_EEPROM_CTL, + IO_ACX_EEPROM_ADDR, + IO_ACX_EEPROM_DATA, + IO_ACX_EEPROM_CFG, + + IO_ACX_PHY_ADDR, + IO_ACX_PHY_DATA, + IO_ACX_PHY_CTL, + + IO_ACX_GPIO_OE, + + IO_ACX_GPIO_OUT, + + IO_ACX_CMD_MAILBOX_OFFS, + IO_ACX_INFO_MAILBOX_OFFS, + IO_ACX_EEPROM_INFORMATION, + + IO_ACX_EE_START, + IO_ACX_SOR_CFG, + IO_ACX_ECPU_CTRL +}; +/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION +** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */ + +/* Values for IO_ACX_INT_TRIG register: */ +/* inform hw that rxdesc in queue needs processing */ +#define INT_TRIG_RXPRC 0x08 +/* inform hw that txdesc in queue needs processing */ +#define INT_TRIG_TXPRC 0x04 +/* ack that we received info from info mailbox */ +#define INT_TRIG_INFOACK 0x02 +/* inform hw that we have filled command mailbox */ +#define INT_TRIG_CMD 0x01 + +struct txhostdesc { + acx_ptr data_phy ACX_PACKED; /* 0x00 [u8 *] */ + u16 data_offset ACX_PACKED; /* 0x04 */ + u16 reserved ACX_PACKED; /* 0x06 */ + u16 Ctl_16 ACX_PACKED; /* 16bit value, endianness!! */ + u16 length ACX_PACKED; /* 0x0a */ + acx_ptr desc_phy_next ACX_PACKED; /* 0x0c [txhostdesc *] */ + acx_ptr pNext ACX_PACKED; /* 0x10 [txhostdesc *] */ + u32 Status ACX_PACKED; /* 0x14, unused on Tx */ +/* From here on you can use this area as you want (variable length, too!) */ + u8 *data ACX_PACKED; +}; + +struct txdesc { + acx_ptr pNextDesc ACX_PACKED; /* pointer to next txdesc */ + acx_ptr HostMemPtr ACX_PACKED; /* 0x04 */ + acx_ptr AcxMemPtr ACX_PACKED; /* 0x08 */ + u32 tx_time ACX_PACKED; /* 0x0c */ + u16 total_length ACX_PACKED; /* 0x10 */ + u16 Reserved ACX_PACKED; /* 0x12 */ + /* the following 16 bytes do not change when acx100 owns the descriptor */ + /* we need to add a union here with a *fixed* size of 16, + * since ptrlen AMD64 (8) != ptrlen x86 (4) */ + union { /* 0x14 */ + struct { + struct client *txc ACX_PACKED; + struct txhostdesc *host_desc ACX_PACKED; + } s ACX_PACKED; + u32 dummy[4] ACX_PACKED; + } fixed_size ACX_PACKED; + u8 Ctl_8 ACX_PACKED; /* 0x24, 8bit value */ + u8 Ctl2_8 ACX_PACKED; /* 0x25, 8bit value */ + u8 error ACX_PACKED; /* 0x26 */ + u8 ack_failures ACX_PACKED; /* 0x27 */ + u8 rts_failures ACX_PACKED; /* 0x28 */ + u8 rts_ok ACX_PACKED; /* 0x29 */ + union { + struct { + u8 rate ACX_PACKED; /* 0x2a */ + u8 queue_ctrl ACX_PACKED; /* 0x2b */ + } r1 ACX_PACKED; + struct { + u16 rate111 ACX_PACKED; /* 0x2a */ + } r2 ACX_PACKED; + } u ACX_PACKED; + u32 queue_info ACX_PACKED; /* 0x2c (acx100, reserved on acx111) */ +}; /* size : 48 = 0x30 */ +/* NB: acx111 txdesc structure is 4 byte larger */ +/* All these 4 extra bytes are reserved. tx alloc code takes them into account */ + +struct rxhostdesc { + acx_ptr data_phy ACX_PACKED; /* 0x00 [rxbuffer_t *] */ + u16 data_offset ACX_PACKED; /* 0x04 */ + u16 reserved ACX_PACKED; /* 0x06 */ + u16 Ctl_16 ACX_PACKED; /* 0x08; 16bit value, endianness!! */ + u16 length ACX_PACKED; /* 0x0a */ + acx_ptr desc_phy_next ACX_PACKED; /* 0x0c [rxhostdesc_t *] */ + acx_ptr pNext ACX_PACKED; /* 0x10 [rxhostdesc_t *] */ + u32 Status ACX_PACKED; /* 0x14 */ +/* From here on you can use this area as you want (variable length, too!) */ + rxbuffer_t *data ACX_PACKED; +}; + +struct rxdesc { + acx_ptr pNextDesc ACX_PACKED; /* 0x00 */ + acx_ptr HostMemPtr ACX_PACKED; /* 0x04 */ + acx_ptr ACXMemPtr ACX_PACKED; /* 0x08 */ + u32 rx_time ACX_PACKED; /* 0x0c */ + u16 total_length ACX_PACKED; /* 0x10 */ + u16 WEP_length ACX_PACKED; /* 0x12 */ + u32 WEP_ofs ACX_PACKED; /* 0x14 */ + +/* the following 16 bytes do not change when acx100 owns the descriptor */ + u8 driverWorkspace[16] ACX_PACKED; /* 0x18 */ + + u8 Ctl_8 ACX_PACKED; + u8 rate ACX_PACKED; + u8 error ACX_PACKED; + u8 SNR ACX_PACKED; /* Signal-to-Noise Ratio */ + u8 RxLevel ACX_PACKED; + u8 queue_ctrl ACX_PACKED; + u16 unknown ACX_PACKED; + u32 unknown2 ACX_PACKED; +}; /* size 52 = 0x34 */ + +//TODO: incorporate into wlandevice_t +struct TIWLAN_DC { + wlandevice_t *priv; + /* pointer to the beginning of the card's tx queue pool. + it is relative to the internal memory mapping of the card! */ + u32 ui32ACXTxQueueStart; + /* pointer to the beginning of the card's rx queue pool. + it is relative to the internal memory mapping of the card! */ + u32 ui32ACXRxQueueStart; + /* pointers to tx buffers, tx host descriptors (in host memory) + ** and tx descrs in device memory */ + u8 *pTxBufferPool; + txhostdesc_t *pTxHostDescQPool; + txdesc_t *pTxDescQPool; /* points to PCI-mapped memory */ + /* same for rx */ + rxbuffer_t *pRxBufferPool; + rxhostdesc_t *pRxHostDescQPool; + rxdesc_t *pRxDescQPool; + /* physical addresses of above host memory areas */ + dma_addr_t RxBufferPoolPhyAddr; + dma_addr_t RxHostDescQPoolPhyAddr; + dma_addr_t TxBufferPoolPhyAddr; + dma_addr_t TxHostDescQPoolPhyAddr; + /* sizes of above host memory areas */ + unsigned int TxBufferPoolSize; + unsigned int TxHostDescQPoolSize; + unsigned int RxBufferPoolSize; + unsigned int RxHostDescQPoolSize; + + unsigned int TxDescrSize; /* size per tx descr; ACX111 = ACX100 + 4 */ + unsigned int tx_pool_count; /* indicates # of ring buffer pool entries */ + unsigned int tx_head; /* current ring buffer pool member index */ + unsigned int tx_tail; + unsigned int rx_pool_count; + unsigned int rx_tail; +}; + +/* figure out tx descriptor pointer, depending on different acx100 or acx111 + * tx descriptor length */ +#define GET_TX_DESC_PTR(dc, index) \ + (struct txdesc *) (((u8 *)(dc)->pTxDescQPool) + ((index) * (dc)->TxDescrSize)) +#define GET_NEXT_TX_DESC_PTR(dc, tx_desc) \ + (struct txdesc *) (((u8 *)(tx_desc)) + (dc)->TxDescrSize) + +#endif /* CONFIG_TIACX_PCI */ + +/*************************************************************** +** USB structures and constants +*/ +#ifdef ACX_USB + +/* Buffer size for fw upload */ +#define ACX100_USB_RWMEM_MAXLEN 2048 + +/* Should be sent to the ctrlout endpoint */ +#define ACX100_USB_ENBULKIN 6 + +/* The number of bulk URBs to use */ +#define ACX100_USB_NUM_BULK_URBS 8 + +/* Should be sent to the bulkout endpoint */ +#define ACX_USB_REQ_UPLOAD_FW 0x10 +#define ACX_USB_REQ_ACK_CS 0x11 +#define ACX_USB_REQ_CMD 0x12 + +/* Used for usb_txbuffer.desc field */ +#define USB_TXBUF_TXDESC 0xA +/* Used for usb_txbuffer.hostData field */ +#define USB_TXBUF_HD_ISDATA 0x10000 +#define USB_TXBUF_HD_DIRECTED 0x20000 +#define USB_TXBUF_HD_BROADCAST 0x40000 +/* Size of header (everything up to data[]) */ +#define USB_TXBUF_HDRSIZE 14 +typedef struct usb_txbuffer { + u16 desc ACX_PACKED; + u16 MPDUlen ACX_PACKED; + u8 index ACX_PACKED; + u8 txRate ACX_PACKED; + u32 hostData ACX_PACKED; + u8 ctrl1 ACX_PACKED; + u8 ctrl2 ACX_PACKED; + u16 dataLength ACX_PACKED; + /* wlan packet content is placed here: */ + u8 data[WLAN_A4FR_MAXLEN_WEP] ACX_PACKED; +} usb_txbuffer_t; + +typedef struct { + void *device; + int number; +} acx_usb_bulk_context_t; + +typedef struct usb_tx { + unsigned busy:1; + struct urb *urb; + wlandevice_t *priv; + client_t *txc; + /* actual USB bulk output data block is here: */ + usb_txbuffer_t bulkout; +} usb_tx_t; + +#endif /* CONFIG_TIACX_USB */ + + +/*============================================================================* + * Main acx per-device data structure (netdev->priv) * + *============================================================================*/ +#define ACX_STATE_FW_LOADED 0x01 +#define ACX_STATE_IFACE_UP 0x02 + +/* MAC mode (BSS type) defines + * Note that they shouldn't be redefined, since they are also used + * during communication with firmware */ +#define ACX_MODE_0_ADHOC 0 +#define ACX_MODE_1_UNUSED 1 +#define ACX_MODE_2_STA 2 +#define ACX_MODE_3_AP 3 +/* These are our own inventions. Sending these to firmware +** makes it stop emitting beacons, which is exactly what we want +** for these modes */ +#define ACX_MODE_MONITOR 0xfe +#define ACX_MODE_OFF 0xff +/* 'Submode': identifies exact status of ADHOC/STA host */ +#define ACX_STATUS_0_STOPPED 0 +#define ACX_STATUS_1_SCANNING 1 +#define ACX_STATUS_2_WAIT_AUTH 2 +#define ACX_STATUS_3_AUTHENTICATED 3 +#define ACX_STATUS_4_ASSOCIATED 4 + +/* FIXME: this should be named something like struct acx_priv (typedef'd to + * acx_priv_t) */ + +/* non-firmware struct, no packing necessary */ +struct wlandevice { + /*** Device chain ***/ + struct wlandevice *next; /* link for list of devices */ + + /*** Linux network device ***/ + struct net_device *netdev; /* pointer to linux netdevice */ + struct net_device *prev_nd; /* FIXME: We should not chain via our + * private struct wlandevice _and_ + * the struct net_device. */ + /*** Device statistics ***/ + struct net_device_stats stats; /* net device statistics */ +#ifdef WIRELESS_EXT + struct iw_statistics wstats; /* wireless statistics */ +#endif + /*** Power managment ***/ + struct pm_dev *pm; /* PM crap */ + + /*** Management timer ***/ + struct timer_list mgmt_timer; + + /*** Locking ***/ + struct semaphore sem; + spinlock_t lock; +#if defined(PARANOID_LOCKING) /* Lock debugging */ + const char *last_sem; + const char *last_lock; + unsigned long sem_time; + unsigned long lock_time; +#endif + + /*** Hardware identification ***/ + const char *chip_name; + u8 chip_type; + u8 form_factor; + u8 radio_type; + u8 eeprom_version; + + /*** Firmware identification ***/ + char firmware_version[FW_ID_SIZE+1]; + u32 firmware_numver; + u32 firmware_id; + + /*** Device state ***/ + u16 dev_state_mask; + u8 led_power; /* power LED status */ + u32 get_mask; /* mask of settings to fetch from the card */ + u32 set_mask; /* mask of settings to write to the card */ + + /* Barely used in USB case */ + u16 irq_status; + + u8 after_interrupt_jobs; /* mini job list for doing actions after an interrupt occurred */ + WORK_STRUCT after_interrupt_task; /* our task for after interrupt actions */ + + /*** scanning ***/ + u16 scan_count; /* number of times to do channel scan */ + u8 scan_mode; /* 0 == active, 1 == passive, 2 == background */ + u8 scan_rate; + u16 scan_duration; + u16 scan_probe_delay; +#if WIRELESS_EXT > 15 + struct iw_spy_data spy_data; /* FIXME: needs to be implemented! */ +#endif + + /*** Wireless network settings ***/ + /* copy of the device address (ifconfig hw ether) that we actually use + ** for 802.11; copied over from the network device's MAC address + ** (ifconfig) when it makes sense only */ + u8 dev_addr[MAX_ADDR_LEN]; + u8 bssid[ETH_ALEN]; /* the BSSID after having joined */ + u8 ap[ETH_ALEN]; /* The AP we want, FF:FF:FF:FF:FF:FF is any */ + u16 aid; /* The Association ID sent from the AP / last used AID if we're an AP */ + u16 mode; /* mode from iwconfig */ + u16 status; /* 802.11 association status */ + u8 essid_active; /* specific ESSID active, or select any? */ + u8 essid_len; /* to avoid dozens of strlen() */ + /* INCLUDES \0 termination for easy printf - but many places + ** simply want the string data memcpy'd plus a length indicator! + ** Keep that in mind... */ + char essid[IW_ESSID_MAX_SIZE+1]; + /* essid we are going to use for association, in case of "essid 'any'" + ** and in case of hidden ESSID (use configured ESSID then) */ + char essid_for_assoc[IW_ESSID_MAX_SIZE+1]; + char nick[IW_ESSID_MAX_SIZE+1]; /* see essid! */ + u8 channel; + u8 reg_dom_id; /* reg domain setting */ + u16 reg_dom_chanmask; + u16 auth_or_assoc_retries; + u16 scan_retries; + unsigned long scan_start; /* YES, jiffies is defined as "unsigned long" */ + + /* stations known to us (if we're an ap) */ + client_t sta_list[32]; /* tab is larger than list, so that */ + client_t *sta_hash_tab[64]; /* hash collisions are not likely */ + client_t *ap_client; /* this one is our AP (STA mode only) */ + + unsigned long dup_msg_expiry; + int dup_count; + u16 last_seq_ctrl; /* duplicate packet detection */ + + /* 802.11 power save mode */ + u8 ps_wakeup_cfg; + u8 ps_listen_interval; + u8 ps_options; + u8 ps_hangover_period; + u16 ps_enhanced_transition_time; + + /*** PHY settings ***/ + u8 fallback_threshold; + u8 stepup_threshold; + u16 rate_basic; + u16 rate_oper; + u16 rate_bcast; + u16 rate_bcast100; + u8 rate_auto; /* false if "iwconfig rate N" (WITHOUT 'auto'!) */ + u8 preamble_mode; /* 0 == Long Preamble, 1 == Short, 2 == Auto */ + u8 preamble_cur; + + u8 tx_disabled; + u8 tx_level_dbm; + u8 tx_level_val; + u8 tx_level_auto; /* whether to do automatic power adjustment */ + + unsigned long recalib_time_last_success; + unsigned long recalib_time_last_attempt; + int recalib_failure_count; + int recalib_msg_ratelimit; + int retry_errors_msg_ratelimit; + + unsigned long brange_time_last_state_change; /* time the power LED was last changed */ + u8 brange_last_state; /* last state of the LED */ + u8 brange_max_quality; /* maximum quality that equates to full speed */ + + u8 sensitivity; + u8 antenna; /* antenna settings */ + u8 ed_threshold; /* energy detect threshold */ + u8 cca; /* clear channel assessment */ + + u16 rts_threshold; + u32 short_retry; + u32 long_retry; + u16 msdu_lifetime; + u16 listen_interval; /* given in units of beacon interval */ + u32 beacon_interval; + + u16 capabilities; + u8 capab_short; + u8 capab_pbcc; + u8 capab_agility; + u8 rate_supported_len; + u8 rate_supported[13]; + + /*** Encryption settings (WEP) ***/ + u32 auth_alg; /* used in transmit_authen1 */ + u8 wep_enabled; + u8 wep_restricted; + u8 wep_current_index; + wep_key_t wep_keys[DOT11_MAX_DEFAULT_WEP_KEYS]; /* the default WEP keys */ + key_struct_t wep_key_struct[10]; + + /*** Card Rx/Tx management ***/ + u16 rx_config_1; + u16 rx_config_2; + u32 tx_cnt_done; + u16 memblocksize; + u32 RxQueueCnt; + u32 TxQueueCnt; + u32 TxQueueFree; + + /*** Unknown ***/ + u8 dtim_interval; + +/*** PCI/USB/... must be last or else hw agnostic code breaks horribly ***/ + + /*** PCI stuff ***/ +#ifdef ACX_PCI + TIWLAN_DC dc; + /* Same as dc.pRxHostDescQPool, but possibly aligned to 4 bytes: */ + rxhostdesc_t *RxHostDescPoolStart; + + u8 need_radio_fw; + u8 irqs_active; /* whether irq sending is activated */ + + const u16 *io; /* points to ACX100 or ACX111 I/O register address set */ + + struct pci_dev *pdev; + + unsigned long membase; + unsigned long membase2; + void *iobase; + void *iobase2; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) + /* 2.6.9-rc3-mm2 (2.6.9-bk4, too) introduced a shorter API version, + then it made its way into 2.6.10 */ + u32 pci_state[16]; /* saved PCI state for suspend/resume */ +#endif + u16 irq_mask; /* interrupt types to mask out (not wanted) with many IRQs activated */ + u16 irq_mask_off; /* interrupt types to mask out (not wanted) with IRQs off */ + unsigned int irq_loops_this_jiffy; + unsigned long irq_last_jiffies; + + /* command interface */ + void *cmd_area; /* points to PCI mapped memory */ + void *info_area; /* points to PCI mapped memory */ + u16 cmd_type; + u16 cmd_status; + u16 info_type; + u16 info_status; +#endif + + /*** USB stuff ***/ +#ifdef ACX_USB + struct usb_device *usbdev; + + rxbuffer_t rxtruncbuf; + /* TODO: convert (bulkins,bulkrx_urbs,rxcons) triple into + ** struct usb_rx (see struct usb_tx for an example) */ + rxbuffer_t bulkins[ACX100_USB_NUM_BULK_URBS]; + struct urb *bulkrx_urbs[ACX100_USB_NUM_BULK_URBS]; + /* Used by rx urb completion handler in order to find + ** corresponding priv/index pair */ + acx_usb_bulk_context_t rxcons[ACX100_USB_NUM_BULK_URBS]; + usb_tx_t usb_tx[ACX100_USB_NUM_BULK_URBS]; + + int bulkinep; /* bulk-in endpoint */ + int bulkoutep; /* bulk-out endpoint */ + int usb_free_tx; + int rxtruncsize; +#endif + +}; + +/* For use with ACX1xx_IE_RXCONFIG */ +/* bit description + * 13 include additional header (length etc.) *required* + * struct is defined in 'struct rxbuffer' + * is this bit acx100 only? does acx111 always put the header, + * and bit setting is irrelevant? --vda + * 10 receive frames only with SSID used in last join cmd + * 9 discard broadcast + * 8 receive packets for multicast address 1 + * 7 receive packets for multicast address 0 + * 6 discard all multicast packets + * 5 discard frames from foreign BSSID + * 4 discard frames with foreign destination MAC address + * 3 promiscuous mode (receive ALL frames, disable filter) + * 2 include FCS + * 1 include phy header + * 0 ??? + */ +#define RX_CFG1_INCLUDE_RXBUF_HDR 0x2000 /* ACX100 only */ +#define RX_CFG1_FILTER_SSID 0x0400 +#define RX_CFG1_FILTER_BCAST 0x0200 +#define RX_CFG1_RCV_MC_ADDR1 0x0100 +#define RX_CFG1_RCV_MC_ADDR0 0x0080 +#define RX_CFG1_FILTER_ALL_MULTI 0x0040 +#define RX_CFG1_FILTER_BSSID 0x0020 +#define RX_CFG1_FILTER_MAC 0x0010 +#define RX_CFG1_RCV_PROMISCUOUS 0x0008 +#define RX_CFG1_INCLUDE_FCS 0x0004 +#define RX_CFG1_INCLUDE_PHY_HDR (WANT_PHY_HDR ? 0x0002 : 0) +/* bit description + * 11 receive association requests etc. + * 10 receive authentication frames + * 9 receive beacon frames + * 8 receive contention free packets + * 7 receive control frames + * 6 receive data frames + * 5 receive broken frames + * 4 receive management frames + * 3 receive probe requests + * 2 receive probe responses + * 1 receive RTS/CTS/ACK frames + * 0 receive other + */ +#define RX_CFG2_RCV_ASSOC_REQ 0x0800 +#define RX_CFG2_RCV_AUTH_FRAMES 0x0400 +#define RX_CFG2_RCV_BEACON_FRAMES 0x0200 +#define RX_CFG2_RCV_CONTENTION_FREE 0x0100 +#define RX_CFG2_RCV_CTRL_FRAMES 0x0080 +#define RX_CFG2_RCV_DATA_FRAMES 0x0040 +#define RX_CFG2_RCV_BROKEN_FRAMES 0x0020 +#define RX_CFG2_RCV_MGMT_FRAMES 0x0010 +#define RX_CFG2_RCV_PROBE_REQ 0x0008 +#define RX_CFG2_RCV_PROBE_RESP 0x0004 +#define RX_CFG2_RCV_ACK_FRAMES 0x0002 +#define RX_CFG2_RCV_OTHER 0x0001 + +/* For use with ACX1xx_IE_FEATURE_CONFIG */ +#define FEATURE1_80MHZ_CLOCK 0x00000040L +#define FEATURE1_4X 0x00000020L +#define FEATURE1_LOW_RX 0x00000008L +#define FEATURE1_EXTRA_LOW_RX 0x00000001L + +#define FEATURE2_SNIFFER 0x00000080L +#define FEATURE2_NO_TXCRYPT 0x00000001L + +/*-- get and set mask values --*/ +#define GETSET_LED_POWER 0x00000001L +#define GETSET_STATION_ID 0x00000002L +#define SET_TEMPLATES 0x00000004L +#define SET_STA_LIST 0x00000008L +#define GETSET_TX 0x00000010L +#define GETSET_RX 0x00000020L +#define SET_RXCONFIG 0x00000040L +#define GETSET_ANTENNA 0x00000080L +#define GETSET_SENSITIVITY 0x00000100L +#define GETSET_TXPOWER 0x00000200L +#define GETSET_ED_THRESH 0x00000400L +#define GETSET_CCA 0x00000800L +#define GETSET_POWER_80211 0x00001000L +#define GETSET_RETRY 0x00002000L +#define GETSET_REG_DOMAIN 0x00004000L +#define GETSET_CHANNEL 0x00008000L +/* Used when ESSID changes etc and we need to scan for AP anew */ +#define GETSET_RESCAN 0x00010000L +#define GETSET_MODE 0x00020000L +#define GETSET_WEP 0x00040000L +#define SET_WEP_OPTIONS 0x00080000L +#define SET_MSDU_LIFETIME 0x00100000L +#define SET_RATE_FALLBACK 0x00200000L +#define GETSET_ALL 0x80000000L + + +/*============================================================================* + * Firmware loading * + *============================================================================*/ +/* Doh, 2.4.x also has CONFIG_FW_LOADER_MODULE + * (but doesn't have the new device model yet which we require!) + * FIXME: exact version that introduced new device handling? */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) +#define USE_FW_LOADER_26 1 +#define USE_FW_LOADER_LEGACY 0 +#else +#define USE_FW_LOADER_26 0 +#define USE_FW_LOADER_LEGACY 1 +#endif +#endif + +#if USE_FW_LOADER_26 +#include /* request_firmware() */ +#include /* struct pci_device */ +#endif + +#if USE_FW_LOADER_LEGACY +extern char *firmware_dir; +#endif + + +/*********************************************************************** +*/ +typedef struct acx100_ie_memblocksize { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u16 size ACX_PACKED; +} acx100_ie_memblocksize_t; + +typedef struct acx100_ie_queueconfig { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u32 AreaSize ACX_PACKED; + u32 RxQueueStart ACX_PACKED; + u8 QueueOptions ACX_PACKED; /* queue options */ + u8 NumTxQueues ACX_PACKED; /* # tx queues */ + u8 NumRxDesc ACX_PACKED; /* for USB only */ + u8 padf2 ACX_PACKED; /* # rx buffers */ + u32 QueueEnd ACX_PACKED; + u32 HostQueueEnd ACX_PACKED; /* QueueEnd2 */ + u32 TxQueueStart ACX_PACKED; + u8 TxQueuePri ACX_PACKED; + u8 NumTxDesc ACX_PACKED; + u16 pad ACX_PACKED; +} acx100_ie_queueconfig_t; + +typedef struct acx111_ie_queueconfig { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u32 tx_memory_block_address ACX_PACKED; + u32 rx_memory_block_address ACX_PACKED; + u32 rx1_queue_address ACX_PACKED; + u32 reserved1 ACX_PACKED; + u32 tx1_queue_address ACX_PACKED; + u8 tx1_attributes ACX_PACKED; + u16 reserved2 ACX_PACKED; + u8 reserved3 ACX_PACKED; +} acx111_ie_queueconfig_t; + +typedef struct acx100_ie_memconfigoption { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u32 DMA_config ACX_PACKED; + u32 pRxHostDesc ACX_PACKED; + u32 rx_mem ACX_PACKED; + u32 tx_mem ACX_PACKED; + u16 RxBlockNum ACX_PACKED; + u16 TxBlockNum ACX_PACKED; +} acx100_ie_memconfigoption_t; + +typedef struct acx111_ie_memoryconfig { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u16 no_of_stations ACX_PACKED; + u16 memory_block_size ACX_PACKED; + u8 tx_rx_memory_block_allocation ACX_PACKED; + u8 count_rx_queues ACX_PACKED; + u8 count_tx_queues ACX_PACKED; + u8 options ACX_PACKED; + u8 fragmentation ACX_PACKED; + u16 reserved1 ACX_PACKED; + u8 reserved2 ACX_PACKED; + + /* start of rx1 block */ + u8 rx_queue1_count_descs ACX_PACKED; + u8 rx_queue1_reserved1 ACX_PACKED; + u8 rx_queue1_type ACX_PACKED; /* must be set to 7 */ + u8 rx_queue1_prio ACX_PACKED; /* must be set to 0 */ + u32 rx_queue1_host_rx_start ACX_PACKED; + /* end of rx1 block */ + + /* start of tx1 block */ + u8 tx_queue1_count_descs ACX_PACKED; + u8 tx_queue1_reserved1 ACX_PACKED; + u8 tx_queue1_reserved2 ACX_PACKED; + u8 tx_queue1_attributes ACX_PACKED; + /* end of tx1 block */ +} acx111_ie_memoryconfig_t; + +typedef struct acx_ie_memmap { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u32 CodeStart ACX_PACKED; + u32 CodeEnd ACX_PACKED; + u32 WEPCacheStart ACX_PACKED; + u32 WEPCacheEnd ACX_PACKED; + u32 PacketTemplateStart ACX_PACKED; + u32 PacketTemplateEnd ACX_PACKED; + u32 QueueStart ACX_PACKED; + u32 QueueEnd ACX_PACKED; + u32 PoolStart ACX_PACKED; + u32 PoolEnd ACX_PACKED; +} acx_ie_memmap_t; + +typedef struct ACX111FeatureConfig { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u32 feature_options ACX_PACKED; + u32 data_flow_options ACX_PACKED; +} ACX111FeatureConfig_t; + +typedef struct ACX111TxLevel { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u8 level ACX_PACKED; +} ACX111TxLevel_t; + +////typedef struct associd { +//// u16 vala ACX_PACKED; +////} associd_t; + +#define PS_CFG_ENABLE 0x80 +#define PS_CFG_PENDING 0x40 /* status flag when entering PS */ +#define PS_CFG_WAKEUP_MODE_MASK 0x07 +#define PS_CFG_WAKEUP_BY_HOST 0x03 +#define PS_CFG_WAKEUP_EACH_ITVL 0x02 +#define PS_CFG_WAKEUP_ON_DTIM 0x01 +#define PS_CFG_WAKEUP_ALL_BEAC 0x00 + +/* Enhanced PS mode: sleep until Rx Beacon w/ the STA's AID bit set +** in the TIM; newer firmwares only(?) */ +#define PS_OPT_ENA_ENHANCED_PS 0x04 +#define PS_OPT_STILL_RCV_BCASTS 0x01 + +typedef struct acx100_ie_powermgmt { + u32 type ACX_PACKED; + u32 len ACX_PACKED; + u8 wakeup_cfg ACX_PACKED; + u8 listen_interval ACX_PACKED; /* for EACH_ITVL: wake up every "beacon units" interval */ + u8 options ACX_PACKED; + u8 hangover_period ACX_PACKED; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */ + u16 enhanced_ps_transition_time ACX_PACKED; /* rem. wake time for Enh. PS */ +} acx100_ie_powermgmt_t; + +typedef struct acx111_ie_powermgmt { + u32 type ACX_PACKED; + u32 len ACX_PACKED; + u8 wakeup_cfg ACX_PACKED; + u8 listen_interval ACX_PACKED; /* for EACH_ITVL: wake up every "beacon units" interval */ + u8 options ACX_PACKED; + u8 hangover_period ACX_PACKED; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */ + u32 beaconRxTime ACX_PACKED; + u32 enhanced_ps_transition_time ACX_PACKED; /* rem. wake time for Enh. PS */ +} acx111_ie_powermgmt_t; + + +/*********************************************************************** +** Commands and template structures +*/ + +/* +** SCAN command structure +** +** even though acx100 scan rates match RATE100 constants, +** acx111 ones do not match! Therefore we do not use RATE100 #defines */ +#define ACX_SCAN_RATE_1 10 +#define ACX_SCAN_RATE_2 20 +#define ACX_SCAN_RATE_5 55 +#define ACX_SCAN_RATE_11 110 +#define ACX_SCAN_RATE_22 220 +#define ACX_SCAN_OPT_ACTIVE 0x00 /* a bit mask */ +#define ACX_SCAN_OPT_PASSIVE 0x01 +/* Background scan: we go into Power Save mode (by transmitting +** NULL data frame to AP with the power mgmt bit set), do the scan, +** and then exit Power Save mode. A plus is that AP buffers frames +** for us while we do background scan. Thus we avoid frame losses. +** Background scan can be active or passive, just like normal one */ +#define ACX_SCAN_OPT_BACKGROUND 0x02 +typedef struct acx100_scan { + u16 count ACX_PACKED; /* number of scans to do, 0xffff == continuous */ + u16 start_chan ACX_PACKED; + u16 flags ACX_PACKED; /* channel list mask; 0x8000 == all channels? */ + u8 max_rate ACX_PACKED; /* max. probe rate */ + u8 options ACX_PACKED; /* bit mask, see defines above */ + u16 chan_duration ACX_PACKED; + u16 max_probe_delay ACX_PACKED; +} acx100_scan_t; /* length 0xc */ + +#define ACX111_SCAN_RATE_6 0x0B +#define ACX111_SCAN_RATE_9 0x0F +#define ACX111_SCAN_RATE_12 0x0A +#define ACX111_SCAN_RATE_18 0x0E +#define ACX111_SCAN_RATE_24 0x09 +#define ACX111_SCAN_RATE_36 0x0D +#define ACX111_SCAN_RATE_48 0x08 +#define ACX111_SCAN_RATE_54 0x0C +#define ACX111_SCAN_OPT_5GHZ 0x04 /* else 2.4GHZ */ +#define ACX111_SCAN_MOD_SHORTPRE 0x01 /* you can combine SHORTPRE and PBCC */ +#define ACX111_SCAN_MOD_PBCC 0x80 +#define ACX111_SCAN_MOD_OFDM 0x40 +typedef struct acx111_scan { + u16 count ACX_PACKED; /* number of scans to do */ + u8 channel_list_select ACX_PACKED; /* 0: scan all channels, 1: from chan_list only */ + u16 reserved1 ACX_PACKED; + u8 reserved2 ACX_PACKED; + u8 rate ACX_PACKED; /* rate for probe requests (if active scan) */ + u8 options ACX_PACKED; /* bit mask, see defines above */ + u16 chan_duration ACX_PACKED; /* min time to wait for reply on one channel (in TU) */ + /* (active scan only) (802.11 section 11.1.3.2.2) */ + u16 max_probe_delay ACX_PACKED; /* max time to wait for reply on one channel (active scan) */ + /* time to listen on a channel (passive scan) */ + u8 modulation ACX_PACKED; + u8 channel_list[26] ACX_PACKED; /* bits 7:0 first byte: channels 8:1 */ + /* bits 7:0 second byte: channels 16:9 */ + /* 26 bytes is enough to cover 802.11a */ +} acx111_scan_t; + + +/* +** Radio calibration command structure +*/ +typedef struct acx111_cmd_radiocalib { +/* 0x80000000 == automatic calibration by firmware, according to interval; + * bits 0..3: select calibration methods to go through: + * calib based on DC, AfeDC, Tx mismatch, Tx equilization */ + u32 methods ACX_PACKED; + u32 interval ACX_PACKED; +} acx111_cmd_radiocalib_t; + + +/* +** Packet template structures +** +** Packet templates store contents of Beacon, Probe response, Probe request, +** Null data frame, and TIM data frame. Firmware automatically transmits +** contents of template at appropriate time: +** - Beacon: when configured as AP or Ad-hoc +** - Probe response: when configured as AP or Ad-hoc, whenever +** a Probe request frame is received +** - Probe request: when host issues SCAN command (active) +** - Null data frame: when entering 802.11 power save mode +** - TIM data: at the end of Beacon frames (if no TIM template +** is configured, then transmits default TIM) +** NB: +** - size field must be set to size of actual template +** (NOT sizeof(struct) - templates are variable in length), +** size field is not itself counted. +** - members flagged with an asterisk must be initialized with host, +** rest must be zero filled. +** - variable length fields shown only in comments */ +typedef struct acx_template_tim { + u16 size ACX_PACKED; + u8 tim_eid ACX_PACKED; /* 00 1 TIM IE ID * */ + u8 len ACX_PACKED; /* 01 1 Length * */ + u8 dtim_cnt ACX_PACKED; /* 02 1 DTIM Count */ + u8 dtim_period ACX_PACKED; /* 03 1 DTIM Period */ + u8 bitmap_ctrl ACX_PACKED; /* 04 1 Bitmap Control * (except bit0) */ + /* 05 n Partial Virtual Bitmap * */ + u8 variable[0x100 - 1-1-1-1-1] ACX_PACKED; +} acx_template_tim_t; + +typedef struct acx100_template_probereq { + u16 size ACX_PACKED; + u16 fc ACX_PACKED; /* 00 2 fc */ + u16 dur ACX_PACKED; /* 02 2 Duration */ + u8 da[6] ACX_PACKED; /* 04 6 Destination Address * */ + u8 sa[6] ACX_PACKED; /* 0A 6 Source Address * */ + u8 bssid[6] ACX_PACKED; /* 10 6 BSSID * */ + u16 seq ACX_PACKED; /* 16 2 Sequence Control */ + u8 timestamp[8] ACX_PACKED;/* 18 8 Timestamp */ + u16 beacon_interval ACX_PACKED; /* 20 2 Beacon Interval * */ + u16 cap ACX_PACKED; /* 22 2 Capability Information * */ + /* 24 n SSID * */ + /* nn n Supported Rates * */ + u8 variable[0x44 - 2-2-6-6-6-2-8-2-2] ACX_PACKED; +} acx100_template_probereq_t; + +typedef struct acx111_template_probereq { + u16 size ACX_PACKED; + u16 fc ACX_PACKED; /* 00 2 fc * */ + u16 dur ACX_PACKED; /* 02 2 Duration */ + u8 da[6] ACX_PACKED; /* 04 6 Destination Address * */ + u8 sa[6] ACX_PACKED; /* 0A 6 Source Address * */ + u8 bssid[6] ACX_PACKED; /* 10 6 BSSID * */ + u16 seq ACX_PACKED; /* 16 2 Sequence Control */ + /* 18 n SSID * */ + /* nn n Supported Rates * */ + u8 variable[0x44 - 2-2-6-6-6-2] ACX_PACKED; +} acx111_template_probereq_t; + +typedef struct acx_template_proberesp { + u16 size ACX_PACKED; + u16 fc ACX_PACKED; /* 00 2 fc * (bits [15:12] and [10:8] per 802.11 section 7.1.3.1) */ + u16 dur ACX_PACKED; /* 02 2 Duration */ + u8 da[6] ACX_PACKED; /* 04 6 Destination Address */ + u8 sa[6] ACX_PACKED; /* 0A 6 Source Address */ + u8 bssid[6] ACX_PACKED; /* 10 6 BSSID */ + u16 seq ACX_PACKED; /* 16 2 Sequence Control */ + u8 timestamp[8] ACX_PACKED;/* 18 8 Timestamp */ + u16 beacon_interval ACX_PACKED; /* 20 2 Beacon Interval * */ + u16 cap ACX_PACKED; /* 22 2 Capability Information * */ + /* 24 n SSID * */ + /* nn n Supported Rates * */ + /* nn 1 DS Parameter Set * */ + u8 variable[0x54 - 2-2-6-6-6-2-8-2-2] ACX_PACKED; +} acx_template_proberesp_t; +#define acx_template_beacon_t acx_template_proberesp_t +#define acx_template_beacon acx_template_proberesp + + +/* +** JOIN command structure +** +** as opposed to acx100, acx111 dtim interval is AFTER rates_basic111. +** NOTE: took me about an hour to get !@#$%^& packing right --> struct packing is eeeeevil... */ +typedef struct acx_joinbss { + u8 bssid[ETH_ALEN] ACX_PACKED; + u16 beacon_interval ACX_PACKED; + union { + struct { + u8 dtim_interval ACX_PACKED; + u8 rates_basic ACX_PACKED; + u8 rates_supported ACX_PACKED; + } acx100 ACX_PACKED; + struct { + u16 rates_basic ACX_PACKED; + u8 dtim_interval ACX_PACKED; + } acx111 ACX_PACKED; + } u ACX_PACKED; + u8 genfrm_txrate ACX_PACKED; /* generated frame (bcn, proberesp, RTS, PSpoll) tx rate */ + u8 genfrm_mod_pre ACX_PACKED; /* generated frame modulation/preamble: + ** bit7: PBCC, bit6: OFDM (else CCK/DQPSK/DBPSK) + ** bit5: short pre */ + u8 macmode ACX_PACKED; /* BSS Type, must be one of ACX_MODE_xxx */ + u8 channel ACX_PACKED; + u8 essid_len ACX_PACKED; + char essid[IW_ESSID_MAX_SIZE] ACX_PACKED; +} acx_joinbss_t; + +#define JOINBSS_RATES_1 0x01 +#define JOINBSS_RATES_2 0x02 +#define JOINBSS_RATES_5 0x04 +#define JOINBSS_RATES_11 0x08 +#define JOINBSS_RATES_22 0x10 + +/* Looks like missing bits are used to indicate 11g rates! +** (it follows from the fact that constants below match 1:1 to RATE111_nn) +** This was actually seen! Look at that Assoc Request sent by acx111, +** it _does_ contain 11g rates in basic set: +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 +01:30:20.074425 Authentication (Open System)-1: Succesful +01:30:20.076539 Authentication (Open System)-2: +01:30:20.076620 Acknowledgment +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] +01:30:20.122413 Assoc Response AID(1) :: Succesful +01:30:20.122679 Acknowledgment +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 +*/ +#define JOINBSS_RATES_BASIC111_1 0x0001 +#define JOINBSS_RATES_BASIC111_2 0x0002 +#define JOINBSS_RATES_BASIC111_5 0x0004 +#define JOINBSS_RATES_BASIC111_11 0x0020 +#define JOINBSS_RATES_BASIC111_22 0x0100 + + +/*********************************************************************** +*/ +typedef struct mem_read_write { + u16 addr ACX_PACKED; + u16 type ACX_PACKED; /* 0x0 int. RAM / 0xffff MAC reg. / 0x81 PHY RAM / 0x82 PHY reg. */ + u32 len ACX_PACKED; + u32 data ACX_PACKED; +} mem_read_write_t; + +typedef struct acxp80211_nullframe { + u16 size ACX_PACKED; + struct wlan_hdr_a3 hdr ACX_PACKED; +} acxp80211_nullframe_t; + +typedef struct { + u32 chksum ACX_PACKED; + u32 size ACX_PACKED; + u8 data[1] ACX_PACKED; /* the byte array of the actual firmware... */ +} firmware_image_t; + +typedef struct { + u32 offset ACX_PACKED; + u32 len ACX_PACKED; +} acx_cmd_radioinit_t; + +typedef struct acx100_ie_wep_options { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u16 NumKeys ACX_PACKED; /* max # of keys */ + u8 WEPOption ACX_PACKED; /* 0 == decrypt default key only, 1 == override decrypt */ + u8 Pad ACX_PACKED; /* used only for acx111 */ +} acx100_ie_wep_options_t; + +typedef struct ie_dot11WEPDefaultKey { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u8 action ACX_PACKED; + u8 keySize ACX_PACKED; + u8 defaultKeyNum ACX_PACKED; + u8 key[29] ACX_PACKED; /* check this! was Key[19]. */ +} ie_dot11WEPDefaultKey_t; + +typedef struct acx111WEPDefaultKey { + u8 MacAddr[ETH_ALEN] ACX_PACKED; + u16 action ACX_PACKED; /* NOTE: this is a u16, NOT a u8!! */ + u16 reserved ACX_PACKED; + u8 keySize ACX_PACKED; + u8 type ACX_PACKED; + u8 index ACX_PACKED; + u8 defaultKeyNum ACX_PACKED; + u8 counter[6] ACX_PACKED; + u8 key[32] ACX_PACKED; /* up to 32 bytes (for TKIP!) */ +} acx111WEPDefaultKey_t; + +typedef struct ie_dot11WEPDefaultKeyID { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u8 KeyID ACX_PACKED; +} ie_dot11WEPDefaultKeyID_t; + +typedef struct acx100_cmd_wep_mgmt { + u8 MacAddr[ETH_ALEN] ACX_PACKED; + u16 Action ACX_PACKED; + u16 KeySize ACX_PACKED; + u8 Key[29] ACX_PACKED; /* 29*8 == 232bits == WEP256 */ +} acx100_cmd_wep_mgmt_t; + +typedef struct defaultkey { + u8 num; +} defaultkey_t; + +typedef struct acx_ie_generic { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + union { + /* struct wep wp ACX_PACKED; */ + ////struct associd asid ACX_PACKED; + /* Association ID IE: just a 16bit value: */ + u16 aid; + /* UNUSED? struct defaultkey dkey ACX_PACKED; */ + /* generic member for quick implementation of commands */ + u8 bytes[32] ACX_PACKED; + } m ACX_PACKED; +} acx_ie_generic_t; + +/* Config Option structs */ + +typedef struct co_antennas { + u8 type ACX_PACKED; + u8 len ACX_PACKED; + u8 list[2] ACX_PACKED; +} co_antennas_t; + +typedef struct co_powerlevels { + u8 type ACX_PACKED; + u8 len ACX_PACKED; + u16 list[8] ACX_PACKED; +} co_powerlevels_t; + +typedef struct co_datarates { + u8 type ACX_PACKED; + u8 len ACX_PACKED; + u8 list[8] ACX_PACKED; +} co_datarates_t; + +typedef struct co_domains { + u8 type ACX_PACKED; + u8 len ACX_PACKED; + u8 list[6] ACX_PACKED; +} co_domains_t; + +typedef struct co_product_id { + u8 type ACX_PACKED; + u8 len ACX_PACKED; + u8 list[128] ACX_PACKED; +} co_product_id_t; + +typedef struct co_manuf_id { + u8 type ACX_PACKED; + u8 len ACX_PACKED; + u8 list[128] ACX_PACKED; +} co_manuf_t; + +typedef struct co_fixed { + u8 type ACX_PACKED; + u8 len ACX_PACKED; + char NVSv[8] ACX_PACKED; + u8 MAC[6] ACX_PACKED; + u16 probe_delay ACX_PACKED; + u32 eof_memory ACX_PACKED; + u8 dot11CCAModes ACX_PACKED; + u8 dot11Diversity ACX_PACKED; + u8 dot11ShortPreambleOption ACX_PACKED; + u8 dot11PBCCOption ACX_PACKED; + u8 dot11ChannelAgility ACX_PACKED; + u8 dot11PhyType ACX_PACKED; +/* u8 dot11TempType ACX_PACKED; + u8 num_var ACX_PACKED; seems to be erased */ +} co_fixed_t; + + +typedef struct acx111_ie_configoption { + co_fixed_t configoption_fixed ACX_PACKED; + co_antennas_t antennas ACX_PACKED; + co_powerlevels_t power_levels ACX_PACKED; + co_datarates_t data_rates ACX_PACKED; + co_domains_t domains ACX_PACKED; + co_product_id_t product_id ACX_PACKED; + co_manuf_t manufacturer ACX_PACKED; +} acx111_ie_configoption_t; + + +/*============================================================================* + * Global data * + *============================================================================*/ +extern const u8 bitpos2ratebyte[]; +extern const u8 bitpos2rate100[]; + +extern const struct iw_handler_def acx_ioctl_handler_def; + +#define MINFREE_TX 3 diff -puN /dev/null drivers/net/wireless/tiacx/Changelog --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/Changelog 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,136 @@ +[20050823] +* driver submitted for kernel inclusion +* modules and config entries got TI prefix added to their names +* #defines renamed: CONFIG_ACX_{PCI,USB} -> ACX_{PCI,USB} + (needed for correct compilation of two modules from the same source) +* debug module parameter renamed to acx_debug (name clash in + non-modular build) +* ACX_DEBUG lowered to 1, PARANOID_LOCKING is off by default now +* version bump to 0.3.0 +* sizes: + text data bss dec hex filename + 78242 264 8 78514 132b2 drivers/net/wireless/tiacx/tiacx_pci.o + 60622 208 20 60850 edb2 drivers/net/wireless/tiacx/tiacx_usb.o + +[20050822] +* idma.c is incorporated into helper.c +* lots of cosmetic work: comments, struct member shuffling, + etc. TIWLAN_DC and other PCI data structs are #ifdef'ed + to be PCI-only. A few todos added. Extra #include's removed. +* added sem locking debug printout code (for amd64 debug) +* beacon tx rate and beacon ratevector is updated according to + "iwconfig rate" command immediately (required mode change before). + +[20050821] +* Switch to wrapper functions for dealing with "tx buffers" + (memory areas where we create packets for tx. For USB, it's just + a part of wlandevice_t, for PCI it's a DMAable buffer pointed to + by txhostdesc). +* Completely hide nature of PCI txdescs under opaque pointer type tx_t* +* acx_l_ether_to_txdesc -> acx_l_ether_to_txbuf, not using knowledge + of PCI txdesc anymore. +* Massive surgery on usb.c, cutting all paths into PCI code. +* PCI code moved to pci.c - USB don't need these pieces anymore +* acx111 and acx100 txhostdesc creation unified into single function + +[20050816] +* PCI code switched to single-txdesc scheme +* version bump to 0.2.5 + +[20050815] +* dev_kfree_skb -> dev_kfree_skb_any + +[20050814] +* Auto rate was reset to lowest rate by scanning code + (AP beacons did it every tenth of a second!). Fixed. +* USB rx is no longer uses PCI-style rx descriptor ring. + tx ring elimination needs 'single-descriptor' setup + to be developed for acx100 (patch exists for acx111). +* Very strange sem locking problems are reported on amd64. + Code which misbehaves looks fine. I do not know what's going on. + Workaround: turn off preemption. + +[20050812] +* acx100 was failing to find out radio module #, + and wanted to load radio module 00. Must be fixed now. +* USB: more simplifications + +[20050810] +* USB: simplified command submission code, removed some + wlandevice_t fields (now unused) + +[20050808] +* USB changes: nuked global statics, simplified issue_cmd, + shortened wlandevice_t. Added some TODOs :) + +[20050807] +* restart scan if AP is not found +* remove use_eth_name parameter. It was deprecated +* disable legacy firmware loader for 2.6. Driver printed a warning + about need to switch to hotplug. +* WARNING: new names for firmware images! + Driver will try to load the following images: + PCI driver: + 'tiacxNNNcMM' (NNN=100/111, MM=radio module ID (in uppercase hex)): + combined firmware for specified chipset and radio. + failing that, it will try to load images named + 'tiacxNNN' (NNN=100/111): main firmware for specified chipset + and 'tiacxNNNrMM': corresponding radio module. + For example, my firmware is in file named 'tiacx111c16'. + Alternatively, I may remove it and use pair of files + 'tiacx111' and 'tiacx111r16' instead. + USB driver: loads image named 'tiacx100usb' + Hint: you can keep both old and new image names (via symlinks + or just by copying files) if you need to run older driver. +* fix "Debug: sleeping function called from invalid context..." for USB +* ACX_{PCI,USB} -> CONFIG_ACX_{PCI,USB} in preparation to 2.6 merge +* version bump to 0.2.4 + +[20050804] +* 'Fixed' deadlock on flush_scheduled_work (need a better fix eventually) +* We didn't completely disable IRQs on ifdown -> "Disabling IRQ#NN" + on second modprobe. Fixed. + +[20050802] +* removed some // comments in order to please Andreas +* moved a field to PCI-only part of wlandevice_t +* Random Version Bump (tm) to 0.2.3 +* no code changes + +[20050730] +* Basically just incorporating acx-20050722.acx100fixed.patch + This is a bit of a backward step, because instead of figuring out + why active scanning doesn't work for acx100, we just disable it. + acx100 owners encouraged to try to make it work + +[20050729] +* Added some IE IDs + +[20050726] +* added probe request config to acx100_s_init_packet_templates, + maybe acx100 is working now + +[20050721] +* del_timer_sync() sem lockup on SMP maybe fixed + +[20050720] +* lots of amd64 warnings fixed, thanks to Matan Peled + +[20050710] +* {tx,rx}hostdesc->desc_phy removed (was not used) +* netdev->type of ARPHRD_IEEE80211_PRISM was sticking forever + after monitor mode. Fixed +* monitor mode tx is working again +* rate reporting added in monitor mode packet header + +[20050709] +* moved PCI specific ioctls to pci.c +* ioctl.c is PCI/USB independent now + +[20050708] +* Fixed one apparent bug (wlandevice_t had different + layout for PCI and USB - conv.c would die horribly) +* Massive code shuffling with only trivial code changes. + Mostly sorting out PCI/USB stuff into relevant files. +* ihw.c eliminated +* helper2.c is PCI/USB independent now diff -puN /dev/null drivers/net/wireless/tiacx/conv.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/conv.c 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,523 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +#include +#include +#include +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#endif + +#include "acx.h" + + +/*---------------------------------------------------------------- +* proto_is_stt +* +* Searches the 802.1h Selective Translation Table for a given +* protocol. +* +* Arguments: +* prottype protocol number (in host order) to search for. +* +* Returns: +* 1 - if the table is empty or a match is found. +* 0 - if the table is non-empty and a match is not found. +* +* Comment: +* Based largely on p80211conv.c of the linux-wlan-ng project +*----------------------------------------------------------------*/ +static inline int +proto_is_stt(unsigned int proto) +{ + /* Always return found for now. This is the behavior used by the */ + /* Zoom Win95 driver when 802.1h mode is selected */ + /* TODO: If necessary, add an actual search we'll probably + need this to match the CMAC's way of doing things. + Need to do some testing to confirm. + */ + + if (proto == 0x80f3) /* APPLETALK */ + return 1; + + return 0; +/* return ((prottype == ETH_P_AARP) || (prottype == ETH_P_IPX)); */ +} + +/* Helpers */ + +static inline void +store_llc_snap(struct wlan_llc *llc) +{ + llc->dsap = 0xaa; /* SNAP, see IEEE 802 */ + llc->ssap = 0xaa; + llc->ctl = 0x03; +} +static inline int +llc_is_snap(const struct wlan_llc *llc) +{ + return (llc->dsap == 0xaa) + && (llc->ssap == 0xaa) + && (llc->ctl == 0x03); +} +static inline void +store_oui_rfc1042(struct wlan_snap *snap) +{ + snap->oui[0] = 0; + snap->oui[1] = 0; + snap->oui[2] = 0; +} +static inline int +oui_is_rfc1042(const struct wlan_snap *snap) +{ + return (snap->oui[0] == 0) + && (snap->oui[1] == 0) + && (snap->oui[2] == 0); +} +static inline void +store_oui_8021h(struct wlan_snap *snap) +{ + snap->oui[0] = 0; + snap->oui[1] = 0; + snap->oui[2] = 0xf8; +} +static inline int +oui_is_8021h(const struct wlan_snap *snap) +{ + return (snap->oui[0] == 0) + && (snap->oui[1] == 0) + && (snap->oui[2] == 0xf8); +} + + +/*---------------------------------------------------------------- +* acx_l_ether_to_txbuf +* +* Uses the contents of the ether frame to build the elements of +* the 802.11 frame. +* +* We don't actually set up the frame header here. That's the +* MAC's job. We're only handling conversion of DIXII or 802.3+LLC +* frames to something that works with 802.11. +* +* Comment: +* Based largely on p80211conv.c of the linux-wlan-ng project +*----------------------------------------------------------------*/ +int +acx_l_ether_to_txbuf(wlandevice_t *priv, void *txbuf, const struct sk_buff *skb) +{ + struct wlan_hdr_a3 *w_hdr; + struct wlan_ethhdr *e_hdr; + struct wlan_llc *e_llc; + struct wlan_snap *e_snap; + const u8 *a1, *a3; + int header_len, payload_len; + int result = -1; + /* protocol type or data length, depending on whether + * DIX or 802.3 ethernet format */ + u16 proto; + u16 fc; + + FN_ENTER; + + if (unlikely(!skb->len)) { + acxlog(L_DEBUG, "zero-length skb!\n"); + goto end; + } + + w_hdr = (struct wlan_hdr_a3*)txbuf; + + switch (priv->mode) { + case ACX_MODE_MONITOR: + /* NB: one day we might want to play with DESC_CTL2_FCS + ** Will need to stop doing "- WLAN_CRC_LEN" here then */ + if (skb->len >= WLAN_A4FR_MAXLEN_WEP - WLAN_CRC_LEN) { + printk("%s: can't tx oversized frame (%d bytes)\n", + priv->netdev->name, skb->len); + goto end; + } + memcpy(w_hdr, skb->data, skb->len); + result = skb->len; + goto end; + } + + /* step 1: classify ether frame, DIX or 802.3? */ + e_hdr = (wlan_ethhdr_t *)skb->data; + proto = ntohs(e_hdr->type); + if (proto <= 1500) { + acxlog(L_DEBUG, "tx: 802.3 len: %d\n", skb->len); + /* codes <= 1500 reserved for 802.3 lengths */ + /* it's 802.3, pass ether payload unchanged, */ + /* trim off ethernet header and copy payload to tx_desc */ + header_len = WLAN_HDR_A3_LEN; + /* TODO: must be equal to skb->len - sizeof(wlan_ethhdr_t), no? */ + /* then we can do payload_len = ... after this big if() */ + payload_len = proto; + } else { + /* it's DIXII, time for some conversion */ + /* Create 802.11 packet. Header also contains llc and snap. */ + + acxlog(L_DEBUG, "tx: DIXII len: %d\n", skb->len); + + /* size of header is 802.11 header + llc + snap */ + header_len = WLAN_HDR_A3_LEN + sizeof(wlan_llc_t) + sizeof(wlan_snap_t); + /* llc is located behind the 802.11 header */ + e_llc = (wlan_llc_t*)(w_hdr + 1); + /* snap is located behind the llc */ + e_snap = (wlan_snap_t*)(e_llc + 1); + + /* setup the LLC header */ + store_llc_snap(e_llc); + + /* setup the SNAP header */ + e_snap->type = htons(proto); + if (proto_is_stt(proto)) { + store_oui_8021h(e_snap); + } else { + store_oui_rfc1042(e_snap); + } + /* trim off ethernet header and copy payload to tx_desc */ + payload_len = skb->len - sizeof(wlan_ethhdr_t); + } + /* TODO: can we just let acx DMA payload from skb instead? */ + memcpy((u8*)txbuf + header_len, skb->data + sizeof(wlan_ethhdr_t), payload_len); + payload_len += header_len; + result = payload_len; + + /* Set up the 802.11 header */ + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi); + a1 = e_hdr->daddr; + a3 = priv->bssid; + break; + case ACX_MODE_2_STA: + fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_TODSi); + a1 = priv->bssid; + a3 = e_hdr->daddr; + break; + case ACX_MODE_3_AP: + fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_FROMDSi); + a1 = e_hdr->daddr; + a3 = e_hdr->saddr; + break; + default: + printk("%s: error - converting eth to wlan in unknown mode\n", + priv->netdev->name); + result = -1; + goto end; + } + if (priv->wep_enabled) + SET_BIT(fc, WF_FC_ISWEPi); + + w_hdr->fc = fc; + w_hdr->dur = 0; + MAC_COPY(w_hdr->a1, a1); + MAC_COPY(w_hdr->a2, priv->dev_addr); + MAC_COPY(w_hdr->a3, a3); + w_hdr->seq = 0; + +#if DEBUG_CONVERT + if (acx_debug & L_DATA) { + printk("original eth frame [%d]: ", skb->len); + acx_dump_bytes(skb->data, skb->len); + printk("802.11 frame [%d]: ", payload_len); + acx_dump_bytes(w_hdr, payload_len); + } +#endif + +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_rxbuf_to_ether +* +* Uses the contents of a received 802.11 frame to build an ether +* frame. +* +* This function extracts the src and dest address from the 802.11 +* frame to use in the construction of the eth frame. +* +* Based largely on p80211conv.c of the linux-wlan-ng project +*----------------------------------------------------------------*/ +struct sk_buff* +acx_rxbuf_to_ether(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + struct wlan_hdr *w_hdr; + struct wlan_ethhdr *e_hdr; + struct wlan_llc *e_llc; + struct wlan_snap *e_snap; + struct sk_buff *skb; + const u8 *daddr; + const u8 *saddr; + const u8 *e_payload; + int buflen; + int payload_length; + unsigned int payload_offset; + u16 fc; + + FN_ENTER; + + /* This looks complex because it must handle possible + ** phy header in rxbuff */ + w_hdr = acx_get_wlan_hdr(priv, rxbuf); + payload_offset = WLAN_HDR_A3_LEN; /* it is relative to w_hdr */ + payload_length = RXBUF_BYTES_USED(rxbuf) /* entire rxbuff... */ + - ((u8*)w_hdr - (u8*)rxbuf) /* minus space before 802.11 frame */ + - WLAN_HDR_A3_LEN; /* minus 802.11 header */ + + /* setup some vars for convenience */ + fc = w_hdr->fc; + switch (WF_FC_FROMTODSi & fc) { + case 0: + daddr = w_hdr->a1; + saddr = w_hdr->a2; + break; + case WF_FC_FROMDSi: + daddr = w_hdr->a1; + saddr = w_hdr->a3; + break; + case WF_FC_TODSi: + daddr = w_hdr->a3; + saddr = w_hdr->a2; + break; + default: /* WF_FC_FROMTODSi */ + payload_offset += (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); + payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); + if (unlikely(0 > payload_length)) { + acxlog(L_DEBUG, "A4 frame too short!\n"); + goto ret_null; + } + daddr = w_hdr->a3; + saddr = w_hdr->a4; + } + + if ((WF_FC_ISWEPi & fc) && (CHIPTYPE_ACX100 == priv->chip_type)) { + /* chop off the IV+ICV WEP header and footer */ + acxlog(L_DATA | L_DEBUG, "rx: WEP packet, " + "chopping off IV and ICV\n"); + payload_length -= 8; + payload_offset += 4; + } + + e_hdr = (wlan_ethhdr_t*) ((u8*) w_hdr + payload_offset); + + e_llc = (wlan_llc_t*) e_hdr; + e_snap = (wlan_snap_t*) (e_llc+1); + e_payload = (u8*) (e_snap+1); + + acxlog(L_DATA, "rx: payload_offset %d, payload_length %d\n", + payload_offset, payload_length); + acxlog(L_XFER | L_DATA, + "rx: frame info: llc.dsap %X, llc.ssap %X, llc.ctl %X, " + "snap.oui %02X%02X%02X, snap.type %X\n", + e_llc->dsap, e_llc->ssap, + e_llc->ctl, e_snap->oui[0], + e_snap->oui[1], e_snap->oui[2], + e_snap->type); + + /* Test for the various encodings */ + if ((payload_length >= sizeof(wlan_ethhdr_t)) + && ((e_llc->dsap != 0xaa) || (e_llc->ssap != 0xaa)) + && ( (mac_is_equal(daddr, e_hdr->daddr)) + || (mac_is_equal(saddr, e_hdr->saddr)) + ) + ) { + acxlog(L_DEBUG | L_DATA, "rx: 802.3 ENCAP len: %d\n", payload_length); + /* 802.3 Encapsulated */ + /* Test for an overlength frame */ + + if (unlikely(payload_length > WLAN_MAX_ETHFRM_LEN)) { + /* A bogus length ethfrm has been encap'd. */ + /* Is someone trying an oflow attack? */ + printk("%s: rx: ENCAP frame too large (%d > %d)\n", + priv->netdev->name, payload_length, WLAN_MAX_ETHFRM_LEN); + goto ret_null; + } + + /* allocate space and setup host buffer */ + buflen = payload_length; + /* FIXME: implement skb ring buffer similar to + * xircom_tulip_cb.c? */ + /* Attempt to align IP header (14 bytes eth header + 2 = 16) */ + skb = dev_alloc_skb(buflen + 2); + if (unlikely(!skb)) + goto no_skb; + skb_reserve(skb, 2); + skb_put(skb, buflen); /* make room */ + + /* now copy the data from the 80211 frame */ + memcpy(skb->data, e_hdr, payload_length); /* copy the data */ + + } else if ( (payload_length >= sizeof(wlan_llc_t)+sizeof(wlan_snap_t)) + && llc_is_snap(e_llc) ) { + /* it's a SNAP */ + + if ( !oui_is_rfc1042(e_snap) + || (proto_is_stt(ieee2host16(e_snap->type)) /* && (ethconv == WLAN_ETHCONV_8021h) */)) { + acxlog(L_DEBUG | L_DATA, "rx: SNAP+RFC1042 len: %d\n", payload_length); + /* it's a SNAP + RFC1042 frame && protocol is in STT */ + /* build 802.3 + RFC1042 */ + + /* Test for an overlength frame */ + if (unlikely(payload_length + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) { + /* A bogus length ethfrm has been sent. */ + /* Is someone trying an oflow attack? */ + printk("%s: rx: SNAP frame too large (%d > %d)\n", + priv->netdev->name, + payload_length, WLAN_MAX_ETHFRM_LEN); + goto ret_null; + } + + /* allocate space and setup host buffer */ + buflen = payload_length + WLAN_ETHHDR_LEN; + skb = dev_alloc_skb(buflen + 2); + if (unlikely(!skb)) + goto no_skb; + skb_reserve(skb, 2); + skb_put(skb, buflen); /* make room */ + + /* create 802.3 header */ + e_hdr = (wlan_ethhdr_t*) skb->data; + MAC_COPY(e_hdr->daddr, daddr); + MAC_COPY(e_hdr->saddr, saddr); + e_hdr->type = htons(payload_length); + + /* Now copy the data from the 80211 frame. + Make room in front for the eth header, and keep the + llc and snap from the 802.11 payload */ + memcpy(skb->data + WLAN_ETHHDR_LEN, + e_llc, + payload_length); + + } else { + acxlog(L_DEBUG | L_DATA, "rx: 802.1h/RFC1042 len: %d\n", + payload_length); + /* it's an 802.1h frame (an RFC1042 && protocol is not in STT) */ + /* build a DIXII + RFC894 */ + + /* Test for an overlength frame */ + if (unlikely(payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t) + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) { + /* A bogus length ethfrm has been sent. */ + /* Is someone trying an oflow attack? */ + printk("%s: rx: DIXII frame too large (%d > %d)\n", + priv->netdev->name, + (int)(payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t)), + WLAN_MAX_ETHFRM_LEN - WLAN_ETHHDR_LEN); + goto ret_null; + } + + /* allocate space and setup host buffer */ + buflen = payload_length + WLAN_ETHHDR_LEN + - sizeof(wlan_llc_t) - sizeof(wlan_snap_t); + skb = dev_alloc_skb(buflen + 2); + if (unlikely(!skb)) + goto no_skb; + skb_reserve(skb, 2); + skb_put(skb, buflen); /* make room */ + + /* create 802.3 header */ + e_hdr = (wlan_ethhdr_t *) skb->data; + MAC_COPY(e_hdr->daddr, daddr); + MAC_COPY(e_hdr->saddr, saddr); + e_hdr->type = e_snap->type; + + /* Now copy the data from the 80211 frame. + Make room in front for the eth header, and cut off the + llc and snap from the 802.11 payload */ + memcpy(skb->data + WLAN_ETHHDR_LEN, e_payload, + payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t)); + } + + } else { + acxlog(L_DEBUG | L_DATA, "rx: NON-ENCAP len: %d\n", payload_length); + /* any NON-ENCAP */ + /* it's a generic 80211+LLC or IPX 'Raw 802.3' */ + /* build an 802.3 frame */ + /* allocate space and setup hostbuf */ + + /* Test for an overlength frame */ + if (unlikely(payload_length + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) { + /* A bogus length ethfrm has been sent. */ + /* Is someone trying an oflow attack? */ + printk("%s: rx: OTHER frame too large (%d > %d)\n", + priv->netdev->name, payload_length, + WLAN_MAX_ETHFRM_LEN - WLAN_ETHHDR_LEN); + goto ret_null; + } + + /* allocate space and setup host buffer */ + buflen = payload_length + WLAN_ETHHDR_LEN; + skb = dev_alloc_skb(buflen + 2); + if (unlikely(!skb)) + goto no_skb; + skb_reserve(skb, 2); + skb_put(skb, buflen); /* make room */ + + /* set up the 802.3 header */ + e_hdr = (wlan_ethhdr_t *) skb->data; + MAC_COPY(e_hdr->daddr, daddr); + MAC_COPY(e_hdr->saddr, saddr); + e_hdr->type = htons(payload_length); + + /* now copy the data from the 80211 frame */ + memcpy(skb->data + WLAN_ETHHDR_LEN, e_llc, payload_length); + } + + skb->dev = priv->netdev; + skb->protocol = eth_type_trans(skb, priv->netdev); + +#if DEBUG_CONVERT + if (acx_debug & L_DATA) { + printk("p802.11 frame [%d]: ", RXBUF_BYTES_RCVD(rxbuf)); + acx_dump_bytes(w_hdr, RXBUF_BYTES_RCVD(rxbuf)); + printk("eth frame [%d]: ", skb->len); + acx_dump_bytes(skb->data, skb->len); + } +#endif + + FN_EXIT0; + return skb; + +no_skb: + printk("%s: rx: no memory for skb (%d bytes)\n", + priv->netdev->name, buflen + 2); +ret_null: + FN_EXIT1((int)NULL); + return NULL; +} diff -puN /dev/null drivers/net/wireless/tiacx/helper2.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/helper2.c 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,2422 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#endif /* WE >= 13 */ + +#include "acx.h" + + +/*********************************************************************** +*/ +static client_t *acx_l_sta_list_alloc(wlandevice_t *priv); +static client_t *acx_l_sta_list_get_from_hash(wlandevice_t *priv, const u8 *address); + +static int acx_l_process_data_frame_master(wlandevice_t *priv, rxbuffer_t *rxbuf); +static int acx_l_process_data_frame_client(wlandevice_t *priv, rxbuffer_t *rxbuf); +/* static int acx_l_process_NULL_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala); */ +static int acx_l_process_mgmt_frame(wlandevice_t *priv, rxbuffer_t *rxbuf); +static void acx_l_process_disassoc_from_sta(wlandevice_t *priv, const wlan_fr_disassoc_t *req); +static void acx_l_process_disassoc_from_ap(wlandevice_t *priv, const wlan_fr_disassoc_t *req); +static void acx_l_process_deauth_from_sta(wlandevice_t *priv, const wlan_fr_deauthen_t *req); +static void acx_l_process_deauth_from_ap(wlandevice_t *priv, const wlan_fr_deauthen_t *req); +static int acx_l_process_probe_response(wlandevice_t *priv, wlan_fr_proberesp_t *req, const rxbuffer_t *rxbuf); +static int acx_l_process_assocresp(wlandevice_t *priv, const wlan_fr_assocresp_t *req); +static int acx_l_process_reassocresp(wlandevice_t *priv, const wlan_fr_reassocresp_t *req); +static int acx_l_process_authen(wlandevice_t *priv, const wlan_fr_authen_t *req); +static int acx_l_transmit_assocresp(wlandevice_t *priv, const wlan_fr_assocreq_t *req); +static int acx_l_transmit_reassocresp(wlandevice_t *priv, const wlan_fr_reassocreq_t *req); +static int acx_l_transmit_deauthen(wlandevice_t *priv, const u8 *addr, u16 reason); +static int acx_l_transmit_authen1(wlandevice_t *priv); +static int acx_l_transmit_authen2(wlandevice_t *priv, const wlan_fr_authen_t *req, client_t *clt); +static int acx_l_transmit_authen3(wlandevice_t *priv, const wlan_fr_authen_t *req); +static int acx_l_transmit_authen4(wlandevice_t *priv, const wlan_fr_authen_t *req); +static int acx_l_transmit_assoc_req(wlandevice_t *priv); + +static void acx_s_activate_power_save_mode(wlandevice_t *priv, /*@unused@*/ int vala); + + +/*********************************************************************** +*/ +const char* +acx_get_status_name(u16 status) +{ + static const char * const str[] = { + "STOPPED", "SCANNING", "WAIT_AUTH", + "AUTHENTICATED", "ASSOCIATED", "INVALID??" + }; + return str[(status < VEC_SIZE(str)) ? status : VEC_SIZE(str)-1]; +} + + +/*---------------------------------------------------------------- +* acx_l_sta_list_init +*----------------------------------------------------------------*/ +void +acx_l_sta_list_init(wlandevice_t *priv) +{ + FN_ENTER; + memset(priv->sta_hash_tab, 0, sizeof(priv->sta_hash_tab)); + memset(priv->sta_list, 0, sizeof(priv->sta_list)); + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_sta_list_get_from_hash +*----------------------------------------------------------------*/ +static inline client_t* +acx_l_sta_list_get_from_hash(wlandevice_t *priv, const u8 *address) +{ + return priv->sta_hash_tab[address[5] % VEC_SIZE(priv->sta_hash_tab)]; +} + + +/*---------------------------------------------------------------- +* acx_l_sta_list_get +*----------------------------------------------------------------*/ +client_t* +acx_l_sta_list_get(wlandevice_t *priv, const u8 *address) +{ + client_t *client; + FN_ENTER; + client = acx_l_sta_list_get_from_hash(priv, address); + while (client) { + if (mac_is_equal(address, client->address)) { + client->mtime = jiffies; + break; + } + client = client->next; + } + FN_EXIT0; + return client; +} + + +/*---------------------------------------------------------------- +* acx_l_sta_list_del +*----------------------------------------------------------------*/ +void +acx_l_sta_list_del(wlandevice_t *priv, client_t *victim) +{ + client_t *client, *next; + + client = acx_l_sta_list_get_from_hash(priv, victim->address); + next = client; + /* tricky. next = client on first iteration only, + ** on all other iters next = client->next */ + while (next) { + if (next == victim) { + client->next = victim->next; + /* Overkill */ + memset(victim, 0, sizeof(*victim)); + break; + } + client = next; + next = client->next; + } +} + + +/*---------------------------------------------------------------- +* acx_l_sta_list_alloc +* +* Never fails - will evict oldest client if needed +*----------------------------------------------------------------*/ +static client_t* +acx_l_sta_list_alloc(wlandevice_t *priv) +{ + int i; + unsigned long age, oldest_age; + client_t *client, *oldest; + + FN_ENTER; + + oldest = &priv->sta_list[0]; + oldest_age = 0; + for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { + client = &priv->sta_list[i]; + + if (!client->used) { + goto found; + } else { + age = jiffies - client->mtime; + if (oldest_age < age) { + oldest_age = age; + oldest = client; + } + } + } + acx_l_sta_list_del(priv, oldest); + client = oldest; +found: + memset(client, 0, sizeof(*client)); + FN_EXIT0; + return client; +} + + +/*---------------------------------------------------------------- +* acx_l_sta_list_add +* +* Never fails - will evict oldest client if needed +*----------------------------------------------------------------*/ +/* In case we will reimplement it differently... */ +#define STA_LIST_ADD_CAN_FAIL 0 + +static client_t* +acx_l_sta_list_add(wlandevice_t *priv, const u8 *address) +{ + client_t *client; + int index; + + FN_ENTER; + + client = acx_l_sta_list_alloc(priv); + + client->mtime = jiffies; + MAC_COPY(client->address, address); + client->used = CLIENT_EXIST_1; + client->auth_alg = WLAN_AUTH_ALG_SHAREDKEY; + client->auth_step = 1; + /* give some tentative peer rate values + ** (needed because peer may do auth without probing us first, + ** thus we'll have no idea of peer's ratevector yet). + ** Will be overwritten by scanning or assoc code */ + client->rate_cap = priv->rate_basic; + client->rate_cfg = priv->rate_basic; + client->rate_cur = 1 << lowest_bit(priv->rate_basic); + + index = address[5] % VEC_SIZE(priv->sta_hash_tab); + client->next = priv->sta_hash_tab[index]; + priv->sta_hash_tab[index] = client; + + acxlog_mac(L_ASSOC, "sta_list_add: sta=", address, "\n"); + + FN_EXIT0; + return client; +} + + +/*---------------------------------------------------------------- +* acx_l_sta_list_get_or_add +* +* Never fails - will evict oldest client if needed +*----------------------------------------------------------------*/ +static client_t* +acx_l_sta_list_get_or_add(wlandevice_t *priv, const u8 *address) +{ + client_t *client = acx_l_sta_list_get(priv, address); + if (!client) + client = acx_l_sta_list_add(priv, address); + return client; +} + + +/*---------------------------------------------------------------- +* acx_set_status +* +* This function is called in many atomic regions, must not sleep +*----------------------------------------------------------------*/ +void +acx_set_status(wlandevice_t *priv, u16 new_status) +{ +#define QUEUE_OPEN_AFTER_ASSOC 1 /* this really seems to be needed now */ + u16 old_status = priv->status; + + FN_ENTER; + + acxlog(L_ASSOC, "%s(%d):%s\n", + __func__, new_status, acx_get_status_name(new_status)); + +#if WIRELESS_EXT > 13 /* wireless_send_event() and SIOCGIWSCAN */ + /* wireless_send_event never sleeps */ + if (ACX_STATUS_4_ASSOCIATED == new_status) { + union iwreq_data wrqu; + + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL); + + wrqu.data.length = 0; + wrqu.data.flags = 0; + MAC_COPY(wrqu.ap_addr.sa_data, priv->bssid); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, NULL); + } else { + union iwreq_data wrqu; + + /* send event with empty BSSID to indicate we're not associated */ + MAC_ZERO(wrqu.ap_addr.sa_data); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, NULL); + } +#endif + + priv->status = new_status; + + switch (new_status) { + case ACX_STATUS_1_SCANNING: + priv->scan_retries = 0; + /* 2.5s initial scan time + * (used to be 1.5s, but failed to find WEP APs!) */ + acx_set_timer(priv, 1000000); + break; + case ACX_STATUS_2_WAIT_AUTH: + case ACX_STATUS_3_AUTHENTICATED: + priv->auth_or_assoc_retries = 0; + acx_set_timer(priv, 1500000); /* 1.5 s */ + break; + } + +#if QUEUE_OPEN_AFTER_ASSOC + if (new_status == ACX_STATUS_4_ASSOCIATED) { + if (old_status < ACX_STATUS_4_ASSOCIATED) { + /* ah, we're newly associated now, + * so let's indicate carrier */ + acx_carrier_on(priv->netdev, "after association"); + acx_wake_queue(priv->netdev, "after association"); + } + } else { + /* not associated any more, so let's kill carrier */ + if (old_status >= ACX_STATUS_4_ASSOCIATED) { + acx_carrier_off(priv->netdev, "after losing association"); + acx_stop_queue(priv->netdev, "after losing association"); + } + } +#endif + FN_EXIT0; +} + + +/*------------------------------------------------------------------------------ + * acx_i_timer + * + * Fires up periodically. Used to kick scan/auth/assoc if something goes wrong + *----------------------------------------------------------------------------*/ +void +acx_i_timer(unsigned long address) +{ + unsigned long flags; + wlandevice_t *priv = (wlandevice_t *)address; + + FN_ENTER; + + acx_lock(priv, flags); + + acxlog(L_DEBUG|L_ASSOC, "%s: priv->status=%d (%s)\n", + __func__, priv->status, acx_get_status_name(priv->status)); + + switch (priv->status) { + case ACX_STATUS_1_SCANNING: + /* was set to 0 by set_status() */ + if (++priv->scan_retries < 7) { + acx_set_timer(priv, 1000000); + /* used to interrogate for scan status. + ** We rely on SCAN_COMPLETE IRQ instead */ + acxlog(L_ASSOC, "continuing scan (%d sec)\n", + priv->scan_retries); + } else { + acxlog(L_ASSOC, "stopping scan\n"); + /* send stop_scan cmd when we leave the interrupt context, + * and make a decision what to do next (COMPLETE_SCAN) */ + acx_schedule_after_interrupt_task(priv, + ACX_AFTER_IRQ_CMD_STOP_SCAN + ACX_AFTER_IRQ_COMPLETE_SCAN); + } + break; + case ACX_STATUS_2_WAIT_AUTH: + /* was set to 0 by set_status() */ + if (++priv->auth_or_assoc_retries < 10) { + acxlog(L_ASSOC, "resend authen1 request (attempt %d)\n", + priv->auth_or_assoc_retries + 1); + acx_l_transmit_authen1(priv); + } else { + /* time exceeded: fall back to scanning mode */ + acxlog(L_ASSOC, + "authen1 request reply timeout, giving up\n"); + /* we are a STA, need to find AP anyhow */ + acx_set_status(priv, ACX_STATUS_1_SCANNING); + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_RESTART_SCAN); + } + /* used to be 1500000, but some other driver uses 2.5s */ + acx_set_timer(priv, 2500000); + break; + case ACX_STATUS_3_AUTHENTICATED: + /* was set to 0 by set_status() */ + if (++priv->auth_or_assoc_retries < 10) { + acxlog(L_ASSOC, "resend assoc request (attempt %d)\n", + priv->auth_or_assoc_retries + 1); + acx_l_transmit_assoc_req(priv); + } else { + /* time exceeded: give up */ + acxlog(L_ASSOC, + "association request reply timeout, giving up\n"); + /* we are a STA, need to find AP anyhow */ + acx_set_status(priv, ACX_STATUS_1_SCANNING); + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_RESTART_SCAN); + } + acx_set_timer(priv, 2500000); /* see above */ + break; + case ACX_STATUS_4_ASSOCIATED: + default: + break; + } + + acx_unlock(priv, flags); + + FN_EXIT0; +} + + +/*------------------------------------------------------------------------------ + * acx_set_timer + * + * Sets the 802.11 state management timer's timeout. + *----------------------------------------------------------------------------*/ +void +acx_set_timer(wlandevice_t *priv, u32 timeout) +{ + FN_ENTER; + + acxlog(L_DEBUG|L_IRQ, "%s(%u ms)\n", __func__, timeout/1000); + if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) { + printk("attempt to set the timer " + "when the card interface is not up!\n"); + goto end; + } + + /* first check if the timer was already initialized, THEN modify it */ + if (priv->mgmt_timer.function) { + mod_timer(&priv->mgmt_timer, + jiffies + (timeout * HZ / 1000000)); + } +end: + FN_EXIT0; +} + + +/*------------------------------------------------------------------------------ + * acx_l_rx_ieee802_11_frame + * + * Called from IRQ context only + * STATUS: FINISHED, UNVERIFIED. + *----------------------------------------------------------------------------*/ +int +acx_l_rx_ieee802_11_frame(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + unsigned int ftype, fstype; + const wlan_hdr_t *hdr; + int result = NOT_OK; + + FN_ENTER; + + hdr = acx_get_wlan_hdr(priv, rxbuf); + + /* see IEEE 802.11-1999.pdf chapter 7 "MAC frame formats" */ + if ((hdr->fc & WF_FC_PVERi) != 0) { + printk_ratelimited(KERN_INFO "rx: unsupported 802.11 protocol\n"); + goto end; + } + + ftype = hdr->fc & WF_FC_FTYPEi; + fstype = hdr->fc & WF_FC_FSTYPEi; + + switch (ftype) { + /* check data frames first, for speed */ + case WF_FTYPE_DATAi: + switch (fstype) { + case WF_FSTYPE_DATAONLYi: + /* FIXME: will fail if two peers send 2 streams of DUPs */ + if (priv->dup_count + && time_after(jiffies, priv->dup_msg_expiry)) { + printk(KERN_INFO "%s: rx: %d DUPs received in 10 secs\n", + priv->netdev->name, priv->dup_count); + priv->dup_count = 0; + } + if (unlikely(hdr->seq == priv->last_seq_ctrl)) { + if (!priv->dup_count++) + priv->dup_msg_expiry = jiffies + 10*HZ; + /* simply discard it and indicate error */ + priv->stats.rx_errors++; + break; + } + priv->last_seq_ctrl = hdr->seq; /* le_to_cpu? */ + + /* TODO: + if (WF_FC_FROMTODSi == (hdr->fc & WF_FC_FROMTODSi)) { + result = acx_l_process_data_frame_wds(priv, rxbuf); + break; + } + */ + + switch (priv->mode) { + case ACX_MODE_3_AP: + result = acx_l_process_data_frame_master(priv, rxbuf); + break; + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + result = acx_l_process_data_frame_client(priv, rxbuf); + break; + } + case WF_FSTYPE_DATA_CFACKi: + case WF_FSTYPE_DATA_CFPOLLi: + case WF_FSTYPE_DATA_CFACK_CFPOLLi: + case WF_FSTYPE_CFPOLLi: + case WF_FSTYPE_CFACK_CFPOLLi: + /* see above. + acx_process_class_frame(priv, rxbuf, 3); */ + break; + case WF_FSTYPE_NULLi: + /* acx_l_process_NULL_frame(priv, rxbuf, 3); */ + break; + /* FIXME: same here, see above */ + case WF_FSTYPE_CFACKi: + default: + break; + } + break; + case WF_FTYPE_MGMTi: + result = acx_l_process_mgmt_frame(priv, rxbuf); + break; + case WF_FTYPE_CTLi: + if (fstype == WF_FSTYPE_PSPOLLi) + result = OK; + /* this call is irrelevant, since + * acx_process_class_frame is a stub, so return + * immediately instead. + * return acx_process_class_frame(priv, rxbuf, 3); */ + break; + default: + break; + } +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_assocresp +* +* We are an AP here +*----------------------------------------------------------------*/ +static const u8 +dot11ratebyte[] = { + DOT11RATEBYTE_1, + DOT11RATEBYTE_2, + DOT11RATEBYTE_5_5, + DOT11RATEBYTE_6_G, + DOT11RATEBYTE_9_G, + DOT11RATEBYTE_11, + DOT11RATEBYTE_12_G, + DOT11RATEBYTE_18_G, + DOT11RATEBYTE_22, + DOT11RATEBYTE_24_G, + DOT11RATEBYTE_36_G, + DOT11RATEBYTE_48_G, + DOT11RATEBYTE_54_G, +}; + +static int +find_pos(const u8 *p, int size, u8 v) +{ + int i; + for (i = 0; i < size; i++) + if (p[i] == v) + return i; + /* printk a message about strange byte? */ + return 0; +} + +static void +add_bits_to_ratemasks(u8* ratevec, int len, u16* brate, u16* orate) +{ + while (len--) { + int n = 1 << find_pos(dot11ratebyte, + sizeof(dot11ratebyte), *ratevec & 0x7f); + if (*ratevec & 0x80) + *brate |= n; + *orate |= n; + ratevec++; + } +} + +static int +acx_l_transmit_assocresp(wlandevice_t *priv, const wlan_fr_assocreq_t *req) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct assocresp_frame_body *body; + u8 *p; + const u8 *da; + /* const u8 *sa; */ + const u8 *bssid; + client_t *clt; + + FN_ENTER; + + /* sa = req->hdr->a1; */ + da = req->hdr->a2; + bssid = req->hdr->a3; + + clt = acx_l_sta_list_get(priv, da); + if (!clt) + goto ok; + + /* Assoc without auth is a big no-no */ + /* Let's be liberal: if already assoc'ed STA sends assoc req again, + ** we won't be rude */ + if (clt->used != CLIENT_AUTHENTICATED_2 + && clt->used != CLIENT_ASSOCIATED_3) { + acx_l_transmit_deauthen(priv, da, WLAN_MGMT_REASON_CLASS2_NONAUTH); + goto bad; + } + + clt->used = CLIENT_ASSOCIATED_3; + + if (clt->aid == 0) { + clt->aid = ++priv->aid; + } + clt->cap_info = ieee2host16(*(req->cap_info)); + /* We cheat here a bit. We don't really care which rates are flagged + ** as basic by the client, so we stuff them in single ratemask */ + clt->rate_cap = 0; + if (req->supp_rates) + add_bits_to_ratemasks(req->supp_rates->rates, + req->supp_rates->len, &clt->rate_cap, &clt->rate_cap); + if (req->ext_rates) + add_bits_to_ratemasks(req->ext_rates->rates, + req->ext_rates->len, &clt->rate_cap, &clt->rate_cap); + /* We can check that client supports all basic rates, + ** and deny assoc if not. But let's be liberal, right? ;) */ + clt->rate_cfg = clt->rate_cap & priv->rate_oper; + if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(priv->rate_oper); + clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); + clt->fallback_count = clt->stepup_count = 0; + clt->ignore_count = 16; + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(tx); + if (!head) + goto bad; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_ASSOCRESPi; + head->dur = req->hdr->dur; + MAC_COPY(head->da, da); + /* MAC_COPY(head->sa, sa); */ + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, bssid); + head->seq = req->hdr->seq; + + body->cap_info = host2ieee16(priv->capabilities); + body->status = host2ieee16(0); + body->aid = host2ieee16(clt->aid); + p = wlan_fill_ie_rates((u8*)&body->rates, priv->rate_supported_len, + priv->rate_supported); + p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, + priv->rate_supported); + + acx_l_dma_tx_data(priv, tx, p - (u8*)head); +ok: + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_reassocresp + +You may be wondering, just like me, what a hell is ReAuth. +In practice it was seen sent by STA when STA feels like losing connection. + +[802.11] + +5.4.2.3 Reassociation + +Association is sufficient for no-transition message delivery between +IEEE 802.11 stations. Additional functionality is needed to support +BSS-transition mobility. The additional required functionality +is provided by the reassociation service. Reassociation is a DSS. +The reassociation service is invoked to ÒmoveÓ a current association +from one AP to another. This keeps the DS informed of the current +mapping between AP and STA as the station moves from BSS to BSS within +an ESS. Reassociation also enables changing association attributes +of an established association while the STA remains associated with +the same AP. Reassociation is always initiated by the mobile STA. + +5.4.3.1 Authentication +... +A STA may be authenticated with many other STAs at any given instant. + +5.4.3.1.1 Preauthentication + +Because the authentication process could be time-consuming (depending +on the authentication protocol in use), the authentication service can +be invoked independently of the association service. Preauthentication +is typically done by a STA while it is already associated with an AP +(with which it previously authenticated). IEEE 802.11 does not require +that STAs preauthenticate with APs. However, authentication is required +before an association can be established. If the authentication is left +until reassociation time, this may impact the speed with which a STA can +reassociate between APs, limiting BSS-transition mobility performance. +The use of preauthentication takes the authentication service overhead +out of the time-critical reassociation process. + +5.7.3 Reassociation + +For a STA to reassociate, the reassociation service causes the following +message to occur: + + Reassociation request + +* Message type: Management +* Message subtype: Reassociation request +* Information items: + - IEEE address of the STA + - IEEE address of the AP with which the STA will reassociate + - IEEE address of the AP with which the STA is currently associated + - ESSID +* Direction of message: From STA to 'new' AP + +The address of the current AP is included for efficiency. The inclusion +of the current AP address facilitates MAC reassociation to be independent +of the DS implementation. + + Reassociation response +* Message type: Management +* Message subtype: Reassociation response +* Information items: + - Result of the requested reassociation. (success/failure) + - If the reassociation is successful, the response shall include the AID. +* Direction of message: From AP to STA + +7.2.3.6 Reassociation Request frame format + +The frame body of a management frame of subtype Reassociation Request +contains the information shown in Table 9. + +Table 9 Reassociation Request frame body +Order Information +1 Capability information +2 Listen interval +3 Current AP address +4 SSID +5 Supported rates + +7.2.3.7 Reassociation Response frame format + +The frame body of a management frame of subtype Reassociation Response +contains the information shown in Table 10. + +Table 10 Reassociation Response frame body +Order Information +1 Capability information +2 Status code +3 Association ID (AID) +4 Supported rates + +*----------------------------------------------------------------*/ +static int +acx_l_transmit_reassocresp(wlandevice_t *priv, const wlan_fr_reassocreq_t *req) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct reassocresp_frame_body *body; + u8 *p; + const u8 *da; + /* const u8 *sa; */ + const u8 *bssid; + client_t *clt; + + FN_ENTER; + + /* sa = req->hdr->a1; */ + da = req->hdr->a2; + bssid = req->hdr->a3; + + /* Must be already authenticated, so it must be in the list */ + clt = acx_l_sta_list_get(priv, da); + if (!clt) + goto ok; + + /* Assoc without auth is a big no-no */ + /* Already assoc'ed STAs sending ReAssoc req are ok per 802.11 */ + if (clt->used != CLIENT_AUTHENTICATED_2 + && clt->used != CLIENT_ASSOCIATED_3) { + acx_l_transmit_deauthen(priv, da, WLAN_MGMT_REASON_CLASS2_NONAUTH); + goto bad; + } + + clt->used = CLIENT_ASSOCIATED_3; + if (clt->aid == 0) { + clt->aid = ++priv->aid; + } + if (req->cap_info) + clt->cap_info = ieee2host16(*(req->cap_info)); + /* We cheat here a bit. We don't really care which rates are flagged + ** as basic by the client, so we stuff them in single ratemask */ + clt->rate_cap = 0; + if (req->supp_rates) + add_bits_to_ratemasks(req->supp_rates->rates, + req->supp_rates->len, &clt->rate_cap, &clt->rate_cap); + if (req->ext_rates) + add_bits_to_ratemasks(req->ext_rates->rates, + req->ext_rates->len, &clt->rate_cap, &clt->rate_cap); + /* We can check that client supports all basic rates, + ** and deny assoc if not. But let's be liberal, right? ;) */ + clt->rate_cfg = clt->rate_cap & priv->rate_oper; + if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(priv->rate_oper); + clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); + clt->fallback_count = clt->stepup_count = 0; + clt->ignore_count = 16; + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto ok; + head = acx_l_get_txbuf(tx); + if (!head) + goto ok; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_REASSOCRESPi; + head->dur = req->hdr->dur; + MAC_COPY(head->da, da); + /* MAC_COPY(head->sa, sa); */ + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, bssid); + head->seq = req->hdr->seq; + + /* IEs: 1. caps */ + body->cap_info = host2ieee16(priv->capabilities); + /* 2. status code */ + body->status = host2ieee16(0); + /* 3. AID */ + body->aid = host2ieee16(clt->aid); + /* 4. supp rates */ + p = wlan_fill_ie_rates((u8*)&body->rates, priv->rate_supported_len, + priv->rate_supported); + /* 5. ext supp rates */ + p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, + priv->rate_supported); + + acx_l_dma_tx_data(priv, tx, p - (u8*)head); +ok: + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_process_disassoc_from_sta +*----------------------------------------------------------------*/ +static void +acx_l_process_disassoc_from_sta(wlandevice_t *priv, const wlan_fr_disassoc_t *req) +{ + const u8 *ta; + client_t *clt; + + FN_ENTER; + + ta = req->hdr->a2; + clt = acx_l_sta_list_get(priv, ta); + if (!clt) + goto end; + + if (clt->used != CLIENT_ASSOCIATED_3 + && clt->used != CLIENT_AUTHENTICATED_2) { + /* it's disassociating, but it's + ** not even authenticated! Let it know that */ + acxlog_mac(L_ASSOC|L_XFER, "peer ", ta, "has sent disassoc " + "req but it is not even auth'ed! sending deauth\n"); + acx_l_transmit_deauthen(priv, ta, + WLAN_MGMT_REASON_CLASS2_NONAUTH); + clt->used = CLIENT_EXIST_1; + } else { + /* mark it as auth'ed only */ + clt->used = CLIENT_AUTHENTICATED_2; + } +end: + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_process_deauthen_from_sta +*----------------------------------------------------------------*/ +static void +acx_l_process_deauth_from_sta(wlandevice_t *priv, const wlan_fr_deauthen_t *req) +{ + const wlan_hdr_t *hdr; + client_t *client; + + FN_ENTER; + + hdr = req->hdr; + + if (acx_debug & L_ASSOC) { + acx_print_mac("DEAUTHEN priv->addr=", priv->dev_addr, " "); + acx_print_mac("a1", hdr->a1, " "); + acx_print_mac("a2", hdr->a2, " "); + acx_print_mac("a3", hdr->a3, " "); + acx_print_mac("priv->bssid", priv->bssid, "\n"); + } + + if (!mac_is_equal(priv->dev_addr, hdr->a1)) { + goto end; + } + + acxlog_mac(L_DEBUG, "STA ", hdr->a2, " sent us deauthen packet\n"); + + client = acx_l_sta_list_get(priv, hdr->a2); + if (!client) { + goto end; + } + client->used = CLIENT_EXIST_1; +end: + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_process_disassoc_from_ap +*----------------------------------------------------------------*/ +static void +acx_l_process_disassoc_from_ap(wlandevice_t *priv, const wlan_fr_disassoc_t *req) +{ + FN_ENTER; + + if (!priv->ap_client) { + /* Hrm, we aren't assoc'ed yet anyhow... */ + goto end; + } + if (mac_is_equal(priv->dev_addr, req->hdr->a1)) { + /* TODO: send a deauth... */ + acx_l_transmit_deauthen(priv, priv->bssid, + WLAN_MGMT_REASON_DEAUTH_LEAVING); + /* Start scan anew */ + SET_BIT(priv->set_mask, GETSET_RESCAN); + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG); + } +end: + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_process_deauth_from_ap +*----------------------------------------------------------------*/ +static void +acx_l_process_deauth_from_ap(wlandevice_t *priv, const wlan_fr_deauthen_t *req) +{ + FN_ENTER; + + if (!priv->ap_client) { + /* Hrm, we aren't assoc'ed yet anyhow... */ + goto end; + } + /* Chk: is ta is verified to be from our AP? */ + if (mac_is_equal(priv->dev_addr, req->hdr->a1)) { + acxlog(L_DEBUG, "AP sent us deauth packet\n"); + /* not needed: acx_set_status(priv, ACX_STATUS_1_SCANNING) */ + SET_BIT(priv->set_mask, GETSET_RESCAN); + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG); + } +end: + FN_EXIT0; +} + + +/*------------------------------------------------------------------------------ + * acx_l_rx + * + * The end of the Rx path. Pulls data from a rxhostdesc into a socket + * buffer and feeds it to the network stack via netif_rx(). + * + * Arguments: + * rxdesc: the rxhostdesc to pull the data from + * priv: the acx100 private struct of the interface + *----------------------------------------------------------------------------*/ +void +acx_l_rx(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + FN_ENTER; + if (likely(priv->dev_state_mask & ACX_STATE_IFACE_UP)) { + struct sk_buff *skb; + skb = acx_rxbuf_to_ether(priv, rxbuf); + if (likely(skb)) { + netif_rx(skb); + priv->netdev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes += skb->len; + } + } + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_process_data_frame_master +*----------------------------------------------------------------*/ +static int +acx_l_process_data_frame_master(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + struct wlan_hdr *hdr; + struct tx *tx; + void *txbuf; + int len; + int result = NOT_OK; + + FN_ENTER; + + hdr = acx_get_wlan_hdr(priv, rxbuf); + + switch (WF_FC_FROMTODSi & hdr->fc) { + case 0: + case WF_FC_FROMDSi: + acxlog(L_DEBUG, "ap->sta or adhoc->adhoc data frame ignored\n"); + goto done; + case WF_FC_TODSi: + break; + default: /* WF_FC_FROMTODSi */ + acxlog(L_DEBUG, "wds data frame ignored (todo)\n"); + goto done; + } + + /* check if it is our BSSID, if not, leave */ + if (!mac_is_equal(priv->bssid, hdr->a1)) { + goto done; + } + + if (mac_is_equal(priv->dev_addr, hdr->a3)) { + /* this one is for us */ + acx_l_rx(priv, rxbuf); + } else { + if (mac_is_bcast(hdr->a3)) { + /* this one is bcast, rx it too */ + acx_l_rx(priv, rxbuf); + } + tx = acx_l_alloc_tx(priv); + if (!tx) { + goto fail; + } + /* repackage, tx, and hope it someday reaches its destination */ + /* order is important, we do it in-place */ + MAC_COPY(hdr->a1, hdr->a3); + MAC_COPY(hdr->a3, hdr->a2); + MAC_COPY(hdr->a2, priv->bssid); + /* To_DS = 0, From_DS = 1 */ + hdr->fc = WF_FC_FROMDSi + WF_FTYPE_DATAi; + + len = RXBUF_BYTES_RCVD(rxbuf); + txbuf = acx_l_get_txbuf(tx); + if (txbuf) { + memcpy(txbuf, &rxbuf->hdr_a3, len); + acx_l_dma_tx_data(priv, tx, len); + } + } +done: + result = OK; +fail: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_l_process_data_frame_client +*----------------------------------------------------------------*/ +static int +acx_l_process_data_frame_client(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + const u8 *da, *bssid; + const wlan_hdr_t *hdr; + netdevice_t *dev = priv->netdev; + int result = NOT_OK; + + FN_ENTER; + + if (ACX_STATUS_4_ASSOCIATED != priv->status) goto drop; + + hdr = acx_get_wlan_hdr(priv, rxbuf); + + switch (WF_FC_FROMTODSi & hdr->fc) { + case 0: + if (priv->mode != ACX_MODE_0_ADHOC) { + acxlog(L_DEBUG, "adhoc->adhoc data frame ignored\n"); + goto drop; + } + bssid = hdr->a3; + break; + case WF_FC_FROMDSi: + if (priv->mode != ACX_MODE_2_STA) { + acxlog(L_DEBUG, "ap->sta data frame ignored\n"); + goto drop; + } + bssid = hdr->a2; + break; + case WF_FC_TODSi: + acxlog(L_DEBUG, "sta->ap data frame ignored\n"); + goto drop; + default: /* WF_FC_FROMTODSi: wds->wds */ + acxlog(L_DEBUG, "wds data frame ignored (todo)\n"); + goto drop; + } + + da = hdr->a1; + + if (unlikely(acx_debug & L_DEBUG)) { + acx_print_mac("rx: da=", da, ""); + acx_print_mac(" bssid=", bssid, ""); + acx_print_mac(" priv->bssid=", priv->bssid, ""); + acx_print_mac(" priv->addr=", priv->dev_addr, "\n"); + } + + /* promiscuous mode --> receive all packets */ + if (unlikely(dev->flags & IFF_PROMISC)) + goto process; + + /* FIRST, check if it is our BSSID */ + if (!mac_is_equal(priv->bssid, bssid)) { + /* is not our BSSID, so bail out */ + goto drop; + } + + /* then, check if it is our address */ + if (mac_is_equal(priv->dev_addr, da)) { + goto process; + } + + /* then, check if it is broadcast */ + if (mac_is_bcast(da)) { + goto process; + } + + if (mac_is_mcast(da)) { + /* unconditionally receive all multicasts */ + if (dev->flags & IFF_ALLMULTI) + goto process; + + /* FIXME: check against the list of + * multicast addresses that are configured + * for the interface (ifconfig) */ + acxlog(L_XFER, "FIXME: multicast packet, need to check " + "against a list of multicast addresses " + "(to be created!); accepting packet for now\n"); + /* for now, just accept it here */ + goto process; + } + + acxlog(L_DEBUG, "rx: foreign packet, dropping\n"); + goto drop; +process: + /* receive packet */ + acx_l_rx(priv, rxbuf); + + result = OK; +drop: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_l_process_mgmt_frame +* +* Theory of operation: mgmt packet gets parsed (to make it easy +* to access variable-sized IEs), results stored in 'parsed'. +* Then we react to the packet. +* NB: wlan_mgmt_decode_XXX are dev-independent (shoudnt have been named acx_XXX) +*----------------------------------------------------------------*/ +typedef union parsed_mgmt_req { + wlan_fr_mgmt_t mgmt; + wlan_fr_assocreq_t assocreq; + wlan_fr_reassocreq_t reassocreq; + wlan_fr_assocresp_t assocresp; + wlan_fr_reassocresp_t reassocresp; + wlan_fr_beacon_t beacon; + wlan_fr_disassoc_t disassoc; + wlan_fr_authen_t authen; + wlan_fr_deauthen_t deauthen; + wlan_fr_proberesp_t proberesp; +} parsed_mgmt_req_t; + +extern void BUG_excessive_stack_usage(void); + +static int +acx_l_process_mgmt_frame(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + parsed_mgmt_req_t parsed; /* takes ~100 bytes of stack */ + wlan_hdr_t *hdr; + int adhoc, sta_scan, sta, ap; + int len; + + if (sizeof(parsed) > 256) BUG_excessive_stack_usage(); + + FN_ENTER; + + hdr = acx_get_wlan_hdr(priv, rxbuf); + + /* Management frames never have these set */ + if (WF_FC_FROMTODSi & hdr->fc) { + FN_EXIT1(NOT_OK); + return NOT_OK; + } + + len = RXBUF_BYTES_RCVD(rxbuf); + if (WF_FC_ISWEPi & hdr->fc) + len -= 0x10; + + adhoc = (priv->mode == ACX_MODE_0_ADHOC); + sta_scan = ((priv->mode == ACX_MODE_2_STA) + && (priv->status != ACX_STATUS_4_ASSOCIATED)); + sta = ((priv->mode == ACX_MODE_2_STA) + && (priv->status == ACX_STATUS_4_ASSOCIATED)); + ap = (priv->mode == ACX_MODE_3_AP); + + switch (WF_FC_FSTYPEi & hdr->fc) { + /* beacons first, for speed */ + case WF_FSTYPE_BEACONi: + memset(&parsed.beacon, 0, sizeof(parsed.beacon)); + parsed.beacon.hdr = hdr; + parsed.beacon.len = len; + if (acx_debug & L_DATA) { + printk("BCN len:%d fc:%04X dur:%04X seq:%04X\n", + len, hdr->fc, hdr->dur, hdr->seq); + acx_print_mac("BCN a1:", hdr->a1, "\n"); + acx_print_mac("BCN a2:", hdr->a2, "\n"); + acx_print_mac("BCN a3:", hdr->a3, "\n"); + } + wlan_mgmt_decode_beacon(&parsed.beacon); + /* beacon and probe response are very similar, so... */ + acx_l_process_probe_response(priv, &parsed.beacon, rxbuf); + break; + case WF_FSTYPE_ASSOCREQi: + if (!ap) + break; + memset(&parsed.assocreq, 0, sizeof(parsed.assocreq)); + parsed.assocreq.hdr = hdr; + parsed.assocreq.len = len; + wlan_mgmt_decode_assocreq(&parsed.assocreq); + if (mac_is_equal(hdr->a1, priv->bssid) + && mac_is_equal(hdr->a3, priv->bssid)) { + acx_l_transmit_assocresp(priv, &parsed.assocreq); + } + break; + case WF_FSTYPE_REASSOCREQi: + if (!ap) + break; + memset(&parsed.assocreq, 0, sizeof(parsed.assocreq)); + parsed.assocreq.hdr = hdr; + parsed.assocreq.len = len; + wlan_mgmt_decode_assocreq(&parsed.assocreq); + /* reassocreq and assocreq are equivalent */ + acx_l_transmit_reassocresp(priv, &parsed.reassocreq); + break; + case WF_FSTYPE_ASSOCRESPi: + if (!sta_scan) + break; + memset(&parsed.assocresp, 0, sizeof(parsed.assocresp)); + parsed.assocresp.hdr = hdr; + parsed.assocresp.len = len; + wlan_mgmt_decode_assocresp(&parsed.assocresp); + acx_l_process_assocresp(priv, &parsed.assocresp); + break; + case WF_FSTYPE_REASSOCRESPi: + if (!sta_scan) + break; + memset(&parsed.assocresp, 0, sizeof(parsed.assocresp)); + parsed.assocresp.hdr = hdr; + parsed.assocresp.len = len; + wlan_mgmt_decode_assocresp(&parsed.assocresp); + acx_l_process_reassocresp(priv, &parsed.reassocresp); + break; + case WF_FSTYPE_PROBEREQi: + if (ap || adhoc) { + /* FIXME: since we're supposed to be an AP, + ** we need to return a Probe Response packet. + ** Currently firmware is doing it for us, + ** but firmware is buggy! See comment elsewhere --vda */ + } + break; + case WF_FSTYPE_PROBERESPi: + memset(&parsed.proberesp, 0, sizeof(parsed.proberesp)); + parsed.proberesp.hdr = hdr; + parsed.proberesp.len = len; + wlan_mgmt_decode_proberesp(&parsed.proberesp); + acx_l_process_probe_response(priv, &parsed.proberesp, rxbuf); + break; + case 6: + case 7: + /* exit */ + break; + case WF_FSTYPE_ATIMi: + /* exit */ + break; + case WF_FSTYPE_DISASSOCi: + if (!sta && !ap) + break; + memset(&parsed.disassoc, 0, sizeof(parsed.disassoc)); + parsed.disassoc.hdr = hdr; + parsed.disassoc.len = len; + wlan_mgmt_decode_disassoc(&parsed.disassoc); + if (sta) + acx_l_process_disassoc_from_ap(priv, &parsed.disassoc); + else + acx_l_process_disassoc_from_sta(priv, &parsed.disassoc); + break; + case WF_FSTYPE_AUTHENi: + if (!sta_scan && !ap) + break; + memset(&parsed.authen, 0, sizeof(parsed.authen)); + parsed.authen.hdr = hdr; + parsed.authen.len = len; + wlan_mgmt_decode_authen(&parsed.authen); + acx_l_process_authen(priv, &parsed.authen); + break; + case WF_FSTYPE_DEAUTHENi: + if (!sta && !ap) + break; + memset(&parsed.deauthen, 0, sizeof(parsed.deauthen)); + parsed.deauthen.hdr = hdr; + parsed.deauthen.len = len; + wlan_mgmt_decode_deauthen(&parsed.deauthen); + if (sta) + acx_l_process_deauth_from_ap(priv, &parsed.deauthen); + else + acx_l_process_deauth_from_sta(priv, &parsed.deauthen); + break; + } + + FN_EXIT1(OK); + return OK; +} + + +#if UNUSED +/*---------------------------------------------------------------- +* acx_process_class_frame +* +* Called from IRQ context only +*----------------------------------------------------------------*/ +static int +acx_process_class_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala) +{ + return OK; +} +#endif + + +/*---------------------------------------------------------------- +* acx_l_process_NULL_frame +*----------------------------------------------------------------*/ +#if BOGUS_ITS_NOT_A_NULL_FRAME_HANDLER_AT_ALL +static int +acx_l_process_NULL_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala) +{ + const signed char *esi; + const u8 *ebx; + const wlan_hdr_t *hdr; + const client_t *client; + int result = NOT_OK; + + hdr = acx_get_wlan_hdr(priv, rxbuf); + + switch (WF_FC_FROMTODSi & hdr->fc) { + case 0: + esi = hdr->a1; + ebx = hdr->a2; + break; + case WF_FC_FROMDSi: + esi = hdr->a1; + ebx = hdr->a3; + break; + case WF_FC_TODSi: + esi = hdr->a1; + ebx = hdr->a2; + break; + default: /* WF_FC_FROMTODSi */ + esi = hdr->a1; /* added by me! --vda */ + ebx = hdr->a2; + } + + if (esi[0x0] < 0) { + result = OK; + goto done; + } + + client = acx_l_sta_list_get(priv, ebx); + if (client) + result = NOT_OK; + else { +#if IS_IT_BROKEN + acxlog(L_DEBUG | L_XFER, "\n"); + acx_l_transmit_deauthen(priv, ebx, + WLAN_MGMT_REASON_CLASS2_NONAUTH /* 6 */); +#else + acxlog(L_DEBUG, "received NULL frame from unknown client! " + "We really shouldn't send deauthen here, right?\n"); +#endif + result = OK; + } +done: + return result; +} +#endif + + +/*---------------------------------------------------------------- +* acx_l_process_probe_response +*----------------------------------------------------------------*/ +static int +acx_l_process_probe_response(wlandevice_t *priv, wlan_fr_proberesp_t *req, + const rxbuffer_t *rxbuf) +{ + struct client *bss; + wlan_hdr_t *hdr; + + FN_ENTER; + + hdr = req->hdr; + + if (mac_is_equal(hdr->a3, priv->dev_addr)) { + acxlog(L_ASSOC, "huh, scan found our own MAC!?\n"); + goto ok; /* just skip this one silently */ + } + + bss = acx_l_sta_list_get_or_add(priv, hdr->a2); + + /* NB: be careful modifying bss data! It may be one + ** of already known clients (like our AP is we are a STA) + ** Thus do not blindly modify e.g. current ratemask! */ + + if (STA_LIST_ADD_CAN_FAIL && !bss) { + /* uh oh, we found more sites/stations than we can handle with + * our current setup: pull the emergency brake and stop scanning! */ + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_STOP_SCAN); + /* TODO: a nice comment what below call achieves --vda */ + acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH); + goto ok; + } + /* NB: get_or_add already filled bss->address = hdr->a2 */ + MAC_COPY(bss->bssid, hdr->a3); + + /* copy the ESSID element */ + if (req->ssid && req->ssid->len <= IW_ESSID_MAX_SIZE) { + bss->essid_len = req->ssid->len; + memcpy(bss->essid, req->ssid->ssid, req->ssid->len); + bss->essid[req->ssid->len] = '\0'; + } else { + /* Either no ESSID IE or oversized one */ + printk("%s: received packet has bogus ESSID\n", + priv->netdev->name); + } + + if (req->ds_parms) + bss->channel = req->ds_parms->curr_ch; + if (req->cap_info) + bss->cap_info = ieee2host16(*req->cap_info); + + bss->sir = acx_signal_to_winlevel(rxbuf->phy_level); + bss->snr = acx_signal_to_winlevel(rxbuf->phy_snr); + + bss->rate_cap = 0; /* operational mask */ + bss->rate_bas = 0; /* basic mask */ + if (req->supp_rates) + add_bits_to_ratemasks(req->supp_rates->rates, + req->supp_rates->len, &bss->rate_bas, &bss->rate_cap); + if (req->ext_rates) + add_bits_to_ratemasks(req->ext_rates->rates, + req->ext_rates->len, &bss->rate_bas, &bss->rate_cap); + /* Fix up any possible bogosity - code elsewhere + * is not expecting empty masks */ + if (!bss->rate_cap) + bss->rate_cap = priv->rate_basic; + if (!bss->rate_bas) + bss->rate_bas = 1 << lowest_bit(bss->rate_cap); + if (!bss->rate_cur) + bss->rate_cur = 1 << lowest_bit(bss->rate_bas); + + /* People moan about this being too noisy at L_ASSOC */ + acxlog(L_DEBUG, + "found %s: ESSID='%s' ch=%d " + "BSSID="MACSTR" caps=0x%04X SIR=%d SNR=%d\n", + (bss->cap_info & WF_MGMT_CAP_IBSS) ? "Ad-Hoc peer" : "AP", + bss->essid, bss->channel, MAC(bss->bssid), bss->cap_info, + bss->sir, bss->snr); +ok: + FN_EXIT0; + return OK; +} + + +/*---------------------------------------------------------------- +* get_status_string +*----------------------------------------------------------------*/ +static const char* +get_status_string(unsigned int status) +{ + /* A bit shortened, but hopefully still understandable */ + static const char * const status_str[] = { + /* 0 */ "Successful", + /* 1 */ "Unspecified failure", + /* 2 */ "reserved", + /* 3 */ "reserved", + /* 4 */ "reserved", + /* 5 */ "reserved", + /* 6 */ "reserved", + /* 7 */ "reserved", + /* 8 */ "reserved", + /* 9 */ "reserved", + /*10 */ "Cannot support all requested capabilities in Capability Information field", + /*11 */ "Reassoc denied (reason outside of 802.11b scope)", + /*12 */ "Assoc denied (reason outside of 802.11b scope), maybe MAC filtering by peer?", + /*13 */ "Responding station doesnt support specified auth algorithm", + /*14 */ "Auth rejected: wrong transaction sequence number", + /*15 */ "Auth rejected: challenge failure", + /*16 */ "Auth rejected: timeout for next frame in sequence", + /*17 */ "Assoc denied: too many STAs on this AP", + /*18 */ "Assoc denied: requesting STA doesnt support all data rates in basic set", + /*19 */ "Assoc denied: requesting STA doesnt support Short Preamble", + /*20 */ "Assoc denied: requesting STA doesnt support PBCC Modulation", + /*21 */ "Assoc denied: requesting STA doesnt support Channel Agility" + /*22 */ "reserved", + /*23 */ "reserved", + /*24 */ "reserved", + /*25 */ "Assoc denied: requesting STA doesnt support Short Slot Time", + /*26 */ "Assoc denied: requesting STA doesnt support DSSS-OFDM" + }; + + return status_str[status < VEC_SIZE(status_str) ? status : 2]; +} + + +/*---------------------------------------------------------------- +* acx_l_process_assocresp +*----------------------------------------------------------------*/ +static int +acx_l_process_assocresp(wlandevice_t *priv, const wlan_fr_assocresp_t *req) +{ + const wlan_hdr_t *hdr; + int res = OK; + + FN_ENTER; + hdr = req->hdr; + + if ((ACX_MODE_2_STA == priv->mode) + && mac_is_equal(priv->dev_addr, hdr->a1)) { + u16 st = ieee2host16(*(req->status)); + if (WLAN_MGMT_STATUS_SUCCESS == st) { + priv->aid = ieee2host16(*(req->aid)); + /* tell the card we are associated when we are out of interrupt context */ + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_ASSOCIATE); + } else { + + /* TODO: we shall delete peer from sta_list, and try other candidates... */ + + printk("%s: association FAILED: peer sent " + "response code %d (%s)\n", + priv->netdev->name, st, get_status_string(st)); + res = NOT_OK; + } + } + + FN_EXIT1(res); + return res; +} + + +/*---------------------------------------------------------------- +* acx_l_process_reassocresp +*----------------------------------------------------------------*/ +static int +acx_l_process_reassocresp(wlandevice_t *priv, const wlan_fr_reassocresp_t *req) +{ + const wlan_hdr_t *hdr; + int result = 4; + u16 st; + + FN_ENTER; + hdr = req->hdr; + + if (!mac_is_equal(priv->dev_addr, hdr->a1)) { + result = 3; + goto end; + } + st = ieee2host16(*(req->status)); + if (st == WLAN_MGMT_STATUS_SUCCESS) { + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + } else { + printk("%s: reassociation FAILED: peer sent " + "response code %d (%s)\n", + priv->netdev->name, st, get_status_string(st)); + } + result = OK; +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_l_process_authen +* +* Called only in STA_SCAN or AP mode +*----------------------------------------------------------------*/ +static int +acx_l_process_authen(wlandevice_t *priv, const wlan_fr_authen_t *req) +{ + const wlan_hdr_t *hdr; + client_t *clt; + wlan_ie_challenge_t *chal; + u16 alg, seq, status; + int ap, result; + + FN_ENTER; + + hdr = req->hdr; + + if (acx_debug & L_ASSOC) { + acx_print_mac("AUTHEN priv->addr=", priv->dev_addr, " "); + acx_print_mac("a1=", hdr->a1, " "); + acx_print_mac("a2=", hdr->a2, " "); + acx_print_mac("a3=", hdr->a3, " "); + acx_print_mac("priv->bssid=", priv->bssid, "\n"); + } + + /* TODO: move first check up in caller chain, + ** it's not auth specific */ + if (!mac_is_equal(priv->dev_addr, hdr->a1) + || !mac_is_equal(priv->bssid, hdr->a3)) { + result = OK; + goto end; + } + + alg = ieee2host16(*(req->auth_alg)); + seq = ieee2host16(*(req->auth_seq)); + status = ieee2host16(*(req->status)); + + ap = (priv->mode == ACX_MODE_3_AP); + + if (priv->auth_alg <= 1) { + if (priv->auth_alg != alg) { + acxlog(L_ASSOC, "authentication algorithm mismatch: " + "want: %d, req: %d\n", priv->auth_alg, alg); + result = NOT_OK; + goto end; + } + } + acxlog(L_ASSOC, "algorithm is ok\n"); + + if (ap) { + clt = acx_l_sta_list_get_or_add(priv, hdr->a2); + if (STA_LIST_ADD_CAN_FAIL && !clt) { + acxlog(L_ASSOC, "could not allocate room for client\n"); + result = NOT_OK; + goto end; + } + } else { + clt = priv->ap_client; + if (!mac_is_equal(clt->address, hdr->a2)) { + printk("%s: malformed auth frame from AP?!\n", + priv->netdev->name); + result = NOT_OK; + goto end; + } + } + + /* now check which step in the authentication sequence we are + * currently in, and act accordingly */ + acxlog(L_ASSOC, "acx_process_authen auth seq step %d\n", seq); + switch (seq) { + case 1: + if (!ap) + break; + acx_l_transmit_authen2(priv, req, clt); + break; + case 2: + if (ap) + break; + if (status == WLAN_MGMT_STATUS_SUCCESS) { + if (alg == WLAN_AUTH_ALG_OPENSYSTEM) { + acx_set_status(priv, ACX_STATUS_3_AUTHENTICATED); + acx_l_transmit_assoc_req(priv); + } else + if (alg == WLAN_AUTH_ALG_SHAREDKEY) { + acx_l_transmit_authen3(priv, req); + } + } else { + printk("%s: auth FAILED: peer sent " + "response code %d (%s), " + "still waiting for authentication\n", + priv->netdev->name, + status, get_status_string(status)); + acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH); + } + break; + case 3: + if (!ap) + break; + if ((clt->auth_alg != WLAN_AUTH_ALG_SHAREDKEY) + || (alg != WLAN_AUTH_ALG_SHAREDKEY) + || (clt->auth_step != 2)) + break; + chal = req->challenge; + if (!chal + || memcmp(chal->challenge, clt->challenge_text, WLAN_CHALLENGE_LEN) + || (chal->eid != WLAN_EID_CHALLENGE) + || (chal->len != WLAN_CHALLENGE_LEN) + ) + break; + acx_l_transmit_authen4(priv, req); + MAC_COPY(clt->address, hdr->a2); + clt->used = CLIENT_AUTHENTICATED_2; + clt->auth_step = 4; + clt->seq = ieee2host16(hdr->seq); + break; + case 4: + if (ap) + break; + /* ok, we're through: we're authenticated. Woohoo!! */ + acx_set_status(priv, ACX_STATUS_3_AUTHENTICATED); + acxlog(L_ASSOC, "Authenticated!\n"); + /* now that we're authenticated, request association */ + acx_l_transmit_assoc_req(priv); + break; + } + result = NOT_OK; +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_gen_challenge +*----------------------------------------------------------------*/ +static void +acx_gen_challenge(wlan_ie_challenge_t* d) +{ + FN_ENTER; + d->eid = WLAN_EID_CHALLENGE; + d->len = WLAN_CHALLENGE_LEN; + get_random_bytes(d->challenge, WLAN_CHALLENGE_LEN); + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_deauthen +* STATUS: should be ok, but UNVERIFIED. +*----------------------------------------------------------------*/ +static int +acx_l_transmit_deauthen(wlandevice_t *priv, const u8 *addr, u16 reason) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct deauthen_frame_body *body; + + FN_ENTER; + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(tx); + if (!head) + goto bad; + body = (void*)(head + 1); + + head->fc = (WF_FTYPE_MGMTi | WF_FSTYPE_DEAUTHENi); + head->dur = 0; + MAC_COPY(head->da, addr); + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, priv->bssid); + head->seq = 0; + + acxlog(L_DEBUG | L_ASSOC | L_XFER, + "sending deauthen to "MACSTR" for %d\n", + MAC(addr), reason); + + body->reason = host2ieee16(reason); + + /* body is fixed size here, but beware of cutting-and-pasting this - + ** do not use sizeof(*body) for variable sized mgmt packets! */ + acx_l_dma_tx_data(priv, tx, WLAN_HDR_A3_LEN + sizeof(*body)); + + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_authen1 +*----------------------------------------------------------------*/ +static int +acx_l_transmit_authen1(wlandevice_t *priv) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct auth_frame_body *body; + + FN_ENTER; + + acxlog(L_ASSOC, "Sending authentication1 request, " + "awaiting response!\n"); + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(tx); + if (!head) + goto bad; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_AUTHENi; + head->dur = host2ieee16(0x8000); + MAC_COPY(head->da, priv->bssid); + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, priv->bssid); + head->seq = 0; + + body->auth_alg = host2ieee16(priv->auth_alg); + body->auth_seq = host2ieee16(1); + body->status = host2ieee16(0); + + acx_l_dma_tx_data(priv, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2); + + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_authen2 +*----------------------------------------------------------------*/ +static int +acx_l_transmit_authen2(wlandevice_t *priv, const wlan_fr_authen_t *req, + client_t *clt) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct auth_frame_body *body; + unsigned int packet_len; + + FN_ENTER; + + if (!clt) + goto ok; + + MAC_COPY(clt->address, req->hdr->a2); +#if UNUSED + clt->ps = ((WF_FC_PWRMGTi & req->hdr->fc) != 0); +#endif + clt->auth_alg = ieee2host16(*(req->auth_alg)); + clt->auth_step = 2; + clt->seq = ieee2host16(req->hdr->seq); + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(tx); + if (!head) + goto bad; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */ + head->dur = req->hdr->dur; + MAC_COPY(head->da, req->hdr->a2); + /* MAC_COPY(head->sa, req->hdr->a1); */ + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, req->hdr->a3); + head->seq = req->hdr->seq; + + /* already in IEEE format, no endianness conversion */ + body->auth_alg = *(req->auth_alg); + body->auth_seq = host2ieee16(2); + body->status = host2ieee16(0); + + packet_len = WLAN_HDR_A3_LEN + 2 + 2 + 2; + if (ieee2host16(*(req->auth_alg)) == WLAN_AUTH_ALG_OPENSYSTEM) { + clt->used = CLIENT_AUTHENTICATED_2; + } else { /* shared key */ + acx_gen_challenge(&body->challenge); + memcpy(&clt->challenge_text, body->challenge.challenge, WLAN_CHALLENGE_LEN); + packet_len += 2 + 2 + 2 + 1+1+WLAN_CHALLENGE_LEN; + } + + acxlog_mac(L_ASSOC | L_XFER, + "transmit_auth2: BSSID=", head->bssid, "\n"); + + acx_l_dma_tx_data(priv, tx, packet_len); +ok: + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_authen3 +*----------------------------------------------------------------*/ +static int +acx_l_transmit_authen3(wlandevice_t *priv, const wlan_fr_authen_t *req) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct auth_frame_body *body; + unsigned int packet_len; + + FN_ENTER; + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto ok; + head = acx_l_get_txbuf(tx); + if (!head) + goto ok; + body = (void*)(head + 1); + + head->fc = WF_FC_ISWEPi + WF_FSTYPE_AUTHENi; + /* FIXME: is this needed?? authen4 does it... + head->dur = req->hdr->dur; + head->seq = req->hdr->seq; + */ + MAC_COPY(head->da, priv->bssid); + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, priv->bssid); + + /* already in IEEE format, no endianness conversion */ + body->auth_alg = *(req->auth_alg); + body->auth_seq = host2ieee16(3); + body->status = host2ieee16(0); + memcpy(&body->challenge, req->challenge, req->challenge->len + 2); + packet_len = WLAN_HDR_A3_LEN + 8 + req->challenge->len; + + acxlog(L_ASSOC | L_XFER, "transmit_authen3!\n"); + + acx_l_dma_tx_data(priv, tx, packet_len); +ok: + FN_EXIT1(OK); + return OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_authen4 +*----------------------------------------------------------------*/ +static int +acx_l_transmit_authen4(wlandevice_t *priv, const wlan_fr_authen_t *req) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct auth_frame_body *body; + + FN_ENTER; + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto ok; + head = acx_l_get_txbuf(tx); + if (!head) + goto ok; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */ + head->dur = req->hdr->dur; + MAC_COPY(head->da, req->hdr->a2); + /* MAC_COPY(head->sa, req->hdr->a1); */ + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, req->hdr->a3); + head->seq = req->hdr->seq; + + /* already in IEEE format, no endianness conversion */ + body->auth_alg = *(req->auth_alg); + body->auth_seq = host2ieee16(4); + body->status = host2ieee16(0); + + acx_l_dma_tx_data(priv, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2); +ok: + FN_EXIT1(OK); + return OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_assoc_req +* +* priv->ap_client is a current candidate AP here +*----------------------------------------------------------------*/ +static int +acx_l_transmit_assoc_req(wlandevice_t *priv) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + u8 *body, *p, *prate; + unsigned int packet_len; + u16 cap; + + FN_ENTER; + + acxlog(L_ASSOC, "sending association request, " + "awaiting response. NOT ASSOCIATED YET\n"); + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(tx); + if (!head) + goto bad; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_ASSOCREQi; + head->dur = host2ieee16(0x8000); + MAC_COPY(head->da, priv->bssid); + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, priv->bssid); + head->seq = 0; + + p = body; + /* now start filling the AssocReq frame body */ + + /* since this assoc request will most likely only get + * sent in the STA to AP case (and not when Ad-Hoc IBSS), + * the cap combination indicated here will thus be + * WF_MGMT_CAP_ESSi *always* (no IBSS ever) + * The specs are more than non-obvious on all that: + * + * 802.11 7.3.1.4 Capability Information field + ** APs set the ESS subfield to 1 and the IBSS subfield to 0 within + ** Beacon or Probe Response management frames. STAs within an IBSS + ** set the ESS subfield to 0 and the IBSS subfield to 1 in transmitted + ** Beacon or Probe Response management frames + ** + ** APs set the Privacy subfield to 1 within transmitted Beacon, + ** Probe Response, Association Response, and Reassociation Response + ** if WEP is required for all data type frames within the BSS. + ** STAs within an IBSS set the Privacy subfield to 1 in Beacon + ** or Probe Response management frames if WEP is required + ** for all data type frames within the IBSS */ + + /* note that returning 0 will be refused by several APs... + * (so this indicates that you're probably supposed to + * "confirm" the ESS mode) */ + cap = WF_MGMT_CAP_ESSi; + + /* this one used to be a check on wep_restricted, + * but more likely it's wep_enabled instead */ + if (priv->wep_enabled) + SET_BIT(cap, WF_MGMT_CAP_PRIVACYi); + + /* Probably we can just set these always, because our hw is + ** capable of shortpre and PBCC --vda */ + /* only ask for short preamble if the peer station supports it */ + if (priv->ap_client->cap_info & WF_MGMT_CAP_SHORT) + SET_BIT(cap, WF_MGMT_CAP_SHORTi); + /* only ask for PBCC support if the peer station supports it */ + if (priv->ap_client->cap_info & WF_MGMT_CAP_PBCC) + SET_BIT(cap, WF_MGMT_CAP_PBCCi); + + /* IEs: 1. caps */ + *(u16*)p = cap; p += 2; + /* 2. listen interval */ + *(u16*)p = host2ieee16(priv->listen_interval); p += 2; + /* 3. ESSID */ + p = wlan_fill_ie_ssid(p, + strlen(priv->essid_for_assoc), priv->essid_for_assoc); + /* 4. supp rates */ + prate = p; + p = wlan_fill_ie_rates(p, + priv->rate_supported_len, priv->rate_supported); + /* 5. ext supp rates */ + p = wlan_fill_ie_rates_ext(p, + priv->rate_supported_len, priv->rate_supported); + + if (acx_debug & L_DEBUG) { + printk("association: rates element\n"); + acx_dump_bytes(prate, p - prate); + } + + /* calculate lengths */ + packet_len = WLAN_HDR_A3_LEN + (p - body); + + acxlog(L_ASSOC, "association: requesting caps 0x%04X, ESSID '%s'\n", + cap, priv->essid_for_assoc); + + acx_l_dma_tx_data(priv, tx, packet_len); + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_disassoc +* +* FIXME: looks like incomplete implementation of a helper: +* acx_l_transmit_disassoc(priv, clt) - kick this client (we're an AP) +* acx_l_transmit_disassoc(priv, NULL) - leave BSSID (we're a STA) +*----------------------------------------------------------------*/ +int +acx_l_transmit_disassoc(wlandevice_t *priv, client_t *clt) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct disassoc_frame_body *body; + + FN_ENTER; +/* if (clt != NULL) { */ + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(tx); + if (!head) + goto bad; + body = (void*)(head + 1); + +/* clt->used = CLIENT_AUTHENTICATED_2; - not (yet?) associated */ + + head->fc = WF_FSTYPE_DISASSOCi; + head->dur = 0; + /* huh? It muchly depends on whether we're STA or AP... + ** sta->ap: da=bssid, sa=own, bssid=bssid + ** ap->sta: da=sta, sa=bssid, bssid=bssid. FIXME! */ + MAC_COPY(head->da, priv->bssid); + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, priv->dev_addr); + head->seq = 0; + + /* "Class 3 frame received from nonassociated station." */ + body->reason = host2ieee16(7); + + /* fixed size struct, ok to sizeof */ + acx_l_dma_tx_data(priv, tx, WLAN_HDR_A3_LEN + sizeof(*body)); +/* } */ + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_s_complete_scan +* +* Called either from after_interrupt_task() if: +* 1) there was Scan_Complete IRQ, or +* 2) scanning expired in timer() +* We need to decide which ESS or IBSS to join. +* Iterates thru priv->sta_list: +* if priv->ap is not bcast, will join only specified +* ESS or IBSS with this bssid +* checks peers' caps for ESS/IBSS bit +* checks peers' SSID, allows exact match or hidden SSID +* If station to join is chosen: +* points priv->ap_client to the chosen struct client +* sets priv->essid_for_assoc for future assoc attempt +* Auth/assoc is not yet performed +* Returns OK if there is no need to restart scan +*----------------------------------------------------------------*/ +int +acx_s_complete_scan(wlandevice_t *priv) +{ + struct client *bss; + unsigned long flags; + u16 needed_cap; + int i; + int idx_found = -1; + int result = OK; + + FN_ENTER; + + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + needed_cap = WF_MGMT_CAP_IBSS; /* 2, we require Ad-Hoc */ + break; + case ACX_MODE_2_STA: + needed_cap = WF_MGMT_CAP_ESS; /* 1, we require Managed */ + break; + default: + printk("acx: driver bug: mode=%d in complete_scan()\n", priv->mode); + dump_stack(); + goto end; + } + + acx_lock(priv, flags); + + /* TODO: sta_iterator hiding implementation would be nice here... */ + + for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { + bss = &priv->sta_list[i]; + if (!bss->used) continue; + + acxlog(L_ASSOC, "Scan Table: SSID='%s' CH=%d SIR=%d SNR=%d\n", + bss->essid, bss->channel, bss->sir, bss->snr); + + if (!mac_is_bcast(priv->ap)) + if (!mac_is_equal(bss->bssid, priv->ap)) + continue; /* keep looking */ + + /* broken peer with no mode flags set? */ + if (unlikely(!(bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)))) { + printk("%s: strange peer "MACSTR" found with " + "neither ESS (AP) nor IBSS (Ad-Hoc) " + "capability - skipped\n", + priv->netdev->name, MAC(bss->address)); + continue; + } + acxlog(L_ASSOC, "peer_cap 0x%04X, needed_cap 0x%04X\n", + bss->cap_info, needed_cap); + + /* does peer station support what we need? */ + if ((bss->cap_info & needed_cap) != needed_cap) + continue; /* keep looking */ + + /* strange peer with NO basic rates?! */ + if (unlikely(!bss->rate_bas)) { + printk("%s: strange peer "MACSTR" with empty rate set " + "- skipped\n", + priv->netdev->name, MAC(bss->address)); + continue; + } + + /* do we support all basic rates of this peer? */ + if ((bss->rate_bas & priv->rate_oper) != bss->rate_bas) { +/* we probably need to have all rates as operational rates, + even in case of an 11M-only configuration */ +#if THIS_IS_TROUBLESOME + printk("%s: peer "MACSTR": incompatible basic rates " + "(AP requests 0x%04X, we have 0x%04X) " + "- skipped\n", + priv->netdev->name, MAC(bss->address), + bss->rate_bas, priv->rate_oper); + continue; +#else + printk("%s: peer "MACSTR": incompatible basic rates " + "(AP requests 0x%04X, we have 0x%04X). " + "Considering anyway...\n", + priv->netdev->name, MAC(bss->address), + bss->rate_bas, priv->rate_oper); +#endif + } + + if ( !(priv->reg_dom_chanmask & (1<<(bss->channel-1))) ) { + printk("%s: warning: peer "MACSTR" is on channel %d " + "outside of channel range of current " + "regulatory domain - couldn't join " + "even if other settings match. " + "You might want to adapt your config\n", + priv->netdev->name, MAC(bss->address), + bss->channel); + continue; /* keep looking */ + } + + if (!priv->essid_active || !strcmp(bss->essid, priv->essid)) { + acxlog(L_ASSOC, + "found station with matching ESSID! ('%s' " + "station, '%s' config)\n", + bss->essid, + (priv->essid_active) ? priv->essid : "[any]"); + /* TODO: continue looking for peer with better SNR */ + bss->used = CLIENT_JOIN_CANDIDATE; + idx_found = i; + + /* stop searching if this station is + * on the current channel, otherwise + * keep looking for an even better match */ + if (bss->channel == priv->channel) + break; + } else + if (!bss->essid[0] + || ((' ' == bss->essid[0]) && !bss->essid[1]) + ) { + /* hmm, station with empty or single-space SSID: + * using hidden SSID broadcast? + */ + /* This behaviour is broken: which AP from zillion + ** of APs with hidden SSID you'd try? + ** We should use Probe requests to get Probe responses + ** and check for real SSID (are those never hidden?) */ + bss->used = CLIENT_JOIN_CANDIDATE; + if (idx_found == -1) + idx_found = i; + acxlog(L_ASSOC, "found station with empty or " + "single-space (hidden) SSID, considering " + "for assoc attempt\n"); + /* ...and keep looking for better matches */ + } else { + acxlog(L_ASSOC, "ESSID doesn't match! ('%s' " + "station, '%s' config)\n", + bss->essid, + (priv->essid_active) ? priv->essid : "[any]"); + } + } + + /* TODO: iterate thru join candidates instead */ + /* TODO: rescan if not associated within some timeout */ + if (idx_found != -1) { + char *essid_src; + size_t essid_len; + + bss = &priv->sta_list[idx_found]; + priv->ap_client = bss; + + if (bss->essid[0] == '\0') { + /* if the ESSID of the station we found is empty + * (no broadcast), then use user configured ESSID + * instead */ + essid_src = priv->essid; + essid_len = priv->essid_len; + } else { + essid_src = bss->essid; + essid_len = strlen(bss->essid); + } + + acx_update_capabilities(priv); + + memcpy(priv->essid_for_assoc, essid_src, essid_len); + priv->essid_for_assoc[essid_len] = '\0'; + priv->channel = bss->channel; + MAC_COPY(priv->bssid, bss->bssid); + + bss->rate_cfg = (bss->rate_cap & priv->rate_oper); + bss->rate_cur = 1 << lowest_bit(bss->rate_cfg); + bss->rate_100 = acx_rate111to100(bss->rate_cur); + + acxlog_mac(L_ASSOC, + "matching station found: ", priv->bssid, ", joining\n"); + + /* TODO: do we need to switch to the peer's channel first? */ + + if (ACX_MODE_0_ADHOC == priv->mode) { + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + } else { + acx_l_transmit_authen1(priv); + acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH); + } + } else { /* idx_found == -1 */ + /* uh oh, no station found in range */ + if (ACX_MODE_0_ADHOC == priv->mode) { + printk("%s: no matching station found in range, " + "generating our own IBSS instead\n", + priv->netdev->name); + /* we do it hostap way: */ + MAC_COPY(priv->bssid, priv->dev_addr); + priv->bssid[0] |= 0x02; /* 'local assigned addr' bit */ + /* add IBSS bit to our caps... */ + acx_update_capabilities(priv); + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + /* In order to cmd_join be called below */ + idx_found = 0; + } else { + /* we shall scan again, AP can be + ** just temporarily powered off */ + acxlog(L_ASSOC, + "no matching station found in range yet\n"); + acx_set_status(priv, ACX_STATUS_1_SCANNING); + result = NOT_OK; + } + } + + acx_unlock(priv, flags); + + if (idx_found != -1) { + if (ACX_MODE_0_ADHOC == priv->mode) { + /* need to update channel in beacon template */ + SET_BIT(priv->set_mask, SET_TEMPLATES); + if (ACX_STATE_IFACE_UP & priv->dev_state_mask) + acx_s_update_card_settings(priv, 0, 0); + } + /* Inform firmware on our decision to start or join BSS */ + acx_s_cmd_join_bssid(priv, priv->bssid); + } + +end: + FN_EXIT1(result); + return result; +} + + +#if (POWER_SAVE_80211 == 0) +/*---------------------------------------------------------------- +* acx_s_activate_power_save_mode +*----------------------------------------------------------------*/ +static void +acx_s_activate_power_save_mode(wlandevice_t *priv, /*@unused@*/ int vala) +{ + acx100_ie_powermgmt_t pm; + + FN_ENTER; + + acx_s_interrogate(priv, &pm, ACX1xx_IE_POWER_MGMT); + if (pm.wakeup_cfg != 0x81) + goto end; + + pm.wakeup_cfg = 0; + pm.options = 0; + pm.hangover_period = 0; + acx_s_configure(priv, &pm, ACX1xx_IE_POWER_MGMT); +end: + FN_EXIT0; +} +#endif diff -puN /dev/null drivers/net/wireless/tiacx/helper.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/helper.c 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,4304 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef ACX_PCI +#include +#endif +#include +#include +#include +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#endif +#include +#include + +#include "acx.h" + + +/*********************************************************************** +*/ + +/* Now that the pci_alloc_consistent() problem has been resolved, + * feel free to modify buffer count for ACX100 to 32, too. + * But it's not required since the card isn't too fast anyway */ +#define RXBUFFERCOUNT_ACX100 16 +#define TXBUFFERCOUNT_ACX100 16 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53) +/* dma_alloc_coherent() uses GFP_KERNEL, much less problematic than + * the pci_alloc_consistent() used below using GFP_ATOMIC (quite often causes + * a larger alloc to fail), so use less buffers there to be more successful */ +#define RXBUFFERCOUNT_ACX111 32 +#define TXBUFFERCOUNT_ACX111 32 +#else +#define RXBUFFERCOUNT_ACX111 16 +#define TXBUFFERCOUNT_ACX111 16 +#endif +#define TXBUFFERCOUNT_USB 10 +#define RXBUFFERCOUNT_USB 10 + + +/*********************************************************************** +*/ + +/* minutes to wait until next radio recalibration: */ +#define RECALIB_PAUSE 5 + +const u8 reg_domain_ids[] = + { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40, 0x41, 0x51 }; +/* stupid workaround for the fact that in C the size of an external array + * cannot be determined from within a second file */ +const u8 reg_domain_ids_len = sizeof(reg_domain_ids); +const u16 reg_domain_channel_masks[] = + { 0x07ff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc }; + + +/*********************************************************************** +** Debugging support +*/ +#ifdef PARANOID_LOCKING +static unsigned max_lock_time; +static unsigned max_sem_time; + +void +acx_lock_unhold() { max_lock_time = 0; } +void +acx_sem_unhold() { max_sem_time = 0; } + +static inline const char* +sanitize_str(const char *s) +{ + const char* t = strrchr(s, '/'); + if (t) return t + 1; + return s; +} + +void +acx_lock_debug(wlandevice_t *priv, const char* where) +{ + int count = 100*1000*1000; + where = sanitize_str(where); + while (--count) { + if (!spin_is_locked(&priv->lock)) break; + cpu_relax(); + } + if (!count) { + printk(KERN_EMERG "LOCKUP: already taken at %s!\n", priv->last_lock); + BUG(); + } + priv->last_lock = where; + rdtscl(priv->lock_time); +} +void +acx_unlock_debug(wlandevice_t *priv, const char* where) +{ + unsigned diff; +#ifdef SMP + if (!spin_is_locked(&priv->lock)) { + where = sanitize_str(where); + printk(KERN_EMERG "STRAY UNLOCK at %s!\n", where); + BUG(); + } +#endif + rdtscl(diff); + diff -= priv->lock_time; + if (diff > max_lock_time) { + where = sanitize_str(where); + printk("max lock hold time %d CPU ticks " + "from %s to %s\n", diff, priv->last_lock, where); + max_lock_time = diff; + } +} +void +acx_down_debug(wlandevice_t *priv, const char* where) +{ + int sem_count; + int count = 5000/5; + where = sanitize_str(where); + + while (--count) { + sem_count = atomic_read(&priv->sem.count); + if (sem_count) break; + msleep(5); + } + if (!count) { + printk(KERN_EMERG "D STATE at %s! last sem at %s\n", + where, priv->last_sem); + dump_stack(); + } + priv->last_sem = where; + priv->sem_time = jiffies; + down(&priv->sem); + if (acx_debug & L_LOCK) { + printk("%s: sem_down %d -> %d\n", + where, sem_count, atomic_read(&priv->sem.count)); + } +} +void +acx_up_debug(wlandevice_t *priv, const char* where) +{ + unsigned diff; + int sem_count = atomic_read(&priv->sem.count); + if (sem_count) { + where = sanitize_str(where); + printk(KERN_EMERG "STRAY UP at %s! sem.count=%d\n", where, sem_count); + dump_stack(); + } + diff = jiffies - priv->sem_time; + if (diff > max_sem_time) { + where = sanitize_str(where); + printk("max sem hold time %d jiffies " + "from %s to %s\n", diff, priv->last_sem, where); + max_sem_time = diff; + } + up(&priv->sem); + if (acx_debug & L_LOCK) { + where = sanitize_str(where); + printk("%s: sem_up %d -> %d\n", + where, sem_count, atomic_read(&priv->sem.count)); + } +} +#endif /* PARANOID_LOCKING */ + + +/*********************************************************************** +*/ +#if ACX_DEBUG > 1 + +static int acx_debug_func_indent; +#define DEBUG_TSC 0 +#define FUNC_INDENT_INCREMENT 2 + +#if DEBUG_TSC +#define TIMESTAMP(d) unsigned long d; rdtscl(d) +#else +#define TIMESTAMP(d) unsigned long d = jiffies +#endif + +static const char +spaces[] = " " " "; /* Nx10 spaces */ + +void +log_fn_enter(const char *funcname) +{ + int indent; + TIMESTAMP(d); + + indent = acx_debug_func_indent; + if (indent >= sizeof(spaces)) + indent = sizeof(spaces)-1; + + printk("%08lx %s==> %s\n", + d, + spaces + (sizeof(spaces)-1) - indent, + funcname + ); + + acx_debug_func_indent += FUNC_INDENT_INCREMENT; +} +void +log_fn_exit(const char *funcname) +{ + int indent; + TIMESTAMP(d); + + acx_debug_func_indent -= FUNC_INDENT_INCREMENT; + + indent = acx_debug_func_indent; + if (indent >= sizeof(spaces)) + indent = sizeof(spaces)-1; + + printk("%08lx %s<== %s\n", + d, + spaces + (sizeof(spaces)-1) - indent, + funcname + ); +} +void +log_fn_exit_v(const char *funcname, int v) +{ + int indent; + TIMESTAMP(d); + + acx_debug_func_indent -= FUNC_INDENT_INCREMENT; + + indent = acx_debug_func_indent; + if (indent >= sizeof(spaces)) + indent = sizeof(spaces)-1; + + printk("%08lx %s<== %s: %08X\n", + d, + spaces + (sizeof(spaces)-1) - indent, + funcname, + v + ); +} +#endif /* ACX_DEBUG > 1 */ + + +/*********************************************************************** +** Basically a msleep with logging +*/ +void +acx_s_msleep(int ms) +{ + FN_ENTER; + msleep(ms); + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_get_packet_type_string +*----------------------------------------------------------------*/ +#if ACX_DEBUG +const char* +acx_get_packet_type_string(u16 fc) +{ + static const char * const mgmt_arr[] = { + "MGMT/AssocReq", "MGMT/AssocResp", "MGMT/ReassocReq", + "MGMT/ReassocResp", "MGMT/ProbeReq", "MGMT/ProbeResp", + "MGMT/UNKNOWN", "MGMT/UNKNOWN", "MGMT/Beacon", "MGMT/ATIM", + "MGMT/Disassoc", "MGMT/Authen", "MGMT/Deauthen" + }; + static const char * const ctl_arr[] = { + "CTL/PSPoll", "CTL/RTS", "CTL/CTS", "CTL/Ack", "CTL/CFEnd", + "CTL/CFEndCFAck" + }; + static const char * const data_arr[] = { + "DATA/DataOnly", "DATA/Data CFAck", "DATA/Data CFPoll", + "DATA/Data CFAck/CFPoll", "DATA/Null", "DATA/CFAck", + "DATA/CFPoll", "DATA/CFAck/CFPoll" + }; + const char *str = "UNKNOWN"; + u8 fstype = (WF_FC_FSTYPE & fc) >> 4; + u8 ctl; + + FN_ENTER; + switch (WF_FC_FTYPE & fc) { + case WF_FTYPE_MGMT: + str = "MGMT/UNKNOWN"; + if (fstype < VEC_SIZE(mgmt_arr)) + str = mgmt_arr[fstype]; + break; + case WF_FTYPE_CTL: + ctl = fstype - 0x0a; + str = "CTL/UNKNOWN"; + if (ctl < VEC_SIZE(ctl_arr)) + str = ctl_arr[ctl]; + break; + case WF_FTYPE_DATA: + str = "DATA/UNKNOWN"; + if (fstype < VEC_SIZE(data_arr)) + str = data_arr[fstype]; + break; + } + FN_EXIT0; + return str; +} +#endif + + +/*********************************************************************** +** maps acx111 tx descr rate field to acx100 one +*/ +const u8 +bitpos2rate100[] = { + RATE100_1 ,/* 0 */ + RATE100_2 ,/* 1 */ + RATE100_5 ,/* 2 */ + RATE100_2 ,/* 3, should not happen */ + RATE100_2 ,/* 4, should not happen */ + RATE100_11 ,/* 5 */ + RATE100_2 ,/* 6, should not happen */ + RATE100_2 ,/* 7, should not happen */ + RATE100_22 ,/* 8 */ + RATE100_2 ,/* 9, should not happen */ + RATE100_2 ,/* 10, should not happen */ + RATE100_2 ,/* 11, should not happen */ + RATE100_2 ,/* 12, should not happen */ + RATE100_2 ,/* 13, should not happen */ + RATE100_2 ,/* 14, should not happen */ + RATE100_2 ,/* 15, should not happen */ +}; + +u8 +acx_rate111to100(u16 r) { + return bitpos2rate100[highest_bit(r)]; +} + + +/*---------------------------------------------------------------- +* acx_l_rxmonitor +* Called from IRQ context only +*----------------------------------------------------------------*/ +static void +acx_l_rxmonitor(wlandevice_t *priv, const rxbuffer_t *rxbuf) +{ + wlansniffrm_t *msg; + struct sk_buff *skb; + void *datap; + unsigned int skb_len; + int payload_offset; + + FN_ENTER; + + /* we are in big luck: the acx100 doesn't modify any of the fields */ + /* in the 802.11 frame. just pass this packet into the PF_PACKET */ + /* subsystem. yeah. */ +#if 0 + if (!(priv->rx_config_1 & RX_CFG1_INCLUDE_RXBUF_HDR)) { + printk("rx_config_1 is missing RX_CFG1_INCLUDE_RXBUF_HDR\n"); + goto end; + } +#endif + payload_offset = ((u8*)acx_get_wlan_hdr(priv, rxbuf) - (u8*)rxbuf); + skb_len = RXBUF_BYTES_USED(rxbuf) - payload_offset; + + /* sanity check */ + if (skb_len > (WLAN_A4FR_MAXLEN_WEP)) { + printk("monitor mode panic: oversized frame!\n"); + goto end; + } + + if (priv->netdev->type == ARPHRD_IEEE80211_PRISM) + skb_len += sizeof(*msg); + + /* allocate skb */ + skb = dev_alloc_skb(skb_len); + if (!skb) { + printk("%s: no memory for skb (%u bytes)\n", + priv->netdev->name, skb_len); + goto end; + } + + skb_put(skb, skb_len); + + /* when in raw 802.11 mode, just copy frame as-is */ + if (priv->netdev->type == ARPHRD_IEEE80211) + datap = skb->data; + else { /* otherwise, emulate prism header */ + msg = (wlansniffrm_t*)skb->data; + datap = msg + 1; + + msg->msgcode = WLANSNIFFFRM; + msg->msglen = sizeof(*msg); + strncpy(msg->devname, priv->netdev->name, sizeof(msg->devname)-1); + msg->devname[sizeof(msg->devname)-1] = '\0'; + + msg->hosttime.did = WLANSNIFFFRM_hosttime; + msg->hosttime.status = WLANITEM_STATUS_data_ok; + msg->hosttime.len = 4; + msg->hosttime.data = jiffies; + + msg->mactime.did = WLANSNIFFFRM_mactime; + msg->mactime.status = WLANITEM_STATUS_data_ok; + msg->mactime.len = 4; + msg->mactime.data = rxbuf->time; + + msg->channel.did = WLANSNIFFFRM_channel; + msg->channel.status = WLANITEM_STATUS_data_ok; + msg->channel.len = 4; + msg->channel.data = priv->channel; + + msg->rssi.did = WLANSNIFFFRM_rssi; + msg->rssi.status = WLANITEM_STATUS_no_value; + msg->rssi.len = 4; + msg->rssi.data = 0; + + msg->sq.did = WLANSNIFFFRM_sq; + msg->sq.status = WLANITEM_STATUS_no_value; + msg->sq.len = 4; + msg->sq.data = 0; + + msg->signal.did = WLANSNIFFFRM_signal; + msg->signal.status = WLANITEM_STATUS_data_ok; + msg->signal.len = 4; + msg->signal.data = rxbuf->phy_snr; + + msg->noise.did = WLANSNIFFFRM_noise; + msg->noise.status = WLANITEM_STATUS_data_ok; + msg->noise.len = 4; + msg->noise.data = rxbuf->phy_level; + + msg->rate.did = WLANSNIFFFRM_rate; + msg->rate.status = WLANITEM_STATUS_data_ok; + msg->rate.len = 4; + msg->rate.data = rxbuf->phy_plcp_signal/5; + + msg->istx.did = WLANSNIFFFRM_istx; + msg->istx.status = WLANITEM_STATUS_data_ok; + msg->istx.len = 4; + msg->istx.data = 0; /* tx=0: it's not a tx packet */ + + skb_len -= sizeof(*msg); + + msg->frmlen.did = WLANSNIFFFRM_signal; + msg->frmlen.status = WLANITEM_STATUS_data_ok; + msg->frmlen.len = 4; + msg->frmlen.data = skb_len; + } + + memcpy(datap, ((unsigned char*)rxbuf)+payload_offset, skb_len); + + skb->dev = priv->netdev; + skb->dev->last_rx = jiffies; + + skb->mac.raw = skb->data; + skb->ip_summed = CHECKSUM_NONE; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_80211_RAW); + netif_rx(skb); + + priv->stats.rx_packets++; + priv->stats.rx_bytes += skb->len; +end: + FN_EXIT0; +} + + +/*********************************************************************** +*/ +/* + * Calculate level like the feb 2003 windows driver seems to do + */ +u8 +acx_signal_to_winlevel(u8 rawlevel) +{ + /* u8 winlevel = (u8) (0.5 + 0.625 * rawlevel); */ + u8 winlevel = ((4 + (rawlevel * 5)) / 8); + + if (winlevel > 100) + winlevel = 100; + return winlevel; +} + +u8 +acx_signal_determine_quality(u8 signal, u8 noise) +{ + int qual; + + qual = (((signal - 30) * 100 / 70) + (100 - noise * 4)) / 2; + + if (qual > 100) + return 100; + if (qual < 0) + return 0; + return qual; +} + + +/*************************************************************** +** acx_l_process_rxbuf +** +** NB: used by USB code also +*/ +void +acx_l_process_rxbuf(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + struct wlan_hdr *hdr; + unsigned int buf_len; + unsigned int qual; + u16 fc; + + hdr = acx_get_wlan_hdr(priv, rxbuf); + /* length of frame from control field to last byte of FCS */ + buf_len = RXBUF_BYTES_RCVD(rxbuf); + fc = le16_to_cpu(hdr->fc); + + if ( ((WF_FC_FSTYPE & fc) != WF_FSTYPE_BEACON) + || (acx_debug & L_XFER_BEACON) + ) { + acxlog(L_XFER|L_DATA, "rx: %s " + "time %u len %u signal %u SNR %u macstat %02X " + "phystat %02X phyrate %u status %u\n", + acx_get_packet_type_string(fc), + le32_to_cpu(rxbuf->time), + buf_len, + acx_signal_to_winlevel(rxbuf->phy_level), + acx_signal_to_winlevel(rxbuf->phy_snr), + rxbuf->mac_status, + rxbuf->phy_stat_baseband, + rxbuf->phy_plcp_signal, + priv->status); + } + + if (unlikely(acx_debug & L_DATA)) { + printk("rx: 802.11 buf[%u]: ", buf_len); + acx_dump_bytes(hdr, buf_len); + } + + /* FIXME: should check for Rx errors (rxbuf->mac_status? + * discard broken packets - but NOT for monitor!) + * and update Rx packet statistics here */ + + if (unlikely(priv->mode == ACX_MODE_MONITOR)) { + acx_l_rxmonitor(priv, rxbuf); + } else if (likely(buf_len >= WLAN_HDR_A3_LEN)) { + acx_l_rx_ieee802_11_frame(priv, rxbuf); + } else { + acxlog(L_DEBUG | L_XFER | L_DATA, + "rx: NOT receiving packet (%s): " + "size too small (%u)\n", + acx_get_packet_type_string(fc), + buf_len); + } + + /* Now check Rx quality level, AFTER processing packet. + * I tried to figure out how to map these levels to dBm + * values, but for the life of me I really didn't + * manage to get it. Either these values are not meant to + * be expressed in dBm, or it's some pretty complicated + * calculation. */ + +#if FROM_SCAN_SOURCE_ONLY + /* only consider packets originating from the MAC + * address of the device that's managing our BSSID. + * Disable it for now, since it removes information (levels + * from different peers) and slows the Rx path. */ + if (priv->ap_client + && mac_is_equal(hdr->a2, priv->ap_client->address)) { +#endif + priv->wstats.qual.level = acx_signal_to_winlevel(rxbuf->phy_level); + priv->wstats.qual.noise = acx_signal_to_winlevel(rxbuf->phy_snr); +#ifndef OLD_QUALITY + qual = acx_signal_determine_quality(priv->wstats.qual.level, + priv->wstats.qual.noise); +#else + qual = (priv->wstats.qual.noise <= 100) ? + 100 - priv->wstats.qual.noise : 0; +#endif + priv->wstats.qual.qual = qual; + priv->wstats.qual.updated = 7; /* all 3 indicators updated */ +#if FROM_SCAN_SOURCE_ONLY + } +#endif +} + + +/*---------------------------------------------------------------- +* acx100_s_init_memory_pools +* +* Comment: +* FIXME: This function still needs a cleanup +*----------------------------------------------------------------*/ +static int +acx100_s_init_memory_pools(wlandevice_t *priv, const acx_ie_memmap_t *mmt) +{ + acx100_ie_memblocksize_t MemoryBlockSize; + acx100_ie_memconfigoption_t MemoryConfigOption; + u32 TotalMemoryBlocks; + + u32 RxBlockNum; + u32 TotalRxBlockSize; + u32 TxBlockNum; + u32 TotalTxBlockSize; + + FN_ENTER; + + /* Let's see if we can follow this: + first we select our memory block size (which I think is + completely arbitrary) */ + MemoryBlockSize.size = cpu_to_le16(priv->memblocksize); + + /* Then we alert the card to our decision of block size */ + if (OK != acx_s_configure(priv, &MemoryBlockSize, ACX100_IE_BLOCK_SIZE)) { + printk("acx: MemoryBlockSizeWrite FAILED\n"); + goto bad; + } + + /* We figure out how many total blocks we can create, using + the block size we chose, and the beginning and ending + memory pointers, i.e.: end-start/size */ + TotalMemoryBlocks = (le32_to_cpu(mmt->PoolEnd) - le32_to_cpu(mmt->PoolStart)) / priv->memblocksize; + + acxlog(L_DEBUG, "TotalMemoryBlocks=%u (%u bytes)\n", + TotalMemoryBlocks, TotalMemoryBlocks*priv->memblocksize); + + /* This one I have no idea on */ + /* block-transfer=0x20000 + * indirect descriptors=0x10000 + */ +#ifdef ACX_USB + MemoryConfigOption.DMA_config = cpu_to_le32(0x20000); +#else + MemoryConfigOption.DMA_config = cpu_to_le32(0x30000); + + /* Declare start of the Rx host pool */ +//Should be of acx_ptr type, no? + MemoryConfigOption.pRxHostDesc = cpu_to_le32((u32)(long)priv->RxHostDescPoolStart); + acxlog(L_DEBUG, "pRxHostDesc 0x%08X, RxHostDescPoolStart 0x%p\n", + MemoryConfigOption.pRxHostDesc, priv->RxHostDescPoolStart); +#endif + + /* 50% of the allotment of memory blocks go to tx descriptors */ + TxBlockNum = TotalMemoryBlocks / 2; + MemoryConfigOption.TxBlockNum = cpu_to_le16(TxBlockNum); + + /* and 50% go to the rx descriptors */ + RxBlockNum = TotalMemoryBlocks - TxBlockNum; + MemoryConfigOption.RxBlockNum = cpu_to_le16(RxBlockNum); + + /* size of the tx and rx descriptor queues */ + TotalTxBlockSize = TxBlockNum * priv->memblocksize; + TotalRxBlockSize = RxBlockNum * priv->memblocksize; + acxlog(L_DEBUG, "TxBlockNum %u RxBlockNum %u TotalTxBlockSize %u " + "TotalTxBlockSize %u\n", TxBlockNum, RxBlockNum, + TotalTxBlockSize, TotalRxBlockSize); + + + /* align the tx descriptor queue to an alignment of 0x20 (32 bytes) */ + MemoryConfigOption.rx_mem = + cpu_to_le32((le32_to_cpu(mmt->PoolStart) + 0x1f) & ~0x1f); + + /* align the rx descriptor queue to units of 0x20 + * and offset it by the tx descriptor queue */ + MemoryConfigOption.tx_mem = + cpu_to_le32((le32_to_cpu(mmt->PoolStart) + TotalRxBlockSize + 0x1f) & ~0x1f); + acxlog(L_DEBUG, "rx_mem %08X rx_mem %08X\n", + MemoryConfigOption.tx_mem, MemoryConfigOption.rx_mem); + + /* alert the device to our decision */ + if (OK != acx_s_configure(priv, &MemoryConfigOption, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) { + printk("acx: configure memory config options FAILED\n"); + goto bad; + } + + /* and tell the device to kick it into gear */ + if (OK != acx_s_issue_cmd(priv, ACX100_CMD_INIT_MEMORY, NULL, 0)) { + printk("acx: init memory FAILED\n"); + goto bad; + } + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx100_s_create_dma_regions +* +* Note that this fn messes up heavily with hardware, but we cannot +* lock it (we need to sleep). Not a problem since IRQs can't happen +*----------------------------------------------------------------*/ +void BUG_acx100_ie_queueconfig_t_must_be_0x20_bytes(void); + +int +acx100_s_create_dma_regions(wlandevice_t *priv) +{ + acx100_ie_queueconfig_t qcfg; + acx_ie_memmap_t MemMap; + int res = NOT_OK; +#ifdef ACX_PCI + struct TIWLAN_DC *pDc; + pDc = &priv->dc; + pDc->priv = priv; +#endif + + FN_ENTER; + + /* read out the acx100 physical start address for the queues */ + if (OK != acx_s_interrogate(priv, &MemMap, ACX1xx_IE_MEMORY_MAP)) { + printk("acx: ctlMemoryMapRead FAILED\n"); + goto fail; + } + + /* # of items in Rx and Tx queues */ + priv->TxQueueCnt = TXBUFFERCOUNT_ACX100; + priv->RxQueueCnt = RXBUFFERCOUNT_ACX100; + +#ifdef ACX_USB + qcfg.NumTxDesc = TXBUFFERCOUNT_USB; + qcfg.NumRxDesc = RXBUFFERCOUNT_USB; +#endif + +#ifdef ACX_PCI + /* calculate size of queues */ + qcfg.AreaSize = cpu_to_le32( + (sizeof(struct txdesc) * TXBUFFERCOUNT_ACX100 + + sizeof(struct rxdesc) * RXBUFFERCOUNT_ACX100 + 8) + ); +#endif + qcfg.NumTxQueues = 1; /* number of tx queues */ + + /* sets the beginning of the tx descriptor queue */ + qcfg.TxQueueStart = MemMap.QueueStart; + qcfg.TxQueuePri = 0; +#ifdef ACX_PCI + pDc->ui32ACXTxQueueStart = le32_to_cpu(MemMap.QueueStart); + + /* sets the beginning of the rx descriptor queue, after the tx descrs */ + pDc->ui32ACXRxQueueStart = pDc->ui32ACXTxQueueStart + + priv->TxQueueCnt * sizeof(txdesc_t); + qcfg.RxQueueStart = cpu_to_le32(pDc->ui32ACXRxQueueStart); +#endif + qcfg.QueueOptions = 1; /* auto reset descriptor */ + +#ifdef ACX_PCI + /* sets the end of the rx descriptor queue */ + qcfg.QueueEnd = cpu_to_le32(pDc->ui32ACXRxQueueStart + + priv->RxQueueCnt * sizeof(struct rxdesc)); +#endif + + /* sets the beginning of the next queue */ + qcfg.HostQueueEnd = cpu_to_le32(le32_to_cpu(qcfg.QueueEnd) + 8); + + acxlog(L_DEBUG, "initializing Queue Indicator\n"); + + if (sizeof(qcfg) != 0x20) + BUG_acx100_ie_queueconfig_t_must_be_0x20_bytes(); + if (OK != acx_s_configure(priv, &qcfg, ACX1xx_IE_QUEUE_CONFIG)) { + printk("acx: ctlQueueConfigurationWrite FAILED\n"); + goto fail; + } + +#ifdef ACX_PCI + if (OK != acx_s_create_tx_host_desc_queue(pDc)) { + printk("acx: create_tx_host_desc_queue FAILED\n"); + goto fail; + } + if (OK != acx_s_create_rx_host_desc_queue(pDc)) { + printk("acx: create_rx_host_desc_queue FAILED\n"); + goto fail; + } + + pDc->pTxDescQPool = NULL; + pDc->pRxDescQPool = NULL; + + acx_s_create_tx_desc_queue(pDc); + acx_create_rx_desc_queue(pDc); +#endif + if (OK != acx_s_interrogate(priv, &MemMap, ACX1xx_IE_MEMORY_MAP)) { + printk("acx: FAILED to read memory map\n"); + goto fail; + } + + /* FIXME: huh, why call ACX1xx_IE_MEMORY_MAP twice in the USB case? + Is this needed?? */ +#ifdef ACX_USB + if (OK != acx_s_configure(priv, &MemMap, ACX1xx_IE_MEMORY_MAP)) { + printk("acx: FAILED to write memory map\n"); + goto fail; + } +#endif + + MemMap.PoolStart = cpu_to_le32((le32_to_cpu(MemMap.QueueEnd) + 0x1f + 4) & ~0x1f); + + if (OK != acx_s_configure(priv, &MemMap, ACX1xx_IE_MEMORY_MAP)) { + printk("acx: ctlMemoryMapWrite FAILED\n"); + goto fail; + } + + if (OK != acx100_s_init_memory_pools(priv, &MemMap)) { + printk("acx: acx100_init_memory_pools FAILED\n"); + goto fail; + } + + res = OK; + goto end; + +fail: + acx_s_msleep(1000); /* ? */ +#ifdef ACX_PCI + acx_free_desc_queues(pDc); +#endif +end: + FN_EXIT1(res); + return res; +} + + +/*---------------------------------------------------------------- +* acx111_s_create_dma_regions +* +* Note that this fn messes up heavily with hardware, but we cannot +* lock it (we need to sleep). Not a problem since IRQs can't happen +*----------------------------------------------------------------*/ +#define ACX111_PERCENT(percent) ((percent)/5) + +int +acx111_s_create_dma_regions(wlandevice_t *priv) +{ +#ifdef ACX_PCI + struct acx111_ie_memoryconfig memconf; + struct acx111_ie_queueconfig queueconf; + struct TIWLAN_DC *pDc; + + FN_ENTER; + + pDc = &priv->dc; + pDc->priv = priv; + + /* Calculate memory positions and queue sizes */ + + priv->TxQueueCnt = TXBUFFERCOUNT_ACX111; + priv->RxQueueCnt = RXBUFFERCOUNT_ACX111; + + /* Set up our host descriptor pool + data pool */ + if (OK != acx_s_create_tx_host_desc_queue(pDc)) { + printk("acx: create_tx_host_desc_queue FAILED\n"); + goto fail; + } + if (OK != acx_s_create_rx_host_desc_queue(pDc)) { + printk("acx: create_rx_host_desc_queue FAILED\n"); + goto fail; + } + + memset(&memconf, 0, sizeof(memconf)); + /* the number of STAs (STA contexts) to support + ** NB: was set to 1 and everything seemed to work nevertheless... */ + memconf.no_of_stations = cpu_to_le16(VEC_SIZE(priv->sta_list)); + /* specify the memory block size. Default is 256 */ + memconf.memory_block_size = cpu_to_le16(priv->memblocksize); + /* let's use 50%/50% for tx/rx (specify percentage, units of 5%) */ + memconf.tx_rx_memory_block_allocation = ACX111_PERCENT(50); + /* set the count of our queues + ** NB: struct acx111_ie_memoryconfig shall be modified + ** if we ever will switch to more than one rx and/or tx queue */ + memconf.count_rx_queues = 1; + memconf.count_tx_queues = 1; + /* 0 == Busmaster Indirect Memory Organization, which is what we want + * (using linked host descs with their allocated mem). + * 2 == Generic Bus Slave */ + /* done by memset: memconf.options = 0; */ + /* let's use 25% for fragmentations and 75% for frame transfers + * (specified in units of 5%) */ + memconf.fragmentation = ACX111_PERCENT(75); + /* Rx descriptor queue config */ + memconf.rx_queue1_count_descs = RXBUFFERCOUNT_ACX111; + memconf.rx_queue1_type = 7; /* must be set to 7 */ + /* done by memset: memconf.rx_queue1_prio = 0; low prio */ + memconf.rx_queue1_host_rx_start = cpu_to_le32(pDc->RxHostDescQPoolPhyAddr); + /* Tx descriptor queue config */ + memconf.tx_queue1_count_descs = TXBUFFERCOUNT_ACX111; + /* done by memset: memconf.tx_queue1_attributes = 0; lowest priority */ + + /* NB1: this looks wrong: (memconf,ACX1xx_IE_QUEUE_CONFIG), + ** (queueconf,ACX1xx_IE_MEMORY_CONFIG_OPTIONS) look swapped, eh? + ** But it is actually correct wrt IE numbers. + ** NB2: sizeof(memconf) == 28 == 0x1c but configure(ACX1xx_IE_QUEUE_CONFIG) + ** writes 0x20 bytes (because same IE for acx100 uses struct acx100_ie_queueconfig + ** wich is 4 bytes larger. what a mess...) */ + if (OK != acx_s_configure(priv, &memconf, ACX1xx_IE_QUEUE_CONFIG)) { + printk("acx: setting acx111_ie_memoryconfig FAILED\n"); + goto fail; + } + + /* read out queueconf */ + if (OK != acx_s_interrogate(priv, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) { + printk("acx: read queuehead FAILED\n"); + } + + pDc->ui32ACXRxQueueStart = le32_to_cpu(queueconf.rx1_queue_address); + pDc->ui32ACXTxQueueStart = le32_to_cpu(queueconf.tx1_queue_address); + + acxlog(L_INIT, "dump queue head (from card):\n" + "len: %u\n" + "tx_memory_block_address: %X\n" + "rx_memory_block_address: %X\n" + "rx1_queue address: %X\n" + "tx1_queue address: %X\n", + le16_to_cpu(queueconf.len), + le32_to_cpu(queueconf.tx_memory_block_address), + le32_to_cpu(queueconf.rx_memory_block_address), + pDc->ui32ACXRxQueueStart, + pDc->ui32ACXTxQueueStart); + pDc->pRxDescQPool = (struct rxdesc *) ((u8 *)priv->iobase2 + + pDc->ui32ACXRxQueueStart); + + pDc->pTxDescQPool = (struct txdesc *) ((u8 *)priv->iobase2 + + pDc->ui32ACXTxQueueStart); + acx_s_create_tx_desc_queue(pDc); + acx_create_rx_desc_queue(pDc); + + FN_EXIT1(OK); + return OK; + +fail: + acx_free_desc_queues(pDc); + + FN_EXIT1(NOT_OK); +#endif /* CONFIG_TIACX_PCI */ + return NOT_OK; +} + + +/*************************************************************** +*/ +void +acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr) +{ + acxlog(L_ASSOC, "acx: unknown EID %d in mgmt frame at offset %d\n", + ie_ptr->eid, (int) ((u8*)ie_ptr - (u8*)hdr)); + if (acx_debug & (L_DATA|L_ASSOC)) { + printk("frame (%s): ", + acx_get_packet_type_string(le16_to_cpu(hdr->fc))); + acx_dump_bytes(hdr, len); + } +} + + +/*************************************************************** +*/ +#if ACX_DEBUG +void +acx_dump_bytes(const void *data, int num) +{ + const u8* ptr = (const u8*)data; + + if (num <= 0) { + printk("\n"); + return; + } + + while (num >= 16) { + printk( "%02X %02X %02X %02X %02X %02X %02X %02X " + "%02X %02X %02X %02X %02X %02X %02X %02X\n", + ptr[0], ptr[1], ptr[2], ptr[3], + ptr[4], ptr[5], ptr[6], ptr[7], + ptr[8], ptr[9], ptr[10], ptr[11], + ptr[12], ptr[13], ptr[14], ptr[15]); + num -= 16; + ptr += 16; + } + if (num > 0) { + while (--num > 0) + printk("%02X ", *ptr++); + printk("%02X\n", *ptr); + } +} +#endif + + +/*---------------------------------------------------------------- +* acx_i_start_xmit +* +* Called by network core. Can be called outside of process context. +*----------------------------------------------------------------*/ +int +acx_i_start_xmit(struct sk_buff *skb, netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + tx_t *tx; + void *txbuf; + unsigned long flags; + int txresult = NOT_OK; + int len; + + FN_ENTER; + + if (unlikely(!skb)) { + /* indicate success */ + txresult = OK; + goto end_no_unlock; + } + if (unlikely(!priv)) { + goto end_no_unlock; + } + +/* Example from tg3.c. Do we need to do this similarly? + local_irq_save(flags); + if (!spin_trylock(&tp->lock)) { + local_irq_restore(flags); + return NETDEV_TX_LOCKED; + } + ... + mmiowb(); + spin_unlock_irqrestore(&tp->lock, flags); +*/ + acx_lock(priv, flags); + + if (unlikely(!(priv->dev_state_mask & ACX_STATE_IFACE_UP))) { + goto end; + } + if (unlikely(priv->mode == ACX_MODE_OFF)) { + goto end; + } + if (unlikely(acx_queue_stopped(dev))) { + acxlog(L_DEBUG, "%s: called when queue stopped\n", __func__); + goto end; + } + if (unlikely(ACX_STATUS_4_ASSOCIATED != priv->status)) { + acxlog(L_XFER, "trying to xmit, but not associated yet: " + "aborting...\n"); + /* silently drop the packet, since we're not connected yet */ + txresult = OK; + /* ...but indicate an error nevertheless */ + priv->stats.tx_errors++; + goto end; + } + +#if 0 + /* we're going to transmit now, so stop another packet from entering. + * FIXME: most likely we shouldn't do it like that, but instead: + * stop the queue during card init, then wake the queue once + * we're associated to the network, then stop the queue whenever + * we don't have any free Tx buffers left, and wake it again once a + * Tx buffer becomes free again. And of course also stop the + * queue once we lose association to the network (since it + * doesn't make sense to allow more user packets if we can't + * forward them to a network). + * FIXME: Hmm, seems this is all wrong. We SHOULD leave the + * queue open from the beginning (as long as we're not full, + * and also even before we're even associated), + * otherwise we'll get NETDEV WATCHDOG transmit timeouts... */ + acx_stop_queue(dev, "during tx"); +#endif + tx = acx_l_alloc_tx(priv); + if (unlikely(!tx)) { + printk("%s: start_xmit: txdesc ring is full, dropping tx\n", + dev->name); + txresult = NOT_OK; + goto end; + } + + txbuf = acx_l_get_txbuf(tx); + if (!txbuf) { + /* Card was removed */ + txresult = NOT_OK; + goto end; + } + len = acx_l_ether_to_txbuf(priv, txbuf, skb); + if (len < 0) { + /* Error in packet conversion */ + txresult = NOT_OK; + goto end; + } + acx_l_dma_tx_data(priv, tx, len); + dev->trans_start = jiffies; + + txresult = OK; + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + +end: + acx_unlock(priv, flags); + +end_no_unlock: + if ((txresult == OK) && skb) + dev_kfree_skb_any(skb); + + FN_EXIT1(txresult); + return txresult; +} + + +/*********************************************************************** +** Interrogate/configure commands +*/ +static const u16 +CtlLength[] = { + 0, + ACX100_IE_ACX_TIMER_LEN, + ACX1xx_IE_POWER_MGMT_LEN, + ACX1xx_IE_QUEUE_CONFIG_LEN, + ACX100_IE_BLOCK_SIZE_LEN, + ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN, + ACX1xx_IE_RATE_FALLBACK_LEN, + ACX100_IE_WEP_OPTIONS_LEN, + ACX1xx_IE_MEMORY_MAP_LEN, /* ACX1xx_IE_SSID_LEN, */ + 0, + ACX1xx_IE_ASSOC_ID_LEN, + 0, + ACX1xx_IE_CONFIG_OPTIONS_LEN, + ACX1xx_IE_FWREV_LEN, + ACX1xx_IE_FCS_ERROR_COUNT_LEN, + ACX1xx_IE_MEDIUM_USAGE_LEN, + ACX1xx_IE_RXCONFIG_LEN, + 0, + 0, + ACX1xx_IE_FIRMWARE_STATISTICS_LEN, + 0, + ACX1xx_IE_FEATURE_CONFIG_LEN, + ACX111_IE_KEY_CHOOSE_LEN, +}; + +static const u16 +CtlLengthDot11[] = { + 0, + ACX1xx_IE_DOT11_STATION_ID_LEN, + 0, + ACX100_IE_DOT11_BEACON_PERIOD_LEN, + ACX1xx_IE_DOT11_DTIM_PERIOD_LEN, + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN, + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN, + ACX100_IE_DOT11_WEP_DEFAULT_KEY_LEN, + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN, + 0, + ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN, + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN, + 0, + ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN, + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN, + ACX100_IE_DOT11_ED_THRESHOLD_LEN, + ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN, + 0, + 0, + 0, +}; + + +#if !ACX_DEBUG +int +acx_s_configure(wlandevice_t *priv, void *pdr, int type) +{ +#else +int +acx_s_configure_debug(wlandevice_t *priv, void *pdr, int type, const char* typestr) +{ +#endif + u16 len; + + /* TODO implement and check other acx111 commands */ + if ((priv->chip_type == CHIPTYPE_ACX111) + && (type == ACX1xx_IE_DOT11_CURRENT_ANTENNA)) { + /* acx111 has differing struct size */ + acxlog(L_CTL, "Configure Command %s is not supported " + "under acx111 (yet)\n", typestr); + return NOT_OK; + } + + if (type < 0x1000) + len = CtlLength[type]; + else + len = CtlLengthDot11[type-0x1000]; + + acxlog(L_XFER, "configure(type:%s,len:%u)\n", typestr, len); + if (unlikely(!len)) { + acxlog(L_DEBUG, "zero-length type %s?!\n", typestr); + } + + ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type); + ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len); + return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIGURE, pdr, len + 4); +} + +#if !ACX_DEBUG +int +acx_s_interrogate(wlandevice_t *priv, void *pdr, int type) +{ +#else +int +acx_s_interrogate_debug(wlandevice_t *priv, void *pdr, int type, + const char* typestr) +{ +#endif + u16 len; + + if (type < 0x1000) + len = CtlLength[type]; + else + len = CtlLengthDot11[type-0x1000]; + + ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type); + ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len); + + acxlog(L_CTL, "interrogate(type:%s,len:%u)\n", typestr, len); + + return acx_s_issue_cmd(priv, ACX1xx_CMD_INTERROGATE, pdr, len + 4); +} + + +/*********************************************************************** +** acx_l_update_ratevector +** +** Updates priv->rate_supported[_len] according to rate_{basic,oper} +*/ +const u8 +bitpos2ratebyte[] = { + DOT11RATEBYTE_1, + DOT11RATEBYTE_2, + DOT11RATEBYTE_5_5, + DOT11RATEBYTE_6_G, + DOT11RATEBYTE_9_G, + DOT11RATEBYTE_11, + DOT11RATEBYTE_12_G, + DOT11RATEBYTE_18_G, + DOT11RATEBYTE_22, + DOT11RATEBYTE_24_G, + DOT11RATEBYTE_36_G, + DOT11RATEBYTE_48_G, + DOT11RATEBYTE_54_G, +}; + +void +acx_l_update_ratevector(wlandevice_t *priv) +{ + u16 bcfg = priv->rate_basic; + u16 ocfg = priv->rate_oper; + u8 *supp = priv->rate_supported; + const u8 *dot11 = bitpos2ratebyte; + + FN_ENTER; + + while (ocfg) { + if (ocfg & 1) { + *supp = *dot11; + if (bcfg & 1) { + *supp |= 0x80; + } + supp++; + } + dot11++; + ocfg >>= 1; + bcfg >>= 1; + } + priv->rate_supported_len = supp - priv->rate_supported; + if (acx_debug & L_ASSOC) { + printk("new ratevector: "); + acx_dump_bytes(priv->rate_supported, priv->rate_supported_len); + } + FN_EXIT0; +} + + +#ifdef CONFIG_PROC_FS +/*********************************************************************** +** /proc files +*/ +/*********************************************************************** +** acx_l_proc_output +** Generate content for our /proc entry +** +** Arguments: +** buf is a pointer to write output to +** priv is the usual pointer to our private struct wlandevice +** Returns: +** number of bytes actually written to buf +** Side effects: +** none +*/ +static int +acx_l_proc_output(char *buf, wlandevice_t *priv) +{ + char *p = buf; + int i; + + FN_ENTER; + + p += sprintf(p, + "acx driver version:\t\t" WLAN_RELEASE "\n" + "Wireless extension version:\t" STRING(WIRELESS_EXT) "\n" + "chip name:\t\t\t%s (0x%08X)\n" + "radio type:\t\t\t0x%02X\n" + /* TODO: add radio type string from acx_display_hardware_details */ + "form factor:\t\t\t0x%02X\n" + /* TODO: add form factor string from acx_display_hardware_details */ + "EEPROM version:\t\t\t0x%02X\n" + "firmware version:\t\t%s (0x%08X)\n", + priv->chip_name, priv->firmware_id, + priv->radio_type, + priv->form_factor, + priv->eeprom_version, + priv->firmware_version, priv->firmware_numver); + + for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { + struct client *bss = &priv->sta_list[i]; + if (!bss->used) continue; + p += sprintf(p, "BSS %u BSSID "MACSTR" ESSID %s channel %u " + "Cap 0x%X SIR %u SNR %u\n", + i, MAC(bss->bssid), (char*)bss->essid, bss->channel, + bss->cap_info, bss->sir, bss->snr); + } + p += sprintf(p, "status:\t\t\t%u (%s)\n", + priv->status, acx_get_status_name(priv->status)); + /* TODO: add more interesting stuff (essid, ...) here */ + + FN_EXIT1(p - buf); + return p - buf; +} + + +/*********************************************************************** +*/ +static int +acx_s_proc_diag_output(char *buf, wlandevice_t *priv) +{ + char *p = buf; + fw_stats_t *fw_stats; + unsigned long flags; + + FN_ENTER; + + fw_stats = kmalloc(sizeof(fw_stats_t), GFP_KERNEL); + if (!fw_stats) { + FN_EXIT1(0); + return 0; + } + memset(fw_stats, 0, sizeof(fw_stats_t)); + + acx_lock(priv, flags); + +#ifdef ACX_PCI +{ + int i; + const char *rtl, *thd, *ttl; + const struct rxhostdesc *pRxDesc; + txdesc_t *pTxDesc; + TIWLAN_DC *pDc = &priv->dc; + p += sprintf(p, "** Rx buf **\n"); + for (i = 0; i < pDc->rx_pool_count; i++) { + rtl = (i == pDc->rx_tail) ? " [tail]" : ""; + pRxDesc = &pDc->pRxHostDescQPool[i]; + if ((pRxDesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) + && (pRxDesc->Status & cpu_to_le32(BIT31)) ) + p += sprintf(p, "%02u FULL%s\n", i, rtl); + else + p += sprintf(p, "%02u empty%s\n", i, rtl); + } + p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", priv->TxQueueFree, + acx_queue_stopped(priv->netdev) ? "STOPPED" : "running"); + pTxDesc = pDc->pTxDescQPool; + for (i = 0; i < pDc->tx_pool_count; i++) { + thd = (i == pDc->tx_head) ? " [head]" : ""; + ttl = (i == pDc->tx_tail) ? " [tail]" : ""; + if (pTxDesc->Ctl_8 & DESC_CTL_ACXDONE) + p += sprintf(p, "%02u DONE (%02X)%s%s\n", i, pTxDesc->Ctl_8, thd, ttl); + else + if (!(pTxDesc->Ctl_8 & DESC_CTL_HOSTOWN)) + p += sprintf(p, "%02u TxWait (%02X)%s%s\n", i, pTxDesc->Ctl_8, thd, ttl); + else + p += sprintf(p, "%02u empty (%02X)%s%s\n", i, pTxDesc->Ctl_8, thd, ttl); + pTxDesc = GET_NEXT_TX_DESC_PTR(pDc, pTxDesc); + } +} +#endif + p += sprintf(p, + "\n" + "** network status **\n" + "dev_state_mask 0x%04X\n" + "status %u (%s), " + "mode %u, channel %u, " + "reg_dom_id 0x%02X, reg_dom_chanmask 0x%04X, ", + priv->dev_state_mask, + priv->status, acx_get_status_name(priv->status), + priv->mode, priv->channel, + priv->reg_dom_id, priv->reg_dom_chanmask + ); + p += sprintf(p, + "ESSID \"%s\", essid_active %d, essid_len %d, " + "essid_for_assoc \"%s\", nick \"%s\"\n" + "WEP ena %d, restricted %d, idx %d\n", + priv->essid, priv->essid_active, (int)priv->essid_len, + priv->essid_for_assoc, priv->nick, + priv->wep_enabled, priv->wep_restricted, + priv->wep_current_index); + p += sprintf(p, "dev_addr "MACSTR"\n", MAC(priv->dev_addr)); + p += sprintf(p, "bssid "MACSTR"\n", MAC(priv->bssid)); + p += sprintf(p, "ap_filter "MACSTR"\n", MAC(priv->ap)); + + p += sprintf(p, + "\n" + "** PHY status **\n" + "tx_disabled %d, tx_level_dbm %d, tx_level_val %d, tx_level_auto %d\n" + "sensitivity %d, antenna 0x%02X, ed_threshold %d, cca %d, preamble_mode %d\n" + "rts_threshold %d, short_retry %d, long_retry %d, msdu_lifetime %d, listen_interval %d, beacon_interval %d\n", + priv->tx_disabled, priv->tx_level_dbm, priv->tx_level_val, priv->tx_level_auto, + priv->sensitivity, priv->antenna, priv->ed_threshold, priv->cca, priv->preamble_mode, + priv->rts_threshold, priv->short_retry, priv->long_retry, priv->msdu_lifetime, priv->listen_interval, priv->beacon_interval); + +#ifdef ACX_PCI +{ + TIWLAN_DC *pDc = &priv->dc; + p += sprintf(p, + "\n" + "** TIWLAN_DC **\n" + "ui32ACXTxQueueStart %u, ui32ACXRxQueueStart %u\n" + "pTxBufferPool %p, TxBufferPoolSize %u, TxBufferPoolPhyAddr %08llx\n" + "TxDescrSize %u, pTxDescQPool %p, tx_pool_count %u\n" + "pTxHostDescQPool %p, TxHostDescQPoolSize %u, TxHostDescQPoolPhyAddr %08llx\n" + "pRxDescQPool %p, rx_pool_count %d\n" + "pRxHostDescQPool %p, RxHostDescQPoolSize %u, RxHostDescQPoolPhyAddr %08llx\n" + "pRxBufferPool %p, RxBufferPoolSize %u, RxBufferPoolPhyAddr %08llx\n", + pDc->ui32ACXTxQueueStart, pDc->ui32ACXRxQueueStart, + pDc->pTxBufferPool, pDc->TxBufferPoolSize, (u64)pDc->TxBufferPoolPhyAddr, + pDc->TxDescrSize, pDc->pTxDescQPool, pDc->tx_pool_count, + pDc->pTxHostDescQPool, pDc->TxHostDescQPoolSize, (u64)pDc->TxHostDescQPoolPhyAddr, + pDc->pRxDescQPool, pDc->rx_pool_count, + pDc->pRxHostDescQPool, pDc->RxHostDescQPoolSize, (u64)pDc->RxHostDescQPoolPhyAddr, + pDc->pRxBufferPool, pDc->RxBufferPoolSize, (u64)pDc->RxBufferPoolPhyAddr); +} +#endif + + acx_unlock(priv, flags); + + if (OK != acx_s_interrogate(priv, fw_stats, ACX1xx_IE_FIRMWARE_STATISTICS)) + p += sprintf(p, + "\n" + "** Firmware **\n" + "QUERY FAILED!!\n"); + else { + p += sprintf(p, + "\n" + "** Firmware **\n" + "version \"%s\"\n" + "tx_desc_overfl %u, rx_OutOfMem %u, rx_hdr_overfl %u, rx_hdr_use_next %u\n" + "rx_dropped_frame %u, rx_frame_ptr_err %u, rx_xfr_hint_trig %u, rx_dma_req %u\n" + "rx_dma_err %u, tx_dma_req %u, tx_dma_err %u, cmd_cplt %u, fiq %u\n" + "rx_hdrs %u, rx_cmplt %u, rx_mem_overfl %u, rx_rdys %u, irqs %u\n" + "acx_trans_procs %u, decrypt_done %u, dma_0_done %u, dma_1_done %u\n", + priv->firmware_version, + le32_to_cpu(fw_stats->tx_desc_of), + le32_to_cpu(fw_stats->rx_oom), + le32_to_cpu(fw_stats->rx_hdr_of), + le32_to_cpu(fw_stats->rx_hdr_use_next), + le32_to_cpu(fw_stats->rx_dropped_frame), + le32_to_cpu(fw_stats->rx_frame_ptr_err), + le32_to_cpu(fw_stats->rx_xfr_hint_trig), + le32_to_cpu(fw_stats->rx_dma_req), + le32_to_cpu(fw_stats->rx_dma_err), + le32_to_cpu(fw_stats->tx_dma_req), + le32_to_cpu(fw_stats->tx_dma_err), + le32_to_cpu(fw_stats->cmd_cplt), + le32_to_cpu(fw_stats->fiq), + le32_to_cpu(fw_stats->rx_hdrs), + le32_to_cpu(fw_stats->rx_cmplt), + le32_to_cpu(fw_stats->rx_mem_of), + le32_to_cpu(fw_stats->rx_rdys), + le32_to_cpu(fw_stats->irqs), + le32_to_cpu(fw_stats->acx_trans_procs), + le32_to_cpu(fw_stats->decrypt_done), + le32_to_cpu(fw_stats->dma_0_done), + le32_to_cpu(fw_stats->dma_1_done)); + p += sprintf(p, + "tx_exch_complet %u, commands %u, acx_rx_procs %u\n" + "hw_pm_mode_changes %u, host_acks %u, pci_pm %u, acm_wakeups %u\n" + "wep_key_count %u, wep_default_key_count %u, dot11_def_key_mib %u\n" + "wep_key_not_found %u, wep_decrypt_fail %u\n", + le32_to_cpu(fw_stats->tx_exch_complet), + le32_to_cpu(fw_stats->commands), + le32_to_cpu(fw_stats->acx_rx_procs), + le32_to_cpu(fw_stats->hw_pm_mode_changes), + le32_to_cpu(fw_stats->host_acks), + le32_to_cpu(fw_stats->pci_pm), + le32_to_cpu(fw_stats->acm_wakeups), + le32_to_cpu(fw_stats->wep_key_count), + le32_to_cpu(fw_stats->wep_default_key_count), + le32_to_cpu(fw_stats->dot11_def_key_mib), + le32_to_cpu(fw_stats->wep_key_not_found), + le32_to_cpu(fw_stats->wep_decrypt_fail)); + } + + kfree(fw_stats); + + FN_EXIT1(p - buf); + return p - buf; +} + + +/*********************************************************************** +*/ +static int +acx_proc_eeprom_output(char *buf, wlandevice_t *priv) +{ + char *p = buf; +#ifdef ACX_PCI + int i; + + FN_ENTER; + + for (i = 0; i < 0x400; i++) { + acx_read_eeprom_offset(priv, i, p++); + } + + FN_EXIT1(p - buf); +#endif + return p - buf; +} + + +/*********************************************************************** +*/ +static int +acx_s_proc_phy_output(char *buf, wlandevice_t *priv) +{ + char *p = buf; + int i; + + FN_ENTER; + + /* + if (RADIO_RFMD_11 != priv->radio_type) { + printk("sorry, not yet adapted for radio types " + "other than RFMD, please verify " + "PHY size etc. first!\n"); + goto end; + } + */ + + /* The PHY area is only 0x80 bytes long; further pages after that + * only have some page number registers with altered value, + * all other registers remain the same. */ + for (i = 0; i < 0x80; i++) { + acx_s_read_phy_reg(priv, i, p++); + } + + FN_EXIT1(p - buf); + return p - buf; +} + + +/*********************************************************************** +** acx_e_read_proc_XXXX +** Handle our /proc entry +** +** Arguments: +** standard kernel read_proc interface +** Returns: +** number of bytes written to buf +** Side effects: +** none +*/ +static int +acx_e_read_proc(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + wlandevice_t *priv = (wlandevice_t *)data; + unsigned long flags; + int length; + + FN_ENTER; + + acx_sem_lock(priv); + acx_lock(priv, flags); + /* fill buf */ + length = acx_l_proc_output(buf, priv); + acx_unlock(priv, flags); + acx_sem_unlock(priv); + + /* housekeeping */ + if (length <= offset + count) + *eof = 1; + *start = buf + offset; + length -= offset; + if (length > count) + length = count; + if (length < 0) + length = 0; + FN_EXIT1(length); + return length; +} + +static int +acx_e_read_proc_diag(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + wlandevice_t *priv = (wlandevice_t *)data; + int length; + + FN_ENTER; + + acx_sem_lock(priv); + /* fill buf */ + length = acx_s_proc_diag_output(buf, priv); + acx_sem_unlock(priv); + + /* housekeeping */ + if (length <= offset + count) + *eof = 1; + *start = buf + offset; + length -= offset; + if (length > count) + length = count; + if (length < 0) + length = 0; + FN_EXIT1(length); + return length; +} + +static int +acx_e_read_proc_eeprom(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + wlandevice_t *priv = (wlandevice_t *)data; + int length; + + FN_ENTER; + + acx_sem_lock(priv); + /* fill buf */ + length = acx_proc_eeprom_output(buf, priv); + acx_sem_unlock(priv); + + /* housekeeping */ + if (length <= offset + count) + *eof = 1; + *start = buf + offset; + length -= offset; + if (length > count) + length = count; + if (length < 0) + length = 0; + FN_EXIT1(length); + return length; +} + +static int +acx_e_read_proc_phy(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + wlandevice_t *priv = (wlandevice_t *)data; + int length; + + FN_ENTER; + + acx_sem_lock(priv); + /* fill buf */ + length = acx_s_proc_phy_output(buf, priv); + acx_sem_unlock(priv); + + /* housekeeping */ + if (length <= offset + count) + *eof = 1; + *start = buf + offset; + length -= offset; + if (length > count) + length = count; + if (length < 0) + length = 0; + FN_EXIT1(length); + return length; +} + + +/*********************************************************************** +** /proc files registration +*/ +static const char * const +proc_files[] = { "", "_diag", "_eeprom", "_phy" }; + +static read_proc_t * const +acx_proc_funcs[] = { + acx_e_read_proc, + acx_e_read_proc_diag, + acx_e_read_proc_eeprom, + acx_e_read_proc_phy +}; + +static int +manage_proc_entries(const struct net_device *dev, int remove) +{ + /* doh, netdev_priv() doesn't have const! */ + wlandevice_t *priv = acx_netdev_priv((struct net_device *)dev); + char procbuf[80]; + int i; + + for (i = 0; i < 4; i++) { + sprintf(procbuf, "driver/acx_%s", dev->name); + strcat(procbuf, proc_files[i]); + if (!remove) { + acxlog(L_INIT, "creating /proc entry %s\n", procbuf); + if (!create_proc_read_entry(procbuf, 0, 0, acx_proc_funcs[i], priv)) + return NOT_OK; + } else { + acxlog(L_INIT, "removing /proc entry %s\n", procbuf); + remove_proc_entry(procbuf, NULL); + } + } + return OK; +} + +int +acx_proc_register_entries(const struct net_device *dev) +{ + return manage_proc_entries(dev, 0); +} + +int +acx_proc_unregister_entries(const struct net_device *dev) +{ + return manage_proc_entries(dev, 1); +} +#endif /* CONFIG_PROC_FS */ + + +/*********************************************************************** +** acx_s_read_fw +** +** Loads a firmware image +** +** Returns: +** 0 unable to load file +** pointer to firmware success +*/ +#if USE_FW_LOADER_26 +firmware_image_t* +acx_s_read_fw(struct device *dev, const char *file, u32 *size) +#else +#undef acx_s_read_fw +firmware_image_t* +acx_s_read_fw(const char *file, u32 *size) +#endif +{ + firmware_image_t *res = NULL; + +#if USE_FW_LOADER_LEGACY + mm_segment_t orgfs; + unsigned long page; + char *buffer; + struct file *inf; + int retval; + u32 offset = 0; + char *filename; +#endif + +#if USE_FW_LOADER_26 + const struct firmware *fw_entry; + + acxlog(L_DEBUG, "requesting firmware image '%s'\n", file); + if (!request_firmware(&fw_entry, file, dev)) { + *size = 8 + le32_to_cpu(*(u32 *)(fw_entry->data + 4)); + if (fw_entry->size != *size) { + printk("acx: firmware size does not match " + "firmware header: %d != %d\n", + (int) fw_entry->size, (int) *size); + } + res = vmalloc(*size); + if (!res) { + printk("acx: no memory for firmware " + "(%u bytes)\n", *size); + return NULL; + } + memcpy(res, fw_entry->data, fw_entry->size); + release_firmware(fw_entry); + return res; + } + printk("acx: no firmware image was provided. " + "Check your hotplug scripts\n"); +#endif + +#if USE_FW_LOADER_LEGACY + printk("acx: firmware upload via firmware_dir module parameter " + "is deprecated. Switch to using hotplug\n"); + + orgfs = get_fs(); /* store original fs */ + set_fs(KERNEL_DS); + + /* Read in whole file then check the size */ + page = __get_free_page(GFP_KERNEL); + if (unlikely(0 == page)) { + printk("acx: no memory for firmware upload\n"); + goto fail; + } + + filename = kmalloc(PATH_MAX, GFP_KERNEL); + if (unlikely(!filename)) { + printk("acx: no memory for firmware upload\n"); + goto fail; + } + if (!firmware_dir) { + firmware_dir = "/usr/share/acx"; + acxlog(L_DEBUG, "no firmware directory specified " + "via module parameter firmware_dir, " + "using default %s\n", firmware_dir); + } + snprintf(filename, PATH_MAX, "%s/%s", firmware_dir, file); + acxlog(L_DEBUG, "reading firmware image '%s'\n", filename); + + buffer = (char*)page; + + /* Note that file must be given as absolute path: + * a relative path works on first loading, + * but any subsequent firmware loading during card + * eject/insert will fail, most likely since the first + * module loading happens in user space (and thus + * filp_open can figure out the absolute path from a + * relative path) whereas the card reinsert processing + * probably happens in kernel space where you don't have + * a current directory to be able to figure out an + * absolute path from a relative path... */ + inf = filp_open(filename, O_RDONLY, 0); + kfree(filename); + if (OK != IS_ERR(inf)) { + const char *err; + + switch (-PTR_ERR(inf)) { + case 2: err = "file not found"; + break; + default: + err = "unknown error"; + break; + } + printk("acx: error %ld trying to open file '%s': %s\n", + -PTR_ERR(inf), file, err); + goto fail; + } + + if (unlikely((NULL == inf->f_op) || (NULL == inf->f_op->read))) { + printk("acx: %s does not have a read method?!\n", file); + goto fail_close; + } + + offset = 0; + do { + retval = inf->f_op->read(inf, buffer, PAGE_SIZE, &inf->f_pos); + + if (unlikely(0 > retval)) { + printk("acx: error %d reading file '%s'\n", + -retval, file); + vfree(res); + res = NULL; + } else if (0 == retval) { + if (0 == offset) { + printk("acx: firmware image file " + "'%s' is empty?!\n", file); + } + } else if (0 < retval) { + /* allocate result buffer here if needed, + * since we don't want to waste resources/time + * (in case file opening/reading fails) + * by doing allocation in front of the loop instead. */ + if (NULL == res) { + *size = 8 + le32_to_cpu(*(u32 *)(4 + buffer)); + + res = vmalloc(*size); + if (NULL == res) { + printk("acx: unable to " + "allocate %u bytes for " + "firmware module upload\n", + *size); + goto fail_close; + } + acxlog(L_DEBUG, "allocated %u bytes " + "for firmware module loading\n", + *size); + } + if ((unlikely(offset + retval > *size))) { + printk("acx: ERROR: allocation " + "was less than firmware image size?!\n"); + goto fail_close; + } + memcpy((u8*)res + offset, buffer, retval); + offset += retval; + } + } while (0 < retval); + +fail_close: + retval = filp_close(inf, NULL); + + if (unlikely(retval)) { + printk("acx: error %d closing file '%s'\n", -retval, file); + } + + if (unlikely((NULL != res) && (offset != le32_to_cpu(res->size) + 8))) { + printk("acx: firmware is reporting a different size " + "(0x%08X; 0x%08X was read)\n", + le32_to_cpu(res->size) + 8, offset); + vfree(res); + res = NULL; + } + +fail: + if (page) + free_page(page); + set_fs(orgfs); +#endif + + /* checksum will be verified in write_fw, so don't bother here */ + return res; +} + + +/*********************************************************************** +** acx_s_set_wepkey +*/ +static void +acx100_s_set_wepkey(wlandevice_t *priv) +{ + ie_dot11WEPDefaultKey_t dk; + int i; + + for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) { + if (priv->wep_keys[i].size != 0) { + acxlog(L_INIT, "setting WEP key: %d with " + "total size: %d\n", i, (int) priv->wep_keys[i].size); + dk.action = 1; + dk.keySize = priv->wep_keys[i].size; + dk.defaultKeyNum = i; + memcpy(dk.key, priv->wep_keys[i].key, dk.keySize); + acx_s_configure(priv, &dk, ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE); + } + } +} + +static void +acx111_s_set_wepkey(wlandevice_t *priv) +{ + acx111WEPDefaultKey_t dk; + int i; + + for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) { + if (priv->wep_keys[i].size != 0) { + acxlog(L_INIT, "setting WEP key: %d with " + "total size: %d\n", i, (int) priv->wep_keys[i].size); + memset(&dk, 0, sizeof(dk)); + dk.action = cpu_to_le16(1); /* "add key"; yes, that's a 16bit value */ + dk.keySize = priv->wep_keys[i].size; + + /* are these two lines necessary? */ + dk.type = 0; /* default WEP key */ + dk.index = 0; /* ignored when setting default key */ + + dk.defaultKeyNum = i; + memcpy(dk.key, priv->wep_keys[i].key, dk.keySize); + acx_s_issue_cmd(priv, ACX1xx_CMD_WEP_MGMT, &dk, sizeof(dk)); + } + } +} + +static void +acx_s_set_wepkey(wlandevice_t *priv) +{ + if (priv->chip_type == CHIPTYPE_ACX111) + acx111_s_set_wepkey(priv); + else + acx100_s_set_wepkey(priv); +} + + +/*********************************************************************** +** acx100_s_init_wep +** +** FIXME: this should probably be moved into the new card settings +** management, but since we're also modifying the memory map layout here +** due to the WEP key space we want, we should take care... +*/ +static int +acx100_s_init_wep(wlandevice_t *priv) +{ +/* int i; + acx100_cmd_wep_mgmt_t wep_mgmt; size = 37 bytes */ + acx100_ie_wep_options_t options; + ie_dot11WEPDefaultKeyID_t dk; + acx_ie_memmap_t pt; + int res = NOT_OK; + + FN_ENTER; + + if (OK != acx_s_interrogate(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { + printk("%s: ctlMemoryMapRead failed\n", priv->netdev->name); + goto fail; + } + + acxlog(L_DEBUG, "CodeEnd:%X\n", pt.CodeEnd); + + pt.WEPCacheStart = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4); + pt.WEPCacheEnd = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4); + + if (OK != acx_s_configure(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { + printk("%s: ctlMemoryMapWrite FAILED\n", priv->netdev->name); + goto fail; + } + + /* let's choose maximum setting: 4 default keys, plus 10 other keys: */ + options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10); + options.WEPOption = 0x00; + + acxlog(L_ASSOC, "%s: writing WEP options\n", __func__); + acx_s_configure(priv, &options, ACX100_IE_WEP_OPTIONS); + + acx100_s_set_wepkey(priv); + + if (priv->wep_keys[priv->wep_current_index].size != 0) { + acxlog(L_ASSOC, "setting active default WEP key number: %d\n", + priv->wep_current_index); + dk.KeyID = priv->wep_current_index; + acx_s_configure(priv, &dk, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); /* 0x1010 */ + } + /* FIXME!!! wep_key_struct is filled nowhere! But priv + * is initialized to 0, and we don't REALLY need those keys either */ +/* for (i = 0; i < 10; i++) { + if (priv->wep_key_struct[i].len != 0) { + MAC_COPY(wep_mgmt.MacAddr, priv->wep_key_struct[i].addr); + wep_mgmt.KeySize = cpu_to_le16(priv->wep_key_struct[i].len); + memcpy(&wep_mgmt.Key, priv->wep_key_struct[i].key, le16_to_cpu(wep_mgmt.KeySize)); + wep_mgmt.Action = cpu_to_le16(1); + acxlog(L_ASSOC, "writing WEP key %d (len %d)\n", i, le16_to_cpu(wep_mgmt.KeySize)); + if (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_WEP_MGMT, &wep_mgmt, sizeof(wep_mgmt))) { + priv->wep_key_struct[i].index = i; + } + } + } */ + + /* now retrieve the updated WEPCacheEnd pointer... */ + if (OK != acx_s_interrogate(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { + printk("%s: ctlMemoryMapRead #2 FAILED\n", priv->netdev->name); + goto fail; + } + /* ...and tell it to start allocating templates at that location */ + /* (no endianness conversion needed) */ + pt.PacketTemplateStart = pt.WEPCacheEnd; + + if (OK != acx_s_configure(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { + printk("%s: ctlMemoryMapWrite #2 FAILED\n", priv->netdev->name); + goto fail; + } + res = OK; + +fail: + FN_EXIT1(res); + return res; +} + + +/*********************************************************************** +*/ +static int +acx_s_init_max_null_data_template(wlandevice_t *priv) +{ + struct acxp80211_nullframe b; + int result; + + FN_ENTER; + memset(&b, 0, sizeof(b)); + b.size = cpu_to_le16(sizeof(b) - 2); + result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_NULL_DATA, &b, sizeof(b)); + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx_s_init_max_beacon_template +*/ +static int +acx_s_init_max_beacon_template(wlandevice_t *priv) +{ + struct acx_template_beacon b; + int result; + + FN_ENTER; + memset(&b, 0, sizeof(b)); + b.size = cpu_to_le16(sizeof(b) - 2); + result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_BEACON, &b, sizeof(b)); + + FN_EXIT1(result); + return result; +} + +/*********************************************************************** +** acx_s_init_max_tim_template +*/ +static int +acx_s_init_max_tim_template(wlandevice_t *priv) +{ + acx_template_tim_t t; + + memset(&t, 0, sizeof(t)); + t.size = cpu_to_le16(sizeof(t) - 2); + return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t)); +} + + +/*********************************************************************** +** acx_s_init_max_probe_response_template +*/ +static int +acx_s_init_max_probe_response_template(wlandevice_t *priv) +{ + struct acx_template_proberesp pr; + + memset(&pr, 0, sizeof(pr)); + pr.size = cpu_to_le16(sizeof(pr) - 2); + + return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, sizeof(pr)); +} + + +/*********************************************************************** +** acx_s_init_max_probe_request_template +*/ +static int +acx_s_init_max_probe_request_template(wlandevice_t *priv) +{ + union { + acx100_template_probereq_t p100; + acx111_template_probereq_t p111; + } pr; + int res; + + FN_ENTER; + memset(&pr, 0, sizeof(pr)); + pr.p100.size = cpu_to_le16(sizeof(pr) - 2); + res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &pr, sizeof(pr)); + FN_EXIT1(res); + return res; +} + + +/*********************************************************************** +** acx_s_set_tim_template +** +** In full blown driver we will regularly update partial virtual bitmap +** by calling this function +** (it can be done by irq handler on each DTIM irq or by timer...) + +[802.11 7.3.2.6] TIM information element: +- 1 EID +- 1 Length +1 1 DTIM Count + indicates how many beacons (including this) appear before next DTIM + (0=this one is a DTIM) +2 1 DTIM Period + number of beacons between successive DTIMs + (0=reserved, 1=all TIMs are DTIMs, 2=every other, etc) +3 1 Bitmap Control + bit0: Traffic Indicator bit associated with Assoc ID 0 (Bcast AID?) + set to 1 in TIM elements with a value of 0 in the DTIM Count field + when one or more broadcast or multicast frames are buffered at the AP. + bit1-7: Bitmap Offset (logically Bitmap_Offset = Bitmap_Control & 0xFE). +4 n Partial Virtual Bitmap + Visible part of traffic-indication bitmap. + Full bitmap consists of 2008 bits (251 octets) such that bit number N + (0<=N<=2007) in the bitmap corresponds to bit number (N mod 8) + in octet number N/8 where the low-order bit of each octet is bit0, + and the high order bit is bit7. + Each set bit in virtual bitmap corresponds to traffic buffered by AP + for a specific station (with corresponding AID?). + Partial Virtual Bitmap shows a part of bitmap which has non-zero. + Bitmap Offset is a number of skipped zero octets (see above). + 'Missing' octets at the tail are also assumed to be zero. + Example: Length=6, Bitmap_Offset=2, Partial_Virtual_Bitmap=55 55 55 + This means that traffic-indication bitmap is: + 00000000 00000000 01010101 01010101 01010101 00000000 00000000... + (is bit0 in the map is always 0 and real value is in Bitmap Control bit0?) +*/ +static int +acx_s_set_tim_template(wlandevice_t *priv) +{ +/* For now, configure smallish test bitmap, all zero ("no pending data") */ + enum { bitmap_size = 5 }; + + acx_template_tim_t t; + int result; + + FN_ENTER; + + memset(&t, 0, sizeof(t)); + t.size = 5 + bitmap_size; /* eid+len+count+period+bmap_ctrl + bmap */ + t.tim_eid = WLAN_EID_TIM; + t.len = 3 + bitmap_size; /* count+period+bmap_ctrl + bmap */ + result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t)); + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx_fill_beacon_or_proberesp_template +** +** For frame format info, please see 802.11-1999.pdf item 7.2.3.9 and below!! +** +** STATUS: done +** WARNING/FIXME/TODO: this needs to be called (via SET_TEMPLATES) *whenever* +** *any* of the parameters contained in it change!!! +** fishy status fixed +** +** NB: we use the fact that +** struct acx_template_proberesp and struct acx_template_beacon are the same +** (well, almost...) +** +** [802.11] Beacon's body consist of these IEs: +** 1 Timestamp +** 2 Beacon interval +** 3 Capability information +** 4 SSID +** 5 Supported rates (up to 8 rates) +** 6 FH Parameter Set (frequency-hopping PHYs only) +** 7 DS Parameter Set (direct sequence PHYs only) +** 8 CF Parameter Set (only if PCF is supported) +** 9 IBSS Parameter Set (ad-hoc only) +** +** Beacon only: +** 10 TIM (AP only) (see 802.11 7.3.2.6) +** 11 Country Information (802.11d) +** 12 FH Parameters (802.11d) +** 13 FH Pattern Table (802.11d) +** ... (?!! did not yet find relevant PDF file... --vda) +** 19 ERP Information (extended rate PHYs) +** 20 Extended Supported Rates (if more than 8 rates) +** +** Proberesp only: +** 10 Country information (802.11d) +** 11 FH Parameters (802.11d) +** 12 FH Pattern Table (802.11d) +** 13-n Requested information elements (802.11d) +** ???? +** 18 ERP Information (extended rate PHYs) +** 19 Extended Supported Rates (if more than 8 rates) +*/ +static int +acx_fill_beacon_or_proberesp_template(wlandevice_t *priv, + struct acx_template_beacon *templ, + u16 fc /* in host order! */) +{ + int len; + u8 *p; + + FN_ENTER; + + memset(templ, 0, sizeof(*templ)); + MAC_BCAST(templ->da); + MAC_COPY(templ->sa, priv->dev_addr); + MAC_COPY(templ->bssid, priv->bssid); + + templ->beacon_interval = cpu_to_le16(priv->beacon_interval); + acx_update_capabilities(priv); + templ->cap = cpu_to_le16(priv->capabilities); + + p = templ->variable; + p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid); + p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported); + p = wlan_fill_ie_ds_parms(p, priv->channel); + /* NB: should go AFTER tim, but acx seem to keep tim last always */ + p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported); + + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + /* ATIM window */ + p = wlan_fill_ie_ibss_parms(p, 0); break; + case ACX_MODE_3_AP: + /* TIM IE is set up as separate template */ + break; + } + + len = p - (u8*)templ; + templ->fc = cpu_to_le16(WF_FTYPE_MGMT | fc); + /* - 2: do not count 'u16 size' field */ + templ->size = cpu_to_le16(len - 2); + + FN_EXIT1(len); + return len; +} + + +/*********************************************************************** +** acx_s_set_beacon_template +*/ +static int +acx_s_set_beacon_template(wlandevice_t *priv) +{ + struct acx_template_beacon bcn; + int len, result; + + FN_ENTER; + + len = acx_fill_beacon_or_proberesp_template(priv, &bcn, WF_FSTYPE_BEACON); + result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_BEACON, &bcn, len); + + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx_s_set_probe_response_template +*/ +static int +acx_s_set_probe_response_template(wlandevice_t *priv) +{ + struct acx_template_proberesp pr; + int len, result; + + FN_ENTER; + + len = acx_fill_beacon_or_proberesp_template(priv, &pr, WF_FSTYPE_PROBERESP); + result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, len); + + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx100_s_init_packet_templates() +** +** NOTE: order is very important here, to have a correct memory layout! +** init templates: max Probe Request (station mode), max NULL data, +** max Beacon, max TIM, max Probe Response. +** +** acxInitPacketTemplates() +** STATUS: almost ok, except for struct definitions. +*/ +static int +acx100_s_init_packet_templates(wlandevice_t *priv) +{ + acx_ie_memmap_t mm; + int result = NOT_OK; + + FN_ENTER; + + acxlog(L_DEBUG, "sizeof(memmap)=%d bytes\n", (int)sizeof(mm)); + + /* acx100 still do not emit probe requests, thus this call + ** is sourt of not needed. But we want it to work someday */ + if (OK != acx_s_init_max_probe_request_template(priv)) + goto failed; + +#if NOT_WORKING_YET + /* FIXME: creating the NULL data template breaks + * communication right now, needs further testing. + * Also, need to set the template once we're joining a network. */ + if (OK != acx_s_init_max_null_data_template(priv)) + goto failed; +#endif + + if (OK != acx_s_init_max_beacon_template(priv)) + goto failed; + + /* TODO: beautify code by moving init_tim down just before set_tim */ + if (OK != acx_s_init_max_tim_template(priv)) + goto failed; + + if (OK != acx_s_init_max_probe_response_template(priv)) + goto failed; + + if (OK != acx_s_set_tim_template(priv)) + goto failed; + + if (OK != acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP)) { + printk("%s: memmap req FAILED\n", priv->netdev->name); + goto failed; + } + + mm.QueueStart = cpu_to_le32(le32_to_cpu(mm.PacketTemplateEnd) + 4); + if (OK != acx_s_configure(priv, &mm, ACX1xx_IE_MEMORY_MAP)) { + printk("%s: memmap cfg FAILED\n", priv->netdev->name); + goto failed; + } + + result = OK; + goto success; + +failed: + acxlog(L_DEBUG | L_INIT, + /* "cb=0x%X\n" */ + "pACXMemoryMap:\n" + ".CodeStart=0x%X\n" + ".CodeEnd=0x%X\n" + ".WEPCacheStart=0x%X\n" + ".WEPCacheEnd=0x%X\n" + ".PacketTemplateStart=0x%X\n" + ".PacketTemplateEnd=0x%X\n", + /* len, */ + le32_to_cpu(mm.CodeStart), + le32_to_cpu(mm.CodeEnd), + le32_to_cpu(mm.WEPCacheStart), + le32_to_cpu(mm.WEPCacheEnd), + le32_to_cpu(mm.PacketTemplateStart), + le32_to_cpu(mm.PacketTemplateEnd)); + +success: + FN_EXIT1(result); + return result; +} + +static int +acx111_s_init_packet_templates(wlandevice_t *priv) +{ + int result = NOT_OK; + + FN_ENTER; + + acxlog(L_DEBUG | L_INIT, "initializing max packet templates\n"); + + if (OK != acx_s_init_max_probe_request_template(priv)) + goto failed; + + if (OK != acx_s_init_max_null_data_template(priv)) + goto failed; + + if (OK != acx_s_init_max_beacon_template(priv)) + goto failed; + + if (OK != acx_s_init_max_tim_template(priv)) + goto failed; + + if (OK != acx_s_init_max_probe_response_template(priv)) + goto failed; + + /* the other templates will be set later (acx_start) */ + /* + if (OK != acx_s_set_tim_template(priv)) + goto failed;*/ + + result = OK; + goto success; + +failed: + printk("%s: acx111_init_packet_templates() FAILED\n", priv->netdev->name); + +success: + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +*/ +static int +acx100_s_set_probe_request_template(wlandevice_t *priv) +{ + struct acx100_template_probereq probereq; + char *p; + int res; + int frame_len; + + FN_ENTER; + + memset(&probereq, 0, sizeof(probereq)); + + probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi; + MAC_BCAST(probereq.da); + MAC_COPY(probereq.sa, priv->dev_addr); + MAC_BCAST(probereq.bssid); + + probereq.beacon_interval = cpu_to_le16(priv->beacon_interval); + acx_update_capabilities(priv); + probereq.cap = cpu_to_le16(priv->capabilities); + + p = probereq.variable; + acxlog(L_ASSOC, "SSID='%s' len=%d\n", priv->essid, priv->essid_len); + p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid); + p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported); + /* FIXME: should these be here or AFTER ds_parms? */ + p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported); + /* HUH?? who said it must be here? I've found nothing in 802.11! --vda*/ + /* p = wlan_fill_ie_ds_parms(p, priv->channel); */ + frame_len = p - (char*)&probereq; + probereq.size = frame_len - 2; + + res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len); + FN_EXIT0; + return res; +} + +static int +acx111_s_set_probe_request_template(wlandevice_t *priv) +{ + struct acx111_template_probereq probereq; + char *p; + int res; + int frame_len; + + FN_ENTER; + + memset(&probereq, 0, sizeof(probereq)); + + probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi; + MAC_BCAST(probereq.da); + MAC_COPY(probereq.sa, priv->dev_addr); + MAC_BCAST(probereq.bssid); + + p = probereq.variable; + p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid); + p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported); + p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported); + frame_len = p - (char*)&probereq; + probereq.size = frame_len - 2; + + res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len); + FN_EXIT0; + return res; +} + +static int +acx_s_set_probe_request_template(wlandevice_t *priv) +{ + if (priv->chip_type == CHIPTYPE_ACX111) { + return acx111_s_set_probe_request_template(priv); + } else { + return acx100_s_set_probe_request_template(priv); + } +} + + +/*********************************************************************** +** FIXME: this should be solved in a general way for all radio types +** by decoding the radio firmware module, +** since it probably has some standard structure describing how to +** set the power level of the radio module which it controls. +** Or maybe not, since the radio module probably has a function interface +** instead which then manages Tx level programming :-\ +*/ +static int +acx100_s_set_tx_level(wlandevice_t *priv, u8 level_dbm) +{ + /* since it can be assumed that at least the Maxim radio has a + * maximum power output of 20dBm and since it also can be + * assumed that these values drive the DAC responsible for + * setting the linear Tx level, I'd guess that these values + * should be the corresponding linear values for a dBm value, + * in other words: calculate the values from that formula: + * Y [dBm] = 10 * log (X [mW]) + * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm) + * and you're done... + * Hopefully that's ok, but you never know if we're actually + * right... (especially since Windows XP doesn't seem to show + * actual Tx dBm values :-P) */ +#ifdef ACX_PCI + /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the + * values are EXACTLY mW!!! Not sure about RFMD and others, + * though... */ + static const u8 dbm2val_maxim[21] = { + 63, 63, 63, 62, + 61, 61, 60, 60, + 59, 58, 57, 55, + 53, 50, 47, 43, + 38, 31, 23, 13, + 0 + }; + static const u8 dbm2val_rfmd[21] = { + 0, 0, 0, 1, + 2, 2, 3, 3, + 4, 5, 6, 8, + 10, 13, 16, 20, + 25, 32, 41, 50, + 63 + }; + const u8 *table; + + switch (priv->radio_type) { + case RADIO_MAXIM_0D: + table = &dbm2val_maxim[0]; + break; + case RADIO_RFMD_11: + case RADIO_RALINK_15: + table = &dbm2val_rfmd[0]; + break; + default: + printk("%s: unknown/unsupported radio type, " + "cannot modify tx power level yet!\n", + priv->netdev->name); + return NOT_OK; + } + printk("%s: changing radio power level to %u dBm (%u)\n", + priv->netdev->name, level_dbm, table[level_dbm]); + acx_s_write_phy_reg(priv, 0x11, table[level_dbm]); +#endif + return OK; +} + +static int +acx111_s_set_tx_level(wlandevice_t *priv, u8 level_dbm) +{ + struct ACX111TxLevel tx_level; + + /* my acx111 card has two power levels in its configoptions (== EEPROM): + * 1 (30mW) [15dBm] + * 2 (10mW) [10dBm] + * For now, just assume all other acx111 cards have the same. + * Ideally we would query it here, but we first need a + * standard way to query individual configoptions easily. */ + if (level_dbm <= 12) { + tx_level.level = 2; /* 10 dBm */ + priv->tx_level_dbm = 10; + } else { + tx_level.level = 1; /* 15 dBm */ + priv->tx_level_dbm = 15; + } + if (level_dbm != priv->tx_level_dbm) + acxlog(L_INIT, "ACX111 firmware has specific " + "power levels only: adjusted %d dBm to %d dBm!\n", + level_dbm, priv->tx_level_dbm); + + if (OK != acx_s_configure(priv, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL)) { + printk("%s: error setting acx111 tx level\n", priv->netdev->name); + return NOT_OK; + } + return OK; +} + +static int +acx_s_set_tx_level(wlandevice_t *priv, u8 level_dbm) +{ + if (priv->chip_type == CHIPTYPE_ACX111) { + return acx111_s_set_tx_level(priv, level_dbm); + } + return acx100_s_set_tx_level(priv, level_dbm); +} + + +/*********************************************************************** +*/ +#ifdef UNUSED +/* Returns the current tx level (ACX111) */ +static u8 +acx111_s_get_tx_level(wlandevice_t *priv) +{ + struct ACX111TxLevel tx_level; + + tx_level.level = 0; + if (OK != acx_s_interrogate(priv, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL)) { + printk("%s: error getting acx111 tx level\n", priv->netdev->name); + } + return tx_level.level; +} +#endif + + +/*********************************************************************** +*/ +int +acx111_s_get_feature_config(wlandevice_t *priv, + u32 *feature_options, u32 *data_flow_options) +{ + struct ACX111FeatureConfig fc; + + if (priv->chip_type != CHIPTYPE_ACX111) { + return NOT_OK; + } + + memset(&fc, 0, sizeof(struct ACX111FeatureConfig)); + + if (OK != acx_s_interrogate(priv, &fc, ACX1xx_IE_FEATURE_CONFIG)) { + printk("%s: error reading feature config\n", + priv->netdev->name); + return NOT_OK; + } + acxlog(L_DEBUG, + "got Feature option:0x%X, DataFlow option: 0x%X\n", + fc.feature_options, + fc.data_flow_options); + + if (feature_options) + *feature_options = le32_to_cpu(fc.feature_options); + if (data_flow_options) + *data_flow_options = le32_to_cpu(fc.data_flow_options); + + return OK; +} + +int +acx111_s_set_feature_config(wlandevice_t *priv, + u32 feature_options, u32 data_flow_options, + unsigned int mode /* 0 == remove, 1 == add, 2 == set */) +{ + struct ACX111FeatureConfig fc; + + if (priv->chip_type != CHIPTYPE_ACX111) { + return NOT_OK; + } + + if ((mode < 0) || (mode > 2)) + return NOT_OK; + + if (mode != 2) + /* need to modify old data */ + acx111_s_get_feature_config(priv, &fc.feature_options, &fc.data_flow_options); + else { + /* need to set a completely new value */ + fc.feature_options = 0; + fc.data_flow_options = 0; + } + + if (mode == 0) { /* remove */ + CLEAR_BIT(fc.feature_options, cpu_to_le32(feature_options)); + CLEAR_BIT(fc.data_flow_options, cpu_to_le32(data_flow_options)); + } else { /* add or set */ + SET_BIT(fc.feature_options, cpu_to_le32(feature_options)); + SET_BIT(fc.data_flow_options, cpu_to_le32(data_flow_options)); + } + + acxlog(L_DEBUG, + "old: feature 0x%08X dataflow 0x%08X. mode: %u\n" + "new: feature 0x%08X dataflow 0x%08X\n", + feature_options, data_flow_options, mode, + le32_to_cpu(fc.feature_options), + le32_to_cpu(fc.data_flow_options)); + + if (OK != acx_s_configure(priv, &fc, ACX1xx_IE_FEATURE_CONFIG)) { + printk("%s: error setting feature config\n", priv->netdev->name); + return NOT_OK; + } + + return OK; +} + + +/*********************************************************************** +*/ +int +acx_s_recalib_radio(wlandevice_t *priv) +{ + if (CHIPTYPE_ACX111 == priv->chip_type) { + acx111_cmd_radiocalib_t cal; + + printk("%s: recalibrating radio\n", priv->netdev->name); + /* automatic recalibration, choose all methods: */ + cal.methods = cpu_to_le32(0x8000000f); + /* automatic recalibration every 60 seconds (value in TUs) + * FIXME: what is the firmware default here?? */ + cal.interval = cpu_to_le32(58594); + return acx_s_issue_cmd_timeo(priv, ACX111_CMD_RADIOCALIB, + &cal, sizeof(cal), CMD_TIMEOUT_MS(100)); + } else { + if (/* (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0)) && + (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0)) && */ + (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_TX, &(priv->channel), 1)) && + (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_RX, &(priv->channel), 1)) ) + return OK; + return NOT_OK; + } +} + + +/*********************************************************************** +** acx_s_cmd_start_scan +** +** Issue scan command to the hardware +*/ +static void +acx100_s_scan_chan(wlandevice_t *priv) +{ + acx100_scan_t s; + + FN_ENTER; + + memset(&s, 0, sizeof(s)); + s.count = cpu_to_le16(priv->scan_count); + s.start_chan = cpu_to_le16(1); + s.flags = cpu_to_le16(0x8000); + s.max_rate = priv->scan_rate; + s.options = priv->scan_mode; + s.chan_duration = cpu_to_le16(priv->scan_duration); + s.max_probe_delay = cpu_to_le16(priv->scan_probe_delay); + + acx_s_issue_cmd(priv, ACX1xx_CMD_SCAN, &s, sizeof(s)); + FN_EXIT0; +} + +static void +acx111_s_scan_chan(wlandevice_t *priv) +{ + acx111_scan_t s; + + FN_ENTER; + + memset(&s, 0, sizeof(s)); + s.count = cpu_to_le16(priv->scan_count); + s.channel_list_select = 0; /* scan every allowed channel */ + /*s.channel_list_select = 1;*/ /* scan given channels */ + s.rate = priv->scan_rate; + s.options = priv->scan_mode; + s.chan_duration = cpu_to_le16(priv->scan_duration); + s.max_probe_delay = cpu_to_le16(priv->scan_probe_delay); + /*s.modulation = 0x40;*/ /* long preamble? OFDM? -> only for active scan */ + s.modulation = 0; + /*s.channel_list[0] = 6; + s.channel_list[1] = 4;*/ + + acx_s_issue_cmd(priv, ACX1xx_CMD_SCAN, &s, sizeof(s)); + FN_EXIT0; +} + +void +acx_s_cmd_start_scan(wlandevice_t *priv) +{ + /* time_before check is 'just in case' thing */ + if (!(priv->irq_status & HOST_INT_SCAN_COMPLETE) + && time_before(jiffies, priv->scan_start + 10*HZ) + ) { + acxlog(L_INIT, "start_scan: seems like previous scan " + "is still running. Not starting anew. Please report\n"); + return; + } + + acxlog(L_INIT, "starting radio scan\n"); + /* remember that fw is commanded to do scan */ + priv->scan_start = jiffies; + CLEAR_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE); + /* issue it */ + if (priv->chip_type == CHIPTYPE_ACX100) { + acx100_s_scan_chan(priv); + } else { + acx111_s_scan_chan(priv); + } +} + + +/*********************************************************************** +** acx_s_update_card_settings +** +** Applies accumulated changes in various priv->xxxx members +** Called by ioctl commit handler, acx_start, acx_set_defaults, +** acx_s_after_interrupt_task (if IRQ_CMD_UPDATE_CARD_CFG), +*/ +static void +acx111_s_sens_radio_16_17(wlandevice_t *priv) +{ + u32 feature1, feature2; + + if ((priv->sensitivity < 1) || (priv->sensitivity > 3)) { + printk("%s: invalid sensitivity setting (1..3), " + "setting to 1\n", priv->netdev->name); + priv->sensitivity = 1; + } + acx111_s_get_feature_config(priv, &feature1, &feature2); + CLEAR_BIT(feature1, FEATURE1_LOW_RX|FEATURE1_EXTRA_LOW_RX); + if (priv->sensitivity > 1) + SET_BIT(feature1, FEATURE1_LOW_RX); + if (priv->sensitivity > 2) + SET_BIT(feature1, FEATURE1_EXTRA_LOW_RX); + acx111_s_feature_set(priv, feature1, feature2); +} + +void +acx_s_update_card_settings(wlandevice_t *priv, int get_all, int set_all) +{ + unsigned long flags; + unsigned int start_scan = 0; + int i; + + FN_ENTER; + + if (get_all) + SET_BIT(priv->get_mask, GETSET_ALL); + if (set_all) + SET_BIT(priv->set_mask, GETSET_ALL); + /* Why not just set masks to 0xffffffff? We can get rid of GETSET_ALL */ + + acxlog(L_INIT, "get_mask 0x%08X, set_mask 0x%08X\n", + priv->get_mask, priv->set_mask); + + /* Track dependencies betweed various settings */ + + if (priv->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_WEP)) { + acxlog(L_INIT, "important setting has been changed. " + "Need to update packet templates, too\n"); + SET_BIT(priv->set_mask, SET_TEMPLATES); + } + if (priv->set_mask & (GETSET_CHANNEL|GETSET_ALL)) { + /* This will actually tune RX/TX to the channel */ + SET_BIT(priv->set_mask, GETSET_RX|GETSET_TX); + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_3_AP: + /* Beacons contain channel# - update them */ + SET_BIT(priv->set_mask, SET_TEMPLATES); + } + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + start_scan = 1; + } + } + + /* Apply settings */ + +#ifdef WHY_SHOULD_WE_BOTHER /* imagine we were just powered off */ + /* send a disassoc request in case it's required */ + if (priv->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_CHANNEL|GETSET_WEP|GETSET_ALL)) { + if (ACX_MODE_2_STA == priv->mode) { + if (ACX_STATUS_4_ASSOCIATED == priv->status) { + acxlog(L_ASSOC, "we were ASSOCIATED - " + "sending disassoc request\n"); + acx_lock(priv, flags); + acx_l_transmit_disassoc(priv, NULL); + /* FIXME: deauth? */ + acx_unlock(priv, flags); + } + /* need to reset some other stuff as well */ + acxlog(L_DEBUG, "resetting bssid\n"); + MAC_ZERO(priv->bssid); + SET_BIT(priv->set_mask, SET_TEMPLATES|SET_STA_LIST); + /* FIXME: should start scanning */ + start_scan = 1; + } + } +#endif + + if (priv->get_mask & (GETSET_STATION_ID|GETSET_ALL)) { + u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN]; + const u8 *paddr; + + acx_s_interrogate(priv, &stationID, ACX1xx_IE_DOT11_STATION_ID); + paddr = &stationID[4]; + for (i = 0; i < ETH_ALEN; i++) { + /* we copy the MAC address (reversed in + * the card) to the netdevice's MAC + * address, and on ifup it will be + * copied into iwpriv->dev_addr */ + priv->netdev->dev_addr[ETH_ALEN - 1 - i] = paddr[i]; + } + CLEAR_BIT(priv->get_mask, GETSET_STATION_ID); + } + + if (priv->get_mask & (GETSET_SENSITIVITY|GETSET_ALL)) { + if ((RADIO_RFMD_11 == priv->radio_type) + || (RADIO_MAXIM_0D == priv->radio_type) + || (RADIO_RALINK_15 == priv->radio_type)) { + acx_s_read_phy_reg(priv, 0x30, &priv->sensitivity); + } else { + acxlog(L_INIT, "don't know how to get sensitivity " + "for radio type 0x%02X\n", priv->radio_type); + priv->sensitivity = 0; + } + acxlog(L_INIT, "got sensitivity value %u\n", priv->sensitivity); + + CLEAR_BIT(priv->get_mask, GETSET_SENSITIVITY); + } + + if (priv->get_mask & (GETSET_ANTENNA|GETSET_ALL)) { + u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN]; + + memset(antenna, 0, sizeof(antenna)); + acx_s_interrogate(priv, antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA); + priv->antenna = antenna[4]; + acxlog(L_INIT, "got antenna value 0x%02X\n", priv->antenna); + CLEAR_BIT(priv->get_mask, GETSET_ANTENNA); + } + + if (priv->get_mask & (GETSET_ED_THRESH|GETSET_ALL)) { + if (priv->chip_type == CHIPTYPE_ACX100) { + u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN]; + + memset(ed_threshold, 0, sizeof(ed_threshold)); + acx_s_interrogate(priv, ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD); + priv->ed_threshold = ed_threshold[4]; + } else { + acxlog(L_INIT, "acx111 doesn't support ED\n"); + priv->ed_threshold = 0; + } + acxlog(L_INIT, "got Energy Detect (ED) threshold %u\n", priv->ed_threshold); + CLEAR_BIT(priv->get_mask, GETSET_ED_THRESH); + } + + if (priv->get_mask & (GETSET_CCA|GETSET_ALL)) { + if (priv->chip_type == CHIPTYPE_ACX100) { + u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN]; + + memset(cca, 0, sizeof(priv->cca)); + acx_s_interrogate(priv, cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE); + priv->cca = cca[4]; + } else { + acxlog(L_INIT, "acx111 doesn't support CCA\n"); + priv->cca = 0; + } + acxlog(L_INIT, "got Channel Clear Assessment (CCA) value %u\n", priv->cca); + CLEAR_BIT(priv->get_mask, GETSET_CCA); + } + + if (priv->get_mask & (GETSET_REG_DOMAIN|GETSET_ALL)) { + acx_ie_generic_t dom; + + acx_s_interrogate(priv, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN); + priv->reg_dom_id = dom.m.bytes[0]; + /* FIXME: should also set chanmask somehow */ + acxlog(L_INIT, "got regulatory domain 0x%02X\n", priv->reg_dom_id); + CLEAR_BIT(priv->get_mask, GETSET_REG_DOMAIN); + } + + if (priv->set_mask & (GETSET_STATION_ID|GETSET_ALL)) { + u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN]; + u8 *paddr; + + paddr = &stationID[4]; + for (i = 0; i < ETH_ALEN; i++) { + /* copy the MAC address we obtained when we noticed + * that the ethernet iface's MAC changed + * to the card (reversed in + * the card!) */ + paddr[i] = priv->dev_addr[ETH_ALEN - 1 - i]; + } + acx_s_configure(priv, &stationID, ACX1xx_IE_DOT11_STATION_ID); + CLEAR_BIT(priv->set_mask, GETSET_STATION_ID); + } + + if (priv->set_mask & (SET_TEMPLATES|GETSET_ALL)) { + acxlog(L_INIT, "updating packet templates\n"); + /* Doesn't work for acx100, do it only for acx111 for now */ + if (priv->chip_type == CHIPTYPE_ACX111) { + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + if (OK != acx_s_set_probe_request_template(priv)) + acxlog(L_INIT, "acx_set_probe_request_template FAILED\n"); + } + } + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_3_AP: + /* FIXME: why only for AP? STA need probe req templates... */ + if (OK != acx_s_set_beacon_template(priv)) + acxlog(L_INIT, "acx_set_beacon_template FAILED\n"); + if (OK != acx_s_set_tim_template(priv)) + acxlog(L_INIT, "acx_set_tim_template FAILED\n"); + /* BTW acx111 firmware would not send probe responses + ** if probe request does not have all basic rates flagged + ** by 0x80! Thus firmware does not conform to 802.11, + ** it should ignore 0x80 bit in ratevector from STA. + ** We can 'fix' it by not using this template and + ** sending probe responses by hand. TODO --vda */ + if (OK != acx_s_set_probe_response_template(priv)) + acxlog(L_INIT, "acx_set_probe_response_template FAILED\n"); + } + /* Needed if generated frames are to be emitted at different tx rate now */ + acxlog(L_IRQ, "redoing cmd_join_bssid() after template cfg\n"); + acx_s_cmd_join_bssid(priv, priv->bssid); + CLEAR_BIT(priv->set_mask, SET_TEMPLATES); + } + if (priv->set_mask & (SET_STA_LIST|GETSET_ALL)) { + /* TODO insert a sweet if here */ + acx_lock(priv, flags); + acx_l_sta_list_init(priv); + CLEAR_BIT(priv->set_mask, SET_STA_LIST); + acx_unlock(priv, flags); + } + if (priv->set_mask & (SET_RATE_FALLBACK|GETSET_ALL)) { + u8 rate[4 + ACX1xx_IE_RATE_FALLBACK_LEN]; + + /* configure to not do fallbacks when not in auto rate mode */ + rate[4] = (priv->rate_auto) ? /* priv->txrate_fallback_retries */ 1 : 0; + acxlog(L_INIT, "updating Tx fallback to %u retries\n", rate[4]); + acx_s_configure(priv, &rate, ACX1xx_IE_RATE_FALLBACK); + CLEAR_BIT(priv->set_mask, SET_RATE_FALLBACK); + } + if (priv->set_mask & (GETSET_TXPOWER|GETSET_ALL)) { + acxlog(L_INIT, "updating transmit power: %u dBm\n", + priv->tx_level_dbm); + acx_s_set_tx_level(priv, priv->tx_level_dbm); + CLEAR_BIT(priv->set_mask, GETSET_TXPOWER); + } + + if (priv->set_mask & (GETSET_SENSITIVITY|GETSET_ALL)) { + acxlog(L_INIT, "updating sensitivity value: %u\n", + priv->sensitivity); + switch (priv->radio_type) { + case RADIO_RFMD_11: + case RADIO_MAXIM_0D: + case RADIO_RALINK_15: + acx_s_write_phy_reg(priv, 0x30, priv->sensitivity); + break; + case RADIO_RADIA_16: + case RADIO_UNKNOWN_17: + acx111_s_sens_radio_16_17(priv); + break; + default: + acxlog(L_INIT, "don't know how to modify sensitivity " + "for radio type 0x%02X\n", priv->radio_type); + } + CLEAR_BIT(priv->set_mask, GETSET_SENSITIVITY); + } + + if (priv->set_mask & (GETSET_ANTENNA|GETSET_ALL)) { + /* antenna */ + u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN]; + + memset(antenna, 0, sizeof(antenna)); + antenna[4] = priv->antenna; + acxlog(L_INIT, "updating antenna value: 0x%02X\n", + priv->antenna); + acx_s_configure(priv, &antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA); + CLEAR_BIT(priv->set_mask, GETSET_ANTENNA); + } + + if (priv->set_mask & (GETSET_ED_THRESH|GETSET_ALL)) { + /* ed_threshold */ + acxlog(L_INIT, "updating Energy Detect (ED) threshold: %u\n", + priv->ed_threshold); + if (CHIPTYPE_ACX100 == priv->chip_type) { + u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN]; + + memset(ed_threshold, 0, sizeof(ed_threshold)); + ed_threshold[4] = priv->ed_threshold; + acx_s_configure(priv, &ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD); + } + else + acxlog(L_INIT, "ACX111 doesn't support ED!\n"); + CLEAR_BIT(priv->set_mask, GETSET_ED_THRESH); + } + + if (priv->set_mask & (GETSET_CCA|GETSET_ALL)) { + /* CCA value */ + acxlog(L_INIT, "updating Channel Clear Assessment " + "(CCA) value: 0x%02X\n", priv->cca); + if (CHIPTYPE_ACX100 == priv->chip_type) { + u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN]; + + memset(cca, 0, sizeof(cca)); + cca[4] = priv->cca; + acx_s_configure(priv, &cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE); + } + else + acxlog(L_INIT, "acx111 doesn't support CCA!\n"); + CLEAR_BIT(priv->set_mask, GETSET_CCA); + } + + if (priv->set_mask & (GETSET_LED_POWER|GETSET_ALL)) { + /* Enable Tx */ + acxlog(L_INIT, "updating power LED status: %u\n", priv->led_power); + + acx_lock(priv, flags); + acx_l_power_led(priv, priv->led_power); + CLEAR_BIT(priv->set_mask, GETSET_LED_POWER); + acx_unlock(priv, flags); + } + +/* this seems to cause Tx lockup after some random time (Tx error 0x20), + * so let's disable it for now until further investigation */ +/* Maybe fixed now after locking is fixed. Need to retest */ +#if POWER_SAVE_80211 + if (priv->set_mask & (GETSET_POWER_80211|GETSET_ALL)) { + acx100_ie_powermgmt_t pm; + + /* change 802.11 power save mode settings */ + acxlog(L_INIT, "updating 802.11 power save mode settings: " + "wakeup_cfg 0x%02X, listen interval %u, " + "options 0x%02X, hangover period %u, " + "enhanced_ps_transition_time %d\n", + priv->ps_wakeup_cfg, priv->ps_listen_interval, + priv->ps_options, priv->ps_hangover_period, + priv->ps_enhanced_transition_time); + acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT); + acxlog(L_INIT, "Previous PS mode settings: wakeup_cfg 0x%02X, " + "listen interval %u, options 0x%02X, " + "hangover period %u, " + "enhanced_ps_transition_time %d\n", + pm.wakeup_cfg, pm.listen_interval, pm.options, + pm.hangover_period, pm.enhanced_ps_transition_time); + pm.wakeup_cfg = priv->ps_wakeup_cfg; + pm.listen_interval = priv->ps_listen_interval; + pm.options = priv->ps_options; + pm.hangover_period = priv->ps_hangover_period; + pm.enhanced_ps_transition_time = cpu_to_le16(priv->ps_enhanced_transition_time); + acx_s_configure(priv, &pm, ACX100_IE_POWER_MGMT); + acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT); + acxlog(L_INIT, "wakeup_cfg: 0x%02X\n", pm.wakeup_cfg); + acx_s_msleep(40); + acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT); + acxlog(L_INIT, "power save mode change %s\n", + (pm.wakeup_cfg & PS_CFG_PENDING) ? "FAILED" : "was successful"); + /* FIXME: maybe verify via PS_CFG_PENDING bit here + * that power save mode change was successful. */ + /* FIXME: we shouldn't trigger a scan immediately after + * fiddling with power save mode (since the firmware is sending + * a NULL frame then). Does this need locking?? */ + CLEAR_BIT(priv->set_mask, GETSET_POWER_80211); + } +#endif + + if (priv->set_mask & (GETSET_CHANNEL|GETSET_ALL)) { + /* channel */ + acxlog(L_INIT, "updating channel to: %u\n", priv->channel); + CLEAR_BIT(priv->set_mask, GETSET_CHANNEL); + } + + if (priv->set_mask & (GETSET_TX|GETSET_ALL)) { + /* set Tx */ + acxlog(L_INIT, "updating: %s Tx\n", + priv->tx_disabled ? "disable" : "enable"); + if (priv->tx_disabled) + acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0); + /* ^ */ + /* FIXME: this used to be 1, but since we don't transfer a parameter... */ + else + acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_TX, &(priv->channel), 1); + CLEAR_BIT(priv->set_mask, GETSET_TX); + } + + if (priv->set_mask & (GETSET_RX|GETSET_ALL)) { + /* Enable Rx */ + acxlog(L_INIT, "updating: enable Rx on channel: %u\n", + priv->channel); + acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_RX, &(priv->channel), 1); + CLEAR_BIT(priv->set_mask, GETSET_RX); + } + + if (priv->set_mask & (GETSET_RETRY|GETSET_ALL)) { + u8 short_retry[4 + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN]; + u8 long_retry[4 + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN]; + + acxlog(L_INIT, "updating short retry limit: %u, long retry limit: %u\n", + priv->short_retry, priv->long_retry); + short_retry[0x4] = priv->short_retry; + long_retry[0x4] = priv->long_retry; + acx_s_configure(priv, &short_retry, ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT); + acx_s_configure(priv, &long_retry, ACX1xx_IE_DOT11_LONG_RETRY_LIMIT); + CLEAR_BIT(priv->set_mask, GETSET_RETRY); + } + + if (priv->set_mask & (SET_MSDU_LIFETIME|GETSET_ALL)) { + u8 xmt_msdu_lifetime[4 + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN]; + + acxlog(L_INIT, "updating tx MSDU lifetime: %u\n", + priv->msdu_lifetime); + *(u32 *)&xmt_msdu_lifetime[4] = cpu_to_le32((u32)priv->msdu_lifetime); + acx_s_configure(priv, &xmt_msdu_lifetime, ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME); + CLEAR_BIT(priv->set_mask, SET_MSDU_LIFETIME); + } + + if (priv->set_mask & (GETSET_REG_DOMAIN|GETSET_ALL)) { + /* reg_domain */ + acx_ie_generic_t dom; + unsigned mask; + + acxlog(L_INIT, "updating regulatory domain: 0x%02X\n", + priv->reg_dom_id); + for (i = 0; i < sizeof(reg_domain_ids); i++) + if (reg_domain_ids[i] == priv->reg_dom_id) + break; + + if (sizeof(reg_domain_ids) == i) { + acxlog(L_INIT, "Invalid or unsupported regulatory " + "domain 0x%02X specified, falling back to " + "FCC (USA)! Please report if this sounds " + "fishy!\n", priv->reg_dom_id); + i = 0; + priv->reg_dom_id = reg_domain_ids[i]; + } + + priv->reg_dom_chanmask = reg_domain_channel_masks[i]; + dom.m.bytes[0] = priv->reg_dom_id; + acx_s_configure(priv, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN); + + mask = (1 << (priv->channel - 1)); + if (!(priv->reg_dom_chanmask & mask)) { + /* hmm, need to adjust our channel to reside within domain */ + mask = 1; + for (i = 1; i <= 14; i++) { + if (priv->reg_dom_chanmask & mask) { + printk("%s: adjusting " + "selected channel from %d " + "to %d due to new regulatory " + "domain\n", priv->netdev->name, + priv->channel, i); + priv->channel = i; + break; + } + mask <<= 1; + } + } + CLEAR_BIT(priv->set_mask, GETSET_REG_DOMAIN); + } + + if (priv->set_mask & (GETSET_MODE|GETSET_ALL)) { + priv->netdev->type = ARPHRD_ETHER; + + switch (priv->mode) { + case ACX_MODE_3_AP: + + acx_lock(priv, flags); + acx_l_sta_list_init(priv); + priv->aid = 0; + priv->ap_client = NULL; + MAC_COPY(priv->bssid, priv->dev_addr); + /* this basically says "we're connected" */ + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + acx_unlock(priv, flags); + + acx111_s_feature_off(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); + /* start sending beacons */ + acx_s_cmd_join_bssid(priv, priv->bssid); + break; + case ACX_MODE_MONITOR: + /* TODO: what exactly do we want here? */ + /* priv->netdev->type = ARPHRD_ETHER; */ + /* priv->netdev->type = ARPHRD_IEEE80211; */ + priv->netdev->type = ARPHRD_IEEE80211_PRISM; + acx111_s_feature_on(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); + /* this stops beacons */ + acx_s_cmd_join_bssid(priv, priv->bssid); + /* this basically says "we're connected" */ + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + SET_BIT(priv->set_mask, SET_RXCONFIG|SET_WEP_OPTIONS); + break; + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + acx111_s_feature_off(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); + priv->aid = 0; + priv->ap_client = NULL; + /* we want to start looking for peer or AP */ + start_scan = 1; + break; + case ACX_MODE_OFF: + /* TODO: disable RX/TX, stop any scanning activity etc: */ + /* priv->tx_disabled = 1; */ + /* SET_BIT(priv->set_mask, GETSET_RX|GETSET_TX); */ + + /* This stops beacons (invalid macmode...) */ + acx_s_cmd_join_bssid(priv, priv->bssid); + acx_set_status(priv, ACX_STATUS_0_STOPPED); + break; + } + CLEAR_BIT(priv->set_mask, GETSET_MODE); + } + + if (priv->set_mask & (SET_RXCONFIG|GETSET_ALL)) { + acx_s_initialize_rx_config(priv); + CLEAR_BIT(priv->set_mask, SET_RXCONFIG); + } + + if (priv->set_mask & (GETSET_RESCAN|GETSET_ALL)) { + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + start_scan = 1; + break; + } + CLEAR_BIT(priv->set_mask, GETSET_RESCAN); + } + + if (priv->set_mask & (GETSET_WEP|GETSET_ALL)) { + /* encode */ + + ie_dot11WEPDefaultKeyID_t dkey; +#if DEBUG_WEP + struct { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u8 val ACX_PACKED; + } keyindic; +#endif + acxlog(L_INIT, "updating WEP key settings\n"); + + acx_s_set_wepkey(priv); + + dkey.KeyID = priv->wep_current_index; + acxlog(L_INIT, "setting WEP key %u as default\n", dkey.KeyID); + acx_s_configure(priv, &dkey, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); +#if DEBUG_WEP + keyindic.val = 3; + acx_s_configure(priv, &keyindic, ACX111_IE_KEY_CHOOSE); +#endif + start_scan = 1; + CLEAR_BIT(priv->set_mask, GETSET_WEP); + } + + if (priv->set_mask & (SET_WEP_OPTIONS|GETSET_ALL)) { + acx100_ie_wep_options_t options; + + if (priv->chip_type == CHIPTYPE_ACX111) { + acxlog(L_DEBUG, "setting WEP Options for ACX111 is not supported\n"); + } else { + acxlog(L_INIT, "setting WEP Options\n"); + + /* let's choose maximum setting: 4 default keys, + * plus 10 other keys: */ + options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10); + /* don't decrypt default key only, + * don't override decryption: */ + options.WEPOption = 0; + if (priv->mode == ACX_MODE_MONITOR) { + /* don't decrypt default key only, + * override decryption mechanism: */ + options.WEPOption = 2; + } + + acx_s_configure(priv, &options, ACX100_IE_WEP_OPTIONS); + } + CLEAR_BIT(priv->set_mask, SET_WEP_OPTIONS); + } + + /* Rescan was requested */ + if (start_scan) { + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + /* We can avoid clearing list if join code + ** will be a bit more clever about not picking + ** 'bad' AP over and over again */ + acx_lock(priv, flags); + priv->ap_client = NULL; + acx_l_sta_list_init(priv); + acx_set_status(priv, ACX_STATUS_1_SCANNING); + acx_unlock(priv, flags); + + acx_s_cmd_start_scan(priv); + } + } + + /* debug, rate, and nick don't need any handling */ + /* what about sniffing mode?? */ + + acxlog(L_INIT, "get_mask 0x%08X, set_mask 0x%08X - after update\n", + priv->get_mask, priv->set_mask); + +/* end: */ + FN_EXIT0; +} + + +/*********************************************************************** +*/ +void +acx_s_initialize_rx_config(wlandevice_t *priv) +{ + struct { + u16 id ACX_PACKED; + u16 len ACX_PACKED; + u16 rx_cfg1 ACX_PACKED; + u16 rx_cfg2 ACX_PACKED; + } cfg; + + switch (priv->mode) { + case ACX_MODE_OFF: + priv->rx_config_1 = (u16) (0 + /* | RX_CFG1_INCLUDE_RXBUF_HDR */ + /* | RX_CFG1_FILTER_SSID */ + /* | RX_CFG1_FILTER_BCAST */ + /* | RX_CFG1_RCV_MC_ADDR1 */ + /* | RX_CFG1_RCV_MC_ADDR0 */ + /* | RX_CFG1_FILTER_ALL_MULTI */ + /* | RX_CFG1_FILTER_BSSID */ + /* | RX_CFG1_FILTER_MAC */ + /* | RX_CFG1_RCV_PROMISCUOUS */ + /* | RX_CFG1_INCLUDE_FCS */ + /* | RX_CFG1_INCLUDE_PHY_HDR */ + ); + priv->rx_config_2 = (u16) (0 + /*| RX_CFG2_RCV_ASSOC_REQ */ + /*| RX_CFG2_RCV_AUTH_FRAMES */ + /*| RX_CFG2_RCV_BEACON_FRAMES */ + /*| RX_CFG2_RCV_CONTENTION_FREE */ + /*| RX_CFG2_RCV_CTRL_FRAMES */ + /*| RX_CFG2_RCV_DATA_FRAMES */ + /*| RX_CFG2_RCV_BROKEN_FRAMES */ + /*| RX_CFG2_RCV_MGMT_FRAMES */ + /*| RX_CFG2_RCV_PROBE_REQ */ + /*| RX_CFG2_RCV_PROBE_RESP */ + /*| RX_CFG2_RCV_ACK_FRAMES */ + /*| RX_CFG2_RCV_OTHER */ + ); + break; + case ACX_MODE_MONITOR: + priv->rx_config_1 = (u16) (0 + /* | RX_CFG1_INCLUDE_RXBUF_HDR */ + /* | RX_CFG1_FILTER_SSID */ + /* | RX_CFG1_FILTER_BCAST */ + /* | RX_CFG1_RCV_MC_ADDR1 */ + /* | RX_CFG1_RCV_MC_ADDR0 */ + /* | RX_CFG1_FILTER_ALL_MULTI */ + /* | RX_CFG1_FILTER_BSSID */ + /* | RX_CFG1_FILTER_MAC */ + | RX_CFG1_RCV_PROMISCUOUS + /* | RX_CFG1_INCLUDE_FCS */ + /* | RX_CFG1_INCLUDE_PHY_HDR */ + ); + priv->rx_config_2 = (u16) (0 + | RX_CFG2_RCV_ASSOC_REQ + | RX_CFG2_RCV_AUTH_FRAMES + | RX_CFG2_RCV_BEACON_FRAMES + | RX_CFG2_RCV_CONTENTION_FREE + | RX_CFG2_RCV_CTRL_FRAMES + | RX_CFG2_RCV_DATA_FRAMES + | RX_CFG2_RCV_BROKEN_FRAMES + | RX_CFG2_RCV_MGMT_FRAMES + | RX_CFG2_RCV_PROBE_REQ + | RX_CFG2_RCV_PROBE_RESP + | RX_CFG2_RCV_ACK_FRAMES + | RX_CFG2_RCV_OTHER + ); + break; + default: + priv->rx_config_1 = (u16) (0 + /* | RX_CFG1_INCLUDE_RXBUF_HDR */ + /* | RX_CFG1_FILTER_SSID */ + /* | RX_CFG1_FILTER_BCAST */ + /* | RX_CFG1_RCV_MC_ADDR1 */ + /* | RX_CFG1_RCV_MC_ADDR0 */ + /* | RX_CFG1_FILTER_ALL_MULTI */ + /* | RX_CFG1_FILTER_BSSID */ + | RX_CFG1_FILTER_MAC + /* | RX_CFG1_RCV_PROMISCUOUS */ + /* | RX_CFG1_INCLUDE_FCS */ + /* | RX_CFG1_INCLUDE_PHY_HDR */ + ); + priv->rx_config_2 = (u16) (0 + | RX_CFG2_RCV_ASSOC_REQ + | RX_CFG2_RCV_AUTH_FRAMES + | RX_CFG2_RCV_BEACON_FRAMES + | RX_CFG2_RCV_CONTENTION_FREE + | RX_CFG2_RCV_CTRL_FRAMES + | RX_CFG2_RCV_DATA_FRAMES + /*| RX_CFG2_RCV_BROKEN_FRAMES */ + | RX_CFG2_RCV_MGMT_FRAMES + | RX_CFG2_RCV_PROBE_REQ + | RX_CFG2_RCV_PROBE_RESP + /*| RX_CFG2_RCV_ACK_FRAMES */ + | RX_CFG2_RCV_OTHER + ); + break; + } +#if DEBUG_WEP + if (CHIPTYPE_ACX100 == priv->chip_type) + /* only ACX100 supports that */ +#endif + priv->rx_config_1 |= RX_CFG1_INCLUDE_RXBUF_HDR; + + acxlog(L_INIT, "setting RXconfig to %04X:%04X\n", + priv->rx_config_1, priv->rx_config_2); + cfg.rx_cfg1 = cpu_to_le16(priv->rx_config_1); + cfg.rx_cfg2 = cpu_to_le16(priv->rx_config_2); + acx_s_configure(priv, &cfg, ACX1xx_IE_RXCONFIG); +} + + +/*********************************************************************** +** acx_schedule_after_interrupt_task +** +** Schedule the call of the after_interrupt method after leaving +** the interrupt context. +*/ +void +acx_schedule_after_interrupt_task(wlandevice_t *priv, unsigned int set_flag) +{ + SET_BIT(priv->after_interrupt_jobs, set_flag); + SCHEDULE_WORK(&priv->after_interrupt_task); +} + + +/*********************************************************************** +** Helper +*/ +static void +acx_s_after_interrupt_recalib(wlandevice_t *priv) +{ + int res; + + /* this helps with ACX100 at least; + * hopefully ACX111 also does a + * recalibration here */ + + /* clear flag beforehand, since we want to make sure + * it's cleared; then only set it again on specific circumstances */ + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); + + /* better wait a bit between recalibrations to + * prevent overheating due to torturing the card + * into working too long despite high temperature + * (just a safety measure) */ + if (priv->recalib_time_last_success + && time_before(jiffies, priv->recalib_time_last_success + + RECALIB_PAUSE * 60 * HZ)) { + priv->recalib_msg_ratelimit++; + if (priv->recalib_msg_ratelimit <= 5) + printk("%s: less than " STRING(RECALIB_PAUSE) + " minutes since last radio recalibration, " + "not recalibrating (maybe card is too hot?)\n", + priv->netdev->name); + if (priv->recalib_msg_ratelimit == 5) + printk("disabling above message\n"); + return; + } + + priv->recalib_msg_ratelimit = 0; + + /* note that commands sometimes fail (card busy), + * so only clear flag if we were fully successful */ + res = acx_s_recalib_radio(priv); + if (res == OK) { + printk("%s: successfully recalibrated radio\n", + priv->netdev->name); + priv->recalib_time_last_success = jiffies; + priv->recalib_failure_count = 0; + } else { + /* failed: resubmit, but only limited + * amount of times within some time range + * to prevent endless loop */ + + priv->recalib_time_last_success = 0; /* we failed */ + + /* if some time passed between last + * attempts, then reset failure retry counter + * to be able to do next recalib attempt */ + if (time_after(jiffies, priv->recalib_time_last_attempt + HZ)) + priv->recalib_failure_count = 0; + + if (++priv->recalib_failure_count <= 5) { + priv->recalib_time_last_attempt = jiffies; + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); + } + } +} + +/*********************************************************************** +** acx_e_after_interrupt_task +*/ +static void +acx_e_after_interrupt_task(void *data) +{ + netdevice_t *dev = (netdevice_t *) data; + wlandevice_t *priv; + + FN_ENTER; + + priv = (struct wlandevice *) dev->priv; + + acx_sem_lock(priv); + + if (!priv->after_interrupt_jobs) + goto end; /* no jobs to do */ + +#if TX_CLEANUP_IN_SOFTIRQ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_TX_CLEANUP) { + acx_lock(priv, flags); + acx_l_clean_tx_desc(priv); + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_TX_CLEANUP); + acx_unlock(priv, flags); + } +#endif + /* we see lotsa tx errors */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_RADIO_RECALIB) { + acx_s_after_interrupt_recalib(priv); + } + + /* a poor interrupt code wanted to do update_card_settings() */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_UPDATE_CARD_CFG) { + if (ACX_STATE_IFACE_UP & priv->dev_state_mask) + acx_s_update_card_settings(priv, 0, 0); + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_UPDATE_CARD_CFG); + } + + /* 1) we detected that no Scan_Complete IRQ came from fw, or + ** 2) we found too many STAs */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_STOP_SCAN) { + acxlog(L_IRQ, "sending a stop scan cmd...\n"); + acx_s_issue_cmd(priv, ACX1xx_CMD_STOP_SCAN, NULL, 0); + /* HACK: set the IRQ bit, since we won't get a + * scan complete IRQ any more on ACX111 (works on ACX100!), + * since _we_, not a fw, have stopped the scan */ + SET_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE); + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_STOP_SCAN); + } + + /* either fw sent Scan_Complete or we detected that + ** no Scan_Complete IRQ came from fw. Finish scanning, + ** pick join partner if any */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_COMPLETE_SCAN) { + if (priv->status == ACX_STATUS_1_SCANNING) { + if (OK != acx_s_complete_scan(priv)) { + SET_BIT(priv->after_interrupt_jobs, + ACX_AFTER_IRQ_RESTART_SCAN); + } + } else { + /* + scan kills current join status - restore it + ** (do we need it for STA?) */ + /* + does it happen only with active scans? + ** active and passive scans? ALL scans including + ** background one? */ + /* + was not verified that everything is restored + ** (but at least we start to emit beacons again) */ + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_3_AP: + acxlog(L_IRQ, "redoing cmd_join_bssid() after scan\n"); + acx_s_cmd_join_bssid(priv, priv->bssid); + } + } + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_COMPLETE_SCAN); + } + + /* STA auth or assoc timed out, start over again */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_RESTART_SCAN) { + acxlog(L_IRQ, "sending a start_scan cmd...\n"); + acx_s_cmd_start_scan(priv); + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_RESTART_SCAN); + } + + /* whee, we got positive assoc response! 8) */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_ASSOCIATE) { + acx_ie_generic_t pdr; + /* tiny race window exists, checking that we still a STA */ + switch (priv->mode) { + case ACX_MODE_2_STA: + pdr.m.aid = cpu_to_le16(priv->aid); + acx_s_configure(priv, &pdr, ACX1xx_IE_ASSOC_ID); + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + acxlog(L_ASSOC | L_DEBUG, "ASSOCIATED!\n"); + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_ASSOCIATE); + } + } +end: + acx_sem_unlock(priv); + FN_EXIT0; +} + + +/*********************************************************************** +*/ +void +acx_init_task_scheduler(wlandevice_t *priv) +{ + /* configure task scheduler */ + INIT_WORK(&priv->after_interrupt_task, acx_e_after_interrupt_task, priv->netdev); +} + + +/*********************************************************************** +** acx_cmd_join_bssid +** +** Common code for both acx100 and acx111. +*/ +/* NB: does NOT match RATE100_nn but matches ACX[111]_SCAN_RATE_n */ +static const u8 +bitpos2genframe_txrate[] = { + 10, /* 0. 1 Mbit/s */ + 20, /* 1. 2 Mbit/s */ + 55, /* 2. 5.5 Mbit/s */ + 0x0B, /* 3. 6 Mbit/s */ + 0x0F, /* 4. 9 Mbit/s */ + 110, /* 5. 11 Mbit/s */ + 0x0A, /* 6. 12 Mbit/s */ + 0x0E, /* 7. 18 Mbit/s */ + 220, /* 8. 22 Mbit/s */ + 0x09, /* 9. 24 Mbit/s */ + 0x0D, /* 10. 36 Mbit/s */ + 0x08, /* 11. 48 Mbit/s */ + 0x0C, /* 12. 54 Mbit/s */ + 10, /* 13. 1 Mbit/s, should never happen */ + 10, /* 14. 1 Mbit/s, should never happen */ + 10, /* 15. 1 Mbit/s, should never happen */ +}; + +/* Looks scary, eh? +** Actually, each one compiled into one AND and one SHIFT, +** 31 bytes in x86 asm (more if uints are replaced by u16/u8) */ +static unsigned int +rate111to5bits(unsigned int rate) +{ + return (rate & 0x7) + | ( (rate & RATE111_11) / (RATE111_11/JOINBSS_RATES_11) ) + | ( (rate & RATE111_22) / (RATE111_22/JOINBSS_RATES_22) ) + ; +} + +extern void BUG_joinbss_must_be_0x30_bytes_in_length(void); + +void +acx_s_cmd_join_bssid(wlandevice_t *priv, const u8 *bssid) +{ + acx_joinbss_t tmp; + int dtim_interval; + int i; + + /* compile-time check for proper size of fixed-size struct */ + if (sizeof(acx_joinbss_t) != 0x30) + BUG_joinbss_must_be_0x30_bytes_in_length(); + + FN_ENTER; + + dtim_interval = (ACX_MODE_0_ADHOC == priv->mode) ? + 1 : priv->dtim_interval; + + memset(&tmp, 0, sizeof(tmp)); + + for (i = 0; i < ETH_ALEN; i++) { + tmp.bssid[i] = bssid[ETH_ALEN-1 - i]; + } + + tmp.beacon_interval = cpu_to_le16(priv->beacon_interval); + + /* basic rate set. Control frame responses (such as ACK or CTS frames) + ** are sent with one of these rates */ + if (CHIPTYPE_ACX111 == priv->chip_type) { + /* It was experimentally determined that rates_basic + ** can take 11g rates as well, not only rates + ** defined with JOINBSS_RATES_BASIC111_nnn. + ** Just use RATE111_nnn constants... */ + tmp.u.acx111.dtim_interval = dtim_interval; + tmp.u.acx111.rates_basic = cpu_to_le16(priv->rate_basic); + acxlog(L_ASSOC, "%s rates_basic %04X, rates_supported %04X\n", + __func__, priv->rate_basic, priv->rate_oper); + } else { + tmp.u.acx100.dtim_interval = dtim_interval; + tmp.u.acx100.rates_basic = rate111to5bits(priv->rate_basic); + tmp.u.acx100.rates_supported = rate111to5bits(priv->rate_oper); + acxlog(L_ASSOC, "%s rates_basic %04X->%02X, " + "rates_supported %04X->%02X\n", + __func__, + priv->rate_basic, tmp.u.acx100.rates_basic, + priv->rate_oper, tmp.u.acx100.rates_supported); + } + + /* Setting up how Beacon, Probe Response, RTS, and PS-Poll frames + ** will be sent (rate/modulation/preamble) */ + tmp.genfrm_txrate = bitpos2genframe_txrate[lowest_bit(priv->rate_basic)]; + tmp.genfrm_mod_pre = 0; /* FIXME: was = priv->capab_short (which is always 0); */ + /* we can use short pre *if* all peers can understand it */ + /* FIXME #2: we need to correctly set PBCC/OFDM bits here too */ + + /* we switch fw to STA mode in MONITOR mode, it seems to be + ** the only mode where fw does not emit beacons by itself + ** but allows us to send anything (we really want to retain + ** ability to tx arbitrary frames in MONITOR mode) + */ + tmp.macmode = (priv->mode != ACX_MODE_MONITOR ? priv->mode : ACX_MODE_2_STA); + tmp.channel = priv->channel; + tmp.essid_len = priv->essid_len; + /* NOTE: the code memcpy'd essid_len + 1 before, which is WRONG! */ + memcpy(tmp.essid, priv->essid, tmp.essid_len); + acx_s_issue_cmd(priv, ACX1xx_CMD_JOIN, &tmp, tmp.essid_len + 0x11); + + acxlog(L_ASSOC | L_DEBUG, "BSS_Type = %u\n", tmp.macmode); + acxlog_mac(L_ASSOC | L_DEBUG, "JoinBSSID MAC:", priv->bssid, "\n"); + + acx_update_capabilities(priv); + FN_EXIT0; +} + + +/*********************************************************************** +** acx_s_set_defaults +** Called from acx_s_init_mac +*/ +int +acx_s_set_defaults(wlandevice_t *priv) +{ + unsigned long flags; + + FN_ENTER; + + /* query some settings from the card. + * NOTE: for some settings, e.g. CCA and ED (ACX100!), an initial + * query is REQUIRED, otherwise the card won't work correctly!! */ + priv->get_mask = GETSET_ANTENNA|GETSET_SENSITIVITY|GETSET_STATION_ID|GETSET_REG_DOMAIN; + /* Only ACX100 supports ED and CCA */ + if (CHIPTYPE_ACX100 == priv->chip_type) + priv->get_mask |= GETSET_CCA|GETSET_ED_THRESH; + + acx_s_update_card_settings(priv, 0, 0); + + acx_lock(priv, flags); + +#ifdef ACX_PCI + /* set our global interrupt mask */ + if (priv->chip_type == CHIPTYPE_ACX111) { + priv->irq_mask = (u16) ~(0 + /* | HOST_INT_RX_DATA */ + | HOST_INT_TX_COMPLETE + /* | HOST_INT_TX_XFER */ + | HOST_INT_RX_COMPLETE + /* | HOST_INT_DTIM */ + /* | HOST_INT_BEACON */ + /* | HOST_INT_TIMER */ + /* | HOST_INT_KEY_NOT_FOUND */ + | HOST_INT_IV_ICV_FAILURE + | HOST_INT_CMD_COMPLETE + | HOST_INT_INFO + /* | HOST_INT_OVERFLOW */ + /* | HOST_INT_PROCESS_ERROR */ + | HOST_INT_SCAN_COMPLETE + | HOST_INT_FCS_THRESHOLD + /* | HOST_INT_UNKNOWN */ + ); + priv->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */ + } else { + priv->irq_mask = (u16) ~(0 + /* | HOST_INT_RX_DATA */ + | HOST_INT_TX_COMPLETE + /* | HOST_INT_TX_XFER */ + | HOST_INT_RX_COMPLETE + /* | HOST_INT_DTIM */ + /* | HOST_INT_BEACON */ + /* | HOST_INT_TIMER */ + /* | HOST_INT_KEY_NOT_FOUND */ + /* | HOST_INT_IV_ICV_FAILURE */ + | HOST_INT_CMD_COMPLETE + | HOST_INT_INFO + /* | HOST_INT_OVERFLOW */ + /* | HOST_INT_PROCESS_ERROR */ + | HOST_INT_SCAN_COMPLETE + /* | HOST_INT_FCS_THRESHOLD */ + /* | HOST_INT_UNKNOWN */ + ); + priv->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */ + } +#endif + + priv->led_power = 1; /* LED is active on startup */ + priv->brange_max_quality = 60; /* LED blink max quality is 60 */ + priv->brange_time_last_state_change = jiffies; + + /* copy the MAC address we just got from the card + * into our MAC address used during current 802.11 session */ + MAC_COPY(priv->dev_addr, priv->netdev->dev_addr); + sprintf(priv->essid, "STA%02X%02X%02X", + priv->dev_addr[3], priv->dev_addr[4], priv->dev_addr[5]); + priv->essid_len = sizeof("STAxxxxxx") - 1; /* make sure to adapt if changed above! */ + priv->essid_active = 1; + + /* we have a nick field to waste, so why not abuse it + * to announce the driver version? ;-) */ + strncpy(priv->nick, "acx " WLAN_RELEASE, IW_ESSID_MAX_SIZE); + +#ifdef ACX_PCI + if (priv->chip_type == CHIPTYPE_ACX111) { + /* Hope this is correct, only tested with domain 0x30 */ + acx_read_eeprom_offset(priv, 0x16F, &priv->reg_dom_id); + } else if (priv->eeprom_version < 5) { + acx_read_eeprom_offset(priv, 0x16F, &priv->reg_dom_id); + } else { + acx_read_eeprom_offset(priv, 0x171, &priv->reg_dom_id); + } +#endif + + priv->channel = 1; + /* 0xffff would be better, but then we won't get a "scan complete" + * interrupt, so our current infrastructure will fail: */ + priv->scan_count = 1; + priv->scan_mode = ACX_SCAN_OPT_PASSIVE; + /* Doesn't work for acx100, do it only for acx111 for now */ + if (priv->chip_type == CHIPTYPE_ACX111) { + priv->scan_mode = ACX_SCAN_OPT_ACTIVE; + } + priv->scan_duration = 100; + priv->scan_probe_delay = 200; + priv->scan_rate = ACX_SCAN_RATE_1; + + priv->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM; + priv->preamble_mode = 2; /* auto */ + priv->listen_interval = 100; + priv->beacon_interval = DEFAULT_BEACON_INTERVAL; + priv->mode = ACX_MODE_OFF; + priv->dtim_interval = DEFAULT_DTIM_INTERVAL; + + priv->msdu_lifetime = DEFAULT_MSDU_LIFETIME; + SET_BIT(priv->set_mask, SET_MSDU_LIFETIME); + + priv->rts_threshold = DEFAULT_RTS_THRESHOLD; + + /* use standard default values for retry limits */ + priv->short_retry = 7; /* max. retries for (short) non-RTS packets */ + priv->long_retry = 4; /* max. retries for long (RTS) packets */ + SET_BIT(priv->set_mask, GETSET_RETRY); + + priv->fallback_threshold = 3; + priv->stepup_threshold = 10; + priv->rate_bcast = RATE111_1; + priv->rate_bcast100 = RATE100_1; + priv->rate_basic = RATE111_1 | RATE111_2; + priv->rate_auto = 1; + if (priv->chip_type == CHIPTYPE_ACX111) { + priv->rate_oper = RATE111_ALL; + } else { + priv->rate_oper = RATE111_ACX100_COMPAT; + } + + /* configure card to do rate fallback when in auto rate mode. */ + SET_BIT(priv->set_mask, SET_RATE_FALLBACK); + + /* Supported Rates element - the rates here are given in units of + * 500 kbit/s, plus 0x80 added. See 802.11-1999.pdf item 7.3.2.2 */ + acx_l_update_ratevector(priv); + + priv->capab_short = 0; + priv->capab_pbcc = 1; + priv->capab_agility = 0; + + SET_BIT(priv->set_mask, SET_RXCONFIG); + + /* set some more defaults */ + if (priv->chip_type == CHIPTYPE_ACX111) { + /* 30mW (15dBm) is default, at least in my acx111 card: */ + priv->tx_level_dbm = 15; + } else { + /* don't use max. level, since it might be dangerous + * (e.g. WRT54G people experience + * excessive Tx power damage!) */ + priv->tx_level_dbm = 18; + } + priv->tx_level_auto = 1; + SET_BIT(priv->set_mask, GETSET_TXPOWER); + + if (priv->chip_type == CHIPTYPE_ACX111) { + /* start with sensitivity level 1 out of 3: */ + priv->sensitivity = 1; + } + + /* better re-init the antenna value we got above */ + SET_BIT(priv->set_mask, GETSET_ANTENNA); + + priv->ps_wakeup_cfg = 0; + priv->ps_listen_interval = 0; + priv->ps_options = 0; + priv->ps_hangover_period = 0; + priv->ps_enhanced_transition_time = 0; +#if POWER_SAVE_80211 + SET_BIT(priv->set_mask, GETSET_POWER_80211); +#endif + + MAC_BCAST(priv->ap); + + acx_unlock(priv, flags); + acx_lock_unhold(); // hold time 844814 CPU ticks @2GHz + + acx_s_initialize_rx_config(priv); + + FN_EXIT1(OK); + return OK; +} + + +/*********************************************************************** +** acx_s_init_mac +*/ +int +acx_s_init_mac(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result = NOT_OK; + + FN_ENTER; + +#ifdef ACX_PCI + priv->memblocksize = 256; /* 256 is default */ + + acx_init_mboxes(priv); + + /* try to load radio for both ACX100 and ACX111, since both + * chips have at least some firmware versions making use of an + * external radio module */ + acx_s_upload_radio(priv); +#else + priv->memblocksize = 128; +#endif + + if (priv->chip_type == CHIPTYPE_ACX111) { + /* for ACX111, the order is different from ACX100 + 1. init packet templates + 2. create station context and create dma regions + 3. init wep default keys + */ + if (OK != acx111_s_init_packet_templates(priv)) + goto fail; + + if (OK != acx111_s_create_dma_regions(priv)) { + printk("%s: acx111_create_dma_regions FAILED\n", + dev->name); + goto fail; + } +#if DEBUG_WEP + /* don't decrypt WEP in firmware */ + if (OK != acx111_s_feature_on(priv, 0, FEATURE2_SNIFFER)) + goto fail; +#endif + } else { + if (OK != acx100_s_init_wep(priv)) + goto fail; + acxlog(L_DEBUG, "between init_wep and init_packet_templates\n"); + if (OK != acx100_s_init_packet_templates(priv)) + goto fail; + + if (OK != acx100_s_create_dma_regions(priv)) { + printk("%s: acx100_create_dma_regions FAILED\n", + dev->name); + goto fail; + } + } + + MAC_COPY(dev->dev_addr, priv->dev_addr); + result = OK; + +fail: + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx_s_start +*/ +void +acx_s_start(wlandevice_t *priv) +{ + FN_ENTER; + + /* + * Ok, now we do everything that can possibly be done with ioctl + * calls to make sure that when it was called before the card + * was up we get the changes asked for + */ + + SET_BIT(priv->set_mask, SET_TEMPLATES|SET_STA_LIST|GETSET_WEP + |GETSET_TXPOWER|GETSET_ANTENNA|GETSET_ED_THRESH|GETSET_CCA + |GETSET_REG_DOMAIN|GETSET_MODE|GETSET_CHANNEL + |GETSET_TX|GETSET_RX); + + acxlog(L_INIT, "updating initial settings on iface activation...\n"); + acx_s_update_card_settings(priv, 0, 0); + + FN_EXIT0; +} + + +/*********************************************************************** +** acx_update_capabilities +*/ +void +acx_update_capabilities(wlandevice_t *priv) +{ + u16 cap = 0; + + switch (priv->mode) { + case ACX_MODE_3_AP: + SET_BIT(cap, WF_MGMT_CAP_ESS); break; + case ACX_MODE_0_ADHOC: + SET_BIT(cap, WF_MGMT_CAP_IBSS); break; + /* other types of stations do not emit beacons */ + } + + if (priv->wep_restricted) { + SET_BIT(cap, WF_MGMT_CAP_PRIVACY); + } + if (priv->capab_short) { + SET_BIT(cap, WF_MGMT_CAP_SHORT); + } + if (priv->capab_pbcc) { + SET_BIT(cap, WF_MGMT_CAP_PBCC); + } + if (priv->capab_agility) { + SET_BIT(cap, WF_MGMT_CAP_AGILITY); + } + acxlog(L_DEBUG, "caps updated from 0x%04X to 0x%04X\n", + priv->capabilities, cap); + priv->capabilities = cap; +} + +#ifdef UNUSED +/*********************************************************************** +** FIXME: check whether this function is indeed acx111 only, +** rename ALL relevant definitions to indicate actual card scope! +*/ +void +acx111_s_read_configoption(wlandevice_t *priv) +{ + acx111_ie_configoption_t co, co2; + int i; + const u8 *pEle; + + if (OK != acx_s_interrogate(priv, &co, ACX111_IE_CONFIG_OPTIONS) ) { + printk("%s: reading ConfigOption FAILED\n", priv->netdev->name); + return; + }; + if (!(acx_debug & L_DEBUG)) + return; + + memcpy(&co2.configoption_fixed, &co.configoption_fixed, + sizeof(co.configoption_fixed)); + + pEle = (u8 *)&co.configoption_fixed + sizeof(co.configoption_fixed) - 4; + + co2.antennas.type = pEle[0]; + co2.antennas.len = pEle[1]; + printk("AntennaID:%02X Len:%02X Data:", + co2.antennas.type, co2.antennas.len); + for (i = 0; i < pEle[1]; i++) { + co2.antennas.list[i] = pEle[i+2]; + printk("%02X ", pEle[i+2]); + } + printk("\n"); + + pEle += pEle[1] + 2; + co2.power_levels.type = pEle[0]; + co2.power_levels.len = pEle[1]; + printk("PowerLevelID:%02X Len:%02X Data:", + co2.power_levels.type, co2.power_levels.len); + for (i = 0; i < pEle[1]*2; i++) { + co2.power_levels.list[i] = pEle[i+2]; + printk("%02X ", pEle[i+2]); + } + printk("\n"); + + pEle += pEle[1]*2 + 2; + co2.data_rates.type = pEle[0]; + co2.data_rates.len = pEle[1]; + printk("DataRatesID:%02X Len:%02X Data:", + co2.data_rates.type, co2.data_rates.len); + for (i = 0; i < pEle[1]; i++) { + co2.data_rates.list[i] = pEle[i+2]; + printk("%02X ", pEle[i+2]); + } + printk("\n"); + + pEle += pEle[1] + 2; + co2.domains.type = pEle[0]; + co2.domains.len = pEle[1]; + printk("DomainID:%02X Len:%02X Data:", + co2.domains.type, co2.domains.len); + for (i = 0; i < pEle[1]; i++) { + co2.domains.list[i] = pEle[i+2]; + printk("%02X ", pEle[i+2]); + } + printk("\n"); + + pEle += pEle[1] + 2; + co2.product_id.type = pEle[0]; + co2.product_id.len = pEle[1]; + for (i = 0; i < pEle[1]; i++) { + co2.product_id.list[i] = pEle[i+2]; + } + printk("ProductID:%02X Len:%02X Data:%.*s\n", + co2.product_id.type, co2.product_id.len, + co2.product_id.len, (char *)co2.product_id.list); + + pEle += pEle[1] + 2; + co2.manufacturer.type = pEle[0]; + co2.manufacturer.len = pEle[1]; + for (i = 0; i < pEle[1]; i++) { + co2.manufacturer.list[i] = pEle[i+2]; + } + printk("ManufacturerID:%02X Len:%02X Data:%.*s\n", + co2.manufacturer.type, co2.manufacturer.len, + co2.manufacturer.len, (char *)co2.manufacturer.list); +/* + printk("EEPROM part:\n"); + for (i=0; i<58; i++) { + printk("%02X =======> 0x%02X\n", + i, (u8 *)co.configoption_fixed.NVSv[i-2]); + } +*/ +} +#endif + + +/*********************************************************************** +** Not inlined: it's larger than it seems +*/ +void +acx_print_mac(const char *head, const u8 *mac, const char *tail) +{ + printk("%s"MACSTR"%s", head, MAC(mac), tail); +} diff -puN /dev/null drivers/net/wireless/tiacx/ioctl.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/ioctl.c 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,3033 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +#include +#include +#include +#include +#include +#include /* required for 2.4.x kernels; verify_write() */ + +#include +#include +#if WIRELESS_EXT >= 13 +#include +#endif /* WE >= 13 */ + +#include "acx.h" + + +/*================================================================*/ + +/* if you plan to reorder something, make sure to reorder all other places + * accordingly! */ +/* someone broke SET/GET convention: SETs must have even position, GETs odd */ +#define ACX100_IOCTL SIOCIWFIRSTPRIV +enum { + ACX100_IOCTL_DEBUG = ACX100_IOCTL, + ACX100_IOCTL_GET__________UNUSED1, + ACX100_IOCTL_SET_PLED, + ACX100_IOCTL_GET_PLED, + ACX100_IOCTL_SET_RATES, + ACX100_IOCTL_LIST_DOM, + ACX100_IOCTL_SET_DOM, + ACX100_IOCTL_GET_DOM, + ACX100_IOCTL_SET_SCAN_PARAMS, + ACX100_IOCTL_GET_SCAN_PARAMS, + ACX100_IOCTL_SET_PREAMB, + ACX100_IOCTL_GET_PREAMB, + ACX100_IOCTL_SET_ANT, + ACX100_IOCTL_GET_ANT, + ACX100_IOCTL_RX_ANT, + ACX100_IOCTL_TX_ANT, + ACX100_IOCTL_SET_PHY_AMP_BIAS, + ACX100_IOCTL_GET_PHY_CHAN_BUSY, + ACX100_IOCTL_SET_ED, + ACX100_IOCTL_GET__________UNUSED3, + ACX100_IOCTL_SET_CCA, + ACX100_IOCTL_GET__________UNUSED4, + ACX100_IOCTL_MONITOR, + ACX100_IOCTL_TEST, + ACX100_IOCTL_DBG_SET_MASKS, + ACX111_IOCTL_INFO, + ACX100_IOCTL_DBG_SET_IO, + ACX100_IOCTL_DBG_GET_IO +}; + +/* channel frequencies + * TODO: Currently, every other 802.11 driver keeps its own copy of this. In + * the long run this should be integrated into ieee802_11.h or wireless.h or + * whatever IEEE802.11x framework evolves */ +static const u16 acx_channel_freq[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484, +}; + +static const struct iw_priv_args acx_ioctl_private_args[] = { +#if ACX_DEBUG +{ cmd : ACX100_IOCTL_DEBUG, + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + get_args : 0, + name : "SetDebug" }, +#endif +{ cmd : ACX100_IOCTL_SET_PLED, + set_args : IW_PRIV_TYPE_BYTE | 2, + get_args : 0, + name : "SetLEDPower" }, +{ cmd : ACX100_IOCTL_GET_PLED, + set_args : 0, + get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2, + name : "GetLEDPower" }, +{ cmd : ACX100_IOCTL_SET_RATES, + set_args : IW_PRIV_TYPE_CHAR | 256, + get_args : 0, + name : "SetRates" }, +{ cmd : ACX100_IOCTL_LIST_DOM, + set_args : 0, + get_args : 0, + name : "ListRegDomain" }, +{ cmd : ACX100_IOCTL_SET_DOM, + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + get_args : 0, + name : "SetRegDomain" }, +{ cmd : ACX100_IOCTL_GET_DOM, + set_args : 0, + get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + name : "GetRegDomain" }, +{ cmd : ACX100_IOCTL_SET_SCAN_PARAMS, + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, + get_args : 0, + name : "SetScanParams" }, +{ cmd : ACX100_IOCTL_GET_SCAN_PARAMS, + set_args : 0, + get_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, + name : "GetScanParams" }, +{ cmd : ACX100_IOCTL_SET_PREAMB, + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + get_args : 0, + name : "SetSPreamble" }, +{ cmd : ACX100_IOCTL_GET_PREAMB, + set_args : 0, + get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + name : "GetSPreamble" }, +{ cmd : ACX100_IOCTL_SET_ANT, + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + get_args : 0, + name : "SetAntenna" }, +{ cmd : ACX100_IOCTL_GET_ANT, + set_args : 0, + get_args : 0, + name : "GetAntenna" }, +{ cmd : ACX100_IOCTL_RX_ANT, + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + get_args : 0, + name : "SetRxAnt" }, +{ cmd : ACX100_IOCTL_TX_ANT, + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + get_args : 0, + name : "SetTxAnt" }, +{ cmd : ACX100_IOCTL_SET_PHY_AMP_BIAS, + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + get_args : 0, + name : "SetPhyAmpBias"}, +{ cmd : ACX100_IOCTL_GET_PHY_CHAN_BUSY, + set_args : 0, + get_args : 0, + name : "GetPhyChanBusy" }, +{ cmd : ACX100_IOCTL_SET_ED, + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + get_args : 0, + name : "SetED" }, +{ cmd : ACX100_IOCTL_SET_CCA, + set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + get_args : 0, + name : "SetCCA" }, +{ cmd : ACX100_IOCTL_MONITOR, + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + get_args : 0, + name : "monitor" }, +{ cmd : ACX100_IOCTL_TEST, + set_args : 0, + get_args : 0, + name : "Test" }, +{ cmd : ACX100_IOCTL_DBG_SET_MASKS, + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, + get_args : 0, + name : "DbgSetMasks" }, +{ cmd : ACX111_IOCTL_INFO, + set_args : 0, + get_args : 0, + name : "GetAcx111Info" }, +{ cmd : ACX100_IOCTL_DBG_SET_IO, + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, + get_args : 0, + name : "DbgSetIO" }, +{ cmd : ACX100_IOCTL_DBG_GET_IO, + set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, + get_args : 0, + name : "DbgGetIO" }, +}; + + +/*------------------------------------------------------------------------------ + * acx_ioctl_commit + *----------------------------------------------------------------------------*/ +static int +acx_ioctl_commit(struct net_device *dev, + struct iw_request_info *info, + void *zwrq, char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + FN_ENTER; + + acx_sem_lock(priv); + if (ACX_STATE_IFACE_UP & priv->dev_state_mask) + acx_s_update_card_settings(priv, 0, 0); + acx_sem_unlock(priv); + + FN_EXIT0; + return OK; +} + + +/*********************************************************************** +*/ +static int +acx_ioctl_get_name( + struct net_device *dev, + struct iw_request_info *info, + char *cwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + static const char * const names[] = { "IEEE 802.11b+/g+", "IEEE 802.11b+" }; + + strcpy(cwrq, names[(CHIPTYPE_ACX111 == priv->chip_type) ? 0 : 1]); + + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_freq +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_freq( + struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int channel = -1; + unsigned int mult = 1; + int result; + + FN_ENTER; + + if (fwrq->e == 0 && fwrq->m <= 1000) { + /* Setting by channel number */ + channel = fwrq->m; + } else { + /* If setting by frequency, convert to a channel */ + int i; + + for (i = 0; i < (6 - fwrq->e); i++) + mult *= 10; + + for (i = 1; i <= 14; i++) + if (fwrq->m == acx_channel_freq[i - 1] * mult) + channel = i; + } + + if (channel > 14) { + result = -EINVAL; + goto end; + } + + acx_sem_lock(priv); + + priv->channel = channel; + /* hmm, the following code part is strange, but this is how + * it was being done before... */ + acxlog(L_IOCTL, "Changing to channel %d\n", channel); + SET_BIT(priv->set_mask, GETSET_CHANNEL); + + result = -EINPROGRESS; /* need to call commit handler */ + + acx_sem_unlock(priv); +end: + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +*/ +static inline int +acx_ioctl_get_freq( + struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + fwrq->e = 0; + fwrq->m = priv->channel; + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_mode +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_mode( + struct net_device *dev, + struct iw_request_info *info, + u32 *uwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + + FN_ENTER; + + acx_sem_lock(priv); + + switch (*uwrq) { + case IW_MODE_AUTO: + priv->mode = ACX_MODE_OFF; + break; +#if WIRELESS_EXT > 14 + case IW_MODE_MONITOR: + priv->mode = ACX_MODE_MONITOR; + break; +#endif /* WIRELESS_EXT > 14 */ + case IW_MODE_ADHOC: + priv->mode = ACX_MODE_0_ADHOC; + break; + case IW_MODE_INFRA: + priv->mode = ACX_MODE_2_STA; + break; + case IW_MODE_MASTER: + printk("acx: master mode (HostAP) is very, very " + "experimental! It might work partially, but " + "better get prepared for nasty surprises " + "at any time\n"); + priv->mode = ACX_MODE_3_AP; + break; + case IW_MODE_REPEAT: + case IW_MODE_SECOND: + default: + result = -EOPNOTSUPP; + goto end_unlock; + } + + SET_BIT(priv->set_mask, GETSET_MODE); + result = -EINPROGRESS; + +end_unlock: + acx_sem_unlock(priv); + + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +*/ +static int +acx_ioctl_get_mode( + struct net_device *dev, + struct iw_request_info *info, + u32 *uwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result = 0; + + switch (priv->mode) { + case ACX_MODE_OFF: + *uwrq = IW_MODE_AUTO; break; +#if WIRELESS_EXT > 14 + case ACX_MODE_MONITOR: + *uwrq = IW_MODE_MONITOR; break; +#endif /* WIRELESS_EXT > 14 */ + case ACX_MODE_0_ADHOC: + *uwrq = IW_MODE_ADHOC; break; + case ACX_MODE_2_STA: + *uwrq = IW_MODE_INFRA; break; + case ACX_MODE_3_AP: + *uwrq = IW_MODE_MASTER; break; + default: + result = -EOPNOTSUPP; + } + return result; +} + + +/*********************************************************************** +*/ +static int +acx_ioctl_set_sens( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + acx_sem_lock(priv); + + priv->sensitivity = (1 == vwrq->disabled) ? 0 : vwrq->value; + SET_BIT(priv->set_mask, GETSET_SENSITIVITY); + + acx_sem_unlock(priv); + + return -EINPROGRESS; +} + + +/*********************************************************************** +*/ +static int +acx_ioctl_get_sens( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + /* acx_sem_lock(priv); */ + + vwrq->value = priv->sensitivity; + vwrq->disabled = (vwrq->value == 0); + vwrq->fixed = 1; + + /* acx_sem_unlock(priv); */ + + return OK; +} + + +/*------------------------------------------------------------------------------ + * acx_ioctl_set_ap + * + * Sets the MAC address of the AP to associate with + * + * Call context: Process + * STATUS: NEW + *----------------------------------------------------------------------------*/ +static int +acx_ioctl_set_ap( + struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result = 0; + const u8 *ap; + + FN_ENTER; + if (NULL == awrq) { + result = -EFAULT; + goto end; + } + if (ARPHRD_ETHER != awrq->sa_family) { + result = -EINVAL; + goto end; + } + + ap = awrq->sa_data; + acxlog_mac(L_IOCTL, "Set AP=", ap, "\n"); + + MAC_COPY(priv->ap, ap); + + /* We want to start rescan in managed or ad-hoc mode, + ** otherwise just set priv->ap. + ** "iwconfig ap mode managed": we must be able + ** to set ap _first_ and _then_ set mode */ + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + /* FIXME: if there is a convention on what zero AP means, + ** please add a comment about that. I don't know of any --vda */ + if (mac_is_zero(ap)) { + /* "off" == 00:00:00:00:00:00 */ + MAC_BCAST(priv->ap); + acxlog(L_IOCTL, "Not reassociating\n"); + } else { + acxlog(L_IOCTL, "Forcing reassociation\n"); + SET_BIT(priv->set_mask, GETSET_RESCAN); + } + break; + } + result = -EINPROGRESS; +end: + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +*/ +static int +acx_ioctl_get_ap( + struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + if (ACX_STATUS_4_ASSOCIATED == priv->status) { + /* as seen in Aironet driver, airo.c */ + MAC_COPY(awrq->sa_data, priv->bssid); + } else { + MAC_ZERO(awrq->sa_data); + } + awrq->sa_family = ARPHRD_ETHER; + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_get_aplist +* +* Comment: deprecated in favour of iwscan. +* We simply return the list of currently available stations in range, +* don't do a new scan. +*----------------------------------------------------------------*/ +static int +acx_ioctl_get_aplist( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + struct sockaddr *address = (struct sockaddr *) extra; + struct iw_quality qual[IW_MAX_AP]; + int i, cur; + int result = OK; + + FN_ENTER; + + /* we have AP list only in STA mode */ + if (ACX_MODE_2_STA != priv->mode) { + result = -EOPNOTSUPP; + goto end; + } + + cur = 0; + for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { + struct client *bss = &priv->sta_list[i]; + if (!bss->used) continue; + MAC_COPY(address[cur].sa_data, bss->bssid); + address[cur].sa_family = ARPHRD_ETHER; + qual[cur].level = bss->sir; + qual[cur].noise = bss->snr; +#ifndef OLD_QUALITY + qual[cur].qual = acx_signal_determine_quality(qual[cur].level, + qual[cur].noise); +#else + qual[cur].qual = (qual[cur].noise <= 100) ? + 100 - qual[cur].noise : 0; +#endif + /* no scan: level/noise/qual not updated: */ + qual[cur].updated = 0; + cur++; + } + if (cur) { + dwrq->flags = 1; + memcpy(extra + sizeof(struct sockaddr)*cur, &qual, + sizeof(struct iw_quality)*cur); + } + dwrq->length = cur; +end: + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +*/ +static int +acx_ioctl_set_scan( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + + FN_ENTER; + + acx_sem_lock(priv); + + /* don't start scan if device is not up yet */ + if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) { + result = -EAGAIN; + goto end_unlock; + } + + /* This is NOT a rescan for new AP! + ** Do not use SET_BIT(GETSET_RESCAN); */ + acx_s_cmd_start_scan(priv); + result = OK; + +end_unlock: + acx_sem_unlock(priv); +/* end: */ + FN_EXIT1(result); + return result; +} + + +#if WIRELESS_EXT > 13 +/*********************************************************************** +** acx_s_scan_add_station +*/ +/* helper. not sure wheter it's really a _s_leeping fn */ +static char* +acx_s_scan_add_station( + wlandevice_t *priv, + char *ptr, + char *end_buf, + struct client *bss) +{ + struct iw_event iwe; + char *ptr_rate; + + FN_ENTER; + + /* MAC address has to be added first */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + MAC_COPY(iwe.u.ap_addr.sa_data, bss->bssid); + acxlog_mac(L_IOCTL, "scan, station address: ", bss->bssid, "\n"); + ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_ADDR_LEN); + + /* Add ESSID */ + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = bss->essid_len; + iwe.u.data.flags = 1; + acxlog(L_IOCTL, "scan, essid: %s\n", bss->essid); + ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid); + + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + if (bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)) { + if (bss->cap_info & WF_MGMT_CAP_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + acxlog(L_IOCTL, "scan, mode: %d\n", iwe.u.mode); + ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_UINT_LEN); + } + + /* Add frequency */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = acx_channel_freq[bss->channel - 1] * 100000; + iwe.u.freq.e = 1; + acxlog(L_IOCTL, "scan, frequency: %d\n", iwe.u.freq.m); + ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_FREQ_LEN); + + /* Add link quality */ + iwe.cmd = IWEVQUAL; + /* FIXME: these values should be expressed in dBm, but we don't know + * how to calibrate it yet */ + iwe.u.qual.level = bss->sir; + iwe.u.qual.noise = bss->snr; +#ifndef OLD_QUALITY + iwe.u.qual.qual = acx_signal_determine_quality(iwe.u.qual.level, + iwe.u.qual.noise); +#else + iwe.u.qual.qual = (iwe.u.qual.noise <= 100) ? + 100 - iwe.u.qual.noise : 0; +#endif + iwe.u.qual.updated = 7; + acxlog(L_IOCTL, "scan, link quality: %d/%d/%d\n", + iwe.u.qual.level, iwe.u.qual.noise, iwe.u.qual.qual); + ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_QUAL_LEN); + + /* Add encryption */ + iwe.cmd = SIOCGIWENCODE; + if (bss->cap_info & WF_MGMT_CAP_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + acxlog(L_IOCTL, "scan, encryption flags: %X\n", iwe.u.data.flags); + ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid); + + /* add rates */ + iwe.cmd = SIOCGIWRATE; + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + ptr_rate = ptr + IW_EV_LCP_LEN; + + { + u16 rate = bss->rate_cap; + const u8* p = bitpos2ratebyte; + while (rate) { + if (rate & 1) { + iwe.u.bitrate.value = *p * 500000; /* units of 500kb/s */ + acxlog(L_IOCTL, "scan, rate: %d\n", iwe.u.bitrate.value); + ptr = iwe_stream_add_value(ptr, ptr_rate, end_buf, &iwe, IW_EV_PARAM_LEN); + } + rate >>= 1; + p++; + }} + + if ((ptr_rate - ptr) > (ptrdiff_t)IW_EV_LCP_LEN) + ptr = ptr_rate; + + /* drop remaining station data items for now */ + + FN_EXIT0; + return ptr; +} + + +/*********************************************************************** + * acx_ioctl_get_scan + */ +static int +acx_ioctl_get_scan( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + char *ptr = extra; + int i; + int result = OK; + + FN_ENTER; + + acx_sem_lock(priv); + + /* no scan available if device is not up yet */ + if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) { + acxlog(L_IOCTL, "iface not up yet\n"); + result = -EAGAIN; + goto end_unlock; + } +#if 0 /* Why is this needed? If needed, add a comment */ + if (priv->scan_start && time_before(jiffies, priv->scan_start + 3*HZ)) { + acxlog(L_IOCTL, "scan in progress, no results yet\n"); + result = -EAGAIN; + goto end_unlock; + } +#endif + +#if ENODATA_TO_BE_USED_AFTER_SCAN_ERROR_ONLY + if (priv->bss_table_count == 0) { + /* no stations found */ + result = -ENODATA; + goto end_unlock; + } +#endif + + for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { + struct client *bss = &priv->sta_list[i]; + if (!bss->used) continue; + ptr = acx_s_scan_add_station(priv, ptr, + extra + IW_SCAN_MAX_DATA, bss); + } + dwrq->length = ptr - extra; + dwrq->flags = 0; + +end_unlock: + acx_sem_unlock(priv); +/* end: */ + FN_EXIT1(result); + return result; +} +#endif /* WIRELESS_EXT > 13 */ + + +/*---------------------------------------------------------------- +* acx_ioctl_set_essid +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_essid( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int len = dwrq->length; + int result; + + FN_ENTER; + + acxlog(L_IOCTL, "Set ESSID '%*s', length %d, flags 0x%04X\n", + len, extra, len, dwrq->flags); + + if (len < 0) { + result = -EINVAL; + goto end; + } + + acx_sem_lock(priv); + + /* ESSID disabled? */ + if (0 == dwrq->flags) { + priv->essid_active = 0; + + } else { + if (dwrq->length > IW_ESSID_MAX_SIZE+1) { + result = -E2BIG; + goto end_unlock; + } + + if (len > sizeof(priv->essid)) + len = sizeof(priv->essid); + memcpy(priv->essid, extra, len-1); + priv->essid[len-1] = '\0'; + /* Paranoia: just in case there is a '\0'... */ + priv->essid_len = strlen(priv->essid); + priv->essid_active = 1; + } + + SET_BIT(priv->set_mask, GETSET_RESCAN); + + result = -EINPROGRESS; + +end_unlock: + acx_sem_unlock(priv); +end: + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +*/ +static int +acx_ioctl_get_essid( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + dwrq->flags = priv->essid_active; + if (priv->essid_active) { + memcpy(extra, priv->essid, priv->essid_len); + extra[priv->essid_len] = '\0'; + dwrq->length = priv->essid_len + 1; + dwrq->flags = 1; + } + return OK; +} + + +/*---------------------------------------------------------------- +* acx_l_update_client_rates +*----------------------------------------------------------------*/ +static void +acx_l_update_client_rates(wlandevice_t *priv, u16 rate) +{ + int i; + for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { + client_t *clt = &priv->sta_list[i]; + if (!clt->used) continue; + clt->rate_cfg = (clt->rate_cap & rate); + if (!clt->rate_cfg) { + /* no compatible rates left: kick client */ + acxlog_mac(L_ASSOC, "client ",clt->address," kicked: " + "rates are not compatible anymore\n"); + acx_l_sta_list_del(priv, clt); + continue; + } + clt->rate_cur &= clt->rate_cfg; + if (!clt->rate_cur) { + /* current rate become invalid, choose a valid one */ + clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); + } + clt->fallback_count = clt->stepup_count = 0; + clt->ignore_count = 16; + } + switch (priv->mode) { + case ACX_MODE_2_STA: + if (priv->ap_client && !priv->ap_client->used) { + /* Owwww... we kicked our AP!! :) */ + SET_BIT(priv->set_mask, GETSET_RESCAN); + } + } +} + + +/*********************************************************************** +*/ +/* maps bits from acx111 rate to rate in Mbits */ +static const unsigned int +acx111_rate_tbl[] = { + 1000000, /* 0 */ + 2000000, /* 1 */ + 5500000, /* 2 */ + 6000000, /* 3 */ + 9000000, /* 4 */ + 11000000, /* 5 */ + 12000000, /* 6 */ + 18000000, /* 7 */ + 22000000, /* 8 */ + 24000000, /* 9 */ + 36000000, /* 10 */ + 48000000, /* 11 */ + 54000000, /* 12 */ + 500000, /* 13, should not happen */ + 500000, /* 14, should not happen */ + 500000, /* 15, should not happen */ +}; + +/*********************************************************************** + * acx_ioctl_set_rate + */ +static int +acx_ioctl_set_rate( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + u16 txrate_cfg = 1; + unsigned long flags; + int autorate; + int result = -EINVAL; + + FN_ENTER; + acxlog(L_IOCTL, + "rate %d fixed 0x%X disabled 0x%X flags 0x%X\n", + vwrq->value, vwrq->fixed, vwrq->disabled, vwrq->flags); + + if ((0 == vwrq->fixed) || (1 == vwrq->fixed)) { + int i = VEC_SIZE(acx111_rate_tbl)-1; + if (vwrq->value == -1) + /* "iwconfig rate auto" --> choose highest */ + vwrq->value = (CHIPTYPE_ACX100 == priv->chip_type) ? + 22000000 : 54000000; + while (i >= 0) { + if (vwrq->value == acx111_rate_tbl[i]) { + txrate_cfg <<= i; + i = 0; + break; + } + i--; + } + if (i == -1) { /* no matching rate */ + result = -EINVAL; + goto end; + } + } else { /* rate N, N<1000 (driver specific): we don't use this */ + result = -EOPNOTSUPP; + goto end; + } + /* now: only one bit is set in txrate_cfg, corresponding to + ** indicated rate */ + + autorate = (vwrq->fixed == 0) && (RATE111_1 != txrate_cfg); + if (autorate) { + /* convert 00100000 -> 00111111 */ + txrate_cfg = (txrate_cfg<<1)-1; + } + + if (CHIPTYPE_ACX100 == priv->chip_type) { + txrate_cfg &= RATE111_ACX100_COMPAT; + if (!txrate_cfg) { + result = -ENOTSUPP; /* rate is not supported by acx100 */ + goto end; + } + } + + acx_sem_lock(priv); + acx_lock(priv, flags); + + priv->rate_auto = autorate; + priv->rate_oper = txrate_cfg; + priv->rate_basic = txrate_cfg; + /* only do that in auto mode, non-auto will be able to use + * one specific Tx rate only anyway */ + if (autorate) { + /* only use 802.11b base rates, for standard 802.11b H/W + * compatibility */ + priv->rate_basic &= RATE111_80211B_COMPAT; + } + priv->rate_bcast = 1 << lowest_bit(txrate_cfg); + if (CHIPTYPE_ACX100 == priv->chip_type) + priv->rate_bcast100 = acx_rate111to100(priv->rate_bcast); + acx_l_update_ratevector(priv); + acx_l_update_client_rates(priv, txrate_cfg); + + /* Do/don't do tx rate fallback; beacon contents and rate */ + SET_BIT(priv->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES); + result = -EINPROGRESS; + + acx_unlock(priv, flags); + acx_sem_unlock(priv); +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_get_rate +*----------------------------------------------------------------*/ +static int +acx_ioctl_get_rate( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + /* TODO: remember rate of last tx, show it. think about multiple peers... */ + wlandevice_t *priv = acx_netdev_priv(dev); + vwrq->value = acx111_rate_tbl[highest_bit(priv->rate_oper)]; + vwrq->fixed = !priv->rate_auto; + vwrq->disabled = 0; + return OK; +} + +static int +acx_ioctl_set_rts( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int val = vwrq->value; + + if (vwrq->disabled) + val = 2312; + if ((val < 0) || (val > 2312)) + return -EINVAL; + + priv->rts_threshold = val; + return OK; +} + +static inline int +acx_ioctl_get_rts( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + vwrq->value = priv->rts_threshold; + vwrq->disabled = (vwrq->value >= 2312); + vwrq->fixed = 1; + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_encode +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_encode( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int index; + int result; + + FN_ENTER; + acxlog(L_IOCTL, + "Set Encoding flags=0x%04X, size=%d, key: %s\n", + dwrq->flags, dwrq->length, extra ? "set" : "No key"); + + acx_sem_lock(priv); + + index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + if (dwrq->length > 0) { + /* if index is 0 or invalid, use default key */ + if ((index < 0) || (index > 3)) + index = (int)priv->wep_current_index; + + if (0 == (dwrq->flags & IW_ENCODE_NOKEY)) { + if (dwrq->length > 29) + dwrq->length = 29; /* restrict it */ + + if (dwrq->length > 13) + priv->wep_keys[index].size = 29; /* 29*8 == 232, WEP256 */ + else + if (dwrq->length > 5) + priv->wep_keys[index].size = 13; /* 13*8 == 104bit, WEP128 */ + else + if (dwrq->length > 0) + priv->wep_keys[index].size = 5; /* 5*8 == 40bit, WEP64 */ + else + /* disable key */ + priv->wep_keys[index].size = 0; + + memset(priv->wep_keys[index].key, 0, sizeof(priv->wep_keys[index].key)); + memcpy(priv->wep_keys[index].key, extra, dwrq->length); + } + + } else { + /* set transmit key */ + if ((index >= 0) && (index <= 3)) + priv->wep_current_index = index; + else + if (0 == (dwrq->flags & IW_ENCODE_MODE)) { + /* complain if we were not just setting + * the key mode */ + result = -EINVAL; + goto end_unlock; + } + } + + priv->wep_enabled = !(dwrq->flags & IW_ENCODE_DISABLED); + + if (dwrq->flags & IW_ENCODE_OPEN) { + priv->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM; + priv->wep_restricted = 0; + + } else if (dwrq->flags & IW_ENCODE_RESTRICTED) { + priv->auth_alg = WLAN_AUTH_ALG_SHAREDKEY; + priv->wep_restricted = 1; + } + + /* set flag to make sure the card WEP settings get updated */ + SET_BIT(priv->set_mask, GETSET_WEP); + + acxlog(L_IOCTL, "len=%d, key at 0x%p, flags=0x%X\n", + dwrq->length, extra, + dwrq->flags); + + for (index = 0; index <= 3; index++) { + if (priv->wep_keys[index].size) { + acxlog(L_IOCTL, + "index=%d, size=%d, key at 0x%p\n", + priv->wep_keys[index].index, + (int) priv->wep_keys[index].size, + priv->wep_keys[index].key); + } + } + result = -EINPROGRESS; + +end_unlock: + acx_sem_unlock(priv); + + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_get_encode +*----------------------------------------------------------------*/ +static int +acx_ioctl_get_encode( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + if (priv->wep_enabled == 0) { + dwrq->flags = IW_ENCODE_DISABLED; + + } else { + if ((index < 0) || (index > 3)) + index = (int)priv->wep_current_index; + + dwrq->flags = + (priv->wep_restricted == 1) ? IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN; + dwrq->length = priv->wep_keys[index].size; + + memcpy(extra, + priv->wep_keys[index].key, + priv->wep_keys[index].size); + } + + /* set the current index */ + SET_BIT(dwrq->flags, index + 1); + + acxlog(L_IOCTL, "len=%d, key=%p, flags=0x%X\n", + dwrq->length, dwrq->pointer, + dwrq->flags); + + return OK; +} + + +/*********************************************************************** +*/ +static int +acx_ioctl_set_power( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + acxlog(L_IOCTL, "Set 802.11 Power Save flags=0x%04X\n", vwrq->flags); + if (vwrq->disabled) { + CLEAR_BIT(priv->ps_wakeup_cfg, PS_CFG_ENABLE); + SET_BIT(priv->set_mask, GETSET_POWER_80211); + return -EINPROGRESS; + } + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + u16 ps_timeout = (vwrq->value * 1024) / 1000; + + if (ps_timeout > 255) + ps_timeout = 255; + acxlog(L_IOCTL, "setting PS timeout value to %d time units " + "due to %dus\n", ps_timeout, vwrq->value); + priv->ps_hangover_period = ps_timeout; + } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { + u16 ps_periods = vwrq->value / 1000000; + + if (ps_periods > 255) + ps_periods = 255; + acxlog(L_IOCTL, "setting PS period value to %d periods " + "due to %dus\n", ps_periods, vwrq->value); + priv->ps_listen_interval = ps_periods; + CLEAR_BIT(priv->ps_wakeup_cfg, PS_CFG_WAKEUP_MODE_MASK); + SET_BIT(priv->ps_wakeup_cfg, PS_CFG_WAKEUP_EACH_ITVL); + } + switch (vwrq->flags & IW_POWER_MODE) { + /* FIXME: are we doing the right thing here? */ + case IW_POWER_UNICAST_R: + CLEAR_BIT(priv->ps_options, PS_OPT_STILL_RCV_BCASTS); + break; + case IW_POWER_MULTICAST_R: + SET_BIT(priv->ps_options, PS_OPT_STILL_RCV_BCASTS); + break; + case IW_POWER_ALL_R: + SET_BIT(priv->ps_options, PS_OPT_STILL_RCV_BCASTS); + break; + case IW_POWER_ON: + break; + default: + acxlog(L_IOCTL, "unknown PS mode\n"); + return -EINVAL; + } + + SET_BIT(priv->ps_wakeup_cfg, PS_CFG_ENABLE); + SET_BIT(priv->set_mask, GETSET_POWER_80211); + + return -EINPROGRESS; + +} + + +/*********************************************************************** +*/ +static int +acx_ioctl_get_power( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + acxlog(L_IOCTL, "Get 802.11 Power Save flags = 0x%04X\n", vwrq->flags); + vwrq->disabled = ((priv->ps_wakeup_cfg & PS_CFG_ENABLE) == 0); + if (vwrq->disabled) + return OK; + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + vwrq->value = priv->ps_hangover_period * 1000 / 1024; + vwrq->flags = IW_POWER_TIMEOUT; + } else { + vwrq->value = priv->ps_listen_interval * 1000000; + vwrq->flags = IW_POWER_PERIOD|IW_POWER_RELATIVE; + } + if (priv->ps_options & PS_OPT_STILL_RCV_BCASTS) + SET_BIT(vwrq->flags, IW_POWER_ALL_R); + else + SET_BIT(vwrq->flags, IW_POWER_UNICAST_R); + + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_get_txpow +*----------------------------------------------------------------*/ +static inline int +acx_ioctl_get_txpow( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + vwrq->flags = IW_TXPOW_DBM; + vwrq->disabled = 0; + vwrq->fixed = 1; + vwrq->value = priv->tx_level_dbm; + + acxlog(L_IOCTL, "get txpower:%d dBm\n", priv->tx_level_dbm); + + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_txpow +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_txpow( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + + FN_ENTER; + acxlog(L_IOCTL, "set txpower:%d, disabled:%d, flags:0x%04X\n", + vwrq->value, vwrq->disabled, vwrq->flags); + + acx_sem_lock(priv); + + if (vwrq->disabled != priv->tx_disabled) { + SET_BIT(priv->set_mask, GETSET_TX); /* Tx status needs update later */ + } + + priv->tx_disabled = vwrq->disabled; + if (vwrq->value == -1) { + if (vwrq->disabled) { + priv->tx_level_dbm = 0; + acxlog(L_IOCTL, "disable radio tx\n"); + } else { + priv->tx_level_auto = 1; + acxlog(L_IOCTL, "set tx power auto (NIY)\n"); + } + } else { + priv->tx_level_dbm = vwrq->value <= 20 ? vwrq->value : 20; + priv->tx_level_auto = 0; + acxlog(L_IOCTL, "set txpower=%d dBm\n", priv->tx_level_dbm); + } + SET_BIT(priv->set_mask, GETSET_TXPOWER); + + result = -EINPROGRESS; + + acx_sem_unlock(priv); + + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_get_range +*----------------------------------------------------------------*/ +static int +acx_ioctl_get_range( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + if (dwrq->pointer != NULL) { + struct iw_range *range = (struct iw_range *)extra; + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned int i; + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + range->num_channels = 0; + for (i = 1; i <= 14; i++) { + if (priv->reg_dom_chanmask & (1 << (i - 1))) { + range->freq[range->num_channels].i = i; + range->freq[range->num_channels].m = acx_channel_freq[i - 1] * 100000; + range->freq[range->num_channels++].e = 1; /* MHz values */ + } + } + range->num_frequency = range->num_channels; + + range->min_rts = 0; + range->max_rts = 2312; + /* range->min_frag = 256; + * range->max_frag = 2312; + */ + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->encoding_size[2] = 29; + range->num_encoding_sizes = 3; + range->max_encoding_tokens = 4; + + range->min_pmp = 0; + range->max_pmp = 5000000; + range->min_pmt = 0; + range->max_pmt = 65535 * 1000; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + for (i = 0; i <= IW_MAX_TXPOWER - 1; i++) + range->txpower[i] = 20 * i / (IW_MAX_TXPOWER - 1); + range->num_txpower = IW_MAX_TXPOWER; + range->txpower_capa = IW_TXPOW_DBM; + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 0x9; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 1; + range->max_retry = 255; + + range->r_time_flags = IW_RETRY_LIFETIME; + range->min_r_time = 0; + /* FIXME: lifetime ranges and orders of magnitude are strange?? */ + range->max_r_time = 65535; + + if (CHIPTYPE_ACX111 == priv->chip_type) + range->sensitivity = 3; + else + range->sensitivity = 255; + /* TODO: USB really should have range->sensitivity = 0; */ + + for (i=0; i < priv->rate_supported_len; i++) { + range->bitrate[i] = (priv->rate_supported[i] & ~0x80) * 500000; + /* never happens, but keep it, to be safe: */ + if (range->bitrate[i] == 0) + break; + } + range->num_bitrates = i; + + range->max_qual.qual = 100; + range->max_qual.level = 100; + range->max_qual.noise = 100; + /* TODO: better values */ + range->avg_qual.qual = 90; + range->avg_qual.level = 80; + range->avg_qual.noise = 2; + } + + return OK; +} + + +/*================================================================*/ +/* Private functions */ +/*================================================================*/ + +#if WIRELESS_EXT < 13 +/*---------------------------------------------------------------- +* acx_ioctl_get_iw_priv +* +* +* STATUS: FINISHED +* Comment: I added the monitor mode and changed the stuff below +* to look more like the orinoco driver +*----------------------------------------------------------------*/ +static int +acx_ioctl_get_iw_priv(struct iwreq *iwr) +{ + int result = -EINVAL; + + if (!iwr->u.data.pointer) + return -EINVAL; + result = verify_area(VERIFY_WRITE, iwr->u.data.pointer, + sizeof(acx_ioctl_private_args)); + if (result) + return result; + + iwr->u.data.length = VEC_SIZE(acx_ioctl_private_args); + if (copy_to_user(iwr->u.data.pointer, acx_ioctl_private_args, sizeof(acx_ioctl_private_args)) != 0) + result = -EFAULT; + + return result; +} +#endif + + +/*---------------------------------------------------------------- +* acx_ioctl_get_nick +*----------------------------------------------------------------*/ +static inline int +acx_ioctl_get_nick( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + /* FIXME : consider spinlock here */ + strcpy(extra, priv->nick); + /* FIXME : consider spinlock here */ + + dwrq->length = strlen(extra) + 1; + + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_nick +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_nick( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + + FN_ENTER; + + acx_sem_lock(priv); + + if (dwrq->length > IW_ESSID_MAX_SIZE + 1) { + result = -E2BIG; + goto end_unlock; + } + + /* extra includes trailing \0, so it's ok */ + strcpy(priv->nick, extra); + result = OK; + +end_unlock: + acx_sem_unlock(priv); + + FN_EXIT1(result); + return result; +} + + +/*------------------------------------------------------------------------------ + * acx_ioctl_get_retry + *----------------------------------------------------------------------------*/ +static int +acx_ioctl_get_retry( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned int type = vwrq->flags & IW_RETRY_TYPE; + unsigned int modifier = vwrq->flags & IW_RETRY_MODIFIER; + int result; + + acx_sem_lock(priv); + + /* return the short retry number by default */ + if (type == IW_RETRY_LIFETIME) { + vwrq->flags = IW_RETRY_LIFETIME; + vwrq->value = priv->msdu_lifetime; + } else if (modifier == IW_RETRY_MAX) { + vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + vwrq->value = priv->long_retry; + } else { + vwrq->flags = IW_RETRY_LIMIT; + if (priv->long_retry != priv->short_retry) + SET_BIT(vwrq->flags, IW_RETRY_MIN); + vwrq->value = priv->short_retry; + } + + /* can't be disabled */ + vwrq->disabled = (u8)0; + result = OK; + + acx_sem_unlock(priv); + + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_retry +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_retry( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + + FN_ENTER; + + if (!vwrq) { + result = -EFAULT; + goto end; + } + if (vwrq->disabled) { + result = -EINVAL; + goto end; + } + + acx_sem_lock(priv); + + result = -EINVAL; + if (IW_RETRY_LIMIT == (vwrq->flags & IW_RETRY_TYPE)) { + printk("old retry limits: short %d long %d\n", + priv->short_retry, priv->long_retry); + if (vwrq->flags & IW_RETRY_MAX) { + priv->long_retry = vwrq->value; + } else if (vwrq->flags & IW_RETRY_MIN) { + priv->short_retry = vwrq->value; + } else { + /* no modifier: set both */ + priv->long_retry = vwrq->value; + priv->short_retry = vwrq->value; + } + printk("new retry limits: short %d long %d\n", + priv->short_retry, priv->long_retry); + SET_BIT(priv->set_mask, GETSET_RETRY); + result = -EINPROGRESS; + } + else if (vwrq->flags & IW_RETRY_LIFETIME) { + priv->msdu_lifetime = vwrq->value; + printk("new MSDU lifetime: %d\n", priv->msdu_lifetime); + SET_BIT(priv->set_mask, SET_MSDU_LIFETIME); + result = -EINPROGRESS; + } + + acx_sem_unlock(priv); +end: + FN_EXIT1(result); + return result; +} + + +/******************************* private ioctls ******************************/ + + +/*---------------------------------------------------------------- +* acx_ioctl_set_debug +*----------------------------------------------------------------*/ +#if ACX_DEBUG +static int +acx_ioctl_set_debug( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + unsigned int debug_new = *((unsigned int *)extra); + int result = -EINVAL; + + acxlog(L_ANY, "setting debug from %04X to %04X\n", acx_debug, debug_new); + acx_debug = debug_new; + + result = OK; + return result; + +} +#endif + +/*---------------------------------------------------------------- +* acx_ioctl_list_reg_domain +*----------------------------------------------------------------*/ +extern const u8 reg_domain_ids[]; +extern const u8 reg_domain_ids_len; +static const char * const +reg_domain_strings[] = { + " 1-11 FCC (USA)", + " 1-11 DOC/IC (Canada)", + /* BTW: WLAN use in ETSI is regulated by + * ETSI standard EN 300 328-2 V1.1.2 */ + " 1-13 ETSI (Europe)", + "10-11 Spain", + "10-13 France", + " 14 MKK (Japan)", + " 1-14 MKK1", + " 3-9 Israel (not all firmware versions)", + NULL /* needs to remain as last entry */ +}; + +static int +acx_ioctl_list_reg_domain( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + + int i = 1; + const char * const *entry = reg_domain_strings; + + printk("dom# chan# domain/country\n"); + while (*entry) + printk("%4d %s\n", i++, *entry++); + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_reg_domain +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_reg_domain( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + + FN_ENTER; + + if ((*extra < 1) || ((size_t)*extra > reg_domain_ids_len)) { + result = -EINVAL; + goto end; + } + + acx_sem_lock(priv); + + priv->reg_dom_id = reg_domain_ids[*extra - 1]; + SET_BIT(priv->set_mask, GETSET_REG_DOMAIN); + + result = -EINPROGRESS; + + acx_sem_unlock(priv); +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_get_reg_domain +*----------------------------------------------------------------*/ +static int +acx_ioctl_get_reg_domain( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int dom,i; + + /* no locking */ + dom = priv->reg_dom_id; + + for (i=1; i <= 7; i++) { + if (reg_domain_ids[i-1] == dom) { + acxlog(L_IOCTL, "regulatory domain is currently set " + "to %d (0x%X): %s\n", i, dom, + reg_domain_strings[i-1]); + *extra = i; + break; + } + } + + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_short_preamble +*----------------------------------------------------------------*/ +static const char * const +preamble_modes[] = { + "off", + "on", + "auto (peer capability dependent)", + "unknown mode, error" +}; + +static int +acx_ioctl_set_short_preamble( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int i; + int result; + + FN_ENTER; + + if ((unsigned char)*extra > 2) { + result = -EINVAL; + goto end; + } + + acx_sem_lock(priv); + + priv->preamble_mode = (u8)*extra; + switch (priv->preamble_mode) { + case 0: /* long */ + priv->preamble_cur = 0; + break; + case 1: + /* short, kick incapable peers */ + priv->preamble_cur = 1; + for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { + client_t *clt = &priv->sta_list[i]; + if (!clt->used) continue; + if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) { + clt->used = CLIENT_EMPTY_SLOT_0; + } + } + switch (priv->mode) { + case ACX_MODE_2_STA: + if (priv->ap_client && !priv->ap_client->used) { + /* We kicked our AP :) */ + SET_BIT(priv->set_mask, GETSET_RESCAN); + } + } + break; + case 2: /* auto. short only if all peers are short-capable */ + priv->preamble_cur = 1; + for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { + client_t *clt = &priv->sta_list[i]; + if (!clt->used) continue; + if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) { + priv->preamble_cur = 0; + break; + } + } + break; + } + printk("new short preamble setting: configured %s, active %s\n", + preamble_modes[priv->preamble_mode], + preamble_modes[priv->preamble_cur]); + result = OK; + + acx_sem_unlock(priv); +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_get_short_preamble +*----------------------------------------------------------------*/ +static int +acx_ioctl_get_short_preamble( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + acx_sem_lock(priv); + + printk("current short preamble setting: configured %s, active %s\n", + preamble_modes[priv->preamble_mode], + preamble_modes[priv->preamble_cur]); + + *extra = (char)priv->preamble_mode; + + acx_sem_unlock(priv); + + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_antenna +* +* Comment: TX and RX antenna can be set separately but this function good +* for testing 0-4 bits +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_antenna( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + acx_sem_lock(priv); + + printk("old antenna value: 0x%02X (COMBINED bit mask)\n" + "Rx antenna selection:\n" + "0x00 ant. 1\n" + "0x40 ant. 2\n" + "0x80 full diversity\n" + "0xc0 partial diversity\n" + "0x0f dwell time mask (in units of us)\n" + "Tx antenna selection:\n" + "0x00 ant. 2\n" /* yep, those ARE reversed! */ + "0x20 ant. 1\n" + "new antenna value: 0x%02X\n", + priv->antenna, (u8)*extra); + + priv->antenna = (u8)*extra; + SET_BIT(priv->set_mask, GETSET_ANTENNA); + + acx_sem_unlock(priv); + + return -EINPROGRESS; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_get_antenna +*----------------------------------------------------------------*/ +static int +acx_ioctl_get_antenna( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + /* no locking. it's pointless to lock a single load */ + printk("current antenna value: 0x%02X (COMBINED bit mask)\n" + "Rx antenna selection:\n" + "0x00 ant. 1\n" + "0x40 ant. 2\n" + "0x80 full diversity\n" + "0xc0 partial diversity\n" + "Tx antenna selection:\n" + "0x00 ant. 2\n" /* yep, those ARE reversed! */ + "0x20 ant. 1\n", priv->antenna); + + return 0; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_rx_antenna +* +* +* Arguments: +* 0 = antenna1; 1 = antenna2; 2 = full diversity; 3 = partial diversity +* Comment: Could anybody test which antenna is the external one +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_rx_antenna( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + + FN_ENTER; + + if (*extra > 3) { + result = -EINVAL; + goto end; + } + + printk("old antenna value: 0x%02X\n", priv->antenna); + + acx_sem_lock(priv); + + priv->antenna &= 0x3f; + SET_BIT(priv->antenna, (*extra << 6)); + SET_BIT(priv->set_mask, GETSET_ANTENNA); + printk("new antenna value: 0x%02X\n", priv->antenna); + result = -EINPROGRESS; + + acx_sem_unlock(priv); +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_tx_antenna +* +* +* Arguments: 0 == antenna2; 1 == antenna1; +* Comment: Could anybody test which antenna is the external one +*----------------------------------------------------------------*/ +static int +acx_ioctl_set_tx_antenna( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + + FN_ENTER; + + if (*extra > 1) { + result = -EINVAL; + goto end; + } + + printk("old antenna value: 0x%02X\n", priv->antenna); + + acx_sem_lock(priv); + + priv->antenna &= ~0x30; + SET_BIT(priv->antenna, ((*extra & 0x01) << 5)); + SET_BIT(priv->set_mask, GETSET_ANTENNA); + printk("new antenna value: 0x%02X\n", priv->antenna); + result = -EINPROGRESS; + + acx_sem_unlock(priv); +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_wlansniff +* STATUS: NEW +* can we just remove this in favor of monitor mode? --vda +*----------------------------------------------------------------*/ +static int +acx_ioctl_wlansniff( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned int *params = (unsigned int*)extra; + unsigned int enable = (unsigned int)(params[0] > 0); + int result; + + FN_ENTER; + + acx_sem_lock(priv); + + /* not using printk() here, since it distorts kismet display + * when printk messages activated */ + acxlog(L_IOCTL, "setting monitor to: 0x%02X\n", params[0]); + + switch (params[0]) { + case 0: + priv->netdev->type = ARPHRD_ETHER; + break; + case 1: + priv->netdev->type = ARPHRD_IEEE80211_PRISM; + break; + case 2: + priv->netdev->type = ARPHRD_IEEE80211; + break; + } + + if (params[0]) { + priv->mode = ACX_MODE_MONITOR; + SET_BIT(priv->set_mask, GETSET_MODE); + } + + if (enable) { + priv->channel = params[1]; + SET_BIT(priv->set_mask, GETSET_RX); + } + result = -EINPROGRESS; + + acx_sem_unlock(priv); + + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_unknown11 +* FIXME: looks like some sort of "iwpriv kick_sta MAC" but it's broken +*----------------------------------------------------------------*/ +static int +acx_ioctl_unknown11( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned long flags; + client_t client; + int result; + + acx_sem_lock(priv); + acx_lock(priv, flags); + + acx_l_transmit_disassoc(priv, &client); + result = OK; + + acx_unlock(priv, flags); + acx_sem_unlock(priv); + + return result; +} + + +/*********************************************************************** +** debug helper function to be able to debug various issues relatively easily +*/ +static int +acx_ioctl_dbg_set_masks( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + const unsigned int *params = (unsigned int*)extra; + int result; + + acx_sem_lock(priv); + + acxlog(L_IOCTL, "setting flags in settings mask: " + "get_mask %08X set_mask %08X\n" + "before: get_mask %08X set_mask %08X\n", + params[0], params[1], + priv->get_mask, priv->set_mask); + SET_BIT(priv->get_mask, params[0]); + SET_BIT(priv->set_mask, params[1]); + acxlog(L_IOCTL, "after: get_mask %08X set_mask %08X\n", + priv->get_mask, priv->set_mask); + result = -EINPROGRESS; /* immediately call commit handler */ + + acx_sem_unlock(priv); + + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_rates +* +* This ioctl takes string parameter. Examples: +* iwpriv wlan0 SetRates "1,2" +* use 1 and 2 Mbit rates, both are in basic rate set +* iwpriv wlan0 SetRates "1,2 5,11" +* use 1,2,5.5,11 Mbit rates. 1 and 2 are basic +* iwpriv wlan0 SetRates "1,2 5c,11c" +* same ('c' means 'CCK modulation' and it is a default for 5 and 11) +* iwpriv wlan0 SetRates "1,2 5p,11p" +* use 1,2,5.5,11 Mbit, 1,2 are basic. 5 and 11 are using PBCC +* iwpriv wlan0 SetRates "1,2,5,11 22p" +* use 1,2,5.5,11,22 Mbit. 1,2,5.5 and 11 are basic. 22 is using PBCC +* (this is the maximum acx100 can do (modulo x4 mode)) +* iwpriv wlan0 SetRates "1,2,5,11 22" +* same. 802.11 defines only PBCC modulation +* for 22 and 33 Mbit rates, so there is no ambiguity +* iwpriv wlan0 SetRates "1,2,5,11 6o,9o,12o,18o,24o,36o,48o,54o" +* 1,2,5.5 and 11 are basic. 11g OFDM rates are enabled but +* they are not in basic rate set. 22 Mbit is disabled. +* iwpriv wlan0 SetRates "1,2,5,11 6,9,12,18,24,36,48,54" +* same. OFDM is default for 11g rates except 22 and 33 Mbit, +* thus 'o' is optional +* iwpriv wlan0 SetRates "1,2,5,11 6d,9d,12d,18d,24d,36d,48d,54d" +* 1,2,5.5 and 11 are basic. 11g CCK-OFDM rates are enabled +* (acx111 does not support CCK-OFDM, driver will reject this cmd) +* iwpriv wlan0 SetRates "6,9,12 18,24,36,48,54" +* 6,9,12 are basic, rest of 11g rates is enabled. Using OFDM +*----------------------------------------------------------------*/ +#include "setrate.c" + +/* disallow: 33Mbit (unsupported by hw) */ +/* disallow: CCKOFDM (unsupported by hw) */ +static int +acx111_supported(int mbit, int modulation, void *opaque) +{ + if (mbit==33) return -ENOTSUPP; + if (modulation==DOT11_MOD_CCKOFDM) return -ENOTSUPP; + return OK; +} + +static const u16 +acx111mask[] = { + [DOT11_RATE_1 ] = RATE111_1 , + [DOT11_RATE_2 ] = RATE111_2 , + [DOT11_RATE_5 ] = RATE111_5 , + [DOT11_RATE_11] = RATE111_11, + [DOT11_RATE_22] = RATE111_22, + /* [DOT11_RATE_33] = */ + [DOT11_RATE_6 ] = RATE111_6 , + [DOT11_RATE_9 ] = RATE111_9 , + [DOT11_RATE_12] = RATE111_12, + [DOT11_RATE_18] = RATE111_18, + [DOT11_RATE_24] = RATE111_24, + [DOT11_RATE_36] = RATE111_36, + [DOT11_RATE_48] = RATE111_48, + [DOT11_RATE_54] = RATE111_54, +}; + +static u32 +acx111_gen_mask(int mbit, int modulation, void *opaque) +{ + /* lower 16 bits show selected 1, 2, CCK and OFDM rates */ + /* upper 16 bits show selected PBCC rates */ + u32 m = acx111mask[rate_mbit2enum(mbit)]; + if (modulation==DOT11_MOD_PBCC) + return m<<16; + return m; +} + +static int +verify_rate(u32 rate, int chip_type) +{ + /* never happens. be paranoid */ + if (!rate) return -EINVAL; + + /* disallow: mixing PBCC and CCK at 5 and 11Mbit + ** (can be supported, but needs complicated handling in tx code) */ + if (( rate & ((RATE111_11+RATE111_5)<<16) ) + && ( rate & (RATE111_11+RATE111_5) ) + ) { + return -ENOTSUPP; + } + if (CHIPTYPE_ACX100 == chip_type) { + if ( rate & ~(RATE111_ACX100_COMPAT+(RATE111_ACX100_COMPAT<<16)) ) + return -ENOTSUPP; + } + return 0; +} + +static int +acx_ioctl_set_rates(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned long flags; + int result; + u32 brate = 0, orate = 0; /* basic, operational rate set */ + + FN_ENTER; + + acxlog(L_IOCTL, "set_rates %s\n", extra); + result = fill_ratemasks(extra, &brate, &orate, + acx111_supported, acx111_gen_mask, 0); + if (result) goto end; + SET_BIT(orate, brate); + acxlog(L_IOCTL, "brate %08X orate %08X\n", brate, orate); + + result = verify_rate(brate, priv->chip_type); + if (result) goto end; + result = verify_rate(orate, priv->chip_type); + if (result) goto end; + + acx_sem_lock(priv); + acx_lock(priv, flags); + + priv->rate_basic = brate; + priv->rate_oper = orate; + /* TODO: ideally, we shall monitor highest basic rate + ** which was successfully sent to every peer + ** (say, last we checked, everybody could hear 5.5 Mbits) + ** and use that for bcasts when we want to reach all peers. + ** For beacons, we probably shall use lowest basic rate + ** because we want to reach all *potential* new peers too */ + priv->rate_bcast = 1 << lowest_bit(brate); + if (CHIPTYPE_ACX100 == priv->chip_type) + priv->rate_bcast100 = acx_rate111to100(priv->rate_bcast); + priv->rate_auto = !has_only_one_bit(orate); + acx_l_update_client_rates(priv, orate); + /* TODO: get rid of ratevector, build it only when needed */ + acx_l_update_ratevector(priv); + + /* Do/don't do tx rate fallback; beacon contents and rate */ + SET_BIT(priv->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES); + result = -EINPROGRESS; + + acx_unlock(priv, flags); + acx_sem_unlock(priv); +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_get_phy_chan_busy_percentage +*----------------------------------------------------------------*/ +static int +acx_ioctl_get_phy_chan_busy_percentage( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + struct { /* added ACX_PACKED, not tested --vda */ + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u32 busytime ACX_PACKED; + u32 totaltime ACX_PACKED; + } usage; + + acx_sem_lock(priv); + + if (OK != acx_s_interrogate(priv, &usage, ACX1xx_IE_MEDIUM_USAGE)) + return NOT_OK; + printk("%s: average busy percentage since last invocation: %d%% " + "(microseconds: %u of %u)\n", + dev->name, + 100 * (le32_to_cpu(usage.busytime) / 100) / (le32_to_cpu(usage.totaltime) / 100), + le32_to_cpu(usage.busytime), le32_to_cpu(usage.totaltime)); + /* prevent calculation overflow */ + + acx_sem_unlock(priv); + + return OK; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_ed_threshold +*----------------------------------------------------------------*/ +static inline int +acx_ioctl_set_ed_threshold( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + acx_sem_lock(priv); + + printk("old ED threshold value: %d\n", priv->ed_threshold); + priv->ed_threshold = (unsigned char)*extra; + printk("new ED threshold value: %d\n", (unsigned char)*extra); + SET_BIT(priv->set_mask, GETSET_ED_THRESH); + + acx_sem_unlock(priv); + + return -EINPROGRESS; +} + + +/*---------------------------------------------------------------- +* acx_ioctl_set_cca +*----------------------------------------------------------------*/ +static inline int +acx_ioctl_set_cca( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + + acx_sem_lock(priv); + + printk("old CCA value: 0x%02X\n", priv->cca); + priv->cca = (unsigned char)*extra; + printk("new CCA value: 0x%02X\n", (unsigned char)*extra); + SET_BIT(priv->set_mask, GETSET_CCA); + result = -EINPROGRESS; + + acx_sem_unlock(priv); + + return result; +} + + +/*********************************************************************** +*/ +static const char * const +scan_modes[] = { "active", "passive", "background" }; + +static void +acx_print_scan_params(wlandevice_t *priv, const char* head) +{ + printk("%s: %smode %d (%s), min chan time %dTU, " + "max chan time %dTU, max scan rate byte: %d\n", + priv->netdev->name, head, + priv->scan_mode, scan_modes[priv->scan_mode], + priv->scan_probe_delay, priv->scan_duration, priv->scan_rate); +} + +static int +acx_ioctl_set_scan_params( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + const int *params = (int *)extra; + + acx_sem_lock(priv); + + acx_print_scan_params(priv, "old scan parameters: "); + if ((params[0] != -1) && (params[0] >= 0) && (params[0] <= 2)) + priv->scan_mode = params[0]; + if (params[1] != -1) + priv->scan_probe_delay = params[1]; + if (params[2] != -1) + priv->scan_duration = params[2]; + if ((params[3] != -1) && (params[3] <= 255)) + priv->scan_rate = params[3]; + acx_print_scan_params(priv, "new scan parameters: "); + SET_BIT(priv->set_mask, GETSET_RESCAN); + result = -EINPROGRESS; + + acx_sem_unlock(priv); + + return result; +} + +static int +acx_ioctl_get_scan_params( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + int *params = (int *)extra; + + acx_sem_lock(priv); + + acx_print_scan_params(priv, "current scan parameters: "); + params[0] = priv->scan_mode; + params[1] = priv->scan_probe_delay; + params[2] = priv->scan_duration; + params[3] = priv->scan_rate; + result = OK; + + acx_sem_unlock(priv); + + return result; +} + + +/*********************************************************************** +*/ +static int +acx100_ioctl_set_led_power( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + static const char * const led_modes[] = { "off", "on", "LinkQuality" }; + + wlandevice_t *priv = acx_netdev_priv(dev); + int result; + + acx_sem_lock(priv); + + printk("%s: power LED status: old %d (%s), ", + dev->name, + priv->led_power, + led_modes[priv->led_power]); + priv->led_power = extra[0]; + if (priv->led_power > 2) priv->led_power = 2; + printk("new %d (%s)\n", + priv->led_power, + led_modes[priv->led_power]); + + if (priv->led_power == 2) { + printk("%s: max link quality setting: old %d, ", + dev->name, priv->brange_max_quality); + if (extra[1]) + priv->brange_max_quality = extra[1]; + printk("new %d\n", priv->brange_max_quality); + } + + SET_BIT(priv->set_mask, GETSET_LED_POWER); + + result = -EINPROGRESS; + + acx_sem_unlock(priv); + + return result; +} + + +/*********************************************************************** +*/ +static inline int +acx100_ioctl_get_led_power( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + acx_sem_lock(priv); + + extra[0] = priv->led_power; + if (priv->led_power == 2) + extra[1] = priv->brange_max_quality; + else + extra[1] = -1; + + acx_sem_unlock(priv); + + return OK; +} + + +/*********************************************************************** +*/ +#if WIRELESS_EXT >= 13 +static const iw_handler acx_ioctl_handler[] = +{ + (iw_handler) acx_ioctl_commit, /* SIOCSIWCOMMIT */ + (iw_handler) acx_ioctl_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) acx_ioctl_set_freq, /* SIOCSIWFREQ */ + (iw_handler) acx_ioctl_get_freq, /* SIOCGIWFREQ */ + (iw_handler) acx_ioctl_set_mode, /* SIOCSIWMODE */ + (iw_handler) acx_ioctl_get_mode, /* SIOCGIWMODE */ + (iw_handler) acx_ioctl_set_sens, /* SIOCSIWSENS */ + (iw_handler) acx_ioctl_get_sens, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) acx_ioctl_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ +#if IW_HANDLER_VERSION > 4 + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#else /* IW_HANDLER_VERSION > 4 */ +#ifdef WIRELESS_SPY + (iw_handler) NULL /* acx_ioctl_set_spy FIXME */, /* SIOCSIWSPY */ + (iw_handler) NULL /* acx_ioctl_get_spy FIXME */, /* SIOCGIWSPY */ +#else /* WSPY */ + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ +#endif /* WSPY */ + (iw_handler) NULL, /* [nothing] */ + (iw_handler) NULL, /* [nothing] */ +#endif /* IW_HANDLER_VERSION > 4 */ + (iw_handler) acx_ioctl_set_ap, /* SIOCSIWAP */ + (iw_handler) acx_ioctl_get_ap, /* SIOCGIWAP */ + (iw_handler) NULL, /* [nothing] */ + (iw_handler) acx_ioctl_get_aplist, /* SIOCGIWAPLIST */ +#if WIRELESS_EXT > 13 + (iw_handler) acx_ioctl_set_scan, /* SIOCSIWSCAN */ + (iw_handler) acx_ioctl_get_scan, /* SIOCGIWSCAN */ +#else /* WE > 13 */ + (iw_handler) NULL, /* SIOCSIWSCAN */ + (iw_handler) NULL, /* SIOCGIWSCAN */ +#endif /* WE > 13 */ + (iw_handler) acx_ioctl_set_essid, /* SIOCSIWESSID */ + (iw_handler) acx_ioctl_get_essid, /* SIOCGIWESSID */ + (iw_handler) acx_ioctl_set_nick, /* SIOCSIWNICKN */ + (iw_handler) acx_ioctl_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* [nothing] */ + (iw_handler) NULL, /* [nothing] */ + (iw_handler) acx_ioctl_set_rate, /* SIOCSIWRATE */ + (iw_handler) acx_ioctl_get_rate, /* SIOCGIWRATE */ + (iw_handler) acx_ioctl_set_rts, /* SIOCSIWRTS */ + (iw_handler) acx_ioctl_get_rts, /* SIOCGIWRTS */ + (iw_handler) NULL /* acx_ioctl_set_frag FIXME */, /* SIOCSIWFRAG */ + (iw_handler) NULL /* acx_ioctl_get_frag FIXME */, /* SIOCGIWFRAG */ + (iw_handler) acx_ioctl_set_txpow, /* SIOCSIWTXPOW */ + (iw_handler) acx_ioctl_get_txpow, /* SIOCGIWTXPOW */ + (iw_handler) acx_ioctl_set_retry, /* SIOCSIWRETRY */ + (iw_handler) acx_ioctl_get_retry, /* SIOCGIWRETRY */ + (iw_handler) acx_ioctl_set_encode, /* SIOCSIWENCODE */ + (iw_handler) acx_ioctl_get_encode, /* SIOCGIWENCODE */ + (iw_handler) acx_ioctl_set_power, /* SIOCSIWPOWER */ + (iw_handler) acx_ioctl_get_power, /* SIOCGIWPOWER */ +}; + +static const iw_handler acx_ioctl_private_handler[] = +{ +#if ACX_DEBUG +[ACX100_IOCTL_DEBUG - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_debug, +#else +[ACX100_IOCTL_DEBUG - ACX100_IOCTL] = (iw_handler) NULL, +#endif +[ACX100_IOCTL_SET_PLED - ACX100_IOCTL] = (iw_handler) acx100_ioctl_set_led_power, +[ACX100_IOCTL_GET_PLED - ACX100_IOCTL] = (iw_handler) acx100_ioctl_get_led_power, +[ACX100_IOCTL_SET_RATES - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_rates, +[ACX100_IOCTL_LIST_DOM - ACX100_IOCTL] = (iw_handler) acx_ioctl_list_reg_domain, +[ACX100_IOCTL_SET_DOM - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_reg_domain, +[ACX100_IOCTL_GET_DOM - ACX100_IOCTL] = (iw_handler) acx_ioctl_get_reg_domain, +[ACX100_IOCTL_SET_SCAN_PARAMS - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_scan_params, +[ACX100_IOCTL_GET_SCAN_PARAMS - ACX100_IOCTL] = (iw_handler) acx_ioctl_get_scan_params, +[ACX100_IOCTL_SET_PREAMB - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_short_preamble, +[ACX100_IOCTL_GET_PREAMB - ACX100_IOCTL] = (iw_handler) acx_ioctl_get_short_preamble, +[ACX100_IOCTL_SET_ANT - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_antenna, +[ACX100_IOCTL_GET_ANT - ACX100_IOCTL] = (iw_handler) acx_ioctl_get_antenna, +[ACX100_IOCTL_RX_ANT - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_rx_antenna, +[ACX100_IOCTL_TX_ANT - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_tx_antenna, +[ACX100_IOCTL_SET_PHY_AMP_BIAS - ACX100_IOCTL] = (iw_handler) acx100_ioctl_set_phy_amp_bias, +[ACX100_IOCTL_GET_PHY_CHAN_BUSY - ACX100_IOCTL] = (iw_handler) acx_ioctl_get_phy_chan_busy_percentage, +[ACX100_IOCTL_SET_ED - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_ed_threshold, +[ACX100_IOCTL_SET_CCA - ACX100_IOCTL] = (iw_handler) acx_ioctl_set_cca, +[ACX100_IOCTL_MONITOR - ACX100_IOCTL] = (iw_handler) acx_ioctl_wlansniff, +[ACX100_IOCTL_TEST - ACX100_IOCTL] = (iw_handler) acx_ioctl_unknown11, +[ACX100_IOCTL_DBG_SET_MASKS - ACX100_IOCTL] = (iw_handler) acx_ioctl_dbg_set_masks, +[ACX111_IOCTL_INFO - ACX100_IOCTL] = (iw_handler) acx111_ioctl_info, +[ACX100_IOCTL_DBG_SET_IO - ACX100_IOCTL] = (iw_handler) acx_ioctl_dbg_set_io, +[ACX100_IOCTL_DBG_GET_IO - ACX100_IOCTL] = (iw_handler) acx_ioctl_dbg_get_io, +}; + +const struct iw_handler_def acx_ioctl_handler_def = +{ + .num_standard = VEC_SIZE(acx_ioctl_handler), + .num_private = VEC_SIZE(acx_ioctl_private_handler), + .num_private_args = VEC_SIZE(acx_ioctl_private_args), + .standard = (iw_handler *) acx_ioctl_handler, + .private = (iw_handler *) acx_ioctl_private_handler, + .private_args = (struct iw_priv_args *) acx_ioctl_private_args, +#if WIRELESS_EXT > 15 + .spy_offset = ((void *) (&((struct wlandevice *) NULL)->spy_data) - (void *) NULL), +#endif /* WE > 15 */ +}; + +#endif /* WE >= 13 */ + + +#if WIRELESS_EXT < 13 +/*================================================================*/ +/* Main function */ +/*================================================================*/ +/*---------------------------------------------------------------- +* acx_e_ioctl_old +* +* +* STATUS: FINISHED +* +* Comment: +* This is the *OLD* ioctl handler. +* Make sure to not only place your additions here, but instead mainly +* in the new one (acx_ioctl_handler[])! +*----------------------------------------------------------------*/ +int +acx_e_ioctl_old(netdevice_t *dev, struct ifreq *ifr, int cmd) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result = 0; + struct iwreq *iwr = (struct iwreq *)ifr; + + acxlog(L_IOCTL, "%s cmd = 0x%04X\n", __func__, cmd); + + /* This is the way it is done in the orinoco driver. + * Check to see if device is present. + */ + if (0 == netif_device_present(dev)) { + return -ENODEV; + } + + switch (cmd) { +/* WE 13 and higher will use acx_ioctl_handler_def */ + case SIOCGIWNAME: + /* get name == wireless protocol */ + result = acx_ioctl_get_name(dev, NULL, + (char *)&(iwr->u.name), NULL); + break; + + case SIOCSIWNWID: /* pre-802.11, */ + case SIOCGIWNWID: /* not supported. */ + result = -EOPNOTSUPP; + break; + + case SIOCSIWFREQ: + /* set channel/frequency (Hz) + data can be frequency or channel : + 0-1000 = channel + > 1000 = frequency in Hz */ + result = acx_ioctl_set_freq(dev, NULL, &(iwr->u.freq), NULL); + break; + + case SIOCGIWFREQ: + /* get channel/frequency (Hz) */ + result = acx_ioctl_get_freq(dev, NULL, &(iwr->u.freq), NULL); + break; + + case SIOCSIWMODE: + /* set operation mode */ + result = acx_ioctl_set_mode(dev, NULL, &(iwr->u.mode), NULL); + break; + + case SIOCGIWMODE: + /* get operation mode */ + result = acx_ioctl_get_mode(dev, NULL, &(iwr->u.mode), NULL); + break; + + case SIOCSIWSENS: + /* Set sensitivity */ + result = acx_ioctl_set_sens(dev, NULL, &(iwr->u.sens), NULL); + break; + + case SIOCGIWSENS: + /* Get sensitivity */ + result = acx_ioctl_get_sens(dev, NULL, &(iwr->u.sens), NULL); + break; + +#if WIRELESS_EXT > 10 + case SIOCGIWRANGE: + /* Get range of parameters */ + { + struct iw_range range; + result = acx_ioctl_get_range(dev, NULL, + &(iwr->u.data), (char *)&range); + if (copy_to_user(iwr->u.data.pointer, &range, + sizeof(struct iw_range))) + result = -EFAULT; + } + break; +#endif + + case SIOCGIWPRIV: + result = acx_ioctl_get_iw_priv(iwr); + break; + + /* FIXME: */ + /* case SIOCSIWSPY: */ + /* case SIOCGIWSPY: */ + /* case SIOCSIWTHRSPY: */ + /* case SIOCGIWTHRSPY: */ + + case SIOCSIWAP: + /* set access point by MAC address */ + result = acx_ioctl_set_ap(dev, NULL, &(iwr->u.ap_addr), + NULL); + break; + + case SIOCGIWAP: + /* get access point MAC address */ + result = acx_ioctl_get_ap(dev, NULL, &(iwr->u.ap_addr), + NULL); + break; + + case SIOCGIWAPLIST: + /* get list of access points in range */ + result = acx_ioctl_get_aplist(dev, NULL, &(iwr->u.data), + NULL); + break; + +#if NOT_FINISHED_YET + /* FIXME: do proper interfacing to activate that! */ + case SIOCSIWSCAN: + /* start a station scan */ + result = acx_ioctl_set_scan(iwr, priv); + break; + + case SIOCGIWSCAN: + /* get list of stations found during scan */ + result = acx_ioctl_get_scan(iwr, priv); + break; +#endif + + case SIOCSIWESSID: + /* set ESSID (network name) */ + { + char essid[IW_ESSID_MAX_SIZE+1]; + + if (iwr->u.essid.length > IW_ESSID_MAX_SIZE) + { + result = -E2BIG; + break; + } + if (copy_from_user(essid, iwr->u.essid.pointer, + iwr->u.essid.length)) + { + result = -EFAULT; + break; + } + result = acx_ioctl_set_essid(dev, NULL, + &(iwr->u.essid), essid); + } + break; + + case SIOCGIWESSID: + /* get ESSID */ + { + char essid[IW_ESSID_MAX_SIZE+1]; + if (iwr->u.essid.pointer) + result = acx_ioctl_get_essid(dev, NULL, + &(iwr->u.essid), essid); + if (copy_to_user(iwr->u.essid.pointer, essid, + iwr->u.essid.length)) + result = -EFAULT; + } + break; + + case SIOCSIWNICKN: + /* set nick */ + { + char nick[IW_ESSID_MAX_SIZE+1]; + + if (iwr->u.data.length > IW_ESSID_MAX_SIZE) + { + result = -E2BIG; + break; + } + if (copy_from_user(nick, iwr->u.data.pointer, + iwr->u.data.length)) + { + result = -EFAULT; + break; + } + result = acx_ioctl_set_nick(dev, NULL, + &(iwr->u.data), nick); + } + break; + + case SIOCGIWNICKN: + /* get nick */ + { + char nick[IW_ESSID_MAX_SIZE+1]; + if (iwr->u.data.pointer) + result = acx_ioctl_get_nick(dev, NULL, + &(iwr->u.data), nick); + if (copy_to_user(iwr->u.data.pointer, nick, + iwr->u.data.length)) + result = -EFAULT; + } + break; + + case SIOCSIWRATE: + /* set default bit rate (bps) */ + result = acx_ioctl_set_rate(dev, NULL, &(iwr->u.bitrate), + NULL); + break; + + case SIOCGIWRATE: + /* get default bit rate (bps) */ + result = acx_ioctl_get_rate(dev, NULL, &(iwr->u.bitrate), + NULL); + break; + + case SIOCSIWRTS: + /* set RTS threshold value */ + result = acx_ioctl_set_rts(dev, NULL, &(iwr->u.rts), NULL); + break; + case SIOCGIWRTS: + /* get RTS threshold value */ + result = acx_ioctl_get_rts(dev, NULL, &(iwr->u.rts), NULL); + break; + + /* FIXME: */ + /* case SIOCSIWFRAG: */ + /* case SIOCGIWFRAG: */ + +#if WIRELESS_EXT > 9 + case SIOCGIWTXPOW: + /* get tx power */ + result = acx_ioctl_get_txpow(dev, NULL, &(iwr->u.txpower), + NULL); + break; + + case SIOCSIWTXPOW: + /* set tx power */ + result = acx_ioctl_set_txpow(dev, NULL, &(iwr->u.txpower), + NULL); + break; +#endif + + case SIOCSIWRETRY: + result = acx_ioctl_set_retry(dev, NULL, &(iwr->u.retry), NULL); + break; + + case SIOCGIWRETRY: + result = acx_ioctl_get_retry(dev, NULL, &(iwr->u.retry), NULL); + break; + + case SIOCSIWENCODE: + { + /* set encoding token & mode */ + u8 key[29]; + if (iwr->u.encoding.pointer) { + if (iwr->u.encoding.length > 29) { + result = -E2BIG; + break; + } + if (copy_from_user(key, iwr->u.encoding.pointer, + iwr->u.encoding.length)) { + result = -EFAULT; + break; + } + } + else + if (iwr->u.encoding.length) { + result = -EINVAL; + break; + } + result = acx_ioctl_set_encode(dev, NULL, + &(iwr->u.encoding), key); + } + break; + + case SIOCGIWENCODE: + { + /* get encoding token & mode */ + u8 key[29]; + + result = acx_ioctl_get_encode(dev, NULL, + &(iwr->u.encoding), key); + if (iwr->u.encoding.pointer) { + if (copy_to_user(iwr->u.encoding.pointer, + key, iwr->u.encoding.length)) + result = -EFAULT; + } + } + break; + + /******************** iwpriv ioctls below ********************/ +#if ACX_DEBUG + case ACX100_IOCTL_DEBUG: + acx_ioctl_set_debug(dev, NULL, NULL, iwr->u.name); + break; +#endif + + case ACX100_IOCTL_SET_PLED: + acx100_ioctl_set_led_power(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_GET_PLED: + acx100_ioctl_get_led_power(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_LIST_DOM: + acx_ioctl_list_reg_domain(dev, NULL, NULL, NULL); + break; + + case ACX100_IOCTL_SET_DOM: + acx_ioctl_set_reg_domain(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_GET_DOM: + acx_ioctl_get_reg_domain(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_SET_SCAN_PARAMS: + acx_ioctl_set_scan_params(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_GET_SCAN_PARAMS: + acx_ioctl_get_scan_params(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_SET_PREAMB: + acx_ioctl_set_short_preamble(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_GET_PREAMB: + acx_ioctl_get_short_preamble(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_SET_ANT: + acx_ioctl_set_antenna(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_GET_ANT: + acx_ioctl_get_antenna(dev, NULL, NULL, NULL); + break; + + case ACX100_IOCTL_RX_ANT: + acx_ioctl_set_rx_antenna(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_TX_ANT: + acx_ioctl_set_tx_antenna(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_SET_ED: + acx_ioctl_set_ed_threshold(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_SET_CCA: + acx_ioctl_set_cca(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_MONITOR: /* set sniff (monitor) mode */ + acxlog(L_IOCTL, "%s: IWPRIV monitor\n", dev->name); + + /* can only be done by admin */ + if (!capable(CAP_NET_ADMIN)) { + result = -EPERM; + break; + } + result = acx_ioctl_wlansniff(dev, NULL, NULL, iwr->u.name); + break; + + case ACX100_IOCTL_TEST: + acx_ioctl_unknown11(dev, NULL, NULL, NULL); + break; + + case ACX111_IOCTL_INFO: + acx111_ioctl_info(dev, NULL, NULL, NULL); + break; + + default: + acxlog(L_IOCTL, "wireless ioctl 0x%04X queried " + "but not implemented yet\n", cmd); + result = -EOPNOTSUPP; + break; + } + + if ((priv->dev_state_mask & ACX_STATE_IFACE_UP) && priv->set_mask) { + acx_sem_lock(priv); + acx_s_update_card_settings(priv, 0, 0); + acx_sem_unlock(priv); + } + + /* older WEs don't have a commit handler, + * so we need to fix return code in this case */ + if (-EINPROGRESS == result) + result = 0; + + return result; +} +#endif /* WE < 13 */ diff -puN /dev/null drivers/net/wireless/tiacx/Kconfig --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/Kconfig 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,59 @@ +config TIACX + bool "TI acx100/acx111 802.11b/g wireless chipsets" + depends on NET_RADIO && EXPERIMENTAL + ---help--- + A driver for 802.11b/g wireless cards based on + Texas Instruments acx100 and acx111 chipsets. + + This driver supports Host AP mode that allows + your computer to act as an IEEE 802.11 access point. + This driver is quite new and experimental. + + These chipsets need their firmware loaded at startup. + You will need to provide a firmware image via hotplug. + + Firmware may be in a form of single image 40-100kb in size + (a 'combined' firmware) or two images - main image + (again 40-100kb) and radio image (~10kb or less). + + Firmware images are requested from hotplug using following names: + + tiacx100 - main firmware image for acx100 chipset + tiacx100rNN - radio acx100 firmware for radio type NN + tiacx100cNN - combined acx100 firmware for radio type NN + tiacx111 - main acx111 firmware + tiacx111rNN - radio acx111 firmware for radio type NN + tiacx111cNN - combined acx111 firmware for radio type NN + + Driver will attempt to load combined image first. + If no such image is found, it will try to load main image + and radio image instead. + + Firmware files are not covered by GPL and are not distributed + with this driver for legal reasons. + + Texas Instruments did not take part in development of this driver + in any way, shape or form. + +config TIACX_PCI + tristate "TI acx100/acx111 802.11b/g PCI" + depends on PCI && TIACX && FW_LOADER + ---help--- + A driver for PCI and CardBus incarnations of acx100 and acx111. + + This driver is quite new and experimental. + + The driver can be compiled as a module and will be named "tiacx_pci". + +config TIACX_USB + tristate "TI acx100/acx111 802.11b/g USB" + depends on USB && TIACX && FW_LOADER + ---help--- + A driver for USB incarnation of acx100 and acx111. + + There is only one currently known device in this category, + D-Link DWL-120+, but newer devices seem to be on the horizon. + + This driver is quite new and experimental. + + The driver can be compiled as a module and will be named "tiacx_usb". diff -puN /dev/null drivers/net/wireless/tiacx/Makefile --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/Makefile 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,6 @@ +tiacx_pci-objs := wlan.o conv.o helper2.o ioctl.o pci.o pci_helper.o + +tiacx_usb-objs := wlan.o conv.o helper2.o ioctl.o usb.o usb_helper.o + +obj-$(CONFIG_TIACX_PCI) += tiacx_pci.o +obj-$(CONFIG_TIACX_USB) += tiacx_usb.o diff -puN /dev/null drivers/net/wireless/tiacx/pci.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/pci.c 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,4777 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ +#define ACX_PCI 1 + +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#endif +#include +#include +#include +#include + +#include "acx.h" + + +/********************************************************************/ +/* Module information */ +/********************************************************************/ +MODULE_AUTHOR("The ACX100 Open Source Driver development team"); +MODULE_DESCRIPTION("Driver for TI ACX1xx based wireless cards (CardBus/PCI/USB)"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual MPL/GPL"); +#endif + + +/*================================================================*/ +/* Local Constants */ +#define PCI_TYPE (PCI_USES_MEM | PCI_ADDR0 | PCI_NO_ACPI_WAKE) +#define PCI_ACX100_REGION1 0x01 +#define PCI_ACX100_REGION1_SIZE 0x1000 /* Memory size - 4K bytes */ +#define PCI_ACX100_REGION2 0x02 +#define PCI_ACX100_REGION2_SIZE 0x10000 /* Memory size - 64K bytes */ + +#define PCI_ACX111_REGION1 0x00 +#define PCI_ACX111_REGION1_SIZE 0x2000 /* Memory size - 8K bytes */ +#define PCI_ACX111_REGION2 0x01 +#define PCI_ACX111_REGION2_SIZE 0x20000 /* Memory size - 128K bytes */ + +/* Texas Instruments Vendor ID */ +#define PCI_VENDOR_ID_TI 0x104c + +/* ACX100 22Mb/s WLAN controller */ +#define PCI_DEVICE_ID_TI_TNETW1100A 0x8400 +#define PCI_DEVICE_ID_TI_TNETW1100B 0x8401 + +/* ACX111 54Mb/s WLAN controller */ +#define PCI_DEVICE_ID_TI_TNETW1130 0x9066 + +/* PCI Class & Sub-Class code, Network-'Other controller' */ +#define PCI_CLASS_NETWORK_OTHERS 0x280 + + +/*================================================================*/ +/* Local Static Definitions */ +#define CARD_EEPROM_ID_SIZE 6 +#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */ + +#if ACX_DEBUG +unsigned int acx_debug = L_ASSOC|L_INIT; +#endif + +#if SEPARATE_DRIVER_INSTANCES +int card = 0; +#endif /* SEPARATE_DRIVER_INSTANCES */ + +#if USE_FW_LOADER_LEGACY +char *firmware_dir; +#endif + +static const char name_acx100[] = "ACX100"; +static const char name_tnetw1100a[] = "TNETW1100A"; +static const char name_tnetw1100b[] = "TNETW1100B"; + +static const char name_acx111[] = "ACX111"; +static const char name_tnetw1130[] = "TNETW1130"; + +static const struct pci_device_id +acx_pci_id_tbl[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_TI, + .device = PCI_DEVICE_ID_TI_TNETW1100A, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = CHIPTYPE_ACX100, + }, + { + .vendor = PCI_VENDOR_ID_TI, + .device = PCI_DEVICE_ID_TI_TNETW1100B, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = CHIPTYPE_ACX100, + }, + { + .vendor = PCI_VENDOR_ID_TI, + .device = PCI_DEVICE_ID_TI_TNETW1130, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = CHIPTYPE_ACX111, + }, + { + .vendor = 0, + .device = 0, + .subvendor = 0, + .subdevice = 0, + .driver_data = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, acx_pci_id_tbl); + +static void acx_l_disable_irq(wlandevice_t *priv); +static void acx_l_enable_irq(wlandevice_t *priv); +static int acx_e_probe_pci(struct pci_dev *pdev, + const struct pci_device_id *id); +static void acx_e_remove_pci(struct pci_dev *pdev); + +#ifdef CONFIG_PM +static int acx_e_suspend(struct pci_dev *pdev, u32 state); +static int acx_e_resume(struct pci_dev *pdev); +#endif + +static void acx_i_tx_timeout(netdevice_t *dev); +static struct net_device_stats *acx_e_get_stats(netdevice_t *dev); +static struct iw_statistics *acx_e_get_wireless_stats(netdevice_t *dev); + +static irqreturn_t acx_i_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void acx_i_set_multicast_list(netdevice_t *dev); + +static int acx_e_open(netdevice_t *dev); +static int acx_e_close(netdevice_t *dev); +static void acx_s_up(netdevice_t *dev); +static void acx_s_down(netdevice_t *dev); + + +/* FIXME: checks should be removed once driver is included in the kernel */ +#ifndef __devexit_p +#warning *** your kernel is EXTREMELY old since it does not even know about +#warning __devexit_p - this driver could easily FAIL to work, so better +#warning upgrade your kernel! *** +#define __devexit_p(x) x +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11) +/* pci_name() got introduced at start of 2.6.x, + * got mandatory (slot_name member removed) in 2.6.11-bk1 */ +#define pci_name(x) x->slot_name +#endif + +static struct pci_driver acx_pci_drv_id = { + .name = "acx_pci", + .id_table = acx_pci_id_tbl, + .probe = acx_e_probe_pci, + .remove = __devexit_p(acx_e_remove_pci), +#ifdef CONFIG_PM + .suspend = acx_e_suspend, + .resume = acx_e_resume +#endif /* CONFIG_PM */ +}; + +typedef struct acx_device { + netdevice_t *newest; +} acx_device_t; + +/* if this driver was only about PCI devices, then we probably wouldn't + * need this linked list. + * But if we want to register ALL kinds of devices in one global list, + * then we need it and need to maintain it properly. */ +static struct acx_device root_acx_dev = { + .newest = NULL, +}; +DECLARE_MUTEX(root_acx_dev_sem); + + +/*********************************************************************** +** Register access +*/ +/* If INLINE_IO is not defined, it means user wants to have out-of-line +** acx_r/w_regNN() functions. We are doing this with #define magic +** in acx_inline.h: */ +#ifndef INLINE_IO +#define INLINE_IO /* defined to nothing */ +#include "acx_inline.h" +#endif + + +/*********************************************************************** +** EEPROM and PHY read/write helpers +*/ +/*********************************************************************** +** acx_read_eeprom_offset +** +** Function called to read an octet in the EEPROM. +** +** This function is used by acx_probe_pci to check if the +** connected card is a legal one or not. +** +** Arguments: +** priv ptr to wlandevice structure +** addr address to read in the EEPROM +** charbuf ptr to a char. This is where the read octet +** will be stored +** +** Returns: +** zero (0) - failed +** one (1) - success +** +** NOT ADAPTED FOR ACX111!! +*/ +int +acx_read_eeprom_offset(wlandevice_t *priv, u32 addr, u8 *charbuf) +{ + int result = NOT_OK; + int count; + + acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0); + acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr); + acx_write_flush(priv); + acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 2); + + count = 0xffff; + while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) { + /* scheduling away instead of CPU burning loop + * doesn't seem to work here at all: + * awful delay, sometimes also failure. + * Doesn't matter anyway (only small delay). */ + if (unlikely(!--count)) { + printk("%s: timeout waiting for EEPROM read\n", + priv->netdev->name); + goto fail; + } + } + + *charbuf = acx_read_reg8(priv, IO_ACX_EEPROM_DATA); + acxlog(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf); + result = OK; + +fail: + return result; +} + + +/*********************************************************************** +** Dummy EEPROM read? why?! +*/ +static int +acx_read_eeprom_area(wlandevice_t *priv) +{ + int offs; + u8 tmp[0x3b]; + + for (offs = 0x8c; offs < 0xb9; offs++) { + acx_read_eeprom_offset(priv, offs, &tmp[offs - 0x8c]); + } + return OK; +} + + +/*********************************************************************** +** We don't lock hw accesses here since we never r/w eeprom in IRQ +** Note: this function sleeps only because of GFP_KERNEL alloc +*/ +#ifdef UNUSED +int +acx_s_write_eeprom_offset(wlandevice_t *priv, u32 addr, u32 len, const u8 *charbuf) +{ + u8 *data_verify = NULL; + unsigned long flags; + int count, i; + int result = NOT_OK; + u16 gpio_orig; + + printk("acx: WARNING! I would write to EEPROM now. " + "Since I really DON'T want to unless you know " + "what you're doing (THIS CODE WILL PROBABLY " + "NOT WORK YET!), I will abort that now. And " + "definitely make sure to make a " + "/proc/driver/acx_wlan0_eeprom backup copy first!!! " + "(the EEPROM content includes the PCI config header!! " + "If you kill important stuff, then you WILL " + "get in trouble and people DID get in trouble already)\n"); + return OK; + + FN_ENTER; + + data_verify = kmalloc(len, GFP_KERNEL); + if (!data_verify) { + goto end; + } + + /* first we need to enable the OE (EEPROM Output Enable) GPIO line + * to be able to write to the EEPROM. + * NOTE: an EEPROM writing success has been reported, + * but you probably have to modify GPIO_OUT, too, + * and you probably need to activate a different GPIO + * line instead! */ + gpio_orig = acx_read_reg16(priv, IO_ACX_GPIO_OE); + acx_write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig & ~1); + acx_write_flush(priv); + + /* ok, now start writing the data out */ + for (i = 0; i < len; i++) { + acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0); + acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i); + acx_write_reg32(priv, IO_ACX_EEPROM_DATA, *(charbuf + i)); + acx_write_flush(priv); + acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 1); + + while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) { + if (unlikely(++count > 0xffff)) { + printk("WARNING, DANGER!!! " + "Timeout waiting for EEPROM write\n"); + goto end; + } + } + } + + /* disable EEPROM writing */ + acx_write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig); + acx_write_flush(priv); + + /* now start a verification run */ + count = 0xffff; + for (i = 0; i < len; i++) { + acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0); + acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i); + acx_write_flush(priv); + acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 2); + + while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) { + if (unlikely(!--count)) { + printk("timeout waiting for EEPROM read\n"); + goto end; + } + } + + data_verify[i] = acx_read_reg16(priv, IO_ACX_EEPROM_DATA); + } + + if (0 == memcmp(charbuf, data_verify, len)) + result = OK; /* read data matches, success */ + +end: + kfree(data_verify); + FN_EXIT1(result); + return result; +} +#endif /* UNUSED */ + + +/*********************************************************************** +** acx_s_read_phy_reg +** +** Messing with rx/tx disabling and enabling here +** (acx_write_reg32(priv, IO_ACX_ENABLE, 0b000000xx)) kills traffic +*/ +int +acx_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf) +{ + int result = NOT_OK; + int count; + + FN_ENTER; + + acx_write_reg32(priv, IO_ACX_PHY_ADDR, reg); + acx_write_flush(priv); + acx_write_reg32(priv, IO_ACX_PHY_CTL, 2); + + count = 0xffff; + while (acx_read_reg32(priv, IO_ACX_PHY_CTL)) { + /* scheduling away instead of CPU burning loop + * doesn't seem to work here at all: + * awful delay, sometimes also failure. + * Doesn't matter anyway (only small delay). */ + if (unlikely(!--count)) { + printk("%s: timeout waiting for phy read\n", + priv->netdev->name); + *charbuf = 0; + goto fail; + } + } + + acxlog(L_DEBUG, "count was %u\n", count); + *charbuf = acx_read_reg8(priv, IO_ACX_PHY_DATA); + + acxlog(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg); + result = OK; + goto fail; /* silence compiler warning */ +fail: + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +*/ +int +acx_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value) +{ + FN_ENTER; + + /* FIXME: we didn't use 32bit access here since mprusko said that + * it results in distorted sensitivity on his card (huh!?!? + * doesn't happen with my setup...) + * But with the access reordering and flushing it + * shouldn't happen any more... + * FIXME: which radio is in the problematic card? My working one + * is 0x11 */ + acx_write_reg32(priv, IO_ACX_PHY_DATA, value); + acx_write_reg32(priv, IO_ACX_PHY_ADDR, reg); + acx_write_flush(priv); + acx_write_reg32(priv, IO_ACX_PHY_CTL, 1); + acx_write_flush(priv); + acxlog(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg); + + FN_EXIT1(OK); + return OK; +} + + +#define NO_AUTO_INCREMENT 1 + +/*********************************************************************** +** acx_s_write_fw +** +** Write the firmware image into the card. +** +** Arguments: +** priv wlan device structure +** apfw_image firmware image. +** +** Returns: +** 1 firmware image corrupted +** 0 success +*/ +static int +acx_s_write_fw(wlandevice_t *priv, const firmware_image_t *apfw_image, u32 offset) +{ + int len, size; + u32 sum, v32; + /* we skip the first four bytes which contain the control sum */ + const u8 *image = (u8*)apfw_image + 4; + + /* start the image checksum by adding the image size value */ + sum = image[0]+image[1]+image[2]+image[3]; + image += 4; + + acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0); + +#if NO_AUTO_INCREMENT + acxlog(L_INIT, "not using auto increment for firmware loading\n"); + acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ +#else + acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ + acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ + acx_write_flush(priv); +#endif + + len = 0; + size = le32_to_cpu(apfw_image->size) & (~3); + + while (likely(len < size)) { + v32 = be32_to_cpu(*(u32*)image); + sum += image[0]+image[1]+image[2]+image[3]; + image += 4; + len += 4; + +#if NO_AUTO_INCREMENT + acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4); + acx_write_flush(priv); +#endif + acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, v32); + } + + acxlog(L_DEBUG, "%s: firmware written\n", __func__); + + /* compare our checksum with the stored image checksum */ + return (sum != le32_to_cpu(apfw_image->chksum)); +} + + +/*********************************************************************** +** acx_s_validate_fw +** +** Compare the firmware image given with +** the firmware image written into the card. +** +** Arguments: +** priv wlan device structure +** apfw_image firmware image. +** +** Returns: +** NOT_OK firmware image corrupted or not correctly written +** OK success +*/ +static int +acx_s_validate_fw(wlandevice_t *priv, const firmware_image_t *apfw_image, + u32 offset) +{ + u32 v32, w32, sum; + int len, size; + int result = OK; + /* we skip the first four bytes which contain the control sum */ + const u8 *image = (u8*)apfw_image + 4; + + /* start the image checksum by adding the image size value */ + sum = image[0]+image[1]+image[2]+image[3]; + image += 4; + + acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0); + +#if NO_AUTO_INCREMENT + acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ +#else + acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ + acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ +#endif + + len = 0; + size = le32_to_cpu(apfw_image->size) & (~3); + + while (likely(len < size)) { + v32 = be32_to_cpu(*(u32*)image); + image += 4; + len += 4; + +#if NO_AUTO_INCREMENT + acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4); +#endif + w32 = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA); + + if (unlikely(w32 != v32)) { + printk("acx: FATAL: firmware upload: " + "data parts at offset %d don't match (0x%08X vs. 0x%08X)! " + "I/O timing issues or defective memory, with DWL-xx0+? " + "ACX_IO_WIDTH=16 may help. Please report\n", + len, v32, w32); + result = NOT_OK; + break; + } + + sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24); + } + + /* sum control verification */ + if (result != NOT_OK) { + if (sum != le32_to_cpu(apfw_image->chksum)) { + printk("acx: FATAL: firmware upload: " + "checksums don't match!\n"); + result = NOT_OK; + } + } + + return result; +} + + +/*********************************************************************** +** acx_s_upload_fw +** +** Arguments: +** wlandevice: private device that contains card device +** Returns: +** NOT_OK: failed +** OK: success +** Call context: +** acx_reset_dev +*/ +static int +acx_s_upload_fw(wlandevice_t *priv) +{ + firmware_image_t *apfw_image = NULL; + int res = NOT_OK; + int try; + u32 size; + char filename[sizeof("tiacx1NNcNN")]; + + FN_ENTER; + + /* Try combined, then main image */ + priv->need_radio_fw = 0; + sprintf(filename, "tiacx1%02dc%02X", + (priv->chip_type == CHIPTYPE_ACX111)*11, + priv->radio_type); + apfw_image = acx_s_read_fw(&priv->pdev->dev, filename, &size); + if (!apfw_image) { + priv->need_radio_fw = 1; + filename[sizeof("tiacx1NN")-1] = '\0'; + apfw_image = acx_s_read_fw(&priv->pdev->dev, filename, &size); + if (!apfw_image) { + FN_EXIT1(NOT_OK); + return NOT_OK; + } + } + + for (try = 1; try <= 5; try++) { + res = acx_s_write_fw(priv, apfw_image, 0); + acxlog(L_DEBUG|L_INIT, "acx_write_fw (main/combined):%d\n", res); + if (OK == res) { + res = acx_s_validate_fw(priv, apfw_image, 0); + acxlog(L_DEBUG|L_INIT, "acx_validate_fw " + "(main/combined):%d\n", res); + } + + if (OK == res) { + SET_BIT(priv->dev_state_mask, ACX_STATE_FW_LOADED); + break; + } + printk("acx: firmware upload attempt #%d FAILED, " + "retrying...\n", try); + acx_s_msleep(1000); /* better wait for a while... */ + } + + vfree(apfw_image); + + FN_EXIT1(res); + return res; +} + + +/*********************************************************************** +** acx_s_upload_radio +** +** Uploads the appropriate radio module firmware +** into the card. +*/ +int +acx_s_upload_radio(wlandevice_t *priv) +{ + acx_ie_memmap_t mm; + firmware_image_t *radio_image = NULL; + acx_cmd_radioinit_t radioinit; + int res = NOT_OK; + int try; + u32 offset; + u32 size; + char filename[sizeof("tiacx1NNrNN")]; + + if (!priv->need_radio_fw) return OK; + + FN_ENTER; + + acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP); + offset = le32_to_cpu(mm.CodeEnd); + + sprintf(filename, "tiacx1%02dr%02X", + (priv->chip_type == CHIPTYPE_ACX111)*11, + priv->radio_type); + radio_image = acx_s_read_fw(&priv->pdev->dev, filename, &size); + if (!radio_image) { + printk("acx: can't load radio module '%s'\n", filename); + goto fail; + } + + acx_s_issue_cmd(priv, ACX1xx_CMD_SLEEP, NULL, 0); + + for (try = 1; try <= 5; try++) { + res = acx_s_write_fw(priv, radio_image, offset); + acxlog(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res); + if (OK == res) { + res = acx_s_validate_fw(priv, radio_image, offset); + acxlog(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res); + } + + if (OK == res) + break; + printk("acx: radio firmware upload attempt #%d FAILED, " + "retrying...\n", try); + acx_s_msleep(1000); /* better wait for a while... */ + } + + acx_s_issue_cmd(priv, ACX1xx_CMD_WAKE, NULL, 0); + radioinit.offset = cpu_to_le32(offset); + /* no endian conversion needed, remains in card CPU area: */ + radioinit.len = radio_image->size; + + vfree(radio_image); + + if (OK != res) + goto fail; + + /* will take a moment so let's have a big timeout */ + acx_s_issue_cmd_timeo(priv, ACX1xx_CMD_RADIOINIT, + &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000)); + + res = acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP); + if (OK != res) { + printk("%s: error reading memory map\n", priv->netdev->name); + } +fail: + FN_EXIT1(res); + return res; +} + + +/*********************************************************************** +** acx_l_reset_mac +** +** Arguments: +** wlandevice: private device that contains card device +** Side effects: +** MAC will be reset +** Call context: +** acx_reset_dev +** Comment: +** resets onboard acx100 MAC +** +** Requires lock to be taken +*/ +static void +acx_l_reset_mac(wlandevice_t *priv) +{ + u16 temp; + + FN_ENTER; + + /* halt eCPU */ + temp = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1; + acx_write_reg16(priv, IO_ACX_ECPU_CTRL, temp); + + /* now do soft reset of eCPU */ + temp = acx_read_reg16(priv, IO_ACX_SOFT_RESET) | 0x1; + acxlog(L_DEBUG, "%s: enable soft reset...\n", __func__); + acx_write_reg16(priv, IO_ACX_SOFT_RESET, temp); + acx_write_flush(priv); + + /* now reset bit again */ + acxlog(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__); + /* deassert eCPU reset */ + acx_write_reg16(priv, IO_ACX_SOFT_RESET, temp & ~0x1); + + /* now start a burst read from initial flash EEPROM */ + temp = acx_read_reg16(priv, IO_ACX_EE_START) | 0x1; + acx_write_reg16(priv, IO_ACX_EE_START, temp); + acx_write_flush(priv); + + FN_EXIT0; +} + + +/*********************************************************************** +** acx_s_verify_init +*/ +static int +acx_s_verify_init(wlandevice_t *priv) +{ + int result = NOT_OK; + int timer; + + FN_ENTER; + + for (timer = 40; timer > 0; timer--) { + u16 irqstat = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES); + if (irqstat & HOST_INT_FCS_THRESHOLD) { + result = OK; + acx_write_reg16(priv, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD); + break; + } + /* HZ / 50 resulted in 24 schedules for ACX100 on my machine, + * so better schedule away longer for greater efficiency, + * decrease loop count */ + acx_s_msleep(50); + } + + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx_cmd_status_str +*/ +static const char* +acx_cmd_status_str(unsigned int state) +{ + static const char * const cmd_error_strings[] = { + "Idle", + "Success", + "Unknown Command", + "Invalid Information Element", + "Channel rejected", + "Channel invalid in current regulatory domain", + "MAC invalid", + "Command rejected (read-only information element)", + "Command rejected", + "Already asleep", + "TX in progress", + "Already awake", + "Write only", + "RX in progress", + "Invalid parameter", + "Scan in progress", + "Failed" + }; + return state < VEC_SIZE(cmd_error_strings) ? + cmd_error_strings[state] : "UNKNOWN REASON"; +} + + +/*********************************************************************** +** A few low-level helpers +** +** Note: these functions are not protected by lock +** and thus are never allowed to be called from IRQ. +** Also they must not race with fw upload which uses same hw regs +*/ + +/*********************************************************************** +** acx_read_info_status +*/ +/* Info mailbox format: +2 bytes: type +2 bytes: status +more bytes may follow + docs say about status: + 0x0000 info available (set by hw) + 0x0001 information received (must be set by host) + 0x1000 info available, mailbox overflowed (messages lost) (set by hw) + but in practice we've seen: + 0x9000 when we did not set status to 0x0001 on prev message + 0x1001 when we did set it + 0x0000 was never seen + conclusion: this is really a bitfield: + 0x1000 is 'info available' bit + 'mailbox overflowed' bit is 0x8000, not 0x1000 + value of 0x0000 probably means that there is no message at all + P.S. I dunno how in hell hw is supposed to notice that messages are lost - + it does NOT clear bit 0x0001, and this bit will probably stay forever set + after we set it once. Let's hope this will be fixed in firmware someday +*/ +static void +acx_read_info_status(wlandevice_t *priv) +{ + u32 value; + + acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0); + acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); + + acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, + acx_read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS)); + + /* make sure we only read the data once all cfg registers are written: */ + acx_write_flush(priv); + value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA); + + priv->info_type = (u16)value; + priv->info_status = (value >> 16); + + /* inform hw that we have read this info message */ + acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, priv->info_type | 0x00010000); + acx_write_flush(priv); + /* now bother hw to notice it: */ + acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_INFOACK); + acx_write_flush(priv); + + acxlog(L_CTL, "info_type 0x%04X, info_status 0x%04X\n", + priv->info_type, priv->info_status); +} + + +/*********************************************************************** +** acx_write_cmd_type_or_status +*/ +static void +acx_write_cmd_type_or_status(wlandevice_t *priv, u32 val) +{ + acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0); + acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */ + + acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, + acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS)); + + /* make sure we only write the data once all config registers are written */ + acx_write_flush(priv); + acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, val); + acx_write_flush(priv); +} +static inline void +acx_write_cmd_type(wlandevice_t *priv, u32 val) +{ + acx_write_cmd_type_or_status(priv, val); +} +static inline void +acx_write_cmd_status(wlandevice_t *priv, u32 val) +{ + acx_write_cmd_type_or_status(priv, val<<16); +} + + +/*********************************************************************** +** acx_read_cmd_status +*/ +static void +acx_read_cmd_status(wlandevice_t *priv) +{ + u32 value; + + acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0); + acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */ + + acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, + acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS)); + + /* make sure we only read the data once all config registers are written */ + acx_write_flush(priv); + value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA); + + priv->cmd_type = (u16)value; + priv->cmd_status = (value >> 16); + + acxlog(L_CTL, "cmd_type 0x%04X, cmd_status 0x%04X [%s]\n", + priv->cmd_type, priv->cmd_status, + acx_cmd_status_str(priv->cmd_status)); +} + + +/*********************************************************************** +** acx_s_reset_dev +** +** Arguments: +** netdevice that contains the wlandevice priv variable +** Returns: +** NOT_OK on fail +** OK on success +** Side effects: +** device is hard reset +** Call context: +** acx_probe_pci +** Comment: +** This resets the acx100 device using low level hardware calls +** as well as uploads and verifies the firmware to the card +*/ +static int +acx_s_reset_dev(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + const char* msg = ""; + unsigned long flags; + int result = NOT_OK; + u16 hardware_info; + u16 ecpu_ctrl; + + FN_ENTER; + + /* we're doing a reset, so hardware is unavailable */ + + /* reset the device to make sure the eCPU is stopped + * to upload the firmware correctly */ + + acx_lock(priv, flags); + + acx_l_reset_mac(priv); + + ecpu_ctrl = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) & 1; + if (!ecpu_ctrl) { + msg = "eCPU is already running. "; + goto fail_unlock; + } + +#if WE_DONT_NEED_THAT_DO_WE + if (acx_read_reg16(priv, IO_ACX_SOR_CFG) & 2) { + /* eCPU most likely means "embedded CPU" */ + msg = "eCPU did not start after boot from flash. "; + goto fail_unlock; + } + + /* check sense on reset flags */ + if (acx_read_reg16(priv, IO_ACX_SOR_CFG) & 0x10) { + printk("%s: eCPU did not start after boot (SOR), " + "is this fatal?\n", dev->name); + } +#endif + /* scan, if any, is stopped now, setting corresponding IRQ bit */ + priv->irq_status |= HOST_INT_SCAN_COMPLETE; + + acx_unlock(priv, flags); + + /* without this delay acx100 may fail to report hardware_info + ** (see below). Most probably eCPU runs some init code */ + acx_s_msleep(10); + + /* Need to know radio type before fw load */ + hardware_info = acx_read_reg16(priv, IO_ACX_EEPROM_INFORMATION); + priv->form_factor = hardware_info & 0xff; + priv->radio_type = hardware_info >> 8; + + /* load the firmware */ + if (OK != acx_s_upload_fw(priv)) + goto fail; + + acx_s_msleep(10); + + /* now start eCPU by clearing bit */ + acxlog(L_DEBUG, "booted eCPU up and waiting for completion...\n"); + acx_write_reg16(priv, IO_ACX_ECPU_CTRL, ecpu_ctrl & ~0x1); + + /* wait for eCPU bootup */ + if (OK != acx_s_verify_init(priv)) { + msg = "timeout waiting for eCPU. "; + goto fail; + } + + acxlog(L_DEBUG, "eCPU has woken up, card is ready to be configured\n"); + + if (priv->chip_type == CHIPTYPE_ACX111) { + acxlog(L_DEBUG, "cleaning up cmd mailbox access area\n"); + acx_write_cmd_status(priv, 0); + acx_read_cmd_status(priv); + if (priv->cmd_status) { + msg = "error cleaning cmd mailbox area. "; + goto fail; + } + } + + /* TODO what is this one doing ?? adapt for acx111 */ + if ((OK != acx_read_eeprom_area(priv)) + && (CHIPTYPE_ACX100 == priv->chip_type)) { + /* does "CIS" mean "Card Information Structure"? + * If so, then this would be a PCMCIA message... + */ + msg = "CIS error. "; + goto fail; + } + + result = OK; + FN_EXIT1(result); + return result; + +/* Finish error message. Indicate which function failed */ +fail_unlock: + acx_unlock(priv, flags); +fail: + printk("acx: %sreset_dev() FAILED\n", msg); + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx_init_mboxes +*/ +void +acx_init_mboxes(wlandevice_t *priv) +{ + u32 cmd_offs, info_offs; + + FN_ENTER; + + cmd_offs = acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS); + info_offs = acx_read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS); + priv->cmd_area = (u8 *)priv->iobase2 + cmd_offs + 0x4; + priv->info_area = (u8 *)priv->iobase2 + info_offs + 0x4; + acxlog(L_DEBUG, "iobase2=%p\n" + "cmd_mbox_offset=%X cmd_area=%p\n" + "info_mbox_offset=%X info_area=%p\n", + priv->iobase2, + cmd_offs, priv->cmd_area, + info_offs, priv->info_area); + + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_s_issue_cmd_timeo +* Excecutes a command in the command mailbox +* +* Arguments: +* *pcmdparam = an pointer to the data. The data mustn't include +* the 4 byte command header! +* +* NB: we do _not_ take lock inside, so be sure to not touch anything +* which may interfere with IRQ handler operation +* +* TODO: busy wait is a bit silly, so: +* 1) stop doing many iters - go to sleep after first +* 2) go to waitqueue based approach: wait, not poll! +*----------------------------------------------------------------*/ +#undef FUNC +#define FUNC "issue_cmd" + +#if !ACX_DEBUG +int +acx_s_issue_cmd_timeo( + wlandevice_t *priv, + unsigned int cmd, + void *pcmdparam, + unsigned paramlen, + unsigned timeout) +{ +#else +int +acx_s_issue_cmd_timeo_debug( + wlandevice_t *priv, + unsigned cmd, + void *pcmdparam, + unsigned paramlen, + unsigned timeout, + const char* cmdstr) +{ + unsigned long start = jiffies; +#endif + unsigned counter; + int result = NOT_OK; + u16 irqtype; + u16 cmd_status; + + FN_ENTER; + + acxlog(L_CTL, FUNC"(cmd:%s,timeout:%ums)\n", cmdstr, timeout/100); + + if (!(priv->dev_state_mask & ACX_STATE_FW_LOADED)) { + acxlog(L_CTL, "firmware not loaded yet, " + "cannot execute command!\n"); + goto done; + } + + if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) { + printk("input pdr (len=%u):\n", paramlen); + acx_dump_bytes(pcmdparam, paramlen); + } + + /* wait for firmware to become idle for our command submission */ + counter = 199; /* in ms */ + do { + acx_read_cmd_status(priv); + /* Test for IDLE state */ + if (!priv->cmd_status) + break; + if (counter % 10 == 0) { + /* we waited 10 iterations, no luck. Sleep 10 ms */ + acx_s_msleep(10); + } + } while (--counter); + + if (!counter) { + /* the card doesn't get idle, we're in trouble */ + printk("%s: trying to issue firmware command " + "but Command Register is not IDLE: 0x%04X\n", + priv->netdev->name, priv->cmd_status); + goto done; + } else if (counter < 199) { /* if waited >0ms... */ + acxlog(L_CTL|L_DEBUG, FUNC"(): waited for idle %dms. " + "Please report\n", 199 - counter); + } + + /* now write the parameters of the command if needed */ + if (pcmdparam && paramlen) { + /* if it's an INTERROGATE command, just pass the length + * of parameters to read, as data */ + memcpy(priv->cmd_area, pcmdparam, + (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : paramlen); + } + /* now write the actual command type */ + priv->cmd_type = cmd; + acx_write_cmd_type(priv, cmd); + /* execute command */ + acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_CMD); + acx_write_flush(priv); + + /* wait for firmware to process command */ + + /* make sure we have at least *some* timeout value */ + if (unlikely(timeout > 120000)) + timeout = 120000; + timeout = ((timeout/100)-1) | 1; /* in ms, nonzero */ + /* clear it. can be set only by IRQ handler: */ + priv->irq_status &= ~HOST_INT_CMD_COMPLETE; + + /* we schedule away sometimes (timeout can be large) */ + counter = timeout; + do { + if (!priv->irqs_active) { /* IRQ disabled: poll */ + irqtype = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES); + if (irqtype & HOST_INT_CMD_COMPLETE) { + acx_write_reg16(priv, IO_ACX_IRQ_ACK, HOST_INT_CMD_COMPLETE); + break; + } + } else { /* Wait when IRQ will set the bit */ + irqtype = priv->irq_status; + if (irqtype & HOST_INT_CMD_COMPLETE) + break; + } + + if (counter % 10 == 0) { + /* we waited 10 iterations, no luck. Sleep 10 ms */ + acx_s_msleep(10); + } + } while (--counter); + + /* save state for debugging */ + acx_read_cmd_status(priv); + cmd_status = priv->cmd_status; + + /* put the card in IDLE state */ + priv->cmd_status = 0; + acx_write_cmd_status(priv, 0); + + if (!counter) { /* timed out! */ + printk("%s: "FUNC"(0x%04X) timed out %s for Cmd_Complete. " + "irq bits:0x%04X irq status:0x%04X timeout:%dms " + "cmd status:%d (%s). Bailing\n", + priv->netdev->name, cmd, + (priv->irqs_active) ? "waiting" : "polling", + irqtype, priv->irq_status, timeout, + cmd_status, acx_cmd_status_str(cmd_status)); + if (acx_debug) + dump_stack(); + goto done; + } else if (timeout - counter > 1) { /* if waited >1ms... */ + acxlog(L_CTL|L_DEBUG, FUNC"(%s): %s for Cmd_Complete %dms. count:%d. " + "Please report\n", cmdstr, + (priv->irqs_active) ? "waited" : "polled", + timeout - counter, counter); + } + + if (1 != cmd_status) { /* it is not a 'Success' */ + printk("%s: "FUNC"(0x%04X) FAILED: %d (%s). Took %dms of %d\n", + priv->netdev->name, cmd, + cmd_status, acx_cmd_status_str(cmd_status), + timeout - counter, timeout); + if (acx_debug & L_CTL) + dump_stack(); + /* zero out result buffer */ + if (pcmdparam && paramlen) + memset(pcmdparam, 0, paramlen); + goto done; + } + + /* read in result parameters if needed */ + if (pcmdparam && paramlen && (cmd == ACX1xx_CMD_INTERROGATE)) { + memcpy(pcmdparam, priv->cmd_area, paramlen); + if (acx_debug & L_DEBUG) { + printk("output pdr (len=%u): ", paramlen); + acx_dump_bytes(pcmdparam, paramlen); + } + } + result = OK; +done: + acxlog(L_CTL, FUNC"(%s): took %ld jiffies to complete\n", + cmdstr, jiffies-start); + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_s_get_firmware_version +*----------------------------------------------------------------*/ +static void +acx_s_get_firmware_version(wlandevice_t *priv) +{ + fw_ver_t fw; + u8 hexarr[4] = { 0, 0, 0, 0 }; + int hexidx = 0, val = 0; + const char *num; + char c; + + FN_ENTER; + + acx_s_interrogate(priv, &fw, ACX1xx_IE_FWREV); + memcpy(priv->firmware_version, fw.fw_id, FW_ID_SIZE); + priv->firmware_version[FW_ID_SIZE] = '\0'; + acxlog(L_DEBUG, "fw_ver: fw_id='%s' hw_id=%08X\n", + priv->firmware_version, fw.hw_id); + + if (strncmp(fw.fw_id, "Rev ", 4) != 0) { + printk("acx: strange firmware version string " + "'%s', please report\n", priv->firmware_version); + priv->firmware_numver = 0x01090407; /* assume 1.9.4.7 */ + } else { + num = &fw.fw_id[4]; + while (1) { + c = *num++; + if ((c == '.') || (c == '\0')) { + hexarr[hexidx++] = val; + if ((hexidx > 3) || (c == '\0')) /* end? */ + break; + val = 0; + continue; + } + if ((c >= '0') && (c <= '9')) + c -= '0'; + else + c = c - 'a' + (char)10; + val = val*16 + c; + } + + priv->firmware_numver = (u32)( + (hexarr[0] << 24) + (hexarr[1] << 16) + + (hexarr[2] << 8) + hexarr[3]); + acxlog(L_DEBUG, "firmware_numver 0x%08X\n", priv->firmware_numver); + } + if (priv->chip_type == CHIPTYPE_ACX111) { + if (priv->firmware_numver == 0x00010011) { + /* This one does not survive floodpinging */ + printk("acx: firmware '%s' is known to be buggy, " + "please upgrade\n", priv->firmware_version); + } + if (priv->firmware_numver == 0x02030131) { + /* With this one, all rx packets look mangled + ** Most probably we simply do not know how to use it + ** properly */ + printk("acx: firmware '%s' does not work well " + "with this driver\n", priv->firmware_version); + } + } + + priv->firmware_id = le32_to_cpu(fw.hw_id); + + /* we're able to find out more detailed chip names now */ + switch (priv->firmware_id & 0xffff0000) { + case 0x01010000: + case 0x01020000: + priv->chip_name = name_tnetw1100a; + break; + case 0x01030000: + priv->chip_name = name_tnetw1100b; + break; + case 0x03000000: + case 0x03010000: + priv->chip_name = name_tnetw1130; + break; + default: + printk("acx: unknown chip ID 0x%08X, " + "please report\n", priv->firmware_id); + break; + } + + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_display_hardware_details +* +* Arguments: +* priv: ptr to wlandevice that contains all the details +* displayed by this function +* Call context: +* acx_probe_pci +* Comment: +* This function will display strings to the system log according +* to device form_factor and radio type. It will needed to be +*----------------------------------------------------------------*/ +static void +acx_display_hardware_details(wlandevice_t *priv) +{ + const char *radio_str, *form_str; + + FN_ENTER; + + switch (priv->radio_type) { + case RADIO_MAXIM_0D: + /* hmm, the DWL-650+ seems to have two variants, + * according to a windows driver changelog comment: + * RFMD and Maxim. */ + radio_str = "Maxim"; + break; + case RADIO_RFMD_11: + radio_str = "RFMD"; + break; + case RADIO_RALINK_15: + radio_str = "Ralink"; + break; + case RADIO_RADIA_16: + radio_str = "Radia"; + break; + case RADIO_UNKNOWN_17: + /* TI seems to have a radio which is + * additionally 802.11a capable, too */ + radio_str = "802.11a/b/g radio?! Please report"; + break; + case RADIO_UNKNOWN_19: + radio_str = "A radio used by Safecom cards?! Please report"; + break; + default: + radio_str = "UNKNOWN, please report the radio type name!"; + break; + } + + switch (priv->form_factor) { + case 0x00: + form_str = "unspecified"; + break; + case 0x01: + form_str = "(mini-)PCI / CardBus"; + break; + case 0x02: + form_str = "USB"; + break; + case 0x03: + form_str = "Compact Flash"; + break; + default: + form_str = "UNKNOWN, Please report"; + break; + } + + printk("acx: form factor 0x%02X (%s), " + "radio type 0x%02X (%s), EEPROM version 0x%02X, " + "uploaded firmware '%s' (0x%08X)\n", + priv->form_factor, form_str, priv->radio_type, radio_str, + priv->eeprom_version, priv->firmware_version, + priv->firmware_id); + + FN_EXIT0; +} + +/*********************************************************************** +*/ +#ifdef NONESSENTIAL_FEATURES +typedef struct device_id { + unsigned char id[6]; + char *descr; + char *type; +} device_id_t; + +static const device_id_t +device_ids[] = +{ + { + {'G', 'l', 'o', 'b', 'a', 'l'}, + NULL, + NULL, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + "uninitialized", + "SpeedStream SS1021 or Gigafast WF721-AEX" + }, + { + {0x80, 0x81, 0x82, 0x83, 0x84, 0x85}, + "non-standard", + "DrayTek Vigor 520" + }, + { + {'?', '?', '?', '?', '?', '?'}, + "non-standard", + "Level One WPC-0200" + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + "empty", + "DWL-650+ variant" + } +}; + +static void +acx_show_card_eeprom_id(wlandevice_t *priv) +{ + unsigned char buffer[CARD_EEPROM_ID_SIZE]; + int i; + + memset(&buffer, 0, CARD_EEPROM_ID_SIZE); + /* use direct EEPROM access */ + for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) { + if (OK != acx_read_eeprom_offset(priv, + ACX100_EEPROM_ID_OFFSET + i, + &buffer[i])) + { + printk("acx: reading EEPROM FAILED\n"); + break; + } + } + + for (i = 0; i < VEC_SIZE(device_ids); i++) { + if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) { + if (device_ids[i].descr) { + printk("acx: EEPROM card ID string check " + "found %s card ID: is this %s?\n", + device_ids[i].descr, device_ids[i].type); + } + break; + } + } + if (i == VEC_SIZE(device_ids)) { + printk("acx: EEPROM card ID string check found " + "unknown card: expected 'Global', got '%.*s\'. " + "Please report\n", CARD_EEPROM_ID_SIZE, buffer); + } +} +#endif /* NONESSENTIAL_FEATURES */ + + +/*********************************************************************** +*/ +static void +acx_s_device_chain_add(struct net_device *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + down(&root_acx_dev_sem); + priv->prev_nd = root_acx_dev.newest; + root_acx_dev.newest = dev; + priv->netdev = dev; + up(&root_acx_dev_sem); +} + +static void +acx_s_device_chain_remove(struct net_device *dev) +{ + struct net_device *querydev; + struct net_device *olderdev; + struct net_device *newerdev; + + down(&root_acx_dev_sem); + querydev = root_acx_dev.newest; + newerdev = NULL; + while (NULL != querydev) { + olderdev = ((struct wlandevice *) querydev->priv)->prev_nd; + if (0 == strcmp(querydev->name, dev->name)) { + if (NULL == newerdev) { + /* if we were at the beginning of the + * list, then it's the list head that + * we need to update to point at the + * next older device */ + root_acx_dev.newest = olderdev; + } else { + /* it's the device that is newer than us + * that we need to update to point at + * the device older than us */ + ((struct wlandevice *) newerdev->priv)-> + prev_nd = olderdev; + } + break; + } + /* "newerdev" is actually the device of the old iteration, + * but since the list starts (root_acx_dev.newest) + * with the newest devices, + * it's newer than the ones following. + * Oh the joys of iterating from newest to oldest :-\ */ + newerdev = querydev; + + /* keep checking old devices for matches until we hit the end + * of the list */ + querydev = olderdev; + } + up(&root_acx_dev_sem); +} + + +/*---------------------------------------------------------------- +* acx_free_desc_queues +* +* Releases the queues that have been allocated, the +* others have been initialised to NULL in acx100.c so this +* function can be used if only part of the queues were +* allocated. +*----------------------------------------------------------------*/ +static inline void +acx_free_coherent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53) + dma_free_coherent(hwdev == NULL ? NULL : &hwdev->dev, + size, vaddr, dma_handle); +#else + pci_free_consistent(hwdev, size, vaddr, dma_handle); +#endif +} + +void +acx_free_desc_queues(TIWLAN_DC *pDc) +{ +#define ACX_FREE_QUEUE(size, ptr, phyaddr) \ + if (ptr) { \ + acx_free_coherent(0, size, ptr, phyaddr); \ + ptr = NULL; \ + size = 0; \ + } + + FN_ENTER; + + ACX_FREE_QUEUE(pDc->TxHostDescQPoolSize, pDc->pTxHostDescQPool, pDc->TxHostDescQPoolPhyAddr); + ACX_FREE_QUEUE(pDc->TxBufferPoolSize, pDc->pTxBufferPool, pDc->TxBufferPoolPhyAddr); + + pDc->pTxDescQPool = NULL; + pDc->tx_pool_count = 0; + + ACX_FREE_QUEUE(pDc->RxHostDescQPoolSize, pDc->pRxHostDescQPool, pDc->RxHostDescQPoolPhyAddr); + ACX_FREE_QUEUE(pDc->RxBufferPoolSize, pDc->pRxBufferPool, pDc->RxBufferPoolPhyAddr); + + pDc->pRxDescQPool = NULL; + pDc->rx_pool_count = 0; + + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_s_delete_dma_regions +*----------------------------------------------------------------*/ +static void +acx_s_delete_dma_regions(wlandevice_t *priv) +{ + unsigned long flags; + + FN_ENTER; + /* disable radio Tx/Rx. Shouldn't we use the firmware commands + * here instead? Or are we that much down the road that it's no + * longer possible here? */ + acx_write_reg16(priv, IO_ACX_ENABLE, 0); + + acx_s_msleep(100); + + acx_lock(priv, flags); + acx_free_desc_queues(&priv->dc); + acx_unlock(priv, flags); + + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_e_probe_pci +* +* Probe routine called when a PCI device w/ matching ID is found. +* Here's the sequence: +* - Allocate the PCI resources. +* - Read the PCMCIA attribute memory to make sure we have a WLAN card +* - Reset the MAC +* - Initialize the dev and wlan data +* - Initialize the MAC +* +* Arguments: +* pdev ptr to pci device structure containing info about +* pci configuration. +* id ptr to the device id entry that matched this device. +* +* Returns: +* zero - success +* negative - failed +* +* Call context: +* process thread +----------------------------------------------------------------*/ +static const u16 +IO_ACX100[] = +{ + 0x0000, /* IO_ACX_SOFT_RESET */ + + 0x0014, /* IO_ACX_SLV_MEM_ADDR */ + 0x0018, /* IO_ACX_SLV_MEM_DATA */ + 0x001c, /* IO_ACX_SLV_MEM_CTL */ + 0x0020, /* IO_ACX_SLV_END_CTL */ + + 0x0034, /* IO_ACX_FEMR */ + + 0x007c, /* IO_ACX_INT_TRIG */ + 0x0098, /* IO_ACX_IRQ_MASK */ + 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */ + 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */ + 0x00ac, /* IO_ACX_IRQ_ACK */ + 0x00b0, /* IO_ACX_HINT_TRIG */ + + 0x0104, /* IO_ACX_ENABLE */ + + 0x0250, /* IO_ACX_EEPROM_CTL */ + 0x0254, /* IO_ACX_EEPROM_ADDR */ + 0x0258, /* IO_ACX_EEPROM_DATA */ + 0x025c, /* IO_ACX_EEPROM_CFG */ + + 0x0268, /* IO_ACX_PHY_ADDR */ + 0x026c, /* IO_ACX_PHY_DATA */ + 0x0270, /* IO_ACX_PHY_CTL */ + + 0x0290, /* IO_ACX_GPIO_OE */ + + 0x0298, /* IO_ACX_GPIO_OUT */ + + 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */ + 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */ + 0x02ac, /* IO_ACX_EEPROM_INFORMATION */ + + 0x02d0, /* IO_ACX_EE_START */ + 0x02d4, /* IO_ACX_SOR_CFG */ + 0x02d8 /* IO_ACX_ECPU_CTRL */ +}; + +static const u16 +IO_ACX111[] = +{ + 0x0000, /* IO_ACX_SOFT_RESET */ + + 0x0014, /* IO_ACX_SLV_MEM_ADDR */ + 0x0018, /* IO_ACX_SLV_MEM_DATA */ + 0x001c, /* IO_ACX_SLV_MEM_CTL */ + 0x0020, /* IO_ACX_SLV_END_CTL */ + + 0x0034, /* IO_ACX_FEMR */ + + 0x00b4, /* IO_ACX_INT_TRIG */ + 0x00d4, /* IO_ACX_IRQ_MASK */ + /* we need NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */ + 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */ + 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */ + 0x00e8, /* IO_ACX_IRQ_ACK */ + 0x00ec, /* IO_ACX_HINT_TRIG */ + + 0x01d0, /* IO_ACX_ENABLE */ + + 0x0338, /* IO_ACX_EEPROM_CTL */ + 0x033c, /* IO_ACX_EEPROM_ADDR */ + 0x0340, /* IO_ACX_EEPROM_DATA */ + 0x0344, /* IO_ACX_EEPROM_CFG */ + + 0x0350, /* IO_ACX_PHY_ADDR */ + 0x0354, /* IO_ACX_PHY_DATA */ + 0x0358, /* IO_ACX_PHY_CTL */ + + 0x0374, /* IO_ACX_GPIO_OE */ + + 0x037c, /* IO_ACX_GPIO_OUT */ + + 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */ + 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */ + 0x0390, /* IO_ACX_EEPROM_INFORMATION */ + + 0x0100, /* IO_ACX_EE_START */ + 0x0104, /* IO_ACX_SOR_CFG */ + 0x0108, /* IO_ACX_ECPU_CTRL */ +}; + +static int __devinit +acx_e_probe_pci(struct pci_dev *pdev, const struct pci_device_id *id) +{ + unsigned long mem_region1 = 0; + unsigned long mem_region2 = 0; + unsigned long mem_region1_size; + unsigned long mem_region2_size; + unsigned long phymem1; + unsigned long phymem2; + void *mem1 = NULL; + void *mem2 = NULL; + wlandevice_t *priv = NULL; + struct net_device *dev = NULL; + const char *chip_name; + int result = -EIO; + int err; + u8 chip_type; + +#if SEPARATE_DRIVER_INSTANCES + struct pci_dev *tdev; + unsigned int inited; + static int turn = 0; +#endif /* SEPARATE_DRIVER_INSTANCES */ + + FN_ENTER; + +#if SEPARATE_DRIVER_INSTANCES + if (card) { + turn++; + inited = 0; + pci_for_each_dev(tdev) { + if (tdev->vendor != PCI_VENDOR_ID_TI) + continue; + + if (tdev == pdev) + break; + if (pci_get_drvdata(tdev)) + inited++; + } + if (inited + turn != card) { + FN_EXIT1(result); + return -ENODEV; + } + } +#endif /* SEPARATE_DRIVER_INSTANCES */ + + /* Enable the PCI device */ + if (pci_enable_device(pdev)) { + printk("acx: pci_enable_device() FAILED\n"); + result = -ENODEV; + goto fail_pci_enable_device; + } + + /* enable busmastering (required for CardBus) */ + pci_set_master(pdev); + + /* chiptype is u8 but id->driver_data is ulong + ** Works for now (possible values are 1 and 2) */ + chip_type = (u8)id->driver_data; + /* acx100 and acx111 have different PCI memory regions */ + if (chip_type == CHIPTYPE_ACX100) { + chip_name = name_acx100; + mem_region1 = PCI_ACX100_REGION1; + mem_region1_size = PCI_ACX100_REGION1_SIZE; + + mem_region2 = PCI_ACX100_REGION2; + mem_region2_size = PCI_ACX100_REGION2_SIZE; + } else if (chip_type == CHIPTYPE_ACX111) { + chip_name = name_acx111; + mem_region1 = PCI_ACX111_REGION1; + mem_region1_size = PCI_ACX111_REGION1_SIZE; + + mem_region2 = PCI_ACX111_REGION2; + mem_region2_size = PCI_ACX111_REGION2_SIZE; + } else { + printk("acx: unknown chip type 0x%04X\n", chip_type); + result = -EIO; + goto fail_unknown_chiptype; + } + + /* Figure out our resources */ + phymem1 = pci_resource_start(pdev, mem_region1); + phymem2 = pci_resource_start(pdev, mem_region2); + + if (!request_mem_region + (phymem1, pci_resource_len(pdev, mem_region1), "ACX1xx_1")) { + printk("acx: cannot reserve PCI memory region 1 (are you sure " + "you have CardBus support in kernel?)\n"); + result = -EIO; + goto fail_request_mem_region1; + } + + if (!request_mem_region + (phymem2, pci_resource_len(pdev, mem_region2), "ACX1xx_2")) { + printk("acx: cannot reserve PCI memory region 2\n"); + result = -EIO; + goto fail_request_mem_region2; + } + + mem1 = ioremap(phymem1, mem_region1_size); + if (NULL == mem1) { + printk("acx: ioremap() FAILED\n"); + result = -EIO; + goto fail_ioremap1; + } + + mem2 = ioremap(phymem2, mem_region2_size); + if (NULL == mem2) { + printk("acx: ioremap() #2 FAILED\n"); + result = -EIO; + goto fail_ioremap2; + } + + /* Log the device */ + printk("acx: found %s-based wireless network card at %s, irq:%d, " + "phymem1:0x%lX, phymem2:0x%lX, mem1:0x%p, mem1_size:%ld, " + "mem2:0x%p, mem2_size:%ld\n", + chip_name, pci_name(pdev), pdev->irq, phymem1, phymem2, + mem1, mem_region1_size, + mem2, mem_region2_size); + acxlog(L_ANY, "initial debug setting is 0x%04X\n", acx_debug); + + if (0 == pdev->irq) { + printk("acx: can't use IRQ 0\n"); + result = -EIO; + goto fail_irq; + } + + acxlog(L_DEBUG, "allocating %d (0x%X) bytes for wlandevice_t\n", + (int) sizeof(wlandevice_t), (int) sizeof(wlandevice_t)); + priv = kmalloc(sizeof(wlandevice_t), GFP_KERNEL); + if (!priv) { + printk("acx: no memory for wlandevice_t\n"); + result = -EIO; + goto fail_alloc_priv; + } + + memset(priv, 0, sizeof(wlandevice_t)); + + spin_lock_init(&priv->lock); /* initial state: unlocked */ + /* We do not start with downed sem: we want PARANOID_LOCKING to work */ + sema_init(&priv->sem, 1); /* initial state: 1 (upped) */ + + /* since nobody can see new netdev yet, we can as well + ** just _presume_ that we're under sem (instead of actually taking it): */ + /* acx_sem_lock(priv); */ + + priv->pdev = pdev; + priv->chip_type = chip_type; + priv->chip_name = chip_name; + priv->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111; + + priv->membase = phymem1; + priv->iobase = mem1; + + priv->membase2 = phymem2; + priv->iobase2 = mem2; + + /* to find crashes due to weird driver access + * to unconfigured interface (ifup) */ + priv->mgmt_timer.function = (void (*)(unsigned long))0x0000dead; + +#ifdef NONESSENTIAL_FEATURES + acx_show_card_eeprom_id(priv); +#endif /* NONESSENTIAL_FEATURES */ + + dev = kmalloc(sizeof(netdevice_t), GFP_KERNEL); + if (unlikely(NULL == dev)) { + printk("acx: no memory for netdevice_t\n"); + result = -EIO; + goto fail_alloc_netdev; + } + + memset(dev, 0, sizeof(netdevice_t)); + ether_setup(dev); + + /* now we have our device, so make sure the kernel doesn't try + * to send packets even though we're not associated to a network yet */ + acx_stop_queue(dev, "after setup"); + + dev->priv = priv; + +#ifdef SET_MODULE_OWNER + SET_MODULE_OWNER(dev); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 70) + /* this define and its netdev member exist since 2.5.70 */ + SET_NETDEV_DEV(dev, &pdev->dev); +#endif + + /* register new dev in linked list */ + acx_s_device_chain_add(dev); + + acxlog(L_IRQ | L_INIT, "using IRQ %d\n", pdev->irq); + + dev->irq = pdev->irq; + /* TODO this is maybe incompatible to ACX111 */ + dev->base_addr = pci_resource_start(pdev, 0); + + /* need to be able to restore PCI state after a suspend */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) + /* 2.6.9-rc3-mm2 (2.6.9-bk4, too) introduced this shorter version, + then it made its way into 2.6.10 */ + pci_save_state(pdev); +#else + pci_save_state(pdev, priv->pci_state); +#endif + + /* NB: acx_read_reg() reads may return bogus data before reset_dev(). + ** acx100 seems to be more affected than acx111 */ + + if (OK != acx_s_reset_dev(dev)) { + result = -EIO; + goto fail_reset; + } + + if (dev_alloc_name(dev, "wlan%d") < 0) { + result = -EIO; + goto fail_alloc_name; + } + printk("acx: net device %s, driver compiled " + "against wireless extensions %d and Linux %s\n", + dev->name, WIRELESS_EXT, UTS_RELEASE); + + /* now that device init was successful, fill remaining fields... */ + dev->open = &acx_e_open; + dev->stop = &acx_e_close; + dev->hard_start_xmit = &acx_i_start_xmit; + dev->get_stats = &acx_e_get_stats; + dev->get_wireless_stats = &acx_e_get_wireless_stats; +#if WIRELESS_EXT >= 13 + dev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; +#else + dev->do_ioctl = &acx_e_ioctl_old; +#endif + dev->set_multicast_list = &acx_i_set_multicast_list; + dev->tx_timeout = &acx_i_tx_timeout; + dev->watchdog_timeo = 4 * HZ; /* 400 */ + + /* ok, basic setup is finished, now start initialising the card */ + + if (OK != acx_read_eeprom_offset(priv, 0x05, &priv->eeprom_version)) { + result = -EIO; + goto fail_read_eeprom_version; + } + + if (OK != acx_s_init_mac(dev)) { + printk("acx: init_mac() FAILED\n"); + result = -EIO; + goto fail_init_mac; + } + if (OK != acx_s_set_defaults(priv)) { + printk("%s: acx_set_defaults FAILED\n", dev->name); + goto fail_set_defaults; + } + + /* needs to be after acx_s_init_mac() due to necessary init stuff */ + acx_s_get_firmware_version(priv); + + acx_display_hardware_details(priv); + + pci_set_drvdata(pdev, dev); + + /* ...and register the card, AFTER everything else has been set up, + * since otherwise an ioctl could step on our feet due to + * firmware operations happening in parallel or uninitialized data */ + err = register_netdev(dev); + if (OK != err) { + printk("acx: register_netdev() FAILED: %d\n", err); + result = -EIO; + goto fail_register_netdev; + } + + acx_carrier_off(dev, "on probe"); + +#ifdef CONFIG_PROC_FS + if (OK != acx_proc_register_entries(dev)) { + result = -EIO; + goto fail_proc_register_entries; + } +#endif + + /* after register_netdev() userspace may start working with dev + * (in particular, on other CPUs), we only need to up the sem */ + /* acx_sem_unlock(priv); */ + + printk("acx_pci module " WLAN_RELEASE " loaded successfully\n"); + result = OK; + goto done; + + /* error paths: undo everything in reverse order... */ + +#ifdef CONFIG_PROC_FS +fail_proc_register_entries: + + if (priv->dev_state_mask & ACX_STATE_IFACE_UP) + acx_s_down(dev); + + unregister_netdev(dev); + + /* after unregister_netdev() userspace is guaranteed to finish + * working with it. netdev does not exist anymore. + * For paranoid reasons I am taking sem anyway */ + acx_sem_lock(priv); +#endif + +fail_register_netdev: + + acx_s_delete_dma_regions(priv); + pci_set_drvdata(pdev, NULL); + +fail_set_defaults: +fail_init_mac: +fail_read_eeprom_version: +fail_alloc_name: +fail_reset: + + acx_s_device_chain_remove(dev); + kfree(dev); +fail_alloc_netdev: + + kfree(priv); +fail_alloc_priv: +fail_irq: + + iounmap(mem2); +fail_ioremap2: + + iounmap(mem1); +fail_ioremap1: + + release_mem_region(pci_resource_start(pdev, mem_region2), + pci_resource_len(pdev, mem_region2)); +fail_request_mem_region2: + + release_mem_region(pci_resource_start(pdev, mem_region1), + pci_resource_len(pdev, mem_region1)); +fail_request_mem_region1: +fail_unknown_chiptype: + + pci_disable_device(pdev); +fail_pci_enable_device: + + pci_set_power_state(pdev, 3); + +done: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_e_remove_pci +* +* Deallocate PCI resources for the ACX100 chip. +* +* This should NOT execute any other hardware operations on the card, +* since the card might already be ejected. Instead, that should be done +* in cleanup_module, since the card is most likely still available there. +* +* Arguments: +* pdev ptr to PCI device structure containing info about +* PCI configuration. +* +* Call context: +* process thread +----------------------------------------------------------------*/ +static void __devexit +acx_e_remove_pci(struct pci_dev *pdev) +{ + struct net_device *dev; + wlandevice_t *priv; + unsigned long mem_region1 = 0, mem_region2 = 0; + + FN_ENTER; + + dev = (struct net_device *) pci_get_drvdata(pdev); + if (!dev) { + acxlog(L_DEBUG, "%s: card is unused. Skipping any release code\n", + __func__); + goto end; + } + + priv = (struct wlandevice *) dev->priv; + if (!priv) { + acxlog(L_DEBUG, "%s: card is unused. Skipping any release code\n", + __func__); + goto end; + } + + /* unregister the device to not let the kernel + * (e.g. ioctls) access a half-deconfigured device + * NB: this will cause acx_e_close() to be called, + * thus we shouldn't call it under sem! */ + acxlog(L_INIT, "removing device %s\n", dev->name); + unregister_netdev(dev); + + /* unregister_netdev ensures that no references to us left. + * For paranoid reasons we continue to follow the rules */ + acx_sem_lock(priv); + + if (priv->chip_type == CHIPTYPE_ACX100) { + mem_region1 = PCI_ACX100_REGION1; + mem_region2 = PCI_ACX100_REGION2; + } else { + mem_region1 = PCI_ACX111_REGION1; + mem_region2 = PCI_ACX111_REGION2; + } + +#ifdef CONFIG_PROC_FS + acx_proc_unregister_entries(dev); +#endif + + /* find our PCI device in the global acx list and remove it */ + acx_s_device_chain_remove(dev); + + if (priv->dev_state_mask & ACX_STATE_IFACE_UP) + acx_s_down(dev); + + CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP); + + acx_s_delete_dma_regions(priv); + + /* finally, clean up PCI bus state */ + if (priv->iobase) iounmap(priv->iobase); + if (priv->iobase2) iounmap(priv->iobase2); + + release_mem_region(pci_resource_start(pdev, mem_region1), + pci_resource_len(pdev, mem_region1)); + + release_mem_region(pci_resource_start(pdev, mem_region2), + pci_resource_len(pdev, mem_region2)); + + pci_disable_device(pdev); + + /* remove dev registration */ + pci_set_drvdata(pdev, NULL); + + /* Free netdev (quite late, + * since otherwise we might get caught off-guard + * by a netdev timeout handler execution + * expecting to see a working dev...) + * But don't use free_netdev() here, + * it's supported by newer kernels only */ + kfree(priv); + kfree(dev); + + /* put device into ACPI D3 mode (shutdown) */ + pci_set_power_state(pdev, 3); + +end: + FN_EXIT0; +} + + +/*********************************************************************** +*/ +#ifdef CONFIG_PM +static int if_was_up = 0; /* FIXME: HACK, do it correctly sometime instead */ +static int +acx_e_suspend(struct pci_dev *pdev, /*@unused@*/ u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + wlandevice_t *priv = acx_netdev_priv(dev); + + FN_ENTER; + + acx_sem_lock(priv); + + printk("acx: experimental suspend handler called for %p\n", priv); + if (netif_device_present(dev)) { + if_was_up = 1; + acx_s_down(dev); + } + else + if_was_up = 0; + + netif_device_detach(dev); /* This one cannot sleep */ + acx_s_delete_dma_regions(priv); + + acx_sem_unlock(priv); + + FN_EXIT0; + return OK; +} + +static int +acx_e_resume(struct pci_dev *pdev) +{ + struct net_device *dev; + wlandevice_t *priv; + + printk(KERN_WARNING "rsm: resume\n"); + dev = pci_get_drvdata(pdev); + printk(KERN_WARNING "rsm: got dev\n"); + + if (!netif_running(dev)) + return 0; + + priv = dev->priv; + + acx_sem_lock(priv); + + printk(KERN_WARNING "rsm: got priv\n"); + FN_ENTER; + printk("acx: experimental resume handler called for %p!\n", priv); + pci_set_power_state(pdev, 0); + acxlog(L_DEBUG, "rsm: power state set\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) + /* 2.6.9-rc3-mm2 (2.6.9-bk4, too) introduced this shorter version, + then it made its way into 2.6.10 */ + pci_restore_state(pdev); +#else + pci_restore_state(pdev, priv->pci_state); +#endif + acxlog(L_DEBUG, "rsm: PCI state restored\n"); + acx_s_reset_dev(dev); + acxlog(L_DEBUG, "rsm: device reset done\n"); + + if (OK != acx_s_init_mac(dev)) { + printk("rsm: init_mac FAILED\n"); + goto fail; + } + acxlog(L_DEBUG, "rsm: init MAC done\n"); + + if (1 == if_was_up) + acx_s_up(dev); + acxlog(L_DEBUG, "rsm: acx up\n"); + + /* now even reload all card parameters as they were before suspend, + * and possibly be back in the network again already :-) + * FIXME: should this be done in that scheduled task instead?? */ + if (ACX_STATE_IFACE_UP & priv->dev_state_mask) + acx_s_update_card_settings(priv, 0, 1); + acxlog(L_DEBUG, "rsm: settings updated\n"); + netif_device_attach(dev); + acxlog(L_DEBUG, "rsm: device attached\n"); +fail: /* we need to return OK here anyway, right? */ + acx_sem_unlock(priv); + FN_EXIT0; + return OK; +} +#endif /* CONFIG_PM */ + + +/*---------------------------------------------------------------- +* acx_s_up +* +* Side effects: +* - Enables on-card interrupt requests +* - calls acx_start +* Call context: +* - process thread +* Comment: +* This function is called by acx_open (when ifconfig sets the +* device as up). +*----------------------------------------------------------------*/ +static void +acx_s_up(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned long flags; + + FN_ENTER; + + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + /* actual scan cmd will happen in start() */ + acx_set_status(priv, ACX_STATUS_1_SCANNING); break; + case ACX_MODE_3_AP: + case ACX_MODE_MONITOR: + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); break; + } + + acx_lock(priv, flags); + acx_l_enable_irq(priv); + acx_unlock(priv, flags); + + /* acx fw < 1.9.3.e has a hardware timer, and older drivers + ** used to use it. But we don't do that anymore, our OS + ** has reliable software timers */ + init_timer(&priv->mgmt_timer); + priv->mgmt_timer.function = acx_i_timer; + priv->mgmt_timer.data = (unsigned long)priv; + + SET_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP); + acx_s_start(priv); + + /* acx_start_queue(dev, "on startup"); */ + + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_s_down +* +* Side effects: +* - disables on-card interrupt request +* Call context: +* process thread +* Comment: +* this disables the netdevice +*----------------------------------------------------------------*/ +static void +acx_s_down(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned long flags; + + FN_ENTER; + + acx_stop_queue(dev, "during close"); + + /* we really don't want to have an asynchronous tasklet disturb us + ** after something vital for its job has been shut down, so + ** end all remaining work now. + ** + ** NB: carrier_off (done by set_status below) would lead to + ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK(). + ** That's why we do FLUSH first. + ** + ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK() + ** waits for acx_e_after_interrupt_task to complete if it is running + ** on another CPU, but acx_e_after_interrupt_task + ** will sleep on sem forever, because it is taken by us! + ** Work around that by temporary sem unlock. + ** This will fail miserably if we'll be hit by concurrent + ** iwconfig or something in between. TODO! + */ + acx_sem_unlock(priv); + FLUSH_SCHEDULED_WORK(); + acx_sem_lock(priv); + + acx_set_status(priv, ACX_STATUS_0_STOPPED); + + /* kernel/timer.c says it's illegal to del_timer_sync() + ** a timer which restarts itself. We guarantee this cannot + ** ever happen because acx_i_timer() never does this if + ** status is ACX_STATUS_0_STOPPED + */ + del_timer_sync(&priv->mgmt_timer); + + acx_lock(priv, flags); + acx_l_disable_irq(priv); + acx_unlock(priv, flags); + + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_e_open +* +* WLAN device open method. Called from p80211netdev when kernel +* device open (start) method is called in response to the +* SIOCSIFFLAGS ioctl changing the flags bit IFF_UP +* from clear to set. +* +* Returns: +* 0 success +* >0 f/w reported error +* <0 driver reported error +* +* Call context: +* process thread +----------------------------------------------------------------*/ +static int +acx_e_open(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + int result = OK; + + FN_ENTER; + + acxlog(L_INIT, "module count++\n"); + WLAN_MOD_INC_USE_COUNT; + + acx_sem_lock(priv); + + acx_init_task_scheduler(priv); + + /* request shared IRQ handler */ + if (request_irq(dev->irq, acx_i_interrupt, SA_SHIRQ, dev->name, dev)) { + printk("%s: request_irq FAILED\n", dev->name); + result = -EAGAIN; + goto done; + } + acxlog(L_DEBUG | L_IRQ, "request_irq %d successful\n", dev->irq); + + /* ifup device */ + acx_s_up(dev); + + /* We don't currently have to do anything else. + * The setup of the MAC should be subsequently completed via + * the mlme commands. + * Higher layers know we're ready from dev->start==1 and + * dev->tbusy==0. Our rx path knows to pass up received/ + * frames because of dev->flags&IFF_UP is true. + */ +done: + acx_sem_unlock(priv); + + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_e_close +* +* WLAN device close method. Called from network core when kernel +* device close method is called in response to the +* SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP +* from set to clear. +* (i.e. called for "ifconfig DEV down") +* +* Returns: +* 0 success +* >0 f/w reported error +* <0 driver reported error +* +* Call context: +* process thread +----------------------------------------------------------------*/ +static int +acx_e_close(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + + FN_ENTER; + + acx_sem_lock(priv); + + /* ifdown device */ + CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP); + if (netif_device_present(dev)) { + acx_s_down(dev); + } + + /* disable all IRQs, release shared IRQ handler */ + acx_write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff); + acx_write_reg16(priv, IO_ACX_FEMR, 0x0); + free_irq(dev->irq, dev); + + /* We currently don't have to do anything else. + * Higher layers know we're not ready from dev->start==0 and + * dev->tbusy==1. Our rx path knows to not pass up received + * frames because of dev->flags&IFF_UP is false. + */ + acxlog(L_INIT, "module count--\n"); + WLAN_MOD_DEC_USE_COUNT; + + acx_sem_unlock(priv); + + acxlog(L_INIT, "closed device\n"); + FN_EXIT0; + return OK; +} + + +/*---------------------------------------------------------------- +* acx_i_tx_timeout +* +* Called from network core. Must not sleep! +*----------------------------------------------------------------*/ +static void +acx_i_tx_timeout(netdevice_t *dev) +{ + wlandevice_t *priv; + unsigned long flags; + unsigned int tx_num_cleaned; + + FN_ENTER; + + priv = (wlandevice_t *)dev->priv; + + acx_lock(priv, flags); + + /* clean processed tx descs, they may have been completely full */ + tx_num_cleaned = acx_l_clean_tx_desc(priv); + + /* nothing cleaned, yet (almost) no free buffers available? + * --> clean all tx descs, no matter which status!! + * Note that I strongly suspect that doing emergency cleaning + * may confuse the firmware. This is a last ditch effort to get + * ANYTHING to work again... + * + * TODO: it's best to simply reset & reinit hw from scratch... + */ + if ((priv->TxQueueFree <= 2) && (tx_num_cleaned == 0)) { + printk("%s: FAILED to free any of the many full tx buffers. " + "Switching to emergency freeing. " + "Please report!\n", dev->name); + acx_l_clean_tx_desc_emergency(priv); + } + + if (acx_queue_stopped(dev) && (ACX_STATUS_4_ASSOCIATED == priv->status)) + acx_wake_queue(dev, "after tx timeout"); + + /* stall may have happened due to radio drift, so recalib radio */ + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); + + /* do unimportant work last */ + printk("%s: tx timeout!\n", dev->name); + priv->stats.tx_errors++; + + acx_unlock(priv, flags); + + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_e_get_stats +*----------------------------------------------------------------*/ +static struct net_device_stats* +acx_e_get_stats(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); +#if ANNOYING_GETS_CALLED_TOO_OFTEN + FN_ENTER; + FN_EXIT1((int)&priv->stats); +#endif + return &priv->stats; +} + + +/*---------------------------------------------------------------- +* acx_e_get_wireless_stats +*----------------------------------------------------------------*/ +static struct iw_statistics* +acx_e_get_wireless_stats(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + FN_ENTER; + FN_EXIT0; + return &priv->wstats; +} + + +/*---------------------------------------------------------------- +* acx_i_set_multicast_list +*----------------------------------------------------------------*/ +static void +acx_i_set_multicast_list(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned long flags; + + FN_ENTER; + + acx_lock(priv, flags); + + printk("FIXME: most likely needs refinement, " + "first implementation version only...\n"); + + /* ACX firmwares don't have allmulti capability, + * so just use promiscuous mode instead in this case. */ + if (dev->flags & (IFF_PROMISC|IFF_ALLMULTI)) { + SET_BIT(priv->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); + CLEAR_BIT(priv->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); + SET_BIT(priv->set_mask, SET_RXCONFIG); + /* let kernel know in case *we* needed to set promiscuous */ + dev->flags |= (IFF_PROMISC|IFF_ALLMULTI); + } + else { + CLEAR_BIT(priv->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); + SET_BIT(priv->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); + SET_BIT(priv->set_mask, SET_RXCONFIG); + dev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI); + } + + /* cannot update card settings directly here, atomic context */ + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG); + + acx_unlock(priv, flags); + + FN_EXIT0; +} + +static void +acx_l_update_link_quality_led(wlandevice_t *priv) +{ + int qual; + + qual = acx_signal_determine_quality(priv->wstats.qual.level, priv->wstats.qual.noise); + if (qual > priv->brange_max_quality) + qual = priv->brange_max_quality; + + if (time_after(jiffies, priv->brange_time_last_state_change + + (HZ/2 - HZ/2 * (unsigned long) qual/priv->brange_max_quality ) )) { + acx_l_power_led(priv, (priv->brange_last_state == 0)); + priv->brange_last_state ^= 1; /* toggle */ + priv->brange_time_last_state_change = jiffies; + } +} + + +/*---------------------------------------------------------------- +* acx_l_enable_irq +*----------------------------------------------------------------*/ +static void +acx_l_enable_irq(wlandevice_t *priv) +{ + FN_ENTER; + acx_write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask); + acx_write_reg16(priv, IO_ACX_FEMR, 0x8000); + priv->irqs_active = 1; + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_disable_irq +*----------------------------------------------------------------*/ +static void +acx_l_disable_irq(wlandevice_t *priv) +{ + FN_ENTER; + acx_write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask_off); + acx_write_reg16(priv, IO_ACX_FEMR, 0x0); + priv->irqs_active = 0; + FN_EXIT0; +} + +/* scan is complete. all frames now on the receive queue are valid */ +#define INFO_SCAN_COMPLETE 0x0001 +#define INFO_WEP_KEY_NOT_FOUND 0x0002 +/* hw has been reset as the result of a watchdog timer timeout */ +#define INFO_WATCH_DOG_RESET 0x0003 +/* failed to send out NULL frame from PS mode notification to AP */ +/* recommended action: try entering 802.11 PS mode again */ +#define INFO_PS_FAIL 0x0004 +/* encryption/decryption process on a packet failed */ +#define INFO_IV_ICV_FAILURE 0x0005 + +static void +acx_l_handle_info_irq(wlandevice_t *priv) +{ +#if ACX_DEBUG + static const char * const info_type_msg[] = { + "(unknown)", + "scan complete", + "WEP key not found", + "internal watchdog reset was done", + "failed to send powersave (NULL frame) notification to AP", + "encrypt/decrypt on a packet has failed", + "TKIP tx keys disabled", + "TKIP rx keys disabled", + "TKIP rx: key ID not found", + "???", + "???", + "???", + "???", + "???", + "???", + "???", + "TKIP IV value exceeds thresh" + }; +#endif + acx_read_info_status(priv); + acxlog(L_IRQ, "got Info IRQ: status 0x%04X type 0x%04X: %s\n", + priv->info_status, priv->info_type, + info_type_msg[(priv->info_type >= VEC_SIZE(info_type_msg)) ? + 0 : priv->info_type] + ); +} + + +/*---------------------------------------------------------------- +* acx_i_interrupt +* +* IRQ handler (atomic context, must not sleep, blah, blah) +*----------------------------------------------------------------*/ +static void +acx_log_unusual_irq(u16 irqtype) { + /* + if (!printk_ratelimit()) + return; + */ + + printk("acx: got"); + if (irqtype & HOST_INT_RX_DATA) { + printk(" Rx_Data"); + } + /* HOST_INT_TX_COMPLETE */ + if (irqtype & HOST_INT_TX_XFER) { + printk(" Tx_Xfer"); + } + /* HOST_INT_RX_COMPLETE */ + if (irqtype & HOST_INT_DTIM) { + printk(" DTIM"); + } + if (irqtype & HOST_INT_BEACON) { + printk(" Beacon"); + } + if (irqtype & HOST_INT_TIMER) { + acxlog(L_IRQ, " Timer"); + } + if (irqtype & HOST_INT_KEY_NOT_FOUND) { + printk(" Key_Not_Found"); + } + if (irqtype & HOST_INT_IV_ICV_FAILURE) { + printk(" IV_ICV_Failure"); + } + /* HOST_INT_CMD_COMPLETE */ + /* HOST_INT_INFO */ + if (irqtype & HOST_INT_OVERFLOW) { + printk(" Overflow"); + } + if (irqtype & HOST_INT_PROCESS_ERROR) { + printk(" Process_Error"); + } + /* HOST_INT_SCAN_COMPLETE */ + if (irqtype & HOST_INT_FCS_THRESHOLD) { + printk(" FCS_Threshold"); + } + if (irqtype & HOST_INT_UNKNOWN) { + printk(" Unknown"); + } + printk(" IRQ(s)\n"); +} + +static irqreturn_t +acx_i_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + wlandevice_t *priv; + unsigned long flags; + unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY; + u16 irqtype, unmasked; + + priv = (wlandevice_t *) (((netdevice_t *) dev_id)->priv); + + /* LOCKING: can just spin_lock() since IRQs are disabled anyway. + * I am paranoid */ + acx_lock(priv, flags); + + unmasked = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR); + if (unlikely(0xffff == unmasked)) { + /* 0xffff value hints at missing hardware, + * so don't do anything. + * FIXME: that's not very clean - maybe we are able to + * establish a flag which definitely tells us that some + * hardware is absent and which we could check here? + * Hmm, but other drivers do the very same thing... */ + acxlog(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n"); + goto none; + } + + /* We will check only "interesting" IRQ types */ + irqtype = unmasked & ~priv->irq_mask; + if (!irqtype) { + /* We are on a shared IRQ line and it wasn't our IRQ */ + acxlog(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n", + unmasked, priv->irq_mask); + goto none; + } + + /* Done here because IRQ_NONEs taking three lines of log + ** drive me crazy */ + FN_ENTER; + +#define IRQ_ITERATE 1 +#if IRQ_ITERATE +if (jiffies != priv->irq_last_jiffies) { + priv->irq_loops_this_jiffy = 0; + priv->irq_last_jiffies = jiffies; +} + +/* safety condition; we'll normally abort loop below + * in case no IRQ type occurred */ +while (--irqcount) { +#endif + /* ACK all IRQs asap */ + acx_write_reg16(priv, IO_ACX_IRQ_ACK, 0xffff); + + acxlog(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n", + unmasked, priv->irq_mask, irqtype); + + /* Handle most important IRQ types first */ + if (irqtype & HOST_INT_RX_COMPLETE) { + acxlog(L_IRQ, "got Rx_Complete IRQ\n"); + acx_l_process_rx_desc(priv); + } + if (irqtype & HOST_INT_TX_COMPLETE) { + acxlog(L_IRQ, "got Tx_Complete IRQ\n"); + /* don't clean up on each Tx complete, wait a bit + * unless we're going towards full, in which case + * we do it immediately, too (otherwise we might lockup + * with a full Tx buffer if we go into + * acx_l_clean_tx_desc() at a time when we won't wakeup + * the net queue in there for some reason...) */ + if ((++priv->tx_cnt_done % (priv->TxQueueCnt >> 2) == 0) || + (priv->TxQueueFree < (priv->TxQueueCnt >> 1))) + { +#if TX_CLEANUP_IN_SOFTIRQ + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_TX_CLEANUP); +#else + acx_l_clean_tx_desc(priv); +#endif + /* no need to set tx_cnt_done back to 0 here, since + * an overflow cannot cause counter misalignment on + * check above */ + } +#if 0 +/* this shouldn't happen here generally, since we'd also enable user packet + * xmit for management packets, which we really DON'T want */ +/* BS: disabling this caused my card to stop working after a few + * seconds when floodpinging. This should be reinvestigated! */ + if (acx_queue_stopped(dev_id)) { + acx_wake_queue(dev_id, "after Tx complete"); + } +#endif + } + + /* Less frequent ones */ + if (irqtype & (0 + | HOST_INT_CMD_COMPLETE + | HOST_INT_INFO + | HOST_INT_SCAN_COMPLETE + )) { + if (irqtype & HOST_INT_CMD_COMPLETE) { + acxlog(L_IRQ, "got Command_Complete IRQ\n"); + /* save the state for the running issue_cmd() */ + SET_BIT(priv->irq_status, HOST_INT_CMD_COMPLETE); + } + if (irqtype & HOST_INT_INFO) { + acx_l_handle_info_irq(priv); + } + if (irqtype & HOST_INT_SCAN_COMPLETE) { + acxlog(L_IRQ, "got Scan_Complete IRQ\n"); + /* need to do that in process context */ + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_COMPLETE_SCAN); + /* remember that fw is not scanning anymore */ + SET_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE); + } + } + + /* These we just log, but either they happen rarely + * or we keep them masked out */ + if (irqtype & (0 + | HOST_INT_RX_DATA + /* | HOST_INT_TX_COMPLETE */ + | HOST_INT_TX_XFER + /* | HOST_INT_RX_COMPLETE */ + | HOST_INT_DTIM + | HOST_INT_BEACON + | HOST_INT_TIMER + | HOST_INT_KEY_NOT_FOUND + | HOST_INT_IV_ICV_FAILURE + /* | HOST_INT_CMD_COMPLETE */ + /* | HOST_INT_INFO */ + | HOST_INT_OVERFLOW + | HOST_INT_PROCESS_ERROR + /* | HOST_INT_SCAN_COMPLETE */ + | HOST_INT_FCS_THRESHOLD + | HOST_INT_UNKNOWN + )) { + acx_log_unusual_irq(irqtype); + } + +#if IRQ_ITERATE + unmasked = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR); + irqtype = unmasked & ~priv->irq_mask; + /* Bail out if no new IRQ bits or if all are masked out */ + if (!irqtype) + break; + + if (unlikely(++priv->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) { + printk(KERN_ERR "acx: too many interrupts per jiffy!\n"); + /* Looks like card floods us with IRQs! Try to stop that */ + acx_write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff); + /* This will short-circuit all future attempts to handle IRQ. + * We cant do much more... */ + priv->irq_mask = 0; + break; + } +} +#endif + /* Routine to perform blink with range */ + if (unlikely(priv->led_power == 2)) + acx_l_update_link_quality_led(priv); + +/* handled: */ + /* acx_write_flush(priv); - not needed, last op was read anyway */ + acx_unlock(priv, flags); + FN_EXIT0; + return IRQ_HANDLED; + +none: + acx_unlock(priv, flags); + return IRQ_NONE; +} + + +/*---------------------------------------------------------------- +* acx_l_power_led +*----------------------------------------------------------------*/ +void +acx_l_power_led(wlandevice_t *priv, int enable) +{ + u16 gpio_pled = + (CHIPTYPE_ACX111 == priv->chip_type) ? 0x0040 : 0x0800; + + /* A hack. Not moving message rate limiting to priv->xxx + * (it's only a debug message after all) */ + static int rate_limit = 0; + + if (rate_limit++ < 3) + acxlog(L_IOCTL, "Please report in case toggling the power " + "LED doesn't work for your card!\n"); + if (enable) + acx_write_reg16(priv, IO_ACX_GPIO_OUT, + acx_read_reg16(priv, IO_ACX_GPIO_OUT) & ~gpio_pled); + else + acx_write_reg16(priv, IO_ACX_GPIO_OUT, + acx_read_reg16(priv, IO_ACX_GPIO_OUT) | gpio_pled); +} + + +/*********************************************************************** +** Ioctls +*/ + +/*********************************************************************** +** Ioctls for easy debug of I/O things +*/ +/* specialized register io for debug purposes, see ioctl below */ +static inline u32 +_acx_read_reg32(wlandevice_t *priv, unsigned int offset) +{ +#if ACX_IO_WIDTH == 32 + return readl(priv->iobase + offset); +#else + return readw(priv->iobase + offset) + + (readw(priv->iobase + offset + 2) << 16); +#endif +} + +static inline void +_acx_write_reg32(wlandevice_t *priv, unsigned int offset, u32 val) +{ +#if ACX_IO_WIDTH == 32 + writel(val, priv->iobase + offset); +#else + writew(val & 0xffff, priv->iobase + offset); + writew(val >> 16, priv->iobase + offset + 2); +#endif +} + +int +acx_ioctl_dbg_get_io( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + int result = -EINVAL; +#if ACX_DEBUG + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned int *params = (unsigned int*)extra; + + /* expected value order: DbgGetIO type address magic */ + + if (params[2] != 0x1234) { + acxlog(L_IOCTL, "wrong magic: 0x%04X doesn't match 0x%04X! " + "If you don't know what you're doing, then please " + "stop NOW, this can be DANGEROUS!\n", + params[2], 0x1234); + goto end; + } + + switch (params[0]) { + case 0x0: /* Internal RAM */ + acxlog(L_IOCTL, "sorry, access to internal RAM " + "is not implemented yet\n"); + break; + case 0xffff: /* MAC registers */ + acxlog(L_IOCTL, "value at register 0x%04X is 0x%08X\n", + params[1], _acx_read_reg32(priv, params[1])); + break; + case 0x81: /* PHY RAM table */ + acxlog(L_IOCTL, "sorry, access to PHY RAM " + "is not implemented yet\n"); + break; + case 0x82: /* PHY registers */ + acxlog(L_IOCTL, "sorry, access to PHY registers " + "is not implemented yet\n"); + break; + default: + acxlog(L_IOCTL, "Invalid I/O type specified, aborting!\n"); + goto end; + } + result = OK; +end: +#endif /* ACX_DEBUG */ + return result; +} + +int +acx_ioctl_dbg_set_io( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + int result = -EINVAL; +#if ACX_DEBUG + wlandevice_t *priv = acx_netdev_priv(dev); + int *params = (int*)extra; + + /* expected value order: DbgSetIO type address value magic */ + + if (params[3] != 0x1234) { + acxlog(L_ANY, "wrong magic: 0x%04X doesn't match 0x%04X! " + "If you don't know what you're doing, then please " + "stop, this can be DANGEROUS!\n", + params[3], 0x1234); + goto end; + } + + switch (params[0]) { + case 0x0: /* Internal RAM */ + acxlog(L_IOCTL, "sorry, access to internal RAM " + "is not implemented yet\n"); + break; + case 0xffff: /* MAC registers */ + acxlog(L_IOCTL, "setting value at register 0x%04X " + "to 0x%08X\n", params[1], params[2]); + _acx_write_reg32(priv, params[1], params[2]); + break; + case 0x81: /* PHY RAM table */ + acxlog(L_IOCTL, "sorry, access to PHY RAM " + "is not implemented yet\n"); + break; + case 0x82: /* PHY registers */ + acxlog(L_IOCTL, "sorry, access to PHY registers " + "is not implemented yet\n"); + break; + default: + acxlog(L_ANY, "Invalid I/O type specified, aborting!\n"); + goto end; + } + result = OK; +end: +#endif /* ACX_DEBUG */ + return result; +} + + +/*********************************************************************** +*/ +int +acx111_ioctl_info( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ +#if ACX_DEBUG + wlandevice_t *priv = acx_netdev_priv(dev); + const TIWLAN_DC *pDc = &priv->dc; + const struct rxdesc *pRxDesc; + const struct rxhostdesc *rx_host_desc; + struct txdesc *tx_desc; + const struct txhostdesc *tx_host_desc; + struct acx111_ie_memoryconfig memconf; + struct acx111_ie_queueconfig queueconf; + unsigned long flags; + int i; + char memmap[0x34]; + char rxconfig[0x8]; + char fcserror[0x8]; + char ratefallback[0x5]; + + if ( !(acx_debug & (L_IOCTL|L_DEBUG)) ) + return OK; + /* using printk() since we checked debug flag already */ + + acx_sem_lock(priv); + + if (CHIPTYPE_ACX111 != priv->chip_type) { + printk("acx111-specific function called " + "with non-acx111 chip, aborting\n"); + goto end_ok; + } + + /* get Acx111 Memory Configuration */ + memset(&memconf, 0, sizeof(memconf)); + + /* BTW, fails with 12 (Write only) error code --vda */ + if (OK != acx_s_interrogate(priv, &memconf, ACX1xx_IE_QUEUE_CONFIG)) { + printk("read memconf FAILED\n"); + } + + /* get Acx111 Queue Configuration */ + memset(&queueconf, 0, sizeof(queueconf)); + + if (OK != acx_s_interrogate(priv, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) { + printk("read queuehead FAILED\n"); + } + + /* get Acx111 Memory Map */ + memset(memmap, 0, sizeof(memmap)); + + if (OK != acx_s_interrogate(priv, &memmap, ACX1xx_IE_MEMORY_MAP)) { + printk("read mem map FAILED\n"); + } + + /* get Acx111 Rx Config */ + memset(rxconfig, 0, sizeof(rxconfig)); + + if (OK != acx_s_interrogate(priv, &rxconfig, ACX1xx_IE_RXCONFIG)) { + printk("read rxconfig FAILED\n"); + } + + /* get Acx111 fcs error count */ + memset(fcserror, 0, sizeof(fcserror)); + + if (OK != acx_s_interrogate(priv, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT)) { + printk("read fcserror FAILED\n"); + } + + /* get Acx111 rate fallback */ + memset(ratefallback, 0, sizeof(ratefallback)); + + if (OK != acx_s_interrogate(priv, &ratefallback, ACX1xx_IE_RATE_FALLBACK)) { + printk("read ratefallback FAILED\n"); + } + + /* force occurrence of a beacon interrupt */ + /* TODO: comment why is this necessary */ + acx_write_reg16(priv, IO_ACX_HINT_TRIG, HOST_INT_BEACON); + + /* dump Acx111 Mem Configuration */ + printk("dump mem config:\n" + "data read: %d, struct size: %d\n" + "Number of stations: %1X\n" + "Memory block size: %1X\n" + "tx/rx memory block allocation: %1X\n" + "count rx: %X / tx: %X queues\n" + "options %1X\n" + "fragmentation %1X\n" + "Rx Queue 1 Count Descriptors: %X\n" + "Rx Queue 1 Host Memory Start: %X\n" + "Tx Queue 1 Count Descriptors: %X\n" + "Tx Queue 1 Attributes: %X\n", + memconf.len, (int) sizeof(memconf), + memconf.no_of_stations, + memconf.memory_block_size, + memconf.tx_rx_memory_block_allocation, + memconf.count_rx_queues, memconf.count_tx_queues, + memconf.options, + memconf.fragmentation, + memconf.rx_queue1_count_descs, + memconf.rx_queue1_host_rx_start, + memconf.tx_queue1_count_descs, + memconf.tx_queue1_attributes); + + /* dump Acx111 Queue Configuration */ + printk("dump queue head:\n" + "data read: %d, struct size: %d\n" + "tx_memory_block_address (from card): %X\n" + "rx_memory_block_address (from card): %X\n" + "rx1_queue address (from card): %X\n" + "tx1_queue address (from card): %X\n" + "tx1_queue attributes (from card): %X\n", + queueconf.len, (int) sizeof(queueconf), + queueconf.tx_memory_block_address, + queueconf.rx_memory_block_address, + queueconf.rx1_queue_address, + queueconf.tx1_queue_address, + queueconf.tx1_attributes); + + /* dump Acx111 Mem Map */ + printk("dump mem map:\n" + "data read: %d, struct size: %d\n" + "Code start: %X\n" + "Code end: %X\n" + "WEP default key start: %X\n" + "WEP default key end: %X\n" + "STA table start: %X\n" + "STA table end: %X\n" + "Packet template start: %X\n" + "Packet template end: %X\n" + "Queue memory start: %X\n" + "Queue memory end: %X\n" + "Packet memory pool start: %X\n" + "Packet memory pool end: %X\n" + "iobase: %p\n" + "iobase2: %p\n", + *((u16 *)&memmap[0x02]), (int) sizeof(memmap), + *((u32 *)&memmap[0x04]), + *((u32 *)&memmap[0x08]), + *((u32 *)&memmap[0x0C]), + *((u32 *)&memmap[0x10]), + *((u32 *)&memmap[0x14]), + *((u32 *)&memmap[0x18]), + *((u32 *)&memmap[0x1C]), + *((u32 *)&memmap[0x20]), + *((u32 *)&memmap[0x24]), + *((u32 *)&memmap[0x28]), + *((u32 *)&memmap[0x2C]), + *((u32 *)&memmap[0x30]), + priv->iobase, + priv->iobase2); + + /* dump Acx111 Rx Config */ + printk("dump rx config:\n" + "data read: %d, struct size: %d\n" + "rx config: %X\n" + "rx filter config: %X\n", + *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig), + *((u16 *)&rxconfig[0x04]), + *((u16 *)&rxconfig[0x06])); + + /* dump Acx111 fcs error */ + printk("dump fcserror:\n" + "data read: %d, struct size: %d\n" + "fcserrors: %X\n", + *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror), + *((u32 *)&fcserror[0x04])); + + /* dump Acx111 rate fallback */ + printk("dump rate fallback:\n" + "data read: %d, struct size: %d\n" + "ratefallback: %X\n", + *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback), + *((u8 *)&ratefallback[0x04])); + + /* protect against IRQ */ + acx_lock(priv, flags); + + /* dump acx111 internal rx descriptor ring buffer */ + pRxDesc = pDc->pRxDescQPool; + + /* loop over complete receive pool */ + for (i = 0; i < pDc->rx_pool_count; i++) { + printk("\ndump internal rxdesc %d:\n" + "mem pos %p\n" + "next 0x%X\n" + "acx mem pointer (dynamic) 0x%X\n" + "CTL (dynamic) 0x%X\n" + "Rate (dynamic) 0x%X\n" + "RxStatus (dynamic) 0x%X\n" + "Mod/Pre (dynamic) 0x%X\n", + i, + pRxDesc, + acx2cpu(pRxDesc->pNextDesc), + acx2cpu(pRxDesc->ACXMemPtr), + pRxDesc->Ctl_8, + pRxDesc->rate, + pRxDesc->error, + pRxDesc->SNR); + pRxDesc++; + } + + /* dump host rx descriptor ring buffer */ + + rx_host_desc = pDc->pRxHostDescQPool; + + /* loop over complete receive pool */ + for (i = 0; i < pDc->rx_pool_count; i++) { + printk("\ndump host rxdesc %d:\n" + "mem pos %p\n" + "buffer mem pos 0x%X\n" + "buffer mem offset 0x%X\n" + "CTL 0x%X\n" + "Length 0x%X\n" + "next 0x%X\n" + "Status 0x%X\n", + i, + rx_host_desc, + acx2cpu(rx_host_desc->data_phy), + rx_host_desc->data_offset, + le16_to_cpu(rx_host_desc->Ctl_16), + le16_to_cpu(rx_host_desc->length), + acx2cpu(rx_host_desc->desc_phy_next), + rx_host_desc->Status); + rx_host_desc++; + } + + /* dump acx111 internal tx descriptor ring buffer */ + tx_desc = pDc->pTxDescQPool; + + /* loop over complete transmit pool */ + for (i = 0; i < pDc->tx_pool_count; i++) { + printk("\ndump internal txdesc %d:\n" + "size 0x%X\n" + "mem pos %p\n" + "next 0x%X\n" + "acx mem pointer (dynamic) 0x%X\n" + "host mem pointer (dynamic) 0x%X\n" + "length (dynamic) 0x%X\n" + "CTL (dynamic) 0x%X\n" + "CTL2 (dynamic) 0x%X\n" + "Status (dynamic) 0x%X\n" + "Rate (dynamic) 0x%X\n", + i, + (int) sizeof(struct txdesc), + tx_desc, + acx2cpu(tx_desc->pNextDesc), + acx2cpu(tx_desc->AcxMemPtr), + acx2cpu(tx_desc->HostMemPtr), + le16_to_cpu(tx_desc->total_length), + tx_desc->Ctl_8, + tx_desc->Ctl2_8, tx_desc->error, + tx_desc->u.r1.rate); + tx_desc = GET_NEXT_TX_DESC_PTR(pDc, tx_desc); + } + + /* dump host tx descriptor ring buffer */ + + tx_host_desc = pDc->pTxHostDescQPool; + + /* loop over complete host send pool */ + for (i = 0; i < pDc->tx_pool_count * 2; i++) { + printk("\ndump host txdesc %d:\n" + "mem pos %p\n" + "buffer mem pos 0x%X\n" + "buffer mem offset 0x%X\n" + "CTL 0x%X\n" + "Length 0x%X\n" + "next 0x%X\n" + "Status 0x%X\n", + i, + tx_host_desc, + acx2cpu(tx_host_desc->data_phy), + tx_host_desc->data_offset, + le16_to_cpu(tx_host_desc->Ctl_16), + le16_to_cpu(tx_host_desc->length), + acx2cpu(tx_host_desc->desc_phy_next), + le32_to_cpu(tx_host_desc->Status)); + tx_host_desc++; + } + + /* acx_write_reg16(priv, 0xb4, 0x4); */ + + acx_unlock(priv, flags); +end_ok: + + acx_sem_unlock(priv); +#endif /* ACX_DEBUG */ + return OK; +} + + +/*********************************************************************** +*/ +int +acx100_ioctl_set_phy_amp_bias( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned long flags; + u16 gpio_old; + + if (priv->chip_type != CHIPTYPE_ACX100) { + /* WARNING!!! + * Removing this check *might* damage + * hardware, since we're tweaking GPIOs here after all!!! + * You've been warned... + * WARNING!!! */ + printk("acx: sorry, setting bias level for non-acx100 " + "is not supported yet\n"); + return OK; + } + + if (*extra > 7) { + printk("acx: invalid bias parameter, range is 0-7\n"); + return -EINVAL; + } + + acx_sem_lock(priv); + + /* Need to lock accesses to [IO_ACX_GPIO_OUT]: + * IRQ handler uses it to update LED */ + acx_lock(priv, flags); + gpio_old = acx_read_reg16(priv, IO_ACX_GPIO_OUT); + acx_write_reg16(priv, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8)); + acx_unlock(priv, flags); + + acxlog(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old); + printk("%s: PHY power amplifier bias: old:%d, new:%d\n", + dev->name, + (gpio_old & 0x0700) >> 8, (unsigned char)*extra); + + acx_sem_unlock(priv); + + return OK; +} + + +/*************************************************************** +** acx_l_alloc_tx +** Actually returns a txdesc_t* ptr +*/ +tx_t* +acx_l_alloc_tx(wlandevice_t* priv) +{ + struct TIWLAN_DC *pDc = &priv->dc; + struct txdesc *tx_desc; + u8 ctl8; + + FN_ENTER; + + tx_desc = GET_TX_DESC_PTR(pDc, pDc->tx_head); + /* why?! rmb(); */ + ctl8 = tx_desc->Ctl_8; + if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_DONE))) { + /* whoops, descr at current index is not free, so probably + * ring buffer already full */ + /* FIXME: this causes a deadlock situation (endless + * loop) in case the current descriptor remains busy, + * so handle it a bit better in the future!! */ + printk("acx: BUG: tx_head->Ctl8=0x%02X, (0x%02X & " + "0x"DESC_CTL_DONE_STR") != 0x"DESC_CTL_HOSTOWN_STR + ": failed to find free tx descr\n", + ctl8, ctl8); + tx_desc = NULL; + goto end; + } + + priv->TxQueueFree--; + acxlog(L_BUFT, "tx: got desc %u, %u remain\n", pDc->tx_head, priv->TxQueueFree); + +/* + * This comment is probably not entirely correct, needs further discussion + * (restored commented-out code below to fix Tx ring buffer overflow, + * since it's much better to have a slightly less efficiently used ring + * buffer rather than one which easily overflows): + * + * This doesn't do anything other than limit our maximum number of + * buffers used at a single time (we might as well just declare + * MINFREE_TX less descriptors when we open up.) We should just let it + * slide here, and back off MINFREE_TX in acx_l_clean_tx_desc, when given the + * opportunity to let the queue start back up. + */ + if (priv->TxQueueFree < MINFREE_TX) { + acxlog(L_BUF, "stop queue (%u tx desc left)\n", + priv->TxQueueFree); + acx_stop_queue(priv->netdev, NULL); + } + + /* returning current descriptor, so advance to next free one */ + pDc->tx_head = (pDc->tx_head + 1) % pDc->tx_pool_count; +end: + FN_EXIT0; + + return (tx_t*)tx_desc; +} + + +/*************************************************************** +*/ +void* +acx_l_get_txbuf(tx_t* tx_opaque) +{ + txhostdesc_t *hostdesc = ((txdesc_t*)tx_opaque)->fixed_size.s.host_desc; + /* FIXME: happens on card eject; better method? */ + if (unlikely((long)-1 == (long)hostdesc)) + return NULL; + return hostdesc->data; +} + + +/*************************************************************** +** acx_l_dma_tx_data +** +** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). +** Can be called from acx_i_start_xmit (data frames from net core). +*/ +void +acx_l_dma_tx_data(wlandevice_t *priv, tx_t* tx_opaque, int hostdesc_len) +{ + struct txdesc *tx_desc = (struct txdesc*)tx_opaque; + struct txhostdesc *hostdesc; + client_t *clt; + u8 Ctl_8, Ctl2_8; + + FN_ENTER; + + hostdesc = tx_desc->fixed_size.s.host_desc; + /* FIXME: happens on card eject; better method? */ + if (unlikely((long)-1 == (long)hostdesc)) + goto end; + + hostdesc->data_offset = 0; + tx_desc->total_length = hostdesc->length = cpu_to_le16(hostdesc_len); + + /* modify flag status in separate variable to be able to write it back + * in one big swoop later (also in order to have less device memory + * accesses) */ + Ctl_8 = tx_desc->Ctl_8; + Ctl2_8 = tx_desc->Ctl2_8; + + /* DON'T simply set Ctl field to 0 here globally, + * it needs to maintain a consistent flag status (those are state flags!!), + * otherwise it may lead to severe disruption. Only set or reset particular + * flags at the exact moment this is needed... + * FIXME: what about Ctl2? Equally problematic? */ + + + /* let chip do RTS/CTS handshaking before sending + * in case packet size exceeds threshold */ + if (le16_to_cpu(tx_desc->total_length) > priv->rts_threshold) + SET_BIT(Ctl2_8, DESC_CTL2_RTS); + else + CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS); + +#if DEBUG_WEP + if (priv->wep_enabled) + SET_BIT(Ctl2_8, DESC_CTL2_WEP); + else + CLEAR_BIT(Ctl2_8, DESC_CTL2_WEP); +#endif + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_3_AP: + clt = acx_l_sta_list_get(priv, ((wlan_hdr_t*)hostdesc->data)->a1); + break; + case ACX_MODE_2_STA: + clt = priv->ap_client; + break; + default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ + clt = NULL; + break; + } + + if (unlikely(clt && !clt->rate_cur)) { + printk("acx: driver bug! bad ratemask\n"); + goto end; + } + + /* used in tx cleanup routine for auto rate and accounting: */ + tx_desc->fixed_size.s.txc = clt; + + if (CHIPTYPE_ACX111 == priv->chip_type) { + u16 rate_cur = clt ? clt->rate_cur : priv->rate_bcast; + /* note that if !tx_desc->do_auto, txrate->cur + ** has only one nonzero bit */ + tx_desc->u.r2.rate111 = cpu_to_le16( + rate_cur + /* WARNING: I was never able to make it work with prism54 AP. + ** It was falling down to 1Mbit where shortpre is not applicable, + ** and not working at all at "5,11 basic rates only" setting. + ** I even didn't see tx packets in radio packet capture. + ** Disabled for now --vda */ + /*| ((peer->shortpre && txrate->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */ + ); +#if TODO_FIGURE_OUT_WHEN_TO_SET_THIS + /* should add this to rate111 above as necessary */ + | (txrate->pbcc511 ? RATE111_PBCC511 : 0) +#endif + } else { /* ACX100 */ + u8 rate_100 = clt ? clt->rate_100 : priv->rate_bcast100; + tx_desc->u.r1.rate = rate_100; +#if TODO_FIGURE_OUT_WHEN_TO_SET_THIS + if (txrate->pbcc511) { + if (n == RATE100_5 || n == RATE100_11) + n |= RATE100_PBCC511; + } + + if (peer->shortpre && (txrate->cur != RATE111_1)) + SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */ +#endif + /* set autodma and reclaim and 1st mpdu */ + SET_BIT(Ctl_8, DESC_CTL_AUTODMA | DESC_CTL_RECLAIM | DESC_CTL_FIRSTFRAG); + } + /* don't need to clean ack/rts statistics here, already + * done on descr cleanup */ + + /* clears Ctl DESC_CTL_HOSTOWN bit, thus telling that the descriptors + * are now owned by the acx100; do this as LAST operation */ + CLEAR_BIT(Ctl_8, DESC_CTL_HOSTOWN); + wmb(); /* make sure everything else is written before we release our descriptor to the adapter here */ + CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); + + /* write back modified flags */ + tx_desc->Ctl2_8 = Ctl2_8; + tx_desc->Ctl_8 = Ctl_8; + + tx_desc->tx_time = cpu_to_le32(jiffies); + wmb(); /* make sure all descr writes finish before we tell the adapter that it's its turn now */ + acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); + acx_write_flush(priv); + + /* log the packet content AFTER sending it, + * in order to not delay sending any further than absolutely needed + * Do separate logs for acx100/111 to have human-readable rates */ + if (unlikely(acx_debug & (L_XFER | L_DATA))) { + u16 fc = ((wlan_hdr_t*)hostdesc->data)->fc; + if (CHIPTYPE_ACX111 == priv->chip_type) + printk("tx: pkt (%s): len %d " + "rate %04X%s status %u\n", + acx_get_packet_type_string(le16_to_cpu(fc)), + le16_to_cpu(tx_desc->total_length), + le16_to_cpu(tx_desc->u.r2.rate111), + (le16_to_cpu(tx_desc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "", + priv->status); + else + printk("tx: pkt (%s): len %d rate %03u%s status %u\n", + acx_get_packet_type_string(fc), + le16_to_cpu(tx_desc->total_length), + tx_desc->u.r1.rate, + (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "", + priv->status); + + if (acx_debug & L_DATA) { + printk("tx: 802.11 [%d]: ", hostdesc_len); + acx_dump_bytes(hostdesc->data, hostdesc_len); + } + } +end: + FN_EXIT0; +} + + +/*********************************************************************** +*/ +static void +acx_l_handle_tx_error(wlandevice_t *priv, u8 error, unsigned int finger) +{ + const char *err = "unknown error"; + + /* hmm, should we handle this as a mask + * of *several* bits? + * For now I think only caring about + * individual bits is ok... */ + switch (error) { + case 0x01: + err = "no Tx due to error in other fragment"; + priv->wstats.discard.fragment++; + break; + case 0x02: + err = "Tx aborted"; + priv->stats.tx_aborted_errors++; + break; + case 0x04: + err = "Tx desc wrong parameters"; + priv->wstats.discard.misc++; + break; + case 0x08: + err = "WEP key not found"; + priv->wstats.discard.misc++; + break; + case 0x10: + err = "MSDU lifetime timeout? - try changing " + "'iwconfig retry lifetime XXX'"; + priv->wstats.discard.misc++; + break; + case 0x20: + err = "excessive Tx retries due to either distance " + "too high or unable to Tx or Tx frame error - " + "try changing 'iwconfig txpower XXX' or " + "'sens'itivity or 'retry'"; + priv->wstats.discard.retries++; + /* FIXME: set (GETSET_TX|GETSET_RX) here + * (this seems to recalib radio on ACX100) + * after some more jiffies passed?? + * But OTOH Tx error 0x20 also seems to occur on + * overheating, so I'm not sure whether we + * actually want that, since people maybe won't notice + * then that their hardware is slowly getting + * cooked... + * Or is it still a safe long distance from utter + * radio non-functionality despite many radio + * recalibs + * to final destructive overheating of the hardware? + * In this case we really should do recalib here... + * I guess the only way to find out is to do a + * potentially fatal self-experiment :-\ + * Or maybe only recalib in case we're using Tx + * rate auto (on errors switching to lower speed + * --> less heat?) or 802.11 power save mode? */ + + /* ok, just do it. + * ENABLE_TX|ENABLE_RX helps, so even do + * DISABLE_TX and DISABLE_RX in order to perhaps + * have more impact. */ + if (++priv->retry_errors_msg_ratelimit % 4 == 0) { + if (priv->retry_errors_msg_ratelimit <= 20) + printk("%s: several excessive Tx " + "retry errors occurred, attempting " + "to recalibrate radio. Radio " + "drift might be caused by increasing " + "card temperature, please check the card " + "before it's too late!\n", + priv->netdev->name); + if (priv->retry_errors_msg_ratelimit == 20) + printk("disabling above " + "notification message\n"); + + acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); + } + break; + case 0x40: + err = "Tx buffer overflow"; + priv->stats.tx_fifo_errors++; + break; + case 0x80: + err = "DMA error"; + priv->wstats.discard.misc++; + break; + } + priv->stats.tx_errors++; + if (priv->stats.tx_errors <= 20) + printk("%s: tx error 0x%02X, buf %02u! (%s)\n", + priv->netdev->name, error, finger, err); + else + printk("%s: tx error 0x%02X, buf %02u!\n", + priv->netdev->name, error, finger); +} + + +/*********************************************************************** +*/ +/* Theory of operation: +** client->rate_cap is a bitmask of rates client is capable of. +** client->rate_cfg is a bitmask of allowed (configured) rates. +** It is set as a result of iwconfig rate N [auto] +** or iwpriv set_rates "N,N,N N,N,N" commands. +** It can be fixed (e.g. 0x0080 == 18Mbit only), +** auto (0x00ff == 18Mbit or any lower value), +** and code handles any bitmask (0x1081 == try 54Mbit,18Mbit,1Mbit _only_). +** +** client->rate_cur is a value for rate111 field in tx descriptor. +** It is always set to txrate_cfg sans zero or more most significant +** bits. This routine handles selection of new rate_cur value depending on +** outcome of last tx event. +** +** client->rate_100 is a precalculated rate value for acx100 +** (we can do without it, but will need to calculate it on each tx). +** +** You cannot configure mixed usage of 5.5 and/or 11Mbit rate +** with PBCC and CCK modulation. Either both at CCK or both at PBCC. +** In theory you can implement it, but so far it is considered not worth doing. +** +** 22Mbit, of course, is PBCC always. */ + +/* maps acx100 tx descr rate field to acx111 one */ +static u16 +rate100to111(u8 r) +{ + switch (r) { + case RATE100_1: return RATE111_1; + case RATE100_2: return RATE111_2; + case RATE100_5: + case (RATE100_5 | RATE100_PBCC511): return RATE111_5; + case RATE100_11: + case (RATE100_11 | RATE100_PBCC511): return RATE111_11; + case RATE100_22: return RATE111_22; + default: + printk("acx: unexpected acx100 txrate: %u! " + "Please report\n", r); + return RATE111_2; + } +} + + +static void +acx_l_handle_txrate_auto(wlandevice_t *priv, struct client *txc, + unsigned int idx, u8 rate100, u16 rate111, u8 error) +{ + u16 sent_rate; + u16 cur = txc->rate_cur; + int slower_rate_was_used; + + /* FIXME: need to implement some kind of rate success memory + * which stores the success percentage per rate, to be taken + * into account when considering allowing a new rate, since it + * doesn't really help to stupidly count fallback/stepup, + * since one invalid rate will spoil the party anyway + * (such as 22M in case of 11M-only peers) */ + + /* vda: hmm. current code will do this: + ** 1. send packets at 11 Mbit, stepup++ + ** 2. will try to send at 22Mbit. hardware will see no ACK, + ** retries at 11Mbit, success. code notes that used rate + ** is lower. stepup = 0, fallback++ + ** 3. repeat step 2 fallback_count times. Fall back to + ** 11Mbit. go to step 1. + ** If stepup_count is large (say, 16) and fallback_count + ** is small (3), this wouldn't be too bad wrt throughput */ + + /* do some preparations, i.e. calculate the one rate that was + * used to send this packet */ + if (CHIPTYPE_ACX111 == priv->chip_type) { + sent_rate = 1 << highest_bit(rate111 & RATE111_ALL); + } else { + sent_rate = rate100to111(rate100); + } + /* sent_rate has only one bit set now, corresponding to tx rate + * which was used by hardware to tx this particular packet */ + + /* now do the actual auto rate management */ + acxlog(L_DEBUG, "tx: %sclient=%p/"MACSTR" used=%04X cur=%04X cfg=%04X " + "__=%u/%u ^^=%u/%u\n", + (txc->ignore_count > 0) ? "[IGN] " : "", + txc, MAC(txc->address), sent_rate, cur, txc->rate_cfg, + txc->fallback_count, priv->fallback_threshold, + txc->stepup_count, priv->stepup_threshold + ); + + /* we need to ignore old packets already in the tx queue since + * they use older rate bytes configured before our last rate change, + * otherwise our mechanism will get confused by interpreting old data. + * Do it here only, in order to have the logging above */ + if (txc->ignore_count) { + txc->ignore_count--; + return; + } + + /* true only if the only nonzero bit in sent_rate is + ** less significant than highest nonzero bit in cur */ + slower_rate_was_used = ( cur > ((sent_rate<<1)-1) ); + + if (slower_rate_was_used || (error & 0x30)) { + txc->stepup_count = 0; + if (++txc->fallback_count <= priv->fallback_threshold) + return; + txc->fallback_count = 0; + + /* clear highest 1 bit in cur */ + sent_rate = RATE111_54; + while (!(cur & sent_rate)) sent_rate >>= 1; + CLEAR_BIT(cur, sent_rate); + + if (cur) { /* we can't disable all rates! */ + acxlog(L_XFER, "tx: falling back to ratemask %04X\n", cur); + txc->rate_cur = cur; + txc->ignore_count = priv->TxQueueCnt - priv->TxQueueFree; + } + } else if (!slower_rate_was_used) { + txc->fallback_count = 0; + if (++txc->stepup_count <= priv->stepup_threshold) + return; + txc->stepup_count = 0; + + /* sanitize. Sort of not needed, but I dont trust hw that much... + ** what if it can report bogus tx rates sometimes? */ + while (!(cur & sent_rate)) sent_rate >>= 1; + /* try to find a higher sent_rate that isn't yet in our + * current set, but is an allowed cfg */ + while (1) { + sent_rate <<= 1; + if (sent_rate > txc->rate_cfg) + /* no higher rates allowed by config */ + return; + if (!(cur & sent_rate) && (txc->rate_cfg & sent_rate)) + /* found */ + break; + /* not found, try higher one */ + } + SET_BIT(cur, sent_rate); + acxlog(L_XFER, "tx: stepping up to ratemask %04X\n", cur); + txc->rate_cur = cur; + /* FIXME: totally bogus - we could be sending to many peers at once... */ + txc->ignore_count = priv->TxQueueCnt - priv->TxQueueFree; + } + + /* calculate acx100 style rate byte if needed */ + if (CHIPTYPE_ACX100 == priv->chip_type) { + txc->rate_100 = bitpos2rate100[highest_bit(cur)]; + } +} + + +/*---------------------------------------------------------------- +* acx_l_log_txbuffer +*----------------------------------------------------------------*/ +#if !ACX_DEBUG +static inline void acx_l_log_txbuffer(const TIWLAN_DC *pDc) {} +#else +static void +acx_l_log_txbuffer(const TIWLAN_DC *pDc) +{ + txdesc_t *pTxDesc; + int i; + + /* no FN_ENTER here, we don't want that */ + /* no locks here, since it's entirely non-critical code */ + pTxDesc = pDc->pTxDescQPool; + for (i = 0; i < pDc->tx_pool_count; i++) { + if ((pTxDesc->Ctl_8 & DESC_CTL_DONE) == DESC_CTL_DONE) + printk("tx: buf %d done\n", i); + pTxDesc = GET_NEXT_TX_DESC_PTR(pDc, pTxDesc); + } +} +#endif + + +/*---------------------------------------------------------------- +* acx_l_clean_tx_desc +* +* Comment: +* This function probably resets the txdescs' status when the ACX100 +* signals the TX done IRQ (txdescs have been processed), starting with +* the pool index of the descriptor which we would use next, +* in order to make sure that we can be as fast as possible +* in filling new txdescs. +* Oops, now we have our own index, so everytime we get called we know +* where the next packet to be cleaned is. +* Hmm, still need to loop through the whole ring buffer now, +* since we lost sync for some reason when ping flooding or so... +* (somehow we don't get the IRQ for acx_l_clean_tx_desc any more when +* too many packets are being sent!) +* FIXME: currently we only process one packet, but this gets out of +* sync for some reason when ping flooding, so we need to loop, +* but the previous smart loop implementation causes the ping latency +* to rise dramatically (~3000 ms), at least on CardBus PheeNet WL-0022. +* Dunno what to do :-\ +*----------------------------------------------------------------*/ +unsigned int +acx_l_clean_tx_desc(wlandevice_t *priv) +{ + TIWLAN_DC *pDc = &priv->dc; + txdesc_t *pTxDesc; + struct client *txc; + unsigned int watch, finger; + unsigned int num_cleaned = 0, num_processed = 0; + u16 r111; + u8 error, ack_failures, rts_failures, rts_ok, r100; /* keep old desc status */ + + FN_ENTER; + + if (unlikely(acx_debug & L_DEBUG)) + acx_l_log_txbuffer(pDc); + + acxlog(L_BUFT, "tx: cleaning up bufs from %u\n", pDc->tx_tail); + + watch = pDc->tx_tail; + finger = watch; + + do { + pTxDesc = GET_TX_DESC_PTR(pDc, finger); + + /* abort if txdesc is not marked as "Tx finished" and "owned" */ + if ((pTxDesc->Ctl_8 & DESC_CTL_DONE) != DESC_CTL_DONE) { + /* we do need to have at least one cleaned, + * otherwise we wouldn't get called in the first place. + * So better stay around some more, unless + * we already processed more descs than the ring + * size. */ + if ((num_cleaned == 0) && (num_processed < pDc->tx_pool_count)) + goto next; + else + break; + } + + /* remember descr values... */ + error = pTxDesc->error; + ack_failures = pTxDesc->ack_failures; + rts_failures = pTxDesc->rts_failures; + rts_ok = pTxDesc->rts_ok; + r100 = pTxDesc->u.r1.rate; + r111 = pTxDesc->u.r2.rate111; + +#if WIRELESS_EXT > 13 /* wireless_send_event() and IWEVTXDROP are WE13 */ + /* need to check for certain error conditions before we + * clean the descriptor: we still need valid descr data here */ + if (unlikely(0x30 & error)) { + /* only send IWEVTXDROP in case of retry or lifetime exceeded; + * all other errors mean we screwed up locally */ + union iwreq_data wrqu; + wlan_hdr_t *hdr; + txhostdesc_t *hostdesc; + + hostdesc = pTxDesc->fixed_size.s.host_desc; + /* FIXME: happens on card eject; better method? */ + if (unlikely((long)-1 == (long)hostdesc)) + goto end; + hdr = (wlan_hdr_t *)hostdesc->data; + MAC_COPY(wrqu.addr.sa_data, hdr->a1); + wireless_send_event(priv->netdev, IWEVTXDROP, &wrqu, NULL); + } +#endif + /* ...and free the descr */ + pTxDesc->error = 0; + pTxDesc->ack_failures = 0; + pTxDesc->rts_failures = 0; + pTxDesc->rts_ok = 0; + /* signal host owning it LAST, since ACX already knows that this + * descriptor is finished since it set Ctl_8 accordingly: + * if _OWN is set at the beginning instead, our own get_tx + * might choose a Tx desc that isn't fully cleared + * (in case of bad locking). */ + pTxDesc->Ctl_8 = DESC_CTL_HOSTOWN; + priv->TxQueueFree++; + num_cleaned++; + + if ((priv->TxQueueFree >= MINFREE_TX + 3) + && (priv->status == ACX_STATUS_4_ASSOCIATED) + && (acx_queue_stopped(priv->netdev)) + ) { + acxlog(L_BUF, "tx: wake queue (avail. Tx desc %u)\n", + priv->TxQueueFree); + acx_wake_queue(priv->netdev, NULL); + } + + /* do error checking, rate handling and logging + * AFTER having done the work, it's faster */ + + /* do rate handling */ + txc = pTxDesc->fixed_size.s.txc; + if (txc && priv->rate_auto) { + acx_l_handle_txrate_auto(priv, txc, finger, r100, r111, error); + } + + if (unlikely(error)) + acx_l_handle_tx_error(priv, error, finger); + + if (CHIPTYPE_ACX111 == priv->chip_type) + acxlog(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n", + finger, ack_failures, rts_failures, rts_ok, r111); + else + acxlog(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n", + finger, ack_failures, rts_failures, rts_ok, le16_to_cpu(r111) & 0xff); +next: + /* update pointer for descr to be cleaned next */ + finger = (finger + 1) % pDc->tx_pool_count; + num_processed++; + } while (watch != finger); + + /* remember last position */ + pDc->tx_tail = finger; +end: + FN_EXIT1(num_cleaned); + return num_cleaned; +} + +/* clean *all* Tx descriptors, and regardless of their previous state. + * Used for brute-force reset handling. */ +void +acx_l_clean_tx_desc_emergency(wlandevice_t *priv) +{ + TIWLAN_DC *pDc = &priv->dc; + txdesc_t *pTxDesc; + unsigned int i; + + FN_ENTER; + + for (i = 0; i < pDc->tx_pool_count; i++) { + pTxDesc = GET_TX_DESC_PTR(pDc, i); + + /* free it */ + pTxDesc->ack_failures = 0; + pTxDesc->rts_failures = 0; + pTxDesc->rts_ok = 0; + pTxDesc->error = 0; + pTxDesc->Ctl_8 = DESC_CTL_HOSTOWN; + } + + priv->TxQueueFree = pDc->tx_pool_count; + + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_log_rxbuffer +* +* Called from IRQ context only +*----------------------------------------------------------------*/ +#if !ACX_DEBUG +static inline void acx_l_log_rxbuffer(const TIWLAN_DC *pDc) {} +#else +static void +acx_l_log_rxbuffer(const TIWLAN_DC *pDc) +{ + const struct rxhostdesc *pRxDesc; + int i; + + /* no FN_ENTER here, we don't want that */ + + pRxDesc = pDc->pRxHostDescQPool; + for (i = 0; i < pDc->rx_pool_count; i++) { + if ((pRxDesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) + && (pRxDesc->Status & cpu_to_le32(BIT31))) + printk("rx: buf %d full\n", i); + pRxDesc++; + } +} +#endif + + +/*************************************************************** +** acx_l_process_rx_desc +** +** Called directly and only from the IRQ handler +*/ +void +acx_l_process_rx_desc(wlandevice_t *priv) +{ + struct rxhostdesc *RxHostPool; + TIWLAN_DC *pDc; + struct rxhostdesc *pRxHostDesc; + unsigned int curr_idx; + unsigned int count = 0; + + FN_ENTER; + + pDc = &priv->dc; + if (unlikely(acx_debug & L_BUFR)) { + acx_l_log_rxbuffer(pDc); + } + + RxHostPool = pDc->pRxHostDescQPool; + + /* First, have a loop to determine the first descriptor that's + * full, just in case there's a mismatch between our current + * rx_tail and the full descriptor we're supposed to handle. */ + while (1) { + curr_idx = pDc->rx_tail; + pRxHostDesc = &RxHostPool[pDc->rx_tail]; + pDc->rx_tail = (pDc->rx_tail + 1) % pDc->rx_pool_count; + rmb(); + if ((pRxHostDesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) + && (pRxHostDesc->Status & cpu_to_le32(BIT31))) { + /* found it! */ + break; + } + count++; + if (unlikely(count > pDc->rx_pool_count)) { + /* hmm, no luck: all descriptors empty, bail out */ + goto end; + } + } + + /* now process descriptors, starting with the first we figured out */ + while (1) { + acxlog(L_BUFR, "%s: using curr_idx %u, rx_tail is now %u\n", + __func__, curr_idx, pDc->rx_tail); + + /* FIXME: comment needed - why rmb()? */ + rmb(); + acx_l_process_rxbuf(priv, pRxHostDesc->data); + + pRxHostDesc->Status = 0; + /* make sure adapter sees CTL_HOSTOWN change only after + * all other fields have been written */ + wmb(); + /* Host no longer owns this, needs to be LAST */ + CLEAR_BIT(pRxHostDesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); + + /* ok, descriptor is handled, now check the next descriptor */ + curr_idx = pDc->rx_tail; + pRxHostDesc = &RxHostPool[pDc->rx_tail]; + + /* if next descriptor is empty, then bail out */ + /* FIXME: is this check really entirely correct?? */ + rmb(); + /* + if (!(pRxHostDesc->Ctl & cpu_to_le16(DESC_CTL_HOSTOWN)) + && !(pRxHostDesc->Status & cpu_to_le32(BIT31))) */ + if (!(pRxHostDesc->Status & cpu_to_le32(BIT31))) + break; + else + pDc->rx_tail = (pDc->rx_tail + 1) % pDc->rx_pool_count; + } +end: + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_s_create_tx_host_desc_queue +*----------------------------------------------------------------*/ +static inline void* +acx_alloc_coherent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle, int flag) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53) + return dma_alloc_coherent(hwdev == NULL ? NULL : &hwdev->dev, + size, dma_handle, flag); +#else +#warning Using old PCI-specific DMA allocation, may fail with out-of-mem! +#warning Upgrade kernel if it does... + return pci_alloc_consistent(hwdev, size, dma_handle); +#endif +} + +static void* +allocate(wlandevice_t *priv, size_t size, dma_addr_t *phy, const char *msg) +{ + void *ptr = acx_alloc_coherent(priv->pdev, size, phy, GFP_KERNEL); + if (ptr) { + acxlog(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n", + msg, (int)size, ptr, (unsigned long long)*phy); + memset(ptr, 0, size); + return ptr; + } + printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n", + msg, (int)size); + return NULL; +} + +int +acx_s_create_tx_host_desc_queue(TIWLAN_DC *pDc) +{ + wlandevice_t *priv = pDc->priv; + struct txhostdesc *host_desc; + struct txhostdesc *host_desc_phy; + u8 *frame_buffer; + u8 *frame_buffer_phy; + unsigned int align_offs, alignment; + int i; + + FN_ENTER; + + /* allocate TX buffer */ + pDc->TxBufferPoolSize = priv->TxQueueCnt * WLAN_A4FR_MAXLEN_WEP; + pDc->pTxBufferPool = allocate(priv, pDc->TxBufferPoolSize, + &pDc->TxBufferPoolPhyAddr, "pTxBufferPool"); + if (!pDc->pTxBufferPool) + goto fail; + + /* allocate the TX host descriptor queue pool */ + pDc->TxHostDescQPoolSize = priv->TxQueueCnt * 2*sizeof(struct txhostdesc) + 3; + pDc->pTxHostDescQPool = allocate(priv, pDc->TxHostDescQPoolSize, + &pDc->TxHostDescQPoolPhyAddr, "pTxHostDescQPool"); + if (!pDc->pTxHostDescQPool) + goto fail; + + /* check for proper alignment of TX host descriptor pool */ + alignment = (long) pDc->pTxHostDescQPool & 3; + if (alignment) { + printk("acx: TxHostDescQPool is not aligned properly\n"); + align_offs = 4 - alignment; + } else { + align_offs = 0; + } + +/* Each tx frame buffer is accessed by hardware via +** txdesc -> txhostdesc(s) -> framebuffer(s) +** We use only one txhostdesc per txdesc, but it looks like +** acx111 is buggy: it accesses second txhostdesc +** (via hostdesc.desc_phy_next field) even if +** txdesc->length == hostdesc->length and thus +** entire packet was placed into first txhostdesc. +** Due to this bug acx111 hangs unless second txhostdesc +** has hostdesc.length = 3 (or larger) +** Storing NULL into hostdesc.desc_phy_next +** doesn't seem to help. +*/ +/* It is not known whether we need to have 'extra' second +** txhostdescs for acx100. Maybe it is acx111-only bug. +*/ + host_desc = (struct txhostdesc *) ((u8 *) pDc->pTxHostDescQPool + align_offs); + host_desc_phy = (struct txhostdesc *) ((u8 *) pDc->TxHostDescQPoolPhyAddr + align_offs); + frame_buffer = (u8 *) pDc->pTxBufferPool; + frame_buffer_phy = (u8 *) pDc->TxBufferPoolPhyAddr; + + for (i = 0; i < priv->TxQueueCnt*2; i++) { + if (!(i & 1)) { + host_desc->data_phy = ptr2acx(frame_buffer_phy); + /* host_desc->data_offset = ... */ + /* host_desc->reserved = ... */ + host_desc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); + host_desc->desc_phy_next = ptr2acx(host_desc_phy + 1); + host_desc->pNext = ptr2acx(NULL); + /* host_desc->Status = ... */ + /* below: non-hardware fields */ + host_desc->data = frame_buffer; + + frame_buffer += WLAN_A4FR_MAXLEN_WEP; + frame_buffer_phy += WLAN_A4FR_MAXLEN_WEP; + } else { + /* host_desc->data_phy = ... */ + /* host_desc->data_offset = ... */ + /* host_desc->reserved = ... */ + /* host_desc->Ctl_16 = ... */ + host_desc->length = 3; /* bug workaround */ + /* host_desc->desc_phy_next = ... */ + /* host_desc->pNext = ... */ + /* host_desc->Status = ... */ + /* below: non-hardware fields */ + /* host_desc->data = ... */ + } + host_desc_phy++; + host_desc++; + } + FN_EXIT1(OK); + return OK; +fail: + /* dealloc will be done by free function on error case */ + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*************************************************************** +** acx_s_create_rx_host_desc_queue +*/ +/* the whole size of a data buffer (header plus data body) + * plus 32 bytes safety offset at the end */ +#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32) + +int +acx_s_create_rx_host_desc_queue(TIWLAN_DC *pDc) +{ + wlandevice_t *priv = pDc->priv; + struct rxhostdesc *host_desc; + rxbuffer_t *data; + struct rxhostdesc *host_desc_phy; + rxbuffer_t *data_phy; + unsigned int align_offs, alignment; + int result = NOT_OK; + int i; + + FN_ENTER; + + /* allocate the RX host descriptor queue pool */ + pDc->RxHostDescQPoolSize = (priv->RxQueueCnt * sizeof(struct rxhostdesc)) + 0x3; + + pDc->pRxHostDescQPool = allocate(priv, pDc->RxHostDescQPoolSize, + &pDc->RxHostDescQPoolPhyAddr, "pRxHostDescQPool"); + if (!pDc->pRxHostDescQPool) + goto fail; + + /* allocate Rx buffer pool which will be used by the acx + * to store the whole content of the received frames in it */ + pDc->RxBufferPoolSize = ( priv->RxQueueCnt * RX_BUFFER_SIZE ); + pDc->pRxBufferPool = allocate(priv, pDc->RxBufferPoolSize, + &pDc->RxBufferPoolPhyAddr, "pRxBufferPool"); + if (!pDc->pRxBufferPool) + goto fail; + + data = pDc->pRxBufferPool; + data_phy = (rxbuffer_t *) pDc->RxBufferPoolPhyAddr; + + /* check for proper alignment of RX host descriptor pool */ + alignment = (long) pDc->pRxHostDescQPool & 3; + if (alignment) { + printk("acx: RxHostDescQPool is not aligned properly\n"); + align_offs = 4 - alignment; + } else { + align_offs = 0; + } + + host_desc = (struct rxhostdesc *) ((u8 *) pDc->pRxHostDescQPool + align_offs); + host_desc_phy = (struct rxhostdesc *) ((u8 *) pDc->RxHostDescQPoolPhyAddr + align_offs); + priv->RxHostDescPoolStart = host_desc_phy; + + /* don't make any popular C programming pointer arithmetic mistakes + * here, otherwise I'll kill you... + * (and don't dare asking me why I'm warning you about that...) */ + for (i = 0; i < priv->RxQueueCnt - 1; i++) { + host_desc->data = data; + data++; /* proceed to content of next buffer */ + host_desc->data_phy = ptr2acx(data_phy); + data_phy++; /* proceed to content of next buffer */ + host_desc->length = cpu_to_le16(RX_BUFFER_SIZE); + CLEAR_BIT(host_desc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); + host_desc->desc_phy_next = ptr2acx(host_desc_phy + 1); + host_desc_phy++; + host_desc++; + } + host_desc->data = data; + host_desc->data_phy = ptr2acx(data_phy); + host_desc->length = cpu_to_le16(RX_BUFFER_SIZE); + CLEAR_BIT(host_desc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); + host_desc->desc_phy_next = ptr2acx((u8 *) pDc->RxHostDescQPoolPhyAddr + align_offs); + result = OK; +fail: + /* dealloc will be done by free function on error case */ + FN_EXIT1(result); + return result; +} + + +/*************************************************************** +** acx_s_create_tx_desc_queue +*/ +extern void BUG_txdesc_must_be_0x30_bytes_in_length(void); + +void +acx_s_create_tx_desc_queue(TIWLAN_DC *pDc) +{ + wlandevice_t *priv = pDc->priv; + struct txdesc *tx_desc; + struct txhostdesc *tx_hostdesc; + dma_addr_t hostmemptr; /* remains 0 in USB case */ + u32 mem_offs; + int i; + + FN_ENTER; + + pDc->tx_pool_count = priv->TxQueueCnt; + + pDc->TxDescrSize = sizeof(struct txdesc); + + if (sizeof(struct txdesc) != 0x30) + BUG_txdesc_must_be_0x30_bytes_in_length(); + + if (priv->chip_type == CHIPTYPE_ACX111) { + /* the acx111 txdesc is 4 bytes larger */ + pDc->TxDescrSize = sizeof(struct txdesc)+4; + } + + if (pDc->pTxDescQPool == NULL) { /* calculate it */ + pDc->pTxDescQPool = (struct txdesc *) (priv->iobase2 + + pDc->ui32ACXTxQueueStart); + } + + acxlog(L_DEBUG, "priv->iobase2 = 0x%p\n" + "pDc->ui32ACXTxQueueStart = 0x%08X\n" + "pDc->pTxDescQPool = 0x%p\n", + priv->iobase2, + pDc->ui32ACXTxQueueStart, + pDc->pTxDescQPool); + + priv->TxQueueFree = priv->TxQueueCnt; + pDc->tx_head = 0; + pDc->tx_tail = 0; + tx_desc = pDc->pTxDescQPool; + mem_offs = pDc->ui32ACXTxQueueStart; + hostmemptr = pDc->TxHostDescQPoolPhyAddr; + tx_hostdesc = pDc->pTxHostDescQPool; + + if (CHIPTYPE_ACX111 == priv->chip_type) { + /* ACX111 has a preinitialized Tx buffer! */ + /* loop over whole send pool */ + /* FIXME: do we have to do the hostmemptr stuff here?? */ + for (i = 0; i < pDc->tx_pool_count; i++) { + tx_desc->HostMemPtr = ptr2acx(hostmemptr); + tx_desc->Ctl_8 = DESC_CTL_HOSTOWN; + tx_desc->fixed_size.s.host_desc = tx_hostdesc; + + /* reserve two (hdr desc and payload desc) */ + tx_hostdesc += 2; + hostmemptr += 2 * sizeof(struct txhostdesc); + tx_desc = (struct txdesc *)(((u8 *)tx_desc) + pDc->TxDescrSize); + } + + } else { + /* ACX100 Tx buffer needs to be initialized by us */ + /* clear whole send pool */ + memset(pDc->pTxDescQPool, 0, pDc->tx_pool_count * pDc->TxDescrSize); + + /* loop over whole send pool */ + for (i = 0; i < pDc->tx_pool_count; i++) { + acxlog(L_DEBUG, "configure card tx descriptor: 0x%p, " + "size: 0x%X\n", tx_desc, pDc->TxDescrSize); + + /* pointer to hostdesc memory */ + /* FIXME: type-incorrect assignment, might cause trouble + * in some cases */ + tx_desc->HostMemPtr = ptr2acx(hostmemptr); + /* initialise ctl */ + tx_desc->Ctl_8 = DESC_CTL_INIT; + tx_desc->Ctl2_8 = 0; + /* point to next txdesc */ + tx_desc->pNextDesc = cpu2acx(mem_offs + pDc->TxDescrSize); + /* pointer to first txhostdesc */ + tx_desc->fixed_size.s.host_desc = tx_hostdesc; + + /* reserve two (hdr desc and payload desc) */ + tx_hostdesc += 2; + hostmemptr += 2 * sizeof(struct txhostdesc); + /* go to the next one */ + mem_offs += pDc->TxDescrSize; + tx_desc = (struct txdesc *)(((u8 *)tx_desc) + pDc->TxDescrSize); + } + /* go back to the last one */ + tx_desc = (struct txdesc *)(((u8 *)tx_desc) - pDc->TxDescrSize); + /* and point to the first making it a ring buffer */ + tx_desc->pNextDesc = cpu2acx(pDc->ui32ACXTxQueueStart); + } + FN_EXIT0; +} + + +/*************************************************************** +** acx_create_rx_desc_queue +*/ +void +acx_create_rx_desc_queue(TIWLAN_DC *pDc) +{ + wlandevice_t *priv = pDc->priv; + struct rxdesc *rx_desc; + u32 mem_offs; + int i; + + FN_ENTER; + + pDc->rx_pool_count = priv->RxQueueCnt; + pDc->rx_tail = 0; + + /* ACX111 doesn't need any further config: preconfigures itself. + * Simply print ring buffer for debugging */ + if (CHIPTYPE_ACX111 == priv->chip_type) { + /* pRxDescQPool already set here */ + rx_desc = pDc->pRxDescQPool; + for (i = 0; i < pDc->rx_pool_count; i++) { + acxlog(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rx_desc); + rx_desc = pDc->pRxDescQPool = (struct rxdesc *) + (priv->iobase2 + acx2cpu(rx_desc->pNextDesc)); + } + } else { + /* we didn't pre-calculate pRxDescQPool in case of ACX100 */ + /* pRxDescQPool should be right AFTER Tx pool */ + pDc->pRxDescQPool = (struct rxdesc *) + ((u8 *) pDc->pTxDescQPool + (priv->TxQueueCnt * sizeof(struct txdesc))); + + memset(pDc->pRxDescQPool, 0, pDc->rx_pool_count * sizeof(struct rxdesc)); + + /* loop over whole receive pool */ + rx_desc = pDc->pRxDescQPool; + mem_offs = pDc->ui32ACXRxQueueStart; + for (i = 0; i < pDc->rx_pool_count; i++) { + acxlog(L_DEBUG, "rx descriptor @ 0x%p\n", rx_desc); + rx_desc->Ctl_8 = DESC_CTL_RECLAIM | DESC_CTL_AUTODMA; + /* point to next rxdesc */ + rx_desc->pNextDesc = cpu2acx(mem_offs + sizeof(struct rxdesc)); + /* go to the next one */ + mem_offs += sizeof(struct rxdesc); + rx_desc++; + } + /* go to the last one */ + rx_desc--; + + /* and point to the first making it a ring buffer */ + rx_desc->pNextDesc = cpu2acx(pDc->ui32ACXRxQueueStart); + } + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_e_init_module +* +* Module initialization routine, called once at module load time. +* +* Returns: +* 0 - success +* ~0 - failure, module is unloaded. +* +* Call context: +* process thread (insmod or modprobe) +----------------------------------------------------------------*/ +/* introduced earlier than 2.6.10, but takes more memory, so don't use it + * if there's no compile warning by kernel */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) + +#if ACX_DEBUG +module_param(acx_debug, uint, 0); +#endif +#if USE_FW_LOADER_LEGACY +module_param(firmware_dir, charp, 0); +#endif + +#else + +#if ACX_DEBUG +/* doh, 2.6.x screwed up big time: here the define has its own ";" + * ("double ; detected"), yet in 2.4.x it DOESN'T (the sane thing to do), + * grrrrr! */ +MODULE_PARM(debug, "i"); +#endif +#if USE_FW_LOADER_LEGACY +MODULE_PARM(firmware_dir, "s"); +#endif + +#endif + +MODULE_PARM_DESC(debug, "Debug level mask (see L_xxx constants)"); +#if USE_FW_LOADER_LEGACY +MODULE_PARM_DESC(firmware_dir, "Directory to load acx100 firmware files from"); +#endif +#if SEPARATE_DRIVER_INSTANCES +MODULE_PARM(card, "i"); +MODULE_PARM_DESC(card, "Associate only with card-th acx100 card from this " + "driver instance"); +#endif /* SEPARATE_DRIVER_INSTANCES */ + +static int __init +acx_e_init_module(void) +{ + int res; + + FN_ENTER; + + printk("acx: this driver is still EXPERIMENTAL\n" + "acx: reading README file and/or Craig's HOWTO is " + "recommended, visit http://acx100.sf.net in case " + "of further questions/discussion\n"); + +#if (ACX_IO_WIDTH==32) + printk("acx: compiled to use 32bit I/O access. " + "I/O timing issues might occur, such as " + "non-working firmware upload. Report them\n"); +#else + printk("acx: compiled to use 16bit I/O access only " + "(compatibility mode)\n"); +#endif + +#ifdef __LITTLE_ENDIAN + acxlog(L_INIT, "running on a little-endian CPU\n"); +#else + acxlog(L_INIT, "running on a BIG-ENDIAN CPU\n"); +#endif + acxlog(L_INIT, "acx_pci module " WLAN_RELEASE " initialized, " + "waiting for cards to probe...\n"); + + res = pci_module_init(&acx_pci_drv_id); + FN_EXIT1(res); + return res; +} + + +/*---------------------------------------------------------------- +* acx_e_cleanup_module +* +* Called at module unload time. This is our last chance to +* clean up after ourselves. +* +* Call context: +* process thread +----------------------------------------------------------------*/ +static void __exit +acx_e_cleanup_module(void) +{ + const struct net_device *dev; + unsigned long flags; + + FN_ENTER; + + /* Since the whole module is about to be unloaded, + * we recursively shutdown all cards we handled instead + * of doing it in remove_pci() (which will be activated by us + * via pci_unregister_driver at the end). + * remove_pci() might just get called after a card eject, + * that's why hardware operations have to be done here instead + * when the hardware is available. */ + + down(&root_acx_dev_sem); + + dev = root_acx_dev.newest; + while (dev != NULL) { + /* doh, netdev_priv() doesn't have const! */ + wlandevice_t *priv = acx_netdev_priv((struct net_device *)dev); + + acx_sem_lock(priv); + + /* disable both Tx and Rx to shut radio down properly */ + acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0); + acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0); + +#if REDUNDANT + /* put the eCPU to sleep to save power + * Halting is not possible currently, + * since not supported by all firmware versions */ + acx_s_issue_cmd(priv, ACX100_CMD_SLEEP, NULL, 0); +#endif + acx_lock(priv, flags); + + /* disable power LED to save power :-) */ + acxlog(L_INIT, "switching off power LED to save power :-)\n"); + acx_l_power_led(priv, 0); + + /* stop our eCPU */ + if (priv->chip_type == CHIPTYPE_ACX111) { + /* FIXME: does this actually keep halting the eCPU? + * I don't think so... + */ + acx_l_reset_mac(priv); + } else { + u16 temp; + + /* halt eCPU */ + temp = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1; + acx_write_reg16(priv, IO_ACX_ECPU_CTRL, temp); + acx_write_flush(priv); + } + + acx_unlock(priv, flags); + + acx_sem_unlock(priv); + + dev = priv->prev_nd; + } + + up(&root_acx_dev_sem); + + /* now let the PCI layer recursively remove + * all PCI related things (acx_e_remove_pci()) */ + pci_unregister_driver(&acx_pci_drv_id); + + FN_EXIT0; +} + +module_init(acx_e_init_module) +module_exit(acx_e_cleanup_module) + +#if USE_FW_LOADER_LEGACY +static int __init acx_get_firmware_dir(const char *str) +{ + /* I've seen other drivers just pass the string pointer, + * so hopefully that's safe */ + firmware_dir = str; + return OK; +} + +__setup("acx_firmware_dir=", acx_get_firmware_dir); +#endif diff -puN /dev/null drivers/net/wireless/tiacx/pci_helper.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/pci_helper.c 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,2 @@ +#define ACX_PCI 1 +#include "helper.c" diff -puN /dev/null drivers/net/wireless/tiacx/setrate.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/setrate.c 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,213 @@ +/* TODO: stop #including, move into wireless.c + * until then, keep in sync copies in prism54/ and acx/ dirs + * code+data size: less than 1k */ + +enum { + DOT11_RATE_1, + DOT11_RATE_2, + DOT11_RATE_5, + DOT11_RATE_11, + DOT11_RATE_22, + DOT11_RATE_33, + DOT11_RATE_6, + DOT11_RATE_9, + DOT11_RATE_12, + DOT11_RATE_18, + DOT11_RATE_24, + DOT11_RATE_36, + DOT11_RATE_48, + DOT11_RATE_54 +}; +enum { + DOT11_MOD_DBPSK, + DOT11_MOD_DQPSK, + DOT11_MOD_CCK, + DOT11_MOD_OFDM, + DOT11_MOD_CCKOFDM, + DOT11_MOD_PBCC +}; +static const u8 ratelist[] = { 1,2,5,11,22,33,6,9,12,18,24,36,48,54 }; +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 }; +static const u8 default_modulation[] = { + DOT11_MOD_DBPSK, + DOT11_MOD_DQPSK, + DOT11_MOD_CCK, + DOT11_MOD_CCK, + DOT11_MOD_PBCC, + DOT11_MOD_PBCC, + DOT11_MOD_OFDM, + DOT11_MOD_OFDM, + DOT11_MOD_OFDM, + DOT11_MOD_OFDM, + DOT11_MOD_OFDM, + DOT11_MOD_OFDM, + DOT11_MOD_OFDM, + DOT11_MOD_OFDM +}; + +static /* TODO: remove 'static' when moved to wireless.c */ +int +rate_mbit2enum(int n) { + int i=0; + while(i=DOT11_RATE_6) return DOT11_MOD_OFDM; */ + return default_modulation[r_enum]; + } + if(suffix=='c') { + if(r_enumDOT11_RATE_11) return -EINVAL; + return DOT11_MOD_CCK; + } + if(suffix=='p') { + if(r_enumDOT11_RATE_33) return -EINVAL; + return DOT11_MOD_PBCC; + } + if(suffix=='o') { + if(r_enumINT_MAX) return -EINVAL; + + rate_enum = rate_mbit2enum(rate_mbit); + if(rate_enum<0) return rate_enum; + + c = *str; + mod = get_modulation(rate_enum, c); + if(mod<0) return mod; + + if(c>='a' && c<='z') c = *++str; + if(c!=',' && c!=' ' && c!='\0') return -EINVAL; + + if(supported) { + int r = supported(rate_mbit, mod, opaque); + if(r) return r; + } + + *vector++ = dot11ratebyte[rate_enum] | or_mask; + + size--; + str++; + } while(size>0 && c==','); + + if(size<1) return -E2BIG; + *vector=0; /* TODO: sort, remove dups? */ + + *pstr = str-1; + return 0; +} + +static /* TODO: remove 'static' when moved to wireless.c */ +int +fill_ratevectors(const char *str, u8 *brate, u8 *orate, int size, + int (*supported)(int mbit, int mod, void *opaque), void *opaque) +{ + int r; + + r = fill_ratevector(&str, brate, size, supported, opaque, 0x80); + if(r) return r; + + orate[0] = 0; + if(*str==' ') { + str++; + r = fill_ratevector(&str, orate, size, supported, opaque, 0); + if(r) return r; + /* TODO: sanitize, e.g. remove/error on rates already in basic rate set? */ + } + if(*str) + return -EINVAL; + + return 0; +} +#endif + +/* TODO: use u64 masks? */ + +static int +fill_ratemask(const char **pstr, u32* mask, + int (*supported)(int mbit, int mod,void *opaque), + u32 (*gen_mask)(int mbit, int mod,void *opaque), + void *opaque) +{ + unsigned long rate_mbit; + int rate_enum,mod; + u32 m = 0; + const char *str = *pstr; + char c; + + do { + rate_mbit = simple_strtoul(str, (char**)&str, 10); + if(rate_mbit>INT_MAX) return -EINVAL; + + rate_enum = rate_mbit2enum(rate_mbit); + if(rate_enum<0) return rate_enum; + + c = *str; + mod = get_modulation(rate_enum, c); + if(mod<0) return mod; + + if(c>='a' && c<='z') c = *++str; + if(c!=',' && c!=' ' && c!='\0') return -EINVAL; + + if(supported) { + int r = supported(rate_mbit, mod, opaque); + if(r) return r; + } + + m |= gen_mask(rate_mbit, mod, opaque); + str++; + } while(c==','); + + *pstr = str-1; + *mask |= m; + return 0; +} + +static /* TODO: remove 'static' when moved to wireless.c */ +int +fill_ratemasks(const char *str, u32 *bmask, u32 *omask, + int (*supported)(int mbit, int mod,void *opaque), + u32 (*gen_mask)(int mbit, int mod,void *opaque), + void *opaque) +{ + int r; + + r = fill_ratemask(&str, bmask, supported, gen_mask, opaque); + if(r) return r; + + if(*str==' ') { + str++; + r = fill_ratemask(&str, omask, supported, gen_mask, opaque); + if(r) return r; + } + if(*str) + return -EINVAL; + return 0; +} diff -puN /dev/null drivers/net/wireless/tiacx/usb.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/usb.c 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,1967 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +/*********************************************************************** +** USB support for TI ACX100 based devices. Many parts are taken from +** the PCI driver. +** +** Authors: +** Martin Wawro +** Andreas Mohr +** +** Issues: +** - Note that this driver relies on a native little-endian byteformat +** at some points +** +** LOCKING +** callback functions called by USB core are running in interrupt context +** and thus have names with _i_. +*/ +#define ACX_USB 1 + +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) +#include +#endif +#include +#include +#include +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#endif + +#include "acx.h" + + +/*********************************************************************** +** try to make it compile for both 2.4.x and 2.6.x kernels +*/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) + +/* number of endpoints of an interface */ +#define NUM_EP(intf) (intf)->altsetting[0].desc.bNumEndpoints +#define EP(intf, nr) (intf)->altsetting[0].endpoint[(nr)].desc +#define GET_DEV(udev) usb_get_dev((udev)) +#define PUT_DEV(udev) usb_put_dev((udev)) +#define SET_NETDEV_OWNER(ndev, owner) /* not needed anymore ??? */ + +#define ASYNC_UNLINK URB_ASYNC_UNLINK +#define QUEUE_BULK 0 +#define ZERO_PACKET URB_ZERO_PACKET + +static inline int +submit_urb(struct urb *urb, int mem_flags) +{ + return usb_submit_urb(urb, mem_flags); +} +static inline struct urb* +alloc_urb(int iso_pk, int mem_flags) +{ + return usb_alloc_urb(iso_pk, mem_flags); +} + +#else + +/* 2.4.x kernels */ +#define USB_24 1 + +#define NUM_EP(intf) (intf)->altsetting[0].bNumEndpoints +#define EP(intf, nr) (intf)->altsetting[0].endpoint[(nr)] + +#define GET_DEV(udev) usb_inc_dev_use((udev)) +#define PUT_DEV(udev) usb_dec_dev_use((udev)) + +#ifndef SET_NETDEV_DEV +#define SET_NETDEV_DEV(x, y) +#endif +#define SET_NETDEV_OWNER(ndev, owner) ndev->owner = owner + +#define ASYNC_UNLINK USB_ASYNC_UNLINK +#define QUEUE_BULK USB_QUEUE_BULK +#define ZERO_PACKET USB_ZERO_PACKET + +static inline int +submit_urb(struct urb *urb, int mem_flags) +{ + return usb_submit_urb(urb); +} +static inline struct urb* +alloc_urb(int iso_pk, int mem_flags) +{ + return usb_alloc_urb(iso_pk); +} + +static inline void +usb_set_intfdata(struct usb_interface *intf, void *data) +{ +} + +#endif /* #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) */ + + +/*********************************************************************** +** Module Stuff +*/ +#if ACX_DEBUG +unsigned int acx_debug = L_ASSOC|L_INIT; +#endif + +#if USE_FW_LOADER_LEGACY +char *firmware_dir; +#endif + +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual MPL/GPL"); +#endif +MODULE_AUTHOR("Martin Wawro "); +MODULE_DESCRIPTION("TI ACX100 WLAN USB Driver"); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) + +#if ACX_DEBUG +module_param(acx_debug, uint, 0); +#endif +#if USE_FW_LOADER_LEGACY +module_param(firmware_dir, charp, 0); +#endif + +#else + +#if ACX_DEBUG +MODULE_PARM(debug, "i"); +#endif +#if USE_FW_LOADER_LEGACY +MODULE_PARM(firmware_dir, "s"); +#endif + +#endif + +MODULE_PARM_DESC(debug, "Debug level mask: 0x0000 - 0x3fff"); +#if USE_FW_LOADER_LEGACY +MODULE_PARM_DESC(firmware_dir, "Directory to load acx100 firmware files from"); +#endif + +/*********************************************************************** +*/ +#define ACX100_VENDOR_ID 0x2001 +#define ACX100_PRODUCT_ID_UNBOOTED 0x3B01 +#define ACX100_PRODUCT_ID_BOOTED 0x3B00 + +/* RX-Timeout: NONE (request waits forever) */ +#define ACX100_USB_RX_TIMEOUT (0) + +#define ACX100_USB_TX_TIMEOUT (4*HZ) + +#define USB_CTRL_HARD_TIMEOUT 5500 /* steps in ms */ + + +/*********************************************************************** +** Prototypes +*/ +#if USB_24 +static void* acx100usb_e_probe(struct usb_device *, unsigned int, const struct usb_device_id *); +static void acx100usb_e_disconnect(struct usb_device *, void *); +static void acx100usb_i_complete_tx(struct urb *); +static void acx100usb_i_complete_rx(struct urb *); +#else +static int acx100usb_e_probe(struct usb_interface *, const struct usb_device_id *); +static void acx100usb_e_disconnect(struct usb_interface *); +static void acx100usb_i_complete_tx(struct urb *, struct pt_regs *); +static void acx100usb_i_complete_rx(struct urb *, struct pt_regs *); +#endif +static int acx100usb_e_open(struct net_device *); +static int acx100usb_e_close(struct net_device *); +static void acx100usb_i_set_rx_mode(struct net_device *); +static int acx100usb_e_init_network_device(struct net_device *); +static int acx100usb_boot(struct usb_device *); + +static struct net_device_stats * acx_e_get_stats(struct net_device *); +static struct iw_statistics *acx_e_get_wireless_stats(struct net_device *); + +static void acx100usb_l_poll_rx(wlandevice_t *, int number); + +static void acx100usb_i_tx_timeout(struct net_device *); + +/* static void dump_device(struct usb_device *); */ +/* static void dump_device_descriptor(struct usb_device_descriptor *); */ +/* static void dump_config_descriptor(struct usb_config_descriptor *); */ +#if USB_24 +static void dump_endpoint_descriptor(struct usb_endpoint_descriptor *); +static void dump_interface_descriptor(struct usb_interface_descriptor *); +#endif + + +/*********************************************************************** +** Module Data +*/ +#define TXBUFSIZE sizeof(usb_txbuffer_t) +//// Bogus! We CANNOT pretend that rxbuffer_t is larger than it is. +/* make it a multiply of 64 */ +/* #define RXBUFSIZE ((sizeof(rxbuffer_t)+63) & ~63) */ +#define RXBUFSIZE sizeof(rxbuffer_t) + +static const struct usb_device_id +acx100usb_ids[] = { + { USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_BOOTED) }, + { USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_UNBOOTED) }, + {} +}; + + +/* USB driver data structure as required by the kernel's USB core */ +static struct usb_driver +acx100usb_driver = { + .name = "acx_usb", + .probe = acx100usb_e_probe, + .disconnect = acx100usb_e_disconnect, + .id_table = acx100usb_ids +}; + + +/*********************************************************************** +** USB helper +*/ +static void +acx_unlink_and_free_urb(struct urb* urb) { + if (!urb) + return; + if (urb->status == -EINPROGRESS) { + usb_unlink_urb(urb); +//TODO: timeout! + while (urb->status == -EINPROGRESS) { + mdelay(2); + } + } + usb_free_urb(urb); +} + + +/*********************************************************************** +*/ +#if ACX_DEBUG +#if USB_24 +static char* +acx100usb_pstatus(int val) +{ +#define CASE(status) case status: return ""#status"" + switch (val) { + CASE(USB_ST_NOERROR); + CASE(USB_ST_CRC); + CASE(USB_ST_BITSTUFF); + CASE(USB_ST_DATAOVERRUN); + CASE(USB_ST_BUFFEROVERRUN); + CASE(USB_ST_BUFFERUNDERRUN); + CASE(USB_ST_SHORT_PACKET); + CASE(USB_ST_URB_KILLED); + CASE(USB_ST_URB_PENDING); + CASE(USB_ST_REMOVED); + CASE(USB_ST_TIMEOUT); + CASE(USB_ST_NOTSUPPORTED); + CASE(USB_ST_BANDWIDTH_ERROR); + CASE(USB_ST_URB_INVALID_ERROR); + CASE(USB_ST_URB_REQUEST_ERROR); + CASE(USB_ST_STALL); + default: + return "UNKNOWN"; + } +} +#else +static char* +acx100usb_pstatus(int val) +{ + static char status[20]; + + if (val < 0) + sprintf(status, "errno %d", -val); + else + sprintf(status, "length %d", val); + + return status; +} +#endif /* USB_24 */ +#endif /* ACX_DEBUG */ + + +/*********************************************************************** +** EEPROM and PHY read/write helpers +*/ +/*********************************************************************** +** acx_s_read_phy_reg +*/ +int +acx_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf) +{ + int result = NOT_OK; + mem_read_write_t mem; + + FN_ENTER; + + mem.addr = cpu_to_le16(reg); + mem.type = cpu_to_le16(0x82); + mem.len = cpu_to_le32(4); + acx_s_issue_cmd(priv, ACX1xx_CMD_MEM_READ, &mem, 8); + *charbuf = mem.data; + acxlog(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg); + result = OK; + goto fail; /* silence compiler warning */ +fail: + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +*/ +int +acx_s_write_phy_reg(wlandevice_t *priv, u32 reg, u8 value) +{ + mem_read_write_t mem; + + FN_ENTER; + + mem.addr = cpu_to_le16(reg); + mem.type = cpu_to_le16(0x82); + mem.len = cpu_to_le32(4); + mem.data = value; + acx_s_issue_cmd(priv, ACX1xx_CMD_MEM_WRITE, &mem, sizeof(mem)); + acxlog(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg); + + FN_EXIT1(OK); + return OK; +} + + +/*********************************************************************** +** acx_s_issue_cmd_timeo +** Excecutes a command in the command mailbox +** +** Arguments: +** *pcmdparam = an pointer to the data. The data mustn't include +** the 4 byte command header! +*/ +#undef FUNC +#define FUNC "issue_cmd" + +#if !ACX_DEBUG +int +acx_s_issue_cmd_timeo( + wlandevice_t *priv, + unsigned cmd, + void *pdr, + unsigned paramlen, + unsigned timeout) +{ +#else +int +acx_s_issue_cmd_timeo_debug( + wlandevice_t *priv, + unsigned cmd, + void *pdr, + unsigned paramlen, + unsigned timeout, + const char* cmdstr) +{ +#endif + /* USB ignores timeout param */ + + struct usb_device *usbdev; + struct { + u16 cmd ACX_PACKED; + u16 status ACX_PACKED; + u8 data[3000]; //former boguspad. try [ACX100_USB_RWMEM_MAXLEN-4] later! + } *loc; + int acklen, blocklen, inpipe, outpipe; + int result; + + FN_ENTER; + + acxlog(L_CTL, FUNC"(cmd:%s,paramlen:%u,type:%d)\n", + cmdstr, paramlen, + pdr ? le16_to_cpu(((acx_ie_generic_t *)pdr)->type) : -1); + + loc = kmalloc(sizeof(*loc), GFP_KERNEL); + if (!loc) { + printk("acx: "FUNC"() failed to alloc data buffer\n"); + goto fail; + } + + /* get context from wlandevice */ + usbdev = priv->usbdev; + + /* check which kind of command was issued */ + loc->cmd = cpu_to_le16(cmd); + loc->status = 0; + +/* NB: paramlen == ((acx_ie_generic_t*)pdr)->len + 4 on entry +** +** Interrogate: write 8-byte (cmd,status,rid,frmlen), +** read data[frmlen+8] back +** Configure: write (cmd,status,rid,frmlen,data[frmlen]) +** +** Possibly bogus special handling of ACX1xx_IE_SCAN_STATUS removed +*/ + + /* now write the parameters of the command if needed */ + acklen = sizeof(loc->data); + blocklen = paramlen; + if (pdr && paramlen) { + /* if it's an INTERROGATE command, just pass the length + * of parameters to read, as data */ + if (cmd == ACX1xx_CMD_INTERROGATE) { + blocklen = 4; + acklen = paramlen + 4; + } + memcpy(loc->data, pdr, blocklen); + } + blocklen += 4; /* account for cmd,status */ + + /* obtain the I/O pipes */ + outpipe = usb_sndctrlpipe(usbdev, 0); + inpipe = usb_rcvctrlpipe(usbdev, 0); + acxlog(L_CTL, "ctrl inpipe=0x%X outpipe=0x%X\n", inpipe, outpipe); + acxlog(L_CTL, "sending USB control msg (out) (blocklen=%d)\n", blocklen); + if (acx_debug & L_DATA) + acx_dump_bytes(loc, blocklen); + + result = usb_control_msg(usbdev, outpipe, + ACX_USB_REQ_CMD, /* request */ + USB_TYPE_VENDOR|USB_DIR_OUT, /* requesttype */ + 0, /* value */ + 0, /* index */ + loc, /* dataptr */ + blocklen, /* size */ + USB_CTRL_HARD_TIMEOUT /* timeout in ms */ + ); + acxlog(L_CTL, "wrote %d bytes\n", result); + if (result < 0) { + goto fail; + } + + /* check for device acknowledge */ + acxlog(L_CTL, "sending USB control msg (in) (acklen=%d) " + "sizeof(loc->data)=%d\n", acklen, (int) sizeof(loc->data)); + loc->status = 0; /* delete old status flag -> set to fail */ +//shall we zero out the rest? + result = usb_control_msg(usbdev, inpipe, + ACX_USB_REQ_CMD, /* request */ + USB_TYPE_VENDOR|USB_DIR_IN, /* requesttype */ + 0, /* value */ + 0, /* index */ + loc, /* dataptr */ + acklen, /* size */ + USB_CTRL_HARD_TIMEOUT /* timeout in ms */ + ); + acxlog(L_CTL, "read %d bytes\n", result); + if (result < 0) { + goto fail; + } + +//check for result==paramlen+4? Was seen: +//interrogate(type:ACX100_IE_DOT11_ED_THRESHOLD,len:4) +//issue_cmd(cmd:ACX1xx_CMD_INTERROGATE,paramlen:8,type:4111) +//ctrl inpipe=0x80000280 outpipe=0x80000200 +//sending USB control msg (out) (blocklen=8) +//01 00 00 00 0F 10 04 00 +//wrote 8 bytes +//sending USB control msg (in) (acklen=12) sizeof(loc->data +//read 4 bytes <==== MUST BE 12!! + + if (le16_to_cpu(loc->status) != 1) { + printk("%s: warning: command returned status %u\n", + priv->netdev->name, le16_to_cpu(loc->status)); + } + if ((cmd == ACX1xx_CMD_INTERROGATE) && pdr && paramlen) { + memcpy(pdr, loc->data, paramlen); + acxlog(L_CTL, "response frame: cmd=%u status=%u\n", + le16_to_cpu(loc->cmd), + le16_to_cpu(loc->status)); + if (acx_debug & L_DATA) { + printk("incoming bytes (%d):\n", paramlen); + acx_dump_bytes(pdr, paramlen); + } + } + kfree(loc); + FN_EXIT1(OK); + return OK; +fail: + kfree(loc); + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*********************************************************************** +** acx100usb_e_probe() +** +** Inputs: +** dev -> Pointer to usb_device structure that may or may not be claimed +** ifNum -> Interface number +** devID -> Device ID (vendor and product specific stuff) +************************************************************************ +** Returns: +** (void *) Pointer to (custom) driver context or NULL if we are not interested +** or unable to handle the offered device. +** +** Description: +** This function is invoked by the kernel's USB core whenever a new device is +** attached to the system or the module is loaded. It is presented a usb_device +** structure from which information regarding the device is obtained and evaluated. +** In case this driver is able to handle one of the offered devices, it returns +** a non-null pointer to a driver context and thereby claims the device. +*/ +#if USB_24 +#define OUTOFMEM NULL +static void* +acx100usb_e_probe(struct usb_device *usbdev, unsigned int ifNum, + const struct usb_device_id *devID) +{ + void *res = NULL; +#else +#define OUTOFMEM -ENOMEM +static int +acx100usb_e_probe(struct usb_interface *intf, const struct usb_device_id *devID) +{ + struct usb_device *usbdev = interface_to_usbdev(intf); + int res = 0; +#endif + wlandevice_t *priv = NULL; + struct net_device *dev = NULL; + struct usb_config_descriptor *config; + struct usb_endpoint_descriptor *epdesc; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + struct usb_host_endpoint *ep; +#endif + struct usb_interface_descriptor *ifdesc; + const char* msg; + int i, numep; + int numconfigs, numfaces, result = 0; + + FN_ENTER; + + /* First check if this is the "unbooted" hardware */ + if ((usbdev->descriptor.idVendor == ACX100_VENDOR_ID) + && (usbdev->descriptor.idProduct == ACX100_PRODUCT_ID_UNBOOTED)) { + /* Boot the device (i.e. upload the firmware) */ + acx100usb_boot(usbdev); + + /* OK, we are done with booting. Normally, the + ** ID for the unbooted device should disappear + ** and it will not need a driver anyway...so + ** return a NULL + */ + acxlog(L_INIT, "finished booting, returning from probe()\n"); +#if USB_24 + res = NULL; +#else + res = 0; /* is that ok?? */ +#endif + goto end; + } + + if ((usbdev->descriptor.idVendor != ACX100_VENDOR_ID) + || (usbdev->descriptor.idProduct != ACX100_PRODUCT_ID_BOOTED)) { + goto end_nodev; + } + + /* Ok, so it's our device and it is already booted */ + + /* allocate memory for the device driver context */ + priv = kmalloc(sizeof(struct wlandevice), GFP_KERNEL); + if (!priv) { + result = sizeof(struct wlandevice); + msg = "acx: could not allocate %d bytes " + "for device driver context\n"; + goto end_nomem; + } + memset(priv, 0, sizeof(wlandevice_t)); + priv->chip_type = CHIPTYPE_ACX100; + /* FIXME: should be read from register (via firmware) using standard ACX code */ + priv->radio_type = RADIO_MAXIM_0D; + priv->usbdev = usbdev; + + spin_lock_init(&priv->lock); /* initial state: unlocked */ + sema_init(&priv->sem, 1); /* initial state: 1 (upped) */ + + /* Initialize the device context and also check + ** if this is really the hardware we know about. + ** If not sure, at least notify the user that he + ** may be in trouble... + */ + numconfigs = (int)usbdev->descriptor.bNumConfigurations; + if (numconfigs != 1) + printk("acx: number of configurations is %d, " + "this driver only knows how to handle 1, " + "be prepared for surprises\n", numconfigs); +#if USB_24 + config = usbdev->actconfig; +#else + config = &usbdev->config->desc; +#endif + numfaces = config->bNumInterfaces; + if (numfaces != 1) + printk("acx: number of interfaces is %d, " + "this driver only knows how to handle 1, " + "be prepared for surprises\n", numfaces); +#if USB_24 + ifdesc = config->interface->altsetting; +#else + ifdesc = &intf->altsetting->desc; +#endif + numep = ifdesc->bNumEndpoints; + acxlog(L_DEBUG, "# of endpoints: %d\n", numep); + + /* obtain information about the endpoint + ** addresses, begin with some default values + */ + priv->bulkoutep = 1; + priv->bulkinep = 1; + for (i = 0; i < numep; i++) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + ep = usbdev->ep_in[i]; + if (!ep) + continue; + epdesc = &ep->desc; +#else + epdesc = usb_epnum_to_ep_desc(usbdev, i); + if (!epdesc) + continue; +#endif + if (epdesc->bmAttributes & USB_ENDPOINT_XFER_BULK) { + if (epdesc->bEndpointAddress & 0x80) + priv->bulkinep = epdesc->bEndpointAddress & 0xF; + else + priv->bulkoutep = epdesc->bEndpointAddress & 0xF; + } + } + acxlog(L_DEBUG, "bulkout ep: 0x%X\n", priv->bulkoutep); + acxlog(L_DEBUG, "bulkin ep: 0x%X\n", priv->bulkinep); + + /* Set the packet-size equivalent to the buffer size */ + /* already done by memset: priv->rxtruncsize = 0; */ + acxlog(L_DEBUG, "TXBUFSIZE=%d RXBUFSIZE=%d\n", + (int) TXBUFSIZE, (int) RXBUFSIZE); + + priv->usb_free_tx = ACX100_USB_NUM_BULK_URBS; + /* already done by memset: + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { + priv->usb_tx[i].busy = 0; + } + */ + + /* Allocate memory for a network device */ + dev = kmalloc(sizeof(struct net_device), GFP_ATOMIC); + if (!dev) { + msg = "acx: no memory for netdev\n"; + goto end_nomem; + } + + /* Setup network device */ + memset(dev, 0, sizeof(struct net_device)); + dev->init = (void *)&acx100usb_e_init_network_device; + dev->priv = priv; + priv->netdev = dev; + + /* Setup URBs for bulk-in/out messages */ + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { + priv->bulkrx_urbs[i] = alloc_urb(0, GFP_KERNEL); + if (!priv->bulkrx_urbs[i]) { + msg = "acx: no memory for input URB\n"; + goto end_nomem; + } + priv->bulkrx_urbs[i]->status = 0; + + priv->usb_tx[i].urb = alloc_urb(0, GFP_KERNEL); + if (!priv->usb_tx[i].urb) { + msg = "acx: no memory for output URB\n"; + goto end_nomem; + } + priv->usb_tx[i].urb->status = 0; + + priv->usb_tx[i].priv = priv; + } + + /* Allocate a network device name */ + acxlog(L_INIT, "allocating device name\n"); + result = dev_alloc_name(dev, "wlan%d"); + if (result < 0) { + msg = "acx: no memory for wlan device name\n"; + goto end_nomem; + } +#if USB_26 + usb_set_intfdata(intf, priv); + SET_NETDEV_DEV(dev, &intf->dev); +#endif + /* Register the network device */ + acxlog(L_INIT, "registering network device\n"); + result = register_netdev(dev); + if (result != 0) { + msg = "acx: failed to register network device " + "for USB WLAN (errcode=%d)\n"; + goto end_nomem; + } +#ifdef CONFIG_PROC_FS + if (OK != acx_proc_register_entries(dev)) { + printk("acx: /proc registration failed\n"); + } +#endif + + /* Everything went OK, we are happy now */ +#if USB_24 + res = priv; +#else + res = 0; +#endif + goto end; + +end_nomem: + printk(msg, result); + + /* we rely on the fact that free(NULL) is harmless */ + kfree(dev); + if (priv) { + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { + usb_free_urb(priv->bulkrx_urbs[i]); + usb_free_urb(priv->usb_tx[i].urb); + } + kfree(priv); + } + res = OUTOFMEM; + goto end; + +end_nodev: + + /* no device we could handle, return NULL */ +#if USB_24 + res = NULL; +#else + res = -EIO; +#endif +end: + FN_EXIT1((int)res); + return res; +} + + +/*********************************************************************** +** acx100usb_e_disconnect(): +** Inputs: +** dev -> Pointer to usb_device structure handled by this module +** devContext -> Pointer to own device context (acx100usb_context) +************************************************************************ +** Description: +** This function is invoked whenever the user pulls the plug from the USB +** device or the module is removed from the kernel. In these cases, the +** network devices have to be taken down and all allocated memory has +** to be freed. +*/ +#if USB_24 +static void +acx100usb_e_disconnect(struct usb_device *usbdev, void *devContext) +{ + wlandevice_t *priv = (wlandevice_t *)devContext; +#else +static void +acx100usb_e_disconnect(struct usb_interface *intf) +{ + wlandevice_t *priv = usb_get_intfdata(intf); +#endif + int i, result; + + /* No WLAN device...no sense */ + if (!priv) + return; + + acx_sem_lock(priv); + + /* stop the transmit queue */ + if (priv->netdev) { + rtnl_lock(); + if (!acx_queue_stopped(priv->netdev)) { + acx_stop_queue(priv->netdev, "on USB disconnect"); + } + rtnl_unlock(); +#ifdef CONFIG_PROC_FS + acx_proc_unregister_entries(priv->netdev); +#endif + } + + /* now abort pending URBs and free them */ + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { + acx_unlink_and_free_urb(priv->bulkrx_urbs[i]); + acx_unlink_and_free_urb(priv->usb_tx[i].urb); + } + + /* Unregister the network devices */ + if (priv->netdev) { + rtnl_lock(); + result = unregister_netdevice(priv->netdev); + rtnl_unlock(); + kfree(priv->netdev); + } + + acx_sem_unlock(priv); + + /* finally free the WLAN device */ + kfree(priv); +} + + +/*********************************************************************** +** acx100usb_boot(): +** Inputs: +** usbdev -> Pointer to kernel's usb_device structure +** endpoint -> Address of the endpoint for control transfers +************************************************************************ +** Returns: +** (int) Errorcode or 0 on success +** +** Description: +** This function triggers the loading of the firmware image from harddisk +** and then uploads the firmware to the USB device. After uploading the +** firmware and transmitting the checksum, the device resets and appears +** as a new device on the USB bus (the device we can finally deal with) +*/ +static int +acx100usb_boot(struct usb_device *usbdev) +{ + static const char filename[] = "tiacx100usb"; + + char *firmware = NULL; + char *usbbuf; + unsigned int offset; + unsigned int len, inpipe, outpipe; + u32 checksum; + u32 size; + int result; + + usbbuf = kmalloc(ACX100_USB_RWMEM_MAXLEN, GFP_KERNEL); + if (!usbbuf) { + printk(KERN_ERR "acx: no memory for USB transfer buffer (" + STRING(ACX100_USB_RWMEM_MAXLEN)" bytes)\n"); + result = -ENOMEM; + goto end; + } + firmware = (char *)acx_s_read_fw(&usbdev->dev, filename, &size); + if (!firmware) { + result = -EIO; + goto end; + } + acxlog(L_INIT, "firmware size: %d bytes\n", size); + + /* Obtain the I/O pipes */ + outpipe = usb_sndctrlpipe(usbdev, 0); + inpipe = usb_rcvctrlpipe(usbdev, 0); + + /* now upload the firmware, slice the data into blocks */ + offset = 8; + while (offset < size) { + len = size - offset; + if (len >= ACX100_USB_RWMEM_MAXLEN) { + len = ACX100_USB_RWMEM_MAXLEN; + } + acxlog(L_INIT, "uploading firmware (%d bytes, offset=%d)\n", + len, offset); + result = 0; + memcpy(usbbuf, firmware + offset, len); + result = usb_control_msg(usbdev, outpipe, + ACX_USB_REQ_UPLOAD_FW, + USB_TYPE_VENDOR|USB_DIR_OUT, + size - 8, /* value */ + 0, /* index */ + usbbuf, /* dataptr */ + len, /* size */ + 3000 /* timeout in ms */ + ); + offset += len; + if (result < 0) { +#if ACX_DEBUG + printk(KERN_ERR "acx: error %d (%s) during upload " + "of firmware, aborting\n", result, + acx100usb_pstatus(result)); +#else + printk(KERN_ERR "acx: error %d during upload " + "of firmware, aborting\n", result); +#endif + goto end; + } + } + + /* finally, send the checksum and reboot the device */ + checksum = le32_to_cpu(*(u32 *)firmware); + /* is this triggers the reboot? */ + result = usb_control_msg(usbdev, outpipe, + ACX_USB_REQ_UPLOAD_FW, + USB_TYPE_VENDOR|USB_DIR_OUT, + checksum & 0xffff, /* value */ + checksum >> 16, /* index */ + NULL, /* dataptr */ + 0, /* size */ + 3000 /* timeout in ms */ + ); + if (result < 0) { + printk(KERN_ERR "acx: error %d during tx of checksum, " + "aborting\n", result); + goto end; + } + result = usb_control_msg(usbdev, inpipe, + ACX_USB_REQ_ACK_CS, + USB_TYPE_VENDOR|USB_DIR_IN, + checksum & 0xffff, /* value */ + checksum >> 16, /* index */ + usbbuf, /* dataptr */ + 8, /* size */ + 3000 /* timeout in ms */ + ); + if (result < 0) { + printk(KERN_ERR "acx: error %d during ACK of checksum, " + "aborting\n", result); + goto end; + } + if (*usbbuf != 0x10) { + kfree(usbbuf); + printk(KERN_ERR "acx: invalid checksum?\n"); + result = -EINVAL; + goto end; + } + result = 0; +end: + vfree(firmware); + kfree(usbbuf); + return result; +} + + +/*********************************************************************** +** acx100usb_e_init_network_device(): +** Inputs: +** dev -> Pointer to network device +************************************************************************ +** Description: +** Basic setup of a network device for use with the WLAN device. +*/ +static int +acx100usb_e_init_network_device(struct net_device *dev) { + int result = 0; + wlandevice_t *priv; + + /* Setup the device and stop the queue */ + ether_setup(dev); + acx_stop_queue(dev, "on init"); + + priv = dev->priv; + + acx_sem_lock(priv); + + /* put the ACX100 out of sleep mode */ + acx_s_issue_cmd(priv, ACX1xx_CMD_WAKE, NULL, 0); + + /* Register the callbacks for the network device functions */ + dev->open = &acx100usb_e_open; + dev->stop = &acx100usb_e_close; + dev->hard_start_xmit = (void *)&acx_i_start_xmit; + dev->get_stats = (void *)&acx_e_get_stats; + dev->get_wireless_stats = (void *)&acx_e_get_wireless_stats; +#if WIRELESS_EXT >= 13 + dev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; +#else + dev->do_ioctl = (void *)&acx_e_ioctl_old; +#endif + dev->set_multicast_list = (void *)&acx100usb_i_set_rx_mode; +#ifdef HAVE_TX_TIMEOUT + dev->tx_timeout = &acx100usb_i_tx_timeout; + dev->watchdog_timeo = 4 * HZ; +#endif + result = acx_s_init_mac(dev); + if (OK != result) + goto end; + result = acx_s_set_defaults(priv); + if (OK != result) { + printk("%s: acx_set_defaults FAILED\n", dev->name); + goto end; + } + + SET_MODULE_OWNER(dev); +end: + acx_sem_unlock(priv); + + return result; +} + + +/*********************************************************************** +** acx100usb_e_open +** This function is called when the user sets up the network interface. +** It initializes a management timer, sets up the USB card and starts +** the network tx queue and USB receive. +*/ +static int +acx100usb_e_open(struct net_device *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + unsigned long flags; + int i; + + FN_ENTER; + + acx_sem_lock(priv); + + /* put the ACX100 out of sleep mode */ + acx_s_issue_cmd(priv, ACX1xx_CMD_WAKE, NULL, 0); + + acx_init_task_scheduler(priv); + + init_timer(&priv->mgmt_timer); + priv->mgmt_timer.function = acx_i_timer; + priv->mgmt_timer.data = (unsigned long)priv; + + /* set ifup to 1, since acx_start needs it */ + SET_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP); + acx_s_start(priv); + + acx_start_queue(dev, "on open"); + + acx_lock(priv, flags); + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { + acx100usb_l_poll_rx(priv, i); + } + acx_unlock(priv, flags); + + WLAN_MOD_INC_USE_COUNT; + + acx_sem_unlock(priv); + + FN_EXIT0; + return 0; +} + + +/*********************************************************************** +** acx100usb_l_poll_rx +** This function initiates a bulk-in USB transfer (in case the interface +** is up). +*/ +static void +acx100usb_l_poll_rx(wlandevice_t *priv, int number) +{ + struct usb_device *usbdev; + rxbuffer_t *inbuf; + acx_usb_bulk_context_t *rxcon; + struct urb *rxurb; + int errcode; + unsigned int inpipe; + + FN_ENTER; + + if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) { + goto end; + } + + rxcon = &priv->rxcons[number]; + inbuf = &priv->bulkins[number]; + rxurb = priv->bulkrx_urbs[number]; + usbdev = priv->usbdev; + + rxcon->device = priv; + rxcon->number = number; + inpipe = usb_rcvbulkpipe(usbdev, priv->bulkinep); + if (rxurb->status == -EINPROGRESS) { + printk(KERN_ERR "acx: error, rx triggered while rx urb in progress\n"); + /* FIXME: this is nasty, receive is being cancelled by this code + * on the other hand, this should not happen anyway... + */ + usb_unlink_urb(rxurb); + } + rxurb->actual_length = 0; + usb_fill_bulk_urb(rxurb, usbdev, inpipe, + inbuf, /* dataptr */ + RXBUFSIZE, /* size */ + acx100usb_i_complete_rx, /* handler */ + rxcon /* handler param */ + ); + rxurb->transfer_flags = ASYNC_UNLINK|QUEUE_BULK; +#if USB_24 + rxurb->timeout = 0; + rxurb->status = 0; +#endif + /* ATOMIC: we may be called from complete_rx() usb callback */ + errcode = submit_urb(rxurb, GFP_ATOMIC); + /* FIXME: evaluate the error code ! */ + acxlog(L_USBRXTX, "SUBMIT RX (%d) inpipe=0x%X size=%d errcode=%d\n", + number, inpipe, (int) RXBUFSIZE, errcode); + +end: + FN_EXIT0; +} + + +/*********************************************************************** +** acx100usb_i_complete_rx(): +** Inputs: +** urb -> Pointer to USB request block +** regs -> Pointer to register-buffer for syscalls (see asm/ptrace.h) +************************************************************************ +** Description: +** This function is invoked by USB subsystem whenever a bulk receive +** request returns. +** The received data is then committed to the network stack and the next +** USB receive is triggered. +*/ +#if USB_24 +static void +acx100usb_i_complete_rx(struct urb *urb) +#else +static void +acx100usb_i_complete_rx(struct urb *urb, struct pt_regs *regs) +#endif +{ + wlandevice_t *priv; + rxbuffer_t *ptr; + rxbuffer_t *inbuf; + unsigned long flags; + int size, number, remsize, packetsize; + + FN_ENTER; + + if (!urb->context) { + printk(KERN_ERR "acx: error, urb context was NULL\n"); + goto end; /* at least try to prevent the worst */ + } + + priv = ((acx_usb_bulk_context_t *)urb->context)->device; + number = ((acx_usb_bulk_context_t *)urb->context)->number; + size = urb->actual_length; + remsize = size; + + acxlog(L_USBRXTX, "RETURN RX (%d) status=%d size=%d\n", + number, urb->status, size); + + acx_lock(priv, flags); + + inbuf = &priv->bulkins[number]; + ptr = inbuf; + + /* check if the transfer was aborted */ + switch (urb->status) { + case 0: /* No error */ + break; + case -EOVERFLOW: + printk(KERN_ERR "acx: error in rx, data overrun -> emergency stop\n"); + /* LOCKING BUG! acx100usb_e_close(priv->netdev); */ + goto end_unlock; + case -ECONNRESET: + goto do_poll_rx; + default: + priv->stats.rx_errors++; + printk("acx: rx error (urb status=%d)\n", urb->status); + goto do_poll_rx; + } + + if (!size) + printk("acx: warning, encountered zerolength rx packet\n"); + + if (urb->transfer_buffer != inbuf) + goto do_poll_rx; + + /* check if previous frame was truncated + ** FIXME: this code can only handle truncation + ** of consecutive packets! + */ + if (priv->rxtruncsize) { + int tail_size; + + ptr = &priv->rxtruncbuf; + packetsize = RXBUF_BYTES_RCVD(ptr) + RXBUF_HDRSIZE; + if (acx_debug & L_USBRXTX) { + printk("handling truncated frame (truncsize=%d size=%d " + "packetsize(from trunc)=%d)\n", + priv->rxtruncsize, size, packetsize); + acx_dump_bytes(ptr, RXBUF_HDRSIZE); + acx_dump_bytes(inbuf, RXBUF_HDRSIZE); + } + + /* bytes needed for rxtruncbuf completion: */ + tail_size = packetsize - priv->rxtruncsize; + + if (size < tail_size) { + /* there is not enough data to complete this packet, + ** simply append the stuff to the truncation buffer + */ + memcpy(((char *)ptr) + priv->rxtruncsize, inbuf, size); + priv->rxtruncsize += size; + remsize = 0; + } else { + /* ok, this data completes the previously + ** truncated packet. copy it into a descriptor + ** and give it to the rest of the stack */ + + /* append tail to previously truncated part + ** NB: priv->rxtruncbuf (pointed to by ptr) can't + ** overflow because this is already checked before + ** truncation buffer was filled. See below, + ** "if (packetsize > sizeof(rxbuffer_t))..." code */ + memcpy(((char *)ptr) + priv->rxtruncsize, inbuf, tail_size); + + if (acx_debug & L_USBRXTX) { + printk("full trailing packet + 12 bytes:\n"); + acx_dump_bytes(inbuf, tail_size + RXBUF_HDRSIZE); + } + acx_l_process_rxbuf(priv, ptr); + priv->rxtruncsize = 0; + ptr = (rxbuffer_t *) (((char *)inbuf) + tail_size); + remsize -= tail_size; + } + acxlog(L_USBRXTX, "post-merge size=%d remsize=%d\n", + size, remsize); + } + + /* size = USB data block size + ** remsize = unprocessed USB bytes left + ** ptr = current pos in USB data block + */ + while (remsize) { + if (remsize < RXBUF_HDRSIZE) { + printk("acx: truncated rx header (%d bytes)!\n", + remsize); + break; + } + packetsize = RXBUF_BYTES_RCVD(ptr) + RXBUF_HDRSIZE; + acxlog(L_USBRXTX, "packet with packetsize=%d\n", packetsize); + if (packetsize > sizeof(rxbuffer_t)) { + printk("acx: packet exceeds max wlan " + "frame size (%d > %d). size=%d\n", + packetsize, (int) sizeof(rxbuffer_t), size); + /* FIXME: put some real error-handling in here! */ + break; + } + + /* skip null packets (does this really happen?!) */ + if (packetsize == RXBUF_HDRSIZE) { + remsize -= RXBUF_HDRSIZE; + if (acx_debug & L_USBRXTX) { + printk("acx: null packet, new remsize=%d. " + "header follows:\n", remsize); + acx_dump_bytes(ptr, RXBUF_HDRSIZE); + } + ptr = (rxbuffer_t *)(((char *)ptr) + RXBUF_HDRSIZE); + continue; + } + + if (packetsize > remsize) { + /* frame truncation handling */ + if (acx_debug & L_USBRXTX) { + printk("need to truncate packet, " + "packetsize=%d remsize=%d " + "size=%d\n", + packetsize, remsize, size); + acx_dump_bytes(ptr, RXBUF_HDRSIZE); + } + memcpy(&priv->rxtruncbuf, ptr, remsize); + priv->rxtruncsize = remsize; + break; + } else { /* packetsize <= remsize */ + /* now handle the received data */ + acx_l_process_rxbuf(priv, ptr); + + ptr = (rxbuffer_t *)(((char *)ptr) + packetsize); + remsize -= packetsize; + if ((acx_debug & L_USBRXTX) && remsize) { + printk("more than one packet in buffer, " + "second packet hdr follows\n"); + acx_dump_bytes(ptr, RXBUF_HDRSIZE); + } + } + } + +do_poll_rx: + /* look for the next rx */ + if (priv->dev_state_mask & ACX_STATE_IFACE_UP) { + /* receive of frame completed, now look for the next one */ + acx100usb_l_poll_rx(priv, number); + } + +end_unlock: + acx_unlock(priv, flags); +end: + FN_EXIT0; +} + + +/*********************************************************************** +** acx100usb_i_complete_tx(): +** Inputs: +** urb -> Pointer to USB request block +** regs -> Pointer to register-buffer for syscalls (see asm/ptrace.h) +************************************************************************ +** Description: +** This function is invoked upon termination of a USB transfer. As the +** USB device is only capable of sending a limited amount of bytes per +** transfer to the bulk-out endpoint, this routine checks if there are +** more bytes to send and triggers subsequent transfers. In case the +** transfer size exactly matches the maximum bulk-out size, it triggers +** a transfer of a null-frame, telling the card that this is it. Upon +** completion of a frame, it checks whether the Tx ringbuffer contains +** more data to send and invokes the Tx routines if this is the case. +** If there are no more occupied Tx descriptors, the Tx Mutex is unlocked +** and the network queue is switched back to life again. +*/ +#if USB_24 +static void +acx100usb_i_complete_tx(struct urb *urb) +#else +static void +acx100usb_i_complete_tx(struct urb *urb, struct pt_regs *regs) +#endif +{ + wlandevice_t *priv; + usb_tx_t *tx; + unsigned long flags; + + FN_ENTER; + + if (!urb->context) { + printk(KERN_ERR "acx: error, NULL context in tx completion callback\n"); + /* FIXME: real error-handling code must go here! */ + goto end; + } + + tx = (usb_tx_t *)urb->context; + priv = tx->priv; + + acxlog(L_USBRXTX, "RETURN TX (%p): status=%d size=%d\n", + tx, urb->status, urb->actual_length); + + /* handle USB transfer errors */ + switch (urb->status) { + case 0: /* No error */ + break; + case -ECONNRESET: + break; + /* FIXME: real error-handling code here please */ + default: + printk(KERN_ERR "acx: tx error, urb status=%d\n", urb->status); + /* FIXME: real error-handling code here please */ + } + + acx_lock(priv, flags); + + /* free the URB and check for more data */ + priv->usb_free_tx++; + tx->busy = 0; + + acx_unlock(priv, flags); +end: + FN_EXIT0; +} + + +/*********************************************************************** +** acx100usb_e_close(): +** Inputs: +** dev -> Pointer to network device structure +************************************************************************ +** Returns: +** (int) 0 on success, or error-code +** +** Description: +** This function stops the network functionality of the interface (invoked +** when the user calls ifconfig down). The tx queue is halted and +** the device is marked as down. In case there were any pending USB bulk +** transfers, these are unlinked (asynchronously). The module in-use count +** is also decreased in this function. +*/ +static int +acx100usb_e_close(struct net_device *dev) +{ + wlandevice_t *priv; + int i, already_down; + + priv = dev->priv; + if (!priv) { + printk(KERN_ERR "acx: dev->priv empty, FAILED\n"); + return -ENODEV; + } + + FN_ENTER; + +#if WE_STILL_DONT_CARE_ABOUT_IT + /* Transmit a disassociate frame */ + lock + acx_l_transmit_disassoc(priv, &client); + unlock +#endif + + acx_sem_lock(priv); + + /* stop the transmit queue */ + if (!acx_queue_stopped(dev)) { + acx_stop_queue(dev, "on iface stop"); + } + +//maybe LOCKING BUG, see pci.c if you wonder why + FLUSH_SCHEDULED_WORK(); + + /* mark the device as DOWN */ + if (priv->dev_state_mask & ACX_STATE_IFACE_UP) { + already_down = 0; + CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP); + } else { + /* I don't think it can happen */ + printk("acx: double down, please report!\n"); + already_down = 1; + } + + /* wait until all tx are out */ +#if 0 + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { + while (priv->usb_tx[i].urb->status == -EINPROGRESS) { + mdelay(5); + } + } +#endif + + /* stop pending rx/tx urb transfers */ + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { + acx_unlink_and_free_urb(priv->bulkrx_urbs[i]); + acx_unlink_and_free_urb(priv->usb_tx[i].urb); + } + + /* disable rx and tx */ + acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0); + acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0); + + /* power down the device */ + acx_s_issue_cmd(priv, ACX1xx_CMD_SLEEP, NULL, 0); + + acx_sem_unlock(priv); + + /* decrease module-in-use count (if necessary) */ + if (!already_down) + WLAN_MOD_DEC_USE_COUNT; + + FN_EXIT1(0); + return 0; +} + + +/*************************************************************** +** acx_l_alloc_tx +** Actually returns a usb_tx_t* ptr +*/ +tx_t* +acx_l_alloc_tx(wlandevice_t* priv) +{ + int i; + usb_tx_t *tx = NULL; + + FN_ENTER; + + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { + if (!priv->usb_tx[i].busy) { + tx = &priv->usb_tx[i]; + tx->busy = 1; + break; + } + } + if (i >= ACX100_USB_NUM_BULK_URBS) { + printk("acx: tx buffers full\n"); + } + + FN_EXIT0; + + return (tx_t*)tx; +} + + +/*************************************************************** +*/ +void* +acx_l_get_txbuf(tx_t* tx_opaque) +{ + usb_tx_t* tx = (usb_tx_t*)tx_opaque; + return &tx->bulkout.data; +} + + +/*************************************************************** +** acx_l_dma_tx_data +** +** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). +** Can be called from acx_i_start_xmit (data frames from net core). +*/ +void +acx_l_dma_tx_data(wlandevice_t *priv, tx_t* tx_opaque, int wlanpkt_len) +{ + struct usb_device *usbdev; + struct urb* txurb; + usb_tx_t* tx; + usb_txbuffer_t* txbuf; + client_t *clt; + wlan_hdr_t* whdr; + unsigned int outpipe; + int ucode; + u8 rate100; + + FN_ENTER; + + tx = ((usb_tx_t *)tx_opaque); + txurb = tx->urb; + txbuf = &tx->bulkout; + whdr = (wlan_hdr_t *)txbuf->data; + + priv->usb_free_tx--; + acxlog(L_DEBUG, "using buf#%d free=%d len=%d\n", tx - priv->usb_tx, + priv->usb_free_tx, wlanpkt_len); + + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_3_AP: + clt = acx_l_sta_list_get(priv, whdr->a1); + break; + case ACX_MODE_2_STA: + clt = priv->ap_client; + break; + default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ + clt = NULL; + break; + } + + if (unlikely(clt && !clt->rate_cur)) { + printk("acx: driver bug! bad ratemask\n"); + goto end; + } + + /* used in tx cleanup routine for auto rate and accounting: */ +//TODO: currently unused - fix that + tx->txc = clt; + + rate100 = clt ? clt->rate_100 : priv->rate_bcast100; + + /* fill the USB transfer header */ + txbuf->desc = cpu_to_le16(USB_TXBUF_TXDESC); + txbuf->MPDUlen = cpu_to_le16(wlanpkt_len); + txbuf->ctrl1 = 0; + txbuf->ctrl2 = 0; + txbuf->hostData = cpu_to_le32(wlanpkt_len | (rate100 << 24)); + if (1 == priv->preamble_cur) + SET_BIT(txbuf->ctrl1, DESC_CTL_SHORT_PREAMBLE); + SET_BIT(txbuf->ctrl1, DESC_CTL_FIRSTFRAG); + txbuf->txRate = rate100; + txbuf->index = 1; + txbuf->dataLength = cpu_to_le16(wlanpkt_len); + + if ( (WF_FC_FTYPEi & whdr->fc) == WF_FTYPE_DATAi ) + SET_BIT(txbuf->hostData, cpu_to_le32(USB_TXBUF_HD_ISDATA)); + if (mac_is_directed(whdr->a1)) + SET_BIT(txbuf->hostData, cpu_to_le32(USB_TXBUF_HD_DIRECTED)); + else if (mac_is_bcast(whdr->a1)) + SET_BIT(txbuf->hostData, cpu_to_le32(USB_TXBUF_HD_BROADCAST)); + + if (acx_debug & L_DATA) { + printk("dump of bulk out urb:\n"); + acx_dump_bytes(txbuf, wlanpkt_len + USB_TXBUF_HDRSIZE); + } + + if (txurb->status == -EINPROGRESS) { + printk("acx: trying to submit tx urb while already in progress\n"); + } + + /* now schedule the USB transfer */ + usbdev = priv->usbdev; + outpipe = usb_sndbulkpipe(usbdev, priv->bulkoutep); +//can be removed, please try & test: + tx->priv = priv; + + usb_fill_bulk_urb(txurb, usbdev, outpipe, + txbuf, /* dataptr */ + wlanpkt_len + USB_TXBUF_HDRSIZE, /* size */ + acx100usb_i_complete_tx, /* handler */ + tx /* handler param */ + ); + + txurb->transfer_flags = ASYNC_UNLINK|QUEUE_BULK|ZERO_PACKET; +#if USB_24 + txurb->status = 0; + txurb->timeout = ACX100_USB_TX_TIMEOUT; +#endif + ucode = submit_urb(txurb, GFP_ATOMIC); + acxlog(L_USBRXTX, "SUBMIT TX (%p): outpipe=0x%X buf=%p txsize=%d " + "errcode=%d\n", tx, outpipe, txbuf, + wlanpkt_len + USB_TXBUF_HDRSIZE, ucode); + + if (ucode) { + printk(KERN_ERR "acx: submit_urb() error=%d txsize=%d\n", + ucode, wlanpkt_len + USB_TXBUF_HDRSIZE); + + /* on error, just mark the frame as done and update + ** the statistics + */ + priv->stats.tx_errors++; + tx->busy = 0; + priv->usb_free_tx++; + } +end: + FN_EXIT0; +} + + +/*********************************************************************** +*/ +static struct net_device_stats* +acx_e_get_stats(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + return &priv->stats; +} + + +/*********************************************************************** +*/ +static struct iw_statistics* +acx_e_get_wireless_stats(netdevice_t *dev) +{ + wlandevice_t *priv = acx_netdev_priv(dev); + return &priv->wstats; +} + + +/*********************************************************************** +*/ +static void +acx100usb_i_set_rx_mode(struct net_device *dev) +{ +} + + +/*********************************************************************** +*/ +#ifdef HAVE_TX_TIMEOUT +static void +acx100usb_i_tx_timeout(struct net_device *dev) +{ + wlandevice_t *priv; + unsigned long flags; + int i; + + FN_ENTER; + + priv = dev->priv; + + acx_lock(priv, flags); + /* unlink the URBs */ + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { + if (priv->usb_tx[i].urb->status == -EINPROGRESS) + usb_unlink_urb(priv->usb_tx[i].urb); + } + /* TODO: stats update */ + acx_unlock(priv, flags); + + FN_EXIT0; +} +#endif + + +/*********************************************************************** +** acx_l_power_led +*/ +void +acx_l_power_led(wlandevice_t *priv, int enable) +{ + acxlog(L_IOCTL, "acx: power_led() is not supported on USB\n"); +} + + +/*********************************************************************** +** Ioctls +*/ + +/*********************************************************************** +** Ioctls for easy debug of I/O things (stubs only for USB) +*/ +int +acx_ioctl_dbg_get_io( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + acxlog(L_IOCTL, "acx: dbg_get_io() is not supported on USB\n"); + return -EINVAL; +} + +int +acx_ioctl_dbg_set_io( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + acxlog(L_IOCTL, "acx: dbg_set_io() is not supported on USB\n"); + return -EINVAL; +} + + +/*********************************************************************** +*/ +int +acx111_ioctl_info( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + return OK; +} + + +/*********************************************************************** +*/ +int +acx100_ioctl_set_phy_amp_bias( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + printk("acx: set_phy_amp_bias() is not supported on USB\n"); + return OK; +} + + +/*********************************************************************** +** init_module(): +** Inputs: +** +************************************************************************ +** Returns: +** (int) Errorcode on failure, 0 on success +** +** Description: +** This function is invoked upon loading of the kernel module. It registers +** itself at the kernel's USB subsystem. +*/ +int +init_module(void) +{ + printk(KERN_INFO "Initializing acx100 WLAN USB kernel module\n"); + return usb_register(&acx100usb_driver); +} + + + +/*********************************************************************** +** cleanup_module(): +** Inputs: +** +************************************************************************ +** Description: +** This function is invoked as last step of the module unloading. It simply +** deregisters this module at the kernel's USB subsystem. +*/ +void +cleanup_module() +{ + usb_deregister(&acx100usb_driver); + printk(KERN_INFO "Cleaning up acx100 WLAN USB kernel module\n"); +} + + +/*********************************************************************** +** DEBUG STUFF +*/ +#if ACX_DEBUG + +#if UNUSED +static void +dump_device(struct usb_device *usbdev) +{ + int i; + struct usb_config_descriptor *cd; + + printk("acx device dump:\n"); + printk(" devnum: %d\n", usbdev->devnum); + printk(" speed: %d\n", usbdev->speed); + printk(" tt: 0x%X\n", (unsigned int)(usbdev->tt)); + printk(" ttport: %d\n", (unsigned int)(usbdev->ttport)); + printk(" toggle[0]: 0x%X toggle[1]: 0x%X\n", (unsigned int)(usbdev->toggle[0]), (unsigned int)(usbdev->toggle[1])); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8) + /* halted removed in 2.6.9-rc1 */ + /* DOH, Canbreak... err... Mandrake decided to do their very own very + * special version "2.6.8.1" which already includes this change, so we + * need to blacklist that version already (i.e. 2.6.8) */ + printk(" halted[0]: 0x%X halted[1]: 0x%X\n", usbdev->halted[0], usbdev->halted[1]); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) + /* This saw a change after 2.6.10 */ + printk(" ep_in wMaxPacketSize: "); + for (i = 0; i < 16; ++i) + printk("%d ", usbdev->ep_in[i]->desc.wMaxPacketSize); + printk("\n"); + printk(" ep_out wMaxPacketSize: "); + for (i = 0; i < 15; ++i) + printk("%d ", usbdev->ep_out[i]->desc.wMaxPacketSize); + printk("\n"); +#else + printk(" epmaxpacketin: "); + for (i = 0; i < 16; i++) + printk("%d ", usbdev->epmaxpacketin[i]); + printk("\n"); + printk(" epmaxpacketout: "); + for (i = 0; i < 16; i++) + printk("%d ", usbdev->epmaxpacketout[i]); + printk("\n"); +#endif + printk(" parent: 0x%X\n", (unsigned int)usbdev->parent); + printk(" bus: 0x%X\n", (unsigned int)usbdev->bus); +#if NO_DATATYPE + printk(" configs: "); + for (i = 0; i < usbdev->descriptor.bNumConfigurations; i++) + printk("0x%X ", usbdev->config[i]); + printk("\n"); +#endif + printk(" actconfig: %p\n", usbdev->actconfig); + dump_device_descriptor(&usbdev->descriptor); +#if USB_24 + cd = usbdev->actconfig; + dump_config_descriptor(cd); + { + struct usb_interface *ifc = cd->interface; + if (ifc) { + printk("iface: altsetting=%p act_altsetting=%d " + "num_altsetting=%d max_altsetting=%d\n", + ifc->altsetting, ifc->act_altsetting, + ifc->num_altsetting, ifc->max_altsetting); + dump_interface_descriptor(ifc->altsetting); + dump_endpoint_descriptor(ifc->altsetting->endpoint); + } + } +#else + cd = &usbdev->config->desc; + dump_config_descriptor(cd); +#endif +} + + +/*********************************************************************** +*/ +static void +dump_config_descriptor(struct usb_config_descriptor *cd) +{ + printk("Configuration Descriptor:\n"); + if (!cd) { + printk("NULL\n"); + return; + } + printk(" bLength: %d (0x%X)\n", cd->bLength, cd->bLength); + printk(" bDescriptorType: %d (0x%X)\n", cd->bDescriptorType, cd->bDescriptorType); + printk(" bNumInterfaces: %d (0x%X)\n", cd->bNumInterfaces, cd->bNumInterfaces); + printk(" bConfigurationValue: %d (0x%X)\n", cd->bConfigurationValue, cd->bConfigurationValue); + printk(" iConfiguration: %d (0x%X)\n", cd->iConfiguration, cd->iConfiguration); + printk(" bmAttributes: %d (0x%X)\n", cd->bmAttributes, cd->bmAttributes); + /* printk(" MaxPower: %d (0x%X)\n", cd->bMaxPower, cd->bMaxPower); */ +} + + +static void +dump_device_descriptor(struct usb_device_descriptor *dd) +{ + printk("Device Descriptor:\n"); + if (!dd) { + printk("NULL\n"); + return; + } + printk(" bLength: %d (0x%X)\n", dd->bLength, dd->bLength); + printk(" bDescriptortype: %d (0x%X)\n", dd->bDescriptorType, dd->bDescriptorType); + printk(" bcdUSB: %d (0x%X)\n", dd->bcdUSB, dd->bcdUSB); + printk(" bDeviceClass: %d (0x%X)\n", dd->bDeviceClass, dd->bDeviceClass); + printk(" bDeviceSubClass: %d (0x%X)\n", dd->bDeviceSubClass, dd->bDeviceSubClass); + printk(" bDeviceProtocol: %d (0x%X)\n", dd->bDeviceProtocol, dd->bDeviceProtocol); + printk(" bMaxPacketSize0: %d (0x%X)\n", dd->bMaxPacketSize0, dd->bMaxPacketSize0); + printk(" idVendor: %d (0x%X)\n", dd->idVendor, dd->idVendor); + printk(" idProduct: %d (0x%X)\n", dd->idProduct, dd->idProduct); + printk(" bcdDevice: %d (0x%X)\n", dd->bcdDevice, dd->bcdDevice); + printk(" iManufacturer: %d (0x%X)\n", dd->iManufacturer, dd->iManufacturer); + printk(" iProduct: %d (0x%X)\n", dd->iProduct, dd->iProduct); + printk(" iSerialNumber: %d (0x%X)\n", dd->iSerialNumber, dd->iSerialNumber); + printk(" bNumConfigurations: %d (0x%X)\n", dd->bNumConfigurations, dd->bNumConfigurations); +} +#endif /* UNUSED */ + + +/*********************************************************************** +*/ +#if USB_24 +static void +dump_endpoint_descriptor(struct usb_endpoint_descriptor *ep) +{ + printk("Endpoint Descriptor:\n"); + if (!ep) { + printk("NULL\n"); + return; + } + printk(" bLength: %d (0x%X)\n", ep->bLength, ep->bLength); + printk(" bDescriptorType: %d (0x%X)\n", ep->bDescriptorType, ep->bDescriptorType); + printk(" bEndpointAddress: %d (0x%X)\n", ep->bEndpointAddress, ep->bEndpointAddress); + printk(" bmAttributes: 0x%X\n", ep->bmAttributes); + printk(" wMaxPacketSize: %d (0x%X)\n", ep->wMaxPacketSize, ep->wMaxPacketSize); + printk(" bInterval: %d (0x%X)\n", ep->bInterval, ep->bInterval); + printk(" bRefresh: %d (0x%X)\n", ep->bRefresh, ep->bRefresh); + printk(" bSyncAdrress: %d (0x%X)\n", ep->bSynchAddress, ep->bSynchAddress); +} + +static void +dump_interface_descriptor(struct usb_interface_descriptor *id) +{ + printk("Interface Descriptor:\n"); + if (!id) { + printk("NULL\n"); + return; + } + printk(" bLength: %d (0x%X)\n", id->bLength, id->bLength); + printk(" bDescriptorType: %d (0x%X)\n", id->bDescriptorType, id->bDescriptorType); + printk(" bInterfaceNumber: %d (0x%X)\n", id->bInterfaceNumber, id->bInterfaceNumber); + printk(" bAlternateSetting: %d (0x%X)\n", id->bAlternateSetting, id->bAlternateSetting); + printk(" bNumEndpoints: %d (0x%X)\n", id->bNumEndpoints, id->bNumEndpoints); + printk(" bInterfaceClass: %d (0x%X)\n", id->bInterfaceClass, id->bInterfaceClass); + printk(" bInterfaceSubClass: %d (0x%X)\n", id->bInterfaceSubClass, id->bInterfaceSubClass); + printk(" bInterfaceProtocol: %d (0x%X)\n", id->bInterfaceProtocol, id->bInterfaceProtocol); + printk(" iInterface: %d (0x%X)\n", id->iInterface, id->iInterface); + printk(" endpoint: 0x%X\n", (unsigned int)(id->endpoint)); +} +#endif /* USB_24 */ + +#endif /* ACX_DEBUG */ diff -puN /dev/null drivers/net/wireless/tiacx/usb_helper.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/usb_helper.c 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,2 @@ +#define ACX_USB 1 +#include "helper.c" diff -puN /dev/null drivers/net/wireless/tiacx/wlan.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/wlan.c 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,391 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +/*********************************************************************** +** This code is based on elements which are +** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. +** info@linux-wlan.com +** http://www.linux-wlan.com +*/ + +#include +#include + +#include +#include +#include +#if WIRELESS_EXT >= 13 +#include +#endif + +#include "acx.h" + + +/*********************************************************************** +*/ +#define LOG_BAD_EID(hdr,len,ie_ptr) acx_log_bad_eid(hdr, len, ((wlan_ie_t*)ie_ptr)) + +#define IE_EID(ie_ptr) (((wlan_ie_t*)(ie_ptr))->eid) +#define IE_LEN(ie_ptr) (((wlan_ie_t*)(ie_ptr))->len) +#define OFFSET(hdr,off) (WLAN_HDR_A3_DATAP(hdr) + (off)) + + +/*********************************************************************** +** wlan_mgmt_decode_XXX +** +** Given a complete frame in f->hdr, sets the pointers in f to +** the areas that correspond to the parts of the frame. +** +** Assumptions: +** 1) f->len and f->hdr are already set +** 2) f->len is the length of the MAC header + data, the CRC +** is NOT included +** 3) all members except len and hdr are zero +** Arguments: +** f frame structure +** +** Returns: +** nothing +** +** Side effects: +** frame structure members are pointing at their +** respective portions of the frame buffer. +*/ +void +wlan_mgmt_decode_beacon(wlan_fr_beacon_t * f) +{ + u8 *ie_ptr; + u8 *end = (u8*)f->hdr + f->len; + + f->type = WLAN_FSTYPE_BEACON; + + /*-- Fixed Fields ----*/ + f->ts = (u64 *) OFFSET(f->hdr, WLAN_BEACON_OFF_TS); + f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_BCN_INT); + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_CAPINFO); + + /*-- Information elements */ + ie_ptr = OFFSET(f->hdr, WLAN_BEACON_OFF_SSID); + while (ie_ptr < end) { + switch (IE_EID(ie_ptr)) { + case WLAN_EID_SSID: + f->ssid = (wlan_ie_ssid_t *) ie_ptr; + break; + case WLAN_EID_SUPP_RATES: + f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; + break; + case WLAN_EID_EXT_RATES: + f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; + break; + case WLAN_EID_FH_PARMS: + f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr; + break; + case WLAN_EID_DS_PARMS: + f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr; + break; + case WLAN_EID_CF_PARMS: + f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr; + break; + case WLAN_EID_IBSS_PARMS: + f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr; + break; + case WLAN_EID_TIM: + f->tim = (wlan_ie_tim_t *) ie_ptr; + break; + case WLAN_EID_ERP_INFO: + f->erp = (wlan_ie_erp_t *) ie_ptr; + break; + case WLAN_EID_GENERIC: + /* WPA: + if (pos[1] >= 4 && + pos[2] == 0x00 && pos[3] == 0x50 && + pos[4] == 0xf2 && pos[5] == 1) { + wpa = pos; + wpa_len = pos[1] + 2; + } + TI x4 mode: last byte is probably 0/1 - disabled/enabled + if (pos[1] == 4 && + pos[2] == 0x80 && pos[3] == 0x00 && + pos[4] == 0x28 && pos[5] == 0/1) { + } + */ + break; + case WLAN_EID_RSN: + /* + rsn = pos; + rsn_len = pos[1] + 2; + */ + break; + default: + LOG_BAD_EID(f->hdr, f->len, ie_ptr); + break; + } + ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); + } +} + + +#if UNUSED +void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t * f) +{ + f->type = WLAN_FSTYPE_ATIM; + /*-- Fixed Fields ----*/ + /*-- Information elements */ +} +#endif /* UNUSED */ + +void +wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t * f) +{ + f->type = WLAN_FSTYPE_DISASSOC; + + /*-- Fixed Fields ----*/ + f->reason = (u16 *) OFFSET(f->hdr, WLAN_DISASSOC_OFF_REASON); + + /*-- Information elements */ +} + + +void +wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t * f) +{ + u8 *ie_ptr; + u8 *end = (u8*)f->hdr + f->len; + + + f->type = WLAN_FSTYPE_ASSOCREQ; + + /*-- Fixed Fields ----*/ + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_CAP_INFO); + f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_LISTEN_INT); + + /*-- Information elements */ + ie_ptr = OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_SSID); + while (ie_ptr < end) { + switch (IE_EID(ie_ptr)) { + case WLAN_EID_SSID: + f->ssid = (wlan_ie_ssid_t *) ie_ptr; + break; + case WLAN_EID_SUPP_RATES: + f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; + break; + case WLAN_EID_EXT_RATES: + f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; + break; + default: + LOG_BAD_EID(f->hdr, f->len, ie_ptr); + break; + } + ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); + } +} + + +void +wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t * f) +{ + f->type = WLAN_FSTYPE_ASSOCRESP; + + /*-- Fixed Fields ----*/ + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_CAP_INFO); + f->status = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_STATUS); + f->aid = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_AID); + + /*-- Information elements */ + f->supp_rates = (wlan_ie_supp_rates_t *) + OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_SUPP_RATES); +} + + +void +wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t * f) +{ + u8 *ie_ptr; + u8 *end = (u8*)f->hdr + f->len; + + f->type = WLAN_FSTYPE_REASSOCREQ; + + /*-- Fixed Fields ----*/ + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CAP_INFO); + f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_LISTEN_INT); + f->curr_ap = (u8 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CURR_AP); + + /*-- Information elements */ + ie_ptr = OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_SSID); + while (ie_ptr < end) { + switch (IE_EID(ie_ptr)) { + case WLAN_EID_SSID: + f->ssid = (wlan_ie_ssid_t *) ie_ptr; + break; + case WLAN_EID_SUPP_RATES: + f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; + break; + case WLAN_EID_EXT_RATES: + f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; + break; + default: + LOG_BAD_EID(f->hdr, f->len, ie_ptr); + break; + } + ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); + } +} + + +void +wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t * f) +{ + f->type = WLAN_FSTYPE_REASSOCRESP; + + /*-- Fixed Fields ----*/ + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_CAP_INFO); + f->status = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_STATUS); + f->aid = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_AID); + + /*-- Information elements */ + f->supp_rates = (wlan_ie_supp_rates_t *) + OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_SUPP_RATES); +} + + +void +wlan_mgmt_decode_probereq(wlan_fr_probereq_t * f) +{ + u8 *ie_ptr; + u8 *end = (u8*)f->hdr + f->len; + + f->type = WLAN_FSTYPE_PROBEREQ; + + /*-- Fixed Fields ----*/ + + /*-- Information elements */ + ie_ptr = OFFSET(f->hdr, WLAN_PROBEREQ_OFF_SSID); + while (ie_ptr < end) { + switch (IE_EID(ie_ptr)) { + case WLAN_EID_SSID: + f->ssid = (wlan_ie_ssid_t *) ie_ptr; + break; + case WLAN_EID_SUPP_RATES: + f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; + break; + case WLAN_EID_EXT_RATES: + f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; + break; + default: + LOG_BAD_EID(f->hdr, f->len, ie_ptr); + break; + } + ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); + } +} + + +/* TODO: decoding of beacon and proberesp can be merged (similar structure) */ +void +wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t * f) +{ + u8 *ie_ptr; + u8 *end = (u8*)f->hdr + f->len; + + f->type = WLAN_FSTYPE_PROBERESP; + + /*-- Fixed Fields ----*/ + f->ts = (u64 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_TS); + f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_BCN_INT); + f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_CAP_INFO); + + /*-- Information elements */ + ie_ptr = OFFSET(f->hdr, WLAN_PROBERESP_OFF_SSID); + while (ie_ptr < end) { + switch (IE_EID(ie_ptr)) { + case WLAN_EID_SSID: + f->ssid = (wlan_ie_ssid_t *) ie_ptr; + break; + case WLAN_EID_SUPP_RATES: + f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; + break; + case WLAN_EID_EXT_RATES: + f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; + break; + case WLAN_EID_FH_PARMS: + f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr; + break; + case WLAN_EID_DS_PARMS: + f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr; + break; + case WLAN_EID_CF_PARMS: + f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr; + break; + case WLAN_EID_IBSS_PARMS: + f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr; + break; + default: + LOG_BAD_EID(f->hdr, f->len, ie_ptr); + break; + } + + ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); + } +} + + +void +wlan_mgmt_decode_authen(wlan_fr_authen_t * f) +{ + u8 *ie_ptr; + u8 *end = (u8*)f->hdr + f->len; + + f->type = WLAN_FSTYPE_AUTHEN; + + /*-- Fixed Fields ----*/ + f->auth_alg = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_ALG); + f->auth_seq = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_SEQ); + f->status = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_STATUS); + + /*-- Information elements */ + ie_ptr = OFFSET(f->hdr, WLAN_AUTHEN_OFF_CHALLENGE); + if ((ie_ptr < end) && (IE_EID(ie_ptr) == WLAN_EID_CHALLENGE)) { + f->challenge = (wlan_ie_challenge_t *) ie_ptr; + } +} + + +void +wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t * f) +{ + f->type = WLAN_FSTYPE_DEAUTHEN; + + /*-- Fixed Fields ----*/ + f->reason = (u16 *) OFFSET(f->hdr, WLAN_DEAUTHEN_OFF_REASON); + + /*-- Information elements */ +} diff -puN /dev/null drivers/net/wireless/tiacx/wlan_compat.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/wlan_compat.h 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,331 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +/*********************************************************************** +** This code is based on elements which are +** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. +** info@linux-wlan.com +** http://www.linux-wlan.com +*/ + +/*=============================================================*/ +/*------ Establish Platform Identity --------------------------*/ +/*=============================================================*/ +/* Key macros: */ +/* WLAN_CPU_FAMILY */ +#define WLAN_Ix86 1 +#define WLAN_PPC 2 +#define WLAN_Ix96 3 +#define WLAN_ARM 4 +#define WLAN_ALPHA 5 +#define WLAN_MIPS 6 +#define WLAN_HPPA 7 +#define WLAN_SPARC 8 +#define WLAN_SH 9 +#define WLAN_x86_64 10 +/* WLAN_CPU_CORE */ +#define WLAN_I386CORE 1 +#define WLAN_PPCCORE 2 +#define WLAN_I296 3 +#define WLAN_ARMCORE 4 +#define WLAN_ALPHACORE 5 +#define WLAN_MIPSCORE 6 +#define WLAN_HPPACORE 7 +/* WLAN_CPU_PART */ +#define WLAN_I386PART 1 +#define WLAN_MPC860 2 +#define WLAN_MPC823 3 +#define WLAN_I296SA 4 +#define WLAN_PPCPART 5 +#define WLAN_ARMPART 6 +#define WLAN_ALPHAPART 7 +#define WLAN_MIPSPART 8 +#define WLAN_HPPAPART 9 +/* WLAN_SYSARCH */ +#define WLAN_PCAT 1 +#define WLAN_MBX 2 +#define WLAN_RPX 3 +#define WLAN_LWARCH 4 +#define WLAN_PMAC 5 +#define WLAN_SKIFF 6 +#define WLAN_BITSY 7 +#define WLAN_ALPHAARCH 7 +#define WLAN_MIPSARCH 9 +#define WLAN_HPPAARCH 10 +/* WLAN_HOSTIF (generally set on the command line, not detected) */ +#define WLAN_PCMCIA 1 +#define WLAN_ISA 2 +#define WLAN_PCI 3 +#define WLAN_USB 4 +#define WLAN_PLX 5 + +/* Note: the PLX HOSTIF above refers to some vendors implementations for */ +/* PCI. It's a PLX chip that is a PCI to PCMCIA adapter, but it */ +/* isn't a real PCMCIA host interface adapter providing all the */ +/* card&socket services. */ + +#ifdef __powerpc__ +#ifndef __ppc__ +#define __ppc__ +#endif +#endif + +#if (defined(CONFIG_PPC) || defined(CONFIG_8xx)) +#ifndef __ppc__ +#define __ppc__ +#endif +#endif + +#if defined(__x86_64__) + #define WLAN_CPU_FAMILY WLAN_x86_64 + #define WLAN_SYSARCH WLAN_PCAT +#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) + #define WLAN_CPU_FAMILY WLAN_Ix86 + #define WLAN_CPU_CORE WLAN_I386CORE + #define WLAN_CPU_PART WLAN_I386PART + #define WLAN_SYSARCH WLAN_PCAT +#elif defined(__ppc__) + #define WLAN_CPU_FAMILY WLAN_PPC + #define WLAN_CPU_CORE WLAN_PPCCORE + #if defined(CONFIG_MBX) + #define WLAN_CPU_PART WLAN_MPC860 + #define WLAN_SYSARCH WLAN_MBX + #elif defined(CONFIG_RPXLITE) + #define WLAN_CPU_PART WLAN_MPC823 + #define WLAN_SYSARCH WLAN_RPX + #elif defined(CONFIG_RPXCLASSIC) + #define WLAN_CPU_PART WLAN_MPC860 + #define WLAN_SYSARCH WLAN_RPX + #else + #define WLAN_CPU_PART WLAN_PPCPART + #define WLAN_SYSARCH WLAN_PMAC + #endif +#elif defined(__arm__) + #define WLAN_CPU_FAMILY WLAN_ARM + #define WLAN_CPU_CORE WLAN_ARMCORE + #define WLAN_CPU_PART WLAN_ARM_PART + #define WLAN_SYSARCH WLAN_SKIFF +#elif defined(__alpha__) + #define WLAN_CPU_FAMILY WLAN_ALPHA + #define WLAN_CPU_CORE WLAN_ALPHACORE + #define WLAN_CPU_PART WLAN_ALPHAPART + #define WLAN_SYSARCH WLAN_ALPHAARCH +#elif defined(__mips__) + #define WLAN_CPU_FAMILY WLAN_MIPS + #define WLAN_CPU_CORE WLAN_MIPSCORE + #define WLAN_CPU_PART WLAN_MIPSPART + #define WLAN_SYSARCH WLAN_MIPSARCH +#elif defined(__hppa__) + #define WLAN_CPU_FAMILY WLAN_HPPA + #define WLAN_CPU_CORE WLAN_HPPACORE + #define WLAN_CPU_PART WLAN_HPPAPART + #define WLAN_SYSARCH WLAN_HPPAARCH +#elif defined(__sparc__) + #define WLAN_CPU_FAMILY WLAN_SPARC + #define WLAN_SYSARCH WLAN_SPARC +#elif defined(__sh__) + #define WLAN_CPU_FAMILY WLAN_SH + #define WLAN_SYSARCH WLAN_SHARCH + #ifndef __LITTLE_ENDIAN__ + #define __LITTLE_ENDIAN__ + #endif +#else + #error "No CPU identified!" +#endif + +/* + Some big endian machines implicitly do all I/O in little endian mode. + + In particular: + Linux/PPC on PowerMacs (PCI) + Arm/Intel Xscale (PCI) + + This may also affect PLX boards and other BE &| PPC platforms; + as new ones are discovered, add them below. +*/ + +#if ((WLAN_SYSARCH == WLAN_SKIFF) || (WLAN_SYSARCH == WLAN_PMAC)) +#define REVERSE_ENDIAN +#endif + +/*=============================================================*/ +/*------ Hardware Portability Macros --------------------------*/ +/*=============================================================*/ +#if (WLAN_CPU_FAMILY == WLAN_PPC) +#define wlan_inw(a) in_be16((unsigned short *)((a)+_IO_BASE)) +#define wlan_inw_le16_to_cpu(a) inw((a)) +#define wlan_outw(v,a) out_be16((unsigned short *)((a)+_IO_BASE), (v)) +#define wlan_outw_cpu_to_le16(v,a) outw((v),(a)) +#else +#define wlan_inw(a) inw((a)) +#define wlan_inw_le16_to_cpu(a) __cpu_to_le16(inw((a))) +#define wlan_outw(v,a) outw((v),(a)) +#define wlan_outw_cpu_to_le16(v,a) outw(__cpu_to_le16((v)),(a)) +#endif + +/*=============================================================*/ +/*------ Bit settings -----------------------------------------*/ +/*=============================================================*/ +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +#define ieee2host16(n) __le16_to_cpu(n) +#define ieee2host32(n) __le32_to_cpu(n) +#define host2ieee16(n) __cpu_to_le16(n) +#define host2ieee32(n) __cpu_to_le32(n) + +/* for constants */ +#ifdef __LITTLE_ENDIAN + #define IEEE16(a,n) a = n, a##i = n, +#else + #ifdef __BIG_ENDIAN + /* shifts would produce gcc warnings. Oh well... */ + #define IEEE16(a,n) a = n, a##i = ((n&0xff)*256 + ((n&0xff00)/256)), + #else + #error give me endianness or give me death + #endif +#endif + +/*=============================================================*/ +/*------ Compiler Portability Macros --------------------------*/ +/*=============================================================*/ +#define __WLAN_ATTRIB_PACK__ __attribute__ ((packed)) +#define __WLAN_PRAGMA_PACK1__ +#define __WLAN_PRAGMA_PACKDFLT__ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,38)) + typedef struct device netdevice_t; +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)) + typedef struct net_device netdevice_t; +#else + #undef netdevice_t + typedef struct net_device netdevice_t; +#endif + +#ifdef WIRELESS_EXT +#if (WIRELESS_EXT < 13) +struct iw_request_info { + __u16 cmd; /* Wireless Extension command */ + __u16 flags; /* More to come ;-) */ +}; +#endif +#endif + +/* Interrupt handler backwards compatibility stuff */ +#ifndef IRQ_NONE +#define IRQ_NONE +#define IRQ_HANDLED +typedef void irqreturn_t; +#endif + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41) /* more or less */ +#define WLAN_MOD_INC_USE_COUNT MOD_INC_USE_COUNT +#define WLAN_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT +#else +#define WLAN_MOD_INC_USE_COUNT +#define WLAN_MOD_DEC_USE_COUNT +#endif + +#ifndef ARPHRD_IEEE80211_PRISM +#define ARPHRD_IEEE80211_PRISM 802 +#endif + +#define ETH_P_80211_RAW (ETH_P_ECONET + 1) + +/*============================================================================* + * Constants * + *============================================================================*/ + +#define WLAN_IEEE_OUI_LEN 3 + +#define WLAN_ETHCONV_ENCAP 1 +#define WLAN_ETHCONV_RFC1042 2 +#define WLAN_ETHCONV_8021h 3 + +#define WLAN_MIN_ETHFRM_LEN 60 +#define WLAN_MAX_ETHFRM_LEN 1514 +#define WLAN_ETHHDR_LEN 14 + +/*============================================================================* + * Types * + *============================================================================*/ + +/* local ether header type */ +typedef struct wlan_ethhdr { + u8 daddr[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 saddr[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u16 type __WLAN_ATTRIB_PACK__; +} wlan_ethhdr_t; + +/* local llc header type */ +typedef struct wlan_llc { + u8 dsap __WLAN_ATTRIB_PACK__; + u8 ssap __WLAN_ATTRIB_PACK__; + u8 ctl __WLAN_ATTRIB_PACK__; +} wlan_llc_t; + +/* local snap header type */ +typedef struct wlan_snap { + u8 oui[WLAN_IEEE_OUI_LEN] __WLAN_ATTRIB_PACK__; + u16 type __WLAN_ATTRIB_PACK__; +} wlan_snap_t; diff -puN /dev/null drivers/net/wireless/tiacx/wlan_hdr.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/wlan_hdr.h 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,498 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +/*********************************************************************** +** This code is based on elements which are +** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. +** info@linux-wlan.com +** http://www.linux-wlan.com +*/ + +/* mini-doc + +Here are all 11b/11g/11a rates and modulations: + + 11b 11g 11a + --- --- --- + 1 |B |B | + 2 |Q |Q | + 5.5|Cp |C p| + 6 | |Od |O + 9 | |od |o +11 |Cp |C p| +12 | |Od |O +18 | |od |o +22 | | p| +24 | |Od |O +33 | | p| +36 | |od |o +48 | |od |o +54 | |od |o + +Mandatory: + B - DBPSK (Differential Binary Phase Shift Keying) + Q - DQPSK (Differential Quaternary Phase Shift Keying) + C - CCK (Complementary Code Keying, a form of DSSS + (Direct Sequence Spread Spectrum) modulation) + O - OFDM (Orthogonal Frequency Division Multiplexing) +Optional: + o - OFDM + d - CCK-OFDM (also known as DSSS-OFDM) + p - PBCC (Packet Binary Convolutional Coding) + +The term CCK-OFDM may be used interchangeably with DSSS-OFDM +(the IEEE 802.11g-2003 standard uses the latter terminology). +In the CCK-OFDM, the PLCP header of the frame uses the CCK form of DSSS, +while the PLCP payload (the MAC frame) is modulated using OFDM. + +Basically, you must use CCK-OFDM if you have mixed 11b/11g environment, +or else (pure OFDM) 11b equipment may not realize that AP +is sending a packet and start sending its own one. +Sadly, looks like acx111 does not support CCK-OFDM, only pure OFDM. + +Re PBCC: avoid using it. It makes sense only if you have +TI "11b+" hardware. You _must_ use PBCC in order to reach 22Mbps on it. + +Preambles: + +Long preamble (at 1Mbit rate, takes 144 us): + 16 bytes ones + 2 bytes 0xF3A0 (lsb sent first) +PLCP header follows (at 1Mbit also): + 1 byte Signal: speed, in 0.1Mbit units, except for: + 33Mbit: 33 (instead of 330 - doesn't fit in octet) + all CCK-OFDM rates: 30 + 1 byte Service + 0,1,4: reserved + 2: 1=locked clock + 3: 1=PBCC + 5: Length Extension (PBCC 22,33Mbit (11g only)) <- + 6: Length Extension (PBCC 22,33Mbit (11g only)) <- BLACK MAGIC HERE + 7: Length Extension <- + 2 bytes Length (time needed to tx this frame) + a) 5.5 Mbit/s CCK + Length = octets*8/5.5, rounded up to integer + b) 11 Mbit/s CCK + Length = octets*8/11, rounded up to integer + Service bit 7: + 0 = rounding took less than 8/11 + 1 = rounding took more than or equal to 8/11 + c) 5.5 Mbit/s PBCC + Length = (octets+1)*8/5.5, rounded up to integer + d) 11 Mbit/s PBCC + Length = (octets+1)*8/11, rounded up to integer + Service bit 7: + 0 = rounding took less than 8/11 + 1 = rounding took more than or equal to 8/11 + e) 22 Mbit/s PBCC + Length = (octets+1)*8/22, rounded up to integer + Service bits 6,7: + 00 = rounding took less than 8/22ths + 01 = rounding took 8/22...15/22ths + 10 = rounding took 16/22ths or more. + f) 33 Mbit/s PBCC + Length = (octets+1)*8/33, rounded up to integer + Service bits 5,6,7: + 000 rounding took less than 8/33 + 001 rounding took 8/33...15/33 + 010 rounding took 16/33...23/33 + 011 rounding took 24/33...31/33 + 100 rounding took 32/33 or more + 2 bytes CRC + +PSDU follows (up to 2346 bytes at selected rate) + +While Signal value alone is not enough to determine rate and modulation, +Signal+Service is always sufficient. + +Short preamble (at 1Mbit rate, takes 72 us): + 7 bytes zeroes + 2 bytes 0x05CF (lsb sent first) +PLCP header follows *at 2Mbit/s*. Format is the same as in long preamble. +PSDU follows (up to 2346 bytes at selected rate) + +OFDM preamble is completely different, uses OFDM +modulation from the start and thus easily identifiable. +Not shown here. +*/ + + +/*********************************************************************** +** Constants +*/ + +/*--- Sizes -----------------------------------------------*/ +#define WLAN_CRC_LEN 4 +#define WLAN_BSS_TS_LEN 8 +#define WLAN_HDR_A3_LEN 24 +#define WLAN_HDR_A4_LEN 30 +#define WLAN_SSID_MAXLEN 32 +#define WLAN_DATA_MAXLEN 2312 +/* NB: these are sizes of unencrypted frames. +** WEPed frames also have IV in front of encrypted data and ICV after it +*/ +#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN) +#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN) +#define WLAN_BEACON_FR_MAXLEN (WLAN_HDR_A3_LEN + 334) +#define WLAN_ATIM_FR_MAXLEN (WLAN_HDR_A3_LEN + 0) +#define WLAN_DISASSOC_FR_MAXLEN (WLAN_HDR_A3_LEN + 2) +#define WLAN_ASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 48) +#define WLAN_ASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16) +#define WLAN_REASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 54) +#define WLAN_REASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16) +#define WLAN_PROBEREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 44) +#define WLAN_PROBERESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 78) +#define WLAN_AUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 261) +#define WLAN_DEAUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 2) +#define WLAN_WEP_NKEYS 4 +#define WLAN_WEP_MAXKEYLEN 13 +#define WLAN_CHALLENGE_IE_LEN 130 +#define WLAN_CHALLENGE_LEN 128 +/* IV structure: +** 3 bytes: Initialization Vector (24 bits) +** 1 byte: 0..5: padding, must be 0; 6..7: key selector (0-3) +*/ +#define WLAN_WEP_IV_LEN 4 +#define WLAN_WEP_ICV_LEN 4 +#define WLAN_A3FR_MAXLEN_WEP (WLAN_A3FR_MAXLEN + 8) +#define WLAN_A4FR_MAXLEN_WEP (WLAN_A4FR_MAXLEN + 8) + +/*--- Frame Control Field -------------------------------------*/ +/* Frame Types */ +#define WLAN_FTYPE_MGMT 0x00 +#define WLAN_FTYPE_CTL 0x01 +#define WLAN_FTYPE_DATA 0x02 + +/* Frame subtypes */ +/* Management */ +#define WLAN_FSTYPE_ASSOCREQ 0x00 +#define WLAN_FSTYPE_ASSOCRESP 0x01 +#define WLAN_FSTYPE_REASSOCREQ 0x02 +#define WLAN_FSTYPE_REASSOCRESP 0x03 +#define WLAN_FSTYPE_PROBEREQ 0x04 +#define WLAN_FSTYPE_PROBERESP 0x05 +#define WLAN_FSTYPE_BEACON 0x08 +#define WLAN_FSTYPE_ATIM 0x09 +#define WLAN_FSTYPE_DISASSOC 0x0a +#define WLAN_FSTYPE_AUTHEN 0x0b +#define WLAN_FSTYPE_DEAUTHEN 0x0c + +/* Control */ +#define WLAN_FSTYPE_PSPOLL 0x0a +#define WLAN_FSTYPE_RTS 0x0b +#define WLAN_FSTYPE_CTS 0x0c +#define WLAN_FSTYPE_ACK 0x0d +#define WLAN_FSTYPE_CFEND 0x0e +#define WLAN_FSTYPE_CFENDCFACK 0x0f + +/* Data */ +#define WLAN_FSTYPE_DATAONLY 0x00 +#define WLAN_FSTYPE_DATA_CFACK 0x01 +#define WLAN_FSTYPE_DATA_CFPOLL 0x02 +#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03 +#define WLAN_FSTYPE_NULL 0x04 +#define WLAN_FSTYPE_CFACK 0x05 +#define WLAN_FSTYPE_CFPOLL 0x06 +#define WLAN_FSTYPE_CFACK_CFPOLL 0x07 + +/*--- FC Constants v. 2.0 ------------------------------------*/ +/* Each constant is defined twice: WF_CONST is in host */ +/* byteorder, WF_CONSTi is in ieee byteorder. */ +/* Usage: */ +/* printf("the frame subtype is %X", WF_FC_FTYPEi & rx.fc); */ +/* tx.fc = WF_FTYPE_CTLi | WF_FSTYPE_RTSi; */ +/*------------------------------------------------------------*/ + +enum { +/*--- Frame Control Field -------------------------------------*/ +/* Protocol version: always 0 for current 802.11 standards */ +IEEE16(WF_FC_PVER, 0x0003) +IEEE16(WF_FC_FTYPE, 0x000c) +IEEE16(WF_FC_FSTYPE, 0x00f0) +IEEE16(WF_FC_TODS, 0x0100) +IEEE16(WF_FC_FROMDS, 0x0200) +IEEE16(WF_FC_FROMTODS, 0x0300) +IEEE16(WF_FC_MOREFRAG, 0x0400) +IEEE16(WF_FC_RETRY, 0x0800) +/* Indicates PS mode in which STA will be after successful completion +** of current frame exchange sequence. Always 0 for AP frames */ +IEEE16(WF_FC_PWRMGT, 0x1000) +/* What MoreData=1 means: +** From AP to STA in PS mode: don't sleep yet, I have more frames for you +** From Contention-Free (CF) Pollable STA in response to a CF-Poll: +** STA has buffered frames for transmission in response to next CF-Poll +** Bcast/mcast frames transmitted from AP: +** when additional bcast/mcast frames remain to be transmitted by AP +** during this beacon interval +** In all other cases MoreData=0 */ +IEEE16(WF_FC_MOREDATA, 0x2000) +IEEE16(WF_FC_ISWEP, 0x4000) +IEEE16(WF_FC_ORDER, 0x8000) + +/* Frame Types */ +IEEE16(WF_FTYPE_MGMT, 0x00) +IEEE16(WF_FTYPE_CTL, 0x04) +IEEE16(WF_FTYPE_DATA, 0x08) + +/* Frame subtypes */ +/* Management */ +IEEE16(WF_FSTYPE_ASSOCREQ, 0x00) +IEEE16(WF_FSTYPE_ASSOCRESP, 0x10) +IEEE16(WF_FSTYPE_REASSOCREQ, 0x20) +IEEE16(WF_FSTYPE_REASSOCRESP, 0x30) +IEEE16(WF_FSTYPE_PROBEREQ, 0x40) +IEEE16(WF_FSTYPE_PROBERESP, 0x50) +IEEE16(WF_FSTYPE_BEACON, 0x80) +IEEE16(WF_FSTYPE_ATIM, 0x90) +IEEE16(WF_FSTYPE_DISASSOC, 0xa0) +IEEE16(WF_FSTYPE_AUTHEN, 0xb0) +IEEE16(WF_FSTYPE_DEAUTHEN, 0xc0) + +/* Control */ +IEEE16(WF_FSTYPE_PSPOLL, 0xa0) +IEEE16(WF_FSTYPE_RTS, 0xb0) +IEEE16(WF_FSTYPE_CTS, 0xc0) +IEEE16(WF_FSTYPE_ACK, 0xd0) +IEEE16(WF_FSTYPE_CFEND, 0xe0) +IEEE16(WF_FSTYPE_CFENDCFACK, 0xf0) + +/* Data */ +IEEE16(WF_FSTYPE_DATAONLY, 0x00) +IEEE16(WF_FSTYPE_DATA_CFACK, 0x10) +IEEE16(WF_FSTYPE_DATA_CFPOLL, 0x20) +IEEE16(WF_FSTYPE_DATA_CFACK_CFPOLL, 0x30) +IEEE16(WF_FSTYPE_NULL, 0x40) +IEEE16(WF_FSTYPE_CFACK, 0x50) +IEEE16(WF_FSTYPE_CFPOLL, 0x60) +IEEE16(WF_FSTYPE_CFACK_CFPOLL, 0x70) +}; + + +/*********************************************************************** +** Macros +*/ + +/*--- Duration Macros ----------------------------------------*/ +/* Macros to get/set the bitfields of the Duration Field */ +/* - the duration value is only valid when bit15 is zero */ +/* - the firmware handles these values, so I'm not going */ +/* these macros right now. */ +/*------------------------------------------------------------*/ + +/*--- Sequence Control Macros -------------------------------*/ +/* Macros to get/set the bitfields of the Sequence Control */ +/* Field. */ +/*------------------------------------------------------------*/ +#define WLAN_GET_SEQ_FRGNUM(n) (((u16)(n)) & (BIT0|BIT1|BIT2|BIT3)) +#define WLAN_GET_SEQ_SEQNUM(n) ((((u16)(n)) & (~(BIT0|BIT1|BIT2|BIT3))) >> 4) + +/*--- Data ptr macro -----------------------------------------*/ +/* Creates a u8* to the data portion of a frame */ +/* Assumes you're passing in a ptr to the beginning of the hdr*/ +/*------------------------------------------------------------*/ +#define WLAN_HDR_A3_DATAP(p) (((u8*)(p)) + WLAN_HDR_A3_LEN) +#define WLAN_HDR_A4_DATAP(p) (((u8*)(p)) + WLAN_HDR_A4_LEN) + + +/*********************************************************************** +** Types +*/ + +/* BSS Timestamp */ +typedef u8 wlan_bss_ts_t[WLAN_BSS_TS_LEN]; + +/* 802.11 header type +** +** Note the following: +** a1 *always* is receiver's mac or bcast/mcast +** a2 *always* is transmitter's mac, if a2 exists +** seq: [0:3] frag#, [4:15] seq# - used for dup detection +** (dups from retries have same seq#) */ +typedef struct wlan_hdr { + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 a1[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 a2[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 a3[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u16 seq __WLAN_ATTRIB_PACK__; + u8 a4[ETH_ALEN] __WLAN_ATTRIB_PACK__; +} wlan_hdr_t; + +/* Separate structs for use if frame type is known */ +typedef struct wlan_hdr_a3 { + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 a1[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 a2[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 a3[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u16 seq __WLAN_ATTRIB_PACK__; +} wlan_hdr_a3_t; + +typedef struct wlan_hdr_mgmt { + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u16 seq __WLAN_ATTRIB_PACK__; +} wlan_hdr_mgmt_t; + +#ifdef NOT_NEEDED_YET +typedef struct { /* ad-hoc peer->peer (to/from DS = 0/0) */ + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u16 seq __WLAN_ATTRIB_PACK__; +} ibss; +typedef struct { /* ap->sta (to/from DS = 0/1) */ + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u16 seq __WLAN_ATTRIB_PACK__; +} fromap; +typedef struct { /* sta->ap (to/from DS = 1/0) */ + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u16 seq __WLAN_ATTRIB_PACK__; +} toap; +typedef struct { /* wds->wds (to/from DS = 1/1), the only 4addr pkt */ + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 ta[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u16 seq __WLAN_ATTRIB_PACK__; + u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__; +} wds; +typedef struct { /* all management packets */ + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 da[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 sa[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u16 seq __WLAN_ATTRIB_PACK__; +} mgmt; +typedef struct { /* has no body, just a FCS */ + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 ta[ETH_ALEN] __WLAN_ATTRIB_PACK__; +} rts; +typedef struct { /* has no body, just a FCS */ + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__; +} cts; +typedef struct { /* has no body, just a FCS */ + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__; +} ack; +typedef struct { /* has no body, just a FCS */ + u16 fc __WLAN_ATTRIB_PACK__; + /* NB: this one holds Assoc ID in dur field: */ + u16 aid __WLAN_ATTRIB_PACK__; + u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 ta[ETH_ALEN] __WLAN_ATTRIB_PACK__; +} pspoll; +typedef struct { /* has no body, just a FCS */ + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__; +} cfend; +typedef struct { /* has no body, just a FCS */ + u16 fc __WLAN_ATTRIB_PACK__; + u16 dur __WLAN_ATTRIB_PACK__; + u8 ra[ETH_ALEN] __WLAN_ATTRIB_PACK__; + u8 bssid[ETH_ALEN] __WLAN_ATTRIB_PACK__; +} cfendcfack; +#endif + +/* Prism header emulation (monitor mode) */ +typedef struct wlanitem_u32 { + u32 did __WLAN_ATTRIB_PACK__; + u16 status __WLAN_ATTRIB_PACK__; + u16 len __WLAN_ATTRIB_PACK__; + u32 data __WLAN_ATTRIB_PACK__; +} wlanitem_u32_t; +#define WLANITEM_STATUS_data_ok 0 +#define WLANITEM_STATUS_no_value 1 +#define WLANITEM_STATUS_invalid_itemname 2 +#define WLANITEM_STATUS_invalid_itemdata 3 +#define WLANITEM_STATUS_missing_itemdata 4 +#define WLANITEM_STATUS_incomplete_itemdata 5 +#define WLANITEM_STATUS_invalid_msg_did 6 +#define WLANITEM_STATUS_invalid_mib_did 7 +#define WLANITEM_STATUS_missing_conv_func 8 +#define WLANITEM_STATUS_string_too_long 9 +#define WLANITEM_STATUS_data_out_of_range 10 +#define WLANITEM_STATUS_string_too_short 11 +#define WLANITEM_STATUS_missing_valid_func 12 +#define WLANITEM_STATUS_unknown 13 +#define WLANITEM_STATUS_invalid_did 14 +#define WLANITEM_STATUS_missing_print_func 15 + +#define WLAN_DEVNAMELEN_MAX 16 +typedef struct wlansniffrm { + u32 msgcode __WLAN_ATTRIB_PACK__; + u32 msglen __WLAN_ATTRIB_PACK__; + u8 devname[WLAN_DEVNAMELEN_MAX] __WLAN_ATTRIB_PACK__; + wlanitem_u32_t hosttime __WLAN_ATTRIB_PACK__; + wlanitem_u32_t mactime __WLAN_ATTRIB_PACK__; + wlanitem_u32_t channel __WLAN_ATTRIB_PACK__; + wlanitem_u32_t rssi __WLAN_ATTRIB_PACK__; + wlanitem_u32_t sq __WLAN_ATTRIB_PACK__; + wlanitem_u32_t signal __WLAN_ATTRIB_PACK__; + wlanitem_u32_t noise __WLAN_ATTRIB_PACK__; + wlanitem_u32_t rate __WLAN_ATTRIB_PACK__; + wlanitem_u32_t istx __WLAN_ATTRIB_PACK__; /* tx? 0:no 1:yes */ + wlanitem_u32_t frmlen __WLAN_ATTRIB_PACK__; +} wlansniffrm_t; +#define WLANSNIFFFRM 0x0041 +#define WLANSNIFFFRM_hosttime 0x1041 +#define WLANSNIFFFRM_mactime 0x2041 +#define WLANSNIFFFRM_channel 0x3041 +#define WLANSNIFFFRM_rssi 0x4041 +#define WLANSNIFFFRM_sq 0x5041 +#define WLANSNIFFFRM_signal 0x6041 +#define WLANSNIFFFRM_noise 0x7041 +#define WLANSNIFFFRM_rate 0x8041 +#define WLANSNIFFFRM_istx 0x9041 +#define WLANSNIFFFRM_frmlen 0xA041 diff -puN /dev/null drivers/net/wireless/tiacx/wlan_mgmt.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/wlan_mgmt.h 2005-09-07 20:10:30.000000000 -0700 @@ -0,0 +1,580 @@ +/*********************************************************************** +** Copyright (C) 2003 ACX100 Open Source Project +** +** The contents of this file are subject to the Mozilla Public +** License Version 1.1 (the "License"); you may not use this file +** except in compliance with the License. You may obtain a copy of +** the License at http://www.mozilla.org/MPL/ +** +** Software distributed under the License is distributed on an "AS +** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +** implied. See the License for the specific language governing +** rights and limitations under the License. +** +** Alternatively, the contents of this file may be used under the +** terms of the GNU Public License version 2 (the "GPL"), in which +** case the provisions of the GPL are applicable instead of the +** above. If you wish to allow the use of your version of this file +** only under the terms of the GPL and not to allow others to use +** your version of this file under the MPL, indicate your decision +** by deleting the provisions above and replace them with the notice +** and other provisions required by the GPL. If you do not delete +** the provisions above, a recipient may use your version of this +** file under either the MPL or the GPL. +** --------------------------------------------------------------------- +** Inquiries regarding the ACX100 Open Source Project can be +** made directly to: +** +** acx100-users@lists.sf.net +** http://acx100.sf.net +** --------------------------------------------------------------------- +*/ + +/*********************************************************************** +** This code is based on elements which are +** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. +** info@linux-wlan.com +** http://www.linux-wlan.com +*/ + +/*********************************************************************** +** Constants +*/ + +/*-- Information Element IDs --------------------*/ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARMS 2 +#define WLAN_EID_DS_PARMS 3 +#define WLAN_EID_CF_PARMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARMS 6 +#define WLAN_EID_COUNTRY 7 /* 802.11d */ +#define WLAN_EID_FH_HOP_PARMS 8 /* 802.11d */ +#define WLAN_EID_FH_TABLE 9 /* 802.11d */ +#define WLAN_EID_REQUEST 10 /* 802.11d */ +/*-- values 11-15 reserved --*/ +#define WLAN_EID_CHALLENGE 16 +/*-- values 17-31 reserved for challenge text extension --*/ +#define WLAN_EID_ERP_INFO 42 +#define WLAN_EID_RSN 48 +#define WLAN_EID_EXT_RATES 50 +#define WLAN_EID_GENERIC 221 + +#if 0 +#define WLAN_EID_PWR_CONSTRAINT 32 /* 11H PowerConstraint */ +#define WLAN_EID_PWR_CAP 33 /* 11H PowerCapability */ +#define WLAN_EID_TPC_REQUEST 34 /* 11H TPC Request */ +#define WLAN_EID_TPC_REPORT 35 /* 11H TPC Report */ +#define WLAN_EID_SUPP_CHANNELS 36 /* 11H Supported Channels */ +#define WLAN_EID_CHANNEL_SWITCH 37 /* 11H ChannelSwitch */ +#define WLAN_EID_MEASURE_REQUEST 38 /* 11H MeasurementRequest */ +#define WLAN_EID_MEASURE_REPORT 39 /* 11H MeasurementReport */ +#define WLAN_EID_QUIET_ID 40 /* 11H Quiet */ +#define WLAN_EID_IBSS_DFS_ID 41 /* 11H IBSS_DFS */ +#define WLAN_EID_NONERP_ID 47 +#endif + +/*-- Reason Codes -------------------------------*/ +#define WLAN_MGMT_REASON_RSVD 0 +#define WLAN_MGMT_REASON_UNSPEC 1 +#define WLAN_MGMT_REASON_PRIOR_AUTH_INVALID 2 +#define WLAN_MGMT_REASON_DEAUTH_LEAVING 3 +#define WLAN_MGMT_REASON_DISASSOC_INACTIVE 4 +#define WLAN_MGMT_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_MGMT_REASON_CLASS2_NONAUTH 6 +#define WLAN_MGMT_REASON_CLASS3_NONASSOC 7 +#define WLAN_MGMT_REASON_DISASSOC_STA_HASLEFT 8 +#define WLAN_MGMT_REASON_CANT_ASSOC_NONAUTH 9 + +/*-- Status Codes -------------------------------*/ +#define WLAN_MGMT_STATUS_SUCCESS 0 +#define WLAN_MGMT_STATUS_UNSPEC_FAILURE 1 +#define WLAN_MGMT_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_MGMT_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_MGMT_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_MGMT_STATUS_UNSUPPORTED_AUTHALG 13 +#define WLAN_MGMT_STATUS_RX_AUTH_NOSEQ 14 +#define WLAN_MGMT_STATUS_CHALLENGE_FAIL 15 +#define WLAN_MGMT_STATUS_AUTH_TIMEOUT 16 +#define WLAN_MGMT_STATUS_ASSOC_DENIED_BUSY 17 +#define WLAN_MGMT_STATUS_ASSOC_DENIED_RATES 18 +/* p80211b additions */ +#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOAGILITY 21 + +/*-- Auth Algorithm Field ---------------------------*/ +#define WLAN_AUTH_ALG_OPENSYSTEM 0 +#define WLAN_AUTH_ALG_SHAREDKEY 1 + +/*-- Management Frame Field Offsets -------------*/ +/* Note: Not all fields are listed because of variable lengths, */ +/* see the code in p80211.c to see how we search for fields */ +/* Note: These offsets are from the start of the frame data */ + +#define WLAN_BEACON_OFF_TS 0 +#define WLAN_BEACON_OFF_BCN_INT 8 +#define WLAN_BEACON_OFF_CAPINFO 10 +#define WLAN_BEACON_OFF_SSID 12 + +#define WLAN_DISASSOC_OFF_REASON 0 + +#define WLAN_ASSOCREQ_OFF_CAP_INFO 0 +#define WLAN_ASSOCREQ_OFF_LISTEN_INT 2 +#define WLAN_ASSOCREQ_OFF_SSID 4 + +#define WLAN_ASSOCRESP_OFF_CAP_INFO 0 +#define WLAN_ASSOCRESP_OFF_STATUS 2 +#define WLAN_ASSOCRESP_OFF_AID 4 +#define WLAN_ASSOCRESP_OFF_SUPP_RATES 6 + +#define WLAN_REASSOCREQ_OFF_CAP_INFO 0 +#define WLAN_REASSOCREQ_OFF_LISTEN_INT 2 +#define WLAN_REASSOCREQ_OFF_CURR_AP 4 +#define WLAN_REASSOCREQ_OFF_SSID 10 + +#define WLAN_REASSOCRESP_OFF_CAP_INFO 0 +#define WLAN_REASSOCRESP_OFF_STATUS 2 +#define WLAN_REASSOCRESP_OFF_AID 4 +#define WLAN_REASSOCRESP_OFF_SUPP_RATES 6 + +#define WLAN_PROBEREQ_OFF_SSID 0 + +#define WLAN_PROBERESP_OFF_TS 0 +#define WLAN_PROBERESP_OFF_BCN_INT 8 +#define WLAN_PROBERESP_OFF_CAP_INFO 10 +#define WLAN_PROBERESP_OFF_SSID 12 + +#define WLAN_AUTHEN_OFF_AUTH_ALG 0 +#define WLAN_AUTHEN_OFF_AUTH_SEQ 2 +#define WLAN_AUTHEN_OFF_STATUS 4 +#define WLAN_AUTHEN_OFF_CHALLENGE 6 + +#define WLAN_DEAUTHEN_OFF_REASON 0 + +enum { +IEEE16(WF_MGMT_CAP_ESS, 0x0001) +IEEE16(WF_MGMT_CAP_IBSS, 0x0002) +/* In (re)assoc request frames by STA: +** Pollable=0, PollReq=0: STA is not CF-Pollable +** 0 1: STA is CF-Pollable, not requesting to be placed on the CF-Polling list +** 1 0: STA is CF-Pollable, requesting to be placed on the CF-Polling list +** 1 1: STA is CF-Pollable, requesting never to be polled +** In beacon, proberesp, (re)assoc resp frames by AP: +** 0 0: No point coordinator at AP +** 0 1: Point coordinator at AP for delivery only (no polling) +** 1 0: Point coordinator at AP for delivery and polling +** 1 1: Reserved */ +IEEE16(WF_MGMT_CAP_CFPOLLABLE, 0x0004) +IEEE16(WF_MGMT_CAP_CFPOLLREQ, 0x0008) +/* 1=non-WEP data frames are disallowed */ +IEEE16(WF_MGMT_CAP_PRIVACY, 0x0010) +/* In beacon, proberesp, (re)assocresp by AP/AdHoc: +** 1=use of shortpre is allowed ("I can receive shortpre") */ +IEEE16(WF_MGMT_CAP_SHORT, 0x0020) +IEEE16(WF_MGMT_CAP_PBCC, 0x0040) +IEEE16(WF_MGMT_CAP_AGILITY, 0x0080) +/* In (re)assoc request frames by STA: +** 1=short slot time implemented and enabled +** NB: AP shall use long slot time beginning at the next Beacon after assoc +** of STA with this bit set to 0 +** In beacon, proberesp, (re)assoc resp frames by AP: +** currently used slot time value: 0/1 - long/short */ +IEEE16(WF_MGMT_CAP_SHORTSLOT, 0x0400) +/* In (re)assoc request frames by STA: 1=CCK-OFDM is implemented and enabled +** In beacon, proberesp, (re)assoc resp frames by AP/AdHoc: +** 1=CCK-OFDM is allowed */ +IEEE16(WF_MGMT_CAP_CCKOFDM, 0x2000) +}; + + +/*********************************************************************** +** Types +*/ + +/* Information Element types */ + +/* prototype structure, all IEs start with these members */ +typedef struct wlan_ie { + u8 eid __WLAN_ATTRIB_PACK__; + u8 len __WLAN_ATTRIB_PACK__; +} wlan_ie_t; + +/*-- Service Set Identity (SSID) -----------------*/ +typedef struct wlan_ie_ssid { + u8 eid __WLAN_ATTRIB_PACK__; + u8 len __WLAN_ATTRIB_PACK__; + u8 ssid[1] __WLAN_ATTRIB_PACK__; /* may be zero */ +} wlan_ie_ssid_t; + +/*-- Supported Rates -----------------------------*/ +typedef struct wlan_ie_supp_rates { + u8 eid __WLAN_ATTRIB_PACK__; + u8 len __WLAN_ATTRIB_PACK__; + u8 rates[1] __WLAN_ATTRIB_PACK__; /* had better be at LEAST one! */ +} wlan_ie_supp_rates_t; + +/*-- FH Parameter Set ----------------------------*/ +typedef struct wlan_ie_fh_parms { + u8 eid __WLAN_ATTRIB_PACK__; + u8 len __WLAN_ATTRIB_PACK__; + u16 dwell __WLAN_ATTRIB_PACK__; + u8 hopset __WLAN_ATTRIB_PACK__; + u8 hoppattern __WLAN_ATTRIB_PACK__; + u8 hopindex __WLAN_ATTRIB_PACK__; +} wlan_ie_fh_parms_t; + +/*-- DS Parameter Set ----------------------------*/ +typedef struct wlan_ie_ds_parms { + u8 eid __WLAN_ATTRIB_PACK__; + u8 len __WLAN_ATTRIB_PACK__; + u8 curr_ch __WLAN_ATTRIB_PACK__; +} wlan_ie_ds_parms_t; + +/*-- CF Parameter Set ----------------------------*/ +typedef struct wlan_ie_cf_parms { + u8 eid __WLAN_ATTRIB_PACK__; + u8 len __WLAN_ATTRIB_PACK__; + u8 cfp_cnt __WLAN_ATTRIB_PACK__; + u8 cfp_period __WLAN_ATTRIB_PACK__; + u16 cfp_maxdur __WLAN_ATTRIB_PACK__; + u16 cfp_durremaining __WLAN_ATTRIB_PACK__; +} wlan_ie_cf_parms_t; + +/*-- TIM ------------------------------------------*/ +typedef struct wlan_ie_tim { + u8 eid __WLAN_ATTRIB_PACK__; + u8 len __WLAN_ATTRIB_PACK__; + u8 dtim_cnt __WLAN_ATTRIB_PACK__; + u8 dtim_period __WLAN_ATTRIB_PACK__; + u8 bitmap_ctl __WLAN_ATTRIB_PACK__; + u8 virt_bm[1] __WLAN_ATTRIB_PACK__; +} wlan_ie_tim_t; + +/*-- IBSS Parameter Set ---------------------------*/ +typedef struct wlan_ie_ibss_parms { + u8 eid __WLAN_ATTRIB_PACK__; + u8 len __WLAN_ATTRIB_PACK__; + u16 atim_win __WLAN_ATTRIB_PACK__; +} wlan_ie_ibss_parms_t; + +/*-- Challenge Text ------------------------------*/ +typedef struct wlan_ie_challenge { + u8 eid __WLAN_ATTRIB_PACK__; + u8 len __WLAN_ATTRIB_PACK__; + u8 challenge[1] __WLAN_ATTRIB_PACK__; +} wlan_ie_challenge_t; + +/*-- ERP (42) -------------------------------------*/ +typedef struct wlan_ie_erp { + u8 eid __WLAN_ATTRIB_PACK__; + u8 len __WLAN_ATTRIB_PACK__; + /* bit 0:Non ERP present + ** 1:Use Protection + ** 2:Barker Preamble mode + ** 3-7:reserved */ + u8 erp __WLAN_ATTRIB_PACK__; +} wlan_ie_erp_t; + +/* Types for parsing mgmt frames */ + +/* prototype structure, all mgmt frame types will start with these members */ +typedef struct wlan_fr_mgmt { + u16 type; + u16 len; /* DOES NOT include CRC */ + wlan_hdr_t *hdr; + /* used for target specific data, skb in Linux */ + /*-- fixed fields -----------*/ + /*-- info elements ----------*/ +} wlan_fr_mgmt_t; + +/*-- Beacon ---------------------------------------*/ +typedef struct wlan_fr_beacon { + u16 type; + u16 len; + wlan_hdr_t *hdr; + /*-- fixed fields -----------*/ + u64 *ts; + u16 *bcn_int; + u16 *cap_info; + /*-- info elements ----------*/ + wlan_ie_ssid_t *ssid; + wlan_ie_supp_rates_t *supp_rates; + wlan_ie_supp_rates_t *ext_rates; + wlan_ie_fh_parms_t *fh_parms; + wlan_ie_ds_parms_t *ds_parms; + wlan_ie_cf_parms_t *cf_parms; + wlan_ie_ibss_parms_t *ibss_parms; + wlan_ie_tim_t *tim; /* in beacon only, not proberesp */ + wlan_ie_erp_t *erp; /* in beacon only, not proberesp */ +} wlan_fr_beacon_t; +#define wlan_fr_proberesp wlan_fr_beacon +#define wlan_fr_proberesp_t wlan_fr_beacon_t + +/*-- IBSS ATIM ------------------------------------*/ +typedef struct wlan_fr_ibssatim { + u16 type; + u16 len; + wlan_hdr_t *hdr; + /*-- fixed fields -----------*/ + /*-- info elements ----------*/ + /* this frame type has a null body */ +} wlan_fr_ibssatim_t; + +/*-- Disassociation -------------------------------*/ +typedef struct wlan_fr_disassoc { + u16 type; + u16 len; + wlan_hdr_t *hdr; + /*-- fixed fields -----------*/ + u16 *reason; + /*-- info elements ----------*/ +} wlan_fr_disassoc_t; + +/*-- Association Request --------------------------*/ +typedef struct wlan_fr_assocreq { + u16 type; + u16 len; + wlan_hdr_t *hdr; + /*-- fixed fields -----------*/ + u16 *cap_info; + u16 *listen_int; + /*-- info elements ----------*/ + wlan_ie_ssid_t *ssid; + wlan_ie_supp_rates_t *supp_rates; + wlan_ie_supp_rates_t *ext_rates; +} wlan_fr_assocreq_t; + +/*-- Association Response -------------------------*/ +typedef struct wlan_fr_assocresp { + u16 type; + u16 len; + wlan_hdr_t *hdr; + /*-- fixed fields -----------*/ + u16 *cap_info; + u16 *status; + u16 *aid; + /*-- info elements ----------*/ + wlan_ie_supp_rates_t *supp_rates; + wlan_ie_supp_rates_t *ext_rates; +} wlan_fr_assocresp_t; + +/*-- Reassociation Request ------------------------*/ +typedef struct wlan_fr_reassocreq { + u16 type; + u16 len; + wlan_hdr_t *hdr; + /*-- fixed fields -----------*/ + u16 *cap_info; + u16 *listen_int; + u8 *curr_ap; + /*-- info elements ----------*/ + wlan_ie_ssid_t *ssid; + wlan_ie_supp_rates_t *supp_rates; + wlan_ie_supp_rates_t *ext_rates; +} wlan_fr_reassocreq_t; + +/*-- Reassociation Response -----------------------*/ +typedef struct wlan_fr_reassocresp { + u16 type; + u16 len; + wlan_hdr_t *hdr; + /*-- fixed fields -----------*/ + u16 *cap_info; + u16 *status; + u16 *aid; + /*-- info elements ----------*/ + wlan_ie_supp_rates_t *supp_rates; + wlan_ie_supp_rates_t *ext_rates; +} wlan_fr_reassocresp_t; + +/*-- Probe Request --------------------------------*/ +typedef struct wlan_fr_probereq { + u16 type; + u16 len; + wlan_hdr_t *hdr; + /*-- fixed fields -----------*/ + /*-- info elements ----------*/ + wlan_ie_ssid_t *ssid; + wlan_ie_supp_rates_t *supp_rates; + wlan_ie_supp_rates_t *ext_rates; +} wlan_fr_probereq_t; + +/*-- Authentication -------------------------------*/ +typedef struct wlan_fr_authen { + u16 type; + u16 len; + wlan_hdr_t *hdr; + /*-- fixed fields -----------*/ + u16 *auth_alg; + u16 *auth_seq; + u16 *status; + /*-- info elements ----------*/ + wlan_ie_challenge_t *challenge; +} wlan_fr_authen_t; + +/*-- Deauthenication -----------------------------*/ +typedef struct wlan_fr_deauthen { + u16 type; + u16 len; + wlan_hdr_t *hdr; + /*-- fixed fields -----------*/ + u16 *reason; + /*-- info elements ----------*/ +} wlan_fr_deauthen_t; + +/* Types for building mgmt frames */ + +/* Warning. Several types used in below structs are +** in fact variable length. Use structs with such fields with caution */ +typedef struct auth_frame_body { + u16 auth_alg __WLAN_ATTRIB_PACK__; + u16 auth_seq __WLAN_ATTRIB_PACK__; + u16 status __WLAN_ATTRIB_PACK__; + wlan_ie_challenge_t challenge __WLAN_ATTRIB_PACK__; +} auth_frame_body_t; + +typedef struct assocresp_frame_body { + u16 cap_info __WLAN_ATTRIB_PACK__; + u16 status __WLAN_ATTRIB_PACK__; + u16 aid __WLAN_ATTRIB_PACK__; + wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__; +} assocresp_frame_body_t; + +typedef struct reassocreq_frame_body { + u16 cap_info __WLAN_ATTRIB_PACK__; + u16 listen_int __WLAN_ATTRIB_PACK__; + u8 current_ap[ETH_ALEN] __WLAN_ATTRIB_PACK__; + wlan_ie_ssid_t ssid __WLAN_ATTRIB_PACK__; +/* access to this one is disabled since ssid_t is variable length: */ + /* wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__; */ +} reassocreq_frame_body_t; + +typedef struct reassocresp_frame_body { + u16 cap_info __WLAN_ATTRIB_PACK__; + u16 status __WLAN_ATTRIB_PACK__; + u16 aid __WLAN_ATTRIB_PACK__; + wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__; +} reassocresp_frame_body_t; + +typedef struct deauthen_frame_body { + u16 reason __WLAN_ATTRIB_PACK__; +} deauthen_frame_body_t; + +typedef struct disassoc_frame_body { + u16 reason __WLAN_ATTRIB_PACK__; +} disassoc_frame_body_t; + +typedef struct probereq_frame_body { + wlan_ie_ssid_t ssid __WLAN_ATTRIB_PACK__; + wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__; +} probereq_frame_body_t; + +typedef struct proberesp_frame_body { + u8 timestamp[8] __WLAN_ATTRIB_PACK__; + u16 beacon_int __WLAN_ATTRIB_PACK__; + u16 cap_info __WLAN_ATTRIB_PACK__; + wlan_ie_ssid_t ssid __WLAN_ATTRIB_PACK__; +/* access to these is disabled since ssid_t is variable length: */ + /* wlan_ie_supp_rates_t rates __WLAN_ATTRIB_PACK__; */ + /* fhps_t fhps __WLAN_ATTRIB_PACK__; */ + /* dsps_t dsps __WLAN_ATTRIB_PACK__; */ + /* cfps_t cfps __WLAN_ATTRIB_PACK__; */ +} proberesp_frame_body_t; + + +/*********************************************************************** +** Functions +*/ + +/* Helpers for parsing mgmt frames */ +void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t *f); +void wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t *f); +void wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t *f); +void wlan_mgmt_decode_authen(wlan_fr_authen_t *f); +void wlan_mgmt_decode_beacon(wlan_fr_beacon_t *f); +void wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t *f); +void wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t *f); +void wlan_mgmt_decode_probereq(wlan_fr_probereq_t *f); +void wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t *f); +void wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t *f); +void wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t *f); + +/* Helpers for building mgmt frames */ +static inline u8* +wlan_fill_ie_ssid(u8 *p, int len, const char *ssid) +{ + struct wlan_ie_ssid *ie = (void*)p; + ie->eid = WLAN_EID_SSID; + ie->len = len; + memcpy(ie->ssid, ssid, len); + return p + len + 2; +} +/* This controls whether we create 802.11g 'ext supported rates' IEs +** or just create overlong 'supported rates' IEs instead +** (non-11g compliant) */ +#define WE_OBEY_802_11G 1 +static inline u8* +wlan_fill_ie_rates(u8 *p, int len, const u8 *rates) +{ + struct wlan_ie_supp_rates *ie = (void*)p; +#if WE_OBEY_802_11G + if (len > 8 ) len = 8; +#endif + /* supported rates (1 to 8 octets) */ + ie->eid = WLAN_EID_SUPP_RATES; + ie->len = len; + memcpy(ie->rates, rates, len); + return p + len + 2; +} +/* This one wouldn't create an IE at all if not needed */ +static inline u8* +wlan_fill_ie_rates_ext(u8 *p, int len, const u8 *rates) +{ + struct wlan_ie_supp_rates *ie = (void*)p; +#if !WE_OBEY_802_11G + return p; +#endif + len -= 8; + if (len < 0) return p; + /* ext supported rates */ + ie->eid = WLAN_EID_EXT_RATES; + ie->len = len; + memcpy(ie->rates, rates+8, len); + return p + len + 2; +} +static inline u8* +wlan_fill_ie_ds_parms(u8 *p, int channel) +{ + struct wlan_ie_ds_parms *ie = (void*)p; + ie->eid = WLAN_EID_DS_PARMS; + ie->len = 1; + ie->curr_ch = channel; + return p + sizeof(*ie); +} +static inline u8* +wlan_fill_ie_ibss_parms(u8 *p, int atim_win) +{ + struct wlan_ie_ibss_parms *ie = (void*)p; + ie->eid = WLAN_EID_IBSS_PARMS; + ie->len = 2; + ie->atim_win = atim_win; + return p + sizeof(*ie); +} +static inline u8* +wlan_fill_ie_tim(u8 *p, int rem, int period, int bcast, + int ofs, int len, const u8 *vbm) +{ + struct wlan_ie_tim *ie = (void*)p; + ie->eid = WLAN_EID_TIM; + ie->len = len + 3; + ie->dtim_cnt = rem; + ie->dtim_period = period; + ie->bitmap_ctl = ofs | (bcast!=0); + if (vbm) + memcpy(ie->virt_bm, vbm, len); /* min 1 byte */ + else + ie->virt_bm[0] = 0; + return p + len + 3 + 2; +} _