diff options
Diffstat (limited to 'drivers/net/wireless/zd1211rw/zd_ieee80211.c')
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_ieee80211.c | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_ieee80211.c b/drivers/net/wireless/zd1211rw/zd_ieee80211.c new file mode 100644 index 000000000000..66905f7b61ff --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_ieee80211.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* zd_ieee80211.c | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License as published by | ||
5 | * the Free Software Foundation; either version 2 of the License, or | ||
6 | * (at your option) any later version. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program; if not, write to the Free Software | ||
15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
16 | */ | ||
17 | |||
18 | /* | ||
19 | * A lot of this code is generic and should be moved into the upper layers | ||
20 | * at some point. | ||
21 | */ | ||
22 | |||
23 | #include <linux/errno.h> | ||
24 | #include <linux/wireless.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <net/ieee80211.h> | ||
27 | |||
28 | #include "zd_def.h" | ||
29 | #include "zd_ieee80211.h" | ||
30 | #include "zd_mac.h" | ||
31 | |||
32 | static const struct channel_range channel_ranges[] = { | ||
33 | [0] = { 0, 0}, | ||
34 | [ZD_REGDOMAIN_FCC] = { 1, 12}, | ||
35 | [ZD_REGDOMAIN_IC] = { 1, 12}, | ||
36 | [ZD_REGDOMAIN_ETSI] = { 1, 14}, | ||
37 | [ZD_REGDOMAIN_JAPAN] = { 1, 14}, | ||
38 | [ZD_REGDOMAIN_SPAIN] = { 1, 14}, | ||
39 | [ZD_REGDOMAIN_FRANCE] = { 1, 14}, | ||
40 | [ZD_REGDOMAIN_JAPAN_ADD] = {14, 15}, | ||
41 | }; | ||
42 | |||
43 | const struct channel_range *zd_channel_range(u8 regdomain) | ||
44 | { | ||
45 | if (regdomain >= ARRAY_SIZE(channel_ranges)) | ||
46 | regdomain = 0; | ||
47 | return &channel_ranges[regdomain]; | ||
48 | } | ||
49 | |||
50 | int zd_regdomain_supports_channel(u8 regdomain, u8 channel) | ||
51 | { | ||
52 | const struct channel_range *range = zd_channel_range(regdomain); | ||
53 | return range->start <= channel && channel < range->end; | ||
54 | } | ||
55 | |||
56 | int zd_regdomain_supported(u8 regdomain) | ||
57 | { | ||
58 | const struct channel_range *range = zd_channel_range(regdomain); | ||
59 | return range->start != 0; | ||
60 | } | ||
61 | |||
62 | /* Stores channel frequencies in MHz. */ | ||
63 | static const u16 channel_frequencies[] = { | ||
64 | 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, | ||
65 | 2452, 2457, 2462, 2467, 2472, 2484, | ||
66 | }; | ||
67 | |||
68 | #define NUM_CHANNELS ARRAY_SIZE(channel_frequencies) | ||
69 | |||
70 | static int compute_freq(struct iw_freq *freq, u32 mhz, u32 hz) | ||
71 | { | ||
72 | u32 factor; | ||
73 | |||
74 | freq->e = 0; | ||
75 | if (mhz >= 1000000000U) { | ||
76 | pr_debug("zd1211 mhz %u to large\n", mhz); | ||
77 | freq->m = 0; | ||
78 | return -EINVAL; | ||
79 | } | ||
80 | |||
81 | factor = 1000; | ||
82 | while (mhz >= factor) { | ||
83 | |||
84 | freq->e += 1; | ||
85 | factor *= 10; | ||
86 | } | ||
87 | |||
88 | factor /= 1000U; | ||
89 | freq->m = mhz * (1000000U/factor) + hz/factor; | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | int zd_channel_to_freq(struct iw_freq *freq, u8 channel) | ||
95 | { | ||
96 | if (channel > NUM_CHANNELS) { | ||
97 | freq->m = 0; | ||
98 | freq->e = 0; | ||
99 | return -EINVAL; | ||
100 | } | ||
101 | if (!channel) { | ||
102 | freq->m = 0; | ||
103 | freq->e = 0; | ||
104 | return -EINVAL; | ||
105 | } | ||
106 | return compute_freq(freq, channel_frequencies[channel-1], 0); | ||
107 | } | ||
108 | |||
109 | static int freq_to_mhz(const struct iw_freq *freq) | ||
110 | { | ||
111 | u32 factor; | ||
112 | int e; | ||
113 | |||
114 | /* Such high frequencies are not supported. */ | ||
115 | if (freq->e > 6) | ||
116 | return -EINVAL; | ||
117 | |||
118 | factor = 1; | ||
119 | for (e = freq->e; e > 0; --e) { | ||
120 | factor *= 10; | ||
121 | } | ||
122 | factor = 1000000U / factor; | ||
123 | |||
124 | if (freq->m % factor) { | ||
125 | return -EINVAL; | ||
126 | } | ||
127 | |||
128 | return freq->m / factor; | ||
129 | } | ||
130 | |||
131 | int zd_find_channel(u8 *channel, const struct iw_freq *freq) | ||
132 | { | ||
133 | int i, r; | ||
134 | u32 mhz; | ||
135 | |||
136 | if (!(freq->flags & IW_FREQ_FIXED)) | ||
137 | return 0; | ||
138 | |||
139 | if (freq->m < 1000) { | ||
140 | if (freq->m > NUM_CHANNELS || freq->m == 0) | ||
141 | return -EINVAL; | ||
142 | *channel = freq->m; | ||
143 | return 1; | ||
144 | } | ||
145 | |||
146 | r = freq_to_mhz(freq); | ||
147 | if (r < 0) | ||
148 | return r; | ||
149 | mhz = r; | ||
150 | |||
151 | for (i = 0; i < NUM_CHANNELS; i++) { | ||
152 | if (mhz == channel_frequencies[i]) { | ||
153 | *channel = i+1; | ||
154 | return 1; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | return -EINVAL; | ||
159 | } | ||
160 | |||
161 | int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain) | ||
162 | { | ||
163 | struct ieee80211_geo geo; | ||
164 | const struct channel_range *range; | ||
165 | int i; | ||
166 | u8 channel; | ||
167 | |||
168 | dev_dbg(zd_mac_dev(zd_netdev_mac(ieee->dev)), | ||
169 | "regdomain %#04x\n", regdomain); | ||
170 | |||
171 | range = zd_channel_range(regdomain); | ||
172 | if (range->start == 0) { | ||
173 | dev_err(zd_mac_dev(zd_netdev_mac(ieee->dev)), | ||
174 | "zd1211 regdomain %#04x not supported\n", | ||
175 | regdomain); | ||
176 | return -EINVAL; | ||
177 | } | ||
178 | |||
179 | memset(&geo, 0, sizeof(geo)); | ||
180 | |||
181 | for (i = 0, channel = range->start; channel < range->end; channel++) { | ||
182 | struct ieee80211_channel *chan = &geo.bg[i++]; | ||
183 | chan->freq = channel_frequencies[channel - 1]; | ||
184 | chan->channel = channel; | ||
185 | } | ||
186 | |||
187 | geo.bg_channels = i; | ||
188 | memcpy(geo.name, "XX ", 4); | ||
189 | ieee80211_set_geo(ieee, &geo); | ||
190 | return 0; | ||
191 | } | ||