📄 ipw2200.c
字号:
/****************************************************************************** Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved. 802.11 status code portion of this file from ethereal-0.10.6: Copyright 2000, Axis Communications AB Ethereal - Network traffic analyzer By Gerald Combs <gerald@ethereal.com> Copyright 1998 Gerald Combs 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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******************************************************************************/#include "ipw2200.h"#define IPW2200_VERSION "1.0.0"#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"#define DRV_COPYRIGHT "Copyright(c) 2003-2004 Intel Corporation"#define DRV_VERSION IPW2200_VERSIONMODULE_DESCRIPTION(DRV_DESCRIPTION);MODULE_VERSION(DRV_VERSION);MODULE_AUTHOR(DRV_COPYRIGHT);MODULE_LICENSE("GPL");static int debug = 0;static int channel = 0;static char *ifname;static int mode = 0;static u32 ipw_debug_level;static int associate = 1;static int auto_create = 1;static int disable = 0;static const char ipw_modes[] = { 'a', 'b', 'g', '?'};static void ipw_rx(struct ipw_priv *priv);static int ipw_queue_tx_reclaim(struct ipw_priv *priv, struct clx2_tx_queue *txq, int qindex);static int ipw_queue_reset(struct ipw_priv *priv);static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf, int len, int sync);static void ipw_tx_queue_free(struct ipw_priv *);static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);static void ipw_rx_queue_replenish(void *);static int ipw_up(struct ipw_priv *);static void ipw_down(struct ipw_priv *);static int ipw_config(struct ipw_priv *);static int init_supported_rates(struct ipw_priv *priv, struct ipw_supported_rates *prates);static u8 band_b_active_channel[MAX_B_CHANNELS] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0};static u8 band_a_active_channel[MAX_A_CHANNELS] = { 36, 40, 44, 48, 149, 153, 157, 161, 165, 52, 56, 60, 64, 0};static int is_valid_channel(int mode_mask, int channel){ int i; if (!channel) return 0; if (mode_mask & IEEE_A) for (i = 0; i < MAX_A_CHANNELS; i++) if (band_a_active_channel[i] == channel) return IEEE_A; if (mode_mask & (IEEE_B | IEEE_G)) for (i = 0; i < MAX_B_CHANNELS; i++) if (band_b_active_channel[i] == channel) return mode_mask & (IEEE_B | IEEE_G); return 0;}static char *snprint_line(char *buf, size_t count, const u8 * data, u32 len, u32 ofs){ int out, i, j, l; char c; out = snprintf(buf, count, "%08X", ofs); for (l = 0, i = 0; i < 2; i++) { out += snprintf(buf + out, count - out, " "); for (j = 0; j < 8 && l < len; j++, l++) out += snprintf(buf + out, count - out, "%02X ", data[(i * 8 + j)]); for (; j < 8; j++) out += snprintf(buf + out, count - out, " "); } out += snprintf(buf + out, count - out, " "); for (l = 0, i = 0; i < 2; i++) { out += snprintf(buf + out, count - out, " "); for (j = 0; j < 8 && l < len; j++, l++) { c = data[(i * 8 + j)]; if (!isascii(c) || !isprint(c)) c = '.'; out += snprintf(buf + out, count - out, "%c", c); } for (; j < 8; j++) out += snprintf(buf + out, count - out, " "); } return buf;}static void printk_buf(int level, const u8 * data, u32 len){ char line[81]; u32 ofs = 0; if (!(ipw_debug_level & level)) return; while (len) { printk(KERN_DEBUG "%s\n", snprint_line(line, sizeof(line), &data[ofs], min(len, 16U), ofs)); ofs += 16; len -= min(len, 16U); }}static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c){ IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32) (b), (u32) (c)); _ipw_write_reg8(a, b, c);}static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c){ IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32) (b), (u32) (c)); _ipw_write_reg16(a, b, c);}static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c){ IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32) (b), (u32) (c)); _ipw_write_reg32(a, b, c);}#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))#define ipw_write8(ipw, ofs, val) \ IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \ _ipw_write8(ipw, ofs, val)#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))#define ipw_write16(ipw, ofs, val) \ IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \ _ipw_write16(ipw, ofs, val)#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))#define ipw_write32(ipw, ofs, val) \ IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \ _ipw_write32(ipw, ofs, val)#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs){ IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32) (ofs)); return _ipw_read8(ipw, ofs);}#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs){ IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32) (ofs)); return _ipw_read16(ipw, ofs);}#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs){ IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32) (ofs)); return _ipw_read32(ipw, ofs);}#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);#define ipw_read_indirect(a, b, c, d) \ IPW_DEBUG_IO("%s %d: read_inddirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \ _ipw_read_indirect(a, b, c, d)static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data, int num);#define ipw_write_indirect(a, b, c, d) \ IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \ _ipw_write_indirect(a, b, c, d)/* indirect write s */static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value){ IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value); _ipw_write32(priv, CX2_INDIRECT_ADDR, reg); _ipw_write32(priv, CX2_INDIRECT_DATA, value);}static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value){ IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value); _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK); _ipw_write8(priv, CX2_INDIRECT_DATA, value); IPW_DEBUG_IO(" reg = 0x%8lX : value = 0x%8X\n", (unsigned long)(priv->hw_base + CX2_INDIRECT_DATA), value);}static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value){ IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value); _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK); _ipw_write16(priv, CX2_INDIRECT_DATA, value);}/* indirect read s */static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg){ u32 word; _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK); IPW_DEBUG_IO(" reg = 0x%8X : \n", reg); word = _ipw_read32(priv, CX2_INDIRECT_DATA); return (word >> ((reg & 0x3) * 8)) & 0xff;}static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg){ u32 value; IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg); _ipw_write32(priv, CX2_INDIRECT_ADDR, reg); value = _ipw_read32(priv, CX2_INDIRECT_DATA); IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value); return value;}/* iterative/auto-increment 32 bit reads and writes */static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf, int num){ u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK; u32 dif_len = addr - aligned_addr; u32 aligned_len; u32 i; IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num); /* Read the first nibble byte by byte */ if (unlikely(dif_len)) { /* Start reading at aligned_addr + dif_len */ _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr); for (i = dif_len; i < 4; i++, buf++) *buf = _ipw_read8(priv, CX2_INDIRECT_DATA + i); num -= dif_len; aligned_addr += 4; } /* Read DWs through autoinc register */ _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr); aligned_len = num & CX2_INDIRECT_ADDR_MASK; for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4) *(u32 *) buf = ipw_read32(priv, CX2_AUTOINC_DATA); /* Copy the last nibble */ dif_len = num - aligned_len; _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr); for (i = 0; i < dif_len; i++, buf++) *buf = ipw_read8(priv, CX2_INDIRECT_DATA + i);}static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf, int num){ u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK; u32 dif_len = addr - aligned_addr; u32 aligned_len; u32 i; IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num); /* Write the first nibble byte by byte */ if (unlikely(dif_len)) { /* Start writing at aligned_addr + dif_len */ _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr); for (i = dif_len; i < 4; i++, buf++) _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf); num -= dif_len; aligned_addr += 4; } /* Write DWs through autoinc register */ _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr); aligned_len = num & CX2_INDIRECT_ADDR_MASK; for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4) _ipw_write32(priv, CX2_AUTOINC_DATA, *(u32 *) buf); /* Copy the last nibble */ dif_len = num - aligned_len; _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr); for (i = 0; i < dif_len; i++, buf++) _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf);}static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf, int num){ memcpy_toio((priv->hw_base + addr), buf, num);}static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask){ ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);}static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask){ ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);}static inline void ipw_enable_interrupts(struct ipw_priv *priv){ if (priv->status & STATUS_INT_ENABLED) return; priv->status |= STATUS_INT_ENABLED; ipw_write32(priv, CX2_INTA_MASK_R, CX2_INTA_MASK_ALL);}static inline void ipw_disable_interrupts(struct ipw_priv *priv){ if (!(priv->status & STATUS_INT_ENABLED)) return; priv->status &= ~STATUS_INT_ENABLED; ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL);}static char *ipw_error_desc(u32 val){ switch (val) { case IPW_FW_ERROR_OK: return "ERROR_OK"; case IPW_FW_ERROR_FAIL: return "ERROR_FAIL"; case IPW_FW_ERROR_MEMORY_UNDERFLOW: return "MEMORY_UNDERFLOW"; case IPW_FW_ERROR_MEMORY_OVERFLOW: return "MEMORY_OVERFLOW"; case IPW_FW_ERROR_BAD_PARAM: return "ERROR_BAD_PARAM"; case IPW_FW_ERROR_BAD_CHECKSUM: return "ERROR_BAD_CHECKSUM"; case IPW_FW_ERROR_NMI_INTERRUPT: return "ERROR_NMI_INTERRUPT"; case IPW_FW_ERROR_BAD_DATABASE: return "ERROR_BAD_DATABASE"; case IPW_FW_ERROR_ALLOC_FAIL: return "ERROR_ALLOC_FAIL"; case IPW_FW_ERROR_DMA_UNDERRUN: return "ERROR_DMA_UNDERRUN"; case IPW_FW_ERROR_DMA_STATUS: return "ERROR_DMA_STATUS"; case IPW_FW_ERROR_DINOSTATUS_ERROR: return "ERROR_DINOSTATUS_ERROR"; case IPW_FW_ERROR_EEPROMSTATUS_ERROR: return "ERROR_EEPROMSTATUS_ERROR"; case IPW_FW_ERROR_SYSASSERT: return "ERROR_SYSASSERT"; case IPW_FW_ERROR_FATAL_ERROR: return "ERROR_FATALSTATUS_ERROR"; default: return "UNKNOWNSTATUS_ERROR"; }}static void ipw_dump_nic_error_log(struct ipw_priv *priv){ u32 desc, time, blink1, blink2, ilink1, ilink2, idata, i, count, base; base = ipw_read32(priv, IPWSTATUS_ERROR_LOG); count = ipw_read_reg32(priv, base); if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { IPW_ERROR("Start IPW Error Log Dump:\n"); IPW_ERROR("Status: 0x%08X, Config: %08X\n", priv->status, priv->config); } for (i = ERROR_START_OFFSET; i <= count * ERROR_ELEM_SIZE; i += ERROR_ELEM_SIZE) { desc = ipw_read_reg32(priv, base + i); time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32)); blink1 = ipw_read_reg32(priv, base + i + 2 * sizeof(u32)); blink2 = ipw_read_reg32(priv, base + i + 3 * sizeof(u32)); ilink1 = ipw_read_reg32(priv, base + i + 4 * sizeof(u32)); ilink2 = ipw_read_reg32(priv, base + i + 5 * sizeof(u32)); idata = ipw_read_reg32(priv, base + i + 6 * sizeof(u32)); IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", ipw_error_desc(desc), time, blink1, blink2, ilink1, ilink2, idata); }}static void ipw_dump_nic_event_log(struct ipw_priv *priv){ u32 ev, time, data, i, count, base; base = ipw_read32(priv, IPW_EVENT_LOG); count = ipw_read_reg32(priv, base); if (EVENT_START_OFFSET <= count * EVENT_ELEM_SIZE) IPW_ERROR("Start IPW Event Log Dump:\n"); for (i = EVENT_START_OFFSET; i <= count * EVENT_ELEM_SIZE; i += EVENT_ELEM_SIZE) { ev = ipw_read_reg32(priv, base + i); time = ipw_read_reg32(priv, base + i + 1 * sizeof(u32)); data = ipw_read_reg32(priv, base + i + 2 * sizeof(u32));#ifdef CONFIG_IPW_DEBUG IPW_ERROR("%i\t0x%08x\t%i\n", time, data, ev);#endif }}static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len){ u32 addr, field_info, field_len, field_count, total_len; IPW_DEBUG_ORD("ordinal = %i\n", ord); if (!priv || !val || !len) { IPW_DEBUG_ORD("Invalid argument\n"); return -EINVAL; } /* verify device ordinal tables have been initialized */ if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) { IPW_DEBUG_ORD("Access ordinals before initialization\n"); return -EINVAL; } switch (IPW_ORD_TABLE_ID_MASK & ord) { case IPW_ORD_TABLE_0_MASK: /* * TABLE 0: Direct access to a table of 32 bit values * * This is a very simple table with the data directly * read from the table */ /* remove the table id from the ordinal */ ord &= IPW_ORD_TABLE_VALUE_MASK; /* boundary check */ if (ord > priv->table0_len) { IPW_DEBUG_ORD("ordinal value (%i) longer then "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -