📄 iwl-3945.c
字号:
/****************************************************************************** * * Copyright(c) 2003 - 2007 Intel Corporation. All rights reserved. * * 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 * *****************************************************************************/#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 <net/mac80211.h>#include <linux/etherdevice.h>#define IWL 3945#include "iwlwifi.h"#include "iwl-helpers.h"#include "iwl-3945.h"#include "iwl-3945-rs.h"#define IWL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np) \ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ IWL_RATE_##r##M_IEEE, \ IWL_RATE_##ip##M_INDEX, \ IWL_RATE_##in##M_INDEX, \ IWL_RATE_##rp##M_INDEX, \ IWL_RATE_##rn##M_INDEX, \ IWL_RATE_##pp##M_INDEX, \ IWL_RATE_##np##M_INDEX, \ IWL_RATE_##r##M_INDEX_TABLE, \ IWL_RATE_##ip##M_INDEX_TABLE }/* * Parameter order: * rate, prev rate, next rate, prev tgg rate, next tgg rate * * If there isn't a valid next or previous rate then INV is used which * maps to IWL_RATE_INVALID * */const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { IWL_DECLARE_RATE_INFO(1, INV, 2, INV, 2, INV, 2), /* 1mbps */ IWL_DECLARE_RATE_INFO(2, 1, 5, 1, 5, 1, 5), /* 2mbps */ IWL_DECLARE_RATE_INFO(5, 2, 6, 2, 11, 2, 11), /*5.5mbps */ IWL_DECLARE_RATE_INFO(11, 9, 12, 5, 12, 5, 18), /* 11mbps */ IWL_DECLARE_RATE_INFO(6, 5, 9, 5, 11, 5, 11), /* 6mbps */ IWL_DECLARE_RATE_INFO(9, 6, 11, 5, 11, 5, 11), /* 9mbps */ IWL_DECLARE_RATE_INFO(12, 11, 18, 11, 18, 11, 18), /* 12mbps */ IWL_DECLARE_RATE_INFO(18, 12, 24, 12, 24, 11, 24), /* 18mbps */ IWL_DECLARE_RATE_INFO(24, 18, 36, 18, 36, 18, 36), /* 24mbps */ IWL_DECLARE_RATE_INFO(36, 24, 48, 24, 48, 24, 48), /* 36mbps */ IWL_DECLARE_RATE_INFO(48, 36, 54, 36, 54, 36, 54), /* 48mbps */ IWL_DECLARE_RATE_INFO(54, 48, INV, 48, INV, 48, INV),/* 54mbps */};/* 1 = enable the iwl_disable_events() function */#define IWL_EVT_DISABLE (0)#define IWL_EVT_DISABLE_SIZE (1532/32)/** * iwl_disable_events - Disable selected events in uCode event log * * Disable an event by writing "1"s into "disable" * bitmap in SRAM. Bit position corresponds to Event # (id/type). * Default values of 0 enable uCode events to be logged. * Use for only special debugging. This function is just a placeholder as-is, * you'll need to provide the special bits! ... * ... and set IWL_EVT_DISABLE to 1. */void iwl_disable_events(struct iwl_priv *priv){ int rc; int i; u32 base; /* SRAM address of event log header */ u32 disable_ptr; /* SRAM address of event-disable bitmap array */ u32 array_size; /* # of u32 entries in array */ u32 evt_disable[IWL_EVT_DISABLE_SIZE] = { 0x00000000, /* 31 - 0 Event id numbers */ 0x00000000, /* 63 - 32 */ 0x00000000, /* 95 - 64 */ 0x00000000, /* 127 - 96 */ 0x00000000, /* 159 - 128 */ 0x00000000, /* 191 - 160 */ 0x00000000, /* 223 - 192 */ 0x00000000, /* 255 - 224 */ 0x00000000, /* 287 - 256 */ 0x00000000, /* 319 - 288 */ 0x00000000, /* 351 - 320 */ 0x00000000, /* 383 - 352 */ 0x00000000, /* 415 - 384 */ 0x00000000, /* 447 - 416 */ 0x00000000, /* 479 - 448 */ 0x00000000, /* 511 - 480 */ 0x00000000, /* 543 - 512 */ 0x00000000, /* 575 - 544 */ 0x00000000, /* 607 - 576 */ 0x00000000, /* 639 - 608 */ 0x00000000, /* 671 - 640 */ 0x00000000, /* 703 - 672 */ 0x00000000, /* 735 - 704 */ 0x00000000, /* 767 - 736 */ 0x00000000, /* 799 - 768 */ 0x00000000, /* 831 - 800 */ 0x00000000, /* 863 - 832 */ 0x00000000, /* 895 - 864 */ 0x00000000, /* 927 - 896 */ 0x00000000, /* 959 - 928 */ 0x00000000, /* 991 - 960 */ 0x00000000, /* 1023 - 992 */ 0x00000000, /* 1055 - 1024 */ 0x00000000, /* 1087 - 1056 */ 0x00000000, /* 1119 - 1088 */ 0x00000000, /* 1151 - 1120 */ 0x00000000, /* 1183 - 1152 */ 0x00000000, /* 1215 - 1184 */ 0x00000000, /* 1247 - 1216 */ 0x00000000, /* 1279 - 1248 */ 0x00000000, /* 1311 - 1280 */ 0x00000000, /* 1343 - 1312 */ 0x00000000, /* 1375 - 1344 */ 0x00000000, /* 1407 - 1376 */ 0x00000000, /* 1439 - 1408 */ 0x00000000, /* 1471 - 1440 */ 0x00000000, /* 1503 - 1472 */ }; base = le32_to_cpu(priv->card_alive.log_event_table_ptr); if (!iwl_hw_valid_rtc_data_addr(base)) { IWL_ERROR("Invalid event log pointer 0x%08X\n", base); return; } rc = iwl_grab_restricted_access(priv); if (rc) { IWL_WARNING("Can not read from adapter at this time.\n"); return; } disable_ptr = iwl_read_restricted_mem(priv, base + (4 * sizeof(u32))); array_size = iwl_read_restricted_mem(priv, base + (5 * sizeof(u32))); iwl_release_restricted_access(priv); if (IWL_EVT_DISABLE && (array_size == IWL_EVT_DISABLE_SIZE)) { IWL_DEBUG_INFO("Disabling selected uCode log events at 0x%x\n", disable_ptr); rc = iwl_grab_restricted_access(priv); for (i = 0; i < IWL_EVT_DISABLE_SIZE; i++) iwl_write_restricted_mem(priv, disable_ptr + (i * sizeof(u32)), evt_disable[i]); iwl_release_restricted_access(priv); } else { IWL_DEBUG_INFO("Selected uCode log events may be disabled\n"); IWL_DEBUG_INFO(" by writing \"1\"s into disable bitmap\n"); IWL_DEBUG_INFO(" in SRAM at 0x%x, size %d u32s\n", disable_ptr, array_size); }}/** * iwl3945_get_antenna_flags - Get antenna flags for RXON command * @priv: eeprom and antenna fields are used to determine antenna flags * * priv->eeprom is used to determine if antenna AUX/MAIN are reversed * priv->antenna specifies the antenna diversity mode: * * IWL_ANTENNA_DIVERISTY - NIC selects best antenna by itself * IWL_ANTENNA_MAIN - Force MAIN antenna * IWL_ANTENNA_AUX - Force AUX antenna */__le32 iwl3945_get_antenna_flags(const struct iwl_priv *priv){ switch (priv->antenna) { case IWL_ANTENNA_DIVERSITY: return 0; case IWL_ANTENNA_MAIN: if (priv->eeprom.antenna_switch_type) return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK; return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK; case IWL_ANTENNA_AUX: if (priv->eeprom.antenna_switch_type) return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK; return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK; } /* bad antenna selector value */ IWL_ERROR("Bad antenna selector value (0x%x)\n", priv->antenna); return 0; /* "diversity" is default if error */}/***************************************************************************** * * Intel PRO/Wireless 3945ABG/BG Network Connection * * RX handler implementations * * Used by iwl-base.c * *****************************************************************************/void iwl_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb){ struct iwl_rx_packet *pkt = (void *)rxb->skb->data; IWL_DEBUG_RX("Statistics notification received (%d vs %d).\n", (int)sizeof(struct iwl_notif_statistics), le32_to_cpu(pkt->len)); memcpy(&priv->statistics, pkt->u.raw, sizeof(priv->statistics)); priv->last_statistics_time = jiffies;}static void iwl3945_handle_data_packet(struct iwl_priv *priv, int is_data, struct iwl_rx_mem_buffer *rxb, struct ieee80211_rx_status *stats, u16 phy_flags){ struct ieee80211_hdr *hdr; struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; struct iwl_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt); struct iwl_rx_frame_end *rx_end = IWL_RX_END(pkt); short len = le16_to_cpu(rx_hdr->len); /* We received data from the HW, so stop the watchdog */ if (unlikely((len + IWL_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) { IWL_DEBUG_DROP("Corruption detected!\n"); return; } /* We only process data packets if the interface is open */ if (unlikely(!priv->is_open)) { IWL_DEBUG_DROP_LIMIT ("Dropping packet while interface is not open.\n"); return; } if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { if (iwl_param_hwcrypto) iwl_set_decrypted_flag(priv, rxb->skb, le32_to_cpu(rx_end->status), stats); iwl_handle_data_packet_monitor(priv, rxb, IWL_RX_DATA(pkt), len, stats, phy_flags); return; } skb_reserve(rxb->skb, (void *)rx_hdr->payload - (void *)pkt); /* Set the size of the skb to the size of the frame */ skb_put(rxb->skb, le16_to_cpu(rx_hdr->len)); hdr = (void *)rxb->skb->data; if (iwl_param_hwcrypto) iwl_set_decrypted_flag(priv, rxb->skb, le32_to_cpu(rx_end->status), stats); ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats); rxb->skb = NULL;}static void iwl3945_rx_reply_rx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb){ struct iwl_rx_packet *pkt = (void *)rxb->skb->data; struct iwl_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt); struct iwl_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt); struct iwl_rx_frame_end *rx_end = IWL_RX_END(pkt); struct ieee80211_hdr *header; u16 phy_flags = le16_to_cpu(rx_hdr->phy_flags); u16 rx_stats_sig_avg = le16_to_cpu(rx_stats->sig_avg); u16 rx_stats_noise_diff = le16_to_cpu(rx_stats->noise_diff); struct ieee80211_rx_status stats = { .mactime = le64_to_cpu(rx_end->timestamp), .freq = ieee80211chan2mhz(le16_to_cpu(rx_hdr->channel)), .channel = le16_to_cpu(rx_hdr->channel), .phymode = (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? MODE_IEEE80211G : MODE_IEEE80211A, .antenna = 0, .rate = rx_hdr->rate, .flag = 0, }; u8 network_packet; int snr; if ((unlikely(rx_stats->phy_count > 20))) { IWL_DEBUG_DROP ("dsp size out of range [0,20]: " "%d/n", rx_stats->phy_count); return; } if (!(rx_end->status & RX_RES_STATUS_NO_CRC32_ERROR) || !(rx_end->status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { IWL_DEBUG_RX("Bad CRC or FIFO: 0x%08X.\n", rx_end->status); return; } if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { iwl3945_handle_data_packet(priv, 1, rxb, &stats, phy_flags); return; } /* Convert 3945's rssi indicator to dBm */ stats.ssi = rx_stats->rssi - IWL_RSSI_OFFSET; /* Set default noise value to -127 */ if (priv->last_rx_noise == 0) priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; /* 3945 provides noise info for OFDM frames only. * sig_avg and noise_diff are measured by the 3945's digital signal * processor (DSP), and indicate linear levels of signal level and * distortion/noise within the packet preamble after * automatic gain control (AGC). sig_avg should stay fairly * constant if the radio's AGC is working well. * Since these values are linear (not dB or dBm), linear * signal-to-noise ratio (SNR) is (sig_avg / noise_diff). * Convert linear SNR to dB SNR, then subtract that from rssi dBm * to obtain noise level in dBm. * Calculate stats.signal (quality indicator in %) based on SNR. */ if (rx_stats_noise_diff) { snr = rx_stats_sig_avg / rx_stats_noise_diff; stats.noise = stats.ssi - iwl_calc_db_from_ratio(snr); stats.signal = iwl_calc_sig_qual(stats.ssi, stats.noise); /* If noise info not available, calculate signal quality indicator (%) * using just the dBm signal level. */ } else { stats.noise = priv->last_rx_noise; stats.signal = iwl_calc_sig_qual(stats.ssi, 0); } IWL_DEBUG_STATS("Rssi %d noise %d qual %d sig_avg %d noise_diff %d\n", stats.ssi, stats.noise, stats.signal, rx_stats_sig_avg, rx_stats_noise_diff); stats.freq = ieee80211chan2mhz(stats.channel); /* can be covered by iwl_report_frame() in most cases *//* IWL_DEBUG_RX("RX status: 0x%08X\n", rx_end->status); */ header = (struct ieee80211_hdr *)IWL_RX_DATA(pkt); network_packet = iwl_is_network_packet(priv, header);#ifdef CONFIG_IWLWIFI_DEBUG if (iwl_debug_level & IWL_DL_STATS && net_ratelimit()) IWL_DEBUG_STATS ("[%c] %d RSSI: %d Signal: %u, Noise: %u, Rate: %u\n", network_packet ? '*' : ' ', stats.channel, stats.ssi, stats.ssi, stats.ssi, stats.rate); if (iwl_debug_level & (IWL_DL_RX)) /* Set "1" to report good data frames in groups of 100 */ iwl_report_frame(priv, pkt, header, 1);#endif if (network_packet) { priv->last_beacon_time = le32_to_cpu(rx_end->beacon_timestamp); priv->last_tsf = le64_to_cpu(rx_end->timestamp); priv->last_rx_rssi = stats.ssi; priv->last_rx_noise = stats.noise; } switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_MGMT: switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON:{ /* If this is a beacon or probe response for * our network then cache the beacon * timestamp */ if ((((priv->iw_mode == IEEE80211_IF_TYPE_STA) && !compare_ether_addr(header->addr2, priv->bssid)) || ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && !compare_ether_addr(header->addr3, priv->bssid)))) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)header; __le32 *pos; pos = (__le32 *) & mgmt->u.beacon. timestamp; priv->timestamp0 = le32_to_cpu(pos[0]); priv->timestamp1 = le32_to_cpu(pos[1]); priv->beacon_int = le16_to_cpu( mgmt->u.beacon.beacon_int); if (priv->call_post_assoc_from_beacon && (priv->iw_mode == IEEE80211_IF_TYPE_STA)) queue_work(priv->workqueue, &priv->post_associate.work); priv->call_post_assoc_from_beacon = 0; } break; } case IEEE80211_STYPE_ACTION: /* TODO: Parse 802.11h frames for CSA... */ break; /* * TODO: There is no callback function from upper * stack to inform us when associated status. this * work around to sniff assoc_resp management frame * and finish the association process. */ case IEEE80211_STYPE_ASSOC_RESP: case IEEE80211_STYPE_REASSOC_RESP:{ struct ieee80211_mgmt *mgnt = (struct ieee80211_mgmt *)header; priv->assoc_id = (~((1 << 15) | (1 << 14)) & le16_to_cpu(mgnt->u. assoc_resp.aid)); priv->assoc_capability =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -