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->stepup_count = 0; + clt->ignore_count = 16; + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(priv, tx); + if (!head) + goto bad; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_ASSOCRESPi; + head->dur = req->hdr->dur; + MAC_COPY(head->da, da); + /* MAC_COPY(head->sa, sa); */ + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, bssid); + head->seq = req->hdr->seq; + + body->cap_info = host2ieee16(priv->capabilities); + body->status = host2ieee16(0); + body->aid = host2ieee16(clt->aid); + p = wlan_fill_ie_rates((u8*)&body->rates, priv->rate_supported_len, + priv->rate_supported); + p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, + priv->rate_supported); + + acx_l_tx_data(priv, tx, p - (u8*)head); +ok: + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_reassocresp + +You may be wondering, just like me, what a hell is ReAuth. +In practice it was seen sent by STA when STA feels like losing connection. + +[802.11] + +5.4.2.3 Reassociation + +Association is sufficient for no-transition message delivery between +IEEE 802.11 stations. Additional functionality is needed to support +BSS-transition mobility. The additional required functionality +is provided by the reassociation service. Reassociation is a DSS. +The reassociation service is invoked to 'move' a current association +from one AP to another. This keeps the DS informed of the current +mapping between AP and STA as the station moves from BSS to BSS within +an ESS. Reassociation also enables changing association attributes +of an established association while the STA remains associated with +the same AP. Reassociation is always initiated by the mobile STA. + +5.4.3.1 Authentication +... +A STA may be authenticated with many other STAs at any given instant. + +5.4.3.1.1 Preauthentication + +Because the authentication process could be time-consuming (depending +on the authentication protocol in use), the authentication service can +be invoked independently of the association service. Preauthentication +is typically done by a STA while it is already associated with an AP +(with which it previously authenticated). IEEE 802.11 does not require +that STAs preauthenticate with APs. However, authentication is required +before an association can be established. If the authentication is left +until reassociation time, this may impact the speed with which a STA can +reassociate between APs, limiting BSS-transition mobility performance. +The use of preauthentication takes the authentication service overhead +out of the time-critical reassociation process. + +5.7.3 Reassociation + +For a STA to reassociate, the reassociation service causes the following +message to occur: + + Reassociation request + +* Message type: Management +* Message subtype: Reassociation request +* Information items: + - IEEE address of the STA + - IEEE address of the AP with which the STA will reassociate + - IEEE address of the AP with which the STA is currently associated + - ESSID +* Direction of message: From STA to 'new' AP + +The address of the current AP is included for efficiency. The inclusion +of the current AP address facilitates MAC reassociation to be independent +of the DS implementation. + + Reassociation response +* Message type: Management +* Message subtype: Reassociation response +* Information items: + - Result of the requested reassociation. (success/failure) + - If the reassociation is successful, the response shall include the AID. +* Direction of message: From AP to STA + +7.2.3.6 Reassociation Request frame format + +The frame body of a management frame of subtype Reassociation Request +contains the information shown in Table 9. + +Table 9 Reassociation Request frame body +Order Information +1 Capability information +2 Listen interval +3 Current AP address +4 SSID +5 Supported rates + +7.2.3.7 Reassociation Response frame format + +The frame body of a management frame of subtype Reassociation Response +contains the information shown in Table 10. + +Table 10 Reassociation Response frame body +Order Information +1 Capability information +2 Status code +3 Association ID (AID) +4 Supported rates + +*----------------------------------------------------------------*/ +static int +acx_l_transmit_reassocresp(wlandevice_t *priv, const wlan_fr_reassocreq_t *req) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct reassocresp_frame_body *body; + u8 *p; + const u8 *da; + /* const u8 *sa; */ + const u8 *bssid; + client_t *clt; + + FN_ENTER; + + /* sa = req->hdr->a1; */ + da = req->hdr->a2; + bssid = req->hdr->a3; + + /* Must be already authenticated, so it must be in the list */ + clt = acx_l_sta_list_get(priv, da); + if (!clt) + goto ok; + + /* Assoc without auth is a big no-no */ + /* Already assoc'ed STAs sending ReAssoc req are ok per 802.11 */ + if (clt->used != CLIENT_AUTHENTICATED_2 + && clt->used != CLIENT_ASSOCIATED_3) { + acx_l_transmit_deauthen(priv, da, WLAN_MGMT_REASON_CLASS2_NONAUTH); + goto bad; + } + + clt->used = CLIENT_ASSOCIATED_3; + if (clt->aid == 0) { + clt->aid = ++priv->aid; + } + if (req->cap_info) + clt->cap_info = ieee2host16(*(req->cap_info)); + /* We cheat here a bit. We don't really care which rates are flagged + ** as basic by the client, so we stuff them in single ratemask */ + clt->rate_cap = 0; + if (req->supp_rates) + add_bits_to_ratemasks(req->supp_rates->rates, + req->supp_rates->len, &clt->rate_cap, &clt->rate_cap); + if (req->ext_rates) + add_bits_to_ratemasks(req->ext_rates->rates, + req->ext_rates->len, &clt->rate_cap, &clt->rate_cap); + /* We can check that client supports all basic rates, + ** and deny assoc if not. But let's be liberal, right? ;) */ + clt->rate_cfg = clt->rate_cap & priv->rate_oper; + if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(priv->rate_oper); + clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); + clt->fallback_count = clt->stepup_count = 0; + clt->ignore_count = 16; + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto ok; + head = acx_l_get_txbuf(priv, tx); + if (!head) + goto ok; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_REASSOCRESPi; + head->dur = req->hdr->dur; + MAC_COPY(head->da, da); + /* MAC_COPY(head->sa, sa); */ + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, bssid); + head->seq = req->hdr->seq; + + /* IEs: 1. caps */ + body->cap_info = host2ieee16(priv->capabilities); + /* 2. status code */ + body->status = host2ieee16(0); + /* 3. AID */ + body->aid = host2ieee16(clt->aid); + /* 4. supp rates */ + p = wlan_fill_ie_rates((u8*)&body->rates, priv->rate_supported_len, + priv->rate_supported); + /* 5. ext supp rates */ + p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, + priv->rate_supported); + + acx_l_tx_data(priv, tx, p - (u8*)head); +ok: + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_process_disassoc_from_sta +*----------------------------------------------------------------*/ +static void +acx_l_process_disassoc_from_sta(wlandevice_t *priv, const wlan_fr_disassoc_t *req) +{ + const u8 *ta; + client_t *clt; + + FN_ENTER; + + ta = req->hdr->a2; + clt = acx_l_sta_list_get(priv, ta); + if (!clt) + goto end; + + if (clt->used != CLIENT_ASSOCIATED_3 + && clt->used != CLIENT_AUTHENTICATED_2) { + /* it's disassociating, but it's + ** not even authenticated! Let it know that */ + acxlog_mac(L_ASSOC|L_XFER, "peer ", ta, "has sent disassoc " + "req but it is not even auth'ed! sending deauth\n"); + acx_l_transmit_deauthen(priv, ta, + WLAN_MGMT_REASON_CLASS2_NONAUTH); + clt->used = CLIENT_EXIST_1; + } else { + /* mark it as auth'ed only */ + clt->used = CLIENT_AUTHENTICATED_2; + } +end: + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_process_deauthen_from_sta +*----------------------------------------------------------------*/ +static void +acx_l_process_deauth_from_sta(wlandevice_t *priv, const wlan_fr_deauthen_t *req) +{ + const wlan_hdr_t *hdr; + client_t *client; + + FN_ENTER; + + hdr = req->hdr; + + if (acx_debug & L_ASSOC) { + acx_print_mac("DEAUTHEN priv->addr=", priv->dev_addr, " "); + acx_print_mac("a1", hdr->a1, " "); + acx_print_mac("a2", hdr->a2, " "); + acx_print_mac("a3", hdr->a3, " "); + acx_print_mac("priv->bssid", priv->bssid, "\n"); + } + + if (!mac_is_equal(priv->dev_addr, hdr->a1)) { + goto end; + } + + acxlog_mac(L_DEBUG, "STA ", hdr->a2, " sent us deauthen packet\n"); + + client = acx_l_sta_list_get(priv, hdr->a2); + if (!client) { + goto end; + } + client->used = CLIENT_EXIST_1; +end: + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_process_disassoc_from_ap +*----------------------------------------------------------------*/ +static void +acx_l_process_disassoc_from_ap(wlandevice_t *priv, const wlan_fr_disassoc_t *req) +{ + FN_ENTER; + + if (!priv->ap_client) { + /* Hrm, we aren't assoc'ed yet anyhow... */ + goto end; + } + if (mac_is_equal(priv->dev_addr, req->hdr->a1)) { + acx_l_transmit_deauthen(priv, priv->bssid, + WLAN_MGMT_REASON_DEAUTH_LEAVING); + /* Start scan anew */ + SET_BIT(priv->set_mask, GETSET_RESCAN); + acx_schedule_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG); + } +end: + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_process_deauth_from_ap +*----------------------------------------------------------------*/ +static void +acx_l_process_deauth_from_ap(wlandevice_t *priv, const wlan_fr_deauthen_t *req) +{ + FN_ENTER; + + if (!priv->ap_client) { + /* Hrm, we aren't assoc'ed yet anyhow... */ + goto end; + } + /* Chk: is ta is verified to be from our AP? */ + if (mac_is_equal(priv->dev_addr, req->hdr->a1)) { + acxlog(L_DEBUG, "AP sent us deauth packet\n"); + /* not needed: acx_set_status(priv, ACX_STATUS_1_SCANNING) */ + SET_BIT(priv->set_mask, GETSET_RESCAN); + acx_schedule_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG); + } +end: + FN_EXIT0; +} + + +/*------------------------------------------------------------------------------ + * acx_l_rx + * + * The end of the Rx path. Pulls data from a rxhostdesc into a socket + * buffer and feeds it to the network stack via netif_rx(). + * + * Arguments: + * rxdesc: the rxhostdesc to pull the data from + * priv: the acx100 private struct of the interface + *----------------------------------------------------------------------------*/ +static void +acx_l_rx(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + FN_ENTER; + if (likely(priv->dev_state_mask & ACX_STATE_IFACE_UP)) { + struct sk_buff *skb; + skb = acx_rxbuf_to_ether(priv, rxbuf); + if (likely(skb)) { + netif_rx(skb); + priv->netdev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes += skb->len; + } + } + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_process_data_frame_master +*----------------------------------------------------------------*/ +static int +acx_l_process_data_frame_master(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + struct wlan_hdr *hdr; + struct tx *tx; + void *txbuf; + int len; + int result = NOT_OK; + + FN_ENTER; + + hdr = acx_get_wlan_hdr(priv, rxbuf); + + switch (WF_FC_FROMTODSi & hdr->fc) { + case 0: + case WF_FC_FROMDSi: + acxlog(L_DEBUG, "ap->sta or adhoc->adhoc data frame ignored\n"); + goto done; + case WF_FC_TODSi: + break; + default: /* WF_FC_FROMTODSi */ + acxlog(L_DEBUG, "wds data frame ignored (todo)\n"); + goto done; + } + + /* check if it is our BSSID, if not, leave */ + if (!mac_is_equal(priv->bssid, hdr->a1)) { + goto done; + } + + if (mac_is_equal(priv->dev_addr, hdr->a3)) { + /* this one is for us */ + acx_l_rx(priv, rxbuf); + } else { + if (mac_is_bcast(hdr->a3)) { + /* this one is bcast, rx it too */ + acx_l_rx(priv, rxbuf); + } + tx = acx_l_alloc_tx(priv); + if (!tx) { + goto fail; + } + /* repackage, tx, and hope it someday reaches its destination */ + /* order is important, we do it in-place */ + MAC_COPY(hdr->a1, hdr->a3); + MAC_COPY(hdr->a3, hdr->a2); + MAC_COPY(hdr->a2, priv->bssid); + /* To_DS = 0, From_DS = 1 */ + hdr->fc = WF_FC_FROMDSi + WF_FTYPE_DATAi; + + len = RXBUF_BYTES_RCVD(rxbuf); + txbuf = acx_l_get_txbuf(priv, tx); + if (txbuf) { + memcpy(txbuf, &rxbuf->hdr_a3, len); + acx_l_tx_data(priv, tx, len); + } + } +done: + result = OK; +fail: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_l_process_data_frame_client +*----------------------------------------------------------------*/ +static int +acx_l_process_data_frame_client(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + const u8 *da, *bssid; + const wlan_hdr_t *hdr; + netdevice_t *dev = priv->netdev; + int result = NOT_OK; + + FN_ENTER; + + if (ACX_STATUS_4_ASSOCIATED != priv->status) + goto drop; + + hdr = acx_get_wlan_hdr(priv, rxbuf); + + switch (WF_FC_FROMTODSi & hdr->fc) { + case 0: + if (priv->mode != ACX_MODE_0_ADHOC) { + acxlog(L_DEBUG, "adhoc->adhoc data frame ignored\n"); + goto drop; + } + bssid = hdr->a3; + break; + case WF_FC_FROMDSi: + if (priv->mode != ACX_MODE_2_STA) { + acxlog(L_DEBUG, "ap->sta data frame ignored\n"); + goto drop; + } + bssid = hdr->a2; + break; + case WF_FC_TODSi: + acxlog(L_DEBUG, "sta->ap data frame ignored\n"); + goto drop; + default: /* WF_FC_FROMTODSi: wds->wds */ + acxlog(L_DEBUG, "wds data frame ignored (todo)\n"); + goto drop; + } + + da = hdr->a1; + + if (unlikely(acx_debug & L_DEBUG)) { + acx_print_mac("rx: da=", da, ""); + acx_print_mac(" bssid=", bssid, ""); + acx_print_mac(" priv->bssid=", priv->bssid, ""); + acx_print_mac(" priv->addr=", priv->dev_addr, "\n"); + } + + /* promiscuous mode --> receive all packets */ + if (unlikely(dev->flags & IFF_PROMISC)) + goto process; + + /* FIRST, check if it is our BSSID */ + if (!mac_is_equal(priv->bssid, bssid)) { + /* is not our BSSID, so bail out */ + goto drop; + } + + /* then, check if it is our address */ + if (mac_is_equal(priv->dev_addr, da)) { + goto process; + } + + /* then, check if it is broadcast */ + if (mac_is_bcast(da)) { + goto process; + } + + if (mac_is_mcast(da)) { + /* unconditionally receive all multicasts */ + if (dev->flags & IFF_ALLMULTI) + goto process; + + /* FIXME: check against the list of + * multicast addresses that are configured + * for the interface (ifconfig) */ + acxlog(L_XFER, "FIXME: multicast packet, need to check " + "against a list of multicast addresses " + "(to be created!); accepting packet for now\n"); + /* for now, just accept it here */ + goto process; + } + + acxlog(L_DEBUG, "rx: foreign packet, dropping\n"); + goto drop; +process: + /* receive packet */ + acx_l_rx(priv, rxbuf); + + result = OK; +drop: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_l_process_mgmt_frame +* +* Theory of operation: mgmt packet gets parsed (to make it easy +* to access variable-sized IEs), results stored in 'parsed'. +* Then we react to the packet. +* NB: wlan_mgmt_decode_XXX are dev-independent (shoudnt have been named acx_XXX) +*----------------------------------------------------------------*/ +typedef union parsed_mgmt_req { + wlan_fr_mgmt_t mgmt; + wlan_fr_assocreq_t assocreq; + wlan_fr_reassocreq_t reassocreq; + wlan_fr_assocresp_t assocresp; + wlan_fr_reassocresp_t reassocresp; + wlan_fr_beacon_t beacon; + wlan_fr_disassoc_t disassoc; + wlan_fr_authen_t authen; + wlan_fr_deauthen_t deauthen; + wlan_fr_proberesp_t proberesp; +} parsed_mgmt_req_t; + +void BUG_excessive_stack_usage(void); + +static int +acx_l_process_mgmt_frame(wlandevice_t *priv, rxbuffer_t *rxbuf) +{ + parsed_mgmt_req_t parsed; /* takes ~100 bytes of stack */ + wlan_hdr_t *hdr; + int adhoc, sta_scan, sta, ap; + int len; + + if (sizeof(parsed) > 256) + BUG_excessive_stack_usage(); + + FN_ENTER; + + hdr = acx_get_wlan_hdr(priv, rxbuf); + + /* Management frames never have these set */ + if (WF_FC_FROMTODSi & hdr->fc) { + FN_EXIT1(NOT_OK); + return NOT_OK; + } + + len = RXBUF_BYTES_RCVD(rxbuf); + if (WF_FC_ISWEPi & hdr->fc) + len -= 0x10; + + adhoc = (priv->mode == ACX_MODE_0_ADHOC); + sta_scan = ((priv->mode == ACX_MODE_2_STA) + && (priv->status != ACX_STATUS_4_ASSOCIATED)); + sta = ((priv->mode == ACX_MODE_2_STA) + && (priv->status == ACX_STATUS_4_ASSOCIATED)); + ap = (priv->mode == ACX_MODE_3_AP); + + switch (WF_FC_FSTYPEi & hdr->fc) { + /* beacons first, for speed */ + case WF_FSTYPE_BEACONi: + memset(&parsed.beacon, 0, sizeof(parsed.beacon)); + parsed.beacon.hdr = hdr; + parsed.beacon.len = len; + if (acx_debug & L_DATA) { + printk("beacon len:%d fc:%04X dur:%04X seq:%04X", + len, hdr->fc, hdr->dur, hdr->seq); + acx_print_mac(" a1:", hdr->a1, ""); + acx_print_mac(" a2:", hdr->a2, ""); + acx_print_mac(" a3:", hdr->a3, "\n"); + } + wlan_mgmt_decode_beacon(&parsed.beacon); + /* beacon and probe response are very similar, so... */ + acx_l_process_probe_response(priv, &parsed.beacon, rxbuf); + break; + case WF_FSTYPE_ASSOCREQi: + if (!ap) + break; + memset(&parsed.assocreq, 0, sizeof(parsed.assocreq)); + parsed.assocreq.hdr = hdr; + parsed.assocreq.len = len; + wlan_mgmt_decode_assocreq(&parsed.assocreq); + if (mac_is_equal(hdr->a1, priv->bssid) + && mac_is_equal(hdr->a3, priv->bssid)) { + acx_l_transmit_assocresp(priv, &parsed.assocreq); + } + break; + case WF_FSTYPE_REASSOCREQi: + if (!ap) + break; + memset(&parsed.assocreq, 0, sizeof(parsed.assocreq)); + parsed.assocreq.hdr = hdr; + parsed.assocreq.len = len; + wlan_mgmt_decode_assocreq(&parsed.assocreq); + /* reassocreq and assocreq are equivalent */ + acx_l_transmit_reassocresp(priv, &parsed.reassocreq); + break; + case WF_FSTYPE_ASSOCRESPi: + if (!sta_scan) + break; + memset(&parsed.assocresp, 0, sizeof(parsed.assocresp)); + parsed.assocresp.hdr = hdr; + parsed.assocresp.len = len; + wlan_mgmt_decode_assocresp(&parsed.assocresp); + acx_l_process_assocresp(priv, &parsed.assocresp); + break; + case WF_FSTYPE_REASSOCRESPi: + if (!sta_scan) + break; + memset(&parsed.assocresp, 0, sizeof(parsed.assocresp)); + parsed.assocresp.hdr = hdr; + parsed.assocresp.len = len; + wlan_mgmt_decode_assocresp(&parsed.assocresp); + acx_l_process_reassocresp(priv, &parsed.reassocresp); + break; + case WF_FSTYPE_PROBEREQi: + if (ap || adhoc) { + /* FIXME: since we're supposed to be an AP, + ** we need to return a Probe Response packet. + ** Currently firmware is doing it for us, + ** but firmware is buggy! See comment elsewhere --vda */ + } + break; + case WF_FSTYPE_PROBERESPi: + memset(&parsed.proberesp, 0, sizeof(parsed.proberesp)); + parsed.proberesp.hdr = hdr; + parsed.proberesp.len = len; + wlan_mgmt_decode_proberesp(&parsed.proberesp); + acx_l_process_probe_response(priv, &parsed.proberesp, rxbuf); + break; + case 6: + case 7: + /* exit */ + break; + case WF_FSTYPE_ATIMi: + /* exit */ + break; + case WF_FSTYPE_DISASSOCi: + if (!sta && !ap) + break; + memset(&parsed.disassoc, 0, sizeof(parsed.disassoc)); + parsed.disassoc.hdr = hdr; + parsed.disassoc.len = len; + wlan_mgmt_decode_disassoc(&parsed.disassoc); + if (sta) + acx_l_process_disassoc_from_ap(priv, &parsed.disassoc); + else + acx_l_process_disassoc_from_sta(priv, &parsed.disassoc); + break; + case WF_FSTYPE_AUTHENi: + if (!sta_scan && !ap) + break; + memset(&parsed.authen, 0, sizeof(parsed.authen)); + parsed.authen.hdr = hdr; + parsed.authen.len = len; + wlan_mgmt_decode_authen(&parsed.authen); + acx_l_process_authen(priv, &parsed.authen); + break; + case WF_FSTYPE_DEAUTHENi: + if (!sta && !ap) + break; + memset(&parsed.deauthen, 0, sizeof(parsed.deauthen)); + parsed.deauthen.hdr = hdr; + parsed.deauthen.len = len; + wlan_mgmt_decode_deauthen(&parsed.deauthen); + if (sta) + acx_l_process_deauth_from_ap(priv, &parsed.deauthen); + else + acx_l_process_deauth_from_sta(priv, &parsed.deauthen); + break; + } + + FN_EXIT1(OK); + return OK; +} + + +#ifdef UNUSED +/*---------------------------------------------------------------- +* acx_process_class_frame +* +* Called from IRQ context only +*----------------------------------------------------------------*/ +static int +acx_process_class_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala) +{ + return OK; +} +#endif + + +/*---------------------------------------------------------------- +* acx_l_process_NULL_frame +*----------------------------------------------------------------*/ +#ifdef BOGUS_ITS_NOT_A_NULL_FRAME_HANDLER_AT_ALL +static int +acx_l_process_NULL_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala) +{ + const signed char *esi; + const u8 *ebx; + const wlan_hdr_t *hdr; + const client_t *client; + int result = NOT_OK; + + hdr = acx_get_wlan_hdr(priv, rxbuf); + + switch (WF_FC_FROMTODSi & hdr->fc) { + case 0: + esi = hdr->a1; + ebx = hdr->a2; + break; + case WF_FC_FROMDSi: + esi = hdr->a1; + ebx = hdr->a3; + break; + case WF_FC_TODSi: + esi = hdr->a1; + ebx = hdr->a2; + break; + default: /* WF_FC_FROMTODSi */ + esi = hdr->a1; /* added by me! --vda */ + ebx = hdr->a2; + } + + if (esi[0x0] < 0) { + result = OK; + goto done; + } + + client = acx_l_sta_list_get(priv, ebx); + if (client) + result = NOT_OK; + else { +#ifdef IS_IT_BROKEN + acxlog(L_DEBUG|L_XFER, "\n"); + acx_l_transmit_deauthen(priv, ebx, + WLAN_MGMT_REASON_CLASS2_NONAUTH); +#else + acxlog(L_DEBUG, "received NULL frame from unknown client! " + "We really shouldn't send deauthen here, right?\n"); +#endif + result = OK; + } +done: + return result; +} +#endif + + +/*---------------------------------------------------------------- +* acx_l_process_probe_response +*----------------------------------------------------------------*/ +static int +acx_l_process_probe_response(wlandevice_t *priv, wlan_fr_proberesp_t *req, + const rxbuffer_t *rxbuf) +{ + struct client *bss; + wlan_hdr_t *hdr; + + FN_ENTER; + + hdr = req->hdr; + + if (mac_is_equal(hdr->a3, priv->dev_addr)) { + acxlog(L_ASSOC, "huh, scan found our own MAC!?\n"); + goto ok; /* just skip this one silently */ + } + + bss = acx_l_sta_list_get_or_add(priv, hdr->a2); + + /* NB: be careful modifying bss data! It may be one + ** of already known clients (like our AP is we are a STA) + ** Thus do not blindly modify e.g. current ratemask! */ + + if (STA_LIST_ADD_CAN_FAIL && !bss) { + /* uh oh, we found more sites/stations than we can handle with + * our current setup: pull the emergency brake and stop scanning! */ + acx_schedule_task(priv, ACX_AFTER_IRQ_CMD_STOP_SCAN); + /* TODO: a nice comment what below call achieves --vda */ + acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH); + goto ok; + } + /* NB: get_or_add already filled bss->address = hdr->a2 */ + MAC_COPY(bss->bssid, hdr->a3); + + /* copy the ESSID element */ + if (req->ssid && req->ssid->len <= IW_ESSID_MAX_SIZE) { + bss->essid_len = req->ssid->len; + memcpy(bss->essid, req->ssid->ssid, req->ssid->len); + bss->essid[req->ssid->len] = '\0'; + } else { + /* Either no ESSID IE or oversized one */ + printk("%s: received packet has bogus ESSID\n", + priv->netdev->name); + } + + if (req->ds_parms) + bss->channel = req->ds_parms->curr_ch; + if (req->cap_info) + bss->cap_info = ieee2host16(*req->cap_info); + + bss->sir = acx_signal_to_winlevel(rxbuf->phy_level); + bss->snr = acx_signal_to_winlevel(rxbuf->phy_snr); + + bss->rate_cap = 0; /* operational mask */ + bss->rate_bas = 0; /* basic mask */ + if (req->supp_rates) + add_bits_to_ratemasks(req->supp_rates->rates, + req->supp_rates->len, &bss->rate_bas, &bss->rate_cap); + if (req->ext_rates) + add_bits_to_ratemasks(req->ext_rates->rates, + req->ext_rates->len, &bss->rate_bas, &bss->rate_cap); + /* Fix up any possible bogosity - code elsewhere + * is not expecting empty masks */ + if (!bss->rate_cap) + bss->rate_cap = priv->rate_basic; + if (!bss->rate_bas) + bss->rate_bas = 1 << lowest_bit(bss->rate_cap); + if (!bss->rate_cur) + bss->rate_cur = 1 << lowest_bit(bss->rate_bas); + + /* People moan about this being too noisy at L_ASSOC */ + acxlog(L_DEBUG, + "found %s: ESSID='%s' ch=%d " + "BSSID="MACSTR" caps=0x%04X SIR=%d SNR=%d\n", + (bss->cap_info & WF_MGMT_CAP_IBSS) ? "Ad-Hoc peer" : "AP", + bss->essid, bss->channel, MAC(bss->bssid), bss->cap_info, + bss->sir, bss->snr); +ok: + FN_EXIT0; + return OK; +} + + +/*---------------------------------------------------------------- +* acx_l_process_assocresp +*----------------------------------------------------------------*/ +static int +acx_l_process_assocresp(wlandevice_t *priv, const wlan_fr_assocresp_t *req) +{ + const wlan_hdr_t *hdr; + int res = OK; + + FN_ENTER; + hdr = req->hdr; + + if ((ACX_MODE_2_STA == priv->mode) + && mac_is_equal(priv->dev_addr, hdr->a1)) { + u16 st = ieee2host16(*(req->status)); + if (WLAN_MGMT_STATUS_SUCCESS == st) { + priv->aid = ieee2host16(*(req->aid)); + /* tell the card we are associated when we are out of interrupt context */ + acx_schedule_task(priv, ACX_AFTER_IRQ_CMD_ASSOCIATE); + } else { + + /* TODO: we shall delete peer from sta_list, and try other candidates... */ + + printk("%s: association FAILED: peer sent " + "response code %d (%s)\n", + priv->netdev->name, st, get_status_string(st)); + res = NOT_OK; + } + } + + FN_EXIT1(res); + return res; +} + + +/*---------------------------------------------------------------- +* acx_l_process_reassocresp +*----------------------------------------------------------------*/ +static int +acx_l_process_reassocresp(wlandevice_t *priv, const wlan_fr_reassocresp_t *req) +{ + const wlan_hdr_t *hdr; + int result = NOT_OK; + u16 st; + + FN_ENTER; + hdr = req->hdr; + + if (!mac_is_equal(priv->dev_addr, hdr->a1)) { + goto end; + } + st = ieee2host16(*(req->status)); + if (st == WLAN_MGMT_STATUS_SUCCESS) { + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + result = OK; + } else { + printk("%s: reassociation FAILED: peer sent " + "response code %d (%s)\n", + priv->netdev->name, st, get_status_string(st)); + } +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_l_process_authen +* +* Called only in STA_SCAN or AP mode +*----------------------------------------------------------------*/ +static int +acx_l_process_authen(wlandevice_t *priv, const wlan_fr_authen_t *req) +{ + const wlan_hdr_t *hdr; + client_t *clt; + wlan_ie_challenge_t *chal; + u16 alg, seq, status; + int ap, result; + + FN_ENTER; + + hdr = req->hdr; + + if (acx_debug & L_ASSOC) { + acx_print_mac("AUTHEN priv->addr=", priv->dev_addr, " "); + acx_print_mac("a1=", hdr->a1, " "); + acx_print_mac("a2=", hdr->a2, " "); + acx_print_mac("a3=", hdr->a3, " "); + acx_print_mac("priv->bssid=", priv->bssid, "\n"); + } + + if (!mac_is_equal(priv->dev_addr, hdr->a1) + || !mac_is_equal(priv->bssid, hdr->a3)) { + result = OK; + goto end; + } + + alg = ieee2host16(*(req->auth_alg)); + seq = ieee2host16(*(req->auth_seq)); + status = ieee2host16(*(req->status)); + + ap = (priv->mode == ACX_MODE_3_AP); + + if (priv->auth_alg <= 1) { + if (priv->auth_alg != alg) { + acxlog(L_ASSOC, "authentication algorithm mismatch: " + "want: %d, req: %d\n", priv->auth_alg, alg); + result = NOT_OK; + goto end; + } + } + acxlog(L_ASSOC, "algorithm is ok\n"); + + if (ap) { + clt = acx_l_sta_list_get_or_add(priv, hdr->a2); + if (STA_LIST_ADD_CAN_FAIL && !clt) { + acxlog(L_ASSOC, "could not allocate room for client\n"); + result = NOT_OK; + goto end; + } + } else { + clt = priv->ap_client; + if (!mac_is_equal(clt->address, hdr->a2)) { + printk("%s: malformed auth frame from AP?!\n", + priv->netdev->name); + result = NOT_OK; + goto end; + } + } + + /* now check which step in the authentication sequence we are + * currently in, and act accordingly */ + acxlog(L_ASSOC, "acx_process_authen auth seq step %d\n", seq); + switch (seq) { + case 1: + if (!ap) + break; + acx_l_transmit_authen2(priv, req, clt); + break; + case 2: + if (ap) + break; + if (status == WLAN_MGMT_STATUS_SUCCESS) { + if (alg == WLAN_AUTH_ALG_OPENSYSTEM) { + acx_set_status(priv, ACX_STATUS_3_AUTHENTICATED); + acx_l_transmit_assoc_req(priv); + } else + if (alg == WLAN_AUTH_ALG_SHAREDKEY) { + acx_l_transmit_authen3(priv, req); + } + } else { + printk("%s: auth FAILED: peer sent " + "response code %d (%s), " + "still waiting for authentication\n", + priv->netdev->name, + status, get_status_string(status)); + acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH); + } + break; + case 3: + if (!ap) + break; + if ((clt->auth_alg != WLAN_AUTH_ALG_SHAREDKEY) + || (alg != WLAN_AUTH_ALG_SHAREDKEY) + || (clt->auth_step != 2)) + break; + chal = req->challenge; + if (!chal + || memcmp(chal->challenge, clt->challenge_text, WLAN_CHALLENGE_LEN) + || (chal->eid != WLAN_EID_CHALLENGE) + || (chal->len != WLAN_CHALLENGE_LEN) + ) + break; + acx_l_transmit_authen4(priv, req); + MAC_COPY(clt->address, hdr->a2); + clt->used = CLIENT_AUTHENTICATED_2; + clt->auth_step = 4; + clt->seq = ieee2host16(hdr->seq); + break; + case 4: + if (ap) + break; + /* ok, we're through: we're authenticated. Woohoo!! */ + acx_set_status(priv, ACX_STATUS_3_AUTHENTICATED); + acxlog(L_ASSOC, "Authenticated!\n"); + /* now that we're authenticated, request association */ + acx_l_transmit_assoc_req(priv); + break; + } + result = NOT_OK; +end: + FN_EXIT1(result); + return result; +} + + +/*---------------------------------------------------------------- +* acx_gen_challenge +*----------------------------------------------------------------*/ +static void +acx_gen_challenge(wlan_ie_challenge_t* d) +{ + FN_ENTER; + d->eid = WLAN_EID_CHALLENGE; + d->len = WLAN_CHALLENGE_LEN; + get_random_bytes(d->challenge, WLAN_CHALLENGE_LEN); + FN_EXIT0; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_deauthen +*----------------------------------------------------------------*/ +static int +acx_l_transmit_deauthen(wlandevice_t *priv, const u8 *addr, u16 reason) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct deauthen_frame_body *body; + + FN_ENTER; + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(priv, tx); + if (!head) + goto bad; + body = (void*)(head + 1); + + head->fc = (WF_FTYPE_MGMTi | WF_FSTYPE_DEAUTHENi); + head->dur = 0; + MAC_COPY(head->da, addr); + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, priv->bssid); + head->seq = 0; + + acxlog(L_DEBUG|L_ASSOC|L_XFER, + "sending deauthen to "MACSTR" for %d\n", + MAC(addr), reason); + + body->reason = host2ieee16(reason); + + /* body is fixed size here, but beware of cutting-and-pasting this - + ** do not use sizeof(*body) for variable sized mgmt packets! */ + acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + sizeof(*body)); + + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_authen1 +*----------------------------------------------------------------*/ +static int +acx_l_transmit_authen1(wlandevice_t *priv) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct auth_frame_body *body; + + FN_ENTER; + + acxlog(L_ASSOC, "Sending authentication1 request, " + "awaiting response!\n"); + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(priv, tx); + if (!head) + goto bad; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_AUTHENi; + head->dur = host2ieee16(0x8000); + MAC_COPY(head->da, priv->bssid); + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, priv->bssid); + head->seq = 0; + + body->auth_alg = host2ieee16(priv->auth_alg); + body->auth_seq = host2ieee16(1); + body->status = host2ieee16(0); + + acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2); + + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_authen2 +*----------------------------------------------------------------*/ +static int +acx_l_transmit_authen2(wlandevice_t *priv, const wlan_fr_authen_t *req, + client_t *clt) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct auth_frame_body *body; + unsigned int packet_len; + + FN_ENTER; + + if (!clt) + goto ok; + + MAC_COPY(clt->address, req->hdr->a2); +#ifdef UNUSED + clt->ps = ((WF_FC_PWRMGTi & req->hdr->fc) != 0); +#endif + clt->auth_alg = ieee2host16(*(req->auth_alg)); + clt->auth_step = 2; + clt->seq = ieee2host16(req->hdr->seq); + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(priv, tx); + if (!head) + goto bad; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */ + head->dur = req->hdr->dur; + MAC_COPY(head->da, req->hdr->a2); + /* MAC_COPY(head->sa, req->hdr->a1); */ + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, req->hdr->a3); + head->seq = req->hdr->seq; + + /* already in IEEE format, no endianness conversion */ + body->auth_alg = *(req->auth_alg); + body->auth_seq = host2ieee16(2); + body->status = host2ieee16(0); + + packet_len = WLAN_HDR_A3_LEN + 2 + 2 + 2; + if (ieee2host16(*(req->auth_alg)) == WLAN_AUTH_ALG_OPENSYSTEM) { + clt->used = CLIENT_AUTHENTICATED_2; + } else { /* shared key */ + acx_gen_challenge(&body->challenge); + memcpy(&clt->challenge_text, body->challenge.challenge, WLAN_CHALLENGE_LEN); + packet_len += 2 + 2 + 2 + 1+1+WLAN_CHALLENGE_LEN; + } + + acxlog_mac(L_ASSOC|L_XFER, + "transmit_auth2: BSSID=", head->bssid, "\n"); + + acx_l_tx_data(priv, tx, packet_len); +ok: + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_authen3 +*----------------------------------------------------------------*/ +static int +acx_l_transmit_authen3(wlandevice_t *priv, const wlan_fr_authen_t *req) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct auth_frame_body *body; + unsigned int packet_len; + + FN_ENTER; + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto ok; + head = acx_l_get_txbuf(priv, tx); + if (!head) + goto ok; + body = (void*)(head + 1); + + head->fc = WF_FC_ISWEPi + WF_FSTYPE_AUTHENi; + /* FIXME: is this needed?? authen4 does it... + head->dur = req->hdr->dur; + head->seq = req->hdr->seq; + */ + MAC_COPY(head->da, priv->bssid); + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, priv->bssid); + + /* already in IEEE format, no endianness conversion */ + body->auth_alg = *(req->auth_alg); + body->auth_seq = host2ieee16(3); + body->status = host2ieee16(0); + memcpy(&body->challenge, req->challenge, req->challenge->len + 2); + packet_len = WLAN_HDR_A3_LEN + 8 + req->challenge->len; + + acxlog(L_ASSOC|L_XFER, "transmit_authen3!\n"); + + acx_l_tx_data(priv, tx, packet_len); +ok: + FN_EXIT1(OK); + return OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_authen4 +*----------------------------------------------------------------*/ +static int +acx_l_transmit_authen4(wlandevice_t *priv, const wlan_fr_authen_t *req) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct auth_frame_body *body; + + FN_ENTER; + + tx = acx_l_alloc_tx(priv); + if (!tx) + goto ok; + head = acx_l_get_txbuf(priv, tx); + if (!head) + goto ok; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */ + head->dur = req->hdr->dur; + MAC_COPY(head->da, req->hdr->a2); + /* MAC_COPY(head->sa, req->hdr->a1); */ + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, req->hdr->a3); + head->seq = req->hdr->seq; + + /* already in IEEE format, no endianness conversion */ + body->auth_alg = *(req->auth_alg); + body->auth_seq = host2ieee16(4); + body->status = host2ieee16(0); + + acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2); +ok: + FN_EXIT1(OK); + return OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_assoc_req +* +* priv->ap_client is a current candidate AP here +*----------------------------------------------------------------*/ +static int +acx_l_transmit_assoc_req(wlandevice_t *priv) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + u8 *body, *p, *prate; + unsigned int packet_len; + u16 cap; + + FN_ENTER; + + acxlog(L_ASSOC, "sending association request, " + "awaiting response. NOT ASSOCIATED YET\n"); + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(priv, tx); + if (!head) + goto bad; + body = (void*)(head + 1); + + head->fc = WF_FSTYPE_ASSOCREQi; + head->dur = host2ieee16(0x8000); + MAC_COPY(head->da, priv->bssid); + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, priv->bssid); + head->seq = 0; + + p = body; + /* now start filling the AssocReq frame body */ + + /* since this assoc request will most likely only get + * sent in the STA to AP case (and not when Ad-Hoc IBSS), + * the cap combination indicated here will thus be + * WF_MGMT_CAP_ESSi *always* (no IBSS ever) + * The specs are more than non-obvious on all that: + * + * 802.11 7.3.1.4 Capability Information field + ** APs set the ESS subfield to 1 and the IBSS subfield to 0 within + ** Beacon or Probe Response management frames. STAs within an IBSS + ** set the ESS subfield to 0 and the IBSS subfield to 1 in transmitted + ** Beacon or Probe Response management frames + ** + ** APs set the Privacy subfield to 1 within transmitted Beacon, + ** Probe Response, Association Response, and Reassociation Response + ** if WEP is required for all data type frames within the BSS. + ** STAs within an IBSS set the Privacy subfield to 1 in Beacon + ** or Probe Response management frames if WEP is required + ** for all data type frames within the IBSS */ + + /* note that returning 0 will be refused by several APs... + * (so this indicates that you're probably supposed to + * "confirm" the ESS mode) */ + cap = WF_MGMT_CAP_ESSi; + + /* this one used to be a check on wep_restricted, + * but more likely it's wep_enabled instead */ + if (priv->wep_enabled) + SET_BIT(cap, WF_MGMT_CAP_PRIVACYi); + + /* Probably we can just set these always, because our hw is + ** capable of shortpre and PBCC --vda */ + /* only ask for short preamble if the peer station supports it */ + if (priv->ap_client->cap_info & WF_MGMT_CAP_SHORT) + SET_BIT(cap, WF_MGMT_CAP_SHORTi); + /* only ask for PBCC support if the peer station supports it */ + if (priv->ap_client->cap_info & WF_MGMT_CAP_PBCC) + SET_BIT(cap, WF_MGMT_CAP_PBCCi); + + /* IEs: 1. caps */ + *(u16*)p = cap; p += 2; + /* 2. listen interval */ + *(u16*)p = host2ieee16(priv->listen_interval); p += 2; + /* 3. ESSID */ + p = wlan_fill_ie_ssid(p, + strlen(priv->essid_for_assoc), priv->essid_for_assoc); + /* 4. supp rates */ + prate = p; + p = wlan_fill_ie_rates(p, + priv->rate_supported_len, priv->rate_supported); + /* 5. ext supp rates */ + p = wlan_fill_ie_rates_ext(p, + priv->rate_supported_len, priv->rate_supported); + + if (acx_debug & L_DEBUG) { + printk("association: rates element\n"); + acx_dump_bytes(prate, p - prate); + } + + /* calculate lengths */ + packet_len = WLAN_HDR_A3_LEN + (p - body); + + acxlog(L_ASSOC, "association: requesting caps 0x%04X, ESSID '%s'\n", + cap, priv->essid_for_assoc); + + acx_l_tx_data(priv, tx, packet_len); + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} + + +/*---------------------------------------------------------------- +* acx_l_transmit_disassoc +* +* FIXME: looks like incomplete implementation of a helper: +* acx_l_transmit_disassoc(priv, clt) - kick this client (we're an AP) +* acx_l_transmit_disassoc(priv, NULL) - leave BSSID (we're a STA) +*----------------------------------------------------------------*/ +#ifdef BROKEN +int +acx_l_transmit_disassoc(wlandevice_t *priv, client_t *clt) +{ + struct tx *tx; + struct wlan_hdr_mgmt *head; + struct disassoc_frame_body *body; + + FN_ENTER; +/* if (clt != NULL) { */ + tx = acx_l_alloc_tx(priv); + if (!tx) + goto bad; + head = acx_l_get_txbuf(priv, tx); + if (!head) + goto bad; + body = (void*)(head + 1); + +/* clt->used = CLIENT_AUTHENTICATED_2; - not (yet?) associated */ + + head->fc = WF_FSTYPE_DISASSOCi; + head->dur = 0; + /* huh? It muchly depends on whether we're STA or AP... + ** sta->ap: da=bssid, sa=own, bssid=bssid + ** ap->sta: da=sta, sa=bssid, bssid=bssid. FIXME! */ + MAC_COPY(head->da, priv->bssid); + MAC_COPY(head->sa, priv->dev_addr); + MAC_COPY(head->bssid, priv->dev_addr); + head->seq = 0; + + /* "Class 3 frame received from nonassociated station." */ + body->reason = host2ieee16(7); + + /* fixed size struct, ok to sizeof */ + acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + sizeof(*body)); +/* } */ + FN_EXIT1(OK); + return OK; +bad: + FN_EXIT1(NOT_OK); + return NOT_OK; +} +#endif + + +/*---------------------------------------------------------------- +* acx_s_complete_scan +* +* Called either from after_interrupt_task() if: +* 1) there was Scan_Complete IRQ, or +* 2) scanning expired in timer() +* We need to decide which ESS or IBSS to join. +* Iterates thru priv->sta_list: +* if priv->ap is not bcast, will join only specified +* ESS or IBSS with this bssid +* checks peers' caps for ESS/IBSS bit +* checks peers' SSID, allows exact match or hidden SSID +* If station to join is chosen: +* points priv->ap_client to the chosen struct client +* sets priv->essid_for_assoc for future assoc attempt +* Auth/assoc is not yet performed +* Returns OK if there is no need to restart scan +*----------------------------------------------------------------*/ +int +acx_s_complete_scan(wlandevice_t *priv) +{ + struct client *bss; + unsigned long flags; + u16 needed_cap; + int i; + int idx_found = -1; + int result = OK; + + FN_ENTER; + + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + needed_cap = WF_MGMT_CAP_IBSS; /* 2, we require Ad-Hoc */ + break; + case ACX_MODE_2_STA: + needed_cap = WF_MGMT_CAP_ESS; /* 1, we require Managed */ + break; + default: + printk("acx: driver bug: mode=%d in complete_scan()\n", priv->mode); + dump_stack(); + goto end; + } + + acx_lock(priv, flags); + + /* TODO: sta_iterator hiding implementation would be nice here... */ + + for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { + bss = &priv->sta_list[i]; + if (!bss->used) continue; + + acxlog(L_ASSOC, "Scan Table: SSID='%s' CH=%d SIR=%d SNR=%d\n", + bss->essid, bss->channel, bss->sir, bss->snr); + + if (!mac_is_bcast(priv->ap)) + if (!mac_is_equal(bss->bssid, priv->ap)) + continue; /* keep looking */ + + /* broken peer with no mode flags set? */ + if (unlikely(!(bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)))) { + printk("%s: strange peer "MACSTR" found with " + "neither ESS (AP) nor IBSS (Ad-Hoc) " + "capability - skipped\n", + priv->netdev->name, MAC(bss->address)); + continue; + } + acxlog(L_ASSOC, "peer_cap 0x%04X, needed_cap 0x%04X\n", + bss->cap_info, needed_cap); + + /* does peer station support what we need? */ + if ((bss->cap_info & needed_cap) != needed_cap) + continue; /* keep looking */ + + /* strange peer with NO basic rates?! */ + if (unlikely(!bss->rate_bas)) { + printk("%s: strange peer "MACSTR" with empty rate set " + "- skipped\n", + priv->netdev->name, MAC(bss->address)); + continue; + } + + /* do we support all basic rates of this peer? */ + if ((bss->rate_bas & priv->rate_oper) != bss->rate_bas) { +/* we probably need to have all rates as operational rates, + even in case of an 11M-only configuration */ +#ifdef THIS_IS_TROUBLESOME + printk("%s: peer "MACSTR": incompatible basic rates " + "(AP requests 0x%04X, we have 0x%04X) " + "- skipped\n", + priv->netdev->name, MAC(bss->address), + bss->rate_bas, priv->rate_oper); + continue; +#else + printk("%s: peer "MACSTR": incompatible basic rates " + "(AP requests 0x%04X, we have 0x%04X). " + "Considering anyway...\n", + priv->netdev->name, MAC(bss->address), + bss->rate_bas, priv->rate_oper); +#endif + } + + if ( !(priv->reg_dom_chanmask & (1<<(bss->channel-1))) ) { + printk("%s: warning: peer "MACSTR" is on channel %d " + "outside of channel range of current " + "regulatory domain - couldn't join " + "even if other settings match. " + "You might want to adapt your config\n", + priv->netdev->name, MAC(bss->address), + bss->channel); + continue; /* keep looking */ + } + + if (!priv->essid_active || !strcmp(bss->essid, priv->essid)) { + acxlog(L_ASSOC, + "found station with matching ESSID! ('%s' " + "station, '%s' config)\n", + bss->essid, + (priv->essid_active) ? priv->essid : "[any]"); + /* TODO: continue looking for peer with better SNR */ + bss->used = CLIENT_JOIN_CANDIDATE; + idx_found = i; + + /* stop searching if this station is + * on the current channel, otherwise + * keep looking for an even better match */ + if (bss->channel == priv->channel) + break; + } else + if (!bss->essid[0] + || ((' ' == bss->essid[0]) && !bss->essid[1]) + ) { + /* hmm, station with empty or single-space SSID: + * using hidden SSID broadcast? + */ + /* This behaviour is broken: which AP from zillion + ** of APs with hidden SSID you'd try? + ** We should use Probe requests to get Probe responses + ** and check for real SSID (are those never hidden?) */ + bss->used = CLIENT_JOIN_CANDIDATE; + if (idx_found == -1) + idx_found = i; + acxlog(L_ASSOC, "found station with empty or " + "single-space (hidden) SSID, considering " + "for assoc attempt\n"); + /* ...and keep looking for better matches */ + } else { + acxlog(L_ASSOC, "ESSID doesn't match! ('%s' " + "station, '%s' config)\n", + bss->essid, + (priv->essid_active) ? priv->essid : "[any]"); + } + } + + /* TODO: iterate thru join candidates instead */ + /* TODO: rescan if not associated within some timeout */ + if (idx_found != -1) { + char *essid_src; + size_t essid_len; + + bss = &priv->sta_list[idx_found]; + priv->ap_client = bss; + + if (bss->essid[0] == '\0') { + /* if the ESSID of the station we found is empty + * (no broadcast), then use user configured ESSID + * instead */ + essid_src = priv->essid; + essid_len = priv->essid_len; + } else { + essid_src = bss->essid; + essid_len = strlen(bss->essid); + } + + acx_update_capabilities(priv); + + memcpy(priv->essid_for_assoc, essid_src, essid_len); + priv->essid_for_assoc[essid_len] = '\0'; + priv->channel = bss->channel; + MAC_COPY(priv->bssid, bss->bssid); + + bss->rate_cfg = (bss->rate_cap & priv->rate_oper); + bss->rate_cur = 1 << lowest_bit(bss->rate_cfg); + bss->rate_100 = acx_rate111to100(bss->rate_cur); + + acxlog_mac(L_ASSOC, + "matching station found: ", priv->bssid, ", joining\n"); + + /* TODO: do we need to switch to the peer's channel first? */ + + if (ACX_MODE_0_ADHOC == priv->mode) { + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + } else { + acx_l_transmit_authen1(priv); + acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH); + } + } else { /* idx_found == -1 */ + /* uh oh, no station found in range */ + if (ACX_MODE_0_ADHOC == priv->mode) { + printk("%s: no matching station found in range, " + "generating our own IBSS instead\n", + priv->netdev->name); + /* we do it hostap way: */ + MAC_COPY(priv->bssid, priv->dev_addr); + priv->bssid[0] |= 0x02; /* 'local assigned addr' bit */ + /* add IBSS bit to our caps... */ + acx_update_capabilities(priv); + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + /* In order to cmd_join be called below */ + idx_found = 0; + } else { + /* we shall scan again, AP can be + ** just temporarily powered off */ + acxlog(L_ASSOC, + "no matching station found in range yet\n"); + acx_set_status(priv, ACX_STATUS_1_SCANNING); + result = NOT_OK; + } + } + + acx_unlock(priv, flags); + + if (idx_found != -1) { + if (ACX_MODE_0_ADHOC == priv->mode) { + /* need to update channel in beacon template */ + SET_BIT(priv->set_mask, SET_TEMPLATES); + if (ACX_STATE_IFACE_UP & priv->dev_state_mask) + acx_s_update_card_settings(priv, 0, 0); + } + /* Inform firmware on our decision to start or join BSS */ + acx_s_cmd_join_bssid(priv, priv->bssid); + } + +end: + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx_s_read_fw +** +** Loads a firmware image +** +** Returns: +** 0 unable to load file +** pointer to firmware success +*/ +#if USE_FW_LOADER_26 +firmware_image_t* +acx_s_read_fw(struct device *dev, const char *file, u32 *size) +#else +#undef acx_s_read_fw +firmware_image_t* +acx_s_read_fw(const char *file, u32 *size) +#endif +{ + firmware_image_t *res; + +#if USE_FW_LOADER_LEGACY + mm_segment_t orgfs; + unsigned long page; + char *buffer; + struct file *inf; + int retval; + int offset; + char *filename; +#endif + +#if USE_FW_LOADER_26 + const struct firmware *fw_entry; + + res = NULL; + acxlog(L_INIT, "requesting firmware image '%s'\n", file); + if (!request_firmware(&fw_entry, file, dev)) { + *size = 8; + if (fw_entry->size >= 8) + *size = 8 + le32_to_cpu(*(u32 *)(fw_entry->data + 4)); + if (fw_entry->size != *size) { + printk("acx: firmware size does not match " + "firmware header: %d != %d, " + "aborting fw upload\n", + (int) fw_entry->size, (int) *size); + goto release_ret; + } + res = vmalloc(*size); + if (!res) { + printk("acx: no memory for firmware " + "(%u bytes)\n", *size); + goto release_ret; + } + memcpy(res, fw_entry->data, fw_entry->size); +release_ret: + release_firmware(fw_entry); + return res; + } + printk("acx: firmware image '%s' was not provided. " + "Check your hotplug scripts\n", file); +#endif + +#if USE_FW_LOADER_LEGACY + printk("acx: firmware upload via firmware_dir module parameter " + "is deprecated. Switch to using hotplug\n"); + + res = NULL; + orgfs = get_fs(); /* store original fs */ + set_fs(KERNEL_DS); + + /* Read in whole file then check the size */ + page = __get_free_page(GFP_KERNEL); + if (unlikely(0 == page)) { + printk("acx: no memory for firmware upload\n"); + goto fail; + } + + filename = kmalloc(PATH_MAX, GFP_KERNEL); + if (unlikely(!filename)) { + printk("acx: no memory for firmware upload\n"); + goto fail; + } + if (!firmware_dir) { + firmware_dir = "/usr/share/acx"; + acxlog(L_DEBUG, "no firmware directory specified " + "via module parameter firmware_dir, " + "using default %s\n", firmware_dir); + } + snprintf(filename, PATH_MAX, "%s/%s", firmware_dir, file); + acxlog(L_INIT, "reading firmware image '%s'\n", filename); + + buffer = (char*)page; + + /* Note that file must be given as absolute path: + * a relative path works on first loading, + * but any subsequent firmware loading during card + * eject/insert will fail, most likely since the first + * module loading happens in user space (and thus + * filp_open can figure out the absolute path from a + * relative path) whereas the card reinsert processing + * probably happens in kernel space where you don't have + * a current directory to be able to figure out an + * absolute path from a relative path... */ + inf = filp_open(filename, O_RDONLY, 0); + kfree(filename); + if (OK != IS_ERR(inf)) { + const char *err; + + switch (-PTR_ERR(inf)) { + case 2: err = "file not found"; + break; + default: + err = "unknown error"; + break; + } + printk("acx: error %ld trying to open file '%s': %s\n", + -PTR_ERR(inf), file, err); + goto fail; + } + + if (unlikely((NULL == inf->f_op) || (NULL == inf->f_op->read))) { + printk("acx: %s does not have a read method?!\n", file); + goto fail_close; + } + + offset = 0; + do { + retval = inf->f_op->read(inf, buffer, PAGE_SIZE, &inf->f_pos); + + if (unlikely(0 > retval)) { + printk("acx: error %d reading file '%s'\n", + -retval, file); + vfree(res); + res = NULL; + } else if (0 == retval) { + if (0 == offset) { + printk("acx: firmware image file " + "'%s' is empty?!\n", file); + } + } else if (0 < retval) { + /* allocate result buffer here if needed, + * since we don't want to waste resources/time + * (in case file opening/reading fails) + * by doing allocation in front of the loop instead. */ + if (NULL == res) { + *size = 8 + le32_to_cpu(*(u32 *)(4 + buffer)); + + res = vmalloc(*size); + if (NULL == res) { + printk("acx: unable to " + "allocate %u bytes for " + "firmware module upload\n", + *size); + goto fail_close; + } + acxlog(L_DEBUG, "allocated %u bytes " + "for firmware module loading\n", + *size); + } + if ((unlikely(offset + retval > *size))) { + printk("acx: ERROR: allocation " + "was less than firmware image size?!\n"); + goto fail_close; + } + memcpy((u8*)res + offset, buffer, retval); + offset += retval; + } + } while (0 < retval); + +fail_close: + retval = filp_close(inf, NULL); + + if (unlikely(retval)) { + printk("acx: error %d closing file '%s'\n", -retval, file); + } + + if (unlikely((NULL != res) && (offset != le32_to_cpu(res->size) + 8))) { + printk("acx: firmware is reporting a different size " + "(0x%08X; 0x%08X was read)\n", + le32_to_cpu(res->size) + 8, offset); + vfree(res); + res = NULL; + } + +fail: + if (page) + free_page(page); + set_fs(orgfs); +#endif + + /* checksum will be verified in write_fw, so don't bother here */ + return res; +} + + +#ifdef POWER_SAVE_80211 +/*---------------------------------------------------------------- +* acx_s_activate_power_save_mode +*----------------------------------------------------------------*/ +static void +acx_s_activate_power_save_mode(wlandevice_t *priv) +{ + acx100_ie_powermgmt_t pm; + + FN_ENTER; + + acx_s_interrogate(priv, &pm, ACX1xx_IE_POWER_MGMT); + if (pm.wakeup_cfg != 0x81) + goto end; + + pm.wakeup_cfg = 0; + pm.options = 0; + pm.hangover_period = 0; + acx_s_configure(priv, &pm, ACX1xx_IE_POWER_MGMT); +end: + FN_EXIT0; +} +#endif + + +/*********************************************************************** +** acx_s_set_wepkey +*/ +static void +acx100_s_set_wepkey(wlandevice_t *priv) +{ + ie_dot11WEPDefaultKey_t dk; + int i; + + for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) { + if (priv->wep_keys[i].size != 0) { + acxlog(L_INIT, "setting WEP key: %d with " + "total size: %d\n", i, (int) priv->wep_keys[i].size); + dk.action = 1; + dk.keySize = priv->wep_keys[i].size; + dk.defaultKeyNum = i; + memcpy(dk.key, priv->wep_keys[i].key, dk.keySize); + acx_s_configure(priv, &dk, ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE); + } + } +} + +static void +acx111_s_set_wepkey(wlandevice_t *priv) +{ + acx111WEPDefaultKey_t dk; + int i; + + for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) { + if (priv->wep_keys[i].size != 0) { + acxlog(L_INIT, "setting WEP key: %d with " + "total size: %d\n", i, (int) priv->wep_keys[i].size); + memset(&dk, 0, sizeof(dk)); + dk.action = cpu_to_le16(1); /* "add key"; yes, that's a 16bit value */ + dk.keySize = priv->wep_keys[i].size; + + /* are these two lines necessary? */ + dk.type = 0; /* default WEP key */ + dk.index = 0; /* ignored when setting default key */ + + dk.defaultKeyNum = i; + memcpy(dk.key, priv->wep_keys[i].key, dk.keySize); + acx_s_issue_cmd(priv, ACX1xx_CMD_WEP_MGMT, &dk, sizeof(dk)); + } + } +} + +static void +acx_s_set_wepkey(wlandevice_t *priv) +{ + if (IS_ACX111(priv)) + acx111_s_set_wepkey(priv); + else + acx100_s_set_wepkey(priv); +} + + +/*********************************************************************** +** acx100_s_init_wep +** +** FIXME: this should probably be moved into the new card settings +** management, but since we're also modifying the memory map layout here +** due to the WEP key space we want, we should take care... +*/ +int +acx100_s_init_wep(wlandevice_t *priv) +{ +/* int i; + acx100_cmd_wep_mgmt_t wep_mgmt; size = 37 bytes */ + acx100_ie_wep_options_t options; + ie_dot11WEPDefaultKeyID_t dk; + acx_ie_memmap_t pt; + int res = NOT_OK; + + FN_ENTER; + + if (OK != acx_s_interrogate(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { + goto fail; + } + + acxlog(L_DEBUG, "CodeEnd:%X\n", pt.CodeEnd); + + pt.WEPCacheStart = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4); + pt.WEPCacheEnd = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4); + + if (OK != acx_s_configure(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { + goto fail; + } + + /* let's choose maximum setting: 4 default keys, plus 10 other keys: */ + options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10); + options.WEPOption = 0x00; + + acxlog(L_ASSOC, "%s: writing WEP options\n", __func__); + acx_s_configure(priv, &options, ACX100_IE_WEP_OPTIONS); + + acx100_s_set_wepkey(priv); + + if (priv->wep_keys[priv->wep_current_index].size != 0) { + acxlog(L_ASSOC, "setting active default WEP key number: %d\n", + priv->wep_current_index); + dk.KeyID = priv->wep_current_index; + acx_s_configure(priv, &dk, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); /* 0x1010 */ + } + /* FIXME!!! wep_key_struct is filled nowhere! But priv + * is initialized to 0, and we don't REALLY need those keys either */ +/* for (i = 0; i < 10; i++) { + if (priv->wep_key_struct[i].len != 0) { + MAC_COPY(wep_mgmt.MacAddr, priv->wep_key_struct[i].addr); + wep_mgmt.KeySize = cpu_to_le16(priv->wep_key_struct[i].len); + memcpy(&wep_mgmt.Key, priv->wep_key_struct[i].key, le16_to_cpu(wep_mgmt.KeySize)); + wep_mgmt.Action = cpu_to_le16(1); + acxlog(L_ASSOC, "writing WEP key %d (len %d)\n", i, le16_to_cpu(wep_mgmt.KeySize)); + if (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_WEP_MGMT, &wep_mgmt, sizeof(wep_mgmt))) { + priv->wep_key_struct[i].index = i; + } + } + } */ + + /* now retrieve the updated WEPCacheEnd pointer... */ + if (OK != acx_s_interrogate(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { + printk("%s: ACX1xx_IE_MEMORY_MAP read #2 FAILED\n", + priv->netdev->name); + goto fail; + } + /* ...and tell it to start allocating templates at that location */ + /* (no endianness conversion needed) */ + pt.PacketTemplateStart = pt.WEPCacheEnd; + + if (OK != acx_s_configure(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { + printk("%s: ACX1xx_IE_MEMORY_MAP write #2 FAILED\n", + priv->netdev->name); + goto fail; + } + res = OK; + +fail: + FN_EXIT1(res); + return res; +} + + +/*********************************************************************** +*/ +static int +acx_s_init_max_null_data_template(wlandevice_t *priv) +{ + struct acx_template_nullframe b; + int result; + + FN_ENTER; + memset(&b, 0, sizeof(b)); + b.size = cpu_to_le16(sizeof(b) - 2); + result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_NULL_DATA, &b, sizeof(b)); + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx_s_init_max_beacon_template +*/ +static int +acx_s_init_max_beacon_template(wlandevice_t *priv) +{ + struct acx_template_beacon b; + int result; + + FN_ENTER; + memset(&b, 0, sizeof(b)); + b.size = cpu_to_le16(sizeof(b) - 2); + result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_BEACON, &b, sizeof(b)); + + FN_EXIT1(result); + return result; +} + +/*********************************************************************** +** acx_s_init_max_tim_template +*/ +static int +acx_s_init_max_tim_template(wlandevice_t *priv) +{ + acx_template_tim_t t; + + memset(&t, 0, sizeof(t)); + t.size = cpu_to_le16(sizeof(t) - 2); + return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t)); +} + + +/*********************************************************************** +** acx_s_init_max_probe_response_template +*/ +static int +acx_s_init_max_probe_response_template(wlandevice_t *priv) +{ + struct acx_template_proberesp pr; + + memset(&pr, 0, sizeof(pr)); + pr.size = cpu_to_le16(sizeof(pr) - 2); + + return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, sizeof(pr)); +} + + +/*********************************************************************** +** acx_s_init_max_probe_request_template +*/ +static int +acx_s_init_max_probe_request_template(wlandevice_t *priv) +{ + union { + acx100_template_probereq_t p100; + acx111_template_probereq_t p111; + } pr; + int res; + + FN_ENTER; + memset(&pr, 0, sizeof(pr)); + pr.p100.size = cpu_to_le16(sizeof(pr) - 2); + res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &pr, sizeof(pr)); + FN_EXIT1(res); + return res; +} + + +/*********************************************************************** +** acx_s_set_tim_template +** +** In full blown driver we will regularly update partial virtual bitmap +** by calling this function +** (it can be done by irq handler on each DTIM irq or by timer...) + +[802.11 7.3.2.6] TIM information element: +- 1 EID +- 1 Length +1 1 DTIM Count + indicates how many beacons (including this) appear before next DTIM + (0=this one is a DTIM) +2 1 DTIM Period + number of beacons between successive DTIMs + (0=reserved, 1=all TIMs are DTIMs, 2=every other, etc) +3 1 Bitmap Control + bit0: Traffic Indicator bit associated with Assoc ID 0 (Bcast AID?) + set to 1 in TIM elements with a value of 0 in the DTIM Count field + when one or more broadcast or multicast frames are buffered at the AP. + bit1-7: Bitmap Offset (logically Bitmap_Offset = Bitmap_Control & 0xFE). +4 n Partial Virtual Bitmap + Visible part of traffic-indication bitmap. + Full bitmap consists of 2008 bits (251 octets) such that bit number N + (0<=N<=2007) in the bitmap corresponds to bit number (N mod 8) + in octet number N/8 where the low-order bit of each octet is bit0, + and the high order bit is bit7. + Each set bit in virtual bitmap corresponds to traffic buffered by AP + for a specific station (with corresponding AID?). + Partial Virtual Bitmap shows a part of bitmap which has non-zero. + Bitmap Offset is a number of skipped zero octets (see above). + 'Missing' octets at the tail are also assumed to be zero. + Example: Length=6, Bitmap_Offset=2, Partial_Virtual_Bitmap=55 55 55 + This means that traffic-indication bitmap is: + 00000000 00000000 01010101 01010101 01010101 00000000 00000000... + (is bit0 in the map is always 0 and real value is in Bitmap Control bit0?) +*/ +static int +acx_s_set_tim_template(wlandevice_t *priv) +{ +/* For now, configure smallish test bitmap, all zero ("no pending data") */ + enum { bitmap_size = 5 }; + + acx_template_tim_t t; + int result; + + FN_ENTER; + + memset(&t, 0, sizeof(t)); + t.size = 5 + bitmap_size; /* eid+len+count+period+bmap_ctrl + bmap */ + t.tim_eid = WLAN_EID_TIM; + t.len = 3 + bitmap_size; /* count+period+bmap_ctrl + bmap */ + result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t)); + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx_fill_beacon_or_proberesp_template +** +** For frame format info, please see 802.11-1999.pdf item 7.2.3.9 and below!! +** +** NB: we use the fact that +** struct acx_template_proberesp and struct acx_template_beacon are the same +** (well, almost...) +** +** [802.11] Beacon's body consist of these IEs: +** 1 Timestamp +** 2 Beacon interval +** 3 Capability information +** 4 SSID +** 5 Supported rates (up to 8 rates) +** 6 FH Parameter Set (frequency-hopping PHYs only) +** 7 DS Parameter Set (direct sequence PHYs only) +** 8 CF Parameter Set (only if PCF is supported) +** 9 IBSS Parameter Set (ad-hoc only) +** +** Beacon only: +** 10 TIM (AP only) (see 802.11 7.3.2.6) +** 11 Country Information (802.11d) +** 12 FH Parameters (802.11d) +** 13 FH Pattern Table (802.11d) +** ... (?!! did not yet find relevant PDF file... --vda) +** 19 ERP Information (extended rate PHYs) +** 20 Extended Supported Rates (if more than 8 rates) +** +** Proberesp only: +** 10 Country information (802.11d) +** 11 FH Parameters (802.11d) +** 12 FH Pattern Table (802.11d) +** 13-n Requested information elements (802.11d) +** ???? +** 18 ERP Information (extended rate PHYs) +** 19 Extended Supported Rates (if more than 8 rates) +*/ +static int +acx_fill_beacon_or_proberesp_template(wlandevice_t *priv, + struct acx_template_beacon *templ, + u16 fc /* in host order! */) +{ + int len; + u8 *p; + + FN_ENTER; + + memset(templ, 0, sizeof(*templ)); + MAC_BCAST(templ->da); + MAC_COPY(templ->sa, priv->dev_addr); + MAC_COPY(templ->bssid, priv->bssid); + + templ->beacon_interval = cpu_to_le16(priv->beacon_interval); + acx_update_capabilities(priv); + templ->cap = cpu_to_le16(priv->capabilities); + + p = templ->variable; + p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid); + p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported); + p = wlan_fill_ie_ds_parms(p, priv->channel); + /* NB: should go AFTER tim, but acx seem to keep tim last always */ + p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported); + + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + /* ATIM window */ + p = wlan_fill_ie_ibss_parms(p, 0); break; + case ACX_MODE_3_AP: + /* TIM IE is set up as separate template */ + break; + } + + len = p - (u8*)templ; + templ->fc = cpu_to_le16(WF_FTYPE_MGMT | fc); + /* - 2: do not count 'u16 size' field */ + templ->size = cpu_to_le16(len - 2); + + FN_EXIT1(len); + return len; +} + + +/*********************************************************************** +** acx_s_set_beacon_template +*/ +static int +acx_s_set_beacon_template(wlandevice_t *priv) +{ + struct acx_template_beacon bcn; + int len, result; + + FN_ENTER; + + len = acx_fill_beacon_or_proberesp_template(priv, &bcn, WF_FSTYPE_BEACON); + result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_BEACON, &bcn, len); + + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx_s_set_probe_response_template +*/ +static int +acx_s_set_probe_response_template(wlandevice_t *priv) +{ + struct acx_template_proberesp pr; + int len, result; + + FN_ENTER; + + len = acx_fill_beacon_or_proberesp_template(priv, &pr, WF_FSTYPE_PROBERESP); + result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, len); + + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +** acx100_s_init_packet_templates() +** +** NOTE: order is very important here, to have a correct memory layout! +** init templates: max Probe Request (station mode), max NULL data, +** max Beacon, max TIM, max Probe Response. +*/ +int +acx100_s_init_packet_templates(wlandevice_t *priv) +{ + acx_ie_memmap_t mm; + int result = NOT_OK; + + FN_ENTER; + + acxlog(L_DEBUG, "sizeof(memmap)=%d bytes\n", (int)sizeof(mm)); + + /* acx100 still do not emit probe requests, thus this call + ** is sourt of not needed. But we want it to work someday */ + if (OK != acx_s_init_max_probe_request_template(priv)) + goto failed; + +#ifdef NOT_WORKING_YET + /* FIXME: creating the NULL data template breaks + * communication right now, needs further testing. + * Also, need to set the template once we're joining a network. */ + if (OK != acx_s_init_max_null_data_template(priv)) + goto failed; +#endif + + if (OK != acx_s_init_max_beacon_template(priv)) + goto failed; + + if (OK != acx_s_init_max_tim_template(priv)) + goto failed; + + if (OK != acx_s_init_max_probe_response_template(priv)) + goto failed; + + if (OK != acx_s_set_tim_template(priv)) + goto failed; + + if (OK != acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP)) { + goto failed; + } + + mm.QueueStart = cpu_to_le32(le32_to_cpu(mm.PacketTemplateEnd) + 4); + if (OK != acx_s_configure(priv, &mm, ACX1xx_IE_MEMORY_MAP)) { + goto failed; + } + + result = OK; + goto success; + +failed: + acxlog(L_DEBUG|L_INIT, + /* "cb=0x%X\n" */ + "pACXMemoryMap:\n" + ".CodeStart=0x%X\n" + ".CodeEnd=0x%X\n" + ".WEPCacheStart=0x%X\n" + ".WEPCacheEnd=0x%X\n" + ".PacketTemplateStart=0x%X\n" + ".PacketTemplateEnd=0x%X\n", + /* len, */ + le32_to_cpu(mm.CodeStart), + le32_to_cpu(mm.CodeEnd), + le32_to_cpu(mm.WEPCacheStart), + le32_to_cpu(mm.WEPCacheEnd), + le32_to_cpu(mm.PacketTemplateStart), + le32_to_cpu(mm.PacketTemplateEnd)); + +success: + FN_EXIT1(result); + return result; +} + +int +acx111_s_init_packet_templates(wlandevice_t *priv) +{ + int result = NOT_OK; + + FN_ENTER; + + acxlog(L_DEBUG|L_INIT, "initializing max packet templates\n"); + + if (OK != acx_s_init_max_probe_request_template(priv)) + goto failed; + + if (OK != acx_s_init_max_null_data_template(priv)) + goto failed; + + if (OK != acx_s_init_max_beacon_template(priv)) + goto failed; + + if (OK != acx_s_init_max_tim_template(priv)) + goto failed; + + if (OK != acx_s_init_max_probe_response_template(priv)) + goto failed; + + /* the other templates will be set later (acx_start) */ + /* + if (OK != acx_s_set_tim_template(priv)) + goto failed;*/ + + result = OK; + goto success; + +failed: + printk("%s: acx111_init_packet_templates() FAILED\n", priv->netdev->name); + +success: + FN_EXIT1(result); + return result; +} + + +/*********************************************************************** +*/ +static int +acx100_s_set_probe_request_template(wlandevice_t *priv) +{ + struct acx100_template_probereq probereq; + char *p; + int res; + int frame_len; + + FN_ENTER; + + memset(&probereq, 0, sizeof(probereq)); + + probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi; + MAC_BCAST(probereq.da); + MAC_COPY(probereq.sa, priv->dev_addr); + MAC_BCAST(probereq.bssid); + + probereq.beacon_interval = cpu_to_le16(priv->beacon_interval); + acx_update_capabilities(priv); + probereq.cap = cpu_to_le16(priv->capabilities); + + p = probereq.variable; + acxlog(L_ASSOC, "SSID='%s' len=%d\n", priv->essid, priv->essid_len); + p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid); + p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported); + /* FIXME: should these be here or AFTER ds_parms? */ + p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported); + /* HUH?? who said it must be here? I've found nothing in 802.11! --vda*/ + /* p = wlan_fill_ie_ds_parms(p, priv->channel); */ + frame_len = p - (char*)&probereq; + probereq.size = frame_len - 2; + + res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len); + FN_EXIT0; + return res; +} + +static int +acx111_s_set_probe_request_template(wlandevice_t *priv) +{ + struct acx111_template_probereq probereq; + char *p; + int res; + int frame_len; + + FN_ENTER; + + memset(&probereq, 0, sizeof(probereq)); + + probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi; + MAC_BCAST(probereq.da); + MAC_COPY(probereq.sa, priv->dev_addr); + MAC_BCAST(probereq.bssid); + + p = probereq.variable; + p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid); + p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported); + p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported); + frame_len = p - (char*)&probereq; + probereq.size = frame_len - 2; + + res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len); + FN_EXIT0; + return res; +} + +static int +acx_s_set_probe_request_template(wlandevice_t *priv) +{ + if (IS_ACX111(priv)) { + return acx111_s_set_probe_request_template(priv); + } else { + return acx100_s_set_probe_request_template(priv); + } +} + + +/*********************************************************************** +** acx_s_update_card_settings +** +** Applies accumulated changes in various priv->xxxx members +** Called by ioctl commit handler, acx_start, acx_set_defaults, +** acx_s_after_interrupt_task (if IRQ_CMD_UPDATE_CARD_CFG), +*/ +static void +acx111_s_sens_radio_16_17(wlandevice_t *priv) +{ + u32 feature1, feature2; + + if ((priv->sensitivity < 1) || (priv->sensitivity > 3)) { + printk("%s: invalid sensitivity setting (1..3), " + "setting to 1\n", priv->netdev->name); + priv->sensitivity = 1; + } + acx111_s_get_feature_config(priv, &feature1, &feature2); + CLEAR_BIT(feature1, FEATURE1_LOW_RX|FEATURE1_EXTRA_LOW_RX); + if (priv->sensitivity > 1) + SET_BIT(feature1, FEATURE1_LOW_RX); + if (priv->sensitivity > 2) + SET_BIT(feature1, FEATURE1_EXTRA_LOW_RX); + acx111_s_feature_set(priv, feature1, feature2); +} + +void +acx_s_update_card_settings(wlandevice_t *priv, int get_all, int set_all) +{ + unsigned long flags; + unsigned int start_scan = 0; + int i; + + FN_ENTER; + + if (get_all) + SET_BIT(priv->get_mask, GETSET_ALL); + if (set_all) + SET_BIT(priv->set_mask, GETSET_ALL); + /* Why not just set masks to 0xffffffff? We can get rid of GETSET_ALL */ + + acxlog(L_INIT, "get_mask 0x%08X, set_mask 0x%08X\n", + priv->get_mask, priv->set_mask); + + /* Track dependencies betweed various settings */ + + if (priv->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_WEP)) { + acxlog(L_INIT, "important setting has been changed. " + "Need to update packet templates, too\n"); + SET_BIT(priv->set_mask, SET_TEMPLATES); + } + if (priv->set_mask & (GETSET_CHANNEL|GETSET_ALL)) { + /* This will actually tune RX/TX to the channel */ + SET_BIT(priv->set_mask, GETSET_RX|GETSET_TX); + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_3_AP: + /* Beacons contain channel# - update them */ + SET_BIT(priv->set_mask, SET_TEMPLATES); + } + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + start_scan = 1; + } + } + + /* Apply settings */ + +#ifdef WHY_SHOULD_WE_BOTHER /* imagine we were just powered off */ + /* send a disassoc request in case it's required */ + if (priv->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_CHANNEL|GETSET_WEP|GETSET_ALL)) { + if (ACX_MODE_2_STA == priv->mode) { + if (ACX_STATUS_4_ASSOCIATED == priv->status) { + acxlog(L_ASSOC, "we were ASSOCIATED - " + "sending disassoc request\n"); + acx_lock(priv, flags); + acx_l_transmit_disassoc(priv, NULL); + /* FIXME: deauth? */ + acx_unlock(priv, flags); + } + /* need to reset some other stuff as well */ + acxlog(L_DEBUG, "resetting bssid\n"); + MAC_ZERO(priv->bssid); + SET_BIT(priv->set_mask, SET_TEMPLATES|SET_STA_LIST); + /* FIXME: should start scanning */ + start_scan = 1; + } + } +#endif + + if (priv->get_mask & (GETSET_STATION_ID|GETSET_ALL)) { + u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN]; + const u8 *paddr; + + acx_s_interrogate(priv, &stationID, ACX1xx_IE_DOT11_STATION_ID); + paddr = &stationID[4]; + for (i = 0; i < ETH_ALEN; i++) { + /* we copy the MAC address (reversed in + * the card) to the netdevice's MAC + * address, and on ifup it will be + * copied into iwpriv->dev_addr */ + priv->netdev->dev_addr[ETH_ALEN - 1 - i] = paddr[i]; + } + CLEAR_BIT(priv->get_mask, GETSET_STATION_ID); + } + + if (priv->get_mask & (GETSET_SENSITIVITY|GETSET_ALL)) { + if ((RADIO_RFMD_11 == priv->radio_type) + || (RADIO_MAXIM_0D == priv->radio_type) + || (RADIO_RALINK_15 == priv->radio_type)) { + acx_s_read_phy_reg(priv, 0x30, &priv->sensitivity); + } else { + acxlog(L_INIT, "don't know how to get sensitivity " + "for radio type 0x%02X\n", priv->radio_type); + priv->sensitivity = 0; + } + acxlog(L_INIT, "got sensitivity value %u\n", priv->sensitivity); + + CLEAR_BIT(priv->get_mask, GETSET_SENSITIVITY); + } + + if (priv->get_mask & (GETSET_ANTENNA|GETSET_ALL)) { + u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN]; + + memset(antenna, 0, sizeof(antenna)); + acx_s_interrogate(priv, antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA); + priv->antenna = antenna[4]; + acxlog(L_INIT, "got antenna value 0x%02X\n", priv->antenna); + CLEAR_BIT(priv->get_mask, GETSET_ANTENNA); + } + + if (priv->get_mask & (GETSET_ED_THRESH|GETSET_ALL)) { + if (IS_ACX100(priv)) { + u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN]; + + memset(ed_threshold, 0, sizeof(ed_threshold)); + acx_s_interrogate(priv, ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD); + priv->ed_threshold = ed_threshold[4]; + } else { + acxlog(L_INIT, "acx111 doesn't support ED\n"); + priv->ed_threshold = 0; + } + acxlog(L_INIT, "got Energy Detect (ED) threshold %u\n", priv->ed_threshold); + CLEAR_BIT(priv->get_mask, GETSET_ED_THRESH); + } + + if (priv->get_mask & (GETSET_CCA|GETSET_ALL)) { + if (IS_ACX100(priv)) { + u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN]; + + memset(cca, 0, sizeof(priv->cca)); + acx_s_interrogate(priv, cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE); + priv->cca = cca[4]; + } else { + acxlog(L_INIT, "acx111 doesn't support CCA\n"); + priv->cca = 0; + } + acxlog(L_INIT, "got Channel Clear Assessment (CCA) value %u\n", priv->cca); + CLEAR_BIT(priv->get_mask, GETSET_CCA); + } + + if (priv->get_mask & (GETSET_REG_DOMAIN|GETSET_ALL)) { + acx_ie_generic_t dom; + + acx_s_interrogate(priv, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN); + priv->reg_dom_id = dom.m.bytes[0]; + /* FIXME: should also set chanmask somehow */ + acxlog(L_INIT, "got regulatory domain 0x%02X\n", priv->reg_dom_id); + CLEAR_BIT(priv->get_mask, GETSET_REG_DOMAIN); + } + + if (priv->set_mask & (GETSET_STATION_ID|GETSET_ALL)) { + u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN]; + u8 *paddr; + + paddr = &stationID[4]; + for (i = 0; i < ETH_ALEN; i++) { + /* copy the MAC address we obtained when we noticed + * that the ethernet iface's MAC changed + * to the card (reversed in + * the card!) */ + paddr[i] = priv->dev_addr[ETH_ALEN - 1 - i]; + } + acx_s_configure(priv, &stationID, ACX1xx_IE_DOT11_STATION_ID); + CLEAR_BIT(priv->set_mask, GETSET_STATION_ID); + } + + if (priv->set_mask & (SET_TEMPLATES|GETSET_ALL)) { + acxlog(L_INIT, "updating packet templates\n"); + /* Doesn't work for acx100, do it only for acx111 for now */ + if (IS_ACX111(priv)) { + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + acx_s_set_probe_request_template(priv); + } + } + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_3_AP: + /* FIXME: why only for AP? STA need probe req templates... */ + acx_s_set_beacon_template(priv); + acx_s_set_tim_template(priv); + /* BTW acx111 firmware would not send probe responses + ** if probe request does not have all basic rates flagged + ** by 0x80! Thus firmware does not conform to 802.11, + ** it should ignore 0x80 bit in ratevector from STA. + ** We can 'fix' it by not using this template and + ** sending probe responses by hand. TODO --vda */ + acx_s_set_probe_response_template(priv); + } + /* Needed if generated frames are to be emitted at different tx rate now */ + acxlog(L_IRQ, "redoing cmd_join_bssid() after template cfg\n"); + acx_s_cmd_join_bssid(priv, priv->bssid); + CLEAR_BIT(priv->set_mask, SET_TEMPLATES); + } + if (priv->set_mask & (SET_STA_LIST|GETSET_ALL)) { + acx_lock(priv, flags); + acx_l_sta_list_init(priv); + CLEAR_BIT(priv->set_mask, SET_STA_LIST); + acx_unlock(priv, flags); + } + if (priv->set_mask & (SET_RATE_FALLBACK|GETSET_ALL)) { + u8 rate[4 + ACX1xx_IE_RATE_FALLBACK_LEN]; + + /* configure to not do fallbacks when not in auto rate mode */ + rate[4] = (priv->rate_auto) ? /* priv->txrate_fallback_retries */ 1 : 0; + acxlog(L_INIT, "updating Tx fallback to %u retries\n", rate[4]); + acx_s_configure(priv, &rate, ACX1xx_IE_RATE_FALLBACK); + CLEAR_BIT(priv->set_mask, SET_RATE_FALLBACK); + } + if (priv->set_mask & (GETSET_TXPOWER|GETSET_ALL)) { + acxlog(L_INIT, "updating transmit power: %u dBm\n", + priv->tx_level_dbm); + acx_s_set_tx_level(priv, priv->tx_level_dbm); + CLEAR_BIT(priv->set_mask, GETSET_TXPOWER); + } + + if (priv->set_mask & (GETSET_SENSITIVITY|GETSET_ALL)) { + acxlog(L_INIT, "updating sensitivity value: %u\n", + priv->sensitivity); + switch (priv->radio_type) { + case RADIO_RFMD_11: + case RADIO_MAXIM_0D: + case RADIO_RALINK_15: + acx_s_write_phy_reg(priv, 0x30, priv->sensitivity); + break; + case RADIO_RADIA_16: + case RADIO_UNKNOWN_17: + acx111_s_sens_radio_16_17(priv); + break; + default: + acxlog(L_INIT, "don't know how to modify sensitivity " + "for radio type 0x%02X\n", priv->radio_type); + } + CLEAR_BIT(priv->set_mask, GETSET_SENSITIVITY); + } + + if (priv->set_mask & (GETSET_ANTENNA|GETSET_ALL)) { + /* antenna */ + u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN]; + + memset(antenna, 0, sizeof(antenna)); + antenna[4] = priv->antenna; + acxlog(L_INIT, "updating antenna value: 0x%02X\n", + priv->antenna); + acx_s_configure(priv, &antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA); + CLEAR_BIT(priv->set_mask, GETSET_ANTENNA); + } + + if (priv->set_mask & (GETSET_ED_THRESH|GETSET_ALL)) { + /* ed_threshold */ + acxlog(L_INIT, "updating Energy Detect (ED) threshold: %u\n", + priv->ed_threshold); + if (IS_ACX100(priv)) { + u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN]; + + memset(ed_threshold, 0, sizeof(ed_threshold)); + ed_threshold[4] = priv->ed_threshold; + acx_s_configure(priv, &ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD); + } + else + acxlog(L_INIT, "acx111 doesn't support ED!\n"); + CLEAR_BIT(priv->set_mask, GETSET_ED_THRESH); + } + + if (priv->set_mask & (GETSET_CCA|GETSET_ALL)) { + /* CCA value */ + acxlog(L_INIT, "updating Channel Clear Assessment " + "(CCA) value: 0x%02X\n", priv->cca); + if (IS_ACX100(priv)) { + u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN]; + + memset(cca, 0, sizeof(cca)); + cca[4] = priv->cca; + acx_s_configure(priv, &cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE); + } + else + acxlog(L_INIT, "acx111 doesn't support CCA!\n"); + CLEAR_BIT(priv->set_mask, GETSET_CCA); + } + + if (priv->set_mask & (GETSET_LED_POWER|GETSET_ALL)) { + /* Enable Tx */ + acxlog(L_INIT, "updating power LED status: %u\n", priv->led_power); + + acx_lock(priv, flags); + if (IS_PCI(priv)) + acxpci_l_power_led(priv, priv->led_power); + CLEAR_BIT(priv->set_mask, GETSET_LED_POWER); + acx_unlock(priv, flags); + } + +/* this seems to cause Tx lockup after some random time (Tx error 0x20), + * so let's disable it for now until further investigation */ +/* Maybe fixed now after locking is fixed. Need to retest */ +#ifdef POWER_SAVE_80211 + if (priv->set_mask & (GETSET_POWER_80211|GETSET_ALL)) { + acx100_ie_powermgmt_t pm; + + /* change 802.11 power save mode settings */ + acxlog(L_INIT, "updating 802.11 power save mode settings: " + "wakeup_cfg 0x%02X, listen interval %u, " + "options 0x%02X, hangover period %u, " + "enhanced_ps_transition_time %d\n", + priv->ps_wakeup_cfg, priv->ps_listen_interval, + priv->ps_options, priv->ps_hangover_period, + priv->ps_enhanced_transition_time); + acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT); + acxlog(L_INIT, "Previous PS mode settings: wakeup_cfg 0x%02X, " + "listen interval %u, options 0x%02X, " + "hangover period %u, " + "enhanced_ps_transition_time %d\n", + pm.wakeup_cfg, pm.listen_interval, pm.options, + pm.hangover_period, pm.enhanced_ps_transition_time); + pm.wakeup_cfg = priv->ps_wakeup_cfg; + pm.listen_interval = priv->ps_listen_interval; + pm.options = priv->ps_options; + pm.hangover_period = priv->ps_hangover_period; + pm.enhanced_ps_transition_time = cpu_to_le16(priv->ps_enhanced_transition_time); + acx_s_configure(priv, &pm, ACX100_IE_POWER_MGMT); + acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT); + acxlog(L_INIT, "wakeup_cfg: 0x%02X\n", pm.wakeup_cfg); + acx_s_msleep(40); + acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT); + acxlog(L_INIT, "power save mode change %s\n", + (pm.wakeup_cfg & PS_CFG_PENDING) ? "FAILED" : "was successful"); + /* FIXME: maybe verify via PS_CFG_PENDING bit here + * that power save mode change was successful. */ + /* FIXME: we shouldn't trigger a scan immediately after + * fiddling with power save mode (since the firmware is sending + * a NULL frame then). Does this need locking?? */ + CLEAR_BIT(priv->set_mask, GETSET_POWER_80211); + } +#endif + + if (priv->set_mask & (GETSET_CHANNEL|GETSET_ALL)) { + /* channel */ + acxlog(L_INIT, "updating channel to: %u\n", priv->channel); + CLEAR_BIT(priv->set_mask, GETSET_CHANNEL); + } + + if (priv->set_mask & (GETSET_TX|GETSET_ALL)) { + /* set Tx */ + acxlog(L_INIT, "updating: %s Tx\n", + priv->tx_disabled ? "disable" : "enable"); + if (priv->tx_disabled) + acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0); + /* ^ */ + /* FIXME: this used to be 1, but since we don't transfer a parameter... */ + else + acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_TX, &(priv->channel), 1); + CLEAR_BIT(priv->set_mask, GETSET_TX); + } + + if (priv->set_mask & (GETSET_RX|GETSET_ALL)) { + /* Enable Rx */ + acxlog(L_INIT, "updating: enable Rx on channel: %u\n", + priv->channel); + acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_RX, &(priv->channel), 1); + CLEAR_BIT(priv->set_mask, GETSET_RX); + } + + if (priv->set_mask & (GETSET_RETRY|GETSET_ALL)) { + u8 short_retry[4 + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN]; + u8 long_retry[4 + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN]; + + acxlog(L_INIT, "updating short retry limit: %u, long retry limit: %u\n", + priv->short_retry, priv->long_retry); + short_retry[0x4] = priv->short_retry; + long_retry[0x4] = priv->long_retry; + acx_s_configure(priv, &short_retry, ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT); + acx_s_configure(priv, &long_retry, ACX1xx_IE_DOT11_LONG_RETRY_LIMIT); + CLEAR_BIT(priv->set_mask, GETSET_RETRY); + } + + if (priv->set_mask & (SET_MSDU_LIFETIME|GETSET_ALL)) { + u8 xmt_msdu_lifetime[4 + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN]; + + acxlog(L_INIT, "updating tx MSDU lifetime: %u\n", + priv->msdu_lifetime); + *(u32 *)&xmt_msdu_lifetime[4] = cpu_to_le32((u32)priv->msdu_lifetime); + acx_s_configure(priv, &xmt_msdu_lifetime, ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME); + CLEAR_BIT(priv->set_mask, SET_MSDU_LIFETIME); + } + + if (priv->set_mask & (GETSET_REG_DOMAIN|GETSET_ALL)) { + /* reg_domain */ + acx_ie_generic_t dom; + unsigned mask; + + acxlog(L_INIT, "updating regulatory domain: 0x%02X\n", + priv->reg_dom_id); + for (i = 0; i < sizeof(reg_domain_ids); i++) + if (reg_domain_ids[i] == priv->reg_dom_id) + break; + + if (sizeof(reg_domain_ids) == i) { + acxlog(L_INIT, "Invalid or unsupported regulatory " + "domain 0x%02X specified, falling back to " + "FCC (USA)! Please report if this sounds " + "fishy!\n", priv->reg_dom_id); + i = 0; + priv->reg_dom_id = reg_domain_ids[i]; + } + + priv->reg_dom_chanmask = reg_domain_channel_masks[i]; + dom.m.bytes[0] = priv->reg_dom_id; + acx_s_configure(priv, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN); + + mask = (1 << (priv->channel - 1)); + if (!(priv->reg_dom_chanmask & mask)) { + /* hmm, need to adjust our channel to reside within domain */ + mask = 1; + for (i = 1; i <= 14; i++) { + if (priv->reg_dom_chanmask & mask) { + printk("%s: adjusting " + "selected channel from %d " + "to %d due to new regulatory " + "domain\n", priv->netdev->name, + priv->channel, i); + priv->channel = i; + break; + } + mask <<= 1; + } + } + CLEAR_BIT(priv->set_mask, GETSET_REG_DOMAIN); + } + + if (priv->set_mask & (GETSET_MODE|GETSET_ALL)) { + priv->netdev->type = ARPHRD_ETHER; + + switch (priv->mode) { + case ACX_MODE_3_AP: + + acx_lock(priv, flags); + acx_l_sta_list_init(priv); + priv->aid = 0; + priv->ap_client = NULL; + MAC_COPY(priv->bssid, priv->dev_addr); + /* this basically says "we're connected" */ + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + acx_unlock(priv, flags); + + acx111_s_feature_off(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); + /* start sending beacons */ + acx_s_cmd_join_bssid(priv, priv->bssid); + break; + case ACX_MODE_MONITOR: + /* priv->netdev->type = ARPHRD_ETHER; */ + /* priv->netdev->type = ARPHRD_IEEE80211; */ + priv->netdev->type = ARPHRD_IEEE80211_PRISM; + acx111_s_feature_on(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); + /* this stops beacons */ + acx_s_cmd_join_bssid(priv, priv->bssid); + /* this basically says "we're connected" */ + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + SET_BIT(priv->set_mask, SET_RXCONFIG|SET_WEP_OPTIONS); + break; + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + acx111_s_feature_off(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); + priv->aid = 0; + priv->ap_client = NULL; + /* we want to start looking for peer or AP */ + start_scan = 1; + break; + case ACX_MODE_OFF: + /* TODO: disable RX/TX, stop any scanning activity etc: */ + /* priv->tx_disabled = 1; */ + /* SET_BIT(priv->set_mask, GETSET_RX|GETSET_TX); */ + + /* This stops beacons (invalid macmode...) */ + acx_s_cmd_join_bssid(priv, priv->bssid); + acx_set_status(priv, ACX_STATUS_0_STOPPED); + break; + } + CLEAR_BIT(priv->set_mask, GETSET_MODE); + } + + if (priv->set_mask & (SET_RXCONFIG|GETSET_ALL)) { + acx_s_initialize_rx_config(priv); + CLEAR_BIT(priv->set_mask, SET_RXCONFIG); + } + + if (priv->set_mask & (GETSET_RESCAN|GETSET_ALL)) { + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + start_scan = 1; + break; + } + CLEAR_BIT(priv->set_mask, GETSET_RESCAN); + } + + if (priv->set_mask & (GETSET_WEP|GETSET_ALL)) { + /* encode */ + + ie_dot11WEPDefaultKeyID_t dkey; +#ifdef DEBUG_WEP + struct { + u16 type ACX_PACKED; + u16 len ACX_PACKED; + u8 val ACX_PACKED; + } keyindic; +#endif + acxlog(L_INIT, "updating WEP key settings\n"); + + acx_s_set_wepkey(priv); + + dkey.KeyID = priv->wep_current_index; + acxlog(L_INIT, "setting WEP key %u as default\n", dkey.KeyID); + acx_s_configure(priv, &dkey, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); +#ifdef DEBUG_WEP + keyindic.val = 3; + acx_s_configure(priv, &keyindic, ACX111_IE_KEY_CHOOSE); +#endif + start_scan = 1; + CLEAR_BIT(priv->set_mask, GETSET_WEP); + } + + if (priv->set_mask & (SET_WEP_OPTIONS|GETSET_ALL)) { + acx100_ie_wep_options_t options; + + if (IS_ACX111(priv)) { + acxlog(L_DEBUG, "setting WEP Options for acx111 is not supported\n"); + } else { + acxlog(L_INIT, "setting WEP Options\n"); + + /* let's choose maximum setting: 4 default keys, + * plus 10 other keys: */ + options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10); + /* don't decrypt default key only, + * don't override decryption: */ + options.WEPOption = 0; + if (priv->mode == ACX_MODE_MONITOR) { + /* don't decrypt default key only, + * override decryption mechanism: */ + options.WEPOption = 2; + } + + acx_s_configure(priv, &options, ACX100_IE_WEP_OPTIONS); + } + CLEAR_BIT(priv->set_mask, SET_WEP_OPTIONS); + } + + /* Rescan was requested */ + if (start_scan) { + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + /* We can avoid clearing list if join code + ** will be a bit more clever about not picking + ** 'bad' AP over and over again */ + acx_lock(priv, flags); + priv->ap_client = NULL; + acx_l_sta_list_init(priv); + acx_set_status(priv, ACX_STATUS_1_SCANNING); + acx_unlock(priv, flags); + + acx_s_cmd_start_scan(priv); + } + } + + /* debug, rate, and nick don't need any handling */ + /* what about sniffing mode?? */ + + acxlog(L_INIT, "get_mask 0x%08X, set_mask 0x%08X - after update\n", + priv->get_mask, priv->set_mask); + +/* end: */ + FN_EXIT0; +} + + +/*********************************************************************** +*/ +void +acx_s_initialize_rx_config(wlandevice_t *priv) +{ + struct { + u16 id ACX_PACKED; + u16 len ACX_PACKED; + u16 rx_cfg1 ACX_PACKED; + u16 rx_cfg2 ACX_PACKED; + } cfg; + + switch (priv->mode) { + case ACX_MODE_OFF: + priv->rx_config_1 = (u16) (0 + /* | RX_CFG1_INCLUDE_RXBUF_HDR */ + /* | RX_CFG1_FILTER_SSID */ + /* | RX_CFG1_FILTER_BCAST */ + /* | RX_CFG1_RCV_MC_ADDR1 */ + /* | RX_CFG1_RCV_MC_ADDR0 */ + /* | RX_CFG1_FILTER_ALL_MULTI */ + /* | RX_CFG1_FILTER_BSSID */ + /* | RX_CFG1_FILTER_MAC */ + /* | RX_CFG1_RCV_PROMISCUOUS */ + /* | RX_CFG1_INCLUDE_FCS */ + /* | RX_CFG1_INCLUDE_PHY_HDR */ + ); + priv->rx_config_2 = (u16) (0 + /*| RX_CFG2_RCV_ASSOC_REQ */ + /*| RX_CFG2_RCV_AUTH_FRAMES */ + /*| RX_CFG2_RCV_BEACON_FRAMES */ + /*| RX_CFG2_RCV_CONTENTION_FREE */ + /*| RX_CFG2_RCV_CTRL_FRAMES */ + /*| RX_CFG2_RCV_DATA_FRAMES */ + /*| RX_CFG2_RCV_BROKEN_FRAMES */ + /*| RX_CFG2_RCV_MGMT_FRAMES */ + /*| RX_CFG2_RCV_PROBE_REQ */ + /*| RX_CFG2_RCV_PROBE_RESP */ + /*| RX_CFG2_RCV_ACK_FRAMES */ + /*| RX_CFG2_RCV_OTHER */ + ); + break; + case ACX_MODE_MONITOR: + priv->rx_config_1 = (u16) (0 + /* | RX_CFG1_INCLUDE_RXBUF_HDR */ + /* | RX_CFG1_FILTER_SSID */ + /* | RX_CFG1_FILTER_BCAST */ + /* | RX_CFG1_RCV_MC_ADDR1 */ + /* | RX_CFG1_RCV_MC_ADDR0 */ + /* | RX_CFG1_FILTER_ALL_MULTI */ + /* | RX_CFG1_FILTER_BSSID */ + /* | RX_CFG1_FILTER_MAC */ + | RX_CFG1_RCV_PROMISCUOUS + /* | RX_CFG1_INCLUDE_FCS */ + /* | RX_CFG1_INCLUDE_PHY_HDR */ + ); + priv->rx_config_2 = (u16) (0 + | RX_CFG2_RCV_ASSOC_REQ + | RX_CFG2_RCV_AUTH_FRAMES + | RX_CFG2_RCV_BEACON_FRAMES + | RX_CFG2_RCV_CONTENTION_FREE + | RX_CFG2_RCV_CTRL_FRAMES + | RX_CFG2_RCV_DATA_FRAMES + | RX_CFG2_RCV_BROKEN_FRAMES + | RX_CFG2_RCV_MGMT_FRAMES + | RX_CFG2_RCV_PROBE_REQ + | RX_CFG2_RCV_PROBE_RESP + | RX_CFG2_RCV_ACK_FRAMES + | RX_CFG2_RCV_OTHER + ); + break; + default: + priv->rx_config_1 = (u16) (0 + /* | RX_CFG1_INCLUDE_RXBUF_HDR */ + /* | RX_CFG1_FILTER_SSID */ + /* | RX_CFG1_FILTER_BCAST */ + /* | RX_CFG1_RCV_MC_ADDR1 */ + /* | RX_CFG1_RCV_MC_ADDR0 */ + /* | RX_CFG1_FILTER_ALL_MULTI */ + /* | RX_CFG1_FILTER_BSSID */ + | RX_CFG1_FILTER_MAC + /* | RX_CFG1_RCV_PROMISCUOUS */ + /* | RX_CFG1_INCLUDE_FCS */ + /* | RX_CFG1_INCLUDE_PHY_HDR */ + ); + priv->rx_config_2 = (u16) (0 + | RX_CFG2_RCV_ASSOC_REQ + | RX_CFG2_RCV_AUTH_FRAMES + | RX_CFG2_RCV_BEACON_FRAMES + | RX_CFG2_RCV_CONTENTION_FREE + | RX_CFG2_RCV_CTRL_FRAMES + | RX_CFG2_RCV_DATA_FRAMES + /*| RX_CFG2_RCV_BROKEN_FRAMES */ + | RX_CFG2_RCV_MGMT_FRAMES + | RX_CFG2_RCV_PROBE_REQ + | RX_CFG2_RCV_PROBE_RESP + /*| RX_CFG2_RCV_ACK_FRAMES */ + | RX_CFG2_RCV_OTHER + ); + break; + } +#ifdef DEBUG_WEP + if (IS_ACX100(priv)) + /* only ACX100 supports that */ +#endif + priv->rx_config_1 |= RX_CFG1_INCLUDE_RXBUF_HDR; + + acxlog(L_INIT, "setting RXconfig to %04X:%04X\n", + priv->rx_config_1, priv->rx_config_2); + cfg.rx_cfg1 = cpu_to_le16(priv->rx_config_1); + cfg.rx_cfg2 = cpu_to_le16(priv->rx_config_2); + acx_s_configure(priv, &cfg, ACX1xx_IE_RXCONFIG); +} + + +/*********************************************************************** +** acx_e_after_interrupt_task +*/ +static int +acx_s_recalib_radio(wlandevice_t *priv) +{ + if (IS_ACX111(priv)) { + acx111_cmd_radiocalib_t cal; + + printk("%s: recalibrating radio\n", priv->netdev->name); + /* automatic recalibration, choose all methods: */ + cal.methods = cpu_to_le32(0x8000000f); + /* automatic recalibration every 60 seconds (value in TUs) + * FIXME: what is the firmware default here?? */ + cal.interval = cpu_to_le32(58594); + return acx_s_issue_cmd_timeo(priv, ACX111_CMD_RADIOCALIB, + &cal, sizeof(cal), CMD_TIMEOUT_MS(100)); + } else { + if (/* (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0)) && + (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0)) && */ + (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_TX, &(priv->channel), 1)) && + (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_RX, &(priv->channel), 1)) ) + return OK; + return NOT_OK; + } +} + +static void +acx_s_after_interrupt_recalib(wlandevice_t *priv) +{ + int res; + + /* this helps with ACX100 at least; + * hopefully ACX111 also does a + * recalibration here */ + + /* clear flag beforehand, since we want to make sure + * it's cleared; then only set it again on specific circumstances */ + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); + + /* better wait a bit between recalibrations to + * prevent overheating due to torturing the card + * into working too long despite high temperature + * (just a safety measure) */ + if (priv->recalib_time_last_success + && time_before(jiffies, priv->recalib_time_last_success + + RECALIB_PAUSE * 60 * HZ)) { + priv->recalib_msg_ratelimit++; + if (priv->recalib_msg_ratelimit <= 5) + printk("%s: less than " STRING(RECALIB_PAUSE) + " minutes since last radio recalibration, " + "not recalibrating (maybe card is too hot?)\n", + priv->netdev->name); + if (priv->recalib_msg_ratelimit == 5) + printk("disabling above message\n"); + return; + } + + priv->recalib_msg_ratelimit = 0; + + /* note that commands sometimes fail (card busy), + * so only clear flag if we were fully successful */ + res = acx_s_recalib_radio(priv); + if (res == OK) { + printk("%s: successfully recalibrated radio\n", + priv->netdev->name); + priv->recalib_time_last_success = jiffies; + priv->recalib_failure_count = 0; + } else { + /* failed: resubmit, but only limited + * amount of times within some time range + * to prevent endless loop */ + + priv->recalib_time_last_success = 0; /* we failed */ + + /* if some time passed between last + * attempts, then reset failure retry counter + * to be able to do next recalib attempt */ + if (time_after(jiffies, priv->recalib_time_last_attempt + HZ)) + priv->recalib_failure_count = 0; + + if (++priv->recalib_failure_count <= 5) { + priv->recalib_time_last_attempt = jiffies; + acx_schedule_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); + } + } +} + +static void +acx_e_after_interrupt_task(void *data) +{ + netdevice_t *dev = (netdevice_t *) data; + wlandevice_t *priv = netdev_priv(dev); + + FN_ENTER; + + acx_sem_lock(priv); + + if (!priv->after_interrupt_jobs) + goto end; /* no jobs to do */ + +#if TX_CLEANUP_IN_SOFTIRQ + /* can happen only on PCI */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_TX_CLEANUP) { + acx_lock(priv, flags); + acxpci_l_clean_txdesc(priv); + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_TX_CLEANUP); + acx_unlock(priv, flags); + } +#endif + /* we see lotsa tx errors */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_RADIO_RECALIB) { + acx_s_after_interrupt_recalib(priv); + } + + /* a poor interrupt code wanted to do update_card_settings() */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_UPDATE_CARD_CFG) { + if (ACX_STATE_IFACE_UP & priv->dev_state_mask) + acx_s_update_card_settings(priv, 0, 0); + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_UPDATE_CARD_CFG); + } + + /* 1) we detected that no Scan_Complete IRQ came from fw, or + ** 2) we found too many STAs */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_STOP_SCAN) { + acxlog(L_IRQ, "sending a stop scan cmd...\n"); + acx_s_issue_cmd(priv, ACX1xx_CMD_STOP_SCAN, NULL, 0); + /* HACK: set the IRQ bit, since we won't get a + * scan complete IRQ any more on ACX111 (works on ACX100!), + * since _we_, not a fw, have stopped the scan */ + SET_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE); + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_STOP_SCAN); + } + + /* either fw sent Scan_Complete or we detected that + ** no Scan_Complete IRQ came from fw. Finish scanning, + ** pick join partner if any */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_COMPLETE_SCAN) { + if (priv->status == ACX_STATUS_1_SCANNING) { + if (OK != acx_s_complete_scan(priv)) { + SET_BIT(priv->after_interrupt_jobs, + ACX_AFTER_IRQ_RESTART_SCAN); + } + } else { + /* + scan kills current join status - restore it + ** (do we need it for STA?) */ + /* + does it happen only with active scans? + ** active and passive scans? ALL scans including + ** background one? */ + /* + was not verified that everything is restored + ** (but at least we start to emit beacons again) */ + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_3_AP: + acxlog(L_IRQ, "redoing cmd_join_bssid() after scan\n"); + acx_s_cmd_join_bssid(priv, priv->bssid); + } + } + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_COMPLETE_SCAN); + } + + /* STA auth or assoc timed out, start over again */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_RESTART_SCAN) { + acxlog(L_IRQ, "sending a start_scan cmd...\n"); + acx_s_cmd_start_scan(priv); + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_RESTART_SCAN); + } + + /* whee, we got positive assoc response! 8) */ + if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_ASSOCIATE) { + acx_ie_generic_t pdr; + /* tiny race window exists, checking that we still a STA */ + switch (priv->mode) { + case ACX_MODE_2_STA: + pdr.m.aid = cpu_to_le16(priv->aid); + acx_s_configure(priv, &pdr, ACX1xx_IE_ASSOC_ID); + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); + acxlog(L_ASSOC|L_DEBUG, "ASSOCIATED!\n"); + CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_ASSOCIATE); + } + } +end: + acx_sem_unlock(priv); + FN_EXIT0; +} + + +/*********************************************************************** +** acx_schedule_task +** +** Schedule the call of the after_interrupt method after leaving +** the interrupt context. +*/ +void +acx_schedule_task(wlandevice_t *priv, unsigned int set_flag) +{ + SET_BIT(priv->after_interrupt_jobs, set_flag); + SCHEDULE_WORK(&priv->after_interrupt_task); +} + + +/*********************************************************************** +*/ +void +acx_init_task_scheduler(wlandevice_t *priv) +{ + /* configure task scheduler */ + INIT_WORK(&priv->after_interrupt_task, acx_e_after_interrupt_task, + priv->netdev); +} + + +/*********************************************************************** +** acx_s_start +*/ +void +acx_s_start(wlandevice_t *priv) +{ + FN_ENTER; + + /* + * Ok, now we do everything that can possibly be done with ioctl + * calls to make sure that when it was called before the card + * was up we get the changes asked for + */ + + SET_BIT(priv->set_mask, SET_TEMPLATES|SET_STA_LIST|GETSET_WEP + |GETSET_TXPOWER|GETSET_ANTENNA|GETSET_ED_THRESH|GETSET_CCA + |GETSET_REG_DOMAIN|GETSET_MODE|GETSET_CHANNEL + |GETSET_TX|GETSET_RX); + + acxlog(L_INIT, "updating initial settings on iface activation...\n"); + acx_s_update_card_settings(priv, 0, 0); + + FN_EXIT0; +} + + +/*********************************************************************** +** acx_update_capabilities +*/ +void +acx_update_capabilities(wlandevice_t *priv) +{ + u16 cap = 0; + + switch (priv->mode) { + case ACX_MODE_3_AP: + SET_BIT(cap, WF_MGMT_CAP_ESS); break; + case ACX_MODE_0_ADHOC: + SET_BIT(cap, WF_MGMT_CAP_IBSS); break; + /* other types of stations do not emit beacons */ + } + + if (priv->wep_restricted) { + SET_BIT(cap, WF_MGMT_CAP_PRIVACY); + } + if (priv->capab_short) { + SET_BIT(cap, WF_MGMT_CAP_SHORT); + } + if (priv->capab_pbcc) { + SET_BIT(cap, WF_MGMT_CAP_PBCC); + } + if (priv->capab_agility) { + SET_BIT(cap, WF_MGMT_CAP_AGILITY); + } + acxlog(L_DEBUG, "caps updated from 0x%04X to 0x%04X\n", + priv->capabilities, cap); + priv->capabilities = cap; +} + +#ifdef UNUSED +/*********************************************************************** +** FIXME: check whether this function is indeed acx111 only, +** rename ALL relevant definitions to indicate actual card scope! +*/ +void +acx111_s_read_configoption(wlandevice_t *priv) +{ + acx111_ie_configoption_t co, co2; + int i; + const u8 *pEle; + + if (OK != acx_s_interrogate(priv, &co, ACX111_IE_CONFIG_OPTIONS) ) { + return; + }; + if (!(acx_debug & L_DEBUG)) + return; + + memcpy(&co2.configoption_fixed, &co.configoption_fixed, + sizeof(co.configoption_fixed)); + + pEle = (u8 *)&co.configoption_fixed + sizeof(co.configoption_fixed) - 4; + + co2.antennas.type = pEle[0]; + co2.antennas.len = pEle[1]; + printk("AntennaID:%02X Len:%02X Data:", + co2.antennas.type, co2.antennas.len); + for (i = 0; i < pEle[1]; i++) { + co2.antennas.list[i] = pEle[i+2]; + printk("%02X ", pEle[i+2]); + } + printk("\n"); + + pEle += pEle[1] + 2; + co2.power_levels.type = pEle[0]; + co2.power_levels.len = pEle[1]; + printk("PowerLevelID:%02X Len:%02X Data:", + co2.power_levels.type, co2.power_levels.len); + for (i = 0; i < pEle[1]*2; i++) { + co2.power_levels.list[i] = pEle[i+2]; + printk("%02X ", pEle[i+2]); + } + printk("\n"); + + pEle += pEle[1]*2 + 2; + co2.data_rates.type = pEle[0]; + co2.data_rates.len = pEle[1]; + printk("DataRatesID:%02X Len:%02X Data:", + co2.data_rates.type, co2.data_rates.len); + for (i = 0; i < pEle[1]; i++) { + co2.data_rates.list[i] = pEle[i+2]; + printk("%02X ", pEle[i+2]); + } + printk("\n"); + + pEle += pEle[1] + 2; + co2.domains.type = pEle[0]; + co2.domains.len = pEle[1]; + printk("DomainID:%02X Len:%02X Data:", + co2.domains.type, co2.domains.len); + for (i = 0; i < pEle[1]; i++) { + co2.domains.list[i] = pEle[i+2]; + printk("%02X ", pEle[i+2]); + } + printk("\n"); + + pEle += pEle[1] + 2; + co2.product_id.type = pEle[0]; + co2.product_id.len = pEle[1]; + for (i = 0; i < pEle[1]; i++) { + co2.product_id.list[i] = pEle[i+2]; + } + printk("ProductID:%02X Len:%02X Data:%.*s\n", + co2.product_id.type, co2.product_id.len, + co2.product_id.len, (char *)co2.product_id.list); + + pEle += pEle[1] + 2; + co2.manufacturer.type = pEle[0]; + co2.manufacturer.len = pEle[1]; + for (i = 0; i < pEle[1]; i++) { + co2.manufacturer.list[i] = pEle[i+2]; + } + printk("ManufacturerID:%02X Len:%02X Data:%.*s\n", + co2.manufacturer.type, co2.manufacturer.len, + co2.manufacturer.len, (char *)co2.manufacturer.list); +/* + printk("EEPROM part:\n"); + for (i=0; i<58; i++) { + printk("%02X =======> 0x%02X\n", + i, (u8 *)co.configoption_fixed.NVSv[i-2]); + } +*/ +} +#endif + + +/*********************************************************************** +*/ +static int __init +acx_e_init_module(void) +{ + int r1,r2; + + acx_struct_size_check(); + + printk("acx: this driver is still EXPERIMENTAL\n" + "acx: reading README file and/or Craig's HOWTO is " + "recommended, visit http://acx100.sf.net in case " + "of further questions/discussion\n"); + +#if defined(CONFIG_ACX_PCI) + r1 = acxpci_e_init_module(); +#else + r1 = -EINVAL; +#endif +#if defined(CONFIG_ACX_USB) + r2 = acxusb_e_init_module(); +#else + r2 = -EINVAL; +#endif + if (r2 && r1) /* both failed! */ + return r2 ? r2 : r1; + /* return success if at least one succeeded */ + return 0; +} + +static void __exit +acx_e_cleanup_module(void) +{ +#if defined(CONFIG_ACX_PCI) + acxpci_e_cleanup_module(); +#endif +#if defined(CONFIG_ACX_USB) + acxusb_e_cleanup_module(); +#endif +} + +module_init(acx_e_init_module) +module_exit(acx_e_cleanup_module) diff -puN drivers/net/wireless/tiacx/conv.c~acx-update-2 drivers/net/wireless/tiacx/conv.c --- devel/drivers/net/wireless/tiacx/conv.c~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/conv.c 2005-10-17 13:06:00.000000000 -0700 @@ -43,22 +43,20 @@ #include "acx.h" -/*---------------------------------------------------------------- -* proto_is_stt -* -* Searches the 802.1h Selective Translation Table for a given -* protocol. -* -* Arguments: -* prottype protocol number (in host order) to search for. -* -* Returns: -* 1 - if the table is empty or a match is found. -* 0 - if the table is non-empty and a match is not found. -* -* Comment: -* Based largely on p80211conv.c of the linux-wlan-ng project -*----------------------------------------------------------------*/ +/*********************************************************************** +** proto_is_stt +** +** Searches the 802.1h Selective Translation Table for a given +** protocol. +** +** prottype - protocol number (in host order) to search for. +** +** Returns: +** 1 - if the table is empty or a match is found. +** 0 - if the table is non-empty and a match is not found. +** +** Based largely on p80211conv.c of the linux-wlan-ng project +*/ static inline int proto_is_stt(unsigned int proto) { @@ -122,21 +120,20 @@ oui_is_8021h(const struct wlan_snap *sna } -/*---------------------------------------------------------------- -* acx_l_ether_to_txbuf -* -* Uses the contents of the ether frame to build the elements of -* the 802.11 frame. -* -* We don't actually set up the frame header here. That's the -* MAC's job. We're only handling conversion of DIXII or 802.3+LLC -* frames to something that works with 802.11. -* -* Comment: -* Based largely on p80211conv.c of the linux-wlan-ng project -*----------------------------------------------------------------*/ +/*********************************************************************** +** acx_ether_to_txbuf +** +** Uses the contents of the ether frame to build the elements of +** the 802.11 frame. +** +** We don't actually set up the frame header here. That's the +** MAC's job. We're only handling conversion of DIXII or 802.3+LLC +** frames to something that works with 802.11. +** +** Based largely on p80211conv.c of the linux-wlan-ng project +*/ int -acx_l_ether_to_txbuf(wlandevice_t *priv, void *txbuf, const struct sk_buff *skb) +acx_ether_to_txbuf(wlandevice_t *priv, void *txbuf, const struct sk_buff *skb) { struct wlan_hdr_a3 *w_hdr; struct wlan_ethhdr *e_hdr; @@ -162,8 +159,8 @@ acx_l_ether_to_txbuf(wlandevice_t *priv, switch (priv->mode) { case ACX_MODE_MONITOR: /* NB: one day we might want to play with DESC_CTL2_FCS - ** Will need to stop doing "- WLAN_CRC_LEN" here then */ - if (skb->len >= WLAN_A4FR_MAXLEN_WEP - WLAN_CRC_LEN) { + ** Will need to stop doing "- WLAN_FCS_LEN" here then */ + if (skb->len >= WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_FCS_LEN) { printk("%s: can't tx oversized frame (%d bytes)\n", priv->netdev->name, skb->len); goto end; @@ -180,11 +177,8 @@ acx_l_ether_to_txbuf(wlandevice_t *priv, acxlog(L_DEBUG, "tx: 802.3 len: %d\n", skb->len); /* codes <= 1500 reserved for 802.3 lengths */ /* it's 802.3, pass ether payload unchanged, */ - /* trim off ethernet header and copy payload to tx_desc */ + /* trim off ethernet header and copy payload to txdesc */ header_len = WLAN_HDR_A3_LEN; - /* TODO: must be equal to skb->len - sizeof(wlan_ethhdr_t), no? */ - /* then we can do payload_len = ... after this big if() */ - payload_len = proto; } else { /* it's DIXII, time for some conversion */ /* Create 802.11 packet. Header also contains llc and snap. */ @@ -208,9 +202,9 @@ acx_l_ether_to_txbuf(wlandevice_t *priv, } else { store_oui_rfc1042(e_snap); } - /* trim off ethernet header and copy payload to tx_desc */ - payload_len = skb->len - sizeof(wlan_ethhdr_t); } + /* trim off ethernet header and copy payload to txbuf */ + payload_len = skb->len - sizeof(wlan_ethhdr_t); /* TODO: can we just let acx DMA payload from skb instead? */ memcpy((u8*)txbuf + header_len, skb->data + sizeof(wlan_ethhdr_t), payload_len); payload_len += header_len; @@ -264,17 +258,17 @@ end: } -/*---------------------------------------------------------------- -* acx_rxbuf_to_ether -* -* Uses the contents of a received 802.11 frame to build an ether -* frame. -* -* This function extracts the src and dest address from the 802.11 -* frame to use in the construction of the eth frame. -* -* Based largely on p80211conv.c of the linux-wlan-ng project -*----------------------------------------------------------------*/ +/*********************************************************************** +** acx_rxbuf_to_ether +** +** Uses the contents of a received 802.11 frame to build an ether +** frame. +** +** This function extracts the src and dest address from the 802.11 +** frame to use in the construction of the eth frame. +** +** Based largely on p80211conv.c of the linux-wlan-ng project +*/ struct sk_buff* acx_rxbuf_to_ether(wlandevice_t *priv, rxbuffer_t *rxbuf) { @@ -286,9 +280,8 @@ acx_rxbuf_to_ether(wlandevice_t *priv, r const u8 *daddr; const u8 *saddr; const u8 *e_payload; - int buflen; - int payload_length; - unsigned int payload_offset; + int buflen, payload_length; + unsigned int payload_offset, mtu; u16 fc; FN_ENTER; @@ -319,37 +312,37 @@ acx_rxbuf_to_ether(wlandevice_t *priv, r default: /* WF_FC_FROMTODSi */ payload_offset += (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); - if (unlikely(0 > payload_length)) { - acxlog(L_DEBUG, "A4 frame too short!\n"); - goto ret_null; - } daddr = w_hdr->a3; saddr = w_hdr->a4; } if ((WF_FC_ISWEPi & fc) && IS_ACX100(priv)) { /* chop off the IV+ICV WEP header and footer */ - acxlog(L_DATA | L_DEBUG, "rx: WEP packet, " + acxlog(L_DATA|L_DEBUG, "rx: WEP packet, " "chopping off IV and ICV\n"); - payload_length -= 8; - payload_offset += 4; + payload_offset += WLAN_WEP_IV_LEN; + payload_length -= WLAN_WEP_IV_LEN + WLAN_WEP_ICV_LEN; } - e_hdr = (wlan_ethhdr_t*) ((u8*) w_hdr + payload_offset); + if (unlikely(payload_length < 0)) { + printk("%s: rx frame too short, ignored\n", priv->netdev->name); + goto ret_null; + } + e_hdr = (wlan_ethhdr_t*) ((u8*) w_hdr + payload_offset); e_llc = (wlan_llc_t*) e_hdr; - e_snap = (wlan_snap_t*) (e_llc+1); - e_payload = (u8*) (e_snap+1); + e_snap = (wlan_snap_t*) (e_llc + 1); + e_payload = (u8*) (e_snap + 1); + mtu = priv->netdev->mtu; acxlog(L_DATA, "rx: payload_offset %d, payload_length %d\n", payload_offset, payload_length); - acxlog(L_XFER | L_DATA, - "rx: frame info: llc.dsap %X, llc.ssap %X, llc.ctl %X, " - "snap.oui %02X%02X%02X, snap.type %X\n", - e_llc->dsap, e_llc->ssap, - e_llc->ctl, e_snap->oui[0], - e_snap->oui[1], e_snap->oui[2], - e_snap->type); + acxlog(L_XFER|L_DATA, + "rx: frame info: llc=%02X%02X%02X " + "snap.oui=%02X%02X%02X snap.type=%04X\n", + e_llc->dsap, e_llc->ssap, e_llc->ctl, + e_snap->oui[0], e_snap->oui[1], e_snap->oui[2], + ntohs(e_snap->type)); /* Test for the various encodings */ if ((payload_length >= sizeof(wlan_ethhdr_t)) @@ -358,22 +351,19 @@ acx_rxbuf_to_ether(wlandevice_t *priv, r || (mac_is_equal(saddr, e_hdr->saddr)) ) ) { - acxlog(L_DEBUG | L_DATA, "rx: 802.3 ENCAP len: %d\n", payload_length); - /* 802.3 Encapsulated */ - /* Test for an overlength frame */ - - if (unlikely(payload_length > WLAN_MAX_ETHFRM_LEN)) { - /* A bogus length ethfrm has been encap'd. */ - /* Is someone trying an oflow attack? */ + /* 802.3 Encapsulated: */ + /* wlan frame body contains complete eth frame (header+body) */ + acxlog(L_DEBUG|L_DATA, "rx: 802.3 ENCAP len=%d\n", payload_length); + + if (unlikely(payload_length > (mtu + ETH_HLEN))) { printk("%s: rx: ENCAP frame too large (%d > %d)\n", - priv->netdev->name, payload_length, WLAN_MAX_ETHFRM_LEN); + priv->netdev->name, + payload_length, mtu + ETH_HLEN); goto ret_null; } /* allocate space and setup host buffer */ buflen = payload_length; - /* FIXME: implement skb ring buffer similar to - * xircom_tulip_cb.c? */ /* Attempt to align IP header (14 bytes eth header + 2 = 16) */ skb = dev_alloc_skb(buflen + 2); if (unlikely(!skb)) @@ -382,30 +372,29 @@ acx_rxbuf_to_ether(wlandevice_t *priv, r skb_put(skb, buflen); /* make room */ /* now copy the data from the 80211 frame */ - memcpy(skb->data, e_hdr, payload_length); /* copy the data */ + memcpy(skb->data, e_hdr, payload_length); } else if ( (payload_length >= sizeof(wlan_llc_t)+sizeof(wlan_snap_t)) && llc_is_snap(e_llc) ) { - /* it's a SNAP */ + /* wlan frame body contains: AA AA 03 ... (it's a SNAP) */ if ( !oui_is_rfc1042(e_snap) || (proto_is_stt(ieee2host16(e_snap->type)) /* && (ethconv == WLAN_ETHCONV_8021h) */)) { - acxlog(L_DEBUG | L_DATA, "rx: SNAP+RFC1042 len: %d\n", payload_length); + acxlog(L_DEBUG|L_DATA, "rx: SNAP+RFC1042 len=%d\n", payload_length); + /* wlan frame body contains: AA AA 03 !(00 00 00) ... -or- */ + /* wlan frame body contains: AA AA 03 00 00 00 0x80f3 ... */ + /* build eth hdr, type = len, copy AA AA 03... as eth body */ /* it's a SNAP + RFC1042 frame && protocol is in STT */ - /* build 802.3 + RFC1042 */ - /* Test for an overlength frame */ - if (unlikely(payload_length + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) { - /* A bogus length ethfrm has been sent. */ - /* Is someone trying an oflow attack? */ + if (unlikely(payload_length > mtu)) { printk("%s: rx: SNAP frame too large (%d > %d)\n", priv->netdev->name, - payload_length, WLAN_MAX_ETHFRM_LEN); + payload_length, mtu); goto ret_null; } /* allocate space and setup host buffer */ - buflen = payload_length + WLAN_ETHHDR_LEN; + buflen = payload_length + ETH_HLEN; skb = dev_alloc_skb(buflen + 2); if (unlikely(!skb)) goto no_skb; @@ -421,30 +410,27 @@ acx_rxbuf_to_ether(wlandevice_t *priv, r /* Now copy the data from the 80211 frame. Make room in front for the eth header, and keep the llc and snap from the 802.11 payload */ - memcpy(skb->data + WLAN_ETHHDR_LEN, - e_llc, - payload_length); + memcpy(skb->data + ETH_HLEN, + e_llc, payload_length); } else { - acxlog(L_DEBUG | L_DATA, "rx: 802.1h/RFC1042 len: %d\n", + /* wlan frame body contains: AA AA 03 00 00 00 [type] [tail] */ + /* build eth hdr, type=[type], copy [tail] as eth body */ + acxlog(L_DEBUG|L_DATA, "rx: 802.1h/RFC1042 len=%d\n", payload_length); /* it's an 802.1h frame (an RFC1042 && protocol is not in STT) */ /* build a DIXII + RFC894 */ - /* Test for an overlength frame */ - if (unlikely(payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t) + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) { - /* A bogus length ethfrm has been sent. */ - /* Is someone trying an oflow attack? */ + payload_length -= sizeof(wlan_llc_t) + sizeof(wlan_snap_t); + if (unlikely(payload_length > mtu)) { printk("%s: rx: DIXII frame too large (%d > %d)\n", priv->netdev->name, - (int)(payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t)), - WLAN_MAX_ETHFRM_LEN - WLAN_ETHHDR_LEN); + payload_length, mtu); goto ret_null; } /* allocate space and setup host buffer */ - buflen = payload_length + WLAN_ETHHDR_LEN - - sizeof(wlan_llc_t) - sizeof(wlan_snap_t); + buflen = payload_length + ETH_HLEN; skb = dev_alloc_skb(buflen + 2); if (unlikely(!skb)) goto no_skb; @@ -460,29 +446,25 @@ acx_rxbuf_to_ether(wlandevice_t *priv, r /* Now copy the data from the 80211 frame. Make room in front for the eth header, and cut off the llc and snap from the 802.11 payload */ - memcpy(skb->data + WLAN_ETHHDR_LEN, e_payload, - payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t)); + memcpy(skb->data + ETH_HLEN, + e_payload, payload_length); } } else { - acxlog(L_DEBUG | L_DATA, "rx: NON-ENCAP len: %d\n", payload_length); + acxlog(L_DEBUG|L_DATA, "rx: NON-ENCAP len=%d\n", payload_length); + /* build eth hdr, type=len, copy wlan body as eth body */ /* any NON-ENCAP */ /* it's a generic 80211+LLC or IPX 'Raw 802.3' */ - /* build an 802.3 frame */ - /* allocate space and setup hostbuf */ + /* build an 802.3 frame */ - /* Test for an overlength frame */ - if (unlikely(payload_length + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) { - /* A bogus length ethfrm has been sent. */ - /* Is someone trying an oflow attack? */ + if (unlikely(payload_length > mtu)) { printk("%s: rx: OTHER frame too large (%d > %d)\n", - priv->netdev->name, payload_length, - WLAN_MAX_ETHFRM_LEN - WLAN_ETHHDR_LEN); + priv->netdev->name, payload_length, mtu); goto ret_null; } /* allocate space and setup host buffer */ - buflen = payload_length + WLAN_ETHHDR_LEN; + buflen = payload_length + ETH_HLEN; skb = dev_alloc_skb(buflen + 2); if (unlikely(!skb)) goto no_skb; @@ -496,7 +478,7 @@ acx_rxbuf_to_ether(wlandevice_t *priv, r e_hdr->type = htons(payload_length); /* now copy the data from the 80211 frame */ - memcpy(skb->data + WLAN_ETHHDR_LEN, e_llc, payload_length); + memcpy(skb->data + ETH_HLEN, e_llc, payload_length); } skb->dev = priv->netdev; diff -L drivers/net/wireless/tiacx/helper2.c -puN drivers/net/wireless/tiacx/helper2.c~acx-update-2 /dev/null --- devel/drivers/net/wireless/tiacx/helper2.c +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,5612 +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 -** --------------------------------------------------------------------- -*/ - -#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 -char *firmware_dir; -#endif -#if SEPARATE_DRIVER_INSTANCES -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)"); - - -/*********************************************************************** -*/ - -/* minutes to wait until next radio recalibration: */ -#define RECALIB_PAUSE 5 - -const u8 reg_domain_ids[] = - { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40, 0x41, 0x51 }; -/* stupid workaround for the fact that in C the size of an external array - * cannot be determined from within a second file */ -const u8 reg_domain_ids_len = sizeof(reg_domain_ids); -const u16 reg_domain_channel_masks[] = - { 0x07ff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc }; - - -/*********************************************************************** -** Debugging support -*/ -#ifdef PARANOID_LOCKING -static unsigned max_lock_time; -static unsigned max_sem_time; - -void -acx_lock_unhold() { max_lock_time = 0; } -void -acx_sem_unhold() { max_sem_time = 0; } - -static inline const char* -sanitize_str(const char *s) -{ - const char* t = strrchr(s, '/'); - if (t) return t + 1; - return s; -} - -void -acx_lock_debug(wlandevice_t *priv, const char* where) -{ - int count = 100*1000*1000; - where = sanitize_str(where); - while (--count) { - if (!spin_is_locked(&priv->lock)) break; - cpu_relax(); - } - if (!count) { - printk(KERN_EMERG "LOCKUP: already taken at %s!\n", priv->last_lock); - BUG(); - } - priv->last_lock = where; - rdtscl(priv->lock_time); -} -void -acx_unlock_debug(wlandevice_t *priv, const char* where) -{ -#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("%08lx %s==> %s\n", - d, - spaces + (sizeof(spaces)-1) - indent, - funcname - ); - - acx_debug_func_indent += FUNC_INDENT_INCREMENT; -} -void -log_fn_exit(const char *funcname) -{ - int indent; - TIMESTAMP(d); - - acx_debug_func_indent -= FUNC_INDENT_INCREMENT; - - indent = acx_debug_func_indent; - if (indent >= sizeof(spaces)) - indent = sizeof(spaces)-1; - - printk("%08lx %s<== %s\n", - d, - spaces + (sizeof(spaces)-1) - indent, - funcname - ); -} -void -log_fn_exit_v(const char *funcname, int v) -{ - int indent; - TIMESTAMP(d); - - acx_debug_func_indent -= FUNC_INDENT_INCREMENT; - - indent = acx_debug_func_indent; - if (indent >= sizeof(spaces)) - indent = sizeof(spaces)-1; - - printk("%08lx %s<== %s: %08X\n", - d, - spaces + (sizeof(spaces)-1) - indent, - funcname, - v - ); -} -#endif /* ACX_DEBUG > 1 */ - - -/*********************************************************************** -** Basically a msleep with logging -*/ -void -acx_s_msleep(int ms) -{ - FN_ENTER; - msleep(ms); - FN_EXIT0; -} - - -/*********************************************************************** -** acx_get_packet_type_string -*/ -#if ACX_DEBUG -const char* -acx_get_packet_type_string(u16 fc) -{ - static const char * const mgmt_arr[] = { - "MGMT/AssocReq", "MGMT/AssocResp", "MGMT/ReassocReq", - "MGMT/ReassocResp", "MGMT/ProbeReq", "MGMT/ProbeResp", - "MGMT/UNKNOWN", "MGMT/UNKNOWN", "MGMT/Beacon", "MGMT/ATIM", - "MGMT/Disassoc", "MGMT/Authen", "MGMT/Deauthen" - }; - static const char * const ctl_arr[] = { - "CTL/PSPoll", "CTL/RTS", "CTL/CTS", "CTL/Ack", "CTL/CFEnd", - "CTL/CFEndCFAck" - }; - static const char * const data_arr[] = { - "DATA/DataOnly", "DATA/Data CFAck", "DATA/Data CFPoll", - "DATA/Data CFAck/CFPoll", "DATA/Null", "DATA/CFAck", - "DATA/CFPoll", "DATA/CFAck/CFPoll" - }; - const char *str = "UNKNOWN"; - u8 fstype = (WF_FC_FSTYPE & fc) >> 4; - u8 ctl; - - FN_ENTER; - switch (WF_FC_FTYPE & fc) { - case WF_FTYPE_MGMT: - str = "MGMT/UNKNOWN"; - if (fstype < VEC_SIZE(mgmt_arr)) - str = mgmt_arr[fstype]; - break; - case WF_FTYPE_CTL: - ctl = fstype - 0x0a; - str = "CTL/UNKNOWN"; - if (ctl < VEC_SIZE(ctl_arr)) - str = ctl_arr[ctl]; - break; - case WF_FTYPE_DATA: - str = "DATA/UNKNOWN"; - if (fstype < VEC_SIZE(data_arr)) - str = data_arr[fstype]; - break; - } - FN_EXIT0; - return str; -} -#endif - - -/*********************************************************************** -** 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"; -} - - -/*********************************************************************** -** maps acx111 tx descr rate field to acx100 one -*/ -const u8 -bitpos2rate100[] = { - RATE100_1 ,/* 0 */ - RATE100_2 ,/* 1 */ - RATE100_5 ,/* 2 */ - RATE100_2 ,/* 3, should not happen */ - RATE100_2 ,/* 4, should not happen */ - RATE100_11 ,/* 5 */ - RATE100_2 ,/* 6, should not happen */ - RATE100_2 ,/* 7, should not happen */ - RATE100_22 ,/* 8 */ - RATE100_2 ,/* 9, should not happen */ - RATE100_2 ,/* 10, should not happen */ - RATE100_2 ,/* 11, should not happen */ - RATE100_2 ,/* 12, should not happen */ - RATE100_2 ,/* 13, should not happen */ - RATE100_2 ,/* 14, should not happen */ - RATE100_2 ,/* 15, should not happen */ -}; - -u8 -acx_rate111to100(u16 r) { - return bitpos2rate100[highest_bit(r)]; -} - - -/*---------------------------------------------------------------- -* acx_l_rxmonitor -* Called from IRQ context only -*----------------------------------------------------------------*/ -static void -acx_l_rxmonitor(wlandevice_t *priv, const rxbuffer_t *rxbuf) -{ - wlansniffrm_t *msg; - struct sk_buff *skb; - void *datap; - unsigned int skb_len; - int payload_offset; - - FN_ENTER; - - /* we are in big luck: the acx100 doesn't modify any of the fields */ - /* in the 802.11 frame. just pass this packet into the PF_PACKET */ - /* subsystem. yeah. */ - payload_offset = ((u8*)acx_get_wlan_hdr(priv, rxbuf) - (u8*)rxbuf); - skb_len = RXBUF_BYTES_USED(rxbuf) - payload_offset; - - /* sanity check */ - if (skb_len > (WLAN_A4FR_MAXLEN_WEP)) { - printk("monitor mode panic: oversized frame!\n"); - goto end; - } - - if (priv->netdev->type == ARPHRD_IEEE80211_PRISM) - skb_len += sizeof(*msg); - - /* allocate skb */ - skb = dev_alloc_skb(skb_len); - if (!skb) { - printk("%s: no memory for skb (%u bytes)\n", - priv->netdev->name, skb_len); - goto end; - } - - skb_put(skb, skb_len); - - /* when in raw 802.11 mode, just copy frame as-is */ - if (priv->netdev->type == ARPHRD_IEEE80211) - datap = skb->data; - else { /* otherwise, emulate prism header */ - msg = (wlansniffrm_t*)skb->data; - datap = msg + 1; - - msg->msgcode = WLANSNIFFFRM; - msg->msglen = sizeof(*msg); - strncpy(msg->devname, priv->netdev->name, sizeof(msg->devname)-1); - msg->devname[sizeof(msg->devname)-1] = '\0'; - - msg->hosttime.did = WLANSNIFFFRM_hosttime; - msg->hosttime.status = WLANITEM_STATUS_data_ok; - msg->hosttime.len = 4; - msg->hosttime.data = jiffies; - - msg->mactime.did = WLANSNIFFFRM_mactime; - msg->mactime.status = WLANITEM_STATUS_data_ok; - msg->mactime.len = 4; - msg->mactime.data = rxbuf->time; - - msg->channel.did = WLANSNIFFFRM_channel; - msg->channel.status = WLANITEM_STATUS_data_ok; - msg->channel.len = 4; - msg->channel.data = priv->channel; - - msg->rssi.did = WLANSNIFFFRM_rssi; - msg->rssi.status = WLANITEM_STATUS_no_value; - msg->rssi.len = 4; - msg->rssi.data = 0; - - msg->sq.did = WLANSNIFFFRM_sq; - msg->sq.status = WLANITEM_STATUS_no_value; - msg->sq.len = 4; - msg->sq.data = 0; - - msg->signal.did = WLANSNIFFFRM_signal; - msg->signal.status = WLANITEM_STATUS_data_ok; - msg->signal.len = 4; - msg->signal.data = rxbuf->phy_snr; - - msg->noise.did = WLANSNIFFFRM_noise; - msg->noise.status = WLANITEM_STATUS_data_ok; - msg->noise.len = 4; - msg->noise.data = rxbuf->phy_level; - - msg->rate.did = WLANSNIFFFRM_rate; - msg->rate.status = WLANITEM_STATUS_data_ok; - msg->rate.len = 4; - msg->rate.data = rxbuf->phy_plcp_signal / 5; - - msg->istx.did = WLANSNIFFFRM_istx; - msg->istx.status = WLANITEM_STATUS_data_ok; - msg->istx.len = 4; - msg->istx.data = 0; /* tx=0: it's not a tx packet */ - - skb_len -= sizeof(*msg); - - msg->frmlen.did = WLANSNIFFFRM_signal; - msg->frmlen.status = WLANITEM_STATUS_data_ok; - msg->frmlen.len = 4; - msg->frmlen.data = skb_len; - } - - memcpy(datap, ((unsigned char*)rxbuf)+payload_offset, skb_len); - - skb->dev = priv->netdev; - skb->dev->last_rx = jiffies; - - skb->mac.raw = skb->data; - skb->ip_summed = CHECKSUM_NONE; - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = htons(ETH_P_80211_RAW); - netif_rx(skb); - - priv->stats.rx_packets++; - priv->stats.rx_bytes += skb->len; -end: - FN_EXIT0; -} - - -/*********************************************************************** -** Calculate level like the feb 2003 windows driver seems to do -*/ -u8 -acx_signal_to_winlevel(u8 rawlevel) -{ - /* u8 winlevel = (u8) (0.5 + 0.625 * rawlevel); */ - u8 winlevel = ((4 + (rawlevel * 5)) / 8); - - if (winlevel > 100) - winlevel = 100; - return winlevel; -} - -u8 -acx_signal_determine_quality(u8 signal, u8 noise) -{ - int qual; - - qual = (((signal - 30) * 100 / 70) + (100 - noise * 4)) / 2; - - if (qual > 100) - return 100; - if (qual < 0) - return 0; - return qual; -} - - -/*********************************************************************** -** acx_l_process_rxbuf -** -** NB: used by USB code also -*/ -void -acx_l_process_rxbuf(wlandevice_t *priv, rxbuffer_t *rxbuf) -{ - struct wlan_hdr *hdr; - unsigned int buf_len; - unsigned int qual; - u16 fc; - - hdr = acx_get_wlan_hdr(priv, rxbuf); - /* length of frame from control field to last byte of FCS */ - buf_len = RXBUF_BYTES_RCVD(rxbuf); - fc = le16_to_cpu(hdr->fc); - - if ( ((WF_FC_FSTYPE & fc) != WF_FSTYPE_BEACON) - || (acx_debug & L_XFER_BEACON) - ) { - acxlog(L_XFER|L_DATA, "rx: %s " - "time %u len %u signal %u SNR %u macstat %02X " - "phystat %02X phyrate %u status %u\n", - acx_get_packet_type_string(fc), - le32_to_cpu(rxbuf->time), - buf_len, - acx_signal_to_winlevel(rxbuf->phy_level), - acx_signal_to_winlevel(rxbuf->phy_snr), - rxbuf->mac_status, - rxbuf->phy_stat_baseband, - rxbuf->phy_plcp_signal, - priv->status); - } - - if (unlikely(acx_debug & L_DATA)) { - printk("rx: 802.11 buf[%u]: ", buf_len); - acx_dump_bytes(hdr, buf_len); - } - - /* FIXME: should check for Rx errors (rxbuf->mac_status? - * discard broken packets - but NOT for monitor!) - * and update Rx packet statistics here */ - - if (unlikely(priv->mode == ACX_MODE_MONITOR)) { - acx_l_rxmonitor(priv, rxbuf); - } else if (likely(buf_len >= WLAN_HDR_A3_LEN)) { - acx_l_rx_ieee802_11_frame(priv, rxbuf); - } else { - acxlog(L_DEBUG | L_XFER | L_DATA, - "rx: NOT receiving packet (%s): " - "size too small (%u)\n", - acx_get_packet_type_string(fc), - buf_len); - } - - /* Now check Rx quality level, AFTER processing packet. - * I tried to figure out how to map these levels to dBm - * values, but for the life of me I really didn't - * manage to get it. Either these values are not meant to - * be expressed in dBm, or it's some pretty complicated - * calculation. */ - -#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 -} - - -/*********************************************************************** -*/ -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]; -} - - -/*********************************************************************** -*/ -void -acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr) -{ - acxlog(L_ASSOC, "acx: unknown EID %d in mgmt frame at offset %d\n", - ie_ptr->eid, (int) ((u8*)ie_ptr - (u8*)hdr)); - if (acx_debug & (L_DATA|L_ASSOC)) { - printk("frame (%s): ", - acx_get_packet_type_string(le16_to_cpu(hdr->fc))); - acx_dump_bytes(hdr, len); - } -} - - -/*********************************************************************** -*/ -#if ACX_DEBUG -void -acx_dump_bytes(const void *data, int num) -{ - const u8* ptr = (const u8*)data; - - if (num <= 0) { - printk("\n"); - return; - } - - while (num >= 16) { - printk( "%02X %02X %02X %02X %02X %02X %02X %02X " - "%02X %02X %02X %02X %02X %02X %02X %02X\n", - ptr[0], ptr[1], ptr[2], ptr[3], - ptr[4], ptr[5], ptr[6], ptr[7], - ptr[8], ptr[9], ptr[10], ptr[11], - ptr[12], ptr[13], ptr[14], ptr[15]); - num -= 16; - ptr += 16; - } - if (num > 0) { - while (--num > 0) - printk("%02X ", *ptr++); - printk("%02X\n", *ptr); - } -} -#endif - - -/*---------------------------------------------------------------- -* acx_i_start_xmit -* -* Called by network core. Can be called outside of process context. -*----------------------------------------------------------------*/ -int -acx_i_start_xmit(struct sk_buff *skb, netdevice_t *dev) -{ - wlandevice_t *priv = acx_netdev_priv(dev); - tx_t *tx; - void *txbuf; - unsigned long flags; - int txresult = NOT_OK; - int len; - - FN_ENTER; - - if (unlikely(!skb)) { - /* indicate success */ - txresult = OK; - goto end_no_unlock; - } - if (unlikely(!priv)) { - goto end_no_unlock; - } - - 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_l_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; -} - - -/*********************************************************************** -** 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; - - /* TODO implement and check other acx111 commands */ - if (IS_ACX111(priv) && (type == ACX1xx_IE_DOT11_CURRENT_ANTENNA)) { - /* acx111 has differing struct size */ - acxlog(L_CTL, FUNC"(%s) is not supported " - "under acx111 (yet)\n", typestr); - return NOT_OK; - } - - if (type < 0x1000) - len = CtlLength[type]; - else - len = CtlLengthDot11[type - 0x1000]; - - acxlog(L_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_inquisistor(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 - -/*********************************************************************** -** 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 -*----------------------------------------------------------------*/ -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_after_interrupt_task(priv, - ACX_AFTER_IRQ_CMD_STOP_SCAN + ACX_AFTER_IRQ_COMPLETE_SCAN); - } - break; - case ACX_STATUS_2_WAIT_AUTH: - /* was set to 0 by set_status() */ - if (++priv->auth_or_assoc_retries < 10) { - acxlog(L_ASSOC, "resend authen1 request (attempt %d)\n", - priv->auth_or_assoc_retries + 1); - acx_l_transmit_authen1(priv); - } else { - /* time exceeded: fall back to scanning mode */ - acxlog(L_ASSOC, - "authen1 request reply timeout, giving up\n"); - /* we are a STA, need to find AP anyhow */ - acx_set_status(priv, ACX_STATUS_1_SCANNING); - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_RESTART_SCAN); - } - /* used to be 1500000, but some other driver uses 2.5s */ - acx_set_timer(priv, 2500000); - break; - case ACX_STATUS_3_AUTHENTICATED: - /* was set to 0 by set_status() */ - if (++priv->auth_or_assoc_retries < 10) { - acxlog(L_ASSOC, "resend assoc request (attempt %d)\n", - priv->auth_or_assoc_retries + 1); - acx_l_transmit_assoc_req(priv); - } else { - /* time exceeded: give up */ - acxlog(L_ASSOC, - "association request reply timeout, giving up\n"); - /* we are a STA, need to find AP anyhow */ - acx_set_status(priv, ACX_STATUS_1_SCANNING); - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_RESTART_SCAN); - } - acx_set_timer(priv, 2500000); /* see above */ - break; - case ACX_STATUS_4_ASSOCIATED: - default: - break; - } - - acx_unlock(priv, flags); - - FN_EXIT0; -} - - -/*------------------------------------------------------------------------------ - * acx_set_timer - * - * Sets the 802.11 state management timer's timeout. - *----------------------------------------------------------------------------*/ -void -acx_set_timer(wlandevice_t *priv, 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_rx_ieee802_11_frame - * - * Called from IRQ context only - *----------------------------------------------------------------------------*/ -int -acx_l_rx_ieee802_11_frame(wlandevice_t *priv, rxbuffer_t *rxbuf) -{ - unsigned int ftype, fstype; - const wlan_hdr_t *hdr; - int result = NOT_OK; - - FN_ENTER; - - hdr = acx_get_wlan_hdr(priv, rxbuf); - - /* see IEEE 802.11-1999.pdf chapter 7 "MAC frame formats" */ - if ((hdr->fc & WF_FC_PVERi) != 0) { - printk_ratelimited(KERN_INFO "rx: unsupported 802.11 protocol\n"); - goto end; - } - - ftype = hdr->fc & WF_FC_FTYPEi; - fstype = hdr->fc & WF_FC_FSTYPEi; - - switch (ftype) { - /* check data frames first, for speed */ - case WF_FTYPE_DATAi: - switch (fstype) { - case WF_FSTYPE_DATAONLYi: - /* FIXME: will fail if two peers send 2 streams of DUPs */ - if (priv->dup_count - && time_after(jiffies, priv->dup_msg_expiry)) { - printk(KERN_INFO "%s: rx: %d DUPs received in 10 secs\n", - priv->netdev->name, priv->dup_count); - priv->dup_count = 0; - } - if (unlikely(hdr->seq == priv->last_seq_ctrl)) { - if (!priv->dup_count++) - priv->dup_msg_expiry = jiffies + 10*HZ; - /* simply discard it and indicate error */ - priv->stats.rx_errors++; - break; - } - priv->last_seq_ctrl = hdr->seq; /* le_to_cpu? */ - - /* TODO: - if (WF_FC_FROMTODSi == (hdr->fc & WF_FC_FROMTODSi)) { - result = acx_l_process_data_frame_wds(priv, rxbuf); - break; - } - */ - - switch (priv->mode) { - case ACX_MODE_3_AP: - result = acx_l_process_data_frame_master(priv, rxbuf); - break; - case ACX_MODE_0_ADHOC: - case ACX_MODE_2_STA: - result = acx_l_process_data_frame_client(priv, rxbuf); - break; - } - case WF_FSTYPE_DATA_CFACKi: - case WF_FSTYPE_DATA_CFPOLLi: - case WF_FSTYPE_DATA_CFACK_CFPOLLi: - case WF_FSTYPE_CFPOLLi: - case WF_FSTYPE_CFACK_CFPOLLi: - /* see above. - acx_process_class_frame(priv, rxbuf, 3); */ - break; - case WF_FSTYPE_NULLi: - /* acx_l_process_NULL_frame(priv, rxbuf, 3); */ - break; - /* FIXME: same here, see above */ - case WF_FSTYPE_CFACKi: - default: - break; - } - break; - case WF_FTYPE_MGMTi: - result = acx_l_process_mgmt_frame(priv, rxbuf); - break; - case WF_FTYPE_CTLi: - if (fstype == WF_FSTYPE_PSPOLLi) - result = OK; - /* this call is irrelevant, since - * acx_process_class_frame is a stub, so return - * immediately instead. - * return acx_process_class_frame(priv, rxbuf, 3); */ - break; - default: - break; - } -end: - FN_EXIT1(result); - return result; -} - - -/*---------------------------------------------------------------- -* acx_l_transmit_assocresp -* -* We are an AP here -*----------------------------------------------------------------*/ -static const u8 -dot11ratebyte[] = { - DOT11RATEBYTE_1, - DOT11RATEBYTE_2, - DOT11RATEBYTE_5_5, - DOT11RATEBYTE_6_G, - DOT11RATEBYTE_9_G, - DOT11RATEBYTE_11, - DOT11RATEBYTE_12_G, - DOT11RATEBYTE_18_G, - DOT11RATEBYTE_22, - DOT11RATEBYTE_24_G, - DOT11RATEBYTE_36_G, - DOT11RATEBYTE_48_G, - DOT11RATEBYTE_54_G, -}; - -static int -find_pos(const u8 *p, int size, u8 v) -{ - int i; - for (i = 0; i < size; i++) - if (p[i] == v) - return i; - /* printk a message about strange byte? */ - return 0; -} - -static void -add_bits_to_ratemasks(u8* ratevec, int len, u16* brate, u16* orate) -{ - while (len--) { - int n = 1 << find_pos(dot11ratebyte, - sizeof(dot11ratebyte), *ratevec & 0x7f); - if (*ratevec & 0x80) - *brate |= n; - *orate |= n; - ratevec++; - } -} - -static int -acx_l_transmit_assocresp(wlandevice_t *priv, const wlan_fr_assocreq_t *req) -{ - struct tx *tx; - struct wlan_hdr_mgmt *head; - struct assocresp_frame_body *body; - u8 *p; - const u8 *da; - /* const u8 *sa; */ - const u8 *bssid; - client_t *clt; - - FN_ENTER; - - /* sa = req->hdr->a1; */ - da = req->hdr->a2; - bssid = req->hdr->a3; - - clt = acx_l_sta_list_get(priv, da); - if (!clt) - goto ok; - - /* Assoc without auth is a big no-no */ - /* Let's be liberal: if already assoc'ed STA sends assoc req again, - ** we won't be rude */ - if (clt->used != CLIENT_AUTHENTICATED_2 - && clt->used != CLIENT_ASSOCIATED_3) { - acx_l_transmit_deauthen(priv, da, WLAN_MGMT_REASON_CLASS2_NONAUTH); - goto bad; - } - - clt->used = CLIENT_ASSOCIATED_3; - - if (clt->aid == 0) { - clt->aid = ++priv->aid; - } - clt->cap_info = ieee2host16(*(req->cap_info)); - /* We cheat here a bit. We don't really care which rates are flagged - ** as basic by the client, so we stuff them in single ratemask */ - clt->rate_cap = 0; - if (req->supp_rates) - add_bits_to_ratemasks(req->supp_rates->rates, - req->supp_rates->len, &clt->rate_cap, &clt->rate_cap); - if (req->ext_rates) - add_bits_to_ratemasks(req->ext_rates->rates, - req->ext_rates->len, &clt->rate_cap, &clt->rate_cap); - /* We can check that client supports all basic rates, - ** and deny assoc if not. But let's be liberal, right? ;) */ - clt->rate_cfg = clt->rate_cap & priv->rate_oper; - if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(priv->rate_oper); - clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); - clt->fallback_count = clt->stepup_count = 0; - clt->ignore_count = 16; - - tx = acx_l_alloc_tx(priv); - if (!tx) - goto bad; - head = acx_l_get_txbuf(priv, tx); - if (!head) - goto bad; - body = (void*)(head + 1); - - head->fc = WF_FSTYPE_ASSOCRESPi; - head->dur = req->hdr->dur; - MAC_COPY(head->da, da); - /* MAC_COPY(head->sa, sa); */ - MAC_COPY(head->sa, priv->dev_addr); - MAC_COPY(head->bssid, bssid); - head->seq = req->hdr->seq; - - body->cap_info = host2ieee16(priv->capabilities); - body->status = host2ieee16(0); - body->aid = host2ieee16(clt->aid); - p = wlan_fill_ie_rates((u8*)&body->rates, priv->rate_supported_len, - priv->rate_supported); - p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, - priv->rate_supported); - - acx_l_tx_data(priv, tx, p - (u8*)head); -ok: - FN_EXIT1(OK); - return OK; -bad: - FN_EXIT1(NOT_OK); - return NOT_OK; -} - - -/*---------------------------------------------------------------- -* acx_l_transmit_reassocresp - -You may be wondering, just like me, what a hell is ReAuth. -In practice it was seen sent by STA when STA feels like losing connection. - -[802.11] - -5.4.2.3 Reassociation - -Association is sufficient for no-transition message delivery between -IEEE 802.11 stations. Additional functionality is needed to support -BSS-transition mobility. The additional required functionality -is provided by the reassociation service. Reassociation is a DSS. -The reassociation service is invoked to ÒmoveÓ a current association -from one AP to another. This keeps the DS informed of the current -mapping between AP and STA as the station moves from BSS to BSS within -an ESS. Reassociation also enables changing association attributes -of an established association while the STA remains associated with -the same AP. Reassociation is always initiated by the mobile STA. - -5.4.3.1 Authentication -... -A STA may be authenticated with many other STAs at any given instant. - -5.4.3.1.1 Preauthentication - -Because the authentication process could be time-consuming (depending -on the authentication protocol in use), the authentication service can -be invoked independently of the association service. Preauthentication -is typically done by a STA while it is already associated with an AP -(with which it previously authenticated). IEEE 802.11 does not require -that STAs preauthenticate with APs. However, authentication is required -before an association can be established. If the authentication is left -until reassociation time, this may impact the speed with which a STA can -reassociate between APs, limiting BSS-transition mobility performance. -The use of preauthentication takes the authentication service overhead -out of the time-critical reassociation process. - -5.7.3 Reassociation - -For a STA to reassociate, the reassociation service causes the following -message to occur: - - Reassociation request - -* Message type: Management -* Message subtype: Reassociation request -* Information items: - - IEEE address of the STA - - IEEE address of the AP with which the STA will reassociate - - IEEE address of the AP with which the STA is currently associated - - ESSID -* Direction of message: From STA to 'new' AP - -The address of the current AP is included for efficiency. The inclusion -of the current AP address facilitates MAC reassociation to be independent -of the DS implementation. - - Reassociation response -* Message type: Management -* Message subtype: Reassociation response -* Information items: - - Result of the requested reassociation. (success/failure) - - If the reassociation is successful, the response shall include the AID. -* Direction of message: From AP to STA - -7.2.3.6 Reassociation Request frame format - -The frame body of a management frame of subtype Reassociation Request -contains the information shown in Table 9. - -Table 9 Reassociation Request frame body -Order Information -1 Capability information -2 Listen interval -3 Current AP address -4 SSID -5 Supported rates - -7.2.3.7 Reassociation Response frame format - -The frame body of a management frame of subtype Reassociation Response -contains the information shown in Table 10. - -Table 10 Reassociation Response frame body -Order Information -1 Capability information -2 Status code -3 Association ID (AID) -4 Supported rates - -*----------------------------------------------------------------*/ -static int -acx_l_transmit_reassocresp(wlandevice_t *priv, const wlan_fr_reassocreq_t *req) -{ - struct tx *tx; - struct wlan_hdr_mgmt *head; - struct reassocresp_frame_body *body; - u8 *p; - const u8 *da; - /* const u8 *sa; */ - const u8 *bssid; - client_t *clt; - - FN_ENTER; - - /* sa = req->hdr->a1; */ - da = req->hdr->a2; - bssid = req->hdr->a3; - - /* Must be already authenticated, so it must be in the list */ - clt = acx_l_sta_list_get(priv, da); - if (!clt) - goto ok; - - /* Assoc without auth is a big no-no */ - /* Already assoc'ed STAs sending ReAssoc req are ok per 802.11 */ - if (clt->used != CLIENT_AUTHENTICATED_2 - && clt->used != CLIENT_ASSOCIATED_3) { - acx_l_transmit_deauthen(priv, da, WLAN_MGMT_REASON_CLASS2_NONAUTH); - goto bad; - } - - clt->used = CLIENT_ASSOCIATED_3; - if (clt->aid == 0) { - clt->aid = ++priv->aid; - } - if (req->cap_info) - clt->cap_info = ieee2host16(*(req->cap_info)); - /* We cheat here a bit. We don't really care which rates are flagged - ** as basic by the client, so we stuff them in single ratemask */ - clt->rate_cap = 0; - if (req->supp_rates) - add_bits_to_ratemasks(req->supp_rates->rates, - req->supp_rates->len, &clt->rate_cap, &clt->rate_cap); - if (req->ext_rates) - add_bits_to_ratemasks(req->ext_rates->rates, - req->ext_rates->len, &clt->rate_cap, &clt->rate_cap); - /* We can check that client supports all basic rates, - ** and deny assoc if not. But let's be liberal, right? ;) */ - clt->rate_cfg = clt->rate_cap & priv->rate_oper; - if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(priv->rate_oper); - clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); - clt->fallback_count = clt->stepup_count = 0; - clt->ignore_count = 16; - - tx = acx_l_alloc_tx(priv); - if (!tx) - goto ok; - head = acx_l_get_txbuf(priv, tx); - if (!head) - goto ok; - body = (void*)(head + 1); - - head->fc = WF_FSTYPE_REASSOCRESPi; - head->dur = req->hdr->dur; - MAC_COPY(head->da, da); - /* MAC_COPY(head->sa, sa); */ - MAC_COPY(head->sa, priv->dev_addr); - MAC_COPY(head->bssid, bssid); - head->seq = req->hdr->seq; - - /* IEs: 1. caps */ - body->cap_info = host2ieee16(priv->capabilities); - /* 2. status code */ - body->status = host2ieee16(0); - /* 3. AID */ - body->aid = host2ieee16(clt->aid); - /* 4. supp rates */ - p = wlan_fill_ie_rates((u8*)&body->rates, priv->rate_supported_len, - priv->rate_supported); - /* 5. ext supp rates */ - p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, - priv->rate_supported); - - acx_l_tx_data(priv, tx, p - (u8*)head); -ok: - FN_EXIT1(OK); - return OK; -bad: - FN_EXIT1(NOT_OK); - return NOT_OK; -} - - -/*---------------------------------------------------------------- -* acx_l_process_disassoc_from_sta -*----------------------------------------------------------------*/ -static void -acx_l_process_disassoc_from_sta(wlandevice_t *priv, const wlan_fr_disassoc_t *req) -{ - const u8 *ta; - client_t *clt; - - FN_ENTER; - - ta = req->hdr->a2; - clt = acx_l_sta_list_get(priv, ta); - if (!clt) - goto end; - - if (clt->used != CLIENT_ASSOCIATED_3 - && clt->used != CLIENT_AUTHENTICATED_2) { - /* it's disassociating, but it's - ** not even authenticated! Let it know that */ - acxlog_mac(L_ASSOC|L_XFER, "peer ", ta, "has sent disassoc " - "req but it is not even auth'ed! sending deauth\n"); - acx_l_transmit_deauthen(priv, ta, - WLAN_MGMT_REASON_CLASS2_NONAUTH); - clt->used = CLIENT_EXIST_1; - } else { - /* mark it as auth'ed only */ - clt->used = CLIENT_AUTHENTICATED_2; - } -end: - FN_EXIT0; -} - - -/*---------------------------------------------------------------- -* acx_l_process_deauthen_from_sta -*----------------------------------------------------------------*/ -static void -acx_l_process_deauth_from_sta(wlandevice_t *priv, const wlan_fr_deauthen_t *req) -{ - const wlan_hdr_t *hdr; - client_t *client; - - FN_ENTER; - - hdr = req->hdr; - - if (acx_debug & L_ASSOC) { - acx_print_mac("DEAUTHEN priv->addr=", priv->dev_addr, " "); - acx_print_mac("a1", hdr->a1, " "); - acx_print_mac("a2", hdr->a2, " "); - acx_print_mac("a3", hdr->a3, " "); - acx_print_mac("priv->bssid", priv->bssid, "\n"); - } - - if (!mac_is_equal(priv->dev_addr, hdr->a1)) { - goto end; - } - - acxlog_mac(L_DEBUG, "STA ", hdr->a2, " sent us deauthen packet\n"); - - client = acx_l_sta_list_get(priv, hdr->a2); - if (!client) { - goto end; - } - client->used = CLIENT_EXIST_1; -end: - FN_EXIT0; -} - - -/*---------------------------------------------------------------- -* acx_l_process_disassoc_from_ap -*----------------------------------------------------------------*/ -static void -acx_l_process_disassoc_from_ap(wlandevice_t *priv, const wlan_fr_disassoc_t *req) -{ - FN_ENTER; - - if (!priv->ap_client) { - /* Hrm, we aren't assoc'ed yet anyhow... */ - goto end; - } - if (mac_is_equal(priv->dev_addr, req->hdr->a1)) { - /* TODO: send a deauth... */ - acx_l_transmit_deauthen(priv, priv->bssid, - WLAN_MGMT_REASON_DEAUTH_LEAVING); - /* Start scan anew */ - SET_BIT(priv->set_mask, GETSET_RESCAN); - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG); - } -end: - FN_EXIT0; -} - - -/*---------------------------------------------------------------- -* acx_l_process_deauth_from_ap -*----------------------------------------------------------------*/ -static void -acx_l_process_deauth_from_ap(wlandevice_t *priv, const wlan_fr_deauthen_t *req) -{ - FN_ENTER; - - if (!priv->ap_client) { - /* Hrm, we aren't assoc'ed yet anyhow... */ - goto end; - } - /* Chk: is ta is verified to be from our AP? */ - if (mac_is_equal(priv->dev_addr, req->hdr->a1)) { - acxlog(L_DEBUG, "AP sent us deauth packet\n"); - /* not needed: acx_set_status(priv, ACX_STATUS_1_SCANNING) */ - SET_BIT(priv->set_mask, GETSET_RESCAN); - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG); - } -end: - FN_EXIT0; -} - - -/*------------------------------------------------------------------------------ - * acx_l_rx - * - * The end of the Rx path. Pulls data from a rxhostdesc into a socket - * buffer and feeds it to the network stack via netif_rx(). - * - * Arguments: - * rxdesc: the rxhostdesc to pull the data from - * priv: the acx100 private struct of the interface - *----------------------------------------------------------------------------*/ -void -acx_l_rx(wlandevice_t *priv, rxbuffer_t *rxbuf) -{ - FN_ENTER; - if (likely(priv->dev_state_mask & ACX_STATE_IFACE_UP)) { - struct sk_buff *skb; - skb = acx_rxbuf_to_ether(priv, rxbuf); - if (likely(skb)) { - netif_rx(skb); - priv->netdev->last_rx = jiffies; - priv->stats.rx_packets++; - priv->stats.rx_bytes += skb->len; - } - } - FN_EXIT0; -} - - -/*---------------------------------------------------------------- -* acx_l_process_data_frame_master -*----------------------------------------------------------------*/ -static int -acx_l_process_data_frame_master(wlandevice_t *priv, rxbuffer_t *rxbuf) -{ - struct wlan_hdr *hdr; - struct tx *tx; - void *txbuf; - int len; - int result = NOT_OK; - - FN_ENTER; - - hdr = acx_get_wlan_hdr(priv, rxbuf); - - switch (WF_FC_FROMTODSi & hdr->fc) { - case 0: - case WF_FC_FROMDSi: - acxlog(L_DEBUG, "ap->sta or adhoc->adhoc data frame ignored\n"); - goto done; - case WF_FC_TODSi: - break; - default: /* WF_FC_FROMTODSi */ - acxlog(L_DEBUG, "wds data frame ignored (todo)\n"); - goto done; - } - - /* check if it is our BSSID, if not, leave */ - if (!mac_is_equal(priv->bssid, hdr->a1)) { - goto done; - } - - if (mac_is_equal(priv->dev_addr, hdr->a3)) { - /* this one is for us */ - acx_l_rx(priv, rxbuf); - } else { - if (mac_is_bcast(hdr->a3)) { - /* this one is bcast, rx it too */ - acx_l_rx(priv, rxbuf); - } - tx = acx_l_alloc_tx(priv); - if (!tx) { - goto fail; - } - /* repackage, tx, and hope it someday reaches its destination */ - /* order is important, we do it in-place */ - MAC_COPY(hdr->a1, hdr->a3); - MAC_COPY(hdr->a3, hdr->a2); - MAC_COPY(hdr->a2, priv->bssid); - /* To_DS = 0, From_DS = 1 */ - hdr->fc = WF_FC_FROMDSi + WF_FTYPE_DATAi; - - len = RXBUF_BYTES_RCVD(rxbuf); - txbuf = acx_l_get_txbuf(priv, tx); - if (txbuf) { - memcpy(txbuf, &rxbuf->hdr_a3, len); - acx_l_tx_data(priv, tx, len); - } - } -done: - result = OK; -fail: - FN_EXIT1(result); - return result; -} - - -/*---------------------------------------------------------------- -* acx_l_process_data_frame_client -*----------------------------------------------------------------*/ -static int -acx_l_process_data_frame_client(wlandevice_t *priv, rxbuffer_t *rxbuf) -{ - const u8 *da, *bssid; - const wlan_hdr_t *hdr; - netdevice_t *dev = priv->netdev; - int result = NOT_OK; - - FN_ENTER; - - if (ACX_STATUS_4_ASSOCIATED != priv->status) goto drop; - - hdr = acx_get_wlan_hdr(priv, rxbuf); - - switch (WF_FC_FROMTODSi & hdr->fc) { - case 0: - if (priv->mode != ACX_MODE_0_ADHOC) { - acxlog(L_DEBUG, "adhoc->adhoc data frame ignored\n"); - goto drop; - } - bssid = hdr->a3; - break; - case WF_FC_FROMDSi: - if (priv->mode != ACX_MODE_2_STA) { - acxlog(L_DEBUG, "ap->sta data frame ignored\n"); - goto drop; - } - bssid = hdr->a2; - break; - case WF_FC_TODSi: - acxlog(L_DEBUG, "sta->ap data frame ignored\n"); - goto drop; - default: /* WF_FC_FROMTODSi: wds->wds */ - acxlog(L_DEBUG, "wds data frame ignored (todo)\n"); - goto drop; - } - - da = hdr->a1; - - if (unlikely(acx_debug & L_DEBUG)) { - acx_print_mac("rx: da=", da, ""); - acx_print_mac(" bssid=", bssid, ""); - acx_print_mac(" priv->bssid=", priv->bssid, ""); - acx_print_mac(" priv->addr=", priv->dev_addr, "\n"); - } - - /* promiscuous mode --> receive all packets */ - if (unlikely(dev->flags & IFF_PROMISC)) - goto process; - - /* FIRST, check if it is our BSSID */ - if (!mac_is_equal(priv->bssid, bssid)) { - /* is not our BSSID, so bail out */ - goto drop; - } - - /* then, check if it is our address */ - if (mac_is_equal(priv->dev_addr, da)) { - goto process; - } - - /* then, check if it is broadcast */ - if (mac_is_bcast(da)) { - goto process; - } - - if (mac_is_mcast(da)) { - /* unconditionally receive all multicasts */ - if (dev->flags & IFF_ALLMULTI) - goto process; - - /* FIXME: check against the list of - * multicast addresses that are configured - * for the interface (ifconfig) */ - acxlog(L_XFER, "FIXME: multicast packet, need to check " - "against a list of multicast addresses " - "(to be created!); accepting packet for now\n"); - /* for now, just accept it here */ - goto process; - } - - acxlog(L_DEBUG, "rx: foreign packet, dropping\n"); - goto drop; -process: - /* receive packet */ - acx_l_rx(priv, rxbuf); - - result = OK; -drop: - FN_EXIT1(result); - return result; -} - - -/*---------------------------------------------------------------- -* acx_l_process_mgmt_frame -* -* Theory of operation: mgmt packet gets parsed (to make it easy -* to access variable-sized IEs), results stored in 'parsed'. -* Then we react to the packet. -* NB: wlan_mgmt_decode_XXX are dev-independent (shoudnt have been named acx_XXX) -*----------------------------------------------------------------*/ -typedef union parsed_mgmt_req { - wlan_fr_mgmt_t mgmt; - wlan_fr_assocreq_t assocreq; - wlan_fr_reassocreq_t reassocreq; - wlan_fr_assocresp_t assocresp; - wlan_fr_reassocresp_t reassocresp; - wlan_fr_beacon_t beacon; - wlan_fr_disassoc_t disassoc; - wlan_fr_authen_t authen; - wlan_fr_deauthen_t deauthen; - wlan_fr_proberesp_t proberesp; -} parsed_mgmt_req_t; - -void BUG_excessive_stack_usage(void); - -static int -acx_l_process_mgmt_frame(wlandevice_t *priv, rxbuffer_t *rxbuf) -{ - parsed_mgmt_req_t parsed; /* takes ~100 bytes of stack */ - wlan_hdr_t *hdr; - int adhoc, sta_scan, sta, ap; - int len; - - if (sizeof(parsed) > 256) - BUG_excessive_stack_usage(); - - FN_ENTER; - - hdr = acx_get_wlan_hdr(priv, rxbuf); - - /* Management frames never have these set */ - if (WF_FC_FROMTODSi & hdr->fc) { - FN_EXIT1(NOT_OK); - return NOT_OK; - } - - len = RXBUF_BYTES_RCVD(rxbuf); - if (WF_FC_ISWEPi & hdr->fc) - len -= 0x10; - - adhoc = (priv->mode == ACX_MODE_0_ADHOC); - sta_scan = ((priv->mode == ACX_MODE_2_STA) - && (priv->status != ACX_STATUS_4_ASSOCIATED)); - sta = ((priv->mode == ACX_MODE_2_STA) - && (priv->status == ACX_STATUS_4_ASSOCIATED)); - ap = (priv->mode == ACX_MODE_3_AP); - - switch (WF_FC_FSTYPEi & hdr->fc) { - /* beacons first, for speed */ - case WF_FSTYPE_BEACONi: - memset(&parsed.beacon, 0, sizeof(parsed.beacon)); - parsed.beacon.hdr = hdr; - parsed.beacon.len = len; - if (acx_debug & L_DATA) { - printk("BCN len:%d fc:%04X dur:%04X seq:%04X\n", - len, hdr->fc, hdr->dur, hdr->seq); - acx_print_mac("BCN a1:", hdr->a1, "\n"); - acx_print_mac("BCN a2:", hdr->a2, "\n"); - acx_print_mac("BCN a3:", hdr->a3, "\n"); - } - wlan_mgmt_decode_beacon(&parsed.beacon); - /* beacon and probe response are very similar, so... */ - acx_l_process_probe_response(priv, &parsed.beacon, rxbuf); - break; - case WF_FSTYPE_ASSOCREQi: - if (!ap) - break; - memset(&parsed.assocreq, 0, sizeof(parsed.assocreq)); - parsed.assocreq.hdr = hdr; - parsed.assocreq.len = len; - wlan_mgmt_decode_assocreq(&parsed.assocreq); - if (mac_is_equal(hdr->a1, priv->bssid) - && mac_is_equal(hdr->a3, priv->bssid)) { - acx_l_transmit_assocresp(priv, &parsed.assocreq); - } - break; - case WF_FSTYPE_REASSOCREQi: - if (!ap) - break; - memset(&parsed.assocreq, 0, sizeof(parsed.assocreq)); - parsed.assocreq.hdr = hdr; - parsed.assocreq.len = len; - wlan_mgmt_decode_assocreq(&parsed.assocreq); - /* reassocreq and assocreq are equivalent */ - acx_l_transmit_reassocresp(priv, &parsed.reassocreq); - break; - case WF_FSTYPE_ASSOCRESPi: - if (!sta_scan) - break; - memset(&parsed.assocresp, 0, sizeof(parsed.assocresp)); - parsed.assocresp.hdr = hdr; - parsed.assocresp.len = len; - wlan_mgmt_decode_assocresp(&parsed.assocresp); - acx_l_process_assocresp(priv, &parsed.assocresp); - break; - case WF_FSTYPE_REASSOCRESPi: - if (!sta_scan) - break; - memset(&parsed.assocresp, 0, sizeof(parsed.assocresp)); - parsed.assocresp.hdr = hdr; - parsed.assocresp.len = len; - wlan_mgmt_decode_assocresp(&parsed.assocresp); - acx_l_process_reassocresp(priv, &parsed.reassocresp); - break; - case WF_FSTYPE_PROBEREQi: - if (ap || adhoc) { - /* FIXME: since we're supposed to be an AP, - ** we need to return a Probe Response packet. - ** Currently firmware is doing it for us, - ** but firmware is buggy! See comment elsewhere --vda */ - } - break; - case WF_FSTYPE_PROBERESPi: - memset(&parsed.proberesp, 0, sizeof(parsed.proberesp)); - parsed.proberesp.hdr = hdr; - parsed.proberesp.len = len; - wlan_mgmt_decode_proberesp(&parsed.proberesp); - acx_l_process_probe_response(priv, &parsed.proberesp, rxbuf); - break; - case 6: - case 7: - /* exit */ - break; - case WF_FSTYPE_ATIMi: - /* exit */ - break; - case WF_FSTYPE_DISASSOCi: - if (!sta && !ap) - break; - memset(&parsed.disassoc, 0, sizeof(parsed.disassoc)); - parsed.disassoc.hdr = hdr; - parsed.disassoc.len = len; - wlan_mgmt_decode_disassoc(&parsed.disassoc); - if (sta) - acx_l_process_disassoc_from_ap(priv, &parsed.disassoc); - else - acx_l_process_disassoc_from_sta(priv, &parsed.disassoc); - break; - case WF_FSTYPE_AUTHENi: - if (!sta_scan && !ap) - break; - memset(&parsed.authen, 0, sizeof(parsed.authen)); - parsed.authen.hdr = hdr; - parsed.authen.len = len; - wlan_mgmt_decode_authen(&parsed.authen); - acx_l_process_authen(priv, &parsed.authen); - break; - case WF_FSTYPE_DEAUTHENi: - if (!sta && !ap) - break; - memset(&parsed.deauthen, 0, sizeof(parsed.deauthen)); - parsed.deauthen.hdr = hdr; - parsed.deauthen.len = len; - wlan_mgmt_decode_deauthen(&parsed.deauthen); - if (sta) - acx_l_process_deauth_from_ap(priv, &parsed.deauthen); - else - acx_l_process_deauth_from_sta(priv, &parsed.deauthen); - break; - } - - FN_EXIT1(OK); - return OK; -} - - -#ifdef UNUSED -/*---------------------------------------------------------------- -* acx_process_class_frame -* -* Called from IRQ context only -*----------------------------------------------------------------*/ -static int -acx_process_class_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala) -{ - return OK; -} -#endif - - -/*---------------------------------------------------------------- -* acx_l_process_NULL_frame -*----------------------------------------------------------------*/ -#ifdef BOGUS_ITS_NOT_A_NULL_FRAME_HANDLER_AT_ALL -static int -acx_l_process_NULL_frame(wlandevice_t *priv, rxbuffer_t *rxbuf, int vala) -{ - const signed char *esi; - const u8 *ebx; - const wlan_hdr_t *hdr; - const client_t *client; - int result = NOT_OK; - - hdr = acx_get_wlan_hdr(priv, rxbuf); - - switch (WF_FC_FROMTODSi & hdr->fc) { - case 0: - esi = hdr->a1; - ebx = hdr->a2; - break; - case WF_FC_FROMDSi: - esi = hdr->a1; - ebx = hdr->a3; - break; - case WF_FC_TODSi: - esi = hdr->a1; - ebx = hdr->a2; - break; - default: /* WF_FC_FROMTODSi */ - esi = hdr->a1; /* added by me! --vda */ - ebx = hdr->a2; - } - - if (esi[0x0] < 0) { - result = OK; - goto done; - } - - client = acx_l_sta_list_get(priv, ebx); - if (client) - result = NOT_OK; - else { -#ifdef IS_IT_BROKEN - acxlog(L_DEBUG | L_XFER, "\n"); - acx_l_transmit_deauthen(priv, ebx, - WLAN_MGMT_REASON_CLASS2_NONAUTH); -#else - acxlog(L_DEBUG, "received NULL frame from unknown client! " - "We really shouldn't send deauthen here, right?\n"); -#endif - result = OK; - } -done: - return result; -} -#endif - - -/*---------------------------------------------------------------- -* acx_l_process_probe_response -*----------------------------------------------------------------*/ -static int -acx_l_process_probe_response(wlandevice_t *priv, wlan_fr_proberesp_t *req, - const rxbuffer_t *rxbuf) -{ - struct client *bss; - wlan_hdr_t *hdr; - - FN_ENTER; - - hdr = req->hdr; - - if (mac_is_equal(hdr->a3, priv->dev_addr)) { - acxlog(L_ASSOC, "huh, scan found our own MAC!?\n"); - goto ok; /* just skip this one silently */ - } - - bss = acx_l_sta_list_get_or_add(priv, hdr->a2); - - /* NB: be careful modifying bss data! It may be one - ** of already known clients (like our AP is we are a STA) - ** Thus do not blindly modify e.g. current ratemask! */ - - if (STA_LIST_ADD_CAN_FAIL && !bss) { - /* uh oh, we found more sites/stations than we can handle with - * our current setup: pull the emergency brake and stop scanning! */ - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_STOP_SCAN); - /* TODO: a nice comment what below call achieves --vda */ - acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH); - goto ok; - } - /* NB: get_or_add already filled bss->address = hdr->a2 */ - MAC_COPY(bss->bssid, hdr->a3); - - /* copy the ESSID element */ - if (req->ssid && req->ssid->len <= IW_ESSID_MAX_SIZE) { - bss->essid_len = req->ssid->len; - memcpy(bss->essid, req->ssid->ssid, req->ssid->len); - bss->essid[req->ssid->len] = '\0'; - } else { - /* Either no ESSID IE or oversized one */ - printk("%s: received packet has bogus ESSID\n", - priv->netdev->name); - } - - if (req->ds_parms) - bss->channel = req->ds_parms->curr_ch; - if (req->cap_info) - bss->cap_info = ieee2host16(*req->cap_info); - - bss->sir = acx_signal_to_winlevel(rxbuf->phy_level); - bss->snr = acx_signal_to_winlevel(rxbuf->phy_snr); - - bss->rate_cap = 0; /* operational mask */ - bss->rate_bas = 0; /* basic mask */ - if (req->supp_rates) - add_bits_to_ratemasks(req->supp_rates->rates, - req->supp_rates->len, &bss->rate_bas, &bss->rate_cap); - if (req->ext_rates) - add_bits_to_ratemasks(req->ext_rates->rates, - req->ext_rates->len, &bss->rate_bas, &bss->rate_cap); - /* Fix up any possible bogosity - code elsewhere - * is not expecting empty masks */ - if (!bss->rate_cap) - bss->rate_cap = priv->rate_basic; - if (!bss->rate_bas) - bss->rate_bas = 1 << lowest_bit(bss->rate_cap); - if (!bss->rate_cur) - bss->rate_cur = 1 << lowest_bit(bss->rate_bas); - - /* People moan about this being too noisy at L_ASSOC */ - acxlog(L_DEBUG, - "found %s: ESSID='%s' ch=%d " - "BSSID="MACSTR" caps=0x%04X SIR=%d SNR=%d\n", - (bss->cap_info & WF_MGMT_CAP_IBSS) ? "Ad-Hoc peer" : "AP", - bss->essid, bss->channel, MAC(bss->bssid), bss->cap_info, - bss->sir, bss->snr); -ok: - FN_EXIT0; - return OK; -} - - -/*---------------------------------------------------------------- -* get_status_string -*----------------------------------------------------------------*/ -static const char* -get_status_string(unsigned int status) -{ - /* A bit shortened, but hopefully still understandable */ - static const char * const status_str[] = { - /* 0 */ "Successful", - /* 1 */ "Unspecified failure", - /* 2 */ "reserved", - /* 3 */ "reserved", - /* 4 */ "reserved", - /* 5 */ "reserved", - /* 6 */ "reserved", - /* 7 */ "reserved", - /* 8 */ "reserved", - /* 9 */ "reserved", - /*10 */ "Cannot support all requested capabilities in Capability Information field", - /*11 */ "Reassoc denied (reason outside of 802.11b scope)", - /*12 */ "Assoc denied (reason outside of 802.11b scope), maybe MAC filtering by peer?", - /*13 */ "Responding station doesnt support specified auth algorithm", - /*14 */ "Auth rejected: wrong transaction sequence number", - /*15 */ "Auth rejected: challenge failure", - /*16 */ "Auth rejected: timeout for next frame in sequence", - /*17 */ "Assoc denied: too many STAs on this AP", - /*18 */ "Assoc denied: requesting STA doesnt support all data rates in basic set", - /*19 */ "Assoc denied: requesting STA doesnt support Short Preamble", - /*20 */ "Assoc denied: requesting STA doesnt support PBCC Modulation", - /*21 */ "Assoc denied: requesting STA doesnt support Channel Agility" - /*22 */ "reserved", - /*23 */ "reserved", - /*24 */ "reserved", - /*25 */ "Assoc denied: requesting STA doesnt support Short Slot Time", - /*26 */ "Assoc denied: requesting STA doesnt support DSSS-OFDM" - }; - - return status_str[status < VEC_SIZE(status_str) ? status : 2]; -} - - -/*---------------------------------------------------------------- -* acx_l_process_assocresp -*----------------------------------------------------------------*/ -static int -acx_l_process_assocresp(wlandevice_t *priv, const wlan_fr_assocresp_t *req) -{ - const wlan_hdr_t *hdr; - int res = OK; - - FN_ENTER; - hdr = req->hdr; - - if ((ACX_MODE_2_STA == priv->mode) - && mac_is_equal(priv->dev_addr, hdr->a1)) { - u16 st = ieee2host16(*(req->status)); - if (WLAN_MGMT_STATUS_SUCCESS == st) { - priv->aid = ieee2host16(*(req->aid)); - /* tell the card we are associated when we are out of interrupt context */ - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_ASSOCIATE); - } else { - - /* TODO: we shall delete peer from sta_list, and try other candidates... */ - - printk("%s: association FAILED: peer sent " - "response code %d (%s)\n", - priv->netdev->name, st, get_status_string(st)); - res = NOT_OK; - } - } - - FN_EXIT1(res); - return res; -} - - -/*---------------------------------------------------------------- -* acx_l_process_reassocresp -*----------------------------------------------------------------*/ -static int -acx_l_process_reassocresp(wlandevice_t *priv, const wlan_fr_reassocresp_t *req) -{ - const wlan_hdr_t *hdr; - int result = NOT_OK; - u16 st; - - FN_ENTER; - hdr = req->hdr; - - if (!mac_is_equal(priv->dev_addr, hdr->a1)) { - goto end; - } - st = ieee2host16(*(req->status)); - if (st == WLAN_MGMT_STATUS_SUCCESS) { - acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); - result = OK; - } else { - printk("%s: reassociation FAILED: peer sent " - "response code %d (%s)\n", - priv->netdev->name, st, get_status_string(st)); - } -end: - FN_EXIT1(result); - return result; -} - - -/*---------------------------------------------------------------- -* acx_l_process_authen -* -* Called only in STA_SCAN or AP mode -*----------------------------------------------------------------*/ -static int -acx_l_process_authen(wlandevice_t *priv, const wlan_fr_authen_t *req) -{ - const wlan_hdr_t *hdr; - client_t *clt; - wlan_ie_challenge_t *chal; - u16 alg, seq, status; - int ap, result; - - FN_ENTER; - - hdr = req->hdr; - - if (acx_debug & L_ASSOC) { - acx_print_mac("AUTHEN priv->addr=", priv->dev_addr, " "); - acx_print_mac("a1=", hdr->a1, " "); - acx_print_mac("a2=", hdr->a2, " "); - acx_print_mac("a3=", hdr->a3, " "); - acx_print_mac("priv->bssid=", priv->bssid, "\n"); - } - - /* TODO: move first check up in caller chain, - ** it's not auth specific */ - if (!mac_is_equal(priv->dev_addr, hdr->a1) - || !mac_is_equal(priv->bssid, hdr->a3)) { - result = OK; - goto end; - } - - alg = ieee2host16(*(req->auth_alg)); - seq = ieee2host16(*(req->auth_seq)); - status = ieee2host16(*(req->status)); - - ap = (priv->mode == ACX_MODE_3_AP); - - if (priv->auth_alg <= 1) { - if (priv->auth_alg != alg) { - acxlog(L_ASSOC, "authentication algorithm mismatch: " - "want: %d, req: %d\n", priv->auth_alg, alg); - result = NOT_OK; - goto end; - } - } - acxlog(L_ASSOC, "algorithm is ok\n"); - - if (ap) { - clt = acx_l_sta_list_get_or_add(priv, hdr->a2); - if (STA_LIST_ADD_CAN_FAIL && !clt) { - acxlog(L_ASSOC, "could not allocate room for client\n"); - result = NOT_OK; - goto end; - } - } else { - clt = priv->ap_client; - if (!mac_is_equal(clt->address, hdr->a2)) { - printk("%s: malformed auth frame from AP?!\n", - priv->netdev->name); - result = NOT_OK; - goto end; - } - } - - /* now check which step in the authentication sequence we are - * currently in, and act accordingly */ - acxlog(L_ASSOC, "acx_process_authen auth seq step %d\n", seq); - switch (seq) { - case 1: - if (!ap) - break; - acx_l_transmit_authen2(priv, req, clt); - break; - case 2: - if (ap) - break; - if (status == WLAN_MGMT_STATUS_SUCCESS) { - if (alg == WLAN_AUTH_ALG_OPENSYSTEM) { - acx_set_status(priv, ACX_STATUS_3_AUTHENTICATED); - acx_l_transmit_assoc_req(priv); - } else - if (alg == WLAN_AUTH_ALG_SHAREDKEY) { - acx_l_transmit_authen3(priv, req); - } - } else { - printk("%s: auth FAILED: peer sent " - "response code %d (%s), " - "still waiting for authentication\n", - priv->netdev->name, - status, get_status_string(status)); - acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH); - } - break; - case 3: - if (!ap) - break; - if ((clt->auth_alg != WLAN_AUTH_ALG_SHAREDKEY) - || (alg != WLAN_AUTH_ALG_SHAREDKEY) - || (clt->auth_step != 2)) - break; - chal = req->challenge; - if (!chal - || memcmp(chal->challenge, clt->challenge_text, WLAN_CHALLENGE_LEN) - || (chal->eid != WLAN_EID_CHALLENGE) - || (chal->len != WLAN_CHALLENGE_LEN) - ) - break; - acx_l_transmit_authen4(priv, req); - MAC_COPY(clt->address, hdr->a2); - clt->used = CLIENT_AUTHENTICATED_2; - clt->auth_step = 4; - clt->seq = ieee2host16(hdr->seq); - break; - case 4: - if (ap) - break; - /* ok, we're through: we're authenticated. Woohoo!! */ - acx_set_status(priv, ACX_STATUS_3_AUTHENTICATED); - acxlog(L_ASSOC, "Authenticated!\n"); - /* now that we're authenticated, request association */ - acx_l_transmit_assoc_req(priv); - break; - } - result = NOT_OK; -end: - FN_EXIT1(result); - return result; -} - - -/*---------------------------------------------------------------- -* acx_gen_challenge -*----------------------------------------------------------------*/ -static void -acx_gen_challenge(wlan_ie_challenge_t* d) -{ - FN_ENTER; - d->eid = WLAN_EID_CHALLENGE; - d->len = WLAN_CHALLENGE_LEN; - get_random_bytes(d->challenge, WLAN_CHALLENGE_LEN); - FN_EXIT0; -} - - -/*---------------------------------------------------------------- -* acx_l_transmit_deauthen -*----------------------------------------------------------------*/ -static int -acx_l_transmit_deauthen(wlandevice_t *priv, const u8 *addr, u16 reason) -{ - struct tx *tx; - struct wlan_hdr_mgmt *head; - struct deauthen_frame_body *body; - - FN_ENTER; - - tx = acx_l_alloc_tx(priv); - if (!tx) - goto bad; - head = acx_l_get_txbuf(priv, tx); - if (!head) - goto bad; - body = (void*)(head + 1); - - head->fc = (WF_FTYPE_MGMTi | WF_FSTYPE_DEAUTHENi); - head->dur = 0; - MAC_COPY(head->da, addr); - MAC_COPY(head->sa, priv->dev_addr); - MAC_COPY(head->bssid, priv->bssid); - head->seq = 0; - - acxlog(L_DEBUG | L_ASSOC | L_XFER, - "sending deauthen to "MACSTR" for %d\n", - MAC(addr), reason); - - body->reason = host2ieee16(reason); - - /* body is fixed size here, but beware of cutting-and-pasting this - - ** do not use sizeof(*body) for variable sized mgmt packets! */ - acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + sizeof(*body)); - - FN_EXIT1(OK); - return OK; -bad: - FN_EXIT1(NOT_OK); - return NOT_OK; -} - - -/*---------------------------------------------------------------- -* acx_l_transmit_authen1 -*----------------------------------------------------------------*/ -static int -acx_l_transmit_authen1(wlandevice_t *priv) -{ - struct tx *tx; - struct wlan_hdr_mgmt *head; - struct auth_frame_body *body; - - FN_ENTER; - - acxlog(L_ASSOC, "Sending authentication1 request, " - "awaiting response!\n"); - - tx = acx_l_alloc_tx(priv); - if (!tx) - goto bad; - head = acx_l_get_txbuf(priv, tx); - if (!head) - goto bad; - body = (void*)(head + 1); - - head->fc = WF_FSTYPE_AUTHENi; - head->dur = host2ieee16(0x8000); - MAC_COPY(head->da, priv->bssid); - MAC_COPY(head->sa, priv->dev_addr); - MAC_COPY(head->bssid, priv->bssid); - head->seq = 0; - - body->auth_alg = host2ieee16(priv->auth_alg); - body->auth_seq = host2ieee16(1); - body->status = host2ieee16(0); - - acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2); - - FN_EXIT1(OK); - return OK; -bad: - FN_EXIT1(NOT_OK); - return NOT_OK; -} - - -/*---------------------------------------------------------------- -* acx_l_transmit_authen2 -*----------------------------------------------------------------*/ -static int -acx_l_transmit_authen2(wlandevice_t *priv, const wlan_fr_authen_t *req, - client_t *clt) -{ - struct tx *tx; - struct wlan_hdr_mgmt *head; - struct auth_frame_body *body; - unsigned int packet_len; - - FN_ENTER; - - if (!clt) - goto ok; - - MAC_COPY(clt->address, req->hdr->a2); -#ifdef UNUSED - clt->ps = ((WF_FC_PWRMGTi & req->hdr->fc) != 0); -#endif - clt->auth_alg = ieee2host16(*(req->auth_alg)); - clt->auth_step = 2; - clt->seq = ieee2host16(req->hdr->seq); - - tx = acx_l_alloc_tx(priv); - if (!tx) - goto bad; - head = acx_l_get_txbuf(priv, tx); - if (!head) - goto bad; - body = (void*)(head + 1); - - head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */ - head->dur = req->hdr->dur; - MAC_COPY(head->da, req->hdr->a2); - /* MAC_COPY(head->sa, req->hdr->a1); */ - MAC_COPY(head->sa, priv->dev_addr); - MAC_COPY(head->bssid, req->hdr->a3); - head->seq = req->hdr->seq; - - /* already in IEEE format, no endianness conversion */ - body->auth_alg = *(req->auth_alg); - body->auth_seq = host2ieee16(2); - body->status = host2ieee16(0); - - packet_len = WLAN_HDR_A3_LEN + 2 + 2 + 2; - if (ieee2host16(*(req->auth_alg)) == WLAN_AUTH_ALG_OPENSYSTEM) { - clt->used = CLIENT_AUTHENTICATED_2; - } else { /* shared key */ - acx_gen_challenge(&body->challenge); - memcpy(&clt->challenge_text, body->challenge.challenge, WLAN_CHALLENGE_LEN); - packet_len += 2 + 2 + 2 + 1+1+WLAN_CHALLENGE_LEN; - } - - acxlog_mac(L_ASSOC | L_XFER, - "transmit_auth2: BSSID=", head->bssid, "\n"); - - acx_l_tx_data(priv, tx, packet_len); -ok: - FN_EXIT1(OK); - return OK; -bad: - FN_EXIT1(NOT_OK); - return NOT_OK; -} - - -/*---------------------------------------------------------------- -* acx_l_transmit_authen3 -*----------------------------------------------------------------*/ -static int -acx_l_transmit_authen3(wlandevice_t *priv, const wlan_fr_authen_t *req) -{ - struct tx *tx; - struct wlan_hdr_mgmt *head; - struct auth_frame_body *body; - unsigned int packet_len; - - FN_ENTER; - - tx = acx_l_alloc_tx(priv); - if (!tx) - goto ok; - head = acx_l_get_txbuf(priv, tx); - if (!head) - goto ok; - body = (void*)(head + 1); - - head->fc = WF_FC_ISWEPi + WF_FSTYPE_AUTHENi; - /* FIXME: is this needed?? authen4 does it... - head->dur = req->hdr->dur; - head->seq = req->hdr->seq; - */ - MAC_COPY(head->da, priv->bssid); - MAC_COPY(head->sa, priv->dev_addr); - MAC_COPY(head->bssid, priv->bssid); - - /* already in IEEE format, no endianness conversion */ - body->auth_alg = *(req->auth_alg); - body->auth_seq = host2ieee16(3); - body->status = host2ieee16(0); - memcpy(&body->challenge, req->challenge, req->challenge->len + 2); - packet_len = WLAN_HDR_A3_LEN + 8 + req->challenge->len; - - acxlog(L_ASSOC | L_XFER, "transmit_authen3!\n"); - - acx_l_tx_data(priv, tx, packet_len); -ok: - FN_EXIT1(OK); - return OK; -} - - -/*---------------------------------------------------------------- -* acx_l_transmit_authen4 -*----------------------------------------------------------------*/ -static int -acx_l_transmit_authen4(wlandevice_t *priv, const wlan_fr_authen_t *req) -{ - struct tx *tx; - struct wlan_hdr_mgmt *head; - struct auth_frame_body *body; - - FN_ENTER; - - tx = acx_l_alloc_tx(priv); - if (!tx) - goto ok; - head = acx_l_get_txbuf(priv, tx); - if (!head) - goto ok; - body = (void*)(head + 1); - - head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */ - head->dur = req->hdr->dur; - MAC_COPY(head->da, req->hdr->a2); - /* MAC_COPY(head->sa, req->hdr->a1); */ - MAC_COPY(head->sa, priv->dev_addr); - MAC_COPY(head->bssid, req->hdr->a3); - head->seq = req->hdr->seq; - - /* already in IEEE format, no endianness conversion */ - body->auth_alg = *(req->auth_alg); - body->auth_seq = host2ieee16(4); - body->status = host2ieee16(0); - - acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2); -ok: - FN_EXIT1(OK); - return OK; -} - - -/*---------------------------------------------------------------- -* acx_l_transmit_assoc_req -* -* priv->ap_client is a current candidate AP here -*----------------------------------------------------------------*/ -static int -acx_l_transmit_assoc_req(wlandevice_t *priv) -{ - struct tx *tx; - struct wlan_hdr_mgmt *head; - u8 *body, *p, *prate; - unsigned int packet_len; - u16 cap; - - FN_ENTER; - - acxlog(L_ASSOC, "sending association request, " - "awaiting response. NOT ASSOCIATED YET\n"); - tx = acx_l_alloc_tx(priv); - if (!tx) - goto bad; - head = acx_l_get_txbuf(priv, tx); - if (!head) - goto bad; - body = (void*)(head + 1); - - head->fc = WF_FSTYPE_ASSOCREQi; - head->dur = host2ieee16(0x8000); - MAC_COPY(head->da, priv->bssid); - MAC_COPY(head->sa, priv->dev_addr); - MAC_COPY(head->bssid, priv->bssid); - head->seq = 0; - - p = body; - /* now start filling the AssocReq frame body */ - - /* since this assoc request will most likely only get - * sent in the STA to AP case (and not when Ad-Hoc IBSS), - * the cap combination indicated here will thus be - * WF_MGMT_CAP_ESSi *always* (no IBSS ever) - * The specs are more than non-obvious on all that: - * - * 802.11 7.3.1.4 Capability Information field - ** APs set the ESS subfield to 1 and the IBSS subfield to 0 within - ** Beacon or Probe Response management frames. STAs within an IBSS - ** set the ESS subfield to 0 and the IBSS subfield to 1 in transmitted - ** Beacon or Probe Response management frames - ** - ** APs set the Privacy subfield to 1 within transmitted Beacon, - ** Probe Response, Association Response, and Reassociation Response - ** if WEP is required for all data type frames within the BSS. - ** STAs within an IBSS set the Privacy subfield to 1 in Beacon - ** or Probe Response management frames if WEP is required - ** for all data type frames within the IBSS */ - - /* note that returning 0 will be refused by several APs... - * (so this indicates that you're probably supposed to - * "confirm" the ESS mode) */ - cap = WF_MGMT_CAP_ESSi; - - /* this one used to be a check on wep_restricted, - * but more likely it's wep_enabled instead */ - if (priv->wep_enabled) - SET_BIT(cap, WF_MGMT_CAP_PRIVACYi); - - /* Probably we can just set these always, because our hw is - ** capable of shortpre and PBCC --vda */ - /* only ask for short preamble if the peer station supports it */ - if (priv->ap_client->cap_info & WF_MGMT_CAP_SHORT) - SET_BIT(cap, WF_MGMT_CAP_SHORTi); - /* only ask for PBCC support if the peer station supports it */ - if (priv->ap_client->cap_info & WF_MGMT_CAP_PBCC) - SET_BIT(cap, WF_MGMT_CAP_PBCCi); - - /* IEs: 1. caps */ - *(u16*)p = cap; p += 2; - /* 2. listen interval */ - *(u16*)p = host2ieee16(priv->listen_interval); p += 2; - /* 3. ESSID */ - p = wlan_fill_ie_ssid(p, - strlen(priv->essid_for_assoc), priv->essid_for_assoc); - /* 4. supp rates */ - prate = p; - p = wlan_fill_ie_rates(p, - priv->rate_supported_len, priv->rate_supported); - /* 5. ext supp rates */ - p = wlan_fill_ie_rates_ext(p, - priv->rate_supported_len, priv->rate_supported); - - if (acx_debug & L_DEBUG) { - printk("association: rates element\n"); - acx_dump_bytes(prate, p - prate); - } - - /* calculate lengths */ - packet_len = WLAN_HDR_A3_LEN + (p - body); - - acxlog(L_ASSOC, "association: requesting caps 0x%04X, ESSID '%s'\n", - cap, priv->essid_for_assoc); - - acx_l_tx_data(priv, tx, packet_len); - FN_EXIT1(OK); - return OK; -bad: - FN_EXIT1(NOT_OK); - return NOT_OK; -} - - -/*---------------------------------------------------------------- -* acx_l_transmit_disassoc -* -* FIXME: looks like incomplete implementation of a helper: -* acx_l_transmit_disassoc(priv, clt) - kick this client (we're an AP) -* acx_l_transmit_disassoc(priv, NULL) - leave BSSID (we're a STA) -*----------------------------------------------------------------*/ -#ifdef BROKEN -int -acx_l_transmit_disassoc(wlandevice_t *priv, client_t *clt) -{ - struct tx *tx; - struct wlan_hdr_mgmt *head; - struct disassoc_frame_body *body; - - FN_ENTER; -/* if (clt != NULL) { */ - tx = acx_l_alloc_tx(priv); - if (!tx) - goto bad; - head = acx_l_get_txbuf(priv, tx); - if (!head) - goto bad; - body = (void*)(head + 1); - -/* clt->used = CLIENT_AUTHENTICATED_2; - not (yet?) associated */ - - head->fc = WF_FSTYPE_DISASSOCi; - head->dur = 0; - /* huh? It muchly depends on whether we're STA or AP... - ** sta->ap: da=bssid, sa=own, bssid=bssid - ** ap->sta: da=sta, sa=bssid, bssid=bssid. FIXME! */ - MAC_COPY(head->da, priv->bssid); - MAC_COPY(head->sa, priv->dev_addr); - MAC_COPY(head->bssid, priv->dev_addr); - head->seq = 0; - - /* "Class 3 frame received from nonassociated station." */ - body->reason = host2ieee16(7); - - /* fixed size struct, ok to sizeof */ - acx_l_tx_data(priv, tx, WLAN_HDR_A3_LEN + sizeof(*body)); -/* } */ - FN_EXIT1(OK); - return OK; -bad: - FN_EXIT1(NOT_OK); - return NOT_OK; -} -#endif - - -/*---------------------------------------------------------------- -* acx_s_complete_scan -* -* Called either from after_interrupt_task() if: -* 1) there was Scan_Complete IRQ, or -* 2) scanning expired in timer() -* We need to decide which ESS or IBSS to join. -* Iterates thru priv->sta_list: -* if priv->ap is not bcast, will join only specified -* ESS or IBSS with this bssid -* checks peers' caps for ESS/IBSS bit -* checks peers' SSID, allows exact match or hidden SSID -* If station to join is chosen: -* points priv->ap_client to the chosen struct client -* sets priv->essid_for_assoc for future assoc attempt -* Auth/assoc is not yet performed -* Returns OK if there is no need to restart scan -*----------------------------------------------------------------*/ -int -acx_s_complete_scan(wlandevice_t *priv) -{ - struct client *bss; - unsigned long flags; - u16 needed_cap; - int i; - int idx_found = -1; - int result = OK; - - FN_ENTER; - - switch (priv->mode) { - case ACX_MODE_0_ADHOC: - needed_cap = WF_MGMT_CAP_IBSS; /* 2, we require Ad-Hoc */ - break; - case ACX_MODE_2_STA: - needed_cap = WF_MGMT_CAP_ESS; /* 1, we require Managed */ - break; - default: - printk("acx: driver bug: mode=%d in complete_scan()\n", priv->mode); - dump_stack(); - goto end; - } - - acx_lock(priv, flags); - - /* TODO: sta_iterator hiding implementation would be nice here... */ - - for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { - bss = &priv->sta_list[i]; - if (!bss->used) continue; - - acxlog(L_ASSOC, "Scan Table: SSID='%s' CH=%d SIR=%d SNR=%d\n", - bss->essid, bss->channel, bss->sir, bss->snr); - - if (!mac_is_bcast(priv->ap)) - if (!mac_is_equal(bss->bssid, priv->ap)) - continue; /* keep looking */ - - /* broken peer with no mode flags set? */ - if (unlikely(!(bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)))) { - printk("%s: strange peer "MACSTR" found with " - "neither ESS (AP) nor IBSS (Ad-Hoc) " - "capability - skipped\n", - priv->netdev->name, MAC(bss->address)); - continue; - } - acxlog(L_ASSOC, "peer_cap 0x%04X, needed_cap 0x%04X\n", - bss->cap_info, needed_cap); - - /* does peer station support what we need? */ - if ((bss->cap_info & needed_cap) != needed_cap) - continue; /* keep looking */ - - /* strange peer with NO basic rates?! */ - if (unlikely(!bss->rate_bas)) { - printk("%s: strange peer "MACSTR" with empty rate set " - "- skipped\n", - priv->netdev->name, MAC(bss->address)); - continue; - } - - /* do we support all basic rates of this peer? */ - if ((bss->rate_bas & priv->rate_oper) != bss->rate_bas) { -/* we probably need to have all rates as operational rates, - even in case of an 11M-only configuration */ -#ifdef THIS_IS_TROUBLESOME - printk("%s: peer "MACSTR": incompatible basic rates " - "(AP requests 0x%04X, we have 0x%04X) " - "- skipped\n", - priv->netdev->name, MAC(bss->address), - bss->rate_bas, priv->rate_oper); - continue; -#else - printk("%s: peer "MACSTR": incompatible basic rates " - "(AP requests 0x%04X, we have 0x%04X). " - "Considering anyway...\n", - priv->netdev->name, MAC(bss->address), - bss->rate_bas, priv->rate_oper); -#endif - } - - if ( !(priv->reg_dom_chanmask & (1<<(bss->channel-1))) ) { - printk("%s: warning: peer "MACSTR" is on channel %d " - "outside of channel range of current " - "regulatory domain - couldn't join " - "even if other settings match. " - "You might want to adapt your config\n", - priv->netdev->name, MAC(bss->address), - bss->channel); - continue; /* keep looking */ - } - - if (!priv->essid_active || !strcmp(bss->essid, priv->essid)) { - acxlog(L_ASSOC, - "found station with matching ESSID! ('%s' " - "station, '%s' config)\n", - bss->essid, - (priv->essid_active) ? priv->essid : "[any]"); - /* TODO: continue looking for peer with better SNR */ - bss->used = CLIENT_JOIN_CANDIDATE; - idx_found = i; - - /* stop searching if this station is - * on the current channel, otherwise - * keep looking for an even better match */ - if (bss->channel == priv->channel) - break; - } else - if (!bss->essid[0] - || ((' ' == bss->essid[0]) && !bss->essid[1]) - ) { - /* hmm, station with empty or single-space SSID: - * using hidden SSID broadcast? - */ - /* This behaviour is broken: which AP from zillion - ** of APs with hidden SSID you'd try? - ** We should use Probe requests to get Probe responses - ** and check for real SSID (are those never hidden?) */ - bss->used = CLIENT_JOIN_CANDIDATE; - if (idx_found == -1) - idx_found = i; - acxlog(L_ASSOC, "found station with empty or " - "single-space (hidden) SSID, considering " - "for assoc attempt\n"); - /* ...and keep looking for better matches */ - } else { - acxlog(L_ASSOC, "ESSID doesn't match! ('%s' " - "station, '%s' config)\n", - bss->essid, - (priv->essid_active) ? priv->essid : "[any]"); - } - } - - /* TODO: iterate thru join candidates instead */ - /* TODO: rescan if not associated within some timeout */ - if (idx_found != -1) { - char *essid_src; - size_t essid_len; - - bss = &priv->sta_list[idx_found]; - priv->ap_client = bss; - - if (bss->essid[0] == '\0') { - /* if the ESSID of the station we found is empty - * (no broadcast), then use user configured ESSID - * instead */ - essid_src = priv->essid; - essid_len = priv->essid_len; - } else { - essid_src = bss->essid; - essid_len = strlen(bss->essid); - } - - acx_update_capabilities(priv); - - memcpy(priv->essid_for_assoc, essid_src, essid_len); - priv->essid_for_assoc[essid_len] = '\0'; - priv->channel = bss->channel; - MAC_COPY(priv->bssid, bss->bssid); - - bss->rate_cfg = (bss->rate_cap & priv->rate_oper); - bss->rate_cur = 1 << lowest_bit(bss->rate_cfg); - bss->rate_100 = acx_rate111to100(bss->rate_cur); - - acxlog_mac(L_ASSOC, - "matching station found: ", priv->bssid, ", joining\n"); - - /* TODO: do we need to switch to the peer's channel first? */ - - if (ACX_MODE_0_ADHOC == priv->mode) { - acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); - } else { - acx_l_transmit_authen1(priv); - acx_set_status(priv, ACX_STATUS_2_WAIT_AUTH); - } - } else { /* idx_found == -1 */ - /* uh oh, no station found in range */ - if (ACX_MODE_0_ADHOC == priv->mode) { - printk("%s: no matching station found in range, " - "generating our own IBSS instead\n", - priv->netdev->name); - /* we do it hostap way: */ - MAC_COPY(priv->bssid, priv->dev_addr); - priv->bssid[0] |= 0x02; /* 'local assigned addr' bit */ - /* add IBSS bit to our caps... */ - acx_update_capabilities(priv); - acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); - /* In order to cmd_join be called below */ - idx_found = 0; - } else { - /* we shall scan again, AP can be - ** just temporarily powered off */ - acxlog(L_ASSOC, - "no matching station found in range yet\n"); - acx_set_status(priv, ACX_STATUS_1_SCANNING); - result = NOT_OK; - } - } - - acx_unlock(priv, flags); - - if (idx_found != -1) { - if (ACX_MODE_0_ADHOC == priv->mode) { - /* need to update channel in beacon template */ - SET_BIT(priv->set_mask, SET_TEMPLATES); - if (ACX_STATE_IFACE_UP & priv->dev_state_mask) - acx_s_update_card_settings(priv, 0, 0); - } - /* Inform firmware on our decision to start or join BSS */ - acx_s_cmd_join_bssid(priv, priv->bssid); - } - -end: - FN_EXIT1(result); - return result; -} - - -/*********************************************************************** -** acx_s_read_fw -** -** Loads a firmware image -** -** Returns: -** 0 unable to load file -** pointer to firmware success -*/ -#if USE_FW_LOADER_26 -firmware_image_t* -acx_s_read_fw(struct device *dev, const char *file, u32 *size) -#else -#undef acx_s_read_fw -firmware_image_t* -acx_s_read_fw(const char *file, u32 *size) -#endif -{ - firmware_image_t *res; - -#if USE_FW_LOADER_LEGACY - mm_segment_t orgfs; - unsigned long page; - char *buffer; - struct file *inf; - int retval; - int offset; - char *filename; -#endif - -#if USE_FW_LOADER_26 - const struct firmware *fw_entry; - - res = NULL; - acxlog(L_DEBUG, "requesting firmware image '%s'\n", file); - if (!request_firmware(&fw_entry, file, dev)) { - *size = 8; - if (fw_entry->size >= 8) - *size = 8 + le32_to_cpu(*(u32 *)(fw_entry->data + 4)); - if (fw_entry->size != *size) { - printk("acx: firmware size does not match " - "firmware header: %d != %d, " - "aborting fw upload\n", - (int) fw_entry->size, (int) *size); - goto release_ret; - } - res = vmalloc(*size); - if (!res) { - printk("acx: no memory for firmware " - "(%u bytes)\n", *size); - goto release_ret; - } - memcpy(res, fw_entry->data, fw_entry->size); -release_ret: - release_firmware(fw_entry); - return res; - } - printk("acx: firmware image '%s' was not provided. " - "Check your hotplug scripts\n", file); -#endif - -#if USE_FW_LOADER_LEGACY - printk("acx: firmware upload via firmware_dir module parameter " - "is deprecated. Switch to using hotplug\n"); - - res = NULL; - orgfs = get_fs(); /* store original fs */ - set_fs(KERNEL_DS); - - /* Read in whole file then check the size */ - page = __get_free_page(GFP_KERNEL); - if (unlikely(0 == page)) { - printk("acx: no memory for firmware upload\n"); - goto fail; - } - - filename = kmalloc(PATH_MAX, GFP_KERNEL); - if (unlikely(!filename)) { - printk("acx: no memory for firmware upload\n"); - goto fail; - } - if (!firmware_dir) { - firmware_dir = "/usr/share/acx"; - acxlog(L_DEBUG, "no firmware directory specified " - "via module parameter firmware_dir, " - "using default %s\n", firmware_dir); - } - snprintf(filename, PATH_MAX, "%s/%s", firmware_dir, file); - acxlog(L_DEBUG, "reading firmware image '%s'\n", filename); - - buffer = (char*)page; - - /* Note that file must be given as absolute path: - * a relative path works on first loading, - * but any subsequent firmware loading during card - * eject/insert will fail, most likely since the first - * module loading happens in user space (and thus - * filp_open can figure out the absolute path from a - * relative path) whereas the card reinsert processing - * probably happens in kernel space where you don't have - * a current directory to be able to figure out an - * absolute path from a relative path... */ - inf = filp_open(filename, O_RDONLY, 0); - kfree(filename); - if (OK != IS_ERR(inf)) { - const char *err; - - switch (-PTR_ERR(inf)) { - case 2: err = "file not found"; - break; - default: - err = "unknown error"; - break; - } - printk("acx: error %ld trying to open file '%s': %s\n", - -PTR_ERR(inf), file, err); - goto fail; - } - - if (unlikely((NULL == inf->f_op) || (NULL == inf->f_op->read))) { - printk("acx: %s does not have a read method?!\n", file); - goto fail_close; - } - - offset = 0; - do { - retval = inf->f_op->read(inf, buffer, PAGE_SIZE, &inf->f_pos); - - if (unlikely(0 > retval)) { - printk("acx: error %d reading file '%s'\n", - -retval, file); - vfree(res); - res = NULL; - } else if (0 == retval) { - if (0 == offset) { - printk("acx: firmware image file " - "'%s' is empty?!\n", file); - } - } else if (0 < retval) { - /* allocate result buffer here if needed, - * since we don't want to waste resources/time - * (in case file opening/reading fails) - * by doing allocation in front of the loop instead. */ - if (NULL == res) { - *size = 8 + le32_to_cpu(*(u32 *)(4 + buffer)); - - res = vmalloc(*size); - if (NULL == res) { - printk("acx: unable to " - "allocate %u bytes for " - "firmware module upload\n", - *size); - goto fail_close; - } - acxlog(L_DEBUG, "allocated %u bytes " - "for firmware module loading\n", - *size); - } - if ((unlikely(offset + retval > *size))) { - printk("acx: ERROR: allocation " - "was less than firmware image size?!\n"); - goto fail_close; - } - memcpy((u8*)res + offset, buffer, retval); - offset += retval; - } - } while (0 < retval); - -fail_close: - retval = filp_close(inf, NULL); - - if (unlikely(retval)) { - printk("acx: error %d closing file '%s'\n", -retval, file); - } - - if (unlikely((NULL != res) && (offset != le32_to_cpu(res->size) + 8))) { - printk("acx: firmware is reporting a different size " - "(0x%08X; 0x%08X was read)\n", - le32_to_cpu(res->size) + 8, offset); - vfree(res); - res = NULL; - } - -fail: - if (page) - free_page(page); - set_fs(orgfs); -#endif - - /* checksum will be verified in write_fw, so don't bother here */ - return res; -} - - -#ifdef POWER_SAVE_80211 -/*---------------------------------------------------------------- -* acx_s_activate_power_save_mode -*----------------------------------------------------------------*/ -static void -acx_s_activate_power_save_mode(wlandevice_t *priv) -{ - acx100_ie_powermgmt_t pm; - - FN_ENTER; - - acx_s_interrogate(priv, &pm, ACX1xx_IE_POWER_MGMT); - if (pm.wakeup_cfg != 0x81) - goto end; - - pm.wakeup_cfg = 0; - pm.options = 0; - pm.hangover_period = 0; - acx_s_configure(priv, &pm, ACX1xx_IE_POWER_MGMT); -end: - FN_EXIT0; -} -#endif - - -/*********************************************************************** -** acx_s_set_wepkey -*/ -static void -acx100_s_set_wepkey(wlandevice_t *priv) -{ - ie_dot11WEPDefaultKey_t dk; - int i; - - for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) { - if (priv->wep_keys[i].size != 0) { - acxlog(L_INIT, "setting WEP key: %d with " - "total size: %d\n", i, (int) priv->wep_keys[i].size); - dk.action = 1; - dk.keySize = priv->wep_keys[i].size; - dk.defaultKeyNum = i; - memcpy(dk.key, priv->wep_keys[i].key, dk.keySize); - acx_s_configure(priv, &dk, ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE); - } - } -} - -static void -acx111_s_set_wepkey(wlandevice_t *priv) -{ - acx111WEPDefaultKey_t dk; - int i; - - for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) { - if (priv->wep_keys[i].size != 0) { - acxlog(L_INIT, "setting WEP key: %d with " - "total size: %d\n", i, (int) priv->wep_keys[i].size); - memset(&dk, 0, sizeof(dk)); - dk.action = cpu_to_le16(1); /* "add key"; yes, that's a 16bit value */ - dk.keySize = priv->wep_keys[i].size; - - /* are these two lines necessary? */ - dk.type = 0; /* default WEP key */ - dk.index = 0; /* ignored when setting default key */ - - dk.defaultKeyNum = i; - memcpy(dk.key, priv->wep_keys[i].key, dk.keySize); - acx_s_issue_cmd(priv, ACX1xx_CMD_WEP_MGMT, &dk, sizeof(dk)); - } - } -} - -static void -acx_s_set_wepkey(wlandevice_t *priv) -{ - if (IS_ACX111(priv)) - acx111_s_set_wepkey(priv); - else - acx100_s_set_wepkey(priv); -} - - -/*********************************************************************** -** acx100_s_init_wep -** -** FIXME: this should probably be moved into the new card settings -** management, but since we're also modifying the memory map layout here -** due to the WEP key space we want, we should take care... -*/ -int -acx100_s_init_wep(wlandevice_t *priv) -{ -/* int i; - acx100_cmd_wep_mgmt_t wep_mgmt; size = 37 bytes */ - acx100_ie_wep_options_t options; - ie_dot11WEPDefaultKeyID_t dk; - acx_ie_memmap_t pt; - int res = NOT_OK; - - FN_ENTER; - - if (OK != acx_s_interrogate(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { - goto fail; - } - - acxlog(L_DEBUG, "CodeEnd:%X\n", pt.CodeEnd); - - pt.WEPCacheStart = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4); - pt.WEPCacheEnd = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4); - - if (OK != acx_s_configure(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { - goto fail; - } - - /* let's choose maximum setting: 4 default keys, plus 10 other keys: */ - options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10); - options.WEPOption = 0x00; - - acxlog(L_ASSOC, "%s: writing WEP options\n", __func__); - acx_s_configure(priv, &options, ACX100_IE_WEP_OPTIONS); - - acx100_s_set_wepkey(priv); - - if (priv->wep_keys[priv->wep_current_index].size != 0) { - acxlog(L_ASSOC, "setting active default WEP key number: %d\n", - priv->wep_current_index); - dk.KeyID = priv->wep_current_index; - acx_s_configure(priv, &dk, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); /* 0x1010 */ - } - /* FIXME!!! wep_key_struct is filled nowhere! But priv - * is initialized to 0, and we don't REALLY need those keys either */ -/* for (i = 0; i < 10; i++) { - if (priv->wep_key_struct[i].len != 0) { - MAC_COPY(wep_mgmt.MacAddr, priv->wep_key_struct[i].addr); - wep_mgmt.KeySize = cpu_to_le16(priv->wep_key_struct[i].len); - memcpy(&wep_mgmt.Key, priv->wep_key_struct[i].key, le16_to_cpu(wep_mgmt.KeySize)); - wep_mgmt.Action = cpu_to_le16(1); - acxlog(L_ASSOC, "writing WEP key %d (len %d)\n", i, le16_to_cpu(wep_mgmt.KeySize)); - if (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_WEP_MGMT, &wep_mgmt, sizeof(wep_mgmt))) { - priv->wep_key_struct[i].index = i; - } - } - } */ - - /* now retrieve the updated WEPCacheEnd pointer... */ - if (OK != acx_s_interrogate(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { - printk("%s: ACX1xx_IE_MEMORY_MAP read #2 FAILED\n", - priv->netdev->name); - goto fail; - } - /* ...and tell it to start allocating templates at that location */ - /* (no endianness conversion needed) */ - pt.PacketTemplateStart = pt.WEPCacheEnd; - - if (OK != acx_s_configure(priv, &pt, ACX1xx_IE_MEMORY_MAP)) { - printk("%s: ACX1xx_IE_MEMORY_MAP write #2 FAILED\n", - priv->netdev->name); - goto fail; - } - res = OK; - -fail: - FN_EXIT1(res); - return res; -} - - -/*********************************************************************** -*/ -static int -acx_s_init_max_null_data_template(wlandevice_t *priv) -{ - struct acx_template_nullframe b; - int result; - - FN_ENTER; - memset(&b, 0, sizeof(b)); - b.size = cpu_to_le16(sizeof(b) - 2); - result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_NULL_DATA, &b, sizeof(b)); - FN_EXIT1(result); - return result; -} - - -/*********************************************************************** -** acx_s_init_max_beacon_template -*/ -static int -acx_s_init_max_beacon_template(wlandevice_t *priv) -{ - struct acx_template_beacon b; - int result; - - FN_ENTER; - memset(&b, 0, sizeof(b)); - b.size = cpu_to_le16(sizeof(b) - 2); - result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_BEACON, &b, sizeof(b)); - - FN_EXIT1(result); - return result; -} - -/*********************************************************************** -** acx_s_init_max_tim_template -*/ -static int -acx_s_init_max_tim_template(wlandevice_t *priv) -{ - acx_template_tim_t t; - - memset(&t, 0, sizeof(t)); - t.size = cpu_to_le16(sizeof(t) - 2); - return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t)); -} - - -/*********************************************************************** -** acx_s_init_max_probe_response_template -*/ -static int -acx_s_init_max_probe_response_template(wlandevice_t *priv) -{ - struct acx_template_proberesp pr; - - memset(&pr, 0, sizeof(pr)); - pr.size = cpu_to_le16(sizeof(pr) - 2); - - return acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, sizeof(pr)); -} - - -/*********************************************************************** -** acx_s_init_max_probe_request_template -*/ -static int -acx_s_init_max_probe_request_template(wlandevice_t *priv) -{ - union { - acx100_template_probereq_t p100; - acx111_template_probereq_t p111; - } pr; - int res; - - FN_ENTER; - memset(&pr, 0, sizeof(pr)); - pr.p100.size = cpu_to_le16(sizeof(pr) - 2); - res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &pr, sizeof(pr)); - FN_EXIT1(res); - return res; -} - - -/*********************************************************************** -** acx_s_set_tim_template -** -** In full blown driver we will regularly update partial virtual bitmap -** by calling this function -** (it can be done by irq handler on each DTIM irq or by timer...) - -[802.11 7.3.2.6] TIM information element: -- 1 EID -- 1 Length -1 1 DTIM Count - indicates how many beacons (including this) appear before next DTIM - (0=this one is a DTIM) -2 1 DTIM Period - number of beacons between successive DTIMs - (0=reserved, 1=all TIMs are DTIMs, 2=every other, etc) -3 1 Bitmap Control - bit0: Traffic Indicator bit associated with Assoc ID 0 (Bcast AID?) - set to 1 in TIM elements with a value of 0 in the DTIM Count field - when one or more broadcast or multicast frames are buffered at the AP. - bit1-7: Bitmap Offset (logically Bitmap_Offset = Bitmap_Control & 0xFE). -4 n Partial Virtual Bitmap - Visible part of traffic-indication bitmap. - Full bitmap consists of 2008 bits (251 octets) such that bit number N - (0<=N<=2007) in the bitmap corresponds to bit number (N mod 8) - in octet number N/8 where the low-order bit of each octet is bit0, - and the high order bit is bit7. - Each set bit in virtual bitmap corresponds to traffic buffered by AP - for a specific station (with corresponding AID?). - Partial Virtual Bitmap shows a part of bitmap which has non-zero. - Bitmap Offset is a number of skipped zero octets (see above). - 'Missing' octets at the tail are also assumed to be zero. - Example: Length=6, Bitmap_Offset=2, Partial_Virtual_Bitmap=55 55 55 - This means that traffic-indication bitmap is: - 00000000 00000000 01010101 01010101 01010101 00000000 00000000... - (is bit0 in the map is always 0 and real value is in Bitmap Control bit0?) -*/ -static int -acx_s_set_tim_template(wlandevice_t *priv) -{ -/* For now, configure smallish test bitmap, all zero ("no pending data") */ - enum { bitmap_size = 5 }; - - acx_template_tim_t t; - int result; - - FN_ENTER; - - memset(&t, 0, sizeof(t)); - t.size = 5 + bitmap_size; /* eid+len+count+period+bmap_ctrl + bmap */ - t.tim_eid = WLAN_EID_TIM; - t.len = 3 + bitmap_size; /* count+period+bmap_ctrl + bmap */ - result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t)); - FN_EXIT1(result); - return result; -} - - -/*********************************************************************** -** acx_fill_beacon_or_proberesp_template -** -** For frame format info, please see 802.11-1999.pdf item 7.2.3.9 and below!! -** -** WARNING/FIXME/TODO: this needs to be called (via SET_TEMPLATES) *whenever* -** *any* of the parameters contained in it change!!! -** fishy status fixed -** -** NB: we use the fact that -** struct acx_template_proberesp and struct acx_template_beacon are the same -** (well, almost...) -** -** [802.11] Beacon's body consist of these IEs: -** 1 Timestamp -** 2 Beacon interval -** 3 Capability information -** 4 SSID -** 5 Supported rates (up to 8 rates) -** 6 FH Parameter Set (frequency-hopping PHYs only) -** 7 DS Parameter Set (direct sequence PHYs only) -** 8 CF Parameter Set (only if PCF is supported) -** 9 IBSS Parameter Set (ad-hoc only) -** -** Beacon only: -** 10 TIM (AP only) (see 802.11 7.3.2.6) -** 11 Country Information (802.11d) -** 12 FH Parameters (802.11d) -** 13 FH Pattern Table (802.11d) -** ... (?!! did not yet find relevant PDF file... --vda) -** 19 ERP Information (extended rate PHYs) -** 20 Extended Supported Rates (if more than 8 rates) -** -** Proberesp only: -** 10 Country information (802.11d) -** 11 FH Parameters (802.11d) -** 12 FH Pattern Table (802.11d) -** 13-n Requested information elements (802.11d) -** ???? -** 18 ERP Information (extended rate PHYs) -** 19 Extended Supported Rates (if more than 8 rates) -*/ -static int -acx_fill_beacon_or_proberesp_template(wlandevice_t *priv, - struct acx_template_beacon *templ, - u16 fc /* in host order! */) -{ - int len; - u8 *p; - - FN_ENTER; - - memset(templ, 0, sizeof(*templ)); - MAC_BCAST(templ->da); - MAC_COPY(templ->sa, priv->dev_addr); - MAC_COPY(templ->bssid, priv->bssid); - - templ->beacon_interval = cpu_to_le16(priv->beacon_interval); - acx_update_capabilities(priv); - templ->cap = cpu_to_le16(priv->capabilities); - - p = templ->variable; - p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid); - p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported); - p = wlan_fill_ie_ds_parms(p, priv->channel); - /* NB: should go AFTER tim, but acx seem to keep tim last always */ - p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported); - - switch (priv->mode) { - case ACX_MODE_0_ADHOC: - /* ATIM window */ - p = wlan_fill_ie_ibss_parms(p, 0); break; - case ACX_MODE_3_AP: - /* TIM IE is set up as separate template */ - break; - } - - len = p - (u8*)templ; - templ->fc = cpu_to_le16(WF_FTYPE_MGMT | fc); - /* - 2: do not count 'u16 size' field */ - templ->size = cpu_to_le16(len - 2); - - FN_EXIT1(len); - return len; -} - - -/*********************************************************************** -** acx_s_set_beacon_template -*/ -static int -acx_s_set_beacon_template(wlandevice_t *priv) -{ - struct acx_template_beacon bcn; - int len, result; - - FN_ENTER; - - len = acx_fill_beacon_or_proberesp_template(priv, &bcn, WF_FSTYPE_BEACON); - result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_BEACON, &bcn, len); - - FN_EXIT1(result); - return result; -} - - -/*********************************************************************** -** acx_s_set_probe_response_template -*/ -static int -acx_s_set_probe_response_template(wlandevice_t *priv) -{ - struct acx_template_proberesp pr; - int len, result; - - FN_ENTER; - - len = acx_fill_beacon_or_proberesp_template(priv, &pr, WF_FSTYPE_PROBERESP); - result = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, len); - - FN_EXIT1(result); - return result; -} - - -/*********************************************************************** -** acx100_s_init_packet_templates() -** -** NOTE: order is very important here, to have a correct memory layout! -** init templates: max Probe Request (station mode), max NULL data, -** max Beacon, max TIM, max Probe Response. -*/ -int -acx100_s_init_packet_templates(wlandevice_t *priv) -{ - acx_ie_memmap_t mm; - int result = NOT_OK; - - FN_ENTER; - - acxlog(L_DEBUG, "sizeof(memmap)=%d bytes\n", (int)sizeof(mm)); - - /* acx100 still do not emit probe requests, thus this call - ** is sourt of not needed. But we want it to work someday */ - if (OK != acx_s_init_max_probe_request_template(priv)) - goto failed; - -#ifdef NOT_WORKING_YET - /* FIXME: creating the NULL data template breaks - * communication right now, needs further testing. - * Also, need to set the template once we're joining a network. */ - if (OK != acx_s_init_max_null_data_template(priv)) - goto failed; -#endif - - if (OK != acx_s_init_max_beacon_template(priv)) - goto failed; - - /* TODO: beautify code by moving init_tim down just before set_tim */ - if (OK != acx_s_init_max_tim_template(priv)) - goto failed; - - if (OK != acx_s_init_max_probe_response_template(priv)) - goto failed; - - if (OK != acx_s_set_tim_template(priv)) - goto failed; - - if (OK != acx_s_interrogate(priv, &mm, ACX1xx_IE_MEMORY_MAP)) { - goto failed; - } - - mm.QueueStart = cpu_to_le32(le32_to_cpu(mm.PacketTemplateEnd) + 4); - if (OK != acx_s_configure(priv, &mm, ACX1xx_IE_MEMORY_MAP)) { - goto failed; - } - - result = OK; - goto success; - -failed: - acxlog(L_DEBUG | L_INIT, - /* "cb=0x%X\n" */ - "pACXMemoryMap:\n" - ".CodeStart=0x%X\n" - ".CodeEnd=0x%X\n" - ".WEPCacheStart=0x%X\n" - ".WEPCacheEnd=0x%X\n" - ".PacketTemplateStart=0x%X\n" - ".PacketTemplateEnd=0x%X\n", - /* len, */ - le32_to_cpu(mm.CodeStart), - le32_to_cpu(mm.CodeEnd), - le32_to_cpu(mm.WEPCacheStart), - le32_to_cpu(mm.WEPCacheEnd), - le32_to_cpu(mm.PacketTemplateStart), - le32_to_cpu(mm.PacketTemplateEnd)); - -success: - FN_EXIT1(result); - return result; -} - -int -acx111_s_init_packet_templates(wlandevice_t *priv) -{ - int result = NOT_OK; - - FN_ENTER; - - acxlog(L_DEBUG | L_INIT, "initializing max packet templates\n"); - - if (OK != acx_s_init_max_probe_request_template(priv)) - goto failed; - - if (OK != acx_s_init_max_null_data_template(priv)) - goto failed; - - if (OK != acx_s_init_max_beacon_template(priv)) - goto failed; - - if (OK != acx_s_init_max_tim_template(priv)) - goto failed; - - if (OK != acx_s_init_max_probe_response_template(priv)) - goto failed; - - /* the other templates will be set later (acx_start) */ - /* - if (OK != acx_s_set_tim_template(priv)) - goto failed;*/ - - result = OK; - goto success; - -failed: - printk("%s: acx111_init_packet_templates() FAILED\n", priv->netdev->name); - -success: - FN_EXIT1(result); - return result; -} - - -/*********************************************************************** -*/ -static int -acx100_s_set_probe_request_template(wlandevice_t *priv) -{ - struct acx100_template_probereq probereq; - char *p; - int res; - int frame_len; - - FN_ENTER; - - memset(&probereq, 0, sizeof(probereq)); - - probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi; - MAC_BCAST(probereq.da); - MAC_COPY(probereq.sa, priv->dev_addr); - MAC_BCAST(probereq.bssid); - - probereq.beacon_interval = cpu_to_le16(priv->beacon_interval); - acx_update_capabilities(priv); - probereq.cap = cpu_to_le16(priv->capabilities); - - p = probereq.variable; - acxlog(L_ASSOC, "SSID='%s' len=%d\n", priv->essid, priv->essid_len); - p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid); - p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported); - /* FIXME: should these be here or AFTER ds_parms? */ - p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported); - /* HUH?? who said it must be here? I've found nothing in 802.11! --vda*/ - /* p = wlan_fill_ie_ds_parms(p, priv->channel); */ - frame_len = p - (char*)&probereq; - probereq.size = frame_len - 2; - - res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len); - FN_EXIT0; - return res; -} - -static int -acx111_s_set_probe_request_template(wlandevice_t *priv) -{ - struct acx111_template_probereq probereq; - char *p; - int res; - int frame_len; - - FN_ENTER; - - memset(&probereq, 0, sizeof(probereq)); - - probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi; - MAC_BCAST(probereq.da); - MAC_COPY(probereq.sa, priv->dev_addr); - MAC_BCAST(probereq.bssid); - - p = probereq.variable; - p = wlan_fill_ie_ssid(p, priv->essid_len, priv->essid); - p = wlan_fill_ie_rates(p, priv->rate_supported_len, priv->rate_supported); - p = wlan_fill_ie_rates_ext(p, priv->rate_supported_len, priv->rate_supported); - frame_len = p - (char*)&probereq; - probereq.size = frame_len - 2; - - res = acx_s_issue_cmd(priv, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len); - FN_EXIT0; - return res; -} - -static int -acx_s_set_probe_request_template(wlandevice_t *priv) -{ - if (IS_ACX111(priv)) { - return acx111_s_set_probe_request_template(priv); - } else { - return acx100_s_set_probe_request_template(priv); - } -} - - -/*********************************************************************** -*/ -int -acx111_s_get_feature_config(wlandevice_t *priv, - u32 *feature_options, u32 *data_flow_options) -{ - struct ACX111FeatureConfig fc; - - if (priv->chip_type != CHIPTYPE_ACX111) { - return NOT_OK; - } - - memset(&fc, 0, sizeof(struct ACX111FeatureConfig)); - - if (OK != acx_s_interrogate(priv, &fc, ACX1xx_IE_FEATURE_CONFIG)) { - return NOT_OK; - } - acxlog(L_DEBUG, - "got Feature option:0x%X, DataFlow option: 0x%X\n", - fc.feature_options, - fc.data_flow_options); - - if (feature_options) - *feature_options = le32_to_cpu(fc.feature_options); - if (data_flow_options) - *data_flow_options = le32_to_cpu(fc.data_flow_options); - - return OK; -} - -int -acx111_s_set_feature_config(wlandevice_t *priv, - u32 feature_options, u32 data_flow_options, - unsigned int mode /* 0 == remove, 1 == add, 2 == set */) -{ - struct ACX111FeatureConfig fc; - - if (priv->chip_type != CHIPTYPE_ACX111) { - return NOT_OK; - } - - if ((mode < 0) || (mode > 2)) - return NOT_OK; - - if (mode != 2) - /* need to modify old data */ - acx111_s_get_feature_config(priv, &fc.feature_options, &fc.data_flow_options); - else { - /* need to set a completely new value */ - fc.feature_options = 0; - fc.data_flow_options = 0; - } - - if (mode == 0) { /* remove */ - CLEAR_BIT(fc.feature_options, cpu_to_le32(feature_options)); - CLEAR_BIT(fc.data_flow_options, cpu_to_le32(data_flow_options)); - } else { /* add or set */ - SET_BIT(fc.feature_options, cpu_to_le32(feature_options)); - SET_BIT(fc.data_flow_options, cpu_to_le32(data_flow_options)); - } - - acxlog(L_DEBUG, - "old: feature 0x%08X dataflow 0x%08X. mode: %u\n" - "new: feature 0x%08X dataflow 0x%08X\n", - feature_options, data_flow_options, mode, - le32_to_cpu(fc.feature_options), - le32_to_cpu(fc.data_flow_options)); - - if (OK != acx_s_configure(priv, &fc, ACX1xx_IE_FEATURE_CONFIG)) { - return NOT_OK; - } - - return OK; -} - - -/*********************************************************************** -*/ -int -acx_s_recalib_radio(wlandevice_t *priv) -{ - if (IS_ACX111(priv)) { - acx111_cmd_radiocalib_t cal; - - printk("%s: recalibrating radio\n", priv->netdev->name); - /* automatic recalibration, choose all methods: */ - cal.methods = cpu_to_le32(0x8000000f); - /* automatic recalibration every 60 seconds (value in TUs) - * FIXME: what is the firmware default here?? */ - cal.interval = cpu_to_le32(58594); - return acx_s_issue_cmd_timeo(priv, ACX111_CMD_RADIOCALIB, - &cal, sizeof(cal), CMD_TIMEOUT_MS(100)); - } else { - if (/* (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0)) && - (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0)) && */ - (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_TX, &(priv->channel), 1)) && - (OK == acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_RX, &(priv->channel), 1)) ) - return OK; - return NOT_OK; - } -} - - -/*********************************************************************** -** acx_s_cmd_start_scan -** -** Issue scan command to the hardware -*/ -static void -acx100_s_scan_chan(wlandevice_t *priv) -{ - acx100_scan_t s; - - FN_ENTER; - - memset(&s, 0, sizeof(s)); - s.count = cpu_to_le16(priv->scan_count); - s.start_chan = cpu_to_le16(1); - s.flags = cpu_to_le16(0x8000); - s.max_rate = priv->scan_rate; - s.options = priv->scan_mode; - s.chan_duration = cpu_to_le16(priv->scan_duration); - s.max_probe_delay = cpu_to_le16(priv->scan_probe_delay); - - acx_s_issue_cmd(priv, ACX1xx_CMD_SCAN, &s, sizeof(s)); - FN_EXIT0; -} - -static void -acx111_s_scan_chan(wlandevice_t *priv) -{ - acx111_scan_t s; - - FN_ENTER; - - memset(&s, 0, sizeof(s)); - s.count = cpu_to_le16(priv->scan_count); - s.channel_list_select = 0; /* scan every allowed channel */ - /*s.channel_list_select = 1;*/ /* scan given channels */ - s.rate = priv->scan_rate; - s.options = priv->scan_mode; - s.chan_duration = cpu_to_le16(priv->scan_duration); - s.max_probe_delay = cpu_to_le16(priv->scan_probe_delay); - /*s.modulation = 0x40;*/ /* long preamble? OFDM? -> only for active scan */ - s.modulation = 0; - /*s.channel_list[0] = 6; - s.channel_list[1] = 4;*/ - - acx_s_issue_cmd(priv, ACX1xx_CMD_SCAN, &s, sizeof(s)); - FN_EXIT0; -} - -void -acx_s_cmd_start_scan(wlandevice_t *priv) -{ - /* time_before check is 'just in case' thing */ - if (!(priv->irq_status & HOST_INT_SCAN_COMPLETE) - && time_before(jiffies, priv->scan_start + 10*HZ) - ) { - acxlog(L_INIT, "start_scan: seems like previous scan " - "is still running. Not starting anew. Please report\n"); - return; - } - - acxlog(L_INIT, "starting radio scan\n"); - /* remember that fw is commanded to do scan */ - priv->scan_start = jiffies; - CLEAR_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE); - /* issue it */ - if (IS_ACX100(priv)) { - acx100_s_scan_chan(priv); - } else { - acx111_s_scan_chan(priv); - } -} - - -/*********************************************************************** -** acx_s_update_card_settings -** -** Applies accumulated changes in various priv->xxxx members -** Called by ioctl commit handler, acx_start, acx_set_defaults, -** acx_s_after_interrupt_task (if IRQ_CMD_UPDATE_CARD_CFG), -*/ -static void -acx111_s_sens_radio_16_17(wlandevice_t *priv) -{ - u32 feature1, feature2; - - if ((priv->sensitivity < 1) || (priv->sensitivity > 3)) { - printk("%s: invalid sensitivity setting (1..3), " - "setting to 1\n", priv->netdev->name); - priv->sensitivity = 1; - } - acx111_s_get_feature_config(priv, &feature1, &feature2); - CLEAR_BIT(feature1, FEATURE1_LOW_RX|FEATURE1_EXTRA_LOW_RX); - if (priv->sensitivity > 1) - SET_BIT(feature1, FEATURE1_LOW_RX); - if (priv->sensitivity > 2) - SET_BIT(feature1, FEATURE1_EXTRA_LOW_RX); - acx111_s_feature_set(priv, feature1, feature2); -} - -void -acx_s_update_card_settings(wlandevice_t *priv, int get_all, int set_all) -{ - unsigned long flags; - unsigned int start_scan = 0; - int i; - - FN_ENTER; - - if (get_all) - SET_BIT(priv->get_mask, GETSET_ALL); - if (set_all) - SET_BIT(priv->set_mask, GETSET_ALL); - /* Why not just set masks to 0xffffffff? We can get rid of GETSET_ALL */ - - acxlog(L_INIT, "get_mask 0x%08X, set_mask 0x%08X\n", - priv->get_mask, priv->set_mask); - - /* Track dependencies betweed various settings */ - - if (priv->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_WEP)) { - acxlog(L_INIT, "important setting has been changed. " - "Need to update packet templates, too\n"); - SET_BIT(priv->set_mask, SET_TEMPLATES); - } - if (priv->set_mask & (GETSET_CHANNEL|GETSET_ALL)) { - /* This will actually tune RX/TX to the channel */ - SET_BIT(priv->set_mask, GETSET_RX|GETSET_TX); - switch (priv->mode) { - case ACX_MODE_0_ADHOC: - case ACX_MODE_3_AP: - /* Beacons contain channel# - update them */ - SET_BIT(priv->set_mask, SET_TEMPLATES); - } - switch (priv->mode) { - case ACX_MODE_0_ADHOC: - case ACX_MODE_2_STA: - start_scan = 1; - } - } - - /* Apply settings */ - -#ifdef WHY_SHOULD_WE_BOTHER /* imagine we were just powered off */ - /* send a disassoc request in case it's required */ - if (priv->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_CHANNEL|GETSET_WEP|GETSET_ALL)) { - if (ACX_MODE_2_STA == priv->mode) { - if (ACX_STATUS_4_ASSOCIATED == priv->status) { - acxlog(L_ASSOC, "we were ASSOCIATED - " - "sending disassoc request\n"); - acx_lock(priv, flags); - acx_l_transmit_disassoc(priv, NULL); - /* FIXME: deauth? */ - acx_unlock(priv, flags); - } - /* need to reset some other stuff as well */ - acxlog(L_DEBUG, "resetting bssid\n"); - MAC_ZERO(priv->bssid); - SET_BIT(priv->set_mask, SET_TEMPLATES|SET_STA_LIST); - /* FIXME: should start scanning */ - start_scan = 1; - } - } -#endif - - if (priv->get_mask & (GETSET_STATION_ID|GETSET_ALL)) { - u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN]; - const u8 *paddr; - - acx_s_interrogate(priv, &stationID, ACX1xx_IE_DOT11_STATION_ID); - paddr = &stationID[4]; - for (i = 0; i < ETH_ALEN; i++) { - /* we copy the MAC address (reversed in - * the card) to the netdevice's MAC - * address, and on ifup it will be - * copied into iwpriv->dev_addr */ - priv->netdev->dev_addr[ETH_ALEN - 1 - i] = paddr[i]; - } - CLEAR_BIT(priv->get_mask, GETSET_STATION_ID); - } - - if (priv->get_mask & (GETSET_SENSITIVITY|GETSET_ALL)) { - if ((RADIO_RFMD_11 == priv->radio_type) - || (RADIO_MAXIM_0D == priv->radio_type) - || (RADIO_RALINK_15 == priv->radio_type)) { - acx_s_read_phy_reg(priv, 0x30, &priv->sensitivity); - } else { - acxlog(L_INIT, "don't know how to get sensitivity " - "for radio type 0x%02X\n", priv->radio_type); - priv->sensitivity = 0; - } - acxlog(L_INIT, "got sensitivity value %u\n", priv->sensitivity); - - CLEAR_BIT(priv->get_mask, GETSET_SENSITIVITY); - } - - if (priv->get_mask & (GETSET_ANTENNA|GETSET_ALL)) { - u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN]; - - memset(antenna, 0, sizeof(antenna)); - acx_s_interrogate(priv, antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA); - priv->antenna = antenna[4]; - acxlog(L_INIT, "got antenna value 0x%02X\n", priv->antenna); - CLEAR_BIT(priv->get_mask, GETSET_ANTENNA); - } - - if (priv->get_mask & (GETSET_ED_THRESH|GETSET_ALL)) { - if (IS_ACX100(priv)) { - u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN]; - - memset(ed_threshold, 0, sizeof(ed_threshold)); - acx_s_interrogate(priv, ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD); - priv->ed_threshold = ed_threshold[4]; - } else { - acxlog(L_INIT, "acx111 doesn't support ED\n"); - priv->ed_threshold = 0; - } - acxlog(L_INIT, "got Energy Detect (ED) threshold %u\n", priv->ed_threshold); - CLEAR_BIT(priv->get_mask, GETSET_ED_THRESH); - } - - if (priv->get_mask & (GETSET_CCA|GETSET_ALL)) { - if (IS_ACX100(priv)) { - u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN]; - - memset(cca, 0, sizeof(priv->cca)); - acx_s_interrogate(priv, cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE); - priv->cca = cca[4]; - } else { - acxlog(L_INIT, "acx111 doesn't support CCA\n"); - priv->cca = 0; - } - acxlog(L_INIT, "got Channel Clear Assessment (CCA) value %u\n", priv->cca); - CLEAR_BIT(priv->get_mask, GETSET_CCA); - } - - if (priv->get_mask & (GETSET_REG_DOMAIN|GETSET_ALL)) { - acx_ie_generic_t dom; - - acx_s_interrogate(priv, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN); - priv->reg_dom_id = dom.m.bytes[0]; - /* FIXME: should also set chanmask somehow */ - acxlog(L_INIT, "got regulatory domain 0x%02X\n", priv->reg_dom_id); - CLEAR_BIT(priv->get_mask, GETSET_REG_DOMAIN); - } - - if (priv->set_mask & (GETSET_STATION_ID|GETSET_ALL)) { - u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN]; - u8 *paddr; - - paddr = &stationID[4]; - for (i = 0; i < ETH_ALEN; i++) { - /* copy the MAC address we obtained when we noticed - * that the ethernet iface's MAC changed - * to the card (reversed in - * the card!) */ - paddr[i] = priv->dev_addr[ETH_ALEN - 1 - i]; - } - acx_s_configure(priv, &stationID, ACX1xx_IE_DOT11_STATION_ID); - CLEAR_BIT(priv->set_mask, GETSET_STATION_ID); - } - - if (priv->set_mask & (SET_TEMPLATES|GETSET_ALL)) { - acxlog(L_INIT, "updating packet templates\n"); - /* Doesn't work for acx100, do it only for acx111 for now */ - if (IS_ACX111(priv)) { - switch (priv->mode) { - case ACX_MODE_0_ADHOC: - case ACX_MODE_2_STA: - if (OK != acx_s_set_probe_request_template(priv)) - acxlog(L_INIT, "acx_set_probe_request_template FAILED\n"); - } - } - switch (priv->mode) { - case ACX_MODE_0_ADHOC: - case ACX_MODE_3_AP: - /* FIXME: why only for AP? STA need probe req templates... */ - if (OK != acx_s_set_beacon_template(priv)) - acxlog(L_INIT, "acx_set_beacon_template FAILED\n"); - if (OK != acx_s_set_tim_template(priv)) - acxlog(L_INIT, "acx_set_tim_template FAILED\n"); - /* BTW acx111 firmware would not send probe responses - ** if probe request does not have all basic rates flagged - ** by 0x80! Thus firmware does not conform to 802.11, - ** it should ignore 0x80 bit in ratevector from STA. - ** We can 'fix' it by not using this template and - ** sending probe responses by hand. TODO --vda */ - if (OK != acx_s_set_probe_response_template(priv)) - acxlog(L_INIT, "acx_set_probe_response_template FAILED\n"); - } - /* Needed if generated frames are to be emitted at different tx rate now */ - acxlog(L_IRQ, "redoing cmd_join_bssid() after template cfg\n"); - acx_s_cmd_join_bssid(priv, priv->bssid); - CLEAR_BIT(priv->set_mask, SET_TEMPLATES); - } - if (priv->set_mask & (SET_STA_LIST|GETSET_ALL)) { - /* TODO insert a sweet if here */ - acx_lock(priv, flags); - acx_l_sta_list_init(priv); - CLEAR_BIT(priv->set_mask, SET_STA_LIST); - acx_unlock(priv, flags); - } - if (priv->set_mask & (SET_RATE_FALLBACK|GETSET_ALL)) { - u8 rate[4 + ACX1xx_IE_RATE_FALLBACK_LEN]; - - /* configure to not do fallbacks when not in auto rate mode */ - rate[4] = (priv->rate_auto) ? /* priv->txrate_fallback_retries */ 1 : 0; - acxlog(L_INIT, "updating Tx fallback to %u retries\n", rate[4]); - acx_s_configure(priv, &rate, ACX1xx_IE_RATE_FALLBACK); - CLEAR_BIT(priv->set_mask, SET_RATE_FALLBACK); - } - if (priv->set_mask & (GETSET_TXPOWER|GETSET_ALL)) { - acxlog(L_INIT, "updating transmit power: %u dBm\n", - priv->tx_level_dbm); - acx_s_set_tx_level(priv, priv->tx_level_dbm); - CLEAR_BIT(priv->set_mask, GETSET_TXPOWER); - } - - if (priv->set_mask & (GETSET_SENSITIVITY|GETSET_ALL)) { - acxlog(L_INIT, "updating sensitivity value: %u\n", - priv->sensitivity); - switch (priv->radio_type) { - case RADIO_RFMD_11: - case RADIO_MAXIM_0D: - case RADIO_RALINK_15: - acx_s_write_phy_reg(priv, 0x30, priv->sensitivity); - break; - case RADIO_RADIA_16: - case RADIO_UNKNOWN_17: - acx111_s_sens_radio_16_17(priv); - break; - default: - acxlog(L_INIT, "don't know how to modify sensitivity " - "for radio type 0x%02X\n", priv->radio_type); - } - CLEAR_BIT(priv->set_mask, GETSET_SENSITIVITY); - } - - if (priv->set_mask & (GETSET_ANTENNA|GETSET_ALL)) { - /* antenna */ - u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN]; - - memset(antenna, 0, sizeof(antenna)); - antenna[4] = priv->antenna; - acxlog(L_INIT, "updating antenna value: 0x%02X\n", - priv->antenna); - acx_s_configure(priv, &antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA); - CLEAR_BIT(priv->set_mask, GETSET_ANTENNA); - } - - if (priv->set_mask & (GETSET_ED_THRESH|GETSET_ALL)) { - /* ed_threshold */ - acxlog(L_INIT, "updating Energy Detect (ED) threshold: %u\n", - priv->ed_threshold); - if (IS_ACX100(priv)) { - u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN]; - - memset(ed_threshold, 0, sizeof(ed_threshold)); - ed_threshold[4] = priv->ed_threshold; - acx_s_configure(priv, &ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD); - } - else - acxlog(L_INIT, "ACX111 doesn't support ED!\n"); - CLEAR_BIT(priv->set_mask, GETSET_ED_THRESH); - } - - if (priv->set_mask & (GETSET_CCA|GETSET_ALL)) { - /* CCA value */ - acxlog(L_INIT, "updating Channel Clear Assessment " - "(CCA) value: 0x%02X\n", priv->cca); - if (IS_ACX100(priv)) { - u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN]; - - memset(cca, 0, sizeof(cca)); - cca[4] = priv->cca; - acx_s_configure(priv, &cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE); - } - else - acxlog(L_INIT, "acx111 doesn't support CCA!\n"); - CLEAR_BIT(priv->set_mask, GETSET_CCA); - } - - if (priv->set_mask & (GETSET_LED_POWER|GETSET_ALL)) { - /* Enable Tx */ - acxlog(L_INIT, "updating power LED status: %u\n", priv->led_power); - - acx_lock(priv, flags); - if (IS_PCI(priv)) - acx_l_power_led(priv, priv->led_power); - CLEAR_BIT(priv->set_mask, GETSET_LED_POWER); - acx_unlock(priv, flags); - } - -/* this seems to cause Tx lockup after some random time (Tx error 0x20), - * so let's disable it for now until further investigation */ -/* Maybe fixed now after locking is fixed. Need to retest */ -#ifdef POWER_SAVE_80211 - if (priv->set_mask & (GETSET_POWER_80211|GETSET_ALL)) { - acx100_ie_powermgmt_t pm; - - /* change 802.11 power save mode settings */ - acxlog(L_INIT, "updating 802.11 power save mode settings: " - "wakeup_cfg 0x%02X, listen interval %u, " - "options 0x%02X, hangover period %u, " - "enhanced_ps_transition_time %d\n", - priv->ps_wakeup_cfg, priv->ps_listen_interval, - priv->ps_options, priv->ps_hangover_period, - priv->ps_enhanced_transition_time); - acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT); - acxlog(L_INIT, "Previous PS mode settings: wakeup_cfg 0x%02X, " - "listen interval %u, options 0x%02X, " - "hangover period %u, " - "enhanced_ps_transition_time %d\n", - pm.wakeup_cfg, pm.listen_interval, pm.options, - pm.hangover_period, pm.enhanced_ps_transition_time); - pm.wakeup_cfg = priv->ps_wakeup_cfg; - pm.listen_interval = priv->ps_listen_interval; - pm.options = priv->ps_options; - pm.hangover_period = priv->ps_hangover_period; - pm.enhanced_ps_transition_time = cpu_to_le16(priv->ps_enhanced_transition_time); - acx_s_configure(priv, &pm, ACX100_IE_POWER_MGMT); - acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT); - acxlog(L_INIT, "wakeup_cfg: 0x%02X\n", pm.wakeup_cfg); - acx_s_msleep(40); - acx_s_interrogate(priv, &pm, ACX100_IE_POWER_MGMT); - acxlog(L_INIT, "power save mode change %s\n", - (pm.wakeup_cfg & PS_CFG_PENDING) ? "FAILED" : "was successful"); - /* FIXME: maybe verify via PS_CFG_PENDING bit here - * that power save mode change was successful. */ - /* FIXME: we shouldn't trigger a scan immediately after - * fiddling with power save mode (since the firmware is sending - * a NULL frame then). Does this need locking?? */ - CLEAR_BIT(priv->set_mask, GETSET_POWER_80211); - } -#endif - - if (priv->set_mask & (GETSET_CHANNEL|GETSET_ALL)) { - /* channel */ - acxlog(L_INIT, "updating channel to: %u\n", priv->channel); - CLEAR_BIT(priv->set_mask, GETSET_CHANNEL); - } - - if (priv->set_mask & (GETSET_TX|GETSET_ALL)) { - /* set Tx */ - acxlog(L_INIT, "updating: %s Tx\n", - priv->tx_disabled ? "disable" : "enable"); - if (priv->tx_disabled) - acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0); - /* ^ */ - /* FIXME: this used to be 1, but since we don't transfer a parameter... */ - else - acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_TX, &(priv->channel), 1); - CLEAR_BIT(priv->set_mask, GETSET_TX); - } - - if (priv->set_mask & (GETSET_RX|GETSET_ALL)) { - /* Enable Rx */ - acxlog(L_INIT, "updating: enable Rx on channel: %u\n", - priv->channel); - acx_s_issue_cmd(priv, ACX1xx_CMD_ENABLE_RX, &(priv->channel), 1); - CLEAR_BIT(priv->set_mask, GETSET_RX); - } - - if (priv->set_mask & (GETSET_RETRY|GETSET_ALL)) { - u8 short_retry[4 + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN]; - u8 long_retry[4 + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN]; - - acxlog(L_INIT, "updating short retry limit: %u, long retry limit: %u\n", - priv->short_retry, priv->long_retry); - short_retry[0x4] = priv->short_retry; - long_retry[0x4] = priv->long_retry; - acx_s_configure(priv, &short_retry, ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT); - acx_s_configure(priv, &long_retry, ACX1xx_IE_DOT11_LONG_RETRY_LIMIT); - CLEAR_BIT(priv->set_mask, GETSET_RETRY); - } - - if (priv->set_mask & (SET_MSDU_LIFETIME|GETSET_ALL)) { - u8 xmt_msdu_lifetime[4 + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN]; - - acxlog(L_INIT, "updating tx MSDU lifetime: %u\n", - priv->msdu_lifetime); - *(u32 *)&xmt_msdu_lifetime[4] = cpu_to_le32((u32)priv->msdu_lifetime); - acx_s_configure(priv, &xmt_msdu_lifetime, ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME); - CLEAR_BIT(priv->set_mask, SET_MSDU_LIFETIME); - } - - if (priv->set_mask & (GETSET_REG_DOMAIN|GETSET_ALL)) { - /* reg_domain */ - acx_ie_generic_t dom; - unsigned mask; - - acxlog(L_INIT, "updating regulatory domain: 0x%02X\n", - priv->reg_dom_id); - for (i = 0; i < sizeof(reg_domain_ids); i++) - if (reg_domain_ids[i] == priv->reg_dom_id) - break; - - if (sizeof(reg_domain_ids) == i) { - acxlog(L_INIT, "Invalid or unsupported regulatory " - "domain 0x%02X specified, falling back to " - "FCC (USA)! Please report if this sounds " - "fishy!\n", priv->reg_dom_id); - i = 0; - priv->reg_dom_id = reg_domain_ids[i]; - } - - priv->reg_dom_chanmask = reg_domain_channel_masks[i]; - dom.m.bytes[0] = priv->reg_dom_id; - acx_s_configure(priv, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN); - - mask = (1 << (priv->channel - 1)); - if (!(priv->reg_dom_chanmask & mask)) { - /* hmm, need to adjust our channel to reside within domain */ - mask = 1; - for (i = 1; i <= 14; i++) { - if (priv->reg_dom_chanmask & mask) { - printk("%s: adjusting " - "selected channel from %d " - "to %d due to new regulatory " - "domain\n", priv->netdev->name, - priv->channel, i); - priv->channel = i; - break; - } - mask <<= 1; - } - } - CLEAR_BIT(priv->set_mask, GETSET_REG_DOMAIN); - } - - if (priv->set_mask & (GETSET_MODE|GETSET_ALL)) { - priv->netdev->type = ARPHRD_ETHER; - - switch (priv->mode) { - case ACX_MODE_3_AP: - - acx_lock(priv, flags); - acx_l_sta_list_init(priv); - priv->aid = 0; - priv->ap_client = NULL; - MAC_COPY(priv->bssid, priv->dev_addr); - /* this basically says "we're connected" */ - acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); - acx_unlock(priv, flags); - - acx111_s_feature_off(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); - /* start sending beacons */ - acx_s_cmd_join_bssid(priv, priv->bssid); - break; - case ACX_MODE_MONITOR: - /* TODO: what exactly do we want here? */ - /* priv->netdev->type = ARPHRD_ETHER; */ - /* priv->netdev->type = ARPHRD_IEEE80211; */ - priv->netdev->type = ARPHRD_IEEE80211_PRISM; - acx111_s_feature_on(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); - /* this stops beacons */ - acx_s_cmd_join_bssid(priv, priv->bssid); - /* this basically says "we're connected" */ - acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); - SET_BIT(priv->set_mask, SET_RXCONFIG|SET_WEP_OPTIONS); - break; - case ACX_MODE_0_ADHOC: - case ACX_MODE_2_STA: - acx111_s_feature_off(priv, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); - priv->aid = 0; - priv->ap_client = NULL; - /* we want to start looking for peer or AP */ - start_scan = 1; - break; - case ACX_MODE_OFF: - /* TODO: disable RX/TX, stop any scanning activity etc: */ - /* priv->tx_disabled = 1; */ - /* SET_BIT(priv->set_mask, GETSET_RX|GETSET_TX); */ - - /* This stops beacons (invalid macmode...) */ - acx_s_cmd_join_bssid(priv, priv->bssid); - acx_set_status(priv, ACX_STATUS_0_STOPPED); - break; - } - CLEAR_BIT(priv->set_mask, GETSET_MODE); - } - - if (priv->set_mask & (SET_RXCONFIG|GETSET_ALL)) { - acx_s_initialize_rx_config(priv); - CLEAR_BIT(priv->set_mask, SET_RXCONFIG); - } - - if (priv->set_mask & (GETSET_RESCAN|GETSET_ALL)) { - switch (priv->mode) { - case ACX_MODE_0_ADHOC: - case ACX_MODE_2_STA: - start_scan = 1; - break; - } - CLEAR_BIT(priv->set_mask, GETSET_RESCAN); - } - - if (priv->set_mask & (GETSET_WEP|GETSET_ALL)) { - /* encode */ - - ie_dot11WEPDefaultKeyID_t dkey; -#ifdef DEBUG_WEP - struct { - u16 type ACX_PACKED; - u16 len ACX_PACKED; - u8 val ACX_PACKED; - } keyindic; -#endif - acxlog(L_INIT, "updating WEP key settings\n"); - - acx_s_set_wepkey(priv); - - dkey.KeyID = priv->wep_current_index; - acxlog(L_INIT, "setting WEP key %u as default\n", dkey.KeyID); - acx_s_configure(priv, &dkey, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); -#ifdef DEBUG_WEP - keyindic.val = 3; - acx_s_configure(priv, &keyindic, ACX111_IE_KEY_CHOOSE); -#endif - start_scan = 1; - CLEAR_BIT(priv->set_mask, GETSET_WEP); - } - - if (priv->set_mask & (SET_WEP_OPTIONS|GETSET_ALL)) { - acx100_ie_wep_options_t options; - - if (IS_ACX111(priv)) { - acxlog(L_DEBUG, "setting WEP Options for ACX111 is not supported\n"); - } else { - acxlog(L_INIT, "setting WEP Options\n"); - - /* let's choose maximum setting: 4 default keys, - * plus 10 other keys: */ - options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10); - /* don't decrypt default key only, - * don't override decryption: */ - options.WEPOption = 0; - if (priv->mode == ACX_MODE_MONITOR) { - /* don't decrypt default key only, - * override decryption mechanism: */ - options.WEPOption = 2; - } - - acx_s_configure(priv, &options, ACX100_IE_WEP_OPTIONS); - } - CLEAR_BIT(priv->set_mask, SET_WEP_OPTIONS); - } - - /* Rescan was requested */ - if (start_scan) { - switch (priv->mode) { - case ACX_MODE_0_ADHOC: - case ACX_MODE_2_STA: - /* We can avoid clearing list if join code - ** will be a bit more clever about not picking - ** 'bad' AP over and over again */ - acx_lock(priv, flags); - priv->ap_client = NULL; - acx_l_sta_list_init(priv); - acx_set_status(priv, ACX_STATUS_1_SCANNING); - acx_unlock(priv, flags); - - acx_s_cmd_start_scan(priv); - } - } - - /* debug, rate, and nick don't need any handling */ - /* what about sniffing mode?? */ - - acxlog(L_INIT, "get_mask 0x%08X, set_mask 0x%08X - after update\n", - priv->get_mask, priv->set_mask); - -/* end: */ - FN_EXIT0; -} - - -/*********************************************************************** -*/ -void -acx_s_initialize_rx_config(wlandevice_t *priv) -{ - struct { - u16 id ACX_PACKED; - u16 len ACX_PACKED; - u16 rx_cfg1 ACX_PACKED; - u16 rx_cfg2 ACX_PACKED; - } cfg; - - switch (priv->mode) { - case ACX_MODE_OFF: - priv->rx_config_1 = (u16) (0 - /* | RX_CFG1_INCLUDE_RXBUF_HDR */ - /* | RX_CFG1_FILTER_SSID */ - /* | RX_CFG1_FILTER_BCAST */ - /* | RX_CFG1_RCV_MC_ADDR1 */ - /* | RX_CFG1_RCV_MC_ADDR0 */ - /* | RX_CFG1_FILTER_ALL_MULTI */ - /* | RX_CFG1_FILTER_BSSID */ - /* | RX_CFG1_FILTER_MAC */ - /* | RX_CFG1_RCV_PROMISCUOUS */ - /* | RX_CFG1_INCLUDE_FCS */ - /* | RX_CFG1_INCLUDE_PHY_HDR */ - ); - priv->rx_config_2 = (u16) (0 - /*| RX_CFG2_RCV_ASSOC_REQ */ - /*| RX_CFG2_RCV_AUTH_FRAMES */ - /*| RX_CFG2_RCV_BEACON_FRAMES */ - /*| RX_CFG2_RCV_CONTENTION_FREE */ - /*| RX_CFG2_RCV_CTRL_FRAMES */ - /*| RX_CFG2_RCV_DATA_FRAMES */ - /*| RX_CFG2_RCV_BROKEN_FRAMES */ - /*| RX_CFG2_RCV_MGMT_FRAMES */ - /*| RX_CFG2_RCV_PROBE_REQ */ - /*| RX_CFG2_RCV_PROBE_RESP */ - /*| RX_CFG2_RCV_ACK_FRAMES */ - /*| RX_CFG2_RCV_OTHER */ - ); - break; - case ACX_MODE_MONITOR: - priv->rx_config_1 = (u16) (0 - /* | RX_CFG1_INCLUDE_RXBUF_HDR */ - /* | RX_CFG1_FILTER_SSID */ - /* | RX_CFG1_FILTER_BCAST */ - /* | RX_CFG1_RCV_MC_ADDR1 */ - /* | RX_CFG1_RCV_MC_ADDR0 */ - /* | RX_CFG1_FILTER_ALL_MULTI */ - /* | RX_CFG1_FILTER_BSSID */ - /* | RX_CFG1_FILTER_MAC */ - | RX_CFG1_RCV_PROMISCUOUS - /* | RX_CFG1_INCLUDE_FCS */ - /* | RX_CFG1_INCLUDE_PHY_HDR */ - ); - priv->rx_config_2 = (u16) (0 - | RX_CFG2_RCV_ASSOC_REQ - | RX_CFG2_RCV_AUTH_FRAMES - | RX_CFG2_RCV_BEACON_FRAMES - | RX_CFG2_RCV_CONTENTION_FREE - | RX_CFG2_RCV_CTRL_FRAMES - | RX_CFG2_RCV_DATA_FRAMES - | RX_CFG2_RCV_BROKEN_FRAMES - | RX_CFG2_RCV_MGMT_FRAMES - | RX_CFG2_RCV_PROBE_REQ - | RX_CFG2_RCV_PROBE_RESP - | RX_CFG2_RCV_ACK_FRAMES - | RX_CFG2_RCV_OTHER - ); - break; - default: - priv->rx_config_1 = (u16) (0 - /* | RX_CFG1_INCLUDE_RXBUF_HDR */ - /* | RX_CFG1_FILTER_SSID */ - /* | RX_CFG1_FILTER_BCAST */ - /* | RX_CFG1_RCV_MC_ADDR1 */ - /* | RX_CFG1_RCV_MC_ADDR0 */ - /* | RX_CFG1_FILTER_ALL_MULTI */ - /* | RX_CFG1_FILTER_BSSID */ - | RX_CFG1_FILTER_MAC - /* | RX_CFG1_RCV_PROMISCUOUS */ - /* | RX_CFG1_INCLUDE_FCS */ - /* | RX_CFG1_INCLUDE_PHY_HDR */ - ); - priv->rx_config_2 = (u16) (0 - | RX_CFG2_RCV_ASSOC_REQ - | RX_CFG2_RCV_AUTH_FRAMES - | RX_CFG2_RCV_BEACON_FRAMES - | RX_CFG2_RCV_CONTENTION_FREE - | RX_CFG2_RCV_CTRL_FRAMES - | RX_CFG2_RCV_DATA_FRAMES - /*| RX_CFG2_RCV_BROKEN_FRAMES */ - | RX_CFG2_RCV_MGMT_FRAMES - | RX_CFG2_RCV_PROBE_REQ - | RX_CFG2_RCV_PROBE_RESP - /*| RX_CFG2_RCV_ACK_FRAMES */ - | RX_CFG2_RCV_OTHER - ); - break; - } -#ifdef DEBUG_WEP - if (IS_ACX100(priv)) - /* only ACX100 supports that */ -#endif - priv->rx_config_1 |= RX_CFG1_INCLUDE_RXBUF_HDR; - - acxlog(L_INIT, "setting RXconfig to %04X:%04X\n", - priv->rx_config_1, priv->rx_config_2); - cfg.rx_cfg1 = cpu_to_le16(priv->rx_config_1); - cfg.rx_cfg2 = cpu_to_le16(priv->rx_config_2); - acx_s_configure(priv, &cfg, ACX1xx_IE_RXCONFIG); -} - - -/*********************************************************************** -** acx_schedule_after_interrupt_task -** -** Schedule the call of the after_interrupt method after leaving -** the interrupt context. -*/ -void -acx_schedule_after_interrupt_task(wlandevice_t *priv, unsigned int set_flag) -{ - SET_BIT(priv->after_interrupt_jobs, set_flag); - SCHEDULE_WORK(&priv->after_interrupt_task); -} - - -/*********************************************************************** -** Helper -*/ -static void -acx_s_after_interrupt_recalib(wlandevice_t *priv) -{ - int res; - - /* this helps with ACX100 at least; - * hopefully ACX111 also does a - * recalibration here */ - - /* clear flag beforehand, since we want to make sure - * it's cleared; then only set it again on specific circumstances */ - CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); - - /* better wait a bit between recalibrations to - * prevent overheating due to torturing the card - * into working too long despite high temperature - * (just a safety measure) */ - if (priv->recalib_time_last_success - && time_before(jiffies, priv->recalib_time_last_success - + RECALIB_PAUSE * 60 * HZ)) { - priv->recalib_msg_ratelimit++; - if (priv->recalib_msg_ratelimit <= 5) - printk("%s: less than " STRING(RECALIB_PAUSE) - " minutes since last radio recalibration, " - "not recalibrating (maybe card is too hot?)\n", - priv->netdev->name); - if (priv->recalib_msg_ratelimit == 5) - printk("disabling above message\n"); - return; - } - - priv->recalib_msg_ratelimit = 0; - - /* note that commands sometimes fail (card busy), - * so only clear flag if we were fully successful */ - res = acx_s_recalib_radio(priv); - if (res == OK) { - printk("%s: successfully recalibrated radio\n", - priv->netdev->name); - priv->recalib_time_last_success = jiffies; - priv->recalib_failure_count = 0; - } else { - /* failed: resubmit, but only limited - * amount of times within some time range - * to prevent endless loop */ - - priv->recalib_time_last_success = 0; /* we failed */ - - /* if some time passed between last - * attempts, then reset failure retry counter - * to be able to do next recalib attempt */ - if (time_after(jiffies, priv->recalib_time_last_attempt + HZ)) - priv->recalib_failure_count = 0; - - if (++priv->recalib_failure_count <= 5) { - priv->recalib_time_last_attempt = jiffies; - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); - } - } -} - -/*********************************************************************** -** acx_e_after_interrupt_task -*/ -static void -acx_e_after_interrupt_task(void *data) -{ - netdevice_t *dev = (netdevice_t *) data; - wlandevice_t *priv; - - FN_ENTER; - - priv = (struct wlandevice *) dev->priv; - - acx_sem_lock(priv); - - if (!priv->after_interrupt_jobs) - goto end; /* no jobs to do */ - -#if TX_CLEANUP_IN_SOFTIRQ - if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_TX_CLEANUP) { - acx_lock(priv, flags); - acx_l_clean_tx_desc(priv); - CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_TX_CLEANUP); - acx_unlock(priv, flags); - } -#endif - /* we see lotsa tx errors */ - if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_RADIO_RECALIB) { - acx_s_after_interrupt_recalib(priv); - } - - /* a poor interrupt code wanted to do update_card_settings() */ - if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_UPDATE_CARD_CFG) { - if (ACX_STATE_IFACE_UP & priv->dev_state_mask) - acx_s_update_card_settings(priv, 0, 0); - CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_UPDATE_CARD_CFG); - } - - /* 1) we detected that no Scan_Complete IRQ came from fw, or - ** 2) we found too many STAs */ - if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_STOP_SCAN) { - acxlog(L_IRQ, "sending a stop scan cmd...\n"); - acx_s_issue_cmd(priv, ACX1xx_CMD_STOP_SCAN, NULL, 0); - /* HACK: set the IRQ bit, since we won't get a - * scan complete IRQ any more on ACX111 (works on ACX100!), - * since _we_, not a fw, have stopped the scan */ - SET_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE); - CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_STOP_SCAN); - } - - /* either fw sent Scan_Complete or we detected that - ** no Scan_Complete IRQ came from fw. Finish scanning, - ** pick join partner if any */ - if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_COMPLETE_SCAN) { - if (priv->status == ACX_STATUS_1_SCANNING) { - if (OK != acx_s_complete_scan(priv)) { - SET_BIT(priv->after_interrupt_jobs, - ACX_AFTER_IRQ_RESTART_SCAN); - } - } else { - /* + scan kills current join status - restore it - ** (do we need it for STA?) */ - /* + does it happen only with active scans? - ** active and passive scans? ALL scans including - ** background one? */ - /* + was not verified that everything is restored - ** (but at least we start to emit beacons again) */ - switch (priv->mode) { - case ACX_MODE_0_ADHOC: - case ACX_MODE_3_AP: - acxlog(L_IRQ, "redoing cmd_join_bssid() after scan\n"); - acx_s_cmd_join_bssid(priv, priv->bssid); - } - } - CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_COMPLETE_SCAN); - } - - /* STA auth or assoc timed out, start over again */ - if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_RESTART_SCAN) { - acxlog(L_IRQ, "sending a start_scan cmd...\n"); - acx_s_cmd_start_scan(priv); - CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_RESTART_SCAN); - } - - /* whee, we got positive assoc response! 8) */ - if (priv->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_ASSOCIATE) { - acx_ie_generic_t pdr; - /* tiny race window exists, checking that we still a STA */ - switch (priv->mode) { - case ACX_MODE_2_STA: - pdr.m.aid = cpu_to_le16(priv->aid); - acx_s_configure(priv, &pdr, ACX1xx_IE_ASSOC_ID); - acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); - acxlog(L_ASSOC | L_DEBUG, "ASSOCIATED!\n"); - CLEAR_BIT(priv->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_ASSOCIATE); - } - } -end: - acx_sem_unlock(priv); - FN_EXIT0; -} - - -/*********************************************************************** -*/ -void -acx_init_task_scheduler(wlandevice_t *priv) -{ - /* configure task scheduler */ - INIT_WORK(&priv->after_interrupt_task, acx_e_after_interrupt_task, priv->netdev); -} - - -/*********************************************************************** -** acx_cmd_join_bssid -** -** Common code for both acx100 and acx111. -*/ -/* NB: does NOT match RATE100_nn but matches ACX[111]_SCAN_RATE_n */ -static const u8 -bitpos2genframe_txrate[] = { - 10, /* 0. 1 Mbit/s */ - 20, /* 1. 2 Mbit/s */ - 55, /* 2. 5.5 Mbit/s */ - 0x0B, /* 3. 6 Mbit/s */ - 0x0F, /* 4. 9 Mbit/s */ - 110, /* 5. 11 Mbit/s */ - 0x0A, /* 6. 12 Mbit/s */ - 0x0E, /* 7. 18 Mbit/s */ - 220, /* 8. 22 Mbit/s */ - 0x09, /* 9. 24 Mbit/s */ - 0x0D, /* 10. 36 Mbit/s */ - 0x08, /* 11. 48 Mbit/s */ - 0x0C, /* 12. 54 Mbit/s */ - 10, /* 13. 1 Mbit/s, should never happen */ - 10, /* 14. 1 Mbit/s, should never happen */ - 10, /* 15. 1 Mbit/s, should never happen */ -}; - -/* Looks scary, eh? -** Actually, each one compiled into one AND and one SHIFT, -** 31 bytes in x86 asm (more if uints are replaced by u16/u8) */ -static unsigned int -rate111to5bits(unsigned int rate) -{ - return (rate & 0x7) - | ( (rate & RATE111_11) / (RATE111_11/JOINBSS_RATES_11) ) - | ( (rate & RATE111_22) / (RATE111_22/JOINBSS_RATES_22) ) - ; -} - -void BUG_joinbss_must_be_0x30_bytes_in_length(void); - -void -acx_s_cmd_join_bssid(wlandevice_t *priv, const u8 *bssid) -{ - acx_joinbss_t tmp; - int dtim_interval; - int i; - - /* compile-time check for proper size of fixed-size struct */ - if (sizeof(acx_joinbss_t) != 0x30) - BUG_joinbss_must_be_0x30_bytes_in_length(); - - FN_ENTER; - - dtim_interval = (ACX_MODE_0_ADHOC == priv->mode) ? - 1 : priv->dtim_interval; - - memset(&tmp, 0, sizeof(tmp)); - - for (i = 0; i < ETH_ALEN; i++) { - tmp.bssid[i] = bssid[ETH_ALEN-1 - i]; - } - - tmp.beacon_interval = cpu_to_le16(priv->beacon_interval); - - /* basic rate set. Control frame responses (such as ACK or CTS frames) - ** are sent with one of these rates */ - if (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_start -*/ -void -acx_s_start(wlandevice_t *priv) -{ - FN_ENTER; - - /* - * Ok, now we do everything that can possibly be done with ioctl - * calls to make sure that when it was called before the card - * was up we get the changes asked for - */ - - SET_BIT(priv->set_mask, SET_TEMPLATES|SET_STA_LIST|GETSET_WEP - |GETSET_TXPOWER|GETSET_ANTENNA|GETSET_ED_THRESH|GETSET_CCA - |GETSET_REG_DOMAIN|GETSET_MODE|GETSET_CHANNEL - |GETSET_TX|GETSET_RX); - - acxlog(L_INIT, "updating initial settings on iface activation...\n"); - acx_s_update_card_settings(priv, 0, 0); - - FN_EXIT0; -} - - -/*********************************************************************** -** acx_update_capabilities -*/ -void -acx_update_capabilities(wlandevice_t *priv) -{ - u16 cap = 0; - - switch (priv->mode) { - case ACX_MODE_3_AP: - SET_BIT(cap, WF_MGMT_CAP_ESS); break; - case ACX_MODE_0_ADHOC: - SET_BIT(cap, WF_MGMT_CAP_IBSS); break; - /* other types of stations do not emit beacons */ - } - - if (priv->wep_restricted) { - SET_BIT(cap, WF_MGMT_CAP_PRIVACY); - } - if (priv->capab_short) { - SET_BIT(cap, WF_MGMT_CAP_SHORT); - } - if (priv->capab_pbcc) { - SET_BIT(cap, WF_MGMT_CAP_PBCC); - } - if (priv->capab_agility) { - SET_BIT(cap, WF_MGMT_CAP_AGILITY); - } - acxlog(L_DEBUG, "caps updated from 0x%04X to 0x%04X\n", - priv->capabilities, cap); - priv->capabilities = cap; -} - -#ifdef UNUSED -/*********************************************************************** -** FIXME: check whether this function is indeed acx111 only, -** rename ALL relevant definitions to indicate actual card scope! -*/ -void -acx111_s_read_configoption(wlandevice_t *priv) -{ - acx111_ie_configoption_t co, co2; - int i; - const u8 *pEle; - - if (OK != acx_s_interrogate(priv, &co, ACX111_IE_CONFIG_OPTIONS) ) { - return; - }; - if (!(acx_debug & L_DEBUG)) - return; - - memcpy(&co2.configoption_fixed, &co.configoption_fixed, - sizeof(co.configoption_fixed)); - - pEle = (u8 *)&co.configoption_fixed + sizeof(co.configoption_fixed) - 4; - - co2.antennas.type = pEle[0]; - co2.antennas.len = pEle[1]; - printk("AntennaID:%02X Len:%02X Data:", - co2.antennas.type, co2.antennas.len); - for (i = 0; i < pEle[1]; i++) { - co2.antennas.list[i] = pEle[i+2]; - printk("%02X ", pEle[i+2]); - } - printk("\n"); - - pEle += pEle[1] + 2; - co2.power_levels.type = pEle[0]; - co2.power_levels.len = pEle[1]; - printk("PowerLevelID:%02X Len:%02X Data:", - co2.power_levels.type, co2.power_levels.len); - for (i = 0; i < pEle[1]*2; i++) { - co2.power_levels.list[i] = pEle[i+2]; - printk("%02X ", pEle[i+2]); - } - printk("\n"); - - pEle += pEle[1]*2 + 2; - co2.data_rates.type = pEle[0]; - co2.data_rates.len = pEle[1]; - printk("DataRatesID:%02X Len:%02X Data:", - co2.data_rates.type, co2.data_rates.len); - for (i = 0; i < pEle[1]; i++) { - co2.data_rates.list[i] = pEle[i+2]; - printk("%02X ", pEle[i+2]); - } - printk("\n"); - - pEle += pEle[1] + 2; - co2.domains.type = pEle[0]; - co2.domains.len = pEle[1]; - printk("DomainID:%02X Len:%02X Data:", - co2.domains.type, co2.domains.len); - for (i = 0; i < pEle[1]; i++) { - co2.domains.list[i] = pEle[i+2]; - printk("%02X ", pEle[i+2]); - } - printk("\n"); - - pEle += pEle[1] + 2; - co2.product_id.type = pEle[0]; - co2.product_id.len = pEle[1]; - for (i = 0; i < pEle[1]; i++) { - co2.product_id.list[i] = pEle[i+2]; - } - printk("ProductID:%02X Len:%02X Data:%.*s\n", - co2.product_id.type, co2.product_id.len, - co2.product_id.len, (char *)co2.product_id.list); - - pEle += pEle[1] + 2; - co2.manufacturer.type = pEle[0]; - co2.manufacturer.len = pEle[1]; - for (i = 0; i < pEle[1]; i++) { - co2.manufacturer.list[i] = pEle[i+2]; - } - printk("ManufacturerID:%02X Len:%02X Data:%.*s\n", - co2.manufacturer.type, co2.manufacturer.len, - co2.manufacturer.len, (char *)co2.manufacturer.list); -/* - printk("EEPROM part:\n"); - for (i=0; i<58; i++) { - printk("%02X =======> 0x%02X\n", - i, (u8 *)co.configoption_fixed.NVSv[i-2]); - } -*/ -} -#endif - - -/*********************************************************************** -** Not inlined: it's larger than it seems -*/ -void -acx_print_mac(const char *head, const u8 *mac, const char *tail) -{ - printk("%s"MACSTR"%s", head, MAC(mac), tail); -} - - -/*********************************************************************** -*/ -static int __init -acx_e_init_module(void) -{ - int r1,r2; - printk("acx: this driver is still EXPERIMENTAL\n" - "acx: reading README file and/or Craig's HOWTO is " - "recommended, visit http://acx100.sf.net in case " - "of further questions/discussion\n"); - -#if defined(CONFIG_ACX_PCI) - r1 = acxpci_e_init_module(); -#else - r1 = -EINVAL; -#endif -#if defined(CONFIG_ACX_USB) - r2 = acxusb_e_init_module(); -#else - r2 = -EINVAL; -#endif - if (r2 && r1) /* both failed! */ - return r2 ? r2 : r1; - /* return success if at least one succeeded */ - return 0; -} - -static void __exit -acx_e_cleanup_module(void) -{ -#if defined(CONFIG_ACX_PCI) - acxpci_e_cleanup_module(); -#endif -#if defined(CONFIG_ACX_USB) - acxusb_e_cleanup_module(); -#endif -} - -module_init(acx_e_init_module) -module_exit(acx_e_cleanup_module) diff -L drivers/net/wireless/tiacx/helper.c -puN drivers/net/wireless/tiacx/helper.c~acx-update-2 /dev/null --- devel/drivers/net/wireless/tiacx/helper.c +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,1052 +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 -** --------------------------------------------------------------------- -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if WIRELESS_EXT >= 13 -#include -#endif -#include - -#include "acx.h" - - -/*********************************************************************** -*/ - -/* Now that the pci_alloc_consistent() problem has been resolved, - * feel free to modify buffer count for ACX100 to 32, too. - * But it's not required since the card isn't too fast anyway */ -#define RXBUFCNT_ACX100 16 -#define TXBUFCNT_ACX100 16 -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53) -/* dma_alloc_coherent() uses GFP_KERNEL, much less problematic than - * the pci_alloc_consistent() used below using GFP_ATOMIC (quite often causes - * a larger alloc to fail), so use less buffers there to be more successful */ -#define RXBUFCNT_ACX111 32 -#define TXBUFCNT_ACX111 32 -#else -#define RXBUFCNT_ACX111 16 -#define TXBUFCNT_ACX111 16 -#endif - -/* 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_RXBUFCNT 10 -#define USB_TXBUFCNT 10 - - -/*---------------------------------------------------------------- -* acx100_s_init_memory_pools -*----------------------------------------------------------------*/ -void BUG_acx100_ie_memconfigoption_must_be_24_bytes_in_length(void); - -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; - - if (sizeof(acx100_ie_memconfigoption_t) != 24) - BUG_acx100_ie_memconfigoption_must_be_24_bytes_in_length(); - - 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 */ -//// this one comes from PCI hostdesc init: - MemoryConfigOption.pRxHostDesc = ptr2acx(priv->RxHostDescPoolStart); - acxlog(L_DEBUG, "pRxHostDesc 0x%08X, RxHostDescPoolStart 0x%p\n", - acx2cpu(MemoryConfigOption.pRxHostDesc), - priv->RxHostDescPoolStart); - } 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 -*----------------------------------------------------------------*/ -void BUG_acx100_ie_queueconfig_t_must_be_0x20_bytes_in_length(void); - -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; - } - - /* # of items in Rx and Tx queues */ - priv->TxQueueCnt = TXBUFCNT_ACX100; - priv->RxQueueCnt = RXBUFCNT_ACX100; - - tx_queue_start = le32_to_cpu(memmap.QueueStart); - rx_queue_start = tx_queue_start + TXBUFCNT_ACX100 * 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_TXBUFCNT; - queueconf.NumRxDesc = USB_RXBUFCNT; - } - - /* calculate size of queues */ - queueconf.AreaSize = cpu_to_le32( - TXBUFCNT_ACX100 * sizeof(txdesc_t) + - RXBUFCNT_ACX100 * 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 + RXBUFCNT_ACX100 * sizeof(rxdesc_t) - ); - /* sets the beginning of the next queue */ - queueconf.HostQueueEnd = cpu_to_le32(le32_to_cpu(queueconf.QueueEnd) + 8); - if (sizeof(queueconf) != 0x20) - BUG_acx100_ie_queueconfig_t_must_be_0x20_bytes_in_length(); - 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 != acx_s_create_hostdesc_queues(priv)) - goto fail; - acx_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)) - acx_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) - -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 */ - - priv->TxQueueCnt = TXBUFCNT_ACX111; - priv->RxQueueCnt = RXBUFCNT_ACX111; - - /* Set up our host descriptor pool + data pool */ - if (IS_PCI(priv)) { - if (OK != acx_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 = RXBUFCNT_ACX111; - memconf.rx_queue1_type = 7; /* must be set to 7 */ - /* done by memset: memconf.rx_queue1_prio = 0; low prio */ -//// this one comes from PCI hostdesc init. BUG! we need aligned one! -/// memconf.rx_queue1_host_rx_start = cpu_to_le32(priv->RxHostDescQPoolPhyAddr); - memconf.rx_queue1_host_rx_start = cpu_to_le32(priv->RxHostDescPoolStart); - /* Tx descriptor queue config */ - memconf.tx_queue1_count_descs = TXBUFCNT_ACX111; - /* done by memset: memconf.tx_queue1_attributes = 0; lowest priority */ - - /* NB1: this looks wrong: (memconf,ACX1xx_IE_QUEUE_CONFIG), - ** (queueconf,ACX1xx_IE_MEMORY_CONFIG_OPTIONS) look swapped, eh? - ** But it is actually correct wrt IE numbers. - ** NB2: sizeof(memconf) == 28 == 0x1c but configure(ACX1xx_IE_QUEUE_CONFIG) - ** writes 0x20 bytes (because same IE for acx100 uses struct acx100_ie_queueconfig - ** 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)) - acx_create_desc_queues(priv, tx_queue_start, rx_queue_start); - - FN_EXIT1(OK); - return OK; -fail: - if (IS_PCI(priv)) - acx_free_desc_queues(priv); - - FN_EXIT1(NOT_OK); - return NOT_OK; -} - - -#ifdef CONFIG_PROC_FS -/*********************************************************************** -** /proc files -*/ -/*********************************************************************** -** acx_l_proc_output -** Generate content for our /proc entry -** -** Arguments: -** buf is a pointer to write output to -** priv is the usual pointer to our private struct wlandevice -** Returns: -** number of bytes actually written to buf -** Side effects: -** none -*/ -static int -acx_l_proc_output(char *buf, wlandevice_t *priv) -{ - char *p = buf; - int i; - - FN_ENTER; - - p += sprintf(p, - "acx driver version:\t\t" WLAN_RELEASE "\n" - "Wireless extension version:\t" STRING(WIRELESS_EXT) "\n" - "chip name:\t\t\t%s (0x%08X)\n" - "radio type:\t\t\t0x%02X\n" - /* TODO: add radio type string from acx_display_hardware_details */ - "form factor:\t\t\t0x%02X\n" - /* TODO: add form factor string from acx_display_hardware_details */ - "EEPROM version:\t\t\t0x%02X\n" - "firmware version:\t\t%s (0x%08X)\n", - priv->chip_name, priv->firmware_id, - priv->radio_type, - priv->form_factor, - priv->eeprom_version, - priv->firmware_version, priv->firmware_numver); - - for (i = 0; i < VEC_SIZE(priv->sta_list); i++) { - struct client *bss = &priv->sta_list[i]; - if (!bss->used) continue; - p += sprintf(p, "BSS %u BSSID "MACSTR" ESSID %s channel %u " - "Cap 0x%X SIR %u SNR %u\n", - i, MAC(bss->bssid), (char*)bss->essid, bss->channel, - bss->cap_info, bss->sir, bss->snr); - } - p += sprintf(p, "status:\t\t\t%u (%s)\n", - priv->status, acx_get_status_name(priv->status)); - /* TODO: add more interesting stuff (essid, ...) here */ - - FN_EXIT1(p - buf); - return p - buf; -} - - -/*********************************************************************** -*/ -static int -acx_s_proc_diag_output(char *buf, wlandevice_t *priv) -{ - char *p = buf; - fw_stats_t *fw_stats; - unsigned long flags; - - FN_ENTER; - - fw_stats = kmalloc(sizeof(fw_stats_t), GFP_KERNEL); - if (!fw_stats) { - FN_EXIT1(0); - return 0; - } - memset(fw_stats, 0, sizeof(fw_stats_t)); - - acx_lock(priv, flags); - - 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 = acx_proc_eeprom_output(buf, priv); - acx_sem_unlock(priv); - } - - /* housekeeping */ - if (length <= offset + count) - *eof = 1; - *start = buf + offset; - length -= offset; - if (length > count) - length = count; - if (length < 0) - length = 0; - FN_EXIT1(length); - return length; -} - -static int -acx_e_read_proc_phy(char *buf, char **start, off_t offset, int count, - int *eof, void *data) -{ - wlandevice_t *priv = (wlandevice_t *)data; - int length; - - FN_ENTER; - - acx_sem_lock(priv); - /* fill buf */ - length = acx_s_proc_phy_output(buf, priv); - acx_sem_unlock(priv); - - /* housekeeping */ - if (length <= offset + count) - *eof = 1; - *start = buf + offset; - length -= offset; - if (length > count) - length = count; - if (length < 0) - length = 0; - FN_EXIT1(length); - return length; -} - - -/*********************************************************************** -** /proc files registration -*/ -static const char * const -proc_files[] = { "", "_diag", "_eeprom", "_phy" }; - -static read_proc_t * const -acx_proc_funcs[] = { - acx_e_read_proc, - acx_e_read_proc_diag, - acx_e_read_proc_eeprom, - acx_e_read_proc_phy -}; - -static int -manage_proc_entries(const struct net_device *dev, int remove) -{ - /* doh, netdev_priv() doesn't have const! */ - wlandevice_t *priv = acx_netdev_priv((struct net_device *)dev); - char procbuf[80]; - int i; - - for (i = 0; i < 4; i++) { - sprintf(procbuf, "driver/acx_%s", dev->name); - strcat(procbuf, proc_files[i]); - if (!remove) { - acxlog(L_INIT, "creating /proc entry %s\n", procbuf); - if (!create_proc_read_entry(procbuf, 0, 0, acx_proc_funcs[i], priv)) - return NOT_OK; - } else { - acxlog(L_INIT, "removing /proc entry %s\n", procbuf); - remove_proc_entry(procbuf, NULL); - } - } - return OK; -} - -int -acx_proc_register_entries(const struct net_device *dev) -{ - return manage_proc_entries(dev, 0); -} - -int -acx_proc_unregister_entries(const struct net_device *dev) -{ - return manage_proc_entries(dev, 1); -} -#endif /* CONFIG_PROC_FS */ - - -/*********************************************************************** -** acx_s_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)) - acx_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 */ - acx_read_eeprom_offset(priv, 0x16F, &priv->reg_dom_id); - } else if (priv->eeprom_version < 5) { - acx_read_eeprom_offset(priv, 0x16F, &priv->reg_dom_id); - } else { - acx_read_eeprom_offset(priv, 0x171, &priv->reg_dom_id); - } - } - - 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_OFF; - priv->dtim_interval = DEFAULT_DTIM_INTERVAL; - - priv->msdu_lifetime = DEFAULT_MSDU_LIFETIME; - SET_BIT(priv->set_mask, SET_MSDU_LIFETIME); - - priv->rts_threshold = DEFAULT_RTS_THRESHOLD; - - /* use standard default values for retry limits */ - priv->short_retry = 7; /* max. retries for (short) non-RTS packets */ - priv->long_retry = 4; /* max. retries for long (RTS) packets */ - SET_BIT(priv->set_mask, GETSET_RETRY); - - priv->fallback_threshold = 3; - priv->stepup_threshold = 10; - priv->rate_bcast = RATE111_1; - priv->rate_bcast100 = RATE100_1; - priv->rate_basic = RATE111_1 | RATE111_2; - priv->rate_auto = 1; - if (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 ACX111TxLevel tx_level; - - /* my acx111 card has two power levels in its configoptions (== EEPROM): - * 1 (30mW) [15dBm] - * 2 (10mW) [10dBm] - * For now, just assume all other acx111 cards have the same. - * Ideally we would query it here, but we first need a - * standard way to query individual configoptions easily. */ - if (level_dbm <= 12) { - tx_level.level = 2; /* 10 dBm */ - priv->tx_level_dbm = 10; - } else { - tx_level.level = 1; /* 15 dBm */ - priv->tx_level_dbm = 15; - } - if (level_dbm != priv->tx_level_dbm) - acxlog(L_INIT, "ACX111 firmware has specific " - "power levels only: adjusted %d dBm to %d dBm!\n", - level_dbm, priv->tx_level_dbm); - - return acx_s_configure(priv, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); -} - -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 acx100_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 ACX111TxLevel 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 = acx_netdev_priv(dev); - int result = NOT_OK; - - FN_ENTER; - - if (IS_PCI(priv)) { - priv->memblocksize = 256; /* 256 is default */ - acx_init_mboxes(priv); - /* try to load radio for both ACX100 and ACX111, since both - * chips have at least some firmware versions making use of an - * external radio module */ - acx_s_upload_radio(priv); - } else { - priv->memblocksize = 128; - } - - 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; -} diff -puN drivers/net/wireless/tiacx/ioctl.c~acx-update-2 drivers/net/wireless/tiacx/ioctl.c --- devel/drivers/net/wireless/tiacx/ioctl.c~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/ioctl.c 2005-10-17 13:06:00.000000000 -0700 @@ -50,7 +50,7 @@ /* if you plan to reorder something, make sure to reorder all other places * accordingly! */ -/* someone broke SET/GET convention: SETs must have even position, GETs odd */ +/* SET/GET convention: SETs must have even position, GETs odd */ #define ACX100_IOCTL SIOCIWFIRSTPRIV enum { ACX100_IOCTL_DEBUG = ACX100_IOCTL, @@ -206,7 +206,7 @@ acx_ioctl_commit(struct net_device *dev, struct iw_request_info *info, void *zwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); FN_ENTER; @@ -229,7 +229,7 @@ acx_ioctl_get_name( char *cwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); static const char * const names[] = { "IEEE 802.11b+/g+", "IEEE 802.11b+" }; strcpy(cwrq, names[IS_ACX111(priv) ? 0 : 1]); @@ -248,7 +248,7 @@ acx_ioctl_set_freq( struct iw_freq *fwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int channel = -1; unsigned int mult = 1; int result; @@ -301,7 +301,7 @@ acx_ioctl_get_freq( struct iw_freq *fwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); fwrq->e = 0; fwrq->m = priv->channel; return OK; @@ -318,7 +318,7 @@ acx_ioctl_set_mode( u32 *uwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; FN_ENTER; @@ -375,7 +375,7 @@ acx_ioctl_get_mode( u32 *uwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result = 0; switch (priv->mode) { @@ -407,7 +407,7 @@ acx_ioctl_set_sens( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); acx_sem_lock(priv); @@ -429,7 +429,7 @@ acx_ioctl_get_sens( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); /* acx_sem_lock(priv); */ @@ -455,7 +455,7 @@ acx_ioctl_set_ap( struct sockaddr *awrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result = 0; const u8 *ap; @@ -509,7 +509,7 @@ acx_ioctl_get_ap( struct sockaddr *awrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); if (ACX_STATUS_4_ASSOCIATED == priv->status) { /* as seen in Aironet driver, airo.c */ @@ -536,7 +536,7 @@ acx_ioctl_get_aplist( struct iw_point *dwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); struct sockaddr *address = (struct sockaddr *) extra; struct iw_quality qual[IW_MAX_AP]; int i, cur; @@ -590,7 +590,7 @@ acx_ioctl_set_scan( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; FN_ENTER; @@ -705,7 +705,8 @@ acx_s_scan_add_station( if (rate & 1) { iwe.u.bitrate.value = *p * 500000; /* units of 500kb/s */ acxlog(L_IOCTL, "scan, rate: %d\n", iwe.u.bitrate.value); - ptr = iwe_stream_add_value(ptr, ptr_rate, end_buf, &iwe, IW_EV_PARAM_LEN); + ptr = iwe_stream_add_value(ptr, ptr_rate, end_buf, + &iwe, IW_EV_PARAM_LEN); } rate >>= 1; p++; @@ -731,7 +732,7 @@ acx_ioctl_get_scan( struct iw_point *dwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); char *ptr = extra; int i; int result = OK; @@ -790,7 +791,7 @@ acx_ioctl_set_essid( struct iw_point *dwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int len = dwrq->length; int result; @@ -846,7 +847,7 @@ acx_ioctl_get_essid( struct iw_point *dwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); dwrq->flags = priv->essid_active; if (priv->essid_active) { @@ -928,16 +929,15 @@ acx_ioctl_set_rate( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); u16 txrate_cfg = 1; unsigned long flags; int autorate; int result = -EINVAL; FN_ENTER; - acxlog(L_IOCTL, - "rate %d fixed 0x%X disabled 0x%X flags 0x%X\n", - vwrq->value, vwrq->fixed, vwrq->disabled, vwrq->flags); + acxlog(L_IOCTL, "rate %d fixed 0x%X disabled 0x%X flags 0x%X\n", + vwrq->value, vwrq->fixed, vwrq->disabled, vwrq->flags); if ((0 == vwrq->fixed) || (1 == vwrq->fixed)) { int i = VEC_SIZE(acx111_rate_tbl)-1; @@ -1019,7 +1019,7 @@ acx_ioctl_get_rate( char *extra) { /* TODO: remember rate of last tx, show it. think about multiple peers... */ - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); vwrq->value = acx111_rate_tbl[highest_bit(priv->rate_oper)]; vwrq->fixed = !priv->rate_auto; vwrq->disabled = 0; @@ -1033,7 +1033,7 @@ acx_ioctl_set_rts( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int val = vwrq->value; if (vwrq->disabled) @@ -1052,7 +1052,7 @@ acx_ioctl_get_rts( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); vwrq->value = priv->rts_threshold; vwrq->disabled = (vwrq->value >= 2312); @@ -1071,14 +1071,14 @@ acx_ioctl_set_encode( struct iw_point *dwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int index; int result; FN_ENTER; - acxlog(L_IOCTL, - "Set Encoding flags=0x%04X, size=%d, key: %s\n", - dwrq->flags, dwrq->length, extra ? "set" : "No key"); + + acxlog(L_IOCTL, "Set Encoding flags=0x%04X, size=%d, key: %s\n", + dwrq->flags, dwrq->length, extra ? "set" : "No key"); acx_sem_lock(priv); @@ -1093,33 +1093,34 @@ acx_ioctl_set_encode( if (dwrq->length > 29) dwrq->length = 29; /* restrict it */ - if (dwrq->length > 13) - priv->wep_keys[index].size = 29; /* 29*8 == 232, WEP256 */ - else - if (dwrq->length > 5) - priv->wep_keys[index].size = 13; /* 13*8 == 104bit, WEP128 */ - else - if (dwrq->length > 0) - priv->wep_keys[index].size = 5; /* 5*8 == 40bit, WEP64 */ - else + if (dwrq->length > 13) { + /* 29*8 == 232, WEP256 */ + priv->wep_keys[index].size = 29; + } else if (dwrq->length > 5) { + /* 13*8 == 104bit, WEP128 */ + priv->wep_keys[index].size = 13; + } else if (dwrq->length > 0) { + /* 5*8 == 40bit, WEP64 */ + priv->wep_keys[index].size = 5; + } else { /* disable key */ priv->wep_keys[index].size = 0; + } - memset(priv->wep_keys[index].key, 0, sizeof(priv->wep_keys[index].key)); + memset(priv->wep_keys[index].key, 0, + sizeof(priv->wep_keys[index].key)); memcpy(priv->wep_keys[index].key, extra, dwrq->length); } - } else { /* set transmit key */ if ((index >= 0) && (index <= 3)) priv->wep_current_index = index; - else - if (0 == (dwrq->flags & IW_ENCODE_MODE)) { - /* complain if we were not just setting - * the key mode */ - result = -EINVAL; - goto end_unlock; - } + else if (0 == (dwrq->flags & IW_ENCODE_MODE)) { + /* complain if we were not just setting + * the key mode */ + result = -EINVAL; + goto end_unlock; + } } priv->wep_enabled = !(dwrq->flags & IW_ENCODE_DISABLED); @@ -1137,13 +1138,11 @@ acx_ioctl_set_encode( SET_BIT(priv->set_mask, GETSET_WEP); acxlog(L_IOCTL, "len=%d, key at 0x%p, flags=0x%X\n", - dwrq->length, extra, - dwrq->flags); + dwrq->length, extra, dwrq->flags); for (index = 0; index <= 3; index++) { if (priv->wep_keys[index].size) { - acxlog(L_IOCTL, - "index=%d, size=%d, key at 0x%p\n", + acxlog(L_IOCTL, "index=%d, size=%d, key at 0x%p\n", priv->wep_keys[index].index, (int) priv->wep_keys[index].size, priv->wep_keys[index].key); @@ -1169,23 +1168,23 @@ acx_ioctl_get_encode( struct iw_point *dwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + FN_ENTER; + if (priv->wep_enabled == 0) { dwrq->flags = IW_ENCODE_DISABLED; - } else { if ((index < 0) || (index > 3)) index = (int)priv->wep_current_index; - dwrq->flags = - (priv->wep_restricted == 1) ? IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN; + dwrq->flags = (priv->wep_restricted == 1) ? + IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN; dwrq->length = priv->wep_keys[index].size; - memcpy(extra, - priv->wep_keys[index].key, - priv->wep_keys[index].size); + memcpy(extra, priv->wep_keys[index].key, + priv->wep_keys[index].size); } /* set the current index */ @@ -1195,6 +1194,7 @@ acx_ioctl_get_encode( dwrq->length, dwrq->pointer, dwrq->flags); + FN_EXIT1(OK); return OK; } @@ -1208,13 +1208,19 @@ acx_ioctl_set_power( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); + int result = -EINPROGRESS; + + FN_ENTER; acxlog(L_IOCTL, "Set 802.11 Power Save flags=0x%04X\n", vwrq->flags); + + acx_sem_lock(priv); + if (vwrq->disabled) { CLEAR_BIT(priv->ps_wakeup_cfg, PS_CFG_ENABLE); SET_BIT(priv->set_mask, GETSET_POWER_80211); - return -EINPROGRESS; + goto end; } if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { u16 ps_timeout = (vwrq->value * 1024) / 1000; @@ -1235,6 +1241,7 @@ acx_ioctl_set_power( CLEAR_BIT(priv->ps_wakeup_cfg, PS_CFG_WAKEUP_MODE_MASK); SET_BIT(priv->ps_wakeup_cfg, PS_CFG_WAKEUP_EACH_ITVL); } + switch (vwrq->flags & IW_POWER_MODE) { /* FIXME: are we doing the right thing here? */ case IW_POWER_UNICAST_R: @@ -1250,14 +1257,17 @@ acx_ioctl_set_power( break; default: acxlog(L_IOCTL, "unknown PS mode\n"); - return -EINVAL; + result = -EINVAL; + goto end; } SET_BIT(priv->ps_wakeup_cfg, PS_CFG_ENABLE); SET_BIT(priv->set_mask, GETSET_POWER_80211); +end: + acx_sem_unlock(priv); - return -EINPROGRESS; - + FN_EXIT1(result); + return result; } @@ -1270,7 +1280,9 @@ acx_ioctl_get_power( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); + + FN_ENTER; acxlog(L_IOCTL, "Get 802.11 Power Save flags = 0x%04X\n", vwrq->flags); vwrq->disabled = ((priv->ps_wakeup_cfg & PS_CFG_ENABLE) == 0); @@ -1288,6 +1300,7 @@ acx_ioctl_get_power( else SET_BIT(vwrq->flags, IW_POWER_UNICAST_R); + FN_EXIT1(OK); return OK; } @@ -1302,7 +1315,9 @@ acx_ioctl_get_txpow( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); + + FN_ENTER; vwrq->flags = IW_TXPOW_DBM; vwrq->disabled = 0; @@ -1311,6 +1326,7 @@ acx_ioctl_get_txpow( acxlog(L_IOCTL, "get txpower:%d dBm\n", priv->tx_level_dbm); + FN_EXIT1(OK); return OK; } @@ -1325,17 +1341,18 @@ acx_ioctl_set_txpow( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; FN_ENTER; + acxlog(L_IOCTL, "set txpower:%d, disabled:%d, flags:0x%04X\n", vwrq->value, vwrq->disabled, vwrq->flags); acx_sem_lock(priv); if (vwrq->disabled != priv->tx_disabled) { - SET_BIT(priv->set_mask, GETSET_TX); /* Tx status needs update later */ + SET_BIT(priv->set_mask, GETSET_TX); } priv->tx_disabled = vwrq->disabled; @@ -1373,84 +1390,92 @@ acx_ioctl_get_range( struct iw_point *dwrq, char *extra) { - if (dwrq->pointer != NULL) { - struct iw_range *range = (struct iw_range *)extra; - wlandevice_t *priv = acx_netdev_priv(dev); - unsigned int i; - - dwrq->length = sizeof(struct iw_range); - memset(range, 0, sizeof(struct iw_range)); - range->num_channels = 0; - for (i = 1; i <= 14; i++) { - if (priv->reg_dom_chanmask & (1 << (i - 1))) { - range->freq[range->num_channels].i = i; - range->freq[range->num_channels].m = acx_channel_freq[i - 1] * 100000; - range->freq[range->num_channels++].e = 1; /* MHz values */ - } - } - range->num_frequency = range->num_channels; + struct iw_range *range = (struct iw_range *)extra; + wlandevice_t *priv = netdev_priv(dev); + int i,n; - range->min_rts = 0; - range->max_rts = 2312; - /* range->min_frag = 256; - * range->max_frag = 2312; - */ - - range->encoding_size[0] = 5; - range->encoding_size[1] = 13; - range->encoding_size[2] = 29; - range->num_encoding_sizes = 3; - range->max_encoding_tokens = 4; - - range->min_pmp = 0; - range->max_pmp = 5000000; - range->min_pmt = 0; - range->max_pmt = 65535 * 1000; - range->pmp_flags = IW_POWER_PERIOD; - range->pmt_flags = IW_POWER_TIMEOUT; - range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; - - for (i = 0; i <= IW_MAX_TXPOWER - 1; i++) - range->txpower[i] = 20 * i / (IW_MAX_TXPOWER - 1); - range->num_txpower = IW_MAX_TXPOWER; - range->txpower_capa = IW_TXPOW_DBM; - - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 0x9; - - range->retry_capa = IW_RETRY_LIMIT; - range->retry_flags = IW_RETRY_LIMIT; - range->min_retry = 1; - range->max_retry = 255; - - range->r_time_flags = IW_RETRY_LIFETIME; - range->min_r_time = 0; - /* FIXME: lifetime ranges and orders of magnitude are strange?? */ - range->max_r_time = 65535; + FN_ENTER; - if (IS_ACX111(priv)) - range->sensitivity = 3; - else - range->sensitivity = 255; - /* TODO: USB really should have range->sensitivity = 0; */ + if (!dwrq->pointer) + goto end; - for (i=0; i < priv->rate_supported_len; i++) { - range->bitrate[i] = (priv->rate_supported[i] & ~0x80) * 500000; - /* never happens, but keep it, to be safe: */ - if (range->bitrate[i] == 0) - break; - } - range->num_bitrates = i; + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + n = 0; + for (i = 1; i <= 14; i++) { + if (priv->reg_dom_chanmask & (1 << (i - 1))) { + range->freq[n].i = i; + range->freq[n].m = acx_channel_freq[i - 1] * 100000; + range->freq[n].e = 1; /* units are MHz */ + n++; + } + } + range->num_channels = n; + range->num_frequency = n; + + range->min_rts = 0; + range->max_rts = 2312; + /* range->min_frag = 256; + * range->max_frag = 2312; + */ + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->encoding_size[2] = 29; + range->num_encoding_sizes = 3; + range->max_encoding_tokens = 4; + + range->min_pmp = 0; + range->max_pmp = 5000000; + range->min_pmt = 0; + range->max_pmt = 65535 * 1000; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + for (i = 0; i <= IW_MAX_TXPOWER - 1; i++) + range->txpower[i] = 20 * i / (IW_MAX_TXPOWER - 1); + range->num_txpower = IW_MAX_TXPOWER; + range->txpower_capa = IW_TXPOW_DBM; + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 0x9; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 1; + range->max_retry = 255; + + range->r_time_flags = IW_RETRY_LIFETIME; + range->min_r_time = 0; + /* FIXME: lifetime ranges and orders of magnitude are strange?? */ + range->max_r_time = 65535; + + if (IS_USB(priv)) + range->sensitivity = 0; + else if (IS_ACX111(priv)) + range->sensitivity = 3; + else + range->sensitivity = 255; - range->max_qual.qual = 100; - range->max_qual.level = 100; - range->max_qual.noise = 100; - /* TODO: better values */ - range->avg_qual.qual = 90; - range->avg_qual.level = 80; - range->avg_qual.noise = 2; + for (i=0; i < priv->rate_supported_len; i++) { + range->bitrate[i] = (priv->rate_supported[i] & ~0x80) * 500000; + /* never happens, but keep it, to be safe: */ + if (range->bitrate[i] == 0) + break; } + range->num_bitrates = i; + + range->max_qual.qual = 100; + range->max_qual.level = 100; + range->max_qual.noise = 100; + /* TODO: better values */ + range->avg_qual.qual = 90; + range->avg_qual.level = 80; + range->avg_qual.noise = 2; +end: + FN_EXIT1(OK); return OK; } @@ -1479,7 +1504,8 @@ acx_ioctl_get_iw_priv(struct iwreq *iwr) return result; iwr->u.data.length = VEC_SIZE(acx_ioctl_private_args); - if (copy_to_user(iwr->u.data.pointer, acx_ioctl_private_args, sizeof(acx_ioctl_private_args)) != 0) + if (copy_to_user(iwr->u.data.pointer, + acx_ioctl_private_args, sizeof(acx_ioctl_private_args)) != 0) result = -EFAULT; return result; @@ -1497,7 +1523,7 @@ acx_ioctl_get_nick( struct iw_point *dwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); /* FIXME : consider spinlock here */ strcpy(extra, priv->nick); @@ -1519,7 +1545,7 @@ acx_ioctl_set_nick( struct iw_point *dwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; FN_ENTER; @@ -1553,11 +1579,13 @@ acx_ioctl_get_retry( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); unsigned int type = vwrq->flags & IW_RETRY_TYPE; unsigned int modifier = vwrq->flags & IW_RETRY_MODIFIER; int result; + FN_ENTER; + acx_sem_lock(priv); /* return the short retry number by default */ @@ -1580,6 +1608,7 @@ acx_ioctl_get_retry( acx_sem_unlock(priv); + FN_EXIT1(result); return result; } @@ -1594,7 +1623,7 @@ acx_ioctl_set_retry( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; FN_ENTER; @@ -1714,7 +1743,7 @@ acx_ioctl_set_reg_domain( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; FN_ENTER; @@ -1748,7 +1777,7 @@ acx_ioctl_get_reg_domain( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int dom,i; /* no locking */ @@ -1786,7 +1815,7 @@ acx_ioctl_set_short_preamble( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int i; int result; @@ -1856,7 +1885,7 @@ acx_ioctl_get_short_preamble( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); acx_sem_lock(priv); @@ -1885,7 +1914,7 @@ acx_ioctl_set_antenna( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); acx_sem_lock(priv); @@ -1921,7 +1950,7 @@ acx_ioctl_get_antenna( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); /* no locking. it's pointless to lock a single load */ printk("current antenna value: 0x%02X (COMBINED bit mask)\n" @@ -1953,7 +1982,7 @@ acx_ioctl_set_rx_antenna( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; FN_ENTER; @@ -1994,7 +2023,7 @@ acx_ioctl_set_tx_antenna( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; FN_ENTER; @@ -2033,7 +2062,7 @@ acx_ioctl_wlansniff( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); unsigned int *params = (unsigned int*)extra; unsigned int enable = (unsigned int)(params[0] > 0); int result; @@ -2088,7 +2117,7 @@ acx_ioctl_unknown11( char *extra) { #ifdef BROKEN - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); unsigned long flags; client_t client; int result; @@ -2118,7 +2147,7 @@ acx_ioctl_dbg_set_masks( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); const unsigned int *params = (unsigned int*)extra; int result; @@ -2236,7 +2265,7 @@ static int acx_ioctl_set_rates(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); unsigned long flags; int result; u32 brate = 0, orate = 0; /* basic, operational rate set */ @@ -2296,34 +2325,36 @@ acx_ioctl_get_phy_chan_busy_percentage( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); struct { /* added ACX_PACKED, not tested --vda */ u16 type ACX_PACKED; u16 len ACX_PACKED; u32 busytime ACX_PACKED; u32 totaltime ACX_PACKED; } usage; + int result; acx_sem_lock(priv); - if (OK != acx_s_interrogate(priv, &usage, ACX1xx_IE_MEDIUM_USAGE)) - goto bad_unlock; + if (OK != acx_s_interrogate(priv, &usage, ACX1xx_IE_MEDIUM_USAGE)) { + result = NOT_OK; + goto end_unlock; + } + usage.busytime = le32_to_cpu(usage.busytime); + usage.totaltime = le32_to_cpu(usage.totaltime); printk("%s: average busy percentage since last invocation: %d%% " - "(microseconds: %u of %u)\n", + "(%u of %u microseconds)\n", dev->name, - 100 * (le32_to_cpu(usage.busytime) / 100) / (le32_to_cpu(usage.totaltime) / 100), - le32_to_cpu(usage.busytime), le32_to_cpu(usage.totaltime)); - /* prevent calculation overflow */ - - acx_sem_unlock(priv); + usage.busytime / ((usage.totaltime / 100) + 1), + usage.busytime, usage.totaltime); - return OK; + result = OK; -bad_unlock: +end_unlock: acx_sem_unlock(priv); - return NOT_OK; + return result; } @@ -2337,7 +2368,7 @@ acx_ioctl_set_ed_threshold( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); acx_sem_lock(priv); @@ -2362,7 +2393,7 @@ acx_ioctl_set_cca( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; acx_sem_lock(priv); @@ -2401,7 +2432,7 @@ acx_ioctl_set_scan_params( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; const int *params = (int *)extra; @@ -2432,7 +2463,7 @@ acx_ioctl_get_scan_params( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; int *params = (int *)extra; @@ -2462,7 +2493,7 @@ acx100_ioctl_set_led_power( { static const char * const led_modes[] = { "off", "on", "LinkQuality" }; - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result; acx_sem_lock(priv); @@ -2504,7 +2535,7 @@ acx100_ioctl_get_led_power( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); acx_sem_lock(priv); @@ -2529,7 +2560,7 @@ acx111_ioctl_info( struct iw_param *vwrq, char *extra) { - if (!IS_PCI((wlandevice_t*)acx_netdev_priv(dev))) + if (!IS_PCI((wlandevice_t*)netdev_priv(dev))) return OK; return acx111pci_ioctl_info(dev, info, vwrq, extra); } @@ -2544,7 +2575,7 @@ acx100_ioctl_set_phy_amp_bias( struct iw_param *vwrq, char *extra) { - if (!IS_PCI((wlandevice_t*)acx_netdev_priv(dev))) { + if (!IS_PCI((wlandevice_t*)netdev_priv(dev))) { printk("acx: set_phy_amp_bias() is not supported on USB\n"); return OK; } @@ -2681,7 +2712,7 @@ const struct iw_handler_def acx_ioctl_ha int acx_e_ioctl_old(netdevice_t *dev, struct ifreq *ifr, int cmd) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result = 0; struct iwreq *iwr = (struct iwreq *)ifr; diff -puN drivers/net/wireless/tiacx/Kconfig~acx-update-2 drivers/net/wireless/tiacx/Kconfig --- devel/drivers/net/wireless/tiacx/Kconfig~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/Kconfig 2005-10-17 13:06:00.000000000 -0700 @@ -7,9 +7,20 @@ config ACX This driver supports Host AP mode that allows your computer to act as an IEEE 802.11 access point. - This driver is quite new and experimental. + This driver is new and experimental. - These chipsets need their firmware loaded at startup. + Texas Instruments did not take part in development of this driver + in any way, shape or form. + + The driver can be compiled as a module and will be named "acx". + +config ACX_PCI + bool "TI acx100/acx111 802.11b/g PCI" + depends on ACX && PCI + ---help--- + Include PCI and CardBus support in acx. + + acx chipsets need their firmware loaded at startup. You will need to provide a firmware image via hotplug. Firmware may be in a form of single image 40-100kb in size @@ -32,22 +43,20 @@ config ACX Firmware files are not covered by GPL and are not distributed with this driver for legal reasons. - Texas Instruments did not take part in development of this driver - in any way, shape or form. - - The driver can be compiled as a module and will be named "acx". - -config ACX_PCI - bool "TI acx100/acx111 802.11b/g PCI" - depends on PCI && ACX - ---help--- - Include PCI and CardBus support in acx. - config ACX_USB bool "TI acx100/acx111 802.11b/g USB" - depends on USB && ACX && BROKEN + depends on ACX && (USB=y || USB=ACX) ---help--- Include USB support in acx. There is only one currently known device in this category, D-Link DWL-120+, but newer devices seem to be on the horizon. + + acx chipsets need their firmware loaded at startup. + You will need to provide a firmware image via hotplug. + + Firmware for USB device is requested from hotplug + by the 'tiacx100usb' name. + + Firmware files are not covered by GPL and are not distributed + with this driver for legal reasons. diff -puN drivers/net/wireless/tiacx/Makefile~acx-update-2 drivers/net/wireless/tiacx/Makefile --- devel/drivers/net/wireless/tiacx/Makefile~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/Makefile 2005-10-17 13:06:00.000000000 -0700 @@ -3,4 +3,4 @@ obj-$(CONFIG_ACX) += acx.o acx-obj-$(CONFIG_ACX_PCI) += pci.o acx-obj-$(CONFIG_ACX_USB) += usb.o -acx-objs := wlan.o conv.o ioctl.o helper.o helper2.o $(acx-obj-y) +acx-objs := wlan.o conv.o ioctl.o common.o $(acx-obj-y) diff -puN drivers/net/wireless/tiacx/pci.c~acx-update-2 drivers/net/wireless/tiacx/pci.c --- devel/drivers/net/wireless/tiacx/pci.c~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/pci.c 2005-10-17 13:06:00.000000000 -0700 @@ -56,8 +56,8 @@ #include "acx.h" -/*================================================================*/ -/* Local Constants */ +/*********************************************************************** +*/ #define PCI_TYPE (PCI_USES_MEM | PCI_ADDR0 | PCI_NO_ACPI_WAKE) #define PCI_ACX100_REGION1 0x01 #define PCI_ACX100_REGION1_SIZE 0x1000 /* Memory size - 4K bytes */ @@ -80,104 +80,94 @@ #define PCI_DEVICE_ID_TI_TNETW1130 0x9066 /* PCI Class & Sub-Class code, Network-'Other controller' */ -#define PCI_CLASS_NETWORK_OTHERS 0x280 +#define PCI_CLASS_NETWORK_OTHERS 0x0280 - -/*================================================================*/ -/* Local Static Definitions */ #define CARD_EEPROM_ID_SIZE 6 -#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */ -static const char name_acx100[] = "ACX100"; -static const char name_tnetw1100a[] = "TNETW1100A"; -static const char name_tnetw1100b[] = "TNETW1100B"; -static const char name_acx111[] = "ACX111"; -static const char name_tnetw1130[] = "TNETW1130"; +/*********************************************************************** +*/ +static void acxpci_i_tx_timeout(netdevice_t *dev); +static irqreturn_t acxpci_i_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void acxpci_i_set_multicast_list(netdevice_t *dev); + +static int acxpci_e_open(netdevice_t *dev); +static int acxpci_e_close(netdevice_t *dev); +static void acxpci_s_up(netdevice_t *dev); +static void acxpci_s_down(netdevice_t *dev); -static const struct pci_device_id -acx_pci_id_tbl[] __devinitdata = { - { - .vendor = PCI_VENDOR_ID_TI, - .device = PCI_DEVICE_ID_TI_TNETW1100A, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = CHIPTYPE_ACX100, - }, - { - .vendor = PCI_VENDOR_ID_TI, - .device = PCI_DEVICE_ID_TI_TNETW1100B, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = CHIPTYPE_ACX100, - }, - { - .vendor = PCI_VENDOR_ID_TI, - .device = PCI_DEVICE_ID_TI_TNETW1130, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = CHIPTYPE_ACX111, - }, - { - .vendor = 0, - .device = 0, - .subvendor = 0, - .subdevice = 0, - .driver_data = 0, - } -}; -MODULE_DEVICE_TABLE(pci, acx_pci_id_tbl); +/*********************************************************************** +** Register access +*/ -static void acx_l_disable_irq(wlandevice_t *priv); -static void acx_l_enable_irq(wlandevice_t *priv); -static int acx_e_probe_pci(struct pci_dev *pdev, - const struct pci_device_id *id); -static void acx_e_remove_pci(struct pci_dev *pdev); +/* Pick one */ +/* #define INLINE_IO static */ +#define INLINE_IO static inline -#ifdef CONFIG_PM -static int acx_e_suspend(struct pci_dev *pdev, pm_message_t state); -static int acx_e_resume(struct pci_dev *pdev); +INLINE_IO u32 +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 +} -static void acx_i_tx_timeout(netdevice_t *dev); -static struct net_device_stats *acx_e_get_stats(netdevice_t *dev); -static struct iw_statistics *acx_e_get_wireless_stats(netdevice_t *dev); - -static irqreturn_t acx_i_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void acx_i_set_multicast_list(netdevice_t *dev); - -static int acx_e_open(netdevice_t *dev); -static int acx_e_close(netdevice_t *dev); -static void acx_s_up(netdevice_t *dev); -static void acx_s_down(netdevice_t *dev); +INLINE_IO u16 +read_reg16(wlandevice_t *priv, unsigned int offset) +{ + return readw((u8 *)priv->iobase + priv->io[offset]); +} +INLINE_IO u8 +read_reg8(wlandevice_t *priv, unsigned int offset) +{ + return readb((u8 *)priv->iobase + priv->io[offset]); +} -/* FIXME: checks should be removed once driver is included in the kernel */ -#ifndef __devexit_p -#warning *** your kernel is EXTREMELY old since it does not even know about -#warning __devexit_p - this driver could easily FAIL to work, so better -#warning upgrade your kernel! *** -#define __devexit_p(x) x +INLINE_IO void +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 +} -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11) -/* pci_name() got introduced at start of 2.6.x, - * got mandatory (slot_name member removed) in 2.6.11-bk1 */ -#define pci_name(x) x->slot_name -#endif +INLINE_IO void +write_reg16(wlandevice_t *priv, unsigned int offset, u16 val) +{ + writew(val, (u8 *)priv->iobase + priv->io[offset]); +} -static struct pci_driver acx_pci_drv_id = { - .name = "acx_pci", - .id_table = acx_pci_id_tbl, - .probe = acx_e_probe_pci, - .remove = __devexit_p(acx_e_remove_pci), -#ifdef CONFIG_PM - .suspend = acx_e_suspend, - .resume = acx_e_resume -#endif /* CONFIG_PM */ -}; +INLINE_IO void +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 +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); +} + +/*********************************************************************** +*/ typedef struct acx_device { netdevice_t *newest; } acx_device_t; @@ -193,26 +183,77 @@ DECLARE_MUTEX(root_acx_dev_sem); /*********************************************************************** -** Register access */ -/* If INLINE_IO is not defined, it means user wants to have out-of-line -** acx_r/w_regNN() functions. We are doing this with #define magic -** in acx_inline.h: */ -#ifndef INLINE_IO -#define INLINE_IO /* defined to nothing */ -#include "acx_inline.h" -#endif +static inline txdesc_t* +get_txdesc(wlandevice_t* priv, int index) +{ + return (txdesc_t*) (((u8*)priv->txdesc_start) + index * priv->txdesc_size); +} + +static inline txdesc_t* +move_txdesc(wlandevice_t* priv, txdesc_t* txdesc, int inc) +{ + return (txdesc_t*) (((u8*)txdesc) + inc * priv->txdesc_size); +} + +static txhostdesc_t* +get_txhostdesc(wlandevice_t* priv, txdesc_t* txdesc) +{ + int index = (u8*)txdesc - (u8*)priv->txdesc_start; + if (ACX_DEBUG && (index % priv->txdesc_size)) { + printk("bad txdesc ptr %p\n", txdesc); + return NULL; + } + index /= priv->txdesc_size; + if (ACX_DEBUG && (index >= TX_CNT)) { + printk("bad txdesc ptr %p\n", txdesc); + return NULL; + } + return &priv->txhostdesc_start[index*2]; +} + +static client_t* +get_txc(wlandevice_t* priv, txdesc_t* txdesc) +{ + int index = (u8*)txdesc - (u8*)priv->txdesc_start; + if (ACX_DEBUG && (index % priv->txdesc_size)) { + printk("bad txdesc ptr %p\n", txdesc); + return NULL; + } + index /= priv->txdesc_size; + if (ACX_DEBUG && (index >= TX_CNT)) { + printk("bad txdesc ptr %p\n", txdesc); + return NULL; + } + return priv->txc[index]; +} + +static void +put_txc(wlandevice_t* priv, txdesc_t* txdesc, client_t* c) +{ + int index = (u8*)txdesc - (u8*)priv->txdesc_start; + if (ACX_DEBUG && (index % priv->txdesc_size)) { + printk("bad txdesc ptr %p\n", txdesc); + return; + } + index /= priv->txdesc_size; + if (ACX_DEBUG && (index >= TX_CNT)) { + printk("bad txdesc ptr %p\n", txdesc); + return; + } + priv->txc[index] = c; +} /*********************************************************************** ** EEPROM and PHY read/write helpers */ /*********************************************************************** -** acx_read_eeprom_offset +** acxpci_read_eeprom_byte ** ** Function called to read an octet in the EEPROM. ** -** This function is used by acx_probe_pci to check if the +** This function is used by acxpci_e_probe to check if the ** connected card is a legal one or not. ** ** Arguments: @@ -220,26 +261,20 @@ DECLARE_MUTEX(root_acx_dev_sem); ** addr address to read in the EEPROM ** charbuf ptr to a char. This is where the read octet ** will be stored -** -** Returns: -** zero (0) - failed -** one (1) - success -** -** NOT ADAPTED FOR ACX111!! */ int -acx_read_eeprom_offset(wlandevice_t *priv, u32 addr, u8 *charbuf) +acxpci_read_eeprom_byte(wlandevice_t *priv, u32 addr, u8 *charbuf) { - int result = NOT_OK; + int result; int count; - acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0); - acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr); - acx_write_flush(priv); - acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 2); + write_reg32(priv, IO_ACX_EEPROM_CFG, 0); + write_reg32(priv, IO_ACX_EEPROM_ADDR, addr); + write_flush(priv); + write_reg32(priv, IO_ACX_EEPROM_CTL, 2); count = 0xffff; - while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) { + while (read_reg16(priv, IO_ACX_EEPROM_CTL)) { /* scheduling away instead of CPU burning loop * doesn't seem to work here at all: * awful delay, sometimes also failure. @@ -247,11 +282,12 @@ acx_read_eeprom_offset(wlandevice_t *pri if (unlikely(!--count)) { printk("%s: timeout waiting for EEPROM read\n", priv->netdev->name); + result = NOT_OK; goto fail; } } - *charbuf = acx_read_reg8(priv, IO_ACX_EEPROM_DATA); + *charbuf = read_reg8(priv, IO_ACX_EEPROM_DATA); acxlog(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf); result = OK; @@ -264,13 +300,13 @@ fail: ** Dummy EEPROM read? why?! */ static int -acx_read_eeprom_area(wlandevice_t *priv) +acxpci_read_eeprom_area(wlandevice_t *priv) { int offs; u8 tmp[0x3b]; for (offs = 0x8c; offs < 0xb9; offs++) { - acx_read_eeprom_offset(priv, offs, &tmp[offs - 0x8c]); + acxpci_read_eeprom_byte(priv, offs, &tmp[offs - 0x8c]); } return OK; } @@ -282,7 +318,7 @@ acx_read_eeprom_area(wlandevice_t *priv) */ #ifdef UNUSED int -acx_s_write_eeprom_offset(wlandevice_t *priv, u32 addr, u32 len, const u8 *charbuf) +acxpci_s_write_eeprom(wlandevice_t *priv, u32 addr, u32 len, const u8 *charbuf) { u8 *data_verify = NULL; unsigned long flags; @@ -314,19 +350,19 @@ acx_s_write_eeprom_offset(wlandevice_t * * but you probably have to modify GPIO_OUT, too, * and you probably need to activate a different GPIO * line instead! */ - gpio_orig = acx_read_reg16(priv, IO_ACX_GPIO_OE); - acx_write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig & ~1); - acx_write_flush(priv); + gpio_orig = read_reg16(priv, IO_ACX_GPIO_OE); + write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig & ~1); + write_flush(priv); /* ok, now start writing the data out */ for (i = 0; i < len; i++) { - acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0); - acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i); - acx_write_reg32(priv, IO_ACX_EEPROM_DATA, *(charbuf + i)); - acx_write_flush(priv); - acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 1); + write_reg32(priv, IO_ACX_EEPROM_CFG, 0); + write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i); + write_reg32(priv, IO_ACX_EEPROM_DATA, *(charbuf + i)); + write_flush(priv); + write_reg32(priv, IO_ACX_EEPROM_CTL, 1); - while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) { + while (read_reg16(priv, IO_ACX_EEPROM_CTL)) { if (unlikely(++count > 0xffff)) { printk("WARNING, DANGER!!! " "Timeout waiting for EEPROM write\n"); @@ -336,25 +372,25 @@ acx_s_write_eeprom_offset(wlandevice_t * } /* disable EEPROM writing */ - acx_write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig); - acx_write_flush(priv); + write_reg16(priv, IO_ACX_GPIO_OE, gpio_orig); + write_flush(priv); /* now start a verification run */ count = 0xffff; for (i = 0; i < len; i++) { - acx_write_reg32(priv, IO_ACX_EEPROM_CFG, 0); - acx_write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i); - acx_write_flush(priv); - acx_write_reg32(priv, IO_ACX_EEPROM_CTL, 2); + write_reg32(priv, IO_ACX_EEPROM_CFG, 0); + write_reg32(priv, IO_ACX_EEPROM_ADDR, addr + i); + write_flush(priv); + write_reg32(priv, IO_ACX_EEPROM_CTL, 2); - while (acx_read_reg16(priv, IO_ACX_EEPROM_CTL)) { + while (read_reg16(priv, IO_ACX_EEPROM_CTL)) { if (unlikely(!--count)) { printk("timeout waiting for EEPROM read\n"); goto end; } } - data_verify[i] = acx_read_reg16(priv, IO_ACX_EEPROM_DATA); + data_verify[i] = read_reg16(priv, IO_ACX_EEPROM_DATA); } if (0 == memcmp(charbuf, data_verify, len)) @@ -372,7 +408,7 @@ end: ** acxpci_s_read_phy_reg ** ** Messing with rx/tx disabling and enabling here -** (acx_write_reg32(priv, IO_ACX_ENABLE, 0b000000xx)) kills traffic +** (write_reg32(priv, IO_ACX_ENABLE, 0b000000xx)) kills traffic */ int acxpci_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf) @@ -382,12 +418,12 @@ acxpci_s_read_phy_reg(wlandevice_t *priv FN_ENTER; - acx_write_reg32(priv, IO_ACX_PHY_ADDR, reg); - acx_write_flush(priv); - acx_write_reg32(priv, IO_ACX_PHY_CTL, 2); + write_reg32(priv, IO_ACX_PHY_ADDR, reg); + write_flush(priv); + write_reg32(priv, IO_ACX_PHY_CTL, 2); count = 0xffff; - while (acx_read_reg32(priv, IO_ACX_PHY_CTL)) { + while (read_reg32(priv, IO_ACX_PHY_CTL)) { /* scheduling away instead of CPU burning loop * doesn't seem to work here at all: * awful delay, sometimes also failure. @@ -401,7 +437,7 @@ acxpci_s_read_phy_reg(wlandevice_t *priv } acxlog(L_DEBUG, "count was %u\n", count); - *charbuf = acx_read_reg8(priv, IO_ACX_PHY_DATA); + *charbuf = read_reg8(priv, IO_ACX_PHY_DATA); acxlog(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg); result = OK; @@ -426,11 +462,11 @@ acxpci_s_write_phy_reg(wlandevice_t *pri * shouldn't happen any more... * FIXME: which radio is in the problematic card? My working one * is 0x11 */ - acx_write_reg32(priv, IO_ACX_PHY_DATA, value); - acx_write_reg32(priv, IO_ACX_PHY_ADDR, reg); - acx_write_flush(priv); - acx_write_reg32(priv, IO_ACX_PHY_CTL, 1); - acx_write_flush(priv); + write_reg32(priv, IO_ACX_PHY_DATA, value); + write_reg32(priv, IO_ACX_PHY_ADDR, reg); + write_flush(priv); + write_reg32(priv, IO_ACX_PHY_CTL, 1); + write_flush(priv); acxlog(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg); FN_EXIT1(OK); @@ -441,7 +477,7 @@ acxpci_s_write_phy_reg(wlandevice_t *pri #define NO_AUTO_INCREMENT 1 /*********************************************************************** -** acx_s_write_fw +** acxpci_s_write_fw ** ** Write the firmware image into the card. ** @@ -454,7 +490,7 @@ acxpci_s_write_phy_reg(wlandevice_t *pri ** 0 success */ static int -acx_s_write_fw(wlandevice_t *priv, const firmware_image_t *apfw_image, u32 offset) +acxpci_s_write_fw(wlandevice_t *priv, const firmware_image_t *apfw_image, u32 offset) { int len, size; u32 sum, v32; @@ -465,15 +501,14 @@ acx_s_write_fw(wlandevice_t *priv, const sum = image[0]+image[1]+image[2]+image[3]; image += 4; - acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0); + write_reg32(priv, IO_ACX_SLV_END_CTL, 0); #if NO_AUTO_INCREMENT - acxlog(L_INIT, "not using auto increment for firmware loading\n"); - acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ + write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ #else - acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ - acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ - acx_write_flush(priv); + write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ + write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ + write_flush(priv); #endif len = 0; @@ -486,13 +521,14 @@ acx_s_write_fw(wlandevice_t *priv, const len += 4; #if NO_AUTO_INCREMENT - acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4); - acx_write_flush(priv); + write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4); + write_flush(priv); #endif - acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, v32); + write_reg32(priv, IO_ACX_SLV_MEM_DATA, v32); } - acxlog(L_DEBUG, "%s: firmware written\n", __func__); + acxlog(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n", + size, sum, le32_to_cpu(apfw_image->chksum)); /* compare our checksum with the stored image checksum */ return (sum != le32_to_cpu(apfw_image->chksum)); @@ -500,7 +536,7 @@ acx_s_write_fw(wlandevice_t *priv, const /*********************************************************************** -** acx_s_validate_fw +** acxpci_s_validate_fw ** ** Compare the firmware image given with ** the firmware image written into the card. @@ -514,7 +550,7 @@ acx_s_write_fw(wlandevice_t *priv, const ** OK success */ static int -acx_s_validate_fw(wlandevice_t *priv, const firmware_image_t *apfw_image, +acxpci_s_validate_fw(wlandevice_t *priv, const firmware_image_t *apfw_image, u32 offset) { u32 v32, w32, sum; @@ -527,13 +563,13 @@ acx_s_validate_fw(wlandevice_t *priv, co sum = image[0]+image[1]+image[2]+image[3]; image += 4; - acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0); + write_reg32(priv, IO_ACX_SLV_END_CTL, 0); #if NO_AUTO_INCREMENT - acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ + write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ #else - acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ - acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ + write_reg32(priv, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ + write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ #endif len = 0; @@ -545,9 +581,9 @@ acx_s_validate_fw(wlandevice_t *priv, co len += 4; #if NO_AUTO_INCREMENT - acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4); + write_reg32(priv, IO_ACX_SLV_MEM_ADDR, offset + len - 4); #endif - w32 = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA); + w32 = read_reg32(priv, IO_ACX_SLV_MEM_DATA); if (unlikely(w32 != v32)) { printk("acx: FATAL: firmware upload: " @@ -576,18 +612,12 @@ acx_s_validate_fw(wlandevice_t *priv, co /*********************************************************************** -** acx_s_upload_fw +** acxpci_s_upload_fw ** -** Arguments: -** wlandevice: private device that contains card device -** Returns: -** NOT_OK: failed -** OK: success -** Call context: -** acx_reset_dev +** Called from acx_reset_dev */ static int -acx_s_upload_fw(wlandevice_t *priv) +acxpci_s_upload_fw(wlandevice_t *priv) { firmware_image_t *apfw_image = NULL; int res = NOT_OK; @@ -614,10 +644,10 @@ acx_s_upload_fw(wlandevice_t *priv) } for (try = 1; try <= 5; try++) { - res = acx_s_write_fw(priv, apfw_image, 0); + res = acxpci_s_write_fw(priv, apfw_image, 0); acxlog(L_DEBUG|L_INIT, "acx_write_fw (main/combined):%d\n", res); if (OK == res) { - res = acx_s_validate_fw(priv, apfw_image, 0); + res = acxpci_s_validate_fw(priv, apfw_image, 0); acxlog(L_DEBUG|L_INIT, "acx_validate_fw " "(main/combined):%d\n", res); } @@ -639,13 +669,12 @@ acx_s_upload_fw(wlandevice_t *priv) /*********************************************************************** -** acx_s_upload_radio +** acxpci_s_upload_radio ** -** Uploads the appropriate radio module firmware -** into the card. +** Uploads the appropriate radio module firmware into the card. */ int -acx_s_upload_radio(wlandevice_t *priv) +acxpci_s_upload_radio(wlandevice_t *priv) { acx_ie_memmap_t mm; firmware_image_t *radio_image = NULL; @@ -675,10 +704,10 @@ acx_s_upload_radio(wlandevice_t *priv) acx_s_issue_cmd(priv, ACX1xx_CMD_SLEEP, NULL, 0); for (try = 1; try <= 5; try++) { - res = acx_s_write_fw(priv, radio_image, offset); + res = acxpci_s_write_fw(priv, radio_image, offset); acxlog(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res); if (OK == res) { - res = acx_s_validate_fw(priv, radio_image, offset); + res = acxpci_s_validate_fw(priv, radio_image, offset); acxlog(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res); } @@ -711,7 +740,7 @@ fail: /*********************************************************************** -** acx_l_reset_mac +** acxpci_l_reset_mac ** ** Arguments: ** wlandevice: private device that contains card device @@ -720,46 +749,44 @@ fail: ** Call context: ** acx_reset_dev ** Comment: -** resets onboard acx100 MAC -** -** Requires lock to be taken +** resets onboard acx MAC */ static void -acx_l_reset_mac(wlandevice_t *priv) +acxpci_l_reset_mac(wlandevice_t *priv) { u16 temp; FN_ENTER; /* halt eCPU */ - temp = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1; - acx_write_reg16(priv, IO_ACX_ECPU_CTRL, temp); + temp = read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1; + write_reg16(priv, IO_ACX_ECPU_CTRL, temp); /* now do soft reset of eCPU */ - temp = acx_read_reg16(priv, IO_ACX_SOFT_RESET) | 0x1; + temp = read_reg16(priv, IO_ACX_SOFT_RESET) | 0x1; acxlog(L_DEBUG, "%s: enable soft reset...\n", __func__); - acx_write_reg16(priv, IO_ACX_SOFT_RESET, temp); - acx_write_flush(priv); + write_reg16(priv, IO_ACX_SOFT_RESET, temp); + write_flush(priv); /* now reset bit again */ acxlog(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__); /* deassert eCPU reset */ - acx_write_reg16(priv, IO_ACX_SOFT_RESET, temp & ~0x1); + write_reg16(priv, IO_ACX_SOFT_RESET, temp & ~0x1); /* now start a burst read from initial flash EEPROM */ - temp = acx_read_reg16(priv, IO_ACX_EE_START) | 0x1; - acx_write_reg16(priv, IO_ACX_EE_START, temp); - acx_write_flush(priv); + temp = read_reg16(priv, IO_ACX_EE_START) | 0x1; + write_reg16(priv, IO_ACX_EE_START, temp); + write_flush(priv); FN_EXIT0; } /*********************************************************************** -** acx_s_verify_init +** acxpci_s_verify_init */ static int -acx_s_verify_init(wlandevice_t *priv) +acxpci_s_verify_init(wlandevice_t *priv) { int result = NOT_OK; int timer; @@ -767,15 +794,13 @@ acx_s_verify_init(wlandevice_t *priv) FN_ENTER; for (timer = 40; timer > 0; timer--) { - u16 irqstat = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES); + u16 irqstat = read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES); if (irqstat & HOST_INT_FCS_THRESHOLD) { result = OK; - acx_write_reg16(priv, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD); + write_reg16(priv, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD); break; } - /* HZ / 50 resulted in 24 schedules for ACX100 on my machine, - * so better schedule away longer for greater efficiency, - * decrease loop count */ + /* Init may take up to ~0.5 sec total */ acx_s_msleep(50); } @@ -793,104 +818,51 @@ acx_s_verify_init(wlandevice_t *priv) */ /*********************************************************************** -** acx_read_info_status -*/ -/* Info mailbox format: -2 bytes: type -2 bytes: status -more bytes may follow - docs say about status: - 0x0000 info available (set by hw) - 0x0001 information received (must be set by host) - 0x1000 info available, mailbox overflowed (messages lost) (set by hw) - but in practice we've seen: - 0x9000 when we did not set status to 0x0001 on prev message - 0x1001 when we did set it - 0x0000 was never seen - conclusion: this is really a bitfield: - 0x1000 is 'info available' bit - 'mailbox overflowed' bit is 0x8000, not 0x1000 - value of 0x0000 probably means that there is no message at all - P.S. I dunno how in hell hw is supposed to notice that messages are lost - - it does NOT clear bit 0x0001, and this bit will probably stay forever set - after we set it once. Let's hope this will be fixed in firmware someday -*/ -static void -acx_read_info_status(wlandevice_t *priv) -{ - u32 value; - - acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0); - acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); - - acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, - acx_read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS)); - - /* make sure we only read the data once all cfg registers are written: */ - acx_write_flush(priv); - value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA); - - priv->info_type = (u16)value; - priv->info_status = (value >> 16); - - /* inform hw that we have read this info message */ - acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, priv->info_type | 0x00010000); - acx_write_flush(priv); - /* now bother hw to notice it: */ - acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_INFOACK); - acx_write_flush(priv); - - acxlog(L_CTL, "info_type 0x%04X, info_status 0x%04X\n", - priv->info_type, priv->info_status); -} - - -/*********************************************************************** -** acx_write_cmd_type_or_status +** acxpci_write_cmd_type_or_status */ static void -acx_write_cmd_type_or_status(wlandevice_t *priv, u32 val) +acxpci_write_cmd_type_or_status(wlandevice_t *priv, u32 val) { - acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0); - acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */ + write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0); + write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */ - acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, - acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS)); + write_reg32(priv, IO_ACX_SLV_MEM_ADDR, + read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS)); /* make sure we only write the data once all config registers are written */ - acx_write_flush(priv); - acx_write_reg32(priv, IO_ACX_SLV_MEM_DATA, val); - acx_write_flush(priv); + write_flush(priv); + write_reg32(priv, IO_ACX_SLV_MEM_DATA, val); + write_flush(priv); } static inline void -acx_write_cmd_type(wlandevice_t *priv, u32 val) +acxpci_write_cmd_type(wlandevice_t *priv, u32 val) { - acx_write_cmd_type_or_status(priv, val); + acxpci_write_cmd_type_or_status(priv, val); } static inline void -acx_write_cmd_status(wlandevice_t *priv, u32 val) +acxpci_write_cmd_status(wlandevice_t *priv, u32 val) { - acx_write_cmd_type_or_status(priv, val<<16); + acxpci_write_cmd_type_or_status(priv, val<<16); } /*********************************************************************** -** acx_read_cmd_status +** acxpci_read_cmd_status */ static void -acx_read_cmd_status(wlandevice_t *priv) +acxpci_read_cmd_status(wlandevice_t *priv) { u32 value; - acx_write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0); - acx_write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */ + write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0); + write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); /* FIXME: why auto increment?? */ - acx_write_reg32(priv, IO_ACX_SLV_MEM_ADDR, - acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS)); + write_reg32(priv, IO_ACX_SLV_MEM_ADDR, + read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS)); /* make sure we only read the data once all config registers are written */ - acx_write_flush(priv); - value = acx_read_reg32(priv, IO_ACX_SLV_MEM_DATA); + write_flush(priv); + value = read_reg32(priv, IO_ACX_SLV_MEM_DATA); priv->cmd_type = (u16)value; priv->cmd_status = (value >> 16); @@ -902,7 +874,7 @@ acx_read_cmd_status(wlandevice_t *priv) /*********************************************************************** -** acx_s_reset_dev +** acxpci_s_reset_dev ** ** Arguments: ** netdevice that contains the wlandevice priv variable @@ -912,15 +884,15 @@ acx_read_cmd_status(wlandevice_t *priv) ** Side effects: ** device is hard reset ** Call context: -** acx_probe_pci +** acxpci_e_probe ** Comment: -** This resets the acx100 device using low level hardware calls +** This resets the device using low level hardware calls ** as well as uploads and verifies the firmware to the card */ static int -acx_s_reset_dev(netdevice_t *dev) +acxpci_s_reset_dev(netdevice_t *dev) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); const char* msg = ""; unsigned long flags; int result = NOT_OK; @@ -936,23 +908,23 @@ acx_s_reset_dev(netdevice_t *dev) acx_lock(priv, flags); - acx_l_reset_mac(priv); + acxpci_l_reset_mac(priv); - ecpu_ctrl = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) & 1; + ecpu_ctrl = read_reg16(priv, IO_ACX_ECPU_CTRL) & 1; if (!ecpu_ctrl) { msg = "eCPU is already running. "; goto fail_unlock; } #ifdef WE_DONT_NEED_THAT_DO_WE - if (acx_read_reg16(priv, IO_ACX_SOR_CFG) & 2) { + if (read_reg16(priv, IO_ACX_SOR_CFG) & 2) { /* eCPU most likely means "embedded CPU" */ msg = "eCPU did not start after boot from flash. "; goto fail_unlock; } /* check sense on reset flags */ - if (acx_read_reg16(priv, IO_ACX_SOR_CFG) & 0x10) { + if (read_reg16(priv, IO_ACX_SOR_CFG) & 0x10) { printk("%s: eCPU did not start after boot (SOR), " "is this fatal?\n", dev->name); } @@ -967,22 +939,22 @@ acx_s_reset_dev(netdevice_t *dev) acx_s_msleep(10); /* Need to know radio type before fw load */ - hardware_info = acx_read_reg16(priv, IO_ACX_EEPROM_INFORMATION); + hardware_info = read_reg16(priv, IO_ACX_EEPROM_INFORMATION); priv->form_factor = hardware_info & 0xff; priv->radio_type = hardware_info >> 8; /* load the firmware */ - if (OK != acx_s_upload_fw(priv)) + if (OK != acxpci_s_upload_fw(priv)) goto fail; acx_s_msleep(10); /* now start eCPU by clearing bit */ acxlog(L_DEBUG, "booted eCPU up and waiting for completion...\n"); - acx_write_reg16(priv, IO_ACX_ECPU_CTRL, ecpu_ctrl & ~0x1); + write_reg16(priv, IO_ACX_ECPU_CTRL, ecpu_ctrl & ~0x1); /* wait for eCPU bootup */ - if (OK != acx_s_verify_init(priv)) { + if (OK != acxpci_s_verify_init(priv)) { msg = "timeout waiting for eCPU. "; goto fail; } @@ -991,16 +963,16 @@ acx_s_reset_dev(netdevice_t *dev) if (IS_ACX111(priv)) { acxlog(L_DEBUG, "cleaning up cmd mailbox access area\n"); - acx_write_cmd_status(priv, 0); - acx_read_cmd_status(priv); + acxpci_write_cmd_status(priv, 0); + acxpci_read_cmd_status(priv); if (priv->cmd_status) { msg = "error cleaning cmd mailbox area. "; goto fail; } } - /* TODO what is this one doing ?? adapt for acx111 */ - if ((OK != acx_read_eeprom_area(priv)) && IS_ACX100(priv)) { + /* Test that EEPROM is readable */ + if ((OK != acxpci_read_eeprom_area(priv)) && IS_ACX100(priv)) { /* does "CIS" mean "Card Information Structure"? * If so, then this would be a PCMCIA message... */ @@ -1023,17 +995,17 @@ fail: /*********************************************************************** -** acx_init_mboxes +** acxpci_init_mboxes */ void -acx_init_mboxes(wlandevice_t *priv) +acxpci_init_mboxes(wlandevice_t *priv) { u32 cmd_offs, info_offs; FN_ENTER; - cmd_offs = acx_read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS); - info_offs = acx_read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS); + cmd_offs = read_reg32(priv, IO_ACX_CMD_MAILBOX_OFFS); + info_offs = read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS); priv->cmd_area = (u8 *)priv->iobase2 + cmd_offs + 0x4; priv->info_area = (u8 *)priv->iobase2 + info_offs + 0x4; acxlog(L_DEBUG, "iobase2=%p\n" @@ -1047,21 +1019,18 @@ acx_init_mboxes(wlandevice_t *priv) } -/*---------------------------------------------------------------- -* acx_s_issue_cmd_timeo -* Excecutes a command in the command mailbox -* -* Arguments: -* *pcmdparam = an pointer to the data. The data mustn't include -* the 4 byte command header! -* -* NB: we do _not_ take lock inside, so be sure to not touch anything -* which may interfere with IRQ handler operation -* -* TODO: busy wait is a bit silly, so: -* 1) stop doing many iters - go to sleep after first -* 2) go to waitqueue based approach: wait, not poll! -*----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_s_issue_cmd_timeo +** +** Sends command to fw, extract result +** +** NB: we do _not_ take lock inside, so be sure to not touch anything +** which may interfere with IRQ handler operation +** +** TODO: busy wait is a bit silly, so: +** 1) stop doing many iters - go to sleep after first +** 2) go to waitqueue based approach: wait, not poll! +*/ #undef FUNC #define FUNC "issue_cmd" @@ -1115,7 +1084,7 @@ acxpci_s_issue_cmd_timeo_debug( /* wait for firmware to become idle for our command submission */ counter = 199; /* in ms */ do { - acx_read_cmd_status(priv); + acxpci_read_cmd_status(priv); /* Test for IDLE state */ if (!priv->cmd_status) break; @@ -1148,10 +1117,10 @@ acxpci_s_issue_cmd_timeo_debug( } /* now write the actual command type */ priv->cmd_type = cmd; - acx_write_cmd_type(priv, cmd); + acxpci_write_cmd_type(priv, cmd); /* execute command */ - acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_CMD); - acx_write_flush(priv); + write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_CMD); + write_flush(priv); /* wait for firmware to process command */ @@ -1168,9 +1137,9 @@ acxpci_s_issue_cmd_timeo_debug( counter = timeout; do { if (!priv->irqs_active) { /* IRQ disabled: poll */ - irqtype = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES); + irqtype = read_reg16(priv, IO_ACX_IRQ_STATUS_NON_DES); if (irqtype & HOST_INT_CMD_COMPLETE) { - acx_write_reg16(priv, IO_ACX_IRQ_ACK, + write_reg16(priv, IO_ACX_IRQ_ACK, HOST_INT_CMD_COMPLETE); break; } @@ -1187,12 +1156,12 @@ acxpci_s_issue_cmd_timeo_debug( } while (--counter); /* save state for debugging */ - acx_read_cmd_status(priv); + acxpci_read_cmd_status(priv); cmd_status = priv->cmd_status; /* put the card in IDLE state */ priv->cmd_status = 0; - acx_write_cmd_status(priv, 0); + acxpci_write_cmd_status(priv, 0); if (!counter) { /* timed out! */ printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. " @@ -1248,9 +1217,11 @@ bad: } -/*---------------------------------------------------------------- -* acx_s_get_firmware_version -*----------------------------------------------------------------*/ +/*********************************************************************** +** acx_s_get_firmware_version +** +** TODO: not pci-specific, move to common.c and use from usb.c too +*/ static void acx_s_get_firmware_version(wlandevice_t *priv) { @@ -1262,9 +1233,11 @@ acx_s_get_firmware_version(wlandevice_t FN_ENTER; + memset(fw.fw_id, 'E', FW_ID_SIZE); acx_s_interrogate(priv, &fw, ACX1xx_IE_FWREV); memcpy(priv->firmware_version, fw.fw_id, FW_ID_SIZE); priv->firmware_version[FW_ID_SIZE] = '\0'; + acxlog(L_DEBUG, "fw_ver: fw_id='%s' hw_id=%08X\n", priv->firmware_version, fw.hw_id); @@ -1316,14 +1289,14 @@ acx_s_get_firmware_version(wlandevice_t switch (priv->firmware_id & 0xffff0000) { case 0x01010000: case 0x01020000: - priv->chip_name = name_tnetw1100a; + priv->chip_name = "TNETW1100A"; break; case 0x01030000: - priv->chip_name = name_tnetw1100b; + priv->chip_name = "TNETW1100B"; break; case 0x03000000: case 0x03010000: - priv->chip_name = name_tnetw1130; + priv->chip_name = "TNETW1130"; break; default: printk("acx: unknown chip ID 0x%08X, " @@ -1335,18 +1308,13 @@ acx_s_get_firmware_version(wlandevice_t } -/*---------------------------------------------------------------- -* acx_display_hardware_details -* -* Arguments: -* priv: ptr to wlandevice that contains all the details -* displayed by this function -* Call context: -* acx_probe_pci -* Comment: -* This function will display strings to the system log according -* to device form_factor and radio type. It will needed to be -*----------------------------------------------------------------*/ +/*********************************************************************** +** acx_display_hardware_details +** +** Displays hw/fw version, radio type etc... +** +** TODO: not pci-specific, move to common.c and use from usb.c too +*/ static void acx_display_hardware_details(wlandevice_t *priv) { @@ -1459,7 +1427,7 @@ acx_show_card_eeprom_id(wlandevice_t *pr memset(&buffer, 0, CARD_EEPROM_ID_SIZE); /* use direct EEPROM access */ for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) { - if (OK != acx_read_eeprom_offset(priv, + if (OK != acxpci_read_eeprom_byte(priv, ACX100_EEPROM_ID_OFFSET + i, &buffer[i])) { @@ -1490,9 +1458,9 @@ acx_show_card_eeprom_id(wlandevice_t *pr /*********************************************************************** */ static void -acx_s_device_chain_add(struct net_device *dev) +acxpci_s_device_chain_add(struct net_device *dev) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); down(&root_acx_dev_sem); priv->prev_nd = root_acx_dev.newest; @@ -1502,7 +1470,7 @@ acx_s_device_chain_add(struct net_device } static void -acx_s_device_chain_remove(struct net_device *dev) +acxpci_s_device_chain_remove(struct net_device *dev) { struct net_device *querydev; struct net_device *olderdev; @@ -1511,10 +1479,10 @@ acx_s_device_chain_remove(struct net_dev down(&root_acx_dev_sem); querydev = root_acx_dev.newest; newerdev = NULL; - while (NULL != querydev) { - olderdev = ((struct wlandevice *) querydev->priv)->prev_nd; + while (querydev) { + olderdev = ((wlandevice_t*)netdev_priv(querydev))->prev_nd; if (0 == strcmp(querydev->name, dev->name)) { - if (NULL == newerdev) { + if (!newerdev) { /* if we were at the beginning of the * list, then it's the list head that * we need to update to point at the @@ -1524,8 +1492,8 @@ acx_s_device_chain_remove(struct net_dev /* it's the device that is newer than us * that we need to update to point at * the device older than us */ - ((struct wlandevice *) newerdev->priv)-> - prev_nd = olderdev; + ((wlandevice_t*)netdev_priv(newerdev))-> + prev_nd = olderdev; } break; } @@ -1545,14 +1513,15 @@ acx_s_device_chain_remove(struct net_dev /*********************************************************************** -** acx_free_desc_queues +** acxpci_free_desc_queues ** ** Releases the queues that have been allocated, the ** others have been initialised to NULL so this ** function can be used if only part of the queues were allocated. */ + static inline void -acx_free_coherent(struct pci_dev *hwdev, size_t size, +free_coherent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53) @@ -1564,38 +1533,36 @@ acx_free_coherent(struct pci_dev *hwdev, } void -acx_free_desc_queues(wlandevice_t *priv) +acxpci_free_desc_queues(wlandevice_t *priv) { #define ACX_FREE_QUEUE(size, ptr, phyaddr) \ if (ptr) { \ - acx_free_coherent(0, size, ptr, phyaddr); \ + free_coherent(0, size, ptr, phyaddr); \ ptr = NULL; \ size = 0; \ } FN_ENTER; - ACX_FREE_QUEUE(priv->TxHostDescQPoolSize, priv->pTxHostDescQPool, priv->TxHostDescQPoolPhyAddr); - ACX_FREE_QUEUE(priv->TxBufferPoolSize, priv->pTxBufferPool, priv->TxBufferPoolPhyAddr); + ACX_FREE_QUEUE(priv->txhostdesc_area_size, priv->txhostdesc_start, priv->txhostdesc_startphy); + ACX_FREE_QUEUE(priv->txbuf_area_size, priv->txbuf_start, priv->txbuf_startphy); - priv->pTxDescQPool = NULL; - priv->tx_pool_count = 0; + priv->txdesc_start = NULL; - ACX_FREE_QUEUE(priv->RxHostDescQPoolSize, priv->pRxHostDescQPool, priv->RxHostDescQPoolPhyAddr); - ACX_FREE_QUEUE(priv->RxBufferPoolSize, priv->pRxBufferPool, priv->RxBufferPoolPhyAddr); + ACX_FREE_QUEUE(priv->rxhostdesc_area_size, priv->rxhostdesc_start, priv->rxhostdesc_startphy); + ACX_FREE_QUEUE(priv->rxbuf_area_size, priv->rxbuf_start, priv->rxbuf_startphy); - priv->pRxDescQPool = NULL; - priv->rx_pool_count = 0; + priv->rxdesc_start = NULL; FN_EXIT0; } -/*---------------------------------------------------------------- -* acx_s_delete_dma_regions -*----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_s_delete_dma_regions +*/ static void -acx_s_delete_dma_regions(wlandevice_t *priv) +acxpci_s_delete_dma_regions(wlandevice_t *priv) { unsigned long flags; @@ -1603,41 +1570,32 @@ acx_s_delete_dma_regions(wlandevice_t *p /* disable radio Tx/Rx. Shouldn't we use the firmware commands * here instead? Or are we that much down the road that it's no * longer possible here? */ - acx_write_reg16(priv, IO_ACX_ENABLE, 0); + write_reg16(priv, IO_ACX_ENABLE, 0); acx_s_msleep(100); acx_lock(priv, flags); - acx_free_desc_queues(priv); + acxpci_free_desc_queues(priv); acx_unlock(priv, flags); FN_EXIT0; } -/*---------------------------------------------------------------- -* acx_e_probe_pci -* -* Probe routine called when a PCI device w/ matching ID is found. -* Here's the sequence: -* - Allocate the PCI resources. -* - Read the PCMCIA attribute memory to make sure we have a WLAN card -* - Reset the MAC -* - Initialize the dev and wlan data -* - Initialize the MAC -* -* Arguments: -* pdev ptr to pci device structure containing info about -* pci configuration. -* id ptr to the device id entry that matched this device. -* -* Returns: -* zero - success -* negative - failed -* -* Call context: -* process thread -----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_e_probe +** +** Probe routine called when a PCI device w/ matching ID is found. +** Here's the sequence: +** - Allocate the PCI resources. +** - Read the PCMCIA attribute memory to make sure we have a WLAN card +** - Reset the MAC +** - Initialize the dev and wlan data +** - Initialize the MAC +** +** pdev - ptr to pci device structure containing info about pci configuration +** id - ptr to the device id entry that matched this device +*/ static const u16 IO_ACX100[] = { @@ -1725,8 +1683,11 @@ IO_ACX111[] = 0x0108, /* IO_ACX_ECPU_CTRL */ }; +static void +dummy_netdev_init(struct net_device *dev) {} + static int __devinit -acx_e_probe_pci(struct pci_dev *pdev, const struct pci_device_id *id) +acxpci_e_probe(struct pci_dev *pdev, const struct pci_device_id *id) { unsigned long mem_region1 = 0; unsigned long mem_region2 = 0; @@ -1765,8 +1726,8 @@ acx_e_probe_pci(struct pci_dev *pdev, co inited++; } if (inited + turn != card) { - FN_EXIT1(result); - return -ENODEV; + result = -ENODEV; + goto done; } } #endif /* SEPARATE_DRIVER_INSTANCES */ @@ -1786,14 +1747,14 @@ acx_e_probe_pci(struct pci_dev *pdev, co chip_type = (u8)id->driver_data; /* acx100 and acx111 have different PCI memory regions */ if (chip_type == CHIPTYPE_ACX100) { - chip_name = name_acx100; + chip_name = "ACX100"; mem_region1 = PCI_ACX100_REGION1; mem_region1_size = PCI_ACX100_REGION1_SIZE; mem_region2 = PCI_ACX100_REGION2; mem_region2_size = PCI_ACX100_REGION2_SIZE; } else if (chip_type == CHIPTYPE_ACX111) { - chip_name = name_acx111; + chip_name = "ACX111"; mem_region1 = PCI_ACX111_REGION1; mem_region1_size = PCI_ACX111_REGION1_SIZE; @@ -1801,7 +1762,6 @@ acx_e_probe_pci(struct pci_dev *pdev, co mem_region2_size = PCI_ACX111_REGION2_SIZE; } else { printk("acx: unknown chip type 0x%04X\n", chip_type); - result = -EIO; goto fail_unknown_chiptype; } @@ -1812,27 +1772,23 @@ acx_e_probe_pci(struct pci_dev *pdev, co if (!request_mem_region(phymem1, pci_resource_len(pdev, mem_region1), "ACX1xx_1")) { printk("acx: cannot reserve PCI memory region 1 (are you sure " "you have CardBus support in kernel?)\n"); - result = -EIO; goto fail_request_mem_region1; } if (!request_mem_region(phymem2, pci_resource_len(pdev, mem_region2), "ACX1xx_2")) { printk("acx: cannot reserve PCI memory region 2\n"); - result = -EIO; goto fail_request_mem_region2; } mem1 = ioremap(phymem1, mem_region1_size); if (NULL == mem1) { printk("acx: ioremap() FAILED\n"); - result = -EIO; goto fail_ioremap1; } mem2 = ioremap(phymem2, mem_region2_size); if (NULL == mem2) { printk("acx: ioremap() #2 FAILED\n"); - result = -EIO; goto fail_ioremap2; } @@ -1847,41 +1803,50 @@ acx_e_probe_pci(struct pci_dev *pdev, co if (0 == pdev->irq) { printk("acx: can't use IRQ 0\n"); - result = -EIO; goto fail_irq; } - acxlog(L_DEBUG, "allocating %d (0x%X) bytes for wlandevice_t\n", - (int) sizeof(wlandevice_t), (int) sizeof(wlandevice_t)); - priv = kmalloc(sizeof(wlandevice_t), GFP_KERNEL); - if (!priv) { - printk("acx: no memory for wlandevice_t\n"); - result = -EIO; - goto fail_alloc_priv; + dev = alloc_netdev(sizeof(wlandevice_t), "wlan%d", dummy_netdev_init); + /* (NB: memsets to 0 entire area) */ + if (!dev) { + printk("acx: no memory for netdevice structure\n"); + goto fail_alloc_netdev; } - memset(priv, 0, sizeof(wlandevice_t)); + ether_setup(dev); + dev->open = &acxpci_e_open; + dev->stop = &acxpci_e_close; + dev->hard_start_xmit = &acx_i_start_xmit; + dev->get_stats = &acx_e_get_stats; + dev->get_wireless_stats = &acx_e_get_wireless_stats; +#if WIRELESS_EXT >= 13 + dev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; +#else + dev->do_ioctl = &acx_e_ioctl_old; +#endif + dev->set_multicast_list = &acxpci_i_set_multicast_list; + dev->tx_timeout = &acxpci_i_tx_timeout; + dev->change_mtu = &acx_e_change_mtu; + dev->watchdog_timeo = 4 * HZ; + dev->irq = pdev->irq; + dev->base_addr = pci_resource_start(pdev, 0); + priv = netdev_priv(dev); spin_lock_init(&priv->lock); /* initial state: unlocked */ /* We do not start with downed sem: we want PARANOID_LOCKING to work */ sema_init(&priv->sem, 1); /* initial state: 1 (upped) */ - /* since nobody can see new netdev yet, we can as well ** just _presume_ that we're under sem (instead of actually taking it): */ /* acx_sem_lock(priv); */ - priv->pdev = pdev; priv->dev_type = DEVTYPE_PCI; priv->chip_type = chip_type; priv->chip_name = chip_name; priv->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111; - priv->membase = phymem1; priv->iobase = mem1; - priv->membase2 = phymem2; priv->iobase2 = mem2; - /* to find crashes due to weird driver access * to unconfigured interface (ifup) */ priv->mgmt_timer.function = (void (*)(unsigned long))0x0000dead; @@ -1890,22 +1855,10 @@ acx_e_probe_pci(struct pci_dev *pdev, co acx_show_card_eeprom_id(priv); #endif /* NONESSENTIAL_FEATURES */ - dev = kmalloc(sizeof(netdevice_t), GFP_KERNEL); - if (unlikely(NULL == dev)) { - printk("acx: no memory for netdevice_t\n"); - result = -EIO; - goto fail_alloc_netdev; - } - - memset(dev, 0, sizeof(netdevice_t)); - ether_setup(dev); - /* now we have our device, so make sure the kernel doesn't try * to send packets even though we're not associated to a network yet */ acx_stop_queue(dev, "after setup"); - dev->priv = priv; - #ifdef SET_MODULE_OWNER SET_MODULE_OWNER(dev); #endif @@ -1915,12 +1868,9 @@ acx_e_probe_pci(struct pci_dev *pdev, co #endif /* register new dev in linked list */ - acx_s_device_chain_add(dev); + acxpci_s_device_chain_add(dev); - acxlog(L_IRQ | L_INIT, "using IRQ %d\n", pdev->irq); - - dev->irq = pdev->irq; - dev->base_addr = pci_resource_start(pdev, 0); + acxlog(L_IRQ|L_INIT, "using IRQ %d\n", pdev->irq); /* need to be able to restore PCI state after a suspend */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) @@ -1931,51 +1881,24 @@ acx_e_probe_pci(struct pci_dev *pdev, co pci_save_state(pdev, priv->pci_state); #endif - /* NB: acx_read_reg() reads may return bogus data before reset_dev(). + /* NB: read_reg() reads may return bogus data before reset_dev(). ** acx100 seems to be more affected than acx111 */ - - if (OK != acx_s_reset_dev(dev)) { - result = -EIO; + if (OK != acxpci_s_reset_dev(dev)) { goto fail_reset; } - if (dev_alloc_name(dev, "wlan%d") < 0) { - result = -EIO; - goto fail_alloc_name; - } - printk("acx: net device %s, driver compiled " - "against wireless extensions %d and Linux %s\n", - dev->name, WIRELESS_EXT, UTS_RELEASE); - - /* now that device init was successful, fill remaining fields... */ - dev->open = &acx_e_open; - dev->stop = &acx_e_close; - dev->hard_start_xmit = &acx_i_start_xmit; - dev->get_stats = &acx_e_get_stats; - dev->get_wireless_stats = &acx_e_get_wireless_stats; -#if WIRELESS_EXT >= 13 - dev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; -#else - dev->do_ioctl = &acx_e_ioctl_old; -#endif - dev->set_multicast_list = &acx_i_set_multicast_list; - dev->tx_timeout = &acx_i_tx_timeout; - dev->watchdog_timeo = 4 * HZ; - /* ok, basic setup is finished, now start initialising the card */ - if (OK != acx_read_eeprom_offset(priv, 0x05, &priv->eeprom_version)) { - result = -EIO; + if (OK != acxpci_read_eeprom_byte(priv, 0x05, &priv->eeprom_version)) { goto fail_read_eeprom_version; } if (OK != acx_s_init_mac(dev)) { printk("acx: init_mac() FAILED\n"); - result = -EIO; goto fail_init_mac; } if (OK != acx_s_set_defaults(priv)) { - printk("%s: acx_set_defaults FAILED\n", dev->name); + printk("acx: set_defaults() FAILED\n"); goto fail_set_defaults; } @@ -1992,30 +1915,28 @@ acx_e_probe_pci(struct pci_dev *pdev, co err = register_netdev(dev); if (OK != err) { printk("acx: register_netdev() FAILED: %d\n", err); - result = -EIO; goto fail_register_netdev; } acx_carrier_off(dev, "on probe"); -#ifdef CONFIG_PROC_FS if (OK != acx_proc_register_entries(dev)) { - result = -EIO; goto fail_proc_register_entries; } -#endif /* after register_netdev() userspace may start working with dev * (in particular, on other CPUs), we only need to up the sem */ /* acx_sem_unlock(priv); */ - printk("acx: PCI module " WLAN_RELEASE " loaded successfully\n"); - result = OK; + printk("acx "WLAN_RELEASE": net device %s, driver compiled " + "against wireless extensions %d and Linux %s\n", + dev->name, WIRELESS_EXT, UTS_RELEASE); #if CMD_DISCOVERY - great_inquisistor(priv); + great_inquisitor(priv); #endif + result = OK; goto done; /* error paths: undo everything in reverse order... */ @@ -2024,7 +1945,7 @@ acx_e_probe_pci(struct pci_dev *pdev, co fail_proc_register_entries: if (priv->dev_state_mask & ACX_STATE_IFACE_UP) - acx_s_down(dev); + acxpci_s_down(dev); unregister_netdev(dev); @@ -2036,21 +1957,17 @@ fail_proc_register_entries: fail_register_netdev: - acx_s_delete_dma_regions(priv); + acxpci_s_delete_dma_regions(priv); pci_set_drvdata(pdev, NULL); fail_set_defaults: fail_init_mac: fail_read_eeprom_version: -fail_alloc_name: fail_reset: - acx_s_device_chain_remove(dev); - kfree(dev); + acxpci_s_device_chain_remove(dev); + free_netdev(dev); fail_alloc_netdev: - - kfree(priv); -fail_alloc_priv: fail_irq: iounmap(mem2); @@ -2079,28 +1996,23 @@ done: } -/*---------------------------------------------------------------- -* acx_e_remove_pci -* -* Deallocate PCI resources for the ACX100 chip. -* -* This should NOT execute any other hardware operations on the card, -* since the card might already be ejected. Instead, that should be done -* in cleanup_module, since the card is most likely still available there. -* -* Arguments: -* pdev ptr to PCI device structure containing info about -* PCI configuration. -* -* Call context: -* process thread -----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_e_remove +** +** Deallocate PCI resources for the acx chip. +** +** This should NOT execute any other hardware operations on the card, +** since the card might already be ejected. Instead, that should be done +** in cleanup_module, since the card is most likely still available there. +** +** pdev - ptr to PCI device structure containing info about pci configuration +*/ static void __devexit -acx_e_remove_pci(struct pci_dev *pdev) +acxpci_e_remove(struct pci_dev *pdev) { struct net_device *dev; wlandevice_t *priv; - unsigned long mem_region1 = 0, mem_region2 = 0; + unsigned long mem_region1, mem_region2; FN_ENTER; @@ -2111,16 +2023,11 @@ acx_e_remove_pci(struct pci_dev *pdev) goto end; } - priv = (struct wlandevice *) dev->priv; - if (!priv) { - acxlog(L_DEBUG, "%s: card is unused. Skipping any release code\n", - __func__); - goto end; - } + priv = netdev_priv(dev); /* unregister the device to not let the kernel * (e.g. ioctls) access a half-deconfigured device - * NB: this will cause acx_e_close() to be called, + * NB: this will cause acxpci_e_close() to be called, * thus we shouldn't call it under sem! */ acxlog(L_INIT, "removing device %s\n", dev->name); unregister_netdev(dev); @@ -2137,19 +2044,17 @@ acx_e_remove_pci(struct pci_dev *pdev) mem_region2 = PCI_ACX111_REGION2; } -#ifdef CONFIG_PROC_FS acx_proc_unregister_entries(dev); -#endif /* find our PCI device in the global acx list and remove it */ - acx_s_device_chain_remove(dev); + acxpci_s_device_chain_remove(dev); if (priv->dev_state_mask & ACX_STATE_IFACE_UP) - acx_s_down(dev); + acxpci_s_down(dev); CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP); - acx_s_delete_dma_regions(priv); + acxpci_s_delete_dma_regions(priv); /* finally, clean up PCI bus state */ if (priv->iobase) iounmap(priv->iobase); @@ -2169,11 +2074,8 @@ acx_e_remove_pci(struct pci_dev *pdev) /* Free netdev (quite late, * since otherwise we might get caught off-guard * by a netdev timeout handler execution - * expecting to see a working dev...) - * But don't use free_netdev() here, - * it's supported by newer kernels only */ - kfree(priv); - kfree(dev); + * expecting to see a working dev...) */ + free_netdev(dev); /* put device into ACPI D3 mode (shutdown) */ pci_set_power_state(pdev, 3); @@ -2184,14 +2086,15 @@ end: /*********************************************************************** +** TODO: PM code needs to be fixed / debugged / tested. */ #ifdef CONFIG_PM static int if_was_up = 0; /* FIXME: HACK, do it correctly sometime instead */ static int -acx_e_suspend(struct pci_dev *pdev, pm_message_t state) +acxpci_e_suspend(struct pci_dev *pdev, pm_message_t state) { struct net_device *dev = pci_get_drvdata(pdev); - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); FN_ENTER; @@ -2200,13 +2103,13 @@ acx_e_suspend(struct pci_dev *pdev, pm_m printk("acx: experimental suspend handler called for %p\n", priv); if (netif_device_present(dev)) { if_was_up = 1; - acx_s_down(dev); + acxpci_s_down(dev); } else if_was_up = 0; netif_device_detach(dev); /* This one cannot sleep */ - acx_s_delete_dma_regions(priv); + acxpci_s_delete_dma_regions(priv); acx_sem_unlock(priv); @@ -2215,7 +2118,7 @@ acx_e_suspend(struct pci_dev *pdev, pm_m } static int -acx_e_resume(struct pci_dev *pdev) +acxpci_e_resume(struct pci_dev *pdev) { struct net_device *dev; wlandevice_t *priv; @@ -2227,7 +2130,7 @@ acx_e_resume(struct pci_dev *pdev) if (!netif_running(dev)) return 0; - priv = dev->priv; + priv = netdev_priv(dev); acx_sem_lock(priv); @@ -2244,7 +2147,7 @@ acx_e_resume(struct pci_dev *pdev) pci_restore_state(pdev, priv->pci_state); #endif acxlog(L_DEBUG, "rsm: PCI state restored\n"); - acx_s_reset_dev(dev); + acxpci_s_reset_dev(dev); acxlog(L_DEBUG, "rsm: device reset done\n"); if (OK != acx_s_init_mac(dev)) { @@ -2254,7 +2157,7 @@ acx_e_resume(struct pci_dev *pdev) acxlog(L_DEBUG, "rsm: init MAC done\n"); if (1 == if_was_up) - acx_s_up(dev); + acxpci_s_up(dev); acxlog(L_DEBUG, "rsm: acx up\n"); /* now even reload all card parameters as they were before suspend, @@ -2273,38 +2176,36 @@ fail: /* we need to return OK here anywa #endif /* CONFIG_PM */ -/*---------------------------------------------------------------- -* acx_s_up -* -* Side effects: -* - Enables on-card interrupt requests -* - calls acx_start -* Call context: -* - process thread -* Comment: -* This function is called by acx_open (when ifconfig sets the -* device as up). -*----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_s_up +** +** This function is called by acxpci_e_open (when ifconfig sets the device as up) +** +** Side effects: +** - Enables on-card interrupt requests +** - calls acx_s_start +*/ + +static void +enable_acx_irq(wlandevice_t *priv) +{ + FN_ENTER; + write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask); + write_reg16(priv, IO_ACX_FEMR, 0x8000); + priv->irqs_active = 1; + FN_EXIT0; +} + static void -acx_s_up(netdevice_t *dev) +acxpci_s_up(netdevice_t *dev) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); unsigned long flags; FN_ENTER; - switch (priv->mode) { - case ACX_MODE_0_ADHOC: - case ACX_MODE_2_STA: - /* actual scan cmd will happen in start() */ - acx_set_status(priv, ACX_STATUS_1_SCANNING); break; - case ACX_MODE_3_AP: - case ACX_MODE_MONITOR: - acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); break; - } - acx_lock(priv, flags); - acx_l_enable_irq(priv); + enable_acx_irq(priv); acx_unlock(priv, flags); /* acx fw < 1.9.3.e has a hardware timer, and older drivers @@ -2314,36 +2215,55 @@ acx_s_up(netdevice_t *dev) priv->mgmt_timer.function = acx_i_timer; priv->mgmt_timer.data = (unsigned long)priv; + /* Need to set ACX_STATE_IFACE_UP first, or else + ** timer won't be started by acx_set_status() */ SET_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP); - acx_s_start(priv); + switch (priv->mode) { + case ACX_MODE_0_ADHOC: + case ACX_MODE_2_STA: + /* actual scan cmd will happen in start() */ + acx_set_status(priv, ACX_STATUS_1_SCANNING); break; + case ACX_MODE_3_AP: + case ACX_MODE_MONITOR: + acx_set_status(priv, ACX_STATUS_4_ASSOCIATED); break; + } - /* acx_start_queue(dev, "on startup"); */ + acx_s_start(priv); FN_EXIT0; } -/*---------------------------------------------------------------- -* acx_s_down -* -* Side effects: -* - disables on-card interrupt request -* Call context: -* process thread -* Comment: -* this disables the netdevice -*----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_s_down +** +** This disables the netdevice +** +** Side effects: +** - disables on-card interrupt request +*/ + +static void +disable_acx_irq(wlandevice_t *priv) +{ + FN_ENTER; + write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask_off); + write_reg16(priv, IO_ACX_FEMR, 0x0); + priv->irqs_active = 0; + FN_EXIT0; +} + static void -acx_s_down(netdevice_t *dev) +acxpci_s_down(netdevice_t *dev) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); unsigned long flags; FN_ENTER; /* Disable IRQs first, so that IRQs cannot race with us */ acx_lock(priv, flags); - acx_l_disable_irq(priv); + disable_acx_irq(priv); acx_unlock(priv, flags); /* we really don't want to have an asynchronous tasklet disturb us @@ -2385,26 +2305,21 @@ acx_s_down(netdevice_t *dev) } -/*---------------------------------------------------------------- -* acx_e_open -* -* WLAN device open method. Called from p80211netdev when kernel -* device open (start) method is called in response to the -* SIOCSIFFLAGS ioctl changing the flags bit IFF_UP -* from clear to set. -* -* Returns: -* 0 success -* >0 f/w reported error -* <0 driver reported error -* -* Call context: -* process thread -----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_e_open +** +** Called as a result of SIOCSIFFLAGS ioctl changing the flags bit IFF_UP +** from clear to set. In other words: ifconfig up. +** +** Returns: +** 0 success +** >0 f/w reported error +** <0 driver reported error +*/ static int -acx_e_open(netdevice_t *dev) +acxpci_e_open(netdevice_t *dev) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); int result = OK; FN_ENTER; @@ -2417,15 +2332,15 @@ acx_e_open(netdevice_t *dev) acx_init_task_scheduler(priv); /* request shared IRQ handler */ - if (request_irq(dev->irq, acx_i_interrupt, SA_SHIRQ, dev->name, dev)) { + if (request_irq(dev->irq, acxpci_i_interrupt, SA_SHIRQ, dev->name, dev)) { printk("%s: request_irq FAILED\n", dev->name); result = -EAGAIN; goto done; } - acxlog(L_DEBUG | L_IRQ, "request_irq %d successful\n", dev->irq); + acxlog(L_DEBUG|L_IRQ, "request_irq %d successful\n", dev->irq); /* ifup device */ - acx_s_up(dev); + acxpci_s_up(dev); /* We don't currently have to do anything else. * The setup of the MAC should be subsequently completed via @@ -2442,27 +2357,21 @@ done: } -/*---------------------------------------------------------------- -* acx_e_close -* -* WLAN device close method. Called from network core when kernel -* device close method is called in response to the -* SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP -* from set to clear. -* (i.e. called for "ifconfig DEV down") -* -* Returns: -* 0 success -* >0 f/w reported error -* <0 driver reported error -* -* Call context: -* process thread -----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_e_close +** +** Called as a result of SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP +** from set to clear. I.e. called by "ifconfig DEV down" +** +** Returns: +** 0 success +** >0 f/w reported error +** <0 driver reported error +*/ static int -acx_e_close(netdevice_t *dev) +acxpci_e_close(netdevice_t *dev) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); FN_ENTER; @@ -2471,12 +2380,12 @@ acx_e_close(netdevice_t *dev) /* ifdown device */ CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP); if (netif_device_present(dev)) { - acx_s_down(dev); + acxpci_s_down(dev); } /* disable all IRQs, release shared IRQ handler */ - acx_write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff); - acx_write_reg16(priv, IO_ACX_FEMR, 0x0); + write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff); + write_reg16(priv, IO_ACX_FEMR, 0x0); free_irq(dev->irq, dev); /* We currently don't have to do anything else. @@ -2495,26 +2404,24 @@ acx_e_close(netdevice_t *dev) } -/*---------------------------------------------------------------- -* acx_i_tx_timeout -* -* Called from network core. Must not sleep! -*----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_i_tx_timeout +** +** Called from network core. Must not sleep! +*/ static void -acx_i_tx_timeout(netdevice_t *dev) +acxpci_i_tx_timeout(netdevice_t *dev) { - wlandevice_t *priv; + wlandevice_t *priv = netdev_priv(dev); unsigned long flags; unsigned int tx_num_cleaned; FN_ENTER; - priv = (wlandevice_t *)dev->priv; - acx_lock(priv, flags); /* clean processed tx descs, they may have been completely full */ - tx_num_cleaned = acx_l_clean_tx_desc(priv); + tx_num_cleaned = acxpci_l_clean_txdesc(priv); /* nothing cleaned, yet (almost) no free buffers available? * --> clean all tx descs, no matter which status!! @@ -2524,18 +2431,18 @@ acx_i_tx_timeout(netdevice_t *dev) * * TODO: it's best to simply reset & reinit hw from scratch... */ - if ((priv->TxQueueFree <= 2) && (tx_num_cleaned == 0)) { + if ((priv->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) { printk("%s: FAILED to free any of the many full tx buffers. " "Switching to emergency freeing. " "Please report!\n", dev->name); - acx_l_clean_tx_desc_emergency(priv); + acxpci_l_clean_txdesc_emergency(priv); } if (acx_queue_stopped(dev) && (ACX_STATUS_4_ASSOCIATED == priv->status)) acx_wake_queue(dev, "after tx timeout"); /* stall may have happened due to radio drift, so recalib radio */ - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); + acx_schedule_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); /* do unimportant work last */ printk("%s: tx timeout!\n", dev->name); @@ -2547,51 +2454,21 @@ acx_i_tx_timeout(netdevice_t *dev) } -/*---------------------------------------------------------------- -* acx_e_get_stats -*----------------------------------------------------------------*/ -static struct net_device_stats* -acx_e_get_stats(netdevice_t *dev) -{ - wlandevice_t *priv = acx_netdev_priv(dev); -#ifdef ANNOYING_GETS_CALLED_TOO_OFTEN - FN_ENTER; - FN_EXIT1((int)&priv->stats); -#endif - return &priv->stats; -} - - -/*---------------------------------------------------------------- -* acx_e_get_wireless_stats -*----------------------------------------------------------------*/ -static struct iw_statistics* -acx_e_get_wireless_stats(netdevice_t *dev) -{ - wlandevice_t *priv = acx_netdev_priv(dev); - FN_ENTER; - FN_EXIT0; - return &priv->wstats; -} - - -/*---------------------------------------------------------------- -* acx_i_set_multicast_list -*----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_i_set_multicast_list +** FIXME: most likely needs refinement +*/ static void -acx_i_set_multicast_list(netdevice_t *dev) +acxpci_i_set_multicast_list(netdevice_t *dev) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); unsigned long flags; FN_ENTER; acx_lock(priv, flags); - printk("FIXME: most likely needs refinement, " - "first implementation version only...\n"); - - /* ACX firmwares don't have allmulti capability, + /* firmwares don't have allmulti capability, * so just use promiscuous mode instead in this case. */ if (dev->flags & (IFF_PROMISC|IFF_ALLMULTI)) { SET_BIT(priv->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); @@ -2599,8 +2476,7 @@ acx_i_set_multicast_list(netdevice_t *de SET_BIT(priv->set_mask, SET_RXCONFIG); /* let kernel know in case *we* needed to set promiscuous */ dev->flags |= (IFF_PROMISC|IFF_ALLMULTI); - } - else { + } else { CLEAR_BIT(priv->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); SET_BIT(priv->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); SET_BIT(priv->set_mask, SET_RXCONFIG); @@ -2608,58 +2484,106 @@ acx_i_set_multicast_list(netdevice_t *de } /* cannot update card settings directly here, atomic context */ - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG); + acx_schedule_task(priv, ACX_AFTER_IRQ_UPDATE_CARD_CFG); acx_unlock(priv, flags); FN_EXIT0; } + +/*************************************************************** +** acxpci_l_process_rxdesc +** +** Called directly and only from the IRQ handler +*/ + +#if !ACX_DEBUG +static inline void log_rxbuffer(const wlandevice_t *priv) {} +#else static void -acx_l_update_link_quality_led(wlandevice_t *priv) +log_rxbuffer(const wlandevice_t *priv) { - int qual; + const struct rxhostdesc *rxhostdesc; + int i; - qual = acx_signal_determine_quality(priv->wstats.qual.level, priv->wstats.qual.noise); - if (qual > priv->brange_max_quality) - qual = priv->brange_max_quality; + /* no FN_ENTER here, we don't want that */ - if (time_after(jiffies, priv->brange_time_last_state_change + - (HZ/2 - HZ/2 * (unsigned long) qual/priv->brange_max_quality ) )) { - acx_l_power_led(priv, (priv->brange_last_state == 0)); - priv->brange_last_state ^= 1; /* toggle */ - priv->brange_time_last_state_change = jiffies; + rxhostdesc = priv->rxhostdesc_start; + if (!rxhostdesc) return; + for (i = 0; i < RX_CNT; i++) { + if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) + && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) + printk("rx: buf %d full\n", i); + rxhostdesc++; } } +#endif - -/*---------------------------------------------------------------- -* acx_l_enable_irq -*----------------------------------------------------------------*/ static void -acx_l_enable_irq(wlandevice_t *priv) +acxpci_l_process_rxdesc(wlandevice_t *priv) { + rxhostdesc_t *hostdesc; + int count, tail; + FN_ENTER; - acx_write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask); - acx_write_reg16(priv, IO_ACX_FEMR, 0x8000); - priv->irqs_active = 1; - FN_EXIT0; -} + if (unlikely(acx_debug & L_BUFR)) + log_rxbuffer(priv); -/*---------------------------------------------------------------- -* acx_l_disable_irq -*----------------------------------------------------------------*/ -static void -acx_l_disable_irq(wlandevice_t *priv) -{ - FN_ENTER; - acx_write_reg16(priv, IO_ACX_IRQ_MASK, priv->irq_mask_off); - acx_write_reg16(priv, IO_ACX_FEMR, 0x0); - priv->irqs_active = 0; + /* First, have a loop to determine the first descriptor that's + * full, just in case there's a mismatch between our current + * rx_tail and the full descriptor we're supposed to handle. */ + count = RX_CNT; + tail = priv->rx_tail; + while (1) { + hostdesc = &priv->rxhostdesc_start[tail]; + /* advance tail regardless of outcome of the below test */ + tail = (tail + 1) % RX_CNT; + + if ((hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) + && (hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) + break; /* found it! */ + + if (--count) /* hmm, no luck: all descs empty, bail out */ + goto end; + } + + /* now process descriptors, starting with the first we figured out */ + while (1) { + acxlog(L_BUFR, "rx: tail=%u Ctl_16=%04X Status=%08X\n", + tail, hostdesc->Ctl_16, hostdesc->Status); + + acx_l_process_rxbuf(priv, hostdesc->data); + + hostdesc->Status = 0; + /* flush all writes before adapter sees CTL_HOSTOWN change */ + wmb(); + /* Host no longer owns this, needs to be LAST */ + CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); + + /* ok, descriptor is handled, now check the next descriptor */ + hostdesc = &priv->rxhostdesc_start[tail]; + + /* if next descriptor is empty, then bail out */ + if (!(hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) + || !(hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) + break; + + tail = (tail + 1) % RX_CNT; + } +end: + priv->rx_tail = tail; FN_EXIT0; } + +/*********************************************************************** +** acxpci_i_interrupt +** +** IRQ handler (atomic context, must not sleep, blah, blah) +*/ + /* scan is complete. all frames now on the receive queue are valid */ #define INFO_SCAN_COMPLETE 0x0001 #define INFO_WEP_KEY_NOT_FOUND 0x0002 @@ -2671,8 +2595,58 @@ acx_l_disable_irq(wlandevice_t *priv) /* encryption/decryption process on a packet failed */ #define INFO_IV_ICV_FAILURE 0x0005 +/* Info mailbox format: +2 bytes: type +2 bytes: status +more bytes may follow + rumors say about status: + 0x0000 info available (set by hw) + 0x0001 information received (must be set by host) + 0x1000 info available, mailbox overflowed (messages lost) (set by hw) + but in practice we've seen: + 0x9000 when we did not set status to 0x0001 on prev message + 0x1001 when we did set it + 0x0000 was never seen + conclusion: this is really a bitfield: + 0x1000 is 'info available' bit + 'mailbox overflowed' bit is 0x8000, not 0x1000 + value of 0x0000 probably means that there are no messages at all + P.S. I dunno how in hell hw is supposed to notice that messages are lost - + it does NOT clear bit 0x0001, and this bit will probably stay forever set + after we set it once. Let's hope this will be fixed in firmware someday +*/ +static void +read_info_status(wlandevice_t *priv) +{ + u32 value; + + write_reg32(priv, IO_ACX_SLV_END_CTL, 0x0); + write_reg32(priv, IO_ACX_SLV_MEM_CTL, 0x1); + + write_reg32(priv, IO_ACX_SLV_MEM_ADDR, + read_reg32(priv, IO_ACX_INFO_MAILBOX_OFFS)); + + /* make sure we only read the data once all cfg registers are written: */ + write_flush(priv); + value = read_reg32(priv, IO_ACX_SLV_MEM_DATA); + + priv->info_type = (u16)value; + priv->info_status = (value >> 16); + + /* inform hw that we have read this info message */ + write_reg32(priv, IO_ACX_SLV_MEM_DATA, priv->info_type | 0x00010000); + write_flush(priv); + /* now bother hw to notice it: */ + write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_INFOACK); + write_flush(priv); + + acxlog(L_CTL, "info_type 0x%04X, info_status 0x%04X\n", + priv->info_type, priv->info_status); +} + + static void -acx_l_handle_info_irq(wlandevice_t *priv) +handle_info_irq(wlandevice_t *priv) { #if ACX_DEBUG static const char * const info_type_msg[] = { @@ -2695,7 +2669,7 @@ acx_l_handle_info_irq(wlandevice_t *priv "TKIP IV value exceeds thresh" }; #endif - acx_read_info_status(priv); + read_info_status(priv); acxlog(L_IRQ, "got Info IRQ: status 0x%04X type 0x%04X: %s\n", priv->info_status, priv->info_type, info_type_msg[(priv->info_type >= VEC_SIZE(info_type_msg)) ? @@ -2704,13 +2678,8 @@ acx_l_handle_info_irq(wlandevice_t *priv } -/*---------------------------------------------------------------- -* acx_i_interrupt -* -* IRQ handler (atomic context, must not sleep, blah, blah) -*----------------------------------------------------------------*/ static void -acx_log_unusual_irq(u16 irqtype) { +log_unusual_irq(u16 irqtype) { /* if (!printk_ratelimit()) return; @@ -2758,8 +2727,29 @@ acx_log_unusual_irq(u16 irqtype) { printk(" IRQ(s)\n"); } + +static void +update_link_quality_led(wlandevice_t *priv) +{ + int qual; + + qual = acx_signal_determine_quality(priv->wstats.qual.level, priv->wstats.qual.noise); + if (qual > priv->brange_max_quality) + qual = priv->brange_max_quality; + + if (time_after(jiffies, priv->brange_time_last_state_change + + (HZ/2 - HZ/2 * (unsigned long)qual / priv->brange_max_quality ) )) { + acxpci_l_power_led(priv, (priv->brange_last_state == 0)); + priv->brange_last_state ^= 1; /* toggle */ + priv->brange_time_last_state_change = jiffies; + } +} + + +#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */ + static irqreturn_t -acx_i_interrupt(int irq, void *dev_id, struct pt_regs *regs) +acxpci_i_interrupt(int irq, void *dev_id, struct pt_regs *regs) { wlandevice_t *priv; unsigned long flags; @@ -2772,7 +2762,7 @@ acx_i_interrupt(int irq, void *dev_id, s * I am paranoid */ acx_lock(priv, flags); - unmasked = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR); + unmasked = read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR); if (unlikely(0xffff == unmasked)) { /* 0xffff value hints at missing hardware, * so don't do anything. @@ -2809,7 +2799,7 @@ if (jiffies != priv->irq_last_jiffies) { while (--irqcount) { #endif /* ACK all IRQs asap */ - acx_write_reg16(priv, IO_ACX_IRQ_ACK, 0xffff); + write_reg16(priv, IO_ACX_IRQ_ACK, 0xffff); acxlog(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n", unmasked, priv->irq_mask, irqtype); @@ -2817,7 +2807,7 @@ while (--irqcount) { /* Handle most important IRQ types first */ if (irqtype & HOST_INT_RX_COMPLETE) { acxlog(L_IRQ, "got Rx_Complete IRQ\n"); - acx_l_process_rx_desc(priv); + acxpci_l_process_rxdesc(priv); } if (irqtype & HOST_INT_TX_COMPLETE) { acxlog(L_IRQ, "got Tx_Complete IRQ\n"); @@ -2825,29 +2815,15 @@ while (--irqcount) { * unless we're going towards full, in which case * we do it immediately, too (otherwise we might lockup * with a full Tx buffer if we go into - * acx_l_clean_tx_desc() at a time when we won't wakeup + * acxpci_l_clean_txdesc() at a time when we won't wakeup * the net queue in there for some reason...) */ - if ((++priv->tx_cnt_done % (priv->TxQueueCnt >> 2) == 0) || - (priv->TxQueueFree < (priv->TxQueueCnt >> 1))) - { + if (priv->tx_free <= TX_START_CLEAN) { #if TX_CLEANUP_IN_SOFTIRQ - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_TX_CLEANUP); + acx_schedule_task(priv, ACX_AFTER_IRQ_TX_CLEANUP); #else - acx_l_clean_tx_desc(priv); + acxpci_l_clean_txdesc(priv); #endif - /* no need to set tx_cnt_done back to 0 here, since - * an overflow cannot cause counter misalignment on - * check above */ - } -#if 0 -/* this shouldn't happen here generally, since we'd also enable user packet - * xmit for management packets, which we really DON'T want */ -/* BS: disabling this caused my card to stop working after a few - * seconds when floodpinging. This should be reinvestigated! */ - if (acx_queue_stopped(dev_id)) { - acx_wake_queue(dev_id, "after Tx complete"); } -#endif } /* Less frequent ones */ @@ -2862,12 +2838,12 @@ while (--irqcount) { SET_BIT(priv->irq_status, HOST_INT_CMD_COMPLETE); } if (irqtype & HOST_INT_INFO) { - acx_l_handle_info_irq(priv); + handle_info_irq(priv); } if (irqtype & HOST_INT_SCAN_COMPLETE) { acxlog(L_IRQ, "got Scan_Complete IRQ\n"); /* need to do that in process context */ - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_COMPLETE_SCAN); + acx_schedule_task(priv, ACX_AFTER_IRQ_COMPLETE_SCAN); /* remember that fw is not scanning anymore */ SET_BIT(priv->irq_status, HOST_INT_SCAN_COMPLETE); } @@ -2893,11 +2869,11 @@ while (--irqcount) { | HOST_INT_FCS_THRESHOLD | HOST_INT_UNKNOWN )) { - acx_log_unusual_irq(irqtype); + log_unusual_irq(irqtype); } #if IRQ_ITERATE - unmasked = acx_read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR); + unmasked = read_reg16(priv, IO_ACX_IRQ_STATUS_CLEAR); irqtype = unmasked & ~priv->irq_mask; /* Bail out if no new IRQ bits or if all are masked out */ if (!irqtype) @@ -2906,7 +2882,7 @@ while (--irqcount) { if (unlikely(++priv->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) { printk(KERN_ERR "acx: too many interrupts per jiffy!\n"); /* Looks like card floods us with IRQs! Try to stop that */ - acx_write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff); + write_reg16(priv, IO_ACX_IRQ_MASK, 0xffff); /* This will short-circuit all future attempts to handle IRQ. * We cant do much more... */ priv->irq_mask = 0; @@ -2916,10 +2892,10 @@ while (--irqcount) { #endif /* Routine to perform blink with range */ if (unlikely(priv->led_power == 2)) - acx_l_update_link_quality_led(priv); + update_link_quality_led(priv); /* handled: */ - /* acx_write_flush(priv); - not needed, last op was read anyway */ + /* write_flush(priv); - not needed, last op was read anyway */ acx_unlock(priv, flags); FN_EXIT0; return IRQ_HANDLED; @@ -2930,11 +2906,11 @@ none: } -/*---------------------------------------------------------------- -* acx_l_power_led -*----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_l_power_led +*/ void -acx_l_power_led(wlandevice_t *priv, int enable) +acxpci_l_power_led(wlandevice_t *priv, int enable) { u16 gpio_pled = IS_ACX111(priv) ? 0x0040 : 0x0800; @@ -2946,11 +2922,11 @@ acx_l_power_led(wlandevice_t *priv, int acxlog(L_IOCTL, "Please report in case toggling the power " "LED doesn't work for your card!\n"); if (enable) - acx_write_reg16(priv, IO_ACX_GPIO_OUT, - acx_read_reg16(priv, IO_ACX_GPIO_OUT) & ~gpio_pled); + write_reg16(priv, IO_ACX_GPIO_OUT, + read_reg16(priv, IO_ACX_GPIO_OUT) & ~gpio_pled); else - acx_write_reg16(priv, IO_ACX_GPIO_OUT, - acx_read_reg16(priv, IO_ACX_GPIO_OUT) | gpio_pled); + write_reg16(priv, IO_ACX_GPIO_OUT, + read_reg16(priv, IO_ACX_GPIO_OUT) | gpio_pled); } @@ -2967,12 +2943,12 @@ acx111pci_ioctl_info( struct iw_param *vwrq, char *extra) { -#if ACX_DEBUG - wlandevice_t *priv = acx_netdev_priv(dev); - const struct rxdesc *pRxDesc; - const struct rxhostdesc *rx_host_desc; - struct txdesc *tx_desc; - const struct txhostdesc *tx_host_desc; +#if ACX_DEBUG > 1 + wlandevice_t *priv = netdev_priv(dev); + rxdesc_t *rxdesc; + txdesc_t *txdesc; + rxhostdesc_t *rxhostdesc; + txhostdesc_t *txhostdesc; struct acx111_ie_memoryconfig memconf; struct acx111_ie_queueconfig queueconf; unsigned long flags; @@ -2988,7 +2964,7 @@ acx111pci_ioctl_info( acx_sem_lock(priv); - if (CHIPTYPE_ACX111 != priv->chip_type) { + if (!IS_ACX111(priv)) { printk("acx111-specific function called " "with non-acx111 chip, aborting\n"); goto end_ok; @@ -3022,7 +2998,7 @@ acx111pci_ioctl_info( /* force occurrence of a beacon interrupt */ /* TODO: comment why is this necessary */ - acx_write_reg16(priv, IO_ACX_HINT_TRIG, HOST_INT_BEACON); + write_reg16(priv, IO_ACX_HINT_TRIG, HOST_INT_BEACON); /* dump Acx111 Mem Configuration */ printk("dump mem config:\n" @@ -3045,7 +3021,7 @@ acx111pci_ioctl_info( memconf.options, memconf.fragmentation, memconf.rx_queue1_count_descs, - memconf.rx_queue1_host_rx_start, + acx2cpu(memconf.rx_queue1_host_rx_start), memconf.tx_queue1_count_descs, memconf.tx_queue1_attributes); @@ -3124,10 +3100,10 @@ acx111pci_ioctl_info( acx_lock(priv, flags); /* dump acx111 internal rx descriptor ring buffer */ - pRxDesc = priv->pRxDescQPool; + rxdesc = priv->rxdesc_start; /* loop over complete receive pool */ - for (i = 0; i < priv->rx_pool_count; i++) { + if (rxdesc) for (i = 0; i < RX_CNT; i++) { printk("\ndump internal rxdesc %d:\n" "mem pos %p\n" "next 0x%X\n" @@ -3137,22 +3113,22 @@ acx111pci_ioctl_info( "RxStatus (dynamic) 0x%X\n" "Mod/Pre (dynamic) 0x%X\n", i, - pRxDesc, - acx2cpu(pRxDesc->pNextDesc), - acx2cpu(pRxDesc->ACXMemPtr), - pRxDesc->Ctl_8, - pRxDesc->rate, - pRxDesc->error, - pRxDesc->SNR); - pRxDesc++; + rxdesc, + acx2cpu(rxdesc->pNextDesc), + acx2cpu(rxdesc->ACXMemPtr), + rxdesc->Ctl_8, + rxdesc->rate, + rxdesc->error, + rxdesc->SNR); + rxdesc++; } /* dump host rx descriptor ring buffer */ - rx_host_desc = priv->pRxHostDescQPool; + rxhostdesc = priv->rxhostdesc_start; /* loop over complete receive pool */ - for (i = 0; i < priv->rx_pool_count; i++) { + if (rxhostdesc) for (i = 0; i < RX_CNT; i++) { printk("\ndump host rxdesc %d:\n" "mem pos %p\n" "buffer mem pos 0x%X\n" @@ -3162,21 +3138,21 @@ acx111pci_ioctl_info( "next 0x%X\n" "Status 0x%X\n", i, - rx_host_desc, - acx2cpu(rx_host_desc->data_phy), - rx_host_desc->data_offset, - le16_to_cpu(rx_host_desc->Ctl_16), - le16_to_cpu(rx_host_desc->length), - acx2cpu(rx_host_desc->desc_phy_next), - rx_host_desc->Status); - rx_host_desc++; + rxhostdesc, + acx2cpu(rxhostdesc->data_phy), + rxhostdesc->data_offset, + le16_to_cpu(rxhostdesc->Ctl_16), + le16_to_cpu(rxhostdesc->length), + acx2cpu(rxhostdesc->desc_phy_next), + rxhostdesc->Status); + rxhostdesc++; } /* dump acx111 internal tx descriptor ring buffer */ - tx_desc = priv->pTxDescQPool; + txdesc = priv->txdesc_start; /* loop over complete transmit pool */ - for (i = 0; i < priv->tx_pool_count; i++) { + if (txdesc) for (i = 0; i < TX_CNT; i++) { printk("\ndump internal txdesc %d:\n" "size 0x%X\n" "mem pos %p\n" @@ -3190,23 +3166,23 @@ acx111pci_ioctl_info( "Rate (dynamic) 0x%X\n", i, (int) sizeof(struct txdesc), - tx_desc, - acx2cpu(tx_desc->pNextDesc), - acx2cpu(tx_desc->AcxMemPtr), - acx2cpu(tx_desc->HostMemPtr), - le16_to_cpu(tx_desc->total_length), - tx_desc->Ctl_8, - tx_desc->Ctl2_8, tx_desc->error, - tx_desc->u.r1.rate); - tx_desc = GET_NEXT_TX_DESC_PTR(priv, tx_desc); + txdesc, + acx2cpu(txdesc->pNextDesc), + acx2cpu(txdesc->AcxMemPtr), + acx2cpu(txdesc->HostMemPtr), + le16_to_cpu(txdesc->total_length), + txdesc->Ctl_8, + txdesc->Ctl2_8, txdesc->error, + txdesc->u.r1.rate); + txdesc = move_txdesc(priv, txdesc, 1); } /* dump host tx descriptor ring buffer */ - tx_host_desc = priv->pTxHostDescQPool; + txhostdesc = priv->txhostdesc_start; /* loop over complete host send pool */ - for (i = 0; i < priv->tx_pool_count * 2; i++) { + if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) { printk("\ndump host txdesc %d:\n" "mem pos %p\n" "buffer mem pos 0x%X\n" @@ -3216,17 +3192,17 @@ acx111pci_ioctl_info( "next 0x%X\n" "Status 0x%X\n", i, - tx_host_desc, - acx2cpu(tx_host_desc->data_phy), - tx_host_desc->data_offset, - le16_to_cpu(tx_host_desc->Ctl_16), - le16_to_cpu(tx_host_desc->length), - acx2cpu(tx_host_desc->desc_phy_next), - le32_to_cpu(tx_host_desc->Status)); - tx_host_desc++; + txhostdesc, + acx2cpu(txhostdesc->data_phy), + txhostdesc->data_offset, + le16_to_cpu(txhostdesc->Ctl_16), + le16_to_cpu(txhostdesc->length), + acx2cpu(txhostdesc->desc_phy_next), + le32_to_cpu(txhostdesc->Status)); + txhostdesc++; } - /* acx_write_reg16(priv, 0xb4, 0x4); */ + /* write_reg16(priv, 0xb4, 0x4); */ acx_unlock(priv, flags); end_ok: @@ -3246,11 +3222,11 @@ acx100pci_ioctl_set_phy_amp_bias( struct iw_param *vwrq, char *extra) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); unsigned long flags; u16 gpio_old; - if (priv->chip_type != CHIPTYPE_ACX100) { + if (!IS_ACX100(priv)) { /* WARNING!!! * Removing this check *might* damage * hardware, since we're tweaking GPIOs here after all!!! @@ -3271,8 +3247,8 @@ acx100pci_ioctl_set_phy_amp_bias( /* Need to lock accesses to [IO_ACX_GPIO_OUT]: * IRQ handler uses it to update LED */ acx_lock(priv, flags); - gpio_old = acx_read_reg16(priv, IO_ACX_GPIO_OUT); - acx_write_reg16(priv, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8)); + gpio_old = read_reg16(priv, IO_ACX_GPIO_OUT); + write_reg16(priv, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8)); acx_unlock(priv, flags); acxlog(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old); @@ -3293,105 +3269,94 @@ acx100pci_ioctl_set_phy_amp_bias( tx_t* acxpci_l_alloc_tx(wlandevice_t* priv) { - struct txdesc *tx_desc; + struct txdesc *txdesc; + int head; u8 ctl8; FN_ENTER; - tx_desc = GET_TX_DESC_PTR(priv, priv->tx_head); - /* why?! rmb(); */ - ctl8 = tx_desc->Ctl_8; - if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_DONE))) { + if(!priv->tx_free) { + printk("acx: BUG: no free txdesc left\n"); + txdesc = NULL; + goto end; + } + + head = priv->tx_head; + txdesc = get_txdesc(priv, head); + ctl8 = txdesc->Ctl_8; + + /* 2005-10-11: there were several bug reports on this happening + ** but now cause seems to be understood & fixed */ + if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_ACXDONE_HOSTOWN))) { /* whoops, descr at current index is not free, so probably * ring buffer already full */ - /* FIXME: this causes a deadlock situation (endless - * loop) in case the current descriptor remains busy, - * so handle it a bit better in the future!! */ - printk("acx: BUG: tx_head->Ctl8=0x%02X, (0x%02X & " - "0x"DESC_CTL_DONE_STR") != 0x"DESC_CTL_HOSTOWN_STR - ": failed to find free tx descr\n", - ctl8, ctl8); - tx_desc = NULL; + printk("acx: BUG: tx_head:%d Ctl8:0x%02X - failed to find " + "free txdesc\n", head, ctl8); + txdesc = NULL; goto end; } - priv->TxQueueFree--; - acxlog(L_BUFT, "tx: got desc %u, %u remain\n", priv->tx_head, priv->TxQueueFree); + /* Needed in case txdesc won't be eventually submitted for tx */ + txdesc->Ctl_8 = DESC_CTL_ACXDONE_HOSTOWN; -/* - * This comment is probably not entirely correct, needs further discussion - * (restored commented-out code below to fix Tx ring buffer overflow, - * since it's much better to have a slightly less efficiently used ring - * buffer rather than one which easily overflows): - * - * This doesn't do anything other than limit our maximum number of - * buffers used at a single time (we might as well just declare - * MINFREE_TX less descriptors when we open up.) We should just let it - * slide here, and back off MINFREE_TX in acx_l_clean_tx_desc, when given the - * opportunity to let the queue start back up. - */ - if (priv->TxQueueFree < MINFREE_TX) { + priv->tx_free--; + acxlog(L_BUFT, "tx: got desc %u, %u remain\n", + head, priv->tx_free); + + /* Keep a few free descs between head and tail of tx ring. + ** It is not absolutely needed, just feels safer */ + if (priv->tx_free < TX_STOP_QUEUE) { acxlog(L_BUF, "stop queue (%u tx desc left)\n", - priv->TxQueueFree); + priv->tx_free); acx_stop_queue(priv->netdev, NULL); } /* returning current descriptor, so advance to next free one */ - priv->tx_head = (priv->tx_head + 1) % priv->tx_pool_count; + priv->tx_head = (head + 1) % TX_CNT; end: FN_EXIT0; - return (tx_t*)tx_desc; + return (tx_t*)txdesc; } -/*************************************************************** +/*********************************************************************** */ void* -acxpci_l_get_txbuf(tx_t* tx_opaque) +acxpci_l_get_txbuf(wlandevice_t *priv, tx_t* tx_opaque) { - txhostdesc_t *hostdesc = ((txdesc_t*)tx_opaque)->fixed_size.s.host_desc; - /* FIXME: happens on card eject; better method? */ - if (unlikely((long)-1 == (long)hostdesc)) - return NULL; - -//amd64 crashes here: -// CORRUPTION DETECTED! hostdesc=00ff81002e2d5000 -// ^^ ?? must be ff - - return hostdesc->data; + return get_txhostdesc(priv, (txdesc_t*)tx_opaque)->data; } -/*************************************************************** +/*********************************************************************** ** acxpci_l_tx_data ** ** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). ** Can be called from acx_i_start_xmit (data frames from net core). */ void -acxpci_l_tx_data(wlandevice_t *priv, tx_t* tx_opaque, int hostdesc_len) +acxpci_l_tx_data(wlandevice_t *priv, tx_t* tx_opaque, int len) { - struct txdesc *tx_desc = (struct txdesc*)tx_opaque; - struct txhostdesc *hostdesc; + txdesc_t *txdesc = (txdesc_t*)tx_opaque; + txhostdesc_t *hostdesc1, *hostdesc2; client_t *clt; u8 Ctl_8, Ctl2_8; FN_ENTER; - hostdesc = tx_desc->fixed_size.s.host_desc; - /* FIXME: happens on card eject; better method? */ - if (unlikely((long)-1 == (long)hostdesc)) + /* fw doesn't tx such packets anyhow */ + if (len < WLAN_HDR_A3_LEN) goto end; - hostdesc->data_offset = 0; - tx_desc->total_length = hostdesc->length = cpu_to_le16(hostdesc_len); + hostdesc1 = get_txhostdesc(priv, txdesc); + hostdesc2 = hostdesc1 + 1; /* modify flag status in separate variable to be able to write it back * in one big swoop later (also in order to have less device memory * accesses) */ - Ctl_8 = tx_desc->Ctl_8; - Ctl2_8 = tx_desc->Ctl2_8; + Ctl_8 = txdesc->Ctl_8; + Ctl2_8 = txdesc->Ctl2_8; /* DON'T simply set Ctl field to 0 here globally, * it needs to maintain a consistent flag status (those are state flags!!), @@ -3399,10 +3364,9 @@ acxpci_l_tx_data(wlandevice_t *priv, tx_ * flags at the exact moment this is needed... * FIXME: what about Ctl2? Equally problematic? */ - /* let chip do RTS/CTS handshaking before sending * in case packet size exceeds threshold */ - if (le16_to_cpu(tx_desc->total_length) > priv->rts_threshold) + if (len > priv->rts_threshold) SET_BIT(Ctl2_8, DESC_CTL2_RTS); else CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS); @@ -3413,14 +3377,36 @@ acxpci_l_tx_data(wlandevice_t *priv, tx_ else CLEAR_BIT(Ctl2_8, DESC_CTL2_WEP); #endif + switch (priv->mode) { case ACX_MODE_0_ADHOC: case ACX_MODE_3_AP: - clt = acx_l_sta_list_get(priv, ((wlan_hdr_t*)hostdesc->data)->a1); + clt = acx_l_sta_list_get(priv, ((wlan_hdr_t*)hostdesc1->data)->a1); break; case ACX_MODE_2_STA: clt = priv->ap_client; break; +#if 0 +/* testing was done on acx111: */ + case ACX_MODE_MONITOR: + SET_BIT(Ctl2_8, 0 +/* sends CTS to self before packet */ + + DESC_CTL2_SEQ /* don't increase sequence field */ +/* not working (looks like good fcs is still added) */ + + DESC_CTL2_FCS /* don't add the FCS */ +/* not tested */ + + DESC_CTL2_MORE_FRAG +/* not tested */ + + DESC_CTL2_RETRY /* don't increase retry field */ +/* not tested */ + + DESC_CTL2_POWER /* don't increase power mgmt. field */ +/* no effect */ + + DESC_CTL2_WEP /* encrypt this frame */ +/* not tested */ + + DESC_CTL2_DUR /* don't increase duration field */ + ); + /* fallthrough */ +#endif default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ clt = NULL; break; @@ -3432,82 +3418,88 @@ acxpci_l_tx_data(wlandevice_t *priv, tx_ } /* used in tx cleanup routine for auto rate and accounting: */ - tx_desc->fixed_size.s.txc = clt; + put_txc(priv, txdesc, clt); + txdesc->total_length = cpu_to_le16(len); + hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN); if (IS_ACX111(priv)) { u16 rate_cur = clt ? clt->rate_cur : priv->rate_bcast; - /* note that if !tx_desc->do_auto, txrate->cur + /* note that if !txdesc->do_auto, txrate->cur ** has only one nonzero bit */ - tx_desc->u.r2.rate111 = cpu_to_le16( + txdesc->u.r2.rate111 = cpu_to_le16( rate_cur /* WARNING: I was never able to make it work with prism54 AP. ** It was falling down to 1Mbit where shortpre is not applicable, ** and not working at all at "5,11 basic rates only" setting. ** I even didn't see tx packets in radio packet capture. ** Disabled for now --vda */ - /*| ((peer->shortpre && txrate->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */ + /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */ ); #ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS /* should add this to rate111 above as necessary */ - | (txrate->pbcc511 ? RATE111_PBCC511 : 0) + | (clt->pbcc511 ? RATE111_PBCC511 : 0) #endif + hostdesc1->length = cpu_to_le16(len); } else { /* ACX100 */ u8 rate_100 = clt ? clt->rate_100 : priv->rate_bcast100; - tx_desc->u.r1.rate = rate_100; + txdesc->u.r1.rate = rate_100; #ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS - if (txrate->pbcc511) { + if (clt->pbcc511) { if (n == RATE100_5 || n == RATE100_11) n |= RATE100_PBCC511; } - if (peer->shortpre && (txrate->cur != RATE111_1)) + if (clt->shortpre && (clt->cur != RATE111_1)) SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */ #endif /* set autodma and reclaim and 1st mpdu */ SET_BIT(Ctl_8, DESC_CTL_AUTODMA | DESC_CTL_RECLAIM | DESC_CTL_FIRSTFRAG); + hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN); } /* don't need to clean ack/rts statistics here, already * done on descr cleanup */ - /* clears Ctl DESC_CTL_HOSTOWN bit, thus telling that the descriptors + /* clears HOSTOWN and ACXDONE bits, thus telling that the descriptors * are now owned by the acx100; do this as LAST operation */ - CLEAR_BIT(Ctl_8, DESC_CTL_HOSTOWN); - wmb(); /* make sure everything else is written before we release our descriptor to the adapter here */ - CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); + CLEAR_BIT(Ctl_8, DESC_CTL_ACXDONE_HOSTOWN); + /* flush writes before we release hostdesc to the adapter here */ + wmb(); + CLEAR_BIT(hostdesc1->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); + CLEAR_BIT(hostdesc2->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); /* write back modified flags */ - tx_desc->Ctl2_8 = Ctl2_8; - tx_desc->Ctl_8 = Ctl_8; + txdesc->Ctl2_8 = Ctl2_8; + txdesc->Ctl_8 = Ctl_8; - tx_desc->tx_time = cpu_to_le32(jiffies); - wmb(); /* make sure all descr writes finish before we tell the adapter that it's its turn now */ - acx_write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); - acx_write_flush(priv); + /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */ +//TODO: should it be a mmiowb() instead? we are protecting against race with write[bwl]() + /* flush writes before we tell the adapter that it's its turn now */ + wmb(); + write_reg16(priv, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); + write_flush(priv); /* log the packet content AFTER sending it, * in order to not delay sending any further than absolutely needed * Do separate logs for acx100/111 to have human-readable rates */ - if (unlikely(acx_debug & (L_XFER | L_DATA))) { - u16 fc = ((wlan_hdr_t*)hostdesc->data)->fc; + if (unlikely(acx_debug & (L_XFER|L_DATA))) { + u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc; if (IS_ACX111(priv)) printk("tx: pkt (%s): len %d " "rate %04X%s status %u\n", - acx_get_packet_type_string(le16_to_cpu(fc)), - le16_to_cpu(tx_desc->total_length), - le16_to_cpu(tx_desc->u.r2.rate111), - (le16_to_cpu(tx_desc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "", + acx_get_packet_type_string(le16_to_cpu(fc)), len, + le16_to_cpu(txdesc->u.r2.rate111), + (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "", priv->status); else printk("tx: pkt (%s): len %d rate %03u%s status %u\n", - acx_get_packet_type_string(fc), - le16_to_cpu(tx_desc->total_length), - tx_desc->u.r1.rate, + acx_get_packet_type_string(fc), len, + txdesc->u.r1.rate, (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "", priv->status); if (acx_debug & L_DATA) { - printk("tx: 802.11 [%d]: ", hostdesc_len); - acx_dump_bytes(hostdesc->data, hostdesc_len); + printk("tx: 802.11 [%d]: ", len); + acx_dump_bytes(hostdesc1->data, len); } } end: @@ -3516,9 +3508,41 @@ end: /*********************************************************************** +** acxpci_l_clean_txdesc +** +** This function resets the txdescs' status when the ACX100 +** signals the TX done IRQ (txdescs have been processed), starting with +** the pool index of the descriptor which we would use next, +** in order to make sure that we can be as fast as possible +** in filling new txdescs. +** Everytime we get called we know where the next packet to be cleaned is. */ + +#if !ACX_DEBUG +static inline void log_txbuffer(const wlandevice_t *priv) {} +#else +static void +log_txbuffer(wlandevice_t *priv) +{ + txdesc_t *txdesc; + int i; + + /* no FN_ENTER here, we don't want that */ + /* no locks here, since it's entirely non-critical code */ + txdesc = priv->txdesc_start; + if (!txdesc) return; + printk("tx: desc->Ctl8's:"); + for (i = 0; i < TX_CNT; i++) { + printk(" %02X", txdesc->Ctl_8); + txdesc = move_txdesc(priv, txdesc, 1); + } + printk("\n"); +} +#endif + + static void -acx_l_handle_tx_error(wlandevice_t *priv, u8 error, unsigned int finger) +handle_tx_error(wlandevice_t *priv, u8 error, unsigned int finger) { const char *err = "unknown error"; @@ -3587,10 +3611,9 @@ acx_l_handle_tx_error(wlandevice_t *priv "before it's too late!\n", priv->netdev->name); if (priv->retry_errors_msg_ratelimit == 20) - printk("disabling above " - "notification message\n"); + printk("disabling above message\n"); - acx_schedule_after_interrupt_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); + acx_schedule_task(priv, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); } break; case 0x40: @@ -3612,8 +3635,26 @@ acx_l_handle_tx_error(wlandevice_t *priv } -/*********************************************************************** -*/ +/* maps acx100 tx descr rate field to acx111 one */ +static u16 +rate100to111(u8 r) +{ + switch (r) { + case RATE100_1: return RATE111_1; + case RATE100_2: return RATE111_2; + case RATE100_5: + case (RATE100_5 | RATE100_PBCC511): return RATE111_5; + case RATE100_11: + case (RATE100_11 | RATE100_PBCC511): return RATE111_11; + case RATE100_22: return RATE111_22; + default: + printk("acx: unexpected acx100 txrate: %u! " + "Please report\n", r); + return RATE111_2; + } +} + + /* Theory of operation: ** client->rate_cap is a bitmask of rates client is capable of. ** client->rate_cfg is a bitmask of allowed (configured) rates. @@ -3637,28 +3678,8 @@ acx_l_handle_tx_error(wlandevice_t *priv ** ** 22Mbit, of course, is PBCC always. */ -/* maps acx100 tx descr rate field to acx111 one */ -static u16 -rate100to111(u8 r) -{ - switch (r) { - case RATE100_1: return RATE111_1; - case RATE100_2: return RATE111_2; - case RATE100_5: - case (RATE100_5 | RATE100_PBCC511): return RATE111_5; - case RATE100_11: - case (RATE100_11 | RATE100_PBCC511): return RATE111_11; - case RATE100_22: return RATE111_22; - default: - printk("acx: unexpected acx100 txrate: %u! " - "Please report\n", r); - return RATE111_2; - } -} - - static void -acx_l_handle_txrate_auto(wlandevice_t *priv, struct client *txc, +handle_txrate_auto(wlandevice_t *priv, struct client *txc, unsigned int idx, u8 rate100, u16 rate111, u8 error) { u16 sent_rate; @@ -3728,7 +3749,7 @@ acx_l_handle_txrate_auto(wlandevice_t *p if (cur) { /* we can't disable all rates! */ acxlog(L_XFER, "tx: falling back to ratemask %04X\n", cur); txc->rate_cur = cur; - txc->ignore_count = priv->TxQueueCnt - priv->TxQueueFree; + txc->ignore_count = TX_CNT - priv->tx_free; } } else if (!slower_rate_was_used) { txc->fallback_count = 0; @@ -3755,7 +3776,7 @@ acx_l_handle_txrate_auto(wlandevice_t *p acxlog(L_XFER, "tx: stepping up to ratemask %04X\n", cur); txc->rate_cur = cur; /* FIXME: totally bogus - we could be sending to many peers at once... */ - txc->ignore_count = priv->TxQueueCnt - priv->TxQueueFree; + txc->ignore_count = TX_CNT - priv->tx_free; } /* calculate acx100 style rate byte if needed */ @@ -3765,95 +3786,60 @@ acx_l_handle_txrate_auto(wlandevice_t *p } -/*---------------------------------------------------------------- -* acx_l_log_txbuffer -*----------------------------------------------------------------*/ -#if !ACX_DEBUG -static inline void acx_l_log_txbuffer(const wlandevice_t *priv) {} -#else -static void -acx_l_log_txbuffer(const wlandevice_t *priv) -{ - txdesc_t *pTxDesc; - int i; - - /* no FN_ENTER here, we don't want that */ - /* no locks here, since it's entirely non-critical code */ - pTxDesc = priv->pTxDescQPool; - if (!pTxDesc) return; - for (i = 0; i < priv->tx_pool_count; i++) { - if ((pTxDesc->Ctl_8 & DESC_CTL_DONE) == DESC_CTL_DONE) - printk("tx: buf %d done\n", i); - pTxDesc = GET_NEXT_TX_DESC_PTR(priv, pTxDesc); - } -} -#endif - - -/*---------------------------------------------------------------- -* acx_l_clean_tx_desc -* -* Comment: -* This function probably resets the txdescs' status when the ACX100 -* signals the TX done IRQ (txdescs have been processed), starting with -* the pool index of the descriptor which we would use next, -* in order to make sure that we can be as fast as possible -* in filling new txdescs. -* Oops, now we have our own index, so everytime we get called we know -* where the next packet to be cleaned is. -* Hmm, still need to loop through the whole ring buffer now, -* since we lost sync for some reason when ping flooding or so... -* (somehow we don't get the IRQ for acx_l_clean_tx_desc any more when -* too many packets are being sent!) -* FIXME: currently we only process one packet, but this gets out of -* sync for some reason when ping flooding, so we need to loop, -* but the previous smart loop implementation causes the ping latency -* to rise dramatically (~3000 ms), at least on CardBus PheeNet WL-0022. -* Dunno what to do :-\ -*----------------------------------------------------------------*/ unsigned int -acx_l_clean_tx_desc(wlandevice_t *priv) +acxpci_l_clean_txdesc(wlandevice_t *priv) { - txdesc_t *pTxDesc; + txdesc_t *txdesc; struct client *txc; - unsigned int watch, finger; - unsigned int num_cleaned = 0, num_processed = 0; + int finger; + int num_cleaned; u16 r111; - u8 error, ack_failures, rts_failures, rts_ok, r100; /* keep old desc status */ + u8 error, ack_failures, rts_failures, rts_ok, r100; FN_ENTER; if (unlikely(acx_debug & L_DEBUG)) - acx_l_log_txbuffer(priv); + log_txbuffer(priv); acxlog(L_BUFT, "tx: cleaning up bufs from %u\n", priv->tx_tail); - watch = priv->tx_tail; - finger = watch; - - do { - pTxDesc = GET_TX_DESC_PTR(priv, finger); - - /* abort if txdesc is not marked as "Tx finished" and "owned" */ - if ((pTxDesc->Ctl_8 & DESC_CTL_DONE) != DESC_CTL_DONE) { - /* we do need to have at least one cleaned, - * otherwise we wouldn't get called in the first place. - * So better stay around some more, unless - * we already processed more descs than the ring - * size. */ - if ((num_cleaned == 0) && (num_processed < priv->tx_pool_count)) - goto next; - else - break; + /* We know first descr which is not free yet. We advance it as far + ** as we see correct bits set in following descs (if next desc + ** is NOT free, we shouldn't advance at all). We know that in + ** front of tx_tail may be "holes" with isolated free descs. + ** We will catch up when all intermediate descs will be freed also */ + + finger = priv->tx_tail; + num_cleaned = 0; + while (finger != priv->tx_head) { + txdesc = get_txdesc(priv, finger); + + /* If we allocated txdesc on tx path but then decided + ** to NOT use it, then it will be left as a free "bubble" + ** in the "allocated for tx" part of the ring. + ** We may meet it on the next ring pass here. */ + + /* stop if not marked as "tx finished" and "host owned" */ + if ((txdesc->Ctl_8 & DESC_CTL_ACXDONE_HOSTOWN) != DESC_CTL_ACXDONE_HOSTOWN) { + /* Moan a lot if none was cleaned */ + if (!num_cleaned) { + if (!(acx_debug & L_DEBUG)) + log_txbuffer(priv); + printk("%s: clean_tx_desc: tail is not free. " + "tail:%d head:%d. Please report\n", + priv->netdev->name, + priv->tx_tail, priv->tx_head); + } + break; } - /* remember descr values... */ - error = pTxDesc->error; - ack_failures = pTxDesc->ack_failures; - rts_failures = pTxDesc->rts_failures; - rts_ok = pTxDesc->rts_ok; - r100 = pTxDesc->u.r1.rate; - r111 = pTxDesc->u.r2.rate111; + /* remember desc values... */ + error = txdesc->error; + ack_failures = txdesc->ack_failures; + rts_failures = txdesc->rts_failures; + rts_ok = txdesc->rts_ok; + r100 = txdesc->u.r1.rate; + r111 = txdesc->u.r2.rate111; #if WIRELESS_EXT > 13 /* wireless_send_event() and IWEVTXDROP are WE13 */ /* need to check for certain error conditions before we @@ -3865,35 +3851,30 @@ acx_l_clean_tx_desc(wlandevice_t *priv) wlan_hdr_t *hdr; txhostdesc_t *hostdesc; - hostdesc = pTxDesc->fixed_size.s.host_desc; - /* FIXME: happens on card eject; better method? */ - if (unlikely((long)-1 == (long)hostdesc)) - goto end; + hostdesc = get_txhostdesc(priv, txdesc); hdr = (wlan_hdr_t *)hostdesc->data; MAC_COPY(wrqu.addr.sa_data, hdr->a1); wireless_send_event(priv->netdev, IWEVTXDROP, &wrqu, NULL); } #endif - /* ...and free the descr */ - pTxDesc->error = 0; - pTxDesc->ack_failures = 0; - pTxDesc->rts_failures = 0; - pTxDesc->rts_ok = 0; + /* ...and free the desc */ + txdesc->error = 0; + txdesc->ack_failures = 0; + txdesc->rts_failures = 0; + txdesc->rts_ok = 0; /* signal host owning it LAST, since ACX already knows that this - * descriptor is finished since it set Ctl_8 accordingly: - * if _OWN is set at the beginning instead, our own get_tx - * might choose a Tx desc that isn't fully cleared - * (in case of bad locking). */ - pTxDesc->Ctl_8 = DESC_CTL_HOSTOWN; - priv->TxQueueFree++; + ** descriptor is finished since it set Ctl_8 accordingly. */ + txdesc->Ctl_8 = DESC_CTL_HOSTOWN; + + priv->tx_free++; num_cleaned++; - if ((priv->TxQueueFree >= MINFREE_TX + 3) - && (priv->status == ACX_STATUS_4_ASSOCIATED) - && (acx_queue_stopped(priv->netdev)) + if ((priv->tx_free >= TX_START_QUEUE) + && (priv->status == ACX_STATUS_4_ASSOCIATED) + && (acx_queue_stopped(priv->netdev)) ) { acxlog(L_BUF, "tx: wake queue (avail. Tx desc %u)\n", - priv->TxQueueFree); + priv->tx_free); acx_wake_queue(priv->netdev, NULL); } @@ -3901,29 +3882,28 @@ acx_l_clean_tx_desc(wlandevice_t *priv) * AFTER having done the work, it's faster */ /* do rate handling */ - txc = pTxDesc->fixed_size.s.txc; + txc = get_txc(priv, txdesc); if (txc && priv->rate_auto) { - acx_l_handle_txrate_auto(priv, txc, finger, r100, r111, error); + handle_txrate_auto(priv, txc, finger, r100, r111, error); } if (unlikely(error)) - acx_l_handle_tx_error(priv, error, finger); + handle_tx_error(priv, error, finger); if (IS_ACX111(priv)) acxlog(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n", finger, ack_failures, rts_failures, rts_ok, r111); else acxlog(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n", - finger, ack_failures, rts_failures, rts_ok, le16_to_cpu(r111) & 0xff); -next: + finger, ack_failures, rts_failures, rts_ok, r100); + /* update pointer for descr to be cleaned next */ - finger = (finger + 1) % priv->tx_pool_count; - num_processed++; - } while (watch != finger); + finger = (finger + 1) % TX_CNT; + } /* remember last position */ priv->tx_tail = finger; -end: +/* end: */ FN_EXIT1(num_cleaned); return num_cleaned; } @@ -3931,155 +3911,48 @@ end: /* clean *all* Tx descriptors, and regardless of their previous state. * Used for brute-force reset handling. */ void -acx_l_clean_tx_desc_emergency(wlandevice_t *priv) +acxpci_l_clean_txdesc_emergency(wlandevice_t *priv) { - txdesc_t *pTxDesc; - unsigned int i; + txdesc_t *txdesc; + int i; FN_ENTER; - for (i = 0; i < priv->tx_pool_count; i++) { - pTxDesc = GET_TX_DESC_PTR(priv, i); + for (i = 0; i < TX_CNT; i++) { + txdesc = get_txdesc(priv, i); /* free it */ - pTxDesc->ack_failures = 0; - pTxDesc->rts_failures = 0; - pTxDesc->rts_ok = 0; - pTxDesc->error = 0; - pTxDesc->Ctl_8 = DESC_CTL_HOSTOWN; + txdesc->ack_failures = 0; + txdesc->rts_failures = 0; + txdesc->rts_ok = 0; + txdesc->error = 0; + txdesc->Ctl_8 = DESC_CTL_HOSTOWN; } - priv->TxQueueFree = priv->tx_pool_count; + priv->tx_free = TX_CNT; FN_EXIT0; } -/*---------------------------------------------------------------- -* acx_l_log_rxbuffer -* -* Called from IRQ context only -*----------------------------------------------------------------*/ -#if !ACX_DEBUG -static inline void acx_l_log_rxbuffer(const wlandevice_t *priv) {} -#else -static void -acx_l_log_rxbuffer(const wlandevice_t *priv) -{ - const struct rxhostdesc *pRxDesc; - int i; - - /* no FN_ENTER here, we don't want that */ - - pRxDesc = priv->pRxHostDescQPool; - for (i = 0; i < priv->rx_pool_count; i++) { - if ((pRxDesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) - && (pRxDesc->Status & cpu_to_le32(BIT31))) - printk("rx: buf %d full\n", i); - pRxDesc++; - } -} -#endif - - -/*************************************************************** -** acx_l_process_rx_desc -** -** Called directly and only from the IRQ handler +/*********************************************************************** +** acxpci_s_create_tx_host_desc_queue */ -void -acx_l_process_rx_desc(wlandevice_t *priv) -{ - struct rxhostdesc *RxHostPool; - struct rxhostdesc *pRxHostDesc; - unsigned int curr_idx; - unsigned int count = 0; - - FN_ENTER; - - if (unlikely(acx_debug & L_BUFR)) { - acx_l_log_rxbuffer(priv); - } - - RxHostPool = priv->pRxHostDescQPool; - - /* First, have a loop to determine the first descriptor that's - * full, just in case there's a mismatch between our current - * rx_tail and the full descriptor we're supposed to handle. */ - while (1) { - curr_idx = priv->rx_tail; - pRxHostDesc = &RxHostPool[priv->rx_tail]; - priv->rx_tail = (priv->rx_tail + 1) % priv->rx_pool_count; - rmb(); - if ((pRxHostDesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) - && (pRxHostDesc->Status & cpu_to_le32(BIT31))) { - /* found it! */ - break; - } - count++; - if (unlikely(count > priv->rx_pool_count)) { - /* hmm, no luck: all descriptors empty, bail out */ - goto end; - } - } - - /* now process descriptors, starting with the first we figured out */ - while (1) { - acxlog(L_BUFR, "%s: using curr_idx %u, rx_tail is now %u\n", - __func__, curr_idx, priv->rx_tail); - - /* FIXME: comment needed - why rmb()? */ - rmb(); - acx_l_process_rxbuf(priv, pRxHostDesc->data); - - pRxHostDesc->Status = 0; - /* make sure adapter sees CTL_HOSTOWN change only after - * all other fields have been written */ - wmb(); - /* Host no longer owns this, needs to be LAST */ - CLEAR_BIT(pRxHostDesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); - - /* ok, descriptor is handled, now check the next descriptor */ - curr_idx = priv->rx_tail; - pRxHostDesc = &RxHostPool[priv->rx_tail]; - - /* if next descriptor is empty, then bail out */ - /* FIXME: is this check really entirely correct?? */ - rmb(); - /* - if (!(pRxHostDesc->Ctl & cpu_to_le16(DESC_CTL_HOSTOWN)) - && !(pRxHostDesc->Status & cpu_to_le32(BIT31))) */ - if (!(pRxHostDesc->Status & cpu_to_le32(BIT31))) - break; - else - priv->rx_tail = (priv->rx_tail + 1) % priv->rx_pool_count; - } -end: - FN_EXIT0; -} - -/*---------------------------------------------------------------- -* acx_s_create_tx_host_desc_queue -*----------------------------------------------------------------*/ -static inline void* -acx_alloc_coherent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle, int flag) +static void* +allocate(wlandevice_t *priv, size_t size, dma_addr_t *phy, const char *msg) { + void *ptr; + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 53) - return dma_alloc_coherent(hwdev == NULL ? NULL : &hwdev->dev, - size, dma_handle, flag); + ptr = dma_alloc_coherent(priv->pdev ? &priv->pdev->dev : NULL, + size, phy, GFP_KERNEL); #else #warning Using old PCI-specific DMA allocation, may fail with out-of-mem! #warning Upgrade kernel if it does... - return pci_alloc_consistent(hwdev, size, dma_handle); + ptr = pci_alloc_consistent(priv->pdev, size, phy); #endif -} -static void* -allocate(wlandevice_t *priv, size_t size, dma_addr_t *phy, const char *msg) -{ - void *ptr = acx_alloc_coherent(priv->pdev, size, phy, GFP_KERNEL); if (ptr) { acxlog(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n", msg, (int)size, ptr, (unsigned long long)*phy); @@ -4091,39 +3964,35 @@ allocate(wlandevice_t *priv, size_t size return NULL; } -int -acx_s_create_tx_host_desc_queue(wlandevice_t *priv) + +static int +acxpci_s_create_tx_host_desc_queue(wlandevice_t *priv) { - txhostdesc_t *host_desc; - txhostdesc_t *host_desc_phy; - u8 *frame_buffer; - u8 *frame_buffer_phy; - unsigned int align_offs, alignment; + txhostdesc_t *hostdesc; + u8 *txbuf; + dma_addr_t hostdesc_phy; + dma_addr_t txbuf_phy; int i; FN_ENTER; /* allocate TX buffer */ - priv->TxBufferPoolSize = priv->TxQueueCnt * WLAN_A4FR_MAXLEN_WEP; - priv->pTxBufferPool = allocate(priv, priv->TxBufferPoolSize, - &priv->TxBufferPoolPhyAddr, "pTxBufferPool"); - if (!priv->pTxBufferPool) + priv->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS; + priv->txbuf_start = allocate(priv, priv->txbuf_area_size, + &priv->txbuf_startphy, "txbuf_start"); + if (!priv->txbuf_start) goto fail; /* allocate the TX host descriptor queue pool */ - priv->TxHostDescQPoolSize = priv->TxQueueCnt * 2*sizeof(txhostdesc_t) + 3; - priv->pTxHostDescQPool = allocate(priv, priv->TxHostDescQPoolSize, - &priv->TxHostDescQPoolPhyAddr, "pTxHostDescQPool"); - if (!priv->pTxHostDescQPool) + priv->txhostdesc_area_size = TX_CNT * 2*sizeof(txhostdesc_t); + priv->txhostdesc_start = allocate(priv, priv->txhostdesc_area_size, + &priv->txhostdesc_startphy, "txhostdesc_start"); + if (!priv->txhostdesc_start) goto fail; - /* check for proper alignment of TX host descriptor pool */ - alignment = (long) priv->pTxHostDescQPool & 3; - if (alignment) { - printk("acx: TxHostDescQPool is not aligned properly\n"); - align_offs = 4 - alignment; - } else { - align_offs = 0; + if ((long) priv->txhostdesc_start & 3) { + printk("acx: driver bug: dma alloc returns unaligned address\n"); + goto fail; } /* Each tx frame buffer is accessed by hardware via @@ -4137,44 +4006,78 @@ acx_s_create_tx_host_desc_queue(wlandevi ** has hostdesc.length = 3 (or larger) ** Storing NULL into hostdesc.desc_phy_next ** doesn't seem to help. -*/ -/* It is not known whether we need to have 'extra' second +** +** It is not known whether we need to have 'extra' second ** txhostdescs for acx100. Maybe it is acx111-only bug. -*/ - host_desc = (txhostdesc_t *) ((u8 *) priv->pTxHostDescQPool + align_offs); - host_desc_phy = (txhostdesc_t *) ((u8 *) priv->TxHostDescQPoolPhyAddr + align_offs); - frame_buffer = (u8 *) priv->pTxBufferPool; - frame_buffer_phy = (u8 *) priv->TxBufferPoolPhyAddr; +** +** Update: acx111 WG311v2 is even more bogus than this. +** We will initialize two hostdesc so that they point +** to adjacent memory areas. +*/ + hostdesc = priv->txhostdesc_start; + hostdesc_phy = priv->txhostdesc_startphy; + txbuf = priv->txbuf_start; + txbuf_phy = priv->txbuf_startphy; - for (i = 0; i < priv->TxQueueCnt*2; i++) { +#if 0 +/* Works for xterasys xn2522g, does not for WG311v2 !!? */ + for (i = 0; i < TX_CNT*2; i++) { + hostdesc_phy += sizeof(txhostdesc_t); if (!(i & 1)) { - host_desc->data_phy = ptr2acx(frame_buffer_phy); - /* host_desc->data_offset = ... */ - /* host_desc->reserved = ... */ - host_desc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); - host_desc->desc_phy_next = ptr2acx(host_desc_phy + 1); - host_desc->pNext = ptr2acx(NULL); - /* host_desc->Status = ... */ + hostdesc->data_phy = cpu2acx(txbuf_phy); + /* hostdesc->data_offset = ... */ + /* hostdesc->reserved = ... */ + hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); + /* hostdesc->length = ... */ + hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); + hostdesc->pNext = ptr2acx(NULL); + /* hostdesc->Status = ... */ /* below: non-hardware fields */ - host_desc->data = frame_buffer; + hostdesc->data = txbuf; - frame_buffer += WLAN_A4FR_MAXLEN_WEP; - frame_buffer_phy += WLAN_A4FR_MAXLEN_WEP; + txbuf += WLAN_A4FR_MAXLEN_WEP_FCS; + txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS; } else { - /* host_desc->data_phy = ... */ - /* host_desc->data_offset = ... */ - /* host_desc->reserved = ... */ - /* host_desc->Ctl_16 = ... */ - host_desc->length = 3; /* bug workaround */ - /* host_desc->desc_phy_next = ... */ - /* host_desc->pNext = ... */ - /* host_desc->Status = ... */ + /* hostdesc->data_phy = ... */ + /* hostdesc->data_offset = ... */ + /* hostdesc->reserved = ... */ + /* hostdesc->Ctl_16 = ... */ + hostdesc->length = 3; /* bug workaround */ + /* hostdesc->desc_phy_next = ... */ + /* hostdesc->pNext = ... */ + /* hostdesc->Status = ... */ /* below: non-hardware fields */ - /* host_desc->data = ... */ + /* hostdesc->data = ... */ + } + hostdesc++; + } +#endif + for (i = 0; i < TX_CNT*2; i++) { + hostdesc_phy += sizeof(txhostdesc_t); + + hostdesc->data_phy = cpu2acx(txbuf_phy); + /* done by memset(0): hostdesc->data_offset = 0; */ + /* hostdesc->reserved = ... */ + hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); + /* hostdesc->length = ... */ + hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); + /* done by memset(0): hostdesc->pNext = ptr2acx(NULL); */ + /* hostdesc->Status = ... */ + /* ->data is a non-hardware field: */ + hostdesc->data = txbuf; + + if (!(i & 1)) { + txbuf += WLAN_HDR_A3_LEN; + txbuf_phy += WLAN_HDR_A3_LEN; + } else { + txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN; + txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN; } - host_desc_phy++; - host_desc++; + hostdesc++; } + hostdesc--; + hostdesc->desc_phy_next = cpu2acx(priv->txhostdesc_startphy); + FN_EXIT1(OK); return OK; fail: @@ -4186,75 +4089,64 @@ fail: /*************************************************************** -** acx_s_create_rx_host_desc_queue +** acxpci_s_create_rx_host_desc_queue */ /* the whole size of a data buffer (header plus data body) * plus 32 bytes safety offset at the end */ #define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32) -int -acx_s_create_rx_host_desc_queue(wlandevice_t *priv) +static int +acxpci_s_create_rx_host_desc_queue(wlandevice_t *priv) { - rxhostdesc_t *host_desc; - rxbuffer_t *data; - rxhostdesc_t *host_desc_phy; - rxbuffer_t *data_phy; - unsigned int align_offs, alignment; + rxhostdesc_t *hostdesc; + rxbuffer_t *rxbuf; + dma_addr_t hostdesc_phy; + dma_addr_t rxbuf_phy; int i; FN_ENTER; /* allocate the RX host descriptor queue pool */ - priv->RxHostDescQPoolSize = (priv->RxQueueCnt * sizeof(rxhostdesc_t)) + 0x3; - - priv->pRxHostDescQPool = allocate(priv, priv->RxHostDescQPoolSize, - &priv->RxHostDescQPoolPhyAddr, "pRxHostDescQPool"); - if (!priv->pRxHostDescQPool) + priv->rxhostdesc_area_size = RX_CNT * sizeof(rxhostdesc_t); + priv->rxhostdesc_start = allocate(priv, priv->rxhostdesc_area_size, + &priv->rxhostdesc_startphy, "rxhostdesc_start"); + if (!priv->rxhostdesc_start) + goto fail; + /* check for proper alignment of RX host descriptor pool */ + if ((long) priv->rxhostdesc_start & 3) { + printk("acx: driver bug: dma alloc returns unaligned address\n"); goto fail; + } /* allocate Rx buffer pool which will be used by the acx * to store the whole content of the received frames in it */ - priv->RxBufferPoolSize = ( priv->RxQueueCnt * RX_BUFFER_SIZE ); - priv->pRxBufferPool = allocate(priv, priv->RxBufferPoolSize, - &priv->RxBufferPoolPhyAddr, "pRxBufferPool"); - if (!priv->pRxBufferPool) + priv->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE; + priv->rxbuf_start = allocate(priv, priv->rxbuf_area_size, + &priv->rxbuf_startphy, "rxbuf_start"); + if (!priv->rxbuf_start) goto fail; - data = priv->pRxBufferPool; - data_phy = (rxbuffer_t *) priv->RxBufferPoolPhyAddr; - - /* check for proper alignment of RX host descriptor pool */ - alignment = (long) priv->pRxHostDescQPool & 3; - if (alignment) { - printk("acx: RxHostDescQPool is not aligned properly\n"); - align_offs = 4 - alignment; - } else { - align_offs = 0; - } - - host_desc = (rxhostdesc_t *) ((u8 *) priv->pRxHostDescQPool + align_offs); - host_desc_phy = (rxhostdesc_t *) ((u8 *) priv->RxHostDescQPoolPhyAddr + align_offs); - priv->RxHostDescPoolStart = host_desc_phy; + rxbuf = priv->rxbuf_start; + rxbuf_phy = priv->rxbuf_startphy; + hostdesc = priv->rxhostdesc_start; + hostdesc_phy = priv->rxhostdesc_startphy; /* don't make any popular C programming pointer arithmetic mistakes * here, otherwise I'll kill you... * (and don't dare asking me why I'm warning you about that...) */ - for (i = 0; i < priv->RxQueueCnt - 1; i++) { - host_desc->data = data; - data++; /* proceed to content of next buffer */ - host_desc->data_phy = ptr2acx(data_phy); - data_phy++; /* proceed to content of next buffer */ - host_desc->length = cpu_to_le16(RX_BUFFER_SIZE); - CLEAR_BIT(host_desc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); - host_desc->desc_phy_next = ptr2acx(host_desc_phy + 1); - host_desc_phy++; - host_desc++; - } - host_desc->data = data; - host_desc->data_phy = ptr2acx(data_phy); - host_desc->length = cpu_to_le16(RX_BUFFER_SIZE); - CLEAR_BIT(host_desc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); - host_desc->desc_phy_next = ptr2acx((u8 *) priv->RxHostDescQPoolPhyAddr + align_offs); + for (i = 0; i < RX_CNT; i++) { + hostdesc->data = rxbuf; + hostdesc->data_phy = cpu2acx(rxbuf_phy); + hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE); + CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); + rxbuf++; + rxbuf_phy += sizeof(rxbuffer_t); + hostdesc_phy += sizeof(rxhostdesc_t); + hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); + hostdesc++; + } + hostdesc--; + hostdesc->desc_phy_next = cpu2acx(priv->rxhostdesc_startphy); FN_EXIT1(OK); return OK; fail: @@ -4266,186 +4158,173 @@ fail: /*************************************************************** -** acx_s_create_hostdesc_queues +** acxpci_s_create_hostdesc_queues */ int -acx_s_create_hostdesc_queues(wlandevice_t *priv) +acxpci_s_create_hostdesc_queues(wlandevice_t *priv) { int result; - result = acx_s_create_tx_host_desc_queue(priv); + result = acxpci_s_create_tx_host_desc_queue(priv); if (OK != result) return result; - result = acx_s_create_rx_host_desc_queue(priv); + result = acxpci_s_create_rx_host_desc_queue(priv); return result; } /*************************************************************** -** acx_create_tx_desc_queue +** acxpci_create_tx_desc_queue */ -void BUG_txdesc_must_be_0x30_bytes_in_length(void); - static void -acx_create_tx_desc_queue(wlandevice_t *priv, u32 tx_queue_start) +acxpci_create_tx_desc_queue(wlandevice_t *priv, u32 tx_queue_start) { - txdesc_t *tx_desc; - txhostdesc_t *tx_hostdesc; + txdesc_t *txdesc; + txhostdesc_t *hostdesc; dma_addr_t hostmemptr; u32 mem_offs; int i; FN_ENTER; - priv->tx_pool_count = priv->TxQueueCnt; - - priv->TxDescrSize = sizeof(txdesc_t); - - if (sizeof(txdesc_t) != 0x30) - BUG_txdesc_must_be_0x30_bytes_in_length(); + priv->txdesc_size = sizeof(txdesc_t); if (IS_ACX111(priv)) { /* the acx111 txdesc is 4 bytes larger */ - priv->TxDescrSize = sizeof(txdesc_t) + 4; + priv->txdesc_size = sizeof(txdesc_t) + 4; } - priv->pTxDescQPool = (txdesc_t *) (priv->iobase2 + tx_queue_start); + priv->txdesc_start = (txdesc_t *) (priv->iobase2 + tx_queue_start); acxlog(L_DEBUG, "priv->iobase2=%p\n" "tx_queue_start=%08X\n" - "priv->pTxDescQPool=%p\n", + "priv->txdesc_start=%p\n", priv->iobase2, tx_queue_start, - priv->pTxDescQPool); + priv->txdesc_start); - priv->TxQueueFree = priv->TxQueueCnt; - priv->tx_head = 0; - priv->tx_tail = 0; - tx_desc = priv->pTxDescQPool; + priv->tx_free = TX_CNT; + /* done by memset: priv->tx_head = 0; */ + /* done by memset: priv->tx_tail = 0; */ + txdesc = priv->txdesc_start; mem_offs = tx_queue_start; - hostmemptr = priv->TxHostDescQPoolPhyAddr; - tx_hostdesc = priv->pTxHostDescQPool; + hostmemptr = priv->txhostdesc_startphy; + hostdesc = priv->txhostdesc_start; if (IS_ACX111(priv)) { /* ACX111 has a preinitialized Tx buffer! */ /* loop over whole send pool */ /* FIXME: do we have to do the hostmemptr stuff here?? */ - for (i = 0; i < priv->tx_pool_count; i++) { - tx_desc->HostMemPtr = ptr2acx(hostmemptr); - tx_desc->Ctl_8 = DESC_CTL_HOSTOWN; - tx_desc->fixed_size.s.host_desc = tx_hostdesc; - + for (i = 0; i < TX_CNT; i++) { + txdesc->HostMemPtr = ptr2acx(hostmemptr); + txdesc->Ctl_8 = DESC_CTL_HOSTOWN; /* reserve two (hdr desc and payload desc) */ - tx_hostdesc += 2; + hostdesc += 2; hostmemptr += 2 * sizeof(txhostdesc_t); - tx_desc = (txdesc_t *)(((u8 *)tx_desc) + priv->TxDescrSize); + txdesc = move_txdesc(priv, txdesc, 1); } - } else { /* ACX100 Tx buffer needs to be initialized by us */ - /* clear whole send pool */ - memset(priv->pTxDescQPool, 0, priv->tx_pool_count * priv->TxDescrSize); + /* clear whole send pool. sizeof is safe here (we are acx100) */ + memset(priv->txdesc_start, 0, TX_CNT * sizeof(txdesc_t)); /* loop over whole send pool */ - for (i = 0; i < priv->tx_pool_count; i++) { + for (i = 0; i < TX_CNT; i++) { acxlog(L_DEBUG, "configure card tx descriptor: 0x%p, " - "size: 0x%X\n", tx_desc, priv->TxDescrSize); + "size: 0x%X\n", txdesc, priv->txdesc_size); /* pointer to hostdesc memory */ /* FIXME: type-incorrect assignment, might cause trouble * in some cases */ - tx_desc->HostMemPtr = ptr2acx(hostmemptr); + txdesc->HostMemPtr = ptr2acx(hostmemptr); /* initialise ctl */ - tx_desc->Ctl_8 = DESC_CTL_INIT; - tx_desc->Ctl2_8 = 0; + txdesc->Ctl_8 = DESC_CTL_INIT; + txdesc->Ctl2_8 = 0; /* point to next txdesc */ - tx_desc->pNextDesc = cpu2acx(mem_offs + priv->TxDescrSize); - /* pointer to first txhostdesc */ - tx_desc->fixed_size.s.host_desc = tx_hostdesc; - + txdesc->pNextDesc = cpu2acx(mem_offs + priv->txdesc_size); /* reserve two (hdr desc and payload desc) */ - tx_hostdesc += 2; + hostdesc += 2; hostmemptr += 2 * sizeof(txhostdesc_t); /* go to the next one */ - mem_offs += priv->TxDescrSize; - tx_desc = (txdesc_t *)(((u8 *)tx_desc) + priv->TxDescrSize); + mem_offs += priv->txdesc_size; + /* ++ is safe here (we are acx100) */ + txdesc++; } /* go back to the last one */ - tx_desc = (txdesc_t *)(((u8 *)tx_desc) - priv->TxDescrSize); + txdesc--; /* and point to the first making it a ring buffer */ - tx_desc->pNextDesc = cpu2acx(tx_queue_start); + txdesc->pNextDesc = cpu2acx(tx_queue_start); } FN_EXIT0; } /*************************************************************** -** acx_create_rx_desc_queue +** acxpci_create_rx_desc_queue */ static void -acx_create_rx_desc_queue(wlandevice_t *priv, u32 rx_queue_start) +acxpci_create_rx_desc_queue(wlandevice_t *priv, u32 rx_queue_start) { - rxdesc_t *rx_desc; + rxdesc_t *rxdesc; u32 mem_offs; int i; FN_ENTER; - priv->rx_pool_count = priv->RxQueueCnt; - priv->rx_tail = 0; + /* done by memset: priv->rx_tail = 0; */ /* ACX111 doesn't need any further config: preconfigures itself. * Simply print ring buffer for debugging */ if (IS_ACX111(priv)) { - /* pRxDescQPool already set here */ + /* rxdesc_start already set here */ - priv->pRxDescQPool = (rxdesc_t *) ((u8 *)priv->iobase2 + rx_queue_start); + priv->rxdesc_start = (rxdesc_t *) ((u8 *)priv->iobase2 + rx_queue_start); - rx_desc = priv->pRxDescQPool; - for (i = 0; i < priv->rx_pool_count; i++) { - acxlog(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rx_desc); - rx_desc = priv->pRxDescQPool = (rxdesc_t *) - (priv->iobase2 + acx2cpu(rx_desc->pNextDesc)); + rxdesc = priv->rxdesc_start; + for (i = 0; i < RX_CNT; i++) { + acxlog(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc); + rxdesc = priv->rxdesc_start = (rxdesc_t *) + (priv->iobase2 + acx2cpu(rxdesc->pNextDesc)); } } else { - /* we didn't pre-calculate pRxDescQPool in case of ACX100 */ - /* pRxDescQPool should be right AFTER Tx pool */ - priv->pRxDescQPool = (rxdesc_t *) - ((u8 *) priv->pTxDescQPool + (priv->TxQueueCnt * sizeof(txdesc_t))); + /* we didn't pre-calculate rxdesc_start in case of ACX100 */ + /* rxdesc_start should be right AFTER Tx pool */ + priv->rxdesc_start = (rxdesc_t *) + ((u8 *) priv->txdesc_start + (TX_CNT * sizeof(txdesc_t))); /* NB: sizeof(txdesc_t) above is valid because we know ** we are in if(acx100) block. Beware of cut-n-pasting elsewhere! ** acx111's txdesc is larger! */ - memset(priv->pRxDescQPool, 0, priv->rx_pool_count * sizeof(rxdesc_t)); + memset(priv->rxdesc_start, 0, RX_CNT * sizeof(rxdesc_t)); /* loop over whole receive pool */ - rx_desc = priv->pRxDescQPool; + rxdesc = priv->rxdesc_start; mem_offs = rx_queue_start; - for (i = 0; i < priv->rx_pool_count; i++) { - acxlog(L_DEBUG, "rx descriptor @ 0x%p\n", rx_desc); - rx_desc->Ctl_8 = DESC_CTL_RECLAIM | DESC_CTL_AUTODMA; + for (i = 0; i < RX_CNT; i++) { + acxlog(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc); + rxdesc->Ctl_8 = DESC_CTL_RECLAIM | DESC_CTL_AUTODMA; /* point to next rxdesc */ - rx_desc->pNextDesc = cpu2acx(mem_offs + sizeof(rxdesc_t)); + rxdesc->pNextDesc = cpu2acx(mem_offs + sizeof(rxdesc_t)); /* go to the next one */ mem_offs += sizeof(rxdesc_t); - rx_desc++; + rxdesc++; } /* go to the last one */ - rx_desc--; + rxdesc--; /* and point to the first making it a ring buffer */ - rx_desc->pNextDesc = cpu2acx(rx_queue_start); + rxdesc->pNextDesc = cpu2acx(rx_queue_start); } FN_EXIT0; } /*************************************************************** -** acx_create_desc_queues +** acxpci_create_desc_queues */ void -acx_create_desc_queues(wlandevice_t *priv, u32 tx_queue_start, u32 rx_queue_start) +acxpci_create_desc_queues(wlandevice_t *priv, u32 tx_queue_start, u32 rx_queue_start) { - acx_create_tx_desc_queue(priv, tx_queue_start); - acx_create_rx_desc_queue(priv, rx_queue_start); + acxpci_create_tx_desc_queue(priv, tx_queue_start); + acxpci_create_rx_desc_queue(priv, rx_queue_start); } @@ -4456,53 +4335,52 @@ char* acxpci_s_proc_diag_output(char *p, wlandevice_t *priv) { const char *rtl, *thd, *ttl; - const rxhostdesc_t *pRxDesc; - const txdesc_t *pTxDesc; + rxhostdesc_t *rxhostdesc; + txdesc_t *txdesc; int i; FN_ENTER; p += sprintf(p, "** Rx buf **\n"); - for (i = 0; i < priv->rx_pool_count; i++) { + rxhostdesc = priv->rxhostdesc_start; + if (rxhostdesc) for (i = 0; i < RX_CNT; i++) { rtl = (i == priv->rx_tail) ? " [tail]" : ""; - pRxDesc = &priv->pRxHostDescQPool[i]; - if ((pRxDesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) - && (pRxDesc->Status & cpu_to_le32(BIT31)) ) + if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) + && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)) ) p += sprintf(p, "%02u FULL%s\n", i, rtl); else p += sprintf(p, "%02u empty%s\n", i, rtl); + rxhostdesc++; } - p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", priv->TxQueueFree, + p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", priv->tx_free, acx_queue_stopped(priv->netdev) ? "STOPPED" : "running"); - pTxDesc = priv->pTxDescQPool; - for (i = 0; i < priv->tx_pool_count; i++) { + txdesc = priv->txdesc_start; + if (txdesc) for (i = 0; i < TX_CNT; i++) { thd = (i == priv->tx_head) ? " [head]" : ""; ttl = (i == priv->tx_tail) ? " [tail]" : ""; - if (pTxDesc->Ctl_8 & DESC_CTL_ACXDONE) - p += sprintf(p, "%02u DONE (%02X)%s%s\n", i, pTxDesc->Ctl_8, thd, ttl); - else - if (!(pTxDesc->Ctl_8 & DESC_CTL_HOSTOWN)) - p += sprintf(p, "%02u TxWait (%02X)%s%s\n", i, pTxDesc->Ctl_8, thd, ttl); + if (txdesc->Ctl_8 & DESC_CTL_ACXDONE) + p += sprintf(p, "%02u free (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl); else - p += sprintf(p, "%02u empty (%02X)%s%s\n", i, pTxDesc->Ctl_8, thd, ttl); - pTxDesc = GET_NEXT_TX_DESC_PTR(priv, pTxDesc); + p += sprintf(p, "%02u tx (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl); + txdesc = move_txdesc(priv, txdesc, 1); } p += sprintf(p, "\n" "** PCI data **\n" - "pTxBufferPool %p, TxBufferPoolSize %u, TxBufferPoolPhyAddr %08llx\n" - "TxDescrSize %u, pTxDescQPool %p, tx_pool_count %u\n" - "pTxHostDescQPool %p, TxHostDescQPoolSize %u, TxHostDescQPoolPhyAddr %08llx\n" - "pRxDescQPool %p, rx_pool_count %d\n" - "pRxHostDescQPool %p, RxHostDescQPoolSize %u, RxHostDescQPoolPhyAddr %08llx\n" - "pRxBufferPool %p, RxBufferPoolSize %u, RxBufferPoolPhyAddr %08llx\n", - priv->pTxBufferPool, priv->TxBufferPoolSize, (u64)priv->TxBufferPoolPhyAddr, - priv->TxDescrSize, priv->pTxDescQPool, priv->tx_pool_count, - priv->pTxHostDescQPool, priv->TxHostDescQPoolSize, (u64)priv->TxHostDescQPoolPhyAddr, - priv->pRxDescQPool, priv->rx_pool_count, - priv->pRxHostDescQPool, priv->RxHostDescQPoolSize, (u64)priv->RxHostDescQPoolPhyAddr, - priv->pRxBufferPool, priv->RxBufferPoolSize, (u64)priv->RxBufferPoolPhyAddr); + "txbuf_start %p, txbuf_area_size %u, txbuf_startphy %08llx\n" + "txdesc_size %u, txdesc_start %p\n" + "txhostdesc_start %p, txhostdesc_area_size %u, txhostdesc_startphy %08llx\n" + "rxdesc_start %p\n" + "rxhostdesc_start %p, rxhostdesc_area_size %u, rxhostdesc_startphy %08llx\n" + "rxbuf_start %p, rxbuf_area_size %u, rxbuf_startphy %08llx\n", + priv->txbuf_start, priv->txbuf_area_size, (u64)priv->txbuf_startphy, + priv->txdesc_size, priv->txdesc_start, + priv->txhostdesc_start, priv->txhostdesc_area_size, (u64)priv->txhostdesc_startphy, + priv->rxdesc_start, + priv->rxhostdesc_start, priv->rxhostdesc_area_size, (u64)priv->rxhostdesc_startphy, + priv->rxbuf_start, priv->rxbuf_area_size, (u64)priv->rxbuf_startphy); + FN_EXIT0; return p; } @@ -4510,7 +4388,7 @@ acxpci_s_proc_diag_output(char *p, wland /*********************************************************************** */ int -acx_proc_eeprom_output(char *buf, wlandevice_t *priv) +acxpci_proc_eeprom_output(char *buf, wlandevice_t *priv) { char *p = buf; int i; @@ -4518,7 +4396,7 @@ acx_proc_eeprom_output(char *buf, wlande FN_ENTER; for (i = 0; i < 0x400; i++) { - acx_read_eeprom_offset(priv, i, p++); + acxpci_read_eeprom_byte(priv, i, p++); } FN_EXIT1(p - buf); @@ -4529,7 +4407,7 @@ acx_proc_eeprom_output(char *buf, wlande /*********************************************************************** */ void -acx_set_interrupt_mask(wlandevice_t *priv) +acxpci_set_interrupt_mask(wlandevice_t *priv) { if (IS_ACX111(priv)) { priv->irq_mask = (u16) ~(0 @@ -4578,7 +4456,7 @@ acx_set_interrupt_mask(wlandevice_t *pri /*********************************************************************** */ int -acx100_s_set_tx_level(wlandevice_t *priv, u8 level_dbm) +acx100pci_s_set_tx_level(wlandevice_t *priv, u8 level_dbm) { /* since it can be assumed that at least the Maxim radio has a * maximum power output of 20dBm and since it also can be @@ -4615,18 +4493,18 @@ acx100_s_set_tx_level(wlandevice_t *priv const u8 *table; switch (priv->radio_type) { - case RADIO_MAXIM_0D: - table = &dbm2val_maxim[0]; - break; - case RADIO_RFMD_11: - case RADIO_RALINK_15: - table = &dbm2val_rfmd[0]; - break; - default: - printk("%s: unknown/unsupported radio type, " - "cannot modify tx power level yet!\n", - priv->netdev->name); - return NOT_OK; + case RADIO_MAXIM_0D: + table = &dbm2val_maxim[0]; + break; + case RADIO_RFMD_11: + case RADIO_RALINK_15: + table = &dbm2val_rfmd[0]; + break; + default: + printk("%s: unknown/unsupported radio type, " + "cannot modify tx power level yet!\n", + priv->netdev->name); + return NOT_OK; } printk("%s: changing radio power level to %u dBm (%u)\n", priv->netdev->name, level_dbm, table[level_dbm]); @@ -4635,18 +4513,75 @@ acx100_s_set_tx_level(wlandevice_t *priv } -/*---------------------------------------------------------------- -* acx_e_init_module -* -* Module initialization routine, called once at module load time. -* -* Returns: -* 0 - success -* ~0 - failure, module is unloaded. -* -* Call context: -* process thread (insmod or modprobe) -----------------------------------------------------------------*/ +/*********************************************************************** +** Data for init_module/cleanup_module +*/ +static const struct pci_device_id +acxpci_id_tbl[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_TI, + .device = PCI_DEVICE_ID_TI_TNETW1100A, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = CHIPTYPE_ACX100, + }, + { + .vendor = PCI_VENDOR_ID_TI, + .device = PCI_DEVICE_ID_TI_TNETW1100B, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = CHIPTYPE_ACX100, + }, + { + .vendor = PCI_VENDOR_ID_TI, + .device = PCI_DEVICE_ID_TI_TNETW1130, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = CHIPTYPE_ACX111, + }, + { + .vendor = 0, + .device = 0, + .subvendor = 0, + .subdevice = 0, + .driver_data = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, acxpci_id_tbl); + +/* FIXME: checks should be removed once driver is included in the kernel */ +#ifndef __devexit_p +#warning *** your kernel is EXTREMELY old since it does not even know about +#warning __devexit_p - this driver could easily FAIL to work, so better +#warning upgrade your kernel! *** +#define __devexit_p(x) x +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11) +/* pci_name() got introduced at start of 2.6.x, + * got mandatory (slot_name member removed) in 2.6.11-bk1 */ +#define pci_name(x) x->slot_name +#endif + +static struct pci_driver +acxpci_drv_id = { + .name = "acx_pci", + .id_table = acxpci_id_tbl, + .probe = acxpci_e_probe, + .remove = __devexit_p(acxpci_e_remove), +#ifdef CONFIG_PM + .suspend = acxpci_e_suspend, + .resume = acxpci_e_resume +#endif /* CONFIG_PM */ +}; + + +/*********************************************************************** +** acxpci_e_init_module +** +** Module initialization routine, called once at module load time +*/ int __init acxpci_e_init_module(void) { @@ -4671,34 +4606,31 @@ acxpci_e_init_module(void) acxlog(L_INIT, "PCI module " WLAN_RELEASE " initialized, " "waiting for cards to probe...\n"); - res = pci_module_init(&acx_pci_drv_id); + res = pci_module_init(&acxpci_drv_id); FN_EXIT1(res); return res; } -/*---------------------------------------------------------------- -* acx_e_cleanup_module -* -* Called at module unload time. This is our last chance to -* clean up after ourselves. -* -* Call context: -* process thread -----------------------------------------------------------------*/ +/*********************************************************************** +** acxpci_e_cleanup_module +** +** Called at module unload time. This is our last chance to +** clean up after ourselves. +*/ void __exit acxpci_e_cleanup_module(void) { - const struct net_device *dev; + struct net_device *dev; unsigned long flags; FN_ENTER; /* Since the whole module is about to be unloaded, * we recursively shutdown all cards we handled instead - * of doing it in remove_pci() (which will be activated by us + * of doing it in acxpci_e_remove() (which will be activated by us * via pci_unregister_driver at the end). - * remove_pci() might just get called after a card eject, + * acxpci_e_remove() might just get called after a card eject, * that's why hardware operations have to be done here instead * when the hardware is available. */ @@ -4707,7 +4639,7 @@ acxpci_e_cleanup_module(void) dev = root_acx_dev.newest; while (dev != NULL) { /* doh, netdev_priv() doesn't have const! */ - wlandevice_t *priv = acx_netdev_priv((struct net_device *)dev); + wlandevice_t *priv = netdev_priv(dev); acx_sem_lock(priv); @@ -4725,21 +4657,21 @@ acxpci_e_cleanup_module(void) /* disable power LED to save power :-) */ acxlog(L_INIT, "switching off power LED to save power :-)\n"); - acx_l_power_led(priv, 0); + acxpci_l_power_led(priv, 0); /* stop our eCPU */ if (IS_ACX111(priv)) { /* FIXME: does this actually keep halting the eCPU? * I don't think so... */ - acx_l_reset_mac(priv); + acxpci_l_reset_mac(priv); } else { u16 temp; /* halt eCPU */ - temp = acx_read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1; - acx_write_reg16(priv, IO_ACX_ECPU_CTRL, temp); - acx_write_flush(priv); + temp = read_reg16(priv, IO_ACX_ECPU_CTRL) | 0x1; + write_reg16(priv, IO_ACX_ECPU_CTRL, temp); + write_flush(priv); } acx_unlock(priv, flags); @@ -4752,8 +4684,8 @@ acxpci_e_cleanup_module(void) up(&root_acx_dev_sem); /* now let the PCI layer recursively remove - * all PCI related things (acx_e_remove_pci()) */ - pci_unregister_driver(&acx_pci_drv_id); + * all PCI related things (acxpci_e_remove()) */ + pci_unregister_driver(&acxpci_drv_id); FN_EXIT0; } diff -puN /dev/null drivers/net/wireless/tiacx/README --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/README 2005-10-17 13:06:00.000000000 -0700 @@ -0,0 +1,33 @@ +Contact: +acx100-devel@lists.sourceforge.net +acx100-users@lists.sourceforge.net + +Bug reports: + +Visit http://www.catb.org/~esr/faqs/smart-questions.html + +Please describe your wireless setup, manufacturer and type +(acx100/acx100usb/acx111?) of your hardware. Which firmware image(s) +are you using? If problem is reproducible, #define ACX_DEBUG 2 +in acx_config.h and modprobe driver with debug=0xffff. +Post resulting kernel log (bzipped). It is large but very useful +for bug hunting. Try older versions of the driver. + +Firmware images: + +You should not supply firmware_dir= parameter anymore. Driver will try +to load the following images via hotplug (not from /usr/share/acx +directory as older driver did, hotplug firmware directory location +varies for different distros, try /lib/firmware or +/usr/lib/hotplug/firmware): + +PCI driver: +'tiacxNNNcMM' (NNN=100/111, MM=radio module ID (in uppercase hex)): +combined firmware for specified chipset and radio. +Failing that, it will try to load images named 'tiacxNNN' +(main firmware for specified chipset) and 'tiacxNNNrMM' (corresponding +radio module). For example, my firmware is in file named 'tiacx111c16'. +Alternatively, I may remove it and use pair of files 'tiacx111' and +'tiacx111r16' instead. +USB driver: +image is named 'tiacx100usb' diff -puN drivers/net/wireless/tiacx/usb.c~acx-update-2 drivers/net/wireless/tiacx/usb.c --- devel/drivers/net/wireless/tiacx/usb.c~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/usb.c 2005-10-17 13:06:00.000000000 -0700 @@ -67,6 +67,9 @@ #include "acx.h" + +/*********************************************************************** +*/ /* number of endpoints of an interface */ #define NUM_EP(intf) (intf)->altsetting[0].desc.bNumEndpoints #define EP(intf, nr) (intf)->altsetting[0].endpoint[(nr)].desc @@ -74,20 +77,10 @@ #define PUT_DEV(udev) usb_put_dev((udev)) #define SET_NETDEV_OWNER(ndev, owner) /* not needed anymore ??? */ -#define ASYNC_UNLINK URB_ASYNC_UNLINK -#define QUEUE_BULK 0 -#define ZERO_PACKET URB_ZERO_PACKET - -static inline int -submit_urb(struct urb *urb, int mem_flags) -{ - return usb_submit_urb(urb, mem_flags); -} -static inline struct urb* -alloc_urb(int iso_pk, int mem_flags) -{ - return usb_alloc_urb(iso_pk, mem_flags); -} +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) +/* removed in 2.6.14. We will use fake value for now */ +#define URB_ASYNC_UNLINK 0 +#endif /*********************************************************************** @@ -117,10 +110,7 @@ static void acx100usb_i_set_rx_mode(stru static int acx100usb_e_init_network_device(struct net_device *); static int acx100usb_boot(struct usb_device *); -static struct net_device_stats * acx_e_get_stats(struct net_device *); -static struct iw_statistics *acx_e_get_wireless_stats(struct net_device *); - -static void acx100usb_l_poll_rx(wlandevice_t *, int number); +static void acx100usb_l_poll_rx(wlandevice_t *, usb_rx_t* rx); static void acx100usb_i_tx_timeout(struct net_device *); @@ -171,10 +161,17 @@ acx100usb_driver = { ** that the URB_ASYNC_UNLINK flag value be set in the urb that is being asked ** to be stopped in order to work properly. ** +** (URB_ASYNC_UNLINK is obsolete, usb_unlink_urb will always be +** asynchronous while usb_kill_urb is synchronous and should be called +** directly (drivers/usb/core/urb.c)) +** ** In light of this, timeout is just for paranoid reasons... +* +* Actually, it's useful for debugging. If we reach timeout, we're doing +* something wrong with the urbs. */ static void -acx_unlink_and_free_urb(struct urb* urb) +acxusb_unlink_urb(struct urb* urb) { if (!urb) return; @@ -186,33 +183,14 @@ acx_unlink_and_free_urb(struct urb* urb) while (--timeout && urb->status == -EINPROGRESS) { mdelay(1); } - /* if (!timeout) then what?? */ + if (!timeout) { + printk("acx_usb: urb unlink timeout!\n"); + } } - - /* just a refcounted kfree, safe undef lock */ - usb_free_urb(urb); } /*********************************************************************** -*/ -#if ACX_DEBUG -static char* -acx100usb_pstatus(int val) -{ - static char status[20]; - - if (val < 0) - sprintf(status, "errno %d", -val); - else - sprintf(status, "length %d", val); - - return status; -} -#endif /* ACX_DEBUG */ - - -/*********************************************************************** ** EEPROM and PHY read/write helpers */ /*********************************************************************** @@ -221,7 +199,6 @@ acx100usb_pstatus(int val) int acxusb_s_read_phy_reg(wlandevice_t *priv, u32 reg, u8 *charbuf) { - int result = NOT_OK; mem_read_write_t mem; FN_ENTER; @@ -229,14 +206,12 @@ acxusb_s_read_phy_reg(wlandevice_t *priv mem.addr = cpu_to_le16(reg); mem.type = cpu_to_le16(0x82); mem.len = cpu_to_le32(4); - acx_s_issue_cmd(priv, ACX1xx_CMD_MEM_READ, &mem, 8); + acx_s_issue_cmd(priv, ACX1xx_CMD_MEM_READ, &mem, sizeof(mem)); *charbuf = mem.data; - acxlog(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg); - result = OK; - goto fail; /* silence compiler warning */ -fail: - FN_EXIT1(result); - return result; + acxlog(L_DEBUG, "read radio PHY[0x%04X]=0x%02X\n", reg, *charbuf); + + FN_EXIT1(OK); + return OK; } @@ -254,7 +229,7 @@ acxusb_s_write_phy_reg(wlandevice_t *pri mem.len = cpu_to_le32(4); mem.data = value; acx_s_issue_cmd(priv, ACX1xx_CMD_MEM_WRITE, &mem, sizeof(mem)); - acxlog(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg); + acxlog(L_DEBUG, "write radio PHY[0x%04X]=0x%02X\n", reg, value); FN_EXIT1(OK); return OK; @@ -373,6 +348,12 @@ acxusb_s_issue_cmd_timeo_debug( blocklen, /* size */ USB_CTRL_HARD_TIMEOUT /* timeout in ms */ ); + + if (result == -ENODEV) { + acxlog(L_CTL, "no device present (unplug?)\n"); + goto good; + } + acxlog(L_CTL, "wrote %d bytes\n", result); if (result < 0) { goto bad; @@ -401,14 +382,16 @@ acxusb_s_issue_cmd_timeo_debug( } //check for result==buflen+4? Was seen: -//interrogate(type:ACX100_IE_DOT11_ED_THRESHOLD,len:4) -//issue_cmd(cmd:ACX1xx_CMD_INTERROGATE,buflen:8,type:4111) -//ctrl inpipe=0x80000280 outpipe=0x80000200 -//sending USB control msg (out) (blocklen=8) -//01 00 00 00 0F 10 04 00 -//wrote 8 bytes -//sending USB control msg (in) (acklen=12) sizeof(loc->data -//read 4 bytes <==== MUST BE 12!! +/* +interrogate(type:ACX100_IE_DOT11_ED_THRESHOLD,len:4) +issue_cmd(cmd:ACX1xx_CMD_INTERROGATE,buflen:8,type:4111) +ctrl inpipe=0x80000280 outpipe=0x80000200 +sending USB control msg (out) (blocklen=8) +01 00 00 00 0F 10 04 00 +wrote 8 bytes +sending USB control msg (in) (acklen=12) sizeof(loc->data +read 4 bytes <==== MUST BE 12!! +*/ cmd_status = le16_to_cpu(loc->status); if (cmd_status != 1) { @@ -422,11 +405,11 @@ acxusb_s_issue_cmd_timeo_debug( le16_to_cpu(loc->cmd), cmd_status); } +good: kfree(loc); FN_EXIT1(OK); return OK; bad: - kfree(loc); /* Give enough info so that callers can avoid ** printing their own diagnostic messages */ #if ACX_DEBUG @@ -435,6 +418,7 @@ bad: printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd); #endif dump_stack(); + kfree(loc); FN_EXIT1(NOT_OK); return NOT_OK; } @@ -459,6 +443,9 @@ bad: ** In case this driver is able to handle one of the offered devices, it returns ** a non-null pointer to a driver context and thereby claims the device. */ +static void +dummy_netdev_init(struct net_device *dev) {} + static int acx100usb_e_probe(struct usb_interface *intf, const struct usb_device_id *devID) { @@ -501,15 +488,18 @@ acx100usb_e_probe(struct usb_interface * /* Ok, so it's our device and it is already booted */ - /* allocate memory for the device driver context */ - priv = kmalloc(sizeof(struct wlandevice), GFP_KERNEL); - if (!priv) { - result = sizeof(struct wlandevice); - msg = "acx: could not allocate %d bytes " - "for device driver context\n"; + /* Allocate memory for a network device */ + dev = alloc_netdev(sizeof(wlandevice_t), "wlan%d", dummy_netdev_init); + /* (NB: memsets to 0 entire area) */ + if (!dev) { + msg = "acx: no memory for netdev\n"; goto end_nomem; } - memset(priv, 0, sizeof(wlandevice_t)); + dev->init = (void *)&acx100usb_e_init_network_device; + + /* Setup private driver context */ + priv = netdev_priv(dev); + priv->netdev = dev; priv->dev_type = DEVTYPE_USB; priv->chip_type = CHIPTYPE_ACX100; /* FIXME: should be read from register (via firmware) using standard ACX code */ @@ -572,53 +562,41 @@ acx100usb_e_probe(struct usb_interface * acxlog(L_DEBUG, "TXBUFSIZE=%d RXBUFSIZE=%d\n", (int) TXBUFSIZE, (int) RXBUFSIZE); - priv->usb_free_tx = ACX100_USB_NUM_BULK_URBS; - /* already done by memset: - for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { - priv->usb_tx[i].busy = 0; - } - */ + priv->tx_free = ACX100_USB_NUM_BULK_URBS; - /* Allocate memory for a network device */ - dev = kmalloc(sizeof(struct net_device), GFP_ATOMIC); - if (!dev) { - msg = "acx: no memory for netdev\n"; + /* Allocate the RX/TX containers. */ + priv->usb_tx = kmalloc(sizeof(usb_tx_t) * ACX100_USB_NUM_BULK_URBS, + GFP_KERNEL); + if (!priv->usb_tx) { + msg = "acx: no memory for tx container"; + goto end_nomem; + } + priv->usb_rx = kmalloc(sizeof(usb_rx_t) * ACX100_USB_NUM_BULK_URBS, + GFP_KERNEL); + if (!priv->usb_rx) { + msg = "acx: no memory for rx container"; goto end_nomem; } - - /* Setup network device */ - memset(dev, 0, sizeof(struct net_device)); - dev->init = (void *)&acx100usb_e_init_network_device; - dev->priv = priv; - priv->netdev = dev; /* Setup URBs for bulk-in/out messages */ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { - priv->bulkrx_urbs[i] = alloc_urb(0, GFP_KERNEL); - if (!priv->bulkrx_urbs[i]) { + priv->usb_rx[i].urb = usb_alloc_urb(0, GFP_KERNEL); + if (!priv->usb_rx[i].urb) { msg = "acx: no memory for input URB\n"; goto end_nomem; } - priv->bulkrx_urbs[i]->status = 0; + priv->usb_rx[i].urb->status = 0; + priv->usb_rx[i].priv = priv; - priv->usb_tx[i].urb = alloc_urb(0, GFP_KERNEL); + priv->usb_tx[i].urb = usb_alloc_urb(0, GFP_KERNEL); if (!priv->usb_tx[i].urb) { msg = "acx: no memory for output URB\n"; goto end_nomem; } priv->usb_tx[i].urb->status = 0; - priv->usb_tx[i].priv = priv; } - /* Allocate a network device name */ - acxlog(L_INIT, "allocating device name\n"); - result = dev_alloc_name(dev, "wlan%d"); - if (result < 0) { - msg = "acx: no memory for wlan device name\n"; - goto end_nomem; - } - usb_set_intfdata(intf, priv); SET_NETDEV_DEV(dev, &intf->dev); @@ -630,16 +608,15 @@ acx100usb_e_probe(struct usb_interface * "for USB WLAN (errcode=%d)\n"; goto end_nomem; } -#ifdef CONFIG_PROC_FS + if (OK != acx_proc_register_entries(dev)) { printk("acx: /proc registration failed\n"); } -#endif printk("acx: USB module " WLAN_RELEASE " loaded successfully\n"); #if CMD_DISCOVERY - great_inquisistor(priv); + great_inquisitor(priv); #endif /* Everything went OK, we are happy now */ @@ -649,15 +626,20 @@ acx100usb_e_probe(struct usb_interface * end_nomem: printk(msg, result); - /* we rely on the fact that free(NULL) is harmless */ - kfree(dev); - if (priv) { - for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { - usb_free_urb(priv->bulkrx_urbs[i]); - usb_free_urb(priv->usb_tx[i].urb); + if (dev) { + if (priv->usb_rx) { + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) + usb_free_urb(priv->usb_rx[i].urb); + kfree(priv->usb_rx); } - kfree(priv); + if (priv->usb_tx) { + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) + usb_free_urb(priv->usb_tx[i].urb); + kfree(priv->usb_tx); + } + free_netdev(dev); } + result = -ENOMEM; goto end; @@ -673,73 +655,61 @@ end: /*********************************************************************** -** acx100usb_e_disconnect(): -** Inputs: -** dev -> Pointer to usb_device structure handled by this module -** devContext -> Pointer to own device context (acx100usb_context) -************************************************************************ -** Description: -** This function is invoked whenever the user pulls the plug from the USB -** device or the module is removed from the kernel. In these cases, the -** network devices have to be taken down and all allocated memory has -** to be freed. +** acx100usb_e_disconnect() +** +** This function is invoked whenever the user pulls the plug from the USB +** device or the module is removed from the kernel. In these cases, the +** network devices have to be taken down and all allocated memory has +** to be freed. */ static void acx100usb_e_disconnect(struct usb_interface *intf) { wlandevice_t *priv = usb_get_intfdata(intf); - unsigned long flags; int i; + unsigned long flags; FN_ENTER; - /* No WLAN device...no sense */ + /* No WLAN device... no sense */ if (!priv) goto end; - /* - * We get the sem *after* FLUSH to avoid a deadlock. - * See pci.c:acx_s_down() for deails. + /* Unregister network device + * + * If the interface is up, unregister_netdev() will take + * care of calling our close() function, which takes + * care of unlinking the urbs, sending the device to + * sleep, etc... + * This can't be called with sem or lock held because + * _close() will try to grab it as well if it's called, + * deadlocking the machine. */ - FLUSH_SCHEDULED_WORK(); - acx_sem_lock(priv); + unregister_netdev(priv->netdev); + acx_sem_lock(priv); acx_lock(priv, flags); - - /* I wonder if above is enough to prevent tx/rx callbacks - ** to start queue again? Like this: - ** complete_rx -> acx_l_process_rxbuf -> associated -> acx_start_queue() - ** Oh well... */ - - /* This device exists no more. */ + /* This device exists no more */ usb_set_intfdata(intf, NULL); + acx_proc_unregister_entries(priv->netdev); - /* stop the transmit queue */ - if (priv->netdev) { - acx_stop_queue(priv->netdev, "on USB disconnect"); -#ifdef CONFIG_PROC_FS - acx_proc_unregister_entries(priv->netdev); -#endif + /* + * Here we only free them. _close() takes care of + * unlinking them. + */ + for (i = 0; i < ACX100_USB_NUM_BULK_URBS; ++i) { + usb_free_urb(priv->usb_rx[i].urb); + usb_free_urb(priv->usb_tx[i].urb); } - /* now abort pending URBs and free them */ - for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { - acx_unlink_and_free_urb(priv->bulkrx_urbs[i]); - acx_unlink_and_free_urb(priv->usb_tx[i].urb); - } + /* The the containers. */ + kfree(priv->usb_rx); + kfree(priv->usb_tx); acx_unlock(priv, flags); - - /* Unregister the network devices */ - if (priv->netdev) { - unregister_netdev(priv->netdev); - kfree(priv->netdev); - } - acx_sem_unlock(priv); - /* finally free the WLAN device */ - kfree(priv); + free_netdev(priv->netdev); end: FN_EXIT0; } @@ -815,14 +785,8 @@ acx100usb_boot(struct usb_device *usbdev ); offset += len; if (result < 0) { -#if ACX_DEBUG - printk(KERN_ERR "acx: error %d (%s) during upload " - "of firmware, aborting\n", result, - acx100usb_pstatus(result)); -#else printk(KERN_ERR "acx: error %d during upload " "of firmware, aborting\n", result); -#endif goto end; } } @@ -883,9 +847,10 @@ end: ** Basic setup of a network device for use with the WLAN device. */ static int -acx100usb_e_init_network_device(struct net_device *dev) { - int result = 0; +acx100usb_e_init_network_device(struct net_device *dev) +{ wlandevice_t *priv; + int result = 0; FN_ENTER; @@ -893,7 +858,7 @@ acx100usb_e_init_network_device(struct n ether_setup(dev); acx_stop_queue(dev, "on init"); - priv = dev->priv; + priv = netdev_priv(dev); acx_sem_lock(priv); @@ -916,6 +881,7 @@ acx100usb_e_init_network_device(struct n dev->tx_timeout = &acx100usb_i_tx_timeout; dev->watchdog_timeo = 4 * HZ; #endif + dev->change_mtu = &acx_e_change_mtu; result = acx_s_init_mac(dev); if (OK != result) goto end; @@ -943,7 +909,7 @@ end: static int acx100usb_e_open(struct net_device *dev) { - wlandevice_t *priv = acx_netdev_priv(dev); + wlandevice_t *priv = netdev_priv(dev); unsigned long flags; int i; @@ -960,7 +926,7 @@ acx100usb_e_open(struct net_device *dev) priv->mgmt_timer.function = acx_i_timer; priv->mgmt_timer.data = (unsigned long)priv; - /* set ifup to 1, since acx_start needs it */ + /* acx_s_start needs it */ SET_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP); acx_s_start(priv); @@ -968,7 +934,7 @@ acx100usb_e_open(struct net_device *dev) acx_lock(priv, flags); for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { - acx100usb_l_poll_rx(priv, i); + acx100usb_l_poll_rx(priv, &priv->usb_rx[i]); } acx_unlock(priv, flags); @@ -983,32 +949,23 @@ acx100usb_e_open(struct net_device *dev) /*********************************************************************** ** acx100usb_l_poll_rx -** This function initiates a bulk-in USB transfer (in case the interface -** is up). +** This function (re)initiates a bulk-in USB transfer on given urb */ static void -acx100usb_l_poll_rx(wlandevice_t *priv, int number) +acx100usb_l_poll_rx(wlandevice_t *priv, usb_rx_t* rx) { struct usb_device *usbdev; - rxbuffer_t *inbuf; - acx_usb_bulk_context_t *rxcon; struct urb *rxurb; - int errcode; + int errcode, rxnum; unsigned int inpipe; FN_ENTER; - if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) { - goto end; - } - - rxcon = &priv->rxcons[number]; - inbuf = &priv->bulkins[number]; - rxurb = priv->bulkrx_urbs[number]; + rxurb = rx->urb; usbdev = priv->usbdev; - rxcon->device = priv; - rxcon->number = number; + rxnum = rx - priv->usb_rx; + inpipe = usb_rcvbulkpipe(usbdev, priv->bulkinep); if (rxurb->status == -EINPROGRESS) { printk(KERN_ERR "acx: error, rx triggered while rx urb in progress\n"); @@ -1016,22 +973,24 @@ acx100usb_l_poll_rx(wlandevice_t *priv, * on the other hand, this should not happen anyway... */ usb_unlink_urb(rxurb); + } else if (rxurb->status == -ECONNRESET) { + acxlog(L_USBRXTX, "acx_usb: _poll_rx: connection reset\n"); + goto end; } rxurb->actual_length = 0; usb_fill_bulk_urb(rxurb, usbdev, inpipe, - inbuf, /* dataptr */ + &rx->bulkin, /* dataptr */ RXBUFSIZE, /* size */ acx100usb_i_complete_rx, /* handler */ - rxcon /* handler param */ + rx /* handler param */ ); - rxurb->transfer_flags = ASYNC_UNLINK|QUEUE_BULK; + rxurb->transfer_flags = URB_ASYNC_UNLINK; /* ATOMIC: we may be called from complete_rx() usb callback */ - errcode = submit_urb(rxurb, GFP_ATOMIC); + errcode = usb_submit_urb(rxurb, GFP_ATOMIC); /* FIXME: evaluate the error code! */ acxlog(L_USBRXTX, "SUBMIT RX (%d) inpipe=0x%X size=%d errcode=%d\n", - number, inpipe, (int) RXBUFSIZE, errcode); - + rxnum, inpipe, (int) RXBUFSIZE, errcode); end: FN_EXIT0; } @@ -1055,32 +1014,38 @@ acx100usb_i_complete_rx(struct urb *urb, wlandevice_t *priv; rxbuffer_t *ptr; rxbuffer_t *inbuf; + usb_rx_t *rx; unsigned long flags; - int size, number, remsize, packetsize; + int size, remsize, packetsize, rxnum; FN_ENTER; - if (!urb->context) { - printk(KERN_ERR "acx: error, urb context was NULL\n"); - goto end; /* at least try to prevent the worst */ - } + BUG_ON(!urb->context); - priv = ((acx_usb_bulk_context_t *)urb->context)->device; + rx = (usb_rx_t *)urb->context; + priv = rx->priv; acx_lock(priv, flags); - /* TODO: we maybe need to check whether urb was unlinked - ** (happens on disconnect and close, see there). How? */ + /* + * Happens on disconnect or close. Don't play with the urb. + * Don't resubmit it. It will get unlinked by close() + */ + if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) { + acxlog(L_USBRXTX, "not doing anything.\n"); + goto end_unlock; + } - number = ((acx_usb_bulk_context_t *)urb->context)->number; + inbuf = &rx->bulkin; size = urb->actual_length; remsize = size; + rxnum = rx - priv->usb_rx; acxlog(L_USBRXTX, "RETURN RX (%d) status=%d size=%d\n", - number, urb->status, size); + rxnum, urb->status, size); - inbuf = &priv->bulkins[number]; - ptr = inbuf; + if(size > sizeof(rxbuffer_t)) + printk("acx_usb: rx too large: %d bytes, lease report\n", size); /* check if the transfer was aborted */ switch (urb->status) { @@ -1091,7 +1056,9 @@ acx100usb_i_complete_rx(struct urb *urb, /* LOCKING BUG! acx100usb_e_close(priv->netdev); */ goto end_unlock; case -ECONNRESET: - goto do_poll_rx; + goto end_unlock; + case -ESHUTDOWN: /* rmmod */ + goto end_unlock; default: priv->stats.rx_errors++; printk("acx: rx error (urb status=%d)\n", urb->status); @@ -1108,11 +1075,12 @@ acx100usb_i_complete_rx(struct urb *urb, ** FIXME: this code can only handle truncation ** of consecutive packets! */ + ptr = inbuf; if (priv->rxtruncsize) { int tail_size; ptr = &priv->rxtruncbuf; - packetsize = RXBUF_BYTES_RCVD(ptr) + RXBUF_HDRSIZE; + packetsize = RXBUF_BYTES_USED(ptr); if (acx_debug & L_USBRXTX) { printk("handling truncated frame (truncsize=%d size=%d " "packetsize(from trunc)=%d)\n", @@ -1166,7 +1134,7 @@ acx100usb_i_complete_rx(struct urb *urb, remsize); break; } - packetsize = RXBUF_BYTES_RCVD(ptr) + RXBUF_HDRSIZE; + packetsize = RXBUF_BYTES_USED(ptr); acxlog(L_USBRXTX, "packet with packetsize=%d\n", packetsize); if (packetsize > sizeof(rxbuffer_t)) { printk("acx: packet exceeds max wlan " @@ -1215,15 +1183,11 @@ acx100usb_i_complete_rx(struct urb *urb, } do_poll_rx: - /* look for the next rx */ - if (priv->dev_state_mask & ACX_STATE_IFACE_UP) { - /* receive of frame completed, now look for the next one */ - acx100usb_l_poll_rx(priv, number); - } - + /* receive of frame completed, now look for the next one */ + acx100usb_l_poll_rx(priv, rx); end_unlock: acx_unlock(priv, flags); -end: +/* end: */ FN_EXIT0; } @@ -1255,31 +1219,41 @@ acx100usb_i_complete_tx(struct urb *urb, wlandevice_t *priv; usb_tx_t *tx; unsigned long flags; + int txnum; FN_ENTER; - if (!urb->context) { - printk(KERN_ERR "acx: error, NULL context in tx completion callback\n"); - /* FIXME: real error-handling code must go here! */ - goto end; - } + BUG_ON(!urb->context); tx = (usb_tx_t *)urb->context; priv = tx->priv; + txnum = tx - priv->usb_tx; + acx_lock(priv, flags); - /* TODO: we maybe need to check whether urb was unlinked - ** (happens on disconnect and close, see there). How? */ + /* + * If the iface isn't up, we don't have any right + * to play with them. The urb may get unlinked. + */ + if (!(priv->dev_state_mask & ACX_STATE_IFACE_UP)) { + acxlog(L_USBRXTX, "not doing anything\n"); + goto end_unlock; + /*FIXME: Do the priv->tx_free++? */ + } - acxlog(L_USBRXTX, "RETURN TX (%p): status=%d size=%d\n", - tx, urb->status, urb->actual_length); + acxlog(L_USBRXTX, "RETURN TX (%d): status=%d size=%d\n", + txnum, urb->status, urb->actual_length); /* handle USB transfer errors */ switch (urb->status) { case 0: /* No error */ break; + case -ESHUTDOWN: + goto end_unlock; + break; case -ECONNRESET: + goto end_unlock; break; /* FIXME: real error-handling code here please */ default: @@ -1288,18 +1262,18 @@ acx100usb_i_complete_tx(struct urb *urb, } /* free the URB and check for more data */ - priv->usb_free_tx++; + priv->tx_free++; tx->busy = 0; -/* end_unlock: */ +end_unlock: acx_unlock(priv, flags); -end: +/* end: */ FN_EXIT0; } /*********************************************************************** -** acx100usb_e_close(): +** acx100usb_e_close() ** ** This function stops the network functionality of the interface (invoked ** when the user calls ifconfig down). The tx queue is halted and @@ -1310,59 +1284,61 @@ end: static int acx100usb_e_close(struct net_device *dev) { - wlandevice_t *priv; + wlandevice_t *priv = netdev_priv(dev); unsigned long flags; int i; - priv = dev->priv; - if (!priv) { - printk(KERN_ERR "acx: dev->priv empty, FAILED\n"); - return -ENODEV; - } - FN_ENTER; -#if WE_STILL_DONT_CARE_ABOUT_IT +#ifdef WE_STILL_DONT_CARE_ABOUT_IT /* Transmit a disassociate frame */ lock acx_l_transmit_disassoc(priv, &client); unlock #endif + acx_sem_lock(priv); + + /* Make sure we don't get any more rx requests */ + acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0); + acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0); + /* - * We get the sem *after* FLUSH to avoid a deadlock. - * See pci.c:acx_s_down() for deails. + * We must do FLUSH *without* holding sem to avoid a deadlock. + * See pci.c:acxpci_s_down() for deails. */ + acx_sem_unlock(priv); FLUSH_SCHEDULED_WORK(); acx_sem_lock(priv); - /* stop the transmit queue, mark the device as DOWN */ + /* Power down the device */ + acx_s_issue_cmd(priv, ACX1xx_CMD_SLEEP, NULL, 0); + + /* Stop the transmit queue, mark the device as DOWN */ acx_lock(priv, flags); acx_stop_queue(dev, "on iface stop"); CLEAR_BIT(priv->dev_state_mask, ACX_STATE_IFACE_UP); - - /* I wonder if above is enough to prevent tx/rx callbacks - ** to start queue again? Like this: - ** complete_rx -> acx_l_process_rxbuf -> associated -> acx_start_queue() - ** Oh well... */ - /* stop pending rx/tx urb transfers */ + /* Make sure you don't free them! */ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { - acx_unlink_and_free_urb(priv->bulkrx_urbs[i]); - acx_unlink_and_free_urb(priv->usb_tx[i].urb); + acxusb_unlink_urb(priv->usb_rx[i].urb); + acxusb_unlink_urb(priv->usb_tx[i].urb); } - acx_unlock(priv, flags); - /* disable rx and tx */ - acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_TX, NULL, 0); - acx_s_issue_cmd(priv, ACX1xx_CMD_DISABLE_RX, NULL, 0); + del_timer_sync(&priv->mgmt_timer); - /* power down the device */ - acx_s_issue_cmd(priv, ACX1xx_CMD_SLEEP, NULL, 0); + priv->tx_free = ACX100_USB_NUM_BULK_URBS; + + for (i = 0; i< ACX100_USB_NUM_BULK_URBS; ++i) { + priv->usb_tx[i].busy = 0; + priv->usb_rx[i].busy = 0; + } + + acx_unlock(priv, flags); acx_sem_unlock(priv); - /* decrease module-in-use count (if necessary) */ + /* Decrease module-in-use count (if necessary) */ WLAN_MOD_DEC_USE_COUNT; @@ -1385,13 +1361,14 @@ acxusb_l_alloc_tx(wlandevice_t* priv) for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { if (!priv->usb_tx[i].busy) { + acxlog(L_USBRXTX, "allocated tx %d\n", i); tx = &priv->usb_tx[i]; tx->busy = 1; break; } } if (i >= ACX100_USB_NUM_BULK_URBS) { - printk("acx: tx buffers full\n"); + printk_ratelimited("acx: tx buffers full\n"); } FN_EXIT0; @@ -1403,7 +1380,7 @@ acxusb_l_alloc_tx(wlandevice_t* priv) /*************************************************************** */ void* -acxusb_l_get_txbuf(tx_t* tx_opaque) +acxusb_l_get_txbuf(wlandevice_t *priv, tx_t* tx_opaque) { usb_tx_t* tx = (usb_tx_t*)tx_opaque; return &tx->bulkout.data; @@ -1436,9 +1413,10 @@ acxusb_l_tx_data(wlandevice_t *priv, tx_ txbuf = &tx->bulkout; whdr = (wlan_hdr_t *)txbuf->data; - priv->usb_free_tx--; - acxlog(L_DEBUG, "using buf#%d free=%d len=%d\n", tx - priv->usb_tx, - priv->usb_free_tx, wlanpkt_len); + priv->tx_free--; + acxlog(L_DEBUG, "using buf#%d free=%d len=%d\n", + (int)(tx - priv->usb_tx), + priv->tx_free, wlanpkt_len); switch (priv->mode) { case ACX_MODE_0_ADHOC: @@ -1496,8 +1474,6 @@ acxusb_l_tx_data(wlandevice_t *priv, tx_ /* now schedule the USB transfer */ usbdev = priv->usbdev; outpipe = usb_sndbulkpipe(usbdev, priv->bulkoutep); -//can be removed, please try & test: - tx->priv = priv; usb_fill_bulk_urb(txurb, usbdev, outpipe, txbuf, /* dataptr */ @@ -1506,8 +1482,8 @@ acxusb_l_tx_data(wlandevice_t *priv, tx_ tx /* handler param */ ); - txurb->transfer_flags = ASYNC_UNLINK|QUEUE_BULK|ZERO_PACKET; - ucode = submit_urb(txurb, GFP_ATOMIC); + txurb->transfer_flags = URB_ASYNC_UNLINK|URB_ZERO_PACKET; + ucode = usb_submit_urb(txurb, GFP_ATOMIC); acxlog(L_USBRXTX, "SUBMIT TX (%p): outpipe=0x%X buf=%p txsize=%d " "errcode=%d\n", tx, outpipe, txbuf, wlanpkt_len + USB_TXBUF_HDRSIZE, ucode); @@ -1521,7 +1497,7 @@ acxusb_l_tx_data(wlandevice_t *priv, tx_ */ priv->stats.tx_errors++; tx->busy = 0; - priv->usb_free_tx++; + priv->tx_free++; } end: FN_EXIT0; @@ -1530,28 +1506,6 @@ end: /*********************************************************************** */ -static struct net_device_stats* -acx_e_get_stats(netdevice_t *dev) -{ - wlandevice_t *priv = acx_netdev_priv(dev); - return &priv->stats; -} - - -/*********************************************************************** -*/ -static struct iw_statistics* -acx_e_get_wireless_stats(netdevice_t *dev) -{ - wlandevice_t *priv = acx_netdev_priv(dev); - FN_ENTER; - FN_EXIT0; - return &priv->wstats; -} - - -/*********************************************************************** -*/ static void acx100usb_i_set_rx_mode(struct net_device *dev) { @@ -1564,19 +1518,16 @@ acx100usb_i_set_rx_mode(struct net_devic static void acx100usb_i_tx_timeout(struct net_device *dev) { - wlandevice_t *priv; + wlandevice_t *priv = netdev_priv(dev); unsigned long flags; int i; FN_ENTER; - priv = dev->priv; - acx_lock(priv, flags); /* unlink the URBs */ for (i = 0; i < ACX100_USB_NUM_BULK_URBS; i++) { - if (priv->usb_tx[i].urb->status == -EINPROGRESS) - usb_unlink_urb(priv->usb_tx[i].urb); + acxusb_unlink_urb(priv->usb_tx[i].urb); } /* TODO: stats update */ acx_unlock(priv, flags); diff -puN drivers/net/wireless/tiacx/wlan.c~acx-update-2 drivers/net/wireless/tiacx/wlan.c --- devel/drivers/net/wireless/tiacx/wlan.c~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/wlan.c 2005-10-17 13:06:00.000000000 -0700 @@ -67,7 +67,7 @@ ** ** Assumptions: ** 1) f->len and f->hdr are already set -** 2) f->len is the length of the MAC header + data, the CRC +** 2) f->len is the length of the MAC header + data, the FCS ** is NOT included ** 3) all members except len and hdr are zero ** Arguments: @@ -124,9 +124,17 @@ wlan_mgmt_decode_beacon(wlan_fr_beacon_t case WLAN_EID_ERP_INFO: f->erp = (wlan_ie_erp_t *) ie_ptr; break; + case WLAN_EID_COUNTRY: + /* was seen: 07 06 47 42 20 01 0D 14 */ case WLAN_EID_NONERP: /* was seen from WRT54GS with OpenWrt: 2F 01 07 */ - break; + case WLAN_EID_UNKNOWN128: + /* was seen by Jacek Jablonski from Orinoco AP */ + /* 80 06 00 60 1D 2C 3B 00 */ + case WLAN_EID_UNKNOWN133: + /* was seen by David Bronaugh from ???? */ + /* 85 1E 00 00 84 12 07 00 FF 00 11 00 61 70 63 31 */ + /* 63 73 72 30 34 32 00 00 00 00 00 00 00 00 00 25 */ case WLAN_EID_GENERIC: /* WPA: hostap code: if (pos[1] >= 4 && @@ -136,10 +144,9 @@ wlan_mgmt_decode_beacon(wlan_fr_beacon_t wpa_len = pos[1] + 2; } TI x4 mode: seen DD 04 08 00 28 00 - 08 00 28 is TI's OUI + (08 00 28 is TI's OUI) last byte is probably 0/1 - disabled/enabled */ - break; case WLAN_EID_RSN: /* hostap does something with it: rsn = pos; @@ -349,6 +356,11 @@ wlan_mgmt_decode_proberesp(wlan_fr_probe case WLAN_EID_IBSS_PARMS: f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr; break; +#ifdef DONT_DO_IT_ADD_REAL_HANDLING_INSTEAD + case WLAN_EID_COUNTRY: + break; + ... +#endif default: LOG_BAD_EID(f->hdr, f->len, ie_ptr); break; diff -puN drivers/net/wireless/tiacx/wlan_compat.h~acx-update-2 drivers/net/wireless/tiacx/wlan_compat.h --- devel/drivers/net/wireless/tiacx/wlan_compat.h~acx-update-2 2005-10-17 13:05:59.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/wlan_compat.h 2005-10-17 13:06:00.000000000 -0700 @@ -195,39 +195,6 @@ /*=============================================================*/ /*------ Bit settings -----------------------------------------*/ /*=============================================================*/ -#define BIT0 0x00000001 -#define BIT1 0x00000002 -#define BIT2 0x00000004 -#define BIT3 0x00000008 -#define BIT4 0x00000010 -#define BIT5 0x00000020 -#define BIT6 0x00000040 -#define BIT7 0x00000080 -#define BIT8 0x00000100 -#define BIT9 0x00000200 -#define BIT10 0x00000400 -#define BIT11 0x00000800 -#define BIT12 0x00001000 -#define BIT13 0x00002000 -#define BIT14 0x00004000 -#define BIT15 0x00008000 -#define BIT16 0x00010000 -#define BIT17 0x00020000 -#define BIT18 0x00040000 -#define BIT19 0x00080000 -#define BIT20 0x00100000 -#define BIT21 0x00200000 -#define BIT22 0x00400000 -#define BIT23 0x00800000 -#define BIT24 0x01000000 -#define BIT25 0x02000000 -#define BIT26 0x04000000 -#define BIT27 0x08000000 -#define BIT28 0x10000000 -#define BIT29 0x20000000 -#define BIT30 0x40000000 -#define BIT31 0x80000000 - #define ieee2host16(n) __le16_to_cpu(n) #define ieee2host32(n) __le32_to_cpu(n) #define host2ieee16(n) __cpu_to_le16(n) @@ -295,16 +262,15 @@ typedef void irqreturn_t; /*============================================================================* * Constants * *============================================================================*/ - #define WLAN_IEEE_OUI_LEN 3 - +/* unused +#define WLAN_ETHHDR_LEN 14 #define WLAN_ETHCONV_ENCAP 1 #define WLAN_ETHCONV_RFC1042 2 #define WLAN_ETHCONV_8021h 3 - #define WLAN_MIN_ETHFRM_LEN 60 #define WLAN_MAX_ETHFRM_LEN 1514 -#define WLAN_ETHHDR_LEN 14 +*/ /*============================================================================* * Types * diff -puN drivers/net/wireless/tiacx/wlan_hdr.h~acx-update-2 drivers/net/wireless/tiacx/wlan_hdr.h --- devel/drivers/net/wireless/tiacx/wlan_hdr.h~acx-update-2 2005-10-17 13:06:00.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/wlan_hdr.h 2005-10-17 13:06:00.000000000 -0700 @@ -150,18 +150,28 @@ Not shown here. ** Constants */ -/*--- Sizes -----------------------------------------------*/ -#define WLAN_CRC_LEN 4 -#define WLAN_BSS_TS_LEN 8 #define WLAN_HDR_A3_LEN 24 #define WLAN_HDR_A4_LEN 30 -#define WLAN_SSID_MAXLEN 32 -#define WLAN_DATA_MAXLEN 2312 -/* NB: these are sizes of unencrypted frames. -** WEPed frames also have IV in front of encrypted data and ICV after it +/* IV structure: +** 3 bytes: Initialization Vector (24 bits) +** 1 byte: 0..5: padding, must be 0; 6..7: key selector (0-3) */ -#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN) -#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN) +#define WLAN_WEP_IV_LEN 4 +/* 802.11 says 2312 but looks like 2312 is a max size of _WEPed data_ */ +#define WLAN_DATA_MAXLEN 2304 +#define WLAN_WEP_ICV_LEN 4 +#define WLAN_FCS_LEN 4 +#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN) +#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN) +#define WLAN_A3FR_MAXLEN_FCS (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + 4) +#define WLAN_A4FR_MAXLEN_FCS (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + 4) +#define WLAN_A3FR_MAXLEN_WEP (WLAN_A3FR_MAXLEN + 8) +#define WLAN_A4FR_MAXLEN_WEP (WLAN_A4FR_MAXLEN + 8) +#define WLAN_A3FR_MAXLEN_WEP_FCS (WLAN_A3FR_MAXLEN_FCS + 8) +#define WLAN_A4FR_MAXLEN_WEP_FCS (WLAN_A4FR_MAXLEN_FCS + 8) + +#define WLAN_BSS_TS_LEN 8 +#define WLAN_SSID_MAXLEN 32 #define WLAN_BEACON_FR_MAXLEN (WLAN_HDR_A3_LEN + 334) #define WLAN_ATIM_FR_MAXLEN (WLAN_HDR_A3_LEN + 0) #define WLAN_DISASSOC_FR_MAXLEN (WLAN_HDR_A3_LEN + 2) @@ -173,18 +183,10 @@ Not shown here. #define WLAN_PROBERESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 78) #define WLAN_AUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 261) #define WLAN_DEAUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 2) -#define WLAN_WEP_NKEYS 4 -#define WLAN_WEP_MAXKEYLEN 13 #define WLAN_CHALLENGE_IE_LEN 130 #define WLAN_CHALLENGE_LEN 128 -/* IV structure: -** 3 bytes: Initialization Vector (24 bits) -** 1 byte: 0..5: padding, must be 0; 6..7: key selector (0-3) -*/ -#define WLAN_WEP_IV_LEN 4 -#define WLAN_WEP_ICV_LEN 4 -#define WLAN_A3FR_MAXLEN_WEP (WLAN_A3FR_MAXLEN + 8) -#define WLAN_A4FR_MAXLEN_WEP (WLAN_A4FR_MAXLEN + 8) +#define WLAN_WEP_MAXKEYLEN 13 +#define WLAN_WEP_NKEYS 4 /*--- Frame Control Field -------------------------------------*/ /* Frame Types */ @@ -312,8 +314,8 @@ IEEE16(WF_FSTYPE_CFACK_CFPOLL, 0x70) /* Macros to get/set the bitfields of the Sequence Control */ /* Field. */ /*------------------------------------------------------------*/ -#define WLAN_GET_SEQ_FRGNUM(n) (((u16)(n)) & (BIT0|BIT1|BIT2|BIT3)) -#define WLAN_GET_SEQ_SEQNUM(n) ((((u16)(n)) & (~(BIT0|BIT1|BIT2|BIT3))) >> 4) +#define WLAN_GET_SEQ_FRGNUM(n) ((u16)(n) & 0x000f) +#define WLAN_GET_SEQ_SEQNUM(n) (((u16)(n) & 0xfff0) >> 4) /*--- Data ptr macro -----------------------------------------*/ /* Creates a u8* to the data portion of a frame */ @@ -327,9 +329,6 @@ IEEE16(WF_FSTYPE_CFACK_CFPOLL, 0x70) ** Types */ -/* BSS Timestamp */ -typedef u8 wlan_bss_ts_t[WLAN_BSS_TS_LEN]; - /* 802.11 header type ** ** Note the following: diff -puN drivers/net/wireless/tiacx/wlan_mgmt.h~acx-update-2 drivers/net/wireless/tiacx/wlan_mgmt.h --- devel/drivers/net/wireless/tiacx/wlan_mgmt.h~acx-update-2 2005-10-17 13:06:00.000000000 -0700 +++ devel-akpm/drivers/net/wireless/tiacx/wlan_mgmt.h 2005-10-17 13:06:00.000000000 -0700 @@ -60,6 +60,8 @@ #define WLAN_EID_NONERP 47 /* was seen from WRT54GS with OpenWrt */ #define WLAN_EID_RSN 48 #define WLAN_EID_EXT_RATES 50 +#define WLAN_EID_UNKNOWN128 128 +#define WLAN_EID_UNKNOWN133 133 #define WLAN_EID_GENERIC 221 #if 0 @@ -281,7 +283,7 @@ typedef struct wlan_ie_erp { /* prototype structure, all mgmt frame types will start with these members */ typedef struct wlan_fr_mgmt { u16 type; - u16 len; /* DOES NOT include CRC */ + u16 len; /* DOES NOT include FCS */ wlan_hdr_t *hdr; /* used for target specific data, skb in Linux */ /*-- fixed fields -----------*/ _