|  | /* | 
|  |  | 
|  | Broadcom BCM43xx wireless driver | 
|  |  | 
|  | Transmission (TX/RX) related functions. | 
|  |  | 
|  | Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, | 
|  | Stefano Brivio <st3@riseup.net> | 
|  | Michael Buesch <mbuesch@freenet.de> | 
|  | Danny van Dyk <kugelfang@gentoo.org> | 
|  | Andreas Jaggi <andreas.jaggi@waterwave.ch> | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 2 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program; see the file COPYING.  If not, write to | 
|  | the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, | 
|  | Boston, MA 02110-1301, USA. | 
|  |  | 
|  | */ | 
|  |  | 
|  | #include "bcm43xx_xmit.h" | 
|  |  | 
|  | #include <linux/etherdevice.h> | 
|  |  | 
|  |  | 
|  | /* Extract the bitrate out of a CCK PLCP header. */ | 
|  | static u8 bcm43xx_plcp_get_bitrate_cck(struct bcm43xx_plcp_hdr4 *plcp) | 
|  | { | 
|  | switch (plcp->raw[0]) { | 
|  | case 0x0A: | 
|  | return IEEE80211_CCK_RATE_1MB; | 
|  | case 0x14: | 
|  | return IEEE80211_CCK_RATE_2MB; | 
|  | case 0x37: | 
|  | return IEEE80211_CCK_RATE_5MB; | 
|  | case 0x6E: | 
|  | return IEEE80211_CCK_RATE_11MB; | 
|  | } | 
|  | assert(0); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Extract the bitrate out of an OFDM PLCP header. */ | 
|  | static u8 bcm43xx_plcp_get_bitrate_ofdm(struct bcm43xx_plcp_hdr4 *plcp) | 
|  | { | 
|  | switch (plcp->raw[0] & 0xF) { | 
|  | case 0xB: | 
|  | return IEEE80211_OFDM_RATE_6MB; | 
|  | case 0xF: | 
|  | return IEEE80211_OFDM_RATE_9MB; | 
|  | case 0xA: | 
|  | return IEEE80211_OFDM_RATE_12MB; | 
|  | case 0xE: | 
|  | return IEEE80211_OFDM_RATE_18MB; | 
|  | case 0x9: | 
|  | return IEEE80211_OFDM_RATE_24MB; | 
|  | case 0xD: | 
|  | return IEEE80211_OFDM_RATE_36MB; | 
|  | case 0x8: | 
|  | return IEEE80211_OFDM_RATE_48MB; | 
|  | case 0xC: | 
|  | return IEEE80211_OFDM_RATE_54MB; | 
|  | } | 
|  | assert(0); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate) | 
|  | { | 
|  | switch (bitrate) { | 
|  | case IEEE80211_CCK_RATE_1MB: | 
|  | return 0x0A; | 
|  | case IEEE80211_CCK_RATE_2MB: | 
|  | return 0x14; | 
|  | case IEEE80211_CCK_RATE_5MB: | 
|  | return 0x37; | 
|  | case IEEE80211_CCK_RATE_11MB: | 
|  | return 0x6E; | 
|  | } | 
|  | assert(0); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate) | 
|  | { | 
|  | switch (bitrate) { | 
|  | case IEEE80211_OFDM_RATE_6MB: | 
|  | return 0xB; | 
|  | case IEEE80211_OFDM_RATE_9MB: | 
|  | return 0xF; | 
|  | case IEEE80211_OFDM_RATE_12MB: | 
|  | return 0xA; | 
|  | case IEEE80211_OFDM_RATE_18MB: | 
|  | return 0xE; | 
|  | case IEEE80211_OFDM_RATE_24MB: | 
|  | return 0x9; | 
|  | case IEEE80211_OFDM_RATE_36MB: | 
|  | return 0xD; | 
|  | case IEEE80211_OFDM_RATE_48MB: | 
|  | return 0x8; | 
|  | case IEEE80211_OFDM_RATE_54MB: | 
|  | return 0xC; | 
|  | } | 
|  | assert(0); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp, | 
|  | const u16 octets, const u8 bitrate, | 
|  | const int ofdm_modulation) | 
|  | { | 
|  | __le32 *data = &(plcp->data); | 
|  | __u8 *raw = plcp->raw; | 
|  |  | 
|  | if (ofdm_modulation) { | 
|  | *data = bcm43xx_plcp_get_ratecode_ofdm(bitrate); | 
|  | assert(!(octets & 0xF000)); | 
|  | *data |= (octets << 5); | 
|  | *data = cpu_to_le32(*data); | 
|  | } else { | 
|  | u32 plen; | 
|  |  | 
|  | plen = octets * 16 / bitrate; | 
|  | if ((octets * 16 % bitrate) > 0) { | 
|  | plen++; | 
|  | if ((bitrate == IEEE80211_CCK_RATE_11MB) | 
|  | && ((octets * 8 % 11) < 4)) { | 
|  | raw[1] = 0x84; | 
|  | } else | 
|  | raw[1] = 0x04; | 
|  | } else | 
|  | raw[1] = 0x04; | 
|  | *data |= cpu_to_le32(plen << 16); | 
|  | raw[0] = bcm43xx_plcp_get_ratecode_cck(bitrate); | 
|  | } | 
|  | } | 
|  |  | 
|  | static u8 bcm43xx_calc_fallback_rate(u8 bitrate) | 
|  | { | 
|  | switch (bitrate) { | 
|  | case IEEE80211_CCK_RATE_1MB: | 
|  | return IEEE80211_CCK_RATE_1MB; | 
|  | case IEEE80211_CCK_RATE_2MB: | 
|  | return IEEE80211_CCK_RATE_1MB; | 
|  | case IEEE80211_CCK_RATE_5MB: | 
|  | return IEEE80211_CCK_RATE_2MB; | 
|  | case IEEE80211_CCK_RATE_11MB: | 
|  | return IEEE80211_CCK_RATE_5MB; | 
|  | case IEEE80211_OFDM_RATE_6MB: | 
|  | return IEEE80211_CCK_RATE_5MB; | 
|  | case IEEE80211_OFDM_RATE_9MB: | 
|  | return IEEE80211_OFDM_RATE_6MB; | 
|  | case IEEE80211_OFDM_RATE_12MB: | 
|  | return IEEE80211_OFDM_RATE_9MB; | 
|  | case IEEE80211_OFDM_RATE_18MB: | 
|  | return IEEE80211_OFDM_RATE_12MB; | 
|  | case IEEE80211_OFDM_RATE_24MB: | 
|  | return IEEE80211_OFDM_RATE_18MB; | 
|  | case IEEE80211_OFDM_RATE_36MB: | 
|  | return IEEE80211_OFDM_RATE_24MB; | 
|  | case IEEE80211_OFDM_RATE_48MB: | 
|  | return IEEE80211_OFDM_RATE_36MB; | 
|  | case IEEE80211_OFDM_RATE_54MB: | 
|  | return IEEE80211_OFDM_RATE_48MB; | 
|  | } | 
|  | assert(0); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static | 
|  | __le16 bcm43xx_calc_duration_id(const struct ieee80211_hdr *wireless_header, | 
|  | u8 bitrate) | 
|  | { | 
|  | const u16 frame_ctl = le16_to_cpu(wireless_header->frame_ctl); | 
|  | __le16 duration_id = wireless_header->duration_id; | 
|  |  | 
|  | switch (WLAN_FC_GET_TYPE(frame_ctl)) { | 
|  | case IEEE80211_FTYPE_DATA: | 
|  | case IEEE80211_FTYPE_MGMT: | 
|  | //TODO: Steal the code from ieee80211, once it is completed there. | 
|  | break; | 
|  | case IEEE80211_FTYPE_CTL: | 
|  | /* Use the original duration/id. */ | 
|  | break; | 
|  | default: | 
|  | assert(0); | 
|  | } | 
|  |  | 
|  | return duration_id; | 
|  | } | 
|  |  | 
|  | static inline | 
|  | u16 ceiling_div(u16 dividend, u16 divisor) | 
|  | { | 
|  | return ((dividend + divisor - 1) / divisor); | 
|  | } | 
|  |  | 
|  | static void bcm43xx_generate_rts(const struct bcm43xx_phyinfo *phy, | 
|  | struct bcm43xx_txhdr *txhdr, | 
|  | u16 *flags, | 
|  | u8 bitrate, | 
|  | const struct ieee80211_hdr_4addr *wlhdr) | 
|  | { | 
|  | u16 fctl; | 
|  | u16 dur; | 
|  | u8 fallback_bitrate; | 
|  | int ofdm_modulation; | 
|  | int fallback_ofdm_modulation; | 
|  | //	u8 *sa, *da; | 
|  | u16 flen; | 
|  |  | 
|  | //FIXME	sa = ieee80211_get_SA((struct ieee80211_hdr *)wlhdr); | 
|  | //FIXME	da = ieee80211_get_DA((struct ieee80211_hdr *)wlhdr); | 
|  | fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate); | 
|  | ofdm_modulation = !(ieee80211_is_cck_rate(bitrate)); | 
|  | fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate)); | 
|  |  | 
|  | flen = sizeof(u16) + sizeof(u16) + ETH_ALEN + ETH_ALEN + IEEE80211_FCS_LEN, | 
|  | bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_plcp), | 
|  | flen, bitrate, | 
|  | !ieee80211_is_cck_rate(bitrate)); | 
|  | bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_fallback_plcp), | 
|  | flen, fallback_bitrate, | 
|  | !ieee80211_is_cck_rate(fallback_bitrate)); | 
|  | fctl = IEEE80211_FTYPE_CTL; | 
|  | fctl |= IEEE80211_STYPE_RTS; | 
|  | dur = le16_to_cpu(wlhdr->duration_id); | 
|  | /*FIXME: should we test for dur==0 here and let it unmodified in this case? | 
|  | *       The following assert checks for this case... | 
|  | */ | 
|  | assert(dur); | 
|  | /*FIXME: The duration calculation is not really correct. | 
|  | *       I am not 100% sure which bitrate to use. We use the RTS rate here, | 
|  | *       but this is likely to be wrong. | 
|  | */ | 
|  | if (phy->type == BCM43xx_PHYTYPE_A) { | 
|  | /* Three times SIFS */ | 
|  | dur += 16 * 3; | 
|  | /* Add ACK duration. */ | 
|  | dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10, | 
|  | bitrate * 4); | 
|  | /* Add CTS duration. */ | 
|  | dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10, | 
|  | bitrate * 4); | 
|  | } else { | 
|  | /* Three times SIFS */ | 
|  | dur += 10 * 3; | 
|  | /* Add ACK duration. */ | 
|  | dur += ceiling_div(8 * (14 /*bytes*/) * 10, | 
|  | bitrate); | 
|  | /* Add CTS duration. */ | 
|  | dur += ceiling_div(8 * (14 /*bytes*/) * 10, | 
|  | bitrate); | 
|  | } | 
|  |  | 
|  | txhdr->rts_cts_frame_control = cpu_to_le16(fctl); | 
|  | txhdr->rts_cts_dur = cpu_to_le16(dur); | 
|  | //printk(BCM43xx_MACFMT "  " BCM43xx_MACFMT "  " BCM43xx_MACFMT "\n", BCM43xx_MACARG(wlhdr->addr1), BCM43xx_MACARG(wlhdr->addr2), BCM43xx_MACARG(wlhdr->addr3)); | 
|  | //printk(BCM43xx_MACFMT "  " BCM43xx_MACFMT "\n", BCM43xx_MACARG(sa), BCM43xx_MACARG(da)); | 
|  | memcpy(txhdr->rts_cts_mac1, wlhdr->addr1, ETH_ALEN);//FIXME! | 
|  | //	memcpy(txhdr->rts_cts_mac2, sa, ETH_ALEN); | 
|  |  | 
|  | *flags |= BCM43xx_TXHDRFLAG_RTSCTS; | 
|  | *flags |= BCM43xx_TXHDRFLAG_RTS; | 
|  | if (ofdm_modulation) | 
|  | *flags |= BCM43xx_TXHDRFLAG_RTSCTS_OFDM; | 
|  | if (fallback_ofdm_modulation) | 
|  | *flags |= BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM; | 
|  | } | 
|  |  | 
|  | void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm, | 
|  | struct bcm43xx_txhdr *txhdr, | 
|  | const unsigned char *fragment_data, | 
|  | const unsigned int fragment_len, | 
|  | const int is_first_fragment, | 
|  | const u16 cookie) | 
|  | { | 
|  | const struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); | 
|  | const struct ieee80211_hdr_4addr *wireless_header = (const struct ieee80211_hdr_4addr *)fragment_data; | 
|  | const struct ieee80211_security *secinfo = &bcm->ieee->sec; | 
|  | u8 bitrate; | 
|  | u8 fallback_bitrate; | 
|  | int ofdm_modulation; | 
|  | int fallback_ofdm_modulation; | 
|  | u16 plcp_fragment_len = fragment_len; | 
|  | u16 flags = 0; | 
|  | u16 control = 0; | 
|  | u16 wsec_rate = 0; | 
|  | u16 encrypt_frame; | 
|  | const u16 ftype = WLAN_FC_GET_TYPE(le16_to_cpu(wireless_header->frame_ctl)); | 
|  | const int is_mgt = (ftype == IEEE80211_FTYPE_MGMT); | 
|  |  | 
|  | /* Now construct the TX header. */ | 
|  | memset(txhdr, 0, sizeof(*txhdr)); | 
|  |  | 
|  | bitrate = ieee80211softmac_suggest_txrate(bcm->softmac, | 
|  | is_multicast_ether_addr(wireless_header->addr1), is_mgt); | 
|  | ofdm_modulation = !(ieee80211_is_cck_rate(bitrate)); | 
|  | fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate); | 
|  | fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate)); | 
|  |  | 
|  | /* Set Frame Control from 80211 header. */ | 
|  | txhdr->frame_control = wireless_header->frame_ctl; | 
|  | /* Copy address1 from 80211 header. */ | 
|  | memcpy(txhdr->mac1, wireless_header->addr1, 6); | 
|  | /* Set the fallback duration ID. */ | 
|  | txhdr->fallback_dur_id = bcm43xx_calc_duration_id((const struct ieee80211_hdr *)wireless_header, | 
|  | fallback_bitrate); | 
|  | /* Set the cookie (used as driver internal ID for the frame) */ | 
|  | txhdr->cookie = cpu_to_le16(cookie); | 
|  |  | 
|  | /* Hardware appends FCS. */ | 
|  | plcp_fragment_len += IEEE80211_FCS_LEN; | 
|  |  | 
|  | /* Hardware encryption. */ | 
|  | encrypt_frame = le16_to_cpup(&wireless_header->frame_ctl) & IEEE80211_FCTL_PROTECTED; | 
|  | if (encrypt_frame && !bcm->ieee->host_encrypt) { | 
|  | const struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)wireless_header; | 
|  | memcpy(txhdr->wep_iv, hdr->payload, 4); | 
|  | /* Hardware appends ICV. */ | 
|  | plcp_fragment_len += 4; | 
|  |  | 
|  | wsec_rate |= (bcm->key[secinfo->active_key].algorithm << BCM43xx_TXHDR_WSEC_ALGO_SHIFT) | 
|  | & BCM43xx_TXHDR_WSEC_ALGO_MASK; | 
|  | wsec_rate |= (secinfo->active_key << BCM43xx_TXHDR_WSEC_KEYINDEX_SHIFT) | 
|  | & BCM43xx_TXHDR_WSEC_KEYINDEX_MASK; | 
|  | } | 
|  |  | 
|  | /* Generate the PLCP header and the fallback PLCP header. */ | 
|  | bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->plcp), | 
|  | plcp_fragment_len, | 
|  | bitrate, ofdm_modulation); | 
|  | bcm43xx_generate_plcp_hdr(&txhdr->fallback_plcp, plcp_fragment_len, | 
|  | fallback_bitrate, fallback_ofdm_modulation); | 
|  |  | 
|  | /* Set the CONTROL field */ | 
|  | if (ofdm_modulation) | 
|  | control |= BCM43xx_TXHDRCTL_OFDM; | 
|  | if (bcm->short_preamble) //FIXME: could be the other way around, please test | 
|  | control |= BCM43xx_TXHDRCTL_SHORT_PREAMBLE; | 
|  | control |= (phy->antenna_diversity << BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT) | 
|  | & BCM43xx_TXHDRCTL_ANTENNADIV_MASK; | 
|  |  | 
|  | /* Set the FLAGS field */ | 
|  | if (!is_multicast_ether_addr(wireless_header->addr1) && | 
|  | !is_broadcast_ether_addr(wireless_header->addr1)) | 
|  | flags |= BCM43xx_TXHDRFLAG_EXPECTACK; | 
|  | if (1 /* FIXME: PS poll?? */) | 
|  | flags |= 0x10; // FIXME: unknown meaning. | 
|  | if (fallback_ofdm_modulation) | 
|  | flags |= BCM43xx_TXHDRFLAG_FALLBACKOFDM; | 
|  | if (is_first_fragment) | 
|  | flags |= BCM43xx_TXHDRFLAG_FIRSTFRAGMENT; | 
|  |  | 
|  | /* Set WSEC/RATE field */ | 
|  | wsec_rate |= (txhdr->plcp.raw[0] << BCM43xx_TXHDR_RATE_SHIFT) | 
|  | & BCM43xx_TXHDR_RATE_MASK; | 
|  |  | 
|  | /* Generate the RTS/CTS packet, if required. */ | 
|  | /* FIXME: We should first try with CTS-to-self, | 
|  | *        if we are on 80211g. If we get too many | 
|  | *        failures (hidden nodes), we should switch back to RTS/CTS. | 
|  | */ | 
|  | if (0/*FIXME txctl->use_rts_cts*/) { | 
|  | bcm43xx_generate_rts(phy, txhdr, &flags, | 
|  | 0/*FIXME txctl->rts_cts_rate*/, | 
|  | wireless_header); | 
|  | } | 
|  |  | 
|  | txhdr->flags = cpu_to_le16(flags); | 
|  | txhdr->control = cpu_to_le16(control); | 
|  | txhdr->wsec_rate = cpu_to_le16(wsec_rate); | 
|  | } | 
|  |  | 
|  | static s8 bcm43xx_rssi_postprocess(struct bcm43xx_private *bcm, | 
|  | u8 in_rssi, int ofdm, | 
|  | int adjust_2053, int adjust_2050) | 
|  | { | 
|  | struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); | 
|  | struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); | 
|  | s32 tmp; | 
|  |  | 
|  | switch (radio->version) { | 
|  | case 0x2050: | 
|  | if (ofdm) { | 
|  | tmp = in_rssi; | 
|  | if (tmp > 127) | 
|  | tmp -= 256; | 
|  | tmp *= 73; | 
|  | tmp /= 64; | 
|  | if (adjust_2050) | 
|  | tmp += 25; | 
|  | else | 
|  | tmp -= 3; | 
|  | } else { | 
|  | if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) { | 
|  | if (in_rssi > 63) | 
|  | in_rssi = 63; | 
|  | tmp = radio->nrssi_lt[in_rssi]; | 
|  | tmp = 31 - tmp; | 
|  | tmp *= -131; | 
|  | tmp /= 128; | 
|  | tmp -= 57; | 
|  | } else { | 
|  | tmp = in_rssi; | 
|  | tmp = 31 - tmp; | 
|  | tmp *= -149; | 
|  | tmp /= 128; | 
|  | tmp -= 68; | 
|  | } | 
|  | if (phy->type == BCM43xx_PHYTYPE_G && | 
|  | adjust_2050) | 
|  | tmp += 25; | 
|  | } | 
|  | break; | 
|  | case 0x2060: | 
|  | if (in_rssi > 127) | 
|  | tmp = in_rssi - 256; | 
|  | else | 
|  | tmp = in_rssi; | 
|  | break; | 
|  | default: | 
|  | tmp = in_rssi; | 
|  | tmp -= 11; | 
|  | tmp *= 103; | 
|  | tmp /= 64; | 
|  | if (adjust_2053) | 
|  | tmp -= 109; | 
|  | else | 
|  | tmp -= 83; | 
|  | } | 
|  |  | 
|  | return (s8)tmp; | 
|  | } | 
|  |  | 
|  | //TODO | 
|  | #if 0 | 
|  | static s8 bcm43xx_rssinoise_postprocess(struct bcm43xx_private *bcm, | 
|  | u8 in_rssi) | 
|  | { | 
|  | struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); | 
|  | s8 ret; | 
|  |  | 
|  | if (phy->type == BCM43xx_PHYTYPE_A) { | 
|  | //TODO: Incomplete specs. | 
|  | ret = 0; | 
|  | } else | 
|  | ret = bcm43xx_rssi_postprocess(bcm, in_rssi, 0, 1, 1); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | int bcm43xx_rx(struct bcm43xx_private *bcm, | 
|  | struct sk_buff *skb, | 
|  | struct bcm43xx_rxhdr *rxhdr) | 
|  | { | 
|  | struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); | 
|  | struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); | 
|  | struct bcm43xx_plcp_hdr4 *plcp; | 
|  | struct ieee80211_rx_stats stats; | 
|  | struct ieee80211_hdr_4addr *wlhdr; | 
|  | u16 frame_ctl; | 
|  | int is_packet_for_us = 0; | 
|  | int err = -EINVAL; | 
|  | const u16 rxflags1 = le16_to_cpu(rxhdr->flags1); | 
|  | const u16 rxflags2 = le16_to_cpu(rxhdr->flags2); | 
|  | const u16 rxflags3 = le16_to_cpu(rxhdr->flags3); | 
|  | const int is_ofdm = !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_OFDM); | 
|  |  | 
|  | if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME) { | 
|  | plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data + 2); | 
|  | /* Skip two unknown bytes and the PLCP header. */ | 
|  | skb_pull(skb, 2 + sizeof(struct bcm43xx_plcp_hdr6)); | 
|  | } else { | 
|  | plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data); | 
|  | /* Skip the PLCP header. */ | 
|  | skb_pull(skb, sizeof(struct bcm43xx_plcp_hdr6)); | 
|  | } | 
|  | /* The SKB contains the PAYLOAD (wireless header + data) | 
|  | * at this point. The FCS at the end is stripped. | 
|  | */ | 
|  |  | 
|  | memset(&stats, 0, sizeof(stats)); | 
|  | stats.mac_time = le16_to_cpu(rxhdr->mactime); | 
|  | stats.rssi = bcm43xx_rssi_postprocess(bcm, rxhdr->rssi, is_ofdm, | 
|  | !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_2053RSSIADJ), | 
|  | !!(rxflags3 & BCM43xx_RXHDR_FLAGS3_2050RSSIADJ)); | 
|  | stats.signal = rxhdr->signal_quality;	//FIXME | 
|  | //TODO	stats.noise = | 
|  | if (is_ofdm) | 
|  | stats.rate = bcm43xx_plcp_get_bitrate_ofdm(plcp); | 
|  | else | 
|  | stats.rate = bcm43xx_plcp_get_bitrate_cck(plcp); | 
|  | //printk("RX ofdm %d, rate == %u\n", is_ofdm, stats.rate); | 
|  | stats.received_channel = radio->channel; | 
|  | //TODO	stats.control = | 
|  | stats.mask = IEEE80211_STATMASK_SIGNAL | | 
|  | //TODO		     IEEE80211_STATMASK_NOISE | | 
|  | IEEE80211_STATMASK_RATE | | 
|  | IEEE80211_STATMASK_RSSI; | 
|  | if (phy->type == BCM43xx_PHYTYPE_A) | 
|  | stats.freq = IEEE80211_52GHZ_BAND; | 
|  | else | 
|  | stats.freq = IEEE80211_24GHZ_BAND; | 
|  | stats.len = skb->len; | 
|  |  | 
|  | bcm->stats.last_rx = jiffies; | 
|  | if (bcm->ieee->iw_mode == IW_MODE_MONITOR) { | 
|  | err = ieee80211_rx(bcm->ieee, skb, &stats); | 
|  | return (err == 0) ? -EINVAL : 0; | 
|  | } | 
|  |  | 
|  | wlhdr = (struct ieee80211_hdr_4addr *)(skb->data); | 
|  |  | 
|  | switch (bcm->ieee->iw_mode) { | 
|  | case IW_MODE_ADHOC: | 
|  | if (memcmp(wlhdr->addr1, bcm->net_dev->dev_addr, ETH_ALEN) == 0 || | 
|  | memcmp(wlhdr->addr3, bcm->ieee->bssid, ETH_ALEN) == 0 || | 
|  | is_broadcast_ether_addr(wlhdr->addr1) || | 
|  | is_multicast_ether_addr(wlhdr->addr1) || | 
|  | bcm->net_dev->flags & IFF_PROMISC) | 
|  | is_packet_for_us = 1; | 
|  | break; | 
|  | case IW_MODE_INFRA: | 
|  | default: | 
|  | /* When receiving multicast or broadcast packets, filter out | 
|  | the packets we send ourself; we shouldn't see those */ | 
|  | if (memcmp(wlhdr->addr3, bcm->ieee->bssid, ETH_ALEN) == 0 || | 
|  | memcmp(wlhdr->addr1, bcm->net_dev->dev_addr, ETH_ALEN) == 0 || | 
|  | (memcmp(wlhdr->addr3, bcm->net_dev->dev_addr, ETH_ALEN) && | 
|  | (is_broadcast_ether_addr(wlhdr->addr1) || | 
|  | is_multicast_ether_addr(wlhdr->addr1) || | 
|  | bcm->net_dev->flags & IFF_PROMISC))) | 
|  | is_packet_for_us = 1; | 
|  | break; | 
|  | } | 
|  |  | 
|  | frame_ctl = le16_to_cpu(wlhdr->frame_ctl); | 
|  | if ((frame_ctl & IEEE80211_FCTL_PROTECTED) && !bcm->ieee->host_decrypt) { | 
|  | frame_ctl &= ~IEEE80211_FCTL_PROTECTED; | 
|  | wlhdr->frame_ctl = cpu_to_le16(frame_ctl); | 
|  | /* trim IV and ICV */ | 
|  | /* FIXME: this must be done only for WEP encrypted packets */ | 
|  | if (skb->len < 32) { | 
|  | dprintkl(KERN_ERR PFX "RX packet dropped (PROTECTED flag " | 
|  | "set and length < 32)\n"); | 
|  | return -EINVAL; | 
|  | } else { | 
|  | memmove(skb->data + 4, skb->data, 24); | 
|  | skb_pull(skb, 4); | 
|  | skb_trim(skb, skb->len - 4); | 
|  | stats.len -= 8; | 
|  | } | 
|  | wlhdr = (struct ieee80211_hdr_4addr *)(skb->data); | 
|  | } | 
|  |  | 
|  | switch (WLAN_FC_GET_TYPE(frame_ctl)) { | 
|  | case IEEE80211_FTYPE_MGMT: | 
|  | ieee80211_rx_mgt(bcm->ieee, wlhdr, &stats); | 
|  | break; | 
|  | case IEEE80211_FTYPE_DATA: | 
|  | if (is_packet_for_us) { | 
|  | err = ieee80211_rx(bcm->ieee, skb, &stats); | 
|  | err = (err == 0) ? -EINVAL : 0; | 
|  | } | 
|  | break; | 
|  | case IEEE80211_FTYPE_CTL: | 
|  | break; | 
|  | default: | 
|  | assert(0); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } |