3 Broadcom BCM43xx wireless driver
5 Transmission (TX/RX) related functions.
7 Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
8 Stefano Brivio <st3@riseup.net>
9 Michael Buesch <mbuesch@freenet.de>
10 Danny van Dyk <kugelfang@gentoo.org>
11 Andreas Jaggi <andreas.jaggi@waterwave.ch>
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; see the file COPYING. If not, write to
25 the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
26 Boston, MA 02110-1301, USA.
30 #include "bcm43xx_xmit.h"
32 #include <linux/etherdevice.h>
35 /* Extract the bitrate out of a CCK PLCP header. */
36 static u8
bcm43xx_plcp_get_bitrate_cck(struct bcm43xx_plcp_hdr4
*plcp
)
38 switch (plcp
->raw
[0]) {
40 return IEEE80211_CCK_RATE_1MB
;
42 return IEEE80211_CCK_RATE_2MB
;
44 return IEEE80211_CCK_RATE_5MB
;
46 return IEEE80211_CCK_RATE_11MB
;
52 /* Extract the bitrate out of an OFDM PLCP header. */
53 static u8
bcm43xx_plcp_get_bitrate_ofdm(struct bcm43xx_plcp_hdr4
*plcp
)
55 switch (plcp
->raw
[0] & 0xF) {
57 return IEEE80211_OFDM_RATE_6MB
;
59 return IEEE80211_OFDM_RATE_9MB
;
61 return IEEE80211_OFDM_RATE_12MB
;
63 return IEEE80211_OFDM_RATE_18MB
;
65 return IEEE80211_OFDM_RATE_24MB
;
67 return IEEE80211_OFDM_RATE_36MB
;
69 return IEEE80211_OFDM_RATE_48MB
;
71 return IEEE80211_OFDM_RATE_54MB
;
77 u8
bcm43xx_plcp_get_ratecode_cck(const u8 bitrate
)
80 case IEEE80211_CCK_RATE_1MB
:
82 case IEEE80211_CCK_RATE_2MB
:
84 case IEEE80211_CCK_RATE_5MB
:
86 case IEEE80211_CCK_RATE_11MB
:
93 u8
bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate
)
96 case IEEE80211_OFDM_RATE_6MB
:
98 case IEEE80211_OFDM_RATE_9MB
:
100 case IEEE80211_OFDM_RATE_12MB
:
102 case IEEE80211_OFDM_RATE_18MB
:
104 case IEEE80211_OFDM_RATE_24MB
:
106 case IEEE80211_OFDM_RATE_36MB
:
108 case IEEE80211_OFDM_RATE_48MB
:
110 case IEEE80211_OFDM_RATE_54MB
:
117 static void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4
*plcp
,
118 const u16 octets
, const u8 bitrate
,
119 const int ofdm_modulation
)
121 __le32
*data
= &(plcp
->data
);
122 __u8
*raw
= plcp
->raw
;
124 if (ofdm_modulation
) {
125 u32 val
= bcm43xx_plcp_get_ratecode_ofdm(bitrate
);
126 assert(!(octets
& 0xF000));
127 val
|= (octets
<< 5);
128 *data
= cpu_to_le32(val
);
132 plen
= octets
* 16 / bitrate
;
133 if ((octets
* 16 % bitrate
) > 0) {
135 if ((bitrate
== IEEE80211_CCK_RATE_11MB
)
136 && ((octets
* 8 % 11) < 4)) {
142 *data
|= cpu_to_le32(plen
<< 16);
143 raw
[0] = bcm43xx_plcp_get_ratecode_cck(bitrate
);
147 static u8
bcm43xx_calc_fallback_rate(u8 bitrate
)
150 case IEEE80211_CCK_RATE_1MB
:
151 return IEEE80211_CCK_RATE_1MB
;
152 case IEEE80211_CCK_RATE_2MB
:
153 return IEEE80211_CCK_RATE_1MB
;
154 case IEEE80211_CCK_RATE_5MB
:
155 return IEEE80211_CCK_RATE_2MB
;
156 case IEEE80211_CCK_RATE_11MB
:
157 return IEEE80211_CCK_RATE_5MB
;
158 case IEEE80211_OFDM_RATE_6MB
:
159 return IEEE80211_CCK_RATE_5MB
;
160 case IEEE80211_OFDM_RATE_9MB
:
161 return IEEE80211_OFDM_RATE_6MB
;
162 case IEEE80211_OFDM_RATE_12MB
:
163 return IEEE80211_OFDM_RATE_9MB
;
164 case IEEE80211_OFDM_RATE_18MB
:
165 return IEEE80211_OFDM_RATE_12MB
;
166 case IEEE80211_OFDM_RATE_24MB
:
167 return IEEE80211_OFDM_RATE_18MB
;
168 case IEEE80211_OFDM_RATE_36MB
:
169 return IEEE80211_OFDM_RATE_24MB
;
170 case IEEE80211_OFDM_RATE_48MB
:
171 return IEEE80211_OFDM_RATE_36MB
;
172 case IEEE80211_OFDM_RATE_54MB
:
173 return IEEE80211_OFDM_RATE_48MB
;
180 __le16
bcm43xx_calc_duration_id(const struct ieee80211_hdr
*wireless_header
,
183 const u16 frame_ctl
= le16_to_cpu(wireless_header
->frame_ctl
);
184 __le16 duration_id
= wireless_header
->duration_id
;
186 switch (WLAN_FC_GET_TYPE(frame_ctl
)) {
187 case IEEE80211_FTYPE_DATA
:
188 case IEEE80211_FTYPE_MGMT
:
189 //TODO: Steal the code from ieee80211, once it is completed there.
191 case IEEE80211_FTYPE_CTL
:
192 /* Use the original duration/id. */
202 u16
ceiling_div(u16 dividend
, u16 divisor
)
204 return ((dividend
+ divisor
- 1) / divisor
);
207 static void bcm43xx_generate_rts(const struct bcm43xx_phyinfo
*phy
,
208 struct bcm43xx_txhdr
*txhdr
,
211 const struct ieee80211_hdr_4addr
*wlhdr
)
217 int fallback_ofdm_modulation
;
221 //FIXME sa = ieee80211_get_SA((struct ieee80211_hdr *)wlhdr);
222 //FIXME da = ieee80211_get_DA((struct ieee80211_hdr *)wlhdr);
223 fallback_bitrate
= bcm43xx_calc_fallback_rate(bitrate
);
224 ofdm_modulation
= !(ieee80211_is_cck_rate(bitrate
));
225 fallback_ofdm_modulation
= !(ieee80211_is_cck_rate(fallback_bitrate
));
227 flen
= sizeof(u16
) + sizeof(u16
) + ETH_ALEN
+ ETH_ALEN
+ IEEE80211_FCS_LEN
,
228 bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4
*)(&txhdr
->rts_cts_plcp
),
230 !ieee80211_is_cck_rate(bitrate
));
231 bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4
*)(&txhdr
->rts_cts_fallback_plcp
),
232 flen
, fallback_bitrate
,
233 !ieee80211_is_cck_rate(fallback_bitrate
));
234 fctl
= IEEE80211_FTYPE_CTL
;
235 fctl
|= IEEE80211_STYPE_RTS
;
236 dur
= le16_to_cpu(wlhdr
->duration_id
);
237 /*FIXME: should we test for dur==0 here and let it unmodified in this case?
238 * The following assert checks for this case...
241 /*FIXME: The duration calculation is not really correct.
242 * I am not 100% sure which bitrate to use. We use the RTS rate here,
243 * but this is likely to be wrong.
245 if (phy
->type
== BCM43xx_PHYTYPE_A
) {
246 /* Three times SIFS */
248 /* Add ACK duration. */
249 dur
+= ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10,
251 /* Add CTS duration. */
252 dur
+= ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10,
255 /* Three times SIFS */
257 /* Add ACK duration. */
258 dur
+= ceiling_div(8 * (14 /*bytes*/) * 10,
260 /* Add CTS duration. */
261 dur
+= ceiling_div(8 * (14 /*bytes*/) * 10,
265 txhdr
->rts_cts_frame_control
= cpu_to_le16(fctl
);
266 txhdr
->rts_cts_dur
= cpu_to_le16(dur
);
267 //printk(BCM43xx_MACFMT " " BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(wlhdr->addr1), BCM43xx_MACARG(wlhdr->addr2), BCM43xx_MACARG(wlhdr->addr3));
268 //printk(BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(sa), BCM43xx_MACARG(da));
269 memcpy(txhdr
->rts_cts_mac1
, wlhdr
->addr1
, ETH_ALEN
);//FIXME!
270 // memcpy(txhdr->rts_cts_mac2, sa, ETH_ALEN);
272 *flags
|= BCM43xx_TXHDRFLAG_RTSCTS
;
273 *flags
|= BCM43xx_TXHDRFLAG_RTS
;
275 *flags
|= BCM43xx_TXHDRFLAG_RTSCTS_OFDM
;
276 if (fallback_ofdm_modulation
)
277 *flags
|= BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM
;
280 void bcm43xx_generate_txhdr(struct bcm43xx_private
*bcm
,
281 struct bcm43xx_txhdr
*txhdr
,
282 const unsigned char *fragment_data
,
283 const unsigned int fragment_len
,
284 const int is_first_fragment
,
287 const struct bcm43xx_phyinfo
*phy
= bcm43xx_current_phy(bcm
);
288 const struct ieee80211_hdr_4addr
*wireless_header
= (const struct ieee80211_hdr_4addr
*)fragment_data
;
289 const struct ieee80211_security
*secinfo
= &bcm
->ieee
->sec
;
293 int fallback_ofdm_modulation
;
294 u16 plcp_fragment_len
= fragment_len
;
299 const u16 ftype
= WLAN_FC_GET_TYPE(le16_to_cpu(wireless_header
->frame_ctl
));
300 const int is_mgt
= (ftype
== IEEE80211_FTYPE_MGMT
);
302 /* Now construct the TX header. */
303 memset(txhdr
, 0, sizeof(*txhdr
));
305 bitrate
= ieee80211softmac_suggest_txrate(bcm
->softmac
,
306 is_multicast_ether_addr(wireless_header
->addr1
), is_mgt
);
307 ofdm_modulation
= !(ieee80211_is_cck_rate(bitrate
));
308 fallback_bitrate
= bcm43xx_calc_fallback_rate(bitrate
);
309 fallback_ofdm_modulation
= !(ieee80211_is_cck_rate(fallback_bitrate
));
311 /* Set Frame Control from 80211 header. */
312 txhdr
->frame_control
= wireless_header
->frame_ctl
;
313 /* Copy address1 from 80211 header. */
314 memcpy(txhdr
->mac1
, wireless_header
->addr1
, 6);
315 /* Set the fallback duration ID. */
316 txhdr
->fallback_dur_id
= bcm43xx_calc_duration_id((const struct ieee80211_hdr
*)wireless_header
,
318 /* Set the cookie (used as driver internal ID for the frame) */
319 txhdr
->cookie
= cpu_to_le16(cookie
);
321 /* Hardware appends FCS. */
322 plcp_fragment_len
+= IEEE80211_FCS_LEN
;
324 /* Hardware encryption. */
325 encrypt_frame
= le16_to_cpup(&wireless_header
->frame_ctl
) & IEEE80211_FCTL_PROTECTED
;
326 if (encrypt_frame
&& !bcm
->ieee
->host_encrypt
) {
327 const struct ieee80211_hdr_3addr
*hdr
= (struct ieee80211_hdr_3addr
*)wireless_header
;
328 memcpy(txhdr
->wep_iv
, hdr
->payload
, 4);
329 /* Hardware appends ICV. */
330 plcp_fragment_len
+= 4;
332 wsec_rate
|= (bcm
->key
[secinfo
->active_key
].algorithm
<< BCM43xx_TXHDR_WSEC_ALGO_SHIFT
)
333 & BCM43xx_TXHDR_WSEC_ALGO_MASK
;
334 wsec_rate
|= (secinfo
->active_key
<< BCM43xx_TXHDR_WSEC_KEYINDEX_SHIFT
)
335 & BCM43xx_TXHDR_WSEC_KEYINDEX_MASK
;
338 /* Generate the PLCP header and the fallback PLCP header. */
339 bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4
*)(&txhdr
->plcp
),
341 bitrate
, ofdm_modulation
);
342 bcm43xx_generate_plcp_hdr(&txhdr
->fallback_plcp
, plcp_fragment_len
,
343 fallback_bitrate
, fallback_ofdm_modulation
);
345 /* Set the CONTROL field */
347 control
|= BCM43xx_TXHDRCTL_OFDM
;
348 if (bcm
->short_preamble
) //FIXME: could be the other way around, please test
349 control
|= BCM43xx_TXHDRCTL_SHORT_PREAMBLE
;
350 control
|= (phy
->antenna_diversity
<< BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT
)
351 & BCM43xx_TXHDRCTL_ANTENNADIV_MASK
;
353 /* Set the FLAGS field */
354 if (!is_multicast_ether_addr(wireless_header
->addr1
) &&
355 !is_broadcast_ether_addr(wireless_header
->addr1
))
356 flags
|= BCM43xx_TXHDRFLAG_EXPECTACK
;
357 if (1 /* FIXME: PS poll?? */)
358 flags
|= 0x10; // FIXME: unknown meaning.
359 if (fallback_ofdm_modulation
)
360 flags
|= BCM43xx_TXHDRFLAG_FALLBACKOFDM
;
361 if (is_first_fragment
)
362 flags
|= BCM43xx_TXHDRFLAG_FIRSTFRAGMENT
;
364 /* Set WSEC/RATE field */
365 wsec_rate
|= (txhdr
->plcp
.raw
[0] << BCM43xx_TXHDR_RATE_SHIFT
)
366 & BCM43xx_TXHDR_RATE_MASK
;
368 /* Generate the RTS/CTS packet, if required. */
369 /* FIXME: We should first try with CTS-to-self,
370 * if we are on 80211g. If we get too many
371 * failures (hidden nodes), we should switch back to RTS/CTS.
373 if (0/*FIXME txctl->use_rts_cts*/) {
374 bcm43xx_generate_rts(phy
, txhdr
, &flags
,
375 0/*FIXME txctl->rts_cts_rate*/,
379 txhdr
->flags
= cpu_to_le16(flags
);
380 txhdr
->control
= cpu_to_le16(control
);
381 txhdr
->wsec_rate
= cpu_to_le16(wsec_rate
);
384 static s8
bcm43xx_rssi_postprocess(struct bcm43xx_private
*bcm
,
385 u8 in_rssi
, int ofdm
,
386 int adjust_2053
, int adjust_2050
)
388 struct bcm43xx_radioinfo
*radio
= bcm43xx_current_radio(bcm
);
389 struct bcm43xx_phyinfo
*phy
= bcm43xx_current_phy(bcm
);
392 switch (radio
->version
) {
405 if (bcm
->sprom
.boardflags
& BCM43xx_BFL_RSSI
) {
408 tmp
= radio
->nrssi_lt
[in_rssi
];
420 if (phy
->type
== BCM43xx_PHYTYPE_G
&&
447 static s8
bcm43xx_rssinoise_postprocess(struct bcm43xx_private
*bcm
,
450 struct bcm43xx_phyinfo
*phy
= bcm43xx_current_phy(bcm
);
453 if (phy
->type
== BCM43xx_PHYTYPE_A
) {
454 //TODO: Incomplete specs.
457 ret
= bcm43xx_rssi_postprocess(bcm
, in_rssi
, 0, 1, 1);
463 int bcm43xx_rx(struct bcm43xx_private
*bcm
,
465 struct bcm43xx_rxhdr
*rxhdr
)
467 struct bcm43xx_radioinfo
*radio
= bcm43xx_current_radio(bcm
);
468 struct bcm43xx_phyinfo
*phy
= bcm43xx_current_phy(bcm
);
469 struct bcm43xx_plcp_hdr4
*plcp
;
470 struct ieee80211_rx_stats stats
;
471 struct ieee80211_hdr_4addr
*wlhdr
;
473 int is_packet_for_us
= 0;
475 const u16 rxflags1
= le16_to_cpu(rxhdr
->flags1
);
476 const u16 rxflags2
= le16_to_cpu(rxhdr
->flags2
);
477 const u16 rxflags3
= le16_to_cpu(rxhdr
->flags3
);
478 const int is_ofdm
= !!(rxflags1
& BCM43xx_RXHDR_FLAGS1_OFDM
);
480 if (rxflags2
& BCM43xx_RXHDR_FLAGS2_TYPE2FRAME
) {
481 plcp
= (struct bcm43xx_plcp_hdr4
*)(skb
->data
+ 2);
482 /* Skip two unknown bytes and the PLCP header. */
483 skb_pull(skb
, 2 + sizeof(struct bcm43xx_plcp_hdr6
));
485 plcp
= (struct bcm43xx_plcp_hdr4
*)(skb
->data
);
486 /* Skip the PLCP header. */
487 skb_pull(skb
, sizeof(struct bcm43xx_plcp_hdr6
));
489 /* The SKB contains the PAYLOAD (wireless header + data)
490 * at this point. The FCS at the end is stripped.
493 memset(&stats
, 0, sizeof(stats
));
494 stats
.mac_time
= le16_to_cpu(rxhdr
->mactime
);
495 stats
.rssi
= rxhdr
->rssi
;
496 stats
.signal
= bcm43xx_rssi_postprocess(bcm
, rxhdr
->rssi
, is_ofdm
,
497 !!(rxflags1
& BCM43xx_RXHDR_FLAGS1_2053RSSIADJ
),
498 !!(rxflags3
& BCM43xx_RXHDR_FLAGS3_2050RSSIADJ
));
499 stats
.noise
= bcm
->stats
.noise
;
501 stats
.rate
= bcm43xx_plcp_get_bitrate_ofdm(plcp
);
503 stats
.rate
= bcm43xx_plcp_get_bitrate_cck(plcp
);
504 stats
.received_channel
= radio
->channel
;
505 stats
.mask
= IEEE80211_STATMASK_SIGNAL
|
506 IEEE80211_STATMASK_NOISE
|
507 IEEE80211_STATMASK_RATE
|
508 IEEE80211_STATMASK_RSSI
;
509 if (phy
->type
== BCM43xx_PHYTYPE_A
)
510 stats
.freq
= IEEE80211_52GHZ_BAND
;
512 stats
.freq
= IEEE80211_24GHZ_BAND
;
513 stats
.len
= skb
->len
;
515 bcm
->stats
.last_rx
= jiffies
;
516 if (bcm
->ieee
->iw_mode
== IW_MODE_MONITOR
) {
517 err
= ieee80211_rx(bcm
->ieee
, skb
, &stats
);
518 return (err
== 0) ? -EINVAL
: 0;
521 wlhdr
= (struct ieee80211_hdr_4addr
*)(skb
->data
);
523 switch (bcm
->ieee
->iw_mode
) {
525 if (memcmp(wlhdr
->addr1
, bcm
->net_dev
->dev_addr
, ETH_ALEN
) == 0 ||
526 memcmp(wlhdr
->addr3
, bcm
->ieee
->bssid
, ETH_ALEN
) == 0 ||
527 is_broadcast_ether_addr(wlhdr
->addr1
) ||
528 is_multicast_ether_addr(wlhdr
->addr1
) ||
529 bcm
->net_dev
->flags
& IFF_PROMISC
)
530 is_packet_for_us
= 1;
534 /* When receiving multicast or broadcast packets, filter out
535 the packets we send ourself; we shouldn't see those */
536 if (memcmp(wlhdr
->addr3
, bcm
->ieee
->bssid
, ETH_ALEN
) == 0 ||
537 memcmp(wlhdr
->addr1
, bcm
->net_dev
->dev_addr
, ETH_ALEN
) == 0 ||
538 (memcmp(wlhdr
->addr3
, bcm
->net_dev
->dev_addr
, ETH_ALEN
) &&
539 (is_broadcast_ether_addr(wlhdr
->addr1
) ||
540 is_multicast_ether_addr(wlhdr
->addr1
) ||
541 bcm
->net_dev
->flags
& IFF_PROMISC
)))
542 is_packet_for_us
= 1;
546 frame_ctl
= le16_to_cpu(wlhdr
->frame_ctl
);
547 switch (WLAN_FC_GET_TYPE(frame_ctl
)) {
548 case IEEE80211_FTYPE_MGMT
:
549 ieee80211_rx_mgt(bcm
->ieee
, wlhdr
, &stats
);
551 case IEEE80211_FTYPE_DATA
:
552 if (is_packet_for_us
) {
553 err
= ieee80211_rx(bcm
->ieee
, skb
, &stats
);
554 err
= (err
== 0) ? -EINVAL
: 0;
557 case IEEE80211_FTYPE_CTL
: