📄 iwl4965-base.c
字号:
/****************************************************************************** * * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * James P. Ketrenos <ipw2100-admin@linux.intel.com> * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************//* * NOTE: This file (iwl-base.c) is used to build to multiple hardware targets * by defining IWL to either 3945 or 4965. The Makefile used when building * the base targets will create base-3945.o and base-4965.o * * The eventual goal is to move as many of the #if IWL / #endif blocks out of * this file and into the hardware specific implementation files (iwl-XXXX.c) * and leave only the common (non #ifdef sprinkled) code in this file */#include <linux/kernel.h>#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/dma-mapping.h>#include <linux/delay.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/wireless.h>#include <linux/firmware.h>#include <linux/etherdevice.h>#include <linux/if_arp.h>#include <net/ieee80211_radiotap.h>#include <net/mac80211.h>#include <asm/div64.h>#define IWL 4965#include "iwlwifi.h"#include "iwl-4965.h"#include "iwl-helpers.h"#ifdef CONFIG_IWLWIFI_DEBUGu32 iwl_debug_level;#endif/****************************************************************************** * * module boiler plate * ******************************************************************************//* module parameters */int iwl_param_disable_hw_scan;int iwl_param_debug;int iwl_param_disable; /* def: enable radio */int iwl_param_antenna; /* def: 0 = both antennas (use diversity) */int iwl_param_hwcrypto; /* def: using software encryption */int iwl_param_qos_enable = 1;int iwl_param_queues_num = IWL_MAX_NUM_QUEUES;/* * module name, copyright, version, etc. * NOTE: DRV_NAME is defined in iwlwifi.h for use by iwl-debug.h and printk */#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link 4965AGN driver for Linux"#ifdef CONFIG_IWLWIFI_DEBUG#define VD "d"#else#define VD#endif#ifdef CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT#define VS "s"#else#define VS#endif#define IWLWIFI_VERSION "1.1.17k" VD VS#define DRV_COPYRIGHT "Copyright(c) 2003-2007 Intel Corporation"#define DRV_VERSION IWLWIFI_VERSION/* Change firmware file name, using "-" and incrementing number, * *only* when uCode interface or architecture changes so that it * is not compatible with earlier drivers. * This number will also appear in << 8 position of 1st dword of uCode file */#define IWL4965_UCODE_API "-1"MODULE_DESCRIPTION(DRV_DESCRIPTION);MODULE_VERSION(DRV_VERSION);MODULE_AUTHOR(DRV_COPYRIGHT);MODULE_LICENSE("GPL");__le16 *ieee80211_get_qos_ctrl(struct ieee80211_hdr *hdr){ u16 fc = le16_to_cpu(hdr->frame_control); int hdr_len = ieee80211_get_hdrlen(fc); if ((fc & 0x00cc) == (IEEE80211_STYPE_QOS_DATA | IEEE80211_FTYPE_DATA)) return (__le16 *) ((u8 *) hdr + hdr_len - QOS_CONTROL_LEN); return NULL;}static const struct ieee80211_hw_mode *iwl_get_hw_mode( struct iwl_priv *priv, int mode){ int i; for (i = 0; i < 3; i++) if (priv->modes[i].mode == mode) return &priv->modes[i]; return NULL;}static int iwl_is_empty_essid(const char *essid, int essid_len){ /* Single white space is for Linksys APs */ if (essid_len == 1 && essid[0] == ' ') return 1; /* Otherwise, if the entire essid is 0, we assume it is hidden */ while (essid_len) { essid_len--; if (essid[essid_len] != '\0') return 0; } return 1;}static const char *iwl_escape_essid(const char *essid, u8 essid_len){ static char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; const char *s = essid; char *d = escaped; if (iwl_is_empty_essid(essid, essid_len)) { memcpy(escaped, "<hidden>", sizeof("<hidden>")); return escaped; } essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE); while (essid_len--) { if (*s == '\0') { *d++ = '\\'; *d++ = '0'; s++; } else *d++ = *s++; } *d = '\0'; return escaped;}static void iwl_print_hex_dump(int level, void *p, u32 len){#ifdef CONFIG_IWLWIFI_DEBUG if (!(iwl_debug_level & level)) return; print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1, p, len, 1);#endif}/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** * DMA services * * Theory of operation * * A queue is a circular buffers with 'Read' and 'Write' pointers. * 2 empty entries always kept in the buffer to protect from overflow. * * For Tx queue, there are low mark and high mark limits. If, after queuing * the packet for Tx, free space become < low mark, Tx queue stopped. When * reclaiming packets (on 'tx done IRQ), if free space become > high mark, * Tx queue resumed. * * The IWL operates with six queues, one receive queue in the device's * sram, one transmit queue for sending commands to the device firmware, * and four transmit queues for data. ***************************************************/static int iwl_queue_space(const struct iwl_queue *q){ int s = q->last_used - q->first_empty; if (q->last_used > q->first_empty) s -= q->n_bd; if (s <= 0) s += q->n_window; /* keep some reserve to not confuse empty and full situations */ s -= 2; if (s < 0) s = 0; return s;}/* XXX: n_bd must be power-of-two size */static inline int iwl_queue_inc_wrap(int index, int n_bd){ return ++index & (n_bd - 1);}/* XXX: n_bd must be power-of-two size */static inline int iwl_queue_dec_wrap(int index, int n_bd){ return --index & (n_bd - 1);}static inline int x2_queue_used(const struct iwl_queue *q, int i){ return q->first_empty > q->last_used ? (i >= q->last_used && i < q->first_empty) : !(i < q->last_used && i >= q->first_empty);}static inline u8 get_cmd_index(struct iwl_queue *q, u32 index, int is_huge){ if (is_huge) return q->n_window; return index & (q->n_window - 1);}static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q, int count, int slots_num, u32 id){ q->n_bd = count; q->n_window = slots_num; q->id = id; /* count must be power-of-two size, otherwise iwl_queue_inc_wrap * and iwl_queue_dec_wrap are broken. */ BUG_ON(!is_power_of_2(count)); /* slots_num must be power-of-two size, otherwise * get_cmd_index is broken. */ BUG_ON(!is_power_of_2(slots_num)); q->low_mark = q->n_window / 4; if (q->low_mark < 4) q->low_mark = 4; q->high_mark = q->n_window / 8; if (q->high_mark < 2) q->high_mark = 2; q->first_empty = q->last_used = 0; return 0;}static int iwl_tx_queue_alloc(struct iwl_priv *priv, struct iwl_tx_queue *txq, u32 id){ struct pci_dev *dev = priv->pci_dev; if (id != IWL_CMD_QUEUE_NUM) { txq->txb = kmalloc(sizeof(txq->txb[0]) * TFD_QUEUE_SIZE_MAX, GFP_KERNEL); if (!txq->txb) { IWL_ERROR("kmalloc for auxilary BD " "structures failed\n"); goto error; } } else txq->txb = NULL; txq->bd = pci_alloc_consistent(dev, sizeof(txq->bd[0]) * TFD_QUEUE_SIZE_MAX, &txq->q.dma_addr); if (!txq->bd) { IWL_ERROR("pci_alloc_consistent(%zd) failed\n", sizeof(txq->bd[0]) * TFD_QUEUE_SIZE_MAX); goto error; } txq->q.id = id; return 0; error: if (txq->txb) { kfree(txq->txb); txq->txb = NULL; } return -ENOMEM;}int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, int slots_num, u32 txq_id){ struct pci_dev *dev = priv->pci_dev; int len; int rc = 0; /* alocate command space + one big command for scan since scan * command is very huge the system will not have two scan at the * same time */ len = sizeof(struct iwl_cmd) * slots_num; if (txq_id == IWL_CMD_QUEUE_NUM) len += IWL_MAX_SCAN_SIZE; txq->cmd = pci_alloc_consistent(dev, len, &txq->dma_addr_cmd); if (!txq->cmd) return -ENOMEM; rc = iwl_tx_queue_alloc(priv, txq, txq_id); if (rc) { pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd); return -ENOMEM; } txq->need_update = 0; /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id); iwl_hw_tx_queue_init(priv, txq); return 0;}/** * iwl_tx_queue_free - Deallocate DMA queue. * @txq: Transmit queue to deallocate. * * Empty queue by removing and destroying all BD's. * Free all buffers. txq itself is not freed. * */void iwl_tx_queue_free(struct iwl_priv *priv, struct iwl_tx_queue *txq){ struct iwl_queue *q = &txq->q; struct pci_dev *dev = priv->pci_dev; int len; if (q->n_bd == 0) return; /* first, empty all BD's */ for (; q->first_empty != q->last_used; q->last_used = iwl_queue_inc_wrap(q->last_used, q->n_bd)) iwl_hw_txq_free_tfd(priv, txq); len = sizeof(struct iwl_cmd) * q->n_window; if (q->id == IWL_CMD_QUEUE_NUM) len += IWL_MAX_SCAN_SIZE; pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd); /* free buffers belonging to queue itself */ if (txq->q.n_bd) pci_free_consistent(dev, sizeof(struct iwl_tfd_frame) * txq->q.n_bd, txq->bd, txq->q.dma_addr); if (txq->txb) { kfree(txq->txb); txq->txb = NULL; } /* 0 fill whole structure */ memset(txq, 0, sizeof(*txq));}const u8 BROADCAST_ADDR[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };/*************** STATION TABLE MANAGEMENT **** * * NOTE: This needs to be overhauled to better synchronize between * how the iwl-4965.c is using iwl_hw_find_station vs. iwl-3945.c * * mac80211 should also be examined to determine if sta_info is duplicating * the functionality provided here *//**************************************************************/#if 0 /* temparary disable till we add real remove station */static u8 iwl_remove_station(struct iwl_priv *priv, const u8 *addr, int is_ap){ int index = IWL_INVALID_STATION; int i; unsigned long flags; spin_lock_irqsave(&priv->sta_lock, flags); if (is_ap) index = IWL_AP_ID; else if (is_broadcast_ether_addr(addr)) index = priv->hw_setting.bcast_sta_id; else for (i = IWL_STA_ID; i < priv->hw_setting.max_stations; i++) if (priv->stations[i].used && !compare_ether_addr(priv->stations[i].sta.sta.addr, addr)) { index = i; break; } if (unlikely(index == IWL_INVALID_STATION)) goto out; if (priv->stations[index].used) { priv->stations[index].used = 0; priv->num_stations--; } BUG_ON(priv->num_stations < 0);out: spin_unlock_irqrestore(&priv->sta_lock, flags); return 0;}#endifstatic void iwl_clear_stations_table(struct iwl_priv *priv){ unsigned long flags; spin_lock_irqsave(&priv->sta_lock, flags); priv->num_stations = 0; memset(priv->stations, 0, sizeof(priv->stations)); spin_unlock_irqrestore(&priv->sta_lock, flags);}u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, int is_ap, u8 flags){ int i; int index = IWL_INVALID_STATION; struct iwl_station_entry *station; unsigned long flags_spin; DECLARE_MAC_BUF(mac); spin_lock_irqsave(&priv->sta_lock, flags_spin); if (is_ap) index = IWL_AP_ID; else if (is_broadcast_ether_addr(addr)) index = priv->hw_setting.bcast_sta_id; else for (i = IWL_STA_ID; i < priv->hw_setting.max_stations; i++) { if (!compare_ether_addr(priv->stations[i].sta.sta.addr, addr)) { index = i; break; } if (!priv->stations[i].used && index == IWL_INVALID_STATION) index = i; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -