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, 49 insertions, 142 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_ieee80211.c b/drivers/net/wireless/zd1211rw/zd_ieee80211.c index 189160efd2a..77cfe5e7823 100644 --- a/drivers/net/wireless/zd1211rw/zd_ieee80211.c +++ b/drivers/net/wireless/zd1211rw/zd_ieee80211.c | |||
@@ -16,178 +16,85 @@ | |||
16 | */ | 16 | */ |
17 | 17 | ||
18 | /* | 18 | /* |
19 | * A lot of this code is generic and should be moved into the upper layers | 19 | * In the long term, we'll probably find a better way of handling regulatory |
20 | * at some point. | 20 | * requirements outside of the driver. |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/wireless.h> | ||
25 | #include <linux/kernel.h> | 23 | #include <linux/kernel.h> |
26 | #include <net/ieee80211.h> | 24 | #include <net/mac80211.h> |
27 | 25 | ||
28 | #include "zd_def.h" | ||
29 | #include "zd_ieee80211.h" | 26 | #include "zd_ieee80211.h" |
30 | #include "zd_mac.h" | 27 | #include "zd_mac.h" |
31 | 28 | ||
29 | struct channel_range { | ||
30 | u8 regdomain; | ||
31 | u8 start; | ||
32 | u8 end; /* exclusive (channel must be less than end) */ | ||
33 | }; | ||
34 | |||
32 | static const struct channel_range channel_ranges[] = { | 35 | static const struct channel_range channel_ranges[] = { |
33 | [0] = { 0, 0}, | 36 | { ZD_REGDOMAIN_FCC, 1, 12 }, |
34 | [ZD_REGDOMAIN_FCC] = { 1, 12}, | 37 | { ZD_REGDOMAIN_IC, 1, 12 }, |
35 | [ZD_REGDOMAIN_IC] = { 1, 12}, | 38 | { ZD_REGDOMAIN_ETSI, 1, 14 }, |
36 | [ZD_REGDOMAIN_ETSI] = { 1, 14}, | 39 | { ZD_REGDOMAIN_JAPAN, 1, 14 }, |
37 | [ZD_REGDOMAIN_JAPAN] = { 1, 14}, | 40 | { ZD_REGDOMAIN_SPAIN, 1, 14 }, |
38 | [ZD_REGDOMAIN_SPAIN] = { 1, 14}, | 41 | { ZD_REGDOMAIN_FRANCE, 1, 14 }, |
39 | [ZD_REGDOMAIN_FRANCE] = { 1, 14}, | ||
40 | 42 | ||
41 | /* Japan originally only had channel 14 available (see CHNL_ID 0x40 in | 43 | /* Japan originally only had channel 14 available (see CHNL_ID 0x40 in |
42 | * 802.11). However, in 2001 the range was extended to include channels | 44 | * 802.11). However, in 2001 the range was extended to include channels |
43 | * 1-13. The ZyDAS devices still use the old region code but are | 45 | * 1-13. The ZyDAS devices still use the old region code but are |
44 | * designed to allow the extra channel access in Japan. */ | 46 | * designed to allow the extra channel access in Japan. */ |
45 | [ZD_REGDOMAIN_JAPAN_ADD] = { 1, 15}, | 47 | { ZD_REGDOMAIN_JAPAN_ADD, 1, 15 }, |
46 | }; | 48 | }; |
47 | 49 | ||
48 | const struct channel_range *zd_channel_range(u8 regdomain) | 50 | static const struct channel_range *zd_channel_range(u8 regdomain) |
49 | { | ||
50 | if (regdomain >= ARRAY_SIZE(channel_ranges)) | ||
51 | regdomain = 0; | ||
52 | return &channel_ranges[regdomain]; | ||
53 | } | ||
54 | |||
55 | int zd_regdomain_supports_channel(u8 regdomain, u8 channel) | ||
56 | { | ||
57 | const struct channel_range *range = zd_channel_range(regdomain); | ||
58 | return range->start <= channel && channel < range->end; | ||
59 | } | ||
60 | |||
61 | int zd_regdomain_supported(u8 regdomain) | ||
62 | { | 51 | { |
63 | const struct channel_range *range = zd_channel_range(regdomain); | 52 | int i; |
64 | return range->start != 0; | 53 | for (i = 0; i < ARRAY_SIZE(channel_ranges); i++) { |
65 | } | 54 | const struct channel_range *range = &channel_ranges[i]; |
66 | 55 | if (range->regdomain == regdomain) | |
67 | /* Stores channel frequencies in MHz. */ | 56 | return range; |
68 | static const u16 channel_frequencies[] = { | ||
69 | 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, | ||
70 | 2452, 2457, 2462, 2467, 2472, 2484, | ||
71 | }; | ||
72 | |||
73 | #define NUM_CHANNELS ARRAY_SIZE(channel_frequencies) | ||
74 | |||
75 | static int compute_freq(struct iw_freq *freq, u32 mhz, u32 hz) | ||
76 | { | ||
77 | u32 factor; | ||
78 | |||
79 | freq->e = 0; | ||
80 | if (mhz >= 1000000000U) { | ||
81 | pr_debug("zd1211 mhz %u to large\n", mhz); | ||
82 | freq->m = 0; | ||
83 | return -EINVAL; | ||
84 | } | ||
85 | |||
86 | factor = 1000; | ||
87 | while (mhz >= factor) { | ||
88 | |||
89 | freq->e += 1; | ||
90 | factor *= 10; | ||
91 | } | ||
92 | |||
93 | factor /= 1000U; | ||
94 | freq->m = mhz * (1000000U/factor) + hz/factor; | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | int zd_channel_to_freq(struct iw_freq *freq, u8 channel) | ||
100 | { | ||
101 | if (channel > NUM_CHANNELS) { | ||
102 | freq->m = 0; | ||
103 | freq->e = 0; | ||
104 | return -EINVAL; | ||
105 | } | ||
106 | if (!channel) { | ||
107 | freq->m = 0; | ||
108 | freq->e = 0; | ||
109 | return -EINVAL; | ||
110 | } | 57 | } |
111 | return compute_freq(freq, channel_frequencies[channel-1], 0); | 58 | return NULL; |
112 | } | 59 | } |
113 | 60 | ||
114 | static int freq_to_mhz(const struct iw_freq *freq) | 61 | #define CHAN_TO_IDX(chan) ((chan) - 1) |
115 | { | ||
116 | u32 factor; | ||
117 | int e; | ||
118 | |||
119 | /* Such high frequencies are not supported. */ | ||
120 | if (freq->e > 6) | ||
121 | return -EINVAL; | ||
122 | |||
123 | factor = 1; | ||
124 | for (e = freq->e; e > 0; --e) { | ||
125 | factor *= 10; | ||
126 | } | ||
127 | factor = 1000000U / factor; | ||
128 | |||
129 | if (freq->m % factor) { | ||
130 | return -EINVAL; | ||
131 | } | ||
132 | |||
133 | return freq->m / factor; | ||
134 | } | ||
135 | 62 | ||
136 | int zd_find_channel(u8 *channel, const struct iw_freq *freq) | 63 | static void unmask_bg_channels(struct ieee80211_hw *hw, |
64 | const struct channel_range *range, | ||
65 | struct ieee80211_hw_mode *mode) | ||
137 | { | 66 | { |
138 | int i, r; | 67 | u8 channel; |
139 | u32 mhz; | ||
140 | |||
141 | if (freq->m < 1000) { | ||
142 | if (freq->m > NUM_CHANNELS || freq->m == 0) | ||
143 | return -EINVAL; | ||
144 | *channel = freq->m; | ||
145 | return 1; | ||
146 | } | ||
147 | |||
148 | r = freq_to_mhz(freq); | ||
149 | if (r < 0) | ||
150 | return r; | ||
151 | mhz = r; | ||
152 | 68 | ||
153 | for (i = 0; i < NUM_CHANNELS; i++) { | 69 | for (channel = range->start; channel < range->end; channel++) { |
154 | if (mhz == channel_frequencies[i]) { | 70 | struct ieee80211_channel *chan = |
155 | *channel = i+1; | 71 | &mode->channels[CHAN_TO_IDX(channel)]; |
156 | return 1; | 72 | chan->flag |= IEEE80211_CHAN_W_SCAN | |
157 | } | 73 | IEEE80211_CHAN_W_ACTIVE_SCAN | |
74 | IEEE80211_CHAN_W_IBSS; | ||
158 | } | 75 | } |
159 | |||
160 | return -EINVAL; | ||
161 | } | 76 | } |
162 | 77 | ||
163 | int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain) | 78 | void zd_geo_init(struct ieee80211_hw *hw, u8 regdomain) |
164 | { | 79 | { |
165 | struct ieee80211_geo geo; | 80 | struct zd_mac *mac = zd_hw_mac(hw); |
166 | const struct channel_range *range; | 81 | const struct channel_range *range; |
167 | int i; | ||
168 | u8 channel; | ||
169 | 82 | ||
170 | dev_dbg(zd_mac_dev(zd_netdev_mac(ieee->dev)), | 83 | dev_dbg(zd_mac_dev(mac), "regdomain %#02x\n", regdomain); |
171 | "regdomain %#04x\n", regdomain); | ||
172 | 84 | ||
173 | range = zd_channel_range(regdomain); | 85 | range = zd_channel_range(regdomain); |
174 | if (range->start == 0) { | 86 | if (!range) { |
175 | dev_err(zd_mac_dev(zd_netdev_mac(ieee->dev)), | 87 | /* The vendor driver overrides the regulatory domain and |
176 | "zd1211 regdomain %#04x not supported\n", | 88 | * allowed channel registers and unconditionally restricts |
177 | regdomain); | 89 | * available channels to 1-11 everywhere. Match their |
178 | return -EINVAL; | 90 | * questionable behaviour only for regdomains which we don't |
91 | * recognise. */ | ||
92 | dev_warn(zd_mac_dev(mac), "Unrecognised regulatory domain: " | ||
93 | "%#02x. Defaulting to FCC.\n", regdomain); | ||
94 | range = zd_channel_range(ZD_REGDOMAIN_FCC); | ||
179 | } | 95 | } |
180 | 96 | ||
181 | memset(&geo, 0, sizeof(geo)); | 97 | unmask_bg_channels(hw, range, &mac->modes[0]); |
182 | 98 | unmask_bg_channels(hw, range, &mac->modes[1]); | |
183 | for (i = 0, channel = range->start; channel < range->end; channel++) { | ||
184 | struct ieee80211_channel *chan = &geo.bg[i++]; | ||
185 | chan->freq = channel_frequencies[channel - 1]; | ||
186 | chan->channel = channel; | ||
187 | } | ||
188 | |||
189 | geo.bg_channels = i; | ||
190 | memcpy(geo.name, "XX ", 4); | ||
191 | ieee80211_set_geo(ieee, &geo); | ||
192 | return 0; | ||
193 | } | 99 | } |
100 | |||