⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bcm43xx_radio.c

📁 无线网卡驱动,有很好的参考价值,在linux_2.6.21下可以直接使用,如果在其他平台,可以参考移植
💻 C
📖 第 1 页 / 共 4 页
字号:
/*  Broadcom BCM43xx 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>  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 "bcm43xx.h"#include "bcm43xx_main.h"#include "bcm43xx_phy.h"#include "bcm43xx_radio.h"#include "bcm43xx_ilt.h"/* Table for bcm43xx_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;	assert((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,	};	assert(channel >= 1 && channel <= 14);	return frequencies_bg[channel - 1];}/* Get the freq, as it has to be written to the device. */static inlineu16 channel2freq_a(u8 channel){	assert(channel <= 200);	return (5000 + 5 * channel);}void bcm43xx_radio_lock(struct bcm43xx_private *bcm){	u32 status;	status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);	status |= BCM43xx_SBF_RADIOREG_LOCK;	bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);	mmiowb();	udelay(10);}void bcm43xx_radio_unlock(struct bcm43xx_private *bcm){	u32 status;	bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER); /* dummy read */	status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);	status &= ~BCM43xx_SBF_RADIOREG_LOCK;	bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);	mmiowb();}u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset){	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);	struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);	switch (phy->type) {	case BCM43xx_PHYTYPE_A:		offset |= 0x0040;		break;	case BCM43xx_PHYTYPE_B:		if (radio->version == 0x2053) {			if (offset < 0x70)				offset += 0x80;			else if (offset < 0x80)				offset += 0x70;		} else if (radio->version == 0x2050) {			offset |= 0x80;		} else			assert(0);		break;	case BCM43xx_PHYTYPE_G:		offset |= 0x80;		break;	}	bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset);	return bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW);}void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val){	bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset);	mmiowb();	bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW, val);}static void bcm43xx_set_all_gains(struct bcm43xx_private *bcm,				  s16 first, s16 second, s16 third){	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);	u16 i;	u16 start = 0x08, end = 0x18;	u16 offset = 0x0400;	u16 tmp;	if (phy->rev <= 1) {		offset = 0x5000;		start = 0x10;		end = 0x20;	}	for (i = 0; i < 4; i++)		bcm43xx_ilt_write(bcm, offset + i, first);	for (i = start; i < end; i++)		bcm43xx_ilt_write(bcm, offset + i, second);	if (third != -1) {		tmp = ((u16)third << 14) | ((u16)third << 6);		bcm43xx_phy_write(bcm, 0x04A0,		                  (bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | tmp);		bcm43xx_phy_write(bcm, 0x04A1,		                  (bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | tmp);		bcm43xx_phy_write(bcm, 0x04A2,		                  (bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | tmp);	}	bcm43xx_dummy_transmission(bcm);}static void bcm43xx_set_original_gains(struct bcm43xx_private *bcm){	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);	u16 i, tmp;	u16 offset = 0x0400;	u16 start = 0x0008, 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;		bcm43xx_ilt_write(bcm, offset + i, tmp);	}	for (i = start; i < end; i++)		bcm43xx_ilt_write(bcm, offset + i, i - start);	bcm43xx_phy_write(bcm, 0x04A0,	                  (bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | 0x4040);	bcm43xx_phy_write(bcm, 0x04A1,	                  (bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | 0x4040);	bcm43xx_phy_write(bcm, 0x04A2,	                  (bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | 0x4000);	bcm43xx_dummy_transmission(bcm);}/* Synthetic PU workaround */static void bcm43xx_synth_pu_workaround(struct bcm43xx_private *bcm, u8 channel){	struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);		if (radio->version != 0x2050 || radio->revision >= 6) {		/* We do not need the workaround. */		return;	}	if (channel <= 10) {		bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,				channel2freq_bg(channel + 4));	} else {		bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,				channel2freq_bg(1));	}	udelay(100);	bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,			channel2freq_bg(channel));}u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel){	struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);	u8 ret = 0;	u16 saved, rssi, temp;	int i, j = 0;	saved = bcm43xx_phy_read(bcm, 0x0403);	bcm43xx_radio_selectchannel(bcm, channel, 0);	bcm43xx_phy_write(bcm, 0x0403, (saved & 0xFFF8) | 5);	if (radio->aci_hw_rssi)		rssi = bcm43xx_phy_read(bcm, 0x048A) & 0x3F;	else		rssi = saved & 0x3F;	/* clamp temp to signed 5bit */	if (rssi > 32)		rssi -= 64;	for (i = 0;i < 100; i++) {		temp = (bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x3F;		if (temp > 32)			temp -= 64;		if (temp < rssi)			j++;		if (j >= 20)			ret = 1;	}	bcm43xx_phy_write(bcm, 0x0403, saved);	return ret;}u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm){	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);	struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);	u8 ret[13];	unsigned int channel = radio->channel;	unsigned int i, j, start, end;	unsigned long phylock_flags;	if (!((phy->type == BCM43xx_PHYTYPE_G) && (phy->rev > 0)))		return 0;	bcm43xx_phy_lock(bcm, phylock_flags);	bcm43xx_radio_lock(bcm);	bcm43xx_phy_write(bcm, 0x0802,	                  bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC);	bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,	                  bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF);	bcm43xx_set_all_gains(bcm, 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] = bcm43xx_radio_aci_detect(bcm, i);	}	bcm43xx_radio_selectchannel(bcm, channel, 0);	bcm43xx_phy_write(bcm, 0x0802,	                  (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC) | 0x0003);	bcm43xx_phy_write(bcm, 0x0403,	                  bcm43xx_phy_read(bcm, 0x0403) & 0xFFF8);	bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,	                  bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x8000);	bcm43xx_set_original_gains(bcm);	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;	}	bcm43xx_radio_unlock(bcm);	bcm43xx_phy_unlock(bcm, phylock_flags);	return ret[channel - 1];}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val){	bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset);	mmiowb();	bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_DATA, (u16)val);}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset){	u16 val;	bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset);	val = bcm43xx_phy_read(bcm, BCM43xx_PHY_NRSSILT_DATA);	return (s16)val;}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val){	u16 i;	s16 tmp;	for (i = 0; i < 64; i++) {		tmp = bcm43xx_nrssi_hw_read(bcm, i);		tmp -= val;		tmp = limit_value(tmp, -32, 31);		bcm43xx_nrssi_hw_write(bcm, i, tmp);	}}/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm){	struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);	s16 i, delta;	s32 tmp;	delta = 0x1F - radio->nrssi[0];	for (i = 0; i < 64; i++) {		tmp = (i - delta) * radio->nrssislope;		tmp /= 0x10000;		tmp += 0x3A;		tmp = limit_value(tmp, 0, 0x3F);		radio->nrssi_lt[i] = tmp;	}}static void bcm43xx_calc_nrssi_offset(struct bcm43xx_private *bcm){	struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);	u16 backup[20] = { 0 };	s16 v47F;	u16 i;	u16 saved = 0xFFFF;	backup[0] = bcm43xx_phy_read(bcm, 0x0001);	backup[1] = bcm43xx_phy_read(bcm, 0x0811);	backup[2] = bcm43xx_phy_read(bcm, 0x0812);	backup[3] = bcm43xx_phy_read(bcm, 0x0814);	backup[4] = bcm43xx_phy_read(bcm, 0x0815);	backup[5] = bcm43xx_phy_read(bcm, 0x005A);	backup[6] = bcm43xx_phy_read(bcm, 0x0059);	backup[7] = bcm43xx_phy_read(bcm, 0x0058);	backup[8] = bcm43xx_phy_read(bcm, 0x000A);	backup[9] = bcm43xx_phy_read(bcm, 0x0003);	backup[10] = bcm43xx_radio_read16(bcm, 0x007A);	backup[11] = bcm43xx_radio_read16(bcm, 0x0043);	bcm43xx_phy_write(bcm, 0x0429,			  bcm43xx_phy_read(bcm, 0x0429) & 0x7FFF);	bcm43xx_phy_write(bcm, 0x0001,			  (bcm43xx_phy_read(bcm, 0x0001) & 0x3FFF) | 0x4000);	bcm43xx_phy_write(bcm, 0x0811,			  bcm43xx_phy_read(bcm, 0x0811) | 0x000C);	bcm43xx_phy_write(bcm, 0x0812,			  (bcm43xx_phy_read(bcm, 0x0812) & 0xFFF3) | 0x0004);	bcm43xx_phy_write(bcm, 0x0802,			  bcm43xx_phy_read(bcm, 0x0802) & ~(0x1 | 0x2));	if (phy->rev >= 6) {		backup[12] = bcm43xx_phy_read(bcm, 0x002E);		backup[13] = bcm43xx_phy_read(bcm, 0x002F);		backup[14] = bcm43xx_phy_read(bcm, 0x080F);		backup[15] = bcm43xx_phy_read(bcm, 0x0810);		backup[16] = bcm43xx_phy_read(bcm, 0x0801);		backup[17] = bcm43xx_phy_read(bcm, 0x0060);		backup[18] = bcm43xx_phy_read(bcm, 0x0014);		backup[19] = bcm43xx_phy_read(bcm, 0x0478);		bcm43xx_phy_write(bcm, 0x002E, 0);		bcm43xx_phy_write(bcm, 0x002F, 0);		bcm43xx_phy_write(bcm, 0x080F, 0);		bcm43xx_phy_write(bcm, 0x0810, 0);		bcm43xx_phy_write(bcm, 0x0478,				  bcm43xx_phy_read(bcm, 0x0478) | 0x0100);		bcm43xx_phy_write(bcm, 0x0801,				  bcm43xx_phy_read(bcm, 0x0801) | 0x0040);		bcm43xx_phy_write(bcm, 0x0060,				  bcm43xx_phy_read(bcm, 0x0060) | 0x0040);		bcm43xx_phy_write(bcm, 0x0014,				  bcm43xx_phy_read(bcm, 0x0014) | 0x0200);	}	bcm43xx_radio_write16(bcm, 0x007A,			      bcm43xx_radio_read16(bcm, 0x007A) | 0x0070);	bcm43xx_radio_write16(bcm, 0x007A,			      bcm43xx_radio_read16(bcm, 0x007A) | 0x0080);	udelay(30);	v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);	if (v47F >= 0x20)		v47F -= 0x40;	if (v47F == 31) {		for (i = 7; i >= 4; i--) {			bcm43xx_radio_write16(bcm, 0x007B, i);			udelay(20);			v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);			if (v47F >= 0x20)				v47F -= 0x40;			if (v47F < 31 && saved == 0xFFFF)				saved = i;		}		if (saved == 0xFFFF)			saved = 4;	} else {		bcm43xx_radio_write16(bcm, 0x007A,				      bcm43xx_radio_read16(bcm, 0x007A) & 0x007F);		bcm43xx_phy_write(bcm, 0x0814,				  bcm43xx_phy_read(bcm, 0x0814) | 0x0001);		bcm43xx_phy_write(bcm, 0x0815,				  bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE);		bcm43xx_phy_write(bcm, 0x0811,				  bcm43xx_phy_read(bcm, 0x0811) | 0x000C);		bcm43xx_phy_write(bcm, 0x0812,				  bcm43xx_phy_read(bcm, 0x0812) | 0x000C);		bcm43xx_phy_write(bcm, 0x0811,				  bcm43xx_phy_read(bcm, 0x0811) | 0x0030);		bcm43xx_phy_write(bcm, 0x0812,				  bcm43xx_phy_read(bcm, 0x0812) | 0x0030);		bcm43xx_phy_write(bcm, 0x005A, 0x0480);		bcm43xx_phy_write(bcm, 0x0059, 0x0810);		bcm43xx_phy_write(bcm, 0x0058, 0x000D);		if (phy->analog == 0) {			bcm43xx_phy_write(bcm, 0x0003, 0x0122);		} else {			bcm43xx_phy_write(bcm, 0x000A,					  bcm43xx_phy_read(bcm, 0x000A)					  | 0x2000);		}		bcm43xx_phy_write(bcm, 0x0814,				  bcm43xx_phy_read(bcm, 0x0814) | 0x0004);		bcm43xx_phy_write(bcm, 0x0815,				  bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB);		bcm43xx_phy_write(bcm, 0x0003,				  (bcm43xx_phy_read(bcm, 0x0003) & 0xFF9F)				  | 0x0040);		bcm43xx_radio_write16(bcm, 0x007A,				      bcm43xx_radio_read16(bcm, 0x007A) | 0x000F);		bcm43xx_set_all_gains(bcm, 3, 0, 1);		bcm43xx_radio_write16(bcm, 0x0043,				      (bcm43xx_radio_read16(bcm, 0x0043)				       & 0x00F0) | 0x000F);		udelay(30);		v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);		if (v47F >= 0x20)			v47F -= 0x40;		if (v47F == -32) {			for (i = 0; i < 4; i++) {				bcm43xx_radio_write16(bcm, 0x007B, i);				udelay(20);				v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);				if (v47F >= 0x20)					v47F -= 0x40;				if (v47F > -31 && saved == 0xFFFF)					saved = i;			}			if (saved == 0xFFFF)				saved = 3;		} else			saved = 0;	}	bcm43xx_radio_write16(bcm, 0x007B, saved);	if (phy->rev >= 6) {		bcm43xx_phy_write(bcm, 0x002E, backup[12]);		bcm43xx_phy_write(bcm, 0x002F, backup[13]);		bcm43xx_phy_write(bcm, 0x080F, backup[14]);		bcm43xx_phy_write(bcm, 0x0810, backup[15]);	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -