From: Denis Vlasenko [20051016] 0.3.13 * Revert 20051013 fix, we have one which actually works. Thanks Jacek Jablonski for testing! [20051013] * trying to fix "yet another similar bug" * usb fix by Carlos Martin [20051012] 0.3.12 * acx_l_clean_tx_desc bug fixed - was stopping tx completely at high load. (It seems there exists yet another similar bug!) * "unknown IE" dump was 2 bytes too short - fixed * DUP logging made less noisy * another usb fix by Carlos Martin [20051003] * several usb fixes by Carlos Martin - thanks! * unknown IE logging made less noisy * few unknown IEs added to the growing collection * version bump to 0.3.11 [20050916] * fix bogus MTU handling, add ability to change MTU * fix WLAN_DATA_MAXLEN: 2312 -> 2304 * version bump to 0.3.10 [20050915] * by popular request default mode is 'managed' * empty handler for EID 7 (country info) is added * fix 'timer not started - iface is not up' * tx[host]desc micro optimizations * version bump to 0.3.9 [20050914] * tx[host]desc ring workings brought a bit back to two-hostdesc scheme. This is an attempt to fix weird WG311v2 bug. I still fail to understand how same chip with same fw can work for me but do not work for a WG311v2 owner. Mystery. * README updated * version bump to 0.3.8 [20050913] * variable and fields with awful names renamed * a few fields dropped (they had constant values) * small optimization to acx_l_clean_tx_desc() * version bump to 0.3.7 [20050912] * stop using 16 byte "private area" in txdesc - fw bug makes it unreliable * better logging of DUPs * version bump to 0.3.6 [20050911] * use alloc_netdev/free_netdev/netdev_priv * acx_inline.h incorporated into pci.c * helper.c + helper2.c = common.c * marking static functions * enable IE_DOT11_CURRENT_ANTENNA for acx111 * version bump to 0.3.5 Signed-off-by: Denis Vlasenko Signed-off-by: Andrew Morton --- dev/null | 6781 ------------------------------- drivers/net/wireless/tiacx/Changelog | 170 drivers/net/wireless/tiacx/Kconfig | 37 drivers/net/wireless/tiacx/Makefile | 2 drivers/net/wireless/tiacx/README | 33 drivers/net/wireless/tiacx/acx_config.h | 6 drivers/net/wireless/tiacx/acx_func.h | 117 drivers/net/wireless/tiacx/acx_struct.h | 338 - drivers/net/wireless/tiacx/common.c | 6634 ++++++++++++++++++++++++++++++ drivers/net/wireless/tiacx/conv.c | 210 drivers/net/wireless/tiacx/ioctl.c | 383 - drivers/net/wireless/tiacx/pci.c | 2686 +++++------- drivers/net/wireless/tiacx/usb.c | 511 +- drivers/net/wireless/tiacx/wlan.c | 20 drivers/net/wireless/tiacx/wlan_compat.h | 40 drivers/net/wireless/tiacx/wlan_hdr.h | 47 drivers/net/wireless/tiacx/wlan_mgmt.h | 4 17 files changed, 9000 insertions(+), 9019 deletions(-) diff -puN drivers/net/wireless/tiacx/acx_config.h~acx-update-2 drivers/net/wireless/tiacx/acx_config.h --- devel/drivers/net/wireless/tiacx/acx_config.h~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/acx_config.h 2005-10-17 13:06:00.000000000 -0700 @@ -1,4 +1,4 @@ -#define WLAN_RELEASE "v0.3.4" +#define WLAN_RELEASE "v0.3.13" /* set to 0 if you don't want any debugging code to be compiled in */ /* set to 1 if you want some debugging */ @@ -28,10 +28,6 @@ * 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 */ diff -puN drivers/net/wireless/tiacx/acx_func.h~acx-update-2 drivers/net/wireless/tiacx/acx_func.h --- devel/drivers/net/wireless/tiacx/acx_func.h~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/acx_func.h 2005-10-17 13:06:00.000000000 -0700 @@ -120,8 +120,6 @@ do { \ #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 */ @@ -135,12 +133,6 @@ acxlog_mac(int level, const char *head, /*********************************************************************** -** Low-level io routines -*/ -#include "acx_inline.h" - - -/*********************************************************************** ** MAC address helpers */ static inline void @@ -262,7 +254,10 @@ has_only_one_bit(u16 v) ** 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 +** but may be run inder lock +** +** A small number of local helpers do not have acx_[eisl]_ prefix. +** They are always close to caller and are to be revieved locally. ** ** Theory of operation: ** @@ -349,17 +344,12 @@ acx_up_helper(wlandevice_t *priv, const /*********************************************************************** -** 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) /* Can race with rx path (which is not protected by sem): ** rx -> process_[re]assocresp() -> set_status(ASSOCIATED) -> wake_queue() ** Can race with tx_complete IRQ: -** IRQ -> acx_l_clean_tx_desc -> acx_wake_queue +** IRQ -> acxpci_l_clean_txdesc -> acx_wake_queue ** Review carefully all callsites */ static inline void acx_stop_queue(netdevice_t *dev, const char *msg) @@ -468,30 +458,7 @@ int acx_s_interrogate(wlandevice_t *priv #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); -} /*********************************************************************** @@ -512,6 +479,20 @@ acx100pci_ioctl_set_phy_amp_bias( /*********************************************************************** +** /proc +*/ +#ifdef CONFIG_PROC_FS +int acx_proc_register_entries(const struct net_device *dev); +int acx_proc_unregister_entries(const struct net_device *dev); +#else +static inline int +acx_proc_register_entries(const struct net_device *dev) { return OK; } +static inline int +acx_proc_unregister_entries(const struct net_device *dev) { return OK; } +#endif + + +/*********************************************************************** ** Unsorted yet :) */ int acxpci_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf); @@ -539,38 +520,34 @@ 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, int timeout_us); void acx_update_capabilities(wlandevice_t *priv); -int acx_read_eeprom_offset(wlandevice_t *priv, u32 addr, u8 *charbuf); +int acxpci_read_eeprom_byte(wlandevice_t *priv, u32 addr, u8 *charbuf); 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 +int acxpci_s_upload_radio(wlandevice_t *priv); + 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); +void acx_init_task_scheduler(wlandevice_t *priv); +void acx_schedule_task(wlandevice_t *priv, unsigned int set_flag); + 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); -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) { @@ -585,19 +562,16 @@ acx_get_wlan_hdr(wlandevice_t *priv, con } struct sk_buff *acx_rxbuf_to_ether(struct wlandevice *priv, rxbuffer_t *rxbuf); +int acx_ether_to_txbuf(wlandevice_t *priv, void *txbuf, const struct sk_buff *skb); -void acx_l_power_led(wlandevice_t *priv, int enable); +void acxpci_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); +unsigned int acxpci_l_clean_txdesc(wlandevice_t *priv); +void acxpci_l_clean_txdesc_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* acxpci_l_alloc_tx(wlandevice_t *priv); tx_t* acxusb_l_alloc_tx(wlandevice_t *priv); @@ -609,14 +583,14 @@ acx_l_alloc_tx(wlandevice_t *priv) return acxusb_l_alloc_tx(priv); } -void* acxpci_l_get_txbuf(tx_t *tx_opaque); -void* acxusb_l_get_txbuf(tx_t *tx_opaque); +void* acxpci_l_get_txbuf(wlandevice_t *priv, tx_t *tx_opaque); +void* acxusb_l_get_txbuf(wlandevice_t *priv, tx_t *tx_opaque); static inline void* acx_l_get_txbuf(wlandevice_t *priv, tx_t *tx_opaque) { if (IS_PCI(priv)) - return acxpci_l_get_txbuf(tx_opaque); - return acxusb_l_get_txbuf(tx_opaque); + return acxpci_l_get_txbuf(priv, tx_opaque); + return acxusb_l_get_txbuf(priv, tx_opaque); } void acxpci_l_tx_data(wlandevice_t *priv, tx_t *tx_opaque, int len); @@ -634,13 +608,11 @@ 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); +void acxpci_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 ""; } @@ -650,22 +622,25 @@ const char* acx_get_packet_type_string(u const char* acx_cmd_status_str(unsigned int state); int acx_i_start_xmit(struct sk_buff *skb, netdevice_t *dev); -void acx_free_desc_queues(wlandevice_t *priv); +void acxpci_free_desc_queues(wlandevice_t *priv); -int acx_s_create_hostdesc_queues(wlandevice_t *priv); -void acx_create_desc_queues(wlandevice_t *priv, u32 tx_queue_start, u32 rx_queue_start); +int acxpci_s_create_hostdesc_queues(wlandevice_t *priv); +void acxpci_create_desc_queues(wlandevice_t *priv, u32 tx_queue_start, u32 rx_queue_start); -int acx_s_set_tx_level(wlandevice_t *priv, u8 level_dbm); int acx100_s_init_wep(wlandevice_t *priv); int acx100_s_init_packet_templates(wlandevice_t *priv); int acx111_s_init_packet_templates(wlandevice_t *priv); -void great_inquisistor(wlandevice_t *priv); +void great_inquisitor(wlandevice_t *priv); char* acxpci_s_proc_diag_output(char *p, wlandevice_t *priv); -int acx_proc_eeprom_output(char *p, wlandevice_t *priv); -void acx_set_interrupt_mask(wlandevice_t *priv); -int acx100_s_set_tx_level(wlandevice_t *priv, u8 level_dbm); +int acxpci_proc_eeprom_output(char *p, wlandevice_t *priv); +void acxpci_set_interrupt_mask(wlandevice_t *priv); +int acx100pci_s_set_tx_level(wlandevice_t *priv, u8 level_dbm); + +int acx_e_change_mtu(struct net_device *dev, int mtu); +struct net_device_stats* acx_e_get_stats(netdevice_t *dev); +struct iw_statistics* acx_e_get_wireless_stats(netdevice_t *dev); int __init acxpci_e_init_module(void); int __init acxusb_e_init_module(void); diff -L drivers/net/wireless/tiacx/acx_inline.h -puN drivers/net/wireless/tiacx/acx_inline.h~acx-update-2 /dev/null --- devel/drivers/net/wireless/tiacx/acx_inline.h +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,119 +0,0 @@ -/*********************************************************************** -** 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 /* ACX_PCI */ diff -puN drivers/net/wireless/tiacx/acx_struct.h~acx-update-2 drivers/net/wireless/tiacx/acx_struct.h --- devel/drivers/net/wireless/tiacx/acx_struct.h~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/acx_struct.h 2005-10-17 13:06:00.000000000 -0700 @@ -110,7 +110,7 @@ enum { acx_debug = 0 }; #define OK 0 #define NOT_OK 1 -/* The supported chip models (taken from pci_device_id.driver_data) */ +/* The supported chip models */ #define CHIPTYPE_ACX100 1 #define CHIPTYPE_ACX111 2 @@ -207,6 +207,26 @@ enum { acx_debug = 0 }; #define ACX_AFTER_IRQ_COMPLETE_SCAN 0x20 #define ACX_AFTER_IRQ_RESTART_SCAN 0x40 +/*********************************************************************** +** Tx/Rx buffer sizes and watermarks +** +** This will alloc and use DMAable buffers of +** WLAN_A4FR_MAXLEN_WEP_FCS * (RX_CNT + TX_CNT) bytes +** RX/TX_CNT=32 -> ~150k DMA buffers +** RX/TX_CNT=16 -> ~75k DMA buffers +** +** 2005-10-10: reduced memory usage by lowering both to 16 +*/ +#define RX_CNT 16 +#define TX_CNT 16 + +/* we clean up txdescs when we have N free txdesc: */ +#define TX_START_CLEAN (TX_CNT - (TX_CNT/4)) +#define TX_EMERG_CLEAN 2 +/* we stop queue if we have less than N free txbufs: */ +#define TX_STOP_QUEUE 3 +/* we start queue if we have more than N free txbufs: */ +#define TX_START_QUEUE 6 /*********************************************************************** ** Interrogate/Configure cmd constants @@ -260,18 +280,11 @@ DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRIT DEF_IE(1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME ,0x1008, 0x04); DEF_IE(1xx_IE_DOT11_GROUP_ADDR ,0x1009, -1); DEF_IE(1xx_IE_DOT11_CURRENT_REG_DOMAIN ,0x100a, 0x02); -#ifdef ACX_USB -DEF_IE(1xx_IE_DOT11_CURRENT_ANTENNA ,0x100b, 0x01); -#else -DEF_IE(1xx_IE_DOT11_CURRENT_ANTENNA ,0x100b, 0x02); -#endif +//It's harmless to have larger struct. Use USB case always. +DEF_IE(1xx_IE_DOT11_CURRENT_ANTENNA ,0x100b, 0x02); /* in fact len=1 for PCI */ DEF_IE(1xx_IE_DOT11_UNKNOWN_100C ,0x100c, -1); /* mapped to cfgInvalid in FW150 */ DEF_IE(1xx_IE_DOT11_TX_POWER_LEVEL ,0x100d, 0x01); -#ifdef ACX_USB -DEF_IE(1xx_IE_DOT11_CURRENT_CCA_MODE ,0x100e, 0x01); -#else -DEF_IE(1xx_IE_DOT11_CURRENT_CCA_MODE ,0x100e, 0x02); -#endif +DEF_IE(1xx_IE_DOT11_CURRENT_CCA_MODE ,0x100e, 0x02); /* in fact len=1 for PCI */ //USB doesn't return anything - len==0?! DEF_IE(100_IE_DOT11_ED_THRESHOLD ,0x100f, 0x04); DEF_IE(1xx_IE_DOT11_WEP_DEFAULT_KEY_SET ,0x1010, 0x01); /* set default key ID */ @@ -280,52 +293,130 @@ DEF_IE(100_IE_DOT11_UNKNOWN_1012 ,0x1012 DEF_IE(100_IE_DOT11_UNKNOWN_1013 ,0x1013, -1); /* mapped to cfgInvalid in FW150 */ #if 0 +/* Experimentally obtained on acx100, fw 1.9.8.b +** -1 means that fw returned 'invalid IE' +** 0200 FC00 nnnn... are test read contents: u16 type, u16 len, data +** (AA are poison bytes marking bytes not written by fw) +** +** Looks like acx100 fw does not update len field (thus len=256-4=FC here) +** A number of IEs seem to trash type,len fields +** IEs marked 'huge' return gobs of data (no poison bytes remain) +*/ +DEF_IE(100_IE_INVAL_00, 0x0000, -1); +DEF_IE(100_IE_INVAL_01, 0x0001, -1); /* IE_ACX_TIMER, len=16 on older fw */ +DEF_IE(100_IE_POWER_MGMT, 0x0002, 4); /* 0200FC00 00040000 AAAAAAAA */ +DEF_IE(100_IE_QUEUE_CONFIG, 0x0003, 28); /* 0300FC00 48060000 9CAD0000 0101AAAA DCB00000 E4B00000 9CAA0000 00AAAAAA */ +DEF_IE(100_IE_BLOCK_SIZE, 0x0004, 2); /* 0400FC00 0001AAAA AAAAAAAA AAAAAAAA */ +/* write only: */ +DEF_IE(100_IE_MEMORY_CONFIG_OPTIONS, 0x0005, 20); +DEF_IE(100_IE_RATE_FALLBACK, 0x0006, 1); /* 0600FC00 00AAAAAA AAAAAAAA AAAAAAAA */ +/* write only: */ +DEF_IE(100_IE_WEP_OPTIONS, 0x0007, 3); +DEF_IE(100_IE_MEMORY_MAP, 0x0008, 40); /* huge: 0800FC00 30000000 6CA20000 70A20000... */ +/* gives INVAL on read: */ +DEF_IE(100_IE_SCAN_STATUS, 0x0009, -1); +DEF_IE(100_IE_ASSOC_ID, 0x000a, 2); /* huge: 0A00FC00 00000000 01040800 00000000... */ +DEF_IE(100_IE_INVAL_0B, 0x000b, -1); +/* 'command rejected': */ +DEF_IE(100_IE_CONFIG_OPTIONS, 0x000c, -3); +DEF_IE(100_IE_FWREV, 0x000d, 24); /* 0D00FC00 52657620 312E392E 382E6200 AAAAAAAA AAAAAAAA 05050201 AAAAAAAA */ +DEF_IE(100_IE_FCS_ERROR_COUNT, 0x000e, 4); +DEF_IE(100_IE_MEDIUM_USAGE, 0x000f, 8); /* E41F0000 2D780300 FCC91300 AAAAAAAA */ +DEF_IE(100_IE_RXCONFIG, 0x0010, 4); /* 1000FC00 00280000 AAAAAAAA AAAAAAAA */ +DEF_IE(100_IE_QUEUE_THRESH, 0x0011, 12); /* 1100FC00 AAAAAAAA 00000000 00000000 */ +DEF_IE(100_IE_BSS_POWER_SAVE, 0x0012, 1); /* 1200FC00 00AAAAAA AAAAAAAA AAAAAAAA */ +/* read only, variable len */ +DEF_IE(100_IE_FIRMWARE_STATISTICS, 0x0013, 256); /* 0000AC00 00000000 ... */ +DEF_IE(100_IE_INT_CONFIG, 0x0014, 20); /* 00000000 00000000 00000000 00000000 5D74D105 00000000 AAAAAAAA AAAAAAAA */ +DEF_IE(100_IE_FEATURE_CONFIG, 0x0015, 8); /* 1500FC00 16000000 AAAAAAAA AAAAAAAA */ +/* returns 'invalid MAC': */ +DEF_IE(100_IE_KEY_CHOOSE, 0x0016, -4); +DEF_IE(100_IE_INVAL_17, 0x0017, -1); +DEF_IE(100_IE_UNKNOWN_18, 0x0018, 0); /* null len?! 1800FC00 AAAAAAAA AAAAAAAA AAAAAAAA */ +DEF_IE(100_IE_UNKNOWN_19, 0x0019, 256); /* huge: 1900FC00 9C1F00EA FEFFFFEA FEFFFFEA... */ +DEF_IE(100_IE_INVAL_1A, 0x001A, -1); + +DEF_IE(100_IE_DOT11_INVAL_1000, 0x1000, -1); +DEF_IE(100_IE_DOT11_STATION_ID, 0x1001, 6); /* huge: 0110FC00 58B10E2F 03000000 00000000... */ +DEF_IE(100_IE_DOT11_INVAL_1002, 0x1002, -1); +DEF_IE(100_IE_DOT11_INVAL_1003, 0x1003, -1); +DEF_IE(100_IE_DOT11_INVAL_1004, 0x1004, -1); +DEF_IE(100_IE_DOT11_SHORT_RETRY_LIMIT, 0x1005, 1); +DEF_IE(100_IE_DOT11_LONG_RETRY_LIMIT, 0x1006, 1); +/* write only: */ +DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE, 0x1007, 32); +DEF_IE(100_IE_DOT11_MAX_XMIT_MSDU_LIFETIME, 0x1008, 4); /* huge: 0810FC00 00020000 F4010000 00000000... */ +/* undoc but returns something */ +DEF_IE(100_IE_DOT11_GROUP_ADDR, 0x1009, 12); /* huge: 0910FC00 00000000 00000000 00000000... */ +DEF_IE(100_IE_DOT11_CURRENT_REG_DOMAIN, 0x100a, 1); /* 0A10FC00 30AAAAAA AAAAAAAA AAAAAAAA */ +DEF_IE(100_IE_DOT11_CURRENT_ANTENNA, 0x100b, 1); /* 0B10FC00 8FAAAAAA AAAAAAAA AAAAAAAA */ +DEF_IE(100_IE_DOT11_INVAL_100C, 0x100c, -1); +DEF_IE(100_IE_DOT11_TX_POWER_LEVEL, 0x100d, 2); /* 00000000 0100AAAA AAAAAAAA AAAAAAAA */ +DEF_IE(100_IE_DOT11_CURRENT_CCA_MODE, 0x100e, 1); /* 0E10FC00 0DAAAAAA AAAAAAAA AAAAAAAA */ +DEF_IE(100_IE_DOT11_ED_THRESHOLD, 0x100f, 4); /* 0F10FC00 70000000 AAAAAAAA AAAAAAAA */ +/* set default key ID */ +DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_SET, 0x1010, 1); /* 1010FC00 00AAAAAA AAAAAAAA AAAAAAAA */ +DEF_IE(100_IE_DOT11_INVAL_1011, 0x1011, -1); +DEF_IE(100_IE_DOT11_INVAL_1012, 0x1012, -1); +DEF_IE(100_IE_DOT11_INVAL_1013, 0x1013, -1); +DEF_IE(100_IE_DOT11_UNKNOWN_1014, 0x1014, 256); /* huge */ +DEF_IE(100_IE_DOT11_UNKNOWN_1015, 0x1015, 256); /* huge */ +DEF_IE(100_IE_DOT11_UNKNOWN_1016, 0x1016, 256); /* huge */ +DEF_IE(100_IE_DOT11_UNKNOWN_1017, 0x1017, 256); /* huge */ +DEF_IE(100_IE_DOT11_UNKNOWN_1018, 0x1018, 256); /* huge */ +DEF_IE(100_IE_DOT11_UNKNOWN_1019, 0x1019, 256); /* huge */ +#endif + +#if 0 /* Experimentally obtained on PCI acx111 Xterasys XN-2522g, fw 1.2.1.34 ** -1 means that fw returned 'invalid IE' ** 0400 0800 nnnn... are test read contents: u16 type, u16 len, data -** (AA are poison bytes marking bytes not written by fw) */ -DEF_IE(111_IE_INVAL_00, 0x0000, -1); -DEF_IE(111_IE_INVAL_01, 0x0001, -1); -DEF_IE(111_IE_POWER_MGMT, 0x0002, 12); +** (AA are poison bytes marking bytes not written by fw) +** +** Looks like acx111 fw reports real len! +*/ +DEF_IE(111_IE_INVAL_00, 0x0000, -1); +DEF_IE(111_IE_INVAL_01, 0x0001, -1); +DEF_IE(111_IE_POWER_MGMT, 0x0002, 12); /* write only, variable len: 12 + rxqueue_cnt*8 + txqueue_cnt*4: */ -DEF_IE(111_IE_MEMORY_CONFIG, 0x0003, 24); -DEF_IE(111_IE_BLOCK_SIZE, 0x0004, 8); /* 04000800 AA00AAAA AAAAAAAA */ +DEF_IE(111_IE_MEMORY_CONFIG, 0x0003, 24); +DEF_IE(111_IE_BLOCK_SIZE, 0x0004, 8); /* 04000800 AA00AAAA AAAAAAAA */ /* variable len: 8 + rxqueue_cnt*8 + txqueue_cnt*8: */ -DEF_IE(111_IE_QUEUE_HEAD, 0x0005, 24); -DEF_IE(111_IE_RATE_FALLBACK, 0x0006, 1); +DEF_IE(111_IE_QUEUE_HEAD, 0x0005, 24); +DEF_IE(111_IE_RATE_FALLBACK, 0x0006, 1); /* acx100 name:WEP_OPTIONS */ /* said to have len:1 (not true, actually returns 12 bytes): */ -DEF_IE(111_IE_RADIO_BAND, 0x0007, 12); /* 07000C00 AAAA1F00 FF03AAAA AAAAAAAA */ -DEF_IE(111_IE_MEMORY_MAP, 0x0008, 48); +DEF_IE(111_IE_RADIO_BAND, 0x0007, 12); /* 07000C00 AAAA1F00 FF03AAAA AAAAAAAA */ +DEF_IE(111_IE_MEMORY_MAP, 0x0008, 48); /* said to have len:4, but gives INVAL on read: */ -DEF_IE(111_IE_SCAN_STATUS, 0x0009, -1); -DEF_IE(111_IE_ASSOC_ID, 0x000a, 2); +DEF_IE(111_IE_SCAN_STATUS, 0x0009, -1); +DEF_IE(111_IE_ASSOC_ID, 0x000a, 2); /* write only, len is not known: */ -DEF_IE(111_IE_UNKNOWN_0B, 0x000b, 0); +DEF_IE(111_IE_UNKNOWN_0B, 0x000b, 0); /* read only, variable len. I see 67 byte reads: */ -DEF_IE(111_IE_CONFIG_OPTIONS, 0x000c, 67); /* 0C004300 01160500 ... */ -DEF_IE(111_IE_FWREV, 0x000d, 24); -DEF_IE(111_IE_FCS_ERROR_COUNT, 0x000e, 4); -DEF_IE(111_IE_MEDIUM_USAGE, 0x000f, 8); -DEF_IE(111_IE_RXCONFIG, 0x0010, 4); -DEF_IE(111_IE_QUEUE_THRESH, 0x0011, 12); -DEF_IE(111_IE_BSS_POWER_SAVE, 0x0012, 1); +DEF_IE(111_IE_CONFIG_OPTIONS, 0x000c, 67); /* 0C004300 01160500 ... */ +DEF_IE(111_IE_FWREV, 0x000d, 24); +DEF_IE(111_IE_FCS_ERROR_COUNT, 0x000e, 4); +DEF_IE(111_IE_MEDIUM_USAGE, 0x000f, 8); +DEF_IE(111_IE_RXCONFIG, 0x0010, 4); +DEF_IE(111_IE_QUEUE_THRESH, 0x0011, 12); +DEF_IE(111_IE_BSS_POWER_SAVE, 0x0012, 1); /* read only, variable len. I see 240 byte reads: */ -DEF_IE(111_IE_FIRMWARE_STATISTICS, 0x0013, 240); /* 1300F000 00000000 ... */ +DEF_IE(111_IE_FIRMWARE_STATISTICS, 0x0013, 240); /* 1300F000 00000000 ... */ /* said to have len=17. looks like fw pads it to 20: */ -DEF_IE(111_IE_INT_CONFIG, 0x0014, 20); /* 14001400 00000000 00000000 00000000 00000000 00000000 */ -DEF_IE(111_IE_FEATURE_CONFIG, 0x0015, 8); +DEF_IE(111_IE_INT_CONFIG, 0x0014, 20); /* 14001400 00000000 00000000 00000000 00000000 00000000 */ +DEF_IE(111_IE_FEATURE_CONFIG, 0x0015, 8); /* said to be name:KEY_INDICATOR, len:4, but gives INVAL on read: */ -DEF_IE(111_IE_KEY_CHOOSE, 0x0016, -1); +DEF_IE(111_IE_KEY_CHOOSE, 0x0016, -1); /* said to have len:4, but in fact returns 8: */ -DEF_IE(111_IE_MAX_USB_XFR, 0x0017, 8); /* 17000800 00014000 00000000 */ -DEF_IE(111_IE_INVAL_18, 0x0018, -1); -DEF_IE(111_IE_INVAL_19, 0x0019, -1); +DEF_IE(111_IE_MAX_USB_XFR, 0x0017, 8); /* 17000800 00014000 00000000 */ +DEF_IE(111_IE_INVAL_18, 0x0018, -1); +DEF_IE(111_IE_INVAL_19, 0x0019, -1); /* undoc but returns something: */ /* huh, fw indicates len=20 but uses 4 more bytes in buffer??? */ -DEF_IE(111_IE_UNKNOWN_1A, 0x001A, 20); /* 1A001400 AA00AAAA 0000020F FF030000 00020000 00000007 04000000 */ +DEF_IE(111_IE_UNKNOWN_1A, 0x001A, 20); /* 1A001400 AA00AAAA 0000020F FF030000 00020000 00000007 04000000 */ -DEF_IE(111_IE_DOT11_INVAL_0000, 0x1000, -1); +DEF_IE(111_IE_DOT11_INVAL_1000, 0x1000, -1); DEF_IE(111_IE_DOT11_STATION_ID, 0x1001, 6); DEF_IE(111_IE_DOT11_FRAG_THRESH, 0x1002, 2); /* acx100 only? gives INVAL on read: */ @@ -463,7 +554,7 @@ typedef struct rxbuffer { ** 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; + u8 data_a3[WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN] ACX_PACKED; /* can add hdr/data_a4 if needed */ } rxbuffer_t; @@ -524,13 +615,6 @@ typedef struct fw_ver { /*--- 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 { @@ -681,11 +765,12 @@ typedef struct { #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) + DESC_CTL_AUTODMA | DESC_CTL_FIRSTFRAG) +#define DESC_CTL_ACXDONE_HOSTOWN (DESC_CTL_ACXDONE | DESC_CTL_HOSTOWN) -#define DESC_CTL_HOSTOWN_STR "80" -#define DESC_CTL_DONE_STR "C0" +/* Descriptor Status field + */ +#define DESC_STATUS_FULL (1 << 31) /* NB: some bits may be interesting for Monitor mode tx (aka Raw tx): */ #define DESC_CTL2_SEQ 0x01 /* don't increase sequence field */ @@ -701,7 +786,7 @@ typedef struct { ** PCI structures */ /* IRQ Constants -** (outside of "#ifdef PCI" because USB (mis)uses HOST_INT_SCAN_COMPLETE */ +** (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 @@ -728,16 +813,12 @@ struct txdesc { 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; + +/* The following 16 bytes do not change when acx100 owns the descriptor */ +/* BUG: fw clears last byte of this area which is supposedly reserved +** for driver use. amd64 blew up. We dare not use it now */ + u32 dummy[4] ACX_PACKED; + u8 Ctl_8 ACX_PACKED; /* 0x24, 8bit value */ u8 Ctl2_8 ACX_PACKED; /* 0x25, 8bit value */ u8 error ACX_PACKED; /* 0x26 */ @@ -872,13 +953,6 @@ struct rxhostdesc { rxbuffer_t *data ACX_PACKED; }; -/* figure out tx descriptor pointer, depending on different acx100 or acx111 - * tx descriptor length */ -#define GET_TX_DESC_PTR(priv, index) \ - (struct txdesc *) (((u8 *)(priv)->pTxDescQPool) + ((index) * (priv)->TxDescrSize)) -#define GET_NEXT_TX_DESC_PTR(priv, tx_desc) \ - (struct txdesc *) (((u8 *)(tx_desc)) + (priv)->TxDescrSize) - #endif /* ACX_PCI */ /*************************************************************** @@ -918,14 +992,9 @@ typedef struct usb_txbuffer { u8 ctrl2 ACX_PACKED; u16 dataLength ACX_PACKED; /* wlan packet content is placed here: */ - u8 data[WLAN_A4FR_MAXLEN_WEP] ACX_PACKED; + u8 data[WLAN_A4FR_MAXLEN_WEP_FCS] 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; @@ -935,11 +1004,17 @@ typedef struct usb_tx { usb_txbuffer_t bulkout; } usb_tx_t; +typedef struct usb_rx { + unsigned busy:1; + struct urb *urb; + wlandevice_t *priv; + rxbuffer_t bulkin; +} usb_rx_t; #endif /* ACX_USB */ /*============================================================================* - * Main acx per-device data structure (netdev->priv) * + * Main acx per-device data structure (netdev_priv(dev)) * *============================================================================*/ #define ACX_STATE_FW_LOADED 0x01 #define ACX_STATE_IFACE_UP 0x02 @@ -1066,6 +1141,7 @@ struct wlandevice { unsigned long dup_msg_expiry; int dup_count; + int nondup_count; u16 last_seq_ctrl; /* duplicate packet detection */ /* 802.11 power save mode */ @@ -1131,55 +1207,44 @@ struct wlandevice { /*** Card Rx/Tx management ***/ u16 rx_config_1; u16 rx_config_2; - u32 tx_cnt_done; u16 memblocksize; -//TODO: rename: xxQueueXx -> xxdesc_xx - u32 RxQueueCnt; - u32 TxQueueCnt; - u32 TxQueueFree; + int tx_free; /*** Unknown ***/ u8 dtim_interval; /*** PCI/USB/... must be last or else hw agnostic code breaks horribly ***/ - rxhostdesc_t *RxHostDescPoolStart; /* hack to let common code compile */ + /* hack to let common code compile. FIXME */ + dma_addr_t rxhostdesc_startphy; /*** PCI stuff ***/ #ifdef ACX_PCI -//TODO: horribly long, Pascal-like names -//Make up our mind on "Queue? Pool? Desc?" usage and rename: -//Xx[Host]DescQPool[PhyAddr/Size] -> xx[host]desc_start[_phy], xx[host]desc_size -//XxBufferPool[PhyAddr/Size] -> xxbuf_start[_phy], xxbuf_size -//xx_pool_count -> xxdesc_count /* 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 */ + ** and tx descs in device memory */ + u8 *txbuf_start; + txhostdesc_t *txhostdesc_start; + txdesc_t *txdesc_start; /* points to PCI-mapped memory */ /* same for rx */ - rxbuffer_t *pRxBufferPool; - rxhostdesc_t *pRxHostDescQPool; - rxdesc_t *pRxDescQPool; + rxbuffer_t *rxbuf_start; + rxhostdesc_t *rxhostdesc_start; + rxdesc_t *rxdesc_start; /* physical addresses of above host memory areas */ - dma_addr_t RxBufferPoolPhyAddr; - dma_addr_t RxHostDescQPoolPhyAddr; - dma_addr_t TxBufferPoolPhyAddr; - dma_addr_t TxHostDescQPoolPhyAddr; + dma_addr_t rxbuf_startphy; + /* dma_addr_t rxhostdesc_startphy; */ + dma_addr_t txbuf_startphy; + dma_addr_t txhostdesc_startphy; /* 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 txbuf_area_size; + unsigned int txhostdesc_area_size; + unsigned int rxbuf_area_size; + unsigned int rxhostdesc_area_size; + + unsigned int txdesc_size; /* size per tx descr; ACX111 = ACX100 + 4 */ + unsigned int tx_head; /* current ring buffer pool member index */ unsigned int tx_tail; - unsigned int rx_pool_count; unsigned int rx_tail; - /* Same as pRxHostDescQPool, but possibly aligned to 4 bytes: */ -//TODO: rename: xxPoolStart -> xx_start - /* rxhostdesc_t *RxHostDescPoolStart; hack to let common code compile */ + + client_t *txc[TX_CNT]; u8 need_radio_fw; u8 irqs_active; /* whether irq sending is activated */ @@ -1217,18 +1282,12 @@ struct wlandevice { 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]; + + usb_tx_t *usb_tx; + usb_rx_t *usb_rx; int bulkinep; /* bulk-in endpoint */ int bulkoutep; /* bulk-out endpoint */ - int usb_free_tx; int rxtruncsize; #endif @@ -1347,10 +1406,6 @@ struct wlandevice { #include /* struct pci_device */ #endif -#if USE_FW_LOADER_LEGACY -extern char *firmware_dir; -#endif - /*********************************************************************** */ @@ -1360,7 +1415,6 @@ typedef struct acx100_ie_memblocksize { u16 size ACX_PACKED; } acx100_ie_memblocksize_t; -//TODO: Make up our mind on "Queue? Pool? Desc?" usage and rename typedef struct acx100_ie_queueconfig { u16 type ACX_PACKED; u16 len ACX_PACKED; @@ -1420,7 +1474,7 @@ typedef struct acx111_ie_memoryconfig { 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; + acx_ptr rx_queue1_host_rx_start ACX_PACKED; /* end of rx1 block */ /* start of tx1 block */ @@ -1431,7 +1485,6 @@ typedef struct acx111_ie_memoryconfig { /* end of tx1 block */ } acx111_ie_memoryconfig_t; -//TODO: Make up our mind on "Queue? Pool? Desc?" usage and rename typedef struct acx_ie_memmap { u16 type ACX_PACKED; u16 len ACX_PACKED; @@ -1447,20 +1500,18 @@ typedef struct acx_ie_memmap { u32 PoolEnd ACX_PACKED; } acx_ie_memmap_t; -//TODO: rename to acx_ie_xxxx -typedef struct ACX111FeatureConfig { +typedef struct acx111_ie_feature_config { u16 type ACX_PACKED; u16 len ACX_PACKED; u32 feature_options ACX_PACKED; u32 data_flow_options ACX_PACKED; -} ACX111FeatureConfig_t; +} acx111_ie_feature_config_t; -//TODO: rename to acx_ie_xxxx -typedef struct ACX111TxLevel { +typedef struct acx111_ie_tx_level { u16 type ACX_PACKED; u16 len ACX_PACKED; u8 level ACX_PACKED; -} ACX111TxLevel_t; +} acx111_ie_tx_level_t; #define PS_CFG_ENABLE 0x80 #define PS_CFG_PENDING 0x40 /* status flag when entering PS */ @@ -1857,6 +1908,23 @@ typedef struct acx111_ie_configoption { } acx111_ie_configoption_t; +/*********************************************************************** +*/ +#define CHECK_SIZEOF(type,size) { \ + extern void BUG_bad_size_for_##type(void); \ + if (sizeof(type)!=(size)) BUG_bad_size_for_##type(); \ +} + +static inline void +acx_struct_size_check(void) +{ + CHECK_SIZEOF(txdesc_t, 0x30); + CHECK_SIZEOF(acx100_ie_memconfigoption_t, 24); + CHECK_SIZEOF(acx100_ie_queueconfig_t, 0x20); + CHECK_SIZEOF(acx_joinbss_t, 0x30); +} + + /*============================================================================* * Global data * *============================================================================*/ @@ -1867,7 +1935,3 @@ extern const u8 reg_domain_ids[]; extern const u8 reg_domain_ids_len; extern const struct iw_handler_def acx_ioctl_handler_def; - -extern char *firmware_dir; - -#define MINFREE_TX 3 diff -puN drivers/net/wireless/tiacx/Changelog~acx-update-2 drivers/net/wireless/tiacx/Changelog --- devel/drivers/net/wireless/tiacx/Changelog~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/Changelog 2005-10-17 13:06:00.000000000 -0700 @@ -1,3 +1,173 @@ +TODO: +e100.c: pci_dma_sync_single_for_cpu(nic->pdev, rx->dma_addr, sizeof(struct rfd), PCI_DMA_FROMDEVICE); +do we need to do something like above for rxhostdescs/rxbufs? + +TODO: from Efthym : +13:12:42 wlan0: rx: 31 DUPs in 551 packets received in 10 sec +13:12:48 wlan0: tx error 0x20, buf 20! (excessive Tx retries +13:12:48 wlan0: tx error 0x20, buf 21! (excessive Tx retries +13:12:48 wlan0: several excessive Tx retry errors occurred, a +13:12:48 wlan0: tx error 0x20, buf 22! (excessive Tx retries +13:12:48 wlan0: tx error 0x20, buf 23! (excessive Tx retries +13:12:48 wlan0: tx error 0x20, buf 24! (excessive Tx retries +13:12:48 wlan0: recalibrating radio +13:12:48 wlan0: successfully recalibrated radio +13:12:52 wlan0: tx error 0x20, buf 25! (excessive Tx retries +13:12:52 wlan0: several excessive Tx retry errors occurred, a +13:12:52 wlan0: tx error 0x20, buf 26! (excessive Tx retries +13:12:52 wlan0: tx error 0x20, buf 27! (excessive Tx retries +13:12:52 wlan0: tx error 0x20, buf 28! (excessive Tx retries +13:12:52 wlan0: tx error 0x20, buf 29! (excessive Tx retries +13:12:52 wlan0: several excessive Tx retry errors occurred, a +13:12:52 wlan0: tx error 0x20, buf 30! (excessive Tx retries +13:12:52 wlan0: tx error 0x20, buf 31! (excessive Tx retries +13:12:52 wlan0: tx error 0x20, buf 00! (excessive Tx retries +13:12:52 wlan0: less than 5 minutes since last radio recalibr +13:12:58 wlan0: tx error 0x20, buf 01! (excessive Tx retries +13:12:58 wlan0: several excessive Tx retry errors occurred, a +13:12:58 wlan0: tx error 0x20, buf 02! (excessive Tx retries +13:12:58 wlan0: tx error 0x20, buf 03! (excessive Tx retries +13:12:58 wlan0: tx error 0x20, buf 04! (excessive Tx retries +13:12:58 wlan0: tx error 0x20, buf 05! (excessive Tx retries +13:12:58 wlan0: several excessive Tx retry errors occurred, a +13:12:58 disabling above notification message +13:12:58 wlan0: tx error 0x20, buf 06! (excessive Tx retries +13:12:58 wlan0: tx error 0x20, buf 07! +13:12:58 wlan0: tx error 0x20, buf 08! +13:12:58 wlan0: less than 5 minutes since last radio recalibr +13:13:06 wlan0: tx error 0x20, buf 09! +13:13:06 wlan0: tx error 0x20, buf 10! +13:13:06 wlan0: tx error 0x20, buf 11! +13:13:06 wlan0: tx error 0x20, buf 12! +13:13:06 wlan0: tx error 0x20, buf 13! +13:13:06 wlan0: tx error 0x20, buf 14! +13:13:06 wlan0: tx error 0x20, buf 15! +13:13:06 wlan0: tx error 0x20, buf 16! +13:13:06 wlan0: less than 5 minutes since last radio recalibr +13:13:18 wlan0: tx error 0x20, buf 17! +13:13:18 wlan0: tx error 0x20, buf 18! +13:13:18 wlan0: tx error 0x20, buf 19! +13:13:18 wlan0: tx error 0x20, buf 20! +13:13:18 wlan0: tx error 0x20, buf 21! +13:13:18 wlan0: tx error 0x20, buf 22! +13:13:18 wlan0: tx error 0x20, buf 23! +13:13:18 wlan0: tx error 0x20, buf 24! +13:13:18 wlan0: less than 5 minutes since last radio recalibr +13:13:25 wlan0: tx error 0x20, buf 25! +13:13:25 wlan0: tx error 0x20, buf 26! +13:13:25 wlan0: tx error 0x20, buf 27! +13:13:25 wlan0: tx error 0x20, buf 28! +13:13:25 wlan0: tx error 0x20, buf 29! +13:13:25 wlan0: tx error 0x20, buf 30! +13:13:25 wlan0: tx error 0x20, buf 31! +13:13:25 wlan0: tx error 0x20, buf 00! +13:13:25 wlan0: less than 5 minutes since last radio recalibr +13:13:25 disabling above message +13:13:32 wlan0: tx error 0x20, buf 01! +13:13:32 wlan0: tx error 0x20, buf 02! +13:13:32 wlan0: tx error 0x20, buf 03! +13:13:32 wlan0: tx error 0x20, buf 04! +13:13:32 wlan0: tx error 0x20, buf 05! +13:13:32 wlan0: tx error 0x20, buf 06! +13:13:32 wlan0: tx error 0x20, buf 07! +13:13:32 wlan0: tx error 0x20, buf 08! +13:13:41 wlan0: tx error 0x20, buf 09! +13:13:41 wlan0: tx error 0x20, buf 10! +13:13:41 wlan0: tx error 0x20, buf 11! +13:13:41 wlan0: tx error 0x20, buf 12! +13:13:41 wlan0: tx error 0x20, buf 13! +13:13:41 wlan0: tx error 0x20, buf 14! +13:13:41 wlan0: tx error 0x20, buf 15! +13:13:41 wlan0: tx error 0x20, buf 16! +13:13:51 wlan0: tx error 0x20, buf 17! +13:13:51 wlan0: tx error 0x20, buf 18! +13:13:51 wlan0: tx error 0x20, buf 19! +13:13:51 wlan0: tx error 0x20, buf 20! +13:13:51 wlan0: tx error 0x20, buf 21! +13:13:51 wlan0: tx error 0x20, buf 22! +13:13:51 wlan0: tx error 0x20, buf 23! +13:13:51 wlan0: tx error 0x20, buf 24! +13:14:02 wlan0: tx error 0x20, buf 25! +13:14:02 wlan0: tx error 0x20, buf 26! +13:14:02 wlan0: tx error 0x20, buf 27! +13:14:02 wlan0: tx error 0x20, buf 28! +13:14:02 wlan0: tx error 0x20, buf 29! +13:14:02 wlan0: tx error 0x20, buf 30! +13:14:02 wlan0: tx error 0x20, buf 31! +13:14:02 wlan0: tx error 0x20, buf 00! +13:14:13 wlan0: tx error 0x20, buf 01! +13:14:13 wlan0: tx error 0x20, buf 02! +13:14:13 wlan0: tx error 0x20, buf 03! +13:14:13 wlan0: tx error 0x20, buf 04! +13:14:13 wlan0: tx error 0x20, buf 05! +13:14:13 wlan0: tx error 0x20, buf 06! +13:14:13 wlan0: tx error 0x20, buf 07! +13:14:13 wlan0: tx error 0x20, buf 08! + + +[20051016] 0.3.13 +* Revert 20051013 fix, we have one which actually works. + Thanks Jacek Jablonski for testing! + +[20051013] +* trying to fix "yet another similar bug" +* usb fix by Carlos Martin + +[20051012] 0.3.12 +* acx_l_clean_tx_desc bug fixed - was stopping tx completely + at high load. (It seems there exists yet another similar bug!) +* "unknown IE" dump was 2 bytes too short - fixed +* DUP logging made less noisy +* another usb fix by Carlos Martin + +[20051003] +* several usb fixes by Carlos Martin - thanks! +* unknown IE logging made less noisy +* few unknown IEs added to the growing collection +* version bump to 0.3.11 + +[20050916] +* fix bogus MTU handling, add ability to change MTU +* fix WLAN_DATA_MAXLEN: 2312 -> 2304 +* version bump to 0.3.10 + +[20050915] +* by popular request default mode is 'managed' +* empty handler for EID 7 (country info) is added +* fix 'timer not started - iface is not up' +* tx[host]desc micro optimizations +* version bump to 0.3.9 + +[20050914] +* tx[host]desc ring workings brought a bit back to two-hostdesc + scheme. This is an attempt to fix weird WG311v2 bug. + I still fail to understand how same chip with same fw can + work for me but do not work for a WG311v2 owner. Mystery. +* README updated +* version bump to 0.3.8 + +[20050913] +* variable and fields with awful names renamed +* a few fields dropped (they had constant values) +* small optimization to acx_l_clean_tx_desc() +* version bump to 0.3.7 + +[20050912] +* stop using 16 byte "private area" in txdesc - fw bug makes it unreliable +* better logging of DUPs +* version bump to 0.3.6 + +[20050911] +* use alloc_netdev/free_netdev/netdev_priv +* acx_inline.h incorporated into pci.c +* helper.c + helper2.c = common.c +* marking static functions +* enable IE_DOT11_CURRENT_ANTENNA for acx111 +* version bump to 0.3.5 + +[20050910] +* minor fixes, 2.6.13-mm2 integration + [20050905] * TIWLAN_DC is dead, ui32ACX[TR]xQueueStart is dead * massive mucking with PCI and USB resulting in: diff -puN /dev/null drivers/net/wireless/tiacx/common.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/common.c 2005-10-17 13:06:00.000000000 -0700 @@ -0,0 +1,6634 @@ +/*********************************************************************** +** 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 +#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); + + +/*********************************************************************** +*/ +#if ACX_DEBUG +unsigned int acx_debug = L_ASSOC|L_INIT; +#endif +#if USE_FW_LOADER_LEGACY +static char *firmware_dir; +#endif +#if SEPARATE_DRIVER_INSTANCES +static int card; +#endif + +/* 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 +/* parameter is 'debug', corresponding var is acx_debug */ +module_param_named(debug, 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(acx_debug, "i"); +#endif +#if USE_FW_LOADER_LEGACY +MODULE_PARM(firmware_dir, "s"); +#endif + +#endif + +#if ACX_DEBUG +MODULE_PARM_DESC(debug, "Debug level mask (see L_xxx constants)"); +#endif +#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 + +/* Shoundn't be needed now, acx.firmware_dir= should work */ +#if 0 /* 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 + +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual MPL/GPL"); +#endif +/* USB had this: MODULE_AUTHOR("Martin Wawro "); */ +MODULE_AUTHOR("ACX100 Open Source Driver development team"); +MODULE_DESCRIPTION("Driver for TI ACX1xx based wireless cards (CardBus/PCI/USB)"); + + +/*********************************************************************** +*/ +/* Probably a number of acx's itermediate buffers for USB transfers, +** not to be confused with number of descriptors in tx/rx rings +** (which are not directly accessible to host in USB devices) */ +#define USB_RX_CNT 10 +#define USB_TX_CNT 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); +static 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) +{ +#ifdef SMP + if (!spin_is_locked(&priv->lock)) { + where = sanitize_str(where); + printk(KERN_EMERG "STRAY UNLOCK at %s!\n", where); + BUG(); + } +#endif + if (acx_debug & L_LOCK) { + unsigned diff; + 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) +{ + 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(); + } + if (acx_debug & L_LOCK) { + unsigned 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("%08ld %s==> %s\n", + d % 100000000, + 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("%08ld %s<== %s\n", + d % 100000000, + 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("%08ld %s<== %s: %08X\n", + d % 100000000, + 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; +} + + +/*********************************************************************** +** 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); +} + + +/*********************************************************************** +** acx_get_status_name +*/ +static 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_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; + + 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; + } + return str; +} +#endif + + +/*********************************************************************** +** acx_cmd_status_str +*/ +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"; +} + + +/*********************************************************************** +** 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]; +} + + +/*********************************************************************** +*/ +void +acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr) +{ + if (acx_debug & L_ASSOC) { + int offset = (u8*)ie_ptr - (u8*)hdr; + printk("acx: unknown EID %d in mgmt frame at offset %d. IE: ", + ie_ptr->eid, offset); + /* IE len can be bogus, IE can extend past packet end. Oh well... */ + acx_dump_bytes(ie_ptr, ie_ptr->len + 2); + if (acx_debug & L_DATA) { + 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 + + +/*********************************************************************** +*/ +int +acx_e_change_mtu(struct net_device *dev, int mtu) +{ + enum { + MIN_MTU = 256, + MAX_MTU = WLAN_DATA_MAXLEN - (ETH_HLEN) + }; + + if (mtu < MIN_MTU || mtu > MAX_MTU) + return -EINVAL; + + dev->mtu = mtu; + return 0; +} + + +/*********************************************************************** +** acx_e_get_stats, acx_e_get_wireless_stats +*/ +struct net_device_stats* +acx_e_get_stats(netdevice_t *dev) +{ + wlandevice_t *priv = netdev_priv(dev); + return &priv->stats; +} + +struct iw_statistics* +acx_e_get_wireless_stats(netdevice_t *dev) +{ + wlandevice_t *priv = netdev_priv(dev); + return &priv->wstats; +} + + +/*********************************************************************** +** 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)]; +} + + +/*********************************************************************** +** Calculate level like the feb 2003 windows driver seems to do +*/ +static 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; +} + + +/*********************************************************************** +** 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, + ACX111_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_WRITE_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, +}; + +#undef FUNC +#define FUNC "configure" +#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; + int res; + + if (type < 0x1000) + len = CtlLength[type]; + else + len = CtlLengthDot11[type - 0x1000]; + + acxlog(L_CTL, FUNC"(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); + res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIGURE, pdr, len + 4); + if (OK != res) { +#if ACX_DEBUG + printk("%s: "FUNC"(type:%s) FAILED\n", priv->netdev->name, typestr); +#else + printk("%s: "FUNC"(type:0x%X) FAILED\n", priv->netdev->name, type); +#endif + /* dump_stack() is already done in issue_cmd() */ + } + return res; +} + +#undef FUNC +#define FUNC "interrogate" +#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; + int res; + + if (type < 0x1000) + len = CtlLength[type]; + else + len = CtlLengthDot11[type-0x1000]; + acxlog(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len); + + ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type); + ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len); + res = acx_s_issue_cmd(priv, ACX1xx_CMD_INTERROGATE, pdr, len + 4); + if (OK != res) { +#if ACX_DEBUG + printk("%s: "FUNC"(type:%s) FAILED\n", priv->netdev->name, typestr); +#else + printk("%s: "FUNC"(type:0x%X) FAILED\n", priv->netdev->name, type); +#endif + /* dump_stack() is already done in issue_cmd() */ + } + return res; +} + +#if CMD_DISCOVERY +void +great_inquisitor(wlandevice_t *priv) +{ + static struct { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + /* 0x200 was too large here: */ + u8 data[0x100 - 4] ACX_PACKED; + } ie; + u16 type; + + FN_ENTER; + + /* 0..0x20, 0x1000..0x1020 */ + for (type = 0; type <= 0x1020; type++) { + if (type == 0x21) + type = 0x1000; + ie.type = cpu_to_le16(type); + ie.len = cpu_to_le16(sizeof(ie) - 4); + acx_s_issue_cmd(priv, ACX1xx_CMD_INTERROGATE, &ie, sizeof(ie)); + } + FN_EXIT0; +} +#endif + + +#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" + "form factor:\t\t\t0x%02X\n" + "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)); + + 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); + + if (IS_PCI(priv)) + p = acxpci_s_proc_diag_output(p, priv); + + 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\n" /* "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); + + 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_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; + + /* fill buf */ + length = 0; + if (IS_PCI(priv)) { + acx_sem_lock(priv); + length = acxpci_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 = 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_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) ) + ; +} + +static void +acx_s_cmd_join_bssid(wlandevice_t *priv, const u8 *bssid) +{ + acx_joinbss_t tmp; + int dtim_interval; + int i; + + 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 (IS_ACX111(priv)) { + /* 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_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 (IS_ACX100(priv)) { + acx100_s_scan_chan(priv); + } else { + acx111_s_scan_chan(priv); + } +} + + +/*********************************************************************** +** acx111 feature config +*/ +static int +acx111_s_get_feature_config(wlandevice_t *priv, + u32 *feature_options, u32 *data_flow_options) +{ + struct acx111_ie_feature_config fc; + + if (!IS_ACX111(priv)) { + return NOT_OK; + } + + memset(&fc, 0, sizeof(fc)); + + if (OK != acx_s_interrogate(priv, &fc, ACX1xx_IE_FEATURE_CONFIG)) { + 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; +} + +static 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 acx111_ie_feature_config fc; + + if (!IS_ACX111(priv)) { + 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)) { + return NOT_OK; + } + + return OK; +} + +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); +} + + +/*********************************************************************** +** acx100_s_init_memory_pools +*/ +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; + int TotalMemoryBlocks; + int RxBlockNum; + int TotalRxBlockSize; + int TxBlockNum; + int 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)) { + 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); + + /* MemoryConfigOption.DMA_config bitmask: + // access to ACX memory is to be done: + 0x00080000 // using PCI conf space?! + 0x00040000 // using IO instructions? + 0x00000000 // using memory access instructions + 0x00020000 // use local memory block linked list (else what?) + 0x00010000 // use host indirect descriptors (else host must access ACX memory?) + */ + if (IS_PCI(priv)) { + MemoryConfigOption.DMA_config = cpu_to_le32(0x30000); + /* Declare start of the Rx host pool */ + MemoryConfigOption.pRxHostDesc = cpu2acx(priv->rxhostdesc_startphy); + acxlog(L_DEBUG, "pRxHostDesc 0x%08X, rxhostdesc_startphy 0x%lX\n", + acx2cpu(MemoryConfigOption.pRxHostDesc), + (long)priv->rxhostdesc_startphy); + } else { + MemoryConfigOption.DMA_config = cpu_to_le32(0x20000); + } + + /* 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)) { + goto bad; + } + + /* and tell the device to kick it into gear */ + if (OK != acx_s_issue_cmd(priv, ACX100_CMD_INIT_MEMORY, NULL, 0)) { + 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 +*/ +static int +acx100_s_create_dma_regions(wlandevice_t *priv) +{ + acx100_ie_queueconfig_t queueconf; + acx_ie_memmap_t memmap; + int res = NOT_OK; + u32 tx_queue_start, rx_queue_start; + + FN_ENTER; + + /* read out the acx100 physical start address for the queues */ + if (OK != acx_s_interrogate(priv, &memmap, ACX1xx_IE_MEMORY_MAP)) { + goto fail; + } + + tx_queue_start = le32_to_cpu(memmap.QueueStart); + rx_queue_start = tx_queue_start + TX_CNT * sizeof(txdesc_t); + + acxlog(L_DEBUG, "initializing Queue Indicator\n"); + + memset(&queueconf, 0, sizeof(queueconf)); + + /* Not needed for PCI, so we can avoid setting them altogether */ + if (IS_USB(priv)) { + queueconf.NumTxDesc = USB_TX_CNT; + queueconf.NumRxDesc = USB_RX_CNT; + } + + /* calculate size of queues */ + queueconf.AreaSize = cpu_to_le32( + TX_CNT * sizeof(txdesc_t) + + RX_CNT * sizeof(rxdesc_t) + 8 + ); + queueconf.NumTxQueues = 1; /* number of tx queues */ + /* sets the beginning of the tx descriptor queue */ + queueconf.TxQueueStart = memmap.QueueStart; + /* done by memset: queueconf.TxQueuePri = 0; */ + queueconf.RxQueueStart = cpu_to_le32(rx_queue_start); + queueconf.QueueOptions = 1; /* auto reset descriptor */ + /* sets the end of the rx descriptor queue */ + queueconf.QueueEnd = cpu_to_le32( + rx_queue_start + RX_CNT * sizeof(rxdesc_t) + ); + /* sets the beginning of the next queue */ + queueconf.HostQueueEnd = cpu_to_le32(le32_to_cpu(queueconf.QueueEnd) + 8); + if (OK != acx_s_configure(priv, &queueconf, ACX1xx_IE_QUEUE_CONFIG)) { + goto fail; + } + + if (IS_PCI(priv)) { + /* sets the beginning of the rx descriptor queue, after the tx descrs */ + if (OK != acxpci_s_create_hostdesc_queues(priv)) + goto fail; + acxpci_create_desc_queues(priv, tx_queue_start, rx_queue_start); + } + + if (OK != acx_s_interrogate(priv, &memmap, ACX1xx_IE_MEMORY_MAP)) { + goto fail; + } + +/* [20050901] seems to be bogus. remove if no one complains */ +#if 0 /* #ifdef ACX_USB */ + if (OK != acx_s_configure(priv, &memmap, ACX1xx_IE_MEMORY_MAP)) { + goto fail; + } +#endif + + memmap.PoolStart = cpu_to_le32( + (le32_to_cpu(memmap.QueueEnd) + 4 + 0x1f) & ~0x1f + ); + + if (OK != acx_s_configure(priv, &memmap, ACX1xx_IE_MEMORY_MAP)) { + goto fail; + } + + if (OK != acx100_s_init_memory_pools(priv, &memmap)) { + goto fail; + } + + res = OK; + goto end; + +fail: + acx_s_msleep(1000); /* ? */ + if (IS_PCI(priv)) + acxpci_free_desc_queues(priv); +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) + +static int +acx111_s_create_dma_regions(wlandevice_t *priv) +{ + struct acx111_ie_memoryconfig memconf; + struct acx111_ie_queueconfig queueconf; + u32 tx_queue_start, rx_queue_start; + + FN_ENTER; + + /* Calculate memory positions and queue sizes */ + + /* Set up our host descriptor pool + data pool */ + if (IS_PCI(priv)) { + if (OK != acxpci_s_create_hostdesc_queues(priv)) + 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 = RX_CNT; + memconf.rx_queue1_type = 7; /* must be set to 7 */ + /* done by memset: memconf.rx_queue1_prio = 0; low prio */ + if (IS_PCI(priv)) { + memconf.rx_queue1_host_rx_start = cpu2acx(priv->rxhostdesc_startphy); + } + /* Tx descriptor queue config */ + memconf.tx_queue1_count_descs = TX_CNT; + /* 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 + ** which is 4 bytes larger. what a mess. TODO: clean it up) */ + if (OK != acx_s_configure(priv, &memconf, ACX1xx_IE_QUEUE_CONFIG)) { + goto fail; + } + + acx_s_interrogate(priv, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS); + + tx_queue_start = le32_to_cpu(queueconf.tx1_queue_address); + rx_queue_start = le32_to_cpu(queueconf.rx1_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" + "tx1_queue address: %X\n" + "rx1_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), + tx_queue_start, + rx_queue_start); + + if (IS_PCI(priv)) + acxpci_create_desc_queues(priv, tx_queue_start, rx_queue_start); + + FN_EXIT1(OK); + return OK; +fail: + if (IS_PCI(priv)) + acxpci_free_desc_queues(priv); + + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*********************************************************************** +** 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 (IS_ACX100(priv)) + priv->get_mask |= GETSET_CCA|GETSET_ED_THRESH; + + acx_s_update_card_settings(priv, 0, 0); + + acx_lock(priv, flags); + + /* set our global interrupt mask */ + if (IS_PCI(priv)) + acxpci_set_interrupt_mask(priv); + + 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); + + if (IS_PCI(priv)) { + if (IS_ACX111(priv)) { + /* Hope this is correct, only tested with domain 0x30 */ + acxpci_read_eeprom_byte(priv, 0x16F, &priv->reg_dom_id); + } else if (priv->eeprom_version < 5) { + acxpci_read_eeprom_byte(priv, 0x16F, &priv->reg_dom_id); + } else { + acxpci_read_eeprom_byte(priv, 0x171, &priv->reg_dom_id); + } + } + + 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 (IS_ACX111(priv)) { + 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_2_STA; + 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 (IS_ACX111(priv)) { + 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 (IS_ACX111(priv)) { + /* 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 (IS_ACX111(priv)) { + /* 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; +#ifdef 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; +} + + +/*********************************************************************** +** 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 +acx111_s_set_tx_level(wlandevice_t *priv, u8 level_dbm) +{ + struct acx111_ie_tx_level 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); + + return acx_s_configure(priv, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); +} + +static int +acx_s_set_tx_level(wlandevice_t *priv, u8 level_dbm) +{ + if (IS_ACX111(priv)) { + return acx111_s_set_tx_level(priv, level_dbm); + } + if (IS_PCI(priv)) { + return acx100pci_s_set_tx_level(priv, level_dbm); + } + return OK; +} + + +/*********************************************************************** +*/ +#ifdef UNUSED +/* Returns the current tx level (ACX111) */ +static u8 +acx111_s_get_tx_level(wlandevice_t *priv) +{ + struct acx111_ie_tx_level tx_level; + + tx_level.level = 0; + acx_s_interrogate(priv, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); + return tx_level.level; +} +#endif + + +/*********************************************************************** +** acx_s_init_mac +*/ +int +acx_s_init_mac(netdevice_t *dev) +{ + wlandevice_t *priv = netdev_priv(dev); + int result = NOT_OK; + + FN_ENTER; + + if (IS_PCI(priv)) { + priv->memblocksize = 256; /* 256 is default */ + acxpci_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 */ + acxpci_s_upload_radio(priv); + } else { + priv->memblocksize = 128; + } + + if (IS_ACX111(priv)) { + /* 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; + } +#ifdef 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_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. */ + 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("%s: monitor mode panic: oversized frame!\n", + priv->netdev->name); + 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; +} + + +/*********************************************************************** +** acx_l_rx_ieee802_11_frame +** +** Called from IRQ context only +*/ + +/* All these contortions are for saner dup logging +** +** We want: (a) to know about excessive dups +** (b) to not spam kernel log about occasional dups +** +** 1/64 threshold was chosen by running "ping -A" +** It gave "rx: 59 DUPs in 2878 packets" only with 4 parallel +** "ping -A" streams running. */ +/* 2005-10-11: bumped up to 1/8 +** subtract a $smallint from dup_count in order to +** avoid "2 DUPs in 19 packets" messages */ +static inline int +acx_l_handle_dup(wlandevice_t *priv, u16 seq) +{ + if (priv->dup_count) { + priv->nondup_count++; + if (time_after(jiffies, priv->dup_msg_expiry)) { + /* Log only if more than 1 dup in 64 packets */ + if (priv->nondup_count/8 < priv->dup_count-5) { + printk(KERN_INFO "%s: rx: %d DUPs in " + "%d packets received in 10 secs\n", + priv->netdev->name, + priv->dup_count, + priv->nondup_count); + } + priv->dup_count = 0; + priv->nondup_count = 0; + } + } + if (unlikely(seq == priv->last_seq_ctrl)) { + if (!priv->dup_count++) + priv->dup_msg_expiry = jiffies + 10*HZ; + priv->stats.rx_errors++; + return 1; /* a dup */ + } + priv->last_seq_ctrl = seq; + return 0; +} + +static 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: + if (acx_l_handle_dup(priv, hdr->seq)) + break; /* a dup, simply discard it */ + + /* 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_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. */ + +#ifdef 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 */ +#ifdef FROM_SCAN_SOURCE_ONLY + } +#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 = 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; + } + + 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; + } + + 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(priv, tx); + if (!txbuf) { + /* Card was removed */ + txresult = NOT_OK; + goto end; + } + len = acx_ether_to_txbuf(priv, txbuf, skb); + if (len < 0) { + /* Error in packet conversion */ + txresult = NOT_OK; + goto end; + } + acx_l_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; +} + + +/*********************************************************************** +** 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; +} + + +/*---------------------------------------------------------------- +* acx_l_sta_list_init +*----------------------------------------------------------------*/ +static 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 +** +** This function does not need locking UNLESS you call it +** as acx_set_status(ACX_STATUS_4_ASSOCIATED), bacause this can +** wake queue. This can race with stop_queue elsewhere. +** See acx_stop_queue comment. */ +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; + /* 1.0 s initial scan time */ + 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_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_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_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, int timeout_us) +{ + FN_ENTER; + + acxlog(L_DEBUG|L_IRQ, "%s(%u ms)\n", __func__, timeout_us/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_us * HZ / 1000000)); + } +end: + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* 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->st