diff options
author | Marcelo Tosatti <marcelo@kvack.org> | 2007-02-10 09:25:27 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-04-28 11:00:54 -0400 |
commit | 876c9d3aeb989cf1961f2c228d309ba5dcfb1172 (patch) | |
tree | 239e9db92d13abc799c1ffc5304d8ec1503dbc61 /drivers/net/wireless/libertas/rx.c | |
parent | 35c3404efa7407811b706453f83d39b2539dcbd0 (diff) |
[PATCH] Marvell Libertas 8388 802.11b/g USB driver
Add the Marvell Libertas 8388 802.11 USB driver.
Signed-off-by: Marcelo Tosatti <marcelo@kvack.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas/rx.c')
-rw-r--r-- | drivers/net/wireless/libertas/rx.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c new file mode 100644 index 000000000000..7e3f78f092dc --- /dev/null +++ b/drivers/net/wireless/libertas/rx.c | |||
@@ -0,0 +1,459 @@ | |||
1 | /** | ||
2 | * This file contains the handling of RX in wlan driver. | ||
3 | */ | ||
4 | #include <linux/etherdevice.h> | ||
5 | #include <linux/types.h> | ||
6 | |||
7 | #include "hostcmd.h" | ||
8 | #include "radiotap.h" | ||
9 | #include "decl.h" | ||
10 | #include "dev.h" | ||
11 | #include "wext.h" | ||
12 | |||
13 | struct eth803hdr { | ||
14 | u8 dest_addr[6]; | ||
15 | u8 src_addr[6]; | ||
16 | u16 h803_len; | ||
17 | } __attribute__ ((packed)); | ||
18 | |||
19 | struct rfc1042hdr { | ||
20 | u8 llc_dsap; | ||
21 | u8 llc_ssap; | ||
22 | u8 llc_ctrl; | ||
23 | u8 snap_oui[3]; | ||
24 | u16 snap_type; | ||
25 | } __attribute__ ((packed)); | ||
26 | |||
27 | struct rxpackethdr { | ||
28 | struct rxpd rx_pd; | ||
29 | struct eth803hdr eth803_hdr; | ||
30 | struct rfc1042hdr rfc1042_hdr; | ||
31 | } __attribute__ ((packed)); | ||
32 | |||
33 | struct rx80211packethdr { | ||
34 | struct rxpd rx_pd; | ||
35 | void *eth80211_hdr; | ||
36 | } __attribute__ ((packed)); | ||
37 | |||
38 | static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb); | ||
39 | |||
40 | /** | ||
41 | * @brief This function computes the avgSNR . | ||
42 | * | ||
43 | * @param priv A pointer to wlan_private structure | ||
44 | * @return avgSNR | ||
45 | */ | ||
46 | static u8 wlan_getavgsnr(wlan_private * priv) | ||
47 | { | ||
48 | u8 i; | ||
49 | u16 temp = 0; | ||
50 | wlan_adapter *adapter = priv->adapter; | ||
51 | if (adapter->numSNRNF == 0) | ||
52 | return 0; | ||
53 | for (i = 0; i < adapter->numSNRNF; i++) | ||
54 | temp += adapter->rawSNR[i]; | ||
55 | return (u8) (temp / adapter->numSNRNF); | ||
56 | |||
57 | } | ||
58 | |||
59 | /** | ||
60 | * @brief This function computes the AvgNF | ||
61 | * | ||
62 | * @param priv A pointer to wlan_private structure | ||
63 | * @return AvgNF | ||
64 | */ | ||
65 | static u8 wlan_getavgnf(wlan_private * priv) | ||
66 | { | ||
67 | u8 i; | ||
68 | u16 temp = 0; | ||
69 | wlan_adapter *adapter = priv->adapter; | ||
70 | if (adapter->numSNRNF == 0) | ||
71 | return 0; | ||
72 | for (i = 0; i < adapter->numSNRNF; i++) | ||
73 | temp += adapter->rawNF[i]; | ||
74 | return (u8) (temp / adapter->numSNRNF); | ||
75 | |||
76 | } | ||
77 | |||
78 | /** | ||
79 | * @brief This function save the raw SNR/NF to our internel buffer | ||
80 | * | ||
81 | * @param priv A pointer to wlan_private structure | ||
82 | * @param prxpd A pointer to rxpd structure of received packet | ||
83 | * @return n/a | ||
84 | */ | ||
85 | static void wlan_save_rawSNRNF(wlan_private * priv, struct rxpd *p_rx_pd) | ||
86 | { | ||
87 | wlan_adapter *adapter = priv->adapter; | ||
88 | if (adapter->numSNRNF < adapter->data_avg_factor) | ||
89 | adapter->numSNRNF++; | ||
90 | adapter->rawSNR[adapter->nextSNRNF] = p_rx_pd->snr; | ||
91 | adapter->rawNF[adapter->nextSNRNF] = p_rx_pd->nf; | ||
92 | adapter->nextSNRNF++; | ||
93 | if (adapter->nextSNRNF >= adapter->data_avg_factor) | ||
94 | adapter->nextSNRNF = 0; | ||
95 | return; | ||
96 | } | ||
97 | |||
98 | /** | ||
99 | * @brief This function computes the RSSI in received packet. | ||
100 | * | ||
101 | * @param priv A pointer to wlan_private structure | ||
102 | * @param prxpd A pointer to rxpd structure of received packet | ||
103 | * @return n/a | ||
104 | */ | ||
105 | static void wlan_compute_rssi(wlan_private * priv, struct rxpd *p_rx_pd) | ||
106 | { | ||
107 | wlan_adapter *adapter = priv->adapter; | ||
108 | |||
109 | ENTER(); | ||
110 | |||
111 | lbs_pr_debug(1, "rxpd: SNR = %d, NF = %d\n", p_rx_pd->snr, p_rx_pd->nf); | ||
112 | lbs_pr_debug(1, "Before computing SNR: SNR- avg = %d, NF-avg = %d\n", | ||
113 | adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, | ||
114 | adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); | ||
115 | |||
116 | adapter->SNR[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->snr; | ||
117 | adapter->NF[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->nf; | ||
118 | wlan_save_rawSNRNF(priv, p_rx_pd); | ||
119 | |||
120 | adapter->rxpd_rate = p_rx_pd->rx_rate; | ||
121 | |||
122 | adapter->SNR[TYPE_RXPD][TYPE_AVG] = wlan_getavgsnr(priv) * AVG_SCALE; | ||
123 | adapter->NF[TYPE_RXPD][TYPE_AVG] = wlan_getavgnf(priv) * AVG_SCALE; | ||
124 | lbs_pr_debug(1, "After computing SNR: SNR-avg = %d, NF-avg = %d\n", | ||
125 | adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, | ||
126 | adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); | ||
127 | |||
128 | adapter->RSSI[TYPE_RXPD][TYPE_NOAVG] = | ||
129 | CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG], | ||
130 | adapter->NF[TYPE_RXPD][TYPE_NOAVG]); | ||
131 | |||
132 | adapter->RSSI[TYPE_RXPD][TYPE_AVG] = | ||
133 | CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, | ||
134 | adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); | ||
135 | |||
136 | LEAVE(); | ||
137 | } | ||
138 | |||
139 | int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb) | ||
140 | { | ||
141 | lbs_pr_debug(1, "skb->data=%p\n", skb->data); | ||
142 | |||
143 | if(IS_MESH_FRAME(skb)) | ||
144 | skb->dev = priv->mesh_dev; | ||
145 | else | ||
146 | skb->dev = priv->wlan_dev.netdev; | ||
147 | skb->protocol = eth_type_trans(skb, priv->wlan_dev.netdev); | ||
148 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
149 | |||
150 | netif_rx(skb); | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * @brief This function processes received packet and forwards it | ||
157 | * to kernel/upper layer | ||
158 | * | ||
159 | * @param priv A pointer to wlan_private | ||
160 | * @param skb A pointer to skb which includes the received packet | ||
161 | * @return 0 or -1 | ||
162 | */ | ||
163 | int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *skb) | ||
164 | { | ||
165 | wlan_adapter *adapter = priv->adapter; | ||
166 | int ret = 0; | ||
167 | |||
168 | struct rxpackethdr *p_rx_pkt; | ||
169 | struct rxpd *p_rx_pd; | ||
170 | |||
171 | int hdrchop; | ||
172 | struct ethhdr *p_ethhdr; | ||
173 | |||
174 | const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; | ||
175 | |||
176 | ENTER(); | ||
177 | |||
178 | if (priv->adapter->debugmode & MRVDRV_DEBUG_RX_PATH) | ||
179 | lbs_dbg_hex("RX packet: ", skb->data, | ||
180 | min_t(unsigned int, skb->len, 100)); | ||
181 | |||
182 | if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) | ||
183 | return process_rxed_802_11_packet(priv, skb); | ||
184 | |||
185 | p_rx_pkt = (struct rxpackethdr *) skb->data; | ||
186 | p_rx_pd = &p_rx_pkt->rx_pd; | ||
187 | if (p_rx_pd->rx_control & RxPD_MESH_FRAME) | ||
188 | SET_MESH_FRAME(skb); | ||
189 | else | ||
190 | UNSET_MESH_FRAME(skb); | ||
191 | |||
192 | lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, | ||
193 | min_t(unsigned int, skb->len, 100)); | ||
194 | |||
195 | if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { | ||
196 | lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); | ||
197 | priv->stats.rx_length_errors++; | ||
198 | ret = 0; | ||
199 | goto done; | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * Check rxpd status and update 802.3 stat, | ||
204 | */ | ||
205 | if (!(p_rx_pd->status & MRVDRV_RXPD_STATUS_OK)) { | ||
206 | lbs_pr_debug(1, "RX error: frame received with bad status\n"); | ||
207 | lbs_pr_alert("rxpd Not OK\n"); | ||
208 | priv->stats.rx_errors++; | ||
209 | ret = 0; | ||
210 | goto done; | ||
211 | } | ||
212 | |||
213 | lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n", | ||
214 | skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); | ||
215 | |||
216 | lbs_dbg_hex("RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, | ||
217 | sizeof(p_rx_pkt->eth803_hdr.dest_addr)); | ||
218 | lbs_dbg_hex("RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, | ||
219 | sizeof(p_rx_pkt->eth803_hdr.src_addr)); | ||
220 | |||
221 | if (memcmp(&p_rx_pkt->rfc1042_hdr, | ||
222 | rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { | ||
223 | /* | ||
224 | * Replace the 803 header and rfc1042 header (llc/snap) with an | ||
225 | * EthernetII header, keep the src/dst and snap_type (ethertype) | ||
226 | * | ||
227 | * The firmware only passes up SNAP frames converting | ||
228 | * all RX Data from 802.11 to 802.2/LLC/SNAP frames. | ||
229 | * | ||
230 | * To create the Ethernet II, just move the src, dst address right | ||
231 | * before the snap_type. | ||
232 | */ | ||
233 | p_ethhdr = (struct ethhdr *) | ||
234 | ((u8 *) & p_rx_pkt->eth803_hdr | ||
235 | + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) | ||
236 | - sizeof(p_rx_pkt->eth803_hdr.dest_addr) | ||
237 | - sizeof(p_rx_pkt->eth803_hdr.src_addr) | ||
238 | - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); | ||
239 | |||
240 | memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, | ||
241 | sizeof(p_ethhdr->h_source)); | ||
242 | memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, | ||
243 | sizeof(p_ethhdr->h_dest)); | ||
244 | |||
245 | /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header | ||
246 | * that was removed | ||
247 | */ | ||
248 | hdrchop = (u8 *) p_ethhdr - (u8 *) p_rx_pkt; | ||
249 | } else { | ||
250 | lbs_dbg_hex("RX Data: LLC/SNAP", | ||
251 | (u8 *) & p_rx_pkt->rfc1042_hdr, | ||
252 | sizeof(p_rx_pkt->rfc1042_hdr)); | ||
253 | |||
254 | /* Chop off the rxpd */ | ||
255 | hdrchop = (u8 *) & p_rx_pkt->eth803_hdr - (u8 *) p_rx_pkt; | ||
256 | } | ||
257 | |||
258 | /* Chop off the leading header bytes so the skb points to the start of | ||
259 | * either the reconstructed EthII frame or the 802.2/llc/snap frame | ||
260 | */ | ||
261 | skb_pull(skb, hdrchop); | ||
262 | |||
263 | /* Take the data rate from the rxpd structure | ||
264 | * only if the rate is auto | ||
265 | */ | ||
266 | if (adapter->is_datarate_auto) | ||
267 | adapter->datarate = libertas_index_to_data_rate(p_rx_pd->rx_rate); | ||
268 | |||
269 | wlan_compute_rssi(priv, p_rx_pd); | ||
270 | |||
271 | lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); | ||
272 | if (libertas_upload_rx_packet(priv, skb)) { | ||
273 | lbs_pr_debug(1, "RX error: libertas_upload_rx_packet" | ||
274 | " returns failure\n"); | ||
275 | ret = -1; | ||
276 | goto done; | ||
277 | } | ||
278 | priv->stats.rx_bytes += skb->len; | ||
279 | priv->stats.rx_packets++; | ||
280 | |||
281 | ret = 0; | ||
282 | done: | ||
283 | LEAVE(); | ||
284 | |||
285 | return ret; | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * @brief This function converts Tx/Rx rates from the Marvell WLAN format | ||
290 | * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) | ||
291 | * | ||
292 | * @param rate Input rate | ||
293 | * @return Output Rate (0 if invalid) | ||
294 | */ | ||
295 | static u8 convert_mv_rate_to_radiotap(u8 rate) | ||
296 | { | ||
297 | switch (rate) { | ||
298 | case 0: /* 1 Mbps */ | ||
299 | return 2; | ||
300 | case 1: /* 2 Mbps */ | ||
301 | return 4; | ||
302 | case 2: /* 5.5 Mbps */ | ||
303 | return 11; | ||
304 | case 3: /* 11 Mbps */ | ||
305 | return 22; | ||
306 | case 4: /* 6 Mbps */ | ||
307 | return 12; | ||
308 | case 5: /* 9 Mbps */ | ||
309 | return 18; | ||
310 | case 6: /* 12 Mbps */ | ||
311 | return 24; | ||
312 | case 7: /* 18 Mbps */ | ||
313 | return 36; | ||
314 | case 8: /* 24 Mbps */ | ||
315 | return 48; | ||
316 | case 9: /* 36 Mbps */ | ||
317 | return 72; | ||
318 | case 10: /* 48 Mbps */ | ||
319 | return 96; | ||
320 | case 11: /* 54 Mbps */ | ||
321 | return 108; | ||
322 | } | ||
323 | lbs_pr_alert( "Invalid Marvell WLAN rate (%i)\n", rate); | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * @brief This function processes a received 802.11 packet and forwards it | ||
329 | * to kernel/upper layer | ||
330 | * | ||
331 | * @param priv A pointer to wlan_private | ||
332 | * @param skb A pointer to skb which includes the received packet | ||
333 | * @return 0 or -1 | ||
334 | */ | ||
335 | static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb) | ||
336 | { | ||
337 | wlan_adapter *adapter = priv->adapter; | ||
338 | int ret = 0; | ||
339 | |||
340 | struct rx80211packethdr *p_rx_pkt; | ||
341 | struct rxpd *prxpd; | ||
342 | struct rx_radiotap_hdr radiotap_hdr; | ||
343 | struct rx_radiotap_hdr *pradiotap_hdr; | ||
344 | |||
345 | ENTER(); | ||
346 | |||
347 | p_rx_pkt = (struct rx80211packethdr *) skb->data; | ||
348 | prxpd = &p_rx_pkt->rx_pd; | ||
349 | |||
350 | // lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); | ||
351 | |||
352 | if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { | ||
353 | lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); | ||
354 | priv->stats.rx_length_errors++; | ||
355 | ret = 0; | ||
356 | goto done; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * Check rxpd status and update 802.3 stat, | ||
361 | */ | ||
362 | if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) { | ||
363 | //lbs_pr_debug(1, "RX error: frame received with bad status\n"); | ||
364 | priv->stats.rx_errors++; | ||
365 | } | ||
366 | |||
367 | lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n", | ||
368 | skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); | ||
369 | |||
370 | /* create the exported radio header */ | ||
371 | switch (priv->adapter->radiomode) { | ||
372 | case WLAN_RADIOMODE_NONE: | ||
373 | /* no radio header */ | ||
374 | /* chop the rxpd */ | ||
375 | skb_pull(skb, sizeof(struct rxpd)); | ||
376 | break; | ||
377 | |||
378 | case WLAN_RADIOMODE_RADIOTAP: | ||
379 | /* radiotap header */ | ||
380 | radiotap_hdr.hdr.it_version = 0; | ||
381 | /* XXX must check this value for pad */ | ||
382 | radiotap_hdr.hdr.it_pad = 0; | ||
383 | radiotap_hdr.hdr.it_len = sizeof(struct rx_radiotap_hdr); | ||
384 | radiotap_hdr.hdr.it_present = RX_RADIOTAP_PRESENT; | ||
385 | /* unknown values */ | ||
386 | radiotap_hdr.flags = 0; | ||
387 | radiotap_hdr.chan_freq = 0; | ||
388 | radiotap_hdr.chan_flags = 0; | ||
389 | radiotap_hdr.antenna = 0; | ||
390 | /* known values */ | ||
391 | radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); | ||
392 | /* XXX must check no carryout */ | ||
393 | radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; | ||
394 | radiotap_hdr.rx_flags = 0; | ||
395 | if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) | ||
396 | radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS; | ||
397 | //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18); | ||
398 | |||
399 | // lbs_dbg_hex1("RX radiomode packet BEF: ", skb->data, min(skb->len, 100)); | ||
400 | |||
401 | /* chop the rxpd */ | ||
402 | skb_pull(skb, sizeof(struct rxpd)); | ||
403 | |||
404 | /* add space for the new radio header */ | ||
405 | if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && | ||
406 | pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, | ||
407 | GFP_ATOMIC)) { | ||
408 | lbs_pr_alert( "%s: couldn't pskb_expand_head\n", | ||
409 | __func__); | ||
410 | } | ||
411 | |||
412 | pradiotap_hdr = | ||
413 | (struct rx_radiotap_hdr *)skb_push(skb, | ||
414 | sizeof(struct | ||
415 | rx_radiotap_hdr)); | ||
416 | memcpy(pradiotap_hdr, &radiotap_hdr, | ||
417 | sizeof(struct rx_radiotap_hdr)); | ||
418 | //lbs_dbg_hex1("RX radiomode packet AFT: ", skb->data, min(skb->len, 100)); | ||
419 | break; | ||
420 | |||
421 | default: | ||
422 | /* unknown header */ | ||
423 | lbs_pr_alert( "Unknown radiomode (%i)\n", | ||
424 | priv->adapter->radiomode); | ||
425 | /* don't export any header */ | ||
426 | /* chop the rxpd */ | ||
427 | skb_pull(skb, sizeof(struct rxpd)); | ||
428 | break; | ||
429 | } | ||
430 | |||
431 | /* Take the data rate from the rxpd structure | ||
432 | * only if the rate is auto | ||
433 | */ | ||
434 | if (adapter->is_datarate_auto) { | ||
435 | adapter->datarate = libertas_index_to_data_rate(prxpd->rx_rate); | ||
436 | } | ||
437 | |||
438 | wlan_compute_rssi(priv, prxpd); | ||
439 | |||
440 | lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); | ||
441 | |||
442 | if (libertas_upload_rx_packet(priv, skb)) { | ||
443 | lbs_pr_debug(1, "RX error: libertas_upload_rx_packet " | ||
444 | "returns failure\n"); | ||
445 | ret = -1; | ||
446 | goto done; | ||
447 | } | ||
448 | |||
449 | priv->stats.rx_bytes += skb->len; | ||
450 | priv->stats.rx_packets++; | ||
451 | |||
452 | ret = 0; | ||
453 | done: | ||
454 | LEAVE(); | ||
455 | |||
456 | skb->protocol = __constant_htons(0x0019); /* ETH_P_80211_RAW */ | ||
457 | |||
458 | return (ret); | ||
459 | } | ||