📄 radio.c
字号:
/* Broadcom B43legacy wireless driver Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, Stefano Brivio <st3@riseup.net> Michael Buesch <mbuesch@freenet.de> Danny van Dyk <kugelfang@gentoo.org> Andreas Jaggi <andreas.jaggi@waterwave.ch> Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net> Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.*/#include <linux/delay.h>#include "b43legacy.h"#include "main.h"#include "phy.h"#include "radio.h"#include "ilt.h"/* Table for b43legacy_radio_calibrationvalue() */static const u16 rcc_table[16] = { 0x0002, 0x0003, 0x0001, 0x000F, 0x0006, 0x0007, 0x0005, 0x000F, 0x000A, 0x000B, 0x0009, 0x000F, 0x000E, 0x000F, 0x000D, 0x000F,};/* Reverse the bits of a 4bit value. * Example: 1101 is flipped 1011 */static u16 flip_4bit(u16 value){ u16 flipped = 0x0000; B43legacy_BUG_ON(!((value & ~0x000F) == 0x0000)); flipped |= (value & 0x0001) << 3; flipped |= (value & 0x0002) << 1; flipped |= (value & 0x0004) >> 1; flipped |= (value & 0x0008) >> 3; return flipped;}/* Get the freq, as it has to be written to the device. */static inlineu16 channel2freq_bg(u8 channel){ /* Frequencies are given as frequencies_bg[index] + 2.4GHz * Starting with channel 1 */ static const u16 frequencies_bg[14] = { 12, 17, 22, 27, 32, 37, 42, 47, 52, 57, 62, 67, 72, 84, }; if (unlikely(channel < 1 || channel > 14)) { printk(KERN_INFO "b43legacy: Channel %d is out of range\n", channel); dump_stack(); return 2412; } return frequencies_bg[channel - 1];}void b43legacy_radio_lock(struct b43legacy_wldev *dev){ u32 status; status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD); status |= B43legacy_SBF_RADIOREG_LOCK; b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status); mmiowb(); udelay(10);}void b43legacy_radio_unlock(struct b43legacy_wldev *dev){ u32 status; b43legacy_read16(dev, B43legacy_MMIO_PHY_VER); /* dummy read */ status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD); status &= ~B43legacy_SBF_RADIOREG_LOCK; b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status); mmiowb();}u16 b43legacy_radio_read16(struct b43legacy_wldev *dev, u16 offset){ struct b43legacy_phy *phy = &dev->phy; switch (phy->type) { case B43legacy_PHYTYPE_B: if (phy->radio_ver == 0x2053) { if (offset < 0x70) offset += 0x80; else if (offset < 0x80) offset += 0x70; } else if (phy->radio_ver == 0x2050) offset |= 0x80; else B43legacy_WARN_ON(1); break; case B43legacy_PHYTYPE_G: offset |= 0x80; break; default: B43legacy_BUG_ON(1); } b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset); return b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_LOW);}void b43legacy_radio_write16(struct b43legacy_wldev *dev, u16 offset, u16 val){ b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset); mmiowb(); b43legacy_write16(dev, B43legacy_MMIO_RADIO_DATA_LOW, val);}static void b43legacy_set_all_gains(struct b43legacy_wldev *dev, s16 first, s16 second, s16 third){ struct b43legacy_phy *phy = &dev->phy; u16 i; u16 start = 0x08; u16 end = 0x18; u16 offset = 0x0400; u16 tmp; if (phy->rev <= 1) { offset = 0x5000; start = 0x10; end = 0x20; } for (i = 0; i < 4; i++) b43legacy_ilt_write(dev, offset + i, first); for (i = start; i < end; i++) b43legacy_ilt_write(dev, offset + i, second); if (third != -1) { tmp = ((u16)third << 14) | ((u16)third << 6); b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) & 0xBFBF) | tmp); b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1) & 0xBFBF) | tmp); b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) & 0xBFBF) | tmp); } b43legacy_dummy_transmission(dev);}static void b43legacy_set_original_gains(struct b43legacy_wldev *dev){ struct b43legacy_phy *phy = &dev->phy; u16 i; u16 tmp; u16 offset = 0x0400; u16 start = 0x0008; u16 end = 0x0018; if (phy->rev <= 1) { offset = 0x5000; start = 0x0010; end = 0x0020; } for (i = 0; i < 4; i++) { tmp = (i & 0xFFFC); tmp |= (i & 0x0001) << 1; tmp |= (i & 0x0002) >> 1; b43legacy_ilt_write(dev, offset + i, tmp); } for (i = start; i < end; i++) b43legacy_ilt_write(dev, offset + i, i - start); b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) & 0xBFBF) | 0x4040); b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1) & 0xBFBF) | 0x4040); b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) & 0xBFBF) | 0x4000); b43legacy_dummy_transmission(dev);}/* Synthetic PU workaround */static void b43legacy_synth_pu_workaround(struct b43legacy_wldev *dev, u8 channel){ struct b43legacy_phy *phy = &dev->phy; might_sleep(); if (phy->radio_ver != 0x2050 || phy->radio_rev >= 6) /* We do not need the workaround. */ return; if (channel <= 10) b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, channel2freq_bg(channel + 4)); else b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, channel2freq_bg(channel)); msleep(1); b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, channel2freq_bg(channel));}u8 b43legacy_radio_aci_detect(struct b43legacy_wldev *dev, u8 channel){ struct b43legacy_phy *phy = &dev->phy; u8 ret = 0; u16 saved; u16 rssi; u16 temp; int i; int j = 0; saved = b43legacy_phy_read(dev, 0x0403); b43legacy_radio_selectchannel(dev, channel, 0); b43legacy_phy_write(dev, 0x0403, (saved & 0xFFF8) | 5); if (phy->aci_hw_rssi) rssi = b43legacy_phy_read(dev, 0x048A) & 0x3F; else rssi = saved & 0x3F; /* clamp temp to signed 5bit */ if (rssi > 32) rssi -= 64; for (i = 0; i < 100; i++) { temp = (b43legacy_phy_read(dev, 0x047F) >> 8) & 0x3F; if (temp > 32) temp -= 64; if (temp < rssi) j++; if (j >= 20) ret = 1; } b43legacy_phy_write(dev, 0x0403, saved); return ret;}u8 b43legacy_radio_aci_scan(struct b43legacy_wldev *dev){ struct b43legacy_phy *phy = &dev->phy; u8 ret[13]; unsigned int channel = phy->channel; unsigned int i; unsigned int j; unsigned int start; unsigned int end; unsigned long phylock_flags; if (!((phy->type == B43legacy_PHYTYPE_G) && (phy->rev > 0))) return 0; b43legacy_phy_lock(dev, phylock_flags); b43legacy_radio_lock(dev); b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) & 0xFFFC); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) & 0x7FFF); b43legacy_set_all_gains(dev, 3, 8, 1); start = (channel - 5 > 0) ? channel - 5 : 1; end = (channel + 5 < 14) ? channel + 5 : 13; for (i = start; i <= end; i++) { if (abs(channel - i) > 2) ret[i-1] = b43legacy_radio_aci_detect(dev, i); } b43legacy_radio_selectchannel(dev, channel, 0); b43legacy_phy_write(dev, 0x0802, (b43legacy_phy_read(dev, 0x0802) & 0xFFFC) | 0x0003); b43legacy_phy_write(dev, 0x0403, b43legacy_phy_read(dev, 0x0403) & 0xFFF8); b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) | 0x8000); b43legacy_set_original_gains(dev); for (i = 0; i < 13; i++) { if (!ret[i]) continue; end = (i + 5 < 13) ? i + 5 : 13; for (j = i; j < end; j++) ret[j] = 1; } b43legacy_radio_unlock(dev); b43legacy_phy_unlock(dev, phylock_flags); return ret[channel - 1];}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val){ b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset); mmiowb(); b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_DATA, (u16)val);}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */s16 b43legacy_nrssi_hw_read(struct b43legacy_wldev *dev, u16 offset){ u16 val; b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset); val = b43legacy_phy_read(dev, B43legacy_PHY_NRSSILT_DATA); return (s16)val;}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val){ u16 i; s16 tmp; for (i = 0; i < 64; i++) { tmp = b43legacy_nrssi_hw_read(dev, i); tmp -= val; tmp = limit_value(tmp, -32, 31); b43legacy_nrssi_hw_write(dev, i, tmp); }}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev){ struct b43legacy_phy *phy = &dev->phy; s16 i; s16 delta; s32 tmp; delta = 0x1F - phy->nrssi[0]; for (i = 0; i < 64; i++) { tmp = (i - delta) * phy->nrssislope; tmp /= 0x10000; tmp += 0x3A; tmp = limit_value(tmp, 0, 0x3F); phy->nrssi_lt[i] = tmp; }}static void b43legacy_calc_nrssi_offset(struct b43legacy_wldev *dev){ struct b43legacy_phy *phy = &dev->phy; u16 backup[20] = { 0 }; s16 v47F; u16 i; u16 saved = 0xFFFF; backup[0] = b43legacy_phy_read(dev, 0x0001); backup[1] = b43legacy_phy_read(dev, 0x0811); backup[2] = b43legacy_phy_read(dev, 0x0812); backup[3] = b43legacy_phy_read(dev, 0x0814); backup[4] = b43legacy_phy_read(dev, 0x0815); backup[5] = b43legacy_phy_read(dev, 0x005A); backup[6] = b43legacy_phy_read(dev, 0x0059); backup[7] = b43legacy_phy_read(dev, 0x0058); backup[8] = b43legacy_phy_read(dev, 0x000A); backup[9] = b43legacy_phy_read(dev, 0x0003); backup[10] = b43legacy_radio_read16(dev, 0x007A); backup[11] = b43legacy_radio_read16(dev, 0x0043); b43legacy_phy_write(dev, 0x0429, b43legacy_phy_read(dev, 0x0429) & 0x7FFF); b43legacy_phy_write(dev, 0x0001, (b43legacy_phy_read(dev, 0x0001) & 0x3FFF) | 0x4000); b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x000C); b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812) & 0xFFF3) | 0x0004); b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) & ~(0x1 | 0x2)); if (phy->rev >= 6) { backup[12] = b43legacy_phy_read(dev, 0x002E); backup[13] = b43legacy_phy_read(dev, 0x002F); backup[14] = b43legacy_phy_read(dev, 0x080F); backup[15] = b43legacy_phy_read(dev, 0x0810); backup[16] = b43legacy_phy_read(dev, 0x0801); backup[17] = b43legacy_phy_read(dev, 0x0060); backup[18] = b43legacy_phy_read(dev, 0x0014); backup[19] = b43legacy_phy_read(dev, 0x0478); b43legacy_phy_write(dev, 0x002E, 0); b43legacy_phy_write(dev, 0x002F, 0); b43legacy_phy_write(dev, 0x080F, 0); b43legacy_phy_write(dev, 0x0810, 0); b43legacy_phy_write(dev, 0x0478, b43legacy_phy_read(dev, 0x0478) | 0x0100); b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801) | 0x0040); b43legacy_phy_write(dev, 0x0060, b43legacy_phy_read(dev, 0x0060) | 0x0040); b43legacy_phy_write(dev, 0x0014, b43legacy_phy_read(dev, 0x0014) | 0x0200); } b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0070); b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x0080); udelay(30); v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F == 31) { for (i = 7; i >= 4; i--) { b43legacy_radio_write16(dev, 0x007B, i); udelay(20); v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F < 31 && saved == 0xFFFF) saved = i; } if (saved == 0xFFFF) saved = 4; } else { b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) & 0x007F); b43legacy_phy_write(dev, 0x0814, b43legacy_phy_read(dev, 0x0814) | 0x0001); b43legacy_phy_write(dev, 0x0815, b43legacy_phy_read(dev, 0x0815) & 0xFFFE); b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x000C); b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) | 0x000C); b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | 0x0030); b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) | 0x0030); b43legacy_phy_write(dev, 0x005A, 0x0480); b43legacy_phy_write(dev, 0x0059, 0x0810); b43legacy_phy_write(dev, 0x0058, 0x000D); if (phy->analog == 0) b43legacy_phy_write(dev, 0x0003, 0x0122); else b43legacy_phy_write(dev, 0x000A, b43legacy_phy_read(dev, 0x000A) | 0x2000); b43legacy_phy_write(dev, 0x0814, b43legacy_phy_read(dev, 0x0814) | 0x0004); b43legacy_phy_write(dev, 0x0815, b43legacy_phy_read(dev, 0x0815) & 0xFFFB); b43legacy_phy_write(dev, 0x0003, (b43legacy_phy_read(dev, 0x0003) & 0xFF9F) | 0x0040); b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, 0x007A) | 0x000F); b43legacy_set_all_gains(dev, 3, 0, 1); b43legacy_radio_write16(dev, 0x0043, (b43legacy_radio_read16(dev, 0x0043) & 0x00F0) | 0x000F); udelay(30); v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F == -32) { for (i = 0; i < 4; i++) { b43legacy_radio_write16(dev, 0x007B, i); udelay(20); v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); if (v47F >= 0x20) v47F -= 0x40; if (v47F > -31 && saved == 0xFFFF) saved = i; } if (saved == 0xFFFF) saved = 3; } else saved = 0; } b43legacy_radio_write16(dev, 0x007B, saved); if (phy->rev >= 6) { b43legacy_phy_write(dev, 0x002E, backup[12]); b43legacy_phy_write(dev, 0x002F, backup[13]); b43legacy_phy_write(dev, 0x080F, backup[14]); b43legacy_phy_write(dev, 0x0810, backup[15]); } b43legacy_phy_write(dev, 0x0814, backup[3]); b43legacy_phy_write(dev, 0x0815, backup[4]); b43legacy_phy_write(dev, 0x005A, backup[5]); b43legacy_phy_write(dev, 0x0059, backup[6]); b43legacy_phy_write(dev, 0x0058, backup[7]); b43legacy_phy_write(dev, 0x000A, backup[8]); b43legacy_phy_write(dev, 0x0003, backup[9]); b43legacy_radio_write16(dev, 0x0043, backup[11]); b43legacy_radio_write16(dev, 0x007A, backup[10]); b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) | 0x1 | 0x2); b43legacy_phy_write(dev, 0x0429, b43legacy_phy_read(dev, 0x0429) | 0x8000); b43legacy_set_original_gains(dev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -