1/******************************************************************************
2 * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
3 *
4 * This program is distributed in the hope that it will be useful, but WITHOUT
5 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
6 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
7 * more details.
8 *
9 * You should have received a copy of the GNU General Public License along with
10 * this program; if not, write to the Free Software Foundation, Inc.,
11 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
12 *
13 * The full GNU General Public License is included in this distribution in the
14 * file called LICENSE.
15 *
16 * Contact Information:
17 * wlanfae <wlanfae@realtek.com>
18******************************************************************************/
19#include "dot11d.h"
20
21struct channel_list {
22	u8      Channel[32];
23	u8      Len;
24};
25
26static struct channel_list ChannelPlan[] = {
27	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 52, 56, 60, 64,
28	  149, 153, 157, 161, 165}, 24},
29	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 11},
30	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52, 56,
31	  60, 64}, 21},
32	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13},
33	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13},
34	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52,
35	  56, 60, 64}, 22},
36	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52,
37	  56, 60, 64}, 22},
38	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13},
39	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52,
40	  56, 60, 64}, 22},
41	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52,
42	 56, 60, 64}, 22},
43	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 14},
44	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13},
45	{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52,
46	  56, 60, 64}, 21}
47};
48
49void dot11d_init(struct rtllib_device *ieee)
50{
51	struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(ieee);
52
53	pDot11dInfo->bEnabled = false;
54
55	pDot11dInfo->State = DOT11D_STATE_NONE;
56	pDot11dInfo->CountryIeLen = 0;
57	memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
58	memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
59	RESET_CIE_WATCHDOG(ieee);
60
61}
62EXPORT_SYMBOL(dot11d_init);
63
64void Dot11d_Channelmap(u8 channel_plan, struct rtllib_device *ieee)
65{
66	int i, max_chan = 14, min_chan = 1;
67
68	ieee->bGlobalDomain = false;
69
70	if (ChannelPlan[channel_plan].Len != 0) {
71		memset(GET_DOT11D_INFO(ieee)->channel_map, 0,
72		       sizeof(GET_DOT11D_INFO(ieee)->channel_map));
73		for (i = 0; i < ChannelPlan[channel_plan].Len; i++) {
74			if (ChannelPlan[channel_plan].Channel[i] < min_chan ||
75			    ChannelPlan[channel_plan].Channel[i] > max_chan)
76				break;
77			GET_DOT11D_INFO(ieee)->channel_map[ChannelPlan
78					[channel_plan].Channel[i]] = 1;
79		}
80	}
81
82	switch (channel_plan) {
83	case COUNTRY_CODE_GLOBAL_DOMAIN:
84		ieee->bGlobalDomain = true;
85		for (i = 12; i <= 14; i++)
86			GET_DOT11D_INFO(ieee)->channel_map[i] = 2;
87		ieee->IbssStartChnl = 10;
88		ieee->ibss_maxjoin_chal = 11;
89		break;
90
91	case COUNTRY_CODE_WORLD_WIDE_13:
92		for (i = 12; i <= 13; i++)
93			GET_DOT11D_INFO(ieee)->channel_map[i] = 2;
94		ieee->IbssStartChnl = 10;
95		ieee->ibss_maxjoin_chal = 11;
96		break;
97
98	default:
99		ieee->IbssStartChnl = 1;
100		ieee->ibss_maxjoin_chal = 14;
101		break;
102	}
103}
104EXPORT_SYMBOL(Dot11d_Channelmap);
105
106
107void Dot11d_Reset(struct rtllib_device *ieee)
108{
109	struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(ieee);
110	u32 i;
111
112	memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
113	memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
114	for (i = 1; i <= 11; i++)
115		(pDot11dInfo->channel_map)[i] = 1;
116	for (i = 12; i <= 14; i++)
117		(pDot11dInfo->channel_map)[i] = 2;
118	pDot11dInfo->State = DOT11D_STATE_NONE;
119	pDot11dInfo->CountryIeLen = 0;
120	RESET_CIE_WATCHDOG(ieee);
121}
122
123void Dot11d_UpdateCountryIe(struct rtllib_device *dev, u8 *pTaddr,
124			    u16 CoutryIeLen, u8 *pCoutryIe)
125{
126	struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(dev);
127	u8 i, j, NumTriples, MaxChnlNum;
128	struct chnl_txpow_triple *pTriple;
129
130	memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
131	memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
132	MaxChnlNum = 0;
133	NumTriples = (CoutryIeLen - 3) / 3;
134	pTriple = (struct chnl_txpow_triple *)(pCoutryIe + 3);
135	for (i = 0; i < NumTriples; i++) {
136		if (MaxChnlNum >= pTriple->FirstChnl) {
137			netdev_info(dev->dev, "Dot11d_UpdateCountryIe(): Invalid country IE, skip it........1\n");
138			return;
139		}
140		if (MAX_CHANNEL_NUMBER < (pTriple->FirstChnl +
141		    pTriple->NumChnls)) {
142			netdev_info(dev->dev, "Dot11d_UpdateCountryIe(): Invalid country IE, skip it........2\n");
143			return;
144		}
145
146		for (j = 0; j < pTriple->NumChnls; j++) {
147			pDot11dInfo->channel_map[pTriple->FirstChnl + j] = 1;
148			pDot11dInfo->MaxTxPwrDbmList[pTriple->FirstChnl + j] =
149						 pTriple->MaxTxPowerInDbm;
150			MaxChnlNum = pTriple->FirstChnl + j;
151		}
152
153		pTriple = (struct chnl_txpow_triple *)((u8 *)pTriple + 3);
154	}
155
156	UPDATE_CIE_SRC(dev, pTaddr);
157
158	pDot11dInfo->CountryIeLen = CoutryIeLen;
159	memcpy(pDot11dInfo->CountryIeBuf, pCoutryIe, CoutryIeLen);
160	pDot11dInfo->State = DOT11D_STATE_LEARNED;
161}
162
163u8 DOT11D_GetMaxTxPwrInDbm(struct rtllib_device *dev, u8 Channel)
164{
165	struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(dev);
166	u8 MaxTxPwrInDbm = 255;
167
168	if (MAX_CHANNEL_NUMBER < Channel) {
169		netdev_info(dev->dev, "DOT11D_GetMaxTxPwrInDbm(): Invalid Channel\n");
170		return MaxTxPwrInDbm;
171	}
172	if (pDot11dInfo->channel_map[Channel])
173		MaxTxPwrInDbm = pDot11dInfo->MaxTxPwrDbmList[Channel];
174
175	return MaxTxPwrInDbm;
176}
177
178void DOT11D_ScanComplete(struct rtllib_device *dev)
179{
180	struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(dev);
181
182	switch (pDot11dInfo->State) {
183	case DOT11D_STATE_LEARNED:
184		pDot11dInfo->State = DOT11D_STATE_DONE;
185		break;
186	case DOT11D_STATE_DONE:
187		Dot11d_Reset(dev);
188		break;
189	case DOT11D_STATE_NONE:
190		break;
191	}
192}
193
194int ToLegalChannel(struct rtllib_device *dev, u8 channel)
195{
196	struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(dev);
197	u8 default_chn = 0;
198	u32 i;
199
200	for (i = 1; i <= MAX_CHANNEL_NUMBER; i++) {
201		if (pDot11dInfo->channel_map[i] > 0) {
202			default_chn = i;
203			break;
204		}
205	}
206
207	if (MAX_CHANNEL_NUMBER < channel) {
208		netdev_err(dev->dev, "%s(): Invalid Channel\n", __func__);
209		return default_chn;
210	}
211
212	if (pDot11dInfo->channel_map[channel] > 0)
213		return channel;
214
215	return default_chn;
216}
217