1/* Implement 802.11d. */
2
3#include "dot11d.h"
4
5void Dot11d_Init(struct ieee80211_device *ieee)
6{
7	PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(ieee);
8
9	pDot11dInfo->bEnabled = false;
10
11	pDot11dInfo->State = DOT11D_STATE_NONE;
12	pDot11dInfo->CountryIeLen = 0;
13	memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
14	memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
15	RESET_CIE_WATCHDOG(ieee);
16
17	netdev_info(ieee->dev, "Dot11d_Init()\n");
18}
19EXPORT_SYMBOL(Dot11d_Init);
20
21/* Reset to the state as we are just entering a regulatory domain. */
22void Dot11d_Reset(struct ieee80211_device *ieee)
23{
24	u32 i;
25	PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(ieee);
26	/* Clear old channel map */
27	memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
28	memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
29	/* Set new channel map */
30	for (i = 1; i <= 11; i++)
31		(pDot11dInfo->channel_map)[i] = 1;
32
33	for (i = 12; i <= 14; i++)
34		(pDot11dInfo->channel_map)[i] = 2;
35
36	pDot11dInfo->State = DOT11D_STATE_NONE;
37	pDot11dInfo->CountryIeLen = 0;
38	RESET_CIE_WATCHDOG(ieee);
39}
40EXPORT_SYMBOL(Dot11d_Reset);
41
42/*
43 * Update country IE from Beacon or Probe Resopnse and configure PHY for
44 * operation in the regulatory domain.
45 *
46 * TODO: Configure Tx power.
47 * Assumption:
48 * 1. IS_DOT11D_ENABLE() is TRUE.
49 * 2. Input IE is an valid one.
50 */
51void Dot11d_UpdateCountryIe(struct ieee80211_device *dev, u8 *pTaddr,
52			    u16 CoutryIeLen, u8 *pCoutryIe)
53{
54	PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev);
55	u8 i, j, NumTriples, MaxChnlNum;
56	PCHNL_TXPOWER_TRIPLE pTriple;
57
58	memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
59	memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
60	MaxChnlNum = 0;
61	NumTriples = (CoutryIeLen - 3) / 3; /* skip 3-byte country string. */
62	pTriple = (PCHNL_TXPOWER_TRIPLE)(pCoutryIe + 3);
63	for (i = 0; i < NumTriples; i++) {
64		if (MaxChnlNum >= pTriple->FirstChnl) {
65			/* It is not in a monotonically increasing order, so
66			 * stop processing.
67			 */
68			netdev_err(dev->dev, "Dot11d_UpdateCountryIe(): Invalid country IE, skip it........1\n");
69			return;
70		}
71		if (MAX_CHANNEL_NUMBER < (pTriple->FirstChnl + pTriple->NumChnls)) {
72			/* It is not a valid set of channel id, so stop
73			 * processing.
74			 */
75			netdev_err(dev->dev, "Dot11d_UpdateCountryIe(): Invalid country IE, skip it........2\n");
76			return;
77		}
78
79		for (j = 0; j < pTriple->NumChnls; j++) {
80			pDot11dInfo->channel_map[pTriple->FirstChnl + j] = 1;
81			pDot11dInfo->MaxTxPwrDbmList[pTriple->FirstChnl + j] = pTriple->MaxTxPowerInDbm;
82			MaxChnlNum = pTriple->FirstChnl + j;
83		}
84
85		pTriple = (PCHNL_TXPOWER_TRIPLE)((u8 *)pTriple + 3);
86	}
87	netdev_info(dev->dev, "Channel List:");
88	for (i = 1; i <= MAX_CHANNEL_NUMBER; i++)
89		if (pDot11dInfo->channel_map[i] > 0)
90			netdev_info(dev->dev, " %d", i);
91	netdev_info(dev->dev, "\n");
92
93	UPDATE_CIE_SRC(dev, pTaddr);
94
95	pDot11dInfo->CountryIeLen = CoutryIeLen;
96	memcpy(pDot11dInfo->CountryIeBuf, pCoutryIe, CoutryIeLen);
97	pDot11dInfo->State = DOT11D_STATE_LEARNED;
98}
99EXPORT_SYMBOL(Dot11d_UpdateCountryIe);
100
101u8 DOT11D_GetMaxTxPwrInDbm(struct ieee80211_device *dev, u8 Channel)
102{
103	PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev);
104	u8 MaxTxPwrInDbm = 255;
105
106	if (MAX_CHANNEL_NUMBER < Channel) {
107		netdev_err(dev->dev, "DOT11D_GetMaxTxPwrInDbm(): Invalid Channel\n");
108		return MaxTxPwrInDbm;
109	}
110	if (pDot11dInfo->channel_map[Channel])
111		MaxTxPwrInDbm = pDot11dInfo->MaxTxPwrDbmList[Channel];
112
113	return MaxTxPwrInDbm;
114}
115EXPORT_SYMBOL(DOT11D_GetMaxTxPwrInDbm);
116
117void DOT11D_ScanComplete(struct ieee80211_device *dev)
118{
119	PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev);
120
121	switch (pDot11dInfo->State) {
122	case DOT11D_STATE_LEARNED:
123		pDot11dInfo->State = DOT11D_STATE_DONE;
124		break;
125
126	case DOT11D_STATE_DONE:
127		if (GET_CIE_WATCHDOG(dev) == 0) {
128			/* Reset country IE if previous one is gone. */
129			Dot11d_Reset(dev);
130		}
131		break;
132	case DOT11D_STATE_NONE:
133		break;
134	}
135}
136EXPORT_SYMBOL(DOT11D_ScanComplete);
137
138int IsLegalChannel(struct ieee80211_device *dev, u8 channel)
139{
140	PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev);
141
142	if (MAX_CHANNEL_NUMBER < channel) {
143		netdev_err(dev->dev, "IsLegalChannel(): Invalid Channel\n");
144		return 0;
145	}
146	if (pDot11dInfo->channel_map[channel] > 0)
147		return 1;
148	return 0;
149}
150EXPORT_SYMBOL(IsLegalChannel);
151
152int ToLegalChannel(struct ieee80211_device *dev, u8 channel)
153{
154	PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev);
155	u8 default_chn = 0;
156	u32 i = 0;
157
158	for (i = 1; i <= MAX_CHANNEL_NUMBER; i++) {
159		if (pDot11dInfo->channel_map[i] > 0) {
160			default_chn = i;
161			break;
162		}
163	}
164
165	if (MAX_CHANNEL_NUMBER < channel) {
166		netdev_err(dev->dev, "IsLegalChannel(): Invalid Channel\n");
167		return default_chn;
168	}
169
170	if (pDot11dInfo->channel_map[channel] > 0)
171		return channel;
172
173	return default_chn;
174}
175EXPORT_SYMBOL(ToLegalChannel);
176